mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-09 13:01:38 -07:00
Compare commits
343 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 539cec7496 | |||
| 000709b594 | |||
| c51f94b600 | |||
| b703d5ca58 | |||
| aff345f57f | |||
| a4664f82cc | |||
| 9fb090d1b8 | |||
| 4a2ad3b13a | |||
| 9d5afee24b | |||
| 6736205723 | |||
| f0a7f1812f | |||
| 9c9ae5987a | |||
| f588593ff1 | |||
| a626adac8e | |||
| 338793b68e | |||
| 59575d9b22 | |||
| b58a0b60d0 | |||
| 3a4c82abf8 | |||
| 946cf52df1 | |||
| a78f062499 | |||
| 3c47503780 | |||
| 6da82e038d | |||
| 38af752cd1 | |||
| 0428f508d8 | |||
| 3c24584290 | |||
| 535b8a9483 | |||
| 038086d1d9 | |||
| 9a16bc5fc5 | |||
| eee8e0faa2 | |||
| 3e7e779abf | |||
| 6a07effdd2 | |||
| 395e0fb225 | |||
| 9be9f0bb2c | |||
| 9e7cc8cf93 | |||
| 2743bf7e44 | |||
| d95c28f41b | |||
| e57224d6e4 | |||
| f67691c457 | |||
| d04c82e547 | |||
| 09fa1c29cd | |||
| ddcaa0de53 | |||
| 621b3c7829 | |||
| 68df35b378 | |||
| c1d853a24e | |||
| 30636f5114 | |||
| 31914e9cb9 | |||
| da6edb3764 | |||
| 9a9625f885 | |||
| 15b40a4f29 | |||
| 5406acc8fa | |||
| d5b0ec712b | |||
| 766d6aa946 | |||
| 895ebb95d5 | |||
| af636eedde | |||
| 57b7822e12 | |||
| 03736d8bcb | |||
| 3a5d80b291 | |||
| 967ccfc7cc | |||
| 712ae1c5ac | |||
| 9cfd4a953e | |||
| 14e2cc17d6 | |||
| 5f41741e8c | |||
| 8a067bc1fc | |||
| cfdf2bfb77 | |||
| 2f8316840e | |||
| a660098106 | |||
| 90d1f9ab27 | |||
| 2097b09abb | |||
| f63f28302e | |||
| 98b539ac5c | |||
| 9d067ae562 | |||
| 5a542c7ad5 | |||
| 73f57c5933 | |||
| 2071d7ba84 | |||
| e8102a40d0 | |||
| 737677f1b6 | |||
| fc047a8043 | |||
| ae52e245ea | |||
| b8bfc715bf | |||
| b0aa08f85b | |||
| d6ee5e89fa | |||
| a263da0bbe | |||
| 9cb02aa51d | |||
| 799f4379d8 | |||
| bd51b21386 | |||
| 276284cbec | |||
| a8021f03a6 | |||
| c35d5d4c42 | |||
| 248a0bfa5f | |||
| 34791707fd | |||
| 37ae9eb609 | |||
| b6838731f5 | |||
| 991c1d4446 | |||
| d0bb1fb844 | |||
| ea43c030aa | |||
| d54f34a162 | |||
| a86cfa6e97 | |||
| 1b83f4a18b | |||
| 90ac400ec5 | |||
| 75c659fa41 | |||
| 8cfff254c9 | |||
| 8c2eb5df78 | |||
| a2f0ac0fd2 | |||
| 2987fc65a1 | |||
| c5bab58180 | |||
| 5ad88ea169 | |||
| 72d6b9b683 | |||
| 2f2c086382 | |||
| d9fafa7000 | |||
| 12e53f2336 | |||
| a2e729c303 | |||
| 7b89174a26 | |||
| 1d7f7a55d2 | |||
| 766485ccab | |||
| 6642aa94d5 | |||
| 1c9ec27d36 | |||
| 05e27fa92d | |||
| 7954a7a6f3 | |||
| 1d621295b1 | |||
| 02c2aff41b | |||
| 5476d43441 | |||
| b25e85a8bb | |||
| 190103883c | |||
| d460dd2bdc | |||
| 7ee7f4b064 | |||
| f2d3376c0b | |||
| 0fcd2f1d88 | |||
| 76f52dd6c9 | |||
| ae0be9c785 | |||
| 3f6f00d8e5 | |||
| c58eeca1b2 | |||
| 5032839abc | |||
| 2b1d85968d | |||
| 5896469f3b | |||
| 70c150fc83 | |||
| 2411febf83 | |||
| 5e7b031a1d | |||
| ef05e0858d | |||
| 880a18f124 | |||
| 5d94887e76 | |||
| 577049c69e | |||
| 46f46e645c | |||
| b13aa5db37 | |||
| 2990747cf8 | |||
| 62c30795e6 | |||
| bb94f4d129 | |||
| 7b53dbeb8a | |||
| 120ef168bf | |||
| 72dfb73c9d | |||
| 67dcd916e8 | |||
| 7f6f971284 | |||
| 67d5b97ff9 | |||
| efb2b05040 | |||
| 656e62d724 | |||
| ce8801c37f | |||
| c6c710465a | |||
| f3dc1f6e3b | |||
| 873b7f8588 | |||
| 7d3dfb1046 | |||
| f4782157d3 | |||
| 8de7d2f18f | |||
| aff8f06e3c | |||
| 0bf1b6c2f1 | |||
| 2017ebc1b5 | |||
| da56a75ad6 | |||
| 1e1228fb37 | |||
| 8d7c37e384 | |||
| cd65a15d81 | |||
| 9235e82451 | |||
| ecee0e2db2 | |||
| 0580eebd2a | |||
| 909a5016ee | |||
| c2c89e54a5 | |||
| 6389d9c11f | |||
| 2784e8ea51 | |||
| 85b71708dd | |||
| 649b5fa528 | |||
| d6a89d667d | |||
| b408ec6bac | |||
| 8545f316ff | |||
| 3bf7b416e7 | |||
| 0b83e3dae5 | |||
| d7d23e65ea | |||
| 2820bbc269 | |||
| 70c5153471 | |||
| 5961d4b316 | |||
| b59c80d6fd | |||
| 8c10f4cdde | |||
| 0cc72b536f | |||
| 8a7c2ea9d0 | |||
| cb1c10ce83 | |||
| 37c2e9bec3 | |||
| 4626cd03da | |||
| 3850be2e11 | |||
| 4c9aa30a1e | |||
| da977cf1e6 | |||
| ee97c5958f | |||
| 778bbee17c | |||
| 4efef08c94 | |||
| 87c835268a | |||
| 83ed0b37cd | |||
| dc49cf766f | |||
| 933754193a | |||
| 76eef47491 | |||
| 0e21f45076 | |||
| 9bfe3a94f4 | |||
| a2009220a2 | |||
| b410383aaf | |||
| b47736260a | |||
| 5154bb551a | |||
| 89084befb0 | |||
| f3caa4aee3 | |||
| 3ec253f385 | |||
| 759e342872 | |||
| 120b1101fb | |||
| 252fd0e928 | |||
| 1fa2af213d | |||
| de84db85cb | |||
| 33d85adf34 | |||
| 383d485e2a | |||
| 68d2b7bb89 | |||
| d667809e0a | |||
| 64ed4389ff | |||
| 4701b31b55 | |||
| e52cc73d50 | |||
| ac0ed13b35 | |||
| 269957b9fb | |||
| 464a675adc | |||
| 55f3e99f63 | |||
| 49fb0acfc9 | |||
| 09db245e4c | |||
| bc7972fbaf | |||
| 6f80d2dc36 | |||
| 46250168d6 | |||
| 509712f771 | |||
| 4b38dbe133 | |||
| ad0ffa4833 | |||
| b89fc9191c | |||
| 829e4cc67e | |||
| beb4699b46 | |||
| 031b0cc534 | |||
| ee504aa596 | |||
| baa5ea9258 | |||
| 9a490e4e0d | |||
| 252de70b0f | |||
| 4ae021cd4c | |||
| af962526df | |||
| 6024af172c | |||
| 8bd5a9bb9e | |||
| a9166f52f8 | |||
| 9bdd4c73a5 | |||
| 2362be11bf | |||
| 5b8a76c583 | |||
| 2a0e4f7a8c | |||
| ece82eecc3 | |||
| 67d02043fe | |||
| 29c80c238d | |||
| a4606e4da8 | |||
| d4df3f6383 | |||
| 58ff3dd1ed | |||
| cf8c9a6be4 | |||
| ea709451e8 | |||
| d47a403d29 | |||
| 1606f756b3 | |||
| 2e9298891e | |||
| f8b85339af | |||
| 12b8f91249 | |||
| 8e395cc6e9 | |||
| feba52002e | |||
| ca2b2c498e | |||
| 6e9e469abd | |||
| 0490ba46f4 | |||
| e0652ee2f4 | |||
| bc99bacb21 | |||
| 173527d631 | |||
| f8cb2bcad2 | |||
| b011487778 | |||
| c8cc130744 | |||
| fe0244606b | |||
| 036fa6482c | |||
| 04ca22b9ea | |||
| bf9ae77fbd | |||
| 4adfc120ba | |||
| d8bb93accc | |||
| e410908ce8 | |||
| d278c852cc | |||
| 364b64718f | |||
| 61335089c6 | |||
| 040b90ce76 | |||
| 843b2350eb | |||
| 526d338300 | |||
| a6aca5d6d1 | |||
| 05c67c3243 | |||
| 5665ae02bc | |||
| 92d3a681cd | |||
| cb66ed52ce | |||
| 30ad923558 | |||
| a31ab31e96 | |||
| ac760a0bdd | |||
| fbf0180411 | |||
| 198ac00994 | |||
| 603764dbca | |||
| cb04333480 | |||
| 0e169fd1c2 | |||
| 68cf51c8f8 | |||
| bfbeb23f54 | |||
| 58e4a011c7 | |||
| 3de15987bd | |||
| 39f8437f4d | |||
| 058132e7e6 | |||
| 9b7c5f2b1b | |||
| 06e61c7315 | |||
| 1b23dbb228 | |||
| cc2ba146a6 | |||
| 3bbdc93882 | |||
| 0091193c04 | |||
| 1b5bcc1a99 | |||
| fb48131f3c | |||
| 9300c99d65 | |||
| 9366bf44a6 | |||
| 1e726bb3e0 | |||
| e0ecae66fd | |||
| 0df7fe4247 | |||
| 84a8e17482 | |||
| 03282c1234 | |||
| b21b5ef222 | |||
| 4e145cf69c | |||
| 341087a82b | |||
| 851fd1f8d1 | |||
| ce43b04688 | |||
| ff1ab74b3c | |||
| aed67ba665 | |||
| 548ab2e1b6 | |||
| 13a3c0e57c | |||
| f26ed39e86 | |||
| 574d2baf09 | |||
| 1e622979f8 | |||
| 7faa146004 | |||
| 75cb2c68cc | |||
| 942e91f94c | |||
| d8f06ed557 | |||
| a73ff00b02 | |||
| a58a08c0c3 |
@@ -1 +1,2 @@
|
|||||||
*.odin linguist-language=Odin
|
*.odin linguist-language=Odin
|
||||||
|
* text=auto
|
||||||
@@ -163,7 +163,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
cd tests\documentation
|
cd tests\documentation
|
||||||
call build.bat
|
rem call build.bat
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
- name: core:math/big tests
|
- name: core:math/big tests
|
||||||
shell: cmd
|
shell: cmd
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ jobs:
|
|||||||
cp LICENSE dist
|
cp LICENSE dist
|
||||||
cp LLVM-C.dll dist
|
cp LLVM-C.dll dist
|
||||||
cp -r shared dist
|
cp -r shared dist
|
||||||
|
cp -r base dist
|
||||||
cp -r core dist
|
cp -r core dist
|
||||||
cp -r vendor dist
|
cp -r vendor dist
|
||||||
cp -r bin dist
|
cp -r bin dist
|
||||||
@@ -56,6 +57,7 @@ jobs:
|
|||||||
cp LICENSE dist
|
cp LICENSE dist
|
||||||
cp libLLVM* dist
|
cp libLLVM* dist
|
||||||
cp -r shared dist
|
cp -r shared dist
|
||||||
|
cp -r base dist
|
||||||
cp -r core dist
|
cp -r core dist
|
||||||
cp -r vendor dist
|
cp -r vendor dist
|
||||||
cp -r examples dist
|
cp -r examples dist
|
||||||
@@ -85,6 +87,7 @@ jobs:
|
|||||||
cp odin dist
|
cp odin dist
|
||||||
cp LICENSE dist
|
cp LICENSE dist
|
||||||
cp -r shared dist
|
cp -r shared dist
|
||||||
|
cp -r base dist
|
||||||
cp -r core dist
|
cp -r core dist
|
||||||
cp -r vendor dist
|
cp -r vendor dist
|
||||||
cp -r examples dist
|
cp -r examples dist
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ tests/core/test_linalg_glsl_math
|
|||||||
tests/core/test_noise
|
tests/core/test_noise
|
||||||
tests/core/test_varint
|
tests/core/test_varint
|
||||||
tests/core/test_xml
|
tests/core/test_xml
|
||||||
|
tests/core/test_core_slice
|
||||||
|
tests/core/test_core_thread
|
||||||
tests/vendor/vendor_botan
|
tests/vendor/vendor_botan
|
||||||
# Visual Studio 2015 cache/options directory
|
# Visual Studio 2015 cache/options directory
|
||||||
.vs/
|
.vs/
|
||||||
@@ -312,3 +314,6 @@ shared/
|
|||||||
examples/bug/
|
examples/bug/
|
||||||
build.sh
|
build.sh
|
||||||
!core/debug/
|
!core/debug/
|
||||||
|
|
||||||
|
# RAD debugger project file
|
||||||
|
*.raddbg
|
||||||
@@ -110,7 +110,7 @@ typeid_of :: proc($T: typeid) -> typeid ---
|
|||||||
swizzle :: proc(x: [N]T, indices: ..int) -> [len(indices)]T ---
|
swizzle :: proc(x: [N]T, indices: ..int) -> [len(indices)]T ---
|
||||||
|
|
||||||
complex :: proc(real, imag: Float) -> Complex_Type ---
|
complex :: proc(real, imag: Float) -> Complex_Type ---
|
||||||
quaternion :: proc(real, imag, jmag, kmag: Float) -> Quaternion_Type ---
|
quaternion :: proc(imag, jmag, kmag, real: Float) -> Quaternion_Type --- // fields must be named
|
||||||
real :: proc(value: Complex_Or_Quaternion) -> Float ---
|
real :: proc(value: Complex_Or_Quaternion) -> Float ---
|
||||||
imag :: proc(value: Complex_Or_Quaternion) -> Float ---
|
imag :: proc(value: Complex_Or_Quaternion) -> Float ---
|
||||||
jmag :: proc(value: Quaternion) -> Float ---
|
jmag :: proc(value: Quaternion) -> Float ---
|
||||||
@@ -5,6 +5,12 @@ package intrinsics
|
|||||||
// Package-Related
|
// Package-Related
|
||||||
is_package_imported :: proc(package_name: string) -> bool ---
|
is_package_imported :: proc(package_name: string) -> bool ---
|
||||||
|
|
||||||
|
// Matrix Related Procedures
|
||||||
|
transpose :: proc(m: $T/matrix[$R, $C]$E) -> matrix[C, R]E ---
|
||||||
|
outer_product :: proc(a: $A/[$X]$E, b: $B/[$Y]E) -> matrix[X, Y]E ---
|
||||||
|
hadamard_product :: proc(a, b: $T/matrix[$R, $C]$E) -> T ---
|
||||||
|
matrix_flatten :: proc(m: $T/matrix[$R, $C]$E) -> [R*C]E ---
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
soa_struct :: proc($N: int, $T: typeid) -> type/#soa[N]T
|
soa_struct :: proc($N: int, $T: typeid) -> type/#soa[N]T
|
||||||
|
|
||||||
@@ -18,9 +18,10 @@
|
|||||||
// This could change at a later date if the all these data structures are
|
// This could change at a later date if the all these data structures are
|
||||||
// implemented within the compiler rather than in this "preload" file
|
// implemented within the compiler rather than in this "preload" file
|
||||||
//
|
//
|
||||||
|
//+no-instrumentation
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
// NOTE(bill): This must match the compiler's
|
// NOTE(bill): This must match the compiler's
|
||||||
Calling_Convention :: enum u8 {
|
Calling_Convention :: enum u8 {
|
||||||
@@ -306,6 +307,7 @@ Allocator_Mode :: enum byte {
|
|||||||
Query_Features,
|
Query_Features,
|
||||||
Query_Info,
|
Query_Info,
|
||||||
Alloc_Non_Zeroed,
|
Alloc_Non_Zeroed,
|
||||||
|
Resize_Non_Zeroed,
|
||||||
}
|
}
|
||||||
|
|
||||||
Allocator_Mode_Set :: distinct bit_set[Allocator_Mode]
|
Allocator_Mode_Set :: distinct bit_set[Allocator_Mode]
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
Maybe :: union($T: typeid) {T}
|
Maybe :: union($T: typeid) {T}
|
||||||
@@ -169,10 +169,16 @@ clear :: proc{clear_dynamic_array, clear_map}
|
|||||||
@builtin
|
@builtin
|
||||||
reserve :: proc{reserve_dynamic_array, reserve_map}
|
reserve :: proc{reserve_dynamic_array, reserve_map}
|
||||||
|
|
||||||
// `resize` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
|
@builtin
|
||||||
|
non_zero_reserve :: proc{non_zero_reserve_dynamic_array}
|
||||||
|
|
||||||
|
// `resize` will try to resize memory of a passed dynamic array to the requested element count (setting the `len`, and possibly `cap`).
|
||||||
@builtin
|
@builtin
|
||||||
resize :: proc{resize_dynamic_array}
|
resize :: proc{resize_dynamic_array}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
non_zero_resize :: proc{non_zero_resize_dynamic_array}
|
||||||
|
|
||||||
// Shrinks the capacity of a dynamic array or map down to the current length, or the given capacity.
|
// Shrinks the capacity of a dynamic array or map down to the current length, or the given capacity.
|
||||||
@builtin
|
@builtin
|
||||||
shrink :: proc{shrink_dynamic_array, shrink_map}
|
shrink :: proc{shrink_dynamic_array, shrink_map}
|
||||||
@@ -234,6 +240,8 @@ delete :: proc{
|
|||||||
delete_dynamic_array,
|
delete_dynamic_array,
|
||||||
delete_slice,
|
delete_slice,
|
||||||
delete_map,
|
delete_map,
|
||||||
|
delete_soa_slice,
|
||||||
|
delete_soa_dynamic_array,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -304,6 +312,7 @@ make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, alloca
|
|||||||
@(builtin, require_results)
|
@(builtin, require_results)
|
||||||
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
||||||
make_dynamic_array_error_loc(loc, len, cap)
|
make_dynamic_array_error_loc(loc, len, cap)
|
||||||
|
array.allocator = allocator // initialize allocator before just in case it fails to allocate any memory
|
||||||
data := mem_alloc_bytes(size_of(E)*cap, align_of(E), allocator, loc) or_return
|
data := mem_alloc_bytes(size_of(E)*cap, align_of(E), allocator, loc) or_return
|
||||||
s := Raw_Dynamic_Array{raw_data(data), len, cap, allocator}
|
s := Raw_Dynamic_Array{raw_data(data), len, cap, allocator}
|
||||||
if data == nil && size_of(E) != 0 {
|
if data == nil && size_of(E) != 0 {
|
||||||
@@ -346,7 +355,7 @@ make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := con
|
|||||||
//
|
//
|
||||||
// Similar to `new`, the first argument is a type, not a value. Unlike new, make's return type is the same as the
|
// Similar to `new`, the first argument is a type, not a value. Unlike new, make's return type is the same as the
|
||||||
// type of its argument, not a pointer to it.
|
// type of its argument, not a pointer to it.
|
||||||
// Make uses the specified allocator, default is context.allocator, default is context.allocator
|
// Make uses the specified allocator, default is context.allocator.
|
||||||
@builtin
|
@builtin
|
||||||
make :: proc{
|
make :: proc{
|
||||||
make_slice,
|
make_slice,
|
||||||
@@ -404,10 +413,7 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_append_elem :: #force_inline proc(array: ^$T/[dynamic]$E, arg: E, should_zero: bool, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
|
||||||
@builtin
|
|
||||||
append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
@@ -418,7 +424,13 @@ append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) ->
|
|||||||
} else {
|
} else {
|
||||||
if cap(array) < len(array)+1 {
|
if cap(array) < len(array)+1 {
|
||||||
cap := 2 * cap(array) + max(8, 1)
|
cap := 2 * cap(array) + max(8, 1)
|
||||||
err = reserve(array, cap, loc) // do not 'or_return' here as it could be a partial success
|
|
||||||
|
// do not 'or_return' here as it could be a partial success
|
||||||
|
if should_zero {
|
||||||
|
err = reserve(array, cap, loc)
|
||||||
|
} else {
|
||||||
|
err = non_zero_reserve(array, cap, loc)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if cap(array)-len(array) > 0 {
|
if cap(array)-len(array) > 0 {
|
||||||
a := (^Raw_Dynamic_Array)(array)
|
a := (^Raw_Dynamic_Array)(array)
|
||||||
@@ -435,7 +447,16 @@ append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
return _append_elem(array, arg, true, loc=loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
return _append_elem(array, arg, false, loc=loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
_append_elems :: #force_inline proc(array: ^$T/[dynamic]$E, should_zero: bool, loc := #caller_location, args: ..E) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
@@ -452,7 +473,13 @@ append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location)
|
|||||||
} else {
|
} else {
|
||||||
if cap(array) < len(array)+arg_len {
|
if cap(array) < len(array)+arg_len {
|
||||||
cap := 2 * cap(array) + max(8, arg_len)
|
cap := 2 * cap(array) + max(8, arg_len)
|
||||||
err = reserve(array, cap, loc) // do not 'or_return' here as it could be a partial success
|
|
||||||
|
// do not 'or_return' here as it could be a partial success
|
||||||
|
if should_zero {
|
||||||
|
err = reserve(array, cap, loc)
|
||||||
|
} else {
|
||||||
|
err = non_zero_reserve(array, cap, loc)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
arg_len = min(cap(array)-len(array), arg_len)
|
arg_len = min(cap(array)-len(array), arg_len)
|
||||||
if arg_len > 0 {
|
if arg_len > 0 {
|
||||||
@@ -468,11 +495,33 @@ append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
return _append_elems(array, true, loc, ..args)
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
return _append_elems(array, false, loc, ..args)
|
||||||
|
}
|
||||||
|
|
||||||
// The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type
|
// The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type
|
||||||
|
_append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, should_zero: bool, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
args := transmute([]E)arg
|
||||||
|
if should_zero {
|
||||||
|
return append_elems(array, ..args, loc=loc)
|
||||||
|
} else {
|
||||||
|
return non_zero_append_elems(array, ..args, loc=loc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
args := transmute([]E)arg
|
return _append_elem_string(array, arg, true, loc)
|
||||||
return append_elems(array, ..args, loc=loc)
|
}
|
||||||
|
@builtin
|
||||||
|
non_zero_append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
return _append_elem_string(array, arg, false, loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -492,6 +541,7 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_
|
|||||||
|
|
||||||
// The append built-in procedure appends elements to the end of a dynamic array
|
// The append built-in procedure appends elements to the end of a dynamic array
|
||||||
@builtin append :: proc{append_elem, append_elems, append_elem_string}
|
@builtin append :: proc{append_elem, append_elems, append_elem_string}
|
||||||
|
@builtin non_zero_append :: proc{non_zero_append_elem, non_zero_append_elems, non_zero_append_elem_string}
|
||||||
|
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
@@ -587,11 +637,14 @@ assign_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle
|
|||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||||
if index+len(args) < len(array) {
|
new_size := index + len(args)
|
||||||
|
if len(args) == 0 {
|
||||||
|
ok = true
|
||||||
|
} else if new_size < len(array) {
|
||||||
copy(array[index:], args)
|
copy(array[index:], args)
|
||||||
ok = true
|
ok = true
|
||||||
} else {
|
} else {
|
||||||
resize(array, index+1+len(args), loc) or_return
|
resize(array, new_size, loc) or_return
|
||||||
copy(array[index:], args)
|
copy(array[index:], args)
|
||||||
ok = true
|
ok = true
|
||||||
}
|
}
|
||||||
@@ -633,8 +686,7 @@ clear_dynamic_array :: proc "contextless" (array: ^$T/[dynamic]$E) {
|
|||||||
// `reserve_dynamic_array` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
|
// `reserve_dynamic_array` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
|
||||||
//
|
//
|
||||||
// Note: Prefer the procedure group `reserve`.
|
// Note: Prefer the procedure group `reserve`.
|
||||||
@builtin
|
_reserve_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, capacity: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
|
||||||
reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
|
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -653,7 +705,12 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal
|
|||||||
new_size := capacity * size_of(E)
|
new_size := capacity * size_of(E)
|
||||||
allocator := a.allocator
|
allocator := a.allocator
|
||||||
|
|
||||||
new_data := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
new_data: []byte
|
||||||
|
if should_zero {
|
||||||
|
new_data = mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
||||||
|
} else {
|
||||||
|
new_data = non_zero_mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
||||||
|
}
|
||||||
if new_data == nil && new_size > 0 {
|
if new_data == nil && new_size > 0 {
|
||||||
return .Out_Of_Memory
|
return .Out_Of_Memory
|
||||||
}
|
}
|
||||||
@@ -663,11 +720,20 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
|
||||||
|
return _reserve_dynamic_array(array, capacity, true, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
non_zero_reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
|
||||||
|
return _reserve_dynamic_array(array, capacity, false, loc)
|
||||||
|
}
|
||||||
|
|
||||||
// `resize_dynamic_array` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
|
// `resize_dynamic_array` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
|
||||||
//
|
//
|
||||||
// Note: Prefer the procedure group `resize`
|
// Note: Prefer the procedure group `resize`
|
||||||
@builtin
|
_resize_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, length: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
|
||||||
resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
|
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -687,7 +753,12 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
|
|||||||
new_size := length * size_of(E)
|
new_size := length * size_of(E)
|
||||||
allocator := a.allocator
|
allocator := a.allocator
|
||||||
|
|
||||||
new_data := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
new_data : []byte
|
||||||
|
if should_zero {
|
||||||
|
new_data = mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
||||||
|
} else {
|
||||||
|
new_data = non_zero_mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
||||||
|
}
|
||||||
if new_data == nil && new_size > 0 {
|
if new_data == nil && new_size > 0 {
|
||||||
return .Out_Of_Memory
|
return .Out_Of_Memory
|
||||||
}
|
}
|
||||||
@@ -698,6 +769,16 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
|
||||||
|
return _resize_dynamic_array(array, length, true, loc=loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
|
||||||
|
return _resize_dynamic_array(array, length, false, loc=loc)
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Shrinks the capacity of a dynamic array down to the current length, or the given capacity.
|
Shrinks the capacity of a dynamic array down to the current length, or the given capacity.
|
||||||
|
|
||||||
@@ -744,39 +825,6 @@ map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@builtin
|
|
||||||
incl_elem :: proc(s: ^$S/bit_set[$E; $U], elem: E) {
|
|
||||||
s^ |= {elem}
|
|
||||||
}
|
|
||||||
@builtin
|
|
||||||
incl_elems :: proc(s: ^$S/bit_set[$E; $U], elems: ..E) {
|
|
||||||
for elem in elems {
|
|
||||||
s^ |= {elem}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@builtin
|
|
||||||
incl_bit_set :: proc(s: ^$S/bit_set[$E; $U], other: S) {
|
|
||||||
s^ |= other
|
|
||||||
}
|
|
||||||
@builtin
|
|
||||||
excl_elem :: proc(s: ^$S/bit_set[$E; $U], elem: E) {
|
|
||||||
s^ &~= {elem}
|
|
||||||
}
|
|
||||||
@builtin
|
|
||||||
excl_elems :: proc(s: ^$S/bit_set[$E; $U], elems: ..E) {
|
|
||||||
for elem in elems {
|
|
||||||
s^ &~= {elem}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@builtin
|
|
||||||
excl_bit_set :: proc(s: ^$S/bit_set[$E; $U], other: S) {
|
|
||||||
s^ &~= other
|
|
||||||
}
|
|
||||||
|
|
||||||
@builtin incl :: proc{incl_elem, incl_elems, incl_bit_set}
|
|
||||||
@builtin excl :: proc{excl_elem, excl_elems, excl_bit_set}
|
|
||||||
|
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
card :: proc(s: $S/bit_set[$E; $U]) -> int {
|
card :: proc(s: $S/bit_set[$E; $U]) -> int {
|
||||||
when size_of(S) == 1 {
|
when size_of(S) == 1 {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
_ :: intrinsics
|
_ :: intrinsics
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -86,6 +86,7 @@ make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, alloc
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
array.allocator = allocator
|
||||||
footer := raw_soa_footer(&array)
|
footer := raw_soa_footer(&array)
|
||||||
if size_of(E) == 0 {
|
if size_of(E) == 0 {
|
||||||
footer.len = length
|
footer.len = length
|
||||||
+11
-11
@@ -1,6 +1,6 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE :: uint(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE)
|
DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE :: uint(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE)
|
||||||
|
|
||||||
@@ -28,11 +28,11 @@ safe_add :: #force_inline proc "contextless" (x, y: uint) -> (uint, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
memory_block_alloc :: proc(allocator: Allocator, capacity: uint, loc := #caller_location) -> (block: ^Memory_Block, err: Allocator_Error) {
|
memory_block_alloc :: proc(allocator: Allocator, capacity: uint, alignment: uint, loc := #caller_location) -> (block: ^Memory_Block, err: Allocator_Error) {
|
||||||
total_size := uint(capacity + size_of(Memory_Block))
|
total_size := uint(capacity + max(alignment, size_of(Memory_Block)))
|
||||||
base_offset := uintptr(size_of(Memory_Block))
|
base_offset := uintptr(max(alignment, size_of(Memory_Block)))
|
||||||
|
|
||||||
min_alignment: int = max(16, align_of(Memory_Block))
|
min_alignment: int = max(16, align_of(Memory_Block), int(alignment))
|
||||||
data := mem_alloc(int(total_size), min_alignment, allocator, loc) or_return
|
data := mem_alloc(int(total_size), min_alignment, allocator, loc) or_return
|
||||||
block = (^Memory_Block)(raw_data(data))
|
block = (^Memory_Block)(raw_data(data))
|
||||||
end := uintptr(raw_data(data)[len(data):])
|
end := uintptr(raw_data(data)[len(data):])
|
||||||
@@ -103,19 +103,19 @@ arena_alloc :: proc(arena: ^Arena, size, alignment: uint, loc := #caller_locatio
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if arena.curr_block == nil || (safe_add(arena.curr_block.used, size) or_else 0) > arena.curr_block.capacity {
|
needed := align_forward_uint(size, alignment)
|
||||||
size = align_forward_uint(size, alignment)
|
if arena.curr_block == nil || (safe_add(arena.curr_block.used, needed) or_else 0) > arena.curr_block.capacity {
|
||||||
if arena.minimum_block_size == 0 {
|
if arena.minimum_block_size == 0 {
|
||||||
arena.minimum_block_size = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE
|
arena.minimum_block_size = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
block_size := max(size, arena.minimum_block_size)
|
block_size := max(needed, arena.minimum_block_size)
|
||||||
|
|
||||||
if arena.backing_allocator.procedure == nil {
|
if arena.backing_allocator.procedure == nil {
|
||||||
arena.backing_allocator = default_allocator()
|
arena.backing_allocator = default_allocator()
|
||||||
}
|
}
|
||||||
|
|
||||||
new_block := memory_block_alloc(arena.backing_allocator, block_size, loc) or_return
|
new_block := memory_block_alloc(arena.backing_allocator, block_size, alignment, loc) or_return
|
||||||
new_block.prev = arena.curr_block
|
new_block.prev = arena.curr_block
|
||||||
arena.curr_block = new_block
|
arena.curr_block = new_block
|
||||||
arena.total_capacity += new_block.capacity
|
arena.total_capacity += new_block.capacity
|
||||||
@@ -134,7 +134,7 @@ arena_init :: proc(arena: ^Arena, size: uint, backing_allocator: Allocator, loc
|
|||||||
arena^ = {}
|
arena^ = {}
|
||||||
arena.backing_allocator = backing_allocator
|
arena.backing_allocator = backing_allocator
|
||||||
arena.minimum_block_size = max(size, 1<<12) // minimum block size of 4 KiB
|
arena.minimum_block_size = max(size, 1<<12) // minimum block size of 4 KiB
|
||||||
new_block := memory_block_alloc(arena.backing_allocator, arena.minimum_block_size, loc) or_return
|
new_block := memory_block_alloc(arena.backing_allocator, arena.minimum_block_size, 0, loc) or_return
|
||||||
arena.curr_block = new_block
|
arena.curr_block = new_block
|
||||||
arena.total_capacity += new_block.capacity
|
arena.total_capacity += new_block.capacity
|
||||||
return nil
|
return nil
|
||||||
@@ -195,7 +195,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
err = .Mode_Not_Implemented
|
err = .Mode_Not_Implemented
|
||||||
case .Free_All:
|
case .Free_All:
|
||||||
arena_free_all(arena, location)
|
arena_free_all(arena, location)
|
||||||
case .Resize:
|
case .Resize, .Resize_Non_Zeroed:
|
||||||
old_data := ([^]byte)(old_memory)
|
old_data := ([^]byte)(old_memory)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
|
||||||
|
default_allocator_proc :: nil_allocator_proc
|
||||||
|
default_allocator :: nil_allocator
|
||||||
|
} else when ODIN_DEFAULT_TO_PANIC_ALLOCATOR {
|
||||||
|
default_allocator_proc :: panic_allocator_proc
|
||||||
|
default_allocator :: panic_allocator
|
||||||
|
} else {
|
||||||
|
default_allocator :: heap_allocator
|
||||||
|
default_allocator_proc :: heap_allocator_proc
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
return nil, .None
|
return nil, .None
|
||||||
case .Free_All:
|
case .Free_All:
|
||||||
return nil, .Mode_Not_Implemented
|
return nil, .Mode_Not_Implemented
|
||||||
case .Resize:
|
case .Resize, .Resize_Non_Zeroed:
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return nil, .None
|
return nil, .None
|
||||||
}
|
}
|
||||||
@@ -31,14 +31,6 @@ nil_allocator :: proc() -> Allocator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
when ODIN_OS == .Freestanding {
|
|
||||||
default_allocator_proc :: nil_allocator_proc
|
|
||||||
default_allocator :: nil_allocator
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||||
size, alignment: int,
|
size, alignment: int,
|
||||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||||
@@ -55,6 +47,10 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
if size > 0 {
|
if size > 0 {
|
||||||
panic("panic allocator, .Resize called", loc=loc)
|
panic("panic allocator, .Resize called", loc=loc)
|
||||||
}
|
}
|
||||||
|
case .Resize_Non_Zeroed:
|
||||||
|
if size > 0 {
|
||||||
|
panic("panic allocator, .Alloc_Non_Zeroed called", loc=loc)
|
||||||
|
}
|
||||||
case .Free:
|
case .Free:
|
||||||
if old_memory != nil {
|
if old_memory != nil {
|
||||||
panic("panic allocator, .Free called", loc=loc)
|
panic("panic allocator, .Free called", loc=loc)
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
_ :: intrinsics
|
_ :: intrinsics
|
||||||
|
|
||||||
// High performance, cache-friendly, open-addressed Robin Hood hashing hash map
|
// High performance, cache-friendly, open-addressed Robin Hood hashing hash map
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
//+private
|
//+private
|
||||||
//+build linux, darwin, freebsd, openbsd
|
//+build linux, darwin, freebsd, openbsd
|
||||||
|
//+no-instrumentation
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
when ODIN_BUILD_MODE == .Dynamic {
|
when ODIN_BUILD_MODE == .Dynamic {
|
||||||
@(link_name="_odin_entry_point", linkage="strong", require/*, link_section=".init"*/)
|
@(link_name="_odin_entry_point", linkage="strong", require/*, link_section=".init"*/)
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
//+private
|
//+private
|
||||||
//+build wasm32, wasm64p32
|
//+build wasm32, wasm64p32
|
||||||
|
//+no-instrumentation
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
|
when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
|
||||||
@(link_name="_start", linkage="strong", require, export)
|
@(link_name="_start", linkage="strong", require, export)
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
//+private
|
//+private
|
||||||
//+build windows
|
//+build windows
|
||||||
|
//+no-instrumentation
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
when ODIN_BUILD_MODE == .Dynamic {
|
when ODIN_BUILD_MODE == .Dynamic {
|
||||||
@(link_name="DllMain", linkage="strong", require)
|
@(link_name="DllMain", linkage="strong", require)
|
||||||
DllMain :: proc "stdcall" (hinstDLL: rawptr, fdwReason: u32, lpReserved: rawptr) -> b32 {
|
DllMain :: proc "system" (hinstDLL: rawptr, fdwReason: u32, lpReserved: rawptr) -> b32 {
|
||||||
context = default_context()
|
context = default_context()
|
||||||
|
|
||||||
// Populate Windows DLL-specific global
|
// Populate Windows DLL-specific global
|
||||||
@@ -28,7 +29,7 @@ when ODIN_BUILD_MODE == .Dynamic {
|
|||||||
} else when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
|
} else when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
|
||||||
when ODIN_ARCH == .i386 || ODIN_NO_CRT {
|
when ODIN_ARCH == .i386 || ODIN_NO_CRT {
|
||||||
@(link_name="mainCRTStartup", linkage="strong", require)
|
@(link_name="mainCRTStartup", linkage="strong", require)
|
||||||
mainCRTStartup :: proc "stdcall" () -> i32 {
|
mainCRTStartup :: proc "system" () -> i32 {
|
||||||
context = default_context()
|
context = default_context()
|
||||||
#force_no_inline _startup_runtime()
|
#force_no_inline _startup_runtime()
|
||||||
intrinsics.__entry_point()
|
intrinsics.__entry_point()
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
|
@(no_instrumentation)
|
||||||
bounds_trap :: proc "contextless" () -> ! {
|
bounds_trap :: proc "contextless" () -> ! {
|
||||||
when ODIN_OS == .Windows {
|
when ODIN_OS == .Windows {
|
||||||
windows_trap_array_bounds()
|
windows_trap_array_bounds()
|
||||||
@@ -8,6 +9,7 @@ bounds_trap :: proc "contextless" () -> ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(no_instrumentation)
|
||||||
type_assertion_trap :: proc "contextless" () -> ! {
|
type_assertion_trap :: proc "contextless" () -> ! {
|
||||||
when ODIN_OS == .Windows {
|
when ODIN_OS == .Windows {
|
||||||
windows_trap_type_assertion()
|
windows_trap_type_assertion()
|
||||||
@@ -21,7 +23,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index
|
|||||||
if uint(index) < uint(count) {
|
if uint(index) < uint(count) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (file: string, line, column: i32, index, count: int) -> ! {
|
handle_error :: proc "contextless" (file: string, line, column: i32, index, count: int) -> ! {
|
||||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||||
print_string(" Index ")
|
print_string(" Index ")
|
||||||
@@ -34,6 +36,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index
|
|||||||
handle_error(file, line, column, index, count)
|
handle_error(file, line, column, index, count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(no_instrumentation)
|
||||||
slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int, len: int) -> ! {
|
slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int, len: int) -> ! {
|
||||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||||
print_string(" Invalid slice indices ")
|
print_string(" Invalid slice indices ")
|
||||||
@@ -46,6 +49,7 @@ slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, h
|
|||||||
bounds_trap()
|
bounds_trap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(no_instrumentation)
|
||||||
multi_pointer_slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int) -> ! {
|
multi_pointer_slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int) -> ! {
|
||||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||||
print_string(" Invalid slice indices ")
|
print_string(" Invalid slice indices ")
|
||||||
@@ -82,7 +86,7 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32,
|
|||||||
if 0 <= low && low <= high && high <= max {
|
if 0 <= low && low <= high && high <= max {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) -> ! {
|
handle_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) -> ! {
|
||||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||||
print_string(" Invalid dynamic array indices ")
|
print_string(" Invalid dynamic array indices ")
|
||||||
@@ -103,7 +107,7 @@ matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32
|
|||||||
uint(column_index) < uint(column_count) {
|
uint(column_index) < uint(column_count) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) -> ! {
|
handle_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) -> ! {
|
||||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||||
print_string(" Matrix indices [")
|
print_string(" Matrix indices [")
|
||||||
@@ -127,7 +131,7 @@ when ODIN_NO_RTTI {
|
|||||||
if ok {
|
if ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
|
handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
|
||||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||||
print_string(" Invalid type assertion\n")
|
print_string(" Invalid type assertion\n")
|
||||||
@@ -140,7 +144,7 @@ when ODIN_NO_RTTI {
|
|||||||
if ok {
|
if ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
|
handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
|
||||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||||
print_string(" Invalid type assertion\n")
|
print_string(" Invalid type assertion\n")
|
||||||
@@ -153,7 +157,7 @@ when ODIN_NO_RTTI {
|
|||||||
if ok {
|
if ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid) -> ! {
|
handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid) -> ! {
|
||||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||||
print_string(" Invalid type assertion from ")
|
print_string(" Invalid type assertion from ")
|
||||||
@@ -198,7 +202,7 @@ when ODIN_NO_RTTI {
|
|||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! {
|
handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! {
|
||||||
|
|
||||||
actual := variant_type(from, from_data)
|
actual := variant_type(from, from_data)
|
||||||
@@ -224,7 +228,7 @@ make_slice_error_loc :: #force_inline proc "contextless" (loc := #caller_locatio
|
|||||||
if 0 <= len {
|
if 0 <= len {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (loc: Source_Code_Location, len: int) -> ! {
|
handle_error :: proc "contextless" (loc: Source_Code_Location, len: int) -> ! {
|
||||||
print_caller_location(loc)
|
print_caller_location(loc)
|
||||||
print_string(" Invalid slice length for make: ")
|
print_string(" Invalid slice length for make: ")
|
||||||
@@ -239,7 +243,7 @@ make_dynamic_array_error_loc :: #force_inline proc "contextless" (loc := #caller
|
|||||||
if 0 <= len && len <= cap {
|
if 0 <= len && len <= cap {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (loc: Source_Code_Location, len, cap: int) -> ! {
|
handle_error :: proc "contextless" (loc: Source_Code_Location, len, cap: int) -> ! {
|
||||||
print_caller_location(loc)
|
print_caller_location(loc)
|
||||||
print_string(" Invalid dynamic array parameters for make: ")
|
print_string(" Invalid dynamic array parameters for make: ")
|
||||||
@@ -256,7 +260,7 @@ make_map_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_loca
|
|||||||
if 0 <= cap {
|
if 0 <= cap {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (loc: Source_Code_Location, cap: int) -> ! {
|
handle_error :: proc "contextless" (loc: Source_Code_Location, cap: int) -> ! {
|
||||||
print_caller_location(loc)
|
print_caller_location(loc)
|
||||||
print_string(" Invalid map capacity for make: ")
|
print_string(" Invalid map capacity for make: ")
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
|
||||||
|
heap_allocator :: proc() -> Allocator {
|
||||||
|
return Allocator{
|
||||||
|
procedure = heap_allocator_proc,
|
||||||
|
data = nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
heap_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||||
|
size, alignment: int,
|
||||||
|
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||||
|
//
|
||||||
|
// NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
|
||||||
|
// Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert
|
||||||
|
// padding. We also store the original pointer returned by heap_alloc right before
|
||||||
|
// the pointer we return to the user.
|
||||||
|
//
|
||||||
|
|
||||||
|
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil, zero_memory := true) -> ([]byte, Allocator_Error) {
|
||||||
|
a := max(alignment, align_of(rawptr))
|
||||||
|
space := size + a - 1
|
||||||
|
|
||||||
|
allocated_mem: rawptr
|
||||||
|
if old_ptr != nil {
|
||||||
|
original_old_ptr := ([^]rawptr)(old_ptr)[-1]
|
||||||
|
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
|
||||||
|
} else {
|
||||||
|
allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
|
||||||
|
}
|
||||||
|
aligned_mem := rawptr(([^]u8)(allocated_mem)[size_of(rawptr):])
|
||||||
|
|
||||||
|
ptr := uintptr(aligned_mem)
|
||||||
|
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a)
|
||||||
|
diff := int(aligned_ptr - ptr)
|
||||||
|
if (size + diff) > space || allocated_mem == nil {
|
||||||
|
return nil, .Out_Of_Memory
|
||||||
|
}
|
||||||
|
|
||||||
|
aligned_mem = rawptr(aligned_ptr)
|
||||||
|
([^]rawptr)(aligned_mem)[-1] = allocated_mem
|
||||||
|
|
||||||
|
return byte_slice(aligned_mem, size), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
aligned_free :: proc(p: rawptr) {
|
||||||
|
if p != nil {
|
||||||
|
heap_free(([^]rawptr)(p)[-1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int, zero_memory := true) -> (new_memory: []byte, err: Allocator_Error) {
|
||||||
|
if p == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
new_memory = aligned_alloc(new_size, new_alignment, p, zero_memory) or_return
|
||||||
|
|
||||||
|
// NOTE: heap_resize does not zero the new memory, so we do it
|
||||||
|
if zero_memory && new_size > old_size {
|
||||||
|
new_region := raw_data(new_memory[old_size:])
|
||||||
|
intrinsics.mem_zero(new_region, new_size - old_size)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch mode {
|
||||||
|
case .Alloc, .Alloc_Non_Zeroed:
|
||||||
|
return aligned_alloc(size, alignment, nil, mode == .Alloc)
|
||||||
|
|
||||||
|
case .Free:
|
||||||
|
aligned_free(old_memory)
|
||||||
|
|
||||||
|
case .Free_All:
|
||||||
|
return nil, .Mode_Not_Implemented
|
||||||
|
|
||||||
|
case .Resize, .Resize_Non_Zeroed:
|
||||||
|
if old_memory == nil {
|
||||||
|
return aligned_alloc(size, alignment, nil, mode == .Resize)
|
||||||
|
}
|
||||||
|
return aligned_resize(old_memory, old_size, size, alignment, mode == .Resize)
|
||||||
|
|
||||||
|
case .Query_Features:
|
||||||
|
set := (^Allocator_Mode_Set)(old_memory)
|
||||||
|
if set != nil {
|
||||||
|
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Resize, .Resize_Non_Zeroed, .Query_Features}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
|
||||||
|
case .Query_Info:
|
||||||
|
return nil, .Mode_Not_Implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||||
|
return _heap_alloc(size, zero_memory)
|
||||||
|
}
|
||||||
|
|
||||||
|
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||||
|
return _heap_resize(ptr, new_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
heap_free :: proc(ptr: rawptr) {
|
||||||
|
_heap_free(ptr)
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
//+build js, wasi, freestanding, essence
|
||||||
|
//+private
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
_heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||||
|
unimplemented("base:runtime 'heap_alloc' procedure is not supported on this platform")
|
||||||
|
}
|
||||||
|
|
||||||
|
_heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||||
|
unimplemented("base:runtime 'heap_resize' procedure is not supported on this platform")
|
||||||
|
}
|
||||||
|
|
||||||
|
_heap_free :: proc(ptr: rawptr) {
|
||||||
|
unimplemented("base:runtime 'heap_free' procedure is not supported on this platform")
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
//+build linux, darwin, freebsd, openbsd
|
||||||
|
//+private
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
when ODIN_OS == .Darwin {
|
||||||
|
foreign import libc "system:System.framework"
|
||||||
|
} else {
|
||||||
|
foreign import libc "system:c"
|
||||||
|
}
|
||||||
|
|
||||||
|
@(default_calling_convention="c")
|
||||||
|
foreign libc {
|
||||||
|
@(link_name="malloc") _unix_malloc :: proc(size: int) -> rawptr ---
|
||||||
|
@(link_name="calloc") _unix_calloc :: proc(num, size: int) -> rawptr ---
|
||||||
|
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---
|
||||||
|
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr ---
|
||||||
|
}
|
||||||
|
|
||||||
|
_heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||||
|
if size <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if zero_memory {
|
||||||
|
return _unix_calloc(1, size)
|
||||||
|
} else {
|
||||||
|
return _unix_malloc(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||||
|
// NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
|
||||||
|
// POSIX platforms. Ensure your caller takes this into account.
|
||||||
|
return _unix_realloc(ptr, new_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
_heap_free :: proc(ptr: rawptr) {
|
||||||
|
_unix_free(ptr)
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
foreign import kernel32 "system:Kernel32.lib"
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
@(default_calling_convention="system")
|
||||||
|
foreign kernel32 {
|
||||||
|
// NOTE(bill): The types are not using the standard names (e.g. DWORD and LPVOID) to just minimizing the dependency
|
||||||
|
|
||||||
|
// default_allocator
|
||||||
|
GetProcessHeap :: proc() -> rawptr ---
|
||||||
|
HeapAlloc :: proc(hHeap: rawptr, dwFlags: u32, dwBytes: uint) -> rawptr ---
|
||||||
|
HeapReAlloc :: proc(hHeap: rawptr, dwFlags: u32, lpMem: rawptr, dwBytes: uint) -> rawptr ---
|
||||||
|
HeapFree :: proc(hHeap: rawptr, dwFlags: u32, lpMem: rawptr) -> b32 ---
|
||||||
|
}
|
||||||
|
|
||||||
|
_heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||||
|
HEAP_ZERO_MEMORY :: 0x00000008
|
||||||
|
return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY if zero_memory else 0, uint(size))
|
||||||
|
}
|
||||||
|
_heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||||
|
if new_size == 0 {
|
||||||
|
_heap_free(ptr)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if ptr == nil {
|
||||||
|
return _heap_alloc(new_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
HEAP_ZERO_MEMORY :: 0x00000008
|
||||||
|
return HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ptr, uint(new_size))
|
||||||
|
}
|
||||||
|
_heap_free :: proc(ptr: rawptr) {
|
||||||
|
if ptr == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
HeapFree(GetProcessHeap(), 0, ptr)
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
@(private="file")
|
@(private="file")
|
||||||
IS_WASM :: ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32
|
IS_WASM :: ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32
|
||||||
@@ -22,50 +22,6 @@ byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byt
|
|||||||
return ([^]byte)(data)[:max(len, 0)]
|
return ([^]byte)(data)[:max(len, 0)]
|
||||||
}
|
}
|
||||||
|
|
||||||
bswap_16 :: proc "contextless" (x: u16) -> u16 {
|
|
||||||
return x>>8 | x<<8
|
|
||||||
}
|
|
||||||
|
|
||||||
bswap_32 :: proc "contextless" (x: u32) -> u32 {
|
|
||||||
return x>>24 | (x>>8)&0xff00 | (x<<8)&0xff0000 | x<<24
|
|
||||||
}
|
|
||||||
|
|
||||||
bswap_64 :: proc "contextless" (x: u64) -> u64 {
|
|
||||||
z := x
|
|
||||||
z = (z & 0x00000000ffffffff) << 32 | (z & 0xffffffff00000000) >> 32
|
|
||||||
z = (z & 0x0000ffff0000ffff) << 16 | (z & 0xffff0000ffff0000) >> 16
|
|
||||||
z = (z & 0x00ff00ff00ff00ff) << 8 | (z & 0xff00ff00ff00ff00) >> 8
|
|
||||||
return z
|
|
||||||
}
|
|
||||||
|
|
||||||
bswap_128 :: proc "contextless" (x: u128) -> u128 {
|
|
||||||
z := transmute([4]u32)x
|
|
||||||
z[0], z[3] = bswap_32(z[3]), bswap_32(z[0])
|
|
||||||
z[1], z[2] = bswap_32(z[2]), bswap_32(z[1])
|
|
||||||
return transmute(u128)z
|
|
||||||
}
|
|
||||||
|
|
||||||
bswap_f16 :: proc "contextless" (f: f16) -> f16 {
|
|
||||||
x := transmute(u16)f
|
|
||||||
z := bswap_16(x)
|
|
||||||
return transmute(f16)z
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bswap_f32 :: proc "contextless" (f: f32) -> f32 {
|
|
||||||
x := transmute(u32)f
|
|
||||||
z := bswap_32(x)
|
|
||||||
return transmute(f32)z
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bswap_f64 :: proc "contextless" (f: f64) -> f64 {
|
|
||||||
x := transmute(u64)f
|
|
||||||
z := bswap_64(x)
|
|
||||||
return transmute(f64)z
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
is_power_of_two_int :: #force_inline proc(x: int) -> bool {
|
is_power_of_two_int :: #force_inline proc(x: int) -> bool {
|
||||||
if x <= 0 {
|
if x <= 0 {
|
||||||
return false
|
return false
|
||||||
@@ -187,7 +143,7 @@ mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #calle
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
_mem_resize :: #force_inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, should_zero: bool, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||||
if allocator.procedure == nil {
|
if allocator.procedure == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -198,15 +154,27 @@ mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAUL
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
} else if ptr == nil {
|
} else if ptr == nil {
|
||||||
return allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
|
if should_zero {
|
||||||
|
return allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
|
||||||
|
} else {
|
||||||
|
return allocator.procedure(allocator.data, .Alloc_Non_Zeroed, new_size, alignment, nil, 0, loc)
|
||||||
|
}
|
||||||
} else if old_size == new_size && uintptr(ptr) % uintptr(alignment) == 0 {
|
} else if old_size == new_size && uintptr(ptr) % uintptr(alignment) == 0 {
|
||||||
data = ([^]byte)(ptr)[:old_size]
|
data = ([^]byte)(ptr)[:old_size]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err = allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc)
|
if should_zero {
|
||||||
|
data, err = allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc)
|
||||||
|
} else {
|
||||||
|
data, err = allocator.procedure(allocator.data, .Resize_Non_Zeroed, new_size, alignment, ptr, old_size, loc)
|
||||||
|
}
|
||||||
if err == .Mode_Not_Implemented {
|
if err == .Mode_Not_Implemented {
|
||||||
data, err = allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
|
if should_zero {
|
||||||
|
data, err = allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
|
||||||
|
} else {
|
||||||
|
data, err = allocator.procedure(allocator.data, .Alloc_Non_Zeroed, new_size, alignment, nil, 0, loc)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -216,6 +184,13 @@ mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAUL
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||||
|
return _mem_resize(ptr, old_size, new_size, alignment, allocator, true, loc)
|
||||||
|
}
|
||||||
|
non_zero_mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||||
|
return _mem_resize(ptr, old_size, new_size, alignment, allocator, false, loc)
|
||||||
|
}
|
||||||
|
|
||||||
memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
|
memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
|
||||||
switch {
|
switch {
|
||||||
case n == 0: return true
|
case n == 0: return true
|
||||||
@@ -589,36 +564,6 @@ string_decode_last_rune :: proc "contextless" (s: string) -> (rune, int) {
|
|||||||
return r, size
|
return r, size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
abs_f16 :: #force_inline proc "contextless" (x: f16) -> f16 {
|
|
||||||
return -x if x < 0 else x
|
|
||||||
}
|
|
||||||
abs_f32 :: #force_inline proc "contextless" (x: f32) -> f32 {
|
|
||||||
return -x if x < 0 else x
|
|
||||||
}
|
|
||||||
abs_f64 :: #force_inline proc "contextless" (x: f64) -> f64 {
|
|
||||||
return -x if x < 0 else x
|
|
||||||
}
|
|
||||||
|
|
||||||
min_f16 :: #force_inline proc "contextless" (a, b: f16) -> f16 {
|
|
||||||
return a if a < b else b
|
|
||||||
}
|
|
||||||
min_f32 :: #force_inline proc "contextless" (a, b: f32) -> f32 {
|
|
||||||
return a if a < b else b
|
|
||||||
}
|
|
||||||
min_f64 :: #force_inline proc "contextless" (a, b: f64) -> f64 {
|
|
||||||
return a if a < b else b
|
|
||||||
}
|
|
||||||
max_f16 :: #force_inline proc "contextless" (a, b: f16) -> f16 {
|
|
||||||
return a if a > b else b
|
|
||||||
}
|
|
||||||
max_f32 :: #force_inline proc "contextless" (a, b: f32) -> f32 {
|
|
||||||
return a if a > b else b
|
|
||||||
}
|
|
||||||
max_f64 :: #force_inline proc "contextless" (a, b: f64) -> f64 {
|
|
||||||
return a if a > b else b
|
|
||||||
}
|
|
||||||
|
|
||||||
abs_complex32 :: #force_inline proc "contextless" (x: complex32) -> f16 {
|
abs_complex32 :: #force_inline proc "contextless" (x: complex32) -> f16 {
|
||||||
p, q := abs(real(x)), abs(imag(x))
|
p, q := abs(real(x)), abs(imag(x))
|
||||||
if p < q {
|
if p < q {
|
||||||
@@ -730,7 +675,7 @@ mul_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
|
|||||||
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
|
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
|
||||||
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
|
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
|
||||||
|
|
||||||
return quaternion(t0, t1, t2, t3)
|
return quaternion(w=t0, x=t1, y=t2, z=t3)
|
||||||
}
|
}
|
||||||
|
|
||||||
mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
||||||
@@ -742,7 +687,7 @@ mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
|||||||
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
|
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
|
||||||
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
|
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
|
||||||
|
|
||||||
return quaternion(t0, t1, t2, t3)
|
return quaternion(w=t0, x=t1, y=t2, z=t3)
|
||||||
}
|
}
|
||||||
|
|
||||||
mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
||||||
@@ -754,7 +699,7 @@ mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
|||||||
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
|
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
|
||||||
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
|
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
|
||||||
|
|
||||||
return quaternion(t0, t1, t2, t3)
|
return quaternion(w=t0, x=t1, y=t2, z=t3)
|
||||||
}
|
}
|
||||||
|
|
||||||
quo_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
|
quo_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
|
||||||
@@ -768,7 +713,7 @@ quo_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
|
|||||||
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
|
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
|
||||||
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
|
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
|
||||||
|
|
||||||
return quaternion(t0, t1, t2, t3)
|
return quaternion(w=t0, x=t1, y=t2, z=t3)
|
||||||
}
|
}
|
||||||
|
|
||||||
quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
||||||
@@ -782,7 +727,7 @@ quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
|||||||
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
|
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
|
||||||
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
|
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
|
||||||
|
|
||||||
return quaternion(t0, t1, t2, t3)
|
return quaternion(w=t0, x=t1, y=t2, z=t3)
|
||||||
}
|
}
|
||||||
|
|
||||||
quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
||||||
@@ -796,7 +741,7 @@ quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
|||||||
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
|
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
|
||||||
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
|
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
|
||||||
|
|
||||||
return quaternion(t0, t1, t2, t3)
|
return quaternion(w=t0, x=t1, y=t2, z=t3)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(link_name="__truncsfhf2", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
@(link_name="__truncsfhf2", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
_OS_Errno :: distinct int
|
||||||
|
|
||||||
|
stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
|
return _stderr_write(data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
//+build freebsd, openbsd
|
||||||
|
//+private
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
foreign import libc "system:c"
|
||||||
|
|
||||||
|
@(default_calling_convention="c")
|
||||||
|
foreign libc {
|
||||||
|
@(link_name="write")
|
||||||
|
_unix_write :: proc(fd: i32, buf: rawptr, size: int) -> int ---
|
||||||
|
|
||||||
|
__error :: proc() -> ^i32 ---
|
||||||
|
}
|
||||||
|
|
||||||
|
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
|
ret := _unix_write(2, raw_data(data), len(data))
|
||||||
|
if ret < len(data) {
|
||||||
|
err := __error()
|
||||||
|
return int(ret), _OS_Errno(err^ if err != nil else 0)
|
||||||
|
}
|
||||||
|
return int(ret), 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
//+build darwin
|
||||||
|
//+private
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
foreign import libc "system:System.framework"
|
||||||
|
|
||||||
|
@(default_calling_convention="c")
|
||||||
|
foreign libc {
|
||||||
|
@(link_name="__stderrp")
|
||||||
|
_stderr: rawptr
|
||||||
|
|
||||||
|
@(link_name="fwrite")
|
||||||
|
_fwrite :: proc(ptr: rawptr, size: uint, nmemb: uint, stream: rawptr) -> uint ---
|
||||||
|
|
||||||
|
@(link_name="__error")
|
||||||
|
_get_errno :: proc() -> ^i32 ---
|
||||||
|
}
|
||||||
|
|
||||||
|
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
|
ret := _fwrite(raw_data(data), 1, len(data), _stderr)
|
||||||
|
if ret < len(data) {
|
||||||
|
err := _get_errno()
|
||||||
|
return int(ret), _OS_Errno(err^ if err != nil else 0)
|
||||||
|
}
|
||||||
|
return int(ret), 0
|
||||||
|
}
|
||||||
+2
-1
@@ -1,7 +1,8 @@
|
|||||||
//+build freestanding
|
//+build freestanding
|
||||||
|
//+private
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
// TODO(bill): reimplement `os.write`
|
// TODO(bill): reimplement `os.write`
|
||||||
_os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
return 0, -1
|
return 0, -1
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
//+build js
|
//+build js
|
||||||
|
//+private
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
foreign import "odin_env"
|
foreign import "odin_env"
|
||||||
|
|
||||||
_os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
foreign odin_env {
|
foreign odin_env {
|
||||||
write :: proc "contextless" (fd: u32, p: []byte) ---
|
write :: proc "contextless" (fd: u32, p: []byte) ---
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
//+private
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
|
||||||
|
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
|
when ODIN_ARCH == .amd64 {
|
||||||
|
SYS_write :: uintptr(1)
|
||||||
|
} else when ODIN_ARCH == .arm64 {
|
||||||
|
SYS_write :: uintptr(64)
|
||||||
|
} else when ODIN_ARCH == .i386 {
|
||||||
|
SYS_write :: uintptr(4)
|
||||||
|
} else when ODIN_ARCH == .arm32 {
|
||||||
|
SYS_write :: uintptr(4)
|
||||||
|
}
|
||||||
|
|
||||||
|
stderr :: 2
|
||||||
|
|
||||||
|
ret := int(intrinsics.syscall(SYS_write, uintptr(stderr), uintptr(raw_data(data)), uintptr(len(data))))
|
||||||
|
if ret < 0 && ret > -4096 {
|
||||||
|
return 0, _OS_Errno(-ret)
|
||||||
|
}
|
||||||
|
return ret, 0
|
||||||
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
//+build wasi
|
//+build wasi
|
||||||
|
//+private
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:sys/wasm/wasi"
|
import "core:sys/wasm/wasi"
|
||||||
|
|
||||||
_os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
data := (wasi.ciovec_t)(data)
|
data := (wasi.ciovec_t)(data)
|
||||||
n, err := wasi.fd_write(1, {data})
|
n, err := wasi.fd_write(1, {data})
|
||||||
return int(n), _OS_Errno(err)
|
return int(n), _OS_Errno(err)
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
//+build windows
|
||||||
|
//+private
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
foreign import kernel32 "system:Kernel32.lib"
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
@(default_calling_convention="system")
|
||||||
|
foreign kernel32 {
|
||||||
|
// NOTE(bill): The types are not using the standard names (e.g. DWORD and LPVOID) to just minimizing the dependency
|
||||||
|
|
||||||
|
// stderr_write
|
||||||
|
GetStdHandle :: proc(which: u32) -> rawptr ---
|
||||||
|
SetHandleInformation :: proc(hObject: rawptr, dwMask: u32, dwFlags: u32) -> b32 ---
|
||||||
|
WriteFile :: proc(hFile: rawptr, lpBuffer: rawptr, nNumberOfBytesToWrite: u32, lpNumberOfBytesWritten: ^u32, lpOverlapped: rawptr) -> b32 ---
|
||||||
|
GetLastError :: proc() -> u32 ---
|
||||||
|
}
|
||||||
|
|
||||||
|
_stderr_write :: proc "contextless" (data: []byte) -> (n: int, err: _OS_Errno) #no_bounds_check {
|
||||||
|
if len(data) == 0 {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
STD_ERROR_HANDLE :: ~u32(0) -12 + 1
|
||||||
|
HANDLE_FLAG_INHERIT :: 0x00000001
|
||||||
|
MAX_RW :: 1<<30
|
||||||
|
|
||||||
|
h := GetStdHandle(STD_ERROR_HANDLE)
|
||||||
|
when size_of(uintptr) == 8 {
|
||||||
|
SetHandleInformation(h, HANDLE_FLAG_INHERIT, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
single_write_length: u32
|
||||||
|
total_write: i64
|
||||||
|
length := i64(len(data))
|
||||||
|
|
||||||
|
for total_write < length {
|
||||||
|
remaining := length - total_write
|
||||||
|
to_write := u32(min(i32(remaining), MAX_RW))
|
||||||
|
|
||||||
|
e := WriteFile(h, &data[total_write], to_write, &single_write_length, nil)
|
||||||
|
if single_write_length <= 0 || !e {
|
||||||
|
err = _OS_Errno(GetLastError())
|
||||||
|
n = int(total_write)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
total_write += i64(single_write_length)
|
||||||
|
}
|
||||||
|
n = int(total_write)
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -123,13 +123,13 @@ encode_rune :: proc "contextless" (c: rune) -> ([4]u8, int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
print_string :: proc "contextless" (str: string) -> (n: int) {
|
print_string :: proc "contextless" (str: string) -> (n: int) {
|
||||||
n, _ = os_write(transmute([]byte)str)
|
n, _ = stderr_write(transmute([]byte)str)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
print_strings :: proc "contextless" (args: ..string) -> (n: int) {
|
print_strings :: proc "contextless" (args: ..string) -> (n: int) {
|
||||||
for str in args {
|
for str in args {
|
||||||
m, err := os_write(transmute([]byte)str)
|
m, err := stderr_write(transmute([]byte)str)
|
||||||
n += m
|
n += m
|
||||||
if err != 0 {
|
if err != 0 {
|
||||||
break
|
break
|
||||||
@@ -139,7 +139,7 @@ print_strings :: proc "contextless" (args: ..string) -> (n: int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
print_byte :: proc "contextless" (b: byte) -> (n: int) {
|
print_byte :: proc "contextless" (b: byte) -> (n: int) {
|
||||||
n, _ = os_write([]byte{b})
|
n, _ = stderr_write([]byte{b})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,7 +178,7 @@ print_rune :: proc "contextless" (r: rune) -> int #no_bounds_check {
|
|||||||
}
|
}
|
||||||
|
|
||||||
b, n := encode_rune(r)
|
b, n := encode_rune(r)
|
||||||
m, _ := os_write(b[:n])
|
m, _ := stderr_write(b[:n])
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +194,7 @@ print_u64 :: proc "contextless" (x: u64) #no_bounds_check {
|
|||||||
}
|
}
|
||||||
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
|
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
|
||||||
|
|
||||||
os_write(a[i:])
|
stderr_write(a[i:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -216,7 +216,7 @@ print_i64 :: proc "contextless" (x: i64) #no_bounds_check {
|
|||||||
i -= 1; a[i] = '-'
|
i -= 1; a[i] = '-'
|
||||||
}
|
}
|
||||||
|
|
||||||
os_write(a[i:])
|
stderr_write(a[i:])
|
||||||
}
|
}
|
||||||
|
|
||||||
print_uint :: proc "contextless" (x: uint) { print_u64(u64(x)) }
|
print_uint :: proc "contextless" (x: uint) { print_u64(u64(x)) }
|
||||||
@@ -4,7 +4,7 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
|
|||||||
foreign import lib "system:NtDll.lib"
|
foreign import lib "system:NtDll.lib"
|
||||||
|
|
||||||
@(private="file")
|
@(private="file")
|
||||||
@(default_calling_convention="stdcall")
|
@(default_calling_convention="system")
|
||||||
foreign lib {
|
foreign lib {
|
||||||
RtlMoveMemory :: proc(dst, s: rawptr, length: int) ---
|
RtlMoveMemory :: proc(dst, s: rawptr, length: int) ---
|
||||||
RtlFillMemory :: proc(dst: rawptr, length: int, fill: i32) ---
|
RtlFillMemory :: proc(dst: rawptr, length: int, fill: i32) ---
|
||||||
@@ -38,6 +38,17 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
|
|||||||
return ptr
|
return ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(link_name="bzero", linkage="strong", require)
|
||||||
|
bzero :: proc "c" (ptr: rawptr, len: int) -> rawptr {
|
||||||
|
if ptr != nil && len != 0 {
|
||||||
|
p := ([^]byte)(ptr)
|
||||||
|
for i := 0; i < len; i += 1 {
|
||||||
|
p[i] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ptr
|
||||||
|
}
|
||||||
|
|
||||||
@(link_name="memmove", linkage="strong", require)
|
@(link_name="memmove", linkage="strong", require)
|
||||||
memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
|
memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
|
||||||
d, s := ([^]byte)(dst), ([^]byte)(src)
|
d, s := ([^]byte)(dst), ([^]byte)(src)
|
||||||
@@ -3,7 +3,7 @@ package runtime
|
|||||||
|
|
||||||
foreign import "system:Foundation.framework"
|
foreign import "system:Foundation.framework"
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
objc_id :: ^intrinsics.objc_object
|
objc_id :: ^intrinsics.objc_object
|
||||||
objc_Class :: ^intrinsics.objc_class
|
objc_Class :: ^intrinsics.objc_class
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
//+private
|
//+private
|
||||||
|
//+no-instrumentation
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
foreign import kernel32 "system:Kernel32.lib"
|
foreign import kernel32 "system:Kernel32.lib"
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
foreign kernel32 {
|
foreign kernel32 {
|
||||||
RaiseException :: proc "stdcall" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: u32, lpArguments: ^uint) -> ! ---
|
RaiseException :: proc "system" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: u32, lpArguments: ^uint) -> ! ---
|
||||||
}
|
}
|
||||||
|
|
||||||
windows_trap_array_bounds :: proc "contextless" () -> ! {
|
windows_trap_array_bounds :: proc "contextless" () -> ! {
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//+private
|
//+private
|
||||||
|
//+no-instrumentation
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
@require foreign import "system:int64.lib"
|
@require foreign import "system:int64.lib"
|
||||||
@@ -12,7 +13,7 @@ windows_trap_array_bounds :: proc "contextless" () -> ! {
|
|||||||
EXCEPTION_ARRAY_BOUNDS_EXCEEDED :: 0xC000008C
|
EXCEPTION_ARRAY_BOUNDS_EXCEEDED :: 0xC000008C
|
||||||
|
|
||||||
foreign kernel32 {
|
foreign kernel32 {
|
||||||
RaiseException :: proc "stdcall" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD, lpArguments: ^ULONG_PTR) -> ! ---
|
RaiseException :: proc "system" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD, lpArguments: ^ULONG_PTR) -> ! ---
|
||||||
}
|
}
|
||||||
|
|
||||||
RaiseException(EXCEPTION_ARRAY_BOUNDS_EXCEEDED, 0, 0, nil)
|
RaiseException(EXCEPTION_ARRAY_BOUNDS_EXCEEDED, 0, 0, nil)
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
||||||
_ctz :: intrinsics.count_trailing_zeros
|
_ctz :: intrinsics.count_trailing_zeros
|
||||||
Binary file not shown.
Binary file not shown.
@@ -4,7 +4,7 @@ import "core:bytes"
|
|||||||
import "core:io"
|
import "core:io"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:unicode/utf8"
|
import "core:unicode/utf8"
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
// Extra errors returns by scanning procedures
|
// Extra errors returns by scanning procedures
|
||||||
Scanner_Extra_Error :: enum i32 {
|
Scanner_Extra_Error :: enum i32 {
|
||||||
|
|||||||
@@ -895,7 +895,7 @@ split_multi_iterator :: proc(s: ^[]byte, substrs: [][]byte, skip_empty := false)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// scrub scruvs invalid utf-8 characters and replaces them with the replacement string
|
// Scrubs invalid utf-8 characters and replaces them with the replacement string
|
||||||
// Adjacent invalid bytes are only replaced once
|
// Adjacent invalid bytes are only replaced once
|
||||||
scrub :: proc(s: []byte, replacement: []byte, allocator := context.allocator) -> []byte {
|
scrub :: proc(s: []byte, replacement: []byte, allocator := context.allocator) -> []byte {
|
||||||
str := s
|
str := s
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
package c
|
package c
|
||||||
|
|
||||||
import builtin "core:builtin"
|
import builtin "base:builtin"
|
||||||
|
|
||||||
char :: builtin.u8 // assuming -funsigned-char
|
char :: builtin.u8 // assuming -funsigned-char
|
||||||
|
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ foreign libc {
|
|||||||
crealf :: proc(z: complex_float) -> float ---
|
crealf :: proc(z: complex_float) -> float ---
|
||||||
}
|
}
|
||||||
|
|
||||||
import builtin "core:builtin"
|
import builtin "base:builtin"
|
||||||
|
|
||||||
complex_float :: distinct builtin.complex64
|
complex_float :: distinct builtin.complex64
|
||||||
complex_double :: distinct builtin.complex128
|
complex_double :: distinct builtin.complex128
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package libc
|
|||||||
|
|
||||||
// 7.12 Mathematics
|
// 7.12 Mathematics
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
when ODIN_OS == .Windows {
|
when ODIN_OS == .Windows {
|
||||||
foreign import libc "system:libucrt.lib"
|
foreign import libc "system:libucrt.lib"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package libc
|
|||||||
|
|
||||||
// 7.16 Variable arguments
|
// 7.16 Variable arguments
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
@(private="file")
|
@(private="file")
|
||||||
@(default_calling_convention="none")
|
@(default_calling_convention="none")
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package libc
|
|||||||
|
|
||||||
// 7.17 Atomics
|
// 7.17 Atomics
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
ATOMIC_BOOL_LOCK_FREE :: true
|
ATOMIC_BOOL_LOCK_FREE :: true
|
||||||
ATOMIC_CHAR_LOCK_FREE :: true
|
ATOMIC_CHAR_LOCK_FREE :: true
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package libc
|
package libc
|
||||||
|
|
||||||
|
import "core:io"
|
||||||
|
|
||||||
when ODIN_OS == .Windows {
|
when ODIN_OS == .Windows {
|
||||||
foreign import libc {
|
foreign import libc {
|
||||||
"system:libucrt.lib",
|
"system:libucrt.lib",
|
||||||
@@ -218,3 +220,102 @@ foreign libc {
|
|||||||
ferror :: proc(stream: ^FILE) -> int ---
|
ferror :: proc(stream: ^FILE) -> int ---
|
||||||
perror :: proc(s: cstring) ---
|
perror :: proc(s: cstring) ---
|
||||||
}
|
}
|
||||||
|
|
||||||
|
to_stream :: proc(file: ^FILE) -> io.Stream {
|
||||||
|
stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
|
||||||
|
unknown_or_eof :: proc(f: ^FILE) -> io.Error {
|
||||||
|
switch {
|
||||||
|
case ferror(f) != 0:
|
||||||
|
return .Unknown
|
||||||
|
case feof(f) != 0:
|
||||||
|
return .EOF
|
||||||
|
case:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file := (^FILE)(stream_data)
|
||||||
|
switch mode {
|
||||||
|
case .Close:
|
||||||
|
if fclose(file) != 0 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
case .Flush:
|
||||||
|
if fflush(file) != 0 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
case .Read:
|
||||||
|
n = i64(fread(raw_data(p), size_of(byte), len(p), file))
|
||||||
|
if n == 0 { err = unknown_or_eof(file) }
|
||||||
|
|
||||||
|
case .Read_At:
|
||||||
|
curr := ftell(file)
|
||||||
|
if curr == -1 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fseek(file, long(offset), SEEK_SET) != 0 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer fseek(file, long(curr), SEEK_SET)
|
||||||
|
|
||||||
|
n = i64(fread(raw_data(p), size_of(byte), len(p), file))
|
||||||
|
if n == 0 { err = unknown_or_eof(file) }
|
||||||
|
|
||||||
|
case .Write:
|
||||||
|
n = i64(fwrite(raw_data(p), size_of(byte), len(p), file))
|
||||||
|
if n == 0 { err = unknown_or_eof(file) }
|
||||||
|
|
||||||
|
case .Write_At:
|
||||||
|
curr := ftell(file)
|
||||||
|
if curr == -1 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fseek(file, long(offset), SEEK_SET) != 0 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer fseek(file, long(curr), SEEK_SET)
|
||||||
|
|
||||||
|
n = i64(fwrite(raw_data(p), size_of(byte), len(p), file))
|
||||||
|
if n == 0 { err = unknown_or_eof(file) }
|
||||||
|
|
||||||
|
case .Seek:
|
||||||
|
if fseek(file, long(offset), int(whence)) != 0 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
case .Size:
|
||||||
|
curr := ftell(file)
|
||||||
|
if curr == -1 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
defer fseek(file, curr, SEEK_SET)
|
||||||
|
|
||||||
|
if fseek(file, 0, SEEK_END) != 0 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
n = i64(ftell(file))
|
||||||
|
if n == -1 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
case .Destroy:
|
||||||
|
return 0, .Empty
|
||||||
|
|
||||||
|
case .Query:
|
||||||
|
return io.query_utility({ .Close, .Flush, .Read, .Read_At, .Write, .Write_At, .Seek, .Size })
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
data = file,
|
||||||
|
procedure = stream_proc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package libc
|
package libc
|
||||||
|
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
|
|
||||||
// 7.24 String handling
|
// 7.24 String handling
|
||||||
|
|
||||||
|
|||||||
+33
-43
@@ -12,7 +12,7 @@ package compress
|
|||||||
|
|
||||||
import "core:io"
|
import "core:io"
|
||||||
import "core:bytes"
|
import "core:bytes"
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
These settings bound how much compression algorithms will allocate for their output buffer.
|
These settings bound how much compression algorithms will allocate for their output buffer.
|
||||||
@@ -20,10 +20,9 @@ import "core:runtime"
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
When a decompression routine doesn't stream its output, but writes to a buffer,
|
// When a decompression routine doesn't stream its output, but writes to a buffer,
|
||||||
we pre-allocate an output buffer to speed up decompression. The default is 1 MiB.
|
// we pre-allocate an output buffer to speed up decompression. The default is 1 MiB.
|
||||||
*/
|
|
||||||
COMPRESS_OUTPUT_ALLOCATE_MIN :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MIN, 1 << 20))
|
COMPRESS_OUTPUT_ALLOCATE_MIN :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MIN, 1 << 20))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -34,16 +33,14 @@ COMPRESS_OUTPUT_ALLOCATE_MIN :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MIN, 1 << 2
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
when size_of(uintptr) == 8 {
|
when size_of(uintptr) == 8 {
|
||||||
/*
|
|
||||||
For 64-bit platforms, we set the default max buffer size to 4 GiB,
|
// For 64-bit platforms, we set the default max buffer size to 4 GiB,
|
||||||
which is GZIP and PKZIP's max payload size.
|
// which is GZIP and PKZIP's max payload size.
|
||||||
*/
|
|
||||||
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 32))
|
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 32))
|
||||||
} else {
|
} else {
|
||||||
/*
|
|
||||||
For 32-bit platforms, we set the default max buffer size to 512 MiB.
|
// For 32-bit platforms, we set the default max buffer size to 512 MiB.
|
||||||
*/
|
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 29))
|
||||||
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 29))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -69,9 +66,8 @@ General_Error :: enum {
|
|||||||
Incompatible_Options,
|
Incompatible_Options,
|
||||||
Unimplemented,
|
Unimplemented,
|
||||||
|
|
||||||
/*
|
// Memory errors
|
||||||
Memory errors
|
|
||||||
*/
|
|
||||||
Allocation_Failed,
|
Allocation_Failed,
|
||||||
Resize_Failed,
|
Resize_Failed,
|
||||||
}
|
}
|
||||||
@@ -86,17 +82,16 @@ GZIP_Error :: enum {
|
|||||||
Payload_Length_Invalid,
|
Payload_Length_Invalid,
|
||||||
Payload_CRC_Invalid,
|
Payload_CRC_Invalid,
|
||||||
|
|
||||||
/*
|
// GZIP's payload can be a maximum of max(u32le), or 4 GiB.
|
||||||
GZIP's payload can be a maximum of max(u32le), or 4 GiB.
|
// If you tell it you expect it to contain more, that's obviously an error.
|
||||||
If you tell it you expect it to contain more, that's obviously an error.
|
|
||||||
*/
|
Payload_Size_Exceeds_Max_Payload,
|
||||||
Payload_Size_Exceeds_Max_Payload,
|
|
||||||
/*
|
// For buffered instead of streamed output, the payload size can't exceed
|
||||||
For buffered instead of streamed output, the payload size can't exceed
|
// the max set by the `COMPRESS_OUTPUT_ALLOCATE_MAX` switch in compress/common.odin.
|
||||||
the max set by the `COMPRESS_OUTPUT_ALLOCATE_MAX` switch in compress/common.odin.
|
//
|
||||||
|
// You can tweak this setting using `-define:COMPRESS_OUTPUT_ALLOCATE_MAX=size_in_bytes`
|
||||||
|
|
||||||
You can tweak this setting using `-define:COMPRESS_OUTPUT_ALLOCATE_MAX=size_in_bytes`
|
|
||||||
*/
|
|
||||||
Output_Exceeds_COMPRESS_OUTPUT_ALLOCATE_MAX,
|
Output_Exceeds_COMPRESS_OUTPUT_ALLOCATE_MAX,
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -137,9 +132,8 @@ Context_Memory_Input :: struct #packed {
|
|||||||
code_buffer: u64,
|
code_buffer: u64,
|
||||||
num_bits: u64,
|
num_bits: u64,
|
||||||
|
|
||||||
/*
|
// If we know the data size, we can optimize the reads and writes.
|
||||||
If we know the data size, we can optimize the reads and writes.
|
|
||||||
*/
|
|
||||||
size_packed: i64,
|
size_packed: i64,
|
||||||
size_unpacked: i64,
|
size_unpacked: i64,
|
||||||
}
|
}
|
||||||
@@ -159,18 +153,16 @@ Context_Stream_Input :: struct #packed {
|
|||||||
code_buffer: u64,
|
code_buffer: u64,
|
||||||
num_bits: u64,
|
num_bits: u64,
|
||||||
|
|
||||||
/*
|
// If we know the data size, we can optimize the reads and writes.
|
||||||
If we know the data size, we can optimize the reads and writes.
|
|
||||||
*/
|
|
||||||
size_packed: i64,
|
size_packed: i64,
|
||||||
size_unpacked: i64,
|
size_unpacked: i64,
|
||||||
|
|
||||||
/*
|
// Flags:
|
||||||
Flags:
|
// `input_fully_in_memory`
|
||||||
`input_fully_in_memory`
|
// true = This tells us we read input from `input_data` exclusively. [] = EOF.
|
||||||
true = This tells us we read input from `input_data` exclusively. [] = EOF.
|
// false = Try to refill `input_data` from the `input` stream.
|
||||||
false = Try to refill `input_data` from the `input` stream.
|
|
||||||
*/
|
|
||||||
input_fully_in_memory: b8,
|
input_fully_in_memory: b8,
|
||||||
|
|
||||||
padding: [1]u8,
|
padding: [1]u8,
|
||||||
@@ -214,7 +206,7 @@ read_slice_from_memory :: #force_inline proc(z: ^Context_Memory_Input, size: int
|
|||||||
@(optimization_mode="speed")
|
@(optimization_mode="speed")
|
||||||
read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int) -> (res: []u8, err: io.Error) {
|
read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int) -> (res: []u8, err: io.Error) {
|
||||||
// TODO: REMOVE ALL USE OF context.temp_allocator here
|
// TODO: REMOVE ALL USE OF context.temp_allocator here
|
||||||
// the is literally no need for it
|
// there is literally no need for it
|
||||||
b := make([]u8, size, context.temp_allocator)
|
b := make([]u8, size, context.temp_allocator)
|
||||||
_ = io.read(z.input, b[:]) or_return
|
_ = io.read(z.input, b[:]) or_return
|
||||||
return b, nil
|
return b, nil
|
||||||
@@ -248,10 +240,8 @@ read_u8_from_stream :: #force_inline proc(z: ^Context_Stream_Input) -> (res: u8,
|
|||||||
|
|
||||||
read_u8 :: proc{read_u8_from_memory, read_u8_from_stream}
|
read_u8 :: proc{read_u8_from_memory, read_u8_from_stream}
|
||||||
|
|
||||||
/*
|
// You would typically only use this at the end of Inflate, to drain bits from the code buffer
|
||||||
You would typically only use this at the end of Inflate, to drain bits from the code buffer
|
// preferentially.
|
||||||
preferentially.
|
|
||||||
*/
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="speed")
|
||||||
read_u8_prefer_code_buffer_lsb :: #force_inline proc(z: ^$C) -> (res: u8, err: io.Error) {
|
read_u8_prefer_code_buffer_lsb :: #force_inline proc(z: ^$C) -> (res: u8, err: io.Error) {
|
||||||
if z.num_bits >= 8 {
|
if z.num_bits >= 8 {
|
||||||
|
|||||||
@@ -11,7 +11,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 shoco
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
import "core:compress"
|
import "core:compress"
|
||||||
|
|
||||||
Shoco_Pack :: struct {
|
Shoco_Pack :: struct {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package dynamic_bit_array
|
package dynamic_bit_array
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package container_intrusive_list
|
package container_intrusive_list
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
// An intrusive doubly-linked list
|
// An intrusive doubly-linked list
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package container_lru
|
package container_lru
|
||||||
|
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
_ :: runtime
|
_ :: runtime
|
||||||
_ :: intrinsics
|
_ :: intrinsics
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package container_priority_queue
|
package container_priority_queue
|
||||||
|
|
||||||
import "core:builtin"
|
import "base:builtin"
|
||||||
|
|
||||||
Priority_Queue :: struct($T: typeid) {
|
Priority_Queue :: struct($T: typeid) {
|
||||||
queue: [dynamic]T,
|
queue: [dynamic]T,
|
||||||
@@ -140,3 +140,18 @@ remove :: proc(pq: ^$Q/Priority_Queue($T), i: int) -> (value: T, ok: bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
peek_safe :: proc(pq: $Q/Priority_Queue($T), loc := #caller_location) -> (res: T, ok: bool) {
|
||||||
|
if builtin.len(pq.queue) > 0 {
|
||||||
|
return pq.queue[0], true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
peek :: proc(pq: $Q/Priority_Queue($T), loc := #caller_location) -> (res: T) {
|
||||||
|
assert(condition=builtin.len(pq.queue)>0, loc=loc)
|
||||||
|
|
||||||
|
if builtin.len(pq.queue) > 0 {
|
||||||
|
return pq.queue[0]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package container_queue
|
package container_queue
|
||||||
|
|
||||||
import "core:builtin"
|
import "base:builtin"
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
_ :: runtime
|
_ :: runtime
|
||||||
|
|
||||||
// Dynamically resizable double-ended queue/ring-buffer
|
// Dynamically resizable double-ended queue/ring-buffer
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package container_small_array
|
package container_small_array
|
||||||
|
|
||||||
import "core:builtin"
|
import "base:builtin"
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
_ :: runtime
|
_ :: runtime
|
||||||
|
|
||||||
Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
|
Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
// map type is being used to accelerate lookups.
|
// map type is being used to accelerate lookups.
|
||||||
package container_topological_sort
|
package container_topological_sort
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
_ :: intrinsics
|
_ :: intrinsics
|
||||||
_ :: runtime
|
_ :: runtime
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
|
//+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.
|
||||||
|
|
||||||
|
See `example` directory for an example library exporting 3 symbols and a host program loading them automatically
|
||||||
|
by defining a symbol table struct.
|
||||||
*/
|
*/
|
||||||
package dynlib
|
package dynlib
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package example
|
||||||
|
|
||||||
|
import "core:dynlib"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
Symbols :: struct {
|
||||||
|
// `foo_` is prefixed, so we look for the symbol `foo_add`.
|
||||||
|
add: proc "c" (int, int) -> int,
|
||||||
|
// We use the tag here to override the symbol to look for, namely `bar_sub`.
|
||||||
|
sub: proc "c" (int, int) -> int `dynlib:"bar_sub"`,
|
||||||
|
|
||||||
|
// Exported global (if exporting an i32, the type must be ^i32 because the symbol is a pointer to the export.)
|
||||||
|
// If it's not a pointer or procedure type, we'll skip the struct field.
|
||||||
|
hellope: ^i32,
|
||||||
|
|
||||||
|
// Handle to free library.
|
||||||
|
// We can have more than one of these so we can match symbols for more than one DLL with one struct.
|
||||||
|
_my_lib_handle: dynlib.Library,
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
sym: Symbols
|
||||||
|
|
||||||
|
// Load symbols from `lib.dll` into Symbols struct.
|
||||||
|
// Each struct field is prefixed with `foo_` before lookup in the DLL's symbol table.
|
||||||
|
// The library's Handle (to unload) will be stored in `sym._my_lib_handle`. This way you can load multiple DLLs in one struct.
|
||||||
|
count, ok := dynlib.initialize_symbols(&sym, "lib.dll", "foo_", "_my_lib_handle")
|
||||||
|
defer dynlib.unload_library(sym._my_lib_handle)
|
||||||
|
fmt.printf("(Initial DLL Load) ok: %v. %v symbols loaded from lib.dll (%p).\n", ok, count, sym._my_lib_handle)
|
||||||
|
|
||||||
|
if count > 0 {
|
||||||
|
fmt.println("42 + 42 =", sym.add(42, 42))
|
||||||
|
fmt.println("84 - 13 =", sym.sub(84, 13))
|
||||||
|
fmt.println("hellope =", sym.hellope^)
|
||||||
|
}
|
||||||
|
|
||||||
|
count, ok = dynlib.initialize_symbols(&sym, "lib.dll", "foo_", "_my_lib_handle")
|
||||||
|
fmt.printf("(DLL Reload) ok: %v. %v symbols loaded from lib.dll (%p).\n", ok, count, sym._my_lib_handle)
|
||||||
|
|
||||||
|
if count > 0 {
|
||||||
|
fmt.println("42 + 42 =", sym.add(42, 42))
|
||||||
|
fmt.println("84 - 13 =", sym.sub(84, 13))
|
||||||
|
fmt.println("hellope =", sym.hellope^)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package library
|
||||||
|
|
||||||
|
@(export)
|
||||||
|
foo_add :: proc "c" (a, b: int) -> (res: int) {
|
||||||
|
return a + b
|
||||||
|
}
|
||||||
|
|
||||||
|
@(export)
|
||||||
|
bar_sub :: proc "c" (a, b: int) -> (res: int) {
|
||||||
|
return a - b
|
||||||
|
}
|
||||||
|
|
||||||
|
@(export)
|
||||||
|
foo_hellope: i32 = 42
|
||||||
+99
-1
@@ -1,5 +1,12 @@
|
|||||||
package dynlib
|
package dynlib
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
import "core:reflect"
|
||||||
|
import "base:runtime"
|
||||||
|
_ :: intrinsics
|
||||||
|
_ :: reflect
|
||||||
|
_ :: runtime
|
||||||
|
|
||||||
/*
|
/*
|
||||||
A handle to a dynamically loaded library.
|
A handle to a dynamically loaded library.
|
||||||
*/
|
*/
|
||||||
@@ -27,6 +34,7 @@ Example:
|
|||||||
LIBRARY_PATH :: "my_library.dll"
|
LIBRARY_PATH :: "my_library.dll"
|
||||||
library, ok := dynlib.load_library(LIBRARY_PATH)
|
library, ok := dynlib.load_library(LIBRARY_PATH)
|
||||||
if ! ok {
|
if ! ok {
|
||||||
|
fmt.eprintln(dynlib.last_error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.println("The library %q was successfully loaded", LIBRARY_PATH)
|
fmt.println("The library %q was successfully loaded", LIBRARY_PATH)
|
||||||
@@ -51,10 +59,12 @@ Example:
|
|||||||
LIBRARY_PATH :: "my_library.dll"
|
LIBRARY_PATH :: "my_library.dll"
|
||||||
library, ok := dynlib.load_library(LIBRARY_PATH)
|
library, ok := dynlib.load_library(LIBRARY_PATH)
|
||||||
if ! ok {
|
if ! ok {
|
||||||
|
fmt.eprintln(dynlib.last_error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
did_unload := dynlib.unload_library(library)
|
did_unload := dynlib.unload_library(library)
|
||||||
if ! did_unload {
|
if ! did_unload {
|
||||||
|
fmt.eprintln(dynlib.last_error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.println("The library %q was successfully unloaded", LIBRARY_PATH)
|
fmt.println("The library %q was successfully unloaded", LIBRARY_PATH)
|
||||||
@@ -82,13 +92,101 @@ Example:
|
|||||||
LIBRARY_PATH :: "my_library.dll"
|
LIBRARY_PATH :: "my_library.dll"
|
||||||
library, ok := dynlib.load_library(LIBRARY_PATH)
|
library, ok := dynlib.load_library(LIBRARY_PATH)
|
||||||
if ! ok {
|
if ! ok {
|
||||||
|
fmt.eprintln(dynlib.last_error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
a, found_a := dynlib.symbol_address(library, "a")
|
a, found_a := dynlib.symbol_address(library, "a")
|
||||||
if found_a do fmt.printf("The symbol %q was found at the address %v", "a", a)
|
if found_a {
|
||||||
|
fmt.printf("The symbol %q was found at the address %v", "a", a)
|
||||||
|
} else {
|
||||||
|
fmt.eprintln(dynlib.last_error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) #optional_ok {
|
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) #optional_ok {
|
||||||
return _symbol_address(library, symbol)
|
return _symbol_address(library, symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Scans a dynamic library for symbols matching a struct's members, assigning found procedure pointers to the corresponding entry.
|
||||||
|
Optionally takes a symbol prefix added to the struct's member name to construct the symbol looked up in the library.
|
||||||
|
Optionally also takes the struct member to assign the library handle to, `__handle` by default.
|
||||||
|
|
||||||
|
This allows using one struct to hold library handles and symbol pointers for more than 1 dynamic library.
|
||||||
|
|
||||||
|
Loading the same library twice unloads the previous incarnation, allowing for straightforward hot reload support.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
* `-1, false` if the library could not be loaded.
|
||||||
|
* The number of symbols assigned on success. `ok` = true if `count` > 0
|
||||||
|
|
||||||
|
See doc.odin for an example.
|
||||||
|
*/
|
||||||
|
initialize_symbols :: proc(symbol_table: ^$T, library_path: string, symbol_prefix := "", handle_field_name := "__handle") -> (count: int, ok: bool) where intrinsics.type_is_struct(T) {
|
||||||
|
assert(symbol_table != nil)
|
||||||
|
handle: Library
|
||||||
|
|
||||||
|
if handle, ok = load_library(library_path); !ok {
|
||||||
|
return -1, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// `symbol_table` must be a struct because of the where clause, so this can't fail.
|
||||||
|
ti := runtime.type_info_base(type_info_of(T))
|
||||||
|
s, _ := ti.variant.(runtime.Type_Info_Struct)
|
||||||
|
|
||||||
|
// Buffer to concatenate the prefix + symbol name.
|
||||||
|
prefixed_symbol_buf: [2048]u8 = ---
|
||||||
|
|
||||||
|
sym_ptr: rawptr
|
||||||
|
for field_name, i in s.names {
|
||||||
|
// Calculate address of struct member
|
||||||
|
field_ptr := rawptr(uintptr(rawptr(symbol_table)) + uintptr(s.offsets[i]))
|
||||||
|
|
||||||
|
// If we've come across the struct member for the handle, store it and continue scanning for other symbols.
|
||||||
|
if field_name == handle_field_name {
|
||||||
|
// We appear to be hot reloading. Unload previous incarnation of the library.
|
||||||
|
if old_handle := (^Library)(field_ptr)^; old_handle != nil {
|
||||||
|
if ok = unload_library(old_handle); !ok {
|
||||||
|
return count, ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(^Library)(field_ptr)^ = handle
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're not the library handle, so the field needs to be a pointer type, be it a procedure pointer or an exported global.
|
||||||
|
if !(reflect.is_procedure(s.types[i]) || reflect.is_pointer(s.types[i])) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's look up or construct the symbol name to find in the library
|
||||||
|
prefixed_name: string
|
||||||
|
|
||||||
|
// Do we have a symbol override tag?
|
||||||
|
if override, tag_ok := reflect.struct_tag_lookup(reflect.Struct_Tag(s.tags[i]), "dynlib"); tag_ok {
|
||||||
|
prefixed_name = string(override)
|
||||||
|
}
|
||||||
|
|
||||||
|
// No valid symbol override tag found, fall back to `<symbol_prefix>name`.
|
||||||
|
if len(prefixed_name) == 0 {
|
||||||
|
offset := copy(prefixed_symbol_buf[:], symbol_prefix)
|
||||||
|
copy(prefixed_symbol_buf[offset:], field_name)
|
||||||
|
prefixed_name = string(prefixed_symbol_buf[:len(symbol_prefix) + len(field_name)])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign procedure (or global) pointer if found.
|
||||||
|
if sym_ptr, ok = symbol_address(handle, prefixed_name); ok {
|
||||||
|
(^rawptr)(field_ptr)^ = sym_ptr
|
||||||
|
count += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count, count > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Returns an error message for the last failed procedure call.
|
||||||
|
*/
|
||||||
|
last_error :: proc() -> string {
|
||||||
|
return _last_error()
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,3 +13,7 @@ _unload_library :: proc(library: Library) -> bool {
|
|||||||
_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_last_error :: proc() -> string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,3 +22,8 @@ _symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found
|
|||||||
found = ptr != nil
|
found = ptr != nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_last_error :: proc() -> string {
|
||||||
|
err := os.dlerror()
|
||||||
|
return "unknown" if err == "" else err
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ package dynlib
|
|||||||
|
|
||||||
import win32 "core:sys/windows"
|
import win32 "core:sys/windows"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
|
import "core:reflect"
|
||||||
|
|
||||||
_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
||||||
// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
|
// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
|
||||||
@@ -27,3 +28,9 @@ _symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found
|
|||||||
found = ptr != nil
|
found = ptr != nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_last_error :: proc() -> string {
|
||||||
|
err := win32.System_Error(win32.GetLastError())
|
||||||
|
err_msg := reflect.enum_string(err)
|
||||||
|
return "unknown" if err_msg == "" else err_msg
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package encoding_endian
|
package encoding_endian
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
import "core:math/bits"
|
import "core:math/bits"
|
||||||
|
|
||||||
Byte_Order :: enum u8 {
|
Byte_Order :: enum u8 {
|
||||||
|
|||||||
@@ -47,8 +47,6 @@ _entities :: proc() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_main :: proc() {
|
_main :: proc() {
|
||||||
using fmt
|
|
||||||
|
|
||||||
options := xml.Options{ flags = { .Ignore_Unsupported, .Intern_Comments, .Unbox_CDATA, .Decode_SGML_Entities }}
|
options := xml.Options{ flags = { .Ignore_Unsupported, .Intern_Comments, .Unbox_CDATA, .Decode_SGML_Entities }}
|
||||||
|
|
||||||
doc, _ := xml.parse(#load("test.html"), options)
|
doc, _ := xml.parse(#load("test.html"), options)
|
||||||
@@ -58,8 +56,6 @@ _main :: proc() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
using fmt
|
|
||||||
|
|
||||||
track: mem.Tracking_Allocator
|
track: mem.Tracking_Allocator
|
||||||
mem.tracking_allocator_init(&track, context.allocator)
|
mem.tracking_allocator_init(&track, context.allocator)
|
||||||
context.allocator = mem.tracking_allocator(&track)
|
context.allocator = mem.tracking_allocator(&track)
|
||||||
@@ -68,9 +64,9 @@ main :: proc() {
|
|||||||
_entities()
|
_entities()
|
||||||
|
|
||||||
if len(track.allocation_map) > 0 {
|
if len(track.allocation_map) > 0 {
|
||||||
println()
|
fmt.println()
|
||||||
for _, v in track.allocation_map {
|
for _, v in track.allocation_map {
|
||||||
printf("%v Leaked %v bytes.\n", v.location, v.size)
|
fmt.printf("%v Leaked %v bytes.\n", v.location, v.size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+3010
-5412
File diff suppressed because it is too large
Load Diff
@@ -2,11 +2,12 @@ package json
|
|||||||
|
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:math/bits"
|
import "core:math/bits"
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
import "core:strconv"
|
import "core:strconv"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
import "core:reflect"
|
import "core:reflect"
|
||||||
import "core:io"
|
import "core:io"
|
||||||
|
import "core:slice"
|
||||||
|
|
||||||
Marshal_Data_Error :: enum {
|
Marshal_Data_Error :: enum {
|
||||||
None,
|
None,
|
||||||
@@ -18,29 +19,40 @@ Marshal_Error :: union #shared_nil {
|
|||||||
io.Error,
|
io.Error,
|
||||||
}
|
}
|
||||||
|
|
||||||
// careful with MJSON maps & non quotes usage as keys without whitespace will lead to bad results
|
// careful with MJSON maps & non quotes usage as keys with whitespace will lead to bad results
|
||||||
Marshal_Options :: struct {
|
Marshal_Options :: struct {
|
||||||
// output based on spec
|
// output based on spec
|
||||||
spec: Specification,
|
spec: Specification,
|
||||||
|
|
||||||
// use line breaks & tab|spaces
|
// Use line breaks & tabs/spaces
|
||||||
pretty: bool,
|
pretty: bool,
|
||||||
|
|
||||||
// spacing
|
// Use spaces for indentation instead of tabs
|
||||||
use_spaces: bool,
|
use_spaces: bool,
|
||||||
|
|
||||||
|
// Given use_spaces true, use this many spaces per indent level. 0 means 4 spaces.
|
||||||
spaces: int,
|
spaces: int,
|
||||||
|
|
||||||
// state
|
// Output uint as hex in JSON5 & MJSON
|
||||||
indentation: int,
|
|
||||||
|
|
||||||
// option to output uint in JSON5 & MJSON
|
|
||||||
write_uint_as_hex: bool,
|
write_uint_as_hex: bool,
|
||||||
|
|
||||||
// mjson output options
|
// If spec is MJSON and this is true, then keys will be quoted.
|
||||||
|
//
|
||||||
|
// WARNING: If your keys contain whitespace and this is false, then the
|
||||||
|
// output will be bad.
|
||||||
mjson_keys_use_quotes: bool,
|
mjson_keys_use_quotes: bool,
|
||||||
|
|
||||||
|
// If spec is MJSON and this is true, then use '=' as delimiter between
|
||||||
|
// keys and values, otherwise ':' is used.
|
||||||
mjson_keys_use_equal_sign: bool,
|
mjson_keys_use_equal_sign: bool,
|
||||||
|
|
||||||
// mjson state
|
// When outputting a map, sort the output by key.
|
||||||
|
//
|
||||||
|
// NOTE: This will temp allocate and sort a list for each map.
|
||||||
|
sort_maps_by_key: bool,
|
||||||
|
|
||||||
|
// Internal state
|
||||||
|
indentation: int,
|
||||||
mjson_skipped_first_braces_start: bool,
|
mjson_skipped_first_braces_start: bool,
|
||||||
mjson_skipped_first_braces_end: bool,
|
mjson_skipped_first_braces_end: bool,
|
||||||
}
|
}
|
||||||
@@ -51,6 +63,9 @@ marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocato
|
|||||||
strings.builder_destroy(&b)
|
strings.builder_destroy(&b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// temp guard in case we are sorting map keys, which will use temp allocations
|
||||||
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = allocator == context.temp_allocator)
|
||||||
|
|
||||||
opt := opt
|
opt := opt
|
||||||
marshal_to_builder(&b, v, &opt) or_return
|
marshal_to_builder(&b, v, &opt) or_return
|
||||||
|
|
||||||
@@ -263,36 +278,81 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
|||||||
map_cap := uintptr(runtime.map_cap(m^))
|
map_cap := uintptr(runtime.map_cap(m^))
|
||||||
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
|
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
|
||||||
|
|
||||||
i := 0
|
if !opt.sort_maps_by_key {
|
||||||
for bucket_index in 0..<map_cap {
|
i := 0
|
||||||
runtime.map_hash_is_valid(hs[bucket_index]) or_continue
|
for bucket_index in 0..<map_cap {
|
||||||
|
runtime.map_hash_is_valid(hs[bucket_index]) or_continue
|
||||||
|
|
||||||
opt_write_iteration(w, opt, i) or_return
|
opt_write_iteration(w, opt, i) or_return
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
key := rawptr(runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index))
|
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))
|
value := rawptr(runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index))
|
||||||
|
|
||||||
// check for string type
|
// check for string type
|
||||||
{
|
{
|
||||||
v := any{key, info.key.id}
|
v := any{key, info.key.id}
|
||||||
ti := runtime.type_info_base(type_info_of(v.id))
|
ti := runtime.type_info_base(type_info_of(v.id))
|
||||||
a := any{v.data, ti.id}
|
a := any{v.data, ti.id}
|
||||||
name: string
|
name: string
|
||||||
|
|
||||||
#partial switch info in ti.variant {
|
#partial switch info in ti.variant {
|
||||||
case runtime.Type_Info_String:
|
case runtime.Type_Info_String:
|
||||||
switch s in a {
|
switch s in a {
|
||||||
case string: name = s
|
case string: name = s
|
||||||
case cstring: name = string(s)
|
case cstring: name = string(s)
|
||||||
|
}
|
||||||
|
opt_write_key(w, opt, name) or_return
|
||||||
|
|
||||||
|
case: return .Unsupported_Type
|
||||||
}
|
}
|
||||||
opt_write_key(w, opt, name) or_return
|
|
||||||
|
|
||||||
case: return .Unsupported_Type
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
marshal_to_writer(w, any{value, info.value.id}, opt) or_return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Entry :: struct {
|
||||||
|
key: string,
|
||||||
|
value: any,
|
||||||
}
|
}
|
||||||
|
|
||||||
marshal_to_writer(w, any{value, info.value.id}, opt) or_return
|
// If we are sorting the map by key, then we temp alloc an array
|
||||||
|
// and sort it, then output the result.
|
||||||
|
sorted := make([dynamic]Entry, 0, map_cap, context.temp_allocator)
|
||||||
|
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))
|
||||||
|
name: string
|
||||||
|
|
||||||
|
// check for string type
|
||||||
|
{
|
||||||
|
v := any{key, info.key.id}
|
||||||
|
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_String:
|
||||||
|
switch s in a {
|
||||||
|
case string: name = s
|
||||||
|
case cstring: name = string(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
case: return .Unsupported_Type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
append(&sorted, Entry { key = name, value = any{value, info.value.id}})
|
||||||
|
}
|
||||||
|
|
||||||
|
slice.sort_by(sorted[:], proc(i, j: Entry) -> bool { return i.key < j.key })
|
||||||
|
|
||||||
|
for s, i in sorted {
|
||||||
|
opt_write_iteration(w, opt, i) or_return
|
||||||
|
opt_write_key(w, opt, s.key) or_return
|
||||||
|
marshal_to_writer(w, s.value, opt) or_return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,8 +484,9 @@ opt_write_key :: proc(w: io.Writer, opt: ^Marshal_Options, name: string) -> (err
|
|||||||
|
|
||||||
// insert start byte and increase indentation on pretty
|
// insert start byte and increase indentation on pretty
|
||||||
opt_write_start :: proc(w: io.Writer, opt: ^Marshal_Options, c: byte) -> (err: io.Error) {
|
opt_write_start :: proc(w: io.Writer, opt: ^Marshal_Options, c: byte) -> (err: io.Error) {
|
||||||
// skip mjson starting braces
|
// Skip MJSON starting braces. We make sure to only do this for c == '{',
|
||||||
if opt.spec == .MJSON && !opt.mjson_skipped_first_braces_start {
|
// skipping a starting '[' is not allowed.
|
||||||
|
if opt.spec == .MJSON && !opt.mjson_skipped_first_braces_start && opt.indentation == 0 && c == '{' {
|
||||||
opt.mjson_skipped_first_braces_start = true
|
opt.mjson_skipped_first_braces_start = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -473,11 +534,9 @@ opt_write_iteration :: proc(w: io.Writer, opt: ^Marshal_Options, iteration: int)
|
|||||||
|
|
||||||
// decrease indent, write spacing and insert end byte
|
// decrease indent, write spacing and insert end byte
|
||||||
opt_write_end :: proc(w: io.Writer, opt: ^Marshal_Options, c: byte) -> (err: io.Error) {
|
opt_write_end :: proc(w: io.Writer, opt: ^Marshal_Options, c: byte) -> (err: io.Error) {
|
||||||
if opt.spec == .MJSON && opt.mjson_skipped_first_braces_start && !opt.mjson_skipped_first_braces_end {
|
if opt.spec == .MJSON && opt.mjson_skipped_first_braces_start && !opt.mjson_skipped_first_braces_end && opt.indentation == 0 && c == '}' {
|
||||||
if opt.indentation == 0 {
|
opt.mjson_skipped_first_braces_end = true
|
||||||
opt.mjson_skipped_first_braces_end = true
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
opt.indentation -= 1
|
opt.indentation -= 1
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package json
|
package json
|
||||||
|
|
||||||
|
import "core:strings"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
JSON
|
JSON
|
||||||
strict JSON
|
strict JSON
|
||||||
@@ -105,3 +107,26 @@ destroy_value :: proc(value: Value, allocator := context.allocator) {
|
|||||||
delete(v)
|
delete(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clone_value :: proc(value: Value, allocator := context.allocator) -> Value {
|
||||||
|
context.allocator = allocator
|
||||||
|
|
||||||
|
#partial switch &v in value {
|
||||||
|
case Object:
|
||||||
|
new_o := make(Object, len(v))
|
||||||
|
for key, elem in v {
|
||||||
|
new_o[strings.clone(key)] = clone_value(elem)
|
||||||
|
}
|
||||||
|
return new_o
|
||||||
|
case Array:
|
||||||
|
new_a := make(Array, len(v))
|
||||||
|
for elem, idx in v {
|
||||||
|
new_a[idx] = clone_value(elem)
|
||||||
|
}
|
||||||
|
return new_a
|
||||||
|
case String:
|
||||||
|
return strings.clone(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import "core:math"
|
|||||||
import "core:reflect"
|
import "core:reflect"
|
||||||
import "core:strconv"
|
import "core:strconv"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
|
|
||||||
Unmarshal_Data_Error :: enum {
|
Unmarshal_Data_Error :: enum {
|
||||||
Invalid_Data,
|
Invalid_Data,
|
||||||
@@ -137,9 +137,9 @@ assign_float :: proc(val: any, f: $T) -> bool {
|
|||||||
case complex64: dst = complex(f32(f), 0)
|
case complex64: dst = complex(f32(f), 0)
|
||||||
case complex128: dst = complex(f64(f), 0)
|
case complex128: dst = complex(f64(f), 0)
|
||||||
|
|
||||||
case quaternion64: dst = quaternion(f16(f), 0, 0, 0)
|
case quaternion64: dst = quaternion(w=f16(f), x=0, y=0, z=0)
|
||||||
case quaternion128: dst = quaternion(f32(f), 0, 0, 0)
|
case quaternion128: dst = quaternion(w=f32(f), x=0, y=0, z=0)
|
||||||
case quaternion256: dst = quaternion(f64(f), 0, 0, 0)
|
case quaternion256: dst = quaternion(w=f64(f), x=0, y=0, z=0)
|
||||||
|
|
||||||
case: return false
|
case: return false
|
||||||
}
|
}
|
||||||
@@ -204,14 +204,31 @@ unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
|
|||||||
|
|
||||||
v := v
|
v := v
|
||||||
ti := reflect.type_info_base(type_info_of(v.id))
|
ti := reflect.type_info_base(type_info_of(v.id))
|
||||||
// NOTE: 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 && token.kind != .Null {
|
||||||
if u, ok := ti.variant.(reflect.Type_Info_Union); ok && len(u.variants) == 1 && token.kind != .Null {
|
// NOTE: If it's a union with only one variant, then treat it as that variant
|
||||||
variant := u.variants[0]
|
if len(u.variants) == 1 {
|
||||||
v.id = variant.id
|
variant := u.variants[0]
|
||||||
ti = reflect.type_info_base(variant)
|
v.id = variant.id
|
||||||
if !reflect.is_pointer_internally(variant) {
|
ti = reflect.type_info_base(variant)
|
||||||
tag := any{rawptr(uintptr(v.data) + u.tag_offset), u.tag_type.id}
|
if !reflect.is_pointer_internally(variant) {
|
||||||
assign_int(tag, 1)
|
tag := any{rawptr(uintptr(v.data) + u.tag_offset), u.tag_type.id}
|
||||||
|
assign_int(tag, 1)
|
||||||
|
}
|
||||||
|
} else if v.id != Value {
|
||||||
|
for variant, i in u.variants {
|
||||||
|
variant_any := any{v.data, variant.id}
|
||||||
|
variant_p := p^
|
||||||
|
if err = unmarshal_value(&variant_p, variant_any); err == nil {
|
||||||
|
p^ = variant_p
|
||||||
|
|
||||||
|
raw_tag := i
|
||||||
|
if !u.no_nil { raw_tag += 1 }
|
||||||
|
tag := any{rawptr(uintptr(v.data) + u.tag_offset), u.tag_type.id}
|
||||||
|
assign_int(tag, raw_tag)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return UNSUPPORTED_TYPE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
package xml
|
||||||
|
|
||||||
/*
|
/*
|
||||||
An XML 1.0 / 1.1 parser
|
An XML 1.0 / 1.1 parser
|
||||||
|
|
||||||
@@ -9,7 +11,7 @@
|
|||||||
List of contributors:
|
List of contributors:
|
||||||
Jeroen van Rijn: Initial implementation.
|
Jeroen van Rijn: Initial implementation.
|
||||||
*/
|
*/
|
||||||
package xml
|
|
||||||
|
|
||||||
import "core:io"
|
import "core:io"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ example :: proc() {
|
|||||||
xml.destroy(docs[round])
|
xml.destroy(docs[round])
|
||||||
}
|
}
|
||||||
|
|
||||||
DOC :: #load("../../../../tests/core/assets/XML/unicode.xml")
|
DOC :: #load("../../../../tests/core/assets/XML/utf8.xml")
|
||||||
input := DOC
|
input := DOC
|
||||||
|
|
||||||
for round in 0..<N {
|
for round in 0..<N {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
package xml
|
||||||
|
|
||||||
/*
|
/*
|
||||||
An XML 1.0 / 1.1 parser
|
An XML 1.0 / 1.1 parser
|
||||||
|
|
||||||
@@ -6,7 +8,7 @@
|
|||||||
|
|
||||||
This file contains helper functions.
|
This file contains helper functions.
|
||||||
*/
|
*/
|
||||||
package xml
|
|
||||||
|
|
||||||
// Find parent's nth child with a given ident.
|
// Find parent's nth child with a given ident.
|
||||||
find_child_by_ident :: proc(doc: ^Document, parent_id: Element_ID, ident: string, nth := 0) -> (res: Element_ID, found: bool) {
|
find_child_by_ident :: proc(doc: ^Document, parent_id: Element_ID, ident: string, nth := 0) -> (res: Element_ID, found: bool) {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
package xml
|
||||||
|
|
||||||
/*
|
/*
|
||||||
An XML 1.0 / 1.1 parser
|
An XML 1.0 / 1.1 parser
|
||||||
|
|
||||||
@@ -9,7 +11,7 @@
|
|||||||
List of contributors:
|
List of contributors:
|
||||||
Jeroen van Rijn: Initial implementation.
|
Jeroen van Rijn: Initial implementation.
|
||||||
*/
|
*/
|
||||||
package xml
|
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:unicode"
|
import "core:unicode"
|
||||||
|
|||||||
@@ -1,39 +1,39 @@
|
|||||||
/*
|
/*
|
||||||
An XML 1.0 / 1.1 parser
|
XML 1.0 / 1.1 parser
|
||||||
|
|
||||||
Copyright 2021-2022 Jeroen van Rijn <nom@duclavier.com>.
|
2021-2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||||
Made available under Odin's BSD-3 license.
|
available under Odin's BSD-3 license.
|
||||||
|
|
||||||
A from-scratch XML implementation, loosely modelled on the [spec](https://www.w3.org/TR/2006/REC-xml11-20060816).
|
from-scratch XML implementation, loosely modelled on the [spec](https://www.w3.org/TR/2006/REC-xml11-20060816).
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
- Supports enough of the XML 1.0/1.1 spec to handle the 99.9% of XML documents in common current usage.
|
- Supports enough of the XML 1.0/1.1 spec to handle the 99.9% of XML documents in common current usage.
|
||||||
- Simple to understand and use. Small.
|
- Simple to understand and use. Small.
|
||||||
|
|
||||||
Caveats:
|
Caveats:
|
||||||
- We do NOT support HTML in this package, as that may or may not be valid XML.
|
- We do NOT support HTML in this package, as that may or may not be valid XML.
|
||||||
If it works, great. If it doesn't, that's not considered a bug.
|
If it works, great. If it doesn't, that's not considered a bug.
|
||||||
|
|
||||||
- We do NOT support UTF-16. If you have a UTF-16 XML file, please convert it to UTF-8 first. Also, our condolences.
|
- We do NOT support UTF-16. If you have a UTF-16 XML file, please convert it to UTF-8 first. Also, our condolences.
|
||||||
- <[!ELEMENT and <[!ATTLIST are not supported, and will be either ignored or return an error depending on the parser options.
|
- <[!ELEMENT and <[!ATTLIST are not supported, and will be either ignored or return an error depending on the parser options.
|
||||||
|
|
||||||
MAYBE:
|
MAYBE:
|
||||||
- XML writer?
|
- XML writer?
|
||||||
- Serialize/deserialize Odin types?
|
- Serialize/deserialize Odin types?
|
||||||
|
|
||||||
List of contributors:
|
List of contributors:
|
||||||
Jeroen van Rijn: Initial implementation.
|
- Jeroen van Rijn: Initial implementation.
|
||||||
*/
|
*/
|
||||||
package xml
|
package xml
|
||||||
// An XML 1.0 / 1.1 parser
|
// An XML 1.0 / 1.1 parser
|
||||||
|
|
||||||
import "core:bytes"
|
import "core:bytes"
|
||||||
import "core:encoding/entity"
|
import "core:encoding/entity"
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:os"
|
import "core:os"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
|
|
||||||
likely :: intrinsics.expect
|
likely :: intrinsics.expect
|
||||||
|
|
||||||
@@ -43,48 +43,32 @@ DEFAULT_OPTIONS :: Options{
|
|||||||
}
|
}
|
||||||
|
|
||||||
Option_Flag :: enum {
|
Option_Flag :: enum {
|
||||||
/*
|
// If the caller says that input may be modified, we can perform in-situ parsing.
|
||||||
If the caller says that input may be modified, we can perform in-situ parsing.
|
// If this flag isn't provided, the XML parser first duplicates the input so that it can.
|
||||||
If this flag isn't provided, the XML parser first duplicates the input so that it can.
|
|
||||||
*/
|
|
||||||
Input_May_Be_Modified,
|
Input_May_Be_Modified,
|
||||||
|
|
||||||
/*
|
// Document MUST start with `<?xml` prologue.
|
||||||
Document MUST start with `<?xml` prologue.
|
|
||||||
*/
|
|
||||||
Must_Have_Prolog,
|
Must_Have_Prolog,
|
||||||
|
|
||||||
/*
|
// Document MUST have a `<!DOCTYPE`.
|
||||||
Document MUST have a `<!DOCTYPE`.
|
|
||||||
*/
|
|
||||||
Must_Have_DocType,
|
Must_Have_DocType,
|
||||||
|
|
||||||
/*
|
// By default we skip comments. Use this option to intern a comment on a parented Element.
|
||||||
By default we skip comments. Use this option to intern a comment on a parented Element.
|
|
||||||
*/
|
|
||||||
Intern_Comments,
|
Intern_Comments,
|
||||||
|
|
||||||
/*
|
// How to handle unsupported parts of the specification, like <! other than <!DOCTYPE and <![CDATA[
|
||||||
How to handle unsupported parts of the specification, like <! other than <!DOCTYPE and <![CDATA[
|
|
||||||
*/
|
|
||||||
Error_on_Unsupported,
|
Error_on_Unsupported,
|
||||||
Ignore_Unsupported,
|
Ignore_Unsupported,
|
||||||
|
|
||||||
/*
|
// By default CDATA tags are passed-through as-is.
|
||||||
By default CDATA tags are passed-through as-is.
|
// This option unwraps them when encountered.
|
||||||
This option unwraps them when encountered.
|
|
||||||
*/
|
|
||||||
Unbox_CDATA,
|
Unbox_CDATA,
|
||||||
|
|
||||||
/*
|
// By default SGML entities like `>`, ` ` and ` ` are passed-through as-is.
|
||||||
By default SGML entities like `>`, ` ` and ` ` are passed-through as-is.
|
// This option decodes them when encountered.
|
||||||
This option decodes them when encountered.
|
|
||||||
*/
|
|
||||||
Decode_SGML_Entities,
|
Decode_SGML_Entities,
|
||||||
|
|
||||||
/*
|
// If a tag body has a comment, it will be stripped unless this option is given.
|
||||||
If a tag body has a comment, it will be stripped unless this option is given.
|
|
||||||
*/
|
|
||||||
Keep_Tag_Body_Comments,
|
Keep_Tag_Body_Comments,
|
||||||
}
|
}
|
||||||
Option_Flags :: bit_set[Option_Flag; u16]
|
Option_Flags :: bit_set[Option_Flag; u16]
|
||||||
@@ -97,28 +81,20 @@ Document :: struct {
|
|||||||
encoding: Encoding,
|
encoding: Encoding,
|
||||||
|
|
||||||
doctype: struct {
|
doctype: struct {
|
||||||
/*
|
// We only scan the <!DOCTYPE IDENT part and skip the rest.
|
||||||
We only scan the <!DOCTYPE IDENT part and skip the rest.
|
|
||||||
*/
|
|
||||||
ident: string,
|
ident: string,
|
||||||
rest: string,
|
rest: string,
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
// If we encounter comments before the root node, and the option to intern comments is given, this is where they'll live.
|
||||||
If we encounter comments before the root node, and the option to intern comments is given, this is where they'll live.
|
// Otherwise they'll be in the element tree.
|
||||||
Otherwise they'll be in the element tree.
|
|
||||||
*/
|
|
||||||
comments: [dynamic]string,
|
comments: [dynamic]string,
|
||||||
|
|
||||||
/*
|
// Internal
|
||||||
Internal
|
|
||||||
*/
|
|
||||||
tokenizer: ^Tokenizer,
|
tokenizer: ^Tokenizer,
|
||||||
allocator: mem.Allocator,
|
allocator: mem.Allocator,
|
||||||
|
|
||||||
/*
|
// Input. Either the original buffer, or a copy if `.Input_May_Be_Modified` isn't specified.
|
||||||
Input. Either the original buffer, or a copy if `.Input_May_Be_Modified` isn't specified.
|
|
||||||
*/
|
|
||||||
input: []u8,
|
input: []u8,
|
||||||
strings_to_free: [dynamic]string,
|
strings_to_free: [dynamic]string,
|
||||||
}
|
}
|
||||||
@@ -158,34 +134,24 @@ Encoding :: enum {
|
|||||||
UTF_8,
|
UTF_8,
|
||||||
ISO_8859_1,
|
ISO_8859_1,
|
||||||
|
|
||||||
/*
|
// Aliases
|
||||||
Aliases
|
|
||||||
*/
|
|
||||||
LATIN_1 = ISO_8859_1,
|
LATIN_1 = ISO_8859_1,
|
||||||
}
|
}
|
||||||
|
|
||||||
Error :: enum {
|
Error :: enum {
|
||||||
/*
|
// General return values.
|
||||||
General return values.
|
|
||||||
*/
|
|
||||||
None = 0,
|
None = 0,
|
||||||
General_Error,
|
General_Error,
|
||||||
Unexpected_Token,
|
Unexpected_Token,
|
||||||
Invalid_Token,
|
Invalid_Token,
|
||||||
|
|
||||||
/*
|
// Couldn't find, open or read file.
|
||||||
Couldn't find, open or read file.
|
|
||||||
*/
|
|
||||||
File_Error,
|
File_Error,
|
||||||
|
|
||||||
/*
|
// File too short.
|
||||||
File too short.
|
|
||||||
*/
|
|
||||||
Premature_EOF,
|
Premature_EOF,
|
||||||
|
|
||||||
/*
|
// XML-specific errors.
|
||||||
XML-specific errors.
|
|
||||||
*/
|
|
||||||
No_Prolog,
|
No_Prolog,
|
||||||
Invalid_Prolog,
|
Invalid_Prolog,
|
||||||
Too_Many_Prologs,
|
Too_Many_Prologs,
|
||||||
@@ -194,11 +160,9 @@ Error :: enum {
|
|||||||
Too_Many_DocTypes,
|
Too_Many_DocTypes,
|
||||||
DocType_Must_Preceed_Elements,
|
DocType_Must_Preceed_Elements,
|
||||||
|
|
||||||
/*
|
// If a DOCTYPE is present _or_ the caller
|
||||||
If a DOCTYPE is present _or_ the caller
|
// asked for a specific DOCTYPE and the DOCTYPE
|
||||||
asked for a specific DOCTYPE and the DOCTYPE
|
// and root tag don't match, we return `.Invalid_DocType`.
|
||||||
and root tag don't match, we return `.Invalid_DocType`.
|
|
||||||
*/
|
|
||||||
Invalid_DocType,
|
Invalid_DocType,
|
||||||
|
|
||||||
Invalid_Tag_Value,
|
Invalid_Tag_Value,
|
||||||
@@ -211,27 +175,20 @@ Error :: enum {
|
|||||||
Unsupported_Version,
|
Unsupported_Version,
|
||||||
Unsupported_Encoding,
|
Unsupported_Encoding,
|
||||||
|
|
||||||
/*
|
// <!FOO are usually skipped.
|
||||||
<!FOO are usually skipped.
|
|
||||||
*/
|
|
||||||
Unhandled_Bang,
|
Unhandled_Bang,
|
||||||
|
|
||||||
Duplicate_Attribute,
|
Duplicate_Attribute,
|
||||||
Conflicting_Options,
|
Conflicting_Options,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Implementation starts here.
|
|
||||||
*/
|
|
||||||
parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_handler := default_error_handler, allocator := context.allocator) -> (doc: ^Document, err: Error) {
|
parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_handler := default_error_handler, allocator := context.allocator) -> (doc: ^Document, err: Error) {
|
||||||
data := data
|
data := data
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
|
|
||||||
opts := validate_options(options) or_return
|
opts := validate_options(options) or_return
|
||||||
|
|
||||||
/*
|
// If `.Input_May_Be_Modified` is not specified, we duplicate the input so that we can modify it in-place.
|
||||||
If `.Input_May_Be_Modified` is not specified, we duplicate the input so that we can modify it in-place.
|
|
||||||
*/
|
|
||||||
if .Input_May_Be_Modified not_in opts.flags {
|
if .Input_May_Be_Modified not_in opts.flags {
|
||||||
data = bytes.clone(data)
|
data = bytes.clone(data)
|
||||||
}
|
}
|
||||||
@@ -252,10 +209,8 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
|||||||
element, parent: Element_ID
|
element, parent: Element_ID
|
||||||
open: Token
|
open: Token
|
||||||
|
|
||||||
/*
|
// If a DOCTYPE is present, the root tag has to match.
|
||||||
If a DOCTYPE is present, the root tag has to match.
|
// If an expected DOCTYPE is given in options (i.e. it's non-empty), the DOCTYPE (if present) and root tag have to match.
|
||||||
If an expected DOCTYPE is given in options (i.e. it's non-empty), the DOCTYPE (if present) and root tag have to match.
|
|
||||||
*/
|
|
||||||
expected_doctype := options.expected_doctype
|
expected_doctype := options.expected_doctype
|
||||||
|
|
||||||
loop: for {
|
loop: for {
|
||||||
@@ -263,17 +218,13 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
|||||||
// NOTE(Jeroen): This is faster as a switch.
|
// NOTE(Jeroen): This is faster as a switch.
|
||||||
switch t.ch {
|
switch t.ch {
|
||||||
case '<':
|
case '<':
|
||||||
/*
|
// Consume peeked `<`
|
||||||
Consume peeked `<`
|
|
||||||
*/
|
|
||||||
advance_rune(t)
|
advance_rune(t)
|
||||||
|
|
||||||
open = scan(t)
|
open = scan(t)
|
||||||
// NOTE(Jeroen): We're not using a switch because this if-else chain ordered by likelihood is 2.5% faster at -o:size and -o:speed.
|
// NOTE(Jeroen): We're not using a switch because this if-else chain ordered by likelihood is 2.5% faster at -o:size and -o:speed.
|
||||||
if likely(open.kind, Token_Kind.Ident) == .Ident {
|
if likely(open.kind, Token_Kind.Ident) == .Ident {
|
||||||
/*
|
// e.g. <odin - Start of new element.
|
||||||
e.g. <odin - Start of new element.
|
|
||||||
*/
|
|
||||||
element = new_element(doc)
|
element = new_element(doc)
|
||||||
if element == 0 { // First Element
|
if element == 0 { // First Element
|
||||||
parent = element
|
parent = element
|
||||||
@@ -286,11 +237,9 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
|||||||
|
|
||||||
parse_attributes(doc, &doc.elements[element].attribs) or_return
|
parse_attributes(doc, &doc.elements[element].attribs) or_return
|
||||||
|
|
||||||
/*
|
// If a DOCTYPE is present _or_ the caller
|
||||||
If a DOCTYPE is present _or_ the caller
|
// asked for a specific DOCTYPE and the DOCTYPE
|
||||||
asked for a specific DOCTYPE and the DOCTYPE
|
// and root tag don't match, we return .Invalid_Root_Tag.
|
||||||
and root tag don't match, we return .Invalid_Root_Tag.
|
|
||||||
*/
|
|
||||||
if element == 0 { // Root tag?
|
if element == 0 { // Root tag?
|
||||||
if len(expected_doctype) > 0 && expected_doctype != open.text {
|
if len(expected_doctype) > 0 && expected_doctype != open.text {
|
||||||
error(t, t.offset, "Root Tag doesn't match DOCTYPE. Expected: %v, got: %v\n", expected_doctype, open.text)
|
error(t, t.offset, "Root Tag doesn't match DOCTYPE. Expected: %v, got: %v\n", expected_doctype, open.text)
|
||||||
@@ -298,23 +247,17 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// One of these should follow:
|
||||||
One of these should follow:
|
// - `>`, which means we've just opened this tag and expect a later element to close it.
|
||||||
- `>`, which means we've just opened this tag and expect a later element to close it.
|
// - `/>`, which means this is an 'empty' or self-closing tag.
|
||||||
- `/>`, which means this is an 'empty' or self-closing tag.
|
|
||||||
*/
|
|
||||||
end_token := scan(t)
|
end_token := scan(t)
|
||||||
#partial switch end_token.kind {
|
#partial switch end_token.kind {
|
||||||
case .Gt:
|
case .Gt:
|
||||||
/*
|
// We're now the new parent.
|
||||||
We're now the new parent.
|
|
||||||
*/
|
|
||||||
parent = element
|
parent = element
|
||||||
|
|
||||||
case .Slash:
|
case .Slash:
|
||||||
/*
|
// Empty tag. Close it.
|
||||||
Empty tag. Close it.
|
|
||||||
*/
|
|
||||||
expect(t, .Gt) or_return
|
expect(t, .Gt) or_return
|
||||||
parent = doc.elements[element].parent
|
parent = doc.elements[element].parent
|
||||||
element = parent
|
element = parent
|
||||||
@@ -325,9 +268,7 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else if open.kind == .Slash {
|
} else if open.kind == .Slash {
|
||||||
/*
|
// Close tag.
|
||||||
Close tag.
|
|
||||||
*/
|
|
||||||
ident := expect(t, .Ident) or_return
|
ident := expect(t, .Ident) or_return
|
||||||
_ = expect(t, .Gt) or_return
|
_ = expect(t, .Gt) or_return
|
||||||
|
|
||||||
@@ -339,9 +280,7 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
|||||||
element = parent
|
element = parent
|
||||||
|
|
||||||
} else if open.kind == .Exclaim {
|
} else if open.kind == .Exclaim {
|
||||||
/*
|
// <!
|
||||||
<!
|
|
||||||
*/
|
|
||||||
next := scan(t)
|
next := scan(t)
|
||||||
#partial switch next.kind {
|
#partial switch next.kind {
|
||||||
case .Ident:
|
case .Ident:
|
||||||
@@ -370,10 +309,8 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
|||||||
}
|
}
|
||||||
|
|
||||||
case .Dash:
|
case .Dash:
|
||||||
/*
|
// Comment: <!-- -->.
|
||||||
Comment: <!-- -->.
|
// The grammar does not allow a comment to end in --->
|
||||||
The grammar does not allow a comment to end in --->
|
|
||||||
*/
|
|
||||||
expect(t, .Dash)
|
expect(t, .Dash)
|
||||||
comment := scan_comment(t) or_return
|
comment := scan_comment(t) or_return
|
||||||
|
|
||||||
@@ -395,23 +332,17 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else if open.kind == .Question {
|
} else if open.kind == .Question {
|
||||||
/*
|
// <?xml
|
||||||
<?xml
|
|
||||||
*/
|
|
||||||
next := scan(t)
|
next := scan(t)
|
||||||
#partial switch next.kind {
|
#partial switch next.kind {
|
||||||
case .Ident:
|
case .Ident:
|
||||||
if len(next.text) == 3 && strings.equal_fold(next.text, "xml") {
|
if len(next.text) == 3 && strings.equal_fold(next.text, "xml") {
|
||||||
parse_prologue(doc) or_return
|
parse_prologue(doc) or_return
|
||||||
} else if len(doc.prologue) > 0 {
|
} else if len(doc.prologue) > 0 {
|
||||||
/*
|
// We've already seen a prologue.
|
||||||
We've already seen a prologue.
|
|
||||||
*/
|
|
||||||
return doc, .Too_Many_Prologs
|
return doc, .Too_Many_Prologs
|
||||||
} else {
|
} else {
|
||||||
/*
|
// Could be `<?xml-stylesheet`, etc. Ignore it.
|
||||||
Could be `<?xml-stylesheet`, etc. Ignore it.
|
|
||||||
*/
|
|
||||||
skip_element(t) or_return
|
skip_element(t) or_return
|
||||||
}
|
}
|
||||||
case:
|
case:
|
||||||
@@ -425,15 +356,11 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
|||||||
}
|
}
|
||||||
|
|
||||||
case -1:
|
case -1:
|
||||||
/*
|
// End of file.
|
||||||
End of file.
|
|
||||||
*/
|
|
||||||
break loop
|
break loop
|
||||||
|
|
||||||
case:
|
case:
|
||||||
/*
|
// This should be a tag's body text.
|
||||||
This should be a tag's body text.
|
|
||||||
*/
|
|
||||||
body_text := scan_string(t, t.offset) or_return
|
body_text := scan_string(t, t.offset) or_return
|
||||||
needs_processing := .Unbox_CDATA in opts.flags
|
needs_processing := .Unbox_CDATA in opts.flags
|
||||||
needs_processing |= .Decode_SGML_Entities in opts.flags
|
needs_processing |= .Decode_SGML_Entities in opts.flags
|
||||||
@@ -613,9 +540,7 @@ parse_prologue :: proc(doc: ^Document) -> (err: Error) {
|
|||||||
doc.encoding = .LATIN_1
|
doc.encoding = .LATIN_1
|
||||||
|
|
||||||
case:
|
case:
|
||||||
/*
|
// Unrecognized encoding, assume UTF-8.
|
||||||
Unrecognized encoding, assume UTF-8.
|
|
||||||
*/
|
|
||||||
error(t, offset, "[parse_prologue] Warning: Unrecognized encoding: %v\n", attr.val)
|
error(t, offset, "[parse_prologue] Warning: Unrecognized encoding: %v\n", attr.val)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -658,11 +583,11 @@ skip_element :: proc(t: ^Tokenizer) -> (err: Error) {
|
|||||||
|
|
||||||
parse_doctype :: proc(doc: ^Document) -> (err: Error) {
|
parse_doctype :: proc(doc: ^Document) -> (err: Error) {
|
||||||
/*
|
/*
|
||||||
<!DOCTYPE greeting SYSTEM "hello.dtd">
|
<!DOCTYPE greeting SYSTEM "hello.dtd">
|
||||||
|
|
||||||
<!DOCTYPE greeting [
|
<!DOCTYPE greeting [
|
||||||
<!ELEMENT greeting (#PCDATA)>
|
<!ELEMENT greeting (#PCDATA)>
|
||||||
]>
|
]>
|
||||||
*/
|
*/
|
||||||
assert(doc != nil)
|
assert(doc != nil)
|
||||||
context.allocator = doc.allocator
|
context.allocator = doc.allocator
|
||||||
@@ -675,9 +600,7 @@ parse_doctype :: proc(doc: ^Document) -> (err: Error) {
|
|||||||
offset := t.offset
|
offset := t.offset
|
||||||
skip_element(t) or_return
|
skip_element(t) or_return
|
||||||
|
|
||||||
/*
|
// -1 because the current offset is that of the closing tag, so the rest of the DOCTYPE tag ends just before it.
|
||||||
-1 because the current offset is that of the closing tag, so the rest of the DOCTYPE tag ends just before it.
|
|
||||||
*/
|
|
||||||
doc.doctype.rest = string(t.src[offset : t.offset - 1])
|
doc.doctype.rest = string(t.src[offset : t.offset - 1])
|
||||||
return .None
|
return .None
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-35
@@ -1,15 +1,15 @@
|
|||||||
package fmt
|
package fmt
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
import "base:runtime"
|
||||||
import "core:math/bits"
|
import "core:math/bits"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:io"
|
import "core:io"
|
||||||
import "core:reflect"
|
import "core:reflect"
|
||||||
import "core:runtime"
|
|
||||||
import "core:strconv"
|
import "core:strconv"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
import "core:time"
|
import "core:time"
|
||||||
import "core:unicode/utf8"
|
import "core:unicode/utf8"
|
||||||
import "core:intrinsics"
|
|
||||||
|
|
||||||
// Internal data structure that stores the required information for formatted printing
|
// Internal data structure that stores the required information for formatted printing
|
||||||
Info :: struct {
|
Info :: struct {
|
||||||
@@ -216,7 +216,7 @@ tprintf :: proc(fmt: string, args: ..any) -> string {
|
|||||||
// Returns: A formatted string
|
// Returns: A formatted string
|
||||||
//
|
//
|
||||||
bprint :: proc(buf: []byte, args: ..any, sep := " ") -> string {
|
bprint :: proc(buf: []byte, args: ..any, sep := " ") -> string {
|
||||||
sb := strings.builder_from_bytes(buf[0:len(buf)])
|
sb := strings.builder_from_bytes(buf)
|
||||||
return sbprint(&sb, ..args, sep=sep)
|
return sbprint(&sb, ..args, sep=sep)
|
||||||
}
|
}
|
||||||
// Creates a formatted string using a supplied buffer as the backing array, appends newline. Writes into the buffer.
|
// Creates a formatted string using a supplied buffer as the backing array, appends newline. Writes into the buffer.
|
||||||
@@ -229,7 +229,7 @@ bprint :: proc(buf: []byte, 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
|
||||||
//
|
//
|
||||||
bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string {
|
bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string {
|
||||||
sb := strings.builder_from_bytes(buf[0:len(buf)])
|
sb := strings.builder_from_bytes(buf)
|
||||||
return sbprintln(&sb, ..args, sep=sep)
|
return sbprintln(&sb, ..args, sep=sep)
|
||||||
}
|
}
|
||||||
// Creates a formatted string using a supplied buffer as the backing array. Writes into the buffer.
|
// Creates a formatted string using a supplied buffer as the backing array. Writes into the buffer.
|
||||||
@@ -242,7 +242,7 @@ bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string {
|
|||||||
// Returns: A formatted string
|
// Returns: A formatted string
|
||||||
//
|
//
|
||||||
bprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string {
|
bprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string {
|
||||||
sb := strings.builder_from_bytes(buf[0:len(buf)])
|
sb := strings.builder_from_bytes(buf)
|
||||||
return sbprintf(&sb, fmt, ..args)
|
return sbprintf(&sb, fmt, ..args)
|
||||||
}
|
}
|
||||||
// Runtime assertion with a formatted message
|
// Runtime assertion with a formatted message
|
||||||
@@ -253,18 +253,24 @@ bprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string {
|
|||||||
// - args: A variadic list of arguments to be formatted
|
// - args: A variadic list of arguments to be formatted
|
||||||
// - loc: The location of the caller
|
// - loc: The location of the caller
|
||||||
//
|
//
|
||||||
// Returns: True if the condition is met, otherwise triggers a runtime assertion with a formatted message
|
@(disabled=ODIN_DISABLE_ASSERT)
|
||||||
//
|
assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_location) {
|
||||||
assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_location) -> bool {
|
|
||||||
if !condition {
|
if !condition {
|
||||||
p := context.assertion_failure_proc
|
// NOTE(dragos): We are using the same trick as in builtin.assert
|
||||||
if p == nil {
|
// to improve performance to make the CPU not
|
||||||
p = runtime.default_assertion_failure_proc
|
// execute speculatively, making it about an order of
|
||||||
|
// magnitude faster
|
||||||
|
@(cold)
|
||||||
|
internal :: proc(loc: runtime.Source_Code_Location, fmt: string, args: ..any) {
|
||||||
|
p := context.assertion_failure_proc
|
||||||
|
if p == nil {
|
||||||
|
p = runtime.default_assertion_failure_proc
|
||||||
|
}
|
||||||
|
message := tprintf(fmt, ..args)
|
||||||
|
p("Runtime assertion", message, loc)
|
||||||
}
|
}
|
||||||
message := tprintf(fmt, ..args)
|
internal(loc, fmt, ..args)
|
||||||
p("Runtime assertion", message, loc)
|
|
||||||
}
|
}
|
||||||
return condition
|
|
||||||
}
|
}
|
||||||
// Runtime panic with a formatted message
|
// Runtime panic with a formatted message
|
||||||
//
|
//
|
||||||
@@ -948,24 +954,10 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d
|
|||||||
start := 0
|
start := 0
|
||||||
|
|
||||||
flags: strconv.Int_Flags
|
flags: strconv.Int_Flags
|
||||||
if fi.hash && !fi.zero { flags |= {.Prefix} }
|
if fi.hash { flags |= {.Prefix} }
|
||||||
if fi.plus { flags |= {.Plus} }
|
if fi.plus { flags |= {.Plus} }
|
||||||
s := strconv.append_bits(buf[start:], u, base, is_signed, bit_size, digits, flags)
|
s := strconv.append_bits(buf[start:], u, base, is_signed, bit_size, digits, flags)
|
||||||
|
|
||||||
if fi.hash && fi.zero && fi.indent == 0 {
|
|
||||||
c: byte = 0
|
|
||||||
switch base {
|
|
||||||
case 2: c = 'b'
|
|
||||||
case 8: c = 'o'
|
|
||||||
case 12: c = 'z'
|
|
||||||
case 16: c = 'x'
|
|
||||||
}
|
|
||||||
if c != 0 {
|
|
||||||
io.write_byte(fi.writer, '0', &fi.n)
|
|
||||||
io.write_byte(fi.writer, c, &fi.n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prev_zero := fi.zero
|
prev_zero := fi.zero
|
||||||
defer fi.zero = prev_zero
|
defer fi.zero = prev_zero
|
||||||
fi.zero = false
|
fi.zero = false
|
||||||
@@ -1232,8 +1224,12 @@ _pad :: proc(fi: ^Info, s: string) {
|
|||||||
//
|
//
|
||||||
// NOTE: Can return "NaN", "+Inf", "-Inf", "+<value>", or "-<value>".
|
// NOTE: Can return "NaN", "+Inf", "-Inf", "+<value>", or "-<value>".
|
||||||
//
|
//
|
||||||
_fmt_float_as :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune, float_fmt: byte) {
|
_fmt_float_as :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune, float_fmt: byte, prec: int) {
|
||||||
prec := fi.prec if fi.prec_set else 3
|
prec := prec
|
||||||
|
if fi.prec_set {
|
||||||
|
prec = fi.prec
|
||||||
|
}
|
||||||
|
|
||||||
buf: [386]byte
|
buf: [386]byte
|
||||||
|
|
||||||
// Can return "NaN", "+Inf", "-Inf", "+<value>", "-<value>".
|
// Can return "NaN", "+Inf", "-Inf", "+<value>", "-<value>".
|
||||||
@@ -1258,11 +1254,13 @@ _fmt_float_as :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune, float_fmt: b
|
|||||||
//
|
//
|
||||||
fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
|
fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
|
||||||
switch verb {
|
switch verb {
|
||||||
case 'f', 'F', 'g', 'G', 'v':
|
case 'g', 'G', 'v':
|
||||||
_fmt_float_as(fi, v, bit_size, verb, 'f')
|
_fmt_float_as(fi, v, bit_size, verb, 'g', -1)
|
||||||
|
case 'f', 'F':
|
||||||
|
_fmt_float_as(fi, v, bit_size, verb, 'f', 3)
|
||||||
case 'e', 'E':
|
case 'e', 'E':
|
||||||
// BUG(): "%.3e" returns "3.000e+00"
|
// BUG(): "%.3e" returns "3.000e+00"
|
||||||
_fmt_float_as(fi, v, bit_size, verb, 'e')
|
_fmt_float_as(fi, v, bit_size, verb, 'e', 6)
|
||||||
|
|
||||||
case 'h', 'H':
|
case 'h', 'H':
|
||||||
prev_fi := fi^
|
prev_fi := fi^
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
//+build !freestanding !js
|
//+build !freestanding
|
||||||
|
//+build !js
|
||||||
package fmt
|
package fmt
|
||||||
|
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
import "core:os"
|
import "core:os"
|
||||||
import "core:io"
|
import "core:io"
|
||||||
import "core:bufio"
|
import "core:bufio"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package hash
|
package hash
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="speed")
|
||||||
crc32 :: proc(data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
|
crc32 :: proc(data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
package hash
|
package hash
|
||||||
|
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="speed")
|
||||||
adler32 :: proc(data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
|
adler32 :: proc(data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
*/
|
*/
|
||||||
package xxhash
|
package xxhash
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
|
|
||||||
mem_copy :: runtime.mem_copy
|
mem_copy :: runtime.mem_copy
|
||||||
byte_swap :: intrinsics.byte_swap
|
byte_swap :: intrinsics.byte_swap
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
package xxhash
|
package xxhash
|
||||||
|
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
=== XXH3 128-bit streaming ===
|
=== XXH3 128-bit streaming ===
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
package xxhash
|
package xxhash
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*************************************************************************
|
*************************************************************************
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
package xxhash
|
package xxhash
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
32-bit hash functions
|
32-bit hash functions
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
package xxhash
|
package xxhash
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
64-bit hash functions
|
64-bit hash functions
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ package image
|
|||||||
import "core:bytes"
|
import "core:bytes"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:compress"
|
import "core:compress"
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
67_108_864 pixels max by default.
|
67_108_864 pixels max by default.
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import "core:mem"
|
|||||||
import "core:strconv"
|
import "core:strconv"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
import "core:unicode"
|
import "core:unicode"
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
|
|
||||||
Image :: image.Image
|
Image :: image.Image
|
||||||
Format :: image.Netpbm_Format
|
Format :: image.Netpbm_Format
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import coretime "core:time"
|
|||||||
import "core:strings"
|
import "core:strings"
|
||||||
import "core:bytes"
|
import "core:bytes"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Cleanup of image-specific data.
|
Cleanup of image-specific data.
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ import "core:hash"
|
|||||||
import "core:bytes"
|
import "core:bytes"
|
||||||
import "core:io"
|
import "core:io"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
|
|
||||||
// Limit chunk sizes.
|
// Limit chunk sizes.
|
||||||
// By default: IDAT = 8k x 8k x 16-bits + 8k filter bytes.
|
// By default: IDAT = 8k x 8k x 16-bits + 8k filter bytes.
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
// operations into an abstracted stream interface.
|
// operations into an abstracted stream interface.
|
||||||
package io
|
package io
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
import "core:unicode/utf8"
|
import "core:unicode/utf8"
|
||||||
|
|
||||||
// Seek whence values
|
// Seek whence values
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
//+build !freestanding
|
||||||
package log
|
package log
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
|||||||
+37
-1
@@ -1,6 +1,6 @@
|
|||||||
package log
|
package log
|
||||||
|
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
|
||||||
|
|
||||||
@@ -116,6 +116,42 @@ panicf :: proc(fmt_str: string, args: ..any, location := #caller_location) -> !
|
|||||||
runtime.panic("log.panicf", location)
|
runtime.panic("log.panicf", location)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(disabled=ODIN_DISABLE_ASSERT)
|
||||||
|
assert :: proc(condition: bool, message := "", loc := #caller_location) {
|
||||||
|
if !condition {
|
||||||
|
@(cold)
|
||||||
|
internal :: proc(message: string, loc: runtime.Source_Code_Location) {
|
||||||
|
p := context.assertion_failure_proc
|
||||||
|
if p == nil {
|
||||||
|
p = runtime.default_assertion_failure_proc
|
||||||
|
}
|
||||||
|
log(.Fatal, message, location=loc)
|
||||||
|
p("runtime assertion", message, loc)
|
||||||
|
}
|
||||||
|
internal(message, loc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(disabled=ODIN_DISABLE_ASSERT)
|
||||||
|
assertf :: proc(condition: bool, fmt_str: string, args: ..any, loc := #caller_location) {
|
||||||
|
if !condition {
|
||||||
|
// NOTE(dragos): We are using the same trick as in builtin.assert
|
||||||
|
// to improve performance to make the CPU not
|
||||||
|
// execute speculatively, making it about an order of
|
||||||
|
// magnitude faster
|
||||||
|
@(cold)
|
||||||
|
internal :: proc(loc: runtime.Source_Code_Location, fmt_str: string, args: ..any) {
|
||||||
|
p := context.assertion_failure_proc
|
||||||
|
if p == nil {
|
||||||
|
p = runtime.default_assertion_failure_proc
|
||||||
|
}
|
||||||
|
message := fmt.tprintf(fmt_str, ..args)
|
||||||
|
log(.Fatal, message, location=loc)
|
||||||
|
p("Runtime assertion", message, loc)
|
||||||
|
}
|
||||||
|
internal(loc, fmt_str, ..args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+70
-56
@@ -1,19 +1,28 @@
|
|||||||
package log
|
package log
|
||||||
|
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
Log_Allocator_Format :: enum {
|
||||||
|
Bytes, // Actual number of bytes.
|
||||||
|
Human, // Bytes in human units like bytes, kibibytes, etc. as appropriate.
|
||||||
|
}
|
||||||
|
|
||||||
Log_Allocator :: struct {
|
Log_Allocator :: struct {
|
||||||
allocator: runtime.Allocator,
|
allocator: runtime.Allocator,
|
||||||
level: Level,
|
level: Level,
|
||||||
prefix: string,
|
prefix: string,
|
||||||
locked: bool,
|
locked: bool,
|
||||||
|
size_fmt: Log_Allocator_Format,
|
||||||
}
|
}
|
||||||
|
|
||||||
log_allocator_init :: proc(la: ^Log_Allocator, level: Level, allocator := context.allocator, prefix := "") {
|
log_allocator_init :: proc(la: ^Log_Allocator, level: Level, size_fmt := Log_Allocator_Format.Bytes,
|
||||||
|
allocator := context.allocator, prefix := "") {
|
||||||
la.allocator = allocator
|
la.allocator = allocator
|
||||||
la.level = level
|
la.level = level
|
||||||
la.prefix = prefix
|
la.prefix = prefix
|
||||||
la.locked = false
|
la.locked = false
|
||||||
|
la.size_fmt = size_fmt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -29,71 +38,80 @@ log_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
|
|||||||
old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, runtime.Allocator_Error) {
|
old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, runtime.Allocator_Error) {
|
||||||
la := (^Log_Allocator)(allocator_data)
|
la := (^Log_Allocator)(allocator_data)
|
||||||
|
|
||||||
|
if context.logger.procedure == nil || la.level < context.logger.lowest_level {
|
||||||
|
return la.allocator.procedure(la.allocator.data, mode, size, alignment, old_memory, old_size, location)
|
||||||
|
}
|
||||||
|
|
||||||
padding := " " if la.prefix != "" else ""
|
padding := " " if la.prefix != "" else ""
|
||||||
|
|
||||||
|
buf: [256]byte = ---
|
||||||
|
|
||||||
if !la.locked {
|
if !la.locked {
|
||||||
la.locked = true
|
la.locked = true
|
||||||
defer la.locked = false
|
defer la.locked = false
|
||||||
|
|
||||||
switch mode {
|
switch mode {
|
||||||
case .Alloc:
|
case .Alloc:
|
||||||
logf(
|
format: string
|
||||||
la.level,
|
switch la.size_fmt {
|
||||||
"%s%s>>> ALLOCATOR(mode=.Alloc, size=%d, alignment=%d)",
|
case .Bytes: format = "%s%s>>> ALLOCATOR(mode=.Alloc, size=%d, alignment=%d)"
|
||||||
la.prefix, padding, size, alignment,
|
case .Human: format = "%s%s>>> ALLOCATOR(mode=.Alloc, size=%m, alignment=%d)"
|
||||||
location = location,
|
}
|
||||||
)
|
str := fmt.bprintf(buf[:], format, la.prefix, padding, size, alignment)
|
||||||
|
context.logger.procedure(context.logger.data, la.level, str, context.logger.options, location)
|
||||||
|
|
||||||
case .Alloc_Non_Zeroed:
|
case .Alloc_Non_Zeroed:
|
||||||
logf(
|
format: string
|
||||||
la.level,
|
switch la.size_fmt {
|
||||||
"%s%s>>> ALLOCATOR(mode=.Alloc_Non_Zeroed, size=%d, alignment=%d)",
|
case .Bytes: format = "%s%s>>> ALLOCATOR(mode=.Alloc_Non_Zeroed, size=%d, alignment=%d)"
|
||||||
la.prefix, padding, size, alignment,
|
case .Human: format = "%s%s>>> ALLOCATOR(mode=.Alloc_Non_Zeroed, size=%m, alignment=%d)"
|
||||||
location = location,
|
}
|
||||||
)
|
str := fmt.bprintf(buf[:], format, la.prefix, padding, size, alignment)
|
||||||
|
context.logger.procedure(context.logger.data, la.level, str, context.logger.options, location)
|
||||||
|
|
||||||
case .Free:
|
case .Free:
|
||||||
if old_size != 0 {
|
if old_size != 0 {
|
||||||
logf(
|
format: string
|
||||||
la.level,
|
switch la.size_fmt {
|
||||||
"%s%s<<< ALLOCATOR(mode=.Free, ptr=%p, size=%d)",
|
case .Bytes: format = "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p, size=%d)"
|
||||||
la.prefix, padding, old_memory, old_size,
|
case .Human: format = "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p, size=%m)"
|
||||||
location = location,
|
}
|
||||||
)
|
str := fmt.bprintf(buf[:], format, la.prefix, padding, old_memory, old_size)
|
||||||
|
context.logger.procedure(context.logger.data, la.level, str, context.logger.options, location)
|
||||||
} else {
|
} else {
|
||||||
logf(
|
str := fmt.bprintf(buf[:], "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p)", la.prefix, padding, old_memory)
|
||||||
la.level,
|
context.logger.procedure(context.logger.data, la.level, str, context.logger.options, location)
|
||||||
"%s%s<<< ALLOCATOR(mode=.Free, ptr=%p)",
|
|
||||||
la.prefix, padding, old_memory,
|
|
||||||
location = location,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case .Free_All:
|
case .Free_All:
|
||||||
logf(
|
str := fmt.bprintf(buf[:], "%s%s<<< ALLOCATOR(mode=.Free_All)", la.prefix, padding)
|
||||||
la.level,
|
context.logger.procedure(context.logger.data, la.level, str, context.logger.options, location)
|
||||||
"%s%s<<< ALLOCATOR(mode=.Free_All)",
|
|
||||||
la.prefix, padding,
|
|
||||||
location = location,
|
|
||||||
)
|
|
||||||
case .Resize:
|
case .Resize:
|
||||||
logf(
|
format: string
|
||||||
la.level,
|
switch la.size_fmt {
|
||||||
"%s%s>>> ALLOCATOR(mode=.Resize, ptr=%p, old_size=%d, size=%d, alignment=%d)",
|
case .Bytes: format = "%s%s>>> ALLOCATOR(mode=.Resize, ptr=%p, old_size=%d, size=%d, alignment=%d)"
|
||||||
la.prefix, padding, old_memory, old_size, size, alignment,
|
case .Human: format = "%s%s>>> ALLOCATOR(mode=.Resize, ptr=%p, old_size=%m, size=%m, alignment=%d)"
|
||||||
location = location,
|
}
|
||||||
)
|
str := fmt.bprintf(buf[:], format, la.prefix, padding, old_memory, old_size, size, alignment)
|
||||||
|
context.logger.procedure(context.logger.data, la.level, str, context.logger.options, location)
|
||||||
|
|
||||||
|
case .Resize_Non_Zeroed:
|
||||||
|
format: string
|
||||||
|
switch la.size_fmt {
|
||||||
|
case .Bytes: format = "%s%s>>> ALLOCATOR(mode=.Resize_Non_Zeroed, ptr=%p, old_size=%d, size=%d, alignment=%d)"
|
||||||
|
case .Human: format = "%s%s>>> ALLOCATOR(mode=.Resize_Non_Zeroed, ptr=%p, old_size=%m, size=%m, alignment=%d)"
|
||||||
|
}
|
||||||
|
str := fmt.bprintf(buf[:], format, la.prefix, padding, old_memory, old_size, size, alignment)
|
||||||
|
context.logger.procedure(context.logger.data, la.level, str, context.logger.options, location)
|
||||||
|
|
||||||
case .Query_Features:
|
case .Query_Features:
|
||||||
logf(
|
str := fmt.bprintf(buf[:], "%s%sALLOCATOR(mode=.Query_Features)", la.prefix, padding)
|
||||||
la.level,
|
context.logger.procedure(context.logger.data, la.level, str, context.logger.options, location)
|
||||||
"%s%ALLOCATOR(mode=.Query_Features)",
|
|
||||||
la.prefix, padding,
|
|
||||||
location = location,
|
|
||||||
)
|
|
||||||
case .Query_Info:
|
case .Query_Info:
|
||||||
logf(
|
str := fmt.bprintf(buf[:], "%s%sALLOCATOR(mode=.Query_Info)", la.prefix, padding)
|
||||||
la.level,
|
context.logger.procedure(context.logger.data, la.level, str, context.logger.options, location)
|
||||||
"%s%ALLOCATOR(mode=.Query_Info)",
|
|
||||||
la.prefix, padding,
|
|
||||||
location = location,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,12 +120,8 @@ log_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
|
|||||||
la.locked = true
|
la.locked = true
|
||||||
defer la.locked = false
|
defer la.locked = false
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logf(
|
str := fmt.bprintf(buf[:], "%s%sALLOCATOR ERROR=%v", la.prefix, padding, err)
|
||||||
la.level,
|
context.logger.procedure(context.logger.data, la.level, str, context.logger.options, location)
|
||||||
"%s%ALLOCATOR ERROR=%v",
|
|
||||||
la.prefix, padding, error,
|
|
||||||
location = location,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return data, err
|
return data, err
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user