mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-09 04:51:37 -07:00
Compare commits
303 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2250eb3e78 | |||
| aebb5a5178 | |||
| b4d0b1d17d | |||
| 20d35acce1 | |||
| 6c4672c158 | |||
| 3095f46d7e | |||
| 3add85e7a7 | |||
| ad5c9469d8 | |||
| 710bb4369f | |||
| 944fdd11f2 | |||
| 07739b48ee | |||
| b269fd00f0 | |||
| 04c391074d | |||
| fb2549a7da | |||
| 11180e36ae | |||
| 8c111f1baf | |||
| 5e149d2cae | |||
| 41bd8cf714 | |||
| 1604f37cb8 | |||
| 935865a978 | |||
| d7fdccb08c | |||
| 98827c867d | |||
| 8a1e7bb6fb | |||
| 8f706a14f8 | |||
| e5af98eabe | |||
| 858c78b844 | |||
| a61d8daec1 | |||
| cceac781e7 | |||
| 113feacbc7 | |||
| 7bcf3b1a0d | |||
| f54977336b | |||
| b0f0e4d02a | |||
| abcbb8b47a | |||
| d85c8f0b2c | |||
| 97e9c50d11 | |||
| 9b75656400 | |||
| 60b6c798a5 | |||
| ad3675cdd6 | |||
| 9d1db48549 | |||
| b0b60fe7ed | |||
| 87b099b5aa | |||
| 77efdcd899 | |||
| 58c0abb98d | |||
| d93cc18dac | |||
| ecddf3b7f1 | |||
| a3821615dc | |||
| f9a7d2bf04 | |||
| c219ca5b1b | |||
| f64e8ffd64 | |||
| e3e04ffa22 | |||
| 043dd98e91 | |||
| b91e7f5c51 | |||
| bb58926b7a | |||
| 1d3c061add | |||
| de5ce90fa7 | |||
| 3f7a369aa1 | |||
| 94b4af5d36 | |||
| e378516011 | |||
| 2b1afa0762 | |||
| 0cec2d7827 | |||
| 8d96c68528 | |||
| 0da6a3e214 | |||
| 41d38bf964 | |||
| 05a1704898 | |||
| 1818df786d | |||
| b23f1dd5ff | |||
| 96abe8627c | |||
| 6ec7845249 | |||
| 56b62996c3 | |||
| 6cb0f5d8c5 | |||
| df53fec828 | |||
| 16fbfd0418 | |||
| a9b18c1ec0 | |||
| 8e4f9cb777 | |||
| 15f7148eae | |||
| 1e5267c8e7 | |||
| 96a4cecee5 | |||
| 8ba36ca85c | |||
| eaab17f8fb | |||
| 1165d65c94 | |||
| ee818304f3 | |||
| 30ff15e538 | |||
| 17a01a81d8 | |||
| 595726e6c5 | |||
| fed03e896c | |||
| 2201f365a1 | |||
| f2505b096d | |||
| 242307dd44 | |||
| 8aab395c70 | |||
| 8506e64345 | |||
| 25f1d0906d | |||
| 67b786c738 | |||
| fd582015fe | |||
| 58e12f0b17 | |||
| 9ffa4a4eb1 | |||
| eb06cb5d23 | |||
| 5c1201fa42 | |||
| c0ca26ac17 | |||
| b41395e3b4 | |||
| d40c207fde | |||
| 8660718ebe | |||
| 485afb011c | |||
| 9e94e9dac1 | |||
| cebe6bd982 | |||
| c58da76562 | |||
| e896efdaeb | |||
| ff0973e0f5 | |||
| ae322739b5 | |||
| f6345d20f7 | |||
| 227aab8f39 | |||
| d6824ea607 | |||
| 2f88ded81a | |||
| bbebb4ad60 | |||
| 700f9c94bd | |||
| c712de0cd0 | |||
| 1f5f417116 | |||
| a573161abd | |||
| f1c13d6bd8 | |||
| a37826e646 | |||
| 3b4169c903 | |||
| 0eb97dba6e | |||
| fa5e6d2d84 | |||
| 4668dafa2b | |||
| cc5faecced | |||
| 0530f86a48 | |||
| 4fea5720a5 | |||
| 30cfdd73b0 | |||
| 950fd2d5ce | |||
| 74d75fb7fb | |||
| 7ee2c1084f | |||
| c0b7dd7da6 | |||
| be09584ea5 | |||
| 5ac8e8f9fd | |||
| 2eea06fc73 | |||
| 0fa269811a | |||
| 6c185a5dca | |||
| f428f26c8e | |||
| 44c9b988bb | |||
| 29987c20c0 | |||
| 51d4dde63c | |||
| 362aa82f59 | |||
| ebfbe4d260 | |||
| e71cf96bbc | |||
| 383c17e842 | |||
| 00b1a41540 | |||
| 8fd318ea7a | |||
| 1deb53cddb | |||
| 05b5b8503d | |||
| 5e1b376e22 | |||
| 309a770cbf | |||
| 393e4a9db6 | |||
| efae99971b | |||
| 4454849252 | |||
| c752d0b541 | |||
| 5969796fbf | |||
| 6520794764 | |||
| 9d3f835e31 | |||
| 70aa2ff90a | |||
| a60a7f64b9 | |||
| 2368014d06 | |||
| 92402a75f6 | |||
| 4bea5dbac1 | |||
| 2b26384b89 | |||
| c685b404ea | |||
| a3e77dcc3b | |||
| 7305478261 | |||
| 94e0707456 | |||
| f95bb77f72 | |||
| d3bd1c2110 | |||
| f745fff640 | |||
| 8f4e3b552e | |||
| 1ea353dbf7 | |||
| e3d41f0a9e | |||
| 3b53c99576 | |||
| a4cec2e8b8 | |||
| e8c5bb4629 | |||
| c72a269b7c | |||
| 94d35d9918 | |||
| 448827c0e4 | |||
| 04278cd654 | |||
| 15942fbf25 | |||
| 214537b420 | |||
| c330e5b5c1 | |||
| ec5a84a537 | |||
| 5b6c96cd18 | |||
| e2fa9be7e2 | |||
| a6eb64df6c | |||
| 9cdb7b2584 | |||
| d1a1e8f646 | |||
| 75fcd50b9a | |||
| c6a446fe87 | |||
| 90369b669b | |||
| f5719ae47c | |||
| 2a70faca14 | |||
| 902e877467 | |||
| caa8863c97 | |||
| ebb1a07dd0 | |||
| 0a16f7a6f1 | |||
| 3620e62ff7 | |||
| d84b29866f | |||
| 68f663ea85 | |||
| efc84cd390 | |||
| ea49331799 | |||
| 059175de3b | |||
| 7b95562827 | |||
| c44f618b7d | |||
| 60ef4fda4d | |||
| 1b143b9fa3 | |||
| ec7e75a57f | |||
| 20223345a4 | |||
| 3812d5e002 | |||
| 2416380f34 | |||
| 5200e3fe7a | |||
| b72d49ceb5 | |||
| 689982a38d | |||
| 334e08c750 | |||
| f84a092977 | |||
| aad41fc762 | |||
| ece78d22d2 | |||
| 5c52f3cf2f | |||
| 889cd5461c | |||
| 6127339c56 | |||
| e296b050ee | |||
| 9bbe26f80f | |||
| 7cd2bc26f4 | |||
| 6dcf38b85b | |||
| a61ae7c861 | |||
| 8a0f9ae108 | |||
| fd1eb17771 | |||
| 3e449e93dd | |||
| 436c5dc40c | |||
| 0729f2b4fb | |||
| 76229cabfa | |||
| a7e492e2c2 | |||
| 69db9c6390 | |||
| 6def86bb4b | |||
| 38c1fd5824 | |||
| d5e6d722d3 | |||
| 2af777b6cb | |||
| 5bda2546f7 | |||
| 36644a3c09 | |||
| 7e582dd671 | |||
| c7ac28f6a1 | |||
| 4bfa1ea76c | |||
| 758ace844c | |||
| b9d7b8d616 | |||
| a294f067a9 | |||
| 2e29687cee | |||
| a0e25be196 | |||
| a0cff82320 | |||
| d510d5e49f | |||
| 9591eb2ed5 | |||
| 21969fec61 | |||
| 6ee818b394 | |||
| b22e43c335 | |||
| 6dc9fdb718 | |||
| f252084b1f | |||
| 9f97056c14 | |||
| d2ca91b830 | |||
| 59705035f9 | |||
| befb0f7868 | |||
| d5bb67e9e6 | |||
| f086b4710a | |||
| a4d16e97a1 | |||
| 6348b56c8b | |||
| 2d1260bec9 | |||
| c753711d86 | |||
| 5726b7d954 | |||
| 4240e0025e | |||
| caa344c88d | |||
| 46b9bd8c0e | |||
| 3426af2d6c | |||
| efc3f9916e | |||
| 95bc1892f5 | |||
| 4cdadeedc3 | |||
| cb0a57eaa9 | |||
| 51a4d97f03 | |||
| d979129a50 | |||
| 9045c9ed0c | |||
| 9a5f3fed8c | |||
| 04bd3cc525 | |||
| 9fc8587e2c | |||
| 2a39c60fe4 | |||
| b11d839fb6 | |||
| 0076c07076 | |||
| a664d9804f | |||
| c4e45d509a | |||
| c1cf6c1a95 | |||
| 317931a3c5 | |||
| 759d095548 | |||
| 7854aa22d9 | |||
| 72d5b87b52 | |||
| 154e0d41c6 | |||
| 3fccc77829 | |||
| 85f1a60cf3 | |||
| cb8bb8bfd8 | |||
| 46b58ad48d | |||
| 7283b5e75c | |||
| 21e6e28a3a | |||
| d77ae9abab | |||
| 363769d4d3 | |||
| b6c47e7963 | |||
| 5533a327eb |
@@ -1,2 +1,6 @@
|
|||||||
*.odin linguist-language=Odin
|
*.odin linguist-language=Odin
|
||||||
* text=auto
|
* text=auto
|
||||||
|
|
||||||
|
# These files must always have *nix line-endings
|
||||||
|
Makefile text eol=lf
|
||||||
|
*.sh text eol=lf
|
||||||
@@ -62,7 +62,7 @@ jobs:
|
|||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
build_macOS:
|
build_macOS:
|
||||||
name: MacOS Build, Check, and Test
|
name: MacOS Build, Check, and Test
|
||||||
runs-on: macos-latest
|
runs-on: macos-13
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: Download LLVM, and setup PATH
|
- name: Download LLVM, and setup PATH
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ jobs:
|
|||||||
build_macos:
|
build_macos:
|
||||||
name: MacOS Build
|
name: MacOS Build
|
||||||
if: github.repository == 'odin-lang/Odin'
|
if: github.repository == 'odin-lang/Odin'
|
||||||
runs-on: macos-latest
|
runs-on: macos-13
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: Download LLVM and setup PATH
|
- name: Download LLVM and setup PATH
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ tests/documentation/all.odin-doc
|
|||||||
tests/internal/test_map
|
tests/internal/test_map
|
||||||
tests/internal/test_pow
|
tests/internal/test_pow
|
||||||
tests/internal/test_rtti
|
tests/internal/test_rtti
|
||||||
|
tests/core/test_base64
|
||||||
|
tests/core/test_cbor
|
||||||
tests/core/test_core_compress
|
tests/core/test_core_compress
|
||||||
tests/core/test_core_container
|
tests/core/test_core_container
|
||||||
tests/core/test_core_filepath
|
tests/core/test_core_filepath
|
||||||
@@ -40,8 +42,10 @@ tests/core/test_core_net
|
|||||||
tests/core/test_core_os_exit
|
tests/core/test_core_os_exit
|
||||||
tests/core/test_core_reflect
|
tests/core/test_core_reflect
|
||||||
tests/core/test_core_strings
|
tests/core/test_core_strings
|
||||||
|
tests/core/test_core_time
|
||||||
tests/core/test_crypto
|
tests/core/test_crypto
|
||||||
tests/core/test_hash
|
tests/core/test_hash
|
||||||
|
tests/core/test_hex
|
||||||
tests/core/test_hxa
|
tests/core/test_hxa
|
||||||
tests/core/test_json
|
tests/core/test_json
|
||||||
tests/core/test_linalg_glsl_math
|
tests/core/test_linalg_glsl_math
|
||||||
@@ -319,3 +323,5 @@ build.sh
|
|||||||
|
|
||||||
# RAD debugger project file
|
# RAD debugger project file
|
||||||
*.raddbg
|
*.raddbg
|
||||||
|
|
||||||
|
misc/featuregen/featuregen
|
||||||
|
|||||||
@@ -169,15 +169,18 @@ type_has_nil :: proc($T: typeid) -> bool ---
|
|||||||
|
|
||||||
type_is_specialization_of :: proc($T, $S: typeid) -> bool ---
|
type_is_specialization_of :: proc($T, $S: typeid) -> bool ---
|
||||||
|
|
||||||
type_is_variant_of :: proc($U, $V: typeid) -> bool where type_is_union(U) ---
|
type_is_variant_of :: proc($U, $V: typeid) -> bool where type_is_union(U) ---
|
||||||
type_union_tag_type :: proc($T: typeid) -> typeid where type_is_union(T) ---
|
type_union_tag_type :: proc($T: typeid) -> typeid where type_is_union(T) ---
|
||||||
type_union_tag_offset :: proc($T: typeid) -> uintptr where type_is_union(T) ---
|
type_union_tag_offset :: proc($T: typeid) -> uintptr where type_is_union(T) ---
|
||||||
type_union_base_tag_value :: proc($T: typeid) -> int where type_is_union(U) ---
|
type_union_base_tag_value :: proc($T: typeid) -> int where type_is_union(U) ---
|
||||||
type_union_variant_count :: proc($T: typeid) -> int where type_is_union(T) ---
|
type_union_variant_count :: proc($T: typeid) -> int where type_is_union(T) ---
|
||||||
type_variant_type_of :: proc($T: typeid, $index: int) -> typeid where type_is_union(T) ---
|
type_variant_type_of :: proc($T: typeid, $index: int) -> typeid where type_is_union(T) ---
|
||||||
type_variant_index_of :: proc($U, $V: typeid) -> int where type_is_union(U) ---
|
type_variant_index_of :: proc($U, $V: typeid) -> int where type_is_union(U) ---
|
||||||
|
|
||||||
type_has_field :: proc($T: typeid, $name: string) -> bool ---
|
type_bit_set_elem_type :: proc($T: typeid) -> typeid where type_is_bit_set(T) ---
|
||||||
|
type_bit_set_underlying_type :: proc($T: typeid) -> typeid where type_is_bit_set(T) ---
|
||||||
|
|
||||||
|
type_has_field :: proc($T: typeid, $name: string) -> bool ---
|
||||||
type_field_type :: proc($T: typeid, $name: string) -> typeid ---
|
type_field_type :: proc($T: typeid, $name: string) -> typeid ---
|
||||||
|
|
||||||
type_proc_parameter_count :: proc($T: typeid) -> int where type_is_proc(T) ---
|
type_proc_parameter_count :: proc($T: typeid) -> int where type_is_proc(T) ---
|
||||||
@@ -282,6 +285,12 @@ simd_reverse :: proc(a: #simd[N]T) -> #simd[N]T ---
|
|||||||
simd_rotate_left :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
|
simd_rotate_left :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
|
||||||
simd_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
|
simd_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
|
||||||
|
|
||||||
|
// Checks if the current target supports the given target features.
|
||||||
|
//
|
||||||
|
// Takes a constant comma-seperated string (eg: "sha512,sse4.1"), or a procedure type which has either
|
||||||
|
// `@(require_target_feature)` or `@(enable_target_feature)` as its input and returns a boolean indicating
|
||||||
|
// if all listed features are supported.
|
||||||
|
has_target_feature :: proc($test: $T) -> bool where type_is_string(T) || type_is_proc(T) ---
|
||||||
|
|
||||||
// WASM targets only
|
// WASM targets only
|
||||||
wasm_memory_grow :: proc(index, delta: uintptr) -> int ---
|
wasm_memory_grow :: proc(index, delta: uintptr) -> int ---
|
||||||
@@ -293,9 +302,9 @@ wasm_memory_size :: proc(index: uintptr) -> int ---
|
|||||||
// 0 - indicates that the thread blocked and then was woken up
|
// 0 - indicates that the thread blocked and then was woken up
|
||||||
// 1 - the loaded value from `ptr` did not match `expected`, the thread did not block
|
// 1 - the loaded value from `ptr` did not match `expected`, the thread did not block
|
||||||
// 2 - the thread blocked, but the timeout
|
// 2 - the thread blocked, but the timeout
|
||||||
@(enable_target_feature="atomics")
|
@(require_target_feature="atomics")
|
||||||
wasm_memory_atomic_wait32 :: proc(ptr: ^u32, expected: u32, timeout_ns: i64) -> u32 ---
|
wasm_memory_atomic_wait32 :: proc(ptr: ^u32, expected: u32, timeout_ns: i64) -> u32 ---
|
||||||
@(enable_target_feature="atomics")
|
@(require_target_feature="atomics")
|
||||||
wasm_memory_atomic_notify32 :: proc(ptr: ^u32, waiters: u32) -> (waiters_woken_up: u32) ---
|
wasm_memory_atomic_notify32 :: proc(ptr: ^u32, waiters: u32) -> (waiters_woken_up: u32) ---
|
||||||
|
|
||||||
// x86 Targets (i386, amd64)
|
// x86 Targets (i386, amd64)
|
||||||
|
|||||||
@@ -273,14 +273,14 @@ Typeid_Kind :: enum u8 {
|
|||||||
}
|
}
|
||||||
#assert(len(Typeid_Kind) < 32)
|
#assert(len(Typeid_Kind) < 32)
|
||||||
|
|
||||||
// Typeid_Bit_Field :: bit_field #align(align_of(uintptr)) {
|
Typeid_Bit_Field :: bit_field uintptr {
|
||||||
// index: 8*size_of(uintptr) - 8,
|
index: uintptr | 8*size_of(uintptr) - 8,
|
||||||
// kind: 5, // Typeid_Kind
|
kind: Typeid_Kind | 5, // Typeid_Kind
|
||||||
// named: 1,
|
named: bool | 1,
|
||||||
// special: 1, // signed, cstring, etc
|
special: bool | 1, // signed, cstring, etc
|
||||||
// reserved: 1,
|
reserved: bool | 1,
|
||||||
// }
|
}
|
||||||
// #assert(size_of(Typeid_Bit_Field) == size_of(uintptr));
|
#assert(size_of(Typeid_Bit_Field) == size_of(uintptr))
|
||||||
|
|
||||||
// NOTE(bill): only the ones that are needed (not all types)
|
// NOTE(bill): only the ones that are needed (not all types)
|
||||||
// This will be set by the compiler
|
// This will be set by the compiler
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ copy_slice :: proc "contextless" (dst, src: $T/[]$E) -> int {
|
|||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
// `copy_from_string` is a built-in procedure that copies elements from a source slice `src` to a destination string `dst`.
|
// `copy_from_string` is a built-in procedure that copies elements from a source string `src` to a destination slice `dst`.
|
||||||
// The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum
|
// The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum
|
||||||
// of len(src) and len(dst).
|
// of len(src) and len(dst).
|
||||||
//
|
//
|
||||||
@@ -53,7 +53,7 @@ copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int
|
|||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
// `copy` is a built-in procedure that copies elements from a source slice `src` to a destination slice/string `dst`.
|
// `copy` is a built-in procedure that copies elements from a source slice/string `src` to a destination slice `dst`.
|
||||||
// The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum
|
// The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum
|
||||||
// of len(src) and len(dst).
|
// of len(src) and len(dst).
|
||||||
@builtin
|
@builtin
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ type_assertion_trap :: proc "contextless" () -> ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
bounds_check_error :: proc "contextless" (file: string, line, column: i32, index, count: int) {
|
bounds_check_error :: proc "contextless" (file: string, line, column: i32, index, count: int) {
|
||||||
if uint(index) < uint(count) {
|
if uint(index) < uint(count) {
|
||||||
return
|
return
|
||||||
@@ -61,6 +62,7 @@ multi_pointer_slice_handle_error :: proc "contextless" (file: string, line, colu
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
multi_pointer_slice_expr_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int) {
|
multi_pointer_slice_expr_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int) {
|
||||||
if lo <= hi {
|
if lo <= hi {
|
||||||
return
|
return
|
||||||
@@ -68,6 +70,7 @@ multi_pointer_slice_expr_error :: proc "contextless" (file: string, line, column
|
|||||||
multi_pointer_slice_handle_error(file, line, column, lo, hi)
|
multi_pointer_slice_handle_error(file, line, column, lo, hi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
slice_expr_error_hi :: proc "contextless" (file: string, line, column: i32, hi: int, len: int) {
|
slice_expr_error_hi :: proc "contextless" (file: string, line, column: i32, hi: int, len: int) {
|
||||||
if 0 <= hi && hi <= len {
|
if 0 <= hi && hi <= len {
|
||||||
return
|
return
|
||||||
@@ -75,6 +78,7 @@ slice_expr_error_hi :: proc "contextless" (file: string, line, column: i32, hi:
|
|||||||
slice_handle_error(file, line, column, 0, hi, len)
|
slice_handle_error(file, line, column, 0, hi, len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
slice_expr_error_lo_hi :: proc "contextless" (file: string, line, column: i32, lo, hi: int, len: int) {
|
slice_expr_error_lo_hi :: proc "contextless" (file: string, line, column: i32, lo, hi: int, len: int) {
|
||||||
if 0 <= lo && lo <= len && lo <= hi && hi <= len {
|
if 0 <= lo && lo <= len && lo <= hi && hi <= len {
|
||||||
return
|
return
|
||||||
@@ -82,6 +86,7 @@ slice_expr_error_lo_hi :: proc "contextless" (file: string, line, column: i32, l
|
|||||||
slice_handle_error(file, line, column, lo, hi, len)
|
slice_handle_error(file, line, column, lo, hi, len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) {
|
dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) {
|
||||||
if 0 <= low && low <= high && high <= max {
|
if 0 <= low && low <= high && high <= max {
|
||||||
return
|
return
|
||||||
@@ -102,6 +107,7 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) {
|
matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) {
|
||||||
if uint(row_index) < uint(row_count) &&
|
if uint(row_index) < uint(row_count) &&
|
||||||
uint(column_index) < uint(column_count) {
|
uint(column_index) < uint(column_count) {
|
||||||
@@ -224,6 +230,7 @@ when ODIN_NO_RTTI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
make_slice_error_loc :: #force_inline proc "contextless" (loc := #caller_location, len: int) {
|
make_slice_error_loc :: #force_inline proc "contextless" (loc := #caller_location, len: int) {
|
||||||
if 0 <= len {
|
if 0 <= len {
|
||||||
return
|
return
|
||||||
@@ -239,6 +246,7 @@ make_slice_error_loc :: #force_inline proc "contextless" (loc := #caller_locatio
|
|||||||
handle_error(loc, len)
|
handle_error(loc, len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
make_dynamic_array_error_loc :: #force_inline proc "contextless" (loc := #caller_location, len, cap: int) {
|
make_dynamic_array_error_loc :: #force_inline proc "contextless" (loc := #caller_location, len, cap: int) {
|
||||||
if 0 <= len && len <= cap {
|
if 0 <= len && len <= cap {
|
||||||
return
|
return
|
||||||
@@ -256,6 +264,7 @@ make_dynamic_array_error_loc :: #force_inline proc "contextless" (loc := #caller
|
|||||||
handle_error(loc, len, cap)
|
handle_error(loc, len, cap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
make_map_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_location, cap: int) {
|
make_map_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_location, cap: int) {
|
||||||
if 0 <= cap {
|
if 0 <= cap {
|
||||||
return
|
return
|
||||||
@@ -274,19 +283,22 @@ make_map_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_loca
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
bounds_check_error_loc :: #force_inline proc "contextless" (loc := #caller_location, index, count: int) {
|
bounds_check_error_loc :: #force_inline proc "contextless" (loc := #caller_location, index, count: int) {
|
||||||
bounds_check_error(loc.file_path, loc.line, loc.column, index, count)
|
bounds_check_error(loc.file_path, loc.line, loc.column, index, count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
slice_expr_error_hi_loc :: #force_inline proc "contextless" (loc := #caller_location, hi: int, len: int) {
|
slice_expr_error_hi_loc :: #force_inline proc "contextless" (loc := #caller_location, hi: int, len: int) {
|
||||||
slice_expr_error_hi(loc.file_path, loc.line, loc.column, hi, len)
|
slice_expr_error_hi(loc.file_path, loc.line, loc.column, hi, len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
slice_expr_error_lo_hi_loc :: #force_inline proc "contextless" (loc := #caller_location, lo, hi: int, len: int) {
|
slice_expr_error_lo_hi_loc :: #force_inline proc "contextless" (loc := #caller_location, lo, hi: int, len: int) {
|
||||||
slice_expr_error_lo_hi(loc.file_path, loc.line, loc.column, lo, hi, len)
|
slice_expr_error_lo_hi(loc.file_path, loc.line, loc.column, lo, hi, len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
dynamic_array_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_location, low, high, max: int) {
|
dynamic_array_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_location, low, high, max: int) {
|
||||||
dynamic_array_expr_error(loc.file_path, loc.line, loc.column, low, high, max)
|
dynamic_array_expr_error(loc.file_path, loc.line, loc.column, low, high, max)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1042,19 +1042,17 @@ fixdfti :: proc(a: u64) -> i128 {
|
|||||||
__write_bits :: proc "contextless" (dst, src: [^]byte, offset: uintptr, size: uintptr) {
|
__write_bits :: proc "contextless" (dst, src: [^]byte, offset: uintptr, size: uintptr) {
|
||||||
for i in 0..<size {
|
for i in 0..<size {
|
||||||
j := offset+i
|
j := offset+i
|
||||||
the_bit := byte((src[i/8]) & (1<<(i&7)) != 0)
|
the_bit := byte((src[i>>3]) & (1<<(i&7)) != 0)
|
||||||
b := the_bit<<(j&7)
|
dst[j>>3] &~= 1<<(j&7)
|
||||||
dst[j/8] &~= b
|
dst[j>>3] |= the_bit<<(j&7)
|
||||||
dst[j/8] |= b
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__read_bits :: proc "contextless" (dst, src: [^]byte, offset: uintptr, size: uintptr) {
|
__read_bits :: proc "contextless" (dst, src: [^]byte, offset: uintptr, size: uintptr) {
|
||||||
for j in 0..<size {
|
for j in 0..<size {
|
||||||
i := offset+j
|
i := offset+j
|
||||||
the_bit := byte((src[i/8]) & (1<<(i&7)) != 0)
|
the_bit := byte((src[i>>3]) & (1<<(i&7)) != 0)
|
||||||
b := the_bit<<(j&7)
|
dst[j>>3] &~= 1<<(j&7)
|
||||||
dst[j/8] &~= b
|
dst[j>>3] |= the_bit<<(j&7)
|
||||||
dst[j/8] |= b
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+22
-16
@@ -6,7 +6,7 @@ _INTEGER_DIGITS :: "0123456789abcdefghijklmnopqrstuvwxyz"
|
|||||||
_INTEGER_DIGITS_VAR := _INTEGER_DIGITS
|
_INTEGER_DIGITS_VAR := _INTEGER_DIGITS
|
||||||
|
|
||||||
when !ODIN_NO_RTTI {
|
when !ODIN_NO_RTTI {
|
||||||
print_any_single :: proc "contextless" (arg: any) {
|
print_any_single :: #force_no_inline proc "contextless" (arg: any) {
|
||||||
x := arg
|
x := arg
|
||||||
if x.data == nil {
|
if x.data == nil {
|
||||||
print_string("nil")
|
print_string("nil")
|
||||||
@@ -72,7 +72,7 @@ when !ODIN_NO_RTTI {
|
|||||||
print_string("<invalid-value>")
|
print_string("<invalid-value>")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println_any :: proc "contextless" (args: ..any) {
|
println_any :: #force_no_inline proc "contextless" (args: ..any) {
|
||||||
context = default_context()
|
context = default_context()
|
||||||
loop: for arg, i in args {
|
loop: for arg, i in args {
|
||||||
assert(arg.id != nil)
|
assert(arg.id != nil)
|
||||||
@@ -122,12 +122,12 @@ encode_rune :: proc "contextless" (c: rune) -> ([4]u8, int) {
|
|||||||
return buf, 4
|
return buf, 4
|
||||||
}
|
}
|
||||||
|
|
||||||
print_string :: proc "contextless" (str: string) -> (n: int) {
|
print_string :: #force_no_inline proc "contextless" (str: string) -> (n: int) {
|
||||||
n, _ = stderr_write(transmute([]byte)str)
|
n, _ = stderr_write(transmute([]byte)str)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
print_strings :: proc "contextless" (args: ..string) -> (n: int) {
|
print_strings :: #force_no_inline proc "contextless" (args: ..string) -> (n: int) {
|
||||||
for str in args {
|
for str in args {
|
||||||
m, err := stderr_write(transmute([]byte)str)
|
m, err := stderr_write(transmute([]byte)str)
|
||||||
n += m
|
n += m
|
||||||
@@ -138,12 +138,12 @@ print_strings :: proc "contextless" (args: ..string) -> (n: int) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
print_byte :: proc "contextless" (b: byte) -> (n: int) {
|
print_byte :: #force_no_inline proc "contextless" (b: byte) -> (n: int) {
|
||||||
n, _ = stderr_write([]byte{b})
|
n, _ = stderr_write([]byte{b})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
print_encoded_rune :: proc "contextless" (r: rune) {
|
print_encoded_rune :: #force_no_inline proc "contextless" (r: rune) {
|
||||||
print_byte('\'')
|
print_byte('\'')
|
||||||
|
|
||||||
switch r {
|
switch r {
|
||||||
@@ -170,7 +170,7 @@ print_encoded_rune :: proc "contextless" (r: rune) {
|
|||||||
print_byte('\'')
|
print_byte('\'')
|
||||||
}
|
}
|
||||||
|
|
||||||
print_rune :: proc "contextless" (r: rune) -> int #no_bounds_check {
|
print_rune :: #force_no_inline proc "contextless" (r: rune) -> int #no_bounds_check {
|
||||||
RUNE_SELF :: 0x80
|
RUNE_SELF :: 0x80
|
||||||
|
|
||||||
if r < RUNE_SELF {
|
if r < RUNE_SELF {
|
||||||
@@ -183,7 +183,7 @@ print_rune :: proc "contextless" (r: rune) -> int #no_bounds_check {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
print_u64 :: proc "contextless" (x: u64) #no_bounds_check {
|
print_u64 :: #force_no_inline proc "contextless" (x: u64) #no_bounds_check {
|
||||||
a: [129]byte
|
a: [129]byte
|
||||||
i := len(a)
|
i := len(a)
|
||||||
b := u64(10)
|
b := u64(10)
|
||||||
@@ -198,7 +198,7 @@ print_u64 :: proc "contextless" (x: u64) #no_bounds_check {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
print_i64 :: proc "contextless" (x: i64) #no_bounds_check {
|
print_i64 :: #force_no_inline proc "contextless" (x: i64) #no_bounds_check {
|
||||||
b :: i64(10)
|
b :: i64(10)
|
||||||
|
|
||||||
u := x
|
u := x
|
||||||
@@ -223,25 +223,29 @@ print_uint :: proc "contextless" (x: uint) { print_u64(u64(x)) }
|
|||||||
print_uintptr :: proc "contextless" (x: uintptr) { print_u64(u64(x)) }
|
print_uintptr :: proc "contextless" (x: uintptr) { print_u64(u64(x)) }
|
||||||
print_int :: proc "contextless" (x: int) { print_i64(i64(x)) }
|
print_int :: proc "contextless" (x: int) { print_i64(i64(x)) }
|
||||||
|
|
||||||
print_caller_location :: proc "contextless" (loc: Source_Code_Location) {
|
print_caller_location :: #force_no_inline proc "contextless" (loc: Source_Code_Location) {
|
||||||
print_string(loc.file_path)
|
print_string(loc.file_path)
|
||||||
when ODIN_ERROR_POS_STYLE == .Default {
|
when ODIN_ERROR_POS_STYLE == .Default {
|
||||||
print_byte('(')
|
print_byte('(')
|
||||||
print_u64(u64(loc.line))
|
print_u64(u64(loc.line))
|
||||||
print_byte(':')
|
if loc.column != 0 {
|
||||||
print_u64(u64(loc.column))
|
print_byte(':')
|
||||||
|
print_u64(u64(loc.column))
|
||||||
|
}
|
||||||
print_byte(')')
|
print_byte(')')
|
||||||
} else when ODIN_ERROR_POS_STYLE == .Unix {
|
} else when ODIN_ERROR_POS_STYLE == .Unix {
|
||||||
print_byte(':')
|
print_byte(':')
|
||||||
print_u64(u64(loc.line))
|
print_u64(u64(loc.line))
|
||||||
print_byte(':')
|
if loc.column != 0 {
|
||||||
print_u64(u64(loc.column))
|
print_byte(':')
|
||||||
|
print_u64(u64(loc.column))
|
||||||
|
}
|
||||||
print_byte(':')
|
print_byte(':')
|
||||||
} else {
|
} else {
|
||||||
#panic("unhandled ODIN_ERROR_POS_STYLE")
|
#panic("unhandled ODIN_ERROR_POS_STYLE")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
print_typeid :: proc "contextless" (id: typeid) {
|
print_typeid :: #force_no_inline proc "contextless" (id: typeid) {
|
||||||
when ODIN_NO_RTTI {
|
when ODIN_NO_RTTI {
|
||||||
if id == nil {
|
if id == nil {
|
||||||
print_string("nil")
|
print_string("nil")
|
||||||
@@ -257,7 +261,9 @@ print_typeid :: proc "contextless" (id: typeid) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
print_type :: proc "contextless" (ti: ^Type_Info) {
|
|
||||||
|
@(optimization_mode="size")
|
||||||
|
print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||||
if ti == nil {
|
if ti == nil {
|
||||||
print_string("nil")
|
print_string("nil")
|
||||||
return
|
return
|
||||||
|
|||||||
+14
-12
@@ -2,7 +2,6 @@
|
|||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
: ${CPPFLAGS=}
|
: ${CPPFLAGS=}
|
||||||
: ${CXX=clang++}
|
|
||||||
: ${CXXFLAGS=}
|
: ${CXXFLAGS=}
|
||||||
: ${LDFLAGS=}
|
: ${LDFLAGS=}
|
||||||
: ${LLVM_CONFIG=}
|
: ${LLVM_CONFIG=}
|
||||||
@@ -26,17 +25,19 @@ error() {
|
|||||||
|
|
||||||
if [ -z "$LLVM_CONFIG" ]; then
|
if [ -z "$LLVM_CONFIG" ]; then
|
||||||
# darwin, linux, openbsd
|
# darwin, linux, openbsd
|
||||||
if [ -n "$(command -v llvm-config-17)" ]; then LLVM_CONFIG="llvm-config-17"
|
if [ -n "$(command -v llvm-config-18)" ]; then LLVM_CONFIG="llvm-config-18"
|
||||||
|
elif [ -n "$(command -v llvm-config-17)" ]; then LLVM_CONFIG="llvm-config-17"
|
||||||
elif [ -n "$(command -v llvm-config-14)" ]; then LLVM_CONFIG="llvm-config-14"
|
elif [ -n "$(command -v llvm-config-14)" ]; then LLVM_CONFIG="llvm-config-14"
|
||||||
elif [ -n "$(command -v llvm-config-13)" ]; then LLVM_CONFIG="llvm-config-13"
|
elif [ -n "$(command -v llvm-config-13)" ]; then LLVM_CONFIG="llvm-config-13"
|
||||||
elif [ -n "$(command -v llvm-config-12)" ]; then LLVM_CONFIG="llvm-config-12"
|
elif [ -n "$(command -v llvm-config-12)" ]; then LLVM_CONFIG="llvm-config-12"
|
||||||
elif [ -n "$(command -v llvm-config-11)" ]; then LLVM_CONFIG="llvm-config-11"
|
elif [ -n "$(command -v llvm-config-11)" ]; then LLVM_CONFIG="llvm-config-11"
|
||||||
# freebsd
|
# freebsd
|
||||||
elif [ -n "$(command -v llvm-config17)" ]; then LLVM_CONFIG="llvm-config-17"
|
elif [ -n "$(command -v llvm-config18)" ]; then LLVM_CONFIG="llvm-config18"
|
||||||
elif [ -n "$(command -v llvm-config14)" ]; then LLVM_CONFIG="llvm-config-14"
|
elif [ -n "$(command -v llvm-config17)" ]; then LLVM_CONFIG="llvm-config17"
|
||||||
elif [ -n "$(command -v llvm-config13)" ]; then LLVM_CONFIG="llvm-config-13"
|
elif [ -n "$(command -v llvm-config14)" ]; then LLVM_CONFIG="llvm-config14"
|
||||||
elif [ -n "$(command -v llvm-config12)" ]; then LLVM_CONFIG="llvm-config-12"
|
elif [ -n "$(command -v llvm-config13)" ]; then LLVM_CONFIG="llvm-config13"
|
||||||
elif [ -n "$(command -v llvm-config11)" ]; then LLVM_CONFIG="llvm-config-11"
|
elif [ -n "$(command -v llvm-config12)" ]; then LLVM_CONFIG="llvm-config12"
|
||||||
|
elif [ -n "$(command -v llvm-config11)" ]; then LLVM_CONFIG="llvm-config11"
|
||||||
# fallback
|
# fallback
|
||||||
elif [ -n "$(command -v llvm-config)" ]; then LLVM_CONFIG="llvm-config"
|
elif [ -n "$(command -v llvm-config)" ]; then LLVM_CONFIG="llvm-config"
|
||||||
else
|
else
|
||||||
@@ -44,21 +45,22 @@ if [ -z "$LLVM_CONFIG" ]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
: ${CXX=$($LLVM_CONFIG --bindir)/clang++}
|
||||||
|
|
||||||
LLVM_VERSION="$($LLVM_CONFIG --version)"
|
LLVM_VERSION="$($LLVM_CONFIG --version)"
|
||||||
LLVM_VERSION_MAJOR="$(echo $LLVM_VERSION | awk -F. '{print $1}')"
|
LLVM_VERSION_MAJOR="$(echo $LLVM_VERSION | awk -F. '{print $1}')"
|
||||||
LLVM_VERSION_MINOR="$(echo $LLVM_VERSION | awk -F. '{print $2}')"
|
LLVM_VERSION_MINOR="$(echo $LLVM_VERSION | awk -F. '{print $2}')"
|
||||||
LLVM_VERSION_PATCH="$(echo $LLVM_VERSION | awk -F. '{print $3}')"
|
LLVM_VERSION_PATCH="$(echo $LLVM_VERSION | awk -F. '{print $3}')"
|
||||||
|
|
||||||
if [ $LLVM_VERSION_MAJOR -lt 11 ] ||
|
if [ $LLVM_VERSION_MAJOR -lt 11 ] || ([ $LLVM_VERSION_MAJOR -gt 14 ] && [ $LLVM_VERSION_MAJOR -lt 17 ]) || [ $LLVM_VERSION_MAJOR -gt 18 ]; then
|
||||||
([ $LLVM_VERSION_MAJOR -gt 14 ] && [ $LLVM_VERSION_MAJOR -lt 17 ]); then
|
error "Invalid LLVM version $LLVM_VERSION: must be 11, 12, 13, 14, 17 or 18"
|
||||||
error "Invalid LLVM version $LLVM_VERSION: must be 11, 12, 13, 14 or 17"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
case "$OS_NAME" in
|
case "$OS_NAME" in
|
||||||
Darwin)
|
Darwin)
|
||||||
if [ "$OS_ARCH" = "arm64" ]; then
|
if [ "$OS_ARCH" = "arm64" ]; then
|
||||||
if [ $LLVM_VERSION_MAJOR -lt 13 ] || [ $LLVM_VERSION_MAJOR -gt 17 ]; then
|
if [ $LLVM_VERSION_MAJOR -lt 13 ]; then
|
||||||
error "Darwin Arm64 requires LLVM 13, 14 or 17"
|
error "Invalid LLVM version $LLVM_VERSION: Darwin Arm64 requires LLVM 13, 14, 17 or 18"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ _reader_read_new_chunk :: proc(b: ^Reader) -> io.Error {
|
|||||||
for i := b.max_consecutive_empty_reads; i > 0; i -= 1 {
|
for i := b.max_consecutive_empty_reads; i > 0; i -= 1 {
|
||||||
n, err := io.read(b.rd, b.buf[b.w:])
|
n, err := io.read(b.rd, b.buf[b.w:])
|
||||||
if n < 0 {
|
if n < 0 {
|
||||||
return .Negative_Read
|
return err if err != nil else .Negative_Read
|
||||||
}
|
}
|
||||||
b.w += n
|
b.w += n
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -189,7 +189,7 @@ reader_read :: proc(b: ^Reader, p: []byte) -> (n: int, err: io.Error) {
|
|||||||
if len(p) >= len(b.buf) {
|
if len(p) >= len(b.buf) {
|
||||||
n, b.err = io.read(b.rd, p)
|
n, b.err = io.read(b.rd, p)
|
||||||
if n < 0 {
|
if n < 0 {
|
||||||
return 0, .Negative_Read
|
return 0, b.err if b.err != nil else .Negative_Read
|
||||||
}
|
}
|
||||||
|
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
@@ -202,7 +202,7 @@ reader_read :: proc(b: ^Reader, p: []byte) -> (n: int, err: io.Error) {
|
|||||||
b.r, b.w = 0, 0
|
b.r, b.w = 0, 0
|
||||||
n, b.err = io.read(b.rd, b.buf)
|
n, b.err = io.read(b.rd, b.buf)
|
||||||
if n < 0 {
|
if n < 0 {
|
||||||
return 0, .Negative_Read
|
return 0, b.err if b.err != nil else .Negative_Read
|
||||||
}
|
}
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return 0, _reader_consume_err(b)
|
return 0, _reader_consume_err(b)
|
||||||
@@ -290,7 +290,7 @@ reader_write_to :: proc(b: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
|
|||||||
write_buf :: proc(b: ^Reader, w: io.Writer) -> (i64, io.Error) {
|
write_buf :: proc(b: ^Reader, w: io.Writer) -> (i64, io.Error) {
|
||||||
n, err := io.write(w, b.buf[b.r:b.w])
|
n, err := io.write(w, b.buf[b.r:b.w])
|
||||||
if n < 0 {
|
if n < 0 {
|
||||||
return 0, .Negative_Write
|
return 0, err if err != nil else .Negative_Write
|
||||||
}
|
}
|
||||||
b.r += n
|
b.r += n
|
||||||
return i64(n), err
|
return i64(n), err
|
||||||
|
|||||||
@@ -95,6 +95,10 @@ writer_write :: proc(b: ^Writer, p: []byte) -> (n: int, err: io.Error) {
|
|||||||
m: int
|
m: int
|
||||||
if writer_buffered(b) == 0 {
|
if writer_buffered(b) == 0 {
|
||||||
m, b.err = io.write(b.wr, p)
|
m, b.err = io.write(b.wr, p)
|
||||||
|
if m < 0 && b.err == nil {
|
||||||
|
b.err = .Negative_Write
|
||||||
|
break
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
m = copy(b.buf[b.n:], p)
|
m = copy(b.buf[b.n:], p)
|
||||||
b.n += m
|
b.n += m
|
||||||
|
|||||||
@@ -359,7 +359,7 @@ buffer_read_from :: proc(b: ^Buffer, r: io.Reader) -> (n: i64, err: io.Error) #n
|
|||||||
resize(&b.buf, i)
|
resize(&b.buf, i)
|
||||||
m, e := io.read(r, b.buf[i:cap(b.buf)])
|
m, e := io.read(r, b.buf[i:cap(b.buf)])
|
||||||
if m < 0 {
|
if m < 0 {
|
||||||
err = .Negative_Read
|
err = e if e != nil else .Negative_Read
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,34 +1,31 @@
|
|||||||
/*
|
/*
|
||||||
package demo
|
Example:
|
||||||
|
package demo
|
||||||
|
|
||||||
import tokenizer "core:c/frontend/tokenizer"
|
import tokenizer "core:c/frontend/tokenizer"
|
||||||
import preprocessor "core:c/frontend/preprocessor"
|
import preprocessor "core:c/frontend/preprocessor"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
t := &tokenizer.Tokenizer{};
|
t := &tokenizer.Tokenizer{};
|
||||||
tokenizer.init_defaults(t);
|
tokenizer.init_defaults(t);
|
||||||
|
|
||||||
cpp := &preprocessor.Preprocessor{};
|
cpp := &preprocessor.Preprocessor{};
|
||||||
cpp.warn, cpp.err = t.warn, t.err;
|
cpp.warn, cpp.err = t.warn, t.err;
|
||||||
preprocessor.init_lookup_tables(cpp);
|
preprocessor.init_lookup_tables(cpp);
|
||||||
preprocessor.init_default_macros(cpp);
|
preprocessor.init_default_macros(cpp);
|
||||||
cpp.include_paths = {"my/path/to/include"};
|
cpp.include_paths = {"my/path/to/include"};
|
||||||
|
|
||||||
tok := tokenizer.tokenize_file(t, "the/source/file.c", 1);
|
tok := tokenizer.tokenize_file(t, "the/source/file.c", 1);
|
||||||
|
|
||||||
tok = preprocessor.preprocess(cpp, tok);
|
tok = preprocessor.preprocess(cpp, tok);
|
||||||
if tok != nil {
|
if tok != nil {
|
||||||
for t := tok; t.kind != .EOF; t = t.next {
|
for t := tok; t.kind != .EOF; t = t.next {
|
||||||
fmt.println(t.lit);
|
fmt.println(t.lit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.println("[Done]");
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.println("[Done]");
|
|
||||||
}
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
package c_frontend_tokenizer
|
package c_frontend_tokenizer
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//+build ignore
|
//+build ignore
|
||||||
package gzip
|
package compress_gzip
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package gzip
|
package compress_gzip
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// package shoco is an implementation of the shoco short string compressor
|
// package shoco is an implementation of the shoco short string compressor
|
||||||
package shoco
|
package compress_shoco
|
||||||
|
|
||||||
DEFAULT_MODEL :: Shoco_Model {
|
DEFAULT_MODEL :: Shoco_Model {
|
||||||
min_char = 39,
|
min_char = 39,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// package shoco is an implementation of the shoco short string compressor
|
// package shoco is an implementation of the shoco short string compressor
|
||||||
package shoco
|
package compress_shoco
|
||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
import "core:compress"
|
import "core:compress"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//+build ignore
|
//+build ignore
|
||||||
package zlib
|
package compress_zlib
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//+vet !using-param
|
//+vet !using-param
|
||||||
package zlib
|
package compress_zlib
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package dynamic_bit_array
|
package container_dynamic_bit_array
|
||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|||||||
@@ -1,53 +1,52 @@
|
|||||||
package dynamic_bit_array
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The Bit Array can be used in several ways:
|
The Bit Array can be used in several ways:
|
||||||
|
|
||||||
-- By default you don't need to instantiate a Bit Array:
|
- By default you don't need to instantiate a Bit Array:
|
||||||
|
|
||||||
package test
|
package test
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:container/bit_array"
|
import "core:container/bit_array"
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
using bit_array
|
using bit_array
|
||||||
|
|
||||||
bits: Bit_Array
|
bits: Bit_Array
|
||||||
|
|
||||||
// returns `true`
|
// returns `true`
|
||||||
fmt.println(set(&bits, 42))
|
fmt.println(set(&bits, 42))
|
||||||
|
|
||||||
// returns `false`, `false`, because this Bit Array wasn't created to allow negative indices.
|
// returns `false`, `false`, because this Bit Array wasn't created to allow negative indices.
|
||||||
was_set, was_retrieved := get(&bits, -1)
|
was_set, was_retrieved := get(&bits, -1)
|
||||||
fmt.println(was_set, was_retrieved)
|
fmt.println(was_set, was_retrieved)
|
||||||
destroy(&bits)
|
destroy(&bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
- A Bit Array can optionally allow for negative indices, if the minimum value was given during creation:
|
||||||
|
|
||||||
|
package test
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:container/bit_array"
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
Foo :: enum int {
|
||||||
|
Negative_Test = -42,
|
||||||
|
Bar = 420,
|
||||||
|
Leaves = 69105,
|
||||||
}
|
}
|
||||||
|
|
||||||
-- A Bit Array can optionally allow for negative indices, if the mininum value was given during creation:
|
using bit_array
|
||||||
|
|
||||||
package test
|
bits := create(int(max(Foo)), int(min(Foo)))
|
||||||
|
defer destroy(bits)
|
||||||
|
|
||||||
import "core:fmt"
|
fmt.printf("Set(Bar): %v\n", set(bits, Foo.Bar))
|
||||||
import "core:container/bit_array"
|
fmt.printf("Get(Bar): %v, %v\n", get(bits, Foo.Bar))
|
||||||
|
fmt.printf("Set(Negative_Test): %v\n", set(bits, Foo.Negative_Test))
|
||||||
main :: proc() {
|
fmt.printf("Get(Leaves): %v, %v\n", get(bits, Foo.Leaves))
|
||||||
Foo :: enum int {
|
fmt.printf("Get(Negative_Test): %v, %v\n", get(bits, Foo.Negative_Test))
|
||||||
Negative_Test = -42,
|
fmt.printf("Freed.\n")
|
||||||
Bar = 420,
|
}
|
||||||
Leaves = 69105,
|
|
||||||
}
|
|
||||||
|
|
||||||
using bit_array
|
|
||||||
|
|
||||||
bits := create(int(max(Foo)), int(min(Foo)))
|
|
||||||
defer destroy(bits)
|
|
||||||
|
|
||||||
fmt.printf("Set(Bar): %v\n", set(bits, Foo.Bar))
|
|
||||||
fmt.printf("Get(Bar): %v, %v\n", get(bits, Foo.Bar))
|
|
||||||
fmt.printf("Set(Negative_Test): %v\n", set(bits, Foo.Negative_Test))
|
|
||||||
fmt.printf("Get(Leaves): %v, %v\n", get(bits, Foo.Leaves))
|
|
||||||
fmt.printf("Get(Negative_Test): %v, %v\n", get(bits, Foo.Negative_Test))
|
|
||||||
fmt.printf("Freed.\n")
|
|
||||||
}
|
|
||||||
*/
|
*/
|
||||||
|
package container_dynamic_bit_array
|
||||||
|
|||||||
@@ -49,15 +49,12 @@ compare_byte_ptrs_constant_time :: proc "contextless" (a, b: ^byte, n: int) -> i
|
|||||||
// the system entropy source. This routine will block if the system entropy
|
// the system entropy source. This routine will block if the system entropy
|
||||||
// source is not ready yet. All system entropy source failures are treated
|
// source is not ready yet. All system entropy source failures are treated
|
||||||
// as catastrophic, resulting in a panic.
|
// as catastrophic, resulting in a panic.
|
||||||
|
//
|
||||||
|
// Support for the system entropy source can be checked with the
|
||||||
|
// `HAS_RAND_BYTES` boolean constant.
|
||||||
rand_bytes :: proc (dst: []byte) {
|
rand_bytes :: proc (dst: []byte) {
|
||||||
// zero-fill the buffer first
|
// zero-fill the buffer first
|
||||||
mem.zero_explicit(raw_data(dst), len(dst))
|
mem.zero_explicit(raw_data(dst), len(dst))
|
||||||
|
|
||||||
_rand_bytes(dst)
|
_rand_bytes(dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
// has_rand_bytes returns true iff the target has support for accessing the
|
|
||||||
// system entropty source.
|
|
||||||
has_rand_bytes :: proc () -> bool {
|
|
||||||
return _has_rand_bytes()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,14 +3,13 @@ package crypto
|
|||||||
|
|
||||||
foreign import libc "system:c"
|
foreign import libc "system:c"
|
||||||
|
|
||||||
|
HAS_RAND_BYTES :: true
|
||||||
|
|
||||||
foreign libc {
|
foreign libc {
|
||||||
arc4random_buf :: proc(buf: [^]byte, nbytes: uint) ---
|
arc4random_buf :: proc(buf: [^]byte, nbytes: uint) ---
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
_rand_bytes :: proc(dst: []byte) {
|
_rand_bytes :: proc(dst: []byte) {
|
||||||
arc4random_buf(raw_data(dst), len(dst))
|
arc4random_buf(raw_data(dst), len(dst))
|
||||||
}
|
}
|
||||||
|
|
||||||
_has_rand_bytes :: proc () -> bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:sys/darwin"
|
|
||||||
|
|
||||||
|
import CF "core:sys/darwin/CoreFoundation"
|
||||||
|
import Sec "core:sys/darwin/Security"
|
||||||
|
|
||||||
|
HAS_RAND_BYTES :: true
|
||||||
|
|
||||||
|
@(private)
|
||||||
_rand_bytes :: proc(dst: []byte) {
|
_rand_bytes :: proc(dst: []byte) {
|
||||||
res := darwin.SecRandomCopyBytes(count=len(dst), bytes=raw_data(dst))
|
err := Sec.RandomCopyBytes(count=len(dst), bytes=raw_data(dst))
|
||||||
if res != .Success {
|
if err != .Success {
|
||||||
msg := darwin.CFStringCopyToOdinString(darwin.SecCopyErrorMessageString(res))
|
msg := CF.StringCopyToOdinString(Sec.CopyErrorMessageString(err))
|
||||||
panic(fmt.tprintf("crypto/rand_bytes: SecRandomCopyBytes returned non-zero result: %v %s", res, msg))
|
panic(fmt.tprintf("crypto/rand_bytes: SecRandomCopyBytes returned non-zero result: %v %s", err, msg))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_has_rand_bytes :: proc () -> bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,10 +6,9 @@
|
|||||||
//+build !js
|
//+build !js
|
||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
|
HAS_RAND_BYTES :: false
|
||||||
|
|
||||||
|
@(private)
|
||||||
_rand_bytes :: proc(dst: []byte) {
|
_rand_bytes :: proc(dst: []byte) {
|
||||||
unimplemented("crypto: rand_bytes not supported on this OS")
|
unimplemented("crypto: rand_bytes not supported on this OS")
|
||||||
}
|
}
|
||||||
|
|
||||||
_has_rand_bytes :: proc () -> bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,8 +6,12 @@ foreign odin_env {
|
|||||||
env_rand_bytes :: proc "contextless" (buf: []byte) ---
|
env_rand_bytes :: proc "contextless" (buf: []byte) ---
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HAS_RAND_BYTES :: true
|
||||||
|
|
||||||
|
@(private)
|
||||||
_MAX_PER_CALL_BYTES :: 65536 // 64kiB
|
_MAX_PER_CALL_BYTES :: 65536 // 64kiB
|
||||||
|
|
||||||
|
@(private)
|
||||||
_rand_bytes :: proc(dst: []byte) {
|
_rand_bytes :: proc(dst: []byte) {
|
||||||
dst := dst
|
dst := dst
|
||||||
|
|
||||||
@@ -18,7 +22,3 @@ _rand_bytes :: proc(dst: []byte) {
|
|||||||
dst = dst[to_read:]
|
dst = dst[to_read:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_has_rand_bytes :: proc () -> bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,8 +4,12 @@ import "core:fmt"
|
|||||||
|
|
||||||
import "core:sys/linux"
|
import "core:sys/linux"
|
||||||
|
|
||||||
|
HAS_RAND_BYTES :: true
|
||||||
|
|
||||||
|
@(private)
|
||||||
_MAX_PER_CALL_BYTES :: 33554431 // 2^25 - 1
|
_MAX_PER_CALL_BYTES :: 33554431 // 2^25 - 1
|
||||||
|
|
||||||
|
@(private)
|
||||||
_rand_bytes :: proc (dst: []byte) {
|
_rand_bytes :: proc (dst: []byte) {
|
||||||
dst := dst
|
dst := dst
|
||||||
l := len(dst)
|
l := len(dst)
|
||||||
@@ -34,7 +38,3 @@ _rand_bytes :: proc (dst: []byte) {
|
|||||||
dst = dst[n_read:]
|
dst = dst[n_read:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_has_rand_bytes :: proc () -> bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import win32 "core:sys/windows"
|
|||||||
import "core:os"
|
import "core:os"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
|
||||||
|
HAS_RAND_BYTES :: true
|
||||||
|
|
||||||
|
@(private)
|
||||||
_rand_bytes :: proc(dst: []byte) {
|
_rand_bytes :: proc(dst: []byte) {
|
||||||
ret := (os.Errno)(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG))
|
ret := (os.Errno)(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG))
|
||||||
if ret != os.ERROR_NONE {
|
if ret != os.ERROR_NONE {
|
||||||
@@ -21,7 +24,3 @@ _rand_bytes :: proc(dst: []byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_has_rand_bytes :: proc () -> bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
A debug stack trace library. Only works when debug symbols are enabled `-debug`.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
import "base:runtime"
|
||||||
|
import "core:debug/trace"
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
global_trace_ctx: trace.Context
|
||||||
|
|
||||||
|
debug_trace_assertion_failure_proc :: proc(prefix, message: string, loc := #caller_location) -> ! {
|
||||||
|
runtime.print_caller_location(loc)
|
||||||
|
runtime.print_string(" ")
|
||||||
|
runtime.print_string(prefix)
|
||||||
|
if len(message) > 0 {
|
||||||
|
runtime.print_string(": ")
|
||||||
|
runtime.print_string(message)
|
||||||
|
}
|
||||||
|
runtime.print_byte('\n')
|
||||||
|
|
||||||
|
ctx := &trace_ctx
|
||||||
|
if !trace.in_resolve(ctx) {
|
||||||
|
buf: [64]trace.Frame
|
||||||
|
runtime.print_string("Debug Trace:\n")
|
||||||
|
frames := trace.frames(ctx, 1, buf[:])
|
||||||
|
for f, i in frames {
|
||||||
|
fl := trace.resolve(ctx, f, context.temp_allocator)
|
||||||
|
if fl.loc.file_path == "" && fl.loc.line == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
runtime.print_caller_location(fl.loc)
|
||||||
|
runtime.print_string(" - frame ")
|
||||||
|
runtime.print_int(i)
|
||||||
|
runtime.print_byte('\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
runtime.trap()
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
trace.init(&global_trace_ctx)
|
||||||
|
defer trace.destroy(&global_trace_ctx)
|
||||||
|
|
||||||
|
context.assertion_failure_proc = debug_trace_assertion_failure_proc
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
package debug_trace
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package debug_trace
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
import "base:runtime"
|
||||||
|
|
||||||
|
Frame :: distinct uintptr
|
||||||
|
|
||||||
|
Frame_Location :: struct {
|
||||||
|
using loc: runtime.Source_Code_Location,
|
||||||
|
allocator: runtime.Allocator,
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_frame_location :: proc(fl: Frame_Location) -> runtime.Allocator_Error {
|
||||||
|
allocator := fl.allocator
|
||||||
|
delete(fl.loc.procedure, allocator) or_return
|
||||||
|
delete(fl.loc.file_path, allocator) or_return
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
Context :: struct {
|
||||||
|
in_resolve: bool, // atomic
|
||||||
|
impl: _Context,
|
||||||
|
}
|
||||||
|
|
||||||
|
init :: proc(ctx: ^Context) -> bool {
|
||||||
|
return _init(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy :: proc(ctx: ^Context) -> bool {
|
||||||
|
return _destroy(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
|
frames :: proc(ctx: ^Context, skip: uint, frames_buffer: []Frame) -> []Frame {
|
||||||
|
return _frames(ctx, skip, frames_buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
|
resolve :: proc(ctx: ^Context, frame: Frame, allocator: runtime.Allocator) -> (result: Frame_Location) {
|
||||||
|
return _resolve(ctx, frame, allocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
|
in_resolve :: proc "contextless" (ctx: ^Context) -> bool {
|
||||||
|
return intrinsics.atomic_load(&ctx.in_resolve)
|
||||||
|
}
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
//+private file
|
||||||
|
//+build linux, darwin
|
||||||
|
package debug_trace
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
import "base:runtime"
|
||||||
|
import "core:strings"
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:c"
|
||||||
|
|
||||||
|
// NOTE: Relies on C++23 which adds <stacktrace> and becomes ABI and that can be used
|
||||||
|
foreign import stdcpplibbacktrace "system:stdc++_libbacktrace"
|
||||||
|
|
||||||
|
foreign import libdl "system:dl"
|
||||||
|
|
||||||
|
backtrace_state :: struct {}
|
||||||
|
backtrace_error_callback :: proc "c" (data: rawptr, msg: cstring, errnum: c.int)
|
||||||
|
backtrace_simple_callback :: proc "c" (data: rawptr, pc: uintptr) -> c.int
|
||||||
|
backtrace_full_callback :: proc "c" (data: rawptr, pc: uintptr, filename: cstring, lineno: c.int, function: cstring) -> c.int
|
||||||
|
backtrace_syminfo_callback :: proc "c" (data: rawptr, pc: uintptr, symname: cstring, symval: uintptr, symsize: uintptr)
|
||||||
|
|
||||||
|
@(default_calling_convention="c", link_prefix="__glibcxx_")
|
||||||
|
foreign stdcpplibbacktrace {
|
||||||
|
backtrace_create_state :: proc(
|
||||||
|
filename: cstring,
|
||||||
|
threaded: c.int,
|
||||||
|
error_callback: backtrace_error_callback,
|
||||||
|
data: rawptr,
|
||||||
|
) -> ^backtrace_state ---
|
||||||
|
backtrace_simple :: proc(
|
||||||
|
state: ^backtrace_state,
|
||||||
|
skip: c.int,
|
||||||
|
callback: backtrace_simple_callback,
|
||||||
|
error_callback: backtrace_error_callback,
|
||||||
|
data: rawptr,
|
||||||
|
) -> c.int ---
|
||||||
|
backtrace_pcinfo :: proc(
|
||||||
|
state: ^backtrace_state,
|
||||||
|
pc: uintptr,
|
||||||
|
callback: backtrace_full_callback,
|
||||||
|
error_callback: backtrace_error_callback,
|
||||||
|
data: rawptr,
|
||||||
|
) -> c.int ---
|
||||||
|
backtrace_syminfo :: proc(
|
||||||
|
state: ^backtrace_state,
|
||||||
|
addr: uintptr,
|
||||||
|
callback: backtrace_syminfo_callback,
|
||||||
|
error_callback: backtrace_error_callback,
|
||||||
|
data: rawptr,
|
||||||
|
) -> c.int ---
|
||||||
|
|
||||||
|
// NOTE(bill): this is technically an internal procedure, but it is exposed
|
||||||
|
backtrace_free :: proc(
|
||||||
|
state: ^backtrace_state,
|
||||||
|
p: rawptr,
|
||||||
|
size: c.size_t, // unused
|
||||||
|
error_callback: backtrace_error_callback, // unused
|
||||||
|
data: rawptr, // unused
|
||||||
|
) ---
|
||||||
|
}
|
||||||
|
|
||||||
|
Dl_info :: struct {
|
||||||
|
dli_fname: cstring,
|
||||||
|
dli_fbase: rawptr,
|
||||||
|
dli_sname: cstring,
|
||||||
|
dli_saddr: rawptr,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(default_calling_convention="c")
|
||||||
|
foreign libdl {
|
||||||
|
dladdr :: proc(addr: rawptr, info: ^Dl_info) -> c.int ---
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="package")
|
||||||
|
_Context :: struct {
|
||||||
|
state: ^backtrace_state,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="package")
|
||||||
|
_init :: proc(ctx: ^Context) -> (ok: bool) {
|
||||||
|
defer if !ok do destroy(ctx)
|
||||||
|
|
||||||
|
ctx.impl.state = backtrace_create_state("odin-debug-trace", 1, nil, ctx)
|
||||||
|
return ctx.impl.state != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="package")
|
||||||
|
_destroy :: proc(ctx: ^Context) -> bool {
|
||||||
|
if ctx != nil {
|
||||||
|
backtrace_free(ctx.impl.state, nil, 0, nil, nil)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="package")
|
||||||
|
_frames :: proc "contextless" (ctx: ^Context, skip: uint, frames_buffer: []Frame) -> (frames: []Frame) {
|
||||||
|
Backtrace_Context :: struct {
|
||||||
|
ctx: ^Context,
|
||||||
|
frames: []Frame,
|
||||||
|
frame_count: int,
|
||||||
|
}
|
||||||
|
|
||||||
|
btc := &Backtrace_Context{
|
||||||
|
ctx = ctx,
|
||||||
|
frames = frames_buffer,
|
||||||
|
}
|
||||||
|
backtrace_simple(
|
||||||
|
ctx.impl.state,
|
||||||
|
c.int(skip + 2),
|
||||||
|
proc "c" (user: rawptr, address: uintptr) -> c.int {
|
||||||
|
btc := (^Backtrace_Context)(user)
|
||||||
|
address := Frame(address)
|
||||||
|
if address == 0 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if btc.frame_count == len(btc.frames) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
btc.frames[btc.frame_count] = address
|
||||||
|
btc.frame_count += 1
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
btc,
|
||||||
|
)
|
||||||
|
|
||||||
|
if btc.frame_count > 0 {
|
||||||
|
frames = btc.frames[:btc.frame_count]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="package")
|
||||||
|
_resolve :: proc(ctx: ^Context, frame: Frame, allocator: runtime.Allocator) -> Frame_Location {
|
||||||
|
intrinsics.atomic_store(&ctx.in_resolve, true)
|
||||||
|
defer intrinsics.atomic_store(&ctx.in_resolve, false)
|
||||||
|
|
||||||
|
Backtrace_Context :: struct {
|
||||||
|
rt_ctx: runtime.Context,
|
||||||
|
allocator: runtime.Allocator,
|
||||||
|
frame: Frame_Location,
|
||||||
|
}
|
||||||
|
|
||||||
|
btc := &Backtrace_Context{
|
||||||
|
rt_ctx = context,
|
||||||
|
allocator = allocator,
|
||||||
|
}
|
||||||
|
done := backtrace_pcinfo(
|
||||||
|
ctx.impl.state,
|
||||||
|
uintptr(frame),
|
||||||
|
proc "c" (data: rawptr, address: uintptr, file: cstring, line: c.int, symbol: cstring) -> c.int {
|
||||||
|
btc := (^Backtrace_Context)(data)
|
||||||
|
context = btc.rt_ctx
|
||||||
|
|
||||||
|
frame := &btc.frame
|
||||||
|
|
||||||
|
if file != nil {
|
||||||
|
frame.file_path = strings.clone_from_cstring(file, btc.allocator)
|
||||||
|
} else if info: Dl_info; dladdr(rawptr(address), &info) != 0 && info.dli_fname != "" {
|
||||||
|
frame.file_path = strings.clone_from_cstring(info.dli_fname, btc.allocator)
|
||||||
|
}
|
||||||
|
if symbol != nil {
|
||||||
|
frame.procedure = strings.clone_from_cstring(symbol, btc.allocator)
|
||||||
|
} else if info: Dl_info; dladdr(rawptr(address), &info) != 0 && info.dli_sname != "" {
|
||||||
|
frame.procedure = strings.clone_from_cstring(info.dli_sname, btc.allocator)
|
||||||
|
} else {
|
||||||
|
frame.procedure = fmt.aprintf("(procedure: 0x%x)", allocator=btc.allocator)
|
||||||
|
}
|
||||||
|
frame.line = i32(line)
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
btc,
|
||||||
|
)
|
||||||
|
if done != 0 {
|
||||||
|
return btc.frame
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(bill): pcinfo cannot resolve, but it might be possible to get the procedure name at least
|
||||||
|
backtrace_syminfo(
|
||||||
|
ctx.impl.state,
|
||||||
|
uintptr(frame),
|
||||||
|
proc "c" (data: rawptr, address: uintptr, symbol: cstring, _ignore0, _ignore1: uintptr) {
|
||||||
|
if symbol != nil {
|
||||||
|
btc := (^Backtrace_Context)(data)
|
||||||
|
context = btc.rt_ctx
|
||||||
|
btc.frame.procedure = strings.clone_from_cstring(symbol, btc.allocator)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
btc,
|
||||||
|
)
|
||||||
|
|
||||||
|
return btc.frame
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
//+build !windows !linux !darwin
|
||||||
|
package debug_trace
|
||||||
|
|
||||||
|
_Context :: struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
_init :: proc(ctx: ^Context) -> (ok: bool) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
_destroy :: proc(ctx: ^Context) -> bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
_frames :: proc(ctx: ^Context, skip: uint, allocator: runtime.Allocator) -> []Frame {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_resolve :: proc(ctx: ^Context, frame: Frame, allocator: runtime.Allocator) -> (result: runtime.Source_Code_Location) {
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
//+private
|
||||||
|
//+build windows
|
||||||
|
package debug_trace
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
import "base:runtime"
|
||||||
|
|
||||||
|
import win32 "core:sys/windows"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
_Context :: struct {
|
||||||
|
hProcess: win32.HANDLE,
|
||||||
|
lock: win32.SRWLOCK,
|
||||||
|
}
|
||||||
|
|
||||||
|
_init :: proc "contextless" (ctx: ^Context) -> (ok: bool) {
|
||||||
|
defer if !ok { _destroy(ctx) }
|
||||||
|
ctx.impl.hProcess = win32.GetCurrentProcess()
|
||||||
|
win32.SymInitialize(ctx.impl.hProcess, nil, true) or_return
|
||||||
|
win32.SymSetOptions(win32.SYMOPT_LOAD_LINES)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
_destroy :: proc "contextless" (ctx: ^Context) -> bool {
|
||||||
|
if ctx != nil {
|
||||||
|
win32.SymCleanup(ctx.impl.hProcess)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
_frames :: proc "contextless" (ctx: ^Context, skip: uint, frames_buffer: []Frame) -> []Frame {
|
||||||
|
frame_count := win32.RtlCaptureStackBackTrace(u32(skip) + 2, u32(len(frames_buffer)), ([^]rawptr)(&frames_buffer[0]), nil)
|
||||||
|
for i in 0..<frame_count {
|
||||||
|
// NOTE: Return address is one after the call instruction so subtract a byte to
|
||||||
|
// end up back inside the call instruction which is needed for SymFromAddr.
|
||||||
|
frames_buffer[i] -= 1
|
||||||
|
}
|
||||||
|
return frames_buffer[:frame_count]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_resolve :: proc(ctx: ^Context, frame: Frame, allocator: runtime.Allocator) -> (fl: Frame_Location) {
|
||||||
|
intrinsics.atomic_store(&ctx.in_resolve, true)
|
||||||
|
defer intrinsics.atomic_store(&ctx.in_resolve, false)
|
||||||
|
|
||||||
|
// NOTE(bill): Dbghelp is not thread-safe
|
||||||
|
win32.AcquireSRWLockExclusive(&ctx.impl.lock)
|
||||||
|
defer win32.ReleaseSRWLockExclusive(&ctx.impl.lock)
|
||||||
|
|
||||||
|
data: [size_of(win32.SYMBOL_INFOW) + size_of([256]win32.WCHAR)]byte
|
||||||
|
symbol := (^win32.SYMBOL_INFOW)(&data[0])
|
||||||
|
symbol.SizeOfStruct = size_of(symbol)
|
||||||
|
symbol.MaxNameLen = 255
|
||||||
|
if win32.SymFromAddrW(ctx.impl.hProcess, win32.DWORD64(frame), &{}, symbol) {
|
||||||
|
fl.procedure, _ = win32.wstring_to_utf8(&symbol.Name[0], -1, allocator)
|
||||||
|
} else {
|
||||||
|
fl.procedure = fmt.aprintf("(procedure: 0x%x)", frame, allocator=allocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
line: win32.IMAGEHLP_LINE64
|
||||||
|
line.SizeOfStruct = size_of(line)
|
||||||
|
if win32.SymGetLineFromAddrW64(ctx.impl.hProcess, win32.DWORD64(frame), &{}, &line) {
|
||||||
|
fl.file_path, _ = win32.wstring_to_utf8(line.FileName, -1, allocator)
|
||||||
|
fl.line = i32(line.LineNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
//+build ignore
|
|
||||||
/*
|
/*
|
||||||
Package core:dynlib implements loading of shared libraries/DLLs and their symbols.
|
Package `core:dynlib` implements loading of shared libraries/DLLs and their symbols.
|
||||||
|
|
||||||
The behaviour of dynamically loaded libraries is specific to the target platform of the program.
|
The behaviour of dynamically loaded libraries is specific to the target platform of the program.
|
||||||
For in depth detail on the underlying behaviour please refer to your target platform's documentation.
|
For in depth detail on the underlying behaviour please refer to your target platform's documentation.
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ initialize_symbols :: proc(
|
|||||||
prefixed_symbol_buf: [2048]u8 = ---
|
prefixed_symbol_buf: [2048]u8 = ---
|
||||||
|
|
||||||
count = 0
|
count = 0
|
||||||
for field, i in reflect.struct_fields_zipped(T) {
|
for field in reflect.struct_fields_zipped(T) {
|
||||||
// Calculate address of struct member
|
// Calculate address of struct member
|
||||||
field_ptr := rawptr(uintptr(symbol_table) + field.offset)
|
field_ptr := rawptr(uintptr(symbol_table) + field.offset)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package base32
|
package encoding_base32
|
||||||
|
|
||||||
// @note(zh): Encoding utility for Base32
|
// @note(zh): Encoding utility for Base32
|
||||||
// A secondary param can be used to supply a custom alphabet to
|
// A secondary param can be used to supply a custom alphabet to
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
package base64
|
package encoding_base64
|
||||||
|
|
||||||
|
import "core:io"
|
||||||
|
import "core:mem"
|
||||||
|
import "core:strings"
|
||||||
|
|
||||||
// @note(zh): Encoding utility for Base64
|
// @note(zh): Encoding utility for Base64
|
||||||
// A secondary param can be used to supply a custom alphabet to
|
// A secondary param can be used to supply a custom alphabet to
|
||||||
@@ -39,59 +43,132 @@ DEC_TABLE := [128]int {
|
|||||||
49, 50, 51, -1, -1, -1, -1, -1,
|
49, 50, 51, -1, -1, -1, -1, -1,
|
||||||
}
|
}
|
||||||
|
|
||||||
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string #no_bounds_check {
|
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> (encoded: string, err: mem.Allocator_Error) #optional_allocator_error {
|
||||||
length := len(data)
|
out_length := encoded_len(data)
|
||||||
if length == 0 {
|
if out_length == 0 {
|
||||||
return ""
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
out_length := ((4 * length / 3) + 3) &~ 3
|
out := strings.builder_make(0, out_length, allocator) or_return
|
||||||
out := make([]byte, out_length, allocator)
|
ioerr := encode_into(strings.to_stream(&out), data, ENC_TBL)
|
||||||
|
|
||||||
c0, c1, c2, block: int
|
assert(ioerr == nil, "string builder should not IO error")
|
||||||
|
assert(strings.builder_cap(out) == out_length, "buffer resized, `encoded_len` was wrong")
|
||||||
|
|
||||||
for i, d := 0, 0; i < length; i, d = i + 3, d + 4 {
|
return strings.to_string(out), nil
|
||||||
c0, c1, c2 = int(data[i]), -1, -1
|
|
||||||
|
|
||||||
if i + 1 < length { c1 = int(data[i + 1]) }
|
|
||||||
if i + 2 < length { c2 = int(data[i + 2]) }
|
|
||||||
|
|
||||||
block = (c0 << 16) | (max(c1, 0) << 8) | max(c2, 0)
|
|
||||||
|
|
||||||
out[d] = ENC_TBL[block >> 18 & 63]
|
|
||||||
out[d + 1] = ENC_TBL[block >> 12 & 63]
|
|
||||||
out[d + 2] = c1 == -1 ? PADDING : ENC_TBL[block >> 6 & 63]
|
|
||||||
out[d + 3] = c2 == -1 ? PADDING : ENC_TBL[block & 63]
|
|
||||||
}
|
|
||||||
return string(out)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check {
|
encode_into :: proc(w: io.Writer, data: []byte, ENC_TBL := ENC_TABLE) -> io.Error {
|
||||||
length := len(data)
|
length := len(data)
|
||||||
if length == 0 {
|
if length == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
pad_count := data[length - 1] == PADDING ? (data[length - 2] == PADDING ? 2 : 1) : 0
|
c0, c1, c2, block: int
|
||||||
out_length := ((length * 6) >> 3) - pad_count
|
out: [4]byte
|
||||||
out := make([]byte, out_length, allocator)
|
for i := 0; i < length; i += 3 {
|
||||||
|
#no_bounds_check {
|
||||||
|
c0, c1, c2 = int(data[i]), -1, -1
|
||||||
|
|
||||||
c0, c1, c2, c3: int
|
if i + 1 < length { c1 = int(data[i + 1]) }
|
||||||
b0, b1, b2: int
|
if i + 2 < length { c2 = int(data[i + 2]) }
|
||||||
|
|
||||||
for i, j := 0, 0; i < length; i, j = i + 4, j + 3 {
|
block = (c0 << 16) | (max(c1, 0) << 8) | max(c2, 0)
|
||||||
c0 = DEC_TBL[data[i]]
|
|
||||||
c1 = DEC_TBL[data[i + 1]]
|
|
||||||
c2 = DEC_TBL[data[i + 2]]
|
|
||||||
c3 = DEC_TBL[data[i + 3]]
|
|
||||||
|
|
||||||
b0 = (c0 << 2) | (c1 >> 4)
|
out[0] = ENC_TBL[block >> 18 & 63]
|
||||||
b1 = (c1 << 4) | (c2 >> 2)
|
out[1] = ENC_TBL[block >> 12 & 63]
|
||||||
b2 = (c2 << 6) | c3
|
out[2] = c1 == -1 ? PADDING : ENC_TBL[block >> 6 & 63]
|
||||||
|
out[3] = c2 == -1 ? PADDING : ENC_TBL[block & 63]
|
||||||
out[j] = byte(b0)
|
}
|
||||||
out[j + 1] = byte(b1)
|
io.write_full(w, out[:]) or_return
|
||||||
out[j + 2] = byte(b2)
|
}
|
||||||
}
|
return nil
|
||||||
return out
|
}
|
||||||
|
|
||||||
|
encoded_len :: proc(data: []byte) -> int {
|
||||||
|
length := len(data)
|
||||||
|
if length == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((4 * length / 3) + 3) &~ 3
|
||||||
|
}
|
||||||
|
|
||||||
|
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> (decoded: []byte, err: mem.Allocator_Error) #optional_allocator_error {
|
||||||
|
out_length := decoded_len(data)
|
||||||
|
|
||||||
|
out := strings.builder_make(0, out_length, allocator) or_return
|
||||||
|
ioerr := decode_into(strings.to_stream(&out), data, DEC_TBL)
|
||||||
|
|
||||||
|
assert(ioerr == nil, "string builder should not IO error")
|
||||||
|
assert(strings.builder_cap(out) == out_length, "buffer resized, `decoded_len` was wrong")
|
||||||
|
|
||||||
|
return out.buf[:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
decode_into :: proc(w: io.Writer, data: string, DEC_TBL := DEC_TABLE) -> io.Error {
|
||||||
|
length := decoded_len(data)
|
||||||
|
if length == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c0, c1, c2, c3: int
|
||||||
|
b0, b1, b2: int
|
||||||
|
buf: [3]byte
|
||||||
|
i, j: int
|
||||||
|
for ; j + 3 <= length; i, j = i + 4, j + 3 {
|
||||||
|
#no_bounds_check {
|
||||||
|
c0 = DEC_TBL[data[i]]
|
||||||
|
c1 = DEC_TBL[data[i + 1]]
|
||||||
|
c2 = DEC_TBL[data[i + 2]]
|
||||||
|
c3 = DEC_TBL[data[i + 3]]
|
||||||
|
|
||||||
|
b0 = (c0 << 2) | (c1 >> 4)
|
||||||
|
b1 = (c1 << 4) | (c2 >> 2)
|
||||||
|
b2 = (c2 << 6) | c3
|
||||||
|
|
||||||
|
buf[0] = byte(b0)
|
||||||
|
buf[1] = byte(b1)
|
||||||
|
buf[2] = byte(b2)
|
||||||
|
}
|
||||||
|
|
||||||
|
io.write_full(w, buf[:]) or_return
|
||||||
|
}
|
||||||
|
|
||||||
|
rest := length - j
|
||||||
|
if rest > 0 {
|
||||||
|
#no_bounds_check {
|
||||||
|
c0 = DEC_TBL[data[i]]
|
||||||
|
c1 = DEC_TBL[data[i + 1]]
|
||||||
|
c2 = DEC_TBL[data[i + 2]]
|
||||||
|
|
||||||
|
b0 = (c0 << 2) | (c1 >> 4)
|
||||||
|
b1 = (c1 << 4) | (c2 >> 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch rest {
|
||||||
|
case 1: io.write_byte(w, byte(b0)) or_return
|
||||||
|
case 2: io.write_full(w, {byte(b0), byte(b1)}) or_return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
decoded_len :: proc(data: string) -> int {
|
||||||
|
length := len(data)
|
||||||
|
if length == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
padding: int
|
||||||
|
if data[length - 1] == PADDING {
|
||||||
|
if length > 1 && data[length - 2] == PADDING {
|
||||||
|
padding = 2
|
||||||
|
} else {
|
||||||
|
padding = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((length * 6) >> 3) - padding
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,673 @@
|
|||||||
|
package encoding_cbor
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
|
||||||
|
import "core:encoding/json"
|
||||||
|
import "core:io"
|
||||||
|
import "core:mem"
|
||||||
|
import "core:strconv"
|
||||||
|
import "core:strings"
|
||||||
|
|
||||||
|
// If we are decoding a stream of either a map or list, the initial capacity will be this value.
|
||||||
|
INITIAL_STREAMED_CONTAINER_CAPACITY :: 8
|
||||||
|
|
||||||
|
// If we are decoding a stream of either text or bytes, the initial capacity will be this value.
|
||||||
|
INITIAL_STREAMED_BYTES_CAPACITY :: 16
|
||||||
|
|
||||||
|
// The default maximum amount of bytes to allocate on a buffer/container at once to prevent
|
||||||
|
// malicious input from causing massive allocations.
|
||||||
|
DEFAULT_MAX_PRE_ALLOC :: mem.Kilobyte
|
||||||
|
|
||||||
|
// Known/common headers are defined, undefined headers can still be valid.
|
||||||
|
// Higher 3 bits is for the major type and lower 5 bits for the additional information.
|
||||||
|
Header :: enum u8 {
|
||||||
|
U8 = (u8(Major.Unsigned) << 5) | u8(Add.One_Byte),
|
||||||
|
U16 = (u8(Major.Unsigned) << 5) | u8(Add.Two_Bytes),
|
||||||
|
U32 = (u8(Major.Unsigned) << 5) | u8(Add.Four_Bytes),
|
||||||
|
U64 = (u8(Major.Unsigned) << 5) | u8(Add.Eight_Bytes),
|
||||||
|
|
||||||
|
Neg_U8 = (u8(Major.Negative) << 5) | u8(Add.One_Byte),
|
||||||
|
Neg_U16 = (u8(Major.Negative) << 5) | u8(Add.Two_Bytes),
|
||||||
|
Neg_U32 = (u8(Major.Negative) << 5) | u8(Add.Four_Bytes),
|
||||||
|
Neg_U64 = (u8(Major.Negative) << 5) | u8(Add.Eight_Bytes),
|
||||||
|
|
||||||
|
False = (u8(Major.Other) << 5) | u8(Add.False),
|
||||||
|
True = (u8(Major.Other) << 5) | u8(Add.True),
|
||||||
|
|
||||||
|
Nil = (u8(Major.Other) << 5) | u8(Add.Nil),
|
||||||
|
Undefined = (u8(Major.Other) << 5) | u8(Add.Undefined),
|
||||||
|
|
||||||
|
Simple = (u8(Major.Other) << 5) | u8(Add.One_Byte),
|
||||||
|
|
||||||
|
F16 = (u8(Major.Other) << 5) | u8(Add.Two_Bytes),
|
||||||
|
F32 = (u8(Major.Other) << 5) | u8(Add.Four_Bytes),
|
||||||
|
F64 = (u8(Major.Other) << 5) | u8(Add.Eight_Bytes),
|
||||||
|
|
||||||
|
Break = (u8(Major.Other) << 5) | u8(Add.Break),
|
||||||
|
}
|
||||||
|
|
||||||
|
// The higher 3 bits of the header which denotes what type of value it is.
|
||||||
|
Major :: enum u8 {
|
||||||
|
Unsigned,
|
||||||
|
Negative,
|
||||||
|
Bytes,
|
||||||
|
Text,
|
||||||
|
Array,
|
||||||
|
Map,
|
||||||
|
Tag,
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
// The lower 3 bits of the header which denotes additional information for the type of value.
|
||||||
|
Add :: enum u8 {
|
||||||
|
False = 20,
|
||||||
|
True = 21,
|
||||||
|
Nil = 22,
|
||||||
|
Undefined = 23,
|
||||||
|
|
||||||
|
One_Byte = 24,
|
||||||
|
Two_Bytes = 25,
|
||||||
|
Four_Bytes = 26,
|
||||||
|
Eight_Bytes = 27,
|
||||||
|
|
||||||
|
Length_Unknown = 31,
|
||||||
|
Break = Length_Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
Value :: union {
|
||||||
|
u8,
|
||||||
|
u16,
|
||||||
|
u32,
|
||||||
|
u64,
|
||||||
|
|
||||||
|
Negative_U8,
|
||||||
|
Negative_U16,
|
||||||
|
Negative_U32,
|
||||||
|
Negative_U64,
|
||||||
|
|
||||||
|
// Pointers so the size of the Value union stays small.
|
||||||
|
^Bytes,
|
||||||
|
^Text,
|
||||||
|
^Array,
|
||||||
|
^Map,
|
||||||
|
^Tag,
|
||||||
|
|
||||||
|
Simple,
|
||||||
|
f16,
|
||||||
|
f32,
|
||||||
|
f64,
|
||||||
|
bool,
|
||||||
|
Undefined,
|
||||||
|
Nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
Bytes :: []byte
|
||||||
|
Text :: string
|
||||||
|
|
||||||
|
Array :: []Value
|
||||||
|
|
||||||
|
Map :: []Map_Entry
|
||||||
|
Map_Entry :: struct {
|
||||||
|
key: Value, // Can be any unsigned, negative, float, Simple, bool, Text.
|
||||||
|
value: Value,
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag :: struct {
|
||||||
|
number: Tag_Number,
|
||||||
|
value: Value, // Value based on the number.
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag_Number :: u64
|
||||||
|
|
||||||
|
Nil :: distinct rawptr
|
||||||
|
Undefined :: distinct rawptr
|
||||||
|
|
||||||
|
// A distinct atom-like number, range from `0..=19` and `32..=max(u8)`.
|
||||||
|
Simple :: distinct u8
|
||||||
|
Atom :: Simple
|
||||||
|
|
||||||
|
Unmarshal_Error :: union #shared_nil {
|
||||||
|
io.Error,
|
||||||
|
mem.Allocator_Error,
|
||||||
|
Decode_Data_Error,
|
||||||
|
Unmarshal_Data_Error,
|
||||||
|
Maybe(Unsupported_Type_Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
Marshal_Error :: union #shared_nil {
|
||||||
|
io.Error,
|
||||||
|
mem.Allocator_Error,
|
||||||
|
Encode_Data_Error,
|
||||||
|
Marshal_Data_Error,
|
||||||
|
Maybe(Unsupported_Type_Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
Decode_Error :: union #shared_nil {
|
||||||
|
io.Error,
|
||||||
|
mem.Allocator_Error,
|
||||||
|
Decode_Data_Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
Encode_Error :: union #shared_nil {
|
||||||
|
io.Error,
|
||||||
|
mem.Allocator_Error,
|
||||||
|
Encode_Data_Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
Decode_Data_Error :: enum {
|
||||||
|
None,
|
||||||
|
Bad_Major, // An invalid major type was encountered.
|
||||||
|
Bad_Argument, // A general unexpected value (most likely invalid additional info in header).
|
||||||
|
Bad_Tag_Value, // When the type of value for the given tag is not valid.
|
||||||
|
Nested_Indefinite_Length, // When an streamed/indefinite length container nests another, this is not allowed.
|
||||||
|
Nested_Tag, // When a tag's value is another tag, this is not allowed.
|
||||||
|
Length_Too_Big, // When the length of a container (map, array, bytes, string) is more than `max(int)`.
|
||||||
|
Disallowed_Streaming, // When the `.Disallow_Streaming` flag is set and a streaming header is encountered.
|
||||||
|
Break, // When the `break` header was found without any stream to break off.
|
||||||
|
}
|
||||||
|
|
||||||
|
Encode_Data_Error :: enum {
|
||||||
|
None,
|
||||||
|
Invalid_Simple, // When a simple is being encoded that is out of the range `0..=19` and `32..=max(u8)`.
|
||||||
|
Int_Too_Big, // When an int is being encoded that is larger than `max(u64)` or smaller than `min(u64)`.
|
||||||
|
Bad_Tag_Value, // When the type of value is not supported by the tag implementation.
|
||||||
|
}
|
||||||
|
|
||||||
|
Unmarshal_Data_Error :: enum {
|
||||||
|
None,
|
||||||
|
Invalid_Parameter, // When the given `any` can not be unmarshalled into.
|
||||||
|
Non_Pointer_Parameter, // When the given `any` is not a pointer.
|
||||||
|
}
|
||||||
|
|
||||||
|
Marshal_Data_Error :: enum {
|
||||||
|
None,
|
||||||
|
Invalid_CBOR_Tag, // When the struct tag `cbor_tag:""` is not a registered name or number.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error that is returned when a type couldn't be marshalled into or out of, as much information
|
||||||
|
// as possible/available is added.
|
||||||
|
Unsupported_Type_Error :: struct {
|
||||||
|
id: typeid,
|
||||||
|
hdr: Header,
|
||||||
|
add: Add,
|
||||||
|
}
|
||||||
|
|
||||||
|
_unsupported :: proc(v: any, hdr: Header, add: Add = nil) -> Maybe(Unsupported_Type_Error) {
|
||||||
|
return Unsupported_Type_Error{
|
||||||
|
id = v.id,
|
||||||
|
hdr = hdr,
|
||||||
|
add = add,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actual value is `-1 - x` (be careful of overflows).
|
||||||
|
|
||||||
|
Negative_U8 :: distinct u8
|
||||||
|
Negative_U16 :: distinct u16
|
||||||
|
Negative_U32 :: distinct u32
|
||||||
|
Negative_U64 :: distinct u64
|
||||||
|
|
||||||
|
// Turns the CBOR negative unsigned int type into a signed integer type.
|
||||||
|
negative_to_int :: proc {
|
||||||
|
negative_u8_to_int,
|
||||||
|
negative_u16_to_int,
|
||||||
|
negative_u32_to_int,
|
||||||
|
negative_u64_to_int,
|
||||||
|
}
|
||||||
|
|
||||||
|
negative_u8_to_int :: #force_inline proc(u: Negative_U8) -> i16 {
|
||||||
|
return -1 - i16(u)
|
||||||
|
}
|
||||||
|
|
||||||
|
negative_u16_to_int :: #force_inline proc(u: Negative_U16) -> i32 {
|
||||||
|
return -1 - i32(u)
|
||||||
|
}
|
||||||
|
|
||||||
|
negative_u32_to_int :: #force_inline proc(u: Negative_U32) -> i64 {
|
||||||
|
return -1 - i64(u)
|
||||||
|
}
|
||||||
|
|
||||||
|
negative_u64_to_int :: #force_inline proc(u: Negative_U64) -> i128 {
|
||||||
|
return -1 - i128(u)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility for converting between the different errors when they are subsets of the other.
|
||||||
|
err_conv :: proc {
|
||||||
|
encode_to_marshal_err,
|
||||||
|
encode_to_marshal_err_p2,
|
||||||
|
decode_to_unmarshal_err,
|
||||||
|
decode_to_unmarshal_err_p,
|
||||||
|
decode_to_unmarshal_err_p2,
|
||||||
|
}
|
||||||
|
|
||||||
|
encode_to_marshal_err :: #force_inline proc(err: Encode_Error) -> Marshal_Error {
|
||||||
|
switch e in err {
|
||||||
|
case nil: return nil
|
||||||
|
case io.Error: return e
|
||||||
|
case mem.Allocator_Error: return e
|
||||||
|
case Encode_Data_Error: return e
|
||||||
|
case: return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
encode_to_marshal_err_p2 :: #force_inline proc(v: $T, v2: $T2, err: Encode_Error) -> (T, T2, Marshal_Error) {
|
||||||
|
return v, v2, err_conv(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
decode_to_unmarshal_err :: #force_inline proc(err: Decode_Error) -> Unmarshal_Error {
|
||||||
|
switch e in err {
|
||||||
|
case nil: return nil
|
||||||
|
case io.Error: return e
|
||||||
|
case mem.Allocator_Error: return e
|
||||||
|
case Decode_Data_Error: return e
|
||||||
|
case: return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decode_to_unmarshal_err_p :: #force_inline proc(v: $T, err: Decode_Error) -> (T, Unmarshal_Error) {
|
||||||
|
return v, err_conv(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
decode_to_unmarshal_err_p2 :: #force_inline proc(v: $T, v2: $T2, err: Decode_Error) -> (T, T2, Unmarshal_Error) {
|
||||||
|
return v, v2, err_conv(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively frees all memory allocated when decoding the passed value.
|
||||||
|
destroy :: proc(val: Value, allocator := context.allocator) {
|
||||||
|
context.allocator = allocator
|
||||||
|
#partial switch v in val {
|
||||||
|
case ^Map:
|
||||||
|
if v == nil { return }
|
||||||
|
for entry in v {
|
||||||
|
destroy(entry.key)
|
||||||
|
destroy(entry.value)
|
||||||
|
}
|
||||||
|
delete(v^)
|
||||||
|
free(v)
|
||||||
|
case ^Array:
|
||||||
|
if v == nil { return }
|
||||||
|
for entry in v {
|
||||||
|
destroy(entry)
|
||||||
|
}
|
||||||
|
delete(v^)
|
||||||
|
free(v)
|
||||||
|
case ^Text:
|
||||||
|
if v == nil { return }
|
||||||
|
delete(v^)
|
||||||
|
free(v)
|
||||||
|
case ^Bytes:
|
||||||
|
if v == nil { return }
|
||||||
|
delete(v^)
|
||||||
|
free(v)
|
||||||
|
case ^Tag:
|
||||||
|
if v == nil { return }
|
||||||
|
destroy(v.value)
|
||||||
|
free(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
to_diagnostic_format either writes or returns a human-readable representation of the value,
|
||||||
|
optionally formatted, defined as the diagnostic format in [[RFC 8949 Section 8;https://www.rfc-editor.org/rfc/rfc8949.html#name-diagnostic-notation]].
|
||||||
|
|
||||||
|
Incidentally, if the CBOR does not contain any of the additional types defined on top of JSON
|
||||||
|
this will also be valid JSON.
|
||||||
|
*/
|
||||||
|
to_diagnostic_format :: proc {
|
||||||
|
to_diagnostic_format_string,
|
||||||
|
to_diagnostic_format_writer,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turns the given CBOR value into a human-readable string.
|
||||||
|
// See docs on the proc group `diagnose` for more info.
|
||||||
|
to_diagnostic_format_string :: proc(val: Value, padding := 0, allocator := context.allocator) -> (string, mem.Allocator_Error) #optional_allocator_error {
|
||||||
|
b := strings.builder_make(allocator)
|
||||||
|
w := strings.to_stream(&b)
|
||||||
|
err := to_diagnostic_format_writer(w, val, padding)
|
||||||
|
if err == .EOF {
|
||||||
|
// The string builder stream only returns .EOF, and only if it can't write (out of memory).
|
||||||
|
return "", .Out_Of_Memory
|
||||||
|
}
|
||||||
|
assert(err == nil)
|
||||||
|
|
||||||
|
return strings.to_string(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes the given CBOR value into the writer as human-readable text.
|
||||||
|
// See docs on the proc group `diagnose` for more info.
|
||||||
|
to_diagnostic_format_writer :: proc(w: io.Writer, val: Value, padding := 0) -> io.Error {
|
||||||
|
@(require_results)
|
||||||
|
indent :: proc(padding: int) -> int {
|
||||||
|
padding := padding
|
||||||
|
if padding != -1 {
|
||||||
|
padding += 1
|
||||||
|
}
|
||||||
|
return padding
|
||||||
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
|
dedent :: proc(padding: int) -> int {
|
||||||
|
padding := padding
|
||||||
|
if padding != -1 {
|
||||||
|
padding -= 1
|
||||||
|
}
|
||||||
|
return padding
|
||||||
|
}
|
||||||
|
|
||||||
|
comma :: proc(w: io.Writer, padding: int) -> io.Error {
|
||||||
|
_ = io.write_string(w, ", " if padding == -1 else ",") or_return
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
newline :: proc(w: io.Writer, padding: int) -> io.Error {
|
||||||
|
if padding != -1 {
|
||||||
|
io.write_string(w, "\n") or_return
|
||||||
|
for _ in 0..<padding {
|
||||||
|
io.write_string(w, "\t") or_return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
padding := padding
|
||||||
|
switch v in val {
|
||||||
|
case u8: io.write_uint(w, uint(v)) or_return
|
||||||
|
case u16: io.write_uint(w, uint(v)) or_return
|
||||||
|
case u32: io.write_uint(w, uint(v)) or_return
|
||||||
|
case u64: io.write_u64(w, v) or_return
|
||||||
|
case Negative_U8: io.write_int(w, int(negative_to_int(v))) or_return
|
||||||
|
case Negative_U16: io.write_int(w, int(negative_to_int(v))) or_return
|
||||||
|
case Negative_U32: io.write_int(w, int(negative_to_int(v))) or_return
|
||||||
|
case Negative_U64: io.write_i128(w, i128(negative_to_int(v))) or_return
|
||||||
|
|
||||||
|
// NOTE: not using io.write_float because it removes the sign,
|
||||||
|
// which we want for the diagnostic format.
|
||||||
|
case f16:
|
||||||
|
buf: [64]byte
|
||||||
|
str := strconv.append_float(buf[:], f64(v), 'f', 2*size_of(f16), 8*size_of(f16))
|
||||||
|
if str[0] == '+' && str != "+Inf" { str = str[1:] }
|
||||||
|
io.write_string(w, str) or_return
|
||||||
|
case f32:
|
||||||
|
buf: [128]byte
|
||||||
|
str := strconv.append_float(buf[:], f64(v), 'f', 2*size_of(f32), 8*size_of(f32))
|
||||||
|
if str[0] == '+' && str != "+Inf" { str = str[1:] }
|
||||||
|
io.write_string(w, str) or_return
|
||||||
|
case f64:
|
||||||
|
buf: [256]byte
|
||||||
|
str := strconv.append_float(buf[:], f64(v), 'f', 2*size_of(f64), 8*size_of(f64))
|
||||||
|
if str[0] == '+' && str != "+Inf" { str = str[1:] }
|
||||||
|
io.write_string(w, str) or_return
|
||||||
|
|
||||||
|
case bool: io.write_string(w, "true" if v else "false") or_return
|
||||||
|
case Nil: io.write_string(w, "nil") or_return
|
||||||
|
case Undefined: io.write_string(w, "undefined") or_return
|
||||||
|
case ^Bytes:
|
||||||
|
io.write_string(w, "h'") or_return
|
||||||
|
for b in v { io.write_int(w, int(b), 16) or_return }
|
||||||
|
io.write_string(w, "'") or_return
|
||||||
|
case ^Text:
|
||||||
|
io.write_string(w, `"`) or_return
|
||||||
|
io.write_string(w, v^) or_return
|
||||||
|
io.write_string(w, `"`) or_return
|
||||||
|
case ^Array:
|
||||||
|
if v == nil || len(v) == 0 {
|
||||||
|
io.write_string(w, "[]") or_return
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
io.write_string(w, "[") or_return
|
||||||
|
|
||||||
|
padding = indent(padding)
|
||||||
|
newline(w, padding) or_return
|
||||||
|
|
||||||
|
for entry, i in v {
|
||||||
|
to_diagnostic_format(w, entry, padding) or_return
|
||||||
|
if i != len(v)-1 {
|
||||||
|
comma(w, padding) or_return
|
||||||
|
newline(w, padding) or_return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
padding = dedent(padding)
|
||||||
|
newline(w, padding) or_return
|
||||||
|
|
||||||
|
io.write_string(w, "]") or_return
|
||||||
|
case ^Map:
|
||||||
|
if v == nil || len(v) == 0 {
|
||||||
|
io.write_string(w, "{}") or_return
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
io.write_string(w, "{") or_return
|
||||||
|
|
||||||
|
padding = indent(padding)
|
||||||
|
newline(w, padding) or_return
|
||||||
|
|
||||||
|
for entry, i in v {
|
||||||
|
to_diagnostic_format(w, entry.key, padding) or_return
|
||||||
|
io.write_string(w, ": ") or_return
|
||||||
|
to_diagnostic_format(w, entry.value, padding) or_return
|
||||||
|
if i != len(v)-1 {
|
||||||
|
comma(w, padding) or_return
|
||||||
|
newline(w, padding) or_return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
padding = dedent(padding)
|
||||||
|
newline(w, padding) or_return
|
||||||
|
|
||||||
|
io.write_string(w, "}") or_return
|
||||||
|
case ^Tag:
|
||||||
|
io.write_u64(w, v.number) or_return
|
||||||
|
io.write_string(w, "(") or_return
|
||||||
|
to_diagnostic_format(w, v.value, padding) or_return
|
||||||
|
io.write_string(w, ")") or_return
|
||||||
|
case Simple:
|
||||||
|
io.write_string(w, "simple(") or_return
|
||||||
|
io.write_uint(w, uint(v)) or_return
|
||||||
|
io.write_string(w, ")") or_return
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Converts from JSON to CBOR.
|
||||||
|
|
||||||
|
Everything is copied to the given allocator, the passed in JSON value can be deleted after.
|
||||||
|
*/
|
||||||
|
from_json :: proc(val: json.Value, allocator := context.allocator) -> (Value, mem.Allocator_Error) #optional_allocator_error {
|
||||||
|
internal :: proc(val: json.Value) -> (ret: Value, err: mem.Allocator_Error) {
|
||||||
|
switch v in val {
|
||||||
|
case json.Null: return Nil{}, nil
|
||||||
|
case json.Integer:
|
||||||
|
i, major := _int_to_uint(v)
|
||||||
|
#partial switch major {
|
||||||
|
case .Unsigned: return i, nil
|
||||||
|
case .Negative: return Negative_U64(i), nil
|
||||||
|
case: unreachable()
|
||||||
|
}
|
||||||
|
case json.Float: return v, nil
|
||||||
|
case json.Boolean: return v, nil
|
||||||
|
case json.String:
|
||||||
|
container := new(Text) or_return
|
||||||
|
|
||||||
|
// We need the string to have a nil byte at the end so we clone to cstring.
|
||||||
|
container^ = string(strings.clone_to_cstring(v) or_return)
|
||||||
|
return container, nil
|
||||||
|
case json.Array:
|
||||||
|
arr := new(Array) or_return
|
||||||
|
arr^ = make([]Value, len(v)) or_return
|
||||||
|
for _, i in arr {
|
||||||
|
arr[i] = internal(v[i]) or_return
|
||||||
|
}
|
||||||
|
return arr, nil
|
||||||
|
case json.Object:
|
||||||
|
m := new(Map) or_return
|
||||||
|
dm := make([dynamic]Map_Entry, 0, len(v)) or_return
|
||||||
|
for mkey, mval in v {
|
||||||
|
append(&dm, Map_Entry{from_json(mkey) or_return, from_json(mval) or_return})
|
||||||
|
}
|
||||||
|
m^ = dm[:]
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
context.allocator = allocator
|
||||||
|
return internal(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Converts from CBOR to JSON.
|
||||||
|
|
||||||
|
NOTE: overflow on integers or floats is not handled.
|
||||||
|
|
||||||
|
Everything is copied to the given allocator, the passed in CBOR value can be `destroy`'ed after.
|
||||||
|
|
||||||
|
If a CBOR map with non-string keys is encountered it is turned into an array of tuples.
|
||||||
|
*/
|
||||||
|
to_json :: proc(val: Value, allocator := context.allocator) -> (json.Value, mem.Allocator_Error) #optional_allocator_error {
|
||||||
|
internal :: proc(val: Value) -> (ret: json.Value, err: mem.Allocator_Error) {
|
||||||
|
switch v in val {
|
||||||
|
case Simple: return json.Integer(v), nil
|
||||||
|
|
||||||
|
case u8: return json.Integer(v), nil
|
||||||
|
case u16: return json.Integer(v), nil
|
||||||
|
case u32: return json.Integer(v), nil
|
||||||
|
case u64: return json.Integer(v), nil
|
||||||
|
|
||||||
|
case Negative_U8: return json.Integer(negative_to_int(v)), nil
|
||||||
|
case Negative_U16: return json.Integer(negative_to_int(v)), nil
|
||||||
|
case Negative_U32: return json.Integer(negative_to_int(v)), nil
|
||||||
|
case Negative_U64: return json.Integer(negative_to_int(v)), nil
|
||||||
|
|
||||||
|
case f16: return json.Float(v), nil
|
||||||
|
case f32: return json.Float(v), nil
|
||||||
|
case f64: return json.Float(v), nil
|
||||||
|
|
||||||
|
case bool: return json.Boolean(v), nil
|
||||||
|
|
||||||
|
case Undefined: return json.Null{}, nil
|
||||||
|
case Nil: return json.Null{}, nil
|
||||||
|
|
||||||
|
case ^Bytes: return json.String(strings.clone(string(v^)) or_return), nil
|
||||||
|
case ^Text: return json.String(strings.clone(v^) or_return), nil
|
||||||
|
|
||||||
|
case ^Map:
|
||||||
|
keys_all_strings :: proc(m: ^Map) -> bool {
|
||||||
|
for entry in m {
|
||||||
|
#partial switch kv in entry.key {
|
||||||
|
case ^Bytes:
|
||||||
|
case ^Text:
|
||||||
|
case: return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if keys_all_strings(v) {
|
||||||
|
obj := make(json.Object, len(v)) or_return
|
||||||
|
for entry in v {
|
||||||
|
k: string
|
||||||
|
#partial switch kv in entry.key {
|
||||||
|
case ^Bytes: k = string(kv^)
|
||||||
|
case ^Text: k = kv^
|
||||||
|
case: unreachable()
|
||||||
|
}
|
||||||
|
|
||||||
|
v := internal(entry.value) or_return
|
||||||
|
obj[k] = v
|
||||||
|
}
|
||||||
|
return obj, nil
|
||||||
|
} else {
|
||||||
|
// Resort to an array of tuples if keys aren't all strings.
|
||||||
|
arr := make(json.Array, 0, len(v)) or_return
|
||||||
|
for entry in v {
|
||||||
|
entry_arr := make(json.Array, 0, 2) or_return
|
||||||
|
append(&entry_arr, internal(entry.key) or_return) or_return
|
||||||
|
append(&entry_arr, internal(entry.value) or_return) or_return
|
||||||
|
append(&arr, entry_arr) or_return
|
||||||
|
}
|
||||||
|
return arr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
case ^Array:
|
||||||
|
arr := make(json.Array, 0, len(v)) or_return
|
||||||
|
for entry in v {
|
||||||
|
append(&arr, internal(entry) or_return) or_return
|
||||||
|
}
|
||||||
|
return arr, nil
|
||||||
|
|
||||||
|
case ^Tag:
|
||||||
|
obj := make(json.Object, 2) or_return
|
||||||
|
obj[strings.clone("number") or_return] = internal(v.number) or_return
|
||||||
|
obj[strings.clone("value") or_return] = internal(v.value) or_return
|
||||||
|
return obj, nil
|
||||||
|
|
||||||
|
case: return json.Null{}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.allocator = allocator
|
||||||
|
return internal(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
_int_to_uint :: proc {
|
||||||
|
_i8_to_uint,
|
||||||
|
_i16_to_uint,
|
||||||
|
_i32_to_uint,
|
||||||
|
_i64_to_uint,
|
||||||
|
_i128_to_uint,
|
||||||
|
}
|
||||||
|
|
||||||
|
_u128_to_u64 :: #force_inline proc(v: u128) -> (u64, Encode_Data_Error) {
|
||||||
|
if v > u128(max(u64)) {
|
||||||
|
return 0, .Int_Too_Big
|
||||||
|
}
|
||||||
|
|
||||||
|
return u64(v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_i8_to_uint :: #force_inline proc(v: i8) -> (u: u8, m: Major) {
|
||||||
|
if v < 0 {
|
||||||
|
return u8(abs(v)-1), .Negative
|
||||||
|
}
|
||||||
|
|
||||||
|
return u8(v), .Unsigned
|
||||||
|
}
|
||||||
|
|
||||||
|
_i16_to_uint :: #force_inline proc(v: i16) -> (u: u16, m: Major) {
|
||||||
|
if v < 0 {
|
||||||
|
return u16(abs(v)-1), .Negative
|
||||||
|
}
|
||||||
|
|
||||||
|
return u16(v), .Unsigned
|
||||||
|
}
|
||||||
|
|
||||||
|
_i32_to_uint :: #force_inline proc(v: i32) -> (u: u32, m: Major) {
|
||||||
|
if v < 0 {
|
||||||
|
return u32(abs(v)-1), .Negative
|
||||||
|
}
|
||||||
|
|
||||||
|
return u32(v), .Unsigned
|
||||||
|
}
|
||||||
|
|
||||||
|
_i64_to_uint :: #force_inline proc(v: i64) -> (u: u64, m: Major) {
|
||||||
|
if v < 0 {
|
||||||
|
return u64(abs(v)-1), .Negative
|
||||||
|
}
|
||||||
|
|
||||||
|
return u64(v), .Unsigned
|
||||||
|
}
|
||||||
|
|
||||||
|
_i128_to_uint :: proc(v: i128) -> (u: u64, m: Major, err: Encode_Data_Error) {
|
||||||
|
if v < 0 {
|
||||||
|
m = .Negative
|
||||||
|
u, err = _u128_to_u64(u128(abs(v) - 1))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
m = .Unsigned
|
||||||
|
u, err = _u128_to_u64(u128(v))
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,886 @@
|
|||||||
|
package encoding_cbor
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
import "base:runtime"
|
||||||
|
|
||||||
|
import "core:bytes"
|
||||||
|
import "core:encoding/endian"
|
||||||
|
import "core:io"
|
||||||
|
import "core:slice"
|
||||||
|
import "core:strings"
|
||||||
|
|
||||||
|
Encoder_Flag :: enum {
|
||||||
|
// CBOR defines a tag header that also acts as a file/binary header,
|
||||||
|
// this way decoders can check the first header of the binary and see if it is CBOR.
|
||||||
|
Self_Described_CBOR,
|
||||||
|
|
||||||
|
// Integers are stored in the smallest integer type it fits.
|
||||||
|
// This involves checking each int against the max of all its smaller types.
|
||||||
|
Deterministic_Int_Size,
|
||||||
|
|
||||||
|
// Floats are stored in the smallest size float type without losing precision.
|
||||||
|
// This involves casting each float down to its smaller types and checking if it changed.
|
||||||
|
Deterministic_Float_Size,
|
||||||
|
|
||||||
|
// Sort maps by their keys in bytewise lexicographic order of their deterministic encoding.
|
||||||
|
// NOTE: In order to do this, all keys of a map have to be pre-computed, sorted, and
|
||||||
|
// then written, this involves temporary allocations for the keys and a copy of the map itself.
|
||||||
|
Deterministic_Map_Sorting,
|
||||||
|
}
|
||||||
|
|
||||||
|
Encoder_Flags :: bit_set[Encoder_Flag]
|
||||||
|
|
||||||
|
// Flags for fully deterministic output (if you are not using streaming/indeterminate length).
|
||||||
|
ENCODE_FULLY_DETERMINISTIC :: Encoder_Flags{.Deterministic_Int_Size, .Deterministic_Float_Size, .Deterministic_Map_Sorting}
|
||||||
|
|
||||||
|
// Flags for the smallest encoding output.
|
||||||
|
ENCODE_SMALL :: Encoder_Flags{.Deterministic_Int_Size, .Deterministic_Float_Size}
|
||||||
|
|
||||||
|
Encoder :: struct {
|
||||||
|
flags: Encoder_Flags,
|
||||||
|
writer: io.Writer,
|
||||||
|
temp_allocator: runtime.Allocator,
|
||||||
|
}
|
||||||
|
|
||||||
|
Decoder_Flag :: enum {
|
||||||
|
// Rejects (with an error `.Disallowed_Streaming`) when a streaming CBOR header is encountered.
|
||||||
|
Disallow_Streaming,
|
||||||
|
|
||||||
|
// Pre-allocates buffers and containers with the size that was set in the CBOR header.
|
||||||
|
// This should only be enabled when you control both ends of the encoding, if you don't,
|
||||||
|
// attackers can craft input that causes massive (`max(u64)`) byte allocations for a few bytes of
|
||||||
|
// CBOR.
|
||||||
|
Trusted_Input,
|
||||||
|
|
||||||
|
// Makes the decoder shrink of excess capacity from allocated buffers/containers before returning.
|
||||||
|
Shrink_Excess,
|
||||||
|
}
|
||||||
|
|
||||||
|
Decoder_Flags :: bit_set[Decoder_Flag]
|
||||||
|
|
||||||
|
Decoder :: struct {
|
||||||
|
// The max amount of bytes allowed to pre-allocate when `.Trusted_Input` is not set on the
|
||||||
|
// flags.
|
||||||
|
max_pre_alloc: int,
|
||||||
|
|
||||||
|
flags: Decoder_Flags,
|
||||||
|
reader: io.Reader,
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Decodes both deterministic and non-deterministic CBOR into a `Value` variant.
|
||||||
|
|
||||||
|
`Text` and `Bytes` can safely be cast to cstrings because of an added 0 byte.
|
||||||
|
|
||||||
|
Allocations are done using the given allocator,
|
||||||
|
*no* allocations are done on the `context.temp_allocator`.
|
||||||
|
|
||||||
|
A value can be (fully and recursively) deallocated using the `destroy` proc in this package.
|
||||||
|
|
||||||
|
Disable streaming/indeterminate lengths with the `.Disallow_Streaming` flag.
|
||||||
|
|
||||||
|
Shrink excess bytes in buffers and containers with the `.Shrink_Excess` flag.
|
||||||
|
|
||||||
|
Mark the input as trusted input with the `.Trusted_Input` flag, this turns off the safety feature
|
||||||
|
of not pre-allocating more than `max_pre_alloc` bytes before reading into the bytes. You should only
|
||||||
|
do this when you own both sides of the encoding and are sure there can't be malicious bytes used as
|
||||||
|
an input.
|
||||||
|
*/
|
||||||
|
decode_from :: proc {
|
||||||
|
decode_from_string,
|
||||||
|
decode_from_reader,
|
||||||
|
decode_from_decoder,
|
||||||
|
}
|
||||||
|
decode :: decode_from
|
||||||
|
|
||||||
|
// Decodes the given string as CBOR.
|
||||||
|
// See docs on the proc group `decode` for more information.
|
||||||
|
decode_from_string :: proc(s: string, flags: Decoder_Flags = {}, allocator := context.allocator) -> (v: Value, err: Decode_Error) {
|
||||||
|
r: strings.Reader
|
||||||
|
strings.reader_init(&r, s)
|
||||||
|
return decode_from_reader(strings.reader_to_stream(&r), flags, allocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads a CBOR value from the given reader.
|
||||||
|
// See docs on the proc group `decode` for more information.
|
||||||
|
decode_from_reader :: proc(r: io.Reader, flags: Decoder_Flags = {}, allocator := context.allocator) -> (v: Value, err: Decode_Error) {
|
||||||
|
return decode_from_decoder(
|
||||||
|
Decoder{ DEFAULT_MAX_PRE_ALLOC, flags, r },
|
||||||
|
allocator=allocator,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads a CBOR value from the given decoder.
|
||||||
|
// See docs on the proc group `decode` for more information.
|
||||||
|
decode_from_decoder :: proc(d: Decoder, allocator := context.allocator) -> (v: Value, err: Decode_Error) {
|
||||||
|
context.allocator = allocator
|
||||||
|
|
||||||
|
d := d
|
||||||
|
|
||||||
|
if d.max_pre_alloc <= 0 {
|
||||||
|
d.max_pre_alloc = DEFAULT_MAX_PRE_ALLOC
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err = _decode_from_decoder(d)
|
||||||
|
// Normal EOF does not exist here, we try to read the exact amount that is said to be provided.
|
||||||
|
if err == .EOF { err = .Unexpected_EOF }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_decode_from_decoder :: proc(d: Decoder, hdr: Header = Header(0)) -> (v: Value, err: Decode_Error) {
|
||||||
|
hdr := hdr
|
||||||
|
r := d.reader
|
||||||
|
if hdr == Header(0) { hdr = _decode_header(r) or_return }
|
||||||
|
switch hdr {
|
||||||
|
case .U8: return _decode_u8 (r)
|
||||||
|
case .U16: return _decode_u16(r)
|
||||||
|
case .U32: return _decode_u32(r)
|
||||||
|
case .U64: return _decode_u64(r)
|
||||||
|
|
||||||
|
case .Neg_U8: return Negative_U8 (_decode_u8 (r) or_return), nil
|
||||||
|
case .Neg_U16: return Negative_U16(_decode_u16(r) or_return), nil
|
||||||
|
case .Neg_U32: return Negative_U32(_decode_u32(r) or_return), nil
|
||||||
|
case .Neg_U64: return Negative_U64(_decode_u64(r) or_return), nil
|
||||||
|
|
||||||
|
case .Simple: return _decode_simple(r)
|
||||||
|
|
||||||
|
case .F16: return _decode_f16(r)
|
||||||
|
case .F32: return _decode_f32(r)
|
||||||
|
case .F64: return _decode_f64(r)
|
||||||
|
|
||||||
|
case .True: return true, nil
|
||||||
|
case .False: return false, nil
|
||||||
|
|
||||||
|
case .Nil: return Nil{}, nil
|
||||||
|
case .Undefined: return Undefined{}, nil
|
||||||
|
|
||||||
|
case .Break: return nil, .Break
|
||||||
|
}
|
||||||
|
|
||||||
|
maj, add := _header_split(hdr)
|
||||||
|
switch maj {
|
||||||
|
case .Unsigned: return _decode_tiny_u8(add)
|
||||||
|
case .Negative: return Negative_U8(_decode_tiny_u8(add) or_return), nil
|
||||||
|
case .Bytes: return _decode_bytes_ptr(d, add)
|
||||||
|
case .Text: return _decode_text_ptr(d, add)
|
||||||
|
case .Array: return _decode_array_ptr(d, add)
|
||||||
|
case .Map: return _decode_map_ptr(d, add)
|
||||||
|
case .Tag: return _decode_tag_ptr(d, add)
|
||||||
|
case .Other: return _decode_tiny_simple(add)
|
||||||
|
case: return nil, .Bad_Major
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Encodes the CBOR value into a binary CBOR.
|
||||||
|
|
||||||
|
Flags can be used to control the output (mainly determinism, which coincidently affects size).
|
||||||
|
|
||||||
|
The default flags `ENCODE_SMALL` (`.Deterministic_Int_Size`, `.Deterministic_Float_Size`) will try
|
||||||
|
to put ints and floats into their smallest possible byte size without losing equality.
|
||||||
|
|
||||||
|
Adding the `.Self_Described_CBOR` flag will wrap the value in a tag that lets generic decoders know
|
||||||
|
the contents are CBOR from just reading the first byte.
|
||||||
|
|
||||||
|
Adding the `.Deterministic_Map_Sorting` flag will sort the encoded maps by the byte content of the
|
||||||
|
encoded key. This flag has a cost on performance and memory efficiency because all keys in a map
|
||||||
|
have to be precomputed, sorted and only then written to the output.
|
||||||
|
|
||||||
|
Empty flags will do nothing extra to the value.
|
||||||
|
|
||||||
|
The allocations for the `.Deterministic_Map_Sorting` flag are done using the given temp_allocator.
|
||||||
|
but are followed by the necessary `delete` and `free` calls if the allocator supports them.
|
||||||
|
This is helpful when the CBOR size is so big that you don't want to collect all the temporary
|
||||||
|
allocations until the end.
|
||||||
|
*/
|
||||||
|
encode_into :: proc {
|
||||||
|
encode_into_bytes,
|
||||||
|
encode_into_builder,
|
||||||
|
encode_into_writer,
|
||||||
|
encode_into_encoder,
|
||||||
|
}
|
||||||
|
encode :: encode_into
|
||||||
|
|
||||||
|
// Encodes the CBOR value into binary CBOR allocated on the given allocator.
|
||||||
|
// See the docs on the proc group `encode_into` for more info.
|
||||||
|
encode_into_bytes :: proc(v: Value, flags := ENCODE_SMALL, allocator := context.allocator, temp_allocator := context.temp_allocator) -> (data: []byte, err: Encode_Error) {
|
||||||
|
b := strings.builder_make(allocator) or_return
|
||||||
|
encode_into_builder(&b, v, flags, temp_allocator) or_return
|
||||||
|
return b.buf[:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encodes the CBOR value into binary CBOR written to the given builder.
|
||||||
|
// See the docs on the proc group `encode_into` for more info.
|
||||||
|
encode_into_builder :: proc(b: ^strings.Builder, v: Value, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator) -> Encode_Error {
|
||||||
|
return encode_into_writer(strings.to_stream(b), v, flags, temp_allocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encodes the CBOR value into binary CBOR written to the given writer.
|
||||||
|
// See the docs on the proc group `encode_into` for more info.
|
||||||
|
encode_into_writer :: proc(w: io.Writer, v: Value, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator) -> Encode_Error {
|
||||||
|
return encode_into_encoder(Encoder{flags, w, temp_allocator}, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encodes the CBOR value into binary CBOR written to the given encoder.
|
||||||
|
// See the docs on the proc group `encode_into` for more info.
|
||||||
|
encode_into_encoder :: proc(e: Encoder, v: Value) -> Encode_Error {
|
||||||
|
e := e
|
||||||
|
|
||||||
|
if e.temp_allocator.procedure == nil {
|
||||||
|
e.temp_allocator = context.temp_allocator
|
||||||
|
}
|
||||||
|
|
||||||
|
if .Self_Described_CBOR in e.flags {
|
||||||
|
_encode_u64(e, TAG_SELF_DESCRIBED_CBOR, .Tag) or_return
|
||||||
|
e.flags &~= { .Self_Described_CBOR }
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v_spec in v {
|
||||||
|
case u8: return _encode_u8(e.writer, v_spec, .Unsigned)
|
||||||
|
case u16: return _encode_u16(e, v_spec, .Unsigned)
|
||||||
|
case u32: return _encode_u32(e, v_spec, .Unsigned)
|
||||||
|
case u64: return _encode_u64(e, v_spec, .Unsigned)
|
||||||
|
case Negative_U8: return _encode_u8(e.writer, u8(v_spec), .Negative)
|
||||||
|
case Negative_U16: return _encode_u16(e, u16(v_spec), .Negative)
|
||||||
|
case Negative_U32: return _encode_u32(e, u32(v_spec), .Negative)
|
||||||
|
case Negative_U64: return _encode_u64(e, u64(v_spec), .Negative)
|
||||||
|
case ^Bytes: return _encode_bytes(e, v_spec^)
|
||||||
|
case ^Text: return _encode_text(e, v_spec^)
|
||||||
|
case ^Array: return _encode_array(e, v_spec^)
|
||||||
|
case ^Map: return _encode_map(e, v_spec^)
|
||||||
|
case ^Tag: return _encode_tag(e, v_spec^)
|
||||||
|
case Simple: return _encode_simple(e.writer, v_spec)
|
||||||
|
case f16: return _encode_f16(e.writer, v_spec)
|
||||||
|
case f32: return _encode_f32(e, v_spec)
|
||||||
|
case f64: return _encode_f64(e, v_spec)
|
||||||
|
case bool: return _encode_bool(e.writer, v_spec)
|
||||||
|
case Nil: return _encode_nil(e.writer)
|
||||||
|
case Undefined: return _encode_undefined(e.writer)
|
||||||
|
case: return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_decode_header :: proc(r: io.Reader) -> (hdr: Header, err: io.Error) {
|
||||||
|
hdr = Header(_decode_u8(r) or_return)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_header_split :: proc(hdr: Header) -> (Major, Add) {
|
||||||
|
return Major(u8(hdr) >> 5), Add(u8(hdr) & 0x1f)
|
||||||
|
}
|
||||||
|
|
||||||
|
_decode_u8 :: proc(r: io.Reader) -> (v: u8, err: io.Error) {
|
||||||
|
byte: [1]byte = ---
|
||||||
|
io.read_full(r, byte[:]) or_return
|
||||||
|
return byte[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_uint :: proc {
|
||||||
|
_encode_u8,
|
||||||
|
_encode_u16,
|
||||||
|
_encode_u32,
|
||||||
|
_encode_u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_u8 :: proc(w: io.Writer, v: u8, major: Major = .Unsigned) -> (err: io.Error) {
|
||||||
|
header := u8(major) << 5
|
||||||
|
if v < u8(Add.One_Byte) {
|
||||||
|
header |= v
|
||||||
|
_, err = io.write_full(w, {header})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
header |= u8(Add.One_Byte)
|
||||||
|
_, err = io.write_full(w, {header, v})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_decode_tiny_u8 :: proc(additional: Add) -> (u8, Decode_Data_Error) {
|
||||||
|
if additional < .One_Byte {
|
||||||
|
return u8(additional), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, .Bad_Argument
|
||||||
|
}
|
||||||
|
|
||||||
|
_decode_u16 :: proc(r: io.Reader) -> (v: u16, err: io.Error) {
|
||||||
|
bytes: [2]byte = ---
|
||||||
|
io.read_full(r, bytes[:]) or_return
|
||||||
|
return endian.unchecked_get_u16be(bytes[:]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_u16 :: proc(e: Encoder, v: u16, major: Major = .Unsigned) -> Encode_Error {
|
||||||
|
if .Deterministic_Int_Size in e.flags {
|
||||||
|
return _encode_deterministic_uint(e.writer, v, major)
|
||||||
|
}
|
||||||
|
return _encode_u16_exact(e.writer, v, major)
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_u16_exact :: proc(w: io.Writer, v: u16, major: Major = .Unsigned) -> (err: io.Error) {
|
||||||
|
bytes: [3]byte = ---
|
||||||
|
bytes[0] = (u8(major) << 5) | u8(Add.Two_Bytes)
|
||||||
|
endian.unchecked_put_u16be(bytes[1:], v)
|
||||||
|
_, err = io.write_full(w, bytes[:])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_decode_u32 :: proc(r: io.Reader) -> (v: u32, err: io.Error) {
|
||||||
|
bytes: [4]byte = ---
|
||||||
|
io.read_full(r, bytes[:]) or_return
|
||||||
|
return endian.unchecked_get_u32be(bytes[:]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_u32 :: proc(e: Encoder, v: u32, major: Major = .Unsigned) -> Encode_Error {
|
||||||
|
if .Deterministic_Int_Size in e.flags {
|
||||||
|
return _encode_deterministic_uint(e.writer, v, major)
|
||||||
|
}
|
||||||
|
return _encode_u32_exact(e.writer, v, major)
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_u32_exact :: proc(w: io.Writer, v: u32, major: Major = .Unsigned) -> (err: io.Error) {
|
||||||
|
bytes: [5]byte = ---
|
||||||
|
bytes[0] = (u8(major) << 5) | u8(Add.Four_Bytes)
|
||||||
|
endian.unchecked_put_u32be(bytes[1:], v)
|
||||||
|
_, err = io.write_full(w, bytes[:])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_decode_u64 :: proc(r: io.Reader) -> (v: u64, err: io.Error) {
|
||||||
|
bytes: [8]byte = ---
|
||||||
|
io.read_full(r, bytes[:]) or_return
|
||||||
|
return endian.unchecked_get_u64be(bytes[:]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_u64 :: proc(e: Encoder, v: u64, major: Major = .Unsigned) -> Encode_Error {
|
||||||
|
if .Deterministic_Int_Size in e.flags {
|
||||||
|
return _encode_deterministic_uint(e.writer, v, major)
|
||||||
|
}
|
||||||
|
return _encode_u64_exact(e.writer, v, major)
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_u64_exact :: proc(w: io.Writer, v: u64, major: Major = .Unsigned) -> (err: io.Error) {
|
||||||
|
bytes: [9]byte = ---
|
||||||
|
bytes[0] = (u8(major) << 5) | u8(Add.Eight_Bytes)
|
||||||
|
endian.unchecked_put_u64be(bytes[1:], v)
|
||||||
|
_, err = io.write_full(w, bytes[:])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_decode_bytes_ptr :: proc(d: Decoder, add: Add, type: Major = .Bytes) -> (v: ^Bytes, err: Decode_Error) {
|
||||||
|
v = new(Bytes) or_return
|
||||||
|
defer if err != nil { free(v) }
|
||||||
|
|
||||||
|
v^ = _decode_bytes(d, add, type) or_return
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_decode_bytes :: proc(d: Decoder, add: Add, type: Major = .Bytes, allocator := context.allocator) -> (v: Bytes, err: Decode_Error) {
|
||||||
|
context.allocator = allocator
|
||||||
|
|
||||||
|
add := add
|
||||||
|
n, scap := _decode_len_str(d, add) or_return
|
||||||
|
|
||||||
|
buf := strings.builder_make(0, scap) or_return
|
||||||
|
defer if err != nil { strings.builder_destroy(&buf) }
|
||||||
|
buf_stream := strings.to_stream(&buf)
|
||||||
|
|
||||||
|
if n == -1 {
|
||||||
|
indefinite_loop: for {
|
||||||
|
header := _decode_header(d.reader) or_return
|
||||||
|
maj: Major
|
||||||
|
maj, add = _header_split(header)
|
||||||
|
#partial switch maj {
|
||||||
|
case type:
|
||||||
|
iter_n, iter_cap := _decode_len_str(d, add) or_return
|
||||||
|
if iter_n == -1 {
|
||||||
|
return nil, .Nested_Indefinite_Length
|
||||||
|
}
|
||||||
|
reserve(&buf.buf, len(buf.buf) + iter_cap) or_return
|
||||||
|
io.copy_n(buf_stream, d.reader, i64(iter_n)) or_return
|
||||||
|
|
||||||
|
case .Other:
|
||||||
|
if add != .Break { return nil, .Bad_Argument }
|
||||||
|
break indefinite_loop
|
||||||
|
|
||||||
|
case:
|
||||||
|
return nil, .Bad_Major
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
io.copy_n(buf_stream, d.reader, i64(n)) or_return
|
||||||
|
}
|
||||||
|
|
||||||
|
v = buf.buf[:]
|
||||||
|
|
||||||
|
// Write zero byte so this can be converted to cstring.
|
||||||
|
strings.write_byte(&buf, 0)
|
||||||
|
|
||||||
|
if .Shrink_Excess in d.flags { shrink(&buf.buf) }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_bytes :: proc(e: Encoder, val: Bytes, major: Major = .Bytes) -> (err: Encode_Error) {
|
||||||
|
assert(len(val) >= 0)
|
||||||
|
_encode_u64(e, u64(len(val)), major) or_return
|
||||||
|
_, err = io.write_full(e.writer, val[:])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_decode_text_ptr :: proc(d: Decoder, add: Add) -> (v: ^Text, err: Decode_Error) {
|
||||||
|
v = new(Text) or_return
|
||||||
|
defer if err != nil { free(v) }
|
||||||
|
|
||||||
|
v^ = _decode_text(d, add) or_return
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_decode_text :: proc(d: Decoder, add: Add, allocator := context.allocator) -> (v: Text, err: Decode_Error) {
|
||||||
|
return (Text)(_decode_bytes(d, add, .Text, allocator) or_return), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_text :: proc(e: Encoder, val: Text) -> Encode_Error {
|
||||||
|
return _encode_bytes(e, transmute([]byte)val, .Text)
|
||||||
|
}
|
||||||
|
|
||||||
|
_decode_array_ptr :: proc(d: Decoder, add: Add) -> (v: ^Array, err: Decode_Error) {
|
||||||
|
v = new(Array) or_return
|
||||||
|
defer if err != nil { free(v) }
|
||||||
|
|
||||||
|
v^ = _decode_array(d, add) or_return
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_decode_array :: proc(d: Decoder, add: Add) -> (v: Array, err: Decode_Error) {
|
||||||
|
n, scap := _decode_len_container(d, add) or_return
|
||||||
|
array := make([dynamic]Value, 0, scap) or_return
|
||||||
|
defer if err != nil {
|
||||||
|
for entry in array { destroy(entry) }
|
||||||
|
delete(array)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; n == -1 || i < n; i += 1 {
|
||||||
|
val, verr := _decode_from_decoder(d)
|
||||||
|
if n == -1 && verr == .Break {
|
||||||
|
break
|
||||||
|
} else if verr != nil {
|
||||||
|
err = verr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
append(&array, val) or_return
|
||||||
|
}
|
||||||
|
|
||||||
|
if .Shrink_Excess in d.flags { shrink(&array) }
|
||||||
|
|
||||||
|
v = array[:]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_array :: proc(e: Encoder, arr: Array) -> Encode_Error {
|
||||||
|
assert(len(arr) >= 0)
|
||||||
|
_encode_u64(e, u64(len(arr)), .Array)
|
||||||
|
for val in arr {
|
||||||
|
encode(e, val) or_return
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_decode_map_ptr :: proc(d: Decoder, add: Add) -> (v: ^Map, err: Decode_Error) {
|
||||||
|
v = new(Map) or_return
|
||||||
|
defer if err != nil { free(v) }
|
||||||
|
|
||||||
|
v^ = _decode_map(d, add) or_return
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_decode_map :: proc(d: Decoder, add: Add) -> (v: Map, err: Decode_Error) {
|
||||||
|
n, scap := _decode_len_container(d, add) or_return
|
||||||
|
items := make([dynamic]Map_Entry, 0, scap) or_return
|
||||||
|
defer if err != nil {
|
||||||
|
for entry in items {
|
||||||
|
destroy(entry.key)
|
||||||
|
destroy(entry.value)
|
||||||
|
}
|
||||||
|
delete(items)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; n == -1 || i < n; i += 1 {
|
||||||
|
key, kerr := _decode_from_decoder(d)
|
||||||
|
if n == -1 && kerr == .Break {
|
||||||
|
break
|
||||||
|
} else if kerr != nil {
|
||||||
|
return nil, kerr
|
||||||
|
}
|
||||||
|
|
||||||
|
value := _decode_from_decoder(d) or_return
|
||||||
|
|
||||||
|
append(&items, Map_Entry{
|
||||||
|
key = key,
|
||||||
|
value = value,
|
||||||
|
}) or_return
|
||||||
|
}
|
||||||
|
|
||||||
|
if .Shrink_Excess in d.flags { shrink(&items) }
|
||||||
|
|
||||||
|
v = items[:]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_map :: proc(e: Encoder, m: Map) -> (err: Encode_Error) {
|
||||||
|
assert(len(m) >= 0)
|
||||||
|
_encode_u64(e, u64(len(m)), .Map) or_return
|
||||||
|
|
||||||
|
if .Deterministic_Map_Sorting not_in e.flags {
|
||||||
|
for entry in m {
|
||||||
|
encode(e, entry.key) or_return
|
||||||
|
encode(e, entry.value) or_return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deterministic_Map_Sorting needs us to sort the entries by the byte contents of the
|
||||||
|
// encoded key.
|
||||||
|
//
|
||||||
|
// This means we have to store and sort them before writing incurring extra (temporary) allocations.
|
||||||
|
|
||||||
|
Map_Entry_With_Key :: struct {
|
||||||
|
encoded_key: []byte,
|
||||||
|
entry: Map_Entry,
|
||||||
|
}
|
||||||
|
|
||||||
|
entries := make([]Map_Entry_With_Key, len(m), e.temp_allocator) or_return
|
||||||
|
defer delete(entries, e.temp_allocator)
|
||||||
|
|
||||||
|
for &entry, i in entries {
|
||||||
|
entry.entry = m[i]
|
||||||
|
|
||||||
|
buf := strings.builder_make(e.temp_allocator) or_return
|
||||||
|
|
||||||
|
ke := e
|
||||||
|
ke.writer = strings.to_stream(&buf)
|
||||||
|
|
||||||
|
encode(ke, entry.entry.key) or_return
|
||||||
|
entry.encoded_key = buf.buf[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort lexicographic on the bytes of the key.
|
||||||
|
slice.sort_by_cmp(entries, proc(a, b: Map_Entry_With_Key) -> slice.Ordering {
|
||||||
|
return slice.Ordering(bytes.compare(a.encoded_key, b.encoded_key))
|
||||||
|
})
|
||||||
|
|
||||||
|
for entry in entries {
|
||||||
|
io.write_full(e.writer, entry.encoded_key) or_return
|
||||||
|
delete(entry.encoded_key, e.temp_allocator)
|
||||||
|
|
||||||
|
encode(e, entry.entry.value) or_return
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_decode_tag_ptr :: proc(d: Decoder, add: Add) -> (v: Value, err: Decode_Error) {
|
||||||
|
tag := _decode_tag(d, add) or_return
|
||||||
|
if t, ok := tag.?; ok {
|
||||||
|
defer if err != nil { destroy(t.value) }
|
||||||
|
tp := new(Tag) or_return
|
||||||
|
tp^ = t
|
||||||
|
return tp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// no error, no tag, this was the self described CBOR tag, skip it.
|
||||||
|
return _decode_from_decoder(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
_decode_tag :: proc(d: Decoder, add: Add) -> (v: Maybe(Tag), err: Decode_Error) {
|
||||||
|
num := _decode_uint_as_u64(d.reader, add) or_return
|
||||||
|
|
||||||
|
// CBOR can be wrapped in a tag that decoders can use to see/check if the binary data is CBOR.
|
||||||
|
// We can ignore it here.
|
||||||
|
if num == TAG_SELF_DESCRIBED_CBOR {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t := Tag{
|
||||||
|
number = num,
|
||||||
|
value = _decode_from_decoder(d) or_return,
|
||||||
|
}
|
||||||
|
|
||||||
|
if nested, ok := t.value.(^Tag); ok {
|
||||||
|
destroy(nested)
|
||||||
|
return nil, .Nested_Tag
|
||||||
|
}
|
||||||
|
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_decode_uint_as_u64 :: proc(r: io.Reader, add: Add) -> (nr: u64, err: Decode_Error) {
|
||||||
|
#partial switch add {
|
||||||
|
case .One_Byte: return u64(_decode_u8(r) or_return), nil
|
||||||
|
case .Two_Bytes: return u64(_decode_u16(r) or_return), nil
|
||||||
|
case .Four_Bytes: return u64(_decode_u32(r) or_return), nil
|
||||||
|
case .Eight_Bytes: return u64(_decode_u64(r) or_return), nil
|
||||||
|
case: return u64(_decode_tiny_u8(add) or_return), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_tag :: proc(e: Encoder, val: Tag) -> Encode_Error {
|
||||||
|
_encode_u64(e, val.number, .Tag) or_return
|
||||||
|
return encode(e, val.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
_decode_simple :: proc(r: io.Reader) -> (v: Simple, err: io.Error) {
|
||||||
|
buf: [1]byte = ---
|
||||||
|
io.read_full(r, buf[:]) or_return
|
||||||
|
return Simple(buf[0]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_simple :: proc(w: io.Writer, v: Simple) -> (err: Encode_Error) {
|
||||||
|
header := u8(Major.Other) << 5
|
||||||
|
|
||||||
|
if v < Simple(Add.False) {
|
||||||
|
header |= u8(v)
|
||||||
|
_, err = io.write_full(w, {header})
|
||||||
|
return
|
||||||
|
} else if v <= Simple(Add.Break) {
|
||||||
|
return .Invalid_Simple
|
||||||
|
}
|
||||||
|
|
||||||
|
header |= u8(Add.One_Byte)
|
||||||
|
_, err = io.write_full(w, {header, u8(v)})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_decode_tiny_simple :: proc(add: Add) -> (Simple, Decode_Data_Error) {
|
||||||
|
if add < Add.False {
|
||||||
|
return Simple(add), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, .Bad_Argument
|
||||||
|
}
|
||||||
|
|
||||||
|
_decode_f16 :: proc(r: io.Reader) -> (v: f16, err: io.Error) {
|
||||||
|
bytes: [2]byte = ---
|
||||||
|
io.read_full(r, bytes[:]) or_return
|
||||||
|
n := endian.unchecked_get_u16be(bytes[:])
|
||||||
|
return transmute(f16)n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_f16 :: proc(w: io.Writer, v: f16) -> (err: io.Error) {
|
||||||
|
bytes: [3]byte = ---
|
||||||
|
bytes[0] = u8(Header.F16)
|
||||||
|
endian.unchecked_put_u16be(bytes[1:], transmute(u16)v)
|
||||||
|
_, err = io.write_full(w, bytes[:])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_decode_f32 :: proc(r: io.Reader) -> (v: f32, err: io.Error) {
|
||||||
|
bytes: [4]byte = ---
|
||||||
|
io.read_full(r, bytes[:]) or_return
|
||||||
|
n := endian.unchecked_get_u32be(bytes[:])
|
||||||
|
return transmute(f32)n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_f32 :: proc(e: Encoder, v: f32) -> io.Error {
|
||||||
|
if .Deterministic_Float_Size in e.flags {
|
||||||
|
return _encode_deterministic_float(e.writer, v)
|
||||||
|
}
|
||||||
|
return _encode_f32_exact(e.writer, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_f32_exact :: proc(w: io.Writer, v: f32) -> (err: io.Error) {
|
||||||
|
bytes: [5]byte = ---
|
||||||
|
bytes[0] = u8(Header.F32)
|
||||||
|
endian.unchecked_put_u32be(bytes[1:], transmute(u32)v)
|
||||||
|
_, err = io.write_full(w, bytes[:])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_decode_f64 :: proc(r: io.Reader) -> (v: f64, err: io.Error) {
|
||||||
|
bytes: [8]byte = ---
|
||||||
|
io.read_full(r, bytes[:]) or_return
|
||||||
|
n := endian.unchecked_get_u64be(bytes[:])
|
||||||
|
return transmute(f64)n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_f64 :: proc(e: Encoder, v: f64) -> io.Error {
|
||||||
|
if .Deterministic_Float_Size in e.flags {
|
||||||
|
return _encode_deterministic_float(e.writer, v)
|
||||||
|
}
|
||||||
|
return _encode_f64_exact(e.writer, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_f64_exact :: proc(w: io.Writer, v: f64) -> (err: io.Error) {
|
||||||
|
bytes: [9]byte = ---
|
||||||
|
bytes[0] = u8(Header.F64)
|
||||||
|
endian.unchecked_put_u64be(bytes[1:], transmute(u64)v)
|
||||||
|
_, err = io.write_full(w, bytes[:])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_bool :: proc(w: io.Writer, v: bool) -> (err: io.Error) {
|
||||||
|
switch v {
|
||||||
|
case true: _, err = io.write_full(w, {u8(Header.True )}); return
|
||||||
|
case false: _, err = io.write_full(w, {u8(Header.False)}); return
|
||||||
|
case: unreachable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_undefined :: proc(w: io.Writer) -> io.Error {
|
||||||
|
_, err := io.write_full(w, {u8(Header.Undefined)})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_nil :: proc(w: io.Writer) -> io.Error {
|
||||||
|
_, err := io.write_full(w, {u8(Header.Nil)})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Streaming
|
||||||
|
|
||||||
|
encode_stream_begin :: proc(w: io.Writer, major: Major) -> (err: io.Error) {
|
||||||
|
assert(major >= Major(.Bytes) && major <= Major(.Map), "illegal stream type")
|
||||||
|
|
||||||
|
header := (u8(major) << 5) | u8(Add.Length_Unknown)
|
||||||
|
_, err = io.write_full(w, {header})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
encode_stream_end :: proc(w: io.Writer) -> io.Error {
|
||||||
|
header := (u8(Major.Other) << 5) | u8(Add.Break)
|
||||||
|
_, err := io.write_full(w, {header})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
encode_stream_bytes :: _encode_bytes
|
||||||
|
encode_stream_text :: _encode_text
|
||||||
|
encode_stream_array_item :: encode
|
||||||
|
|
||||||
|
encode_stream_map_entry :: proc(e: Encoder, key: Value, val: Value) -> Encode_Error {
|
||||||
|
encode(e, key) or_return
|
||||||
|
return encode(e, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For `Bytes` and `Text` strings: Decodes the number of items the header says follows.
|
||||||
|
// If the number is not specified -1 is returned and streaming should be initiated.
|
||||||
|
// A suitable starting capacity is also returned for a buffer that is allocated up the stack.
|
||||||
|
_decode_len_str :: proc(d: Decoder, add: Add) -> (n: int, scap: int, err: Decode_Error) {
|
||||||
|
if add == .Length_Unknown {
|
||||||
|
if .Disallow_Streaming in d.flags {
|
||||||
|
return -1, -1, .Disallowed_Streaming
|
||||||
|
}
|
||||||
|
return -1, INITIAL_STREAMED_BYTES_CAPACITY, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_n := _decode_uint_as_u64(d.reader, add) or_return
|
||||||
|
if _n > u64(max(int)) { return -1, -1, .Length_Too_Big }
|
||||||
|
n = int(_n)
|
||||||
|
|
||||||
|
scap = n + 1 // Space for zero byte.
|
||||||
|
if .Trusted_Input not_in d.flags {
|
||||||
|
scap = min(d.max_pre_alloc, scap)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// For `Array` and `Map` types: Decodes the number of items the header says follows.
|
||||||
|
// If the number is not specified -1 is returned and streaming should be initiated.
|
||||||
|
// A suitable starting capacity is also returned for a buffer that is allocated up the stack.
|
||||||
|
_decode_len_container :: proc(d: Decoder, add: Add) -> (n: int, scap: int, err: Decode_Error) {
|
||||||
|
if add == .Length_Unknown {
|
||||||
|
if .Disallow_Streaming in d.flags {
|
||||||
|
return -1, -1, .Disallowed_Streaming
|
||||||
|
}
|
||||||
|
return -1, INITIAL_STREAMED_CONTAINER_CAPACITY, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_n := _decode_uint_as_u64(d.reader, add) or_return
|
||||||
|
if _n > u64(max(int)) { return -1, -1, .Length_Too_Big }
|
||||||
|
n = int(_n)
|
||||||
|
|
||||||
|
scap = n
|
||||||
|
if .Trusted_Input not_in d.flags {
|
||||||
|
// NOTE: if this is a map it will be twice this.
|
||||||
|
scap = min(d.max_pre_alloc / size_of(Value), scap)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deterministic encoding is (among other things) encoding all values into their smallest
|
||||||
|
// possible representation.
|
||||||
|
// See section 4 of RFC 8949.
|
||||||
|
|
||||||
|
_encode_deterministic_uint :: proc {
|
||||||
|
_encode_u8,
|
||||||
|
_encode_deterministic_u16,
|
||||||
|
_encode_deterministic_u32,
|
||||||
|
_encode_deterministic_u64,
|
||||||
|
_encode_deterministic_u128,
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_deterministic_u16 :: proc(w: io.Writer, v: u16, major: Major = .Unsigned) -> Encode_Error {
|
||||||
|
switch {
|
||||||
|
case v <= u16(max(u8)): return _encode_u8(w, u8(v), major)
|
||||||
|
case: return _encode_u16_exact(w, v, major)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_deterministic_u32 :: proc(w: io.Writer, v: u32, major: Major = .Unsigned) -> Encode_Error {
|
||||||
|
switch {
|
||||||
|
case v <= u32(max(u8)): return _encode_u8(w, u8(v), major)
|
||||||
|
case v <= u32(max(u16)): return _encode_u16_exact(w, u16(v), major)
|
||||||
|
case: return _encode_u32_exact(w, u32(v), major)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_deterministic_u64 :: proc(w: io.Writer, v: u64, major: Major = .Unsigned) -> Encode_Error {
|
||||||
|
switch {
|
||||||
|
case v <= u64(max(u8)): return _encode_u8(w, u8(v), major)
|
||||||
|
case v <= u64(max(u16)): return _encode_u16_exact(w, u16(v), major)
|
||||||
|
case v <= u64(max(u32)): return _encode_u32_exact(w, u32(v), major)
|
||||||
|
case: return _encode_u64_exact(w, u64(v), major)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_deterministic_u128 :: proc(w: io.Writer, v: u128, major: Major = .Unsigned) -> Encode_Error {
|
||||||
|
switch {
|
||||||
|
case v <= u128(max(u8)): return _encode_u8(w, u8(v), major)
|
||||||
|
case v <= u128(max(u16)): return _encode_u16_exact(w, u16(v), major)
|
||||||
|
case v <= u128(max(u32)): return _encode_u32_exact(w, u32(v), major)
|
||||||
|
case v <= u128(max(u64)): return _encode_u64_exact(w, u64(v), major)
|
||||||
|
case: return .Int_Too_Big
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_deterministic_negative :: #force_inline proc(w: io.Writer, v: $T) -> Encode_Error
|
||||||
|
where T == Negative_U8 || T == Negative_U16 || T == Negative_U32 || T == Negative_U64 {
|
||||||
|
return _encode_deterministic_uint(w, v, .Negative)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Deterministic float is a float in the smallest type that stays the same after down casting.
|
||||||
|
_encode_deterministic_float :: proc {
|
||||||
|
_encode_f16,
|
||||||
|
_encode_deterministic_f32,
|
||||||
|
_encode_deterministic_f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_deterministic_f32 :: proc(w: io.Writer, v: f32) -> io.Error {
|
||||||
|
if (f32(f16(v)) == v) {
|
||||||
|
return _encode_f16(w, f16(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
return _encode_f32_exact(w, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
_encode_deterministic_f64 :: proc(w: io.Writer, v: f64) -> io.Error {
|
||||||
|
if (f64(f16(v)) == v) {
|
||||||
|
return _encode_f16(w, f16(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f64(f32(v)) == v) {
|
||||||
|
return _encode_f32_exact(w, f32(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
return _encode_f64_exact(w, v)
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
Package cbor encodes, decodes, marshals and unmarshals types from/into RCF 8949 compatible CBOR binary.
|
||||||
|
Also provided are conversion to and from JSON and the CBOR diagnostic format.
|
||||||
|
|
||||||
|
**Allocations:**
|
||||||
|
|
||||||
|
In general, when in the following table it says allocations are done on the `temp_allocator`, these allocations
|
||||||
|
are still attempted to be deallocated.
|
||||||
|
This allows you to use an allocator with freeing implemented as the `temp_allocator` which is handy with big CBOR.
|
||||||
|
|
||||||
|
- *Encoding*: If the `.Deterministic_Map_Sorting` flag is set on the encoder, this allocates on the given `temp_allocator`
|
||||||
|
some space for the keys of maps in order to sort them and then write them.
|
||||||
|
Other than that there are no allocations (only for the final bytes if you use `cbor.encode_into_bytes`.
|
||||||
|
|
||||||
|
- *Decoding*: Allocates everything on the given allocator and input given can be deleted after decoding.
|
||||||
|
*No* temporary allocations are done.
|
||||||
|
|
||||||
|
- *Marshal*: Same allocation strategy as encoding.
|
||||||
|
|
||||||
|
- *Unmarshal*: Allocates everything on the given allocator and input given can be deleted after unmarshalling.
|
||||||
|
Some temporary allocations are done on the given `temp_allocator`.
|
||||||
|
|
||||||
|
**Determinism:**
|
||||||
|
|
||||||
|
CBOR defines a deterministic en/decoder, which among other things uses the smallest type possible for integers and floats,
|
||||||
|
and sorts map keys by their (encoded) lexical bytewise order.
|
||||||
|
|
||||||
|
You can enable this behaviour using a combination of flags, also available as the `cbor.ENCODE_FULLY_DETERMINISTIC` constant.
|
||||||
|
If you just want the small size that comes with this, but not the map sorting (which has a performance cost) you can use the
|
||||||
|
`cbor.ENCODE_SMALL` constant for the flags.
|
||||||
|
|
||||||
|
A deterministic float is a float in the smallest type (f16, f32, f64) that hasn't changed after conversion.
|
||||||
|
A deterministic integer is an integer in the smallest representation (u8, u16, u32, u64) it fits in.
|
||||||
|
|
||||||
|
**Untrusted Input:**
|
||||||
|
|
||||||
|
By default input is treated as untrusted, this means the sizes that are encoded in the CBOR are not blindly trusted.
|
||||||
|
If you were to trust these sizes, and allocate space for them an attacker would be able to cause massive allocations with small payloads.
|
||||||
|
|
||||||
|
The decoder has a `max_pre_alloc` field that specifies the maximum amount of bytes (roughly) to pre allocate, a KiB by default.
|
||||||
|
|
||||||
|
This does mean reallocations are more common though, you can, if you know the input is trusted, add the `.Trusted_Input` flag to the decoder.
|
||||||
|
|
||||||
|
**Tags:**
|
||||||
|
|
||||||
|
CBOR describes tags that you can wrap values with to assign a number to describe what type of data will follow.
|
||||||
|
|
||||||
|
More information and a list of default tags can be found here: [[RFC 8949 Section 3.4;https://www.rfc-editor.org/rfc/rfc8949.html#name-tagging-of-items]].
|
||||||
|
|
||||||
|
A list of registered extension types can be found here: [[IANA CBOR assignments;https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml]].
|
||||||
|
|
||||||
|
Tags can either be assigned to a distinct Odin type (used by default),
|
||||||
|
or be used with struct tags (`cbor_tag:"base64"`, or `cbor_tag:"1"` for example).
|
||||||
|
|
||||||
|
By default, the following tags are supported/provided by this implementation:
|
||||||
|
|
||||||
|
- *1/epoch*: Assign this tag to `time.Time` or integer fields to use the defined seconds since epoch format.
|
||||||
|
|
||||||
|
- *24/cbor*: Assign this tag to string or byte fields to store encoded CBOR (not decoding it).
|
||||||
|
|
||||||
|
- *34/base64*: Assign this tag to string or byte fields to store and decode the contents in base64.
|
||||||
|
|
||||||
|
- *2 & 3*: Used automatically by the implementation to encode and decode big numbers into/from `core:math/big`.
|
||||||
|
|
||||||
|
- *55799*: Self described CBOR, used when `.Self_Described_CBOR` flag is used to wrap the entire binary.
|
||||||
|
This shows other implementations that we are dealing with CBOR by just looking at the first byte of input.
|
||||||
|
|
||||||
|
- *1010*: An extension tag that defines a string type followed by its value, this is used by this implementation to support Odin's unions.
|
||||||
|
|
||||||
|
Users can provide their own tag implementations using the `cbor.tag_register_type(...)` to register a tag for a distinct Odin type
|
||||||
|
used automatically when it is encountered during marshal and unmarshal.
|
||||||
|
Or with `cbor.tag_register_number(...)` to register a tag number along with an identifier for convenience that can be used with struct tags,
|
||||||
|
e.g. `cbor_tag:"69"` or `cbor_tag:"my_tag"`.
|
||||||
|
|
||||||
|
You can look at the default tags provided for pointers on how these implementations work.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "core:encoding/cbor"
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:time"
|
||||||
|
|
||||||
|
Possibilities :: union {
|
||||||
|
string,
|
||||||
|
int,
|
||||||
|
}
|
||||||
|
|
||||||
|
Data :: struct {
|
||||||
|
str: string,
|
||||||
|
neg: cbor.Negative_U16, // Store a CBOR value directly.
|
||||||
|
now: time.Time `cbor_tag:"epoch"`, // Wrapped in the epoch tag.
|
||||||
|
ignore_this: ^Data `cbor:"-"`, // Ignored by implementation.
|
||||||
|
renamed: f32 `cbor:"renamed :)"`, // Renamed when encoded.
|
||||||
|
my_union: Possibilities, // Union support.
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
now := time.Time{_nsec = 1701117968 * 1e9}
|
||||||
|
|
||||||
|
data := Data{
|
||||||
|
str = "Hello, World!",
|
||||||
|
neg = 300,
|
||||||
|
now = now,
|
||||||
|
ignore_this = &Data{},
|
||||||
|
renamed = 123123.125,
|
||||||
|
my_union = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal the struct into binary CBOR.
|
||||||
|
binary, err := cbor.marshal(data, cbor.ENCODE_FULLY_DETERMINISTIC)
|
||||||
|
assert(err == nil)
|
||||||
|
defer delete(binary)
|
||||||
|
|
||||||
|
// Decode the binary data into a `cbor.Value`.
|
||||||
|
decoded, derr := cbor.decode(string(binary))
|
||||||
|
assert(derr == nil)
|
||||||
|
defer cbor.destroy(decoded)
|
||||||
|
|
||||||
|
// Turn the CBOR into a human readable representation defined as the diagnostic format in [[RFC 8949 Section 8;https://www.rfc-editor.org/rfc/rfc8949.html#name-diagnostic-notation]].
|
||||||
|
diagnosis, eerr := cbor.to_diagnostic_format(decoded)
|
||||||
|
assert(eerr == nil)
|
||||||
|
defer delete(diagnosis)
|
||||||
|
|
||||||
|
fmt.println(diagnosis)
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
{
|
||||||
|
"my_union": 1010([
|
||||||
|
"int",
|
||||||
|
3
|
||||||
|
]),
|
||||||
|
"neg": -301,
|
||||||
|
"now": 1(1701117968),
|
||||||
|
"renamed :)": 123123.12500000,
|
||||||
|
"str": "Hello, World!"
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
package encoding_cbor
|
||||||
|
|
||||||
@@ -0,0 +1,575 @@
|
|||||||
|
package encoding_cbor
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
import "base:runtime"
|
||||||
|
|
||||||
|
import "core:bytes"
|
||||||
|
import "core:io"
|
||||||
|
import "core:mem"
|
||||||
|
import "core:reflect"
|
||||||
|
import "core:slice"
|
||||||
|
import "core:strconv"
|
||||||
|
import "core:strings"
|
||||||
|
import "core:unicode/utf8"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Marshal a value into binary CBOR.
|
||||||
|
|
||||||
|
Flags can be used to control the output (mainly determinism, which coincidently affects size).
|
||||||
|
|
||||||
|
The default flags `ENCODE_SMALL` (`.Deterministic_Int_Size`, `.Deterministic_Float_Size`) will try
|
||||||
|
to put ints and floats into their smallest possible byte size without losing equality.
|
||||||
|
|
||||||
|
Adding the `.Self_Described_CBOR` flag will wrap the value in a tag that lets generic decoders know
|
||||||
|
the contents are CBOR from just reading the first byte.
|
||||||
|
|
||||||
|
Adding the `.Deterministic_Map_Sorting` flag will sort the encoded maps by the byte content of the
|
||||||
|
encoded key. This flag has a cost on performance and memory efficiency because all keys in a map
|
||||||
|
have to be precomputed, sorted and only then written to the output.
|
||||||
|
|
||||||
|
Empty flags will do nothing extra to the value.
|
||||||
|
|
||||||
|
The allocations for the `.Deterministic_Map_Sorting` flag are done using the given `temp_allocator`.
|
||||||
|
but are followed by the necessary `delete` and `free` calls if the allocator supports them.
|
||||||
|
This is helpful when the CBOR size is so big that you don't want to collect all the temporary
|
||||||
|
allocations until the end.
|
||||||
|
*/
|
||||||
|
marshal_into :: proc {
|
||||||
|
marshal_into_bytes,
|
||||||
|
marshal_into_builder,
|
||||||
|
marshal_into_writer,
|
||||||
|
marshal_into_encoder,
|
||||||
|
}
|
||||||
|
|
||||||
|
marshal :: marshal_into
|
||||||
|
|
||||||
|
// Marshals the given value into a CBOR byte stream (allocated using the given allocator).
|
||||||
|
// See docs on the `marshal_into` proc group for more info.
|
||||||
|
marshal_into_bytes :: proc(v: any, flags := ENCODE_SMALL, allocator := context.allocator, temp_allocator := context.temp_allocator) -> (bytes: []byte, err: Marshal_Error) {
|
||||||
|
b, alloc_err := strings.builder_make(allocator)
|
||||||
|
// The builder as a stream also returns .EOF if it ran out of memory so this is consistent.
|
||||||
|
if alloc_err != nil {
|
||||||
|
return nil, .EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
defer if err != nil { strings.builder_destroy(&b) }
|
||||||
|
|
||||||
|
if err = marshal_into_builder(&b, v, flags, temp_allocator); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.buf[:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshals the given value into a CBOR byte stream written to the given builder.
|
||||||
|
// See docs on the `marshal_into` proc group for more info.
|
||||||
|
marshal_into_builder :: proc(b: ^strings.Builder, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator) -> Marshal_Error {
|
||||||
|
return marshal_into_writer(strings.to_writer(b), v, flags, temp_allocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshals the given value into a CBOR byte stream written to the given writer.
|
||||||
|
// See docs on the `marshal_into` proc group for more info.
|
||||||
|
marshal_into_writer :: proc(w: io.Writer, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator) -> Marshal_Error {
|
||||||
|
encoder := Encoder{flags, w, temp_allocator}
|
||||||
|
return marshal_into_encoder(encoder, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshals the given value into a CBOR byte stream written to the given encoder.
|
||||||
|
// See docs on the `marshal_into` proc group for more info.
|
||||||
|
marshal_into_encoder :: proc(e: Encoder, v: any) -> (err: Marshal_Error) {
|
||||||
|
e := e
|
||||||
|
|
||||||
|
if e.temp_allocator.procedure == nil {
|
||||||
|
e.temp_allocator = context.temp_allocator
|
||||||
|
}
|
||||||
|
|
||||||
|
if .Self_Described_CBOR in e.flags {
|
||||||
|
err_conv(_encode_u64(e, TAG_SELF_DESCRIBED_CBOR, .Tag)) or_return
|
||||||
|
e.flags &~= { .Self_Described_CBOR }
|
||||||
|
}
|
||||||
|
|
||||||
|
if v == nil {
|
||||||
|
return _encode_nil(e.writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if type has a tag implementation to use.
|
||||||
|
if impl, ok := _tag_implementations_type[v.id]; ok {
|
||||||
|
return impl->marshal(e, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
ti := runtime.type_info_base(type_info_of(v.id))
|
||||||
|
a := any{v.data, ti.id}
|
||||||
|
|
||||||
|
#partial switch info in ti.variant {
|
||||||
|
case runtime.Type_Info_Named:
|
||||||
|
unreachable()
|
||||||
|
|
||||||
|
case runtime.Type_Info_Pointer:
|
||||||
|
switch vv in v {
|
||||||
|
case Undefined: return _encode_undefined(e.writer)
|
||||||
|
case Nil: return _encode_nil(e.writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
case runtime.Type_Info_Integer:
|
||||||
|
switch vv in v {
|
||||||
|
case Simple: return err_conv(_encode_simple(e.writer, vv))
|
||||||
|
case Negative_U8: return _encode_u8(e.writer, u8(vv), .Negative)
|
||||||
|
case Negative_U16: return err_conv(_encode_u16(e, u16(vv), .Negative))
|
||||||
|
case Negative_U32: return err_conv(_encode_u32(e, u32(vv), .Negative))
|
||||||
|
case Negative_U64: return err_conv(_encode_u64(e, u64(vv), .Negative))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch i in a {
|
||||||
|
case i8: return _encode_uint(e.writer, _int_to_uint(i))
|
||||||
|
case i16: return err_conv(_encode_uint(e, _int_to_uint(i)))
|
||||||
|
case i32: return err_conv(_encode_uint(e, _int_to_uint(i)))
|
||||||
|
case i64: return err_conv(_encode_uint(e, _int_to_uint(i)))
|
||||||
|
case i128: return err_conv(_encode_uint(e, _int_to_uint(i128(i)) or_return))
|
||||||
|
case int: return err_conv(_encode_uint(e, _int_to_uint(i64(i))))
|
||||||
|
|
||||||
|
case u8: return _encode_uint(e.writer, i)
|
||||||
|
case u16: return err_conv(_encode_uint(e, i))
|
||||||
|
case u32: return err_conv(_encode_uint(e, i))
|
||||||
|
case u64: return err_conv(_encode_uint(e, i))
|
||||||
|
case u128: return err_conv(_encode_uint(e, _u128_to_u64(u128(i)) or_return))
|
||||||
|
case uint: return err_conv(_encode_uint(e, u64(i)))
|
||||||
|
case uintptr: return err_conv(_encode_uint(e, u64(i)))
|
||||||
|
|
||||||
|
case i16le: return err_conv(_encode_uint(e, _int_to_uint(i16(i))))
|
||||||
|
case i32le: return err_conv(_encode_uint(e, _int_to_uint(i32(i))))
|
||||||
|
case i64le: return err_conv(_encode_uint(e, _int_to_uint(i64(i))))
|
||||||
|
case i128le: return err_conv(_encode_uint(e, _int_to_uint(i128(i)) or_return))
|
||||||
|
|
||||||
|
case u16le: return err_conv(_encode_uint(e, u16(i)))
|
||||||
|
case u32le: return err_conv(_encode_uint(e, u32(i)))
|
||||||
|
case u64le: return err_conv(_encode_uint(e, u64(i)))
|
||||||
|
case u128le: return err_conv(_encode_uint(e, _u128_to_u64(u128(i)) or_return))
|
||||||
|
|
||||||
|
case i16be: return err_conv(_encode_uint(e, _int_to_uint(i16(i))))
|
||||||
|
case i32be: return err_conv(_encode_uint(e, _int_to_uint(i32(i))))
|
||||||
|
case i64be: return err_conv(_encode_uint(e, _int_to_uint(i64(i))))
|
||||||
|
case i128be: return err_conv(_encode_uint(e, _int_to_uint(i128(i)) or_return))
|
||||||
|
|
||||||
|
case u16be: return err_conv(_encode_uint(e, u16(i)))
|
||||||
|
case u32be: return err_conv(_encode_uint(e, u32(i)))
|
||||||
|
case u64be: return err_conv(_encode_uint(e, u64(i)))
|
||||||
|
case u128be: return err_conv(_encode_uint(e, _u128_to_u64(u128(i)) or_return))
|
||||||
|
}
|
||||||
|
|
||||||
|
case runtime.Type_Info_Rune:
|
||||||
|
buf, w := utf8.encode_rune(a.(rune))
|
||||||
|
return err_conv(_encode_text(e, string(buf[:w])))
|
||||||
|
|
||||||
|
case runtime.Type_Info_Float:
|
||||||
|
switch f in a {
|
||||||
|
case f16: return _encode_f16(e.writer, f)
|
||||||
|
case f32: return _encode_f32(e, f)
|
||||||
|
case f64: return _encode_f64(e, f)
|
||||||
|
|
||||||
|
case f16le: return _encode_f16(e.writer, f16(f))
|
||||||
|
case f32le: return _encode_f32(e, f32(f))
|
||||||
|
case f64le: return _encode_f64(e, f64(f))
|
||||||
|
|
||||||
|
case f16be: return _encode_f16(e.writer, f16(f))
|
||||||
|
case f32be: return _encode_f32(e, f32(f))
|
||||||
|
case f64be: return _encode_f64(e, f64(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
case runtime.Type_Info_Complex:
|
||||||
|
switch z in a {
|
||||||
|
case complex32:
|
||||||
|
arr: [2]Value = {real(z), imag(z)}
|
||||||
|
return err_conv(_encode_array(e, arr[:]))
|
||||||
|
case complex64:
|
||||||
|
arr: [2]Value = {real(z), imag(z)}
|
||||||
|
return err_conv(_encode_array(e, arr[:]))
|
||||||
|
case complex128:
|
||||||
|
arr: [2]Value = {real(z), imag(z)}
|
||||||
|
return err_conv(_encode_array(e, arr[:]))
|
||||||
|
}
|
||||||
|
|
||||||
|
case runtime.Type_Info_Quaternion:
|
||||||
|
switch q in a {
|
||||||
|
case quaternion64:
|
||||||
|
arr: [4]Value = {imag(q), jmag(q), kmag(q), real(q)}
|
||||||
|
return err_conv(_encode_array(e, arr[:]))
|
||||||
|
case quaternion128:
|
||||||
|
arr: [4]Value = {imag(q), jmag(q), kmag(q), real(q)}
|
||||||
|
return err_conv(_encode_array(e, arr[:]))
|
||||||
|
case quaternion256:
|
||||||
|
arr: [4]Value = {imag(q), jmag(q), kmag(q), real(q)}
|
||||||
|
return err_conv(_encode_array(e, arr[:]))
|
||||||
|
}
|
||||||
|
|
||||||
|
case runtime.Type_Info_String:
|
||||||
|
switch s in a {
|
||||||
|
case string: return err_conv(_encode_text(e, s))
|
||||||
|
case cstring: return err_conv(_encode_text(e, string(s)))
|
||||||
|
}
|
||||||
|
|
||||||
|
case runtime.Type_Info_Boolean:
|
||||||
|
switch b in a {
|
||||||
|
case bool: return _encode_bool(e.writer, b)
|
||||||
|
case b8: return _encode_bool(e.writer, bool(b))
|
||||||
|
case b16: return _encode_bool(e.writer, bool(b))
|
||||||
|
case b32: return _encode_bool(e.writer, bool(b))
|
||||||
|
case b64: return _encode_bool(e.writer, bool(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
case runtime.Type_Info_Array:
|
||||||
|
if info.elem.id == byte {
|
||||||
|
raw := ([^]byte)(v.data)
|
||||||
|
return err_conv(_encode_bytes(e, raw[:info.count]))
|
||||||
|
}
|
||||||
|
|
||||||
|
err_conv(_encode_u64(e, u64(info.count), .Array)) or_return
|
||||||
|
for i in 0..<info.count {
|
||||||
|
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||||
|
marshal_into(e, any{rawptr(data), info.elem.id}) or_return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
case runtime.Type_Info_Enumerated_Array:
|
||||||
|
// index := runtime.type_info_base(info.index).variant.(runtime.Type_Info_Enum)
|
||||||
|
err_conv(_encode_u64(e, u64(info.count), .Array)) or_return
|
||||||
|
for i in 0..<info.count {
|
||||||
|
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||||
|
marshal_into(e, any{rawptr(data), info.elem.id}) or_return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
case runtime.Type_Info_Dynamic_Array:
|
||||||
|
if info.elem.id == byte {
|
||||||
|
raw := (^[dynamic]byte)(v.data)
|
||||||
|
return err_conv(_encode_bytes(e, raw[:]))
|
||||||
|
}
|
||||||
|
|
||||||
|
array := (^mem.Raw_Dynamic_Array)(v.data)
|
||||||
|
err_conv(_encode_u64(e, u64(array.len), .Array)) or_return
|
||||||
|
for i in 0..<array.len {
|
||||||
|
data := uintptr(array.data) + uintptr(i*info.elem_size)
|
||||||
|
marshal_into(e, any{rawptr(data), info.elem.id}) or_return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
case runtime.Type_Info_Slice:
|
||||||
|
if info.elem.id == byte {
|
||||||
|
raw := (^[]byte)(v.data)
|
||||||
|
return err_conv(_encode_bytes(e, raw^))
|
||||||
|
}
|
||||||
|
|
||||||
|
array := (^mem.Raw_Slice)(v.data)
|
||||||
|
err_conv(_encode_u64(e, u64(array.len), .Array)) or_return
|
||||||
|
for i in 0..<array.len {
|
||||||
|
data := uintptr(array.data) + uintptr(i*info.elem_size)
|
||||||
|
marshal_into(e, any{rawptr(data), info.elem.id}) or_return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
case runtime.Type_Info_Map:
|
||||||
|
m := (^mem.Raw_Map)(v.data)
|
||||||
|
err_conv(_encode_u64(e, u64(runtime.map_len(m^)), .Map)) or_return
|
||||||
|
if m != nil {
|
||||||
|
if info.map_info == nil {
|
||||||
|
return _unsupported(v.id, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
map_cap := uintptr(runtime.map_cap(m^))
|
||||||
|
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
|
||||||
|
|
||||||
|
if .Deterministic_Map_Sorting not_in e.flags {
|
||||||
|
for bucket_index in 0..<map_cap {
|
||||||
|
runtime.map_hash_is_valid(hs[bucket_index]) or_continue
|
||||||
|
|
||||||
|
key := rawptr(runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index))
|
||||||
|
value := rawptr(runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index))
|
||||||
|
|
||||||
|
marshal_into(e, any{ key, info.key.id }) or_return
|
||||||
|
marshal_into(e, any{ value, info.value.id }) or_return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deterministic_Map_Sorting needs us to sort the entries by the byte contents of the
|
||||||
|
// encoded key.
|
||||||
|
//
|
||||||
|
// This means we have to store and sort them before writing incurring extra (temporary) allocations.
|
||||||
|
//
|
||||||
|
// If the map key is a `string` or `cstring` we only allocate space for a dynamic array of entries
|
||||||
|
// we sort.
|
||||||
|
//
|
||||||
|
// If the map key is of another type we also allocate space for encoding the key into.
|
||||||
|
|
||||||
|
// To sort a string/cstring we need to first sort by their encoded header/length.
|
||||||
|
// This fits in 9 bytes at most.
|
||||||
|
pre_key :: #force_inline proc(e: Encoder, str: string) -> (res: [10]byte) {
|
||||||
|
e := e
|
||||||
|
builder := strings.builder_from_slice(res[:])
|
||||||
|
e.writer = strings.to_stream(&builder)
|
||||||
|
|
||||||
|
assert(_encode_u64(e, u64(len(str)), .Text) == nil)
|
||||||
|
res[9] = u8(len(builder.buf))
|
||||||
|
assert(res[9] < 10)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Encoded_Entry_Fast :: struct($T: typeid) {
|
||||||
|
pre_key: [10]byte,
|
||||||
|
key: T,
|
||||||
|
val_idx: uintptr,
|
||||||
|
}
|
||||||
|
|
||||||
|
Encoded_Entry :: struct {
|
||||||
|
key: ^[dynamic]byte,
|
||||||
|
val_idx: uintptr,
|
||||||
|
}
|
||||||
|
|
||||||
|
switch info.key.id {
|
||||||
|
case string:
|
||||||
|
entries := make([dynamic]Encoded_Entry_Fast(^[]byte), 0, map_cap, e.temp_allocator) or_return
|
||||||
|
defer delete(entries)
|
||||||
|
|
||||||
|
for bucket_index in 0..<map_cap {
|
||||||
|
runtime.map_hash_is_valid(hs[bucket_index]) or_continue
|
||||||
|
|
||||||
|
key := (^[]byte)(runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index))
|
||||||
|
append(&entries, Encoded_Entry_Fast(^[]byte){
|
||||||
|
pre_key = pre_key(e, string(key^)),
|
||||||
|
key = key,
|
||||||
|
val_idx = bucket_index,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
slice.sort_by_cmp(entries[:], proc(a, b: Encoded_Entry_Fast(^[]byte)) -> slice.Ordering {
|
||||||
|
a, b := a, b
|
||||||
|
pre_cmp := slice.Ordering(bytes.compare(a.pre_key[:a.pre_key[9]], b.pre_key[:b.pre_key[9]]))
|
||||||
|
if pre_cmp != .Equal {
|
||||||
|
return pre_cmp
|
||||||
|
}
|
||||||
|
|
||||||
|
return slice.Ordering(bytes.compare(a.key^, b.key^))
|
||||||
|
})
|
||||||
|
|
||||||
|
for &entry in entries {
|
||||||
|
io.write_full(e.writer, entry.pre_key[:entry.pre_key[9]]) or_return
|
||||||
|
io.write_full(e.writer, entry.key^) or_return
|
||||||
|
|
||||||
|
value := rawptr(runtime.map_cell_index_dynamic(vs, info.map_info.vs, entry.val_idx))
|
||||||
|
marshal_into(e, any{ value, info.value.id }) or_return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
case cstring:
|
||||||
|
entries := make([dynamic]Encoded_Entry_Fast(^cstring), 0, map_cap, e.temp_allocator) or_return
|
||||||
|
defer delete(entries)
|
||||||
|
|
||||||
|
for bucket_index in 0..<map_cap {
|
||||||
|
runtime.map_hash_is_valid(hs[bucket_index]) or_continue
|
||||||
|
|
||||||
|
key := (^cstring)(runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index))
|
||||||
|
append(&entries, Encoded_Entry_Fast(^cstring){
|
||||||
|
pre_key = pre_key(e, string(key^)),
|
||||||
|
key = key,
|
||||||
|
val_idx = bucket_index,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
slice.sort_by_cmp(entries[:], proc(a, b: Encoded_Entry_Fast(^cstring)) -> slice.Ordering {
|
||||||
|
a, b := a, b
|
||||||
|
pre_cmp := slice.Ordering(bytes.compare(a.pre_key[:a.pre_key[9]], b.pre_key[:b.pre_key[9]]))
|
||||||
|
if pre_cmp != .Equal {
|
||||||
|
return pre_cmp
|
||||||
|
}
|
||||||
|
|
||||||
|
ab := transmute([]byte)string(a.key^)
|
||||||
|
bb := transmute([]byte)string(b.key^)
|
||||||
|
return slice.Ordering(bytes.compare(ab, bb))
|
||||||
|
})
|
||||||
|
|
||||||
|
for &entry in entries {
|
||||||
|
io.write_full(e.writer, entry.pre_key[:entry.pre_key[9]]) or_return
|
||||||
|
io.write_full(e.writer, transmute([]byte)string(entry.key^)) or_return
|
||||||
|
|
||||||
|
value := rawptr(runtime.map_cell_index_dynamic(vs, info.map_info.vs, entry.val_idx))
|
||||||
|
marshal_into(e, any{ value, info.value.id }) or_return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
case:
|
||||||
|
entries := make([dynamic]Encoded_Entry, 0, map_cap, e.temp_allocator) or_return
|
||||||
|
defer delete(entries)
|
||||||
|
|
||||||
|
for bucket_index in 0..<map_cap {
|
||||||
|
runtime.map_hash_is_valid(hs[bucket_index]) or_continue
|
||||||
|
|
||||||
|
key := rawptr(runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index))
|
||||||
|
key_builder := strings.builder_make(0, 8, e.temp_allocator) or_return
|
||||||
|
marshal_into(Encoder{e.flags, strings.to_stream(&key_builder), e.temp_allocator}, any{ key, info.key.id }) or_return
|
||||||
|
append(&entries, Encoded_Entry{ &key_builder.buf, bucket_index }) or_return
|
||||||
|
}
|
||||||
|
|
||||||
|
slice.sort_by_cmp(entries[:], proc(a, b: Encoded_Entry) -> slice.Ordering {
|
||||||
|
return slice.Ordering(bytes.compare(a.key[:], b.key[:]))
|
||||||
|
})
|
||||||
|
|
||||||
|
for entry in entries {
|
||||||
|
io.write_full(e.writer, entry.key[:]) or_return
|
||||||
|
delete(entry.key^)
|
||||||
|
|
||||||
|
value := rawptr(runtime.map_cell_index_dynamic(vs, info.map_info.vs, entry.val_idx))
|
||||||
|
marshal_into(e, any{ value, info.value.id }) or_return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case runtime.Type_Info_Struct:
|
||||||
|
switch vv in v {
|
||||||
|
case Tag: return err_conv(_encode_tag(e, vv))
|
||||||
|
}
|
||||||
|
|
||||||
|
field_name :: #force_inline proc(info: runtime.Type_Info_Struct, i: int) -> string {
|
||||||
|
if cbor_name := string(reflect.struct_tag_get(reflect.Struct_Tag(info.tags[i]), "cbor")); cbor_name != "" {
|
||||||
|
return cbor_name
|
||||||
|
} else {
|
||||||
|
return info.names[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
marshal_entry :: #force_inline proc(e: Encoder, info: runtime.Type_Info_Struct, v: any, name: string, i: int) -> Marshal_Error {
|
||||||
|
err_conv(_encode_text(e, name)) or_return
|
||||||
|
|
||||||
|
id := info.types[i].id
|
||||||
|
data := rawptr(uintptr(v.data) + info.offsets[i])
|
||||||
|
field_any := any{data, id}
|
||||||
|
|
||||||
|
if tag := string(reflect.struct_tag_get(reflect.Struct_Tag(info.tags[i]), "cbor_tag")); tag != "" {
|
||||||
|
if impl, ok := _tag_implementations_id[tag]; ok {
|
||||||
|
return impl->marshal(e, field_any)
|
||||||
|
}
|
||||||
|
|
||||||
|
nr, ok := strconv.parse_u64_of_base(tag, 10)
|
||||||
|
if !ok { return .Invalid_CBOR_Tag }
|
||||||
|
|
||||||
|
if impl, nok := _tag_implementations_nr[nr]; nok {
|
||||||
|
return impl->marshal(e, field_any)
|
||||||
|
}
|
||||||
|
|
||||||
|
err_conv(_encode_u64(e, nr, .Tag)) or_return
|
||||||
|
}
|
||||||
|
|
||||||
|
return marshal_into(e, field_any)
|
||||||
|
}
|
||||||
|
|
||||||
|
n: u64; {
|
||||||
|
for _, i in info.names {
|
||||||
|
if field_name(info, i) != "-" {
|
||||||
|
n += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err_conv(_encode_u64(e, n, .Map)) or_return
|
||||||
|
}
|
||||||
|
|
||||||
|
if .Deterministic_Map_Sorting in e.flags {
|
||||||
|
Name :: struct {
|
||||||
|
name: string,
|
||||||
|
field: int,
|
||||||
|
}
|
||||||
|
entries := make([dynamic]Name, 0, n, e.temp_allocator) or_return
|
||||||
|
defer delete(entries)
|
||||||
|
|
||||||
|
for _, i in info.names {
|
||||||
|
fname := field_name(info, i)
|
||||||
|
if fname == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
append(&entries, Name{fname, i}) or_return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort lexicographic on the bytes of the key.
|
||||||
|
slice.sort_by_cmp(entries[:], proc(a, b: Name) -> slice.Ordering {
|
||||||
|
return slice.Ordering(bytes.compare(transmute([]byte)a.name, transmute([]byte)b.name))
|
||||||
|
})
|
||||||
|
|
||||||
|
for entry in entries {
|
||||||
|
marshal_entry(e, info, v, entry.name, entry.field) or_return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, i in info.names {
|
||||||
|
fname := field_name(info, i)
|
||||||
|
if fname == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
marshal_entry(e, info, v, fname, i) or_return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
case runtime.Type_Info_Union:
|
||||||
|
switch vv in v {
|
||||||
|
case Value: return err_conv(encode(e, vv))
|
||||||
|
}
|
||||||
|
|
||||||
|
id := reflect.union_variant_typeid(v)
|
||||||
|
if v.data == nil || id == nil {
|
||||||
|
return _encode_nil(e.writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(info.variants) == 1 {
|
||||||
|
return marshal_into(e, any{v.data, id})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode a non-nil multi-variant union as the `TAG_OBJECT_TYPE`.
|
||||||
|
// Which is a tag of an array, where the first element is the textual id/type of the object
|
||||||
|
// that follows it.
|
||||||
|
|
||||||
|
err_conv(_encode_u16(e, TAG_OBJECT_TYPE, .Tag)) or_return
|
||||||
|
_encode_u8(e.writer, 2, .Array) or_return
|
||||||
|
|
||||||
|
vti := reflect.union_variant_type_info(v)
|
||||||
|
#partial switch vt in vti.variant {
|
||||||
|
case reflect.Type_Info_Named:
|
||||||
|
err_conv(_encode_text(e, vt.name)) or_return
|
||||||
|
case:
|
||||||
|
builder := strings.builder_make(e.temp_allocator) or_return
|
||||||
|
defer strings.builder_destroy(&builder)
|
||||||
|
reflect.write_type(&builder, vti)
|
||||||
|
err_conv(_encode_text(e, strings.to_string(builder))) or_return
|
||||||
|
}
|
||||||
|
|
||||||
|
return marshal_into(e, any{v.data, vti.id})
|
||||||
|
|
||||||
|
case runtime.Type_Info_Enum:
|
||||||
|
return marshal_into(e, any{v.data, info.base.id})
|
||||||
|
|
||||||
|
case runtime.Type_Info_Bit_Set:
|
||||||
|
// Store bit_set as big endian just like the protocol.
|
||||||
|
do_byte_swap := !reflect.bit_set_is_big_endian(v)
|
||||||
|
switch ti.size * 8 {
|
||||||
|
case 0:
|
||||||
|
return _encode_u8(e.writer, 0)
|
||||||
|
case 8:
|
||||||
|
x := (^u8)(v.data)^
|
||||||
|
return _encode_u8(e.writer, x)
|
||||||
|
case 16:
|
||||||
|
x := (^u16)(v.data)^
|
||||||
|
if do_byte_swap { x = intrinsics.byte_swap(x) }
|
||||||
|
return err_conv(_encode_u16(e, x))
|
||||||
|
case 32:
|
||||||
|
x := (^u32)(v.data)^
|
||||||
|
if do_byte_swap { x = intrinsics.byte_swap(x) }
|
||||||
|
return err_conv(_encode_u32(e, x))
|
||||||
|
case 64:
|
||||||
|
x := (^u64)(v.data)^
|
||||||
|
if do_byte_swap { x = intrinsics.byte_swap(x) }
|
||||||
|
return err_conv(_encode_u64(e, x))
|
||||||
|
case:
|
||||||
|
panic("unknown bit_size size")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _unsupported(v.id, nil)
|
||||||
|
}
|
||||||
@@ -0,0 +1,381 @@
|
|||||||
|
package encoding_cbor
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
|
|
||||||
|
import "core:encoding/base64"
|
||||||
|
import "core:io"
|
||||||
|
import "core:math"
|
||||||
|
import "core:math/big"
|
||||||
|
import "core:mem"
|
||||||
|
import "core:reflect"
|
||||||
|
import "core:strings"
|
||||||
|
import "core:time"
|
||||||
|
|
||||||
|
// Tags defined in RFC 7049 that we provide implementations for.
|
||||||
|
|
||||||
|
// UTC time in seconds, unmarshalled into a `core:time` `time.Time` or integer.
|
||||||
|
// Use the struct tag `cbor_tag:"1"` or `cbor_tag:"epoch"` to have your `time.Time` field en/decoded as epoch time.
|
||||||
|
TAG_EPOCH_TIME_NR :: 1
|
||||||
|
TAG_EPOCH_TIME_ID :: "epoch"
|
||||||
|
|
||||||
|
// Using `core:math/big`, big integers are properly encoded and decoded during marshal and unmarshal.
|
||||||
|
// These fields use this tag by default, no struct tag required.
|
||||||
|
TAG_UNSIGNED_BIG_NR :: 2
|
||||||
|
// Using `core:math/big`, big integers are properly encoded and decoded during marshal and unmarshal.
|
||||||
|
// These fields use this tag by default, no struct tag required.
|
||||||
|
TAG_NEGATIVE_BIG_NR :: 3
|
||||||
|
|
||||||
|
// TAG_DECIMAL_FRACTION :: 4 // NOTE: We could probably implement this with `math/fixed`.
|
||||||
|
|
||||||
|
// Sometimes it is beneficial to carry an embedded CBOR data item that is not meant to be decoded
|
||||||
|
// immediately at the time the enclosing data item is being decoded. Tag number 24 (CBOR data item)
|
||||||
|
// can be used to tag the embedded byte string as a single data item encoded in CBOR format.
|
||||||
|
// Use the struct tag `cbor_tag:"24"` or `cbor_tag:"cbor"` to keep a non-decoded field (string or bytes) of raw CBOR.
|
||||||
|
TAG_CBOR_NR :: 24
|
||||||
|
TAG_CBOR_ID :: "cbor"
|
||||||
|
|
||||||
|
// The contents of this tag are base64 encoded during marshal and decoded during unmarshal.
|
||||||
|
// Use the struct tag `cbor_tag:"34"` or `cbor_tag:"base64"` to have your field string or bytes field en/decoded as base64.
|
||||||
|
TAG_BASE64_NR :: 34
|
||||||
|
TAG_BASE64_ID :: "base64"
|
||||||
|
|
||||||
|
// A tag that is used to detect the contents of a binary buffer (like a file) are CBOR.
|
||||||
|
// This tag would wrap everything else, decoders can then check for this header and see if the
|
||||||
|
// given content is definitely CBOR.
|
||||||
|
// Added by the encoder if it has the flag `.Self_Described_CBOR`, decoded by default.
|
||||||
|
TAG_SELF_DESCRIBED_CBOR :: 55799
|
||||||
|
|
||||||
|
// A tag that is used to assign a textual type to the object following it.
|
||||||
|
// The tag's value must be an array of 2 items, where the first is text (describing the following type)
|
||||||
|
// and the second is any valid CBOR value.
|
||||||
|
//
|
||||||
|
// See the registration: https://datatracker.ietf.org/doc/draft-rundgren-cotx/05/
|
||||||
|
//
|
||||||
|
// We use this in Odin to marshal and unmarshal unions.
|
||||||
|
TAG_OBJECT_TYPE :: 1010
|
||||||
|
|
||||||
|
// A tag implementation that handles marshals and unmarshals for the tag it is registered on.
|
||||||
|
Tag_Implementation :: struct {
|
||||||
|
data: rawptr,
|
||||||
|
unmarshal: Tag_Unmarshal_Proc,
|
||||||
|
marshal: Tag_Marshal_Proc,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Procedure responsible for umarshalling the tag out of the reader into the given `any`.
|
||||||
|
Tag_Unmarshal_Proc :: #type proc(self: ^Tag_Implementation, d: Decoder, tag_nr: Tag_Number, v: any) -> Unmarshal_Error
|
||||||
|
|
||||||
|
// Procedure responsible for marshalling the tag in the given `any` into the given encoder.
|
||||||
|
Tag_Marshal_Proc :: #type proc(self: ^Tag_Implementation, e: Encoder, v: any) -> Marshal_Error
|
||||||
|
|
||||||
|
// When encountering a tag in the CBOR being unmarshalled, the implementation is used to unmarshal it.
|
||||||
|
// When encountering a struct tag like `cbor_tag:"Tag_Number"`, the implementation is used to marshal it.
|
||||||
|
_tag_implementations_nr: map[Tag_Number]Tag_Implementation
|
||||||
|
|
||||||
|
// Same as the number implementations but friendlier to use as a struct tag.
|
||||||
|
// Instead of `cbor_tag:"34"` you can use `cbor_tag:"base64"`.
|
||||||
|
_tag_implementations_id: map[string]Tag_Implementation
|
||||||
|
|
||||||
|
// Tag implementations that are always used by a type, if that type is encountered in marshal it
|
||||||
|
// will rely on the implementation to marshal it.
|
||||||
|
//
|
||||||
|
// This is good for types that don't make sense or can't marshal in its default form.
|
||||||
|
_tag_implementations_type: map[typeid]Tag_Implementation
|
||||||
|
|
||||||
|
// Register a custom tag implementation to be used when marshalling that type and unmarshalling that tag number.
|
||||||
|
tag_register_type :: proc(impl: Tag_Implementation, nr: Tag_Number, type: typeid) {
|
||||||
|
_tag_implementations_nr[nr] = impl
|
||||||
|
_tag_implementations_type[type] = impl
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register a custom tag implementation to be used when marshalling that tag number or marshalling
|
||||||
|
// a field with the struct tag `cbor_tag:"nr"`.
|
||||||
|
tag_register_number :: proc(impl: Tag_Implementation, nr: Tag_Number, id: string) {
|
||||||
|
_tag_implementations_nr[nr] = impl
|
||||||
|
_tag_implementations_id[id] = impl
|
||||||
|
}
|
||||||
|
|
||||||
|
// Controls initialization of default tag implementations.
|
||||||
|
// JS and WASI default to a panic allocator so we don't want to do it on those.
|
||||||
|
INITIALIZE_DEFAULT_TAGS :: #config(CBOR_INITIALIZE_DEFAULT_TAGS, !ODIN_DEFAULT_TO_PANIC_ALLOCATOR && !ODIN_DEFAULT_TO_NIL_ALLOCATOR)
|
||||||
|
|
||||||
|
@(private, init, disabled=!INITIALIZE_DEFAULT_TAGS)
|
||||||
|
tags_initialize_defaults :: proc() {
|
||||||
|
tags_register_defaults()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registers tags that have implementations provided by this package.
|
||||||
|
// This is done by default and can be controlled with the `CBOR_INITIALIZE_DEFAULT_TAGS` define.
|
||||||
|
tags_register_defaults :: proc() {
|
||||||
|
tag_register_number({nil, tag_time_unmarshal, tag_time_marshal}, TAG_EPOCH_TIME_NR, TAG_EPOCH_TIME_ID)
|
||||||
|
tag_register_number({nil, tag_base64_unmarshal, tag_base64_marshal}, TAG_BASE64_NR, TAG_BASE64_ID)
|
||||||
|
tag_register_number({nil, tag_cbor_unmarshal, tag_cbor_marshal}, TAG_CBOR_NR, TAG_CBOR_ID)
|
||||||
|
|
||||||
|
// These following tags are registered at the type level and don't require an opt-in struct tag.
|
||||||
|
// Encoding these types on its own make no sense or no data is lost to encode it.
|
||||||
|
|
||||||
|
// En/Decoding of `big.Int` fields by default.
|
||||||
|
tag_register_type({nil, tag_big_unmarshal, tag_big_marshal}, TAG_UNSIGNED_BIG_NR, big.Int)
|
||||||
|
tag_register_type({nil, tag_big_unmarshal, tag_big_marshal}, TAG_NEGATIVE_BIG_NR, big.Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tag number 1 contains a numerical value counting the number of seconds from 1970-01-01T00:00Z
|
||||||
|
// in UTC time to the represented point in civil time.
|
||||||
|
//
|
||||||
|
// See RFC 8949 section 3.4.2.
|
||||||
|
@(private)
|
||||||
|
tag_time_unmarshal :: proc(_: ^Tag_Implementation, d: Decoder, _: Tag_Number, v: any) -> (err: Unmarshal_Error) {
|
||||||
|
hdr := _decode_header(d.reader) or_return
|
||||||
|
#partial switch hdr {
|
||||||
|
case .U8, .U16, .U32, .U64, .Neg_U8, .Neg_U16, .Neg_U32, .Neg_U64:
|
||||||
|
switch &dst in v {
|
||||||
|
case time.Time:
|
||||||
|
i: i64
|
||||||
|
_unmarshal_any_ptr(d, &i, hdr) or_return
|
||||||
|
dst = time.unix(i64(i), 0)
|
||||||
|
return
|
||||||
|
case:
|
||||||
|
return _unmarshal_value(d, v, hdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
case .F16, .F32, .F64:
|
||||||
|
switch &dst in v {
|
||||||
|
case time.Time:
|
||||||
|
f: f64
|
||||||
|
_unmarshal_any_ptr(d, &f, hdr) or_return
|
||||||
|
whole, fract := math.modf(f)
|
||||||
|
dst = time.unix(i64(whole), i64(fract * 1e9))
|
||||||
|
return
|
||||||
|
case:
|
||||||
|
return _unmarshal_value(d, v, hdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
case:
|
||||||
|
maj, add := _header_split(hdr)
|
||||||
|
if maj == .Other {
|
||||||
|
i := _decode_tiny_u8(add) or_return
|
||||||
|
|
||||||
|
switch &dst in v {
|
||||||
|
case time.Time:
|
||||||
|
dst = time.unix(i64(i), 0)
|
||||||
|
case:
|
||||||
|
if _assign_int(v, i) { return }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only numbers and floats are allowed in this tag.
|
||||||
|
return .Bad_Tag_Value
|
||||||
|
}
|
||||||
|
|
||||||
|
return _unsupported(v, hdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
tag_time_marshal :: proc(_: ^Tag_Implementation, e: Encoder, v: any) -> Marshal_Error {
|
||||||
|
switch vv in v {
|
||||||
|
case time.Time:
|
||||||
|
// NOTE: we lose precision here, which is one of the reasons for this tag being opt-in.
|
||||||
|
i := time.time_to_unix(vv)
|
||||||
|
|
||||||
|
_encode_u8(e.writer, TAG_EPOCH_TIME_NR, .Tag) or_return
|
||||||
|
return err_conv(_encode_uint(e, _int_to_uint(i)))
|
||||||
|
case:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
tag_big_unmarshal :: proc(_: ^Tag_Implementation, d: Decoder, tnr: Tag_Number, v: any) -> (err: Unmarshal_Error) {
|
||||||
|
hdr := _decode_header(d.reader) or_return
|
||||||
|
maj, add := _header_split(hdr)
|
||||||
|
if maj != .Bytes {
|
||||||
|
// Only bytes are supported in this tag.
|
||||||
|
return .Bad_Tag_Value
|
||||||
|
}
|
||||||
|
|
||||||
|
switch &dst in v {
|
||||||
|
case big.Int:
|
||||||
|
bytes := err_conv(_decode_bytes(d, add)) or_return
|
||||||
|
defer delete(bytes)
|
||||||
|
|
||||||
|
if err := big.int_from_bytes_big(&dst, bytes); err != nil {
|
||||||
|
return .Bad_Tag_Value
|
||||||
|
}
|
||||||
|
|
||||||
|
if tnr == TAG_NEGATIVE_BIG_NR {
|
||||||
|
dst.sign = .Negative
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return _unsupported(v, hdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
tag_big_marshal :: proc(_: ^Tag_Implementation, e: Encoder, v: any) -> Marshal_Error {
|
||||||
|
switch &vv in v {
|
||||||
|
case big.Int:
|
||||||
|
if !big.int_is_initialized(&vv) {
|
||||||
|
_encode_u8(e.writer, TAG_UNSIGNED_BIG_NR, .Tag) or_return
|
||||||
|
return _encode_u8(e.writer, 0, .Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: using the panic_allocator because all procedures should only allocate if the Int
|
||||||
|
// is uninitialized (which we checked).
|
||||||
|
|
||||||
|
is_neg, err := big.is_negative(&vv, mem.panic_allocator())
|
||||||
|
assert(err == nil, "should only error if not initialized, which has been checked")
|
||||||
|
|
||||||
|
tnr: u8 = TAG_NEGATIVE_BIG_NR if is_neg else TAG_UNSIGNED_BIG_NR
|
||||||
|
_encode_u8(e.writer, tnr, .Tag) or_return
|
||||||
|
|
||||||
|
size_in_bytes, berr := big.int_to_bytes_size(&vv, false, mem.panic_allocator())
|
||||||
|
assert(berr == nil, "should only error if not initialized, which has been checked")
|
||||||
|
assert(size_in_bytes >= 0)
|
||||||
|
|
||||||
|
err_conv(_encode_u64(e, u64(size_in_bytes), .Bytes)) or_return
|
||||||
|
|
||||||
|
for offset := (size_in_bytes*8)-8; offset >= 0; offset -= 8 {
|
||||||
|
bits, derr := big.int_bitfield_extract(&vv, offset, 8, mem.panic_allocator())
|
||||||
|
assert(derr == nil, "should only error if not initialized or invalid argument (offset and count), which won't happen")
|
||||||
|
|
||||||
|
io.write_full(e.writer, {u8(bits & 255)}) or_return
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case: unreachable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
tag_cbor_unmarshal :: proc(_: ^Tag_Implementation, d: Decoder, _: Tag_Number, v: any) -> Unmarshal_Error {
|
||||||
|
hdr := _decode_header(d.reader) or_return
|
||||||
|
major, add := _header_split(hdr)
|
||||||
|
#partial switch major {
|
||||||
|
case .Bytes:
|
||||||
|
ti := reflect.type_info_base(type_info_of(v.id))
|
||||||
|
return _unmarshal_bytes(d, v, ti, hdr, add)
|
||||||
|
|
||||||
|
case: return .Bad_Tag_Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
tag_cbor_marshal :: proc(_: ^Tag_Implementation, e: Encoder, v: any) -> Marshal_Error {
|
||||||
|
_encode_u8(e.writer, TAG_CBOR_NR, .Tag) or_return
|
||||||
|
ti := runtime.type_info_base(type_info_of(v.id))
|
||||||
|
#partial switch t in ti.variant {
|
||||||
|
case runtime.Type_Info_String:
|
||||||
|
return marshal_into(e, v)
|
||||||
|
case runtime.Type_Info_Array:
|
||||||
|
elem_base := reflect.type_info_base(t.elem)
|
||||||
|
if elem_base.id != byte { return .Bad_Tag_Value }
|
||||||
|
return marshal_into(e, v)
|
||||||
|
case runtime.Type_Info_Slice:
|
||||||
|
elem_base := reflect.type_info_base(t.elem)
|
||||||
|
if elem_base.id != byte { return .Bad_Tag_Value }
|
||||||
|
return marshal_into(e, v)
|
||||||
|
case runtime.Type_Info_Dynamic_Array:
|
||||||
|
elem_base := reflect.type_info_base(t.elem)
|
||||||
|
if elem_base.id != byte { return .Bad_Tag_Value }
|
||||||
|
return marshal_into(e, v)
|
||||||
|
case:
|
||||||
|
return .Bad_Tag_Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
tag_base64_unmarshal :: proc(_: ^Tag_Implementation, d: Decoder, _: Tag_Number, v: any) -> (err: Unmarshal_Error) {
|
||||||
|
hdr := _decode_header(d.reader) or_return
|
||||||
|
major, add := _header_split(hdr)
|
||||||
|
ti := reflect.type_info_base(type_info_of(v.id))
|
||||||
|
|
||||||
|
if major != .Text && major != .Bytes {
|
||||||
|
return .Bad_Tag_Value
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes := string(err_conv(_decode_bytes(d, add, allocator=context.temp_allocator)) or_return)
|
||||||
|
defer delete(bytes, context.temp_allocator)
|
||||||
|
|
||||||
|
#partial switch t in ti.variant {
|
||||||
|
case reflect.Type_Info_String:
|
||||||
|
|
||||||
|
if t.is_cstring {
|
||||||
|
length := base64.decoded_len(bytes)
|
||||||
|
builder := strings.builder_make(0, length+1)
|
||||||
|
base64.decode_into(strings.to_stream(&builder), bytes) or_return
|
||||||
|
|
||||||
|
raw := (^cstring)(v.data)
|
||||||
|
raw^ = cstring(raw_data(builder.buf))
|
||||||
|
} else {
|
||||||
|
raw := (^string)(v.data)
|
||||||
|
raw^ = string(base64.decode(bytes) or_return)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
case reflect.Type_Info_Slice:
|
||||||
|
elem_base := reflect.type_info_base(t.elem)
|
||||||
|
|
||||||
|
if elem_base.id != byte { return _unsupported(v, hdr) }
|
||||||
|
|
||||||
|
raw := (^[]byte)(v.data)
|
||||||
|
raw^ = base64.decode(bytes) or_return
|
||||||
|
return
|
||||||
|
|
||||||
|
case reflect.Type_Info_Dynamic_Array:
|
||||||
|
elem_base := reflect.type_info_base(t.elem)
|
||||||
|
|
||||||
|
if elem_base.id != byte { return _unsupported(v, hdr) }
|
||||||
|
|
||||||
|
decoded := base64.decode(bytes) or_return
|
||||||
|
|
||||||
|
raw := (^mem.Raw_Dynamic_Array)(v.data)
|
||||||
|
raw.data = raw_data(decoded)
|
||||||
|
raw.len = len(decoded)
|
||||||
|
raw.cap = len(decoded)
|
||||||
|
raw.allocator = context.allocator
|
||||||
|
return
|
||||||
|
|
||||||
|
case reflect.Type_Info_Array:
|
||||||
|
elem_base := reflect.type_info_base(t.elem)
|
||||||
|
|
||||||
|
if elem_base.id != byte { return _unsupported(v, hdr) }
|
||||||
|
|
||||||
|
if base64.decoded_len(bytes) > t.count { return _unsupported(v, hdr) }
|
||||||
|
|
||||||
|
slice := ([^]byte)(v.data)[:len(bytes)]
|
||||||
|
copy(slice, base64.decode(bytes) or_return)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return _unsupported(v, hdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
tag_base64_marshal :: proc(_: ^Tag_Implementation, e: Encoder, v: any) -> Marshal_Error {
|
||||||
|
_encode_u8(e.writer, TAG_BASE64_NR, .Tag) or_return
|
||||||
|
|
||||||
|
ti := runtime.type_info_base(type_info_of(v.id))
|
||||||
|
a := any{v.data, ti.id}
|
||||||
|
|
||||||
|
bytes: []byte
|
||||||
|
switch val in a {
|
||||||
|
case string: bytes = transmute([]byte)val
|
||||||
|
case cstring: bytes = transmute([]byte)string(val)
|
||||||
|
case []byte: bytes = val
|
||||||
|
case [dynamic]byte: bytes = val[:]
|
||||||
|
case:
|
||||||
|
#partial switch t in ti.variant {
|
||||||
|
case runtime.Type_Info_Array:
|
||||||
|
if t.elem.id != byte { return .Bad_Tag_Value }
|
||||||
|
bytes = ([^]byte)(v.data)[:t.count]
|
||||||
|
case:
|
||||||
|
return .Bad_Tag_Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out_len := base64.encoded_len(bytes)
|
||||||
|
err_conv(_encode_u64(e, u64(out_len), .Text)) or_return
|
||||||
|
return base64.encode_into(e.writer, bytes)
|
||||||
|
}
|
||||||
@@ -0,0 +1,932 @@
|
|||||||
|
package encoding_cbor
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
import "base:runtime"
|
||||||
|
|
||||||
|
import "core:io"
|
||||||
|
import "core:mem"
|
||||||
|
import "core:reflect"
|
||||||
|
import "core:strings"
|
||||||
|
import "core:unicode/utf8"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Unmarshals the given CBOR into the given pointer using reflection.
|
||||||
|
Types that require allocation are allocated using the given allocator.
|
||||||
|
|
||||||
|
Some temporary allocations are done on the given `temp_allocator`, but, if you want to,
|
||||||
|
this can be set to a "normal" allocator, because the necessary `delete` and `free` calls are still made.
|
||||||
|
This is helpful when the CBOR size is so big that you don't want to collect all the temporary allocations until the end.
|
||||||
|
|
||||||
|
Disable streaming/indeterminate lengths with the `.Disallow_Streaming` flag.
|
||||||
|
|
||||||
|
Shrink excess bytes in buffers and containers with the `.Shrink_Excess` flag.
|
||||||
|
|
||||||
|
Mark the input as trusted input with the `.Trusted_Input` flag, this turns off the safety feature
|
||||||
|
of not pre-allocating more than `max_pre_alloc` bytes before reading into the bytes. You should only
|
||||||
|
do this when you own both sides of the encoding and are sure there can't be malicious bytes used as
|
||||||
|
an input.
|
||||||
|
*/
|
||||||
|
unmarshal :: proc {
|
||||||
|
unmarshal_from_reader,
|
||||||
|
unmarshal_from_string,
|
||||||
|
}
|
||||||
|
|
||||||
|
unmarshal_from_reader :: proc(r: io.Reader, ptr: ^$T, flags := Decoder_Flags{}, allocator := context.allocator, temp_allocator := context.temp_allocator) -> (err: Unmarshal_Error) {
|
||||||
|
err = unmarshal_from_decoder(Decoder{ DEFAULT_MAX_PRE_ALLOC, flags, r }, ptr, allocator, temp_allocator)
|
||||||
|
|
||||||
|
// Normal EOF does not exist here, we try to read the exact amount that is said to be provided.
|
||||||
|
if err == .EOF { err = .Unexpected_EOF }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshals from a string, see docs on the proc group `Unmarshal` for more info.
|
||||||
|
unmarshal_from_string :: proc(s: string, ptr: ^$T, flags := Decoder_Flags{}, allocator := context.allocator, temp_allocator := context.temp_allocator) -> (err: Unmarshal_Error) {
|
||||||
|
sr: strings.Reader
|
||||||
|
r := strings.to_reader(&sr, s)
|
||||||
|
|
||||||
|
err = unmarshal_from_reader(r, ptr, flags, allocator, temp_allocator)
|
||||||
|
|
||||||
|
// Normal EOF does not exist here, we try to read the exact amount that is said to be provided.
|
||||||
|
if err == .EOF { err = .Unexpected_EOF }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
unmarshal_from_decoder :: proc(d: Decoder, ptr: ^$T, allocator := context.allocator, temp_allocator := context.temp_allocator) -> (err: Unmarshal_Error) {
|
||||||
|
d := d
|
||||||
|
|
||||||
|
err = _unmarshal_any_ptr(d, ptr, nil, allocator, temp_allocator)
|
||||||
|
|
||||||
|
// Normal EOF does not exist here, we try to read the exact amount that is said to be provided.
|
||||||
|
if err == .EOF { err = .Unexpected_EOF }
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_unmarshal_any_ptr :: proc(d: Decoder, v: any, hdr: Maybe(Header) = nil, allocator := context.allocator, temp_allocator := context.temp_allocator) -> Unmarshal_Error {
|
||||||
|
context.allocator = allocator
|
||||||
|
context.temp_allocator = temp_allocator
|
||||||
|
v := v
|
||||||
|
|
||||||
|
if v == nil || v.id == nil {
|
||||||
|
return .Invalid_Parameter
|
||||||
|
}
|
||||||
|
|
||||||
|
v = reflect.any_base(v)
|
||||||
|
ti := type_info_of(v.id)
|
||||||
|
if !reflect.is_pointer(ti) || ti.id == rawptr {
|
||||||
|
return .Non_Pointer_Parameter
|
||||||
|
}
|
||||||
|
|
||||||
|
data := any{(^rawptr)(v.data)^, ti.variant.(reflect.Type_Info_Pointer).elem.id}
|
||||||
|
return _unmarshal_value(d, data, hdr.? or_else (_decode_header(d.reader) or_return))
|
||||||
|
}
|
||||||
|
|
||||||
|
_unmarshal_value :: proc(d: Decoder, v: any, hdr: Header) -> (err: Unmarshal_Error) {
|
||||||
|
v := v
|
||||||
|
ti := reflect.type_info_base(type_info_of(v.id))
|
||||||
|
r := d.reader
|
||||||
|
|
||||||
|
// If it's a union with only one variant, then treat it as that variant
|
||||||
|
if u, ok := ti.variant.(reflect.Type_Info_Union); ok && len(u.variants) == 1 {
|
||||||
|
#partial switch hdr {
|
||||||
|
case .Nil, .Undefined, nil: // no-op.
|
||||||
|
case:
|
||||||
|
variant := u.variants[0]
|
||||||
|
v.id = variant.id
|
||||||
|
ti = reflect.type_info_base(variant)
|
||||||
|
if !reflect.is_pointer_internally(variant) {
|
||||||
|
tag := any{rawptr(uintptr(v.data) + u.tag_offset), u.tag_type.id}
|
||||||
|
assert(_assign_int(tag, 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow generic unmarshal by doing it into a `Value`.
|
||||||
|
switch &dst in v {
|
||||||
|
case Value:
|
||||||
|
dst = err_conv(_decode_from_decoder(d, hdr)) or_return
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch hdr {
|
||||||
|
case .U8:
|
||||||
|
decoded := _decode_u8(r) or_return
|
||||||
|
if !_assign_int(v, decoded) { return _unsupported(v, hdr) }
|
||||||
|
return
|
||||||
|
|
||||||
|
case .U16:
|
||||||
|
decoded := _decode_u16(r) or_return
|
||||||
|
if !_assign_int(v, decoded) { return _unsupported(v, hdr) }
|
||||||
|
return
|
||||||
|
|
||||||
|
case .U32:
|
||||||
|
decoded := _decode_u32(r) or_return
|
||||||
|
if !_assign_int(v, decoded) { return _unsupported(v, hdr) }
|
||||||
|
return
|
||||||
|
|
||||||
|
case .U64:
|
||||||
|
decoded := _decode_u64(r) or_return
|
||||||
|
if !_assign_int(v, decoded) { return _unsupported(v, hdr) }
|
||||||
|
return
|
||||||
|
|
||||||
|
case .Neg_U8:
|
||||||
|
decoded := Negative_U8(_decode_u8(r) or_return)
|
||||||
|
|
||||||
|
switch &dst in v {
|
||||||
|
case Negative_U8:
|
||||||
|
dst = decoded
|
||||||
|
return
|
||||||
|
case Negative_U16:
|
||||||
|
dst = Negative_U16(decoded)
|
||||||
|
return
|
||||||
|
case Negative_U32:
|
||||||
|
dst = Negative_U32(decoded)
|
||||||
|
return
|
||||||
|
case Negative_U64:
|
||||||
|
dst = Negative_U64(decoded)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if reflect.is_unsigned(ti) { return _unsupported(v, hdr) }
|
||||||
|
|
||||||
|
if !_assign_int(v, negative_to_int(decoded)) { return _unsupported(v, hdr) }
|
||||||
|
return
|
||||||
|
|
||||||
|
case .Neg_U16:
|
||||||
|
decoded := Negative_U16(_decode_u16(r) or_return)
|
||||||
|
|
||||||
|
switch &dst in v {
|
||||||
|
case Negative_U16:
|
||||||
|
dst = decoded
|
||||||
|
return
|
||||||
|
case Negative_U32:
|
||||||
|
dst = Negative_U32(decoded)
|
||||||
|
return
|
||||||
|
case Negative_U64:
|
||||||
|
dst = Negative_U64(decoded)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if reflect.is_unsigned(ti) { return _unsupported(v, hdr) }
|
||||||
|
|
||||||
|
if !_assign_int(v, negative_to_int(decoded)) { return _unsupported(v, hdr) }
|
||||||
|
return
|
||||||
|
|
||||||
|
case .Neg_U32:
|
||||||
|
decoded := Negative_U32(_decode_u32(r) or_return)
|
||||||
|
|
||||||
|
switch &dst in v {
|
||||||
|
case Negative_U32:
|
||||||
|
dst = decoded
|
||||||
|
return
|
||||||
|
case Negative_U64:
|
||||||
|
dst = Negative_U64(decoded)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if reflect.is_unsigned(ti) { return _unsupported(v, hdr) }
|
||||||
|
|
||||||
|
if !_assign_int(v, negative_to_int(decoded)) { return _unsupported(v, hdr) }
|
||||||
|
return
|
||||||
|
|
||||||
|
case .Neg_U64:
|
||||||
|
decoded := Negative_U64(_decode_u64(r) or_return)
|
||||||
|
|
||||||
|
switch &dst in v {
|
||||||
|
case Negative_U64:
|
||||||
|
dst = decoded
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if reflect.is_unsigned(ti) { return _unsupported(v, hdr) }
|
||||||
|
|
||||||
|
if !_assign_int(v, negative_to_int(decoded)) { return _unsupported(v, hdr) }
|
||||||
|
return
|
||||||
|
|
||||||
|
case .Simple:
|
||||||
|
decoded := _decode_simple(r) or_return
|
||||||
|
|
||||||
|
// NOTE: Because this is a special type and not to be treated as a general integer,
|
||||||
|
// We only put the value of it in fields that are explicitly of type `Simple`.
|
||||||
|
switch &dst in v {
|
||||||
|
case Simple:
|
||||||
|
dst = decoded
|
||||||
|
return
|
||||||
|
case:
|
||||||
|
return _unsupported(v, hdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
case .F16:
|
||||||
|
decoded := _decode_f16(r) or_return
|
||||||
|
if !_assign_float(v, decoded) { return _unsupported(v, hdr) }
|
||||||
|
return
|
||||||
|
|
||||||
|
case .F32:
|
||||||
|
decoded := _decode_f32(r) or_return
|
||||||
|
if !_assign_float(v, decoded) { return _unsupported(v, hdr) }
|
||||||
|
return
|
||||||
|
|
||||||
|
case .F64:
|
||||||
|
decoded := _decode_f64(r) or_return
|
||||||
|
if !_assign_float(v, decoded) { return _unsupported(v, hdr) }
|
||||||
|
return
|
||||||
|
|
||||||
|
case .True:
|
||||||
|
if !_assign_bool(v, true) { return _unsupported(v, hdr) }
|
||||||
|
return
|
||||||
|
|
||||||
|
case .False:
|
||||||
|
if !_assign_bool(v, false) { return _unsupported(v, hdr) }
|
||||||
|
return
|
||||||
|
|
||||||
|
case .Nil, .Undefined:
|
||||||
|
mem.zero(v.data, ti.size)
|
||||||
|
return
|
||||||
|
|
||||||
|
case .Break:
|
||||||
|
return .Break
|
||||||
|
}
|
||||||
|
|
||||||
|
maj, add := _header_split(hdr)
|
||||||
|
switch maj {
|
||||||
|
case .Unsigned:
|
||||||
|
decoded := _decode_tiny_u8(add) or_return
|
||||||
|
if !_assign_int(v, decoded) { return _unsupported(v, hdr, add) }
|
||||||
|
return
|
||||||
|
|
||||||
|
case .Negative:
|
||||||
|
decoded := Negative_U8(_decode_tiny_u8(add) or_return)
|
||||||
|
|
||||||
|
switch &dst in v {
|
||||||
|
case Negative_U8:
|
||||||
|
dst = decoded
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if reflect.is_unsigned(ti) { return _unsupported(v, hdr, add) }
|
||||||
|
|
||||||
|
if !_assign_int(v, negative_to_int(decoded)) { return _unsupported(v, hdr, add) }
|
||||||
|
return
|
||||||
|
|
||||||
|
case .Other:
|
||||||
|
decoded := _decode_tiny_simple(add) or_return
|
||||||
|
|
||||||
|
// NOTE: Because this is a special type and not to be treated as a general integer,
|
||||||
|
// We only put the value of it in fields that are explicitly of type `Simple`.
|
||||||
|
switch &dst in v {
|
||||||
|
case Simple:
|
||||||
|
dst = decoded
|
||||||
|
return
|
||||||
|
case:
|
||||||
|
return _unsupported(v, hdr, add)
|
||||||
|
}
|
||||||
|
|
||||||
|
case .Tag:
|
||||||
|
switch &dst in v {
|
||||||
|
case ^Tag:
|
||||||
|
tval := err_conv(_decode_tag_ptr(d, add)) or_return
|
||||||
|
if t, is_tag := tval.(^Tag); is_tag {
|
||||||
|
dst = t
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy(tval)
|
||||||
|
return .Bad_Tag_Value
|
||||||
|
case Tag:
|
||||||
|
t := err_conv(_decode_tag(d, add)) or_return
|
||||||
|
if t, is_tag := t.?; is_tag {
|
||||||
|
dst = t
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return .Bad_Tag_Value
|
||||||
|
}
|
||||||
|
|
||||||
|
nr := err_conv(_decode_uint_as_u64(r, add)) or_return
|
||||||
|
|
||||||
|
// Custom tag implementations.
|
||||||
|
if impl, ok := _tag_implementations_nr[nr]; ok {
|
||||||
|
return impl->unmarshal(d, nr, v)
|
||||||
|
} else if nr == TAG_OBJECT_TYPE {
|
||||||
|
return _unmarshal_union(d, v, ti, hdr)
|
||||||
|
} else {
|
||||||
|
// Discard the tag info and unmarshal as its value.
|
||||||
|
return _unmarshal_value(d, v, _decode_header(r) or_return)
|
||||||
|
}
|
||||||
|
|
||||||
|
return _unsupported(v, hdr, add)
|
||||||
|
|
||||||
|
case .Bytes: return _unmarshal_bytes(d, v, ti, hdr, add)
|
||||||
|
case .Text: return _unmarshal_string(d, v, ti, hdr, add)
|
||||||
|
case .Array: return _unmarshal_array(d, v, ti, hdr, add)
|
||||||
|
case .Map: return _unmarshal_map(d, v, ti, hdr, add)
|
||||||
|
|
||||||
|
case: return .Bad_Major
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_unmarshal_bytes :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add) -> (err: Unmarshal_Error) {
|
||||||
|
#partial switch t in ti.variant {
|
||||||
|
case reflect.Type_Info_String:
|
||||||
|
bytes := err_conv(_decode_bytes(d, add)) or_return
|
||||||
|
|
||||||
|
if t.is_cstring {
|
||||||
|
raw := (^cstring)(v.data)
|
||||||
|
assert_safe_for_cstring(string(bytes))
|
||||||
|
raw^ = cstring(raw_data(bytes))
|
||||||
|
} else {
|
||||||
|
// String has same memory layout as a slice, so we can directly use it as a slice.
|
||||||
|
raw := (^mem.Raw_String)(v.data)
|
||||||
|
raw^ = transmute(mem.Raw_String)bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
case reflect.Type_Info_Slice:
|
||||||
|
elem_base := reflect.type_info_base(t.elem)
|
||||||
|
|
||||||
|
if elem_base.id != byte { return _unsupported(v, hdr) }
|
||||||
|
|
||||||
|
bytes := err_conv(_decode_bytes(d, add)) or_return
|
||||||
|
raw := (^mem.Raw_Slice)(v.data)
|
||||||
|
raw^ = transmute(mem.Raw_Slice)bytes
|
||||||
|
return
|
||||||
|
|
||||||
|
case reflect.Type_Info_Dynamic_Array:
|
||||||
|
elem_base := reflect.type_info_base(t.elem)
|
||||||
|
|
||||||
|
if elem_base.id != byte { return _unsupported(v, hdr) }
|
||||||
|
|
||||||
|
bytes := err_conv(_decode_bytes(d, add)) or_return
|
||||||
|
raw := (^mem.Raw_Dynamic_Array)(v.data)
|
||||||
|
raw.data = raw_data(bytes)
|
||||||
|
raw.len = len(bytes)
|
||||||
|
raw.cap = len(bytes)
|
||||||
|
raw.allocator = context.allocator
|
||||||
|
return
|
||||||
|
|
||||||
|
case reflect.Type_Info_Array:
|
||||||
|
elem_base := reflect.type_info_base(t.elem)
|
||||||
|
|
||||||
|
if elem_base.id != byte { return _unsupported(v, hdr) }
|
||||||
|
|
||||||
|
bytes := err_conv(_decode_bytes(d, add, allocator=context.temp_allocator)) or_return
|
||||||
|
defer delete(bytes, context.temp_allocator)
|
||||||
|
|
||||||
|
if len(bytes) > t.count { return _unsupported(v, hdr) }
|
||||||
|
|
||||||
|
// Copy into array type, delete original.
|
||||||
|
slice := ([^]byte)(v.data)[:len(bytes)]
|
||||||
|
n := copy(slice, bytes)
|
||||||
|
assert(n == len(bytes))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return _unsupported(v, hdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
_unmarshal_string :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add) -> (err: Unmarshal_Error) {
|
||||||
|
#partial switch t in ti.variant {
|
||||||
|
case reflect.Type_Info_String:
|
||||||
|
text := err_conv(_decode_text(d, add)) or_return
|
||||||
|
|
||||||
|
if t.is_cstring {
|
||||||
|
raw := (^cstring)(v.data)
|
||||||
|
|
||||||
|
assert_safe_for_cstring(text)
|
||||||
|
raw^ = cstring(raw_data(text))
|
||||||
|
} else {
|
||||||
|
raw := (^string)(v.data)
|
||||||
|
raw^ = text
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
// Enum by its variant name.
|
||||||
|
case reflect.Type_Info_Enum:
|
||||||
|
text := err_conv(_decode_text(d, add, allocator=context.temp_allocator)) or_return
|
||||||
|
defer delete(text, context.temp_allocator)
|
||||||
|
|
||||||
|
for name, i in t.names {
|
||||||
|
if name == text {
|
||||||
|
if !_assign_int(any{v.data, ti.id}, t.values[i]) { return _unsupported(v, hdr) }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Type_Info_Rune:
|
||||||
|
text := err_conv(_decode_text(d, add, allocator=context.temp_allocator)) or_return
|
||||||
|
defer delete(text, context.temp_allocator)
|
||||||
|
|
||||||
|
r := (^rune)(v.data)
|
||||||
|
dr, n := utf8.decode_rune(text)
|
||||||
|
if dr == utf8.RUNE_ERROR || n < len(text) {
|
||||||
|
return _unsupported(v, hdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
r^ = dr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return _unsupported(v, hdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
_unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add) -> (err: Unmarshal_Error) {
|
||||||
|
assign_array :: proc(
|
||||||
|
d: Decoder,
|
||||||
|
da: ^mem.Raw_Dynamic_Array,
|
||||||
|
elemt: ^reflect.Type_Info,
|
||||||
|
length: int,
|
||||||
|
growable := true,
|
||||||
|
) -> (out_of_space: bool, err: Unmarshal_Error) {
|
||||||
|
for idx: uintptr = 0; length == -1 || idx < uintptr(length); idx += 1 {
|
||||||
|
elem_ptr := rawptr(uintptr(da.data) + idx*uintptr(elemt.size))
|
||||||
|
elem := any{elem_ptr, elemt.id}
|
||||||
|
|
||||||
|
hdr := _decode_header(d.reader) or_return
|
||||||
|
|
||||||
|
// Double size if out of capacity.
|
||||||
|
if da.cap <= da.len {
|
||||||
|
// Not growable, error out.
|
||||||
|
if !growable { return true, .Out_Of_Memory }
|
||||||
|
|
||||||
|
cap := 2 * da.cap
|
||||||
|
ok := runtime.__dynamic_array_reserve(da, elemt.size, elemt.align, cap)
|
||||||
|
|
||||||
|
// NOTE: Might be lying here, but it is at least an allocator error.
|
||||||
|
if !ok { return false, .Out_Of_Memory }
|
||||||
|
}
|
||||||
|
|
||||||
|
err = _unmarshal_value(d, elem, hdr)
|
||||||
|
if length == -1 && err == .Break { break }
|
||||||
|
if err != nil { return }
|
||||||
|
|
||||||
|
da.len += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow generically storing the values array.
|
||||||
|
switch &dst in v {
|
||||||
|
case ^Array:
|
||||||
|
dst = err_conv(_decode_array_ptr(d, add)) or_return
|
||||||
|
return
|
||||||
|
case Array:
|
||||||
|
dst = err_conv(_decode_array(d, add)) or_return
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
#partial switch t in ti.variant {
|
||||||
|
case reflect.Type_Info_Slice:
|
||||||
|
length, scap := err_conv(_decode_len_container(d, add)) or_return
|
||||||
|
|
||||||
|
data := mem.alloc_bytes_non_zeroed(t.elem.size * scap, t.elem.align) or_return
|
||||||
|
defer if err != nil { mem.free_bytes(data) }
|
||||||
|
|
||||||
|
da := mem.Raw_Dynamic_Array{raw_data(data), 0, length, context.allocator }
|
||||||
|
|
||||||
|
assign_array(d, &da, t.elem, length) or_return
|
||||||
|
|
||||||
|
if .Shrink_Excess in d.flags {
|
||||||
|
// Ignoring an error here, but this is not critical to succeed.
|
||||||
|
_ = runtime.__dynamic_array_shrink(&da, t.elem.size, t.elem.align, da.len)
|
||||||
|
}
|
||||||
|
|
||||||
|
raw := (^mem.Raw_Slice)(v.data)
|
||||||
|
raw.data = da.data
|
||||||
|
raw.len = da.len
|
||||||
|
return
|
||||||
|
|
||||||
|
case reflect.Type_Info_Dynamic_Array:
|
||||||
|
length, scap := err_conv(_decode_len_container(d, add)) or_return
|
||||||
|
|
||||||
|
data := mem.alloc_bytes_non_zeroed(t.elem.size * scap, t.elem.align) or_return
|
||||||
|
defer if err != nil { mem.free_bytes(data) }
|
||||||
|
|
||||||
|
raw := (^mem.Raw_Dynamic_Array)(v.data)
|
||||||
|
raw.data = raw_data(data)
|
||||||
|
raw.len = 0
|
||||||
|
raw.cap = length
|
||||||
|
raw.allocator = context.allocator
|
||||||
|
|
||||||
|
_ = assign_array(d, raw, t.elem, length) or_return
|
||||||
|
|
||||||
|
if .Shrink_Excess in d.flags {
|
||||||
|
// Ignoring an error here, but this is not critical to succeed.
|
||||||
|
_ = runtime.__dynamic_array_shrink(raw, t.elem.size, t.elem.align, raw.len)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
case reflect.Type_Info_Array:
|
||||||
|
_, scap := err_conv(_decode_len_container(d, add)) or_return
|
||||||
|
length := min(scap, t.count)
|
||||||
|
|
||||||
|
if length > t.count {
|
||||||
|
return _unsupported(v, hdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, length, context.allocator }
|
||||||
|
|
||||||
|
out_of_space := assign_array(d, &da, t.elem, length, growable=false) or_return
|
||||||
|
if out_of_space { return _unsupported(v, hdr) }
|
||||||
|
return
|
||||||
|
|
||||||
|
case reflect.Type_Info_Enumerated_Array:
|
||||||
|
_, scap := err_conv(_decode_len_container(d, add)) or_return
|
||||||
|
length := min(scap, t.count)
|
||||||
|
|
||||||
|
if length > t.count {
|
||||||
|
return _unsupported(v, hdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, length, context.allocator }
|
||||||
|
|
||||||
|
out_of_space := assign_array(d, &da, t.elem, length, growable=false) or_return
|
||||||
|
if out_of_space { return _unsupported(v, hdr) }
|
||||||
|
return
|
||||||
|
|
||||||
|
case reflect.Type_Info_Complex:
|
||||||
|
_, scap := err_conv(_decode_len_container(d, add)) or_return
|
||||||
|
length := min(scap, 2)
|
||||||
|
|
||||||
|
if length > 2 {
|
||||||
|
return _unsupported(v, hdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, 2, context.allocator }
|
||||||
|
|
||||||
|
info: ^runtime.Type_Info
|
||||||
|
switch ti.id {
|
||||||
|
case complex32: info = type_info_of(f16)
|
||||||
|
case complex64: info = type_info_of(f32)
|
||||||
|
case complex128: info = type_info_of(f64)
|
||||||
|
case: unreachable()
|
||||||
|
}
|
||||||
|
|
||||||
|
out_of_space := assign_array(d, &da, info, 2, growable=false) or_return
|
||||||
|
if out_of_space { return _unsupported(v, hdr) }
|
||||||
|
return
|
||||||
|
|
||||||
|
case reflect.Type_Info_Quaternion:
|
||||||
|
_, scap := err_conv(_decode_len_container(d, add)) or_return
|
||||||
|
length := min(scap, 4)
|
||||||
|
|
||||||
|
if length > 4 {
|
||||||
|
return _unsupported(v, hdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, 4, context.allocator }
|
||||||
|
|
||||||
|
info: ^runtime.Type_Info
|
||||||
|
switch ti.id {
|
||||||
|
case quaternion64: info = type_info_of(f16)
|
||||||
|
case quaternion128: info = type_info_of(f32)
|
||||||
|
case quaternion256: info = type_info_of(f64)
|
||||||
|
case: unreachable()
|
||||||
|
}
|
||||||
|
|
||||||
|
out_of_space := assign_array(d, &da, info, 4, growable=false) or_return
|
||||||
|
if out_of_space { return _unsupported(v, hdr) }
|
||||||
|
return
|
||||||
|
|
||||||
|
case: return _unsupported(v, hdr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add) -> (err: Unmarshal_Error) {
|
||||||
|
r := d.reader
|
||||||
|
decode_key :: proc(d: Decoder, v: any, allocator := context.allocator) -> (k: string, err: Unmarshal_Error) {
|
||||||
|
entry_hdr := _decode_header(d.reader) or_return
|
||||||
|
entry_maj, entry_add := _header_split(entry_hdr)
|
||||||
|
#partial switch entry_maj {
|
||||||
|
case .Text:
|
||||||
|
k = err_conv(_decode_text(d, entry_add, allocator)) or_return
|
||||||
|
return
|
||||||
|
case .Bytes:
|
||||||
|
bytes := err_conv(_decode_bytes(d, entry_add, allocator=allocator)) or_return
|
||||||
|
k = string(bytes)
|
||||||
|
return
|
||||||
|
case:
|
||||||
|
err = _unsupported(v, entry_hdr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow generically storing the map array.
|
||||||
|
switch &dst in v {
|
||||||
|
case ^Map:
|
||||||
|
dst = err_conv(_decode_map_ptr(d, add)) or_return
|
||||||
|
return
|
||||||
|
case Map:
|
||||||
|
dst = err_conv(_decode_map(d, add)) or_return
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
#partial switch t in ti.variant {
|
||||||
|
case reflect.Type_Info_Struct:
|
||||||
|
if t.is_raw_union {
|
||||||
|
return _unsupported(v, hdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
length, _ := err_conv(_decode_len_container(d, add)) or_return
|
||||||
|
unknown := length == -1
|
||||||
|
fields := reflect.struct_fields_zipped(ti.id)
|
||||||
|
|
||||||
|
for idx := 0; idx < len(fields) && (unknown || idx < length); idx += 1 {
|
||||||
|
// Decode key, keys can only be strings.
|
||||||
|
key: string
|
||||||
|
if keyv, kerr := decode_key(d, v, context.temp_allocator); unknown && kerr == .Break {
|
||||||
|
break
|
||||||
|
} else if kerr != nil {
|
||||||
|
err = kerr
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
key = keyv
|
||||||
|
}
|
||||||
|
defer delete(key, context.temp_allocator)
|
||||||
|
|
||||||
|
// Find matching field.
|
||||||
|
use_field_idx := -1
|
||||||
|
{
|
||||||
|
for field, field_idx in fields {
|
||||||
|
tag_value := string(reflect.struct_tag_get(field.tag, "cbor"))
|
||||||
|
if tag_value == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if key == tag_value {
|
||||||
|
use_field_idx = field_idx
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if key == field.name {
|
||||||
|
// No break because we want to still check remaining struct tags.
|
||||||
|
use_field_idx = field_idx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skips unused map entries.
|
||||||
|
if use_field_idx < 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
field := fields[use_field_idx]
|
||||||
|
// name := field.name
|
||||||
|
ptr := rawptr(uintptr(v.data) + field.offset)
|
||||||
|
fany := any{ptr, field.type.id}
|
||||||
|
_unmarshal_value(d, fany, _decode_header(r) or_return) or_return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
case reflect.Type_Info_Map:
|
||||||
|
if !reflect.is_string(t.key) {
|
||||||
|
return _unsupported(v, hdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
raw_map := (^mem.Raw_Map)(v.data)
|
||||||
|
if raw_map.allocator.procedure == nil {
|
||||||
|
raw_map.allocator = context.allocator
|
||||||
|
}
|
||||||
|
|
||||||
|
defer if err != nil {
|
||||||
|
_ = runtime.map_free_dynamic(raw_map^, t.map_info)
|
||||||
|
}
|
||||||
|
|
||||||
|
length, scap := err_conv(_decode_len_container(d, add)) or_return
|
||||||
|
unknown := length == -1
|
||||||
|
if !unknown {
|
||||||
|
// Reserve space before setting so we can return allocation errors and be efficient on big maps.
|
||||||
|
new_len := uintptr(min(scap, runtime.map_len(raw_map^)+length))
|
||||||
|
runtime.map_reserve_dynamic(raw_map, t.map_info, new_len) or_return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporary memory to unmarshal keys into before inserting them into the map.
|
||||||
|
elem_backing := mem.alloc_bytes_non_zeroed(t.value.size, t.value.align, context.temp_allocator) or_return
|
||||||
|
defer delete(elem_backing, context.temp_allocator)
|
||||||
|
|
||||||
|
map_backing_value := any{raw_data(elem_backing), t.value.id}
|
||||||
|
|
||||||
|
for idx := 0; unknown || idx < length; idx += 1 {
|
||||||
|
// Decode key, keys can only be strings.
|
||||||
|
key: string
|
||||||
|
if keyv, kerr := decode_key(d, v); unknown && kerr == .Break {
|
||||||
|
break
|
||||||
|
} else if kerr != nil {
|
||||||
|
err = kerr
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
key = keyv
|
||||||
|
}
|
||||||
|
|
||||||
|
if unknown || idx > scap {
|
||||||
|
// Reserve space for new element so we can return allocator errors.
|
||||||
|
new_len := uintptr(runtime.map_len(raw_map^)+1)
|
||||||
|
runtime.map_reserve_dynamic(raw_map, t.map_info, new_len) or_return
|
||||||
|
}
|
||||||
|
|
||||||
|
mem.zero_slice(elem_backing)
|
||||||
|
_unmarshal_value(d, map_backing_value, _decode_header(r) or_return) or_return
|
||||||
|
|
||||||
|
key_ptr := rawptr(&key)
|
||||||
|
key_cstr: cstring
|
||||||
|
if reflect.is_cstring(t.key) {
|
||||||
|
assert_safe_for_cstring(key)
|
||||||
|
key_cstr = cstring(raw_data(key))
|
||||||
|
key_ptr = &key_cstr
|
||||||
|
}
|
||||||
|
|
||||||
|
set_ptr := runtime.__dynamic_map_set_without_hash(raw_map, t.map_info, key_ptr, map_backing_value.data)
|
||||||
|
// We already reserved space for it, so this shouldn't fail.
|
||||||
|
assert(set_ptr != nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if .Shrink_Excess in d.flags {
|
||||||
|
_, _ = runtime.map_shrink_dynamic(raw_map, t.map_info)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
case:
|
||||||
|
return _unsupported(v, hdr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal into a union, based on the `TAG_OBJECT_TYPE` tag of the spec, it denotes a tag which
|
||||||
|
// contains an array of exactly two elements, the first is a textual representation of the following
|
||||||
|
// CBOR value's type.
|
||||||
|
_unmarshal_union :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header) -> (err: Unmarshal_Error) {
|
||||||
|
r := d.reader
|
||||||
|
#partial switch t in ti.variant {
|
||||||
|
case reflect.Type_Info_Union:
|
||||||
|
idhdr: Header
|
||||||
|
target_name: string
|
||||||
|
{
|
||||||
|
vhdr := _decode_header(r) or_return
|
||||||
|
vmaj, vadd := _header_split(vhdr)
|
||||||
|
if vmaj != .Array {
|
||||||
|
return .Bad_Tag_Value
|
||||||
|
}
|
||||||
|
|
||||||
|
n_items, _ := err_conv(_decode_len_container(d, vadd)) or_return
|
||||||
|
if n_items != 2 {
|
||||||
|
return .Bad_Tag_Value
|
||||||
|
}
|
||||||
|
|
||||||
|
idhdr = _decode_header(r) or_return
|
||||||
|
idmaj, idadd := _header_split(idhdr)
|
||||||
|
if idmaj != .Text {
|
||||||
|
return .Bad_Tag_Value
|
||||||
|
}
|
||||||
|
|
||||||
|
target_name = err_conv(_decode_text(d, idadd, context.temp_allocator)) or_return
|
||||||
|
}
|
||||||
|
defer delete(target_name, context.temp_allocator)
|
||||||
|
|
||||||
|
for variant, i in t.variants {
|
||||||
|
tag := i64(i)
|
||||||
|
if !t.no_nil {
|
||||||
|
tag += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#partial switch vti in variant.variant {
|
||||||
|
case reflect.Type_Info_Named:
|
||||||
|
if vti.name == target_name {
|
||||||
|
reflect.set_union_variant_raw_tag(v, tag)
|
||||||
|
return _unmarshal_value(d, any{v.data, variant.id}, _decode_header(r) or_return)
|
||||||
|
}
|
||||||
|
|
||||||
|
case:
|
||||||
|
builder := strings.builder_make(context.temp_allocator)
|
||||||
|
defer strings.builder_destroy(&builder)
|
||||||
|
|
||||||
|
reflect.write_type(&builder, variant)
|
||||||
|
variant_name := strings.to_string(builder)
|
||||||
|
|
||||||
|
if variant_name == target_name {
|
||||||
|
reflect.set_union_variant_raw_tag(v, tag)
|
||||||
|
return _unmarshal_value(d, any{v.data, variant.id}, _decode_header(r) or_return)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No variant matched.
|
||||||
|
return _unsupported(v, idhdr)
|
||||||
|
|
||||||
|
case:
|
||||||
|
// Not a union.
|
||||||
|
return _unsupported(v, hdr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_assign_int :: proc(val: any, i: $T) -> bool {
|
||||||
|
v := reflect.any_core(val)
|
||||||
|
|
||||||
|
// NOTE: should under/over flow be checked here? `encoding/json` doesn't, but maybe that is a
|
||||||
|
// less strict encoding?.
|
||||||
|
|
||||||
|
switch &dst in v {
|
||||||
|
case i8: dst = i8 (i)
|
||||||
|
case i16: dst = i16 (i)
|
||||||
|
case i16le: dst = i16le (i)
|
||||||
|
case i16be: dst = i16be (i)
|
||||||
|
case i32: dst = i32 (i)
|
||||||
|
case i32le: dst = i32le (i)
|
||||||
|
case i32be: dst = i32be (i)
|
||||||
|
case i64: dst = i64 (i)
|
||||||
|
case i64le: dst = i64le (i)
|
||||||
|
case i64be: dst = i64be (i)
|
||||||
|
case i128: dst = i128 (i)
|
||||||
|
case i128le: dst = i128le (i)
|
||||||
|
case i128be: dst = i128be (i)
|
||||||
|
case u8: dst = u8 (i)
|
||||||
|
case u16: dst = u16 (i)
|
||||||
|
case u16le: dst = u16le (i)
|
||||||
|
case u16be: dst = u16be (i)
|
||||||
|
case u32: dst = u32 (i)
|
||||||
|
case u32le: dst = u32le (i)
|
||||||
|
case u32be: dst = u32be (i)
|
||||||
|
case u64: dst = u64 (i)
|
||||||
|
case u64le: dst = u64le (i)
|
||||||
|
case u64be: dst = u64be (i)
|
||||||
|
case u128: dst = u128 (i)
|
||||||
|
case u128le: dst = u128le (i)
|
||||||
|
case u128be: dst = u128be (i)
|
||||||
|
case int: dst = int (i)
|
||||||
|
case uint: dst = uint (i)
|
||||||
|
case uintptr: dst = uintptr(i)
|
||||||
|
case:
|
||||||
|
ti := type_info_of(v.id)
|
||||||
|
if _, ok := ti.variant.(runtime.Type_Info_Bit_Set); ok {
|
||||||
|
do_byte_swap := !reflect.bit_set_is_big_endian(v)
|
||||||
|
switch ti.size * 8 {
|
||||||
|
case 0: // no-op.
|
||||||
|
case 8:
|
||||||
|
x := (^u8)(v.data)
|
||||||
|
x^ = u8(i)
|
||||||
|
case 16:
|
||||||
|
x := (^u16)(v.data)
|
||||||
|
x^ = do_byte_swap ? intrinsics.byte_swap(u16(i)) : u16(i)
|
||||||
|
case 32:
|
||||||
|
x := (^u32)(v.data)
|
||||||
|
x^ = do_byte_swap ? intrinsics.byte_swap(u32(i)) : u32(i)
|
||||||
|
case 64:
|
||||||
|
x := (^u64)(v.data)
|
||||||
|
x^ = do_byte_swap ? intrinsics.byte_swap(u64(i)) : u64(i)
|
||||||
|
case:
|
||||||
|
panic("unknown bit_size size")
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
_assign_float :: proc(val: any, f: $T) -> bool {
|
||||||
|
v := reflect.any_core(val)
|
||||||
|
|
||||||
|
// NOTE: should under/over flow be checked here? `encoding/json` doesn't, but maybe that is a
|
||||||
|
// less strict encoding?.
|
||||||
|
|
||||||
|
switch &dst in v {
|
||||||
|
case f16: dst = f16 (f)
|
||||||
|
case f16le: dst = f16le(f)
|
||||||
|
case f16be: dst = f16be(f)
|
||||||
|
case f32: dst = f32 (f)
|
||||||
|
case f32le: dst = f32le(f)
|
||||||
|
case f32be: dst = f32be(f)
|
||||||
|
case f64: dst = f64 (f)
|
||||||
|
case f64le: dst = f64le(f)
|
||||||
|
case f64be: dst = f64be(f)
|
||||||
|
|
||||||
|
case complex32: dst = complex(f16(f), 0)
|
||||||
|
case complex64: dst = complex(f32(f), 0)
|
||||||
|
case complex128: dst = complex(f64(f), 0)
|
||||||
|
|
||||||
|
case quaternion64: dst = quaternion(w=f16(f), x=0, y=0, z=0)
|
||||||
|
case quaternion128: dst = quaternion(w=f32(f), x=0, y=0, z=0)
|
||||||
|
case quaternion256: dst = quaternion(w=f64(f), x=0, y=0, z=0)
|
||||||
|
|
||||||
|
case: return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
_assign_bool :: proc(val: any, b: bool) -> bool {
|
||||||
|
v := reflect.any_core(val)
|
||||||
|
switch &dst in v {
|
||||||
|
case bool: dst = bool(b)
|
||||||
|
case b8: dst = b8 (b)
|
||||||
|
case b16: dst = b16 (b)
|
||||||
|
case b32: dst = b32 (b)
|
||||||
|
case b64: dst = b64 (b)
|
||||||
|
case: return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check that the decoder added a nil byte to the end.
|
||||||
|
@(private, disabled=ODIN_DISABLE_ASSERT)
|
||||||
|
assert_safe_for_cstring :: proc(s: string, loc := #caller_location) {
|
||||||
|
assert(([^]byte)(raw_data(s))[len(s)] == 0, loc = loc)
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// package csv reads and writes comma-separated values (CSV) files.
|
// package csv reads and writes comma-separated values (CSV) files.
|
||||||
// This package supports the format described in RFC 4180 <https://tools.ietf.org/html/rfc4180.html>
|
// This package supports the format described in RFC 4180 <https://tools.ietf.org/html/rfc4180.html>
|
||||||
package csv
|
package encoding_csv
|
||||||
|
|
||||||
import "core:bufio"
|
import "core:bufio"
|
||||||
import "core:bytes"
|
import "core:bytes"
|
||||||
@@ -91,7 +91,10 @@ DEFAULT_RECORD_BUFFER_CAPACITY :: 256
|
|||||||
|
|
||||||
// reader_init initializes a new Reader from r
|
// reader_init initializes a new Reader from r
|
||||||
reader_init :: proc(reader: ^Reader, r: io.Reader, buffer_allocator := context.allocator) {
|
reader_init :: proc(reader: ^Reader, r: io.Reader, buffer_allocator := context.allocator) {
|
||||||
reader.comma = ','
|
switch reader.comma {
|
||||||
|
case '\x00', '\n', '\r', 0xfffd:
|
||||||
|
reader.comma = ','
|
||||||
|
}
|
||||||
|
|
||||||
context.allocator = buffer_allocator
|
context.allocator = buffer_allocator
|
||||||
reserve(&reader.record_buffer, DEFAULT_RECORD_BUFFER_CAPACITY)
|
reserve(&reader.record_buffer, DEFAULT_RECORD_BUFFER_CAPACITY)
|
||||||
@@ -121,6 +124,7 @@ reader_destroy :: proc(r: ^Reader) {
|
|||||||
// read reads a single record (a slice of fields) from r
|
// read reads a single record (a slice of fields) from r
|
||||||
//
|
//
|
||||||
// All \r\n sequences are normalized to \n, including multi-line field
|
// All \r\n sequences are normalized to \n, including multi-line field
|
||||||
|
@(require_results)
|
||||||
read :: proc(r: ^Reader, allocator := context.allocator) -> (record: []string, err: Error) {
|
read :: proc(r: ^Reader, allocator := context.allocator) -> (record: []string, err: Error) {
|
||||||
if r.reuse_record {
|
if r.reuse_record {
|
||||||
record, err = _read_record(r, &r.last_record, allocator)
|
record, err = _read_record(r, &r.last_record, allocator)
|
||||||
@@ -133,6 +137,7 @@ read :: proc(r: ^Reader, allocator := context.allocator) -> (record: []string, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
// is_io_error checks where an Error is a specific io.Error kind
|
// is_io_error checks where an Error is a specific io.Error kind
|
||||||
|
@(require_results)
|
||||||
is_io_error :: proc(err: Error, io_err: io.Error) -> bool {
|
is_io_error :: proc(err: Error, io_err: io.Error) -> bool {
|
||||||
if v, ok := err.(io.Error); ok {
|
if v, ok := err.(io.Error); ok {
|
||||||
return v == io_err
|
return v == io_err
|
||||||
@@ -140,10 +145,10 @@ is_io_error :: proc(err: Error, io_err: io.Error) -> bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// read_all reads all the remaining records from r.
|
// read_all reads all the remaining records from r.
|
||||||
// Each record is a slice of fields.
|
// Each record is a slice of fields.
|
||||||
// read_all is defined to read until an EOF, and does not treat, and does not treat EOF as an error
|
// read_all is defined to read until an EOF, and does not treat, and does not treat EOF as an error
|
||||||
|
@(require_results)
|
||||||
read_all :: proc(r: ^Reader, allocator := context.allocator) -> ([][]string, Error) {
|
read_all :: proc(r: ^Reader, allocator := context.allocator) -> ([][]string, Error) {
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
records: [dynamic][]string
|
records: [dynamic][]string
|
||||||
@@ -153,13 +158,18 @@ read_all :: proc(r: ^Reader, allocator := context.allocator) -> ([][]string, Err
|
|||||||
return records[:], nil
|
return records[:], nil
|
||||||
}
|
}
|
||||||
if rerr != nil {
|
if rerr != nil {
|
||||||
return nil, rerr
|
// allow for a partial read
|
||||||
|
if record != nil {
|
||||||
|
append(&records, record)
|
||||||
|
}
|
||||||
|
return records[:], rerr
|
||||||
}
|
}
|
||||||
append(&records, record)
|
append(&records, record)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// read reads a single record (a slice of fields) from the provided input.
|
// read reads a single record (a slice of fields) from the provided input.
|
||||||
|
@(require_results)
|
||||||
read_from_string :: proc(input: string, record_allocator := context.allocator, buffer_allocator := context.allocator) -> (record: []string, n: int, err: Error) {
|
read_from_string :: proc(input: string, record_allocator := context.allocator, buffer_allocator := context.allocator) -> (record: []string, n: int, err: Error) {
|
||||||
ir: strings.Reader
|
ir: strings.Reader
|
||||||
strings.reader_init(&ir, input)
|
strings.reader_init(&ir, input)
|
||||||
@@ -175,6 +185,7 @@ read_from_string :: proc(input: string, record_allocator := context.allocator, b
|
|||||||
|
|
||||||
|
|
||||||
// read_all reads all the remaining records from the provided input.
|
// read_all reads all the remaining records from the provided input.
|
||||||
|
@(require_results)
|
||||||
read_all_from_string :: proc(input: string, records_allocator := context.allocator, buffer_allocator := context.allocator) -> ([][]string, Error) {
|
read_all_from_string :: proc(input: string, records_allocator := context.allocator, buffer_allocator := context.allocator) -> ([][]string, Error) {
|
||||||
ir: strings.Reader
|
ir: strings.Reader
|
||||||
strings.reader_init(&ir, input)
|
strings.reader_init(&ir, input)
|
||||||
@@ -186,7 +197,7 @@ read_all_from_string :: proc(input: string, records_allocator := context.allocat
|
|||||||
return read_all(&r, records_allocator)
|
return read_all(&r, records_allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
@private
|
@(private, require_results)
|
||||||
is_valid_delim :: proc(r: rune) -> bool {
|
is_valid_delim :: proc(r: rune) -> bool {
|
||||||
switch r {
|
switch r {
|
||||||
case 0, '"', '\r', '\n', utf8.RUNE_ERROR:
|
case 0, '"', '\r', '\n', utf8.RUNE_ERROR:
|
||||||
@@ -195,8 +206,9 @@ is_valid_delim :: proc(r: rune) -> bool {
|
|||||||
return utf8.valid_rune(r)
|
return utf8.valid_rune(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
@private
|
@(private, require_results)
|
||||||
_read_record :: proc(r: ^Reader, dst: ^[dynamic]string, allocator := context.allocator) -> ([]string, Error) {
|
_read_record :: proc(r: ^Reader, dst: ^[dynamic]string, allocator := context.allocator) -> ([]string, Error) {
|
||||||
|
@(require_results)
|
||||||
read_line :: proc(r: ^Reader) -> ([]byte, io.Error) {
|
read_line :: proc(r: ^Reader) -> ([]byte, io.Error) {
|
||||||
if !r.multiline_fields {
|
if !r.multiline_fields {
|
||||||
line, err := bufio.reader_read_slice(&r.r, '\n')
|
line, err := bufio.reader_read_slice(&r.r, '\n')
|
||||||
@@ -266,6 +278,7 @@ _read_record :: proc(r: ^Reader, dst: ^[dynamic]string, allocator := context.all
|
|||||||
unreachable()
|
unreachable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
length_newline :: proc(b: []byte) -> int {
|
length_newline :: proc(b: []byte) -> int {
|
||||||
if len(b) > 0 && b[len(b)-1] == '\n' {
|
if len(b) > 0 && b[len(b)-1] == '\n' {
|
||||||
return 1
|
return 1
|
||||||
@@ -273,6 +286,7 @@ _read_record :: proc(r: ^Reader, dst: ^[dynamic]string, allocator := context.all
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
next_rune :: proc(b: []byte) -> rune {
|
next_rune :: proc(b: []byte) -> rune {
|
||||||
r, _ := utf8.decode_rune(b)
|
r, _ := utf8.decode_rune(b)
|
||||||
return r
|
return r
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package csv
|
package encoding_csv
|
||||||
|
|
||||||
import "core:io"
|
import "core:io"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
@@ -17,7 +17,10 @@ Writer :: struct {
|
|||||||
|
|
||||||
// writer_init initializes a Writer that writes to w
|
// writer_init initializes a Writer that writes to w
|
||||||
writer_init :: proc(writer: ^Writer, w: io.Writer) {
|
writer_init :: proc(writer: ^Writer, w: io.Writer) {
|
||||||
writer.comma = ','
|
switch writer.comma {
|
||||||
|
case '\x00', '\n', '\r', 0xfffd:
|
||||||
|
writer.comma = ','
|
||||||
|
}
|
||||||
writer.w = w
|
writer.w = w
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package unicode_entity
|
package encoding_unicode_entity
|
||||||
/*
|
/*
|
||||||
A unicode entity encoder/decoder
|
A unicode entity encoder/decoder
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package unicode_entity
|
package encoding_unicode_entity
|
||||||
|
|
||||||
/*
|
/*
|
||||||
------ GENERATED ------ DO NOT EDIT ------ GENERATED ------ DO NOT EDIT ------ GENERATED ------
|
------ GENERATED ------ DO NOT EDIT ------ GENERATED ------ DO NOT EDIT ------ GENERATED ------
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package hex
|
package encoding_hex
|
||||||
|
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package json
|
package encoding_json
|
||||||
|
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:math/bits"
|
import "core:math/bits"
|
||||||
@@ -420,7 +420,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
|||||||
data := rawptr(uintptr(v.data) + info.offsets[i])
|
data := rawptr(uintptr(v.data) + info.offsets[i])
|
||||||
the_value := any{data, id}
|
the_value := any{data, id}
|
||||||
|
|
||||||
if is_omitempty(the_value) {
|
if omitempty && is_omitempty(the_value) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package json
|
package encoding_json
|
||||||
|
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:unicode/utf8"
|
import "core:unicode/utf8"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package json
|
package encoding_json
|
||||||
|
|
||||||
import "core:unicode/utf8"
|
import "core:unicode/utf8"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package json
|
package encoding_json
|
||||||
|
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package json
|
package encoding_json
|
||||||
|
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:math"
|
import "core:math"
|
||||||
@@ -348,7 +348,7 @@ json_name_from_tag_value :: proc(value: string) -> (json_name, extra: string) {
|
|||||||
json_name = value
|
json_name = value
|
||||||
if comma_index := strings.index_byte(json_name, ','); comma_index >= 0 {
|
if comma_index := strings.index_byte(json_name, ','); comma_index >= 0 {
|
||||||
json_name = json_name[:comma_index]
|
json_name = json_name[:comma_index]
|
||||||
extra = json_name[comma_index:]
|
extra = value[1 + comma_index:]
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package json
|
package encoding_json
|
||||||
|
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
|
|||||||
@@ -25,4 +25,4 @@
|
|||||||
```
|
```
|
||||||
|
|
||||||
*/
|
*/
|
||||||
package varint
|
package encoding_varint
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
// package varint implements variable length integer encoding and decoding using
|
// package varint implements variable length integer encoding and decoding using
|
||||||
// the LEB128 format as used by DWARF debug info, Android .dex and other file formats.
|
// the LEB128 format as used by DWARF debug info, Android .dex and other file formats.
|
||||||
package varint
|
package encoding_varint
|
||||||
|
|
||||||
// In theory we should use the bigint package. In practice, varints bigger than this indicate a corrupted file.
|
// In theory we should use the bigint package. In practice, varints bigger than this indicate a corrupted file.
|
||||||
// Instead we'll set limits on the values we'll encode/decode
|
// Instead we'll set limits on the values we'll encode/decode
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package xml
|
package encoding_xml
|
||||||
|
|
||||||
/*
|
/*
|
||||||
An XML 1.0 / 1.1 parser
|
An XML 1.0 / 1.1 parser
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package xml
|
package encoding_xml
|
||||||
|
|
||||||
/*
|
/*
|
||||||
An XML 1.0 / 1.1 parser
|
An XML 1.0 / 1.1 parser
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package xml
|
package encoding_xml
|
||||||
|
|
||||||
/*
|
/*
|
||||||
An XML 1.0 / 1.1 parser
|
An XML 1.0 / 1.1 parser
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ MAYBE:
|
|||||||
List of contributors:
|
List of contributors:
|
||||||
- Jeroen van Rijn: Initial implementation.
|
- Jeroen van Rijn: Initial implementation.
|
||||||
*/
|
*/
|
||||||
package xml
|
package encoding_xml
|
||||||
// An XML 1.0 / 1.1 parser
|
// An XML 1.0 / 1.1 parser
|
||||||
|
|
||||||
import "core:bytes"
|
import "core:bytes"
|
||||||
|
|||||||
+18
-15
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
package fmt implemented formatted I/O with procedures similar to C's printf and Python's format.
|
package fmt implements formatted I/O with procedures similar to C's printf and Python's format.
|
||||||
The format 'verbs' are derived from C's but simpler.
|
The format 'verbs' are derived from C's but simpler.
|
||||||
|
|
||||||
Printing
|
Printing
|
||||||
@@ -33,6 +33,8 @@ Floating-point, complex numbers, and quaternions:
|
|||||||
%E scientific notation, e.g. -1.23456E+78
|
%E scientific notation, e.g. -1.23456E+78
|
||||||
%f decimal point but no exponent, e.g. 123.456
|
%f decimal point but no exponent, e.g. 123.456
|
||||||
%F synonym for %f
|
%F synonym for %f
|
||||||
|
%g synonym for %f with default maximum precision
|
||||||
|
%G synonym for %g
|
||||||
%h hexadecimal (lower-case) representation with 0h prefix (0h01234abcd)
|
%h hexadecimal (lower-case) representation with 0h prefix (0h01234abcd)
|
||||||
%H hexadecimal (upper-case) representation with 0H prefix (0h01234ABCD)
|
%H hexadecimal (upper-case) representation with 0H prefix (0h01234ABCD)
|
||||||
%m number of bytes in the best unit of measurement, e.g. 123.45mib
|
%m number of bytes in the best unit of measurement, e.g. 123.45mib
|
||||||
@@ -61,9 +63,9 @@ For compound values, the elements are printed using these rules recursively; lai
|
|||||||
bit sets {key0 = elem0, key1 = elem1, ...}
|
bit sets {key0 = elem0, key1 = elem1, ...}
|
||||||
pointer to above: &{}, &[], &map[]
|
pointer to above: &{}, &[], &map[]
|
||||||
|
|
||||||
Width is specified by an optional decimal number immediately preceding the verb.
|
Width is specified by an optional decimal number immediately after the '%'.
|
||||||
If not present, the width is whatever is necessary to represent the value.
|
If not present, the width is whatever is necessary to represent the value.
|
||||||
Precision is specified after the (optional) width followed by a period followed by a decimal number.
|
Precision is specified after the (optional) width by a period followed by a decimal number.
|
||||||
If no period is present, a default precision is used.
|
If no period is present, a default precision is used.
|
||||||
A period with no following number specifies a precision of 0.
|
A period with no following number specifies a precision of 0.
|
||||||
|
|
||||||
@@ -75,7 +77,7 @@ Examples:
|
|||||||
%8.f width 8, precision 0
|
%8.f width 8, precision 0
|
||||||
|
|
||||||
Width and precision are measured in units of Unicode code points (runes).
|
Width and precision are measured in units of Unicode code points (runes).
|
||||||
n.b. C's printf uses units of bytes
|
n.b. C's printf uses units of bytes.
|
||||||
|
|
||||||
|
|
||||||
Other flags:
|
Other flags:
|
||||||
@@ -92,7 +94,7 @@ Other flags:
|
|||||||
0 pad with leading zeros rather than spaces
|
0 pad with leading zeros rather than spaces
|
||||||
|
|
||||||
|
|
||||||
Flags are ignored by verbs that don't expect them
|
Flags are ignored by verbs that don't expect them.
|
||||||
|
|
||||||
|
|
||||||
For each printf-like procedure, there is a print function that takes no
|
For each printf-like procedure, there is a print function that takes no
|
||||||
@@ -105,19 +107,20 @@ Explicit argument indices:
|
|||||||
In printf-like procedures, the default behaviour is for each formatting verb to format successive
|
In printf-like procedures, the default behaviour is for each formatting verb to format successive
|
||||||
arguments passed in the call. However, the notation [n] immediately before the verb indicates that
|
arguments passed in the call. However, the notation [n] immediately before the verb indicates that
|
||||||
the nth zero-index argument is to be formatted instead.
|
the nth zero-index argument is to be formatted instead.
|
||||||
The same notation before an '*' for a width or precision selecting the argument index holding the value.
|
The same notation before an '*' for a width or precision specifier selects the argument index
|
||||||
Python-like syntax with argument indices differs for the selecting the argument index: {N:v}
|
holding the value.
|
||||||
|
Python-like syntax with argument indices differs for selecting the argument index: {n:v}
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
fmt.printf("%[1]d %[0]d\n", 13, 37); // C-like syntax
|
fmt.printfln("%[1]d %[0]d", 13, 37) // C-like syntax
|
||||||
fmt.printf("{1:d} {0:d}\n", 13, 37); // Python-like syntax
|
fmt.printfln("{1:d} {0:d}", 13, 37) // Python-like syntax
|
||||||
prints "37 13", whilst:
|
prints "37 13", whilst:
|
||||||
fmt.printf("%[2]*.[1]*[0]f\n", 17.0, 2, 6); // C-like syntax
|
fmt.printfln("%*[2].*[1][0]f", 17.0, 2, 6) // C-like syntax
|
||||||
fmt.printf("%{0:[2]*.[1]*f}\n", 17.0, 2, 6); // Python-like syntax
|
fmt.printfln("{0:*[2].*[1]f}", 17.0, 2, 6) // Python-like syntax
|
||||||
equivalent to:
|
is equivalent to:
|
||||||
fmt.printf("%6.2f\n", 17.0, 2, 6); // C-like syntax
|
fmt.printfln("%6.2f", 17.0) // C-like syntax
|
||||||
fmt.printf("{:6.2f}\n", 17.0, 2, 6); // Python-like syntax
|
fmt.printfln("{:6.2f}", 17.0) // Python-like syntax
|
||||||
prints "17.00"
|
and prints "17.00".
|
||||||
|
|
||||||
Format errors:
|
Format errors:
|
||||||
|
|
||||||
|
|||||||
+205
-210
@@ -25,8 +25,6 @@ Info :: struct {
|
|||||||
prec: int,
|
prec: int,
|
||||||
indent: int,
|
indent: int,
|
||||||
|
|
||||||
reordered: bool,
|
|
||||||
good_arg_index: bool,
|
|
||||||
ignore_user_formatters: bool,
|
ignore_user_formatters: bool,
|
||||||
in_bad: bool,
|
in_bad: bool,
|
||||||
|
|
||||||
@@ -120,11 +118,11 @@ register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Regist
|
|||||||
//
|
//
|
||||||
// Returns: A formatted string.
|
// Returns: A formatted string.
|
||||||
//
|
//
|
||||||
|
@(require_results)
|
||||||
aprint :: proc(args: ..any, sep := " ", allocator := context.allocator) -> string {
|
aprint :: proc(args: ..any, sep := " ", allocator := context.allocator) -> string {
|
||||||
str: strings.Builder
|
str: strings.Builder
|
||||||
strings.builder_init(&str, allocator)
|
strings.builder_init(&str, allocator)
|
||||||
sbprint(&str, ..args, sep=sep)
|
return sbprint(&str, ..args, sep=sep)
|
||||||
return strings.to_string(str)
|
|
||||||
}
|
}
|
||||||
// Creates a formatted string with a newline character at the end
|
// Creates a formatted string with a newline character at the end
|
||||||
//
|
//
|
||||||
@@ -136,11 +134,11 @@ aprint :: proc(args: ..any, sep := " ", allocator := context.allocator) -> strin
|
|||||||
//
|
//
|
||||||
// Returns: A formatted string with a newline character at the end.
|
// Returns: A formatted string with a newline character at the end.
|
||||||
//
|
//
|
||||||
|
@(require_results)
|
||||||
aprintln :: proc(args: ..any, sep := " ", allocator := context.allocator) -> string {
|
aprintln :: proc(args: ..any, sep := " ", allocator := context.allocator) -> string {
|
||||||
str: strings.Builder
|
str: strings.Builder
|
||||||
strings.builder_init(&str, allocator)
|
strings.builder_init(&str, allocator)
|
||||||
sbprintln(&str, ..args, sep=sep)
|
return sbprintln(&str, ..args, sep=sep)
|
||||||
return strings.to_string(str)
|
|
||||||
}
|
}
|
||||||
// Creates a formatted string using a format string and arguments
|
// Creates a formatted string using a format string and arguments
|
||||||
//
|
//
|
||||||
@@ -153,11 +151,11 @@ aprintln :: proc(args: ..any, sep := " ", allocator := context.allocator) -> str
|
|||||||
//
|
//
|
||||||
// Returns: A formatted string. The returned string must be freed accordingly.
|
// Returns: A formatted string. The returned string must be freed accordingly.
|
||||||
//
|
//
|
||||||
|
@(require_results)
|
||||||
aprintf :: proc(fmt: string, args: ..any, allocator := context.allocator, newline := false) -> string {
|
aprintf :: proc(fmt: string, args: ..any, allocator := context.allocator, newline := false) -> string {
|
||||||
str: strings.Builder
|
str: strings.Builder
|
||||||
strings.builder_init(&str, allocator)
|
strings.builder_init(&str, allocator)
|
||||||
sbprintf(&str, fmt, ..args, newline=newline)
|
return sbprintf(&str, fmt, ..args, newline=newline)
|
||||||
return strings.to_string(str)
|
|
||||||
}
|
}
|
||||||
// Creates a formatted string using a format string and arguments, followed by a newline.
|
// Creates a formatted string using a format string and arguments, followed by a newline.
|
||||||
//
|
//
|
||||||
@@ -169,6 +167,7 @@ aprintf :: proc(fmt: string, args: ..any, allocator := context.allocator, newlin
|
|||||||
//
|
//
|
||||||
// Returns: A formatted string. The returned string must be freed accordingly.
|
// Returns: A formatted string. The returned string must be freed accordingly.
|
||||||
//
|
//
|
||||||
|
@(require_results)
|
||||||
aprintfln :: proc(fmt: string, args: ..any, allocator := context.allocator) -> string {
|
aprintfln :: proc(fmt: string, args: ..any, allocator := context.allocator) -> string {
|
||||||
return aprintf(fmt, ..args, allocator=allocator, newline=true)
|
return aprintf(fmt, ..args, allocator=allocator, newline=true)
|
||||||
}
|
}
|
||||||
@@ -182,11 +181,11 @@ aprintfln :: proc(fmt: string, args: ..any, allocator := context.allocator) -> s
|
|||||||
//
|
//
|
||||||
// Returns: A formatted string.
|
// Returns: A formatted string.
|
||||||
//
|
//
|
||||||
|
@(require_results)
|
||||||
tprint :: proc(args: ..any, sep := " ") -> string {
|
tprint :: proc(args: ..any, sep := " ") -> string {
|
||||||
str: strings.Builder
|
str: strings.Builder
|
||||||
strings.builder_init(&str, context.temp_allocator)
|
strings.builder_init(&str, context.temp_allocator)
|
||||||
sbprint(&str, ..args, sep=sep)
|
return sbprint(&str, ..args, sep=sep)
|
||||||
return strings.to_string(str)
|
|
||||||
}
|
}
|
||||||
// Creates a formatted string with a newline character at the end
|
// Creates a formatted string with a newline character at the end
|
||||||
//
|
//
|
||||||
@@ -198,11 +197,11 @@ tprint :: proc(args: ..any, sep := " ") -> string {
|
|||||||
//
|
//
|
||||||
// Returns: A formatted string with a newline character at the end.
|
// Returns: A formatted string with a newline character at the end.
|
||||||
//
|
//
|
||||||
|
@(require_results)
|
||||||
tprintln :: proc(args: ..any, sep := " ") -> string {
|
tprintln :: proc(args: ..any, sep := " ") -> string {
|
||||||
str: strings.Builder
|
str: strings.Builder
|
||||||
strings.builder_init(&str, context.temp_allocator)
|
strings.builder_init(&str, context.temp_allocator)
|
||||||
sbprintln(&str, ..args, sep=sep)
|
return sbprintln(&str, ..args, sep=sep)
|
||||||
return strings.to_string(str)
|
|
||||||
}
|
}
|
||||||
// Creates a formatted string using a format string and arguments
|
// Creates a formatted string using a format string and arguments
|
||||||
//
|
//
|
||||||
@@ -215,11 +214,11 @@ tprintln :: proc(args: ..any, sep := " ") -> string {
|
|||||||
//
|
//
|
||||||
// Returns: A formatted string.
|
// Returns: A formatted string.
|
||||||
//
|
//
|
||||||
|
@(require_results)
|
||||||
tprintf :: proc(fmt: string, args: ..any, newline := false) -> string {
|
tprintf :: proc(fmt: string, args: ..any, newline := false) -> string {
|
||||||
str: strings.Builder
|
str: strings.Builder
|
||||||
strings.builder_init(&str, context.temp_allocator)
|
strings.builder_init(&str, context.temp_allocator)
|
||||||
sbprintf(&str, fmt, ..args, newline=newline)
|
return sbprintf(&str, fmt, ..args, newline=newline)
|
||||||
return strings.to_string(str)
|
|
||||||
}
|
}
|
||||||
// Creates a formatted string using a format string and arguments, followed by a newline.
|
// Creates a formatted string using a format string and arguments, followed by a newline.
|
||||||
//
|
//
|
||||||
@@ -231,6 +230,7 @@ tprintf :: proc(fmt: string, args: ..any, newline := false) -> string {
|
|||||||
//
|
//
|
||||||
// Returns: A formatted string.
|
// Returns: A formatted string.
|
||||||
//
|
//
|
||||||
|
@(require_results)
|
||||||
tprintfln :: proc(fmt: string, args: ..any) -> string {
|
tprintfln :: proc(fmt: string, args: ..any) -> string {
|
||||||
return tprintf(fmt, ..args, newline=true)
|
return tprintf(fmt, ..args, newline=true)
|
||||||
}
|
}
|
||||||
@@ -339,6 +339,7 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
|
|||||||
//
|
//
|
||||||
// Returns: A formatted C string
|
// Returns: A formatted C string
|
||||||
//
|
//
|
||||||
|
@(require_results)
|
||||||
caprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
|
caprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
|
||||||
str: strings.Builder
|
str: strings.Builder
|
||||||
strings.builder_init(&str)
|
strings.builder_init(&str)
|
||||||
@@ -357,6 +358,7 @@ caprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
|
|||||||
//
|
//
|
||||||
// Returns: A formatted C string
|
// Returns: A formatted C string
|
||||||
//
|
//
|
||||||
|
@(require_results)
|
||||||
caprintfln :: proc(format: string, args: ..any) -> cstring {
|
caprintfln :: proc(format: string, args: ..any) -> cstring {
|
||||||
return caprintf(format, ..args, newline=true)
|
return caprintf(format, ..args, newline=true)
|
||||||
}
|
}
|
||||||
@@ -371,6 +373,7 @@ caprintfln :: proc(format: string, args: ..any) -> cstring {
|
|||||||
//
|
//
|
||||||
// Returns: A formatted C string
|
// Returns: A formatted C string
|
||||||
//
|
//
|
||||||
|
@(require_results)
|
||||||
ctprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
|
ctprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
|
||||||
str: strings.Builder
|
str: strings.Builder
|
||||||
strings.builder_init(&str, context.temp_allocator)
|
strings.builder_init(&str, context.temp_allocator)
|
||||||
@@ -389,6 +392,7 @@ ctprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
|
|||||||
//
|
//
|
||||||
// Returns: A formatted C string
|
// Returns: A formatted C string
|
||||||
//
|
//
|
||||||
|
@(require_results)
|
||||||
ctprintfln :: proc(format: string, args: ..any) -> cstring {
|
ctprintfln :: proc(format: string, args: ..any) -> cstring {
|
||||||
return ctprintf(format, ..args, newline=true)
|
return ctprintf(format, ..args, newline=true)
|
||||||
}
|
}
|
||||||
@@ -521,13 +525,107 @@ wprintln :: proc(w: io.Writer, args: ..any, sep := " ", flush := true) -> int {
|
|||||||
// Returns: The number of bytes written
|
// Returns: The number of bytes written
|
||||||
//
|
//
|
||||||
wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true, newline := false) -> int {
|
wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true, newline := false) -> int {
|
||||||
|
MAX_CHECKED_ARGS :: 64
|
||||||
|
assert(len(args) <= MAX_CHECKED_ARGS, "number of args > 64 is unsupported")
|
||||||
|
|
||||||
|
parse_options :: proc(fi: ^Info, fmt: string, index, end: int, unused_args: ^bit_set[0 ..< MAX_CHECKED_ARGS], args: ..any) -> int {
|
||||||
|
i := index
|
||||||
|
|
||||||
|
// Prefix
|
||||||
|
prefix_loop: for ; i < end; i += 1 {
|
||||||
|
switch fmt[i] {
|
||||||
|
case '+':
|
||||||
|
fi.plus = true
|
||||||
|
case '-':
|
||||||
|
fi.minus = true
|
||||||
|
fi.zero = false
|
||||||
|
case ' ':
|
||||||
|
fi.space = true
|
||||||
|
case '#':
|
||||||
|
fi.hash = true
|
||||||
|
case '0':
|
||||||
|
fi.zero = !fi.minus
|
||||||
|
case:
|
||||||
|
break prefix_loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Width
|
||||||
|
if i < end && fmt[i] == '*' {
|
||||||
|
i += 1
|
||||||
|
width_index, _, index_ok := _arg_number(fmt, &i, len(args))
|
||||||
|
|
||||||
|
if index_ok {
|
||||||
|
unused_args^ -= {width_index}
|
||||||
|
|
||||||
|
fi.width, _, fi.width_set = int_from_arg(args, width_index)
|
||||||
|
if !fi.width_set {
|
||||||
|
io.write_string(fi.writer, "%!(BAD WIDTH)", &fi.n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fi.width < 0 {
|
||||||
|
fi.width = -fi.width
|
||||||
|
fi.minus = true
|
||||||
|
fi.zero = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fi.width, i, fi.width_set = _parse_int(fmt, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Precision
|
||||||
|
if i < end && fmt[i] == '.' {
|
||||||
|
i += 1
|
||||||
|
if i < end && fmt[i] == '*' {
|
||||||
|
i += 1
|
||||||
|
precision_index, _, index_ok := _arg_number(fmt, &i, len(args))
|
||||||
|
|
||||||
|
if index_ok {
|
||||||
|
unused_args^ -= {precision_index}
|
||||||
|
fi.prec, _, fi.prec_set = int_from_arg(args, precision_index)
|
||||||
|
if fi.prec < 0 {
|
||||||
|
fi.prec = 0
|
||||||
|
fi.prec_set = false
|
||||||
|
}
|
||||||
|
if !fi.prec_set {
|
||||||
|
io.write_string(fi.writer, "%!(BAD PRECISION)", &fi.n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
prev_i := i
|
||||||
|
fi.prec, i, fi.prec_set = _parse_int(fmt, i)
|
||||||
|
if i == prev_i {
|
||||||
|
fi.prec = 0
|
||||||
|
fi.prec_set = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
error_check_arg :: proc(fi: ^Info, arg_parsed: bool, unused_args: bit_set[0 ..< MAX_CHECKED_ARGS]) -> (int, bool) {
|
||||||
|
if !arg_parsed {
|
||||||
|
for index in unused_args {
|
||||||
|
return index, true
|
||||||
|
}
|
||||||
|
io.write_string(fi.writer, "%!(MISSING ARGUMENT)", &fi.n)
|
||||||
|
} else {
|
||||||
|
io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)", &fi.n)
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
fi: Info
|
fi: Info
|
||||||
arg_index: int = 0
|
|
||||||
end := len(fmt)
|
end := len(fmt)
|
||||||
was_prev_index := false
|
unused_args: bit_set[0 ..< MAX_CHECKED_ARGS]
|
||||||
|
for i in 0 ..< len(args) {
|
||||||
|
unused_args += {i}
|
||||||
|
}
|
||||||
|
|
||||||
loop: for i := 0; i < end; /**/ {
|
loop: for i := 0; i < end; /**/ {
|
||||||
fi = Info{writer = w, good_arg_index = true, reordered = fi.reordered, n = fi.n}
|
fi = Info{writer = w, n = fi.n}
|
||||||
|
|
||||||
prev_i := i
|
prev_i := i
|
||||||
for i < end && !(fmt[i] == '%' || fmt[i] == '{' || fmt[i] == '}') {
|
for i < end && !(fmt[i] == '%' || fmt[i] == '{' || fmt[i] == '}') {
|
||||||
@@ -561,191 +659,65 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true, newline :
|
|||||||
}
|
}
|
||||||
|
|
||||||
if char == '%' {
|
if char == '%' {
|
||||||
prefix_loop: for ; i < end; i += 1 {
|
if i < end && fmt[i] == '%' {
|
||||||
switch fmt[i] {
|
io.write_byte(fi.writer, '%', &fi.n)
|
||||||
case '+':
|
|
||||||
fi.plus = true
|
|
||||||
case '-':
|
|
||||||
fi.minus = true
|
|
||||||
fi.zero = false
|
|
||||||
case ' ':
|
|
||||||
fi.space = true
|
|
||||||
case '#':
|
|
||||||
fi.hash = true
|
|
||||||
case '0':
|
|
||||||
fi.zero = !fi.minus
|
|
||||||
case:
|
|
||||||
break prefix_loop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
arg_index, i, was_prev_index = _arg_number(&fi, arg_index, fmt, i, len(args))
|
|
||||||
|
|
||||||
// Width
|
|
||||||
if i < end && fmt[i] == '*' {
|
|
||||||
i += 1
|
i += 1
|
||||||
fi.width, arg_index, fi.width_set = int_from_arg(args, arg_index)
|
continue loop
|
||||||
if !fi.width_set {
|
|
||||||
io.write_string(w, "%!(BAD WIDTH)", &fi.n)
|
|
||||||
}
|
|
||||||
|
|
||||||
if fi.width < 0 {
|
|
||||||
fi.width = -fi.width
|
|
||||||
fi.minus = true
|
|
||||||
fi.zero = false
|
|
||||||
}
|
|
||||||
was_prev_index = false
|
|
||||||
} else {
|
|
||||||
fi.width, i, fi.width_set = _parse_int(fmt, i)
|
|
||||||
if was_prev_index && fi.width_set { // %[6]2d
|
|
||||||
fi.good_arg_index = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Precision
|
i = parse_options(&fi, fmt, i, end, &unused_args, ..args)
|
||||||
if i < end && fmt[i] == '.' {
|
|
||||||
i += 1
|
|
||||||
if was_prev_index { // %[6].2d
|
|
||||||
fi.good_arg_index = false
|
|
||||||
}
|
|
||||||
if i < end && fmt[i] == '*' {
|
|
||||||
arg_index, i, was_prev_index = _arg_number(&fi, arg_index, fmt, i, len(args))
|
|
||||||
i += 1
|
|
||||||
fi.prec, arg_index, fi.prec_set = int_from_arg(args, arg_index)
|
|
||||||
if fi.prec < 0 {
|
|
||||||
fi.prec = 0
|
|
||||||
fi.prec_set = false
|
|
||||||
}
|
|
||||||
if !fi.prec_set {
|
|
||||||
io.write_string(fi.writer, "%!(BAD PRECISION)", &fi.n)
|
|
||||||
}
|
|
||||||
was_prev_index = false
|
|
||||||
} else {
|
|
||||||
fi.prec, i, fi.prec_set = _parse_int(fmt, i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !was_prev_index {
|
arg_index, arg_parsed, index_ok := _arg_number(fmt, &i, len(args))
|
||||||
arg_index, i, was_prev_index = _arg_number(&fi, arg_index, fmt, i, len(args))
|
|
||||||
|
if !index_ok {
|
||||||
|
arg_index, index_ok = error_check_arg(&fi, arg_parsed, unused_args)
|
||||||
}
|
}
|
||||||
|
|
||||||
if i >= end {
|
if i >= end {
|
||||||
io.write_string(fi.writer, "%!(NO VERB)", &fi.n)
|
io.write_string(fi.writer, "%!(NO VERB)", &fi.n)
|
||||||
break loop
|
break loop
|
||||||
|
} else if fmt[i] == ' ' {
|
||||||
|
io.write_string(fi.writer, "%!(NO VERB)", &fi.n)
|
||||||
|
continue loop
|
||||||
}
|
}
|
||||||
|
|
||||||
verb, w := utf8.decode_rune_in_string(fmt[i:])
|
verb, w := utf8.decode_rune_in_string(fmt[i:])
|
||||||
i += w
|
i += w
|
||||||
|
|
||||||
switch {
|
if index_ok {
|
||||||
case verb == '%':
|
unused_args -= {arg_index}
|
||||||
io.write_byte(fi.writer, '%', &fi.n)
|
|
||||||
case !fi.good_arg_index:
|
|
||||||
io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)", &fi.n)
|
|
||||||
case arg_index >= len(args):
|
|
||||||
io.write_string(fi.writer, "%!(MISSING ARGUMENT)", &fi.n)
|
|
||||||
case:
|
|
||||||
fmt_arg(&fi, args[arg_index], verb)
|
fmt_arg(&fi, args[arg_index], verb)
|
||||||
arg_index += 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} else if char == '{' {
|
} else if char == '{' {
|
||||||
|
arg_index: int
|
||||||
|
arg_parsed, index_ok: bool
|
||||||
|
|
||||||
if i < end && fmt[i] != '}' && fmt[i] != ':' {
|
if i < end && fmt[i] != '}' && fmt[i] != ':' {
|
||||||
new_arg_index, new_i, ok := _parse_int(fmt, i)
|
arg_index, i, arg_parsed = _parse_int(fmt, i)
|
||||||
if ok {
|
if arg_parsed {
|
||||||
fi.reordered = true
|
index_ok = 0 <= arg_index && arg_index < len(args)
|
||||||
was_prev_index = true
|
|
||||||
arg_index = new_arg_index
|
|
||||||
i = new_i
|
|
||||||
} else {
|
|
||||||
io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER ", &fi.n)
|
|
||||||
// Skip over the bad argument
|
|
||||||
start_index := i
|
|
||||||
for i < end && fmt[i] != '}' && fmt[i] != ':' {
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
fmt_arg(&fi, fmt[start_index:i], 'v')
|
|
||||||
io.write_string(fi.writer, ")", &fi.n)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !index_ok {
|
||||||
|
arg_index, index_ok = error_check_arg(&fi, arg_parsed, unused_args)
|
||||||
|
}
|
||||||
|
|
||||||
verb: rune = 'v'
|
verb: rune = 'v'
|
||||||
|
|
||||||
if i < end && fmt[i] == ':' {
|
if i < end && fmt[i] == ':' {
|
||||||
i += 1
|
i += 1
|
||||||
prefix_loop_percent: for ; i < end; i += 1 {
|
i = parse_options(&fi, fmt, i, end, &unused_args, ..args)
|
||||||
switch fmt[i] {
|
|
||||||
case '+':
|
|
||||||
fi.plus = true
|
|
||||||
case '-':
|
|
||||||
fi.minus = true
|
|
||||||
fi.zero = false
|
|
||||||
case ' ':
|
|
||||||
fi.space = true
|
|
||||||
case '#':
|
|
||||||
fi.hash = true
|
|
||||||
case '0':
|
|
||||||
fi.zero = !fi.minus
|
|
||||||
case:
|
|
||||||
break prefix_loop_percent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
arg_index, i, was_prev_index = _arg_number(&fi, arg_index, fmt, i, len(args))
|
|
||||||
|
|
||||||
// Width
|
|
||||||
if i < end && fmt[i] == '*' {
|
|
||||||
i += 1
|
|
||||||
fi.width, arg_index, fi.width_set = int_from_arg(args, arg_index)
|
|
||||||
if !fi.width_set {
|
|
||||||
io.write_string(fi.writer, "%!(BAD WIDTH)", &fi.n)
|
|
||||||
}
|
|
||||||
|
|
||||||
if fi.width < 0 {
|
|
||||||
fi.width = -fi.width
|
|
||||||
fi.minus = true
|
|
||||||
fi.zero = false
|
|
||||||
}
|
|
||||||
was_prev_index = false
|
|
||||||
} else {
|
|
||||||
fi.width, i, fi.width_set = _parse_int(fmt, i)
|
|
||||||
if was_prev_index && fi.width_set { // %[6]2d
|
|
||||||
fi.good_arg_index = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Precision
|
|
||||||
if i < end && fmt[i] == '.' {
|
|
||||||
i += 1
|
|
||||||
if was_prev_index { // %[6].2d
|
|
||||||
fi.good_arg_index = false
|
|
||||||
}
|
|
||||||
if i < end && fmt[i] == '*' {
|
|
||||||
arg_index, i, was_prev_index = _arg_number(&fi, arg_index, fmt, i, len(args))
|
|
||||||
i += 1
|
|
||||||
fi.prec, arg_index, fi.prec_set = int_from_arg(args, arg_index)
|
|
||||||
if fi.prec < 0 {
|
|
||||||
fi.prec = 0
|
|
||||||
fi.prec_set = false
|
|
||||||
}
|
|
||||||
if !fi.prec_set {
|
|
||||||
io.write_string(fi.writer, "%!(BAD PRECISION)", &fi.n)
|
|
||||||
}
|
|
||||||
was_prev_index = false
|
|
||||||
} else {
|
|
||||||
fi.prec, i, fi.prec_set = _parse_int(fmt, i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !was_prev_index {
|
|
||||||
arg_index, i, was_prev_index = _arg_number(&fi, arg_index, fmt, i, len(args))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if i >= end {
|
if i >= end {
|
||||||
io.write_string(fi.writer, "%!(NO VERB)", &fi.n)
|
io.write_string(fi.writer, "%!(NO VERB)", &fi.n)
|
||||||
break loop
|
break loop
|
||||||
|
} else if fmt[i] == '}' {
|
||||||
|
i += 1
|
||||||
|
io.write_string(fi.writer, "%!(NO VERB)", &fi.n)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
w: int = 1
|
w: int = 1
|
||||||
@@ -764,31 +736,35 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true, newline :
|
|||||||
switch {
|
switch {
|
||||||
case brace != '}':
|
case brace != '}':
|
||||||
io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)", &fi.n)
|
io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)", &fi.n)
|
||||||
case !fi.good_arg_index:
|
case index_ok:
|
||||||
io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)", &fi.n)
|
|
||||||
case arg_index >= len(args):
|
|
||||||
io.write_string(fi.writer, "%!(MISSING ARGUMENT)", &fi.n)
|
|
||||||
case:
|
|
||||||
fmt_arg(&fi, args[arg_index], verb)
|
fmt_arg(&fi, args[arg_index], verb)
|
||||||
arg_index += 1
|
unused_args -= {arg_index}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !fi.reordered && arg_index < len(args) {
|
if unused_args != {} {
|
||||||
io.write_string(fi.writer, "%!(EXTRA ", &fi.n)
|
// Use default options when formatting extra arguments.
|
||||||
for arg, index in args[arg_index:] {
|
extra_fi := Info { writer = fi.writer, n = fi.n }
|
||||||
if index > 0 {
|
|
||||||
io.write_string(fi.writer, ", ", &fi.n)
|
io.write_string(extra_fi.writer, "%!(EXTRA ", &extra_fi.n)
|
||||||
|
first_printed := false
|
||||||
|
for index in unused_args {
|
||||||
|
if first_printed {
|
||||||
|
io.write_string(extra_fi.writer, ", ", &extra_fi.n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
arg := args[index]
|
||||||
if arg == nil {
|
if arg == nil {
|
||||||
io.write_string(fi.writer, "<nil>", &fi.n)
|
io.write_string(extra_fi.writer, "<nil>", &extra_fi.n)
|
||||||
} else {
|
} else {
|
||||||
fmt_arg(&fi, args[index], 'v')
|
fmt_arg(&extra_fi, arg, 'v')
|
||||||
}
|
}
|
||||||
|
first_printed = true
|
||||||
}
|
}
|
||||||
io.write_string(fi.writer, ")", &fi.n)
|
io.write_byte(extra_fi.writer, ')', &extra_fi.n)
|
||||||
|
|
||||||
|
fi.n = extra_fi.n
|
||||||
}
|
}
|
||||||
|
|
||||||
if newline {
|
if newline {
|
||||||
@@ -871,18 +847,16 @@ _parse_int :: proc(s: string, offset: int) -> (result: int, new_offset: int, ok:
|
|||||||
// Parses an argument number from a format string and determines if it's valid
|
// Parses an argument number from a format string and determines if it's valid
|
||||||
//
|
//
|
||||||
// Inputs:
|
// Inputs:
|
||||||
// - fi: A pointer to an Info structure
|
|
||||||
// - arg_index: The current argument index
|
|
||||||
// - format: The format string to parse
|
// - format: The format string to parse
|
||||||
// - offset: The current position in the format string
|
// - offset: A pointer to the current position in the format string
|
||||||
// - arg_count: The total number of arguments
|
// - arg_count: The total number of arguments
|
||||||
//
|
//
|
||||||
// Returns:
|
// Returns:
|
||||||
// - index: The parsed argument index
|
// - index: The parsed argument index
|
||||||
// - new_offset: The new position in the format string
|
// - parsed: A boolean indicating if an argument number was parsed
|
||||||
// - ok: A boolean indicating if the parsed argument number is valid
|
// - ok: A boolean indicating if the parsed argument number is within arg_count
|
||||||
//
|
//
|
||||||
_arg_number :: proc(fi: ^Info, arg_index: int, format: string, offset, arg_count: int) -> (index, new_offset: int, ok: bool) {
|
_arg_number :: proc(format: string, offset: ^int, arg_count: int) -> (index: int, parsed, ok: bool) {
|
||||||
parse_arg_number :: proc(format: string) -> (int, int, bool) {
|
parse_arg_number :: proc(format: string) -> (int, int, bool) {
|
||||||
if len(format) < 3 {
|
if len(format) < 3 {
|
||||||
return 0, 1, false
|
return 0, 1, false
|
||||||
@@ -890,30 +864,28 @@ _arg_number :: proc(fi: ^Info, arg_index: int, format: string, offset, arg_count
|
|||||||
|
|
||||||
for i in 1..<len(format) {
|
for i in 1..<len(format) {
|
||||||
if format[i] == ']' {
|
if format[i] == ']' {
|
||||||
width, new_index, ok := _parse_int(format, 1)
|
value, new_index, ok := _parse_int(format, 1)
|
||||||
if !ok || new_index != i {
|
if !ok || new_index != i {
|
||||||
return 0, i+1, false
|
return 0, i+1, false
|
||||||
}
|
}
|
||||||
return width-1, i+1, true
|
return value, i+1, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0, 1, false
|
return 0, 1, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i := offset^
|
||||||
|
|
||||||
if len(format) <= offset || format[offset] != '[' {
|
if len(format) <= i || format[i] != '[' {
|
||||||
return arg_index, offset, false
|
return 0, false, false
|
||||||
}
|
}
|
||||||
fi.reordered = true
|
|
||||||
|
|
||||||
width: int
|
width: int
|
||||||
index, width, ok = parse_arg_number(format[offset:])
|
index, width, parsed = parse_arg_number(format[i:])
|
||||||
if ok && 0 <= index && index < arg_count {
|
offset^ = i + width
|
||||||
return index, offset+width, true
|
ok = parsed && 0 <= index && index < arg_count
|
||||||
}
|
return
|
||||||
fi.good_arg_index = false
|
|
||||||
return arg_index, offset+width, false
|
|
||||||
}
|
}
|
||||||
// Retrieves an integer from a list of any type at the specified index
|
// Retrieves an integer from a list of any type at the specified index
|
||||||
//
|
//
|
||||||
@@ -1900,7 +1872,7 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
|
|||||||
// fi.hash = false;
|
// fi.hash = false;
|
||||||
fi.indent += 1
|
fi.indent += 1
|
||||||
|
|
||||||
if hash {
|
if !is_soa && hash {
|
||||||
io.write_byte(fi.writer, '\n', &fi.n)
|
io.write_byte(fi.writer, '\n', &fi.n)
|
||||||
}
|
}
|
||||||
defer {
|
defer {
|
||||||
@@ -1934,6 +1906,9 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
|
|||||||
n = uintptr((^int)(uintptr(v.data) + info.offsets[actual_field_count])^)
|
n = uintptr((^int)(uintptr(v.data) + info.offsets[actual_field_count])^)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if hash && n > 0 {
|
||||||
|
io.write_byte(fi.writer, '\n', &fi.n)
|
||||||
|
}
|
||||||
|
|
||||||
for index in 0..<n {
|
for index in 0..<n {
|
||||||
if !hash && index > 0 { io.write_string(fi.writer, ", ", &fi.n) }
|
if !hash && index > 0 { io.write_string(fi.writer, ", ", &fi.n) }
|
||||||
@@ -1942,9 +1917,23 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
|
|||||||
|
|
||||||
if !hash && field_count > 0 { io.write_string(fi.writer, ", ", &fi.n) }
|
if !hash && field_count > 0 { io.write_string(fi.writer, ", ", &fi.n) }
|
||||||
|
|
||||||
|
if hash {
|
||||||
|
fi.indent -= 1
|
||||||
|
fmt_write_indent(fi)
|
||||||
|
fi.indent += 1
|
||||||
|
}
|
||||||
io.write_string(fi.writer, base_type_name, &fi.n)
|
io.write_string(fi.writer, base_type_name, &fi.n)
|
||||||
io.write_byte(fi.writer, '{', &fi.n)
|
io.write_byte(fi.writer, '{', &fi.n)
|
||||||
defer io.write_byte(fi.writer, '}', &fi.n)
|
if hash { io.write_byte(fi.writer, '\n', &fi.n) }
|
||||||
|
defer {
|
||||||
|
if hash {
|
||||||
|
fi.indent -= 1
|
||||||
|
fmt_write_indent(fi)
|
||||||
|
fi.indent += 1
|
||||||
|
}
|
||||||
|
io.write_byte(fi.writer, '}', &fi.n)
|
||||||
|
if hash { io.write_string(fi.writer, ",\n", &fi.n) }
|
||||||
|
}
|
||||||
fi.record_level += 1
|
fi.record_level += 1
|
||||||
defer fi.record_level -= 1
|
defer fi.record_level -= 1
|
||||||
|
|
||||||
@@ -2156,14 +2145,18 @@ fmt_named :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Named)
|
|||||||
when ODIN_ERROR_POS_STYLE == .Default {
|
when ODIN_ERROR_POS_STYLE == .Default {
|
||||||
io.write_byte(fi.writer, '(', &fi.n)
|
io.write_byte(fi.writer, '(', &fi.n)
|
||||||
io.write_int(fi.writer, int(a.line), 10, &fi.n)
|
io.write_int(fi.writer, int(a.line), 10, &fi.n)
|
||||||
io.write_byte(fi.writer, ':', &fi.n)
|
if a.column != 0 {
|
||||||
io.write_int(fi.writer, int(a.column), 10, &fi.n)
|
io.write_byte(fi.writer, ':', &fi.n)
|
||||||
|
io.write_int(fi.writer, int(a.column), 10, &fi.n)
|
||||||
|
}
|
||||||
io.write_byte(fi.writer, ')', &fi.n)
|
io.write_byte(fi.writer, ')', &fi.n)
|
||||||
} else when ODIN_ERROR_POS_STYLE == .Unix {
|
} else when ODIN_ERROR_POS_STYLE == .Unix {
|
||||||
io.write_byte(fi.writer, ':', &fi.n)
|
io.write_byte(fi.writer, ':', &fi.n)
|
||||||
io.write_int(fi.writer, int(a.line), 10, &fi.n)
|
io.write_int(fi.writer, int(a.line), 10, &fi.n)
|
||||||
io.write_byte(fi.writer, ':', &fi.n)
|
if a.column != 0 {
|
||||||
io.write_int(fi.writer, int(a.column), 10, &fi.n)
|
io.write_byte(fi.writer, ':', &fi.n)
|
||||||
|
io.write_int(fi.writer, int(a.column), 10, &fi.n)
|
||||||
|
}
|
||||||
io.write_byte(fi.writer, ':', &fi.n)
|
io.write_byte(fi.writer, ':', &fi.n)
|
||||||
} else {
|
} else {
|
||||||
#panic("Unhandled ODIN_ERROR_POS_STYLE")
|
#panic("Unhandled ODIN_ERROR_POS_STYLE")
|
||||||
@@ -2505,8 +2498,11 @@ fmt_bit_field :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Bit
|
|||||||
bit_offset := info.bit_offsets[i]
|
bit_offset := info.bit_offsets[i]
|
||||||
bit_size := info.bit_sizes[i]
|
bit_size := info.bit_sizes[i]
|
||||||
|
|
||||||
value := read_bits(([^]byte)(v.data), bit_offset, bit_size)
|
|
||||||
type := info.types[i]
|
type := info.types[i]
|
||||||
|
value := read_bits(([^]byte)(v.data), bit_offset, bit_size)
|
||||||
|
if reflect.is_endian_big(type) {
|
||||||
|
value <<= u64(8*type.size) - u64(bit_size)
|
||||||
|
}
|
||||||
|
|
||||||
if !reflect.is_unsigned(runtime.type_info_core(type)) {
|
if !reflect.is_unsigned(runtime.type_info_core(type)) {
|
||||||
// Sign Extension
|
// Sign Extension
|
||||||
@@ -2540,7 +2536,6 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
|||||||
if _user_formatters != nil && !fi.ignore_user_formatters {
|
if _user_formatters != nil && !fi.ignore_user_formatters {
|
||||||
formatter := _user_formatters[v.id]
|
formatter := _user_formatters[v.id]
|
||||||
if formatter != nil {
|
if formatter != nil {
|
||||||
fi.ignore_user_formatters = false
|
|
||||||
if ok := formatter(fi, v, verb); !ok {
|
if ok := formatter(fi, v, verb); !ok {
|
||||||
fi.ignore_user_formatters = true
|
fi.ignore_user_formatters = true
|
||||||
fmt_bad_verb(fi, verb)
|
fmt_bad_verb(fi, verb)
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
//+build js
|
//+build js
|
||||||
package fmt
|
package fmt
|
||||||
|
|
||||||
|
import "core:bufio"
|
||||||
import "core:io"
|
import "core:io"
|
||||||
|
import "core:os"
|
||||||
|
|
||||||
foreign import "odin_env"
|
foreign import "odin_env"
|
||||||
|
|
||||||
@@ -31,12 +33,63 @@ stderr := io.Writer{
|
|||||||
data = rawptr(uintptr(2)),
|
data = rawptr(uintptr(2)),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
fd_to_writer :: proc(fd: os.Handle, loc := #caller_location) -> io.Writer {
|
||||||
|
switch fd {
|
||||||
|
case 1: return stdout
|
||||||
|
case 2: return stderr
|
||||||
|
case: panic("`fmt.fprint` variant called with invalid file descriptor for JS, only 1 (stdout) and 2 (stderr) are supported", loc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fprint formats using the default print settings and writes to fd
|
||||||
|
fprint :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true, loc := #caller_location) -> int {
|
||||||
|
buf: [1024]byte
|
||||||
|
b: bufio.Writer
|
||||||
|
defer bufio.writer_flush(&b)
|
||||||
|
|
||||||
|
bufio.writer_init_with_buf(&b, fd_to_writer(fd, loc), buf[:])
|
||||||
|
w := bufio.writer_to_writer(&b)
|
||||||
|
return wprint(w, ..args, sep=sep, flush=flush)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fprintln formats using the default print settings and writes to fd
|
||||||
|
fprintln :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true, loc := #caller_location) -> int {
|
||||||
|
buf: [1024]byte
|
||||||
|
b: bufio.Writer
|
||||||
|
defer bufio.writer_flush(&b)
|
||||||
|
|
||||||
|
bufio.writer_init_with_buf(&b, fd_to_writer(fd, loc), buf[:])
|
||||||
|
|
||||||
|
w := bufio.writer_to_writer(&b)
|
||||||
|
return wprintln(w, ..args, sep=sep, flush=flush)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fprintf formats according to the specified format string and writes to fd
|
||||||
|
fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, newline := false, loc := #caller_location) -> int {
|
||||||
|
buf: [1024]byte
|
||||||
|
b: bufio.Writer
|
||||||
|
defer bufio.writer_flush(&b)
|
||||||
|
|
||||||
|
bufio.writer_init_with_buf(&b, fd_to_writer(fd, loc), buf[:])
|
||||||
|
|
||||||
|
w := bufio.writer_to_writer(&b)
|
||||||
|
return wprintf(w, fmt, ..args, flush=flush, newline=newline)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fprintfln formats according to the specified format string and writes to fd, followed by a newline.
|
||||||
|
fprintfln :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, loc := #caller_location) -> int {
|
||||||
|
return fprintf(fd, fmt, ..args, flush=flush, newline=true, loc=loc)
|
||||||
|
}
|
||||||
|
|
||||||
// print formats using the default print settings and writes to stdout
|
// print formats using the default print settings and writes to stdout
|
||||||
print :: proc(args: ..any, sep := " ", flush := true) -> int { return wprint(w=stdout, args=args, sep=sep, flush=flush) }
|
print :: proc(args: ..any, sep := " ", flush := true) -> int { return wprint(w=stdout, args=args, sep=sep, flush=flush) }
|
||||||
// println formats using the default print settings and writes to stdout
|
// println formats using the default print settings and writes to stdout
|
||||||
println :: proc(args: ..any, sep := " ", flush := true) -> int { return wprintln(w=stdout, args=args, sep=sep, flush=flush) }
|
println :: proc(args: ..any, sep := " ", flush := true) -> int { return wprintln(w=stdout, args=args, sep=sep, flush=flush) }
|
||||||
// printf formats according to the specififed format string and writes to stdout
|
// printf formats according to the specififed format string and writes to stdout
|
||||||
printf :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush) }
|
printf :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush) }
|
||||||
|
// printfln formats according to the specified format string and writes to stdout, followed by a newline.
|
||||||
|
printfln :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush, newline=true) }
|
||||||
|
|
||||||
// eprint formats using the default print settings and writes to stderr
|
// eprint formats using the default print settings and writes to stderr
|
||||||
eprint :: proc(args: ..any, sep := " ", flush := true) -> int { return wprint(w=stderr, args=args, sep=sep, flush=flush) }
|
eprint :: proc(args: ..any, sep := " ", flush := true) -> int { return wprint(w=stderr, args=args, sep=sep, flush=flush) }
|
||||||
@@ -44,3 +97,5 @@ eprint :: proc(args: ..any, sep := " ", flush := true) -> int { return wprint(
|
|||||||
eprintln :: proc(args: ..any, sep := " ", flush := true) -> int { return wprintln(w=stderr, args=args, sep=sep, flush=flush) }
|
eprintln :: proc(args: ..any, sep := " ", flush := true) -> int { return wprintln(w=stderr, args=args, sep=sep, flush=flush) }
|
||||||
// eprintf formats according to the specififed format string and writes to stderr
|
// eprintf formats according to the specififed format string and writes to stderr
|
||||||
eprintf :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stderr, fmt, ..args, flush=flush) }
|
eprintf :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stderr, fmt, ..args, flush=flush) }
|
||||||
|
// eprintfln formats according to the specified format string and writes to stderr, followed by a newline.
|
||||||
|
eprintfln :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush, newline=true) }
|
||||||
|
|||||||
+21
-18
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Formats:
|
Formats:
|
||||||
|
|
||||||
PBM (P1, P4): Portable Bit Map, stores black and white images (1 channel)
|
PBM (P1, P4): Portable Bit Map, stores black and white images (1 channel)
|
||||||
PGM (P2, P5): Portable Gray Map, stores greyscale images (1 channel, 1 or 2 bytes per value)
|
PGM (P2, P5): Portable Gray Map, stores greyscale images (1 channel, 1 or 2 bytes per value)
|
||||||
PPM (P3, P6): Portable Pixel Map, stores colour images (3 channel, 1 or 2 bytes per value)
|
PPM (P3, P6): Portable Pixel Map, stores colour images (3 channel, 1 or 2 bytes per value)
|
||||||
@@ -7,27 +8,29 @@ Formats:
|
|||||||
PFM (Pf, PF): Portable Float Map, stores floating-point images (Pf: 1 channel, PF: 3 channel)
|
PFM (Pf, PF): Portable Float Map, stores floating-point images (Pf: 1 channel, PF: 3 channel)
|
||||||
|
|
||||||
Reading:
|
Reading:
|
||||||
All formats fill out header fields `format`, `width`, `height`, `channels`, `depth`
|
|
||||||
Specific formats use more fields
|
- All formats fill out header fields `format`, `width`, `height`, `channels`, `depth`.
|
||||||
PGM, PPM, and PAM set `maxval` (maximum of 65535)
|
- Specific formats use more fields:
|
||||||
PAM sets `tupltype` if there is one, and can set `channels` to any value (not just 1 or 3)
|
PGM, PPM, and PAM set `maxval` (maximum of 65535)
|
||||||
PFM sets `scale` (float equivalent of `maxval`) and `little_endian` (endianness of stored floats)
|
PAM sets `tupltype` if there is one, and can set `channels` to any value (not just 1 or 3)
|
||||||
Currently doesn't support reading multiple images from one binary-format file
|
PFM sets `scale` (float equivalent of `maxval`) and `little_endian` (endianness of stored floats)
|
||||||
|
- Currently doesn't support reading multiple images from one binary-format file.
|
||||||
|
|
||||||
Writing:
|
Writing:
|
||||||
You can use your own `Netpbm_Info` struct to control how images are written
|
|
||||||
All formats require the header field `format` to be specified
|
- You can use your own `Netpbm_Info` struct to control how images are written.
|
||||||
Additional header fields are required for specific formats
|
- All formats require the header field `format` to be specified.
|
||||||
PGM, PPM, and PAM require `maxval` (maximum of 65535)
|
- Additional header fields are required for specific formats:
|
||||||
PAM also uses `tupltype`, though it may be left as default (empty or nil string)
|
PGM, PPM, and PAM require `maxval` (maximum of 65535)
|
||||||
PFM requires `scale`, and optionally `little_endian`
|
PAM also uses `tupltype`, though it may be left as default (empty or nil string)
|
||||||
|
PFM requires `scale`, and optionally `little_endian`
|
||||||
|
|
||||||
Some syntax differences from the specifications:
|
Some syntax differences from the specifications:
|
||||||
`channels` stores the number of values per pixel, what the PAM specification calls `depth`
|
|
||||||
`depth` instead is the number of bits for a single value (32 for PFM, 16 or 8 otherwise)
|
|
||||||
`scale` and `little_endian` are separated, so the `header` will always store a positive `scale`
|
|
||||||
`little_endian` will only be true for a negative `scale` PFM, every other format will be false
|
|
||||||
`little_endian` only describes the netpbm data being read/written, the image buffer will be native
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
- `channels` stores the number of values per pixel, what the PAM specification calls `depth`
|
||||||
|
- `depth` instead is the number of bits for a single value (32 for PFM, 16 or 8 otherwise)
|
||||||
|
- `scale` and `little_endian` are separated, so the `header` will always store a positive `scale`
|
||||||
|
- `little_endian` will only be true for a negative `scale` PFM, every other format will be false
|
||||||
|
- `little_endian` only describes the netpbm data being read/written, the image buffer will be native
|
||||||
|
*/
|
||||||
package netpbm
|
package netpbm
|
||||||
|
|||||||
+24
-1
@@ -29,7 +29,7 @@ Error :: enum i32 {
|
|||||||
// Invalid_Write means that a write returned an impossible count
|
// Invalid_Write means that a write returned an impossible count
|
||||||
Invalid_Write,
|
Invalid_Write,
|
||||||
|
|
||||||
// Short_Buffer means that a read required a longer buffer than was provided
|
// Short_Buffer means that a read/write required a longer buffer than was provided
|
||||||
Short_Buffer,
|
Short_Buffer,
|
||||||
|
|
||||||
// No_Progress is returned by some implementations of `io.Reader` when many calls
|
// No_Progress is returned by some implementations of `io.Reader` when many calls
|
||||||
@@ -359,6 +359,29 @@ read_at_least :: proc(r: Reader, buf: []byte, min: int) -> (n: int, err: Error)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// write_full writes until the entire contents of `buf` has been written or an error occurs.
|
||||||
|
write_full :: proc(w: Writer, buf: []byte) -> (n: int, err: Error) {
|
||||||
|
return write_at_least(w, buf, len(buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
// write_at_least writes at least `buf[:min]` to the writer and returns the amount written.
|
||||||
|
// If an error occurs before writing everything it is returned.
|
||||||
|
write_at_least :: proc(w: Writer, buf: []byte, min: int) -> (n: int, err: Error) {
|
||||||
|
if len(buf) < min {
|
||||||
|
return 0, .Short_Buffer
|
||||||
|
}
|
||||||
|
for n < min && err == nil {
|
||||||
|
nn: int
|
||||||
|
nn, err = write(w, buf[n:])
|
||||||
|
n += nn
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil && n < min {
|
||||||
|
err = .Short_Write
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// copy copies from src to dst till either EOF is reached on src or an error occurs
|
// copy copies from src to dst till either EOF is reached on src or an error occurs
|
||||||
// It returns the number of bytes copied and the first error that occurred whilst copying, if any.
|
// It returns the number of bytes copied and the first error that occurred whilst copying, if any.
|
||||||
copy :: proc(dst: Writer, src: Reader) -> (written: i64, err: Error) {
|
copy :: proc(dst: Writer, src: Reader) -> (written: i64, err: Error) {
|
||||||
|
|||||||
@@ -356,7 +356,7 @@ int_count_lsb :: proc(a: ^Int, allocator := context.allocator) -> (count: int, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
platform_count_lsb :: #force_inline proc(a: $T) -> (count: int)
|
platform_count_lsb :: #force_inline proc(a: $T) -> (count: int)
|
||||||
where intrinsics.type_is_integer(T) && intrinsics.type_is_unsigned(T) {
|
where intrinsics.type_is_integer(T), intrinsics.type_is_unsigned(T) {
|
||||||
return int(intrinsics.count_trailing_zeros(a)) if a > 0 else 0
|
return int(intrinsics.count_trailing_zeros(a)) if a > 0 else 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -546,7 +546,7 @@ internal_int_shl1 :: proc(dest, src: ^Int, allocator := context.allocator) -> (e
|
|||||||
Like `internal_int_mul_digit` but with an integer as the small input.
|
Like `internal_int_mul_digit` but with an integer as the small input.
|
||||||
*/
|
*/
|
||||||
internal_int_mul_integer :: proc(dest, a: ^Int, b: $T, allocator := context.allocator) -> (err: Error)
|
internal_int_mul_integer :: proc(dest, a: ^Int, b: $T, allocator := context.allocator) -> (err: Error)
|
||||||
where intrinsics.type_is_integer(T) && T != DIGIT {
|
where intrinsics.type_is_integer(T), T != DIGIT {
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
|
|
||||||
t := &Int{}
|
t := &Int{}
|
||||||
@@ -2806,7 +2806,7 @@ internal_int_count_lsb :: proc(a: ^Int) -> (count: int, err: Error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal_platform_count_lsb :: #force_inline proc(a: $T) -> (count: int)
|
internal_platform_count_lsb :: #force_inline proc(a: $T) -> (count: int)
|
||||||
where intrinsics.type_is_integer(T) && intrinsics.type_is_unsigned(T) {
|
where intrinsics.type_is_integer(T), intrinsics.type_is_unsigned(T) {
|
||||||
return int(intrinsics.count_trailing_zeros(a)) if a > 0 else 0
|
return int(intrinsics.count_trailing_zeros(a)) if a > 0 else 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1247,6 +1247,20 @@ internal_random_prime :: proc(a: ^Int, size_in_bits: int, trials: int, flags :=
|
|||||||
a.digit[0] |= 3
|
a.digit[0] |= 3
|
||||||
}
|
}
|
||||||
if .Second_MSB_On in flags {
|
if .Second_MSB_On in flags {
|
||||||
|
/*
|
||||||
|
Ensure there's enough space for the bit to be set.
|
||||||
|
*/
|
||||||
|
if a.used * _DIGIT_BITS < size_in_bits - 1 {
|
||||||
|
new_size := (size_in_bits - 1) / _DIGIT_BITS
|
||||||
|
|
||||||
|
if new_size % _DIGIT_BITS > 0 {
|
||||||
|
new_size += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
internal_grow(a, new_size) or_return
|
||||||
|
a.used = new_size
|
||||||
|
}
|
||||||
|
|
||||||
internal_int_bitfield_set_single(a, size_in_bits - 2) or_return
|
internal_int_bitfield_set_single(a, size_in_bits - 2) or_return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -469,7 +469,7 @@ internal_int_pack_count :: proc(a: ^Int, $T: typeid, nails := 0) -> (size_needed
|
|||||||
Assumes `a` not to be `nil` and to have been initialized.
|
Assumes `a` not to be `nil` and to have been initialized.
|
||||||
*/
|
*/
|
||||||
internal_int_pack :: proc(a: ^Int, buf: []$T, nails := 0, order := Order.LSB_First) -> (written: int, err: Error)
|
internal_int_pack :: proc(a: ^Int, buf: []$T, nails := 0, order := Order.LSB_First) -> (written: int, err: Error)
|
||||||
where intrinsics.type_is_integer(T) && intrinsics.type_is_unsigned(T) && size_of(T) <= 16 {
|
where intrinsics.type_is_integer(T), intrinsics.type_is_unsigned(T), size_of(T) <= 16 {
|
||||||
|
|
||||||
assert(nails >= 0 && nails < (size_of(T) * 8))
|
assert(nails >= 0 && nails < (size_of(T) * 8))
|
||||||
|
|
||||||
@@ -505,7 +505,7 @@ internal_int_pack :: proc(a: ^Int, buf: []$T, nails := 0, order := Order.LSB_Fir
|
|||||||
|
|
||||||
|
|
||||||
internal_int_unpack :: proc(a: ^Int, buf: []$T, nails := 0, order := Order.LSB_First, allocator := context.allocator) -> (err: Error)
|
internal_int_unpack :: proc(a: ^Int, buf: []$T, nails := 0, order := Order.LSB_First, allocator := context.allocator) -> (err: Error)
|
||||||
where intrinsics.type_is_integer(T) && intrinsics.type_is_unsigned(T) && size_of(T) <= 16 {
|
where intrinsics.type_is_integer(T), intrinsics.type_is_unsigned(T), size_of(T) <= 16 {
|
||||||
assert(nails >= 0 && nails < (size_of(T) * 8))
|
assert(nails >= 0 && nails < (size_of(T) * 8))
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
|
|
||||||
|
|||||||
@@ -1724,7 +1724,7 @@ quatFromMat4 :: proc "c" (m: mat4) -> (q: quat) {
|
|||||||
@(require_results)
|
@(require_results)
|
||||||
quatMulVec3 :: proc "c" (q: quat, v: vec3) -> vec3 {
|
quatMulVec3 :: proc "c" (q: quat, v: vec3) -> vec3 {
|
||||||
xyz := vec3{q.x, q.y, q.z}
|
xyz := vec3{q.x, q.y, q.z}
|
||||||
t := cross(xyz, v)
|
t := cross(2.0 * xyz, v)
|
||||||
return v + q.w*t + cross(xyz, t)
|
return v + q.w*t + cross(xyz, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1832,7 +1832,7 @@ dquatFromDmat4 :: proc "c" (m: dmat4) -> (q: dquat) {
|
|||||||
@(require_results)
|
@(require_results)
|
||||||
dquatMulDvec3 :: proc "c" (q: dquat, v: dvec3) -> dvec3 {
|
dquatMulDvec3 :: proc "c" (q: dquat, v: dvec3) -> dvec3 {
|
||||||
xyz := dvec3{q.x, q.y, q.z}
|
xyz := dvec3{q.x, q.y, q.z}
|
||||||
t := cross(xyz, v)
|
t := cross(2.0 * xyz, v)
|
||||||
return v + q.w*t + cross(xyz, t)
|
return v + q.w*t + cross(xyz, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ sqrt :: proc{
|
|||||||
@(require_results) sin_f32be :: proc "contextless" (θ: f32be) -> f32be { return #force_inline f32be(sin_f32(f32(θ))) }
|
@(require_results) sin_f32be :: proc "contextless" (θ: f32be) -> f32be { return #force_inline f32be(sin_f32(f32(θ))) }
|
||||||
@(require_results) sin_f64le :: proc "contextless" (θ: f64le) -> f64le { return #force_inline f64le(sin_f64(f64(θ))) }
|
@(require_results) sin_f64le :: proc "contextless" (θ: f64le) -> f64le { return #force_inline f64le(sin_f64(f64(θ))) }
|
||||||
@(require_results) sin_f64be :: proc "contextless" (θ: f64be) -> f64be { return #force_inline f64be(sin_f64(f64(θ))) }
|
@(require_results) sin_f64be :: proc "contextless" (θ: f64be) -> f64be { return #force_inline f64be(sin_f64(f64(θ))) }
|
||||||
|
// Return the sine of θ in radians.
|
||||||
sin :: proc{
|
sin :: proc{
|
||||||
sin_f16, sin_f16le, sin_f16be,
|
sin_f16, sin_f16le, sin_f16be,
|
||||||
sin_f32, sin_f32le, sin_f32be,
|
sin_f32, sin_f32le, sin_f32be,
|
||||||
@@ -72,6 +73,7 @@ sin :: proc{
|
|||||||
@(require_results) cos_f32be :: proc "contextless" (θ: f32be) -> f32be { return #force_inline f32be(cos_f32(f32(θ))) }
|
@(require_results) cos_f32be :: proc "contextless" (θ: f32be) -> f32be { return #force_inline f32be(cos_f32(f32(θ))) }
|
||||||
@(require_results) cos_f64le :: proc "contextless" (θ: f64le) -> f64le { return #force_inline f64le(cos_f64(f64(θ))) }
|
@(require_results) cos_f64le :: proc "contextless" (θ: f64le) -> f64le { return #force_inline f64le(cos_f64(f64(θ))) }
|
||||||
@(require_results) cos_f64be :: proc "contextless" (θ: f64be) -> f64be { return #force_inline f64be(cos_f64(f64(θ))) }
|
@(require_results) cos_f64be :: proc "contextless" (θ: f64be) -> f64be { return #force_inline f64be(cos_f64(f64(θ))) }
|
||||||
|
// Return the cosine of θ in radians.
|
||||||
cos :: proc{
|
cos :: proc{
|
||||||
cos_f16, cos_f16le, cos_f16be,
|
cos_f16, cos_f16le, cos_f16be,
|
||||||
cos_f32, cos_f32le, cos_f32be,
|
cos_f32, cos_f32le, cos_f32be,
|
||||||
@@ -378,6 +380,7 @@ log10 :: proc{
|
|||||||
@(require_results) tan_f64 :: proc "contextless" (θ: f64) -> f64 { return sin(θ)/cos(θ) }
|
@(require_results) tan_f64 :: proc "contextless" (θ: f64) -> f64 { return sin(θ)/cos(θ) }
|
||||||
@(require_results) tan_f64le :: proc "contextless" (θ: f64le) -> f64le { return f64le(tan_f64(f64(θ))) }
|
@(require_results) tan_f64le :: proc "contextless" (θ: f64le) -> f64le { return f64le(tan_f64(f64(θ))) }
|
||||||
@(require_results) tan_f64be :: proc "contextless" (θ: f64be) -> f64be { return f64be(tan_f64(f64(θ))) }
|
@(require_results) tan_f64be :: proc "contextless" (θ: f64be) -> f64be { return f64be(tan_f64(f64(θ))) }
|
||||||
|
// Return the tangent of θ in radians.
|
||||||
tan :: proc{
|
tan :: proc{
|
||||||
tan_f16, tan_f16le, tan_f16be,
|
tan_f16, tan_f16le, tan_f16be,
|
||||||
tan_f32, tan_f32le, tan_f32be,
|
tan_f32, tan_f32le, tan_f32be,
|
||||||
@@ -1752,7 +1755,28 @@ atan2_f64be :: proc "contextless" (y, x: f64be) -> f64be {
|
|||||||
// TODO(bill): Better atan2_f32
|
// TODO(bill): Better atan2_f32
|
||||||
return f64be(atan2_f64(f64(y), f64(x)))
|
return f64be(atan2_f64(f64(y), f64(x)))
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
Return the arc tangent of y/x in radians. Defined on the domain [-∞, ∞] for x and y with a range of [-π, π]
|
||||||
|
|
||||||
|
Special cases:
|
||||||
|
atan2(y, NaN) = NaN
|
||||||
|
atan2(NaN, x) = NaN
|
||||||
|
atan2(+0, x>=0) = + 0
|
||||||
|
atan2(-0, x>=0) = - 0
|
||||||
|
atan2(+0, x<=-0) = + π
|
||||||
|
atan2(-0, x<=-0) = - π
|
||||||
|
atan2(y>0, 0) = + π/2
|
||||||
|
atan2(y<0, 0) = - π/2
|
||||||
|
atan2(+∞, +∞) = + π/4
|
||||||
|
atan2(-∞, +∞) = - π/4
|
||||||
|
atan2(+∞, -∞) = 3π/4
|
||||||
|
atan2(-∞, -∞) = - 3π/4
|
||||||
|
atan2(y, +∞) = 0
|
||||||
|
atan2(y>0, -∞) = + π
|
||||||
|
atan2(y<0, -∞) = - π
|
||||||
|
atan2(+∞, x) = + π/2
|
||||||
|
atan2(-∞, x) = - π/2
|
||||||
|
*/
|
||||||
atan2 :: proc{
|
atan2 :: proc{
|
||||||
atan2_f64, atan2_f32, atan2_f16,
|
atan2_f64, atan2_f32, atan2_f16,
|
||||||
atan2_f64le, atan2_f64be,
|
atan2_f64le, atan2_f64be,
|
||||||
@@ -1760,6 +1784,7 @@ atan2 :: proc{
|
|||||||
atan2_f16le, atan2_f16be,
|
atan2_f16le, atan2_f16be,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the arc tangent of x, in radians. Defined on the domain of [-∞, ∞] with a range of [-π/2, π/2]
|
||||||
@(require_results)
|
@(require_results)
|
||||||
atan :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
atan :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||||
return atan2(x, 1)
|
return atan2(x, 1)
|
||||||
@@ -1871,6 +1896,7 @@ asin_f16le :: proc "contextless" (x: f16le) -> f16le {
|
|||||||
asin_f16be :: proc "contextless" (x: f16be) -> f16be {
|
asin_f16be :: proc "contextless" (x: f16be) -> f16be {
|
||||||
return f16be(asin_f64(f64(x)))
|
return f16be(asin_f64(f64(x)))
|
||||||
}
|
}
|
||||||
|
// Return the arc sine of x, in radians. Defined on the domain of [-1, 1] with a range of [-π/2, π/2]
|
||||||
asin :: proc{
|
asin :: proc{
|
||||||
asin_f64, asin_f32, asin_f16,
|
asin_f64, asin_f32, asin_f16,
|
||||||
asin_f64le, asin_f64be,
|
asin_f64le, asin_f64be,
|
||||||
@@ -1985,6 +2011,7 @@ acos_f16le :: proc "contextless" (x: f16le) -> f16le {
|
|||||||
acos_f16be :: proc "contextless" (x: f16be) -> f16be {
|
acos_f16be :: proc "contextless" (x: f16be) -> f16be {
|
||||||
return f16be(acos_f64(f64(x)))
|
return f16be(acos_f64(f64(x)))
|
||||||
}
|
}
|
||||||
|
// Return the arc cosine of x, in radians. Defined on the domain of [-1, 1] with a range of [0, π].
|
||||||
acos :: proc{
|
acos :: proc{
|
||||||
acos_f64, acos_f32, acos_f16,
|
acos_f64, acos_f32, acos_f16,
|
||||||
acos_f64le, acos_f64be,
|
acos_f64le, acos_f64be,
|
||||||
|
|||||||
+22
-19
@@ -5,6 +5,7 @@ Package core:math/rand implements various random number generators
|
|||||||
package rand
|
package rand
|
||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
|
import "core:crypto"
|
||||||
import "core:math"
|
import "core:math"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
@@ -105,26 +106,29 @@ init :: proc(r: ^Rand, seed: u64) {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
Initialises a random number generator to use the system random number generator.
|
Initialises a random number generator to use the system random number generator.
|
||||||
The system random number generator is platform specific.
|
The system random number generator is platform specific, and not supported
|
||||||
On `linux` refer to the `getrandom` syscall.
|
on all targets.
|
||||||
On `darwin` refer to `getentropy`.
|
|
||||||
On `windows` refer to `BCryptGenRandom`.
|
|
||||||
|
|
||||||
All other platforms are not supported
|
|
||||||
|
|
||||||
Inputs:
|
Inputs:
|
||||||
- r: The random number generator to use the system random number generator
|
- r: The random number generator to use the system random number generator
|
||||||
|
|
||||||
WARNING: Panics if the system is not either `windows`, `darwin` or `linux`
|
WARNING: Panics if the system random number generator is not supported.
|
||||||
|
Support can be determined via the `core:crypto.HAS_RAND_BYTES` constant.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
import "core:crypto"
|
||||||
import "core:math/rand"
|
import "core:math/rand"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
|
||||||
init_as_system_example :: proc() {
|
init_as_system_example :: proc() {
|
||||||
my_rand: rand.Rand
|
my_rand: rand.Rand
|
||||||
rand.init_as_system(&my_rand)
|
switch crypto.HAS_RAND_BYTES {
|
||||||
fmt.println(rand.uint64(&my_rand))
|
case true:
|
||||||
|
rand.init_as_system(&my_rand)
|
||||||
|
fmt.println(rand.uint64(&my_rand))
|
||||||
|
case false:
|
||||||
|
fmt.println("system random not supported!")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Possible Output:
|
Possible Output:
|
||||||
@@ -133,7 +137,7 @@ Possible Output:
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
init_as_system :: proc(r: ^Rand) {
|
init_as_system :: proc(r: ^Rand) {
|
||||||
if !#defined(_system_random) {
|
if !crypto.HAS_RAND_BYTES {
|
||||||
panic(#procedure + " is not supported on this platform yet")
|
panic(#procedure + " is not supported on this platform yet")
|
||||||
}
|
}
|
||||||
r.state = 0
|
r.state = 0
|
||||||
@@ -144,15 +148,14 @@ init_as_system :: proc(r: ^Rand) {
|
|||||||
@(private)
|
@(private)
|
||||||
_random_u64 :: proc(r: ^Rand) -> u64 {
|
_random_u64 :: proc(r: ^Rand) -> u64 {
|
||||||
r := r
|
r := r
|
||||||
if r == nil {
|
switch {
|
||||||
|
case r == nil:
|
||||||
r = &global_rand
|
r = &global_rand
|
||||||
|
case r.is_system:
|
||||||
|
value: u64
|
||||||
|
crypto.rand_bytes((cast([^]u8)&value)[:size_of(u64)])
|
||||||
|
return value
|
||||||
}
|
}
|
||||||
when #defined(_system_random) {
|
|
||||||
if r.is_system {
|
|
||||||
return _system_random()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
old_state := r.state
|
old_state := r.state
|
||||||
r.state = old_state * 6364136223846793005 + (r.inc|1)
|
r.state = old_state * 6364136223846793005 + (r.inc|1)
|
||||||
@@ -789,8 +792,8 @@ shuffle :: proc(array: $T/[]$E, r: ^Rand = nil) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := i64(0); i < n; i += 1 {
|
for i := i64(n - 1); i > 0; i -= 1 {
|
||||||
j := int63_max(n, r)
|
j := int63_max(i + 1, r)
|
||||||
array[i], array[j] = array[j], array[i]
|
array[i], array[j] = array[j], array[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
package rand
|
|
||||||
|
|
||||||
import "core:sys/darwin"
|
|
||||||
|
|
||||||
@(require_results)
|
|
||||||
_system_random :: proc() -> u64 {
|
|
||||||
for {
|
|
||||||
value: u64
|
|
||||||
ret := darwin.syscall_getentropy(([^]u8)(&value), size_of(value))
|
|
||||||
if ret < 0 {
|
|
||||||
switch ret {
|
|
||||||
case -4: // EINTR
|
|
||||||
continue
|
|
||||||
case -78: // ENOSYS
|
|
||||||
panic("getentropy not available in kernel")
|
|
||||||
case:
|
|
||||||
panic("getentropy failed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package rand
|
|
||||||
|
|
||||||
foreign import "odin_env"
|
|
||||||
foreign odin_env {
|
|
||||||
@(link_name = "rand_bytes")
|
|
||||||
env_rand_bytes :: proc "contextless" (buf: []byte) ---
|
|
||||||
}
|
|
||||||
|
|
||||||
@(require_results)
|
|
||||||
_system_random :: proc() -> u64 {
|
|
||||||
buf: [8]u8
|
|
||||||
env_rand_bytes(buf[:])
|
|
||||||
return transmute(u64)buf
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package rand
|
|
||||||
|
|
||||||
import "core:sys/linux"
|
|
||||||
|
|
||||||
@(require_results)
|
|
||||||
_system_random :: proc() -> u64 {
|
|
||||||
for {
|
|
||||||
value: u64
|
|
||||||
value_buf := (cast([^]u8)&value)[:size_of(u64)]
|
|
||||||
_, errno := linux.getrandom(value_buf, {})
|
|
||||||
#partial switch errno {
|
|
||||||
case .NONE:
|
|
||||||
// Do nothing
|
|
||||||
case .EINTR:
|
|
||||||
// Call interupted by a signal handler, just retry the request.
|
|
||||||
continue
|
|
||||||
case .ENOSYS:
|
|
||||||
// The kernel is apparently prehistoric (< 3.17 circa 2014)
|
|
||||||
// and does not support getrandom.
|
|
||||||
panic("getrandom not available in kernel")
|
|
||||||
case:
|
|
||||||
// All other failures are things that should NEVER happen
|
|
||||||
// unless the kernel interface changes (ie: the Linux
|
|
||||||
// developers break userland).
|
|
||||||
panic("getrandom failed")
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package rand
|
|
||||||
|
|
||||||
import win32 "core:sys/windows"
|
|
||||||
|
|
||||||
@(require_results)
|
|
||||||
_system_random :: proc() -> u64 {
|
|
||||||
value: u64
|
|
||||||
status := win32.BCryptGenRandom(nil, ([^]u8)(&value), size_of(value), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG)
|
|
||||||
if status < 0 {
|
|
||||||
panic("BCryptGenRandom failed")
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
@@ -1124,7 +1124,7 @@ buddy_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
case .Query_Info:
|
case .Query_Info:
|
||||||
info := (^Allocator_Query_Info)(old_memory)
|
info := (^Allocator_Query_Info)(old_memory)
|
||||||
if info != nil && info.pointer != nil {
|
if info != nil && info.pointer != nil {
|
||||||
ptr := old_memory
|
ptr := info.pointer
|
||||||
if !(b.head <= ptr && ptr <= b.tail) {
|
if !(b.head <= ptr && ptr <= b.tail) {
|
||||||
return nil, .Invalid_Pointer
|
return nil, .Invalid_Pointer
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,13 @@ Tracking_Allocator :: struct {
|
|||||||
bad_free_array: [dynamic]Tracking_Allocator_Bad_Free_Entry,
|
bad_free_array: [dynamic]Tracking_Allocator_Bad_Free_Entry,
|
||||||
mutex: sync.Mutex,
|
mutex: sync.Mutex,
|
||||||
clear_on_free_all: bool,
|
clear_on_free_all: bool,
|
||||||
|
|
||||||
|
total_memory_allocated: i64,
|
||||||
|
total_allocation_count: i64,
|
||||||
|
total_memory_freed: i64,
|
||||||
|
total_free_count: i64,
|
||||||
|
peak_memory_allocated: i64,
|
||||||
|
current_memory_allocated: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Allocator, internals_allocator := context.allocator) {
|
tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Allocator, internals_allocator := context.allocator) {
|
||||||
@@ -44,6 +51,7 @@ tracking_allocator_clear :: proc(t: ^Tracking_Allocator) {
|
|||||||
sync.mutex_lock(&t.mutex)
|
sync.mutex_lock(&t.mutex)
|
||||||
clear(&t.allocation_map)
|
clear(&t.allocation_map)
|
||||||
clear(&t.bad_free_array)
|
clear(&t.bad_free_array)
|
||||||
|
t.current_memory_allocated = 0
|
||||||
sync.mutex_unlock(&t.mutex)
|
sync.mutex_unlock(&t.mutex)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,6 +67,21 @@ tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
|
|||||||
tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||||
size, alignment: int,
|
size, alignment: int,
|
||||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> (result: []byte, err: Allocator_Error) {
|
old_memory: rawptr, old_size: int, loc := #caller_location) -> (result: []byte, err: Allocator_Error) {
|
||||||
|
track_alloc :: proc(data: ^Tracking_Allocator, entry: ^Tracking_Allocator_Entry) {
|
||||||
|
data.total_memory_allocated += i64(entry.size)
|
||||||
|
data.total_allocation_count += 1
|
||||||
|
data.current_memory_allocated += i64(entry.size)
|
||||||
|
if data.current_memory_allocated > data.peak_memory_allocated {
|
||||||
|
data.peak_memory_allocated = data.current_memory_allocated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
track_free :: proc(data: ^Tracking_Allocator, entry: ^Tracking_Allocator_Entry) {
|
||||||
|
data.total_memory_freed += i64(entry.size)
|
||||||
|
data.total_free_count += 1
|
||||||
|
data.current_memory_allocated -= i64(entry.size)
|
||||||
|
}
|
||||||
|
|
||||||
data := (^Tracking_Allocator)(allocator_data)
|
data := (^Tracking_Allocator)(allocator_data)
|
||||||
|
|
||||||
sync.mutex_guard(&data.mutex)
|
sync.mutex_guard(&data.mutex)
|
||||||
@@ -100,13 +123,21 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
err = err,
|
err = err,
|
||||||
location = loc,
|
location = loc,
|
||||||
}
|
}
|
||||||
|
track_alloc(data, &data.allocation_map[result_ptr])
|
||||||
case .Free:
|
case .Free:
|
||||||
|
if old_memory != nil && old_memory in data.allocation_map {
|
||||||
|
track_free(data, &data.allocation_map[old_memory])
|
||||||
|
}
|
||||||
delete_key(&data.allocation_map, old_memory)
|
delete_key(&data.allocation_map, old_memory)
|
||||||
case .Free_All:
|
case .Free_All:
|
||||||
if data.clear_on_free_all {
|
if data.clear_on_free_all {
|
||||||
clear_map(&data.allocation_map)
|
clear_map(&data.allocation_map)
|
||||||
|
data.current_memory_allocated = 0
|
||||||
}
|
}
|
||||||
case .Resize, .Resize_Non_Zeroed:
|
case .Resize, .Resize_Non_Zeroed:
|
||||||
|
if old_memory != nil && old_memory in data.allocation_map {
|
||||||
|
track_free(data, &data.allocation_map[old_memory])
|
||||||
|
}
|
||||||
if old_memory != result_ptr {
|
if old_memory != result_ptr {
|
||||||
delete_key(&data.allocation_map, old_memory)
|
delete_key(&data.allocation_map, old_memory)
|
||||||
}
|
}
|
||||||
@@ -118,6 +149,7 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
err = err,
|
err = err,
|
||||||
location = loc,
|
location = loc,
|
||||||
}
|
}
|
||||||
|
track_alloc(data, &data.allocation_map[result_ptr])
|
||||||
|
|
||||||
case .Query_Features:
|
case .Query_Features:
|
||||||
set := (^Allocator_Mode_Set)(old_memory)
|
set := (^Allocator_Mode_Set)(old_memory)
|
||||||
|
|||||||
@@ -258,8 +258,12 @@ _send_tcp :: proc(tcp_sock: TCP_Socket, buf: []byte) -> (int, Network_Error) {
|
|||||||
for total_written < len(buf) {
|
for total_written < len(buf) {
|
||||||
limit := min(int(max(i32)), len(buf) - total_written)
|
limit := min(int(max(i32)), len(buf) - total_written)
|
||||||
remaining := buf[total_written:][:limit]
|
remaining := buf[total_written:][:limit]
|
||||||
res, errno := linux.send(linux.Fd(tcp_sock), remaining, {})
|
res, errno := linux.send(linux.Fd(tcp_sock), remaining, {.NOSIGNAL})
|
||||||
if errno != .NONE {
|
if errno == .EPIPE {
|
||||||
|
// If the peer is disconnected when we are trying to send we will get an `EPIPE` error,
|
||||||
|
// so we turn that into a clearer error
|
||||||
|
return total_written, TCP_Send_Error.Connection_Closed
|
||||||
|
} else if errno != .NONE {
|
||||||
return total_written, TCP_Send_Error(errno)
|
return total_written, TCP_Send_Error(errno)
|
||||||
}
|
}
|
||||||
total_written += int(res)
|
total_written += int(res)
|
||||||
|
|||||||
+15
-2
@@ -21,7 +21,7 @@ import "core:strconv"
|
|||||||
import "core:unicode/utf8"
|
import "core:unicode/utf8"
|
||||||
import "core:encoding/hex"
|
import "core:encoding/hex"
|
||||||
|
|
||||||
split_url :: proc(url: string, allocator := context.allocator) -> (scheme, host, path: string, queries: map[string]string) {
|
split_url :: proc(url: string, allocator := context.allocator) -> (scheme, host, path: string, queries: map[string]string, fragment: string) {
|
||||||
s := url
|
s := url
|
||||||
|
|
||||||
i := strings.index(s, "://")
|
i := strings.index(s, "://")
|
||||||
@@ -30,6 +30,12 @@ split_url :: proc(url: string, allocator := context.allocator) -> (scheme, host,
|
|||||||
s = s[i+3:]
|
s = s[i+3:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i = strings.index(s, "#")
|
||||||
|
if i != -1 {
|
||||||
|
fragment = s[i+1:]
|
||||||
|
s = s[:i]
|
||||||
|
}
|
||||||
|
|
||||||
i = strings.index(s, "?")
|
i = strings.index(s, "?")
|
||||||
if i != -1 {
|
if i != -1 {
|
||||||
query_str := s[i+1:]
|
query_str := s[i+1:]
|
||||||
@@ -62,7 +68,7 @@ split_url :: proc(url: string, allocator := context.allocator) -> (scheme, host,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
join_url :: proc(scheme, host, path: string, queries: map[string]string, allocator := context.allocator) -> string {
|
join_url :: proc(scheme, host, path: string, queries: map[string]string, fragment: string, allocator := context.allocator) -> string {
|
||||||
b := strings.builder_make(allocator)
|
b := strings.builder_make(allocator)
|
||||||
strings.builder_grow(&b, len(scheme) + 3 + len(host) + 1 + len(path))
|
strings.builder_grow(&b, len(scheme) + 3 + len(host) + 1 + len(path))
|
||||||
|
|
||||||
@@ -95,6 +101,13 @@ join_url :: proc(scheme, host, path: string, queries: map[string]string, allocat
|
|||||||
i += 1
|
i += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if fragment != "" {
|
||||||
|
if fragment[0] != '#' {
|
||||||
|
strings.write_string(&b, "#")
|
||||||
|
}
|
||||||
|
strings.write_string(&b, strings.trim_space(fragment))
|
||||||
|
}
|
||||||
|
|
||||||
return strings.to_string(b)
|
return strings.to_string(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import "core:path/filepath"
|
|||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:os"
|
import "core:os"
|
||||||
import "core:slice"
|
import "core:slice"
|
||||||
|
import "core:strings"
|
||||||
|
|
||||||
collect_package :: proc(path: string) -> (pkg: ^ast.Package, success: bool) {
|
collect_package :: proc(path: string) -> (pkg: ^ast.Package, success: bool) {
|
||||||
NO_POS :: tokenizer.Pos{}
|
NO_POS :: tokenizer.Pos{}
|
||||||
@@ -32,11 +33,18 @@ collect_package :: proc(path: string) -> (pkg: ^ast.Package, success: bool) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
src, ok = os.read_entire_file(fullpath)
|
src, ok = os.read_entire_file(fullpath)
|
||||||
if !ok {
|
if !ok {
|
||||||
delete(fullpath)
|
delete(fullpath)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if strings.trim_space(string(src)) == "" {
|
||||||
|
delete(fullpath)
|
||||||
|
delete(src)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
file := ast.new(ast.File, NO_POS, NO_POS)
|
file := ast.new(ast.File, NO_POS, NO_POS)
|
||||||
file.pkg = pkg
|
file.pkg = pkg
|
||||||
file.src = string(src)
|
file.src = string(src)
|
||||||
@@ -69,7 +77,9 @@ parse_package :: proc(pkg: ^ast.Package, p: ^Parser = nil) -> bool {
|
|||||||
if !parse_file(p, file) {
|
if !parse_file(p, file) {
|
||||||
ok = false
|
ok = false
|
||||||
}
|
}
|
||||||
if pkg.name == "" {
|
if file.pkg_decl == nil {
|
||||||
|
error(p, p.curr_tok.pos, "Expected a package declaration at the start of the file")
|
||||||
|
} else if pkg.name == "" {
|
||||||
pkg.name = file.pkg_decl.name
|
pkg.name = file.pkg_decl.name
|
||||||
} else if pkg.name != file.pkg_decl.name {
|
} else if pkg.name != file.pkg_decl.name {
|
||||||
error(p, file.pkg_decl.pos, "different package name, expected '%s', got '%s'", pkg.name, file.pkg_decl.name)
|
error(p, file.pkg_decl.pos, "different package name, expected '%s', got '%s'", pkg.name, file.pkg_decl.name)
|
||||||
|
|||||||
@@ -2117,7 +2117,9 @@ parse_proc_type :: proc(p: ^Parser, tok: tokenizer.Token) -> ^ast.Proc_Type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
expect_token(p, .Open_Paren)
|
expect_token(p, .Open_Paren)
|
||||||
|
p.expr_level += 1
|
||||||
params, _ := parse_field_list(p, .Close_Paren, ast.Field_Flags_Signature_Params)
|
params, _ := parse_field_list(p, .Close_Paren, ast.Field_Flags_Signature_Params)
|
||||||
|
p.expr_level -= 1
|
||||||
expect_closing_parentheses_of_field_list(p)
|
expect_closing_parentheses_of_field_list(p)
|
||||||
results, diverging := parse_results(p)
|
results, diverging := parse_results(p)
|
||||||
|
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ blkcnt_t :: i64
|
|||||||
blksize_t :: i32
|
blksize_t :: i32
|
||||||
fflags_t :: u32
|
fflags_t :: u32
|
||||||
|
|
||||||
when ODIN_ARCH == .amd64 /* LP64 */ {
|
when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 /* LP64 */ {
|
||||||
time_t :: i64
|
time_t :: i64
|
||||||
} else {
|
} else {
|
||||||
time_t :: i32
|
time_t :: i32
|
||||||
|
|||||||
+2
-9
@@ -1,9 +1,7 @@
|
|||||||
//+build js
|
//+build js
|
||||||
package os
|
package os
|
||||||
|
|
||||||
import "base:intrinsics"
|
|
||||||
import "base:runtime"
|
import "base:runtime"
|
||||||
import "core:unicode/utf16"
|
|
||||||
|
|
||||||
is_path_separator :: proc(c: byte) -> bool {
|
is_path_separator :: proc(c: byte) -> bool {
|
||||||
return c == '/' || c == '\\'
|
return c == '/' || c == '\\'
|
||||||
@@ -64,13 +62,8 @@ write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno)
|
|||||||
unimplemented("core:os procedure not supported on JS target")
|
unimplemented("core:os procedure not supported on JS target")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stdout: Handle = 1
|
||||||
|
stderr: Handle = 2
|
||||||
// NOTE(bill): Uses startup to initialize it
|
|
||||||
//stdin := get_std_handle(uint(win32.STD_INPUT_HANDLE))
|
|
||||||
//stdout := get_std_handle(uint(win32.STD_OUTPUT_HANDLE))
|
|
||||||
//stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE))
|
|
||||||
|
|
||||||
|
|
||||||
get_std_handle :: proc "contextless" (h: uint) -> Handle {
|
get_std_handle :: proc "contextless" (h: uint) -> Handle {
|
||||||
context = runtime.default_context()
|
context = runtime.default_context()
|
||||||
|
|||||||
+2
-2
@@ -3,8 +3,8 @@ package os
|
|||||||
import "core:time"
|
import "core:time"
|
||||||
|
|
||||||
File_Info :: struct {
|
File_Info :: struct {
|
||||||
fullpath: string,
|
fullpath: string, // allocated
|
||||||
name: string,
|
name: string, // uses `fullpath` as underlying data
|
||||||
size: i64,
|
size: i64,
|
||||||
mode: File_Mode,
|
mode: File_Mode,
|
||||||
is_dir: bool,
|
is_dir: bool,
|
||||||
|
|||||||
@@ -934,6 +934,27 @@ set_union_value :: proc(dst: any, value: any) -> bool {
|
|||||||
panic("expected a union to reflect.set_union_variant_typeid")
|
panic("expected a union to reflect.set_union_variant_typeid")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
|
bit_set_is_big_endian :: proc(value: any, loc := #caller_location) -> bool {
|
||||||
|
if value == nil { return ODIN_ENDIAN == .Big }
|
||||||
|
|
||||||
|
ti := runtime.type_info_base(type_info_of(value.id))
|
||||||
|
if info, ok := ti.variant.(runtime.Type_Info_Bit_Set); ok {
|
||||||
|
if info.underlying == nil { return ODIN_ENDIAN == .Big }
|
||||||
|
|
||||||
|
underlying_ti := runtime.type_info_base(info.underlying)
|
||||||
|
if underlying_info, uok := underlying_ti.variant.(runtime.Type_Info_Integer); uok {
|
||||||
|
switch underlying_info.endianness {
|
||||||
|
case .Platform: return ODIN_ENDIAN == .Big
|
||||||
|
case .Little: return false
|
||||||
|
case .Big: return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ODIN_ENDIAN == .Big
|
||||||
|
}
|
||||||
|
panic("expected a bit_set to reflect.bit_set_is_big_endian", loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
|
|||||||
@@ -408,7 +408,68 @@ is_relative_multi_pointer :: proc(info: ^Type_Info) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
|
is_endian_platform :: proc(info: ^Type_Info) -> bool {
|
||||||
|
if info == nil { return false}
|
||||||
|
info := info
|
||||||
|
info = type_info_core(info)
|
||||||
|
#partial switch v in info.variant {
|
||||||
|
case Type_Info_Integer:
|
||||||
|
return v.endianness == .Platform
|
||||||
|
case Type_Info_Bit_Set:
|
||||||
|
if v.underlying != nil {
|
||||||
|
return is_endian_platform(v.underlying)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case Type_Info_Pointer:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
|
is_endian_little :: proc(info: ^Type_Info) -> bool {
|
||||||
|
if info == nil { return false}
|
||||||
|
info := info
|
||||||
|
info = type_info_core(info)
|
||||||
|
#partial switch v in info.variant {
|
||||||
|
case Type_Info_Integer:
|
||||||
|
if v.endianness == .Platform {
|
||||||
|
return ODIN_ENDIAN == .Little
|
||||||
|
}
|
||||||
|
return v.endianness == .Little
|
||||||
|
case Type_Info_Bit_Set:
|
||||||
|
if v.underlying != nil {
|
||||||
|
return is_endian_platform(v.underlying)
|
||||||
|
}
|
||||||
|
return ODIN_ENDIAN == .Little
|
||||||
|
case Type_Info_Pointer:
|
||||||
|
return ODIN_ENDIAN == .Little
|
||||||
|
}
|
||||||
|
return ODIN_ENDIAN == .Little
|
||||||
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
|
is_endian_big :: proc(info: ^Type_Info) -> bool {
|
||||||
|
if info == nil { return false}
|
||||||
|
info := info
|
||||||
|
info = type_info_core(info)
|
||||||
|
#partial switch v in info.variant {
|
||||||
|
case Type_Info_Integer:
|
||||||
|
if v.endianness == .Platform {
|
||||||
|
return ODIN_ENDIAN == .Big
|
||||||
|
}
|
||||||
|
return v.endianness == .Big
|
||||||
|
case Type_Info_Bit_Set:
|
||||||
|
if v.underlying != nil {
|
||||||
|
return is_endian_platform(v.underlying)
|
||||||
|
}
|
||||||
|
return ODIN_ENDIAN == .Big
|
||||||
|
case Type_Info_Pointer:
|
||||||
|
return ODIN_ENDIAN == .Big
|
||||||
|
}
|
||||||
|
return ODIN_ENDIAN == .Big
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ _mm_lddqu_si128 :: #force_inline proc "c" (mem_addr: ^__m128i) -> __m128i {
|
|||||||
_mm_movedup_pd :: #force_inline proc "c" (a: __m128d) -> __m128d {
|
_mm_movedup_pd :: #force_inline proc "c" (a: __m128d) -> __m128d {
|
||||||
return simd.shuffle(a, a, 0, 0)
|
return simd.shuffle(a, a, 0, 0)
|
||||||
}
|
}
|
||||||
@(require_results, enable_target_feature="sse3")
|
@(require_results, enable_target_feature="sse2,sse3")
|
||||||
_mm_loaddup_pd :: #force_inline proc "c" (mem_addr: [^]f64) -> __m128d {
|
_mm_loaddup_pd :: #force_inline proc "c" (mem_addr: [^]f64) -> __m128d {
|
||||||
return _mm_load1_pd(mem_addr)
|
return _mm_load1_pd(mem_addr)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ _mm_testnzc_si128 :: #force_inline proc "c" (a: __m128i, mask: __m128i) -> i32 {
|
|||||||
_mm_test_all_zeros :: #force_inline proc "c" (a: __m128i, mask: __m128i) -> i32 {
|
_mm_test_all_zeros :: #force_inline proc "c" (a: __m128i, mask: __m128i) -> i32 {
|
||||||
return _mm_testz_si128(a, mask)
|
return _mm_testz_si128(a, mask)
|
||||||
}
|
}
|
||||||
@(require_results, enable_target_feature="sse4.1")
|
@(require_results, enable_target_feature="sse2,sse4.1")
|
||||||
_mm_test_all_ones :: #force_inline proc "c" (a: __m128i) -> i32 {
|
_mm_test_all_ones :: #force_inline proc "c" (a: __m128i) -> i32 {
|
||||||
return _mm_testc_si128(a, _mm_cmpeq_epi32(a, a))
|
return _mm_testc_si128(a, _mm_cmpeq_epi32(a, a))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -701,3 +701,39 @@ enumerated_array :: proc(ptr: ^$T) -> []intrinsics.type_elem_type(T)
|
|||||||
where intrinsics.type_is_enumerated_array(T) {
|
where intrinsics.type_is_enumerated_array(T) {
|
||||||
return ([^]intrinsics.type_elem_type(T))(ptr)[:len(T)]
|
return ([^]intrinsics.type_elem_type(T))(ptr)[:len(T)]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Turn a `[]E` into `bit_set[E]`
|
||||||
|
// e.g.:
|
||||||
|
// bs := slice.enum_slice_to_bitset(my_flag_slice, rl.ConfigFlags)
|
||||||
|
@(require_results)
|
||||||
|
enum_slice_to_bitset :: proc(enums: []$E, $T: typeid/bit_set[E]) -> (bits: T) where intrinsics.type_is_enum(E), intrinsics.type_bit_set_elem_type(T) == E {
|
||||||
|
for v in enums {
|
||||||
|
bits |= {v}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turn a `bit_set[E]` into a `[]E`
|
||||||
|
// e.g.:
|
||||||
|
// sl := slice.bitset_to_enum_slice(flag_buf[:], bs)
|
||||||
|
@(require_results)
|
||||||
|
bitset_to_enum_slice_with_buffer :: proc(buf: []$E, bs: $T) -> (slice: []E) where intrinsics.type_is_enum(E), intrinsics.type_bit_set_elem_type(T) == E {
|
||||||
|
count := 0
|
||||||
|
for v in bs {
|
||||||
|
buf[count] = v
|
||||||
|
count += 1
|
||||||
|
}
|
||||||
|
return buf[:count]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turn a `bit_set[E]` into a `[]E`, allocates
|
||||||
|
// e.g.:
|
||||||
|
// sl := slice.bitset_to_enum_slice(bs)
|
||||||
|
@(require_results)
|
||||||
|
bitset_to_enum_slice_with_make :: proc(bs: $T, $E: typeid, allocator := context.allocator) -> (slice: []E) where intrinsics.type_is_enum(E), intrinsics.type_bit_set_elem_type(T) == E {
|
||||||
|
ones := intrinsics.count_ones(transmute(E)bs)
|
||||||
|
buf := make([]E, int(ones), allocator)
|
||||||
|
return bitset_to_enum_slice(buf, bs)
|
||||||
|
}
|
||||||
|
|
||||||
|
bitset_to_enum_slice :: proc{bitset_to_enum_slice_with_make, bitset_to_enum_slice_with_buffer}
|
||||||
@@ -104,8 +104,7 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, precision, bit_size: int)
|
|||||||
} else {
|
} else {
|
||||||
switch fmt {
|
switch fmt {
|
||||||
case 'e', 'E':
|
case 'e', 'E':
|
||||||
prec += 1
|
decimal.round(d, prec + 1)
|
||||||
decimal.round(d, prec)
|
|
||||||
case 'f', 'F':
|
case 'f', 'F':
|
||||||
decimal.round(d, d.decimal_point+prec)
|
decimal.round(d, d.decimal_point+prec)
|
||||||
case 'g', 'G':
|
case 'g', 'G':
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ create_raw_unbuffered :: proc(#any_int msg_size, msg_alignment: int, allocator:
|
|||||||
|
|
||||||
ptr := mem.alloc(size, align, allocator) or_return
|
ptr := mem.alloc(size, align, allocator) or_return
|
||||||
c = (^Raw_Chan)(ptr)
|
c = (^Raw_Chan)(ptr)
|
||||||
|
c.allocator = allocator
|
||||||
c.allocation_size = size
|
c.allocation_size = size
|
||||||
c.unbuffered_data = ([^]byte)(ptr)[offset:]
|
c.unbuffered_data = ([^]byte)(ptr)[offset:]
|
||||||
c.msg_size = u16(msg_size)
|
c.msg_size = u16(msg_size)
|
||||||
@@ -99,6 +100,7 @@ create_raw_buffered :: proc(#any_int msg_size, msg_alignment: int, #any_int cap:
|
|||||||
|
|
||||||
ptr := mem.alloc(size, align, allocator) or_return
|
ptr := mem.alloc(size, align, allocator) or_return
|
||||||
c = (^Raw_Chan)(ptr)
|
c = (^Raw_Chan)(ptr)
|
||||||
|
c.allocator = allocator
|
||||||
c.allocation_size = size
|
c.allocation_size = size
|
||||||
|
|
||||||
bptr := ([^]byte)(ptr)
|
bptr := ([^]byte)(ptr)
|
||||||
|
|||||||
@@ -433,7 +433,7 @@ One_Shot_Event :: struct #no_copy {
|
|||||||
// Blocks the current thread until the event is made available with `one_shot_event_signal`.
|
// Blocks the current thread until the event is made available with `one_shot_event_signal`.
|
||||||
one_shot_event_wait :: proc "contextless" (e: ^One_Shot_Event) {
|
one_shot_event_wait :: proc "contextless" (e: ^One_Shot_Event) {
|
||||||
for atomic_load_explicit(&e.state, .Acquire) == 0 {
|
for atomic_load_explicit(&e.state, .Acquire) == 0 {
|
||||||
futex_wait(&e.state, 1)
|
futex_wait(&e.state, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+31
-13
@@ -5,31 +5,49 @@ package sync
|
|||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
import "core:time"
|
import "core:time"
|
||||||
|
|
||||||
|
// NOTE: because `core:sync` is in the dependency chain of a lot of the core packages (mostly through `core:mem`)
|
||||||
|
// without actually calling into it much, I opted for a runtime panic instead of a compile error here.
|
||||||
|
|
||||||
_futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
|
_futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
|
||||||
s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, -1)
|
when !intrinsics.has_target_feature("atomics") {
|
||||||
return s != 0
|
_panic("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it")
|
||||||
|
} else {
|
||||||
|
s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, -1)
|
||||||
|
return s != 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {
|
_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {
|
||||||
s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, i64(duration))
|
when !intrinsics.has_target_feature("atomics") {
|
||||||
return s != 0
|
_panic("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it")
|
||||||
|
} else {
|
||||||
|
s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, i64(duration))
|
||||||
|
return s != 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_futex_signal :: proc "contextless" (f: ^Futex) {
|
_futex_signal :: proc "contextless" (f: ^Futex) {
|
||||||
loop: for {
|
when !intrinsics.has_target_feature("atomics") {
|
||||||
s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), 1)
|
_panic("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it")
|
||||||
if s >= 1 {
|
} else {
|
||||||
return
|
loop: for {
|
||||||
|
s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), 1)
|
||||||
|
if s >= 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_futex_broadcast :: proc "contextless" (f: ^Futex) {
|
_futex_broadcast :: proc "contextless" (f: ^Futex) {
|
||||||
loop: for {
|
when !intrinsics.has_target_feature("atomics") {
|
||||||
s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), ~u32(0))
|
_panic("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it")
|
||||||
if s >= 0 {
|
} else {
|
||||||
return
|
loop: for {
|
||||||
|
s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), ~u32(0))
|
||||||
|
if s >= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package CoreFoundation
|
||||||
|
|
||||||
|
foreign import CoreFoundation "system:CoreFoundation.framework"
|
||||||
|
|
||||||
|
TypeID :: distinct uint
|
||||||
|
OptionFlags :: distinct uint
|
||||||
|
HashCode :: distinct uint
|
||||||
|
Index :: distinct int
|
||||||
|
TypeRef :: distinct rawptr
|
||||||
|
|
||||||
|
Range :: struct {
|
||||||
|
location: Index,
|
||||||
|
length: Index,
|
||||||
|
}
|
||||||
|
|
||||||
|
foreign CoreFoundation {
|
||||||
|
// Releases a Core Foundation object.
|
||||||
|
CFRelease :: proc(cf: TypeRef) ---
|
||||||
|
}
|
||||||
|
|
||||||
|
// Releases a Core Foundation object.
|
||||||
|
Release :: proc {
|
||||||
|
ReleaseObject,
|
||||||
|
ReleaseString,
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseObject :: #force_inline proc(cf: TypeRef) {
|
||||||
|
CFRelease(cf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Releases a Core Foundation string.
|
||||||
|
ReleaseString :: #force_inline proc(theString: String) {
|
||||||
|
CFRelease(TypeRef(theString))
|
||||||
|
}
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
package CoreFoundation
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
|
|
||||||
|
foreign import CoreFoundation "system:CoreFoundation.framework"
|
||||||
|
|
||||||
|
String :: distinct TypeRef // same as CFStringRef
|
||||||
|
|
||||||
|
StringEncoding :: distinct u32
|
||||||
|
|
||||||
|
StringBuiltInEncodings :: enum StringEncoding {
|
||||||
|
MacRoman = 0,
|
||||||
|
WindowsLatin1 = 0x0500,
|
||||||
|
ISOLatin1 = 0x0201,
|
||||||
|
NextStepLatin = 0x0B01,
|
||||||
|
ASCII = 0x0600,
|
||||||
|
Unicode = 0x0100,
|
||||||
|
UTF8 = 0x08000100,
|
||||||
|
NonLossyASCII = 0x0BFF,
|
||||||
|
|
||||||
|
UTF16 = 0x0100,
|
||||||
|
UTF16BE = 0x10000100,
|
||||||
|
UTF16LE = 0x14000100,
|
||||||
|
|
||||||
|
UTF32 = 0x0c000100,
|
||||||
|
UTF32BE = 0x18000100,
|
||||||
|
UTF32LE = 0x1c000100,
|
||||||
|
}
|
||||||
|
|
||||||
|
StringEncodings :: enum Index {
|
||||||
|
MacJapanese = 1,
|
||||||
|
MacChineseTrad = 2,
|
||||||
|
MacKorean = 3,
|
||||||
|
MacArabic = 4,
|
||||||
|
MacHebrew = 5,
|
||||||
|
MacGreek = 6,
|
||||||
|
MacCyrillic = 7,
|
||||||
|
MacDevanagari = 9,
|
||||||
|
MacGurmukhi = 10,
|
||||||
|
MacGujarati = 11,
|
||||||
|
MacOriya = 12,
|
||||||
|
MacBengali = 13,
|
||||||
|
MacTamil = 14,
|
||||||
|
MacTelugu = 15,
|
||||||
|
MacKannada = 16,
|
||||||
|
MacMalayalam = 17,
|
||||||
|
MacSinhalese = 18,
|
||||||
|
MacBurmese = 19,
|
||||||
|
MacKhmer = 20,
|
||||||
|
MacThai = 21,
|
||||||
|
MacLaotian = 22,
|
||||||
|
MacGeorgian = 23,
|
||||||
|
MacArmenian = 24,
|
||||||
|
MacChineseSimp = 25,
|
||||||
|
MacTibetan = 26,
|
||||||
|
MacMongolian = 27,
|
||||||
|
MacEthiopic = 28,
|
||||||
|
MacCentralEurRoman = 29,
|
||||||
|
MacVietnamese = 30,
|
||||||
|
MacExtArabic = 31,
|
||||||
|
MacSymbol = 33,
|
||||||
|
MacDingbats = 34,
|
||||||
|
MacTurkish = 35,
|
||||||
|
MacCroatian = 36,
|
||||||
|
MacIcelandic = 37,
|
||||||
|
MacRomanian = 38,
|
||||||
|
MacCeltic = 39,
|
||||||
|
MacGaelic = 40,
|
||||||
|
MacFarsi = 0x8C,
|
||||||
|
MacUkrainian = 0x98,
|
||||||
|
MacInuit = 0xEC,
|
||||||
|
MacVT100 = 0xFC,
|
||||||
|
MacHFS = 0xFF,
|
||||||
|
ISOLatin2 = 0x0202,
|
||||||
|
ISOLatin3 = 0x0203,
|
||||||
|
ISOLatin4 = 0x0204,
|
||||||
|
ISOLatinCyrillic = 0x0205,
|
||||||
|
ISOLatinArabic = 0x0206,
|
||||||
|
ISOLatinGreek = 0x0207,
|
||||||
|
ISOLatinHebrew = 0x0208,
|
||||||
|
ISOLatin5 = 0x0209,
|
||||||
|
ISOLatin6 = 0x020A,
|
||||||
|
ISOLatinThai = 0x020B,
|
||||||
|
ISOLatin7 = 0x020D,
|
||||||
|
ISOLatin8 = 0x020E,
|
||||||
|
ISOLatin9 = 0x020F,
|
||||||
|
ISOLatin10 = 0x0210,
|
||||||
|
DOSLatinUS = 0x0400,
|
||||||
|
DOSGreek = 0x0405,
|
||||||
|
DOSBalticRim = 0x0406,
|
||||||
|
DOSLatin1 = 0x0410,
|
||||||
|
DOSGreek1 = 0x0411,
|
||||||
|
DOSLatin2 = 0x0412,
|
||||||
|
DOSCyrillic = 0x0413,
|
||||||
|
DOSTurkish = 0x0414,
|
||||||
|
DOSPortuguese = 0x0415,
|
||||||
|
DOSIcelandic = 0x0416,
|
||||||
|
DOSHebrew = 0x0417,
|
||||||
|
DOSCanadianFrench = 0x0418,
|
||||||
|
DOSArabic = 0x0419,
|
||||||
|
DOSNordic = 0x041A,
|
||||||
|
DOSRussian = 0x041B,
|
||||||
|
DOSGreek2 = 0x041C,
|
||||||
|
DOSThai = 0x041D,
|
||||||
|
DOSJapanese = 0x0420,
|
||||||
|
DOSChineseSimplif = 0x0421,
|
||||||
|
DOSKorean = 0x0422,
|
||||||
|
DOSChineseTrad = 0x0423,
|
||||||
|
WindowsLatin2 = 0x0501,
|
||||||
|
WindowsCyrillic = 0x0502,
|
||||||
|
WindowsGreek = 0x0503,
|
||||||
|
WindowsLatin5 = 0x0504,
|
||||||
|
WindowsHebrew = 0x0505,
|
||||||
|
WindowsArabic = 0x0506,
|
||||||
|
WindowsBalticRim = 0x0507,
|
||||||
|
WindowsVietnamese = 0x0508,
|
||||||
|
WindowsKoreanJohab = 0x0510,
|
||||||
|
ANSEL = 0x0601,
|
||||||
|
JIS_X0201_76 = 0x0620,
|
||||||
|
JIS_X0208_83 = 0x0621,
|
||||||
|
JIS_X0208_90 = 0x0622,
|
||||||
|
JIS_X0212_90 = 0x0623,
|
||||||
|
JIS_C6226_78 = 0x0624,
|
||||||
|
ShiftJIS_X0213 = 0x0628,
|
||||||
|
ShiftJIS_X0213_MenKuTen = 0x0629,
|
||||||
|
GB_2312_80 = 0x0630,
|
||||||
|
GBK_95 = 0x0631,
|
||||||
|
GB_18030_2000 = 0x0632,
|
||||||
|
KSC_5601_87 = 0x0640,
|
||||||
|
KSC_5601_92_Johab = 0x0641,
|
||||||
|
CNS_11643_92_P1 = 0x0651,
|
||||||
|
CNS_11643_92_P2 = 0x0652,
|
||||||
|
CNS_11643_92_P3 = 0x0653,
|
||||||
|
ISO_2022_JP = 0x0820,
|
||||||
|
ISO_2022_JP_2 = 0x0821,
|
||||||
|
ISO_2022_JP_1 = 0x0822,
|
||||||
|
ISO_2022_JP_3 = 0x0823,
|
||||||
|
ISO_2022_CN = 0x0830,
|
||||||
|
ISO_2022_CN_EXT = 0x0831,
|
||||||
|
ISO_2022_KR = 0x0840,
|
||||||
|
EUC_JP = 0x0920,
|
||||||
|
EUC_CN = 0x0930,
|
||||||
|
EUC_TW = 0x0931,
|
||||||
|
EUC_KR = 0x0940,
|
||||||
|
ShiftJIS = 0x0A01,
|
||||||
|
KOI8_R = 0x0A02,
|
||||||
|
Big5 = 0x0A03,
|
||||||
|
MacRomanLatin1 = 0x0A04,
|
||||||
|
HZ_GB_2312 = 0x0A05,
|
||||||
|
Big5_HKSCS_1999 = 0x0A06,
|
||||||
|
VISCII = 0x0A07,
|
||||||
|
KOI8_U = 0x0A08,
|
||||||
|
Big5_E = 0x0A09,
|
||||||
|
NextStepJapanese = 0x0B02,
|
||||||
|
EBCDIC_US = 0x0C01,
|
||||||
|
EBCDIC_CP037 = 0x0C02,
|
||||||
|
UTF7 = 0x04000100,
|
||||||
|
UTF7_IMAP = 0x0A10,
|
||||||
|
ShiftJIS_X0213_00 = 0x0628, // Deprecated. Use `ShiftJIS_X0213` instead.
|
||||||
|
}
|
||||||
|
|
||||||
|
@(link_prefix = "CF", default_calling_convention = "c")
|
||||||
|
foreign CoreFoundation {
|
||||||
|
// Copies the character contents of a string to a local C string buffer after converting the characters to a given encoding.
|
||||||
|
StringGetCString :: proc(theString: String, buffer: [^]byte, bufferSize: Index, encoding: StringEncoding) -> b8 ---
|
||||||
|
|
||||||
|
// Returns the number (in terms of UTF-16 code pairs) of Unicode characters in a string.
|
||||||
|
StringGetLength :: proc(theString: String) -> Index ---
|
||||||
|
|
||||||
|
// Returns the maximum number of bytes a string of a specified length (in Unicode characters) will take up if encoded in a specified encoding.
|
||||||
|
StringGetMaximumSizeForEncoding :: proc(length: Index, encoding: StringEncoding) -> Index ---
|
||||||
|
|
||||||
|
// Fetches a range of the characters from a string into a byte buffer after converting the characters to a specified encoding.
|
||||||
|
StringGetBytes :: proc(thestring: String, range: Range, encoding: StringEncoding, lossByte: u8, isExternalRepresentation: b8, buffer: [^]byte, maxBufLen: Index, usedBufLen: ^Index) -> Index ---
|
||||||
|
|
||||||
|
StringIsEncodingAvailable :: proc(encoding: StringEncoding) -> bool ---
|
||||||
|
|
||||||
|
@(link_name = "__CFStringMakeConstantString")
|
||||||
|
StringMakeConstantString :: proc "c" (#const c: cstring) -> String ---
|
||||||
|
}
|
||||||
|
|
||||||
|
STR :: StringMakeConstantString
|
||||||
|
|
||||||
|
StringCopyToOdinString :: proc(
|
||||||
|
theString: String,
|
||||||
|
allocator := context.allocator,
|
||||||
|
) -> (
|
||||||
|
str: string,
|
||||||
|
ok: bool,
|
||||||
|
) #optional_ok {
|
||||||
|
length := StringGetLength(theString)
|
||||||
|
max := StringGetMaximumSizeForEncoding(length, StringEncoding(StringBuiltInEncodings.UTF8))
|
||||||
|
|
||||||
|
buf, err := make([]byte, max, allocator)
|
||||||
|
if err != nil do return
|
||||||
|
|
||||||
|
raw_str := runtime.Raw_String {
|
||||||
|
data = raw_data(buf),
|
||||||
|
}
|
||||||
|
StringGetBytes(theString, {0, length}, StringEncoding(StringBuiltInEncodings.UTF8), 0, false, raw_data(buf), max, (^Index)(&raw_str.len))
|
||||||
|
|
||||||
|
return transmute(string)raw_str, true
|
||||||
|
}
|
||||||
@@ -132,7 +132,7 @@ Application_nextEventMatchingMask :: proc "c" (self: ^Application, mask: EventMa
|
|||||||
|
|
||||||
@(objc_type=Application, objc_name="sendEvent")
|
@(objc_type=Application, objc_name="sendEvent")
|
||||||
Application_sendEvent :: proc "c" (self: ^Application, event: ^Event) {
|
Application_sendEvent :: proc "c" (self: ^Application, event: ^Event) {
|
||||||
msgSend(Event, self, "sendEvent:", event)
|
msgSend(nil, self, "sendEvent:", event)
|
||||||
}
|
}
|
||||||
@(objc_type=Application, objc_name="updateWindows")
|
@(objc_type=Application, objc_name="updateWindows")
|
||||||
Application_updateWindows :: proc "c" (self: ^Application) {
|
Application_updateWindows :: proc "c" (self: ^Application) {
|
||||||
|
|||||||
@@ -23,12 +23,9 @@ StringEncoding :: enum UInteger {
|
|||||||
WindowsCP1250 = 15,
|
WindowsCP1250 = 15,
|
||||||
ISO2022JP = 21,
|
ISO2022JP = 21,
|
||||||
MacOSRoman = 30,
|
MacOSRoman = 30,
|
||||||
|
|
||||||
UTF16 = Unicode,
|
UTF16 = Unicode,
|
||||||
|
|
||||||
UTF16BigEndian = 0x90000100,
|
UTF16BigEndian = 0x90000100,
|
||||||
UTF16LittleEndian = 0x94000100,
|
UTF16LittleEndian = 0x94000100,
|
||||||
|
|
||||||
UTF32 = 0x8c000100,
|
UTF32 = 0x8c000100,
|
||||||
UTF32BigEndian = 0x98000100,
|
UTF32BigEndian = 0x98000100,
|
||||||
UTF32LittleEndian = 0x9c000100,
|
UTF32LittleEndian = 0x9c000100,
|
||||||
@@ -49,12 +46,9 @@ StringCompareOption :: enum UInteger {
|
|||||||
|
|
||||||
unichar :: distinct u16
|
unichar :: distinct u16
|
||||||
|
|
||||||
@(link_prefix="NS", default_calling_convention="c")
|
|
||||||
foreign Foundation {
|
|
||||||
StringFromClass :: proc(cls: Class) -> ^String ---
|
|
||||||
}
|
|
||||||
|
|
||||||
AT :: MakeConstantString
|
AT :: MakeConstantString
|
||||||
|
|
||||||
|
// CFString is 'toll-free bridged' with its Cocoa Foundation counterpart, NSString.
|
||||||
MakeConstantString :: proc "c" (#const c: cstring) -> ^String {
|
MakeConstantString :: proc "c" (#const c: cstring) -> ^String {
|
||||||
foreign Foundation {
|
foreign Foundation {
|
||||||
__CFStringMakeConstantString :: proc "c" (c: cstring) -> ^String ---
|
__CFStringMakeConstantString :: proc "c" (c: cstring) -> ^String ---
|
||||||
@@ -62,6 +56,10 @@ MakeConstantString :: proc "c" (#const c: cstring) -> ^String {
|
|||||||
return __CFStringMakeConstantString(c)
|
return __CFStringMakeConstantString(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(link_prefix="NS", default_calling_convention="c")
|
||||||
|
foreign Foundation {
|
||||||
|
StringFromClass :: proc(cls: Class) -> ^String ---
|
||||||
|
}
|
||||||
|
|
||||||
@(objc_type=String, objc_name="alloc", objc_is_class_method=true)
|
@(objc_type=String, objc_name="alloc", objc_is_class_method=true)
|
||||||
String_alloc :: proc "c" () -> ^String {
|
String_alloc :: proc "c" () -> ^String {
|
||||||
@@ -73,7 +71,6 @@ String_init :: proc "c" (self: ^String) -> ^String {
|
|||||||
return msgSend(^String, self, "init")
|
return msgSend(^String, self, "init")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@(objc_type=String, objc_name="initWithString")
|
@(objc_type=String, objc_name="initWithString")
|
||||||
String_initWithString :: proc "c" (self: ^String, other: ^String) -> ^String {
|
String_initWithString :: proc "c" (self: ^String, other: ^String) -> ^String {
|
||||||
return msgSend(^String, self, "initWithString:", other)
|
return msgSend(^String, self, "initWithString:", other)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user