Compare commits

...

751 Commits

Author SHA1 Message Date
gingerBill aa846d0ea5 Fix union #maybe comparison against nil -llvm-api 2021-04-26 11:46:26 +01:00
gingerBill 8d0428a8b3 Merge pull request #915 from wilsonk/issue-820
Fix for issue 720 (import name is not an identifier)
2021-04-26 10:50:06 +01:00
Kelly Wilson 86c1aed20d Fix for issue 820 (import name is not an identifier) 2021-04-25 23:26:12 -06:00
gingerBill ff620422fa Fix #857 2021-04-25 19:40:02 +01:00
gingerBill 5685a8d885 Fix #911 for -llvm-api backend 2021-04-25 19:19:44 +01:00
gingerBill b8327ad00d Merge pull request #912 from odin-lang/llvm-api-ci-changes
Update Makefile to compile with LLVM C API
2021-04-25 19:02:24 +01:00
gingerBill 1bdce19c18 Update nightly.yml to support -llvm-api 2021-04-25 18:44:05 +01:00
gingerBill 041ff13672 Update CI for Linux 2021-04-25 18:06:29 +01:00
gingerBill 60a2eaf666 Revert changes to macOS Makefile 2021-04-25 18:04:07 +01:00
gingerBill ec2db568c1 Update Makefile for macOS to be more "correct" with Linux 2021-04-25 15:57:35 +01:00
gingerBill 1387fd9047 Make -llvm-api run first before old backend on Linux 2021-04-25 15:52:24 +01:00
gingerBill fb6288a54e Try specifying the specific libs on Linux 2021-04-25 15:48:54 +01:00
gingerBill 4d00058858 Try clang-11 2021-04-24 22:11:38 +01:00
gingerBill 5c26cf9d73 Try llvm-config-11 in the Makefil 2021-04-24 21:58:44 +01:00
gingerBill 6048d25d36 Try llvm-config-11 2021-04-24 21:26:14 +01:00
gingerBill 748f094e15 Add llvm-dev-11 to Linux CI (just testing) 2021-04-24 21:20:18 +01:00
gingerBill 184c686c7e Add clang-11 for Linux; blank out threading_example on darwin 2021-04-24 21:13:40 +01:00
gingerBill 240a568eb9 Update CI to run both old and new backends 2021-04-24 21:09:35 +01:00
gingerBill ad953c4670 Add cast on S_ISDIR 2021-04-24 21:08:15 +01:00
gingerBill 0f9b0c2052 Remove libllvm-11 2021-04-24 21:04:38 +01:00
gingerBill cde334ada3 Modify flags in Makefile 2021-04-24 21:03:22 +01:00
gingerBill 2b4010998d Up ci.yml 2021-04-24 20:55:50 +01:00
gingerBill 4272fe5e85 Update Makefile to compile with LLVM C API 2021-04-24 20:46:47 +01:00
gingerBill c29b643a58 Move out some intrinsics into separate procedures in llvm_backend.cpp; Rename InlineRangeStmt to UnrollRangeStmt (eventually merge the two AST nodes) 2021-04-24 15:00:01 +01:00
gingerBill c9b82a21e9 Move check_builtin_procedure to check_builtin.cpp 2021-04-23 10:01:52 +01:00
gingerBill bd31a99bf7 Remove redundant auto_cast 2021-04-23 09:50:26 +01:00
gingerBill 17bbb48d8a Warn on redundant auto_cast, and make an error on -vet 2021-04-22 17:36:28 +01:00
gingerBill ac53577e9b Improve auto_cast logic 2021-04-22 17:29:48 +01:00
gingerBill 896057b5a7 Reuse unused 'context' variables to minimize stack usage (-llvm-api) 2021-04-22 16:56:02 +01:00
gingerBill 01db195b47 Make main calling convention "odin" rather than "contextless" to simplify code generation 2021-04-22 16:26:28 +01:00
gingerBill d33350e3a5 Add truncate_to_byte and truncate_to_rune for packages strings and bytes 2021-04-22 15:49:10 +01:00
gingerBill 8a86c4c7cc Remove old code 2021-04-22 14:24:30 +01:00
gingerBill 1c9f48031d Change function pass manager passes 2021-04-22 13:54:26 +01:00
gingerBill 7fcd5ecbd5 Change how lb_populate_function_pass_manager works by using the default LLVM passes when not using minimal optimizations 2021-04-22 12:35:01 +01:00
gingerBill b68b090f13 Add intrinsics: overflow_add, overflow_sub, overflow_mul; Change byte swap behaviour in -llvm-api to be the same as the intrinsic 2021-04-22 11:33:46 +01:00
gingerBill 542098dc6f Add intrinsics: byte_swap (integers and floats); count_ones; trailing_zeros; reverse_bits 2021-04-22 11:06:16 +01:00
gingerBill 0a66f8c9a3 Remove intrinsics.x86_mmx type 2021-04-22 10:39:20 +01:00
gingerBill 158e4c0b6c Add @(cold) tag 2021-04-22 10:35:37 +01:00
gingerBill 47c7dc6a9b Add new intrinsics: debug_trap, trap, read_cycle_counter, expect 2021-04-22 10:35:17 +01:00
gingerBill 65551ba8fb Add optimization_mode attribute for procedures
Allowed modes: "none", "minimal", "size", "speed"
Currently: none == minimal and size == speed
2021-04-22 00:04:47 +01:00
gingerBill c7d92562c2 Fix typo 2021-04-21 23:40:19 +01:00
gingerBill 5b3802b8ca Add new -o:<string> flag as an alternative to -opt:<integer> 2021-04-21 23:39:48 +01:00
gingerBill 2fb0383e82 Add -build-mode:llvm-ir for -llvm-api backend 2021-04-21 23:25:08 +01:00
gingerBill 8077d5f565 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-04-21 10:52:34 +01:00
gingerBill 6b45856e81 Remove useless error message for constant binary expressions with non-constant operations 2021-04-21 10:52:26 +01:00
gingerBill 28e5df6e7f Merge pull request #907 from awwdev/fix-variable-name-in-path_unix
fix variable name in path_unix
2021-04-20 21:14:25 +01:00
André 22867ec6f0 fix variable name in path_unix
replace the immutable variable "path" (that is a parameter) with a new variable "path_str"
2021-04-20 22:09:10 +02:00
gingerBill d0a50ff0a3 Merge pull request #905 from jasmcaus/patch-1
Fix potential Typo
2021-04-20 10:44:54 +01:00
gingerBill e9b1d4f633 Fix #906 2021-04-20 10:34:41 +01:00
gingerBill ba9f0dd553 Hack ABI for windows passing pointers to structures 2021-04-19 23:03:22 +01:00
gingerBill c3b3194a00 Update builtin procedures to support the new allocator features (without breaking other code) 2021-04-19 22:44:20 +01:00
gingerBill 201cad51a9 Fix typo 2021-04-19 22:41:52 +01:00
gingerBill d21e522208 Add code that was accidentally removed 2021-04-19 22:35:59 +01:00
gingerBill f1bdd2e60f Improve #optional_ok logic for procedures; Add #optional_second for package runtime usage 2021-04-19 22:31:14 +01:00
gingerBill 0eb75886d1 Allow assignment of procedure calls with #optional_ok to single values 2021-04-19 16:02:36 +01:00
gingerBill 3612569624 Allow casting of #optional_ok call expressions 2021-04-19 15:46:12 +01:00
gingerBill c83d13d0cb Fix update_expr_type behaviour, along with fixing procedure groups updating the proc expr type 2021-04-19 15:12:10 +01:00
gingerBill f98c4d6837 Improve the Allocator interface to support returning Allocator_Error to allow for safer calls
Virtually all code (except for user-written custom allocators) should work as normal. Extra features will need to be added to make the current procedures support the `Allocator_Error` return value (akin to #optional_ok)
2021-04-19 12:31:31 +01:00
gingerBill a4d0092b16 Remove temporary reference types 2021-04-19 11:02:09 +01:00
gingerBill eb49b5f84a Fix override_entity_in_scope behaviour to correctly to report the changes upstream better 2021-04-19 11:01:00 +01:00
Jason Dsouza 9d949ef82e Fix potential Typo : S32 ==> I32 2021-04-19 11:41:02 +05:30
gingerBill ae04af4e4e Add package flags 2021-04-18 20:19:03 +01:00
gingerBill 3baddd4116 Improve init_string determination for constants 2021-04-18 20:13:20 +01:00
gingerBill 6ae468828c Improve odin-doc type information for Named types by storing the base type 2021-04-18 19:59:24 +01:00
gingerBill b59e110fec Add calling_convention to Type 2021-04-18 19:26:36 +01:00
gingerBill 4282688e60 Add calling_convention to odin-doc Type format 2021-04-18 19:26:21 +01:00
gingerBill 9b3fb25a41 Fix enumerated arrays for .odin-doc 2021-04-18 19:15:14 +01:00
gingerBill 2ce9873464 Remove dead comment 2021-04-18 18:53:19 +01:00
gingerBill 986844a0f0 Change elem_counts to i64le from u64 2021-04-18 18:48:56 +01:00
gingerBill 7c1f538c02 Change u32 to u32le 2021-04-18 18:46:29 +01:00
gingerBill 2f1c896290 Add -doc-format command for the new .odin-doc file format (to be used to generate documentation tools) 2021-04-18 18:33:15 +01:00
gingerBill 8827818b1d Clean-up fallback io.read_at and io.write_at behaviour 2021-04-14 21:25:46 +01:00
gingerBill e19958152a Fix floattidf 2021-04-14 20:45:05 +01:00
gingerBill 05a181d719 Fix style issues; Use new attribute @(cold) where appropriate in the new sync package 2021-04-14 20:19:02 +01:00
gingerBill d24784074c Add extra error to io.Error 2021-04-14 20:17:54 +01:00
gingerBill cd2476e084 Add buffer_read_at buffer_write_at 2021-04-14 20:13:26 +01:00
gingerBill ebbc33fdb5 Mockup of the new package os interface (incomplete and non-functioning) 2021-04-14 19:39:12 +01:00
gingerBill 3a4373641b Correct call site attributes 2021-04-14 17:16:10 +01:00
gingerBill 9adec628c1 Add @(cold) attribute to procedure declarations 2021-04-14 17:15:28 +01:00
gingerBill 3e54cddf64 Merge pull request #902 from Kelimion/run_as_user
Add support to core:windows to add/delete users.
2021-04-14 12:15:30 +01:00
gingerBill d602709133 Fix typo 2021-04-14 12:14:44 +01:00
gingerBill 8e1120bc09 Fix typo 2021-04-13 19:23:12 +01:00
gingerBill ebed29fc09 Revert *nix thread stuff to old sync
(I was just testing)
2021-04-13 19:09:04 +01:00
gingerBill bee8beb2c9 Default to pthreads in sync2 for *nix 2021-04-13 19:04:44 +01:00
gingerBill 12296a0dcc Allow intrinsics entities to be exported from other packages if wanted 2021-04-13 18:57:47 +01:00
Jeroen van Rijn 2942e45ff5 Placate -vet for unrelated core:thread update. 2021-04-13 02:23:14 +02:00
Jeroen van Rijn aca5c7c1c6 Placate -vet. 2021-04-13 02:18:47 +02:00
Jeroen van Rijn a1d871360c Add support to core:windows to add/delete users.
main :: proc() {

	using fmt;
	using windows;

	username := "testuser";
	password := "testpass";

	ok := add_user("", username, password);
	fmt.printf("add_user: %v\n", ok);
	pi := windows.PROCESS_INFORMATION{};

	ok2, path := windows.add_user_profile(username);
	fmt.printf("add_user_profile: %v, %v\n", ok2, path);

	ok3 := windows.delete_user_profile(username);
	fmt.printf("delete_user_profile: %v\n", ok3);

	ok4 := windows.delete_user("", username);
	fmt.printf("delete_user: %v\n", ok4);

	// Has optional bool to not wait on the process before returning.
	b := run_as_user(username, password, "C:\\Repro\\repro.exe", "Hellope!", &pi);
	fmt.printf("run_as_user: %v %v\n", b, pi);
}
2021-04-13 02:09:44 +02:00
gingerBill 2b36069924 Fix typo 2021-04-12 17:13:05 +01:00
gingerBill 4fb4ada2c7 Update sync2 to just use atomic intrinsics rather than the parapoly wrappers 2021-04-12 15:22:40 +01:00
gingerBill e3ee005404 Clean up path_unix.odin to make it not depend on package os 2021-04-11 19:05:01 +01:00
gingerBill e8bf1f2064 Minor fixes to platform checking code 2021-04-11 18:59:54 +01:00
gingerBill 1156bd9dd0 Remove thread stuff from sync2; Cleanup package thread 2021-04-11 18:25:56 +01:00
gingerBill 52c193316b Add Thread stuff to new sync package 2021-04-11 15:36:55 +01:00
gingerBill 2db1fe7429 New redesign of core:sync (stored under core:sync/sync2 for the time being) 2021-04-11 15:18:28 +01:00
gingerBill 5bc9e4e4f7 Merge pull request #901 from atkurtul/swarning
fix Syntax Warning
2021-04-09 11:28:06 +01:00
gingerBill 2d99a348b8 Fix proc literal bug not finding the associated DeclInfo 2021-04-09 11:27:44 +01:00
Gitea 011c8d5cda fix Syntax Warning 2021-04-09 13:03:01 +03:00
vassvik 8169cb4853 Fix missing newlines in core:math/linalg/specific.odin 2021-04-09 09:21:20 +02:00
gingerBill 18ab7fb68b Add break; for sanity 2021-04-08 15:32:59 +01:00
gingerBill 3eaf3327d4 Add "Dwarf Version" metadata for darwin on -llvm-api 2021-04-08 15:06:23 +01:00
gingerBill d721ffa6fa Add assertion check on parameter types for lb_emit_call_internal 2021-04-06 11:26:57 +01:00
gingerBill 535048e2b3 Fix LLVMConstIntOfArbitraryPrecision usage for a pointer 2021-04-06 11:07:05 +01:00
gingerBill 050d6f670e Merge pull request #896 from Platin21/Master-Origin
Add's Handling for Return Values for Linker-Stage
2021-04-05 19:06:04 +01:00
Platin21 ae7d7d33d4 Merge remote-tracking branch 'origin/master' into Master-Origin 2021-04-05 19:57:58 +02:00
gingerBill 19470683e7 Add runtime.extendhfsf2 to dependency list 2021-04-05 18:57:09 +01:00
gingerBill 394e4fcbad Add __extendhfsf2 for macOS 2021-04-05 18:44:08 +01:00
Platin21 f722cceef0 Removed debug code 2021-04-05 19:35:03 +02:00
Platin21 66fb2a94ee Adds return values to linker stages 2021-04-05 19:28:19 +02:00
gingerBill f78b2a6090 Undo fix 2021-04-03 18:04:39 +01:00
gingerBill 46bf39cae1 Fix lb_emit_array_ep 2021-04-03 18:03:36 +01:00
gingerBill 46c5c7d1ec Experiment with different opt passes 2021-04-01 23:41:01 +01:00
gingerBill bcda9ddee7 Add core:math/fixed 2021-04-01 17:09:34 +01:00
gingerBill 4a66cbb1f4 Fix signed fixed arithmetic intrinsics 2021-04-01 17:08:34 +01:00
gingerBill f0392d0c75 Cleanup again 2021-04-01 15:46:07 +01:00
gingerBill 3bd39886a0 Cleanup code for fixed-point intrinsics 2021-04-01 15:43:50 +01:00
gingerBill cef698afd6 Implement fixed-point arithmetic intrinsics for -llvm-api backend 2021-04-01 14:36:06 +01:00
gingerBill 0fc04a939e Add f16 support to core:math/linalg 2021-04-01 11:12:40 +01:00
gingerBill 3e1b4c17ac Fix alignment for complex32 and quaternion64 2021-04-01 11:10:17 +01:00
gingerBill b3e788b9d9 Fix missing complex32/quaternion64 checks 2021-04-01 10:56:57 +01:00
gingerBill 63bb26c0e0 Add f16 specific procedures to core:math 2021-04-01 10:52:46 +01:00
gingerBill b3dce34bc6 Add min(f16) and max(f16) support 2021-04-01 10:35:07 +01:00
gingerBill 491b282615 Add extra optimization level pass -opt:2 in lb_populate_module_pass_manager 2021-04-01 10:31:46 +01:00
gingerBill 54e6c50769 Implement f16 functionality 2021-04-01 10:06:00 +01:00
gingerBill a00d7cc705 Merge pull request #884 from corruptmemory/corruptmemory/better-define-errors
Helpful error message for invalid `-define:` cases
2021-03-30 21:59:03 +01:00
Jim Powers 9757af5e4a Helpful error message for invalid -define: cases 2021-03-30 16:49:18 -04:00
gingerBill 3359d0323a Change >> behaviour in LLVM to prevent stupid UB 2021-03-30 20:38:04 +01:00
gingerBill fbd01660ee Experiment with new grammatical parsing rule for expression level (-strict-style) idea 2021-03-30 11:48:32 +01:00
gingerBill bc5e80d7d6 Merge pull request #885 from DanielGavin/parser
Parser fixes
2021-03-30 11:47:45 +01:00
DanielGavin c429c85ade Merge remote-tracking branch 'upstream/master' into parser 2021-03-30 12:42:11 +02:00
DanielGavin 02bbac0903 changed error 2021-03-30 12:40:40 +02:00
DanielGavin b8658547e0 Give error if raw literal hits EOF, and stop removing .Using if it exists in flags and allowed_flags. 2021-03-30 12:38:58 +02:00
gingerBill 2c14accfd0 Merge pull request #879 from Kelimion/partial_hash_updates
Allow seeding CRC32, CRC64 & Adler32 with previous partial hash.
2021-03-30 11:20:19 +01:00
gingerBill 439e2c9242 Fix shifting limits and LLVM code gen bug relating to shifts 2021-03-29 23:15:31 +01:00
gingerBill 6fb0868517 Remove dead code and comments 2021-03-29 22:45:36 +01:00
gingerBill e1588c9322 Remove LLVMPassManagerBuilder usage in lb_populate_module_pass_manager; simplify lb_populate_function_pass_manager 2021-03-29 22:04:54 +01:00
gingerBill 8fcc6ca464 Add LLVM_USE_NO_EXTRA_PASSES build flag 2021-03-29 16:51:06 +01:00
gingerBill faa0240900 Change how lb_populate_module_pass_manager handles the LLVMPassManagerBuilder calls 2021-03-29 16:40:39 +01:00
gingerBill 66941aed0a Clamp maximum optimization level to 2 for -llvm-api 2021-03-29 15:51:15 +01:00
gingerBill fc8c94324e Fix typo 2021-03-29 15:43:33 +01:00
gingerBill 8c20ac1bf0 Add optional LLVM_USE_BASIC_PASSES build flag. If evaluates to a truthy value, it will be do only basic passes for -llvm-api 2021-03-29 14:55:37 +01:00
gingerBill 371094b067 Remove LLVMAddScalarReplAggregatesPass from passes 2021-03-29 14:50:09 +01:00
gingerBill c1e125a009 Change alignment of alloca to a larger one if OdinLLVMBuildTransmute requires it 2021-03-29 14:31:26 +01:00
gingerBill 48767301a4 Another minor change to OdinLLVMBuildTransmute regarding minimum source alignment 2021-03-29 14:11:02 +01:00
gingerBill 253a3edd30 Change OdinLLVMBuildTransmute when to deal with loads 2021-03-29 14:07:25 +01:00
gingerBill 8239cd34eb Issue #823 - Change semantics of disabled attribute to not evaluate any of the parameters at run time 2021-03-27 17:59:01 +00:00
gingerBill 818942b72e Minor code style change 2021-03-27 17:26:38 +00:00
gingerBill 9bac9af022 Clean up code for record type checking 2021-03-27 17:25:56 +00:00
gingerBill 342761e83a Refactor record polymorphic params code for unification 2021-03-27 17:21:12 +00:00
gingerBill 87bc9275fe Correct poly type determination of a where clause for an enumerated array 2021-03-27 16:49:56 +00:00
gingerBill 5ade037b7d Refactor polymorphic parameter for records; Improve error message for parametric polymorphic enumerated array index types 2021-03-27 16:42:42 +00:00
gingerBill 1e587d6635 Fix #883 - polymorphic specialization with target types of enumerated arrays 2021-03-27 15:22:05 +00:00
gingerBill e21d716720 Fix endian conversion to and from floats and ints 2021-03-27 14:33:33 +00:00
gingerBill d62ff39e60 Remove extra passes 2021-03-27 13:30:15 +00:00
gingerBill 0ccf103160 Fix byte swapping for endian specific types in -llvm-api 2021-03-27 12:48:29 +00:00
gingerBill 01c2662de4 Simplify ir_print.cpp escape byte code 2021-03-27 12:17:12 +00:00
gingerBill bd607b131e Fix #882 2021-03-27 12:13:17 +00:00
gingerBill 43ac6ca8f4 Add linalg.matrix_cast 2021-03-26 16:24:56 +00:00
gingerBill 62d2656f69 Add linalg.matrix_cast 2021-03-26 14:33:46 +00:00
gingerBill a611cf545d Add basic error correction in parser to check for unattached else 2021-03-26 11:39:46 +00:00
gingerBill 7463ad23d8 Move variable declarations to aid other compilers 2021-03-25 22:59:15 +00:00
gingerBill 6271d10af7 Fix to OdinLLVMBuildTransmute to goto general_end on different sized data 2021-03-25 22:54:49 +00:00
gingerBill c5c82e0551 Fix pseudo-constant local slice initialization 2021-03-25 22:47:23 +00:00
gingerBill 29ed1d5459 Minor zero enforcement 2021-03-25 22:34:35 +00:00
gingerBill 50b439daa8 Fix pseudo-constant local embedded slice generation 2021-03-25 22:27:52 +00:00
gingerBill 23c68b4f7a Change to assert to test both LLVMIsConstant and LLVMIsGlobalConstant 2021-03-25 22:14:38 +00:00
gingerBill 615104afd1 Revert change :D 2021-03-25 20:59:24 +00:00
gingerBill d3665331c7 Another minor fix to OdinLLVMBuildTransmute 2021-03-25 20:47:10 +00:00
gingerBill 04be6d190e Minor fix to OdinLLVMBuildTransmute 2021-03-25 20:45:22 +00:00
gingerBill 6668fd44cf Merge branch 'master' of https://github.com/odin-lang/Odin 2021-03-25 20:23:49 +00:00
gingerBill 0007ac63ed Correct #c_vararg behaviour on -llvm-api 2021-03-25 20:23:43 +00:00
gingerBill f656968aea Merge pull request #880 from breeo7/master
Add `container.Priority_Queue`
2021-03-25 16:02:22 +00:00
breeo 24e7b5ea78 Add container.Priority_Queue 2021-03-25 15:50:33 +01:00
Jeroen van Rijn f4d0f74dbb Allow seeding CRC32, CRC64 & Adler32 with previous partial hash.
Foo  := []u8{'F', 'o','o', '3', 'F', 'o', 'o', '4'};
crc     := hash.crc32(Foo[0:4]);
crc      = hash.crc32(Foo[4:], crc);
crc_all := hash.crc32(Foo);

fmt.printf("%8x %8x\n", crc, crc_all);
d6285ff7 d6285ff7

a32     := hash.adler32(Foo[0:4]);
a32      = hash.adler32(Foo[4:], a32);
a32_all := hash.adler32(Foo);

fmt.printf("%8x %8x\n", a32, a32_all);
0c5102b0 0c5102b0
2021-03-25 13:48:34 +01:00
gingerBill 7c951cbf0a Add SOA struct len/cap/allocator fields for the debug symbols 2021-03-24 23:12:54 +00:00
gingerBill 2d0e2625ac Ensure pointers are of the same type in LLVM ICmp 2021-03-24 23:01:48 +00:00
gingerBill 1aecd7f5ff Add support for soa slice reference iteration 2021-03-24 22:39:29 +00:00
gingerBill 5faf859a56 Support using on intermediate soa field value from a for-in statement 2021-03-24 22:34:55 +00:00
gingerBill 7028797d53 Implement soa_unzip 2021-03-24 19:29:25 +00:00
gingerBill 6c9d3715d8 Add type hinting to soa_zip 2021-03-24 18:08:34 +00:00
gingerBill 989a03dc77 soa_zip (-llvm-api only): creates an #soa[]struct from passed slices
x := []i32{1, 3, 9};
y := []f32{2, 4, 16};
z := []b32{true, false, true};

s_anonymous := soa_zip(x, y, z);
assert(s_anonymous[0]._1 == 2);

s_named := soa_zip(a=x, b=y, c=z);
assert(s_anonymous[0].b == 2);
2021-03-24 17:33:05 +00:00
gingerBill 7a045bd957 Merge pull request #877 from Kelimion/datetime
Add core:datetime_to_time
2021-03-24 16:59:19 +00:00
gingerBill a5329ae48c Add better package declaration specific error message (#878) 2021-03-24 14:34:30 +00:00
gingerBill 2ec3326653 Support #soa array iteration in a for in loop for -llvm-api backend only 2021-03-24 14:31:44 +00:00
gingerBill bec42e8dd3 Improve core:odin/ast ast.Range_Stmt to use generic number of vals rather than the fixed two to aid with parsing errors 2021-03-24 12:34:06 +00:00
gingerBill d969d0b264 Make for in logic a bit more generic 2021-03-24 12:31:05 +00:00
gingerBill 0e3ecc350a Make the parser support as many identifiers on the LHS in for in loops to improve error messages 2021-03-24 12:11:00 +00:00
gingerBill 295c1550a8 Support using variables in debug information 2021-03-24 10:57:00 +00:00
gingerBill fc1a352285 For bit_set, allow + and - to be aliases for | and &~, respectively 2021-03-23 23:34:01 +00:00
gingerBill 082381284c Remove dead code 2021-03-23 23:23:40 +00:00
gingerBill ccd078620b Improve error message in parser 2021-03-23 23:05:00 +00:00
gingerBill 08f7d3edbe Allow $ in polymorphic record parameter fields (but disallow mixing) 2021-03-23 22:59:10 +00:00
gingerBill c62980eaea Minor improvement to error message about assigning a type to a variable with no inference 2021-03-23 22:33:32 +00:00
gingerBill d88d6a1fdd bit_set support in debug symbols by treating them like a bit field of 1 bit booleans 2021-03-23 22:09:16 +00:00
gingerBill f1e13bdddb Prefix named types with package name in debug types 2021-03-23 20:45:39 +00:00
gingerBill 331167e91f Improve debug type names for composite types (arrays, map, struct, union) 2021-03-23 20:41:49 +00:00
gingerBill e229882fde Clean up some debug type code 2021-03-23 19:33:22 +00:00
gingerBill 300f988905 Add global variables to -llvm-api debug information 2021-03-23 18:34:20 +00:00
gingerBill 7f6a43f0af Move LLVM optimization procedures to a separate file to aid with organization 2021-03-23 18:24:49 +00:00
Jeroen van Rijn 06c5a7fb3e Correct overflowed months. 2021-03-23 18:49:50 +01:00
Jeroen van Rijn 781f784375 Add core:datetime_to_time
datetime_to_time takes separate parameters for date and time values and returns a time.Time and an `ok` bool.

If the values are out of range, they're considered modulo and ok will be set to false.
2021-03-23 18:41:40 +01:00
gingerBill ccd91aee5c Fix debug information for typeid on -llvm-api 2021-03-22 16:14:58 +00:00
gingerBill bf46a3f1d3 Correct debug info for basic composite types (e.g. string, any) 2021-03-22 15:36:18 +00:00
gingerBill 8ab1b32fe1 Add local debug variable support for -llvm-api 2021-03-22 14:51:19 +00:00
gingerBill 0355908af8 Start work on very basic LLVM debug type information 2021-03-22 13:09:23 +00:00
gingerBill fd7d70954e Begin integrating work from branch llvm-debug-symbols 2021-03-20 13:10:53 +00:00
gingerBill cb0bd80f50 Fix LLVMConstArray usage 2021-03-19 16:59:46 +00:00
gingerBill 5a67e6ecbd Fix LLVM asserts 2021-03-19 16:53:22 +00:00
gingerBill c8a823a387 Add ExternC.h 2021-03-19 15:49:07 +00:00
gingerBill 178e891c78 Fix some LLVM asserts by using LLVMConstNamedStruct everywhere 2021-03-19 15:47:10 +00:00
gingerBill bda9eb7348 Update llvm-c headers for Version 11.0.1 2021-03-19 15:46:33 +00:00
gingerBill 2b806f7463 Merge pull request #875 from atkurtul/master
Run an early memcpy pass regardless of the opt flag and return large structs by pointer on linux
2021-03-19 11:07:12 +00:00
atkurtul 6de0b68928 Merge pull request #2 from atkurtul/main
early memcpyopt
2021-03-19 10:26:10 +03:00
atkurtul bb6e6fb4ef Merge pull request #1 from atkurtul/llvm
return by pointer on linux
2021-03-19 07:30:18 +03:00
atil 5f5dfdc00e return by pointer on linux 2021-03-19 07:28:27 +03:00
Atil Kurtulmus 88b8052532 fix windows 2021-03-19 07:23:17 +03:00
atil 2c0ddfb5db fixtypo 2021-03-19 09:57:38 +03:00
atil 2f4902c9b9 fixtypo 2021-03-19 09:54:28 +03:00
atil d28f6144a4 early memcpyopt 2021-03-19 09:52:53 +03:00
gingerBill 3337412228 split*_iterator procedures for package bytes and strings 2021-03-18 13:26:33 +00:00
gingerBill e3f9d99a3b Merge branch 'master' of https://github.com/odin-lang/Odin 2021-03-18 13:25:47 +00:00
gingerBill 359ae29d98 Minor fixes 2021-03-18 13:25:41 +00:00
gingerBill 453b756edc Merge pull request #872 from Kelimion/fix-cubic
Fix typo in cubic().
2021-03-16 15:36:34 +00:00
Jeroen van Rijn d80670fe0c Fix typo in cubic(). 2021-03-16 16:34:59 +01:00
gingerBill 04e0cacd30 Update package core:math/linalg to support matrix3 euler angle operations 2021-03-16 12:14:54 +00:00
gingerBill b94ab4dc05 Make check_single_global_entity use create_checker_context 2021-03-15 14:05:38 +00:00
gingerBill 85fd8aaf37 Merge pull request #868 from nakst/patch-1
Make size of allocation multiple of the alignment
2021-03-15 11:47:48 +00:00
gingerBill 6412a18ae1 Merge pull request #869 from nakst/patch-2
Similar to the update to gb.h
2021-03-14 20:53:30 +00:00
Nakst acefb2edbc Similar to the update to gb.h 2021-03-14 20:37:32 +00:00
Nakst 0d1addf0d4 Make size of allocation multiple of the alignment
To silence an error from GCC's address sanitizer.
2021-03-14 20:36:35 +00:00
gingerBill f5142aaec4 Change from test_* prefix to @(test) attribute for odin test 2021-03-14 18:43:21 +00:00
gingerBill db0ac2ba98 Add "NO TESTS RAN" message to testing.runner if no tests were ran 2021-03-14 18:17:00 +00:00
gingerBill 468ad4837b Add pkg field to testing.Internal_Test 2021-03-14 18:15:08 +00:00
gingerBill 2aa588209e odin test to work with the new core:testing package 2021-03-14 18:01:31 +00:00
gingerBill 10f91a0d3f Make base32 and base64 adhere to -strict-style 2021-03-14 12:54:28 +00:00
gingerBill 8cc4cba06c Add support for backslash \ to consume a newline 2021-03-14 12:53:57 +00:00
gingerBill 8f6439fa6b Simplify expect_semicolon_newline_error rule 2021-03-13 23:17:56 +00:00
gingerBill 81efd2dc64 Remove && false from test code 2021-03-13 21:39:33 +00:00
gingerBill b5c0c68615 Add -strict-style flag: Enforces code style stricter whilst parsing, requiring such things as trailing commas 2021-03-13 21:20:46 +00:00
gingerBill a60d22fefd Make trailing comma usage consistent 2021-03-13 21:18:07 +00:00
gingerBill 8123ff83a3 Fix is_diverging_stmt for invalid statements 2021-03-13 15:01:23 +00:00
gingerBill 4e2a2ac80a Fix formatting of code 2021-03-10 10:53:30 +00:00
gingerBill d23c10d80e Merge pull request #865 from matias-eduardo/patch_llvm_bool_return_types
Add LLVM boolean compatibility to result types
2021-03-10 09:48:57 +00:00
Matias Fernandez ba62bcf116 Add LLVM boolean compatibility to result types 2021-03-09 23:17:34 -04:00
gingerBill 84bb349900 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-03-09 11:40:42 +00:00
gingerBill 3ff7bded64 Add intrinsics.volatile_store and intrinsics.volatile_load 2021-03-09 11:40:36 +00:00
gingerBill 8784b79482 Merge pull request #862 from Kelimion/kelimion/master
Remove double removal of extension when using the -out option.
2021-03-06 23:15:09 +00:00
gingerBill d0f923ba74 Merge pull request #863 from jlreymendez/dll_build_fix
Fix nullref access violation when building with no entry point
2021-03-06 23:14:55 +00:00
Jose Luis Rey Mendez 4e8ec4ce38 Fix nullref access violation when building with no entry point 2021-03-06 20:02:16 -03:00
Jose Luis Rey Mendez 4f1fb73f32 Added vs code to git ignore 2021-03-06 20:01:11 -03:00
Jeroen van Rijn cefde23232 Remove double removal of extension when using the -out option.
When specifying the out parameter, the extension was stripped twice.
If your path contains a ".", this caused issues.

e.g.
cd "C:\Repro\Path With a . In The Name\"
odin run repro.odin -keep-temp-files -out repro.exe

This would cause the files to end up as:
"C:\Repro\Path With a.exe", "C:\Repro\Path With a.ll", "C:\Repro\Path With a.bc" and "C:\Repro\Path With a.obj"

With this patch it works as expected, with or without a . in the file path.
2021-03-06 23:09:15 +01:00
gingerBill 083cec6c88 Remove dead code 2021-03-06 16:11:54 +00:00
gingerBill 45cd5c0b1c Remove test code 2021-03-06 16:11:39 +00:00
gingerBill 572b9d1b3f Fix context bug with deferred_* attributes which caused a new context to be created every time 2021-03-06 16:11:01 +00:00
gingerBill 0ae1b96182 Fix ir_print.cpp i32 line/column values 2021-03-05 15:42:59 +00:00
gingerBill 1988856eed Minimize the size of runtime.Source_Code_Location to use i32 instead of int 2021-03-05 12:56:36 +00:00
gingerBill 15dbc99cb9 Minimize TokenPos size by using i32 for line/column/offset and file_id instead of String
To make `i32` safe, the parser limits the file size of odin files to a maximum of 2GiB (which will be good enough for the vast vast majority of cases)
2021-03-04 16:45:30 +00:00
gingerBill 17eb0ce525 Minor update to math/linalg 2021-03-03 22:18:18 +00:00
gingerBill 619a977856 Improve math/linalg to support both f32 and f64 basic procedures for the specific*.odin files 2021-03-03 16:44:41 +00:00
gingerBill b727b6438b Minimize unneeded casts 2021-03-03 14:31:17 +00:00
gingerBill 75f127af7c Add -vet-extra (checks for unneeded casts and transmutes) 2021-03-03 14:17:48 +00:00
gingerBill c2794b62a9 Clean up logic for << and >> behaviour 2021-03-02 16:48:39 +00:00
gingerBill 4e63ab5edc Re-enable "LLVM Function Pass" 2021-03-02 13:36:57 +00:00
gingerBill 2a1bec9fbb Clean up lb_end_procedure_body logic 2021-03-02 00:43:25 +00:00
gingerBill 6faf024ab4 Remove unneeded return value from incl and excl 2021-03-02 00:40:40 +00:00
gingerBill 35edf45514 Add make_soa and delete_soa; Reorganize soa procedures into a separate file 2021-03-01 18:07:09 +00:00
gingerBill 667aa3671e Fix Addressing for SOA on store; Add intrinsics.type_struct_field_count(T) 2021-03-01 17:54:49 +00:00
gingerBill b428e9ee14 Improve lb_end_procedure_body logic 2021-03-01 15:14:21 +00:00
gingerBill 868117cddd Remove hack in lb_addr_store 2021-03-01 12:59:17 +00:00
gingerBill 9e0210f7f6 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-03-01 12:15:38 +00:00
gingerBill 302742689b Patch win64 ABI problem caused by a bug in LLVM for 128 bit integers 2021-03-01 12:15:28 +00:00
gingerBill 6ffb4d2683 Minor changes to function pass manager in llvm_backend.cpp 2021-02-27 17:47:57 +00:00
gingerBill 4f298a5314 Fix LLVM -opt:2 bug for initializing global variables 2021-02-27 15:54:22 +00:00
gingerBill f49278b5f4 Merge pull request #855 from nakst/master
Update Essence API header
2021-02-27 11:34:38 +00:00
gingerBill a2557142cc Update package os for package path/filepath support on macOS 2021-02-27 11:30:43 +00:00
gingerBill fa09640e7e Fix slice.concatenate 2021-02-27 10:58:10 +00:00
gingerBill 1f9a2df42b Fix crash when a forced dependency doesn't exist 2021-02-27 10:57:44 +00:00
gingerBill ee04dde7c2 HACK check_unchecked_bodies further! 2021-02-26 15:34:12 +00:00
gingerBill 88599eeac1 Update build-m1.sh to remove unneeded macro define 2021-02-26 15:29:41 +00:00
gingerBill 54194af71c Fix patch (yeah... I know) 2021-02-26 15:29:10 +00:00
gingerBill 575c7ff031 Patch issue with minimum dependency system and how it interacts with para poly procedures 2021-02-26 15:09:32 +00:00
nakst 7b4ddd9b18 update essence API header 2021-02-26 14:46:38 +00:00
gingerBill ac155d9036 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-02-26 10:56:40 +00:00
gingerBill d772710ae7 Add message to assert in ir_build_expr_internal 2021-02-26 10:56:32 +00:00
gingerBill 172fc9a46c Merge pull request #853 from ttvd/master
Fixing a typo in llvm backend.
2021-02-25 19:13:49 +00:00
Mykola Konyk 8182d9e828 Fixing a typo in llvm backend. 2021-02-25 13:13:25 -05:00
gingerBill d0ac9f605d Merge branch 'master' of https://github.com/odin-lang/Odin 2021-02-25 13:17:07 +00:00
gingerBill 3eae69effc Make USE_NEW_LLVM_ABI_SYSTEM the actual behaviour and remove the previous approach 2021-02-25 13:17:00 +00:00
gingerBill 53e4c536a1 Merge pull request #852 from Tetralux/fix-dll-mac
Fix -build-mode:shared on Darwin
2021-02-25 12:17:29 +00:00
gingerBill 84deee75cc Make lb_create_enum_attribute ignore certain attributes (they are not properly supported by the actual LLVM C API) 2021-02-25 11:39:46 +00:00
gingerBill 82275082ff Add #force_inline parsing directly to expression statements 2021-02-25 10:01:12 +00:00
gingerBill fc48e9638a Merge branch 'master' of https://github.com/odin-lang/Odin 2021-02-25 09:55:56 +00:00
gingerBill 4a69bfada1 Remove #force_inline for and only have #unroll for 2021-02-25 09:55:48 +00:00
gingerBill 4d13a43590 Enforce -llvm-api on Mac M1 2021-02-25 00:39:44 +00:00
gingerBill b2fdb53e26 fix timings.cpp for M1 2021-02-25 00:39:26 +00:00
gingerBill 58422711d1 Remove sret attribute in llvm_abi.cpp 2021-02-25 00:22:47 +00:00
gingerBill ba817d153c Get compiling on Mac Mini M1 2021-02-24 23:21:34 +00:00
gingerBill 2d88c6c6a5 Begin work on aarch64 ABI for -llvm-api 2021-02-24 16:49:19 +00:00
gingerBill a6fdb5eb5e Add -DUSE_NEW_LLVM_ABI_SYSTEM by default to Windows build for -llvm-api 2021-02-24 16:00:05 +00:00
gingerBill 425bb0579e Default to -microarch:generic 2021-02-24 15:59:31 +00:00
Tetralux a9af8b093d Fix -build-mode:shared on Darwin
Apparently, the '__$startup_runtime' symbol to initialize RTTI stuff has
three underscores (not two) on Darwin!
2021-02-24 02:18:47 +00:00
gingerBill 8f9111e552 Build tag to make all declarations within a file private to the package //+private 2021-02-23 20:40:48 +00:00
gingerBill 731e6ca3a6 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-02-23 20:40:09 +00:00
gingerBill 79eb46bce3 Replace inline uses in the rest of core with #force_inline 2021-02-23 20:39:59 +00:00
gingerBill d39d238754 Merge pull request #851 from WalterPlinge/master
Fixed linalg distance function return type
2021-02-23 20:17:47 +00:00
WalterPlinge 0e9dee62bf Update extended.odin 2021-02-23 20:10:43 +00:00
gingerBill 533dde4648 Add deprecation message for inline and no_inline to use #force_inline and #force_no_inline instead 2021-02-23 16:22:28 +00:00
gingerBill 6988b12012 Replace inline with #force_inline in sys/es/api.odin 2021-02-23 16:15:37 +00:00
gingerBill aa93305015 Replace usage of inline proc with #force_inline proc in the core library 2021-02-23 16:14:47 +00:00
gingerBill 41b854f192 Remove #opaque types 2021-02-23 15:45:06 +00:00
gingerBill 28f279329d Remove bit_field keyword and parsing logic 2021-02-23 15:29:54 +00:00
gingerBill fe33a64b2e Remove #opaque usage in core library 2021-02-23 15:21:05 +00:00
gingerBill f95185a16e Fix new flags 2021-02-23 15:05:03 +00:00
gingerBill 01313eec7f Add flags -ignore-warnings and -warnings-as-errors 2021-02-23 14:59:28 +00:00
gingerBill a1693c0184 Deprecate inline for in favour of #unroll for 2021-02-23 14:45:15 +00:00
gingerBill 657c0ac4f5 Update odin/parser 2021-02-23 14:38:46 +00:00
gingerBill 908a403d78 Add #force_inline, #force_no_inline and #unroll for the transition to deprecate and then remove the keywords inline and no_inline
`inline for` will be replaced with `#unroll for`
2021-02-23 14:37:05 +00:00
gingerBill 28ed310f31 Remove "pure" and "pure_none" calling conventions 2021-02-23 13:10:23 +00:00
gingerBill a652c24ac3 Remove opaque keyboard 2021-02-23 13:02:18 +00:00
gingerBill 595885d3db Remove bit_field in type info, runtime, and general core library 2021-02-19 11:36:23 +00:00
gingerBill efdee0dafb Remove bit_field type from Odin (keyword and dead runtime code still exists) 2021-02-19 11:31:14 +00:00
gingerBill f332cf498d Merge branch 'master' of https://github.com/odin-lang/Odin 2021-02-19 00:05:02 +00:00
gingerBill 6ae619c0a6 Add to package math/bits bitfield_extract and bitfield_insert 2021-02-19 00:04:48 +00:00
gingerBill 7ea86f9c91 Merge pull request #844 from DanielGavin/master
Incorrect setting of allocators in stat_linux and read_dir includes itself and parent
2021-02-12 22:44:22 +00:00
DanielGavin 0f11c47579 set the correct allocators and ignore the previous and parent directory in readdir. 2021-02-12 23:22:53 +01:00
gingerBill 5cced38a6e Add kernel32 memory api 2021-02-11 15:31:51 +00:00
gingerBill d5dfa14f18 Clear up fmt.wprint* length logic 2021-02-11 10:44:38 +00:00
gingerBill fa02dc9736 Fix #840 2021-02-11 10:15:58 +00:00
gingerBill 92431c83ec Change default_temp_allocator_proc behaviour to use the default_allocator when the backing data has not been set. 2021-02-09 14:00:59 +00:00
gingerBill f5418837f0 Minor style clean up 2021-02-04 15:02:07 +00:00
gingerBill 825c5a963f Improve fmt's %#v behaviour for nested records 2021-02-04 14:52:23 +00:00
gingerBill f50ea36c70 Fix miscorrect type usage 2021-02-04 14:24:42 +00:00
gingerBill de9b6e3f6e Correct sys/win32 to match sys/windows 2021-02-04 13:50:48 +00:00
gingerBill 415379e1cf Fix delete_map 2021-02-02 16:30:34 +00:00
gingerBill d168c7936e Fix slices of slices in procedures for -llvm-api 2021-01-27 15:56:32 +00:00
gingerBill aed63a6e8b Update package reflect 2021-01-27 15:27:59 +00:00
gingerBill 98521618e6 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-01-27 15:27:45 +00:00
gingerBill e64eb74eef Fix #831 2021-01-27 15:27:38 +00:00
gingerBill 31528f5ea2 Merge pull request #836 from laleksic/master
Mirror Windows interface for Linux in os module
2021-01-26 12:29:33 +00:00
Luka Aleksić ac184957db Fix CI 2021-01-21 20:55:58 +01:00
Luka Aleksić 92e23ec397 * Add some procedures to path_unix to mirror the path_windows API
* Add files stat_linux and dir_linux to mirror the stat/dir_windows API
* Add helper functions to os_linux that are used by the above
2021-01-21 20:20:38 +01:00
gingerBill c71c86f563 Merge pull request #821 from corruptmemory/corruptmemory/fix-for-c_vararg
Fix for `c_vararg` issue
2021-01-21 16:09:50 +00:00
gingerBill 773be83cad Merge pull request #835 from laleksic/master
Tracking allocator now tracks bad free calls
2021-01-21 16:09:20 +00:00
gingerBill e789e1eac1 Merge pull request #834 from DanielGavin/master
Invalid expression causes segmentation fault in parse_binary_expr in core/odin package
2021-01-21 16:08:30 +00:00
Luka Aleksić 00ebc877a1 Tracking allocator now tracks bad free calls 2021-01-21 00:19:56 +01:00
DanielGavin 3224869c29 handle expr is null in parse_binary_expr 2021-01-20 16:10:16 +01:00
DanielGavin 38c6182280 Merge branch 'master' of https://github.com/DanielGavin/Odin 2021-01-20 16:05:15 +01:00
Jim Powers 24db60eb4b Fix for c_vararg issue
Fixes #817
2021-01-18 20:05:33 -05:00
gingerBill 53d8ec4d15 Add extra error checks 2021-01-18 11:34:09 +00:00
gingerBill 2990b3efd5 Fix #825 2021-01-17 11:59:51 +00:00
gingerBill c653e400db Add extra error checking in parser.cpp 2021-01-15 16:15:03 +00:00
gingerBill e884f8c165 Fix typo 2021-01-15 15:46:15 +00:00
gingerBill 3bcccf88d5 vet all core packages 2021-01-09 23:43:34 +00:00
gingerBill 9e8c46b8de Add time.Tick for performance related timings 2021-01-09 01:08:16 +00:00
gingerBill fba4bfb2d5 Minor cleanup of slice/slice.odin code 2021-01-09 00:40:30 +00:00
gingerBill 79432be784 Add the mini ginger* hashes to package hash 2021-01-09 00:33:34 +00:00
gingerBill 56980e51da Update package io 2021-01-09 00:30:10 +00:00
gingerBill 37253f2621 Add encoding/csv Writer 2021-01-09 00:21:47 +00:00
gingerBill da380d6fc4 Add encoding/csv Reader 2021-01-08 23:24:35 +00:00
gingerBill bf183b2c2c Update c/frontend/preprocessor 2021-01-06 11:22:15 +00:00
gingerBill a07d199a48 Add slice.map_keys, slice.map_values, slice.map_entries, slice.map_entry_infos 2021-01-06 11:21:22 +00:00
gingerBill fa0e4c1294 Add -no-entry-point to help 2021-01-04 08:56:42 +00:00
gingerBill 60fe3c9ec6 Remove unused import from doc.odin 2021-01-02 16:50:24 +00:00
gingerBill a6ce417a35 Clean up doc.odin 2021-01-02 16:44:32 +00:00
gingerBill 6523aefdcc Merge branch 'master' of https://github.com/odin-lang/Odin 2021-01-02 16:36:51 +00:00
gingerBill 31c4a9d770 Add packages "core:c/frontend/tokenizer" and "core:c/frontend/preprocessor" 2021-01-02 16:36:43 +00:00
gingerBill 9fa6427a18 Merge pull request #816 from kennethmaples/file-time-fix
Fix layout of Stat for linux and make usage consistent
2020-12-29 11:38:17 +00:00
kennethmaples 6d5bd8bead Fix layout of Stat for linux and make usage consistent across unix variants 2020-12-29 17:45:19 +08:00
gingerBill 98ad912509 Fix typo 2020-12-27 16:16:04 +00:00
DanielGavin bd6ead32f8 Merge pull request #1 from odin-lang/master
update from master
2020-12-18 14:19:03 +01:00
gingerBill 3558848da8 Fix ir_print.cpp for typeid constants 2020-12-17 17:36:59 +00:00
gingerBill 720f2c7c61 Allow check_expr_with_type_hint to allow assignment of types to typeid without requiring typeid_of 2020-12-17 14:23:45 +00:00
gingerBill e6dfc22b8a Add bytes.buffer_write_to and bytes.buffer_read_from 2020-12-17 00:47:05 +00:00
gingerBill 1470cab842 Make bytes.odin consistent with strings.odin in functionality 2020-12-17 00:36:25 +00:00
gingerBill a31b992d2b Rename bytes/strings.odin to bytes/bytes.odin 2020-12-17 00:25:05 +00:00
gingerBill 5faa560f82 Make container.Map have similar semantics to the built-in map type 2020-12-15 23:23:07 +00:00
gingerBill 6c2b93d519 Improve text/scanner whitespace parameter to use a bit_set instead; Improve error message for for x in y where y is not iterable but allows in as an operator 2020-12-15 22:28:40 +00:00
gingerBill 2957da538d Add strings.Intern 2020-12-15 22:07:53 +00:00
gingerBill 089eccb245 Fix minor constant value declaration bug 2020-12-14 22:47:21 +00:00
gingerBill cbd4aa5392 Remove unused constant 2020-12-14 17:40:57 +00:00
gingerBill 1d333fedaa Support %i as an alternative to %d 2020-12-14 17:00:29 +00:00
gingerBill 82d63306c4 Fix enumerated array index printing bug #808 2020-12-14 16:55:19 +00:00
gingerBill 416051f17b Fix #811 2020-12-14 16:39:31 +00:00
gingerBill f6e2d74d10 Keep -vet happy 2020-12-14 14:36:45 +00:00
gingerBill aa2562fe7c Replace procedure call 2020-12-14 14:33:32 +00:00
gingerBill c17d17a9b4 Remove unused procedure 2020-12-14 14:32:34 +00:00
gingerBill 404c9e40ee Update io/util.odin 2020-12-14 14:31:18 +00:00
gingerBill 34788bfced Merge branch 'master' of https://github.com/odin-lang/Odin 2020-12-13 17:09:47 +00:00
gingerBill cffbd2d276 Add odin/parser/parse_files.odin 2020-12-13 17:09:41 +00:00
gingerBill 9250e4d3df Merge pull request #810 from oskarnp/fix-math-factorial
Fix math.factorial()
2020-12-13 11:17:59 +00:00
Oskar Nordquist 60b9ef1f5d Fix math.factorial() 2020-12-13 02:36:29 +01:00
gingerBill f64584b92a Improve -insert-semicolon rules 2020-12-09 23:40:45 +00:00
gingerBill 9eb12889f4 Merge branch 'master' of https://github.com/odin-lang/Odin 2020-12-09 23:35:14 +00:00
gingerBill 6f6a3f2ccf Fix typos for -insert-semicolon 2020-12-09 23:35:08 +00:00
gingerBill a574ebe5ec Merge pull request #805 from DanielGavin/master
small fixes to the parser
2020-12-09 23:22:11 +00:00
gingerBill 9be0272c13 Merge pull request #806 from arqv/nix
Add Nix derivation
2020-12-09 23:21:21 +00:00
Lyla Bravo 5923470e35 Add Nix derivation 2020-12-09 18:51:37 -03:00
DanielGavin 934809397f small fixes to the parser 2020-12-09 21:33:10 +01:00
gingerBill b6aa549eb8 Fix typo 2020-12-08 17:01:28 +00:00
gingerBill 168532ae8d Add runtime.print_strings 2020-12-08 17:01:06 +00:00
gingerBill d7a5767aa3 If ir_type_requires_mem_zero is stored with zero, don't store again with the zeroinitializer 2020-12-08 15:43:57 +00:00
gingerBill 15bf57e4e1 Fix typo 2020-12-08 15:03:46 +00:00
gingerBill 83cd2473f2 Improve rules for zeroing types with the ir.cpp backend with ir_type_requires_mem_zero 2020-12-08 15:00:07 +00:00
gingerBill 510d1f2518 Add docs to text/scanner 2020-12-07 12:43:28 +00:00
gingerBill 7b55068b04 Unify peek_n with peek and renamescan_peek_n to peek_token 2020-12-07 12:13:41 +00:00
gingerBill b9aa94ee0d Add scan_peek_n to package text/scanner #800 2020-12-07 11:30:54 +00:00
gingerBill 96d8971d87 Add peek_n to package text/scanner 2020-12-07 10:55:27 +00:00
gingerBill 98c8fde098 Remove unused tokens 2020-12-06 13:17:48 +00:00
gingerBill 1ac84b09a1 Add comments to ast.walk related entities 2020-12-06 13:04:11 +00:00
gingerBill 21d8562923 Add ast.walk and ast.inspect 2020-12-06 12:59:26 +00:00
gingerBill c8360f4fff Update ast.clone 2020-12-06 12:08:44 +00:00
gingerBill fe7e4e88c6 Merge pull request #797 from Tetralux/improve-string-parsing
Improve number parsing procedures
2020-12-06 11:43:04 +00:00
Tetralux c06528d702 Improve number parsing procedures
- No longer returns true for partial string parsing.
  All characters in the string must be part of the number.
  i.e: parse_int("2.345") no longer returns (2, true) - it now returns (2, false)

- Return (0, false) on empty strings.
2020-12-06 11:39:03 +00:00
gingerBill ea60db9f53 Add inline asm expression to odin/parser 2020-12-06 01:14:55 +00:00
gingerBill dff4c6b666 Clean up logic 2020-12-06 00:59:23 +00:00
gingerBill 0e9b357a5d Fix typo 2020-12-06 00:55:31 +00:00
gingerBill 1eb1bffd89 Merge pull request #799 from odin-lang/parser-experiments
Add experimental `-insert-semicolon` functionality to tokenizer and parser
2020-12-06 00:53:52 +00:00
gingerBill e8653ac47e Update main.cpp 2020-12-06 00:51:32 +00:00
gingerBill f0683c9102 Merge branch 'master' into parser-experiments 2020-12-06 00:49:48 +00:00
gingerBill ca4657fd31 Add custom semicolon insertion to odin/tokenizer and odin/parser 2020-12-06 00:47:58 +00:00
gingerBill d94414b0f4 Correct ternary if/when parsing 2020-12-06 00:15:35 +00:00
gingerBill 80fead1de5 Add ast.Selector_Expr_Call 2020-12-06 00:03:45 +00:00
gingerBill 069c6cac3f Add package name and source code location to Type_Info_Named 2020-12-05 20:47:50 +00:00
gingerBill 1a8ea6113a Remove hash field in runtime.Source_Code_Location 2020-12-05 19:52:08 +00:00
gingerBill 76e6624dbb Remove type name generation for procedures in ir.cpp 2020-12-05 19:21:00 +00:00
gingerBill 09a52b7ee6 Fix typos 2020-12-05 16:56:09 +00:00
gingerBill 7b36174c17 Fix formatting 2020-12-05 16:38:26 +00:00
gingerBill a82c902f99 Minor correction to bytes.Buffer's vtable 2020-12-05 15:36:02 +00:00
gingerBill 14ae2e0a8d Add bufio.Writer and bufio.Read_Writer 2020-12-05 15:14:21 +00:00
gingerBill 2ab6cdb98e Add package bufio; add bufio.Reader 2020-12-05 12:03:57 +00:00
gingerBill 21c1abe15a Add fmt formatting for runtime.Source_Code_Location 2020-12-05 12:03:38 +00:00
gingerBill 4c41045133 Fix typo in package io 2020-12-05 12:03:07 +00:00
gingerBill 0ed1300bd6 Make bytes.Reader and strings.Reader have the same interface 2020-12-04 21:25:13 +00:00
gingerBill dd63665b58 Update bytes.Reader 2020-12-04 20:33:14 +00:00
gingerBill 9b22583397 Add bytes.Buffer 2020-12-04 20:18:41 +00:00
gingerBill 63f2480951 Begin work on package bytes ([]byte equivalent of package strings) 2020-12-04 19:16:40 +00:00
gingerBill edbb3e3b32 Fix typos 2020-12-04 18:53:17 +00:00
gingerBill ecf324e213 Remove buffered type 2020-12-04 18:50:30 +00:00
gingerBill 7268c80d64 Add strings.fields and strings.fields_proc 2020-12-04 18:50:05 +00:00
gingerBill fd453be831 Deprecate opaque in favour of #opaque in the core library 2020-12-04 18:49:39 +00:00
gingerBill 2a232f2397 Remove the (reserved) keyword macro 2020-12-04 16:14:11 +00:00
gingerBill c4cb7170ee Deprecate keyword opaque in favour of #opaque 2020-12-04 16:13:05 +00:00
gingerBill b6bbe29c8f Remove const as a (reserved) keyword 2020-12-04 16:04:58 +00:00
gingerBill 5d0db4c63a Clean up fmt usage with io.Writer and strings.Builder 2020-12-04 15:04:54 +00:00
gingerBill 05a3bdad58 Allow nested procedures to access @(static) and @(thread_local) variables 2020-12-04 11:28:14 +00:00
gingerBill 0ef02e6737 Improve packages io and strings; add io.Section_Reader 2020-12-03 15:57:46 +00:00
gingerBill 047586afc6 Change ExactValue layout for complex/quaternion types to minimize its size 2020-12-03 14:21:33 +00:00
gingerBill 5acdcfb57c Keep -vet happy 2020-12-03 13:26:33 +00:00
gingerBill 6e04b1c429 Remove using stream on custom stream types in package io 2020-12-03 13:23:36 +00:00
gingerBill 828fe2ce56 Fix #795 2020-12-03 12:25:45 +00:00
gingerBill 18da0b3418 Integrate package io into core library 2020-12-03 10:45:26 +00:00
gingerBill 334a8c46e8 Fix io typos 2020-12-03 00:27:21 +00:00
gingerBill e0fb081cbd Keep -vet happy 2020-12-02 23:46:41 +00:00
gingerBill bca28e94ec Keep fmt happy 2020-12-02 23:43:28 +00:00
gingerBill 875415daa9 Make os._file_stream_vtable private 2020-12-02 23:40:23 +00:00
gingerBill 0cf3ae93c0 Add os.stream_from_handle; fix io.close 2020-12-02 23:39:33 +00:00
gingerBill c4172e3914 Rename stream field names 2020-12-02 23:17:21 +00:00
gingerBill 8e08ae47fb Add strings.Reader 2020-12-02 23:12:43 +00:00
gingerBill d9343ae997 Add package io 2020-12-02 23:12:14 +00:00
gingerBill 4d30b88927 Fix typos 2020-12-02 23:11:46 +00:00
gingerBill 7389ffba6d Fix -llvm-api const initialization of &T{} 2020-12-02 21:07:15 +00:00
gingerBill a6301ab58a Add type_assertion_check2 2020-12-02 11:50:57 +00:00
gingerBill 996c854071 Disable lb_const_hash for the time being 2020-12-02 11:48:50 +00:00
gingerBill a9c1811027 Fix typo 2020-12-02 10:41:18 +00:00
gingerBill 32b1537aa3 Fix xor for constant booleans 2020-12-02 10:39:59 +00:00
gingerBill 2d0c0a7a83 Fix typo 2020-12-01 15:38:15 +00:00
gingerBill 400816ebf7 Fix Pointer store in LLVM backend 2020-12-01 00:40:54 +00:00
gingerBill ef417017f0 Fix typo 2020-11-30 19:46:41 +00:00
gingerBill 7c1c9d22b4 Fix thread.run_with_poly_data* 2020-11-30 19:44:43 +00:00
gingerBill 5803fcc158 Revert function passes 2020-11-29 20:41:01 +00:00
gingerBill f4f2b8f5ad Add _internal field to context 2020-11-29 18:42:13 +00:00
gingerBill dd4f8e9747 Improve default pass manager for -llvm-api 2020-11-29 18:32:49 +00:00
gingerBill f06f33872c Make 16 simple hasher cases for small types 2020-11-29 18:17:21 +00:00
gingerBill 9e13416312 Simplify simple compare hasher code 2020-11-29 17:58:54 +00:00
gingerBill 5ab7ec5b16 Support any comparable type for map keys 2020-11-29 16:37:19 +00:00
gingerBill b922398a96 Sanity check for map key 2020-11-29 16:13:16 +00:00
gingerBill 57f5976ac1 Support map keys for simple compare types 2020-11-29 16:12:21 +00:00
gingerBill 7fbc081119 Improve const hash 2020-11-29 16:03:21 +00:00
gingerBill 1dfe0cdd1d Simplify hashing approach map 2020-11-29 15:50:29 +00:00
gingerBill 97c66c9c73 Add intrinsics.type_hasher_proc; Make map work with generic hasher procedure 2020-11-29 15:27:53 +00:00
gingerBill 085972bb2c Minor clean up 2020-11-29 14:31:42 +00:00
gingerBill 39bed567b3 Add intrinsics.type_equal_proc; Make map use an internal equal procedure to compare keys 2020-11-29 14:22:42 +00:00
gingerBill 2e0fd34e59 Keep -vet happy 2020-11-29 12:52:30 +00:00
gingerBill 9959a069fc Simplify internals of map[K]V 2020-11-29 12:48:33 +00:00
gingerBill c77098a91c Fix for in enum type 2020-11-26 10:19:45 +00:00
gingerBill 89cceb910a Keep -vet happy 2020-11-26 00:09:38 +00:00
gingerBill f36c5de746 Fix typo 2020-11-26 00:05:15 +00:00
gingerBill ca10248740 Add __dynamic_map_fix_keys 2020-11-25 23:50:25 +00:00
gingerBill aa859c2187 Clean up and fix __dynamic_map_erase 2020-11-25 22:36:34 +00:00
gingerBill 8591655334 Clean up __dynamic_map_rehash behaviour 2020-11-25 21:03:20 +00:00
gingerBill 70f5d7a1c9 Enforce zeroing through memset to ensure padding is zeroed with llvm api 2020-11-25 19:50:48 +00:00
gingerBill 1acd5acd70 Remove unused variable 2020-11-25 16:28:13 +00:00
gingerBill dbaf4d24f6 Update package json for new map layout; Correct llvm-api includes for *nix 2020-11-25 16:19:56 +00:00
gingerBill 9c1c9693f2 Patch up gb.h 2020-11-24 15:18:20 +00:00
gingerBill 776c3f4e90 Prepare for M1 Mac 2020-11-24 12:20:48 +00:00
gingerBill a55568b0c4 Make hash internal key be uintptr rather than u64 to reduce entry size 2020-11-23 19:14:36 +00:00
gingerBill b08ec005b2 Keep -vet happy 2020-11-23 18:33:49 +00:00
gingerBill 91758656f6 Change internal layout of map[K]V 2020-11-23 18:25:01 +00:00
gingerBill 4762d2f2d1 map type internal reorganization 2020-11-23 16:56:31 +00:00
gingerBill 67bc35e882 Fix logic for comparisons of struct #raw_union types 2020-11-23 16:19:26 +00:00
gingerBill 4e370e6ed8 Add equal procedure field to runtime.Type_Info_Struct 2020-11-23 15:53:49 +00:00
gingerBill 0b30c3dc5a Add flags: Type_Info_Flags, to runtime.Type_Info 2020-11-23 14:35:31 +00:00
gingerBill 9e42cb1595 Add comparisons to structs where all fields are comparable == and != 2020-11-23 12:20:04 +00:00
gingerBill 4379917c7d Re-enable zeroext for i1 2020-11-22 21:52:39 +00:00
gingerBill dc8e895d72 Merge branch 'master' of https://github.com/odin-lang/Odin 2020-11-22 21:38:52 +00:00
gingerBill a2461bdf6b Modify llvm_abi.cpp to work correctly for win64 abi of i128 types. (it's a pain) 2020-11-22 21:38:45 +00:00
Mikkel Hjortshøj 5cc9ddd40b Update nightly.yml 2020-11-22 02:49:19 +01:00
gingerBill acef96bde7 Merge pull request #787 from dbechrd/dbechrd-mixed-value-error-typo-fix
Update check_expr.cpp
2020-11-22 01:22:48 +00:00
gingerBill e0c028329b Merge pull request #786 from dbechrd/dbechrd-kernel32-typo
Update kernel32.odin
2020-11-22 01:22:18 +00:00
gingerBill 740411f207 Minor change to unicode/letter.odin 2020-11-21 23:14:07 +00:00
gingerBill fa50c8d7d3 Add ODIN_TEST constant for checking if odin test is being run 2020-11-21 11:40:15 +00:00
Dan Bechard 2d878de84d Update check_expr.cpp
Fixed typo in error message `procedure all` -> `procedure call`
2020-11-20 15:43:51 -08:00
Dan Bechard 58f768cb4f Update kernel32.odin
Fix typo in CreateProcessA/W mappings (https://github.com/odin-lang/Odin/issues/785)
2020-11-20 15:39:08 -08:00
gingerBill 260e28c0af Fix casting of untyped strings 2020-11-20 16:30:34 +00:00
gingerBill a14ea5b2b5 Add support for %s and %q for [dynamic]byte 2020-11-20 16:25:11 +00:00
gingerBill 63e4a2341f Support string literals for fixed arrays of runes; Add %q support for arrays/slices of bytes 2020-11-20 16:24:23 +00:00
gingerBill 6416a6f39c Allow string literals for [N]byte 2020-11-20 16:01:59 +00:00
gingerBill 87956676f5 Add %s support for arrays and slices of bytes 2020-11-20 16:01:16 +00:00
gingerBill 913eac13b1 Reorganize runtime code into separate files 2020-11-19 00:08:23 +00:00
gingerBill 3b7fd4711f Fix text/scanner.scan_string 2020-11-18 23:48:01 +00:00
gingerBill fef5172278 Add %q for runes in fmt 2020-11-18 23:40:54 +00:00
gingerBill a39921aa6a Add package text/scanner 2020-11-18 23:33:16 +00:00
gingerBill 9408eb9580 Update odin doc to support multiple package outputs by passing multiple paths; Replace -all with -short
Example:
odin doc core/path core/path/filepath
2020-11-18 23:22:27 +00:00
gingerBill ef2f204c58 Improve system_exec_command_line_app functionality; Restrict test_* procedures to *_test.odin files 2020-11-17 20:33:15 +00:00
gingerBill 30765475c6 Fix up system_exec_command_line_app exit code code 2020-11-17 18:59:48 +00:00
gingerBill 2bd0fd932a Begin rudimentary work on implementing odin test tooling with *_test.odin files 2020-11-17 18:50:30 +00:00
gingerBill 11577db6a8 Minor fixes 2020-11-17 16:36:33 +00:00
gingerBill ede25a88f8 Ignore +build flags in packages comments with odin doc 2020-11-17 16:01:33 +00:00
gingerBill aa5cb7f6a9 Fix proc type printing 2020-11-17 15:54:22 +00:00
gingerBill d730c5b334 Improve file doc logic 2020-11-17 15:48:18 +00:00
gingerBill 34ca4e92eb Fix parser logic for first comment group line in a file 2020-11-17 15:45:55 +00:00
gingerBill 7442f4bab6 Fix typo 2020-11-17 15:17:36 +00:00
gingerBill 4f303603e7 Add more documentation for odin doc flags 2020-11-17 15:15:26 +00:00
gingerBill a0fbc56317 Improve flags for odin doc 2020-11-17 15:13:38 +00:00
gingerBill d90fc18bef Basic odin doc support 2020-11-17 15:05:16 +00:00
gingerBill 00192bb349 Improve flag handling to check for invalid uses 2020-11-17 13:55:08 +00:00
gingerBill edd9d5e50b Add -show-unused-with-location 2020-11-17 13:02:10 +00:00
gingerBill fea8c63ab3 Fix string_compare 2020-11-17 12:19:28 +00:00
gingerBill 6f71d1f2a9 Add -show-unused (Shows unused package declarations of all imported packages)
Crude output at the moment but better than nothing
2020-11-17 12:10:25 +00:00
gingerBill ca4b0527e8 Minimize memory usage for AST nodes by using Slice<T> rather than Array<T> when the parameter doesn't need to grow 2020-11-16 15:18:25 +00:00
gingerBill adf6c85fd3 Minimize Ast flags usage 2020-11-16 01:42:30 +00:00
gingerBill 939878df50 Improve logic for x->y() shorthand 2020-11-15 23:54:18 +00:00
gingerBill 5fafb17d81 Improve generate_entity_dependency_graph: Calculate edges for graph M - Part 2 2020-11-15 22:46:07 +00:00
gingerBill 3a229397e4 Add next_pow2_isize for PtrSet 2020-11-15 21:22:26 +00:00
gingerBill db0bcbc4f4 Fix calling convention for new LLVM ABI, and changePtrSet index to be u32 rather than isize 2020-11-15 21:19:08 +00:00
gingerBill 0d6f5cec37 Implement custom temporary allocator using ring buffer 2020-11-15 19:36:37 +00:00
gingerBill 17ec3e72a6 Add SCOPED_TEMPORARY_BLOCK for temporary allocations within a block 2020-11-15 18:45:40 +00:00
gingerBill 30d922b059 Make set_procedure_abi_types use the permanent_allocator 2020-11-15 18:11:49 +00:00
gingerBill 3c1c10a178 Begin clarifying allocation patterns by changing from heap_allocator to specific arenas 2020-11-15 18:08:52 +00:00
gingerBill 9f93042163 Improve lb_abi_to_odin_type 2020-11-14 17:09:42 +00:00
gingerBill a64ea342df Improve USE_NEW_LLVM_ABI_SYSTEM's System V ABI 2020-11-12 23:40:13 +00:00
gingerBill fa284f9a5a Fix import paths 2020-11-12 14:20:33 +00:00
gingerBill 78b6948ff2 Reorganize package strings 2020-11-12 14:17:41 +00:00
gingerBill a6c5c203ab Begin work on Sys V for new ABI system 2020-11-12 01:21:09 +00:00
gingerBill 70b8b3c7dd Update LLVM backend to begin work on a generic ABI system 2020-11-12 00:43:49 +00:00
gingerBill 6ee4f51670 Add new math procedure: inf_f32 inf_f64 nan_f32 nan_f64 2020-11-11 11:42:21 +00:00
gingerBill e8da2ef65e Update package unicode 2020-11-10 19:00:38 +00:00
gingerBill 6c0fa24e5d Force dependency for @(export) entities 2020-11-10 19:00:16 +00:00
gingerBill 27d0660546 Merge pull request #779 from F0x1fy/master
Add flag -no-entry-point
2020-11-10 17:47:02 +00:00
F0x1fy 0eba4b46b5 Made sure the entry point is not generated when -no-entry-point is specified. 2020-11-10 10:16:22 -07:00
F0x1fy 6b6f1a5283 For the sake of consistency, fixed the placement of the -no-entry-point flag check. 2020-11-10 09:56:16 -07:00
F0x1fy 3bed5fad77 Removed unnecessary newline from previous commit. 2020-11-10 09:55:00 -07:00
F0x1fy 301e1d2ff3 Added -no-entry-point flag and relevant check. 2020-11-10 09:50:53 -07:00
gingerBill 49e140f4db Add utf8.full_rune 2020-11-10 16:47:56 +00:00
gingerBill 95b94a0f56 Fix sync.Channel code; add thread.run_with_poly_data and run_with_poly_data(2|3|4) procedures 2020-11-10 15:00:40 +00:00
gingerBill ee3b3fe6a3 Fix typeid_of bug 2020-11-10 14:48:57 +00:00
gingerBill eea3a1ecd3 Improve sync.Channel to encode the direction into the type 2020-11-09 13:05:02 +00:00
gingerBill 31f4590f4b Fix default parameters on record types 2020-11-09 13:04:36 +00:00
gingerBill 7909a9f5a5 Remove debug code causing bug 2020-11-09 10:36:09 +00:00
gingerBill c26cb470a2 Fix LLVM-API type cycle for procedures of named procedures 2020-11-09 10:27:27 +00:00
gingerBill 3d5e180dec Merge branch 'master' of https://github.com/odin-lang/Odin 2020-11-08 23:55:53 +00:00
gingerBill 44baf56d62 Fix cyclic check in is_type_polymorphic 2020-11-08 23:54:09 +00:00
Mikkel Hjortshøj 11a4dc8ee3 [CI] Done testing webhook 2020-11-08 01:47:09 +01:00
Mikkel Hjortshøj 19e2f7b7bf [CI] testing webhook 2020-11-08 01:46:28 +01:00
Mikkel Hjortshøj 817db70bde [CI] Remove -march for makefile nightly 2020-11-08 01:23:19 +01:00
Mikkel Hjortshøj ef27528ace [CI] Non-sudo install of b2? 2020-11-08 00:31:25 +01:00
Mikkel Hjortshøj a239fcfa3a Update nightly.yml 2020-11-08 00:21:28 +01:00
Mikkel Hjortshøj a77976533c [CI] Add manual trigger to nightly 2020-11-08 00:08:11 +01:00
Mikkel Hjortshøj 06b2a9a3e7 [CI] Try setting python 3.x in nightly upload step 2020-11-08 00:01:31 +01:00
gingerBill 7a7fddd1df Merge pull request #775 from Tetralux/fix-type-table-unix
Fix -build-mode:shared type table on Unix
2020-11-06 20:51:06 +00:00
Tetralux 140bb3ebfc Fix -build-mode:shared type table on Unix
Fixes #527.

Previously on Linux, '__$startup_runtime', the procedure that
initializes the type table for runtime typeids, was NOT called when
a shared library was loaded by the dynamic loader.

This caused the library to not have its type table populated, which
caused an assert to fail if you tried to use runtime typeids - like
core:fmt, for example.

This commit fixes this by calling ld instead of clang, when building a
shared library, so that we can pass "-init '__$startup_runtime'" to it,
when building a shared library.

Try as I might, I could not get clang to correctly pass through
the linker flags that I wanted.
2020-11-06 20:44:11 +00:00
gingerBill 6fab181c0d Merge pull request #774 from F0x1fy/master
Moved internal_windows to internal_any
2020-11-06 19:21:54 +00:00
gingerBill 17271f74c7 Merge pull request #776 from hectormonacci/patch-3
Fix small typo
2020-11-06 19:21:08 +00:00
Héctor M. Monacci 39044b5bb5 Fix small typo
Fix small typo
2020-11-06 15:46:32 -03:00
F0x1fy 94277fe41c As per GingerBill's request, copied the files over to a linux-specific file instead of renaming to . 2020-11-06 11:11:26 -07:00
F0x1fy b5a619e975 Moved internal_windows to internal_any to allow for manual linking related to issue odin-lang/Odin#527. 2020-11-05 22:14:56 -07:00
gingerBill 7c5247f5fb Add package sys/cpu - implements processor feature detection 2020-11-06 00:39:09 +00:00
gingerBill 9ac6d45bd6 Add more procedures to package slice 2020-11-06 00:38:03 +00:00
gingerBill 4cc84002db Merge pull request #773 from Tetralux/fix-slice-last
Fix slice.last()
2020-11-05 15:18:52 +00:00
Tetralux c1d3c3f926 Fix slice.last()
There was a typo that prevented it from being used.
2020-11-05 00:52:32 +00:00
gingerBill 85b2da2e2a Merge pull request #770 from hectormonacci/patch-2
Fix some typos
2020-11-03 13:53:35 +00:00
Héctor M. Monacci 968aa2f688 Fix some typos
Fix some typos
2020-11-03 10:51:56 -03:00
gingerBill 0784b0ac7f Merge pull request #769 from hectormonacci/patch-1
Fix typo
2020-11-03 10:41:39 +00:00
Héctor M. Monacci 44cfa3484f Fix typo
Fix typo (proprosal -> proposal)
2020-11-03 07:40:17 -03:00
gingerBill 54fbdabc38 Add experimental -insert-semicolon functionality to tokenizer and parser 2020-11-01 15:10:06 +00:00
Mikkel Hjortshøj 81398d21ed Merge pull request #768 from Platin21/fix_macos_linking
Fix macOS linking for user/local/libs and object files
2020-10-31 23:18:35 +01:00
Platin21 8c46582667 Fixes text layout now via GitHub 2020-10-31 22:37:11 +01:00
Platin21 f29f7351e9 Revert "Changed tab width was on 2 now 4"
This reverts commit fc7c0ca3b0.
2020-10-31 22:30:03 +01:00
Platin21 fc7c0ca3b0 Changed tab width was on 2 now 4 2020-10-31 22:29:02 +01:00
Platin21 8158239d76 Sets llvm api back to use the generic sdk link 2020-10-31 22:15:16 +01:00
Platin21 f3108493fb Combines all link-able types to a single if and adds .o for linking 2020-10-31 22:12:50 +01:00
Platin21 7694a89d38 Fix for local/lib linking from brew for macOS 2020-10-31 22:05:40 +01:00
gingerBill 75e8e5e06f Merge pull request #767 from SrMordred/patch-1
Update thread_windows.odin
2020-10-31 10:07:33 +00:00
Patric Dexheimer 59b8748c2c Update thread_windows.odin 2020-10-31 02:40:52 -03:00
Patric Dexheimer 2231f02f61 Update thread_windows.odin
`n` was left over being always zero. But you want `win32.WaitForMultipleObjects` to be the number of threads to wait for which u already have with `j`.
2020-10-31 02:37:26 -03:00
Mikkel Hjortshøj f9eadc3e98 Merge pull request #766 from odin-lang/fix-macos-ci
[CI] Fix macOS CI builds
2020-10-29 20:16:19 +01:00
Mikkel Hjortshøj d6057a7ec6 [CI] Fix macOS CPATH for nightly builds 2020-10-29 20:10:26 +01:00
Mikkel Hjortshøj 532d307a75 [CI] Remove test stage for macOS and go back to macos-latest 2020-10-29 20:06:50 +01:00
Mikkel Hjortshøj 6ae8f5a62d [CI] Try and fix CPATH setting for macOS 2020-10-29 20:03:38 +01:00
Mikkel Hjortshøj a5c6487bc1 [CI] Try not installing our own llvm with brew 2020-10-29 19:28:50 +01:00
Mikkel Hjortshøj 6a808235fe [CI] Change deprecated workflow commands for macOS 2020-10-29 19:25:22 +01:00
Mikkel Hjortshøj 61d7cdfe92 [CI] Fix macOS tag 2020-10-29 13:35:53 +01:00
Mikkel Hjortshøj 45815fd26e [CI] Change macos version to 11 from 10.5 2020-10-29 13:33:12 +01:00
Mikkel Hjortshøj c7a2d6970b [CI] Switch xcode version back to 11.7 2020-10-29 13:23:45 +01:00
gingerBill 6912ef1bc1 Merge branch 'master' of https://github.com/odin-lang/Odin 2020-10-29 09:58:15 +00:00
gingerBill 08fae7360a Fix ir printing for nested procedure literals indirectly through identifiers 2020-10-29 09:58:08 +00:00
gingerBill 6772cb0f3b Merge pull request #758 from zhibog/master
Fixed getting windows version. The former function is no longer worki…
2020-10-26 11:36:09 +00:00
gingerBill ce35de47e4 Merge pull request #761 from Tetralux/patch-7
Reuse container.Queue capacity when calling pop_front()
2020-10-26 11:35:25 +00:00
Tetralux 213864a50c Reuse container.Queue capacity when calling pop_front()
Currently, the Queue will never reuse it's full capacity if you call `pop_front`, even if you empty it before pushing more items.

With this change, if you empty the Queue with `pop_front`, then the offset will be set back to the start of the underlying array when you pop the last item.
Future pushes will then reuse the already-allocated--but now empty--space.
2020-10-26 00:12:31 +00:00
gingerBill 4629754f7c Inline asm expression (-llvm-api)
See https://llvm.org/docs/LangRef.html#inline-assembler-expressions
Example:
```
x := asm(i32) -> i32 {
    "bswap $0",
    "=r,r",
}(123);
```
Allowed directives `#side_effect`, `#align_stack`, `#att`, `#intel` e.g. `asm() #side_effect #intel {...}`
2020-10-24 16:32:37 +01:00
gingerBill 0061e63db0 Merge branch 'master' of https://github.com/odin-lang/Odin 2020-10-24 15:08:46 +01:00
gingerBill 5fa488f163 Add intrinsics.cpu_relax for llvm-api 2020-10-24 15:08:35 +01:00
gingerBill 71ef27fef9 Merge pull request #753 from Tetralux/fix-dirnoexist-error
Fix error message when importing package that does not exist
2020-10-24 09:41:26 +01:00
gingerBill 6ea000b648 Merge pull request #752 from powerc9000/patch-4
Fix a few bugs in path based code.
2020-10-24 01:28:15 +01:00
zhibog 05b58bdbb1 Fixed package name 2020-10-24 00:17:36 +02:00
zhibog 4c4112fbc7 Fixed getting windows version. The former function is no longer working on Windows 10. Also fixed the struct to use correct win32 names 2020-10-24 00:14:01 +02:00
gingerBill feeb342c00 Improve multiple return value copy-elision 2020-10-20 17:08:55 +01:00
gingerBill c4dbc88a12 Improve array programming code generation 2020-10-20 15:38:41 +01:00
gingerBill f4b4cd0433 Specific set the code gen level for -llvm-api 2020-10-20 12:43:15 +01:00
gingerBill 4e5b8f2c61 Add -build-mode:assembly for -llvm-api 2020-10-20 12:35:12 +01:00
gingerBill 0be6ddc7e2 Add -microarch:<string> (e.g. -microarch:native) 2020-10-20 12:25:11 +01:00
gingerBill b1bdd95f19 Begin work on making packages import assembly sort files (.S) 2020-10-16 15:32:09 +01:00
gingerBill 063c0548b0 Add new package strings procedures: trim_prefix, trim_suffix, to_valid_utf8 2020-10-16 15:30:14 +01:00
gingerBill 41f6a684e1 Rename slice.sort_proc to slice.sort_by; add slice.sort_by_key 2020-10-16 14:55:36 +01:00
gingerBill 289908e0b8 Add +build ignore tag 2020-10-15 16:25:57 +01:00
gingerBill 5a28a7e0f5 Add intrinsics.type_field_index_of 2020-10-15 16:12:47 +01:00
gingerBill f8e697dbbb Fix indirect selector bug with using on offset_of 2020-10-15 14:58:57 +01:00
gingerBill 7fc3030c63 Update path/filepath to use new slice.sort; Add sort.reverse_interface 2020-10-14 20:47:13 +01:00
gingerBill edd802e1ff Add package slice; New sort.Interface with default sort.sort 2020-10-14 19:52:05 +01:00
gingerBill de13584be2 Add #no_bounds_check to crc procedures 2020-10-14 16:00:08 +01:00
gingerBill 8806283cf7 Improve location information for log.panic* 2020-10-14 15:59:45 +01:00
gingerBill ec5934705c Enforce do bodies to be on the same line as the control statement's condition or token 2020-10-14 15:59:19 +01:00
gingerBill fa33476438 Improve default temp allocator; Fix filepath.abs behaviour on Windows 2020-10-13 14:40:13 +01:00
Tetralux dfac45942c Fix error message when importing package that does not exist
Previously on Linux, if a file in your program tried to import a
package that did not actually exist, read_directory() assumed that
the errno after calling opendir() was ENOTDIR.

This was incorrect.

Instead, we now switch on errno and check for ENOENT, which it is
if the directory does not exist.
2020-10-09 05:56:12 +01:00
gingerBill 1b4bccbc94 Add append_nothing 2020-10-08 12:23:44 +01:00
Clay Murray 062ae56f25 Fix a few bugs in path based code.
Trying to use path.dir and path.rel I found these two issues with the implementation.
2020-10-07 16:32:00 -06:00
gingerBill 6eeb12a986 Improve default temp_allocator; make nil loggers do nothing; improve mem.Scratch_Allocator behaviour 2020-10-02 16:06:55 +01:00
gingerBill a65553293f Add mem.Small_Allocator 2020-10-02 11:09:36 +01:00
gingerBill 8f28312705 Fix pop behaviour, and improve assert messages by using #caller_location 2020-10-02 11:04:17 +01:00
gingerBill 3a4f0d85a6 Fix container.Array and container.Ring 2020-10-01 17:04:56 +01:00
gingerBill c604e359c7 Move flush to within the sbprint* procedures 2020-10-01 12:09:38 +01:00
gingerBill 66c648e5e0 Remove unused variable 2020-10-01 12:05:42 +01:00
gingerBill e83af93394 Allow flushing with strings.Builder; Make fmt.fprint* procedures use a custom flush procedure 2020-10-01 12:03:20 +01:00
gingerBill dd4c02a1b9 Fix multiple declared foreign procedures 2020-10-01 12:02:07 +01:00
gingerBill bc2151f529 Add new os_specific_*.odin files 2020-10-01 10:59:47 +01:00
gingerBill 252a864308 Reimplement the Windows OS dependencies in package runtime 2020-10-01 10:57:02 +01:00
gingerBill 9513cf1ac6 Fix os.stat code; make fmt.panicf diverging 2020-09-30 22:04:39 +01:00
gingerBill c35d533ce5 Replace the *_remove_range with procedures with remove_range 2020-09-30 12:38:40 +01:00
gingerBill 464e733b88 Merge branch 'master' of https://github.com/odin-lang/Odin 2020-09-29 11:11:42 +01:00
gingerBill 519dcc2b76 Add os.read_at and for Windows; fix mem.clone_slice; fix current directory locking code 2020-09-29 11:11:28 +01:00
gingerBill 1818ceb4f2 Merge pull request #747 from Platin21/master
Fixes for MacOS 11 / Big Sur
2020-09-28 12:48:50 +01:00
gingerBill e95addb1f4 Revert user_data addition 2020-09-28 12:40:48 +01:00
gingerBill d343e47a86 Add user_data parameter to filepath.walk and filepath.Walk_Proc 2020-09-28 12:39:34 +01:00
gingerBill 1d21740afb Add filepath.walk 2020-09-28 12:29:14 +01:00
gingerBill 9ae3879956 Add os.stat, os.lstat, os.fstat, filepath.walk 2020-09-28 12:28:02 +01:00
Platin21 6b83159b06 Switched to else instead of not equal 2020-09-27 22:17:35 +03:00
Platin21 d72a01a714 Merge branch 'master' of https://github.com/odin-lang/Odin 2020-09-27 22:13:12 +03:00
Platin21 2ed6785b4a Adds -syslibroot to linker command needed for System.framework 2020-09-27 22:11:23 +03:00
Platin21 f7e40b8572 Adds when statement for framework include 2020-09-27 22:08:33 +03:00
Platin21 a71cbd4087 Changed foreign imports to now use the System Framework 2020-09-27 21:57:27 +03:00
gingerBill 2ebb94fa72 Allow os.read_dir to use a different allocator 2020-09-27 00:31:29 +01:00
gingerBill 96a0125599 Fix typo in reflect.set_union_variant_type_info 2020-09-27 00:30:54 +01:00
gingerBill 626d0736f4 Add more to package reflect (as_string, as_pointer, as_raw_data, relative_pointer_to_absolute) 2020-09-27 00:15:16 +01:00
gingerBill e26f63b448 Update package reflect 2020-09-26 23:51:42 +01:00
gingerBill b9076b0d5b Add package unicode/utf8/utf8string for efficient utf8 codepoint indexing to strings 2020-09-26 23:16:18 +01:00
gingerBill c43b8ef387 Merge branch 'master' of https://github.com/odin-lang/Odin 2020-09-26 23:12:20 +01:00
gingerBill b9f511954a Make runtime.slice_handle_error explicitly diverging 2020-09-26 23:12:04 +01:00
gingerBill 4936713a5e Merge pull request #740 from bttelle/issue-697
Fix issue #697
2020-09-26 21:09:40 +01:00
gingerBill 0bd38ba1f6 Add some comments to explain what the differences between packages path and path/filepath 2020-09-26 20:17:54 +01:00
gingerBill 49eaeccd84 Remove OS specific stuff from package path 2020-09-26 20:02:24 +01:00
gingerBill 840af6825a Update packages os, path, and filepath 2020-09-26 16:02:03 +01:00
gingerBill 3ccaf47566 Remove unused imports 2020-09-25 20:23:29 +01:00
gingerBill 8cc5cd1494 Add package path/filepath; Add os.stat for windows (TODO: unix) 2020-09-25 20:20:53 +01:00
gingerBill 6b634d5e46 Fix fmt with -vet 2020-09-25 15:54:23 +01:00
gingerBill 903ba1c5d8 Add built-in custom formatters for time.Time and time.Duration 2020-09-25 15:47:39 +01:00
gingerBill b42c7f9161 Update package path and fix bugs; add path.match which uses shell pattern matching 2020-09-25 11:43:51 +01:00
gingerBill 654b24e514 Add -disallow-do 2020-09-23 17:17:32 +01:00
gingerBill fc4fdd588e Remove usage of do in core library 2020-09-23 17:17:14 +01:00
gingerBill 4844dd4d96 Add check to block statements to see if they only contain one statement, a value declaration, and err. 2020-09-23 11:16:33 +01:00
gingerBill 609af3a651 Fix and clean up default procedure parameter code for non-constant types 2020-09-22 12:06:05 +01:00
gingerBill 20e4548999 Make llvm-c header use local includes 2020-09-18 13:18:28 +01:00
Joseph Battelle 7490ac2cfd Fix issue #697
Add `is_type_typeid` check to `does_field_type_allow_using`
2020-09-16 21:18:25 -07:00
gingerBill 10afc58d7d Minor style change in parser.odin 2020-09-16 22:28:32 +01:00
gingerBill f5b18482f6 Merge pull request #739 from bttelle/issue-486
Fix issue #486
2020-09-16 22:23:41 +01:00
gingerBill 97d7d8301a Fix ast.Branch_Node parsing in package odin/parser 2020-09-16 22:19:57 +01:00
Joseph Battelle 3a3d415295 Fix issue #486
Use `check_is_assignable_to_using_subtype` in
`is_polymorphic_type_assignable`.

The polymorphic procedure in #486 can now also be written without the
cast:
```odin
print_entity :: proc(e : ^$E, p : proc(^$T) = print_base) { p(e); }
```
2020-09-16 12:32:57 -07:00
gingerBill 33003d1bc1 Update README.md 2020-09-16 20:15:28 +01:00
gingerBill 59d9821bd9 Add intrinsics.type_has_field 2020-09-16 20:08:45 +01:00
gingerBill f530c80216 Fix -debug bug with internal startup procedures 2020-09-16 15:16:29 +01:00
gingerBill 94b27aa64e Merge pull request #737 from nakst/master
update essence platform
2020-09-15 22:28:15 +01:00
nakst c92860e142 update essence platform 2020-09-15 23:22:19 +01:00
gingerBill 4cf240ca05 Fix odin/parser issues 2020-09-15 22:14:23 +01:00
gingerBill ebad8e8990 Change how ir.cpp calls the startup type info procedure 2020-09-15 19:40:37 +01:00
gingerBill 2475c69f00 Remove runtime.os_stdout, since it is not required by the runtime 2020-09-15 14:16:44 +01:00
gingerBill c9dcb7242f Merge branch 'master' of https://github.com/odin-lang/Odin 2020-09-15 12:46:12 +01:00
gingerBill 9d976b04bc Fix +build tag logic 2020-09-15 12:45:44 +01:00
gingerBill 6f1e774a42 Move runtime os specific freestanding stuff to a separate file 2020-09-15 12:36:37 +01:00
gingerBill b94dde2817 Force used of imports in reflect/map.odin 2020-09-15 12:31:21 +01:00
gingerBill 92cd50d3f0 Add TargetOS_freestanding for future use 2020-09-15 12:29:32 +01:00
gingerBill 1ef1407f02 Update ODIN_VERSION 2020-09-15 12:28:15 +01:00
gingerBill edbad0709e Add -default-to-nil-allocator flag (sets ODIN_DEFAULT_TO_NIL_ALLOCATOR) 2020-09-15 12:27:53 +01:00
gingerBill bfc7d74967 Add relfect.map_entry_info_slice 2020-09-15 12:23:49 +01:00
gingerBill 9d91c46cb4 Move all os specific stuff for the runtime to one file 2020-09-15 12:19:56 +01:00
gingerBill 17b3c2ed4c Merge pull request #736 from jockus/json-parse-number-option
Added option to parse number as integer, disabled by default
2020-09-15 12:15:57 +01:00
jockus 8d637f5139 Added parse_integers optional to validation 2020-09-15 12:03:40 +01:00
gingerBill f48a873954 Reorganize package runtime
Separates out the OS specific stuff into different files
2020-09-15 11:52:19 +01:00
gingerBill 4930a9c1a4 Add mem.clone_slice 2020-09-15 11:51:38 +01:00
jockus 195dbd658d Added option to parse number as integer, disabled by default 2020-09-15 11:39:34 +01:00
gingerBill 0cd681e6b7 Expose runtime._startup_runtime to allow for freestanding targets in the future 2020-09-15 10:51:51 +01:00
gingerBill 3211e60018 Merge pull request #734 from krixano/FreeBSD
FreeBSD Support
2020-09-15 10:12:42 +01:00
gingerBill 775bd66382 Merge pull request #735 from bttelle/issue-723
Fix issue #723
2020-09-15 10:05:05 +01:00
Christian Seibold a13eed9894 Cleanup, check sched_param and SCHED_* constants in pthread_freebsd.odin 2020-09-15 01:34:01 -05:00
Joseph Battelle e9c598a426 Fix issue #723
Typo in `check_stmt_internal` for case Ast_UsingStmt; first element
was used for all elements of the enumerated list.
2020-09-14 16:41:53 -07:00
Christian Seibold 65787381c1 Change sizes of pthread types for freebsd 2020-09-14 16:48:55 -05:00
Christian Seibold dd7b29e681 Finish add all errors 2020-09-14 16:18:36 -05:00
Christian Seibold 577be4a8ae Get Odin compiling and produced exe's running on FreeBSD 2020-09-14 15:22:35 -05:00
Christian Seibold ac126a8cd7 Add FreeBSD targets, get gb.h working with FreeBSD, fix odin_root_directory function for FreeBSD and a few other operating systems not yet added 2020-09-14 11:28:41 -05:00
gingerBill d53725fe14 Merge pull request #733 from bttelle/issue-515
Fix issue #515
2020-09-14 14:07:02 +01:00
gingerBill b8bebf4511 Fix typo in Small_Array procedure 2020-09-14 10:10:35 +01:00
Joseph Battelle 2f32b8fb3d Fix issue #515
Modify `check_type_specialization_to` to require exact values
to be equal when called with constant basic types. This also
now allows procedure group members to differ only by constant
value specializations.  See the further example in the issue.
2020-09-13 22:58:05 -07:00
gingerBill 1fd1203d8b Improve error message for multi-valued global declarations not be allowed 2020-09-12 16:04:02 +01:00
gingerBill ccb7c3513b Fix check_arity_match bug 2020-09-12 15:50:42 +01:00
gingerBill bf215377de Add edge case check for "" string 2020-09-11 16:20:46 +01:00
gingerBill d317d3d8b3 Simplify "" string code 2020-09-11 16:18:24 +01:00
gingerBill 77829af9de Fix delete("") on -llvm-api; Fix linalg stuff 2020-09-11 13:46:52 +01:00
gingerBill 97846d8390 Clean up quaternion_from_matrix(3|4) code 2020-09-10 21:19:59 +01:00
gingerBill 079b887313 Make procedure names consistent 2020-09-10 17:32:18 +01:00
gingerBill 6aa708a455 Fix Odin bug with "none" procedure calling conventions in the runtime 2020-09-10 16:36:33 +01:00
gingerBill 8f38b06c60 Add sanity conversion for rand.norm_float64 2020-09-10 15:42:07 +01:00
gingerBill 993fc577b2 Clarify euler angle procedures better 2020-09-10 15:33:50 +01:00
gingerBill 824491f410 Clean up Euler Angle code for math/linalg 2020-09-10 15:23:08 +01:00
gingerBill c1149dbdee Update math and math/linalg; add "pure_none" calling convention 2020-09-10 15:00:19 +01:00
gingerBill 7e625f6ee7 Add extra check for checking uniqueness of package names 2020-09-10 11:13:09 +01:00
gingerBill 2dfa3a5df7 Fix typo 2020-09-07 14:40:36 +01:00
gingerBill 1064622ff7 Improve math/rand functionality 2020-09-07 14:38:01 +01:00
gingerBill f5b8609160 Remove debug print 2020-09-07 12:15:54 +01:00
gingerBill 7f48cf8405 [REFLECTION BREAKING] Modify the internals of the map type to increase performance 2020-09-07 11:41:42 +01:00
gingerBill 7e08bccc9a Merge branch 'master' of https://github.com/odin-lang/Odin 2020-09-04 11:18:58 +01:00
gingerBill 9fd9130891 Add new core procedures: ordered_remove_range; unordered_remove_range; insert_at 2020-09-04 11:18:46 +01:00
gingerBill 9f1f194d18 Merge pull request #727 from JoshuaManton/master-fork
Add allocator parameter to os.read_entire_file()
2020-09-03 08:37:22 +01:00
Joshua Mark Manton 0fe47a2f1b Add allocator parameter to os.read_entire_file() 2020-09-02 18:42:12 -07:00
282 changed files with 57457 additions and 16758 deletions
+11 -6
View File
@@ -7,26 +7,31 @@ jobs:
steps:
- uses: actions/checkout@v1
- name: Download LLVM
run: sudo apt-get install llvm
run: sudo apt-get install llvm-11 clang-11 llvm
- name: build odin
run: make release
- name: Odin run -llvm-api
run: ./odin run examples/demo/demo.odin -llvm-api
- name: Odin run
run: ./odin run examples/demo/demo.odin
- name: Odin check
run: ./odin check examples/demo/demo.odin -vet
build_macOS:
runs-on: macOS-latest
runs-on: macos-latest
steps:
- uses: actions/checkout@v1
- name: Download LLVM and setup PATH
run: |
brew install llvm
echo ::add-path::/usr/local/opt/llvm/bin
echo ::set-env name=CPATH::`xcrun --show-sdk-path`/usr/include
brew install llvm@11
echo "/usr/local/opt/llvm/bin" >> $GITHUB_PATH
TMP_PATH=$(xcrun --show-sdk-path)/user/include
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
- name: build odin
run: make release
- name: Odin run
run: ./odin run examples/demo/demo.odin
run: |
./odin run examples/demo/demo.odin
./odin run examples/demo/demo.odin -llvm-api
- name: Odin check
run: ./odin check examples/demo/demo.odin -vet
build_windows:
+24 -10
View File
@@ -1,6 +1,7 @@
name: Nightly
on:
workflow_dispatch:
schedule:
- cron: 0 20 * * *
@@ -48,9 +49,11 @@ jobs:
steps:
- uses: actions/checkout@v1
- name: (Linux) Download LLVM
run: sudo apt-get install llvm
run: sudo apt-get install llvm-11 clang-11 llvm
- name: build odin
run: make release
run: make nightly
- name: Odin run -llvm-api
run: ./odin run examples/demo/demo.odin -llvm-api
- name: Odin run
run: ./odin run examples/demo/demo.odin
- name: Copy artifacts
@@ -72,10 +75,13 @@ jobs:
- name: Download LLVM and setup PATH
run: |
brew install llvm
echo ::add-path::/usr/local/opt/llvm/bin
echo ::set-env name=CPATH::`xcrun --show-sdk-path`/usr/include
echo "/usr/local/opt/llvm/bin" >> $GITHUB_PATH
TMP_PATH=$(xcrun --show-sdk-path)/user/include
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
- name: build odin
run: make release
run: make nightly
- name: Odin run -llvm-api
run: ./odin run examples/demo/demo.odin -llvm-api
- name: Odin run
run: ./odin run examples/demo/demo.odin
- name: Copy artifacts
@@ -95,21 +101,29 @@ jobs:
needs: [build_windows, build_macos, build_ubuntu]
steps:
- uses: actions/checkout@v1
- uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install B2 CLI
shell: bash
run: sudo pip install --upgrade b2
run: |
python -m pip install --upgrade pip
pip install --upgrade b2
- name: Display Python version
run: python -c "import sys; print(sys.version)"
- name: Download Windows artifacts
uses: actions/download-artifact@v1
with:
name: windows_artifacts
- name: Download Ubuntu artifacts
uses: actions/download-artifact@v1
with:
name: ubuntu_artifacts
- name: Download macOS artifacts
uses: actions/download-artifact@v1
with:
+2
View File
@@ -21,6 +21,8 @@ bld/
![Cc]ore/[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Visual Studio Code options directory
.vscode/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
demo
+8 -2
View File
@@ -1,13 +1,19 @@
GIT_SHA=$(shell git rev-parse --short HEAD)
DISABLED_WARNINGS=-Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined
LDFLAGS=-pthread -ldl -lm -lstdc++
CFLAGS=-std=c++11 -DGIT_SHA=\"$(GIT_SHA)\"
CFLAGS=-std=c++14 -DGIT_SHA=\"$(GIT_SHA)\"
CC=clang
OS=$(shell uname)
ifeq ($(OS), Darwin)
LDFLAGS:=$(LDFLAGS) -liconv
CFLAGS:=$(CFLAGS) $(shell llvm-config --cxxflags --ldflags) -DLLVM_BACKEND_SUPPORT -DUSE_NEW_LLVM_ABI_SYSTEM
LDFLAGS:=$(LDFLAGS) -lLLVM-C
endif
ifeq ($(OS), Linux)
CFLAGS:=$(CFLAGS) $(shell llvm-config-11 --cxxflags --ldflags) -DLLVM_BACKEND_SUPPORT -DUSE_NEW_LLVM_ABI_SYSTEM
LDFLAGS:=$(LDFLAGS) $(shell llvm-config-11 --libs core native --system-libs)
endif
all: debug demo
@@ -22,7 +28,7 @@ release:
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin
nightly:
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 -march=native $(LDFLAGS) -o odin
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 $(LDFLAGS) -o odin
+1 -1
View File
@@ -30,7 +30,7 @@ The proposal process is the process for reviewing a proposal and reaching a deci
* Accept proposal
* Decline proposal
After the proposal is accepted or declined, implementation of the proprosal proceeds in the same way as any other contribution to the project.
After the proposal is accepted or declined, implementation of the proposal proceeds in the same way as any other contribution to the project.
## Design Documents
+14 -7
View File
@@ -60,7 +60,7 @@ main :: proc() {
#### [Getting Started](https://odin-lang.org/docs/install)
Instructions for downloading and install the Odin compiler and libraries.
Instructions for downloading and installing the Odin compiler and libraries.
### Learning Odin
@@ -94,7 +94,7 @@ The official blog of the Odin programming language, featuring announcements, new
## Setup
Odin only supports x86-64 at the moment (64-bit), relies on LLVM for code generation and an external linker.
Odin only supports x86-64/amd64 at the moment (64-bit), relies on LLVM for code generation and an external linker.
In addition, the following platform-specific steps are necessary:
@@ -126,27 +126,34 @@ Please read the [Getting Started Guide](https://github.com/odin-lang/Odin/wiki#g
Please read the [Getting Started Guide](https://github.com/odin-lang/Odin/wiki#getting-started-with-odin).
- Windows
* x86-64
* x86-64/amd64
* MSVC 2010 installed (C++11 support)
* [LLVM binaries](https://github.com/odin-lang/Odin/releases/tag/llvm-windows) for `opt.exe`, `llc.exe`, and `lld-link.exe`
* Requires MSVC's link.exe as the linker
* run `vcvarsall.bat` to setup the path
- MacOS
* x86-64
* x86-64/amd64
* LLVM explicitly installed (`brew install llvm`)
* XCode installed (for the linker)
- GNU/Linux
* x86-64
* x86-64/amd64
* Build tools (ld)
* LLVM installed
* Clang installed (temporary - this is Calling the linker for now)
- FreeBSD
* x86-64/amd64
* Build tools (ld)
* LLVM installed
* Clang installed (temporary - this is Calling the linker for now)
Other platforms may be supported but are experimental for the time being.
## Warnings
* This is still highly in development and the language's design is quite volatile.
* Syntax is not fixed.
* The Odin compiler is still in development.
## Demonstrations:
* First Talk & Demo
Executable
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env bash
release_mode=$1
warnings_to_disable="-std=c++11 -Wno-switch"
libraries="-pthread -ldl -lm -lstdc++ -lz -lcurses -lxml2"
other_args="-DLLVM_BACKEND_SUPPORT"
compiler="clang"
if [ -z "$release_mode" ]; then release_mode="0"; fi
if [ "$release_mode" -eq "0" ]; then
other_args="${other_args} -g"
fi
if [ "$release_mode" -eq "1" ]; then
other_args="${other_args} -O3 -march=native"
fi
if [[ "$(uname)" == "Darwin" ]]; then
# Set compiler to clang on MacOS
# MacOS provides a symlink to clang called gcc, but it's nice to be explicit here.
compiler="clang"
llvm_config_flags="--cxxflags --ldflags"
# llvm_config_flags="${llvm_config_flags} --link-static"
llvm_config="llvm-config ${llvm_config_flags}"
other_args="${other_args} -liconv"
other_args="${other_args} `${llvm_config}` -lLLVM-C"
elif [[ "$(uname)" == "FreeBSD" ]]; then
compiler="clang"
fi
${compiler} src/main.cpp ${warnings_to_disable} ${libraries} ${other_args} -o odin
# && ./odin run examples/demo/demo.odin -llvm-api
+1 -1
View File
@@ -20,7 +20,7 @@ if "%2" == "1" (
)
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
set compiler_defines= -DLLVM_BACKEND_SUPPORT
set compiler_defines= -DLLVM_BACKEND_SUPPORT -DUSE_NEW_LLVM_ABI_SYSTEM
for /f %%i in ('git rev-parse --short HEAD') do set GIT_SHA=%%i
if %ERRORLEVEL% equ 0 set compiler_defines=%compiler_defines% -DGIT_SHA=\"%GIT_SHA%\"
+2
View File
@@ -23,6 +23,8 @@ if [[ "$(uname)" == "Darwin" ]]; then
compiler="clang"
other_args="${other_args} -liconv"
elif [[ "$(uname)" == "FreeBSD" ]]; then
compiler="clang"
fi
${compiler} src/main.cpp ${warnings_to_disable} ${libraries} ${other_args} -o odin && ./odin run examples/demo/demo.odin
+68
View File
@@ -0,0 +1,68 @@
package bufio
import "core:io"
// Read_Writer stores pointers to a Reader and a Writer
Read_Writer :: struct {
r: ^Reader,
w: ^Writer,
}
read_writer_init :: proc(rw: ^Read_Writer, r: ^Reader, w: ^Writer) {
rw.r, rw.w = r, w;
}
read_writer_to_stream :: proc(rw: ^Read_Writer) -> (s: io.Stream) {
s.stream_data = rw;
s.stream_vtable = _read_writer_vtable;
return;
}
@(private)
_read_writer_vtable := &io.Stream_VTable{
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
b := (^Read_Writer)(s.stream_data).r;
return reader_read(b, p);
},
impl_read_byte = proc(s: io.Stream) -> (c: byte, err: io.Error) {
b := (^Read_Writer)(s.stream_data).r;
return reader_read_byte(b);
},
impl_unread_byte = proc(s: io.Stream) -> io.Error {
b := (^Read_Writer)(s.stream_data).r;
return reader_unread_byte(b);
},
impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
b := (^Read_Writer)(s.stream_data).r;
return reader_read_rune(b);
},
impl_unread_rune = proc(s: io.Stream) -> io.Error {
b := (^Read_Writer)(s.stream_data).r;
return reader_unread_rune(b);
},
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
b := (^Read_Writer)(s.stream_data).r;
return reader_write_to(b, w);
},
impl_flush = proc(s: io.Stream) -> io.Error {
b := (^Read_Writer)(s.stream_data).w;
return writer_flush(b);
},
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
b := (^Read_Writer)(s.stream_data).w;
return writer_write(b, p);
},
impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
b := (^Read_Writer)(s.stream_data).w;
return writer_write_byte(b, c);
},
impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
b := (^Read_Writer)(s.stream_data).w;
return writer_write_rune(b, r);
},
impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
b := (^Read_Writer)(s.stream_data).w;
return writer_read_from(b, r);
},
};
+474
View File
@@ -0,0 +1,474 @@
package bufio
import "core:io"
import "core:mem"
import "core:unicode/utf8"
import "core:bytes"
// Reader is a buffered wrapper for an io.Reader
Reader :: struct {
buf: []byte,
buf_allocator: mem.Allocator,
rd: io.Reader, // reader
r, w: int, // read and write positions for buf
err: io.Error,
last_byte: int, // last byte read, invalid is -1
last_rune_size: int, // size of last rune read, invalid is -1
}
DEFAULT_BUF_SIZE :: 4096;
@(private)
MIN_READ_BUFFER_SIZE :: 16;
@(private)
MAX_CONSECUTIVE_EMPTY_READS :: 128;
reader_init :: proc(b: ^Reader, rd: io.Reader, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) {
size := size;
size = max(size, MIN_READ_BUFFER_SIZE);
reader_reset(b, rd);
b.buf_allocator = allocator;
b.buf = make([]byte, size, allocator);
}
reader_init_with_buf :: proc(b: ^Reader, rd: io.Reader, buf: []byte) {
reader_reset(b, rd);
b.buf_allocator = {};
b.buf = buf;
}
// reader_destroy destroys the underlying buffer with its associated allocator IFF that allocator has been set
reader_destroy :: proc(b: ^Reader) {
delete(b.buf, b.buf_allocator);
b^ = {};
}
reader_size :: proc(b: ^Reader) -> int {
return len(b.buf);
}
reader_reset :: proc(b: ^Reader, r: io.Reader) {
b.rd = r;
b.r, b.w = 0, 0;
b.err = nil;
b.last_byte = -1;
b.last_rune_size = -1;
}
@(private)
_reader_read_new_chunk :: proc(b: ^Reader) -> io.Error {
if b.r > 0 {
copy(b.buf, b.buf[b.r:b.w]);
b.w -= b.r;
b.r = 0;
}
if b.w >= len(b.buf) {
return .Buffer_Full;
}
// read new data, and try a limited number of times
for i := MAX_CONSECUTIVE_EMPTY_READS; i > 0; i -= 1 {
n, err := io.read(b.rd, b.buf[b.w:]);
if n < 0 {
return .Negative_Read;
}
b.w += n;
if err != nil {
b.err = err;
return nil;
}
if n > 0 {
return nil;
}
}
b.err = .No_Progress;
return nil;
}
@(private)
_reader_consume_err :: proc(b: ^Reader) -> io.Error {
err := b.err;
b.err = nil;
return err;
}
// reader_peek returns the next n bytes without advancing the reader
// The bytes stop being valid on the next read call
// If reader_peek returns fewer than n bytes, it also return an error
// explaining why the read is short
// The error will be .Buffer_Full if n is larger than the internal buffer size
reader_peek :: proc(b: ^Reader, n: int) -> (data: []byte, err: io.Error) {
n := n;
if n < 0 {
return nil, .Negative_Count;
}
b.last_byte = -1;
b.last_rune_size = -1;
for b.w-b.r < n && b.w-b.r < len(b.buf) && b.err == nil {
if fill_err := _reader_read_new_chunk(b); fill_err != nil {
return nil, fill_err;
}
}
if n > len(b.buf) {
return b.buf[b.r : b.w], .Buffer_Full;
}
if available := b.w - b.r; available < n {
n = available;
err = _reader_consume_err(b);
if err == nil {
err = .Buffer_Full;
}
}
return b.buf[b.r : b.r+n], err;
}
// reader_buffered returns the number of bytes that can be read from the current buffer
reader_buffered :: proc(b: ^Reader) -> int {
return b.w - b.r;
}
// reader_discard skips the next n bytes, and returns the number of bytes that were discarded
reader_discard :: proc(b: ^Reader, n: int) -> (discarded: int, err: io.Error) {
if n < 0 {
return 0, .Negative_Count;
}
if n == 0 {
return;
}
remaining := n;
for {
skip := reader_buffered(b);
if skip == 0 {
if fill_err := _reader_read_new_chunk(b); fill_err != nil {
return 0, fill_err;
}
skip = reader_buffered(b);
}
skip = min(skip, remaining);
b.r += skip;
remaining -= skip;
if remaining == 0 {
return n, nil;
}
if b.err != nil {
return n - remaining, _reader_consume_err(b);
}
}
return;
}
// reader_read reads data into p
// The bytes are taken from at most one read on the underlying Reader, which means n may be less than len(p)
reader_read :: proc(b: ^Reader, p: []byte) -> (n: int, err: io.Error) {
n = len(p);
if n == 0 {
if reader_buffered(b) > 0 {
return 0, nil;
}
return 0, _reader_consume_err(b);
}
if b.r == b.w {
if b.err != nil {
return 0, _reader_consume_err(b);
}
if len(p) >= len(b.buf) {
n, b.err = io.read(b.rd, p);
if n < 0 {
return 0, .Negative_Read;
}
if n > 0 {
b.last_byte = int(p[n-1]);
b.last_rune_size = -1;
}
return n, _reader_consume_err(b);
}
b.r, b.w = 0, 0;
n, b.err = io.read(b.rd, b.buf);
if n < 0 {
return 0, .Negative_Read;
}
if n == 0 {
return 0, _reader_consume_err(b);
}
b.w += n;
}
n = copy(p, b.buf[b.r:b.w]);
b.r += n;
b.last_byte = int(b.buf[b.r-1]);
b.last_rune_size = -1;
return n, nil;
}
// reader_read_byte reads and returns a single byte
// If no byte is available, it return an error
reader_read_byte :: proc(b: ^Reader) -> (byte, io.Error) {
b.last_rune_size = -1;
for b.r == b.w {
if b.err != nil {
return 0, _reader_consume_err(b);
}
if err := _reader_read_new_chunk(b); err != nil {
return 0, err;
}
}
c := b.buf[b.r];
b.r += 1;
b.last_byte = int(c);
return c, nil;
}
// reader_unread_byte unreads the last byte. Only the most recently read byte can be unread
reader_unread_byte :: proc(b: ^Reader) -> io.Error {
if b.last_byte < 0 || b.r == 0 && b.w > 0 {
return .Invalid_Unread;
}
if b.r > 0 {
b.r -= 1;
} else {
// b.r == 0 && b.w == 0
b.w = 1;
}
b.buf[b.r] = byte(b.last_byte);
b.last_byte = -1;
b.last_rune_size = -1;
return nil;
}
// reader_read_rune reads a single UTF-8 encoded unicode character
// and returns the rune and its size in bytes
// If the encoded rune is invalid, it consumes one byte and returns utf8.RUNE_ERROR (U+FFFD) with a size of 1
reader_read_rune :: proc(b: ^Reader) -> (r: rune, size: int, err: io.Error) {
for b.r+utf8.UTF_MAX > b.w &&
!utf8.full_rune(b.buf[b.r:b.w]) &&
b.err == nil &&
b.w-b.w < len(b.buf) {
if err = _reader_read_new_chunk(b); err != nil {
return;
}
}
b.last_rune_size = -1;
if b.r == b.w {
err = _reader_consume_err(b);
return;
}
r, size = rune(b.buf[b.r]), 1;
if r >= utf8.RUNE_SELF {
r, size = utf8.decode_rune(b.buf[b.r : b.w]);
}
b.r += size;
b.last_byte = int(b.buf[b.r-1]);
b.last_rune_size = size;
return;
}
// reader_unread_rune unreads the last rune. Only the most recently read rune can be unread
reader_unread_rune :: proc(b: ^Reader) -> io.Error {
if b.last_rune_size < 0 || b.r < b.last_rune_size {
return .Invalid_Unread;
}
b.r -= b.last_rune_size;
b.last_byte = -1;
b.last_rune_size = -1;
return nil;
}
reader_write_to :: proc(b: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
write_buf :: proc(b: ^Reader, w: io.Writer) -> (i64, io.Error) {
n, err := io.write(w, b.buf[b.r:b.w]);
if n < 0 {
return 0, .Negative_Write;
}
b.r += n;
return i64(n), err;
}
n, err = write_buf(b, w);
if err != nil {
return;
}
m: i64;
if nr, ok := io.to_writer_to(b.rd); ok {
m, err = io.write_to(nr, w);
n += m;
return n, err;
}
if nw, ok := io.to_reader_from(w); ok {
m, err = io.read_from(nw, b.rd);
n += m;
return n, err;
}
if b.w-b.r < len(b.buf) {
if err = _reader_read_new_chunk(b); err != nil {
return;
}
}
for b.r < b.w {
m, err = write_buf(b, w);
n += m;
if err != nil {
return;
}
if err = _reader_read_new_chunk(b); err != nil {
return;
}
}
if b.err == .EOF {
b.err = nil;
}
err = _reader_consume_err(b);
return;
}
// reader_to_stream converts a Reader into an io.Stream
reader_to_stream :: proc(b: ^Reader) -> (s: io.Stream) {
s.stream_data = b;
s.stream_vtable = _reader_vtable;
return;
}
@(private)
_reader_vtable := &io.Stream_VTable{
impl_destroy = proc(s: io.Stream) -> io.Error {
b := (^Reader)(s.stream_data);
reader_destroy(b);
return nil;
},
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
b := (^Reader)(s.stream_data);
return reader_read(b, p);
},
impl_read_byte = proc(s: io.Stream) -> (c: byte, err: io.Error) {
b := (^Reader)(s.stream_data);
return reader_read_byte(b);
},
impl_unread_byte = proc(s: io.Stream) -> io.Error {
b := (^Reader)(s.stream_data);
return reader_unread_byte(b);
},
impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
b := (^Reader)(s.stream_data);
return reader_read_rune(b);
},
impl_unread_rune = proc(s: io.Stream) -> io.Error {
b := (^Reader)(s.stream_data);
return reader_unread_rune(b);
},
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
b := (^Reader)(s.stream_data);
return reader_write_to(b, w);
},
};
//
// Utility procedures
//
// reader_read_slice reads until the first occurrence of delim from the reader
// It returns a slice pointing at the bytes in the buffer
// The bytes stop being valid at the next read
// If reader_read_slice encounters an error before finding a delimiter
// reader_read_slice fails with error .Buffer_Full if the buffer fills without a delim
// Because the data returned from reader_read_slice will be overwritten on the
// next IO operation, reader_read_bytes or reader_read_string is usually preferred
//
// reader_read_slice returns err != nil if and only if line does not end in delim
//
reader_read_slice :: proc(b: ^Reader, delim: byte) -> (line: []byte, err: io.Error) {
s := 0;
for {
if i := bytes.index_byte(b.buf[b.r+s : b.w], delim); i >= 0 {
i += s;
line = b.buf[b.r:][:i+1];
b.r += i + 1;
break;
}
if b.err != nil {
line = b.buf[b.r : b.w];
b.r = b.w;
err = _reader_consume_err(b);
break;
}
if reader_buffered(b) >= len(b.buf) {
b.r = b.w;
line = b.buf;
err = .Buffer_Full;
break;
}
s = b.w - b.r;
if err = _reader_read_new_chunk(b); err != nil {
break;
}
}
if i := len(line)-1; i >= 0 {
b.last_byte = int(line[i]);
b.last_rune_size = -1;
}
return;
}
// reader_read_bytes reads until the first occurrence of delim from the Reader
// It returns an allocated slice containing the data up to and including the delimiter
reader_read_bytes :: proc(b: ^Reader, delim: byte, allocator := context.allocator) -> (buf: []byte, err: io.Error) {
full: [dynamic]byte;
full.allocator = allocator;
frag: []byte;
for {
e: io.Error;
frag, e = reader_read_slice(b, delim);
if e == nil {
break;
}
if e != .Buffer_Full {
err = e;
break;
}
append(&full, ..frag);
}
append(&full, ..frag);
return full[:], err;
}
// reader_read_string reads until the first occurrence of delim from the Reader
// It returns an allocated string containing the data up to and including the delimiter
reader_read_string :: proc(b: ^Reader, delim: byte, allocator := context.allocator) -> (string, io.Error) {
buf, err := reader_read_bytes(b, delim, allocator);
return string(buf), err;
}
+255
View File
@@ -0,0 +1,255 @@
package bufio
import "core:io"
import "core:mem"
import "core:unicode/utf8"
// import "core:bytes"
// Writer is a buffered wrapper for an io.Writer
Writer :: struct {
buf: []byte,
buf_allocator: mem.Allocator,
wr: io.Writer,
n: int,
err: io.Error,
}
writer_init :: proc(b: ^Writer, wr: io.Writer, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) {
size := size;
size = max(size, MIN_READ_BUFFER_SIZE);
writer_reset(b, wr);
b.buf_allocator = allocator;
b.buf = make([]byte, size, allocator);
}
writer_init_with_buf :: proc(b: ^Writer, wr: io.Writer, buf: []byte) {
writer_reset(b, wr);
b.buf_allocator = {};
b.buf = buf;
}
// writer_destroy destroys the underlying buffer with its associated allocator IFF that allocator has been set
writer_destroy :: proc(b: ^Writer) {
delete(b.buf, b.buf_allocator);
b^ = {};
}
// writer_size returns the size of underlying buffer in bytes
writer_size :: proc(b: ^Writer) -> int {
return len(b.buf);
}
writer_reset :: proc(b: ^Writer, w: io.Writer) {
b.wr = w;
b.n = 0;
b.err = nil;
}
// writer_flush writes any buffered data into the underlying io.Writer
writer_flush :: proc(b: ^Writer) -> io.Error {
if b.err != nil {
return b.err;
}
if b.n == 0 {
return nil;
}
n, err := io.write(b.wr, b.buf[0:b.n]);
if n < b.n && err == nil {
err = .Short_Write;
}
if err != nil {
if n > 0 && n < b.n {
copy(b.buf[:b.n-n], b.buf[n : b.n]);
}
b.n -= n;
b.err = err;
return err;
}
b.n = 0;
return nil;
}
// writer_available returns how many bytes are unused in the buffer
writer_available :: proc(b: ^Writer) -> int {
return len(b.buf) - b.n;
}
// writer_buffered returns the number of bytes that have been writted into the current buffer
writer_buffered :: proc(b: ^Writer) -> int {
return b.n;
}
// writer_write writes the contents of p into the buffer
// It returns the number of bytes written
// If n < len(p), it will return an error explaining why the write is short
writer_write :: proc(b: ^Writer, p: []byte) -> (n: int, err: io.Error) {
p := p;
for len(p) > writer_available(b) && b.err == nil {
m: int;
if writer_buffered(b) == 0 {
m, b.err = io.write(b.wr, p);
} else {
m = copy(b.buf[b.n:], p);
b.n += m;
writer_flush(b);
}
n += m;
p = p[m:];
}
if b.err != nil {
return n, b.err;
}
m := copy(b.buf[b.n:], p);
b.n += m;
m += n;
return m, nil;
}
// writer_write_byte writes a single byte
writer_write_byte :: proc(b: ^Writer, c: byte) -> io.Error {
if b.err != nil {
return b.err;
}
if writer_available(b) <= 0 && writer_flush(b) != nil {
return b.err;
}
b.buf[b.n] = c;
b.n += 1;
return nil;
}
// writer_write_rune writes a single unicode code point, and returns the number of bytes written with any error
writer_write_rune :: proc(b: ^Writer, r: rune) -> (size: int, err: io.Error) {
if r < utf8.RUNE_SELF {
err = writer_write_byte(b, byte(r));
size = 0 if err != nil else 1;
return;
}
if b.err != nil {
return 0, b.err;
}
buf: [4]u8;
n := writer_available(b);
if n < utf8.UTF_MAX {
writer_flush(b);
if b.err != nil {
return 0, b.err;
}
n = writer_available(b);
if n < utf8.UTF_MAX {
// this only happens if the buffer is very small
w: int;
buf, w = utf8.encode_rune(r);
return writer_write(b, buf[:w]);
}
}
buf, size = utf8.encode_rune(r);
copy(b.buf[b.n:], buf[:size]);
b.n += size;
return;
}
// writer_write writes a string into the buffer
// It returns the number of bytes written
// If n < len(p), it will return an error explaining why the write is short
writer_write_string :: proc(b: ^Writer, s: string) -> (int, io.Error) {
return writer_write(b, transmute([]byte)s);
}
// writer_read_from is to support io.Reader_From types
// If the underlying writer supports the io,read_from, and b has no buffered data yet,
// this procedure calls the underlying read_from implementation without buffering
writer_read_from :: proc(b: ^Writer, r: io.Reader) -> (n: i64, err: io.Error) {
if b.err != nil {
return 0, b.err;
}
if writer_buffered(b) == 0 {
if w, ok := io.to_reader_from(b.wr); !ok {
n, err = io.read_from(w, r);
b.err = err;
return;
}
}
for {
if writer_available(b) == 0 {
if ferr := writer_flush(b); ferr != nil {
return n, ferr;
}
}
m: int;
nr := 0;
for nr < MAX_CONSECUTIVE_EMPTY_READS {
m, err = io.read(r, b.buf[b.n:]);
if m != 0 || err != nil {
break;
}
nr += 1;
}
if nr == MAX_CONSECUTIVE_EMPTY_READS {
return n, .No_Progress;
}
b.n += m;
n += i64(m);
if err != nil {
break;
}
}
if err == .EOF {
if writer_available(b) == 0 {
err = writer_flush(b);
} else {
err = nil;
}
}
return;
}
// writer_to_stream converts a Writer into an io.Stream
writer_to_stream :: proc(b: ^Writer) -> (s: io.Stream) {
s.stream_data = b;
s.stream_vtable = _writer_vtable;
return;
}
@(private)
_writer_vtable := &io.Stream_VTable{
impl_destroy = proc(s: io.Stream) -> io.Error {
b := (^Writer)(s.stream_data);
writer_destroy(b);
return nil;
},
impl_flush = proc(s: io.Stream) -> io.Error {
b := (^Writer)(s.stream_data);
return writer_flush(b);
},
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
b := (^Writer)(s.stream_data);
return writer_write(b, p);
},
impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
b := (^Writer)(s.stream_data);
return writer_write_byte(b, c);
},
impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
b := (^Writer)(s.stream_data);
return writer_write_rune(b, r);
},
impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
b := (^Writer)(s.stream_data);
return writer_read_from(b, r);
},
};
+15
View File
@@ -43,6 +43,7 @@ complex32 :: complex32;
complex64 :: complex64;
complex128 :: complex128;
quaternion64 :: quaternion64;
quaternion128 :: quaternion128;
quaternion256 :: quaternion256;
@@ -76,6 +77,17 @@ u64be :: u64be;
i128be :: i128be;
u128be :: u128be;
f16le :: f16le;
f32le :: f32le;
f64le :: f64le;
f16be :: f16be;
f32be :: f32be;
f64be :: f64be;
// Procedures
len :: proc(array: Array_Type) -> int ---
cap :: proc(array: Array_Type) -> int ---
@@ -103,3 +115,6 @@ min :: proc(values: ..T) -> T ---
max :: proc(values: ..T) -> T ---
abs :: proc(value: T) -> T ---
clamp :: proc(value, minimum, maximum: T) -> T ---
soa_zip :: proc(slices: ...) -> #soa[]Struct ---
soa_unzip :: proc(value: $S/#soa[]$E) -> (slices: ...) ---
+433
View File
@@ -0,0 +1,433 @@
package bytes
import "core:io"
import "core:unicode/utf8"
MIN_READ :: 512;
@(private)
SMALL_BUFFER_SIZE :: 64;
// A Buffer is a variable-sized buffer of bytes with a io.Stream interface
// The zero value for Buffer is an empty buffer ready to use.
Buffer :: struct {
buf: [dynamic]byte,
off: int,
last_read: Read_Op,
}
@(private)
Read_Op :: enum i8 {
Read = -1,
Invalid = 0,
Read_Rune1 = 1,
Read_Rune2 = 2,
Read_Rune3 = 3,
Read_Rune4 = 4,
}
buffer_init :: proc(b: ^Buffer, buf: []byte) {
resize(&b.buf, len(buf));
copy(b.buf[:], buf);
}
buffer_init_string :: proc(b: ^Buffer, s: string) {
resize(&b.buf, len(s));
copy(b.buf[:], s);
}
buffer_init_allocator :: proc(b: ^Buffer, len, cap: int, allocator := context.allocator) {
b.buf.allocator = allocator;
reserve(&b.buf, cap);
resize(&b.buf, len);
}
buffer_destroy :: proc(b: ^Buffer) {
delete(b.buf);
buffer_reset(b);
}
buffer_to_bytes :: proc(b: ^Buffer) -> []byte {
return b.buf[b.off:];
}
buffer_to_string :: proc(b: ^Buffer) -> string {
if b == nil {
return "<nil>";
}
return string(b.buf[b.off:]);
}
buffer_is_empty :: proc(b: ^Buffer) -> bool {
return len(b.buf) <= b.off;
}
buffer_length :: proc(b: ^Buffer) -> int {
return len(b.buf) - b.off;
}
buffer_capacity :: proc(b: ^Buffer) -> int {
return cap(b.buf);
}
buffer_reset :: proc(b: ^Buffer) {
clear(&b.buf);
b.off = 0;
b.last_read = .Invalid;
}
buffer_truncate :: proc(b: ^Buffer, n: int) {
if n == 0 {
buffer_reset(b);
return;
}
b.last_read = .Invalid;
if n < 0 || n > buffer_length(b) {
panic("bytes.truncate: truncation out of range");
}
resize(&b.buf, b.off+n);
}
@(private)
_buffer_try_grow :: proc(b: ^Buffer, n: int) -> (int, bool) {
if l := len(b.buf); n <= cap(b.buf)-l {
resize(&b.buf, l+n);
return l, true;
}
return 0, false;
}
@(private)
_buffer_grow :: proc(b: ^Buffer, n: int) -> int {
m := buffer_length(b);
if m == 0 && b.off != 0 {
buffer_reset(b);
}
if i, ok := _buffer_try_grow(b, n); ok {
return i;
}
if b.buf == nil && n <= SMALL_BUFFER_SIZE {
b.buf = make([dynamic]byte, n, SMALL_BUFFER_SIZE);
return 0;
}
c := cap(b.buf);
if n <= c/2 - m {
copy(b.buf[:], b.buf[b.off:]);
} else if c > max(int) - c - n {
panic("bytes.Buffer: too large");
} else {
resize(&b.buf, 2*c + n);
copy(b.buf[:], b.buf[b.off:]);
}
b.off = 0;
resize(&b.buf, m+n);
return m;
}
buffer_grow :: proc(b: ^Buffer, n: int) {
if n < 0 {
panic("bytes.buffer_grow: negative count");
}
m := _buffer_grow(b, n);
resize(&b.buf, m);
}
buffer_write_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
b.last_read = .Invalid;
if offset < 0 {
err = .Invalid_Offset;
return;
}
_, ok := _buffer_try_grow(b, offset+len(p));
if !ok {
_ = _buffer_grow(b, offset+len(p));
}
if len(b.buf) <= offset {
return 0, .Short_Write;
}
return copy(b.buf[offset:], p), nil;
}
buffer_write :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
b.last_read = .Invalid;
m, ok := _buffer_try_grow(b, len(p));
if !ok {
m = _buffer_grow(b, len(p));
}
return copy(b.buf[m:], p), nil;
}
buffer_write_string :: proc(b: ^Buffer, s: string) -> (n: int, err: io.Error) {
b.last_read = .Invalid;
m, ok := _buffer_try_grow(b, len(s));
if !ok {
m = _buffer_grow(b, len(s));
}
return copy(b.buf[m:], s), nil;
}
buffer_write_byte :: proc(b: ^Buffer, c: byte) -> io.Error {
b.last_read = .Invalid;
m, ok := _buffer_try_grow(b, 1);
if !ok {
m = _buffer_grow(b, 1);
}
b.buf[m] = c;
return nil;
}
buffer_write_rune :: proc(b: ^Buffer, r: rune) -> (n: int, err: io.Error) {
if r < utf8.RUNE_SELF {
buffer_write_byte(b, byte(r));
return 1, nil;
}
b.last_read = .Invalid;
m, ok := _buffer_try_grow(b, utf8.UTF_MAX);
if !ok {
m = _buffer_grow(b, utf8.UTF_MAX);
}
res: [4]byte;
res, n = utf8.encode_rune(r);
copy(b.buf[m:][:utf8.UTF_MAX], res[:n]);
resize(&b.buf, m+n);
return;
}
buffer_next :: proc(b: ^Buffer, n: int) -> []byte {
n := n;
b.last_read = .Invalid;
m := buffer_length(b);
if n > m {
n = m;
}
data := b.buf[b.off : b.off + n];
b.off += n;
if n > 0 {
b.last_read = .Read;
}
return data;
}
buffer_read :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
b.last_read = .Invalid;
if buffer_is_empty(b) {
buffer_reset(b);
if len(p) == 0 {
return 0, nil;
}
return 0, .EOF;
}
n = copy(p, b.buf[b.off:]);
b.off += n;
if n > 0 {
b.last_read = .Read;
}
return;
}
buffer_read_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
b.last_read = .Invalid;
if offset < 0 || offset >= len(b.buf) {
err = .Invalid_Offset;
return;
}
if 0 <= offset && offset < len(b.buf) {
n = copy(p, b.buf[offset:]);
}
if n > 0 {
b.last_read = .Read;
}
return;
}
buffer_read_byte :: proc(b: ^Buffer) -> (byte, io.Error) {
if buffer_is_empty(b) {
buffer_reset(b);
return 0, .EOF;
}
c := b.buf[b.off];
b.off += 1;
b.last_read = .Read;
return c, nil;
}
buffer_read_rune :: proc(b: ^Buffer) -> (r: rune, size: int, err: io.Error) {
if buffer_is_empty(b) {
buffer_reset(b);
return 0, 0, .EOF;
}
c := b.buf[b.off];
if c < utf8.RUNE_SELF {
b.off += 1;
b.last_read = .Read_Rune1;
return rune(c), 1, nil;
}
r, size = utf8.decode_rune(b.buf[b.off:]);
b.off += size;
b.last_read = Read_Op(i8(size));
return;
}
buffer_unread_byte :: proc(b: ^Buffer) -> io.Error {
if b.last_read == .Invalid {
return .Invalid_Unread;
}
b.last_read = .Invalid;
if b.off > 0 {
b.off -= 1;
}
return nil;
}
buffer_unread_rune :: proc(b: ^Buffer) -> io.Error {
if b.last_read <= .Invalid {
return .Invalid_Unread;
}
if b.off >= int(b.last_read) {
b.off -= int(i8(b.last_read));
}
b.last_read = .Invalid;
return nil;
}
buffer_read_bytes :: proc(b: ^Buffer, delim: byte) -> (line: []byte, err: io.Error) {
i := index_byte(b.buf[b.off:], delim);
end := b.off + i + 1;
if i < 0 {
end = len(b.buf);
err = .EOF;
}
line = b.buf[b.off:end];
b.off = end;
b.last_read = .Read;
return;
}
buffer_read_string :: proc(b: ^Buffer, delim: byte) -> (line: string, err: io.Error) {
slice: []byte;
slice, err = buffer_read_bytes(b, delim);
return string(slice), err;
}
buffer_write_to :: proc(b: ^Buffer, w: io.Writer) -> (n: i64, err: io.Error) {
b.last_read = .Invalid;
if byte_count := buffer_length(b); byte_count > 0 {
m, e := io.write(w, b.buf[b.off:]);
if m > byte_count {
panic("bytes.buffer_write_to: invalid io.write count");
}
b.off += m;
n = i64(m);
if e != nil {
err = e;
return;
}
if m != byte_count {
err = .Short_Write;
return;
}
}
buffer_reset(b);
return;
}
buffer_read_from :: proc(b: ^Buffer, r: io.Reader) -> (n: i64, err: io.Error) #no_bounds_check {
b.last_read = .Invalid;
for {
i := _buffer_grow(b, MIN_READ);
resize(&b.buf, i);
m, e := io.read(r, b.buf[i:cap(b.buf)]);
if m < 0 {
err = .Negative_Read;
return;
}
resize(&b.buf, i+m);
n += i64(m);
if e == .EOF {
return;
}
if e != nil {
err = e;
return;
}
}
return;
}
buffer_to_stream :: proc(b: ^Buffer) -> (s: io.Stream) {
s.stream_data = b;
s.stream_vtable = _buffer_vtable;
return;
}
@(private)
_buffer_vtable := &io.Stream_VTable{
impl_size = proc(s: io.Stream) -> i64 {
b := (^Buffer)(s.stream_data);
return i64(buffer_capacity(b));
},
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
b := (^Buffer)(s.stream_data);
return buffer_read(b, p);
},
impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
b := (^Buffer)(s.stream_data);
return buffer_read_at(b, p, int(offset));
},
impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
b := (^Buffer)(s.stream_data);
return buffer_read_byte(b);
},
impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
b := (^Buffer)(s.stream_data);
return buffer_read_rune(b);
},
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
b := (^Buffer)(s.stream_data);
return buffer_write(b, p);
},
impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
b := (^Buffer)(s.stream_data);
return buffer_write_at(b, p, int(offset));
},
impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
b := (^Buffer)(s.stream_data);
return buffer_write_byte(b, c);
},
impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
b := (^Buffer)(s.stream_data);
return buffer_write_rune(b, r);
},
impl_unread_byte = proc(s: io.Stream) -> io.Error {
b := (^Buffer)(s.stream_data);
return buffer_unread_byte(b);
},
impl_unread_rune = proc(s: io.Stream) -> io.Error {
b := (^Buffer)(s.stream_data);
return buffer_unread_rune(b);
},
impl_destroy = proc(s: io.Stream) -> io.Error {
b := (^Buffer)(s.stream_data);
buffer_destroy(b);
return nil;
},
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
b := (^Buffer)(s.stream_data);
return buffer_write_to(b, w);
},
impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
b := (^Buffer)(s.stream_data);
return buffer_read_from(b, r);
},
};
File diff suppressed because it is too large Load Diff
+177
View File
@@ -0,0 +1,177 @@
package bytes
import "core:io"
import "core:unicode/utf8"
Reader :: struct {
s: []byte, // read-only buffer
i: i64, // current reading index
prev_rune: int, // previous reading index of rune or < 0
}
reader_init :: proc(r: ^Reader, s: []byte) {
r.s = s;
r.i = 0;
r.prev_rune = -1;
}
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
s.stream_data = r;
s.stream_vtable = _reader_vtable;
return;
}
reader_length :: proc(r: ^Reader) -> int {
if r.i >= i64(len(r.s)) {
return 0;
}
return int(i64(len(r.s)) - r.i);
}
reader_size :: proc(r: ^Reader) -> i64 {
return i64(len(r.s));
}
reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
if r.i >= i64(len(r.s)) {
return 0, .EOF;
}
r.prev_rune = -1;
n = copy(p, r.s[r.i:]);
r.i += i64(n);
return;
}
reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) {
if off < 0 {
return 0, .Invalid_Offset;
}
if off >= i64(len(r.s)) {
return 0, .EOF;
}
n = copy(p, r.s[off:]);
if n < len(p) {
err = .EOF;
}
return;
}
reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
r.prev_rune = -1;
if r.i >= i64(len(r.s)) {
return 0, .EOF;
}
b := r.s[r.i];
r.i += 1;
return b, nil;
}
reader_unread_byte :: proc(r: ^Reader) -> io.Error {
if r.i <= 0 {
return .Invalid_Unread;
}
r.prev_rune = -1;
r.i -= 1;
return nil;
}
reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) {
if r.i >= i64(len(r.s)) {
r.prev_rune = -1;
return 0, 0, .EOF;
}
r.prev_rune = int(r.i);
if c := r.s[r.i]; c < utf8.RUNE_SELF {
r.i += 1;
return rune(c), 1, nil;
}
ch, size = utf8.decode_rune(r.s[r.i:]);
r.i += i64(size);
return;
}
reader_unread_rune :: proc(r: ^Reader) -> io.Error {
if r.i <= 0 {
return .Invalid_Unread;
}
if r.prev_rune < 0 {
return .Invalid_Unread;
}
r.i = i64(r.prev_rune);
r.prev_rune = -1;
return nil;
}
reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
r.prev_rune = -1;
abs: i64;
switch whence {
case .Start:
abs = offset;
case .Current:
abs = r.i + offset;
case .End:
abs = i64(len(r.s)) + offset;
case:
return 0, .Invalid_Whence;
}
if abs < 0 {
return 0, .Invalid_Offset;
}
r.i = abs;
return abs, nil;
}
reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
r.prev_rune = -1;
if r.i >= i64(len(r.s)) {
return 0, nil;
}
s := r.s[r.i:];
m: int;
m, err = io.write(w, s);
if m > len(s) {
panic("bytes.Reader.write_to: invalid io.write_string count");
}
r.i += i64(m);
n = i64(m);
if m != len(s) && err == nil {
err = .Short_Write;
}
return;
}
@(private)
_reader_vtable := &io.Stream_VTable{
impl_size = proc(s: io.Stream) -> i64 {
r := (^Reader)(s.stream_data);
return reader_size(r);
},
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
r := (^Reader)(s.stream_data);
return reader_read(r, p);
},
impl_read_at = proc(s: io.Stream, p: []byte, off: i64) -> (n: int, err: io.Error) {
r := (^Reader)(s.stream_data);
return reader_read_at(r, p, off);
},
impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
r := (^Reader)(s.stream_data);
return reader_read_byte(r);
},
impl_unread_byte = proc(s: io.Stream) -> io.Error {
r := (^Reader)(s.stream_data);
return reader_unread_byte(r);
},
impl_read_rune = proc(s: io.Stream) -> (ch: rune, size: int, err: io.Error) {
r := (^Reader)(s.stream_data);
return reader_read_rune(r);
},
impl_unread_rune = proc(s: io.Stream) -> io.Error {
r := (^Reader)(s.stream_data);
return reader_unread_rune(r);
},
impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
r := (^Reader)(s.stream_data);
return reader_seek(r, offset, whence);
},
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
r := (^Reader)(s.stream_data);
return reader_write_to(r, w);
},
};
@@ -0,0 +1,25 @@
package c_frontend_preprocess
import "core:c/frontend/tokenizer"
const_expr :: proc(rest: ^^Token, tok: ^Token) -> i64 {
// TODO(bill): Handle const_expr correctly
// This is effectively a mini-parser
assert(rest != nil);
assert(tok != nil);
rest^ = tokenizer.new_eof(tok);
switch v in tok.val {
case i64:
return v;
case f64:
return i64(v);
case string:
return 0;
case []u16:
// TODO
case []u32:
// TODO
}
return 0;
}
File diff suppressed because it is too large Load Diff
+154
View File
@@ -0,0 +1,154 @@
package c_frontend_preprocess
import "core:unicode/utf8"
unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool, tail_string: string, success: bool) {
hex_to_int :: proc(c: byte) -> int {
switch c {
case '0'..'9': return int(c-'0');
case 'a'..'f': return int(c-'a')+10;
case 'A'..'F': return int(c-'A')+10;
}
return -1;
}
w: int;
if str[0] == quote && quote == '"' {
return;
} else if str[0] >= 0x80 {
r, w = utf8.decode_rune_in_string(str);
return r, true, str[w:], true;
} else if str[0] != '\\' {
return rune(str[0]), false, str[1:], true;
}
if len(str) <= 1 {
return;
}
s := str;
c := s[1];
s = s[2:];
switch c {
case: r = rune(c);
case 'a': r = '\a';
case 'b': r = '\b';
case 'e': r = '\e';
case 'f': r = '\f';
case 'n': r = '\n';
case 'r': r = '\r';
case 't': r = '\t';
case 'v': r = '\v';
case '\\': r = '\\';
case '"': r = '"';
case '\'': r = '\'';
case '0'..'7':
v := int(c-'0');
if len(s) < 2 {
return;
}
for i in 0..<len(s) {
d := int(s[i]-'0');
if d < 0 || d > 7 {
return;
}
v = (v<<3) | d;
}
s = s[2:];
if v > 0xff {
return;
}
r = rune(v);
case 'x', 'u', 'U':
count: int;
switch c {
case 'x': count = 2;
case 'u': count = 4;
case 'U': count = 8;
}
if len(s) < count {
return;
}
for i in 0..<count {
d := hex_to_int(s[i]);
if d < 0 {
return;
}
r = (r<<4) | rune(d);
}
s = s[count:];
if c == 'x' {
break;
}
if r > utf8.MAX_RUNE {
return;
}
multiple_bytes = true;
}
success = true;
tail_string = s;
return;
}
unquote_string :: proc(lit: string, allocator := context.allocator) -> (res: string, allocated, success: bool) {
contains_rune :: proc(s: string, r: rune) -> int {
for c, offset in s {
if c == r {
return offset;
}
}
return -1;
}
assert(len(lit) >= 2);
s := lit;
quote := '"';
if s == `""` {
return "", false, true;
}
if contains_rune(s, '\n') >= 0 {
return s, false, false;
}
if contains_rune(s, '\\') < 0 && contains_rune(s, quote) < 0 {
if quote == '"' {
return s, false, true;
}
}
s = s[1:len(s)-1];
buf_len := 3*len(s) / 2;
buf := make([]byte, buf_len, allocator);
offset := 0;
for len(s) > 0 {
r, multiple_bytes, tail_string, ok := unquote_char(s, byte(quote));
if !ok {
delete(buf);
return s, false, false;
}
s = tail_string;
if r < 0x80 || !multiple_bytes {
buf[offset] = byte(r);
offset += 1;
} else {
b, w := utf8.encode_rune(r);
copy(buf[offset:], b[:w]);
offset += w;
}
}
new_string := string(buf[:offset]);
return new_string, true, true;
}
+34
View File
@@ -0,0 +1,34 @@
/*
package demo
import tokenizer "core:c/frontend/tokenizer"
import preprocessor "core:c/frontend/preprocessor"
import "core:fmt"
main :: proc() {
t := &tokenizer.Tokenizer{};
tokenizer.init_defaults(t);
cpp := &preprocessor.Preprocessor{};
cpp.warn, cpp.err = t.warn, t.err;
preprocessor.init_lookup_tables(cpp);
preprocessor.init_default_macros(cpp);
cpp.include_paths = {"my/path/to/include"};
tok := tokenizer.tokenize_file(t, "the/source/file.c", 1);
tok = preprocessor.preprocess(cpp, tok);
if tok != nil {
for t := tok; t.kind != .EOF; t = t.next {
fmt.println(t.lit);
}
}
fmt.println("[Done]");
}
*/
package c_frontend_tokenizer
+68
View File
@@ -0,0 +1,68 @@
package c_frontend_tokenizer
// NOTE(bill): This is a really dumb approach for a hide set,
// but it's really simple and probably fast enough in practice
Hide_Set :: struct {
next: ^Hide_Set,
name: string,
}
new_hide_set :: proc(name: string) -> ^Hide_Set {
hs := new(Hide_Set);
hs.name = name;
return hs;
}
hide_set_contains :: proc(hs: ^Hide_Set, name: string) -> bool {
for h := hs; h != nil; h = h.next {
if h.name == name {
return true;
}
}
return false;
}
hide_set_union :: proc(a, b: ^Hide_Set) -> ^Hide_Set {
head: Hide_Set;
curr := &head;
for h := a; h != nil; h = h.next {
curr.next = new_hide_set(h.name);
curr = curr.next;
}
curr.next = b;
return head.next;
}
hide_set_intersection :: proc(a, b: ^Hide_Set) -> ^Hide_Set {
head: Hide_Set;
curr := &head;
for h := a; h != nil; h = h.next {
if hide_set_contains(b, h.name) {
curr.next = new_hide_set(h.name);
curr = curr.next;
}
}
return head.next;
}
add_hide_set :: proc(tok: ^Token, hs: ^Hide_Set) -> ^Token {
head: Token;
curr := &head;
tok := tok;
for ; tok != nil; tok = tok.next {
t := copy_token(tok);
t.hide_set = hide_set_union(t.hide_set, hs);
curr.next = t;
curr = curr.next;
}
return head.next;
}
+169
View File
@@ -0,0 +1,169 @@
package c_frontend_tokenizer
Pos :: struct {
file: string,
line: int,
column: int,
offset: int,
}
Token_Kind :: enum {
Invalid,
Ident,
Punct,
Keyword,
Char,
String,
Number,
PP_Number,
Comment,
EOF,
}
File :: struct {
name: string,
id: int,
src: []byte,
display_name: string,
line_delta: int,
}
Token_Type_Hint :: enum u8 {
None,
Int,
Long,
Long_Long,
Unsigned_Int,
Unsigned_Long,
Unsigned_Long_Long,
Float,
Double,
Long_Double,
UTF_8,
UTF_16,
UTF_32,
UTF_Wide,
}
Token_Value :: union {
i64,
f64,
string,
[]u16,
[]u32,
}
Token :: struct {
kind: Token_Kind,
next: ^Token,
lit: string,
pos: Pos,
file: ^File,
line_delta: int,
at_bol: bool,
has_space: bool,
type_hint: Token_Type_Hint,
val: Token_Value,
prefix: string,
// Preprocessor values
hide_set: ^Hide_Set,
origin: ^Token,
}
Is_Keyword_Proc :: #type proc(tok: ^Token) -> bool;
copy_token :: proc(tok: ^Token) -> ^Token {
t := new_clone(tok^);
t.next = nil;
return t;
}
new_eof :: proc(tok: ^Token) -> ^Token {
t := new_clone(tok^);
t.kind = .EOF;
t.lit = "";
return t;
}
default_is_keyword :: proc(tok: ^Token) -> bool {
if tok.kind == .Keyword {
return true;
}
if len(tok.lit) > 0 {
return default_keyword_set[tok.lit];
}
return false;
}
token_name := [Token_Kind]string {
.Invalid = "invalid",
.Ident = "ident",
.Punct = "punct",
.Keyword = "keyword",
.Char = "char",
.String = "string",
.Number = "number",
.PP_Number = "preprocessor number",
.Comment = "comment",
.EOF = "eof",
};
default_keyword_set := map[string]bool{
"auto" = true,
"break" = true,
"case" = true,
"char" = true,
"const" = true,
"continue" = true,
"default" = true,
"do" = true,
"double" = true,
"else" = true,
"enum" = true,
"extern" = true,
"float" = true,
"for" = true,
"goto" = true,
"if" = true,
"int" = true,
"long" = true,
"register" = true,
"restrict" = true,
"return" = true,
"short" = true,
"signed" = true,
"sizeof" = true,
"static" = true,
"struct" = true,
"switch" = true,
"typedef" = true,
"union" = true,
"unsigned" = true,
"void" = true,
"volatile" = true,
"while" = true,
"_Alignas" = true,
"_Alignof" = true,
"_Atomic" = true,
"_Bool" = true,
"_Generic" = true,
"_Noreturn" = true,
"_Thread_local" = true,
"__restrict" = true,
"typeof" = true,
"asm" = true,
"__restrict__" = true,
"__thread" = true,
"__attribute__" = true,
};
+667
View File
@@ -0,0 +1,667 @@
package c_frontend_tokenizer
import "core:fmt"
import "core:os"
import "core:strings"
import "core:unicode/utf8"
Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any);
Tokenizer :: struct {
// Immutable data
path: string,
src: []byte,
// Tokenizing state
ch: rune,
offset: int,
read_offset: int,
line_offset: int,
line_count: int,
// Extra information for tokens
at_bol: bool,
has_space: bool,
// Mutable data
err: Error_Handler,
warn: Error_Handler,
error_count: int,
warning_count: int,
}
init_defaults :: proc(t: ^Tokenizer, err: Error_Handler = default_error_handler, warn: Error_Handler = default_warn_handler) {
t.err = err;
t.warn = warn;
}
@(private)
offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> (pos: Pos) {
pos.file = t.path;
pos.offset = offset;
pos.line = t.line_count;
pos.column = offset - t.line_offset + 1;
return;
}
default_error_handler :: proc(pos: Pos, msg: string, args: ..any) {
fmt.eprintf("%s(%d:%d) ", pos.file, pos.line, pos.column);
fmt.eprintf(msg, ..args);
fmt.eprintf("\n");
}
default_warn_handler :: proc(pos: Pos, msg: string, args: ..any) {
fmt.eprintf("%s(%d:%d) warning: ", pos.file, pos.line, pos.column);
fmt.eprintf(msg, ..args);
fmt.eprintf("\n");
}
error_offset :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
pos := offset_to_pos(t, offset);
if t.err != nil {
t.err(pos, msg, ..args);
}
t.error_count += 1;
}
warn_offset :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
pos := offset_to_pos(t, offset);
if t.warn != nil {
t.warn(pos, msg, ..args);
}
t.warning_count += 1;
}
error :: proc(t: ^Tokenizer, tok: ^Token, msg: string, args: ..any) {
pos := tok.pos;
if t.err != nil {
t.err(pos, msg, ..args);
}
t.error_count += 1;
}
warn :: proc(t: ^Tokenizer, tok: ^Token, msg: string, args: ..any) {
pos := tok.pos;
if t.warn != nil {
t.warn(pos, msg, ..args);
}
t.warning_count += 1;
}
advance_rune :: proc(t: ^Tokenizer) {
if t.read_offset < len(t.src) {
t.offset = t.read_offset;
if t.ch == '\n' {
t.at_bol = true;
t.line_offset = t.offset;
t.line_count += 1;
}
r, w := rune(t.src[t.read_offset]), 1;
switch {
case r == 0:
error_offset(t, t.offset, "illegal character NUL");
case r >= utf8.RUNE_SELF:
r, w = utf8.decode_rune(t.src[t.read_offset:]);
if r == utf8.RUNE_ERROR && w == 1 {
error_offset(t, t.offset, "illegal UTF-8 encoding");
} else if r == utf8.RUNE_BOM && t.offset > 0 {
error_offset(t, t.offset, "illegal byte order mark");
}
}
t.read_offset += w;
t.ch = r;
} else {
t.offset = len(t.src);
if t.ch == '\n' {
t.at_bol = true;
t.line_offset = t.offset;
t.line_count += 1;
}
t.ch = -1;
}
}
advance_rune_n :: proc(t: ^Tokenizer, n: int) {
for in 0..<n {
advance_rune(t);
}
}
is_digit :: proc(r: rune) -> bool {
return '0' <= r && r <= '9';
}
skip_whitespace :: proc(t: ^Tokenizer) {
for {
switch t.ch {
case ' ', '\t', '\r', '\v', '\f', '\n':
t.has_space = true;
advance_rune(t);
case:
return;
}
}
}
scan_comment :: proc(t: ^Tokenizer) -> string {
offset := t.offset-1;
next := -1;
general: {
if t.ch == '/'{ // line comments
advance_rune(t);
for t.ch != '\n' && t.ch >= 0 {
advance_rune(t);
}
next = t.offset;
if t.ch == '\n' {
next += 1;
}
break general;
}
/* style comment */
advance_rune(t);
for t.ch >= 0 {
ch := t.ch;
advance_rune(t);
if ch == '*' && t.ch == '/' {
advance_rune(t);
next = t.offset;
break general;
}
}
error_offset(t, offset, "comment not terminated");
}
lit := t.src[offset : t.offset];
// NOTE(bill): Strip CR for line comments
for len(lit) > 2 && lit[1] == '/' && lit[len(lit)-1] == '\r' {
lit = lit[:len(lit)-1];
}
return string(lit);
}
scan_identifier :: proc(t: ^Tokenizer) -> string {
offset := t.offset;
for is_ident1(t.ch) {
advance_rune(t);
}
return string(t.src[offset : t.offset]);
}
scan_string :: proc(t: ^Tokenizer) -> string {
offset := t.offset-1;
for {
ch := t.ch;
if ch == '\n' || ch < 0 {
error_offset(t, offset, "string literal was not terminated");
break;
}
advance_rune(t);
if ch == '"' {
break;
}
if ch == '\\' {
scan_escape(t);
}
}
return string(t.src[offset : t.offset]);
}
digit_val :: proc(r: rune) -> int {
switch r {
case '0'..'9':
return int(r-'0');
case 'A'..'F':
return int(r-'A' + 10);
case 'a'..'f':
return int(r-'a' + 10);
}
return 16;
}
scan_escape :: proc(t: ^Tokenizer) -> bool {
offset := t.offset;
esc := t.ch;
n: int;
base, max: u32;
switch esc {
case 'a', 'b', 'e', 'f', 'n', 't', 'v', 'r', '\\', '\'', '"':
advance_rune(t);
return true;
case '0'..'7':
for digit_val(t.ch) < 8 {
advance_rune(t);
}
return true;
case 'x':
advance_rune(t);
for digit_val(t.ch) < 16 {
advance_rune(t);
}
return true;
case 'u':
advance_rune(t);
n, base, max = 4, 16, utf8.MAX_RUNE;
case 'U':
advance_rune(t);
n, base, max = 8, 16, utf8.MAX_RUNE;
case:
if t.ch < 0 {
error_offset(t, offset, "escape sequence was not terminated");
} else {
break;
}
return false;
}
x: u32;
main_loop: for n > 0 {
d := u32(digit_val(t.ch));
if d >= base {
if t.ch == '"' || t.ch == '\'' {
break main_loop;
}
if t.ch < 0 {
error_offset(t, t.offset, "escape sequence was not terminated");
} else {
error_offset(t, t.offset, "illegal character '%r' : %d in escape sequence", t.ch, t.ch);
}
return false;
}
x = x*base + d;
advance_rune(t);
n -= 1;
}
if x > max || 0xd800 <= x && x <= 0xe000 {
error_offset(t, offset, "escape sequence is an invalid Unicode code point");
return false;
}
return true;
}
scan_rune :: proc(t: ^Tokenizer) -> string {
offset := t.offset-1;
valid := true;
n := 0;
for {
ch := t.ch;
if ch == '\n' || ch < 0 {
if valid {
error_offset(t, offset, "rune literal not terminated");
valid = false;
}
break;
}
advance_rune(t);
if ch == '\'' {
break;
}
n += 1;
if ch == '\\' {
if !scan_escape(t) {
valid = false;
}
}
}
if valid && n != 1 {
error_offset(t, offset, "illegal rune literal");
}
return string(t.src[offset : t.offset]);
}
scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Token_Kind, string) {
scan_mantissa :: proc(t: ^Tokenizer, base: int) {
for digit_val(t.ch) < base {
advance_rune(t);
}
}
scan_exponent :: proc(t: ^Tokenizer) {
if t.ch == 'e' || t.ch == 'E' || t.ch == 'p' || t.ch == 'P' {
advance_rune(t);
if t.ch == '-' || t.ch == '+' {
advance_rune(t);
}
if digit_val(t.ch) < 10 {
scan_mantissa(t, 10);
} else {
error_offset(t, t.offset, "illegal floating-point exponent");
}
}
}
scan_fraction :: proc(t: ^Tokenizer) -> (early_exit: bool) {
if t.ch == '.' && peek(t) == '.' {
return true;
}
if t.ch == '.' {
advance_rune(t);
scan_mantissa(t, 10);
}
return false;
}
check_end := true;
offset := t.offset;
seen_point := seen_decimal_point;
if seen_point {
offset -= 1;
scan_mantissa(t, 10);
scan_exponent(t);
} else {
if t.ch == '0' {
int_base :: proc(t: ^Tokenizer, base: int, msg: string) {
prev := t.offset;
advance_rune(t);
scan_mantissa(t, base);
if t.offset - prev <= 1 {
error_offset(t, t.offset, msg);
}
}
advance_rune(t);
switch t.ch {
case 'b', 'B':
int_base(t, 2, "illegal binary integer");
case 'x', 'X':
int_base(t, 16, "illegal hexadecimal integer");
case:
seen_point = false;
scan_mantissa(t, 10);
if t.ch == '.' {
seen_point = true;
if scan_fraction(t) {
check_end = false;
}
}
if check_end {
scan_exponent(t);
check_end = false;
}
}
}
}
if check_end {
scan_mantissa(t, 10);
if !scan_fraction(t) {
scan_exponent(t);
}
}
return .Number, string(t.src[offset : t.offset]);
}
scan_punct :: proc(t: ^Tokenizer, ch: rune) -> (kind: Token_Kind) {
kind = .Punct;
switch ch {
case:
kind = .Invalid;
case '<', '>':
if t.ch == ch {
advance_rune(t);
}
if t.ch == '=' {
advance_rune(t);
}
case '!', '+', '-', '*', '/', '%', '^', '=':
if t.ch == '=' {
advance_rune(t);
}
case '#':
if t.ch == '#' {
advance_rune(t);
}
case '&':
if t.ch == '=' || t.ch == '&' {
advance_rune(t);
}
case '|':
if t.ch == '=' || t.ch == '|' {
advance_rune(t);
}
case '(', ')', '[', ']', '{', '}':
// okay
case '~', ',', ':', ';', '?':
// okay
case '`':
// okay
case '.':
if t.ch == '.' && peek(t) == '.' {
advance_rune(t);
advance_rune(t); // consume last '.'
}
}
return;
}
peek :: proc(t: ^Tokenizer) -> byte {
if t.read_offset < len(t.src) {
return t.src[t.read_offset];
}
return 0;
}
peek_str :: proc(t: ^Tokenizer, str: string) -> bool {
if t.read_offset < len(t.src) {
return strings.has_prefix(string(t.src[t.offset:]), str);
}
return false;
}
scan_literal_prefix :: proc(t: ^Tokenizer, str: string, prefix: ^string) -> bool {
if peek_str(t, str) {
offset := t.offset;
for _ in str {
advance_rune(t);
}
prefix^ = string(t.src[offset:][:len(str)-1]);
return true;
}
return false;
}
allow_next_to_be_newline :: proc(t: ^Tokenizer) -> bool {
if t.ch == '\n' {
advance_rune(t);
return true;
} else if t.ch == '\r' && peek(t) == '\n' { // allow for MS-DOS style line endings
advance_rune(t); // \r
advance_rune(t); // \n
return true;
}
return false;
}
scan :: proc(t: ^Tokenizer, f: ^File) -> ^Token {
skip_whitespace(t);
offset := t.offset;
kind: Token_Kind;
lit: string;
prefix: string;
switch ch := t.ch; {
case scan_literal_prefix(t, `u8"`, &prefix):
kind = .String;
lit = scan_string(t);
case scan_literal_prefix(t, `u"`, &prefix):
kind = .String;
lit = scan_string(t);
case scan_literal_prefix(t, `L"`, &prefix):
kind = .String;
lit = scan_string(t);
case scan_literal_prefix(t, `U"`, &prefix):
kind = .String;
lit = scan_string(t);
case scan_literal_prefix(t, `u'`, &prefix):
kind = .Char;
lit = scan_rune(t);
case scan_literal_prefix(t, `L'`, &prefix):
kind = .Char;
lit = scan_rune(t);
case scan_literal_prefix(t, `U'`, &prefix):
kind = .Char;
lit = scan_rune(t);
case is_ident0(ch):
lit = scan_identifier(t);
kind = .Ident;
case '0' <= ch && ch <= '9':
kind, lit = scan_number(t, false);
case:
advance_rune(t);
switch ch {
case -1:
kind = .EOF;
case '\\':
kind = .Punct;
if allow_next_to_be_newline(t) {
t.at_bol = true;
t.has_space = false;
return scan(t, f);
}
case '.':
if is_digit(t.ch) {
kind, lit = scan_number(t, true);
} else {
kind = scan_punct(t, ch);
}
case '"':
kind = .String;
lit = scan_string(t);
case '\'':
kind = .Char;
lit = scan_rune(t);
case '/':
if t.ch == '/' || t.ch == '*' {
kind = .Comment;
lit = scan_comment(t);
t.has_space = true;
break;
}
fallthrough;
case:
kind = scan_punct(t, ch);
if kind == .Invalid && ch != utf8.RUNE_BOM {
error_offset(t, t.offset, "illegal character '%r': %d", ch, ch);
}
}
}
if lit == "" {
lit = string(t.src[offset : t.offset]);
}
if kind == .Comment {
return scan(t, f);
}
tok := new(Token);
tok.kind = kind;
tok.lit = lit;
tok.pos = offset_to_pos(t, offset);
tok.file = f;
tok.prefix = prefix;
tok.at_bol = t.at_bol;
tok.has_space = t.has_space;
t.at_bol, t.has_space = false, false;
return tok;
}
tokenize :: proc(t: ^Tokenizer, f: ^File) -> ^Token {
setup_tokenizer: {
t.src = f.src;
t.ch = ' ';
t.offset = 0;
t.read_offset = 0;
t.line_offset = 0;
t.line_count = len(t.src) > 0 ? 1 : 0;
t.error_count = 0;
t.path = f.name;
advance_rune(t);
if t.ch == utf8.RUNE_BOM {
advance_rune(t);
}
}
t.at_bol = true;
t.has_space = false;
head: Token;
curr := &head;
for {
tok := scan(t, f);
if tok == nil {
break;
}
curr.next = tok;
curr = curr.next;
if tok.kind == .EOF {
break;
}
}
return head.next;
}
add_new_file :: proc(t: ^Tokenizer, name: string, src: []byte, id: int) -> ^File {
file := new(File);
file.id = id;
file.src = src;
file.name = name;
file.display_name = name;
return file;
}
tokenize_file :: proc(t: ^Tokenizer, path: string, id: int, loc := #caller_location) -> ^Token {
src, ok := os.read_entire_file(path);
if !ok {
return nil;
}
return tokenize(t, add_new_file(t, path, src, id));
}
inline_tokenize :: proc(t: ^Tokenizer, tok: ^Token, src: []byte) -> ^Token {
file := new(File);
file.src = src;
if tok.file != nil {
file.id = tok.file.id;
file.name = tok.file.name;
file.display_name = tok.file.name;
}
return tokenize(t, file);
}
+116
View File
@@ -0,0 +1,116 @@
package c_frontend_tokenizer
in_range :: proc(range: []rune, c: rune) -> bool #no_bounds_check {
for i := 0; range[i] != -1; i += 2 {
if range[i] <= c && c <= range[i+1] {
return true;
}
}
return false;
}
// [https://www.sigbus.info/n1570#D] C11 allows ASCII and some multibyte characters in certan Unicode ranges to be used in an identifier.
//
// is_ident0 returns true if a given character is acceptable as the first character of an identifier.
is_ident0 :: proc(c: rune) -> bool {
return in_range(_range_ident0, c);
}
// is_ident0 returns true if a given character is acceptable as a non-first character of an identifier.
is_ident1 :: proc(c: rune) -> bool {
return is_ident0(c) || in_range(_range_ident1, c);
}
// Returns the number of columns needed to display a given character in a fixed-width font.
// Based on https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
char_width :: proc(c: rune) -> int {
switch {
case in_range(_range_width0, c):
return 0;
case in_range(_range_width2, c):
return 2;
}
return 1;
}
display_width :: proc(str: string) -> (w: int) {
for c in str {
w += char_width(c);
}
return;
}
_range_ident0 := []rune{
'_', '_', 'a', 'z', 'A', 'Z', '$', '$',
0x00A8, 0x00A8, 0x00AA, 0x00AA, 0x00AD, 0x00AD, 0x00AF, 0x00AF,
0x00B2, 0x00B5, 0x00B7, 0x00BA, 0x00BC, 0x00BE, 0x00C0, 0x00D6,
0x00D8, 0x00F6, 0x00F8, 0x00FF, 0x0100, 0x02FF, 0x0370, 0x167F,
0x1681, 0x180D, 0x180F, 0x1DBF, 0x1E00, 0x1FFF, 0x200B, 0x200D,
0x202A, 0x202E, 0x203F, 0x2040, 0x2054, 0x2054, 0x2060, 0x206F,
0x2070, 0x20CF, 0x2100, 0x218F, 0x2460, 0x24FF, 0x2776, 0x2793,
0x2C00, 0x2DFF, 0x2E80, 0x2FFF, 0x3004, 0x3007, 0x3021, 0x302F,
0x3031, 0x303F, 0x3040, 0xD7FF, 0xF900, 0xFD3D, 0xFD40, 0xFDCF,
0xFDF0, 0xFE1F, 0xFE30, 0xFE44, 0xFE47, 0xFFFD,
0x10000, 0x1FFFD, 0x20000, 0x2FFFD, 0x30000, 0x3FFFD, 0x40000, 0x4FFFD,
0x50000, 0x5FFFD, 0x60000, 0x6FFFD, 0x70000, 0x7FFFD, 0x80000, 0x8FFFD,
0x90000, 0x9FFFD, 0xA0000, 0xAFFFD, 0xB0000, 0xBFFFD, 0xC0000, 0xCFFFD,
0xD0000, 0xDFFFD, 0xE0000, 0xEFFFD,
-1,
};
_range_ident1 := []rune{
'0', '9', '$', '$', 0x0300, 0x036F, 0x1DC0, 0x1DFF, 0x20D0, 0x20FF, 0xFE20, 0xFE2F,
-1,
};
_range_width0 := []rune{
0x0000, 0x001F, 0x007f, 0x00a0, 0x0300, 0x036F, 0x0483, 0x0486,
0x0488, 0x0489, 0x0591, 0x05BD, 0x05BF, 0x05BF, 0x05C1, 0x05C2,
0x05C4, 0x05C5, 0x05C7, 0x05C7, 0x0600, 0x0603, 0x0610, 0x0615,
0x064B, 0x065E, 0x0670, 0x0670, 0x06D6, 0x06E4, 0x06E7, 0x06E8,
0x06EA, 0x06ED, 0x070F, 0x070F, 0x0711, 0x0711, 0x0730, 0x074A,
0x07A6, 0x07B0, 0x07EB, 0x07F3, 0x0901, 0x0902, 0x093C, 0x093C,
0x0941, 0x0948, 0x094D, 0x094D, 0x0951, 0x0954, 0x0962, 0x0963,
0x0981, 0x0981, 0x09BC, 0x09BC, 0x09C1, 0x09C4, 0x09CD, 0x09CD,
0x09E2, 0x09E3, 0x0A01, 0x0A02, 0x0A3C, 0x0A3C, 0x0A41, 0x0A42,
0x0A47, 0x0A48, 0x0A4B, 0x0A4D, 0x0A70, 0x0A71, 0x0A81, 0x0A82,
0x0ABC, 0x0ABC, 0x0AC1, 0x0AC5, 0x0AC7, 0x0AC8, 0x0ACD, 0x0ACD,
0x0AE2, 0x0AE3, 0x0B01, 0x0B01, 0x0B3C, 0x0B3C, 0x0B3F, 0x0B3F,
0x0B41, 0x0B43, 0x0B4D, 0x0B4D, 0x0B56, 0x0B56, 0x0B82, 0x0B82,
0x0BC0, 0x0BC0, 0x0BCD, 0x0BCD, 0x0C3E, 0x0C40, 0x0C46, 0x0C48,
0x0C4A, 0x0C4D, 0x0C55, 0x0C56, 0x0CBC, 0x0CBC, 0x0CBF, 0x0CBF,
0x0CC6, 0x0CC6, 0x0CCC, 0x0CCD, 0x0CE2, 0x0CE3, 0x0D41, 0x0D43,
0x0D4D, 0x0D4D, 0x0DCA, 0x0DCA, 0x0DD2, 0x0DD4, 0x0DD6, 0x0DD6,
0x0E31, 0x0E31, 0x0E34, 0x0E3A, 0x0E47, 0x0E4E, 0x0EB1, 0x0EB1,
0x0EB4, 0x0EB9, 0x0EBB, 0x0EBC, 0x0EC8, 0x0ECD, 0x0F18, 0x0F19,
0x0F35, 0x0F35, 0x0F37, 0x0F37, 0x0F39, 0x0F39, 0x0F71, 0x0F7E,
0x0F80, 0x0F84, 0x0F86, 0x0F87, 0x0F90, 0x0F97, 0x0F99, 0x0FBC,
0x0FC6, 0x0FC6, 0x102D, 0x1030, 0x1032, 0x1032, 0x1036, 0x1037,
0x1039, 0x1039, 0x1058, 0x1059, 0x1160, 0x11FF, 0x135F, 0x135F,
0x1712, 0x1714, 0x1732, 0x1734, 0x1752, 0x1753, 0x1772, 0x1773,
0x17B4, 0x17B5, 0x17B7, 0x17BD, 0x17C6, 0x17C6, 0x17C9, 0x17D3,
0x17DD, 0x17DD, 0x180B, 0x180D, 0x18A9, 0x18A9, 0x1920, 0x1922,
0x1927, 0x1928, 0x1932, 0x1932, 0x1939, 0x193B, 0x1A17, 0x1A18,
0x1B00, 0x1B03, 0x1B34, 0x1B34, 0x1B36, 0x1B3A, 0x1B3C, 0x1B3C,
0x1B42, 0x1B42, 0x1B6B, 0x1B73, 0x1DC0, 0x1DCA, 0x1DFE, 0x1DFF,
0x200B, 0x200F, 0x202A, 0x202E, 0x2060, 0x2063, 0x206A, 0x206F,
0x20D0, 0x20EF, 0x302A, 0x302F, 0x3099, 0x309A, 0xA806, 0xA806,
0xA80B, 0xA80B, 0xA825, 0xA826, 0xFB1E, 0xFB1E, 0xFE00, 0xFE0F,
0xFE20, 0xFE23, 0xFEFF, 0xFEFF, 0xFFF9, 0xFFFB, 0x10A01, 0x10A03,
0x10A05, 0x10A06, 0x10A0C, 0x10A0F, 0x10A38, 0x10A3A, 0x10A3F, 0x10A3F,
0x1D167, 0x1D169, 0x1D173, 0x1D182, 0x1D185, 0x1D18B, 0x1D1AA, 0x1D1AD,
0x1D242, 0x1D244, 0xE0001, 0xE0001, 0xE0020, 0xE007F, 0xE0100, 0xE01EF,
-1,
};
_range_width2 := []rune{
0x1100, 0x115F, 0x2329, 0x2329, 0x232A, 0x232A, 0x2E80, 0x303E,
0x3040, 0xA4CF, 0xAC00, 0xD7A3, 0xF900, 0xFAFF, 0xFE10, 0xFE19,
0xFE30, 0xFE6F, 0xFF00, 0xFF60, 0xFFE0, 0xFFE6, 0x1F000, 0x1F644,
0x20000, 0x2FFFD, 0x30000, 0x3FFFD,
-1,
};
+5 -2
View File
@@ -10,6 +10,8 @@ Array :: struct(T: typeid) {
allocator: mem.Allocator,
}
ARRAY_DEFAULT_CAPACITY :: 16;
/*
array_init :: proc {
array_init_none,
@@ -42,11 +44,12 @@ array_set_capacity
array_grow
*/
array_init_none :: proc(a: ^$A/Array, allocator := context.allocator) {
array_init_len(a, 0, allocator);
array_init_len_cap(a, 0, ARRAY_DEFAULT_CAPACITY, allocator);
}
array_init_len :: proc(a: ^$A/Array, len: int, allocator := context.allocator) {
array_init_len_cap(a, 0, 16, allocator);
array_init_len_cap(a, len, len, allocator);
}
array_init_len_cap :: proc(a: ^$A/Array($T), len: int, cap: int, allocator := context.allocator) {
a.allocator = allocator;
+52 -38
View File
@@ -1,14 +1,18 @@
package container
import "intrinsics"
_ :: intrinsics;
Map :: struct(Value: typeid) {
Map :: struct(Key, Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
hash: Array(int),
entries: Array(Map_Entry(Value)),
entries: Array(Map_Entry(Key, Value)),
}
Map_Entry :: struct(Value: typeid) {
key: u64,
Map_Entry :: struct(Key, Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
hash: uintptr,
next: int,
key: Key,
value: Value,
}
@@ -47,28 +51,28 @@ multi_map_remove_all
map_init :: proc{map_init_none, map_init_cap};
map_init_none :: proc(m: ^$M/Map($Value), allocator := context.allocator) {
map_init_none :: proc(m: ^$M/Map($Key, $Value), allocator := context.allocator) {
m.hash.allocator = allocator;
m.entries.allocator = allocator;
}
map_init_cap :: proc(m: ^$M/Map($Value), cap: int, allocator := context.allocator) {
map_init_cap :: proc(m: ^$M/Map($Key, $Value), cap: int, allocator := context.allocator) {
m.hash.allocator = allocator;
m.entries.allocator = allocator;
map_reserve(m, cap);
}
map_delete :: proc(m: $M/Map($Value)) {
map_delete :: proc(m: $M/Map($Key, $Value)) {
array_delete(m.hash);
array_delete(m.entries);
}
map_has :: proc(m: $M/Map($Value), key: u64) -> bool {
map_has :: proc(m: $M/Map($Key, $Value), key: Key) -> bool {
return _map_find_or_fail(m, key) >= 0;
}
map_get :: proc(m: $M/Map($Value), key: u64) -> (res: Value, ok: bool) #optional_ok {
map_get :: proc(m: $M/Map($Key, $Value), key: Key) -> (res: Value, ok: bool) #optional_ok {
i := _map_find_or_fail(m, key);
if i < 0 {
return {}, false;
@@ -76,7 +80,7 @@ map_get :: proc(m: $M/Map($Value), key: u64) -> (res: Value, ok: bool) #optional
return array_get(m.entries, i).value, true;
}
map_get_default :: proc(m: $M/Map($Value), key: u64, default: Value) -> (res: Value, ok: bool) #optional_ok {
map_get_default :: proc(m: $M/Map($Key, $Value), key: Key, default: Value) -> (res: Value, ok: bool) #optional_ok {
i := _map_find_or_fail(m, key);
if i < 0 {
return default, false;
@@ -84,7 +88,7 @@ map_get_default :: proc(m: $M/Map($Value), key: u64, default: Value) -> (res: Va
return array_get(m.entries, i).value, true;
}
map_get_ptr :: proc(m: $M/Map($Value), key: u64) -> ^Value {
map_get_ptr :: proc(m: $M/Map($Key, $Value), key: Key) -> ^Value {
i := _map_find_or_fail(m, key);
if i < 0 {
return nil;
@@ -92,7 +96,7 @@ map_get_ptr :: proc(m: $M/Map($Value), key: u64) -> ^Value {
return array_get_ptr(m.entries, i).value;
}
map_set :: proc(m: ^$M/Map($Value), key: u64, value: Value) {
map_set :: proc(m: ^$M/Map($Key, $Value), key: Key, value: Value) {
if array_len(m.hash) == 0 {
_map_grow(m);
}
@@ -104,7 +108,7 @@ map_set :: proc(m: ^$M/Map($Value), key: u64, value: Value) {
}
}
map_remove :: proc(m: ^$M/Map($Value), key: u64) {
map_remove :: proc(m: ^$M/Map($Key, $Value), key: Key) {
fr := _map_find_key(m^, key);
if fr.entry_index >= 0 {
_map_erase(m, fr);
@@ -112,7 +116,7 @@ map_remove :: proc(m: ^$M/Map($Value), key: u64) {
}
map_reserve :: proc(m: ^$M/Map($Value), new_size: int) {
map_reserve :: proc(m: ^$M/Map($Key, $Value), new_size: int) {
nm: M;
map_init(&nm, m.hash.allocator);
array_resize(&nm.hash, new_size);
@@ -130,14 +134,14 @@ map_reserve :: proc(m: ^$M/Map($Value), new_size: int) {
m^ = nm;
}
map_clear :: proc(m: ^$M/Map($Value)) {
map_clear :: proc(m: ^$M/Map($Key, $Value)) {
array_clear(&m.hash);
array_clear(&m.entries);
}
multi_map_find_first :: proc(m: $M/Map($Value), key: u64) -> ^Map_Entry(Value) {
multi_map_find_first :: proc(m: $M/Map($Key, $Value), key: Key) -> ^Map_Entry(Value) {
i := _map_find_or_fail(m, key);
if i < 0 {
return nil;
@@ -145,11 +149,11 @@ multi_map_find_first :: proc(m: $M/Map($Value), key: u64) -> ^Map_Entry(Value) {
return array_get_ptr(m.entries, i);
}
multi_map_find_next :: proc(m: $M/Map($Value), e: ^Map_Entry(Value)) -> ^Map_Entry(Value) {
multi_map_find_next :: proc(m: $M/Map($Key, $Value), e: ^Map_Entry(Value)) -> ^Map_Entry(Value) {
i := e.next;
for i >= 0 {
it := array_get_ptr(m.entries, i);
if it.key == e.key {
if it.hash == e.hash && it.key == e.key {
return it;
}
i = it.next;
@@ -157,7 +161,7 @@ multi_map_find_next :: proc(m: $M/Map($Value), e: ^Map_Entry(Value)) -> ^Map_Ent
return nil;
}
multi_map_count :: proc(m: $M/Map($Value), key: u64) -> int {
multi_map_count :: proc(m: $M/Map($Key, $Value), key: Key) -> int {
n := 0;
e := multi_map_find_first(m, key);
for e != nil {
@@ -169,8 +173,10 @@ multi_map_count :: proc(m: $M/Map($Value), key: u64) -> int {
multi_map_get :: proc{multi_map_get_array, multi_map_get_slice};
multi_map_get_array :: proc(m: $M/Map($Value), key: u64, items: ^Array(Value)) {
if items == nil do return;
multi_map_get_array :: proc(m: $M/Map($Key, $Value), key: Key, items: ^Array(Value)) {
if items == nil {
return;
}
e := multi_map_find_first(m, key);
for e != nil {
array_append(items, e.value);
@@ -178,7 +184,7 @@ multi_map_get_array :: proc(m: $M/Map($Value), key: u64, items: ^Array(Value)) {
}
}
multi_map_get_slice :: proc(m: $M/Map($Value), key: u64, items: []Value) {
multi_map_get_slice :: proc(m: $M/Map($Key, $Value), key: Key, items: []Value) {
e := multi_map_find_first(m, key);
i := 0;
for e != nil && i < len(items) {
@@ -188,7 +194,7 @@ multi_map_get_slice :: proc(m: $M/Map($Value), key: u64, items: []Value) {
}
}
multi_map_get_as_slice :: proc(m: $M/Map($Value), key: u64) -> []Value {
multi_map_get_as_slice :: proc(m: $M/Map($Key, $Value), key: Key) -> []Value {
items: Array(Value);
array_init(&items, 0);
@@ -202,7 +208,7 @@ multi_map_get_as_slice :: proc(m: $M/Map($Value), key: u64) -> []Value {
}
multi_map_insert :: proc(m: ^$M/Map($Value), key: u64, value: Value) {
multi_map_insert :: proc(m: ^$M/Map($Key, $Value), key: Key, value: Value) {
if array_len(m.hash) == 0 {
_map_grow(m);
}
@@ -214,14 +220,14 @@ multi_map_insert :: proc(m: ^$M/Map($Value), key: u64, value: Value) {
}
}
multi_map_remove :: proc(m: ^$M/Map($Value), e: ^Map_Entry(Value)) {
multi_map_remove :: proc(m: ^$M/Map($Key, $Value), e: ^Map_Entry(Value)) {
fr := _map_find_entry(m, e);
if fr.entry_index >= 0 {
_map_erase(m, fr);
}
}
multi_map_remove_all :: proc(m: ^$M/Map($Value), key: u64) {
multi_map_remove_all :: proc(m: ^$M/Map($Key, $Value), key: Key) {
for map_exist(m^, key) {
map_remove(m, key);
}
@@ -237,9 +243,12 @@ Map_Find_Result :: struct {
entry_index: int,
}
_map_add_entry :: proc(m: ^$M/Map($Value), key: u64) -> int {
e: Map_Entry(Value);
_map_add_entry :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int where intrinsics.type_is_valid_map_key(Key) {
hasher := intrinsics.type_hasher_proc(Key);
e: Map_Entry(Key, Value);
e.key = key;
e.hash = hasher(&e.key, 0);
e.next = -1;
idx := array_len(m.entries);
array_push(&m.entries, e);
@@ -269,7 +278,7 @@ _map_erase :: proc(m: ^$M/Map, fr: Map_Find_Result) {
}
_map_find_key :: proc(m: $M/Map($Value), key: u64) -> Map_Find_Result {
_map_find_key :: proc(m: $M/Map($Key, $Value), key: Key) -> Map_Find_Result where intrinsics.type_is_valid_map_key(Key) {
fr: Map_Find_Result;
fr.hash_index = -1;
fr.entry_prev = -1;
@@ -279,11 +288,16 @@ _map_find_key :: proc(m: $M/Map($Value), key: u64) -> Map_Find_Result {
return fr;
}
fr.hash_index = int(key % u64(array_len(m.hash)));
hasher := intrinsics.type_hasher_proc(Key);
key := key;
hash := hasher(&key, 0);
fr.hash_index = int(hash % uintptr(array_len(m.hash)));
fr.entry_index = array_get(m.hash, fr.hash_index);
for fr.entry_index >= 0 {
it := array_get_ptr(m.entries, fr.entry_index);
if it.key == key {
if it.hash == hash && it.key == key {
return fr;
}
fr.entry_prev = fr.entry_index;
@@ -292,7 +306,7 @@ _map_find_key :: proc(m: $M/Map($Value), key: u64) -> Map_Find_Result {
return fr;
}
_map_find_entry :: proc(m: ^$M/Map($Value), e: ^Map_Entry(Value)) -> Map_Find_Result {
_map_find_entry :: proc(m: ^$M/Map($Key, $Value), e: ^Map_Entry(Value)) -> Map_Find_Result {
fr: Map_Find_Result;
fr.hash_index = -1;
fr.entry_prev = -1;
@@ -302,7 +316,7 @@ _map_find_entry :: proc(m: ^$M/Map($Value), e: ^Map_Entry(Value)) -> Map_Find_Re
return fr;
}
fr.hash_index = int(e.key % u64(array_len(m.hash)));
fr.hash_index = int(e.hash % uintptr(array_len(m.hash)));
fr.entry_index = array_get(m.hash, fr.hash_index);
for fr.entry_index >= 0 {
it := array_get_ptr(m.entries, fr.entry_index);
@@ -315,10 +329,10 @@ _map_find_entry :: proc(m: ^$M/Map($Value), e: ^Map_Entry(Value)) -> Map_Find_Re
return fr;
}
_map_find_or_fail :: proc(m: $M/Map($Value), key: u64) -> int {
_map_find_or_fail :: proc(m: $M/Map($Key, $Value), key: Key) -> int {
return _map_find_key(m, key).entry_index;
}
_map_find_or_make :: proc(m: ^$M/Map($Value), key: u64) -> int {
_map_find_or_make :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int {
fr := _map_find_key(m^, key);
if fr.entry_index >= 0 {
return fr.entry_index;
@@ -334,7 +348,7 @@ _map_find_or_make :: proc(m: ^$M/Map($Value), key: u64) -> int {
}
_map_make :: proc(m: ^$M/Map($Value), key: u64) -> int {
_map_make :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int {
fr := _map_find_key(m^, key);
i := _map_add_entry(m, key);
@@ -350,12 +364,12 @@ _map_make :: proc(m: ^$M/Map($Value), key: u64) -> int {
}
_map_full :: proc(m: $M/Map($Value)) -> bool {
_map_full :: proc(m: $M/Map($Key, $Value)) -> bool {
// TODO(bill): Determine good max load factor
return array_len(m.entries) >= (array_len(m.hash) / 4)*3;
}
_map_grow :: proc(m: ^$M/Map($Value)) {
_map_grow :: proc(m: ^$M/Map($Key, $Value)) {
new_size := array_len(m.entries) * 4 + 7; // TODO(bill): Determine good grow rate
map_reserve(m, new_size);
}
+113
View File
@@ -0,0 +1,113 @@
package container
Priority_Queue :: struct(T: typeid) {
data: Array(T),
len: int,
priority: proc(item: T) -> int,
}
priority_queue_init_none :: proc(q: ^$Q/Priority_Queue($T), f: proc(item: T) -> int, allocator := context.allocator) {
queue_init_len(q, f, 0, allocator);
}
priority_queue_init_len :: proc(q: ^$Q/Priority_Queue($T), f: proc(item: T) -> int, len: int, allocator := context.allocator) {
queue_init_len_cap(q, f, 0, 16, allocator);
}
priority_queue_init_len_cap :: proc(q: ^$Q/Priority_Queue($T), f: proc(item: T) -> int, len: int, cap: int, allocator := context.allocator) {
array_init(&q.data, len, cap, allocator);
q.len = len;
q.priority = f;
}
priority_queue_init :: proc{priority_queue_init_none, priority_queue_init_len, priority_queue_init_len_cap};
priority_queue_delete :: proc(q: $Q/Priority_Queue($T)) {
array_delete(q.data);
}
priority_queue_clear :: proc(q: ^$Q/Priority_Queue($T)) {
q.len = 0;
}
priority_queue_len :: proc(q: $Q/Priority_Queue($T)) -> int {
return q.len;
}
priority_queue_cap :: proc(q: $Q/Priority_Queue($T)) -> int {
return array_cap(q.data);
}
priority_queue_space :: proc(q: $Q/Priority_Queue($T)) -> int {
return array_len(q.data) - q.len;
}
priority_queue_reserve :: proc(q: ^$Q/Priority_Queue($T), capacity: int) {
if capacity > q.len {
array_resize(&q.data, new_capacity);
}
}
priority_queue_resize :: proc(q: ^$Q/Priority_Queue($T), length: int) {
if length > q.len {
array_resize(&q.data, new_capacity);
}
q.len = length;
}
_priority_queue_grow :: proc(q: ^$Q/Priority_Queue($T), min_capacity: int = 0) {
new_capacity := max(array_len(q.data)*2 + 8, min_capacity);
array_resize(&q.data, new_capacity);
}
priority_queue_push :: proc(q: ^$Q/Priority_Queue($T), item: T) {
if array_len(q.data) - q.len == 0 {
_priority_queue_grow(q);
}
s := array_slice(q.data);
s[q.len] = item;
i := q.len;
for i > 0 {
p := (i - 1) / 2;
if q.priority(s[p]) <= q.priority(item) do break;
s[i] = s[p];
i = p;
}
q.len += 1;
if q.len > 0 do s[i] = item;
}
priority_queue_pop :: proc(q: ^$Q/Priority_Queue($T)) -> T {
assert(q.len > 0);
s := array_slice(q.data);
min := s[0];
root := s[q.len-1];
q.len -= 1;
i := 0;
for i * 2 + 1 < q.len {
a := i * 2 + 1;
b := i * 2 + 2;
c := b < q.len && q.priority(s[b]) < q.priority(s[a]) ? b : a;
if q.priority(s[c]) >= q.priority(root) do break;
s[i] = s[c];
i = c;
}
if q.len > 0 do s[i] = root;
return min;
}
priority_queue_peek :: proc(q: ^$Q/Priority_Queue($T)) -> T {
assert(q.len > 0);
s := array_slice(q.data);
return s[0];
}
+3
View File
@@ -115,6 +115,9 @@ queue_pop_front :: proc(q: ^$Q/Queue($T)) -> T {
item := queue_get(q^, 0);
q.offset = (q.offset + 1) % array_len(q.data);
q.len -= 1;
if q.len == 0 {
q.offset = 0;
}
return item;
}
+1 -1
View File
@@ -64,7 +64,7 @@ ring_len :: proc(r: ^$R/Ring) -> int {
n := 0;
if r != nil {
n = 1;
for p := ring_next(p); p != r; p = p.next {
for p := ring_next(&p); p != r; p = p.next {
n += 1;
}
}
+1 -1
View File
@@ -66,7 +66,7 @@ small_array_pop_back :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location
return item;
}
small_array_pop_font :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
small_array_pop_front :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
assert(condition=a.len > 0, loc=loc);
item := a.data[0];
s := small_array_slice(a);
+1 -1
View File
@@ -1,3 +1,3 @@
package dynlib
Library :: opaque rawptr;
Library :: distinct rawptr;
+5 -3
View File
@@ -1,11 +1,13 @@
// +build linux, darwin
// +build linux, darwin, freebsd
package dynlib
import "core:os"
load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
flags := os.RTLD_NOW;
if global_symbols do flags |= os.RTLD_GLOBAL;
if global_symbols {
flags |= os.RTLD_GLOBAL;
}
lib := os.dlopen(path, flags);
return Library(lib), lib != nil;
}
@@ -18,4 +20,4 @@ symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found:
ptr = os.dlsym(rawptr(library), symbol);
found = ptr != nil;
return;
}
}
+8 -5
View File
@@ -11,7 +11,7 @@ ENC_TABLE := [32]byte {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', '2', '3', '4', '5', '6', '7'
'Y', 'Z', '2', '3', '4', '5', '6', '7',
};
PADDING :: '=';
@@ -30,7 +30,7 @@ DEC_TABLE := [?]u8 {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string {
@@ -92,10 +92,11 @@ _encode :: proc(out, data: []byte, ENC_TBL := ENC_TABLE, allocator := context.al
}
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check{
if len(data) == 0 do return []byte{};
if len(data) == 0 {
return nil;
}
outi := 0;
olen := len(data);
data := data;
out := make([]byte, len(data) / 8 * 5, allocator);
@@ -113,7 +114,9 @@ decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocato
data = data[1:];
if input == byte(PADDING) && j >= 2 && len(data) < 8 {
assert(!(len(data) + j < 8 - 1), "Corrupted input");
for k := 0; k < 8-1-j; k +=1 do assert(len(data) < k || data[k] == byte(PADDING), "Corrupted input");
for k := 0; k < 8-1-j; k +=1 {
assert(len(data) < k || data[k] == byte(PADDING), "Corrupted input");
}
dlen, end = j, true;
assert(dlen != 1 && dlen != 3 && dlen != 6, "Corrupted input");
break;
+32 -28
View File
@@ -2,46 +2,48 @@ package base64
// @note(zh): Encoding utility for Base64
// A secondary param can be used to supply a custom alphabet to
// @link(encode) and a matching decoding table to @link(decode).
// @link(encode) and a matching decoding table to @link(decode).
// If none is supplied it just uses the standard Base64 alphabet.
// Incase your specific version does not use padding, you may
// truncate it from the encoded output.
ENC_TABLE := [64]byte {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/'
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/',
};
PADDING :: '=';
DEC_TABLE := [128]int {
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, 62, -1, -1, -1, 63,
52, 53, 54, 55, 56, 57, 58, 59,
60, 61, -1, -1, -1, -1, -1, -1,
-1, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, -1, -1, -1, -1, -1,
-1, 26, 27, 28, 29, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, -1, -1, -1, -1, -1
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, 62, -1, -1, -1, 63,
52, 53, 54, 55, 56, 57, 58, 59,
60, 61, -1, -1, -1, -1, -1, -1,
-1, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, -1, -1, -1, -1, -1,
-1, 26, 27, 28, 29, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, -1, -1, -1, -1, -1,
};
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string #no_bounds_check {
length := len(data);
if length == 0 do return "";
if length == 0 {
return "";
}
out_length := ((4 * length / 3) + 3) &~ 3;
out := make([]byte, out_length, allocator);
@@ -51,8 +53,8 @@ encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocato
for i, d := 0, 0; i < length; i, d = i + 3, d + 4 {
c0, c1, c2 = int(data[i]), -1, -1;
if i + 1 < length do c1 = int(data[i + 1]);
if i + 2 < length do c2 = int(data[i + 2]);
if i + 1 < length { c1 = int(data[i + 1]); }
if i + 2 < length { c2 = int(data[i + 2]); }
block = (c0 << 16) | (max(c1, 0) << 8) | max(c2, 0);
@@ -66,7 +68,9 @@ encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocato
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check {
length := len(data);
if length == 0 do return []byte{};
if length == 0 {
return nil;
}
pad_count := data[length - 1] == PADDING ? (data[length - 2] == PADDING ? 2 : 1) : 0;
out_length := ((length * 6) >> 3) - pad_count;
+35 -25
View File
@@ -32,7 +32,9 @@ Parser :: struct {
print_value :: proc(value: Value, pretty := true, indent := 0) {
print_indent :: proc(indent: int) {
for _ in 0..<indent do fmt.print("\t");
for _ in 0..<indent {
fmt.print("\t");
}
}
switch v in value {
@@ -42,22 +44,22 @@ print_value :: proc(value: Value, pretty := true, indent := 0) {
case string: fmt.print(v);
case Array:
fmt.print("[");
if pretty do fmt.println();
if pretty { fmt.println(); }
for e, i in v {
if pretty {
print_indent(indent+1);
print_value(e, pretty, indent+1);
fmt.println(",");
} else {
if i > 0 do fmt.print(", ");
if i > 0 { fmt.print(", "); }
print_value(e);
}
}
if pretty do print_indent(indent);
if pretty { print_indent(indent); }
fmt.print("]");
case Dict:
fmt.print("{");
if pretty do fmt.println();
if pretty { fmt.println(); }
i := 0;
for name, val in v {
@@ -67,14 +69,14 @@ print_value :: proc(value: Value, pretty := true, indent := 0) {
print_value(val, pretty, indent+1);
fmt.println(",");
} else {
if i > 0 do fmt.print(", ");
if i > 0 { fmt.print(", "); }
fmt.printf("%s = ", name);
print_value(val, pretty, indent+1);
i += 1;
}
}
if pretty do print_indent(indent);
if pretty { print_indent(indent); }
fmt.print("}");
case:
fmt.print("nil");
@@ -149,17 +151,23 @@ destroy :: proc(p: ^Parser) {
destroy_value :: proc(value: Value) {
#partial switch v in value {
case Array:
for elem in v do destroy_value(elem);
for elem in v {
destroy_value(elem);
}
delete(v);
case Dict:
for _, dv in v do destroy_value(dv);
for _, dv in v {
destroy_value(dv);
}
delete(v);
}
}
delete(p.tokens);
for s in p.allocated_strings do delete(s);
for s in p.allocated_strings {
delete(s);
}
delete(p.allocated_strings);
delete(p.dict_stack);
@@ -348,7 +356,9 @@ expect_token :: proc(p: ^Parser, kind: Kind) -> Token {
prev := p.curr_token;
if prev.kind != kind {
got := prev.lit;
if got == "\n" do got = ";";
if got == "\n" {
got = ";";
}
error(p, prev.pos, "Expected %s, got %s", kind_to_string[kind], got);
}
next_token(p);
@@ -411,7 +421,7 @@ parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
case .Ident:
next_token(p);
v, ok := lookup_value(p, tok.lit);
if !ok do error(p, tok.pos, "Undeclared identifier %s", tok.lit);
if !ok { error(p, tok.pos, "Undeclared identifier %s", tok.lit); }
return v, tok.pos;
case .True:
@@ -438,7 +448,7 @@ parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
case .String:
next_token(p);
str, ok := unquote_string(p, tok);
if !ok do error(p, tok.pos, "Unable to unquote string");
if !ok { error(p, tok.pos, "Unable to unquote string"); }
return string(str), tok.pos;
case .Open_Paren:
@@ -480,7 +490,7 @@ parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
}
name, ok := unquote_string(p, name_tok);
if !ok do error(p, tok.pos, "Unable to unquote string");
if !ok { error(p, tok.pos, "Unable to unquote string"); }
expect_token(p, .Assign);
elem, _ := parse_expr(p);
@@ -520,7 +530,7 @@ parse_atom_expr :: proc(p: ^Parser, operand: Value, pos: Pos) -> (Value, Pos) {
continue;
}
name, usok := unquote_string(p, tok);
if !usok do error(p, tok.pos, "Unable to unquote string");
if !usok { error(p, tok.pos, "Unable to unquote string"); }
val, found := d[name];
if !found {
error(p, tok.pos, "Field %s not found in dictionary", name);
@@ -594,7 +604,7 @@ parse_unary_expr :: proc(p: ^Parser) -> (Value, Pos) {
next_token(p);
tok := expect_token(p, .String);
v, ok := lookup_value(p, tok.lit);
if !ok do error(p, tok.pos, "Undeclared identifier %s", tok.lit);
if !ok { error(p, tok.pos, "Undeclared identifier %s", tok.lit); }
return parse_atom_expr(p, v, tok.pos);
case .Add, .Sub:
@@ -603,8 +613,8 @@ parse_unary_expr :: proc(p: ^Parser) -> (Value, Pos) {
expr, pos := parse_unary_expr(p);
#partial switch e in expr {
case i64: if op.kind == .Sub do return -e, pos;
case f64: if op.kind == .Sub do return -e, pos;
case i64: if op.kind == .Sub { return -e, pos; }
case f64: if op.kind == .Sub { return -e, pos; }
case:
error(p, op.pos, "Unary operator %s can only be used on integers or floats", op.lit);
return nil, op.pos;
@@ -667,9 +677,9 @@ match_values :: proc(left, right: ^Value) -> bool {
return false;
}
calculate_binary_value :: proc(p: ^Parser, op: Kind, a, b: Value) -> (Value, bool) {
calculate_binary_value :: proc(p: ^Parser, op: Kind, a_, b_: Value) -> (Value, bool) {
// TODO(bill): Calculate value as you go!
x, y := a, b;
x, y := a_, b_;
match_values(&x, &y);
@@ -678,7 +688,7 @@ calculate_binary_value :: proc(p: ^Parser, op: Kind, a, b: Value) -> (Value, boo
case bool:
b, ok := y.(bool);
if !ok do return nil, false;
if !ok { return nil, false; }
#partial switch op {
case .Eq: return a == b, true;
case .NotEq: return a != b, true;
@@ -688,7 +698,7 @@ calculate_binary_value :: proc(p: ^Parser, op: Kind, a, b: Value) -> (Value, boo
case i64:
b, ok := y.(i64);
if !ok do return nil, false;
if !ok { return nil, false; }
#partial switch op {
case .Add: return a + b, true;
case .Sub: return a - b, true;
@@ -705,7 +715,7 @@ calculate_binary_value :: proc(p: ^Parser, op: Kind, a, b: Value) -> (Value, boo
case f64:
b, ok := y.(f64);
if !ok do return nil, false;
if !ok { return nil, false; }
#partial switch op {
case .Add: return a + b, true;
@@ -722,7 +732,7 @@ calculate_binary_value :: proc(p: ^Parser, op: Kind, a, b: Value) -> (Value, boo
case string:
b, ok := y.(string);
if !ok do return nil, false;
if !ok { return nil, false; }
#partial switch op {
case .Add:
@@ -825,7 +835,7 @@ parse_assignment :: proc(p: ^Parser) -> bool {
if allow_token(p, .Ident) || allow_token(p, .String) {
expect_token(p, .Assign);
name, ok := unquote_string(p, tok);
if !ok do error(p, tok.pos, "Unable to unquote string");
if !ok { error(p, tok.pos, "Unable to unquote string"); }
expr, _ := parse_expr(p);
d := top_dict(p);
if _, ok2 := d[name]; ok2 {
+3 -3
View File
@@ -162,9 +162,9 @@ token_lookup :: proc(ident: string) -> Kind {
return Ident;
}
is_literal :: proc(tok: Kind) -> bool do return _literal_start < tok && tok < _literal_end;
is_operator :: proc(tok: Kind) -> bool do return _operator_start < tok && tok < _operator_end;
is_keyword :: proc(tok: Kind) -> bool do return _keyword_start < tok && tok < _keyword_end;
is_literal :: proc(tok: Kind) -> bool { return _literal_start < tok && tok < _literal_end; }
is_operator :: proc(tok: Kind) -> bool { return _operator_start < tok && tok < _operator_end; }
is_keyword :: proc(tok: Kind) -> bool { return _keyword_start < tok && tok < _keyword_end; }
tokenizer_init :: proc(t: ^Tokenizer, src: []byte, file := "") {
+406
View File
@@ -0,0 +1,406 @@
// package csv reads and writes comma-separated values (CSV) files.
// This package supports the format described in RFC 4180 <https://tools.ietf.org/html/rfc4180.html>
package csv
import "core:bufio"
import "core:bytes"
import "core:io"
import "core:strings"
import "core:unicode/utf8"
// Reader is a data structure used for reading records from a CSV-encoded file
//
// The associated procedures for Reader expects its input to conform to RFC 4180.
Reader :: struct {
// comma is the field delimiter
// reader_init will set it to be ','
// A "comma" must be a valid rune, nor can it be \r, \n, or the Unicode replacement character (0xfffd)
comma: rune,
// comment, if not 0, is the comment character
// Lines beginning with the comment character without a preceding whitespace are ignored
comment: rune,
// fields_per_record is the number of expected fields per record
// if fields_per_record is >0, 'read' requires each record to have that field count
// if fields_per_record is 0, 'read' sets it to the field count in the first record
// if fields_per_record is <0, no check is made and records may have a variable field count
fields_per_record: int,
// If trim_leading_space is true, leading whitespace in a field is ignored
// This is done even if the field delimiter (comma), is whitespace
trim_leading_space: bool,
// If lazy_quotes is true, a quote may appear in an unquoted field and a non-doubled quote may appear in a quoted field
lazy_quotes: bool,
// reuse_record controls whether calls to 'read' may return a slice using the backing buffer
// for performance
// By default, each call to 'read' returns a newly allocated slice
reuse_record: bool,
// reuse_record_buffer controls whether calls to 'read' clone the strings of each field or uses
// the data stored in record buffer for performance
// By default, each call to 'read' clones the strings of each field
reuse_record_buffer: bool,
// internal buffers
r: bufio.Reader,
line_count: int, // current line being read in the CSV file
raw_buffer: [dynamic]byte,
record_buffer: [dynamic]byte,
field_indices: [dynamic]int,
last_record: [dynamic]string,
sr: strings.Reader, // used by reader_init_with_string
}
Reader_Error_Kind :: enum {
Bare_Quote,
Quote,
Field_Count,
Invalid_Delim,
}
reader_error_kind_string := [Reader_Error_Kind]string{
.Bare_Quote = "bare \" in non-quoted field",
.Quote = "extra or missing \" in quoted field",
.Field_Count = "wrong field count",
.Invalid_Delim = "invalid delimiter",
};
Reader_Error :: struct {
kind: Reader_Error_Kind,
start_line: int,
line: int,
column: int,
expected, got: int, // used by .Field_Count
}
Error :: union {
Reader_Error,
io.Error,
}
DEFAULT_RECORD_BUFFER_CAPACITY :: 256;
// reader_init initializes a new Reader from r
reader_init :: proc(reader: ^Reader, r: io.Reader, buffer_allocator := context.allocator) {
reader.comma = ',';
context.allocator = buffer_allocator;
reserve(&reader.record_buffer, DEFAULT_RECORD_BUFFER_CAPACITY);
reserve(&reader.raw_buffer, 0);
reserve(&reader.field_indices, 0);
reserve(&reader.last_record, 0);
bufio.reader_init(&reader.r, r);
}
// reader_init_with_string initializes a new Reader from s
reader_init_with_string :: proc(reader: ^Reader, s: string, buffer_allocator := context.allocator) {
strings.reader_init(&reader.sr, s);
r, _ := io.to_reader(strings.reader_to_stream(&reader.sr));
reader_init(reader, r, buffer_allocator);
}
// reader_destroy destroys a Reader
reader_destroy :: proc(r: ^Reader) {
delete(r.raw_buffer);
delete(r.record_buffer);
delete(r.field_indices);
delete(r.last_record);
bufio.reader_destroy(&r.r);
}
// read reads a single record (a slice of fields) from r
//
// All \r\n sequences are normalized to \n, including multi-line field
read :: proc(r: ^Reader, allocator := context.allocator) -> (record: []string, err: Error) {
if r.reuse_record {
record, err = _read_record(r, &r.last_record, allocator);
resize(&r.last_record, len(record));
copy(r.last_record[:], record);
} else {
record, err = _read_record(r, nil, allocator);
}
return;
}
// is_io_error checks where an Error is a specific io.Error kind
is_io_error :: proc(err: Error, io_err: io.Error) -> bool {
if v, ok := err.(io.Error); ok {
return v == io_err;
}
return false;
}
// read_all reads all the remaining records from r.
// Each record is a slice of fields.
// read_all is defined to read until an EOF, and does not treat, and does not treat EOF as an error
read_all :: proc(r: ^Reader, allocator := context.allocator) -> ([][]string, Error) {
context.allocator = allocator;
records: [dynamic][]string;
for {
record, rerr := _read_record(r, nil, allocator);
if is_io_error(rerr, .EOF) {
return records[:], nil;
}
if rerr != nil {
return nil, rerr;
}
append(&records, record);
}
}
// read reads a single record (a slice of fields) from the provided input.
read_from_string :: proc(input: string, record_allocator := context.allocator, buffer_allocator := context.allocator) -> (record: []string, n: int, err: Error) {
ir: strings.Reader;
strings.reader_init(&ir, input);
input_reader, _ := io.to_reader(strings.reader_to_stream(&ir));
r: Reader;
reader_init(&r, input_reader, buffer_allocator);
defer reader_destroy(&r);
record, err = read(&r, record_allocator);
n = int(r.r.r);
return;
}
// read_all reads all the remaining records from the provided input.
read_all_from_string :: proc(input: string, records_allocator := context.allocator, buffer_allocator := context.allocator) -> ([][]string, Error) {
ir: strings.Reader;
strings.reader_init(&ir, input);
input_reader, _ := io.to_reader(strings.reader_to_stream(&ir));
r: Reader;
reader_init(&r, input_reader, buffer_allocator);
defer reader_destroy(&r);
return read_all(&r, records_allocator);
}
@private
is_valid_delim :: proc(r: rune) -> bool {
switch r {
case 0, '"', '\r', '\n', utf8.RUNE_ERROR:
return false;
}
return utf8.valid_rune(r);
}
@private
_read_record :: proc(r: ^Reader, dst: ^[dynamic]string, allocator := context.allocator) -> ([]string, Error) {
read_line :: proc(r: ^Reader) -> ([]byte, io.Error) {
line, err := bufio.reader_read_slice(&r.r, '\n');
if err == .Buffer_Full {
clear(&r.raw_buffer);
append(&r.raw_buffer, ..line);
for err == .Buffer_Full {
line, err = bufio.reader_read_slice(&r.r, '\n');
append(&r.raw_buffer, ..line);
}
line = r.raw_buffer[:];
}
if len(line) > 0 && err == .EOF {
err = nil;
if line[len(line)-1] == '\r' {
line = line[:len(line)-1];
}
}
r.line_count += 1;
// normalize \r\n to \n
n := len(line);
for n >= 2 && string(line[n-2:]) == "\r\n" {
line[n-2] = '\n';
line = line[:n-1];
}
return line, err;
}
length_newline :: proc(b: []byte) -> int {
if len(b) > 0 && b[len(b)-1] == '\n' {
return 1;
}
return 0;
}
next_rune :: proc(b: []byte) -> rune {
r, _ := utf8.decode_rune(b);
return r;
}
if r.comma == r.comment ||
!is_valid_delim(r.comma) ||
(r.comment != 0 && !is_valid_delim(r.comment)) {
err := Reader_Error{
kind = .Invalid_Delim,
line = r.line_count,
};
return nil, err;
}
line, full_line: []byte;
err_read: io.Error;
for err_read == nil {
line, err_read = read_line(r);
if r.comment != 0 && next_rune(line) == r.comment {
line = nil;
continue;
}
if err_read == nil && len(line) == length_newline(line) {
line = nil;
continue;
}
full_line = line;
break;
}
if is_io_error(err_read, .EOF) {
return nil, err_read;
}
err: Error;
quote_len :: len(`"`);
comma_len := utf8.rune_size(r.comma);
record_line := r.line_count;
clear(&r.record_buffer);
clear(&r.field_indices);
parse_field: for {
if r.trim_leading_space {
line = bytes.trim_left_space(line);
}
if len(line) == 0 || line[0] != '"' {
i := bytes.index_rune(line, r.comma);
field := line;
if i >= 0 {
field = field[:i];
} else {
field = field[:len(field) - length_newline(field)];
}
if !r.lazy_quotes {
if j := bytes.index_byte(field, '"'); j >= 0 {
column := utf8.rune_count(full_line[:len(full_line) - len(line[j:])]);
err = Reader_Error{
kind = .Bare_Quote,
start_line = record_line,
line = r.line_count,
column = column,
};
break parse_field;
}
}
append(&r.record_buffer, ..field);
append(&r.field_indices, len(r.record_buffer));
if i >= 0 {
line = line[i+comma_len:];
continue parse_field;
}
break parse_field;
} else {
line = line[quote_len:];
for {
i := bytes.index_byte(line, '"');
switch {
case i >= 0:
append(&r.record_buffer, ..line[:i]);
line = line[i+quote_len:];
switch ch := next_rune(line); {
case ch == '"': // append quote
append(&r.record_buffer, '"');
line = line[quote_len:];
case ch == r.comma: // end of field
line = line[comma_len:];
append(&r.field_indices, len(r.record_buffer));
continue parse_field;
case length_newline(line) == len(line): // end of line
append(&r.field_indices, len(r.record_buffer));
break parse_field;
case r.lazy_quotes: // bare quote
append(&r.record_buffer, '"');
case: // invalid non-escaped quote
column := utf8.rune_count(full_line[:len(full_line) - len(line) - quote_len]);
err = Reader_Error{
kind = .Quote,
start_line = record_line,
line = r.line_count,
column = column,
};
break parse_field;
}
case len(line) > 0:
append(&r.record_buffer, ..line);
if err_read != nil {
break parse_field;
}
line, err_read = read_line(r);
if is_io_error(err_read, .EOF) {
err_read = nil;
}
full_line = line;
case:
if !r.lazy_quotes && err_read == nil {
column := utf8.rune_count(full_line);
err = Reader_Error{
kind = .Quote,
start_line = record_line,
line = r.line_count,
column = column,
};
break parse_field;
}
append(&r.field_indices, len(r.record_buffer));
break parse_field;
}
}
}
}
if err == nil && err_read != nil {
err = err_read;
}
context.allocator = allocator;
dst := dst;
str := string(r.record_buffer[:]);
if dst == nil {
// use local variable
dst = &([dynamic]string){};
}
clear(dst);
resize(dst, len(r.field_indices));
pre_idx: int;
for idx, i in r.field_indices {
field := str[pre_idx:idx];
if !r.reuse_record_buffer {
field = strings.clone(field);
}
dst[i] = field;
pre_idx = idx;
}
if r.fields_per_record > 0 {
if len(dst) != r.fields_per_record && err == nil {
err = Reader_Error{
kind = .Field_Count,
start_line = record_line,
line = r.line_count,
expected = r.fields_per_record,
got = len(dst),
};
}
} else if r.fields_per_record == 0 {
r.fields_per_record = len(dst);
}
return dst[:], err;
}
+147
View File
@@ -0,0 +1,147 @@
package csv
import "core:io"
import "core:strings"
import "core:unicode/utf8"
// Writer is a data structure used for writing records using a CSV-encoding.
Writer :: struct {
// Field delimiter (set to ',' with writer_init)
comma: rune,
// if set to true, \r\n will be used as the line terminator
use_crlf: bool,
w: io.Writer,
}
// writer_init initializes a Writer that writes to w
writer_init :: proc(writer: ^Writer, w: io.Writer) {
writer.comma = ',';
writer.w = w;
}
// write writes a single CSV records to w with any of the necessarily quoting.
// A record is a slice of strings, where each string is a single field.
//
// If the underlying io.Writer requires flushing, make sure to call io.flush
write :: proc(w: ^Writer, record: []string) -> io.Error {
CHAR_SET :: "\n\r\"";
field_needs_quoting :: proc(w: ^Writer, field: string) -> bool {
switch {
case field == "": // No need to quote empty strings
return false;
case field == `\.`: // Postgres is weird
return true;
case w.comma < utf8.RUNE_SELF: // ASCII optimization
for i in 0..<len(field) {
switch field[i] {
case '\n', '\r', '"', byte(w.comma):
return true;
}
}
case:
if strings.contains_rune(field, w.comma) >= 0 {
return true;
}
if strings.contains_any(field, CHAR_SET) {
return true;
}
}
// Leading spaces need quoting
r, _ := utf8.decode_rune_in_string(field);
return strings.is_space(r);
}
if !is_valid_delim(w.comma) {
return .No_Progress; // TODO(bill): Is this a good error?
}
for _, field_idx in record {
// NOTE(bill): declared like this so that the field can be modified later if necessary
field := record[field_idx];
if field_idx > 0 {
if _, err := io.write_rune(w.w, w.comma); err != nil {
return err;
}
}
if !field_needs_quoting(w, field) {
if _, err := io.write_string(w.w, field); err != nil {
return err;
}
continue;
}
if err := io.write_byte(w.w, '"'); err != nil {
return err;
}
for len(field) > 0 {
i := strings.index_any(field, CHAR_SET);
if i < 0 {
i = len(field);
}
if _, err := io.write_string(w.w, field[:i]); err != nil {
return err;
}
field = field[i:];
if len(field) > 0 {
switch field[0] {
case '\r':
if !w.use_crlf {
if err := io.write_byte(w.w, '\r'); err != nil {
return err;
}
}
case '\n':
if w.use_crlf {
if _, err := io.write_string(w.w, "\r\n"); err != nil {
return err;
}
} else {
if err := io.write_byte(w.w, '\n'); err != nil {
return err;
}
}
case '"':
if _, err := io.write_string(w.w, `""`); err != nil {
return err;
}
}
field = field[1:];
}
}
if err := io.write_byte(w.w, '"'); err != nil {
return err;
}
}
if w.use_crlf {
_, err := io.write_string(w.w, "\r\n");
return err;
}
return io.write_byte(w.w, '\n');
}
// write_all writes multiple CSV records to w using write, and then flushes (if necessary).
write_all :: proc(w: ^Writer, records: [][]string) -> io.Error {
for record in records {
err := write(w, record);
if err != nil {
return err;
}
}
return writer_flush(w);
}
// writer_flush flushes the underlying io.Writer.
// If the underlying io.Writer does not support flush, nil is returned.
writer_flush :: proc(w: ^Writer) -> io.Error {
return io.flush(auto_cast w.w);
}
+22 -52
View File
@@ -5,7 +5,6 @@ import "core:math/bits"
import "core:runtime"
import "core:strconv"
import "core:strings"
import "core:reflect"
Marshal_Error :: enum {
None,
@@ -14,7 +13,8 @@ Marshal_Error :: enum {
}
marshal :: proc(v: any, allocator := context.allocator) -> ([]byte, Marshal_Error) {
b := strings.make_builder(allocator);
b: strings.Builder;
strings.init_builder(&b, allocator);
err := marshal_arg(&b, v);
@@ -89,6 +89,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
case Type_Info_Float:
val: f64;
switch f in a {
case f16: val = f64(f);
case f32: val = f64(f);
case f64: val = f64(f);
}
@@ -129,7 +130,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
case b32: val = bool(b);
case b64: val = bool(b);
}
write_string(b, val ? "true" : "false");
write_string_builder(b, val ? "true" : "false");
case Type_Info_Any:
return .Unsupported_Type;
@@ -161,7 +162,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
case Type_Info_Array:
write_byte(b, '[');
for i in 0..<info.count {
if i > 0 do write_string(b, ", ");
if i > 0 { write_string(b, ", "); }
data := uintptr(v.data) + uintptr(i*info.elem_size);
marshal_arg(b, any{rawptr(data), info.elem.id});
@@ -172,7 +173,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
write_byte(b, '[');
array := cast(^mem.Raw_Dynamic_Array)v.data;
for i in 0..<array.len {
if i > 0 do write_string(b, ", ");
if i > 0 { write_string(b, ", "); }
data := uintptr(array.data) + uintptr(i*info.elem_size);
marshal_arg(b, any{rawptr(data), info.elem.id});
@@ -183,7 +184,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
write_byte(b, '[');
slice := cast(^mem.Raw_Slice)v.data;
for i in 0..<slice.len {
if i > 0 do write_string(b, ", ");
if i > 0 { write_string(b, ", "); }
data := uintptr(slice.data) + uintptr(i*info.elem_size);
marshal_arg(b, any{rawptr(data), info.elem.id});
@@ -205,21 +206,15 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
entry_size := ed.elem_size;
for i in 0..<entries.len {
if i > 0 do write_string(b, ", ");
if i > 0 { write_string(b, ", "); }
data := uintptr(entries.data) + uintptr(i*entry_size);
header := cast(^Map_Entry_Header)data;
if reflect.is_string(info.key) {
marshal_arg(b, header.key.str);
} else {
marshal_arg(b, any{rawptr(&header.key.hash), info.key.id});
}
key := rawptr(data + entry_type.offsets[2]);
value := rawptr(data + entry_type.offsets[3]);
marshal_arg(b, any{key, info.key.id});
write_string(b, ": ");
value := data + entry_type.offsets[2];
marshal_arg(b, any{rawptr(value), info.value.id});
marshal_arg(b, any{value, info.value.id});
}
}
write_byte(b, '}');
@@ -227,7 +222,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
case Type_Info_Struct:
write_byte(b, '{');
for name, i in info.names {
if i > 0 do write_string(b, ", ");
if i > 0 { write_string(b, ", "); }
write_quoted_string(b, name);
write_string(b, ": ");
@@ -264,34 +259,6 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
case Type_Info_Enum:
return marshal_arg(b, any{v.data, info.base.id});
case Type_Info_Bit_Field:
data: u64 = 0;
switch ti.size {
case 1: data = cast(u64) (^u8)(v.data)^;
case 2: data = cast(u64)(^u16)(v.data)^;
case 4: data = cast(u64)(^u32)(v.data)^;
case 8: data = cast(u64)(^u64)(v.data)^;
}
write_byte(b, '{');
for name, i in info.names {
if i > 0 do write_string(b, ", ");
bits := u64(info.bits[i]);
offset := u64(info.offsets[i]);
marshal_arg(b, name);
write_string(b, ": ");
n := 8*u64(size_of(u64));
sa := n - bits;
u := data>>offset;
u <<= sa;
u >>= sa;
write_u64(b, u, 10);
}
write_byte(b, '}');
case Type_Info_Bit_Set:
is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool {
if ti == nil {
@@ -321,24 +288,27 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
bit_data = u64(x);
case 16:
x := (^u16)(v.data)^;
if do_byte_swap do x = bits.byte_swap(x);
if do_byte_swap {
x = bits.byte_swap(x);
}
bit_data = u64(x);
case 32:
x := (^u32)(v.data)^;
if do_byte_swap do x = bits.byte_swap(x);
if do_byte_swap {
x = bits.byte_swap(x);
}
bit_data = u64(x);
case 64:
x := (^u64)(v.data)^;
if do_byte_swap do x = bits.byte_swap(x);
if do_byte_swap {
x = bits.byte_swap(x);
}
bit_data = u64(x);
case: panic("unknown bit_size size");
}
write_u64(b, bit_data);
return .Unsupported_Type;
case Type_Info_Opaque:
return .Unsupported_Type;
}
+5 -4
View File
@@ -11,11 +11,12 @@ Parser :: struct {
spec: Specification,
allocator: mem.Allocator,
unmarshal_data: any,
parse_integers: bool,
}
make_parser :: proc(data: []byte, spec := Specification.JSON, allocator := context.allocator) -> Parser {
make_parser :: proc(data: []byte, spec := Specification.JSON, parse_integers := false, allocator := context.allocator) -> Parser {
p: Parser;
p.tok = make_tokenizer(data, spec);
p.tok = make_tokenizer(data, spec, parse_integers);
p.spec = spec;
p.allocator = allocator;
assert(p.allocator.procedure != nil);
@@ -23,9 +24,9 @@ make_parser :: proc(data: []byte, spec := Specification.JSON, allocator := conte
return p;
}
parse :: proc(data: []byte, spec := Specification.JSON, allocator := context.allocator) -> (Value, Error) {
parse :: proc(data: []byte, spec := Specification.JSON, parse_integers := false, allocator := context.allocator) -> (Value, Error) {
context.allocator = allocator;
p := make_parser(data, spec, allocator);
p := make_parser(data, spec, parse_integers, allocator);
if p.spec == Specification.JSON5 {
return parse_value(&p);
+18 -9
View File
@@ -42,12 +42,13 @@ Tokenizer :: struct {
w: int, // current rune width in bytes
curr_line_offset: int,
spec: Specification,
parse_integers: bool,
}
make_tokenizer :: proc(data: []byte, spec := Specification.JSON) -> Tokenizer {
t := Tokenizer{pos = {line=1}, data = data, spec = spec};
make_tokenizer :: proc(data: []byte, spec := Specification.JSON, parse_integers := false) -> Tokenizer {
t := Tokenizer{pos = {line=1}, data = data, spec = spec, parse_integers = parse_integers};
next_rune(&t);
if t.r == utf8.RUNE_BOM {
next_rune(&t);
@@ -182,9 +183,11 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
case "false": token.kind = .False;
case "true": token.kind = .True;
case:
if t.spec == .JSON5 do switch str {
case "Infinity": token.kind = .Infinity;
case "NaN": token.kind = .NaN;
if t.spec == .JSON5 {
switch str {
case "Infinity": token.kind = .Infinity;
case "NaN": token.kind = .NaN;
}
}
}
@@ -217,7 +220,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
fallthrough;
case '0'..'9':
token.kind = .Integer;
token.kind = t.parse_integers ? .Integer : .Float;
if t.spec == .JSON5 { // Hexadecimal Numbers
if curr_rune == '0' && (t.r == 'x' || t.r == 'X') {
next_rune(t);
@@ -360,7 +363,9 @@ is_valid_number :: proc(str: string, spec: Specification) -> bool {
s = s[1:];
case '1'..'9':
s = s[1:];
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' do s = s[1:];
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
s = s[1:];
}
case '.':
if spec == .JSON5 { // Allow leading decimal point
s = s[1:];
@@ -379,7 +384,9 @@ is_valid_number :: proc(str: string, spec: Specification) -> bool {
if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' {
s = s[2:];
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' do s = s[1:];
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
s = s[1:];
}
}
if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {
@@ -391,7 +398,9 @@ is_valid_number :: proc(str: string, spec: Specification) -> bool {
return false;
}
}
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' do s = s[1:];
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
s = s[1:];
}
}
// The string should be empty now to be valid
+4 -2
View File
@@ -24,7 +24,7 @@ Value :: struct {
String,
Array,
Object,
}
},
}
Pos :: struct {
@@ -65,7 +65,9 @@ destroy_value :: proc(value: Value) {
}
delete(v);
case Array:
for elem in v do destroy_value(elem);
for elem in v {
destroy_value(elem);
}
delete(v);
case String:
delete(v);
+2 -2
View File
@@ -3,8 +3,8 @@ package json
import "core:mem"
// NOTE(bill): is_valid will not check for duplicate keys
is_valid :: proc(data: []byte, spec := Specification.JSON) -> bool {
p := make_parser(data, spec, mem.nil_allocator());
is_valid :: proc(data: []byte, spec := Specification.JSON, parse_integers := false) -> bool {
p := make_parser(data, spec, parse_integers, mem.nil_allocator());
if p.spec == Specification.JSON5 {
return validate_value(&p);
}
+590 -448
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -1,14 +1,14 @@
package hash
crc32 :: proc(data: []byte) -> u32 {
result := ~u32(0);
crc32 :: proc(data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
result := ~u32(seed);
for b in data {
result = result>>8 ~ _crc32_table[(result ~ u32(b)) & 0xff];
}
return ~result;
}
crc64 :: proc(data: []byte) -> u64 {
result := ~u64(0);
crc64 :: proc(data: []byte, seed := u32(0)) -> u64 #no_bounds_check {
result := ~u64(seed);
for b in data {
result = result>>8 ~ _crc64_table[(result ~ u64(b)) & 0xff];
}
+2 -2
View File
@@ -2,9 +2,9 @@ package hash
import "core:mem"
adler32 :: proc(data: []byte) -> u32 {
adler32 :: proc(data: []byte, seed := u32(1)) -> u32 {
ADLER_CONST :: 65521;
a, b: u32 = 1, 0;
a, b: u32 = seed & 0xFFFF, seed >> 16;
for x in data {
a = (a + u32(x)) % ADLER_CONST;
b = (b + a) % ADLER_CONST;
+40
View File
@@ -0,0 +1,40 @@
package hash
ginger_hash8 :: proc(x: u8) -> u8 {
h := x * 251;
h += ~(x << 3);
h ~= (x >> 1);
h += ~(x << 7);
h ~= (x >> 6);
h += (x << 2);
return h;
}
ginger_hash16 :: proc(x: u16) -> u16 {
z := (x << 8) | (x >> 8);
h := z;
h += ~(z << 5);
h ~= (z >> 2);
h += ~(z << 13);
h ~= (z >> 10);
h += ~(z << 4);
h = (h << 10) | (h >> 10);
return h;
}
ginger8 :: proc(data: []byte) -> u8 {
h := ginger_hash8(0);
for b in data {
h ~= ginger_hash8(b);
}
return h;
}
ginger16 :: proc(data: []byte) -> u16 {
h := ginger_hash16(0);
for b in data {
h ~= ginger_hash16(u16(b));
}
return h;
}
+63 -49
View File
@@ -1,67 +1,71 @@
// This is purely for documentation
//+ignore
package intrinsics
// Types
x86_mmx :: x86_mmx; // Specialized SIMD Vector type
simd_vector :: proc($N: int, $T: typeid) -> type/#simd[N]T
soa_struct :: proc($N: int, $T: typeid) -> type/#soa[N]T
// Volatile
volatile_load :: proc(dst: ^$T) -> T ---
volatile_store :: proc(dst: ^$T, val: T) -> T ---
// Trapping
debug_trap :: proc() ---
trap :: proc() -> ! ---
// Atomics
atomic_fence :: proc() ---
atomic_fence_acq :: proc() ---
atomic_fence_rel :: proc() ---
atomic_fence_acqrel :: proc() ---
atomic_store :: proc(dst: ^$T, val: $T) ---
atomic_store_rel :: proc(dst: ^$T, val: $T) ---
atomic_store_relaxed :: proc(dst: ^$T, val: $T) ---
atomic_store_unordered :: proc(dst: ^$T, val: $T) ---
atomic_store :: proc(dst: ^$T, val: T) ---
atomic_store_rel :: proc(dst: ^$T, val: T) ---
atomic_store_relaxed :: proc(dst: ^$T, val: T) ---
atomic_store_unordered :: proc(dst: ^$T, val: T) ---
atomic_load :: proc(dst: ^$T) -> T ---
atomic_load_acq :: proc(dst: ^$T) -> T ---
atomic_load_relaxed :: proc(dst: ^$T) -> T ---
atomic_load_unordered :: proc(dst: ^$T) -> T ---
atomic_add :: proc(dst; ^$T, val: $T) -> T ---
atomic_add_acq :: proc(dst; ^$T, val: $T) -> T ---
atomic_add_rel :: proc(dst; ^$T, val: $T) -> T ---
atomic_add_acqrel :: proc(dst; ^$T, val: $T) -> T ---
atomic_add_relaxed :: proc(dst; ^$T, val: $T) -> T ---
atomic_sub :: proc(dst; ^$T, val: $T) -> T ---
atomic_sub_acq :: proc(dst; ^$T, val: $T) -> T ---
atomic_sub_rel :: proc(dst; ^$T, val: $T) -> T ---
atomic_sub_acqrel :: proc(dst; ^$T, val: $T) -> T ---
atomic_sub_relaxed :: proc(dst; ^$T, val: $T) -> T ---
atomic_and :: proc(dst; ^$T, val: $T) -> T ---
atomic_and_acq :: proc(dst; ^$T, val: $T) -> T ---
atomic_and_rel :: proc(dst; ^$T, val: $T) -> T ---
atomic_and_acqrel :: proc(dst; ^$T, val: $T) -> T ---
atomic_and_relaxed :: proc(dst; ^$T, val: $T) -> T ---
atomic_nand :: proc(dst; ^$T, val: $T) -> T ---
atomic_nand_acq :: proc(dst; ^$T, val: $T) -> T ---
atomic_nand_rel :: proc(dst; ^$T, val: $T) -> T ---
atomic_nand_acqrel :: proc(dst; ^$T, val: $T) -> T ---
atomic_nand_relaxed :: proc(dst; ^$T, val: $T) -> T ---
atomic_or :: proc(dst; ^$T, val: $T) -> T ---
atomic_or_acq :: proc(dst; ^$T, val: $T) -> T ---
atomic_or_rel :: proc(dst; ^$T, val: $T) -> T ---
atomic_or_acqrel :: proc(dst; ^$T, val: $T) -> T ---
atomic_or_relaxed :: proc(dst; ^$T, val: $T) -> T ---
atomic_xor :: proc(dst; ^$T, val: $T) -> T ---
atomic_xor_acq :: proc(dst; ^$T, val: $T) -> T ---
atomic_xor_rel :: proc(dst; ^$T, val: $T) -> T ---
atomic_xor_acqrel :: proc(dst; ^$T, val: $T) -> T ---
atomic_xor_relaxed :: proc(dst; ^$T, val: $T) -> T ---
atomic_add :: proc(dst; ^$T, val: T) -> T ---
atomic_add_acq :: proc(dst; ^$T, val: T) -> T ---
atomic_add_rel :: proc(dst; ^$T, val: T) -> T ---
atomic_add_acqrel :: proc(dst; ^$T, val: T) -> T ---
atomic_add_relaxed :: proc(dst; ^$T, val: T) -> T ---
atomic_sub :: proc(dst; ^$T, val: T) -> T ---
atomic_sub_acq :: proc(dst; ^$T, val: T) -> T ---
atomic_sub_rel :: proc(dst; ^$T, val: T) -> T ---
atomic_sub_acqrel :: proc(dst; ^$T, val: T) -> T ---
atomic_sub_relaxed :: proc(dst; ^$T, val: T) -> T ---
atomic_and :: proc(dst; ^$T, val: T) -> T ---
atomic_and_acq :: proc(dst; ^$T, val: T) -> T ---
atomic_and_rel :: proc(dst; ^$T, val: T) -> T ---
atomic_and_acqrel :: proc(dst; ^$T, val: T) -> T ---
atomic_and_relaxed :: proc(dst; ^$T, val: T) -> T ---
atomic_nand :: proc(dst; ^$T, val: T) -> T ---
atomic_nand_acq :: proc(dst; ^$T, val: T) -> T ---
atomic_nand_rel :: proc(dst; ^$T, val: T) -> T ---
atomic_nand_acqrel :: proc(dst; ^$T, val: T) -> T ---
atomic_nand_relaxed :: proc(dst; ^$T, val: T) -> T ---
atomic_or :: proc(dst; ^$T, val: T) -> T ---
atomic_or_acq :: proc(dst; ^$T, val: T) -> T ---
atomic_or_rel :: proc(dst; ^$T, val: T) -> T ---
atomic_or_acqrel :: proc(dst; ^$T, val: T) -> T ---
atomic_or_relaxed :: proc(dst; ^$T, val: T) -> T ---
atomic_xor :: proc(dst; ^$T, val: T) -> T ---
atomic_xor_acq :: proc(dst; ^$T, val: T) -> T ---
atomic_xor_rel :: proc(dst; ^$T, val: T) -> T ---
atomic_xor_acqrel :: proc(dst; ^$T, val: T) -> T ---
atomic_xor_relaxed :: proc(dst; ^$T, val: T) -> T ---
atomic_xchg :: proc(dst; ^$T, val: $T) -> T ---
atomic_xchg_acq :: proc(dst; ^$T, val: $T) -> T ---
atomic_xchg_rel :: proc(dst; ^$T, val: $T) -> T ---
atomic_xchg_acqrel :: proc(dst; ^$T, val: $T) -> T ---
atomic_xchg_relaxed :: proc(dst; ^$T, val: $T) -> T ---
atomic_xchg :: proc(dst; ^$T, val: T) -> T ---
atomic_xchg_acq :: proc(dst; ^$T, val: T) -> T ---
atomic_xchg_rel :: proc(dst; ^$T, val: T) -> T ---
atomic_xchg_acqrel :: proc(dst; ^$T, val: T) -> T ---
atomic_xchg_relaxed :: proc(dst; ^$T, val: T) -> T ---
atomic_cxchg :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchg_acq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
@@ -85,9 +89,14 @@ atomic_cxchgweak_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*opti
// Instructions
alloca :: proc(size, align: int) -> ^u8 ---
alloca :: proc(size, align: int) -> ^u8 ---
cpu_relax :: proc() ---
read_cycle_counter :: proc() -> i64 ---
// Compiler Hints
expect :: proc(val, expected_val: T) -> T ---
cpu_relax :: proc() ---
// Constant type tests
@@ -114,13 +123,12 @@ type_is_ordered_numeric :: proc($T: typeid) -> bool ---
type_is_indexable :: proc($T: typeid) -> bool ---
type_is_sliceable :: proc($T: typeid) -> bool ---
type_is_comparable :: proc($T: typeid) -> bool ---
type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp
type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp (== and !=)
type_is_dereferenceable :: proc($T: typeid) -> bool ---
type_is_valid_map_key :: proc($T: typeid) -> bool ---
type_is_named :: proc($T: typeid) -> bool ---
type_is_pointer :: proc($T: typeid) -> bool ---
type_is_opaque :: proc($T: typeid) -> bool ---
type_is_array :: proc($T: typeid) -> bool ---
type_is_enumerated_array :: proc($T: typeid) -> bool ---
type_is_slice :: proc($T: typeid) -> bool ---
@@ -130,8 +138,6 @@ type_is_struct :: proc($T: typeid) -> bool ---
type_is_union :: proc($T: typeid) -> bool ---
type_is_enum :: proc($T: typeid) -> bool ---
type_is_proc :: proc($T: typeid) -> bool ---
type_is_bit_field :: proc($T: typeid) -> bool ---
type_is_bit_field_value :: proc($T: typeid) -> bool ---
type_is_bit_set :: proc($T: typeid) -> bool ---
type_is_simd_vector :: proc($T: typeid) -> bool ---
@@ -139,6 +145,8 @@ type_has_nil :: proc($T: typeid) -> bool ---
type_is_specialization_of :: proc($T, $S: typeid) -> bool ---
type_has_field :: proc($T: typeid, $name: string) -> bool ---
type_proc_parameter_count :: proc($T: typeid) -> int where type_is_proc(T) ---
type_proc_return_count :: proc($T: typeid) -> int where type_is_proc(T) ---
@@ -147,3 +155,9 @@ type_proc_return_type :: proc($T: typeid, index: int) -> typeid where type_i
type_polymorphic_record_parameter_count :: proc($T: typeid) -> typeid ---
type_polymorphic_record_parameter_value :: proc($T: typeid, index: int) -> $V ---
type_field_index_of :: proc($T: typeid, $name: string) -> uintptr ---
type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) ---
type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) ---
+194
View File
@@ -0,0 +1,194 @@
package io
to_reader :: proc(s: Stream) -> (r: Reader, ok: bool = true) {
r.stream = s;
if s.stream_vtable == nil || s.impl_read == nil {
ok = false;
}
return;
}
to_writer :: proc(s: Stream) -> (w: Writer, ok: bool = true) {
w.stream = s;
if s.stream_vtable == nil || s.impl_write == nil {
ok = false;
}
return;
}
to_closer :: proc(s: Stream) -> (c: Closer, ok: bool = true) {
c.stream = s;
if s.stream_vtable == nil || s.impl_close == nil {
ok = false;
}
return;
}
to_flusher :: proc(s: Stream) -> (f: Flusher, ok: bool = true) {
f.stream = s;
if s.stream_vtable == nil || s.impl_flush == nil {
ok = false;
}
return;
}
to_seeker :: proc(s: Stream) -> (seeker: Seeker, ok: bool = true) {
seeker.stream = s;
if s.stream_vtable == nil || s.impl_seek == nil {
ok = false;
}
return;
}
to_read_writer :: proc(s: Stream) -> (r: Read_Writer, ok: bool = true) {
r.stream = s;
if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil {
ok = false;
}
return;
}
to_read_closer :: proc(s: Stream) -> (r: Read_Closer, ok: bool = true) {
r.stream = s;
if s.stream_vtable == nil || s.impl_read == nil || s.impl_close == nil {
ok = false;
}
return;
}
to_read_write_closer :: proc(s: Stream) -> (r: Read_Write_Closer, ok: bool = true) {
r.stream = s;
if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_close == nil {
ok = false;
}
return;
}
to_read_write_seeker :: proc(s: Stream) -> (r: Read_Write_Seeker, ok: bool = true) {
r.stream = s;
if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_seek == nil {
ok = false;
}
return;
}
to_write_flusher :: proc(s: Stream) -> (w: Write_Flusher, ok: bool = true) {
w.stream = s;
if s.stream_vtable == nil || s.impl_write == nil || s.impl_flush == nil {
ok = false;
}
return;
}
to_write_flush_closer :: proc(s: Stream) -> (w: Write_Flush_Closer, ok: bool = true) {
w.stream = s;
if s.stream_vtable == nil || s.impl_write == nil || s.impl_flush == nil || s.impl_close == nil {
ok = false;
}
return;
}
to_reader_at :: proc(s: Stream) -> (r: Reader_At, ok: bool = true) {
r.stream = s;
if s.stream_vtable == nil || s.impl_read_at == nil {
ok = false;
}
return;
}
to_writer_at :: proc(s: Stream) -> (w: Writer_At, ok: bool = true) {
w.stream = s;
if s.stream_vtable == nil || s.impl_write_at == nil {
ok = false;
}
return;
}
to_reader_from :: proc(s: Stream) -> (r: Reader_From, ok: bool = true) {
r.stream = s;
if s.stream_vtable == nil || s.impl_read_from == nil {
ok = false;
}
return;
}
to_writer_to :: proc(s: Stream) -> (w: Writer_To, ok: bool = true) {
w.stream = s;
if s.stream_vtable == nil || s.impl_write_to == nil {
ok = false;
}
return;
}
to_write_closer :: proc(s: Stream) -> (w: Write_Closer, ok: bool = true) {
w.stream = s;
if s.stream_vtable == nil || s.impl_write == nil || s.impl_close == nil {
ok = false;
}
return;
}
to_write_seeker :: proc(s: Stream) -> (w: Write_Seeker, ok: bool = true) {
w.stream = s;
if s.stream_vtable == nil || s.impl_write == nil || s.impl_seek == nil {
ok = false;
}
return;
}
to_byte_reader :: proc(s: Stream) -> (b: Byte_Reader, ok: bool = true) {
b.stream = s;
if s.stream_vtable == nil || s.impl_read_byte == nil {
ok = false;
if s.stream_vtable != nil && s.impl_read != nil {
ok = true;
}
}
return;
}
to_byte_scanner :: proc(s: Stream) -> (b: Byte_Scanner, ok: bool = true) {
b.stream = s;
if s.stream_vtable != nil {
if s.impl_unread_byte == nil {
ok = false;
return;
}
if s.impl_read_byte != nil {
ok = true;
} else if s.impl_read != nil {
ok = true;
} else {
ok = false;
}
}
return;
}
to_byte_writer :: proc(s: Stream) -> (b: Byte_Writer, ok: bool = true) {
b.stream = s;
if s.stream_vtable == nil || s.impl_write_byte == nil {
ok = false;
if s.stream_vtable != nil && s.impl_write != nil {
ok = true;
}
}
return;
}
to_rune_reader :: proc(s: Stream) -> (r: Rune_Reader, ok: bool = true) {
r.stream = s;
if s.stream_vtable == nil || s.impl_read_rune == nil {
ok = false;
if s.stream_vtable != nil && s.impl_read != nil {
ok = true;
}
}
return;
}
to_rune_scanner :: proc(s: Stream) -> (r: Rune_Scanner, ok: bool = true) {
r.stream = s;
if s.stream_vtable != nil {
if s.impl_unread_rune == nil {
ok = false;
return;
}
if s.impl_read_rune != nil {
ok = true;
} else if s.impl_read != nil {
ok = true;
} else {
ok = false;
}
} else {
ok = false;
}
return;
}
+514
View File
@@ -0,0 +1,514 @@
package io
import "intrinsics"
import "core:runtime"
import "core:unicode/utf8"
Seek_From :: enum {
Start = 0, // seek relative to the origin of the file
Current = 1, // seek relative to the current offset
End = 2, // seek relative to the end
}
Error :: enum i32 {
// No Error
None = 0,
// EOF is the error returned by `read` when no more input is available
EOF,
// Unexpected_EOF means that EOF was encountered in the middle of reading a fixed-sized block of data
Unexpected_EOF,
// Short_Write means that a write accepted fewer bytes than requested but failed to return an explicit error
Short_Write,
// Invalid_Write means that a write returned an impossible count
Invalid_Write,
// Short_Buffer means that a read required a longer buffer than was provided
Short_Buffer,
// No_Progress is returned by some implementations of `io.Reader` when many calls
// to `read` have failed to return any data or error.
// This is usually a signed of a broken `io.Reader` implementation
No_Progress,
Invalid_Whence,
Invalid_Offset,
Invalid_Unread,
Negative_Read,
Negative_Write,
Negative_Count,
Buffer_Full,
// Unknown means that an error has occurred but cannot be categorized
Unknown,
// Empty is returned when a procedure has not been implemented for an io.Stream
Empty = -1,
}
Close_Proc :: proc(using s: Stream) -> Error;
Flush_Proc :: proc(using s: Stream) -> Error;
Seek_Proc :: proc(using s: Stream, offset: i64, whence: Seek_From) -> (n: i64, err: Error);
Size_Proc :: proc(using s: Stream) -> i64;
Read_Proc :: proc(using s: Stream, p: []byte) -> (n: int, err: Error);
Read_At_Proc :: proc(using s: Stream, p: []byte, off: i64) -> (n: int, err: Error);
Read_From_Proc :: proc(using s: Stream, r: Reader) -> (n: i64, err: Error);
Read_Byte_Proc :: proc(using s: Stream) -> (byte, Error);
Read_Rune_Proc :: proc(using s: Stream) -> (ch: rune, size: int, err: Error);
Unread_Byte_Proc :: proc(using s: Stream) -> Error;
Unread_Rune_Proc :: proc(using s: Stream) -> Error;
Write_Proc :: proc(using s: Stream, p: []byte) -> (n: int, err: Error);
Write_At_Proc :: proc(using s: Stream, p: []byte, off: i64) -> (n: int, err: Error);
Write_To_Proc :: proc(using s: Stream, w: Writer) -> (n: i64, err: Error);
Write_Byte_Proc :: proc(using s: Stream, c: byte) -> Error;
Write_Rune_Proc :: proc(using s: Stream, r: rune) -> (size: int, err: Error);
Destroy_Proc :: proc(using s: Stream) -> Error;
Stream :: struct {
using stream_vtable: ^Stream_VTable,
stream_data: rawptr,
}
Stream_VTable :: struct {
impl_close: Close_Proc,
impl_flush: Flush_Proc,
impl_seek: Seek_Proc,
impl_size: Size_Proc,
impl_read: Read_Proc,
impl_read_at: Read_At_Proc,
impl_read_byte: Read_Byte_Proc,
impl_read_rune: Read_Rune_Proc,
impl_write_to: Write_To_Proc,
impl_write: Write_Proc,
impl_write_at: Write_At_Proc,
impl_write_byte: Write_Byte_Proc,
impl_write_rune: Write_Rune_Proc,
impl_read_from: Read_From_Proc,
impl_unread_byte: Unread_Byte_Proc,
impl_unread_rune: Unread_Rune_Proc,
impl_destroy: Destroy_Proc,
}
Reader :: struct {using stream: Stream};
Writer :: struct {using stream: Stream};
Closer :: struct {using stream: Stream};
Flusher :: struct {using stream: Stream};
Seeker :: struct {using stream: Stream};
Read_Writer :: struct {using stream: Stream};
Read_Closer :: struct {using stream: Stream};
Read_Write_Closer :: struct {using stream: Stream};
Read_Write_Seeker :: struct {using stream: Stream};
Write_Closer :: struct {using stream: Stream};
Write_Seeker :: struct {using stream: Stream};
Write_Flusher :: struct {using stream: Stream};
Write_Flush_Closer :: struct {using stream: Stream};
Reader_At :: struct {using stream: Stream};
Writer_At :: struct {using stream: Stream};
Reader_From :: struct {using stream: Stream};
Writer_To :: struct {using stream: Stream};
Byte_Reader :: struct {using stream: Stream};
Byte_Scanner :: struct {using stream: Stream};
Byte_Writer :: struct {using stream: Stream};
Rune_Reader :: struct {using stream: Stream};
Rune_Scanner :: struct {using stream: Stream};
destroy :: proc(s: Stream) -> Error {
close_err := close({s});
if s.stream_vtable != nil && s.impl_destroy != nil {
return s->impl_destroy();
}
if close_err != .None {
return close_err;
}
return .Empty;
}
read :: proc(s: Reader, p: []byte) -> (n: int, err: Error) {
if s.stream_vtable != nil && s.impl_read != nil {
return s->impl_read(p);
}
return 0, .Empty;
}
write :: proc(s: Writer, p: []byte) -> (n: int, err: Error) {
if s.stream_vtable != nil && s.impl_write != nil {
return s->impl_write(p);
}
return 0, .Empty;
}
seek :: proc(s: Seeker, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
if s.stream_vtable != nil && s.impl_seek != nil {
return s->impl_seek(offset, whence);
}
return 0, .Empty;
}
close :: proc(s: Closer) -> Error {
if s.stream_vtable != nil && s.impl_close != nil {
return s->impl_close();
}
// Instead of .Empty, .None is fine in this case
return .None;
}
flush :: proc(s: Flusher) -> Error {
if s.stream_vtable != nil && s.impl_flush != nil {
return s->impl_flush();
}
// Instead of .Empty, .None is fine in this case
return .None;
}
size :: proc(s: Stream) -> i64 {
if s.stream_vtable == nil {
return 0;
}
if s.impl_size != nil {
return s->impl_size();
}
if s.impl_seek == nil {
return 0;
}
curr, end: i64;
err: Error;
if curr, err = s->impl_seek(0, .Current); err != nil {
return 0;
}
if end, err = s->impl_seek(0, .End); err != nil {
return 0;
}
if _, err = s->impl_seek(curr, .Start); err != nil {
return 0;
}
return end;
}
read_at :: proc(r: Reader_At, p: []byte, offset: i64) -> (n: int, err: Error) {
if r.stream_vtable == nil {
return 0, .Empty;
}
if r.impl_read_at != nil {
return r->impl_read_at(p, offset);
}
if r.impl_seek == nil || r.impl_read == nil {
return 0, .Empty;
}
curr_offset: i64;
curr_offset, err = r->impl_seek(offset, .Current);
if err != nil {
return 0, err;
}
n, err = r->impl_read(p);
_, err1 := r->impl_seek(curr_offset, .Start);
if err1 != nil && err == nil {
err = err1;
}
return;
}
write_at :: proc(w: Writer_At, p: []byte, offset: i64) -> (n: int, err: Error) {
if w.stream_vtable == nil {
return 0, .Empty;
}
if w.impl_write_at != nil {
return w->impl_write_at(p, offset);
}
if w.impl_seek == nil || w.impl_write == nil {
return 0, .Empty;
}
curr_offset: i64;
curr_offset, err = w->impl_seek(offset, .Current);
if err != nil {
return 0, err;
}
n, err = w->impl_write(p);
_, err1 := w->impl_seek(curr_offset, .Start);
if err1 != nil && err == nil {
err = err1;
}
return;
}
write_to :: proc(r: Writer_To, w: Writer) -> (n: i64, err: Error) {
if r.stream_vtable == nil || w.stream_vtable == nil {
return 0, .Empty;
}
if r.impl_write_to != nil {
return r->impl_write_to(w);
}
return 0, .Empty;
}
read_from :: proc(w: Reader_From, r: Reader) -> (n: i64, err: Error) {
if r.stream_vtable == nil || w.stream_vtable == nil {
return 0, .Empty;
}
if r.impl_read_from != nil {
return w->impl_read_from(r);
}
return 0, .Empty;
}
read_byte :: proc(r: Byte_Reader) -> (byte, Error) {
if r.stream_vtable == nil {
return 0, .Empty;
}
if r.impl_read_byte != nil {
return r->impl_read_byte();
}
if r.impl_read == nil {
return 0, .Empty;
}
b: [1]byte;
_, err := r->impl_read(b[:]);
return b[0], err;
}
write_byte :: proc{
write_byte_to_byte_writer,
write_byte_to_writer,
};
write_byte_to_byte_writer :: proc(w: Byte_Writer, c: byte) -> Error {
return _write_byte(w, c);
}
write_byte_to_writer :: proc(w: Writer, c: byte) -> Error {
return _write_byte(auto_cast w, c);
}
@(private)
_write_byte :: proc(w: Byte_Writer, c: byte) -> Error {
if w.stream_vtable == nil {
return .Empty;
}
if w.impl_write_byte != nil {
return w->impl_write_byte(c);
}
if w.impl_write == nil {
return .Empty;
}
b := [1]byte{c};
_, err := w->impl_write(b[:]);
return err;
}
read_rune :: proc(br: Rune_Reader) -> (ch: rune, size: int, err: Error) {
if br.stream_vtable == nil {
return 0, 0, .Empty;
}
if br.impl_read_rune != nil {
return br->impl_read_rune();
}
if br.impl_read == nil {
return 0, 0, .Empty;
}
b: [utf8.UTF_MAX]byte;
_, err = br->impl_read(b[:1]);
s0 := b[0];
ch = rune(s0);
size = 1;
if err != nil {
return;
}
if ch < utf8.RUNE_SELF {
return;
}
x := utf8.accept_sizes[s0];
if x >= 0xf0 {
mask := rune(x) << 31 >> 31;
ch = ch &~ mask | utf8.RUNE_ERROR&mask;
return;
}
sz := int(x&7);
n: int;
n, err = br->impl_read(b[1:sz]);
if err != nil || n+1 < sz {
ch = utf8.RUNE_ERROR;
return;
}
ch, size = utf8.decode_rune(b[:sz]);
return;
}
unread_byte :: proc(s: Byte_Scanner) -> Error {
if s.stream_vtable != nil && s.impl_unread_byte != nil {
return s->impl_unread_byte();
}
return .Empty;
}
unread_rune :: proc(s: Rune_Scanner) -> Error {
if s.stream_vtable != nil && s.impl_unread_rune != nil {
return s->impl_unread_rune();
}
return .Empty;
}
write_string :: proc(s: Writer, str: string) -> (n: int, err: Error) {
return write(s, transmute([]byte)str);
}
write_rune :: proc(s: Writer, r: rune) -> (size: int, err: Error) {
if s.stream_vtable != nil && s.impl_write_rune != nil {
return s->impl_write_rune(r);
}
if r < utf8.RUNE_SELF {
err = write_byte(s, byte(r));
if err == nil {
size = 1;
}
return;
}
buf, w := utf8.encode_rune(r);
return write(s, buf[:w]);
}
read_full :: proc(r: Reader, buf: []byte) -> (n: int, err: Error) {
return read_at_least(r, buf, len(buf));
}
read_at_least :: proc(r: Reader, buf: []byte, min: int) -> (n: int, err: Error) {
if len(buf) < min {
return 0, .Short_Buffer;
}
for n < min && err == nil {
nn: int;
nn, err = read(r, buf[n:]);
n += n;
}
if n >= min {
err = nil;
} else if n > 0 && err == .EOF {
err = .Unexpected_EOF;
}
return;
}
// copy copies from src to dst till either EOF is reached on src or an error occurs
// It returns the number of bytes copied and the first error that occurred whilst copying, if any.
copy :: proc(dst: Writer, src: Reader) -> (written: i64, err: Error) {
return _copy_buffer(dst, src, nil);
}
// copy_buffer is the same as copy except that it stages through the provided buffer (if one is required)
// rather than allocating a temporary one on the stack through `intrinsics.alloca`
// If buf is `nil`, it is allocate through `intrinsics.alloca`; otherwise if it has zero length, it will panic
copy_buffer :: proc(dst: Writer, src: Reader, buf: []byte) -> (written: i64, err: Error) {
if buf != nil && len(buf) == 0 {
panic("empty buffer in io.copy_buffer");
}
return _copy_buffer(dst, src, buf);
}
// copy_n copies n bytes (or till an error) from src to dst.
// It returns the number of bytes copied and the first error that occurred whilst copying, if any.
// On return, written == n IFF err == nil
copy_n :: proc(dst: Writer, src: Reader, n: i64) -> (written: i64, err: Error) {
nsrc := limited_reader_init(&Limited_Reader{}, src, n);
written, err = copy(dst, nsrc);
if written == n {
return n, nil;
}
if written < n && err == nil {
// src stopped early and must have been an EOF
err = .EOF;
}
return;
}
@(private)
_copy_buffer :: proc(dst: Writer, src: Reader, buf: []byte) -> (written: i64, err: Error) {
if dst.stream_vtable == nil || src.stream_vtable == nil {
return 0, .Empty;
}
if src.impl_write_to != nil {
return src->impl_write_to(dst);
}
if src.impl_read_from != nil {
return dst->impl_read_from(src);
}
buf := buf;
if buf == nil {
DEFAULT_SIZE :: 4 * 1024;
size := DEFAULT_SIZE;
if src.stream_vtable == _limited_reader_vtable {
l := (^Limited_Reader)(src.stream_data);
if i64(size) > l.n {
if l.n < 1 {
size = 1;
} else {
size = int(l.n);
}
}
}
// NOTE(bill): alloca is fine here
buf = transmute([]byte)runtime.Raw_Slice{intrinsics.alloca(size, 2*align_of(rawptr)), size};
}
for {
nr, er := read(src, buf);
if nr > 0 {
nw, ew := write(dst, buf[0:nr]);
if nw > 0 {
written += i64(nw);
}
if ew != nil {
err = ew;
break;
}
if nr != nw {
err = .Short_Write;
break;
}
}
if er != nil {
if er != .EOF {
err = er;
}
break;
}
}
return;
}
+95
View File
@@ -0,0 +1,95 @@
package io
Multi_Reader :: struct {
readers: [dynamic]Reader,
}
@(private)
_multi_reader_vtable := &Stream_VTable{
impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
mr := (^Multi_Reader)(s.stream_data);
for len(mr.readers) > 0 {
r := mr.readers[0];
n, err = read(r, p);
if err == .EOF {
ordered_remove(&mr.readers, 0);
}
if n > 0 || err != .EOF {
if err == .EOF && len(mr.readers) > 0 {
// Don't return EOF yet, more readers remain
err = nil;
}
return;
}
}
return 0, .EOF;
},
};
multi_reader_init :: proc(mr: ^Multi_Reader, readers: ..Reader, allocator := context.allocator) -> (r: Reader) {
all_readers := make([dynamic]Reader, 0, len(readers), allocator);
for w in readers {
if w.stream_vtable == _multi_reader_vtable {
other := (^Multi_Reader)(w.stream_data);
append(&all_readers, ..other.readers[:]);
} else {
append(&all_readers, w);
}
}
mr.readers = all_readers;
r.stream_vtable = _multi_reader_vtable;
r.stream_data = mr;
return;
}
multi_reader_destroy :: proc(mr: ^Multi_Reader) {
delete(mr.readers);
}
Multi_Writer :: struct {
writers: [dynamic]Writer,
}
@(private)
_multi_writer_vtable := &Stream_VTable{
impl_write = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
mw := (^Multi_Writer)(s.stream_data);
for w in mw.writers {
n, err = write(w, p);
if err != nil {
return;
}
if n != len(p) {
err = .Short_Write;
return;
}
}
return len(p), nil;
},
};
multi_writer_init :: proc(mw: ^Multi_Writer, writers: ..Writer, allocator := context.allocator) -> (out: Writer) {
mw.writers = make([dynamic]Writer, 0, len(writers), allocator);
for w in writers {
if w.stream_vtable == _multi_writer_vtable {
other := (^Multi_Writer)(w.stream_data);
append(&mw.writers, ..other.writers[:]);
} else {
append(&mw.writers, w);
}
}
out.stream_vtable = _multi_writer_vtable;
out.stream_data = mw;
return;
}
multi_writer_destroy :: proc(mw: ^Multi_Writer) {
delete(mw.writers);
}
+176
View File
@@ -0,0 +1,176 @@
package io
import "core:strconv"
write_u64 :: proc(w: Writer, i: u64, base: int = 10) -> (n: int, err: Error) {
buf: [32]byte;
s := strconv.append_bits(buf[:], i, base, false, 64, strconv.digits, nil);
return write_string(w, s);
}
write_i64 :: proc(w: Writer, i: i64, base: int = 10) -> (n: int, err: Error) {
buf: [32]byte;
s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil);
return write_string(w, s);
}
write_uint :: proc(w: Writer, i: uint, base: int = 10) -> (n: int, err: Error) {
return write_u64(w, u64(i), base);
}
write_int :: proc(w: Writer, i: int, base: int = 10) -> (n: int, err: Error) {
return write_i64(w, i64(i), base);
}
Tee_Reader :: struct {
r: Reader,
w: Writer,
}
@(private)
_tee_reader_vtable := &Stream_VTable{
impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
t := (^Tee_Reader)(s.stream_data);
n, err = read(t.r, p);
if n > 0 {
if wn, werr := write(t.w, p[:n]); werr != nil {
return wn, werr;
}
}
return;
},
};
// tee_reader_init returns a Reader that writes to 'w' what it reads from 'r'
// All reads from 'r' performed through it are matched with a corresponding write to 'w'
// There is no internal buffering done
// The write must complete before th read completes
// Any error encountered whilst writing is reported as a 'read' error
// tee_reader_init must call io.destroy when done with
tee_reader_init :: proc(t: ^Tee_Reader, r: Reader, w: Writer, allocator := context.allocator) -> Reader {
t.r, t.w = r, w;
return tee_reader_to_reader(t);
}
tee_reader_to_reader :: proc(t: ^Tee_Reader) -> (r: Reader) {
r.stream_data = t;
r.stream_vtable = _tee_reader_vtable;
return;
}
// A Limited_Reader reads from r but limits the amount of data returned to just n bytes.
// Each call to read updates n to reflect the new amount remaining.
// read returns EOF when n <= 0 or when the underlying r returns EOF.
Limited_Reader :: struct {
r: Reader, // underlying reader
n: i64, // max_bytes
}
@(private)
_limited_reader_vtable := &Stream_VTable{
impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
l := (^Limited_Reader)(s.stream_data);
if l.n <= 0 {
return 0, .EOF;
}
p := p;
if i64(len(p)) > l.n {
p = p[0:l.n];
}
n, err = read(l.r, p);
l.n -= i64(n);
return;
},
};
limited_reader_init :: proc(l: ^Limited_Reader, r: Reader, n: i64) -> Reader {
l.r = r;
l.n = n;
return limited_reader_to_reader(l);
}
limited_reader_to_reader :: proc(l: ^Limited_Reader) -> (r: Reader) {
r.stream_vtable = _limited_reader_vtable;
r.stream_data = l;
return;
}
// Section_Reader implements read, seek, and read_at on a section of an underlying Reader_At
Section_Reader :: struct {
r: Reader_At,
base: i64,
off: i64,
limit: i64,
}
section_reader_init :: proc(s: ^Section_Reader, r: Reader_At, off: i64, n: i64) {
s.r = r;
s.off = off;
s.limit = off + n;
return;
}
section_reader_to_stream :: proc(s: ^Section_Reader) -> (out: Stream) {
out.stream_data = s;
out.stream_vtable = _section_reader_vtable;
return;
}
@(private)
_section_reader_vtable := &Stream_VTable{
impl_read = proc(stream: Stream, p: []byte) -> (n: int, err: Error) {
s := (^Section_Reader)(stream.stream_data);
if s.off >= s.limit {
return 0, .EOF;
}
p := p;
if max := s.limit - s.off; i64(len(p)) > max {
p = p[0:max];
}
n, err = read_at(s.r, p, s.off);
s.off += i64(n);
return;
},
impl_read_at = proc(stream: Stream, p: []byte, off: i64) -> (n: int, err: Error) {
s := (^Section_Reader)(stream.stream_data);
p, off := p, off;
if off < 0 || off >= s.limit - s.base {
return 0, .EOF;
}
off += s.base;
if max := s.limit - off; i64(len(p)) > max {
p = p[0:max];
n, err = read_at(s.r, p, off);
if err == nil {
err = .EOF;
}
return;
}
return read_at(s.r, p, off);
},
impl_seek = proc(stream: Stream, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
s := (^Section_Reader)(stream.stream_data);
offset := offset;
switch whence {
case:
return 0, .Invalid_Whence;
case .Start:
offset += s.base;
case .Current:
offset += s.off;
case .End:
offset += s.limit;
}
if offset < s.base {
return 0, .Invalid_Offset;
}
s.off = offset;
n = offset - s.base;
return;
},
impl_size = proc(stream: Stream) -> i64 {
s := (^Section_Reader)(stream.stream_data);
return s.limit - s.base;
},
};
+18 -7
View File
@@ -43,7 +43,9 @@ create_file_logger :: proc(h: os.Handle, lowest := Level.Debug, opt := Default_F
destroy_file_logger :: proc(log: ^Logger) {
data := cast(^File_Console_Logger_Data)log.data;
if data.file_handle != os.INVALID_HANDLE do os.close(data.file_handle);
if data.file_handle != os.INVALID_HANDLE {
os.close(data.file_handle);
}
free(data);
}
@@ -75,8 +77,8 @@ file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string
t := time.now();
y, m, d := time.date(t);
h, min, s := time.clock(t);
if .Date in options do fmt.sbprintf(&buf, "%d-%02d-%02d ", y, m, d);
if .Time in options do fmt.sbprintf(&buf, "%02d:%02d:%02d", h, min, s);
if .Date in options { fmt.sbprintf(&buf, "%d-%02d-%02d ", y, m, d); }
if .Time in options { fmt.sbprintf(&buf, "%02d:%02d:%02d", h, min, s); }
fmt.sbprint(&buf, "] ");
}
}
@@ -89,7 +91,9 @@ file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string
fmt.sbprintf(&buf, "[{}] ", os.current_thread_id());
}
if data.ident != "" do fmt.sbprintf(&buf, "[%s] ", data.ident);
if data.ident != "" {
fmt.sbprintf(&buf, "[%s] ", data.ident);
}
//TODO(Hoej): When we have better atomics and such, make this thread-safe
fmt.fprintf(h, "%s %s\n", strings.to_string(buf), text);
}
@@ -110,14 +114,21 @@ do_level_header :: proc(opts: Options, level: Level, str: ^strings.Builder) {
}
if .Level in opts {
if .Terminal_Color in opts do fmt.sbprint(str, col);
if .Terminal_Color in opts {
fmt.sbprint(str, col);
}
fmt.sbprint(str, Level_Headers[level]);
if .Terminal_Color in opts do fmt.sbprint(str, RESET);
if .Terminal_Color in opts {
fmt.sbprint(str, RESET);
}
}
}
do_location_header :: proc(opts: Options, buf: ^strings.Builder, location := #caller_location) {
if Location_Header_Opts & opts != nil do fmt.sbprint(buf, "["); else do return;
if Location_Header_Opts & opts == nil {
return;
}
fmt.sbprint(buf, "[");
file := location.file_path;
if .Short_File_Path in opts {
+10 -5
View File
@@ -2,7 +2,6 @@ package log
import "core:runtime"
import "core:fmt"
import "core:sync"
// NOTE(bill, 2019-12-31): These are defined in `package runtime` as they are used in the `context`. This is to prevent an import definition cycle.
@@ -39,7 +38,7 @@ Options :: bit_set[Option];
Full_Timestamp_Opts :: Options{
.Date,
.Time
.Time,
};
Location_Header_Opts :: Options{
.Short_File_Path,
@@ -49,7 +48,7 @@ Location_Header_Opts :: Options{
};
Location_File_Opts :: Options{
.Short_File_Path,
.Long_File_Path
.Long_File_Path,
};
@@ -111,11 +110,11 @@ fatal :: proc(args: ..any, sep := " ", location := #caller_location) {
panic :: proc(args: ..any, location := #caller_location) -> ! {
log(level=.Fatal, args=args, location=location);
runtime.panic("log.panic");
runtime.panic("log.panic", location);
}
panicf :: proc(fmt_str: string, args: ..any, location := #caller_location) -> ! {
logf(level=.Fatal, fmt_str=fmt_str, args=args, location=location);
runtime.panic("log.panicf");
runtime.panic("log.panicf", location);
}
@@ -123,6 +122,9 @@ panicf :: proc(fmt_str: string, args: ..any, location := #caller_location) -> !
log :: proc(level: Level, args: ..any, sep := " ", location := #caller_location) {
logger := context.logger;
if logger.procedure == nil {
return;
}
if level < logger.lowest_level {
return;
}
@@ -132,6 +134,9 @@ log :: proc(level: Level, args: ..any, sep := " ", location := #caller_location)
logf :: proc(level: Level, fmt_str: string, args: ..any, location := #caller_location) {
logger := context.logger;
if logger.procedure == nil {
return;
}
if level < logger.lowest_level {
return;
}
+129 -4
View File
@@ -65,13 +65,13 @@ leading_zeros_u64 :: proc(i: u64) -> int {
byte_swap_u16 :: proc(x: u16) -> u16 {
return u16(runtime.bswap_16(u16(x)));
return runtime.bswap_16(x);
}
byte_swap_u32 :: proc(x: u32) -> u32 {
return u32(runtime.bswap_32(u32(x)));
return runtime.bswap_32(x);
}
byte_swap_u64 :: proc(x: u64) -> u64 {
return u64(runtime.bswap_64(u64(x)));
return runtime.bswap_64(x);
}
byte_swap_i16 :: proc(x: i16) -> i16 {
return i16(runtime.bswap_16(u16(x)));
@@ -83,7 +83,7 @@ byte_swap_i64 :: proc(x: i64) -> i64 {
return i64(runtime.bswap_64(u64(x)));
}
byte_swap_u128 :: proc(x: u128) -> u128 {
return u128(runtime.bswap_128(u128(x)));
return runtime.bswap_128(x);
}
byte_swap_i128 :: proc(x: i128) -> i128 {
return i128(runtime.bswap_128(u128(x)));
@@ -529,3 +529,128 @@ len_u8_table := [256]u8{
};
bitfield_extract_u8 :: proc(value: u8, offset, bits: uint) -> u8 { return (value >> offset) & u8(1<<bits - 1); }
bitfield_extract_u16 :: proc(value: u16, offset, bits: uint) -> u16 { return (value >> offset) & u16(1<<bits - 1); }
bitfield_extract_u32 :: proc(value: u32, offset, bits: uint) -> u32 { return (value >> offset) & u32(1<<bits - 1); }
bitfield_extract_u64 :: proc(value: u64, offset, bits: uint) -> u64 { return (value >> offset) & u64(1<<bits - 1); }
bitfield_extract_u128 :: proc(value: u128, offset, bits: uint) -> u128 { return (value >> offset) & u128(1<<bits - 1); }
bitfield_extract_uint :: proc(value: uint, offset, bits: uint) -> uint { return (value >> offset) & uint(1<<bits - 1); }
bitfield_extract_i8 :: proc(value: i8, offset, bits: uint) -> i8 {
v := (u8(value) >> offset) & u8(1<<bits - 1);
m := u8(1<<(bits-1));
r := (v~m) - m;
return i8(r);
}
bitfield_extract_i16 :: proc(value: i16, offset, bits: uint) -> i16 {
v := (u16(value) >> offset) & u16(1<<bits - 1);
m := u16(1<<(bits-1));
r := (v~m) - m;
return i16(r);
}
bitfield_extract_i32 :: proc(value: i32, offset, bits: uint) -> i32 {
v := (u32(value) >> offset) & u32(1<<bits - 1);
m := u32(1<<(bits-1));
r := (v~m) - m;
return i32(r);
}
bitfield_extract_i64 :: proc(value: i64, offset, bits: uint) -> i64 {
v := (u64(value) >> offset) & u64(1<<bits - 1);
m := u64(1<<(bits-1));
r := (v~m) - m;
return i64(r);
}
bitfield_extract_i128 :: proc(value: i128, offset, bits: uint) -> i128 {
v := (u128(value) >> offset) & u128(1<<bits - 1);
m := u128(1<<(bits-1));
r := (v~m) - m;
return i128(r);
}
bitfield_extract_int :: proc(value: int, offset, bits: uint) -> int {
v := (uint(value) >> offset) & uint(1<<bits - 1);
m := uint(1<<(bits-1));
r := (v~m) - m;
return int(r);
}
bitfield_extract :: proc{
bitfield_extract_u8,
bitfield_extract_u16,
bitfield_extract_u32,
bitfield_extract_u64,
bitfield_extract_u128,
bitfield_extract_uint,
bitfield_extract_i8,
bitfield_extract_i16,
bitfield_extract_i32,
bitfield_extract_i64,
bitfield_extract_i128,
bitfield_extract_int,
};
bitfield_insert_u8 :: proc(base, insert: u8, offset, bits: uint) -> u8 {
mask := u8(1<<bits - 1);
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
}
bitfield_insert_u16 :: proc(base, insert: u16, offset, bits: uint) -> u16 {
mask := u16(1<<bits - 1);
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
}
bitfield_insert_u32 :: proc(base, insert: u32, offset, bits: uint) -> u32 {
mask := u32(1<<bits - 1);
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
}
bitfield_insert_u64 :: proc(base, insert: u64, offset, bits: uint) -> u64 {
mask := u64(1<<bits - 1);
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
}
bitfield_insert_u128 :: proc(base, insert: u128, offset, bits: uint) -> u128 {
mask := u128(1<<bits - 1);
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
}
bitfield_insert_uint :: proc(base, insert: uint, offset, bits: uint) -> uint {
mask := uint(1<<bits - 1);
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
}
bitfield_insert_i8 :: proc(base, insert: i8, offset, bits: uint) -> i8 {
mask := i8(1<<bits - 1);
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
}
bitfield_insert_i16 :: proc(base, insert: i16, offset, bits: uint) -> i16 {
mask := i16(1<<bits - 1);
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
}
bitfield_insert_i32 :: proc(base, insert: i32, offset, bits: uint) -> i32 {
mask := i32(1<<bits - 1);
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
}
bitfield_insert_i64 :: proc(base, insert: i64, offset, bits: uint) -> i64 {
mask := i64(1<<bits - 1);
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
}
bitfield_insert_i128 :: proc(base, insert: i128, offset, bits: uint) -> i128 {
mask := i128(1<<bits - 1);
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
}
bitfield_insert_int :: proc(base, insert: int, offset, bits: uint) -> int {
mask := int(1<<bits - 1);
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
}
bitfield_insert :: proc{
bitfield_insert_u8,
bitfield_insert_u16,
bitfield_insert_u32,
bitfield_insert_u64,
bitfield_insert_u128,
bitfield_insert_uint,
bitfield_insert_i8,
bitfield_insert_i16,
bitfield_insert_i32,
bitfield_insert_i64,
bitfield_insert_i128,
bitfield_insert_int,
};
+133
View File
@@ -0,0 +1,133 @@
package math_fixed
import "core:math"
import "core:strconv"
import "intrinsics"
_ :: intrinsics;
Fixed :: struct($Backing: typeid, Fraction_Width: uint)
where
intrinsics.type_is_integer(Backing),
0 <= Fraction_Width,
Fraction_Width <= 8*size_of(Backing) {
i: Backing,
}
Fixed4_4 :: distinct Fixed(i8, 4);
Fixed5_3 :: distinct Fixed(i8, 3);
Fixed6_2 :: distinct Fixed(i8, 2);
Fixed7_1 :: distinct Fixed(i8, 1);
Fixed8_8 :: distinct Fixed(i16, 8);
Fixed13_3 :: distinct Fixed(i16, 3);
Fixed16_16 :: distinct Fixed(i32, 16);
Fixed26_6 :: distinct Fixed(i32, 6);
Fixed32_32 :: distinct Fixed(i64, 32);
Fixed52_12 :: distinct Fixed(i64, 12);
init_from_f64 :: proc(x: ^$T/Fixed($Backing, $Fraction_Width), val: f64) {
i, f := math.modf(val);
x.i = Backing(f * (1<<Fraction_Width));
x.i &= 1<<Fraction_Width - 1;
x.i |= Backing(i) << Fraction_Width;
}
init_from_parts :: proc(x: ^$T/Fixed($Backing, $Fraction_Width), integer, fraction: Backing) {
i, f := math.modf(val);
x.i = fraction;
x.i &= 1<<Fraction_Width - 1;
x.i |= integer;
}
to_f64 :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> f64 {
res := f64(x.i >> Fraction_Width);
res += f64(x.i & (1<<Fraction_Width-1)) / f64(1<<Fraction_Width);
return res;
}
add :: proc(x, y: $T/Fixed) -> T {
return {x.i + y.i};
}
sub :: proc(x, y: $T/Fixed) -> T {
return {x.i - y.i};
}
mul :: proc(x, y: $T/Fixed($Backing, $Fraction_Width)) -> (z: T) {
z.i = intrinsics.fixed_point_mul(x.i, y.i, Fraction_Width);
return;
}
mul_sat :: proc(x, y: $T/Fixed($Backing, $Fraction_Width)) -> (z: T) {
z.i = intrinsics.fixed_point_mul_sat(x.i, y.i, Fraction_Width);
return;
}
div :: proc(x, y: $T/Fixed($Backing, $Fraction_Width)) -> (z: T) {
z.i = intrinsics.fixed_point_div(x.i, y.i, Fraction_Width);
return;
}
div_sat :: proc(x, y: $T/Fixed($Backing, $Fraction_Width)) -> (z: T) {
z.i = intrinsics.fixed_point_div_sat(x.i, y.i, Fraction_Width);
return;
}
floor :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
return x.i >> Fraction_Width;
}
ceil :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
Integer :: 8*size_of(Backing) - Fraction_Width;
return (x.i + (1 << Integer-1)) >> Fraction_Width;
}
round :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
Integer :: 8*size_of(Backing) - Fraction_Width;
return (x.i + (1 << (Integer - 1))) >> Fraction_Width;
}
append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string {
x := x;
buf: [48]byte;
i := 0;
if x.i < 0 {
buf[i] = '-';
i += 1;
x.i = -x.i;
}
integer := x.i >> Fraction_Width;
fraction := x.i & (1<<Fraction_Width - 1);
s := strconv.append_uint(buf[i:], u64(integer), 10);
i += len(s);
if fraction != 0 {
buf[i] = '.';
i += 1;
for fraction > 0 {
fraction *= 10;
buf[i] = byte('0' + (fraction>>Fraction_Width));
i += 1;
fraction &= 1<<Fraction_Width - 1;
}
}
n := copy(dst, buf[:i]);
return string(dst[:i]);
}
to_string :: proc(x: $T/Fixed($Backing, $Fraction_Width), allocator := context.allocator) -> string {
buf: [48]byte;
s := append(buf[:], x);
str := make([]byte, len(s), allocator);
copy(str, s);
return string(str);
}
+553
View File
@@ -0,0 +1,553 @@
package linalg
import "builtin"
import "core:math"
radians :: proc(degrees: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = degrees * RAD_PER_DEG;
}
} else {
out = degrees * RAD_PER_DEG;
}
return;
}
degrees :: proc(radians: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = radians * DEG_PER_RAD;
}
} else {
out = radians * DEG_PER_RAD;
}
return;
}
min_double :: proc(a, b: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = builtin.min(a[i], b[i]);
}
} else {
out = builtin.min(a, b);
}
return;
}
min_single :: proc(a: $T) -> (out: ELEM_TYPE(T)) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
N :: len(T);
when N == 1 {
out = a[0];
} else when N == 2 {
out = builtin.min(a[0], a[1]);
} else {
out = builtin.min(a[0], a[1]);
for i in 2..<N {
out = builtin.min(out, a[i]);
}
}
} else {
out = a;
}
return;
}
min_triple :: proc(a, b, c: $T) -> T where IS_NUMERIC(ELEM_TYPE(T)) {
return min_double(a, min_double(b, c));
}
min :: proc{min_single, min_double, min_triple};
max_double :: proc(a, b: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = builtin.max(a[i], b[i]);
}
} else {
out = builtin.max(a, b);
}
return;
}
max_single :: proc(a: $T) -> (out: ELEM_TYPE(T)) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
N :: len(T);
when N == 1 {
out = a[0];
} else when N == 2 {
out = builtin.max(a[0], a[1]);
} else when N == 3 {
out = builtin.max(a[0], a[1], a[3]);
}else {
out = builtin.max(a[0], a[1]);
for i in 2..<N {
out = builtin.max(out, a[i]);
}
}
} else {
out = a;
}
return;
}
max_triple :: proc(a, b, c: $T) -> T where IS_NUMERIC(ELEM_TYPE(T)) {
return max_double(a, max_double(b, c));
}
max :: proc{max_single, max_double, max_triple};
abs :: proc(a: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = builtin.abs(a[i]);
}
} else {
out = builtin.abs(a);
}
return;
}
sign :: proc(a: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = #force_inline math.sign(a[i]);
}
} else {
out = #force_inline math.sign(a);
}
return;
}
clamp :: proc(x, a, b: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = builtin.clamp(x[i], a[i], b[i]);
}
} else {
out = builtin.clamp(x, a, b);
}
return;
}
saturate :: proc(x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
return clamp(x, 0.0, 1.0);
}
lerp :: proc(a, b, t: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = a[i]*(1-t[i]) + b[i]*t[i];
}
} else {
out = a * (1.0 - t) + b * t;
}
return;
}
mix :: proc(a, b, t: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = a[i]*(1-t[i]) + b[i]*t[i];
}
} else {
out = a * (1.0 - t) + b * t;
}
return;
}
unlerp :: proc(a, b, x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
return (x - a) / (b - a);
}
step :: proc(e, x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = x[i] < e[i] ? 0.0 : 1.0;
}
} else {
out = x < e ? 0.0 : 1.0;
}
return;
}
smoothstep :: proc(e0, e1, x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
t := saturate(unlerp(e0, e1, x));
return t * t * (3.0 - 2.0 * t);
}
smootherstep :: proc(e0, e1, x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
t := saturate(unlerp(e0, e1, x));
return t * t * t * (t * (6*t - 15) + 10);
}
sqrt :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.sqrt(x[i]);
}
} else {
out = math.sqrt(x);
}
return;
}
inverse_sqrt :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = 1.0/math.sqrt(x[i]);
}
} else {
out = 1.0/math.sqrt(x);
}
return;
}
cos :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.cos(x[i]);
}
} else {
out = math.cos(x);
}
return;
}
sin :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.sin(x[i]);
}
} else {
out = math.sin(x);
}
return;
}
tan :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.tan(x[i]);
}
} else {
out = math.tan(x);
}
return;
}
acos :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.acos(x[i]);
}
} else {
out = math.acos(x);
}
return;
}
asin :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.asin(x[i]);
}
} else {
out = math.asin(x);
}
return;
}
atan :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.atan(x[i]);
}
} else {
out = math.atan(x);
}
return;
}
atan2 :: proc(y, x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.atan2(y[i], x[i]);
}
} else {
out = math.atan2(y, x);
}
return;
}
ln :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.ln(x[i]);
}
} else {
out = math.ln(x);
}
return;
}
log2 :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = INVLN2 * math.ln(x[i]);
}
} else {
out = INVLN2 * math.ln(x);
}
return;
}
log10 :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = INVLN10 * math.ln(x[i]);
}
} else {
out = INVLN10 * math.ln(x);
}
return;
}
log :: proc(x, b: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.ln(x[i]) / math.ln(cast(ELEM_TYPE(T))b[i]);
}
} else {
out = INVLN10 * math.ln(x) / math.ln(cast(ELEM_TYPE(T))b);
}
return;
}
exp :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.exp(x[i]);
}
} else {
out = math.exp(x);
}
return;
}
exp2 :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.exp(LN2 * x[i]);
}
} else {
out = math.exp(LN2 * x);
}
return;
}
exp10 :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.exp(LN10 * x[i]);
}
} else {
out = math.exp(LN10 * x);
}
return;
}
pow :: proc(x, e: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = math.pow(x[i], e[i]);
}
} else {
out = math.pow(x, e);
}
return;
}
ceil :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = #force_inline math.ceil(x[i]);
}
} else {
out = #force_inline math.ceil(x);
}
return;
}
floor :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = #force_inline math.floor(x[i]);
}
} else {
out = #force_inline math.floor(x);
}
return;
}
round :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = #force_inline math.round(x[i]);
}
} else {
out = #force_inline math.round(x);
}
return;
}
fract :: proc(x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
f := #force_inline floor(x);
return x - f;
}
mod :: proc(x, m: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
f := #force_inline floor(x / m);
return x - f * m;
}
face_forward :: proc(N, I, N_ref: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) {
return dot(N_ref, I) < 0 ? N : -N;
}
distance :: proc(p0, p1: $V/[$N]$E) -> E where IS_NUMERIC(E) {
return length(p1 - p0);
}
reflect :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) {
b := n * (2 * dot(n, i));
return i - b;
}
refract :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) {
dv := dot(n, i);
k := 1 - eta*eta - (1 - dv*dv);
a := i * eta;
b := n * eta*dv*math.sqrt(k);
return (a - b) * E(int(k >= 0));
}
is_nan_single :: proc(x: $T) -> bool where IS_FLOAT(T) {
return #force_inline math.is_nan(x);
}
is_nan_array :: proc(x: $A/[$N]$T) -> (out: [N]bool) where IS_FLOAT(T) {
for i in 0..<N {
out[i] = #force_inline is_nan(x[i]);
}
return;
}
is_inf_single :: proc(x: $T) -> bool where IS_FLOAT(T) {
return #force_inline math.is_inf(x);
}
is_inf_array :: proc(x: $A/[$N]$T) -> (out: [N]bool) where IS_FLOAT(T) {
for i in 0..<N {
out[i] = #force_inline is_inf(x[i]);
}
return;
}
classify_single :: proc(x: $T) -> math.Float_Class where IS_FLOAT(T) {
return #force_inline math.classify(x);
}
classify_array :: proc(x: $A/[$N]$T) -> (out: [N]math.Float_Class) where IS_FLOAT(T) {
for i in 0..<N {
out[i] = #force_inline classify_single(x[i]);
}
return;
}
is_nan :: proc{is_nan_single, is_nan_array};
is_inf :: proc{is_inf_single, is_inf_array};
classify :: proc{classify_single, classify_array};
less_than_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x < y; }
less_than_equal_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x <= y; }
greater_than_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x > y; }
greater_than_equal_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x >= y; }
equal_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x == y; }
not_equal_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x != y; }
less_than_array :: proc(x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
for i in 0..<N {
out[i] = x[i] < y[i];
}
return;
}
less_than_equal_array :: proc(x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
for i in 0..<N {
out[i] = x[i] <= y[i];
}
return;
}
greater_than_array :: proc(x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
for i in 0..<N {
out[i] = x[i] > y[i];
}
return;
}
greater_than_equal_array :: proc(x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
for i in 0..<N {
out[i] = x[i] >= y[i];
}
return;
}
equal_array :: proc(x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
for i in 0..<N {
out[i] = x[i] == y[i];
}
return;
}
not_equal_array :: proc(x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
for i in 0..<N {
out[i] = x[i] != y[i];
}
return;
}
less_than :: proc{less_than_single, less_than_array};
less_than_equal :: proc{less_than_equal_single, less_than_equal_array};
greater_than :: proc{greater_than_single, greater_than_array};
greater_than_equal :: proc{greater_than_equal_single, greater_than_equal_array};
equal :: proc{equal_single, equal_array};
not_equal :: proc{not_equal_single, not_equal_array};
any :: proc(x: $A/[$N]bool) -> (out: bool) {
for e in x {
if x {
return true;
}
}
return false;
}
all :: proc(x: $A/[$N]bool) -> (out: bool) {
for e in x {
if !e {
return false;
}
}
return true;
}
not :: proc(x: $A/[$N]bool) -> (out: A) {
for e, i in x {
out[i] = !e;
}
return;
}
+191 -224
View File
@@ -5,17 +5,51 @@ import "intrinsics"
// Generic
TAU :: 6.28318530717958647692528676655900576;
PI :: 3.14159265358979323846264338327950288;
E :: 2.71828182845904523536;
τ :: TAU;
π :: PI;
e :: E;
SQRT_TWO :: 1.41421356237309504880168872420969808;
SQRT_THREE :: 1.73205080756887729352744634150587236;
SQRT_FIVE :: 2.23606797749978969640917366873127623;
LN2 :: 0.693147180559945309417232121458176568;
LN10 :: 2.30258509299404568401799145468436421;
MAX_F64_PRECISION :: 16; // Maximum number of meaningful digits after the decimal point for 'f64'
MAX_F32_PRECISION :: 8; // Maximum number of meaningful digits after the decimal point for 'f32'
RAD_PER_DEG :: TAU/360.0;
DEG_PER_RAD :: 360.0/TAU;
@private IS_NUMERIC :: intrinsics.type_is_numeric;
@private IS_QUATERNION :: intrinsics.type_is_quaternion;
@private IS_ARRAY :: intrinsics.type_is_array;
@private IS_FLOAT :: intrinsics.type_is_float;
@private BASE_TYPE :: intrinsics.type_base_type;
@private ELEM_TYPE :: intrinsics.type_elem_type;
scalar_dot :: proc(a, b: $T) -> T where IS_FLOAT(T), !IS_ARRAY(T) {
return a * b;
}
vector_dot :: proc(a, b: $T/[$N]$E) -> (c: E) where IS_NUMERIC(E) {
for i in 0..<N {
c += a[i] * b[i];
}
return;
}
quaternion64_dot :: proc(a, b: $T/quaternion64) -> (c: f16) {
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z;
}
quaternion128_dot :: proc(a, b: $T/quaternion128) -> (c: f32) {
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z;
}
@@ -23,13 +57,27 @@ quaternion256_dot :: proc(a, b: $T/quaternion256) -> (c: f64) {
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z;
}
dot :: proc{vector_dot, quaternion128_dot, quaternion256_dot};
dot :: proc{scalar_dot, vector_dot, quaternion64_dot, quaternion128_dot, quaternion256_dot};
inner_product :: dot;
outer_product :: proc(a: $A/[$M]$E, b: $B/[$N]E) -> (out: [M][N]E) where IS_NUMERIC(E) {
for i in 0..<M {
for j in 0..<N {
out[i][j] = a[i]*b[j];
}
}
return;
}
quaternion_inverse :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
return conj(q) * quaternion(1.0/dot(q, q), 0, 0, 0);
}
scalar_cross :: proc(a, b: $T) -> T where IS_FLOAT(T), !IS_ARRAY(T) {
return a * b;
}
vector_cross2 :: proc(a, b: $T/[2]$E) -> E where IS_NUMERIC(E) {
return a[0]*b[1] - b[0]*a[1];
}
@@ -41,8 +89,16 @@ vector_cross3 :: proc(a, b: $T/[3]$E) -> (c: T) where IS_NUMERIC(E) {
return;
}
vector_cross :: proc{vector_cross2, vector_cross3};
cross :: vector_cross;
quaternion_cross :: proc(q1, q2: $Q) -> (q3: Q) where IS_QUATERNION(Q) {
q3.x = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y;
q3.y = q1.w * q2.y + q1.y * q2.w + q1.z * q2.x - q1.x * q2.z;
q3.z = q1.w * q2.z + q1.z * q2.w + q1.x * q2.y - q1.y * q2.x;
q3.w = q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z;
return;
}
vector_cross :: proc{scalar_cross, vector_cross2, vector_cross3};
cross :: proc{scalar_cross, vector_cross2, vector_cross3, quaternion_cross};
vector_normalize :: proc(v: $T/[$N]$E) -> T where IS_NUMERIC(E) {
return v / length(v);
@@ -79,231 +135,31 @@ quaternion_length2 :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
return dot(q, q);
}
scalar_triple_product :: proc(a, b, c: $T/[$N]$E) -> E where IS_NUMERIC(E) {
// a . (b x c)
// b . (c x a)
// c . (a x b)
return dot(a, cross(b, c));
}
vector_triple_product :: proc(a, b, c: $T/[$N]$E) -> T where IS_NUMERIC(E) {
// a x (b x c)
// (a . c)b - (a . b)c
return cross(a, cross(b, c));
}
length :: proc{vector_length, quaternion_length};
length2 :: proc{vector_length2, quaternion_length2};
vector_lerp :: proc(x, y, t: $V/[$N]$E) -> V where IS_NUMERIC(E) {
s: V;
for i in 0..<N {
ti := t[i];
s[i] = x[i]*(1-ti) + y[i]*ti;
}
return s;
projection :: proc(x, normal: $T/[$N]$E) -> T where IS_NUMERIC(E) {
return dot(x, normal) / dot(normal, normal) * normal;
}
vector_unlerp :: proc(a, b, x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
s: V;
for i in 0..<N {
ai := a[i];
s[i] = (x[i]-ai)/(b[i]-ai);
}
return s;
}
vector_sin :: proc(angle: $V/[$N]$E) -> V where IS_NUMERIC(E) {
s: V;
for i in 0..<N {
s[i] = math.sin(angle[i]);
}
return s;
}
vector_cos :: proc(angle: $V/[$N]$E) -> V where IS_NUMERIC(E) {
s: V;
for i in 0..<N {
s[i] = math.cos(angle[i]);
}
return s;
}
vector_tan :: proc(angle: $V/[$N]$E) -> V where IS_NUMERIC(E) {
s: V;
for i in 0..<N {
s[i] = math.tan(angle[i]);
}
return s;
}
vector_asin :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
s: V;
for i in 0..<N {
s[i] = math.asin(x[i]);
}
return s;
}
vector_acos :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
s: V;
for i in 0..<N {
s[i] = math.acos(x[i]);
}
return s;
}
vector_atan :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
s: V;
for i in 0..<N {
s[i] = math.atan(x[i]);
}
return s;
}
vector_atan2 :: proc(y, x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
s: V;
for i in 0..<N {
s[i] = math.atan(y[i], x[i]);
}
return s;
}
vector_pow :: proc(x, y: $V/[$N]$E) -> V where IS_NUMERIC(E) {
s: V;
for i in 0..<N {
s[i] = math.pow(x[i], y[i]);
}
return s;
}
vector_expr :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
s: V;
for i in 0..<N {
s[i] = math.expr(x[i]);
}
return s;
}
vector_sqrt :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
s: V;
for i in 0..<N {
s[i] = math.sqrt(x[i]);
}
return s;
}
vector_abs :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
s: V;
for i in 0..<N {
s[i] = abs(x[i]);
}
return s;
}
vector_sign :: proc(v: $V/[$N]$E) -> V where IS_NUMERIC(E) {
s: V;
for i in 0..<N {
s[i] = math.sign(v[i]);
}
return s;
}
vector_floor :: proc(v: $V/[$N]$E) -> V where IS_NUMERIC(E) {
s: V;
for i in 0..<N {
s[i] = math.floor(v[i]);
}
return s;
}
vector_ceil :: proc(v: $V/[$N]$E) -> V where IS_NUMERIC(E) {
s: V;
for i in 0..<N {
s[i] = math.ceil(v[i]);
}
return s;
}
vector_mod :: proc(x, y: $V/[$N]$E) -> V where IS_NUMERIC(E) {
s: V;
for i in 0..<N {
s[i] = math.mod(x[i], y[i]);
}
return s;
}
vector_min :: proc(a, b: $V/[$N]$E) -> V where IS_NUMERIC(E) {
s: V;
for i in 0..<N {
s[i] = min(a[i], b[i]);
}
return s;
}
vector_max :: proc(a, b: $V/[$N]$E) -> V where IS_NUMERIC(E) {
s: V;
for i in 0..<N {
s[i] = max(a[i], b[i]);
}
return s;
}
vector_clamp :: proc(x, a, b: $V/[$N]$E) -> V where IS_NUMERIC(E) {
s: V;
for i in 0..<N {
s[i] = clamp(x[i], a[i], b[i]);
}
return s;
}
vector_mix :: proc(x, y, a: $V/[$N]$E) -> V where IS_NUMERIC(E) {
s: V;
for i in 0..<N {
s[i] = x[i]*(1-a[i]) + y[i]*a[i];
}
return s;
}
vector_step :: proc(edge, x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
s: V;
for i in 0..<N {
s[i] = 0 if x[i] < edge[i] else 1;
}
return s;
}
vector_smoothstep :: proc(edge0, edge1, x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
s: V;
for i in 0..<N {
e0, e1 := edge0[i], edge1[i];
t := clamp((x[i] - e0) / (e1 - e0), 0, 1);
s[i] = t * t * (3 - 2*t);
}
return s;
}
vector_smootherstep :: proc(edge0, edge1, x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
s: V;
for i in 0..<N {
e0, e1 := edge0[i], edge1[i];
t := clamp((x[i] - e0) / (e1 - e0), 0, 1);
s[i] = t * t * t * (t * (6*t - 15) + 10);
}
return s;
}
vector_distance :: proc(p0, p1: $V/[$N]$E) -> V where IS_NUMERIC(E) {
return length(p1 - p0);
}
vector_reflect :: proc(i, n: $V/[$N]$E) -> V where IS_NUMERIC(E) {
b := n * (2 * dot(n, i));
return i - b;
}
vector_refract :: proc(i, n: $V/[$N]$E, eta: E) -> V where IS_NUMERIC(E) {
dv := dot(n, i);
k := 1 - eta*eta - (1 - dv*dv);
a := i * eta;
b := n * eta*dv*math.sqrt(k);
return (a - b) * E(int(k >= 0));
}
identity :: proc($T: typeid/[$N][N]$E) -> (m: T) {
for i in 0..<N do m[i][i] = E(1);
for i in 0..<N {
m[i][i] = E(1);
}
return m;
}
@@ -337,6 +193,17 @@ matrix_mul :: proc(a, b: $M/[$N][N]$E) -> (c: M)
return;
}
matrix_comp_mul :: proc(a, b: $M/[$J][$I]$E) -> (c: M)
where !IS_ARRAY(E),
IS_NUMERIC(E) {
for j in 0..<J {
for i in 0..<I {
c[j][i] = a[j][i] * b[j][i];
}
}
return;
}
matrix_mul_differ :: proc(a: $A/[$J][$I]$E, b: $B/[$K][J]E) -> (c: [K][I]E)
where !IS_ARRAY(E),
IS_NUMERIC(E),
@@ -363,6 +230,19 @@ matrix_mul_vector :: proc(a: $A/[$I][$J]$E, b: $B/[I]E) -> (c: B)
return;
}
quaternion_mul_quaternion :: proc(q1, q2: $Q) -> Q where IS_QUATERNION(Q) {
return q1 * q2;
}
quaternion64_mul_vector3 :: proc(q: $Q/quaternion64, v: $V/[3]$F/f16) -> V {
Raw_Quaternion :: struct {xyz: [3]f16, r: f16};
q := transmute(Raw_Quaternion)q;
v := transmute([3]f16)v;
t := cross(2*q.xyz, v);
return V(v + q.r*t + cross(q.xyz, t));
}
quaternion128_mul_vector3 :: proc(q: $Q/quaternion128, v: $V/[3]$F/f32) -> V {
Raw_Quaternion :: struct {xyz: [3]f32, r: f32};
@@ -372,7 +252,6 @@ quaternion128_mul_vector3 :: proc(q: $Q/quaternion128, v: $V/[3]$F/f32) -> V {
t := cross(2*q.xyz, v);
return V(v + q.r*t + cross(q.xyz, t));
}
quaternion256_mul_vector3 :: proc(q: $Q/quaternion256, v: $V/[3]$F/f64) -> V {
Raw_Quaternion :: struct {xyz: [3]f64, r: f64};
@@ -382,14 +261,16 @@ quaternion256_mul_vector3 :: proc(q: $Q/quaternion256, v: $V/[3]$F/f64) -> V {
t := cross(2*q.xyz, v);
return V(v + q.r*t + cross(q.xyz, t));
}
quaternion_mul_vector3 :: proc{quaternion128_mul_vector3, quaternion256_mul_vector3};
quaternion_mul_vector3 :: proc{quaternion64_mul_vector3, quaternion128_mul_vector3, quaternion256_mul_vector3};
mul :: proc{
matrix_mul,
matrix_mul_differ,
matrix_mul_vector,
quaternion64_mul_vector3,
quaternion128_mul_vector3,
quaternion256_mul_vector3,
quaternion_mul_quaternion,
};
vector_to_ptr :: proc(v: ^$V/[$N]$E) -> ^E where IS_NUMERIC(E), N > 0 #no_bounds_check {
@@ -399,3 +280,89 @@ matrix_to_ptr :: proc(m: ^$A/[$I][$J]$E) -> ^E where IS_NUMERIC(E), I > 0, J > 0
return &m[0][0];
}
to_ptr :: proc{vector_to_ptr, matrix_to_ptr};
// Splines
vector_slerp :: proc(x, y: $T/[$N]$E, a: E) -> T {
cos_alpha := dot(x, y);
alpha := math.acos(cos_alpha);
sin_alpha := math.sin(alpha);
t1 := math.sin((1 - a) * alpha) / sin_alpha;
t2 := math.sin(a * alpha) / sin_alpha;
return x * t1 + y * t2;
}
catmull_rom :: proc(v1, v2, v3, v4: $T/[$N]$E, s: E) -> T {
s2 := s*s;
s3 := s2*s;
f1 := -s3 + 2 * s2 - s;
f2 := 3 * s3 - 5 * s2 + 2;
f3 := -3 * s3 + 4 * s2 + s;
f4 := s3 - s2;
return (f1 * v1 + f2 * v2 + f3 * v3 + f4 * v4) * 0.5;
}
hermite :: proc(v1, t1, v2, t2: $T/[$N]$E, s: E) -> T {
s2 := s*s;
s3 := s2*s;
f1 := 2 * s3 - 3 * s2 + 1;
f2 := -2 * s3 + 3 * s2;
f3 := s3 - 2 * s2 + s;
f4 := s3 - s2;
return f1 * v1 + f2 * v2 + f3 * t1 + f4 * t2;
}
cubic :: proc(v1, v2, v3, v4: $T/[$N]$E, s: E) -> T {
return ((v1 * s + v2) * s + v3) * s + v4;
}
array_cast :: proc(v: $A/[$N]$T, $Elem_Type: typeid) -> (w: [N]Elem_Type) {
for i in 0..<N {
w[i] = Elem_Type(v[i]);
}
return;
}
matrix_cast :: proc(v: $A/[$M][$N]$T, $Elem_Type: typeid) -> (w: [M][N]Elem_Type) {
for i in 0..<M {
for j in 0..<N {
w[i][j] = Elem_Type(v[i][j]);
}
}
return;
}
to_f32 :: #force_inline proc(v: $A/[$N]$T) -> [N]f32 { return array_cast(v, f32); }
to_f64 :: #force_inline proc(v: $A/[$N]$T) -> [N]f64 { return array_cast(v, f64); }
to_i8 :: #force_inline proc(v: $A/[$N]$T) -> [N]i8 { return array_cast(v, i8); }
to_i16 :: #force_inline proc(v: $A/[$N]$T) -> [N]i16 { return array_cast(v, i16); }
to_i32 :: #force_inline proc(v: $A/[$N]$T) -> [N]i32 { return array_cast(v, i32); }
to_i64 :: #force_inline proc(v: $A/[$N]$T) -> [N]i64 { return array_cast(v, i64); }
to_int :: #force_inline proc(v: $A/[$N]$T) -> [N]int { return array_cast(v, int); }
to_u8 :: #force_inline proc(v: $A/[$N]$T) -> [N]u8 { return array_cast(v, u8); }
to_u16 :: #force_inline proc(v: $A/[$N]$T) -> [N]u16 { return array_cast(v, u16); }
to_u32 :: #force_inline proc(v: $A/[$N]$T) -> [N]u32 { return array_cast(v, u32); }
to_u64 :: #force_inline proc(v: $A/[$N]$T) -> [N]u64 { return array_cast(v, u64); }
to_uint :: #force_inline proc(v: $A/[$N]$T) -> [N]uint { return array_cast(v, uint); }
to_complex32 :: #force_inline proc(v: $A/[$N]$T) -> [N]complex32 { return array_cast(v, complex32); }
to_complex64 :: #force_inline proc(v: $A/[$N]$T) -> [N]complex64 { return array_cast(v, complex64); }
to_complex128 :: #force_inline proc(v: $A/[$N]$T) -> [N]complex128 { return array_cast(v, complex128); }
to_quaternion64 :: #force_inline proc(v: $A/[$N]$T) -> [N]quaternion64 { return array_cast(v, quaternion64); }
to_quaternion128 :: #force_inline proc(v: $A/[$N]$T) -> [N]quaternion128 { return array_cast(v, quaternion128); }
to_quaternion256 :: #force_inline proc(v: $A/[$N]$T) -> [N]quaternion256 { return array_cast(v, quaternion256); }
File diff suppressed because it is too large Load Diff
+127
View File
@@ -0,0 +1,127 @@
package linalg
Euler_Angle_Order :: enum {
// Tait-Bryan
XYZ,
XZY,
YXZ,
YZX,
ZXY,
ZYX,
// Proper Euler
XYX,
XZX,
YXY,
YZY,
ZXZ,
ZYZ,
}
quaternion_from_euler_angles :: proc{quaternion_from_euler_angles_f16, quaternion_from_euler_angles_f32, quaternion_from_euler_angles_f64};
quaternion_from_euler_angle_x :: proc{quaternion_from_euler_angle_x_f16, quaternion_from_euler_angle_x_f32, quaternion_from_euler_angle_x_f64};
quaternion_from_euler_angle_y :: proc{quaternion_from_euler_angle_y_f16, quaternion_from_euler_angle_y_f32, quaternion_from_euler_angle_y_f64};
quaternion_from_euler_angle_z :: proc{quaternion_from_euler_angle_z_f16, quaternion_from_euler_angle_z_f32, quaternion_from_euler_angle_z_f64};
quaternion_from_pitch_yaw_roll :: proc{quaternion_from_pitch_yaw_roll_f16, quaternion_from_pitch_yaw_roll_f32, quaternion_from_pitch_yaw_roll_f64};
euler_angles_from_quaternion :: proc{euler_angles_from_quaternion_f16, euler_angles_from_quaternion_f32, euler_angles_from_quaternion_f64};
euler_angles_xyz_from_quaternion :: proc{euler_angles_xyz_from_quaternion_f16, euler_angles_xyz_from_quaternion_f32, euler_angles_xyz_from_quaternion_f64};
euler_angles_yxz_from_quaternion :: proc{euler_angles_yxz_from_quaternion_f16, euler_angles_yxz_from_quaternion_f32, euler_angles_yxz_from_quaternion_f64};
euler_angles_xzx_from_quaternion :: proc{euler_angles_xzx_from_quaternion_f16, euler_angles_xzx_from_quaternion_f32, euler_angles_xzx_from_quaternion_f64};
euler_angles_xyx_from_quaternion :: proc{euler_angles_xyx_from_quaternion_f16, euler_angles_xyx_from_quaternion_f32, euler_angles_xyx_from_quaternion_f64};
euler_angles_yxy_from_quaternion :: proc{euler_angles_yxy_from_quaternion_f16, euler_angles_yxy_from_quaternion_f32, euler_angles_yxy_from_quaternion_f64};
euler_angles_yzy_from_quaternion :: proc{euler_angles_yzy_from_quaternion_f16, euler_angles_yzy_from_quaternion_f32, euler_angles_yzy_from_quaternion_f64};
euler_angles_zyz_from_quaternion :: proc{euler_angles_zyz_from_quaternion_f16, euler_angles_zyz_from_quaternion_f32, euler_angles_zyz_from_quaternion_f64};
euler_angles_zxz_from_quaternion :: proc{euler_angles_zxz_from_quaternion_f16, euler_angles_zxz_from_quaternion_f32, euler_angles_zxz_from_quaternion_f64};
euler_angles_xzy_from_quaternion :: proc{euler_angles_xzy_from_quaternion_f16, euler_angles_xzy_from_quaternion_f32, euler_angles_xzy_from_quaternion_f64};
euler_angles_yzx_from_quaternion :: proc{euler_angles_yzx_from_quaternion_f16, euler_angles_yzx_from_quaternion_f32, euler_angles_yzx_from_quaternion_f64};
euler_angles_zyx_from_quaternion :: proc{euler_angles_zyx_from_quaternion_f16, euler_angles_zyx_from_quaternion_f32, euler_angles_zyx_from_quaternion_f64};
euler_angles_zxy_from_quaternion :: proc{euler_angles_zxy_from_quaternion_f16, euler_angles_zxy_from_quaternion_f32, euler_angles_zxy_from_quaternion_f64};
roll_from_quaternion :: proc{roll_from_quaternion_f16, roll_from_quaternion_f32, roll_from_quaternion_f64};
pitch_from_quaternion :: proc{pitch_from_quaternion_f16, pitch_from_quaternion_f32, pitch_from_quaternion_f64};
yaw_from_quaternion :: proc{yaw_from_quaternion_f16, yaw_from_quaternion_f32, yaw_from_quaternion_f64};
pitch_yaw_roll_from_quaternion :: proc{pitch_yaw_roll_from_quaternion_f16, pitch_yaw_roll_from_quaternion_f32, pitch_yaw_roll_from_quaternion_f64};
matrix3_from_euler_angles :: proc{matrix3_from_euler_angles_f16, matrix3_from_euler_angles_f32, matrix3_from_euler_angles_f64};
matrix3_from_euler_angle_x :: proc{matrix3_from_euler_angle_x_f16, matrix3_from_euler_angle_x_f32, matrix3_from_euler_angle_x_f64};
matrix3_from_euler_angle_y :: proc{matrix3_from_euler_angle_y_f16, matrix3_from_euler_angle_y_f32, matrix3_from_euler_angle_y_f64};
matrix3_from_euler_angle_z :: proc{matrix3_from_euler_angle_z_f16, matrix3_from_euler_angle_z_f32, matrix3_from_euler_angle_z_f64};
matrix3_from_derived_euler_angle_x :: proc{matrix3_from_derived_euler_angle_x_f16, matrix3_from_derived_euler_angle_x_f32, matrix3_from_derived_euler_angle_x_f64};
matrix3_from_derived_euler_angle_y :: proc{matrix3_from_derived_euler_angle_y_f16, matrix3_from_derived_euler_angle_y_f32, matrix3_from_derived_euler_angle_y_f64};
matrix3_from_derived_euler_angle_z :: proc{matrix3_from_derived_euler_angle_z_f16, matrix3_from_derived_euler_angle_z_f32, matrix3_from_derived_euler_angle_z_f64};
matrix3_from_euler_angles_xy :: proc{matrix3_from_euler_angles_xy_f16, matrix3_from_euler_angles_xy_f32, matrix3_from_euler_angles_xy_f64};
matrix3_from_euler_angles_yx :: proc{matrix3_from_euler_angles_yx_f16, matrix3_from_euler_angles_yx_f32, matrix3_from_euler_angles_yx_f64};
matrix3_from_euler_angles_xz :: proc{matrix3_from_euler_angles_xz_f16, matrix3_from_euler_angles_xz_f32, matrix3_from_euler_angles_xz_f64};
matrix3_from_euler_angles_zx :: proc{matrix3_from_euler_angles_zx_f16, matrix3_from_euler_angles_zx_f32, matrix3_from_euler_angles_zx_f64};
matrix3_from_euler_angles_yz :: proc{matrix3_from_euler_angles_yz_f16, matrix3_from_euler_angles_yz_f32, matrix3_from_euler_angles_yz_f64};
matrix3_from_euler_angles_zy :: proc{matrix3_from_euler_angles_zy_f16, matrix3_from_euler_angles_zy_f32, matrix3_from_euler_angles_zy_f64};
matrix3_from_euler_angles_xyz :: proc{matrix3_from_euler_angles_xyz_f16, matrix3_from_euler_angles_xyz_f32, matrix3_from_euler_angles_xyz_f64};
matrix3_from_euler_angles_yxz :: proc{matrix3_from_euler_angles_yxz_f16, matrix3_from_euler_angles_yxz_f32, matrix3_from_euler_angles_yxz_f64};
matrix3_from_euler_angles_xzx :: proc{matrix3_from_euler_angles_xzx_f16, matrix3_from_euler_angles_xzx_f32, matrix3_from_euler_angles_xzx_f64};
matrix3_from_euler_angles_xyx :: proc{matrix3_from_euler_angles_xyx_f16, matrix3_from_euler_angles_xyx_f32, matrix3_from_euler_angles_xyx_f64};
matrix3_from_euler_angles_yxy :: proc{matrix3_from_euler_angles_yxy_f16, matrix3_from_euler_angles_yxy_f32, matrix3_from_euler_angles_yxy_f64};
matrix3_from_euler_angles_yzy :: proc{matrix3_from_euler_angles_yzy_f16, matrix3_from_euler_angles_yzy_f32, matrix3_from_euler_angles_yzy_f64};
matrix3_from_euler_angles_zyz :: proc{matrix3_from_euler_angles_zyz_f16, matrix3_from_euler_angles_zyz_f32, matrix3_from_euler_angles_zyz_f64};
matrix3_from_euler_angles_zxz :: proc{matrix3_from_euler_angles_zxz_f16, matrix3_from_euler_angles_zxz_f32, matrix3_from_euler_angles_zxz_f64};
matrix3_from_euler_angles_xzy :: proc{matrix3_from_euler_angles_xzy_f16, matrix3_from_euler_angles_xzy_f32, matrix3_from_euler_angles_xzy_f64};
matrix3_from_euler_angles_yzx :: proc{matrix3_from_euler_angles_yzx_f16, matrix3_from_euler_angles_yzx_f32, matrix3_from_euler_angles_yzx_f64};
matrix3_from_euler_angles_zyx :: proc{matrix3_from_euler_angles_zyx_f16, matrix3_from_euler_angles_zyx_f32, matrix3_from_euler_angles_zyx_f64};
matrix3_from_euler_angles_zxy :: proc{matrix3_from_euler_angles_zxy_f16, matrix3_from_euler_angles_zxy_f32, matrix3_from_euler_angles_zxy_f64};
matrix3_from_yaw_pitch_roll :: proc{matrix3_from_yaw_pitch_roll_f16, matrix3_from_yaw_pitch_roll_f32, matrix3_from_yaw_pitch_roll_f64};
euler_angles_from_matrix3 :: proc{euler_angles_from_matrix3_f16, euler_angles_from_matrix3_f32, euler_angles_from_matrix3_f64};
euler_angles_xyz_from_matrix3 :: proc{euler_angles_xyz_from_matrix3_f16, euler_angles_xyz_from_matrix3_f32, euler_angles_xyz_from_matrix3_f64};
euler_angles_yxz_from_matrix3 :: proc{euler_angles_yxz_from_matrix3_f16, euler_angles_yxz_from_matrix3_f32, euler_angles_yxz_from_matrix3_f64};
euler_angles_xzx_from_matrix3 :: proc{euler_angles_xzx_from_matrix3_f16, euler_angles_xzx_from_matrix3_f32, euler_angles_xzx_from_matrix3_f64};
euler_angles_xyx_from_matrix3 :: proc{euler_angles_xyx_from_matrix3_f16, euler_angles_xyx_from_matrix3_f32, euler_angles_xyx_from_matrix3_f64};
euler_angles_yxy_from_matrix3 :: proc{euler_angles_yxy_from_matrix3_f16, euler_angles_yxy_from_matrix3_f32, euler_angles_yxy_from_matrix3_f64};
euler_angles_yzy_from_matrix3 :: proc{euler_angles_yzy_from_matrix3_f16, euler_angles_yzy_from_matrix3_f32, euler_angles_yzy_from_matrix3_f64};
euler_angles_zyz_from_matrix3 :: proc{euler_angles_zyz_from_matrix3_f16, euler_angles_zyz_from_matrix3_f32, euler_angles_zyz_from_matrix3_f64};
euler_angles_zxz_from_matrix3 :: proc{euler_angles_zxz_from_matrix3_f16, euler_angles_zxz_from_matrix3_f32, euler_angles_zxz_from_matrix3_f64};
euler_angles_xzy_from_matrix3 :: proc{euler_angles_xzy_from_matrix3_f16, euler_angles_xzy_from_matrix3_f32, euler_angles_xzy_from_matrix3_f64};
euler_angles_yzx_from_matrix3 :: proc{euler_angles_yzx_from_matrix3_f16, euler_angles_yzx_from_matrix3_f32, euler_angles_yzx_from_matrix3_f64};
euler_angles_zyx_from_matrix3 :: proc{euler_angles_zyx_from_matrix3_f16, euler_angles_zyx_from_matrix3_f32, euler_angles_zyx_from_matrix3_f64};
euler_angles_zxy_from_matrix3 :: proc{euler_angles_zxy_from_matrix3_f16, euler_angles_zxy_from_matrix3_f32, euler_angles_zxy_from_matrix3_f64};
matrix4_from_euler_angles :: proc{matrix4_from_euler_angles_f16, matrix4_from_euler_angles_f32, matrix4_from_euler_angles_f64};
matrix4_from_euler_angle_x :: proc{matrix4_from_euler_angle_x_f16, matrix4_from_euler_angle_x_f32, matrix4_from_euler_angle_x_f64};
matrix4_from_euler_angle_y :: proc{matrix4_from_euler_angle_y_f16, matrix4_from_euler_angle_y_f32, matrix4_from_euler_angle_y_f64};
matrix4_from_euler_angle_z :: proc{matrix4_from_euler_angle_z_f16, matrix4_from_euler_angle_z_f32, matrix4_from_euler_angle_z_f64};
matrix4_from_derived_euler_angle_x :: proc{matrix4_from_derived_euler_angle_x_f16, matrix4_from_derived_euler_angle_x_f32, matrix4_from_derived_euler_angle_x_f64};
matrix4_from_derived_euler_angle_y :: proc{matrix4_from_derived_euler_angle_y_f16, matrix4_from_derived_euler_angle_y_f32, matrix4_from_derived_euler_angle_y_f64};
matrix4_from_derived_euler_angle_z :: proc{matrix4_from_derived_euler_angle_z_f16, matrix4_from_derived_euler_angle_z_f32, matrix4_from_derived_euler_angle_z_f64};
matrix4_from_euler_angles_xy :: proc{matrix4_from_euler_angles_xy_f16, matrix4_from_euler_angles_xy_f32, matrix4_from_euler_angles_xy_f64};
matrix4_from_euler_angles_yx :: proc{matrix4_from_euler_angles_yx_f16, matrix4_from_euler_angles_yx_f32, matrix4_from_euler_angles_yx_f64};
matrix4_from_euler_angles_xz :: proc{matrix4_from_euler_angles_xz_f16, matrix4_from_euler_angles_xz_f32, matrix4_from_euler_angles_xz_f64};
matrix4_from_euler_angles_zx :: proc{matrix4_from_euler_angles_zx_f16, matrix4_from_euler_angles_zx_f32, matrix4_from_euler_angles_zx_f64};
matrix4_from_euler_angles_yz :: proc{matrix4_from_euler_angles_yz_f16, matrix4_from_euler_angles_yz_f32, matrix4_from_euler_angles_yz_f64};
matrix4_from_euler_angles_zy :: proc{matrix4_from_euler_angles_zy_f16, matrix4_from_euler_angles_zy_f32, matrix4_from_euler_angles_zy_f64};
matrix4_from_euler_angles_xyz :: proc{matrix4_from_euler_angles_xyz_f16, matrix4_from_euler_angles_xyz_f32, matrix4_from_euler_angles_xyz_f64};
matrix4_from_euler_angles_yxz :: proc{matrix4_from_euler_angles_yxz_f16, matrix4_from_euler_angles_yxz_f32, matrix4_from_euler_angles_yxz_f64};
matrix4_from_euler_angles_xzx :: proc{matrix4_from_euler_angles_xzx_f16, matrix4_from_euler_angles_xzx_f32, matrix4_from_euler_angles_xzx_f64};
matrix4_from_euler_angles_xyx :: proc{matrix4_from_euler_angles_xyx_f16, matrix4_from_euler_angles_xyx_f32, matrix4_from_euler_angles_xyx_f64};
matrix4_from_euler_angles_yxy :: proc{matrix4_from_euler_angles_yxy_f16, matrix4_from_euler_angles_yxy_f32, matrix4_from_euler_angles_yxy_f64};
matrix4_from_euler_angles_yzy :: proc{matrix4_from_euler_angles_yzy_f16, matrix4_from_euler_angles_yzy_f32, matrix4_from_euler_angles_yzy_f64};
matrix4_from_euler_angles_zyz :: proc{matrix4_from_euler_angles_zyz_f16, matrix4_from_euler_angles_zyz_f32, matrix4_from_euler_angles_zyz_f64};
matrix4_from_euler_angles_zxz :: proc{matrix4_from_euler_angles_zxz_f16, matrix4_from_euler_angles_zxz_f32, matrix4_from_euler_angles_zxz_f64};
matrix4_from_euler_angles_xzy :: proc{matrix4_from_euler_angles_xzy_f16, matrix4_from_euler_angles_xzy_f32, matrix4_from_euler_angles_xzy_f64};
matrix4_from_euler_angles_yzx :: proc{matrix4_from_euler_angles_yzx_f16, matrix4_from_euler_angles_yzx_f32, matrix4_from_euler_angles_yzx_f64};
matrix4_from_euler_angles_zyx :: proc{matrix4_from_euler_angles_zyx_f16, matrix4_from_euler_angles_zyx_f32, matrix4_from_euler_angles_zyx_f64};
matrix4_from_euler_angles_zxy :: proc{matrix4_from_euler_angles_zxy_f16, matrix4_from_euler_angles_zxy_f32, matrix4_from_euler_angles_zxy_f64};
matrix4_from_yaw_pitch_roll :: proc{matrix4_from_yaw_pitch_roll_f16, matrix4_from_yaw_pitch_roll_f32, matrix4_from_yaw_pitch_roll_f64};
euler_angles_from_matrix4 :: proc{euler_angles_from_matrix4_f16, euler_angles_from_matrix4_f32, euler_angles_from_matrix4_f64};
euler_angles_xyz_from_matrix4 :: proc{euler_angles_xyz_from_matrix4_f16, euler_angles_xyz_from_matrix4_f32, euler_angles_xyz_from_matrix4_f64};
euler_angles_yxz_from_matrix4 :: proc{euler_angles_yxz_from_matrix4_f16, euler_angles_yxz_from_matrix4_f32, euler_angles_yxz_from_matrix4_f64};
euler_angles_xzx_from_matrix4 :: proc{euler_angles_xzx_from_matrix4_f16, euler_angles_xzx_from_matrix4_f32, euler_angles_xzx_from_matrix4_f64};
euler_angles_xyx_from_matrix4 :: proc{euler_angles_xyx_from_matrix4_f16, euler_angles_xyx_from_matrix4_f32, euler_angles_xyx_from_matrix4_f64};
euler_angles_yxy_from_matrix4 :: proc{euler_angles_yxy_from_matrix4_f16, euler_angles_yxy_from_matrix4_f32, euler_angles_yxy_from_matrix4_f64};
euler_angles_yzy_from_matrix4 :: proc{euler_angles_yzy_from_matrix4_f16, euler_angles_yzy_from_matrix4_f32, euler_angles_yzy_from_matrix4_f64};
euler_angles_zyz_from_matrix4 :: proc{euler_angles_zyz_from_matrix4_f16, euler_angles_zyz_from_matrix4_f32, euler_angles_zyz_from_matrix4_f64};
euler_angles_zxz_from_matrix4 :: proc{euler_angles_zxz_from_matrix4_f16, euler_angles_zxz_from_matrix4_f32, euler_angles_zxz_from_matrix4_f64};
euler_angles_xzy_from_matrix4 :: proc{euler_angles_xzy_from_matrix4_f16, euler_angles_xzy_from_matrix4_f32, euler_angles_xzy_from_matrix4_f64};
euler_angles_yzx_from_matrix4 :: proc{euler_angles_yzx_from_matrix4_f16, euler_angles_yzx_from_matrix4_f32, euler_angles_yzx_from_matrix4_f64};
euler_angles_zyx_from_matrix4 :: proc{euler_angles_zyx_from_matrix4_f16, euler_angles_zyx_from_matrix4_f32, euler_angles_zyx_from_matrix4_f64};
euler_angles_zxy_from_matrix4 :: proc{euler_angles_zxy_from_matrix4_f16, euler_angles_zxy_from_matrix4_f32, euler_angles_zxy_from_matrix4_f64};
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+222
View File
@@ -0,0 +1,222 @@
package linalg
Scalar_Components :: enum u8 {
x = 0,
r = 0,
}
Vector2_Components :: enum u8 {
x = 0,
y = 1,
r = 0,
g = 1,
}
Vector3_Components :: enum u8 {
x = 0,
y = 1,
z = 2,
r = 0,
g = 1,
b = 2,
}
Vector4_Components :: enum u8 {
x = 0,
y = 1,
z = 2,
w = 3,
r = 0,
g = 1,
b = 2,
a = 3,
}
scalar_f32_swizzle1 :: proc(f: f32, c0: Scalar_Components) -> f32 {
return f;
}
scalar_f32_swizzle2 :: proc(f: f32, c0, c1: Scalar_Components) -> Vector2f32 {
return {f, f};
}
scalar_f32_swizzle3 :: proc(f: f32, c0, c1, c2: Scalar_Components) -> Vector3f32 {
return {f, f, f};
}
scalar_f32_swizzle4 :: proc(f: f32, c0, c1, c2, c3: Scalar_Components) -> Vector4f32 {
return {f, f, f, f};
}
vector2f32_swizzle1 :: proc(v: Vector2f32, c0: Vector2_Components) -> f32 {
return v[c0];
}
vector2f32_swizzle2 :: proc(v: Vector2f32, c0, c1: Vector2_Components) -> Vector2f32 {
return {v[c0], v[c1]};
}
vector2f32_swizzle3 :: proc(v: Vector2f32, c0, c1, c2: Vector2_Components) -> Vector3f32 {
return {v[c0], v[c1], v[c2]};
}
vector2f32_swizzle4 :: proc(v: Vector2f32, c0, c1, c2, c3: Vector2_Components) -> Vector4f32 {
return {v[c0], v[c1], v[c2], v[c3]};
}
vector3f32_swizzle1 :: proc(v: Vector3f32, c0: Vector3_Components) -> f32 {
return v[c0];
}
vector3f32_swizzle2 :: proc(v: Vector3f32, c0, c1: Vector3_Components) -> Vector2f32 {
return {v[c0], v[c1]};
}
vector3f32_swizzle3 :: proc(v: Vector3f32, c0, c1, c2: Vector3_Components) -> Vector3f32 {
return {v[c0], v[c1], v[c2]};
}
vector3f32_swizzle4 :: proc(v: Vector3f32, c0, c1, c2, c3: Vector3_Components) -> Vector4f32 {
return {v[c0], v[c1], v[c2], v[c3]};
}
vector4f32_swizzle1 :: proc(v: Vector4f32, c0: Vector4_Components) -> f32 {
return v[c0];
}
vector4f32_swizzle2 :: proc(v: Vector4f32, c0, c1: Vector4_Components) -> Vector2f32 {
return {v[c0], v[c1]};
}
vector4f32_swizzle3 :: proc(v: Vector4f32, c0, c1, c2: Vector4_Components) -> Vector3f32 {
return {v[c0], v[c1], v[c2]};
}
vector4f32_swizzle4 :: proc(v: Vector4f32, c0, c1, c2, c3: Vector4_Components) -> Vector4f32 {
return {v[c0], v[c1], v[c2], v[c3]};
}
scalar_f64_swizzle1 :: proc(f: f64, c0: Scalar_Components) -> f64 {
return f;
}
scalar_f64_swizzle2 :: proc(f: f64, c0, c1: Scalar_Components) -> Vector2f64 {
return {f, f};
}
scalar_f64_swizzle3 :: proc(f: f64, c0, c1, c2: Scalar_Components) -> Vector3f64 {
return {f, f, f};
}
scalar_f64_swizzle4 :: proc(f: f64, c0, c1, c2, c3: Scalar_Components) -> Vector4f64 {
return {f, f, f, f};
}
vector2f64_swizzle1 :: proc(v: Vector2f64, c0: Vector2_Components) -> f64 {
return v[c0];
}
vector2f64_swizzle2 :: proc(v: Vector2f64, c0, c1: Vector2_Components) -> Vector2f64 {
return {v[c0], v[c1]};
}
vector2f64_swizzle3 :: proc(v: Vector2f64, c0, c1, c2: Vector2_Components) -> Vector3f64 {
return {v[c0], v[c1], v[c2]};
}
vector2f64_swizzle4 :: proc(v: Vector2f64, c0, c1, c2, c3: Vector2_Components) -> Vector4f64 {
return {v[c0], v[c1], v[c2], v[c3]};
}
vector3f64_swizzle1 :: proc(v: Vector3f64, c0: Vector3_Components) -> f64 {
return v[c0];
}
vector3f64_swizzle2 :: proc(v: Vector3f64, c0, c1: Vector3_Components) -> Vector2f64 {
return {v[c0], v[c1]};
}
vector3f64_swizzle3 :: proc(v: Vector3f64, c0, c1, c2: Vector3_Components) -> Vector3f64 {
return {v[c0], v[c1], v[c2]};
}
vector3f64_swizzle4 :: proc(v: Vector3f64, c0, c1, c2, c3: Vector3_Components) -> Vector4f64 {
return {v[c0], v[c1], v[c2], v[c3]};
}
vector4f64_swizzle1 :: proc(v: Vector4f64, c0: Vector4_Components) -> f64 {
return v[c0];
}
vector4f64_swizzle2 :: proc(v: Vector4f64, c0, c1: Vector4_Components) -> Vector2f64 {
return {v[c0], v[c1]};
}
vector4f64_swizzle3 :: proc(v: Vector4f64, c0, c1, c2: Vector4_Components) -> Vector3f64 {
return {v[c0], v[c1], v[c2]};
}
vector4f64_swizzle4 :: proc(v: Vector4f64, c0, c1, c2, c3: Vector4_Components) -> Vector4f64 {
return {v[c0], v[c1], v[c2], v[c3]};
}
scalar_swizzle :: proc{
scalar_f32_swizzle1,
scalar_f32_swizzle2,
scalar_f32_swizzle3,
scalar_f32_swizzle4,
scalar_f64_swizzle1,
scalar_f64_swizzle2,
scalar_f64_swizzle3,
scalar_f64_swizzle4,
};
vector2_swizzle :: proc{
vector2f32_swizzle1,
vector2f32_swizzle2,
vector2f32_swizzle3,
vector2f32_swizzle4,
vector2f64_swizzle1,
vector2f64_swizzle2,
vector2f64_swizzle3,
vector2f64_swizzle4,
};
vector3_swizzle :: proc{
vector3f32_swizzle1,
vector3f32_swizzle2,
vector3f32_swizzle3,
vector3f32_swizzle4,
vector3f64_swizzle1,
vector3f64_swizzle2,
vector3f64_swizzle3,
vector3f64_swizzle4,
};
vector4_swizzle :: proc{
vector4f32_swizzle1,
vector4f32_swizzle2,
vector4f32_swizzle3,
vector4f32_swizzle4,
vector4f64_swizzle1,
vector4f64_swizzle2,
vector4f64_swizzle3,
vector4f64_swizzle4,
};
swizzle :: proc{
scalar_f32_swizzle1,
scalar_f32_swizzle2,
scalar_f32_swizzle3,
scalar_f32_swizzle4,
scalar_f64_swizzle1,
scalar_f64_swizzle2,
scalar_f64_swizzle3,
scalar_f64_swizzle4,
vector2f32_swizzle1,
vector2f32_swizzle2,
vector2f32_swizzle3,
vector2f32_swizzle4,
vector2f64_swizzle1,
vector2f64_swizzle2,
vector2f64_swizzle3,
vector2f64_swizzle4,
vector3f32_swizzle1,
vector3f32_swizzle2,
vector3f32_swizzle3,
vector3f32_swizzle4,
vector3f64_swizzle1,
vector3f64_swizzle2,
vector3f64_swizzle3,
vector3f64_swizzle4,
vector4f32_swizzle1,
vector4f32_swizzle2,
vector4f32_swizzle3,
vector4f32_swizzle4,
vector4f64_swizzle1,
vector4f64_swizzle2,
vector4f64_swizzle3,
vector4f64_swizzle4,
};
+266 -60
View File
@@ -10,7 +10,7 @@ Float_Class :: enum {
Neg_Zero, // the negative zero
NaN, // Not-A-Number (NaN)
Inf, // positive infinity
Neg_Inf // negative infinity
Neg_Inf, // negative infinity
};
TAU :: 6.28318530717958647692528676655900576;
@@ -31,6 +31,7 @@ LN10 :: 2.30258509299404568401799145468436421;
MAX_F64_PRECISION :: 16; // Maximum number of meaningful digits after the decimal point for 'f64'
MAX_F32_PRECISION :: 8; // Maximum number of meaningful digits after the decimal point for 'f32'
MAX_F16_PRECISION :: 4; // Maximum number of meaningful digits after the decimal point for 'f16'
RAD_PER_DEG :: TAU/360.0;
DEG_PER_RAD :: 360.0/TAU;
@@ -38,80 +39,101 @@ DEG_PER_RAD :: 360.0/TAU;
@(default_calling_convention="none")
foreign _ {
@(link_name="llvm.sqrt.f16")
sqrt_f16 :: proc(x: f16) -> f16 ---;
@(link_name="llvm.sqrt.f32")
sqrt_f32 :: proc(x: f32) -> f32 ---;
@(link_name="llvm.sqrt.f64")
sqrt_f64 :: proc(x: f64) -> f64 ---;
@(link_name="llvm.sin.f16")
sin_f16 :: proc(θ: f16) -> f16 ---;
@(link_name="llvm.sin.f32")
sin_f32 :: proc(θ: f32) -> f32 ---;
@(link_name="llvm.sin.f64")
sin_f64 :: proc(θ: f64) -> f64 ---;
@(link_name="llvm.cos.f16")
cos_f16 :: proc(θ: f16) -> f16 ---;
@(link_name="llvm.cos.f32")
cos_f32 :: proc(θ: f32) -> f32 ---;
@(link_name="llvm.cos.f64")
cos_f64 :: proc(θ: f64) -> f64 ---;
@(link_name="llvm.pow.f16")
pow_f16 :: proc(x, power: f16) -> f16 ---;
@(link_name="llvm.pow.f32")
pow_f32 :: proc(x, power: f32) -> f32 ---;
@(link_name="llvm.pow.f64")
pow_f64 :: proc(x, power: f64) -> f64 ---;
@(link_name="llvm.fmuladd.f16")
fmuladd_f16 :: proc(a, b, c: f16) -> f16 ---;
@(link_name="llvm.fmuladd.f32")
fmuladd_f32 :: proc(a, b, c: f32) -> f32 ---;
@(link_name="llvm.fmuladd.f64")
fmuladd_f64 :: proc(a, b, c: f64) -> f64 ---;
@(link_name="llvm.log.f16")
ln_f16 :: proc(x: f16) -> f16 ---;
@(link_name="llvm.log.f32")
ln_f32 :: proc(x: f32) -> f32 ---;
@(link_name="llvm.log.f64")
ln_f64 :: proc(x: f64) -> f64 ---;
@(link_name="llvm.exp.f16")
exp_f16 :: proc(x: f16) -> f16 ---;
@(link_name="llvm.exp.f32")
exp_f32 :: proc(x: f32) -> f32 ---;
@(link_name="llvm.exp.f64")
exp_f64 :: proc(x: f64) -> f64 ---;
@(link_name="llvm.ldexp.f16")
ldexp_f16 :: proc(val: f16, exp: i32) -> f16 ---;
@(link_name="llvm.ldexp.f32")
ldexp_f32 :: proc(val: f32, exp: i32) -> f32 ---;
@(link_name="llvm.ldexp.f64")
ldexp_f64 :: proc(val: f64, exp: i32) -> f64 ---;
}
sqrt :: proc{sqrt_f32, sqrt_f64};
sin :: proc{sin_f32, sin_f64};
cos :: proc{cos_f32, cos_f64};
pow :: proc{pow_f32, pow_f64};
fmuladd :: proc{fmuladd_f32, fmuladd_f64};
ln :: proc{ln_f32, ln_f64};
exp :: proc{exp_f32, exp_f64};
sqrt :: proc{sqrt_f16, sqrt_f32, sqrt_f64};
sin :: proc{sin_f16, sin_f32, sin_f64};
cos :: proc{cos_f16, cos_f32, cos_f64};
pow :: proc{pow_f16, pow_f32, pow_f64};
fmuladd :: proc{fmuladd_f16, fmuladd_f32, fmuladd_f64};
ln :: proc{ln_f16, ln_f32, ln_f64};
exp :: proc{exp_f16, exp_f32, exp_f64};
ldexp :: proc{ldexp_f32, ldexp_f64};
ldexp :: proc{ldexp_f16, ldexp_f32, ldexp_f64};
log_f16 :: proc(x, base: f16) -> f16 { return ln(x) / ln(base); }
log_f32 :: proc(x, base: f32) -> f32 { return ln(x) / ln(base); }
log_f64 :: proc(x, base: f64) -> f64 { return ln(x) / ln(base); }
log :: proc{log_f32, log_f64};
log :: proc{log_f16, log_f32, log_f64};
log2_f16 :: proc(x: f16) -> f16 { return ln(x)/LN2; }
log2_f32 :: proc(x: f32) -> f32 { return ln(x)/LN2; }
log2_f64 :: proc(x: f64) -> f64 { return ln(x)/LN2; }
log2 :: proc{log2_f32, log2_f64};
log2 :: proc{log2_f16, log2_f32, log2_f64};
log10_f16 :: proc(x: f16) -> f16 { return ln(x)/LN10; }
log10_f32 :: proc(x: f32) -> f32 { return ln(x)/LN10; }
log10_f64 :: proc(x: f64) -> f64 { return ln(x)/LN10; }
log10 :: proc{log10_f32, log10_f64};
log10 :: proc{log10_f16, log10_f32, log10_f64};
tan_f32 :: proc "c" (θ: f32) -> f32 { return sin(θ)/cos(θ); }
tan_f64 :: proc "c" (θ: f64) -> f64 { return sin(θ)/cos(θ); }
tan :: proc{tan_f32, tan_f64};
tan_f16 :: proc(θ: f16) -> f16 { return sin(θ)/cos(θ); }
tan_f32 :: proc(θ: f32) -> f32 { return sin(θ)/cos(θ); }
tan_f64 :: proc(θ: f64) -> f64 { return sin(θ)/cos(θ); }
tan :: proc{tan_f16, tan_f32, tan_f64};
lerp :: proc(a, b: $T, t: $E) -> (x: T) { return a*(1-t) + b*t; }
saturate :: proc(a: $T) -> (x: T) { return clamp(a, 0, 1); };
unlerp_f16 :: proc(a, b, x: f16) -> (t: f16) { return (x-a)/(b-a); }
unlerp_f32 :: proc(a, b, x: f32) -> (t: f32) { return (x-a)/(b-a); }
unlerp_f64 :: proc(a, b, x: f64) -> (t: f64) { return (x-a)/(b-a); }
unlerp :: proc{unlerp_f32, unlerp_f64};
unlerp :: proc{unlerp_f16, unlerp_f32, unlerp_f64};
wrap :: proc(x, y: $T) -> T where intrinsics.type_is_numeric(T), !intrinsics.type_is_array(T) {
@@ -148,18 +170,30 @@ gain :: proc(t, g: $T) -> T where intrinsics.type_is_numeric(T) {
}
sign_f16 :: proc(x: f16) -> f16 { return f16(int(0 < x) - int(x < 0)); }
sign_f32 :: proc(x: f32) -> f32 { return f32(int(0 < x) - int(x < 0)); }
sign_f64 :: proc(x: f64) -> f64 { return f64(int(0 < x) - int(x < 0)); }
sign :: proc{sign_f32, sign_f64};
sign :: proc{sign_f16, sign_f32, sign_f64};
sign_bit_f16 :: proc(x: f16) -> bool {
return (transmute(u16)x) & (1<<15) != 0;
}
sign_bit_f32 :: proc(x: f32) -> bool {
return (transmute(u32)x) & (1<<31) != 0;
}
sign_bit_f64 :: proc(x: f64) -> bool {
return (transmute(u64)x) & (1<<63) != 0;
}
sign_bit :: proc{sign_bit_f32, sign_bit_f64};
sign_bit :: proc{sign_bit_f16, sign_bit_f32, sign_bit_f64};
copy_sign_f16 :: proc(x, y: f16) -> f16 {
ix := transmute(u16)x;
iy := transmute(u16)y;
ix &= 0x7fff;
ix |= iy & 0x8000;
return transmute(f16)ix;
}
copy_sign_f32 :: proc(x, y: f32) -> f32 {
ix := transmute(u32)x;
iy := transmute(u32)y;
@@ -174,15 +208,47 @@ copy_sign_f64 :: proc(x, y: f64) -> f64 {
ix |= iy & 0x8000_0000_0000_0000;
return transmute(f64)ix;
}
copy_sign :: proc{copy_sign_f32, copy_sign_f64};
copy_sign :: proc{copy_sign_f16, copy_sign_f32, copy_sign_f64};
to_radians_f16 :: proc(degrees: f16) -> f16 { return degrees * RAD_PER_DEG; }
to_radians_f32 :: proc(degrees: f32) -> f32 { return degrees * RAD_PER_DEG; }
to_radians_f64 :: proc(degrees: f64) -> f64 { return degrees * RAD_PER_DEG; }
to_degrees_f16 :: proc(radians: f16) -> f16 { return radians * DEG_PER_RAD; }
to_degrees_f32 :: proc(radians: f32) -> f32 { return radians * DEG_PER_RAD; }
to_degrees_f64 :: proc(radians: f64) -> f64 { return radians * DEG_PER_RAD; }
to_radians :: proc{to_radians_f32, to_radians_f64};
to_degrees :: proc{to_degrees_f32, to_degrees_f64};
to_radians :: proc{to_radians_f16, to_radians_f32, to_radians_f64};
to_degrees :: proc{to_degrees_f16, to_degrees_f32, to_degrees_f64};
trunc_f16 :: proc(x: f16) -> f16 {
trunc_internal :: proc(f: f16) -> f16 {
mask :: 0x1f;
shift :: 16 - 6;
bias :: 0xf;
if f < 1 {
switch {
case f < 0: return -trunc_internal(-f);
case f == 0: return f;
case: return 0;
}
}
x := transmute(u16)f;
e := (x >> shift) & mask - bias;
if e < shift {
x &= ~(1 << (shift-e)) - 1;
}
return transmute(f16)x;
}
switch classify(x) {
case .Zero, .Neg_Zero, .NaN, .Inf, .Neg_Inf:
return x;
case .Normal, .Subnormal: // carry on
}
return trunc_internal(x);
}
trunc_f32 :: proc(x: f32) -> f32 {
trunc_internal :: proc(f: f32) -> f32 {
@@ -244,21 +310,39 @@ trunc_f64 :: proc(x: f64) -> f64 {
return trunc_internal(x);
}
trunc :: proc{trunc_f32, trunc_f64};
trunc :: proc{trunc_f16, trunc_f32, trunc_f64};
round_f16 :: proc(x: f16) -> f16 {
return ceil(x - 0.5) if x < 0 else floor(x + 0.5);
}
round_f32 :: proc(x: f32) -> f32 {
return ceil(x - 0.5) if x < 0 else floor(x + 0.5);
}
round_f64 :: proc(x: f64) -> f64 {
return ceil(x - 0.5) if x < 0 else floor(x + 0.5);
}
round :: proc{round_f32, round_f64};
round :: proc{round_f16, round_f32, round_f64};
ceil_f16 :: proc(x: f16) -> f16 { return -floor(-x); }
ceil_f32 :: proc(x: f32) -> f32 { return -floor(-x); }
ceil_f64 :: proc(x: f64) -> f64 { return -floor(-x); }
ceil :: proc{ceil_f32, ceil_f64};
ceil :: proc{ceil_f16, ceil_f32, ceil_f64};
floor_f16 :: proc(x: f16) -> f16 {
if x == 0 || is_nan(x) || is_inf(x) {
return x;
}
if x < 0 {
d, fract := modf(-x);
if fract != 0.0 {
d = d + 1;
}
return -d;
}
d, _ := modf(x);
return d;
}
floor_f32 :: proc(x: f32) -> f32 {
if x == 0 || is_nan(x) || is_inf(x) {
return x;
@@ -287,7 +371,7 @@ floor_f64 :: proc(x: f64) -> f64 {
d, _ := modf(x);
return d;
}
floor :: proc{floor_f32, floor_f64};
floor :: proc{floor_f16, floor_f32, floor_f64};
floor_div :: proc(x, y: $T) -> T
@@ -309,7 +393,32 @@ floor_mod :: proc(x, y: $T) -> T
return r;
}
modf_f16 :: proc(x: f16) -> (int: f16, frac: f16) {
shift :: 16 - 5 - 1;
mask :: 0x1f;
bias :: 15;
if x < 1 {
switch {
case x < 0:
int, frac = modf(-x);
return -int, -frac;
case x == 0:
return x, x;
}
return 0, x;
}
i := transmute(u16)x;
e := uint(i>>shift)&mask - bias;
if e < shift {
i &~= 1<<(shift-e) - 1;
}
int = transmute(f16)i;
frac = x - int;
return;
}
modf_f32 :: proc(x: f32) -> (int: f32, frac: f32) {
shift :: 32 - 8 - 1;
mask :: 0xff;
@@ -362,9 +471,17 @@ modf_f64 :: proc(x: f64) -> (int: f64, frac: f64) {
frac = x - int;
return;
}
modf :: proc{modf_f32, modf_f64};
modf :: proc{modf_f16, modf_f32, modf_f64};
split_decimal :: modf;
mod_f16 :: proc(x, y: f16) -> (n: f16) {
z := abs(y);
n = remainder(abs(x), z);
if sign(n) < 0 {
n += z;
}
return copy_sign(n, x);
}
mod_f32 :: proc(x, y: f32) -> (n: f32) {
z := abs(y);
n = remainder(abs(x), z);
@@ -381,11 +498,12 @@ mod_f64 :: proc(x, y: f64) -> (n: f64) {
}
return copy_sign(n, x);
}
mod :: proc{mod_f32, mod_f64};
mod :: proc{mod_f16, mod_f32, mod_f64};
remainder_f16 :: proc(x, y: f16) -> f16 { return x - round(x/y) * y; }
remainder_f32 :: proc(x, y: f32) -> f32 { return x - round(x/y) * y; }
remainder_f64 :: proc(x, y: f64) -> f64 { return x - round(x/y) * y; }
remainder :: proc{remainder_f32, remainder_f64};
remainder :: proc{remainder_f16, remainder_f32, remainder_f64};
@@ -404,25 +522,13 @@ lcm :: proc(x, y: $T) -> T
return x / gcd(x, y) * y;
}
frexp_f16 :: proc(x: f16) -> (significand: f16, exponent: int) {
f, e := frexp_f64(f64(x));
return f16(f), e;
}
frexp_f32 :: proc(x: f32) -> (significand: f32, exponent: int) {
switch {
case x == 0:
return 0, 0;
case x < 0:
significand, exponent = frexp(-x);
return -significand, exponent;
}
ex := trunc(log2(x));
exponent = int(ex);
significand = x / pow(2.0, ex);
if abs(significand) >= 1 {
exponent += 1;
significand /= 2;
}
if exponent == 1024 && significand == 0 {
significand = 0.99999999999999988898;
}
return;
f, e := frexp_f64(f64(x));
return f32(f), e;
}
frexp_f64 :: proc(x: f64) -> (significand: f64, exponent: int) {
switch {
@@ -444,7 +550,7 @@ frexp_f64 :: proc(x: f64) -> (significand: f64, exponent: int) {
}
return;
}
frexp :: proc{frexp_f32, frexp_f64};
frexp :: proc{frexp_f16, frexp_f32, frexp_f64};
@@ -507,9 +613,33 @@ factorial :: proc(n: int) -> int {
assert(n >= 0, "parameter must not be negative");
assert(n < len(table), "parameter is too large to lookup in the table");
return 0;
return table[n];
}
classify_f16 :: proc(x: f16) -> Float_Class {
switch {
case x == 0:
i := transmute(i16)x;
if i < 0 {
return .Neg_Zero;
}
return .Zero;
case x*0.5 == x:
if x < 0 {
return .Neg_Inf;
}
return .Inf;
case !(x == x):
return .NaN;
}
u := transmute(u16)x;
exp := int(u>>10) & (1<<5 - 1);
if exp == 0 {
return .Subnormal;
}
return .Normal;
}
classify_f32 :: proc(x: f32) -> Float_Class {
switch {
case x == 0:
@@ -557,17 +687,28 @@ classify_f64 :: proc(x: f64) -> Float_Class {
}
return .Normal;
}
classify :: proc{classify_f32, classify_f64};
classify :: proc{classify_f16, classify_f32, classify_f64};
is_nan_f16 :: proc(x: f16) -> bool { return classify(x) == .NaN; }
is_nan_f32 :: proc(x: f32) -> bool { return classify(x) == .NaN; }
is_nan_f64 :: proc(x: f64) -> bool { return classify(x) == .NaN; }
is_nan :: proc{is_nan_f32, is_nan_f64};
is_nan :: proc{is_nan_f16, is_nan_f32, is_nan_f64};
// is_inf reports whether f is an infinity, according to sign.
// If sign > 0, is_inf reports whether f is positive infinity.
// If sign < 0, is_inf reports whether f is negative infinity.
// If sign == 0, is_inf reports whether f is either infinity.
is_inf_f16 :: proc(x: f16, sign: int = 0) -> bool {
class := classify(abs(x));
switch {
case sign > 0:
return class == .Inf;
case sign < 0:
return class == .Neg_Inf;
}
return class == .Inf || class == .Neg_Inf;
}
is_inf_f32 :: proc(x: f32, sign: int = 0) -> bool {
class := classify(abs(x));
switch {
@@ -588,7 +729,35 @@ is_inf_f64 :: proc(x: f64, sign: int = 0) -> bool {
}
return class == .Inf || class == .Neg_Inf;
}
is_inf :: proc{is_inf_f32, is_inf_f64};
is_inf :: proc{is_inf_f16, is_inf_f32, is_inf_f64};
inf_f16 :: proc(sign: int) -> f16 {
return f16(inf_f16(sign));
}
inf_f32 :: proc(sign: int) -> f32 {
return f32(inf_f64(sign));
}
inf_f64 :: proc(sign: int) -> f64 {
v: u64;
if sign >= 0 {
v = 0x7ff00000_00000000;
} else {
v = 0xfff00000_00000000;
}
return transmute(f64)v;
}
nan_f16 :: proc() -> f16 {
return f16(nan_f64());
}
nan_f32 :: proc() -> f32 {
return f32(nan_f64());
}
nan_f64 :: proc() -> f64 {
v: u64 = 0x7ff80000_00000001;
return transmute(f64)v;
}
@@ -647,7 +816,10 @@ cumsum :: proc(dst, src: $T/[]$E) -> T
}
atan2_f16 :: proc(y, x: f16) -> f16 {
// TODO(bill): Better atan2_f16
return f16(atan2_f64(f64(y), f64(x)));
}
atan2_f32 :: proc(y, x: f32) -> f32 {
// TODO(bill): Better atan2_f32
return f32(atan2_f64(f64(y), f64(x)));
@@ -740,49 +912,68 @@ atan2_f64 :: proc(y, x: f64) -> f64 {
}
atan2 :: proc{atan2_f32, atan2_f64};
atan2 :: proc{atan2_f16, atan2_f32, atan2_f64};
atan_f16 :: proc(x: f16) -> f16 {
return atan2_f16(x, 1);
}
atan_f32 :: proc(x: f32) -> f32 {
return atan2_f32(x, 1);
}
atan_f64 :: proc(x: f64) -> f64 {
return atan2_f64(x, 1);
}
atan :: proc{atan_f32, atan_f64};
atan :: proc{atan_f16, atan_f32, atan_f64};
asin_f16 :: proc(x: f16) -> f16 {
return atan2_f16(x, 1 + sqrt_f16(1 - x*x));
}
asin_f32 :: proc(x: f32) -> f32 {
return atan2_f32(x, 1 + sqrt_f32(1 - x*x));
}
asin_f64 :: proc(x: f64) -> f64 {
return atan2_f64(x, 1 + sqrt_f64(1 - x*x));
}
asin :: proc{asin_f32, asin_f64};
asin :: proc{asin_f16, asin_f32, asin_f64};
acos_f16 :: proc(x: f16) -> f16 {
return 2 * atan2_f16(sqrt_f16(1 - x), sqrt_f16(1 + x));
}
acos_f32 :: proc(x: f32) -> f32 {
return 2 * atan2_f32(sqrt_f32(1 - x), sqrt_f32(1 + x));
}
acos_f64 :: proc(x: f64) -> f64 {
return 2 * atan2_f64(sqrt_f64(1 - x), sqrt_f64(1 + x));
}
acos :: proc{acos_f32, acos_f64};
acos :: proc{acos_f16, acos_f32, acos_f64};
sinh_f16 :: proc(x: f16) -> f16 {
return (exp(x) - exp(-x))*0.5;
}
sinh_f32 :: proc(x: f32) -> f32 {
return (exp(x) - exp(-x))*0.5;
}
sinh_f64 :: proc(x: f64) -> f64 {
return (exp(x) - exp(-x))*0.5;
}
sinh :: proc{sinh_f32, sinh_f64};
sinh :: proc{sinh_f16, sinh_f32, sinh_f64};
cosh_f16 :: proc(x: f16) -> f16 {
return (exp(x) + exp(-x))*0.5;
}
cosh_f32 :: proc(x: f32) -> f32 {
return (exp(x) + exp(-x))*0.5;
}
cosh_f64 :: proc(x: f64) -> f64 {
return (exp(x) + exp(-x))*0.5;
}
cosh :: proc{cosh_f32, cosh_f64};
cosh :: proc{cosh_f16, cosh_f32, cosh_f64};
tanh_f16 :: proc(x: f16) -> f16 {
t := exp(2*x);
return (t - 1) / (t + 1);
}
tanh_f32 :: proc(x: f32) -> f32 {
t := exp(2*x);
return (t - 1) / (t + 1);
@@ -791,7 +982,22 @@ tanh_f64 :: proc(x: f64) -> f64 {
t := exp(2*x);
return (t - 1) / (t + 1);
}
tanh :: proc{tanh_f32, tanh_f64};
tanh :: proc{tanh_f16, tanh_f32, tanh_f64};
F16_DIG :: 3;
F16_EPSILON :: 0.00097656;
F16_GUARD :: 0;
F16_MANT_DIG :: 11;
F16_MAX :: 65504.0;
F16_MAX_10_EXP :: 4;
F16_MAX_EXP :: 15;
F16_MIN :: 6.10351562e-5;
F16_MIN_10_EXP :: -4;
F16_MIN_EXP :: -14;
F16_NORMALIZE :: 0;
F16_RADIX :: 2;
F16_ROUNDS :: 1;
F32_DIG :: 6;
+7 -1
View File
@@ -17,7 +17,7 @@ import "core:math"
//
// sample = norm_float64() * std_dev + mean
//
norm_float64 :: proc(r: ^Rand = global_rand_ptr) -> f64 {
norm_float64 :: proc(r: ^Rand = nil) -> f64 {
rn :: 3.442619855899;
@(static)
@@ -116,6 +116,12 @@ norm_float64 :: proc(r: ^Rand = global_rand_ptr) -> f64 {
0.008624485, 0.005548995, 0.0026696292,
};
r := r;
if r == nil {
// NOTE(bill, 2020-09-07): Do this so that people can
// enforce the global random state if necessary with `nil`
r = &global_rand;
}
for {
j := i32(uint32(r));
+40 -26
View File
@@ -10,11 +10,9 @@ Rand :: struct {
_GLOBAL_SEED_DATA := 1234567890;
@(private, static)
global_rand := create(u64(uintptr(&_GLOBAL_SEED_DATA)));
@(private, static)
global_rand_ptr := &global_rand;
set_global_seed :: proc(seed: u64) {
init(global_rand_ptr, seed);
init(&global_rand, seed);
}
create :: proc(seed: u64) -> Rand {
@@ -32,6 +30,12 @@ init :: proc(r: ^Rand, seed: u64) {
}
_random :: proc(r: ^Rand) -> u32 {
r := r;
if r == nil {
// NOTE(bill, 2020-09-07): Do this so that people can
// enforce the global random state if necessary with `nil`
r = &global_rand;
}
old_state := r.state;
r.state = old_state * 6364136223846793005 + (r.inc|1);
xor_shifted := u32(((old_state>>18) ~ old_state) >> 27);
@@ -39,15 +43,15 @@ _random :: proc(r: ^Rand) -> u32 {
return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 31));
}
uint32 :: proc(r: ^Rand = global_rand_ptr) -> u32 { return _random(r); }
uint32 :: proc(r: ^Rand = nil) -> u32 { return _random(r); }
uint64 :: proc(r: ^Rand = global_rand_ptr) -> u64 {
uint64 :: proc(r: ^Rand = nil) -> u64 {
a := u64(_random(r));
b := u64(_random(r));
return (a<<32) | b;
}
uint128 :: proc(r: ^Rand = global_rand_ptr) -> u128 {
uint128 :: proc(r: ^Rand = nil) -> u128 {
a := u128(_random(r));
b := u128(_random(r));
c := u128(_random(r));
@@ -55,12 +59,14 @@ uint128 :: proc(r: ^Rand = global_rand_ptr) -> u128 {
return (a<<96) | (b<<64) | (c<<32) | d;
}
int31 :: proc(r: ^Rand = global_rand_ptr) -> i32 { return i32(uint32(r) << 1 >> 1); }
int63 :: proc(r: ^Rand = global_rand_ptr) -> i64 { return i64(uint64(r) << 1 >> 1); }
int127 :: proc(r: ^Rand = global_rand_ptr) -> i128 { return i128(uint128(r) << 1 >> 1); }
int31 :: proc(r: ^Rand = nil) -> i32 { return i32(uint32(r) << 1 >> 1); }
int63 :: proc(r: ^Rand = nil) -> i64 { return i64(uint64(r) << 1 >> 1); }
int127 :: proc(r: ^Rand = nil) -> i128 { return i128(uint128(r) << 1 >> 1); }
int31_max :: proc(n: i32, r: ^Rand = global_rand_ptr) -> i32 {
if n <= 0 do panic("Invalid argument to int31_max");
int31_max :: proc(n: i32, r: ^Rand = nil) -> i32 {
if n <= 0 {
panic("Invalid argument to int31_max");
}
if n&(n-1) == 0 {
return int31(r) & (n-1);
}
@@ -72,8 +78,10 @@ int31_max :: proc(n: i32, r: ^Rand = global_rand_ptr) -> i32 {
return v % n;
}
int63_max :: proc(n: i64, r: ^Rand = global_rand_ptr) -> i64 {
if n <= 0 do panic("Invalid argument to int63_max");
int63_max :: proc(n: i64, r: ^Rand = nil) -> i64 {
if n <= 0 {
panic("Invalid argument to int63_max");
}
if n&(n-1) == 0 {
return int63(r) & (n-1);
}
@@ -85,8 +93,10 @@ int63_max :: proc(n: i64, r: ^Rand = global_rand_ptr) -> i64 {
return v % n;
}
int127_max :: proc(n: i128, r: ^Rand = global_rand_ptr) -> i128 {
if n <= 0 do panic("Invalid argument to int63_max");
int127_max :: proc(n: i128, r: ^Rand = nil) -> i128 {
if n <= 0 {
panic("Invalid argument to int63_max");
}
if n&(n-1) == 0 {
return int127(r) & (n-1);
}
@@ -98,8 +108,10 @@ int127_max :: proc(n: i128, r: ^Rand = global_rand_ptr) -> i128 {
return v % n;
}
int_max :: proc(n: int, r: ^Rand = global_rand_ptr) -> int {
if n <= 0 do panic("Invalid argument to int_max");
int_max :: proc(n: int, r: ^Rand = nil) -> int {
if n <= 0 {
panic("Invalid argument to int_max");
}
when size_of(int) == 4 {
return int(int31_max(i32(n), r));
} else {
@@ -107,14 +119,14 @@ int_max :: proc(n: int, r: ^Rand = global_rand_ptr) -> int {
}
}
float64 :: proc(r: ^Rand = global_rand_ptr) -> f64 { return f64(int63_max(1<<53, r)) / (1 << 53); }
float32 :: proc(r: ^Rand = global_rand_ptr) -> f32 { return f32(float64(r)); }
float64 :: proc(r: ^Rand = nil) -> f64 { return f64(int63_max(1<<53, r)) / (1 << 53); }
float32 :: proc(r: ^Rand = nil) -> f32 { return f32(float64(r)); }
float64_range :: proc(lo, hi: f64, r: ^Rand = global_rand_ptr) -> f64 { return (hi-lo)*float64(r) + lo; }
float32_range :: proc(lo, hi: f32, r: ^Rand = global_rand_ptr) -> f32 { return (hi-lo)*float32(r) + lo; }
float64_range :: proc(lo, hi: f64, r: ^Rand = nil) -> f64 { return (hi-lo)*float64(r) + lo; }
float32_range :: proc(lo, hi: f32, r: ^Rand = nil) -> f32 { return (hi-lo)*float32(r) + lo; }
read :: proc(p: []byte, r: ^Rand = global_rand_ptr) -> (n: int) {
read :: proc(p: []byte, r: ^Rand = nil) -> (n: int) {
pos := i8(0);
val := i64(0);
for n = 0; n < len(p); n += 1 {
@@ -130,10 +142,10 @@ read :: proc(p: []byte, r: ^Rand = global_rand_ptr) -> (n: int) {
}
// perm returns a slice of n ints in a pseudo-random permutation of integers in the range [0, n)
perm :: proc(n: int, r: ^Rand = global_rand_ptr) -> []int {
perm :: proc(n: int, r: ^Rand = nil) -> []int {
m := make([]int, n);
for i := 0; i < n; i += 1 {
j := int_max(i+1);
j := int_max(i+1, r);
m[i] = m[j];
m[j] = i;
}
@@ -141,9 +153,11 @@ perm :: proc(n: int, r: ^Rand = global_rand_ptr) -> []int {
}
shuffle :: proc(array: $T/[]$E, r: ^Rand = global_rand_ptr) {
shuffle :: proc(array: $T/[]$E, r: ^Rand = nil) {
n := i64(len(array));
if n < 2 do return;
if n < 2 {
return;
}
for i := i64(0); i < n; i += 1 {
j := int63_max(n, r);
+127 -32
View File
@@ -28,11 +28,20 @@ Allocator_Query_Info :: struct {
}
*/
Allocator_Error :: runtime.Allocator_Error;
/*
Allocator_Error :: enum byte {
None = 0,
Out_Of_Memory = 1,
Invalid_Pointer = 2,
Invalid_Argument = 3,
}
*/
Allocator_Proc :: runtime.Allocator_Proc;
/*
Allocator_Proc :: #type proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64 = 0, location := #caller_location) -> rawptr;
old_memory: rawptr, old_size: int, location: Source_Code_Location = #caller_location) -> ([]byte, Allocator_Error);
*/
Allocator :: runtime.Allocator;
@@ -45,42 +54,98 @@ Allocator :: struct {
DEFAULT_ALIGNMENT :: 2*align_of(rawptr);
alloc :: inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
if size == 0 do return nil;
if allocator.procedure == nil do return nil;
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, 0, loc);
}
free :: inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) {
if ptr == nil do return;
if allocator.procedure == nil do return;
allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, 0, loc);
}
free_all :: inline proc(allocator := context.allocator, loc := #caller_location) {
if allocator.procedure != nil {
allocator.procedure(allocator.data, Allocator_Mode.Free_All, 0, 0, nil, 0, 0, loc);
alloc :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
if size == 0 {
return nil;
}
if allocator.procedure == nil {
return nil;
}
data, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc);
_ = err;
return raw_data(data);
}
resize :: inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
alloc_bytes :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
if size == 0 {
return nil, nil;
}
if allocator.procedure == nil {
return nil, nil;
}
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc);
}
free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
if ptr == nil {
return nil;
}
if allocator.procedure == nil {
return nil;
}
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, loc);
return err;
}
free_bytes :: proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
if bytes == nil {
return nil;
}
if allocator.procedure == nil {
return nil;
}
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, raw_data(bytes), len(bytes), loc);
return err;
}
free_all :: proc(allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
if allocator.procedure != nil {
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free_All, 0, 0, nil, 0, loc);
return err;
}
return nil;
}
resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
if allocator.procedure == nil {
return nil;
}
if new_size == 0 {
if ptr != nil {
allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, 0, loc);
allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc);
}
return nil;
} else if ptr == nil {
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, 0, loc);
_, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc);
_ = err;
return nil;
}
return allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, 0, loc);
data, err := allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc);
_ = err;
return raw_data(data);
}
resize_bytes :: proc(old_data: []byte, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
if allocator.procedure == nil {
return nil, nil;
}
ptr := raw_data(old_data);
old_size := len(old_data);
if new_size == 0 {
if ptr != nil {
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc);
return nil, err;
}
return nil, nil;
} else if ptr == nil {
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc);
}
return allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc);
}
query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: Allocator_Mode_Set) {
if allocator.procedure != nil {
allocator.procedure(allocator.data, Allocator_Mode.Query_Features, 0, 0, &set, 0, 0, loc);
allocator.procedure(allocator.data, Allocator_Mode.Query_Features, 0, 0, &set, 0, loc);
return set;
}
return nil;
@@ -89,7 +154,7 @@ query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: A
query_info :: proc(pointer: rawptr, allocator: Allocator, loc := #caller_location) -> (props: Allocator_Query_Info) {
props.pointer = pointer;
if allocator.procedure != nil {
allocator.procedure(allocator.data, Allocator_Mode.Query_Info, 0, 0, &props, 0, 0, loc);
allocator.procedure(allocator.data, Allocator_Mode.Query_Info, 0, 0, &props, 0, loc);
}
return;
}
@@ -110,7 +175,7 @@ delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #cal
}
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) {
raw := transmute(Raw_Map)m;
delete_slice(raw.hashes);
delete_slice(raw.hashes, raw.entries.allocator, loc);
free(raw.entries.data, raw.entries.allocator, loc);
}
@@ -124,22 +189,22 @@ delete :: proc{
};
new :: inline proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T {
new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T {
return new_aligned(T, align_of(T), allocator, loc);
}
new_aligned :: inline proc($T: typeid, alignment: int, allocator := context.allocator, loc := #caller_location) -> ^T {
new_aligned :: proc($T: typeid, alignment: int, allocator := context.allocator, loc := #caller_location) -> ^T {
ptr := (^T)(alloc(size_of(T), alignment, allocator, loc));
if ptr != nil do ptr^ = T{};
if ptr != nil { ptr^ = T{}; }
return ptr;
}
new_clone :: inline proc(data: $T, allocator := context.allocator, loc := #caller_location) -> ^T {
new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> ^T {
ptr := (^T)(alloc(size_of(T), align_of(T), allocator, loc));
if ptr != nil do ptr^ = data;
if ptr != nil { ptr^ = data; }
return ptr;
}
make_slice :: inline proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
make_slice :: proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
return make_aligned(T, len, align_of(E), allocator, loc);
}
make_aligned :: proc($T: typeid/[]$E, auto_cast len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> T {
@@ -188,20 +253,50 @@ make :: proc{
default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment: int, allocator := context.allocator, loc := #caller_location) -> rawptr {
if old_memory == nil do return alloc(new_size, alignment, allocator, loc);
if old_memory == nil {
return alloc(new_size, alignment, allocator, loc);
}
if new_size == 0 {
free(old_memory, allocator, loc);
return nil;
}
if new_size == old_size do return old_memory;
if new_size == old_size {
return old_memory;
}
new_memory := alloc(new_size, alignment, allocator, loc);
if new_memory == nil do return nil;
if new_memory == nil {
return nil;
}
copy(new_memory, old_memory, min(old_size, new_size));
free(old_memory, allocator, loc);
return new_memory;
}
default_resize_bytes_align :: proc(old_data: []byte, new_size, alignment: int, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
old_memory := raw_data(old_data);
old_size := len(old_data);
if old_memory == nil {
return alloc_bytes(new_size, alignment, allocator, loc);
}
if new_size == 0 {
err := free_bytes(old_data, allocator, loc);
return nil, err;
}
if new_size == old_size {
return old_data, .None;
}
new_memory, err := alloc_bytes(new_size, alignment, allocator, loc);
if new_memory == nil || err != nil {
return nil, err;
}
runtime.copy(new_memory, old_data);
free_bytes(old_data, allocator, loc);
return new_memory, err;
}
+352 -221
View File
@@ -5,8 +5,8 @@ import "core:runtime"
nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
return nil;
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
return nil, nil;
}
nil_allocator :: proc() -> Allocator {
@@ -47,7 +47,7 @@ arena_allocator :: proc(arena: ^Arena) -> Allocator {
arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr {
old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, Allocator_Error) {
arena := cast(^Arena)allocator_data;
switch mode {
@@ -55,7 +55,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
total_size := size + alignment;
if arena.offset + total_size > len(arena.data) {
return nil;
return nil, .Out_Of_Memory;
}
#no_bounds_check end := &arena.data[arena.offset];
@@ -63,7 +63,8 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
ptr := align_forward(end, uintptr(alignment));
arena.offset += total_size;
arena.peak_used = max(arena.peak_used, arena.offset);
return zero(ptr, size);
zero(ptr, size);
return byte_slice(ptr, size), nil;
case .Free:
// NOTE(bill): Free all at once
@@ -73,20 +74,20 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
arena.offset = 0;
case .Resize:
return default_resize_align(old_memory, old_size, size, alignment, arena_allocator(arena));
return default_resize_bytes_align(byte_slice(old_memory, old_size), size, alignment, arena_allocator(arena));
case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory);
if set != nil {
set^ = {.Alloc, .Free_All, .Resize, .Query_Features};
}
return set;
return nil, nil;
case .Query_Info:
return nil;
return nil, nil;
}
return nil;
return nil, nil;
}
begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory {
@@ -107,131 +108,177 @@ end_arena_temp_memory :: proc(using tmp: Arena_Temp_Memory) {
Scratch_Allocator :: struct {
data: []byte,
curr_offset: int,
prev_offset: int,
backup_allocator: Allocator,
leaked_allocations: [dynamic]rawptr,
default_to_default_allocator: bool,
data: []byte,
curr_offset: int,
prev_allocation: rawptr,
backup_allocator: Allocator,
leaked_allocations: [dynamic][]byte,
}
scratch_allocator_init :: proc(scratch: ^Scratch_Allocator, data: []byte, backup_allocator := context.allocator) {
scratch.data = data;
scratch.curr_offset = 0;
scratch.prev_offset = 0;
scratch.backup_allocator = backup_allocator;
scratch_allocator_init :: proc(s: ^Scratch_Allocator, size: int, backup_allocator := context.allocator) {
s.data = make_aligned([]byte, size, 2*align_of(rawptr), backup_allocator);
s.curr_offset = 0;
s.prev_allocation = nil;
s.backup_allocator = backup_allocator;
s.leaked_allocations.allocator = backup_allocator;
}
scratch_allocator_destroy :: proc(using scratch: ^Scratch_Allocator) {
if scratch == nil {
scratch_allocator_destroy :: proc(s: ^Scratch_Allocator) {
if s == nil {
return;
}
for ptr in leaked_allocations {
free(ptr, backup_allocator);
for ptr in s.leaked_allocations {
free_bytes(ptr, s.backup_allocator);
}
delete(leaked_allocations);
delete(data, backup_allocator);
scratch^ = {};
delete(s.leaked_allocations);
delete(s.data, s.backup_allocator);
s^ = {};
}
scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
scratch := (^Scratch_Allocator)(allocator_data);
s := (^Scratch_Allocator)(allocator_data);
if scratch.data == nil {
DEFAULT_SCRATCH_BACKING_SIZE :: 1<<22;
if s.data == nil {
DEFAULT_BACKING_SIZE :: 1<<22;
if !(context.allocator.procedure != scratch_allocator_proc &&
context.allocator.data != allocator_data) {
panic("cyclic initialization of the scratch allocator with itself");
}
scratch_allocator_init(scratch, make([]byte, 1<<22));
scratch_allocator_init(s, DEFAULT_BACKING_SIZE);
}
size := size;
switch mode {
case .Alloc:
size = align_forward_int(size, alignment);
switch {
case scratch.curr_offset+size <= len(scratch.data):
offset := align_forward_uintptr(uintptr(scratch.curr_offset), uintptr(alignment));
ptr := &scratch.data[offset];
zero(ptr, size);
scratch.prev_offset = int(offset);
scratch.curr_offset = int(offset) + size;
return ptr;
case size <= len(scratch.data):
offset := align_forward_uintptr(uintptr(0), uintptr(alignment));
ptr := &scratch.data[offset];
zero(ptr, size);
scratch.prev_offset = int(offset);
scratch.curr_offset = int(offset) + size;
return ptr;
case s.curr_offset+size <= len(s.data):
start := uintptr(raw_data(s.data));
ptr := start + uintptr(s.curr_offset);
ptr = align_forward_uintptr(ptr, uintptr(alignment));
zero(rawptr(ptr), size);
s.prev_allocation = rawptr(ptr);
offset := int(ptr - start);
s.curr_offset = offset + size;
return byte_slice(rawptr(ptr), size), nil;
case size <= len(s.data):
start := uintptr(raw_data(s.data));
ptr := align_forward_uintptr(start, uintptr(alignment));
zero(rawptr(ptr), size);
s.prev_allocation = rawptr(ptr);
offset := int(ptr - start);
s.curr_offset = offset + size;
return byte_slice(rawptr(ptr), size), nil;
}
// TODO(bill): Should leaks be notified about? Should probably use a logging system that is built into the context system
a := scratch.backup_allocator;
a := s.backup_allocator;
if a.procedure == nil {
a = context.allocator;
scratch.backup_allocator = a;
s.backup_allocator = a;
}
ptr := alloc(size, alignment, a, loc);
if scratch.leaked_allocations == nil {
scratch.leaked_allocations = make([dynamic]rawptr, a);
ptr, err := alloc_bytes(size, alignment, a, loc);
if err != nil {
return ptr, err;
}
append(&scratch.leaked_allocations, ptr);
if s.leaked_allocations == nil {
s.leaked_allocations = make([dynamic][]byte, a);
}
append(&s.leaked_allocations, ptr);
return ptr;
if logger := context.logger; logger.lowest_level <= .Warning {
if logger.procedure != nil {
logger.procedure(logger.data, .Warning, "mem.Scratch_Allocator resorted to backup_allocator" , logger.options, loc);
}
}
return ptr, err;
case .Free:
last_ptr := rawptr(&scratch.data[scratch.prev_offset]);
if old_memory == last_ptr {
full_size := scratch.curr_offset - scratch.prev_offset;
scratch.curr_offset = scratch.prev_offset;
zero(last_ptr, full_size);
return nil;
start := uintptr(raw_data(s.data));
end := start + uintptr(len(s.data));
old_ptr := uintptr(old_memory);
if s.prev_allocation == old_memory {
s.curr_offset = int(uintptr(s.prev_allocation) - start);
s.prev_allocation = nil;
return nil, nil;
}
// NOTE(bill): It's scratch memory, don't worry about freeing
if start <= old_ptr && old_ptr < end {
// NOTE(bill): Cannot free this pointer but it is valid
return nil, nil;
}
if len(s.leaked_allocations) != 0 {
for data, i in s.leaked_allocations {
ptr := raw_data(data);
if ptr == old_memory {
free_bytes(data, s.backup_allocator);
ordered_remove(&s.leaked_allocations, i);
return nil, nil;
}
}
}
return nil, .Invalid_Pointer;
// panic("invalid pointer passed to default_temp_allocator");
case .Free_All:
scratch.curr_offset = 0;
scratch.prev_offset = 0;
for ptr in scratch.leaked_allocations {
free(ptr, scratch.backup_allocator);
s.curr_offset = 0;
s.prev_allocation = nil;
for ptr in s.leaked_allocations {
free_bytes(ptr, s.backup_allocator);
}
clear(&scratch.leaked_allocations);
clear(&s.leaked_allocations);
case .Resize:
last_ptr := rawptr(&scratch.data[scratch.prev_offset]);
if old_memory == last_ptr && len(scratch.data)-scratch.prev_offset >= size {
scratch.curr_offset = scratch.prev_offset+size;
return old_memory;
begin := uintptr(raw_data(s.data));
end := begin + uintptr(len(s.data));
old_ptr := uintptr(old_memory);
if begin <= old_ptr && old_ptr < end && old_ptr+uintptr(size) < end {
s.curr_offset = int(old_ptr-begin)+size;
return byte_slice(old_memory, size), nil;
}
return scratch_allocator_proc(allocator_data, Allocator_Mode.Alloc, size, alignment, old_memory, old_size, flags, loc);
data, err := scratch_allocator_proc(allocator_data, .Alloc, size, alignment, old_memory, old_size, loc);
if err != nil {
return data, err;
}
runtime.copy(data, byte_slice(old_memory, old_size));
_, err = scratch_allocator_proc(allocator_data, .Free, 0, alignment, old_memory, old_size, loc);
return data, err;
case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory);
if set != nil {
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
}
return set;
return nil, nil;
case .Query_Info:
return nil;
return nil, nil;
}
return nil;
return nil, nil;
}
scratch_allocator :: proc(scratch: ^Scratch_Allocator) -> Allocator {
scratch_allocator :: proc(allocator: ^Scratch_Allocator) -> Allocator {
return Allocator{
procedure = scratch_allocator_proc,
data = scratch,
data = allocator,
};
}
Stack_Allocation_Header :: struct {
prev_offset: int,
padding: int,
@@ -262,32 +309,33 @@ stack_allocator :: proc(stack: ^Stack) -> Allocator {
stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr {
old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, Allocator_Error) {
s := cast(^Stack)allocator_data;
if s.data == nil {
return nil;
return nil, .Invalid_Argument;
}
raw_alloc :: proc(s: ^Stack, size, alignment: int) -> rawptr {
raw_alloc :: proc(s: ^Stack, size, alignment: int) -> ([]byte, Allocator_Error) {
curr_addr := uintptr(raw_data(s.data)) + uintptr(s.curr_offset);
padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Stack_Allocation_Header));
if s.curr_offset + padding + size > len(s.data) {
return nil;
return nil, .Out_Of_Memory;
}
s.prev_offset = s.curr_offset;
s.curr_offset += padding;
next_addr := curr_addr + uintptr(padding);
header := (^Stack_Allocation_Header)(next_addr - size_of(Stack_Allocation_Header));
header.padding = auto_cast padding;
header.prev_offset = auto_cast s.prev_offset;
header.padding = padding;
header.prev_offset = s.prev_offset;
s.curr_offset += size;
s.peak_used = max(s.peak_used, s.curr_offset);
return zero(rawptr(next_addr), size);
zero(rawptr(next_addr), size);
return byte_slice(rawptr(next_addr), size), nil;
}
switch mode {
@@ -295,7 +343,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return raw_alloc(s, size, alignment);
case .Free:
if old_memory == nil {
return nil;
return nil, nil;
}
start := uintptr(raw_data(s.data));
end := start + uintptr(len(s.data));
@@ -307,19 +355,19 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
if curr_addr >= start+uintptr(s.curr_offset) {
// NOTE(bill): Allow double frees
return nil;
return nil, nil;
}
header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header));
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)));
if old_offset != int(header.prev_offset) {
panic("Out of order stack allocator free");
if old_offset != header.prev_offset {
// panic("Out of order stack allocator free");
return nil, .Invalid_Pointer;
}
s.curr_offset = int(old_offset);
s.prev_offset = int(header.prev_offset);
s.curr_offset = old_offset;
s.prev_offset = header.prev_offset;
case .Free_All:
s.prev_offset = 0;
@@ -330,7 +378,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return raw_alloc(s, size, alignment);
}
if size == 0 {
return nil;
return nil, nil;
}
start := uintptr(raw_data(s.data));
@@ -342,20 +390,22 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
if curr_addr >= start+uintptr(s.curr_offset) {
// NOTE(bill): Allow double frees
return nil;
return nil, nil;
}
if old_size == size {
return old_memory;
return byte_slice(old_memory, size), nil;
}
header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header));
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)));
if old_offset != int(header.prev_offset) {
ptr := raw_alloc(s, size, alignment);
copy(ptr, old_memory, min(old_size, size));
return ptr;
if old_offset != header.prev_offset {
data, err := raw_alloc(s, size, alignment);
if err == nil {
runtime.copy(data, byte_slice(old_memory, old_size));
}
return data, err;
}
old_memory_size := uintptr(s.curr_offset) - (curr_addr - start);
@@ -367,19 +417,19 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
zero(rawptr(curr_addr + uintptr(diff)), diff);
}
return old_memory;
return byte_slice(old_memory, size), nil;
case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory);
if set != nil {
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
}
return set;
return nil, nil;
case .Query_Info:
return nil;
return nil, nil;
}
return nil;
return nil, nil;
}
@@ -414,20 +464,20 @@ small_stack_allocator :: proc(stack: ^Small_Stack) -> Allocator {
small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr {
old_memory: rawptr, old_size: int, ocation := #caller_location) -> ([]byte, Allocator_Error) {
s := cast(^Small_Stack)allocator_data;
if s.data == nil {
return nil;
return nil, .Invalid_Argument;
}
align := clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2);
raw_alloc :: proc(s: ^Small_Stack, size, alignment: int) -> rawptr {
raw_alloc :: proc(s: ^Small_Stack, size, alignment: int) -> ([]byte, Allocator_Error) {
curr_addr := uintptr(raw_data(s.data)) + uintptr(s.offset);
padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Small_Stack_Allocation_Header));
if s.offset + padding + size > len(s.data) {
return nil;
return nil, .Out_Of_Memory;
}
s.offset += padding;
@@ -439,7 +489,8 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
s.peak_used = max(s.peak_used, s.offset);
return zero(rawptr(next_addr), size);
zero(rawptr(next_addr), size);
return byte_slice(rawptr(next_addr), size), nil;
}
switch mode {
@@ -447,25 +498,26 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return raw_alloc(s, size, align);
case .Free:
if old_memory == nil {
return nil;
return nil, nil;
}
start := uintptr(raw_data(s.data));
end := start + uintptr(len(s.data));
curr_addr := uintptr(old_memory);
if !(start <= curr_addr && curr_addr < end) {
panic("Out of bounds memory address passed to stack allocator (free)");
// panic("Out of bounds memory address passed to stack allocator (free)");
return nil, .Invalid_Pointer;
}
if curr_addr >= start+uintptr(s.offset) {
// NOTE(bill): Allow double frees
return nil;
return nil, nil;
}
header := (^Small_Stack_Allocation_Header)(curr_addr - size_of(Small_Stack_Allocation_Header));
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)));
s.offset = int(old_offset);
s.offset = old_offset;
case .Free_All:
s.offset = 0;
@@ -475,41 +527,44 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return raw_alloc(s, size, align);
}
if size == 0 {
return nil;
return nil, nil;
}
start := uintptr(raw_data(s.data));
end := start + uintptr(len(s.data));
curr_addr := uintptr(old_memory);
if !(start <= curr_addr && curr_addr < end) {
panic("Out of bounds memory address passed to stack allocator (resize)");
// panic("Out of bounds memory address passed to stack allocator (resize)");
return nil, .Invalid_Pointer;
}
if curr_addr >= start+uintptr(s.offset) {
// NOTE(bill): Treat as a double free
return nil;
return nil, nil;
}
if old_size == size {
return old_memory;
return byte_slice(old_memory, size), nil;
}
ptr := raw_alloc(s, size, align);
copy(ptr, old_memory, min(old_size, size));
return ptr;
data, err := raw_alloc(s, size, align);
if err == nil {
runtime.copy(data, byte_slice(old_memory, old_size));
}
return data, err;
case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory);
if set != nil {
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
}
return set;
return nil, nil;
case .Query_Info:
return nil;
return nil, nil;
}
return nil;
return nil, nil;
}
@@ -540,42 +595,44 @@ DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT :: 6554;
dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int,
flags: u64 = 0, loc := #caller_location) -> rawptr {
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
pool := (^Dynamic_Pool)(allocator_data);
switch mode {
case .Alloc:
return dynamic_pool_alloc(pool, size);
return dynamic_pool_alloc_bytes(pool, size);
case .Free:
//
return nil, nil;
case .Free_All:
dynamic_pool_free_all(pool);
return nil, nil;
case .Resize:
if old_size >= size {
return old_memory;
return byte_slice(old_memory, size), nil;
}
ptr := dynamic_pool_alloc(pool, size);
copy(ptr, old_memory, old_size);
return ptr;
data, err := dynamic_pool_alloc_bytes(pool, size);
if err == nil {
runtime.copy(data, byte_slice(old_memory, old_size));
}
return data, err;
case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory);
if set != nil {
set^ = {.Alloc, .Free_All, .Resize, .Query_Features, .Query_Info};
}
return set;
return nil, nil;
case .Query_Info:
info := (^Allocator_Query_Info)(old_memory);
if info != nil && info.pointer != nil {
info.size = pool.block_size;
info.alignment = pool.alignment;
return info;
return byte_slice(info, size_of(info^)), nil;
}
return nil;
return nil, nil;
}
return nil;
return nil, nil;
}
@@ -610,8 +667,14 @@ dynamic_pool_destroy :: proc(using pool: ^Dynamic_Pool) {
}
dynamic_pool_alloc :: proc(using pool: ^Dynamic_Pool, bytes: int) -> rawptr {
cycle_new_block :: proc(using pool: ^Dynamic_Pool) {
dynamic_pool_alloc :: proc(pool: ^Dynamic_Pool, bytes: int) -> rawptr {
data, err := dynamic_pool_alloc_bytes(pool, bytes);
assert(err == nil);
return raw_data(data);
}
dynamic_pool_alloc_bytes :: proc(using pool: ^Dynamic_Pool, bytes: int) -> ([]byte, Allocator_Error) {
cycle_new_block :: proc(using pool: ^Dynamic_Pool) -> (err: Allocator_Error) {
if block_allocator.procedure == nil {
panic("You must call pool_init on a Pool before using it");
}
@@ -624,42 +687,47 @@ dynamic_pool_alloc :: proc(using pool: ^Dynamic_Pool, bytes: int) -> rawptr {
if len(unused_blocks) > 0 {
new_block = pop(&unused_blocks);
} else {
new_block = block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
block_size, alignment,
nil, 0);
data: []byte;
data, err = block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
block_size, alignment,
nil, 0);
new_block = raw_data(data);
}
bytes_left = block_size;
current_pos = new_block;
current_block = new_block;
return;
}
n := bytes;
extra := alignment - (n % alignment);
n += extra;
if n >= out_band_size {
assert(block_allocator.procedure != nil);
memory := block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
memory, err := block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
block_size, alignment,
nil, 0);
if memory != nil {
append(&out_band_allocations, (^byte)(memory));
append(&out_band_allocations, raw_data(memory));
}
return memory;
return memory, err;
}
if bytes_left < n {
cycle_new_block(pool);
err := cycle_new_block(pool);
if err != nil {
return nil, err;
}
if current_block == nil {
return nil;
return nil, .Out_Of_Memory;
}
}
memory := current_pos;
current_pos = ptr_offset((^byte)(current_pos), n);
bytes_left -= n;
return memory;
return byte_slice(memory, bytes), nil;
}
@@ -692,7 +760,7 @@ dynamic_pool_free_all :: proc(using pool: ^Dynamic_Pool) {
panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
old_memory: rawptr, old_size: int,loc := #caller_location) -> ([]byte, Allocator_Error) {
switch mode {
case .Alloc:
@@ -715,13 +783,13 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
if set != nil {
set^ = {.Query_Features};
}
return set;
return nil, nil;
case .Query_Info:
return nil;
return nil, nil;
}
return nil;
return nil, nil;
}
panic_allocator :: proc() -> Allocator {
@@ -732,84 +800,33 @@ panic_allocator :: proc() -> Allocator {
}
alloca_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
switch mode {
case .Alloc:
switch alignment {
case: return intrinsics.alloca(size, 2*align_of(uintptr));
case 0: return intrinsics.alloca(size, 0);
case 1: return intrinsics.alloca(size, 1);
case 2: return intrinsics.alloca(size, 2);
case 4: return intrinsics.alloca(size, 4);
case 8: return intrinsics.alloca(size, 8);
case 16: return intrinsics.alloca(size, 16);
case 32: return intrinsics.alloca(size, 32);
case 64: return intrinsics.alloca(size, 64);
case 128: return intrinsics.alloca(size, 128);
case 256: return intrinsics.alloca(size, 256);
case 512: return intrinsics.alloca(size, 512);
case 1024: return intrinsics.alloca(size, 1024);
case 2048: return intrinsics.alloca(size, 2048);
case 4096: return intrinsics.alloca(size, 4096);
case 8192: return intrinsics.alloca(size, 8192);
case 16384: return intrinsics.alloca(size, 16384);
case 32768: return intrinsics.alloca(size, 32768);
case 65536: return intrinsics.alloca(size, 65536);
}
case .Resize:
return default_resize_align(old_memory, old_size, size, alignment, alloca_allocator());
case .Free:
// Do nothing
case .Free_All:
// Do nothing
case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory);
if set != nil {
set^ = {.Alloc, .Resize, .Query_Features};
}
return set;
case .Query_Info:
return nil;
}
return nil;
}
alloca_allocator :: proc() -> Allocator {
return Allocator{
procedure = alloca_allocator_proc,
data = nil,
};
}
Tracking_Allocator_Entry :: struct {
memory: rawptr,
size: int,
alignment: int,
err: Allocator_Error,
location: runtime.Source_Code_Location,
}
Tracking_Allocator_Bad_Free_Entry :: struct {
memory: rawptr,
location: runtime.Source_Code_Location,
}
Tracking_Allocator :: struct {
backing: Allocator,
allocation_map: map[rawptr]Tracking_Allocator_Entry,
bad_free_array: [dynamic]Tracking_Allocator_Bad_Free_Entry,
clear_on_free_all: bool,
}
tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Allocator, allocation_map_allocator := context.allocator) {
tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Allocator, internals_allocator := context.allocator) {
t.backing = backing_allocator;
t.allocation_map.allocator = allocation_map_allocator;
t.allocation_map.allocator = internals_allocator;
t.bad_free_array.allocator = internals_allocator;
}
tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
delete(t.allocation_map);
delete(t.bad_free_array);
}
tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
@@ -819,7 +836,9 @@ tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
};
}
tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
data := (^Tracking_Allocator)(allocator_data);
if mode == .Query_Info {
info := (^Allocator_Query_Info)(old_memory);
@@ -827,15 +846,27 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, si
if entry, ok := data.allocation_map[info.pointer]; ok {
info.size = entry.size;
info.alignment = entry.alignment;
return info;
}
info.pointer = nil;
}
return nil;
return nil, nil;
}
result := data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, flags, loc);
result: []byte;
err: Allocator_Error;
if mode == .Free && old_memory not_in data.allocation_map {
append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
memory = old_memory,
location = loc,
});
} else {
result, err = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc);
if err != nil {
return result, err;
}
}
result_ptr := raw_data(result);
if data.allocation_map.allocator.procedure == nil {
data.allocation_map.allocator = context.allocator;
@@ -843,22 +874,24 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, si
switch mode {
case .Alloc:
data.allocation_map[result] = Tracking_Allocator_Entry{
memory = result,
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
memory = result_ptr,
size = size,
alignment = alignment,
err = err,
location = loc,
};
case .Free:
delete_key(&data.allocation_map, old_memory);
case .Resize:
if old_memory != result {
if old_memory != result_ptr {
delete_key(&data.allocation_map, old_memory);
}
data.allocation_map[result] = Tracking_Allocator_Entry{
memory = result,
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
memory = result_ptr,
size = size,
alignment = alignment,
err = err,
location = loc,
};
@@ -872,11 +905,109 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, si
if set != nil {
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features, .Query_Info};
}
return set;
return nil, nil;
case .Query_Info:
unreachable();
return nil, nil;
}
return result;
return result, err;
}
// Small_Allocator primary allocates memory from its local buffer of size BUFFER_SIZE
// If that buffer's memory is exhausted, it will use the backing allocator (a scratch allocator is recommended)
// Memory allocated with Small_Allocator cannot be freed individually using 'free' and must be freed using 'free_all'
Small_Allocator :: struct(BUFFER_SIZE: int)
where
BUFFER_SIZE >= 2*size_of(uintptr),
BUFFER_SIZE & (BUFFER_SIZE-1) == 0 {
buffer: [BUFFER_SIZE]byte,
backing: Allocator,
start: uintptr,
curr: uintptr,
end: uintptr,
chunk_size: int,
}
small_allocator :: proc(s: ^$S/Small_Allocator, backing := context.allocator) -> (a: Allocator) {
if s.backing.procedure == nil {
s.backing = backing;
}
a.data = s;
a.procedure = proc(allocator_data: rawptr, mode: Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
s := (^S)(allocator_data);
if s.chunk_size <= 0 {
s.chunk_size = 4*1024;
}
if s.start == 0 {
s.start = uintptr(&s.buffer[0]);
s.curr = s.start;
s.end = s.start + uintptr(S.BUFFER_SIZE);
(^rawptr)(s.start)^ = nil;
s.curr += size_of(rawptr);
}
switch mode {
case .Alloc:
s.curr = align_forward_uintptr(s.curr, uintptr(alignment));
if size > int(s.end - s.curr) {
to_allocate := size_of(rawptr) + size + alignment;
if to_allocate < s.chunk_size {
to_allocate = s.chunk_size;
}
s.chunk_size *= 2;
p := alloc(to_allocate, 16, s.backing, loc);
(^rawptr)(s.start)^ = p;
s.start = uintptr(p);
s.curr = s.start;
s.end = s.start + uintptr(to_allocate);
(^rawptr)(s.start)^ = nil;
s.curr += size_of(rawptr);
s.curr = align_forward_uintptr(s.curr, uintptr(alignment));
}
p := rawptr(s.curr);
s.curr += uintptr(size);
return mem_zero(p, size);
case .Free:
// NOP
return nil;
case .Resize:
// No need copying the code
return default_resize_align(old_memory, old_size, size, alignment, small_allocator(s, s.backing), loc);
case .Free_All:
p := (^rawptr)(&s.buffer[0])^;
for p != nil {
next := (^rawptr)(p)^;
free(next, s.backing, loc);
p = next;
}
// Reset to default
s.start = uintptr(&s.buffer[0]);
s.curr = s.start;
s.end = s.start + uintptr(S.BUFFER_SIZE);
(^rawptr)(s.start)^ = nil;
s.curr += size_of(rawptr);
case .Query_Features:
return nil, nil;
case .Query_Info:
return nil, nil;
}
return nil, nil;
};
return a;
}
+47 -31
View File
@@ -6,10 +6,10 @@ import "core:intrinsics"
set :: proc(data: rawptr, value: byte, len: int) -> rawptr {
return runtime.memset(data, i32(value), len);
}
zero :: inline proc(data: rawptr, len: int) -> rawptr {
zero :: proc(data: rawptr, len: int) -> rawptr {
return set(data, 0, len);
}
zero_item :: inline proc(item: $P/^$T) {
zero_item :: proc(item: $P/^$T) {
set(item, 0, size_of(T));
}
zero_slice :: proc(data: $T/[]$E) {
@@ -23,7 +23,7 @@ copy :: proc(dst, src: rawptr, len: int) -> rawptr {
copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr {
return runtime.mem_copy_non_overlapping(dst, src, len);
}
compare :: inline proc(a, b: []byte) -> int {
compare :: proc(a, b: []byte) -> int {
res := compare_byte_ptrs(raw_data(a), raw_data(b), min(len(a), len(b)));
if res == 0 && len(a) != len(b) {
return len(a) <= len(b) ? -1 : +1;
@@ -121,36 +121,39 @@ simple_equal :: proc(a, b: $T) -> bool where intrinsics.type_is_simple_compare(T
return compare_byte_ptrs((^byte)(&a), (^byte)(&b), size_of(T)) == 0;
}
compare_ptrs :: inline proc(a, b: rawptr, n: int) -> int {
compare_ptrs :: proc(a, b: rawptr, n: int) -> int {
return compare_byte_ptrs((^byte)(a), (^byte)(b), n);
}
ptr_offset :: inline proc(ptr: $P/^$T, n: int) -> P {
ptr_offset :: proc(ptr: $P/^$T, n: int) -> P {
new := int(uintptr(ptr)) + size_of(T)*n;
return P(uintptr(new));
}
ptr_sub :: inline proc(a, b: $P/^$T) -> int {
ptr_sub :: proc(a, b: $P/^$T) -> int {
return (int(uintptr(a)) - int(uintptr(b)))/size_of(T);
}
slice_ptr :: inline proc(ptr: ^$T, len: int) -> []T {
slice_ptr :: proc(ptr: ^$T, len: int) -> []T {
assert(len >= 0);
return transmute([]T)Raw_Slice{data = ptr, len = len};
}
slice_ptr_to_bytes :: proc(ptr: rawptr, len: int) -> []byte {
assert(len >= 0);
return transmute([]byte)Raw_Slice{data = ptr, len = len};
byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte {
return transmute([]u8)Raw_Slice{data=data, len=max(len, 0)};
}
slice_ptr_to_bytes :: proc(data: rawptr, len: int) -> []byte {
return transmute([]u8)Raw_Slice{data=data, len=max(len, 0)};
}
slice_to_bytes :: inline proc(slice: $E/[]$T) -> []byte {
slice_to_bytes :: proc(slice: $E/[]$T) -> []byte {
s := transmute(Raw_Slice)slice;
s.len *= size_of(T);
return transmute([]byte)s;
}
slice_data_cast :: inline proc($T: typeid/[]$A, slice: $S/[]$B) -> T {
slice_data_cast :: proc($T: typeid/[]$A, slice: $S/[]$B) -> T {
when size_of(A) == 0 || size_of(B) == 0 {
return nil;
} else {
@@ -165,7 +168,7 @@ slice_to_components :: proc(slice: $E/[]$T) -> (data: ^T, len: int) {
return s.data, s.len;
}
buffer_from_slice :: inline proc(backing: $T/[]$E) -> [dynamic]E {
buffer_from_slice :: proc(backing: $T/[]$E) -> [dynamic]E {
return transmute([dynamic]E)Raw_Dynamic_Array{
data = raw_data(backing),
len = 0,
@@ -174,29 +177,31 @@ buffer_from_slice :: inline proc(backing: $T/[]$E) -> [dynamic]E {
};
}
ptr_to_bytes :: inline proc(ptr: ^$T, len := 1) -> []byte {
ptr_to_bytes :: proc(ptr: ^$T, len := 1) -> []byte {
assert(len >= 0);
return transmute([]byte)Raw_Slice{ptr, len*size_of(T)};
}
any_to_bytes :: inline proc(val: any) -> []byte {
any_to_bytes :: proc(val: any) -> []byte {
ti := type_info_of(val.id);
size := ti != nil ? ti.size : 0;
return transmute([]byte)Raw_Slice{val.data, size};
}
kilobytes :: inline proc(x: int) -> int do return (x) * 1024;
megabytes :: inline proc(x: int) -> int do return kilobytes(x) * 1024;
gigabytes :: inline proc(x: int) -> int do return megabytes(x) * 1024;
terabytes :: inline proc(x: int) -> int do return gigabytes(x) * 1024;
kilobytes :: proc(x: int) -> int { return (x) * 1024; }
megabytes :: proc(x: int) -> int { return kilobytes(x) * 1024; }
gigabytes :: proc(x: int) -> int { return megabytes(x) * 1024; }
terabytes :: proc(x: int) -> int { return gigabytes(x) * 1024; }
is_power_of_two :: inline proc(x: uintptr) -> bool {
if x <= 0 do return false;
is_power_of_two :: proc(x: uintptr) -> bool {
if x <= 0 {
return false;
}
return (x & (x-1)) == 0;
}
align_forward :: inline proc(ptr: rawptr, align: uintptr) -> rawptr {
align_forward :: proc(ptr: rawptr, align: uintptr) -> rawptr {
return rawptr(align_forward_uintptr(uintptr(ptr), align));
}
@@ -205,18 +210,20 @@ align_forward_uintptr :: proc(ptr, align: uintptr) -> uintptr {
p := ptr;
modulo := p & (align-1);
if modulo != 0 do p += align - modulo;
if modulo != 0 {
p += align - modulo;
}
return p;
}
align_forward_int :: inline proc(ptr, align: int) -> int {
align_forward_int :: proc(ptr, align: int) -> int {
return int(align_forward_uintptr(uintptr(ptr), uintptr(align)));
}
align_forward_uint :: inline proc(ptr, align: uint) -> uint {
align_forward_uint :: proc(ptr, align: uint) -> uint {
return uint(align_forward_uintptr(uintptr(ptr), uintptr(align)));
}
align_backward :: inline proc(ptr: rawptr, align: uintptr) -> rawptr {
align_backward :: proc(ptr: rawptr, align: uintptr) -> rawptr {
return rawptr(align_backward_uintptr(uintptr(ptr), align));
}
@@ -225,10 +232,10 @@ align_backward_uintptr :: proc(ptr, align: uintptr) -> uintptr {
return align_forward_uintptr(ptr - align + 1, align);
}
align_backward_int :: inline proc(ptr, align: int) -> int {
align_backward_int :: proc(ptr, align: int) -> int {
return int(align_backward_uintptr(uintptr(ptr), uintptr(align)));
}
align_backward_uint :: inline proc(ptr, align: uint) -> uint {
align_backward_uint :: proc(ptr, align: uint) -> uint {
return uint(align_backward_uintptr(uintptr(ptr), uintptr(align)));
}
@@ -259,12 +266,13 @@ align_formula :: proc(size, align: int) -> int {
}
calc_padding_with_header :: proc(ptr: uintptr, align: uintptr, header_size: int) -> int {
p := uintptr(ptr);
a := uintptr(align);
p, a := ptr, align;
modulo := p & (a-1);
padding := uintptr(0);
if modulo != 0 do padding = a - modulo;
if modulo != 0 {
padding = a - modulo;
}
needed_space := uintptr(header_size);
if padding < needed_space {
@@ -279,3 +287,11 @@ calc_padding_with_header :: proc(ptr: uintptr, align: uintptr, header_size: int)
return int(padding);
}
clone_slice :: proc(slice: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> T {
new_slice := make(T, len(slice), allocator, loc);
runtime.copy(new_slice, slice);
return new_slice;
}
+4 -4
View File
@@ -38,20 +38,20 @@ Raw_Quaternion256 :: struct {imag, jmag, kmag: f64, real: f64};
Raw_Quaternion128_Vector_Scalar :: struct {vector: [3]f32, scalar: f32};
Raw_Quaternion256_Vector_Scalar :: struct {vector: [3]f64, scalar: f64};
make_any :: inline proc(data: rawptr, id: typeid) -> any {
make_any :: proc(data: rawptr, id: typeid) -> any {
return transmute(any)Raw_Any{data, id};
}
raw_array_data :: proc(a: $P/^($T/[$N]$E)) -> ^E {
return (^E)(a);
}
raw_string_data :: inline proc(s: $T/string) -> ^byte {
raw_string_data :: proc(s: $T/string) -> ^byte {
return (transmute(Raw_String)s).data;
}
raw_slice_data :: inline proc(a: $T/[]$E) -> ^E {
raw_slice_data :: proc(a: $T/[]$E) -> ^E {
return cast(^E)(transmute(Raw_Slice)a).data;
}
raw_dynamic_array_data :: inline proc(a: $T/[dynamic]$E) -> ^E {
raw_dynamic_array_data :: proc(a: $T/[dynamic]$E) -> ^E {
return cast(^E)(transmute(Raw_Dynamic_Array)a).data;
}
+120 -36
View File
@@ -5,6 +5,8 @@ import "core:odin/tokenizer"
Proc_Tag :: enum {
Bounds_Check,
No_Bounds_Check,
Optional_Ok,
Optional_Second,
}
Proc_Tags :: distinct bit_set[Proc_Tag; u32];
@@ -21,6 +23,7 @@ Proc_Calling_Convention :: enum i32 {
C_Decl,
Std_Call,
Fast_Call,
None,
Foreign_Block_Default = -1,
}
@@ -31,18 +34,61 @@ Node_State_Flag :: enum {
}
Node_State_Flags :: distinct bit_set[Node_State_Flag];
Comment_Group :: struct {
list: []tokenizer.Token,
}
Node :: struct {
pos: tokenizer.Pos,
end: tokenizer.Pos,
derived: any,
state_flags: Node_State_Flags,
derived: any,
}
Comment_Group :: struct {
using node: Node,
list: []tokenizer.Token,
}
Package_Kind :: enum {
Normal,
Runtime,
Init,
}
Package :: struct {
using node: Node,
kind: Package_Kind,
id: int,
name: string,
fullpath: string,
files: map[string]^File,
user_data: rawptr,
}
File :: struct {
using node: Node,
id: int,
pkg: ^Package,
fullpath: string,
src: []byte,
docs: ^Comment_Group,
pkg_decl: ^Package_Decl,
pkg_token: tokenizer.Token,
pkg_name: string,
decls: [dynamic]^Stmt,
imports: [dynamic]^Import_Decl,
directive_count: int,
comments: [dynamic]^Comment_Group,
syntax_warning_count: int,
syntax_error_count: int,
}
// Base Types
Expr :: struct {
using expr_base: Node,
@@ -142,6 +188,7 @@ Paren_Expr :: struct {
Selector_Expr :: struct {
using node: Expr,
expr: ^Expr,
op: tokenizer.Token,
field: ^Ident,
}
@@ -150,6 +197,13 @@ Implicit_Selector_Expr :: struct {
field: ^Ident,
}
Selector_Call_Expr :: struct {
using node: Expr,
expr: ^Expr,
call: ^Call_Expr,
modified_call: bool,
}
Index_Expr :: struct {
using node: Expr,
expr: ^Expr,
@@ -202,9 +256,9 @@ Ternary_Expr :: struct {
Ternary_If_Expr :: struct {
using node: Expr,
x: ^Expr,
x: ^Expr,
op1: tokenizer.Token,
cond: ^Expr,
cond: ^Expr,
op2: tokenizer.Token,
y: ^Expr,
}
@@ -213,7 +267,7 @@ Ternary_When_Expr :: struct {
using node: Expr,
x: ^Expr,
op1: tokenizer.Token,
cond: ^Expr,
cond: ^Expr,
op2: tokenizer.Token,
y: ^Expr,
}
@@ -242,6 +296,27 @@ Auto_Cast :: struct {
expr: ^Expr,
}
Inline_Asm_Dialect :: enum u8 {
Default = 0,
ATT = 1,
Intel = 2,
}
Inline_Asm_Expr :: struct {
using node: Expr,
tok: tokenizer.Token,
param_types: []^Expr,
return_type: ^Expr,
has_side_effects: bool,
is_align_stack: bool,
dialect: Inline_Asm_Dialect,
open: tokenizer.Pos,
constraints_string: ^Expr,
asm_string: ^Expr,
close: tokenizer.Pos,
}
@@ -292,6 +367,7 @@ If_Stmt :: struct {
init: ^Stmt,
cond: ^Expr,
body: ^Stmt,
else_pos: tokenizer.Pos,
else_stmt: ^Stmt,
}
@@ -327,13 +403,26 @@ Range_Stmt :: struct {
using node: Stmt,
label: ^Expr,
for_pos: tokenizer.Pos,
val0: ^Expr,
val1: ^Expr,
vals: []^Expr,
in_pos: tokenizer.Pos,
expr: ^Expr,
body: ^Stmt,
}
Inline_Range_Stmt :: struct {
using node: Stmt,
label: ^Expr,
inline_pos: tokenizer.Pos,
for_pos: tokenizer.Pos,
val0: ^Expr,
val1: ^Expr,
in_pos: tokenizer.Pos,
expr: ^Expr,
body: ^Stmt,
}
Case_Clause :: struct {
using node: Stmt,
@@ -424,12 +513,12 @@ Foreign_Block_Decl :: struct {
Foreign_Import_Decl :: struct {
using node: Decl,
docs: ^Comment_Group,
attributes: [dynamic]^Attribute, // dynamic as parsing will add to them lazily
foreign_tok: tokenizer.Token,
import_tok: tokenizer.Token,
name: ^Ident,
collection_name: string,
fullpaths: []string,
attributes: [dynamic]^Attribute, // dynamic as parsing will add to them lazily
comment: ^Comment_Group,
}
@@ -443,7 +532,9 @@ unparen_expr :: proc(expr: ^Expr) -> (val: ^Expr) {
}
for {
e, ok := val.derived.(Paren_Expr);
if !ok do break;
if !ok || e.expr == nil {
break;
}
val = e.expr;
}
return;
@@ -471,6 +562,7 @@ Field_Flags_Struct :: Field_Flags{
};
Field_Flags_Record_Poly_Params :: Field_Flags{
.Typeid_Token,
.Default_Parameters,
};
Field_Flags_Signature :: Field_Flags{
.Ellipsis,
@@ -539,12 +631,6 @@ Distinct_Type :: struct {
type: ^Expr,
}
Opaque_Type :: struct {
using node: Expr,
tok: tokenizer.Token_Kind,
type: ^Expr,
}
Poly_Type :: struct {
using node: Expr,
dollar: tokenizer.Pos,
@@ -593,23 +679,23 @@ Struct_Type :: struct {
tok_pos: tokenizer.Pos,
poly_params: ^Field_List,
align: ^Expr,
fields: ^Field_List,
name_count: int,
where_token: tokenizer.Token,
where_clauses: []^Expr,
is_packed: bool,
is_raw_union: bool,
fields: ^Field_List,
name_count: int,
}
Union_Type :: struct {
using node: Expr,
tok_pos: tokenizer.Pos,
poly_params: ^Field_List,
align: ^Expr,
variants: []^Expr,
where_token: tokenizer.Token,
tok_pos: tokenizer.Pos,
poly_params: ^Field_List,
align: ^Expr,
is_maybe: bool,
where_token: tokenizer.Token,
where_clauses: []^Expr,
is_maybe: bool,
variants: []^Expr,
}
Enum_Type :: struct {
@@ -623,15 +709,6 @@ Enum_Type :: struct {
is_using: bool,
}
Bit_Field_Type :: struct {
using node: Expr,
tok_pos: tokenizer.Pos,
align: ^Expr,
open: tokenizer.Pos,
fields: []^Field_Value, // Field_Value with ':' rather than '='
close: tokenizer.Pos,
}
Bit_Set_Type :: struct {
using node: Expr,
tok_pos: tokenizer.Pos,
@@ -647,3 +724,10 @@ Map_Type :: struct {
key: ^Expr,
value: ^Expr,
}
Relative_Type :: struct {
using node: Expr,
tag: ^Expr,
type: ^Expr,
}
+22 -65
View File
@@ -67,6 +67,11 @@ clone_node :: proc(node: ^Node) -> ^Node {
align = ti.align;
}
switch in node.derived {
case Package, File:
panic("Cannot clone this node type");
}
res := cast(^Node)mem.alloc(size, align);
src: rawptr = node;
if node.derived != nil {
@@ -74,8 +79,9 @@ clone_node :: proc(node: ^Node) -> ^Node {
}
mem.copy(res, src, size);
res.derived.data = rawptr(res);
res.derived.id = node.derived.id;
switch n in node.derived {
switch r in &res.derived {
case Bad_Expr:
case Ident:
case Implicit:
@@ -83,155 +89,131 @@ clone_node :: proc(node: ^Node) -> ^Node {
case Basic_Lit:
case Ellipsis:
r := cast(^Ellipsis)res;
r.expr = clone(r.expr);
case Proc_Lit:
r := cast(^Proc_Lit)res;
r.type = auto_cast clone(r.type);
r.body = clone(r.body);
case Comp_Lit:
r := cast(^Comp_Lit)res;
r.type = clone(r.type);
r.elems = clone(r.elems);
case Tag_Expr:
r := cast(^Tag_Expr)res;
r.expr = clone(r.expr);
case Unary_Expr:
r := cast(^Unary_Expr)res;
r.expr = clone(r.expr);
case Binary_Expr:
r := cast(^Binary_Expr)res;
r.left = clone(r.left);
r.right = clone(r.right);
case Paren_Expr:
r := cast(^Paren_Expr)res;
r.expr = clone(r.expr);
case Selector_Expr:
r := cast(^Selector_Expr)res;
r.expr = clone(r.expr);
r.field = auto_cast clone(r.field);
case Implicit_Selector_Expr:
r.field = auto_cast clone(r.field);
case Selector_Call_Expr:
r.expr = clone(r.expr);
r.call = auto_cast clone(r.call);
case Index_Expr:
r := cast(^Index_Expr)res;
r.expr = clone(r.expr);
r.index = clone(r.index);
case Deref_Expr:
r := cast(^Deref_Expr)res;
r.expr = clone(r.expr);
case Slice_Expr:
r := cast(^Slice_Expr)res;
r.expr = clone(r.expr);
r.low = clone(r.low);
r.high = clone(r.high);
case Call_Expr:
r := cast(^Call_Expr)res;
r.expr = clone(r.expr);
r.args = clone(r.args);
case Field_Value:
r := cast(^Field_Value)res;
r.field = clone(r.field);
r.value = clone(r.value);
case Ternary_Expr:
r := cast(^Ternary_Expr)res;
r.cond = clone(r.cond);
r.x = clone(r.x);
r.y = clone(r.y);
case Ternary_If_Expr:
r := cast(^Ternary_If_Expr)res;
r.x = clone(r.x);
r.cond = clone(r.cond);
r.y = clone(r.y);
case Ternary_When_Expr:
r := cast(^Ternary_When_Expr)res;
r.x = clone(r.x);
r.cond = clone(r.cond);
r.y = clone(r.y);
case Type_Assertion:
r := cast(^Type_Assertion)res;
r.expr = clone(r.expr);
r.type = clone(r.type);
case Type_Cast:
r := cast(^Type_Cast)res;
r.type = clone(r.type);
r.expr = clone(r.expr);
case Auto_Cast:
r := cast(^Auto_Cast)res;
r.expr = clone(r.expr);
case Inline_Asm_Expr:
r.param_types = clone(r.param_types);
r.return_type = clone(r.return_type);
r.constraints_string = clone(r.constraints_string);
r.asm_string = clone(r.asm_string);
case Bad_Stmt:
// empty
case Empty_Stmt:
// empty
case Expr_Stmt:
r := cast(^Expr_Stmt)res;
r.expr = clone(r.expr);
case Tag_Stmt:
r := cast(^Expr_Stmt)res;
r.expr = clone(r.expr);
r.stmt = clone(r.stmt);
case Assign_Stmt:
r := cast(^Assign_Stmt)res;
r.lhs = clone(r.lhs);
r.rhs = clone(r.rhs);
case Block_Stmt:
r := cast(^Block_Stmt)res;
r.label = auto_cast clone(r.label);
r.stmts = clone(r.stmts);
case If_Stmt:
r := cast(^If_Stmt)res;
r.label = auto_cast clone(r.label);
r.init = clone(r.init);
r.cond = clone(r.cond);
r.body = clone(r.body);
r.else_stmt = clone(r.else_stmt);
case When_Stmt:
r := cast(^When_Stmt)res;
r.cond = clone(r.cond);
r.body = clone(r.body);
r.else_stmt = clone(r.else_stmt);
case Return_Stmt:
r := cast(^Return_Stmt)res;
r.results = clone(r.results);
case Defer_Stmt:
r := cast(^Defer_Stmt)res;
r.stmt = clone(r.stmt);
case For_Stmt:
r := cast(^For_Stmt)res;
r.label = auto_cast clone(r.label);
r.init = clone(r.init);
r.cond = clone(r.cond);
r.post = clone(r.post);
r.body = clone(r.body);
case Range_Stmt:
r := cast(^Range_Stmt)res;
r.label = auto_cast clone(r.label);
r.val0 = clone(r.val0);
r.val1 = clone(r.val1);
r.vals = clone(r.vals);
r.expr = clone(r.expr);
r.body = clone(r.body);
case Case_Clause:
r := cast(^Case_Clause)res;
r.list = clone(r.list);
r.body = clone(r.body);
case Switch_Stmt:
r := cast(^Switch_Stmt)res;
r.label = auto_cast clone(r.label);
r.init = clone(r.init);
r.cond = clone(r.cond);
r.body = clone(r.body);
case Type_Switch_Stmt:
r := cast(^Type_Switch_Stmt)res;
r.label = auto_cast clone(r.label);
r.tag = clone(r.tag);
r.expr = clone(r.expr);
r.body = clone(r.body);
case Branch_Stmt:
r := cast(^Branch_Stmt)res;
r.label = auto_cast clone(r.label);
case Using_Stmt:
r := cast(^Using_Stmt)res;
r.list = clone(r.list);
case Bad_Decl:
case Value_Decl:
r := cast(^Value_Decl)res;
r.attributes = clone(r.attributes);
r.names = clone(r.names);
r.type = clone(r.type);
@@ -239,85 +221,60 @@ clone_node :: proc(node: ^Node) -> ^Node {
case Package_Decl:
case Import_Decl:
case Foreign_Block_Decl:
r := cast(^Foreign_Block_Decl)res;
r.attributes = clone(r.attributes);
r.foreign_library = clone(r.foreign_library);
r.body = clone(r.body);
case Foreign_Import_Decl:
r := cast(^Foreign_Import_Decl)res;
r.name = auto_cast clone(r.name);
case Proc_Group:
r := cast(^Proc_Group)res;
r.args = clone(r.args);
case Attribute:
r := cast(^Attribute)res;
r.elems = clone(r.elems);
case Field:
r := cast(^Field)res;
r.names = clone(r.names);
r.type = clone(r.type);
r.default_value = clone(r.default_value);
case Field_List:
r := cast(^Field_List)res;
r.list = clone(r.list);
case Typeid_Type:
r := cast(^Typeid_Type)res;
r.specialization = clone(r.specialization);
case Helper_Type:
r := cast(^Helper_Type)res;
r.type = clone(r.type);
case Distinct_Type:
r := cast(^Distinct_Type)res;
r.type = clone(r.type);
case Opaque_Type:
r := cast(^Opaque_Type)res;
r.type = clone(r.type);
case Poly_Type:
r := cast(^Poly_Type)res;
r.type = auto_cast clone(r.type);
r.specialization = clone(r.specialization);
case Proc_Type:
r := cast(^Proc_Type)res;
r.params = auto_cast clone(r.params);
r.results = auto_cast clone(r.results);
case Pointer_Type:
r := cast(^Pointer_Type)res;
r.elem = clone(r.elem);
case Array_Type:
r := cast(^Array_Type)res;
r.len = clone(r.len);
r.elem = clone(r.elem);
case Dynamic_Array_Type:
r := cast(^Dynamic_Array_Type)res;
r.elem = clone(r.elem);
case Struct_Type:
r := cast(^Struct_Type)res;
r.poly_params = auto_cast clone(r.poly_params);
r.align = clone(r.align);
r.fields = auto_cast clone(r.fields);
case Union_Type:
r := cast(^Union_Type)res;
r.poly_params = auto_cast clone(r.poly_params);
r.align = clone(r.align);
r.variants = clone(r.variants);
case Enum_Type:
r := cast(^Enum_Type)res;
r.base_type = clone(r.base_type);
r.fields = clone(r.fields);
case Bit_Field_Type:
r := cast(^Bit_Field_Type)res;
r.fields = clone(r.fields);
case Bit_Set_Type:
r := cast(^Bit_Set_Type)res;
r.elem = clone(r.elem);
r.underlying = clone(r.underlying);
case Map_Type:
r := cast(^Map_Type)res;
r.key = clone(r.key);
r.value = clone(r.value);
case:
fmt.panicf("Unhandled node kind: %T", n);
fmt.panicf("Unhandled node kind: %T", r);
}
return res;
-40
View File
@@ -1,40 +0,0 @@
package odin_ast
import "core:odin/tokenizer"
Package_Kind :: enum {
Normal,
Runtime,
Init,
}
Package :: struct {
kind: Package_Kind,
id: int,
name: string,
fullpath: string,
files: []^File,
user_data: rawptr,
}
File :: struct {
id: int,
pkg: ^Package,
fullpath: string,
src: []byte,
pkg_decl: ^Package_Decl,
pkg_token: tokenizer.Token,
pkg_name: string,
decls: [dynamic]^Stmt,
imports: [dynamic]^Import_Decl,
directive_count: int,
comments: [dynamic]^Comment_Group,
syntax_warning_count: int,
syntax_error_count: int,
}
+405
View File
@@ -0,0 +1,405 @@
package odin_ast
import "core:fmt"
// A Visitor's visit procedure is invoked for each node encountered by walk
// If the result visitor is not nil, walk visits each of the children of node with the new visitor,
// followed by a call of v.visit(v, nil)
Visitor :: struct {
visit: proc(visitor: ^Visitor, node: ^Node) -> ^Visitor,
data: rawptr,
}
// inspect traverses an AST in depth-first order
// It starts by calling f(node), and node must be non-nil
// If f returns true, inspect invokes f recursively for each of the non-nil children of node,
// followed by a call of f(nil)
inspect :: proc(node: ^Node, f: proc(^Node) -> bool) {
v := &Visitor{
visit = proc(v: ^Visitor, node: ^Node) -> ^Visitor {
f := (proc(^Node) -> bool)(v.data);
if f(node) {
return v;
}
return nil;
},
data = rawptr(f),
};
walk(v, node);
}
// walk traverses an AST in depth-first order: It starts by calling v.visit(v, node), and node must not be nil
// If the visitor returned by v.visit(v, node) is not nil, walk is invoked recursively with the new visitor for
// each of the non-nil children of node, followed by a call of the new visit procedure
walk :: proc(v: ^Visitor, node: ^Node) {
walk_expr_list :: proc(v: ^Visitor, list: []^Expr) {
for x in list {
walk(v, x);
}
}
walk_stmt_list :: proc(v: ^Visitor, list: []^Stmt) {
for x in list {
walk(v, x);
}
}
walk_attribute_list :: proc(v: ^Visitor, list: []^Attribute) {
for x in list {
walk(v, x);
}
}
v := v;
if v = v->visit(node); v == nil {
return;
}
switch n in &node.derived {
case File:
if n.docs != nil {
walk(v, n.docs);
}
walk_stmt_list(v, n.decls[:]);
case Package:
for _, f in n.files {
walk(v, f);
}
case Comment_Group:
// empty
case Bad_Expr:
case Ident:
case Implicit:
case Undef:
case Basic_Lit:
case Basic_Directive:
case Ellipsis:
if n.expr != nil {
walk(v, n.expr);
}
case Proc_Lit:
walk(v, n.type);
walk(v, n.body);
walk_expr_list(v, n.where_clauses);
case Comp_Lit:
if n.type != nil {
walk(v, n.type);
}
walk_expr_list(v, n.elems);
case Tag_Expr:
walk(v, n.expr);
case Unary_Expr:
walk(v, n.expr);
case Binary_Expr:
walk(v, n.left);
walk(v, n.right);
case Paren_Expr:
walk(v, n.expr);
case Selector_Expr:
walk(v, n.expr);
walk(v, n.field);
case Implicit_Selector_Expr:
walk(v, n.field);
case Selector_Call_Expr:
walk(v, n.expr);
walk(v, n.call);
case Index_Expr:
walk(v, n.expr);
walk(v, n.index);
case Deref_Expr:
walk(v, n.expr);
case Slice_Expr:
walk(v, n.expr);
if n.low != nil {
walk(v, n.low);
}
if n.high != nil {
walk(v, n.high);
}
case Call_Expr:
walk(v, n.expr);
walk_expr_list(v, n.args);
case Field_Value:
walk(v, n.field);
walk(v, n.value);
case Ternary_Expr:
walk(v, n.cond);
walk(v, n.x);
walk(v, n.y);
case Ternary_If_Expr:
walk(v, n.x);
walk(v, n.cond);
walk(v, n.y);
case Ternary_When_Expr:
walk(v, n.x);
walk(v, n.cond);
walk(v, n.y);
case Type_Assertion:
walk(v, n.expr);
if n.type != nil {
walk(v, n.type);
}
case Type_Cast:
walk(v, n.type);
walk(v, n.expr);
case Auto_Cast:
walk(v, n.expr);
case Inline_Asm_Expr:
walk_expr_list(v, n.param_types);
walk(v, n.return_type);
walk(v, n.constraints_string);
walk(v, n.asm_string);
case Bad_Stmt:
case Empty_Stmt:
case Expr_Stmt:
walk(v, n.expr);
case Tag_Stmt:
walk(v, n.stmt);
case Assign_Stmt:
walk_expr_list(v, n.lhs);
walk_expr_list(v, n.rhs);
case Block_Stmt:
if n.label != nil {
walk(v, n.label);
}
walk_stmt_list(v, n.stmts);
case If_Stmt:
if n.label != nil {
walk(v, n.label);
}
if n.init != nil {
walk(v, n.init);
}
walk(v, n.cond);
walk(v, n.body);
if n.else_stmt != nil {
walk(v, n.else_stmt);
}
case When_Stmt:
walk(v, n.cond);
walk(v, n.body);
if n.else_stmt != nil {
walk(v, n.else_stmt);
}
case Return_Stmt:
walk_expr_list(v, n.results);
case Defer_Stmt:
walk(v, n.stmt);
case For_Stmt:
if n.label != nil {
walk(v, n.label);
}
if n.init != nil {
walk(v, n.init);
}
if n.cond != nil {
walk(v, n.cond);
}
if n.post != nil {
walk(v, n.post);
}
walk(v, n.body);
case Range_Stmt:
if n.label != nil {
walk(v, n.label);
}
for val in n.vals {
if val != nil {
walk(v, val);
}
}
walk(v, n.expr);
walk(v, n.body);
case Inline_Range_Stmt:
if n.label != nil {
walk(v, n.label);
}
if n.val0 != nil {
walk(v, n.val0);
}
if n.val1 != nil {
walk(v, n.val1);
}
walk(v, n.expr);
walk(v, n.body);
case Case_Clause:
walk_expr_list(v, n.list);
walk_stmt_list(v, n.body);
case Switch_Stmt:
if n.label != nil {
walk(v, n.label);
}
if n.init != nil {
walk(v, n.init);
}
if n.cond != nil {
walk(v, n.cond);
}
walk(v, n.body);
case Type_Switch_Stmt:
if n.label != nil {
walk(v, n.label);
}
if n.tag != nil {
walk(v, n.tag);
}
if n.expr != nil {
walk(v, n.expr);
}
walk(v, n.body);
case Branch_Stmt:
if n.label != nil {
walk(v, n.label);
}
case Using_Stmt:
walk_expr_list(v, n.list);
case Bad_Decl:
case Value_Decl:
if n.docs != nil {
walk(v, n.docs);
}
walk_attribute_list(v, n.attributes[:]);
walk_expr_list(v, n.names);
if n.type != nil {
walk(v, n.type);
}
walk_expr_list(v, n.values);
if n.comment != nil {
walk(v, n.comment);
}
case Package_Decl:
if n.docs != nil {
walk(v, n.docs);
}
if n.comment != nil {
walk(v, n.comment);
}
case Import_Decl:
if n.docs != nil {
walk(v, n.docs);
}
if n.comment != nil {
walk(v, n.comment);
}
case Foreign_Block_Decl:
if n.docs != nil {
walk(v, n.docs);
}
walk_attribute_list(v, n.attributes[:]);
if n.foreign_library != nil {
walk(v, n.foreign_library);
}
walk(v, n.body);
case Foreign_Import_Decl:
if n.docs != nil {
walk(v, n.docs);
}
walk_attribute_list(v, n.attributes[:]);
walk(v, n.name);
if n.comment != nil {
walk(v, n.comment);
}
case Proc_Group:
walk_expr_list(v, n.args);
case Attribute:
walk_expr_list(v, n.elems);
case Field:
if n.docs != nil {
walk(v, n.docs);
}
walk_expr_list(v, n.names);
if n.type != nil {
walk(v, n.type);
}
if n.default_value != nil {
walk(v, n.default_value);
}
if n.comment != nil {
walk(v, n.comment);
}
case Field_List:
for x in n.list {
walk(v, x);
}
case Typeid_Type:
if n.specialization != nil {
walk(v, n.specialization);
}
case Helper_Type:
walk(v, n.type);
case Distinct_Type:
walk(v, n.type);
case Poly_Type:
walk(v, n.type);
if n.specialization != nil {
walk(v, n.specialization);
}
case Proc_Type:
walk(v, n.params);
walk(v, n.results);
case Pointer_Type:
walk(v, n.elem);
case Array_Type:
if n.tag != nil {
walk(v, n.tag);
}
if n.len != nil {
walk(v, n.len);
}
walk(v, n.elem);
case Dynamic_Array_Type:
if n.tag != nil {
walk(v, n.tag);
}
walk(v, n.elem);
case Struct_Type:
if n.poly_params != nil {
walk(v, n.poly_params);
}
if n.align != nil {
walk(v, n.align);
}
walk_expr_list(v, n.where_clauses);
walk(v, n.fields);
case Union_Type:
if n.poly_params != nil {
walk(v, n.poly_params);
}
if n.align != nil {
walk(v, n.align);
}
walk_expr_list(v, n.where_clauses);
walk_expr_list(v, n.variants);
case Enum_Type:
if n.base_type != nil {
walk(v, n.base_type);
}
walk_expr_list(v, n.fields);
case Bit_Set_Type:
walk(v, n.elem);
if n.underlying != nil {
walk(v, n.underlying);
}
case Map_Type:
walk(v, n.key);
walk(v, n.value);
case Relative_Type:
walk(v, n.tag);
walk(v, n.type);
case:
fmt.panicf("ast.walk: unexpected node type %T", n);
}
v->visit(nil);
}
+256
View File
@@ -0,0 +1,256 @@
package odin_doc_format
import "core:mem"
Array :: struct($T: typeid) {
offset: u32le,
length: u32le,
}
String :: distinct Array(byte);
Version_Type_Major :: 0;
Version_Type_Minor :: 1;
Version_Type_Patch :: 0;
Version_Type :: struct {
major, minor, patch: u8,
_: u8,
};
Version_Type_Default :: Version_Type{
major=Version_Type_Major,
minor=Version_Type_Minor,
patch=Version_Type_Patch,
};
Magic_String :: "odindoc\x00";
Header_Base :: struct {
magic: [8]byte,
_: u32le,
version: Version_Type,
total_size: u32le,
header_size: u32le,
hash: u32le,
}
Header :: struct {
using base: Header_Base,
// NOTE: These arrays reserve the zero element as a sentinel value
files: Array(File),
pkgs: Array(Pkg),
entities: Array(Entity),
types: Array(Type),
}
File_Index :: distinct u32le;
Pkg_Index :: distinct u32le;
Entity_Index :: distinct u32le;
Type_Index :: distinct u32le;
Position :: struct {
file: File_Index,
line: u32le,
column: u32le,
offset: u32le,
};
File :: struct {
pkg: Pkg_Index,
name: String,
}
Pkg_Flag :: enum u32le {
Builtin = 0,
Runtime = 1,
Init = 2,
}
Pkg_Flags :: distinct bit_set[Pkg_Flag; u32le];
Pkg :: struct {
fullpath: String,
name: String,
flags: Pkg_Flags,
docs: String,
files: Array(File_Index),
entities: Array(Entity_Index),
}
Entity_Kind :: enum u32le {
Invalid = 0,
Constant = 1,
Variable = 2,
Type_Name = 3,
Procedure = 4,
Proc_Group = 5,
Import_Name = 6,
Library_Name = 7,
}
Entity_Flag :: enum u32le {
Foreign = 0,
Export = 1,
Param_Using = 2,
Param_Const = 3,
Param_Auto_Cast = 4,
Param_Ellipsis = 5,
Param_CVararg = 6,
Param_No_Alias = 7,
Type_Alias = 8,
Var_Thread_Local = 9,
}
Entity_Flags :: distinct bit_set[Entity_Flag; u32le];
Entity :: struct {
kind: Entity_Kind,
flags: Entity_Flags,
pos: Position,
name: String,
type: Type_Index,
init_string: String,
_: u32le,
comment: String,
docs: String,
foreign_library: Entity_Index,
link_name: String,
attributes: Array(Attribute),
grouped_entities: Array(Entity_Index), // Procedure Groups
where_clauses: Array(String), // Procedures
}
Attribute :: struct {
name: String,
value: String,
}
Type_Kind :: enum u32le {
Invalid = 0,
Basic = 1,
Named = 2,
Generic = 3,
Pointer = 4,
Array = 5,
Enumerated_Array = 6,
Slice = 7,
Dynamic_Array = 8,
Map = 9,
Struct = 10,
Union = 11,
Enum = 12,
Tuple = 13,
Proc = 14,
Bit_Set = 15,
Simd_Vector = 16,
SOA_Struct_Fixed = 17,
SOA_Struct_Slice = 18,
SOA_Struct_Dynamic = 19,
Relative_Pointer = 20,
Relative_Slice = 21,
}
Type_Elems_Cap :: 4;
Type :: struct {
kind: Type_Kind,
flags: u32le, // Type_Kind specific
name: String,
custom_align: String,
// Used by some types
elem_count_len: u32le,
elem_counts: [Type_Elems_Cap]i64le,
// Each of these is esed by some types, not all
calling_convention: String, // Procedures
types: Array(Type_Index),
entities: Array(Entity_Index),
polymorphic_params: Type_Index, // Struct, Union
where_clauses: Array(String), // Struct, Union
}
Type_Flags_Basic :: distinct bit_set[Type_Flag_Basic; u32le];
Type_Flag_Basic :: enum u32le {
Untyped = 1,
}
Type_Flags_Struct :: distinct bit_set[Type_Flag_Struct; u32le];
Type_Flag_Struct :: enum u32le {
Polymorphic = 0,
Packed = 1,
Raw_Union = 2,
}
Type_Flags_Union :: distinct bit_set[Type_Flag_Union; u32le];
Type_Flag_Union :: enum u32le {
Polymorphic = 0,
No_Nil = 1,
Maybe = 2,
}
Type_Flags_Proc :: distinct bit_set[Type_Flag_Proc; u32le];
Type_Flag_Proc :: enum u32le {
Polymorphic = 0,
Diverging = 1,
Optional_Ok = 2,
Variadic = 3,
C_Vararg = 4,
}
Type_Flags_Bit_Set :: distinct bit_set[Type_Flag_Bit_Set; u32le];
Type_Flag_Bit_Set :: enum u32le {
Range = 1,
Op_Lt = 2,
Op_Lt_Eq = 3,
Underlying_Type = 4,
}
from_array :: proc(base: ^Header_Base, a: $A/Array($T)) -> []T {
s: mem.Raw_Slice;
s.data = rawptr(uintptr(base) + uintptr(a.offset));
s.len = int(a.length);
return transmute([]T)s;
}
from_string :: proc(base: ^Header_Base, s: String) -> string {
return string(from_array(base, s));
}
Reader_Error :: enum {
None,
Header_Too_Small,
Invalid_Magic,
Data_Too_Small,
Invalid_Version,
}
read_from_bytes :: proc(data: []byte) -> (h: ^Header, err: Reader_Error) {
if len(data) < size_of(Header_Base) {
err = .Header_Too_Small;
return;
}
header_base := (^Header_Base)(raw_data(data));
if header_base.magic != Magic_String {
err = .Invalid_Magic;
return;
}
if len(data) < int(header_base.total_size) {
err = .Data_Too_Small;
return;
}
if header_base.version != Version_Type_Default {
err = .Invalid_Version;
return;
}
h = (^Header)(header_base);
return;
}
+89
View File
@@ -0,0 +1,89 @@
package odin_parser
import "core:odin/tokenizer"
import "core:odin/ast"
import "core:path/filepath"
import "core:fmt"
import "core:os"
import "core:slice"
collect_package :: proc(path: string) -> (pkg: ^ast.Package, success: bool) {
NO_POS :: tokenizer.Pos{};
pkg_path, pkg_path_ok := filepath.abs(path);
if !pkg_path_ok {
return;
}
path_pattern := fmt.tprintf("%s/*.odin", pkg_path);
matches, err := filepath.glob(path_pattern);
defer delete(matches);
if err != nil {
return;
}
pkg = ast.new(ast.Package, NO_POS, NO_POS);
pkg.fullpath = pkg_path;
for match in matches {
src: []byte;
fullpath, ok := filepath.abs(match);
if !ok {
return;
}
src, ok = os.read_entire_file(fullpath);
if !ok {
delete(fullpath);
return;
}
file := ast.new(ast.File, NO_POS, NO_POS);
file.pkg = pkg;
file.src = src;
file.fullpath = fullpath;
pkg.files[fullpath] = file;
}
success = true;
return;
}
parse_package :: proc(pkg: ^ast.Package, p: ^Parser = nil) -> bool {
p := p;
if p == nil {
p = &Parser{};
p^ = default_parser();
}
ok := true;
files := make([]^ast.File, len(pkg.files), context.temp_allocator);
i := 0;
for _, file in pkg.files {
files[i] = file;
i += 1;
}
slice.sort(files);
for file in files {
if !parse_file(p, file) {
ok = false;
}
if pkg.name == "" {
pkg.name = file.pkg_decl.name;
} else if pkg.name != file.pkg_decl.name {
error(p, file.pkg_decl.pos, "different package name, expected '%s', got '%s'", pkg.name, file.pkg_decl.name);
}
}
return ok;
}
parse_package_from_path :: proc(path: string, p: ^Parser = nil) -> (pkg: ^ast.Package, ok: bool) {
pkg, ok = collect_package(path);
if !ok {
return;
}
ok = parse_package(pkg, p);
return;
}
File diff suppressed because it is too large Load Diff
+113 -116
View File
@@ -34,127 +34,118 @@ Token_Kind :: enum u32 {
Comment,
B_Literal_Begin,
Ident,
Integer,
Float,
Imag,
Rune,
String,
Ident, // main
Integer, // 12345
Float, // 123.45
Imag, // 123.45i
Rune, // 'a'
String, // "abc"
B_Literal_End,
B_Operator_Begin,
Eq,
Not,
Hash,
At,
Dollar,
Pointer,
Question,
Add,
Sub,
Mul,
Quo,
Mod,
Mod_Mod,
And,
Or,
Xor,
And_Not,
Shl,
Shr,
Eq, // =
Not, // !
Hash, // #
At, // @
Dollar, // $
Pointer, // ^
Question, // ?
Add, // +
Sub, // -
Mul, // *
Quo, // /
Mod, // %
Mod_Mod, // %%
And, // &
Or, // |
Xor, // ~
And_Not, // &~
Shl, // <<
Shr, // >>
Cmp_And,
Cmp_Or,
Cmp_And, // &&
Cmp_Or, // ||
B_Assign_Op_Begin,
Add_Eq,
Sub_Eq,
Mul_Eq,
Quo_Eq,
Mod_Eq,
Mod_Mod_Eq,
And_Eq,
Or_Eq,
Xor_Eq,
And_Not_Eq,
Shl_Eq,
Shr_Eq,
Cmp_And_Eq,
Cmp_Or_Eq,
Add_Eq, // +=
Sub_Eq, // -=
Mul_Eq, // *=
Quo_Eq, // /=
Mod_Eq, // %=
Mod_Mod_Eq, // %%=
And_Eq, // &=
Or_Eq, // |=
Xor_Eq, // ~=
And_Not_Eq, // &~=
Shl_Eq, // <<=
Shr_Eq, // >>=
Cmp_And_Eq, // &&=
Cmp_Or_Eq, // ||=
B_Assign_Op_End,
Arrow_Right,
Arrow_Left,
Double_Arrow_Right,
Undef,
Arrow_Right, // ->
Undef, // ---
B_Comparison_Begin,
Cmp_Eq,
Not_Eq,
Lt,
Gt,
Lt_Eq,
Gt_Eq,
Cmp_Eq, // ==
Not_Eq, // !=
Lt, // <
Gt, // >
Lt_Eq, // <=
Gt_Eq, // >=
B_Comparison_End,
Open_Paren,
Close_Paren,
Open_Bracket,
Close_Bracket,
Open_Brace,
Close_Brace,
Colon,
Semicolon,
Period,
Comma,
Ellipsis,
Range_Half,
Back_Slash,
Open_Paren, // (
Close_Paren, // )
Open_Bracket, // [
Close_Bracket, // ]
Open_Brace, // {
Close_Brace, // }
Colon, // :
Semicolon, // ;
Period, // .
Comma, // ,
Ellipsis, // ..
Range_Half, // ..<
Back_Slash, // \
B_Operator_End,
B_Keyword_Begin,
Import,
Foreign,
Package,
Typeid,
When,
Where,
If,
Else,
For,
Switch,
In,
Not_In,
Do,
Case,
Break,
Continue,
Fallthrough,
Defer,
Return,
Proc,
Macro,
Struct,
Union,
Enum,
Bit_Field,
Bit_Set,
Map,
Dynamic,
Auto_Cast,
Cast,
Transmute,
Distinct,
Opaque,
Using,
Inline,
No_Inline,
Context,
Size_Of,
Align_Of,
Offset_Of,
Type_Of,
Const,
Import, // import
Foreign, // foreign
Package, // package
Typeid, // typeid
When, // when
Where, // where
If, // if
Else, // else
For, // for
Switch, // switch
In, // in
Not_In, // not_in
Do, // do
Case, // case
Break, // break
Continue, // continue
Fallthrough, // fallthrough
Defer, // defer
Return, // return
Proc, // proc
Struct, // struct
Union, // union
Enum, // enum
Bit_Set, // bit_set
Map, // map
Dynamic, // dynamic
Auto_Cast, // auto_cast
Cast, // cast
Transmute, // transmute
Distinct, // distinct
Using, // using
Inline, // inline
No_Inline, // no_inline
Context, // context
Asm, // asm
B_Keyword_End,
COUNT,
@@ -219,8 +210,6 @@ tokens := [Token_Kind.COUNT]string {
"",
"->",
"<-",
"=>",
"---",
"",
@@ -268,11 +257,9 @@ tokens := [Token_Kind.COUNT]string {
"defer",
"return",
"proc",
"macro",
"struct",
"union",
"enum",
"bit_field",
"bit_set",
"map",
"dynamic",
@@ -280,21 +267,29 @@ tokens := [Token_Kind.COUNT]string {
"cast",
"transmute",
"distinct",
"opaque",
"using",
"inline",
"no_inline",
"context",
"size_of",
"align_of",
"offset_of",
"type_of",
"const",
"asm",
"",
};
custom_keyword_tokens: []string;
is_newline :: proc(tok: Token) -> bool {
return tok.kind == .Semicolon && tok.text == "\n";
}
token_to_string :: proc(tok: Token) -> string {
if is_newline(tok) {
return "newline";
}
return to_string(tok.kind);
}
to_string :: proc(kind: Token_Kind) -> string {
if Token_Kind.Invalid <= kind && kind < Token_Kind.COUNT {
return tokens[kind];
@@ -318,6 +313,8 @@ is_operator :: proc(kind: Token_Kind) -> bool {
return true;
case .In, .Not_In:
return true;
case .If:
return true;
}
return false;
}
+81 -36
View File
@@ -1,22 +1,31 @@
package odin_tokenizer
import "core:fmt"
import "core:unicode"
import "core:unicode/utf8"
Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any);
Flag :: enum {
Insert_Semicolon,
}
Flags :: distinct bit_set[Flag; u32];
Tokenizer :: struct {
// Immutable data
path: string,
src: []byte,
err: Error_Handler,
flags: Flags,
// Tokenizing state
ch: rune,
offset: int,
read_offset: int,
line_offset: int,
line_count: int,
insert_semicolon: bool,
// Mutable data
error_count: int,
@@ -105,11 +114,18 @@ peek_byte :: proc(t: ^Tokenizer, offset := 0) -> byte {
}
skip_whitespace :: proc(t: ^Tokenizer) {
for t.ch == ' ' ||
t.ch == '\t' ||
t.ch == '\n' ||
t.ch == '\r' {
advance_rune(t);
for {
switch t.ch {
case ' ', '\t', '\r':
advance_rune(t);
case '\n':
if t.insert_semicolon {
return;
}
advance_rune(t);
case:
return;
}
}
}
@@ -122,12 +138,13 @@ is_letter :: proc(r: rune) -> bool {
return true;
}
}
// TODO(bill): Add unicode lookup tables
return false;
return unicode.is_letter(r);
}
is_digit :: proc(r: rune) -> bool {
// TODO(bill): Add unicode lookup tables
return '0' <= r && r <= '9';
if '0' <= r && r <= '9' {
return true;
}
return unicode.is_digit(r);
}
@@ -150,13 +167,21 @@ scan_comment :: proc(t: ^Tokenizer) -> string {
/* style comment */
advance_rune(t);
for t.ch >= 0 {
nest := 1;
for t.ch >= 0 && nest > 0 {
ch := t.ch;
advance_rune(t);
if ch == '/' && t.ch == '*' {
nest += 1;
}
if ch == '*' && t.ch == '/' {
nest -= 1;
advance_rune(t);
next = t.offset;
break general;
if nest == 0 {
break general;
}
}
}
@@ -210,7 +235,7 @@ scan_raw_string :: proc(t: ^Tokenizer) -> string {
for {
ch := t.ch;
if ch == '\n' || ch < 0 {
if ch == utf8.RUNE_EOF {
error(t, offset, "raw string literal was not terminated");
break;
}
@@ -241,7 +266,7 @@ scan_escape :: proc(t: ^Tokenizer) -> bool {
n: int;
base, max: u32;
switch t.ch {
case 'a', 'b', 'e', 'f', 'n', 't', 'v', '\\', '\'', '\"':
case 'a', 'b', 'e', 'f', 'n', 't', 'v', 'r', '\\', '\'', '\"':
advance_rune(t);
return true;
@@ -372,7 +397,7 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Token_Kind, str
scan_exponent(t, &kind);
} else {
if t.ch == '0' {
int_base :: inline proc(t: ^Tokenizer, kind: ^Token_Kind, base: int, msg: string) {
int_base :: proc(t: ^Tokenizer, kind: ^Token_Kind, base: int, msg: string) {
prev := t.offset;
advance_rune(t);
scan_mantissa(t, base);
@@ -406,9 +431,9 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Token_Kind, str
}
switch digit_count {
case 8, 16: break;
case 4, 8, 16: break;
case:
error(t, t.offset, "invalid hexadecimal floating-point number, expected 8 or 16 digits, got %d", digit_count);
error(t, t.offset, "invalid hexadecimal floating-point number, expected 4, 8, or 16 digits, got %d", digit_count);
}
}
@@ -483,6 +508,8 @@ scan :: proc(t: ^Tokenizer) -> Token {
lit: string;
pos := offset_to_pos(t, offset);
insert_semicolon := false;
switch ch := t.ch; true {
case is_letter(ch):
lit = scan_identifier(t);
@@ -501,41 +528,53 @@ scan :: proc(t: ^Tokenizer) -> Token {
break check_keyword;
}
}
if kind == .Ident && lit == "notin" {
kind = .Not_In;
#partial switch kind {
case .Ident, .Context, .Typeid, .Break, .Continue, .Fallthrough, .Return:
insert_semicolon = true;
}
}
case '0' <= ch && ch <= '9':
insert_semicolon = true;
kind, lit = scan_number(t, false);
case:
advance_rune(t);
switch ch {
case -1:
kind = .EOF;
if t.insert_semicolon {
t.insert_semicolon = false;
kind = .Semicolon;
lit = "\n";
}
case '\n':
t.insert_semicolon = false;
kind = .Semicolon;
lit = "\n";
case '"':
insert_semicolon = true;
kind = .String;
lit = scan_string(t);
case '\'':
insert_semicolon = true;
kind = .Rune;
lit = scan_rune(t);
case '`':
insert_semicolon = true;
kind = .String;
lit = scan_raw_string(t);
case '=':
if t.ch == '>' {
advance_rune(t);
kind = .Double_Arrow_Right;
} else {
kind = switch2(t, .Eq, .Cmp_Eq);
}
case '=': kind = switch2(t, .Eq, .Cmp_Eq);
case '!': kind = switch2(t, .Not, .Not_Eq);
case '#':
kind = .Hash;
if t.ch == '!' {
insert_semicolon = t.insert_semicolon;
kind = .Comment;
lit = scan_comment(t);
}
case '?': kind = .Question;
case '?':
insert_semicolon = true;
kind = .Question;
case '@': kind = .At;
case '$': kind = .Dollar;
case '^': kind = .Pointer;
@@ -554,6 +593,7 @@ scan :: proc(t: ^Tokenizer) -> Token {
case '*': kind = switch2(t, .Mul, .Mul_Eq);
case '/':
if t.ch == '/' || t.ch == '*' {
insert_semicolon = t.insert_semicolon;
kind = .Comment;
lit = scan_comment(t);
} else {
@@ -569,13 +609,7 @@ scan :: proc(t: ^Tokenizer) -> Token {
}
case '|': kind = switch3(t, .Or, .Or_Eq, '|', .Cmp_Or);
case '~': kind = .Xor;
case '<':
if t.ch == '-' {
advance_rune(t);
kind = .Arrow_Left;
} else {
kind = switch4(t, .Lt, .Lt_Eq, '<', .Shl, .Shl_Eq);
}
case '<': kind = switch4(t, .Lt, .Lt_Eq, '<', .Shl, .Shl_Eq);
case '>': kind = switch4(t, .Gt, .Gt_Eq, '>', .Shr,.Shr_Eq);
case '.':
@@ -596,11 +630,17 @@ scan :: proc(t: ^Tokenizer) -> Token {
case ',': kind = .Comma;
case ';': kind = .Semicolon;
case '(': kind = .Open_Paren;
case ')': kind = .Close_Paren;
case ')':
insert_semicolon = true;
kind = .Close_Paren;
case '[': kind = .Open_Bracket;
case ']': kind = .Close_Bracket;
case ']':
insert_semicolon = true;
kind = .Close_Bracket;
case '{': kind = .Open_Brace;
case '}': kind = .Close_Brace;
case '}':
insert_semicolon = true;
kind = .Close_Brace;
case '\\': kind = .Back_Slash;
@@ -608,10 +648,15 @@ scan :: proc(t: ^Tokenizer) -> Token {
if ch != utf8.RUNE_BOM {
error(t, t.offset, "illegal character '%r': %d", ch, ch);
}
insert_semicolon = t.insert_semicolon; // preserve insert_semicolon info
kind = .Invalid;
}
}
if .Insert_Semicolon in t.flags {
t.insert_semicolon = insert_semicolon;
}
if lit == "" {
lit = string(t.src[offset : t.offset]);
}
+70
View File
@@ -0,0 +1,70 @@
package os
import "core:strings"
import "core:mem"
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
dirp: Dir;
dirp, err = _fdopendir(fd);
if err != ERROR_NONE {
return;
}
defer _closedir(dirp);
dirpath: string;
dirpath, err = absolute_path_from_handle(fd);
if err != ERROR_NONE {
return;
}
defer delete(dirpath);
n := n;
size := n;
if n <= 0 {
n = -1;
size = 100;
}
dfi := make([dynamic]File_Info, 0, size, allocator);
for {
entry: Dirent;
end_of_stream: bool;
entry, err, end_of_stream = _readdir(dirp);
if err != ERROR_NONE {
for fi_ in dfi {
file_info_delete(fi_, allocator);
}
delete(dfi);
return;
} else if end_of_stream {
break;
}
fi_: File_Info;
filename := cast(string)(transmute(cstring)mem.Raw_Cstring{ data = &entry.name[0] });
if filename == "." || filename == ".." {
continue;
}
fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator);
defer delete(fullpath, context.temp_allocator);
fi_, err = stat(fullpath, allocator);
if err != ERROR_NONE {
for fi__ in dfi {
file_info_delete(fi__, allocator);
}
delete(dfi);
return;
}
append(&dfi, fi_);
}
return dfi[:], ERROR_NONE;
}
+70
View File
@@ -0,0 +1,70 @@
package os
import "core:strings"
import "core:mem"
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
dirp: Dir;
dirp, err = _fdopendir(fd);
if err != ERROR_NONE {
return;
}
defer _closedir(dirp);
dirpath: string;
dirpath, err = absolute_path_from_handle(fd);
if err != ERROR_NONE {
return;
}
defer delete(dirpath);
n := n;
size := n;
if n <= 0 {
n = -1;
size = 100;
}
dfi := make([dynamic]File_Info, 0, size, allocator);
for {
entry: Dirent;
end_of_stream: bool;
entry, err, end_of_stream = _readdir(dirp);
if err != ERROR_NONE {
for fi_ in dfi {
file_info_delete(fi_, allocator);
}
delete(dfi);
return;
} else if end_of_stream {
break;
}
fi_: File_Info;
filename := cast(string)(transmute(cstring)mem.Raw_Cstring{ data = &entry.name[0] });
if filename == "." || filename == ".." {
continue;
}
fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator);
defer delete(fullpath, context.temp_allocator);
fi_, err = stat(fullpath, allocator);
if err != ERROR_NONE {
for fi__ in dfi {
file_info_delete(fi__, allocator);
}
delete(dfi);
return;
}
append(&dfi, fi_);
}
return dfi[:], ERROR_NONE;
}
+107
View File
@@ -0,0 +1,107 @@
package os
import win32 "core:sys/windows"
import "core:strings"
import "core:time"
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW) -> (fi: File_Info) {
// Ignore "." and ".."
if d.cFileName[0] == '.' && d.cFileName[1] == 0 {
return;
}
if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 {
return;
}
path := strings.concatenate({base_path, `\`, win32.utf16_to_utf8(d.cFileName[:])});
fi.fullpath = path;
fi.name = basename(path);
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow);
if d.dwFileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
fi.mode |= 0o444;
} else {
fi.mode |= 0o666;
}
is_sym := false;
if d.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_Point == 0 {
is_sym = false;
} else {
is_sym = d.dwReserved0 == win32.IO_REPARSE_TAG_SYMLINK || d.dwReserved0 == win32.IO_REPARSE_TAG_MOUNT_POINT;
}
if is_sym {
fi.mode |= File_Mode_Sym_Link;
} else {
if d.dwFileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
fi.mode |= 0o111 | File_Mode_Dir;
}
// fi.mode |= file_type_mode(h);
}
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime));
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime));
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime));
fi.is_dir = fi.mode & File_Mode_Dir != 0;
return;
}
if fd == 0 {
return nil, ERROR_INVALID_HANDLE;
}
context.allocator = allocator;
h := win32.HANDLE(fd);
dir_fi, _ := file_info_from_get_file_information_by_handle("", h);
if !dir_fi.is_dir {
return nil, ERROR_FILE_IS_NOT_DIR;
}
n := n;
size := n;
if n <= 0 {
n = -1;
size = 100;
}
dfi := make([dynamic]File_Info, 0, size);
wpath: []u16;
wpath, err = cleanpath_from_handle_u16(fd);
if len(wpath) == 0 || err != ERROR_NONE {
return;
}
wpath_search := make([]u16, len(wpath)+3, context.temp_allocator);
copy(wpath_search, wpath);
wpath_search[len(wpath)+0] = '\\';
wpath_search[len(wpath)+1] = '*';
wpath_search[len(wpath)+2] = 0;
path := cleanpath_from_buf(wpath);
find_data := &win32.WIN32_FIND_DATAW{};
find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data);
defer win32.FindClose(find_handle);
for n != 0 && find_handle != nil {
fi: File_Info;
fi = find_data_to_file_info(path, find_data);
if fi.name != "" {
append(&dfi, fi);
n -= 1;
}
if !win32.FindNextFileW(find_handle, find_data) {
e := Errno(win32.GetLastError());
if e == ERROR_NO_MORE_FILES {
break;
}
return dfi[:], e;
}
}
return dfi[:], ERROR_NONE;
}
+103
View File
@@ -0,0 +1,103 @@
package os
import win32 "core:sys/windows"
import "core:mem"
// lookup_env gets the value of the environment variable named by the key
// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true
// Otherwise the returned value will be empty and the boolean will be false
// NOTE: the value will be allocated with the supplied allocator
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
if key == "" {
return;
}
wkey := win32.utf8_to_wstring(key);
b := make([dynamic]u16, 100, context.temp_allocator);
for {
n := win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)));
if n == 0 {
err := win32.GetLastError();
if err == u32(ERROR_ENVVAR_NOT_FOUND) {
return "", false;
}
}
if n <= u32(len(b)) {
value = win32.utf16_to_utf8(b[:n], allocator);
found = true;
return;
}
resize(&b, len(b)*2);
}
}
// get_env retrieves the value of the environment variable named by the key
// It returns the value, which will be empty if the variable is not present
// To distinguish between an empty value and an unset value, use lookup_env
// NOTE: the value will be allocated with the supplied allocator
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator);
return;
}
// set_env sets the value of the environment variable named by the key
set_env :: proc(key, value: string) -> Errno {
k := win32.utf8_to_wstring(key);
v := win32.utf8_to_wstring(value);
if !win32.SetEnvironmentVariableW(k, v) {
return Errno(win32.GetLastError());
}
return 0;
}
// unset_env unsets a single environment variable
unset_env :: proc(key: string) -> Errno {
k := win32.utf8_to_wstring(key);
if !win32.SetEnvironmentVariableW(k, nil) {
return Errno(win32.GetLastError());
}
return 0;
}
// environ returns a copy of strings representing the environment, in the form "key=value"
// NOTE: the slice of strings and the strings with be allocated using the supplied allocator
environ :: proc(allocator := context.allocator) -> []string {
envs := win32.GetEnvironmentStringsW();
if envs == nil {
return nil;
}
defer win32.FreeEnvironmentStringsW(envs);
r := make([dynamic]string, 0, 50, allocator);
for from, i, p := 0, 0, envs; true; i += 1 {
c := (^u16)(uintptr(p) + uintptr(i*2))^;
if c == 0 {
if i <= from {
break;
}
w := mem.slice_ptr(mem.ptr_offset(p, from), i-from);
append(&r, win32.utf16_to_utf8(w, allocator));
from = i + 1;
}
}
return r[:];
}
// clear_env deletes all environment variables
clear_env :: proc() {
envs := environ(context.temp_allocator);
for env in envs {
for j in 1..<len(env) {
if env[j] == '=' {
unset_env(env[0:j]);
break;
}
}
}
}
+499
View File
@@ -0,0 +1,499 @@
package os
import win32 "core:sys/windows"
import "intrinsics"
is_path_separator :: proc(c: byte) -> bool {
return c == '/' || c == '\\';
}
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) {
if len(path) == 0 {
return INVALID_HANDLE, ERROR_FILE_NOT_FOUND;
}
access: u32;
switch mode & (O_RDONLY|O_WRONLY|O_RDWR) {
case O_RDONLY: access = win32.FILE_GENERIC_READ;
case O_WRONLY: access = win32.FILE_GENERIC_WRITE;
case O_RDWR: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE;
}
if mode&O_APPEND != 0 {
access &~= win32.FILE_GENERIC_WRITE;
access |= win32.FILE_APPEND_DATA;
}
if mode&O_CREATE != 0 {
access |= win32.FILE_GENERIC_WRITE;
}
share_mode := win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE;
sa: ^win32.SECURITY_ATTRIBUTES = nil;
sa_inherit := win32.SECURITY_ATTRIBUTES{nLength = size_of(win32.SECURITY_ATTRIBUTES), bInheritHandle = true};
if mode&O_CLOEXEC == 0 {
sa = &sa_inherit;
}
create_mode: u32;
switch {
case mode&(O_CREATE|O_EXCL) == (O_CREATE | O_EXCL):
create_mode = win32.CREATE_NEW;
case mode&(O_CREATE|O_TRUNC) == (O_CREATE | O_TRUNC):
create_mode = win32.CREATE_ALWAYS;
case mode&O_CREATE == O_CREATE:
create_mode = win32.OPEN_ALWAYS;
case mode&O_TRUNC == O_TRUNC:
create_mode = win32.TRUNCATE_EXISTING;
case:
create_mode = win32.OPEN_EXISTING;
}
wide_path := win32.utf8_to_wstring(path);
handle := Handle(win32.CreateFileW(wide_path, access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL|win32.FILE_FLAG_BACKUP_SEMANTICS, nil));
if handle != INVALID_HANDLE {
return handle, ERROR_NONE;
}
err := Errno(win32.GetLastError());
return INVALID_HANDLE, err;
}
close :: proc(fd: Handle) -> Errno {
if !win32.CloseHandle(win32.HANDLE(fd)) {
return Errno(win32.GetLastError());
}
return ERROR_NONE;
}
flush :: proc(fd: Handle) -> (err: Errno) {
if !win32.FlushFileBuffers(win32.HANDLE(fd)) {
err = Errno(win32.GetLastError());
}
return;
}
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
if len(data) == 0 {
return 0, ERROR_NONE;
}
single_write_length: win32.DWORD;
total_write: i64;
length := i64(len(data));
for total_write < length {
remaining := length - total_write;
to_write := win32.DWORD(min(i32(remaining), MAX_RW));
e := win32.WriteFile(win32.HANDLE(fd), &data[total_write], to_write, &single_write_length, nil);
if single_write_length <= 0 || !e {
err := Errno(win32.GetLastError());
return int(total_write), err;
}
total_write += i64(single_write_length);
}
return int(total_write), ERROR_NONE;
}
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
if len(data) == 0 {
return 0, ERROR_NONE;
}
single_read_length: win32.DWORD;
total_read: i64;
length := i64(len(data));
for total_read < length {
remaining := length - total_read;
to_read := min(win32.DWORD(remaining), MAX_RW);
e := win32.ReadFile(win32.HANDLE(fd), &data[total_read], to_read, &single_read_length, nil);
if single_read_length <= 0 || !e {
err := Errno(win32.GetLastError());
return int(total_read), err;
}
total_read += i64(single_read_length);
}
return int(total_read), ERROR_NONE;
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
w: u32;
switch whence {
case 0: w = win32.FILE_BEGIN;
case 1: w = win32.FILE_CURRENT;
case 2: w = win32.FILE_END;
}
hi := i32(offset>>32);
lo := i32(offset);
ft := win32.GetFileType(win32.HANDLE(fd));
if ft == win32.FILE_TYPE_PIPE {
return 0, ERROR_FILE_IS_PIPE;
}
dw_ptr := win32.SetFilePointer(win32.HANDLE(fd), lo, &hi, w);
if dw_ptr == win32.INVALID_SET_FILE_POINTER {
err := Errno(win32.GetLastError());
return 0, err;
}
return i64(hi)<<32 + i64(dw_ptr), ERROR_NONE;
}
file_size :: proc(fd: Handle) -> (i64, Errno) {
length: win32.LARGE_INTEGER;
err: Errno;
if !win32.GetFileSizeEx(win32.HANDLE(fd), &length) {
err = Errno(win32.GetLastError());
}
return i64(length), err;
}
@(private)
MAX_RW :: 1<<30;
@(private)
pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
buf := data;
if len(buf) > MAX_RW {
buf = buf[:MAX_RW];
}
curr_offset, e := seek(fd, offset, 1);
if e != 0 {
return 0, e;
}
defer seek(fd, curr_offset, 0);
o := win32.OVERLAPPED{
OffsetHigh = u32(offset>>32),
Offset = u32(offset),
};
h := win32.HANDLE(fd);
done: win32.DWORD;
if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
e = Errno(win32.GetLastError());
done = 0;
}
return int(done), e;
}
@(private)
pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
buf := data;
if len(buf) > MAX_RW {
buf = buf[:MAX_RW];
}
curr_offset, e := seek(fd, offset, 1);
if e != 0 {
return 0, e;
}
defer seek(fd, curr_offset, 0);
o := win32.OVERLAPPED{
OffsetHigh = u32(offset>>32),
Offset = u32(offset),
};
h := win32.HANDLE(fd);
done: win32.DWORD;
if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
e = Errno(win32.GetLastError());
done = 0;
}
return int(done), e;
}
read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) {
if offset < 0 {
return 0, ERROR_NEGATIVE_OFFSET;
}
b, offset := data, offset;
for len(b) > 0 {
m, e := pread(fd, b, offset);
if e != 0 {
err = e;
break;
}
n += m;
b = b[m:];
offset += i64(m);
}
return;
}
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) {
if offset < 0 {
return 0, ERROR_NEGATIVE_OFFSET;
}
b, offset := data, offset;
for len(b) > 0 {
m, e := pwrite(fd, b, offset);
if e != 0 {
err = e;
break;
}
n += m;
b = b[m:];
offset += i64(m);
}
return;
}
// NOTE(bill): Uses startup to initialize it
stdin := get_std_handle(uint(win32.STD_INPUT_HANDLE));
stdout := get_std_handle(uint(win32.STD_OUTPUT_HANDLE));
stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE));
get_std_handle :: proc "contextless" (h: uint) -> Handle {
fd := win32.GetStdHandle(win32.DWORD(h));
when size_of(uintptr) == 8 {
win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0);
}
return Handle(fd);
}
exists :: proc(path: string) -> bool {
wpath := win32.utf8_to_wstring(path, context.temp_allocator);
attribs := win32.GetFileAttributesW(wpath);
return i32(attribs) != win32.INVALID_FILE_ATTRIBUTES;
}
is_file :: proc(path: string) -> bool {
wpath := win32.utf8_to_wstring(path, context.temp_allocator);
attribs := win32.GetFileAttributesW(wpath);
if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES {
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY == win32.FILE_ATTRIBUTE_DIRECTORY;
}
return false;
}
is_dir :: proc(path: string) -> bool {
wpath := win32.utf8_to_wstring(path, context.temp_allocator);
attribs := win32.GetFileAttributesW(wpath);
if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES {
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY != win32.FILE_ATTRIBUTE_DIRECTORY;
}
return false;
}
// NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName
@private cwd_lock := win32.SRWLOCK{}; // zero is initialized
get_current_directory :: proc(allocator := context.allocator) -> string {
win32.AcquireSRWLockExclusive(&cwd_lock);
sz_utf16 := win32.GetCurrentDirectoryW(0, nil);
dir_buf_wstr := make([]u16, sz_utf16, context.temp_allocator); // the first time, it _includes_ the NUL.
sz_utf16 = win32.GetCurrentDirectoryW(win32.DWORD(len(dir_buf_wstr)), raw_data(dir_buf_wstr));
assert(int(sz_utf16)+1 == len(dir_buf_wstr)); // the second time, it _excludes_ the NUL.
win32.ReleaseSRWLockExclusive(&cwd_lock);
return win32.utf16_to_utf8(dir_buf_wstr, allocator);
}
set_current_directory :: proc(path: string) -> (err: Errno) {
wstr := win32.utf8_to_wstring(path);
win32.AcquireSRWLockExclusive(&cwd_lock);
if !win32.SetCurrentDirectoryW(wstr) {
err = Errno(win32.GetLastError());
}
win32.ReleaseSRWLockExclusive(&cwd_lock);
return;
}
change_directory :: proc(path: string) -> Errno {
wpath := win32.utf8_to_wstring(path, context.temp_allocator);
return Errno(win32.SetCurrentDirectoryW(wpath));
}
make_directory :: proc(path: string, mode: u32) -> Errno {
wpath := win32.utf8_to_wstring(path, context.temp_allocator);
return Errno(win32.CreateDirectoryW(wpath, nil));
}
remove_directory :: proc(path: string) -> Errno {
wpath := win32.utf8_to_wstring(path, context.temp_allocator);
return Errno(win32.RemoveDirectoryW(wpath));
}
@(private)
is_abs :: proc(path: string) -> bool {
if len(path) > 0 && path[0] == '/' {
return true;
}
when ODIN_OS == "windows" {
if len(path) > 2 {
switch path[0] {
case 'A'..'Z', 'a'..'z':
return path[1] == ':' && is_path_separator(path[2]);
}
}
}
return false;
}
@(private)
fix_long_path :: proc(path: string) -> string {
if len(path) < 248 {
return path;
}
if len(path) >= 2 && path[:2] == `\\` {
return path;
}
if !is_abs(path) {
return path;
}
prefix :: `\\?`;
path_buf := make([]byte, len(prefix)+len(path)+len(`\`), context.temp_allocator);
copy(path_buf, prefix);
n := len(path);
r, w := 0, len(prefix);
for r < n {
switch {
case is_path_separator(path[r]):
r += 1;
case path[r] == '.' && (r+1 == n || is_path_separator(path[r+1])):
r += 1;
case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || is_path_separator(path[r+2])):
return path;
case:
path_buf[w] = '\\';
w += 1;
for ; r < n && !is_path_separator(path[r]); r += 1 {
path_buf[w] = path[r];
w += 1;
}
}
}
if w == len(`\\?\c:`) {
path_buf[w] = '\\';
w += 1;
}
return string(path_buf[:w]);
}
link :: proc(old_name, new_name: string) -> Errno {
n := win32.utf8_to_wstring(fix_long_path(new_name));
o := win32.utf8_to_wstring(fix_long_path(old_name));
return Errno(win32.CreateHardLinkW(n, o, nil));
}
unlink :: proc(path: string) -> Errno {
wpath := win32.utf8_to_wstring(path, context.temp_allocator);
return Errno(win32.DeleteFileW(wpath));
}
rename :: proc(old_path, new_path: string) -> Errno {
from := win32.utf8_to_wstring(old_path, context.temp_allocator);
to := win32.utf8_to_wstring(new_path, context.temp_allocator);
return Errno(win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING));
}
ftruncate :: proc(fd: Handle, length: i64) -> (err: Errno) {
curr_off, e := seek(fd, 0, 1);
if e != 0 {
return e;
}
defer seek(fd, curr_off, 0);
_, e = seek(fd, length, 0);
if e != 0 {
return e;
}
ok := win32.SetEndOfFile(win32.HANDLE(fd));
if !ok {
return Errno(win32.GetLastError());
}
return ERROR_NONE;
}
truncate :: proc(path: string, length: i64) -> (err: Errno) {
fd: Handle;
fd, err = open(path, O_WRONLY|O_CREATE, 0o666);
if err != 0 {
return;
}
defer close(fd);
err = ftruncate(fd, length);
return;
}
remove :: proc(name: string) -> Errno {
p := win32.utf8_to_wstring(fix_long_path(name));
err, err1: win32.DWORD;
if !win32.DeleteFileW(p) {
err = win32.GetLastError();
}
if err == 0 {
return 0;
}
if !win32.RemoveDirectoryW(p) {
err1 = win32.GetLastError();
}
if err1 == 0 {
return 0;
}
if err != err1 {
a := win32.GetFileAttributesW(p);
if a == ~u32(0) {
err = win32.GetLastError();
} else {
if a & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
err = err1;
} else if a & win32.FILE_ATTRIBUTE_READONLY != 0 {
if win32.SetFileAttributesW(p, a &~ win32.FILE_ATTRIBUTE_READONLY) {
err = 0;
if !win32.DeleteFileW(p) {
err = win32.GetLastError();
}
}
}
}
}
return Errno(err);
}
pipe :: proc() -> (r, w: Handle, err: Errno) {
sa: win32.SECURITY_ATTRIBUTES;
sa.nLength = size_of(win32.SECURITY_ATTRIBUTES);
sa.bInheritHandle = true;
if !win32.CreatePipe((^win32.HANDLE)(&r), (^win32.HANDLE)(&w), &sa, 0) {
err = Errno(win32.GetLastError());
}
return;
}
+13 -12
View File
@@ -70,7 +70,7 @@ file_size_from_path :: proc(path: string) -> i64 {
return length;
}
read_entire_file :: proc(name: string) -> (data: []byte, success: bool) {
read_entire_file :: proc(name: string, allocator := context.allocator) -> (data: []byte, success: bool) {
fd, err := open(name, O_RDONLY, 0);
if err != 0 {
return nil, false;
@@ -86,7 +86,7 @@ read_entire_file :: proc(name: string) -> (data: []byte, success: bool) {
return nil, true;
}
data = make([]byte, int(length));
data = make([]byte, int(length), allocator);
if data == nil {
return nil, false;
}
@@ -133,8 +133,7 @@ read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) {
heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, mem.Allocator_Error) {
//
// NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
// Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert
@@ -142,7 +141,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
// the pointer we return to the user.
//
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> rawptr {
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
a := max(alignment, align_of(rawptr));
space := size + a - 1;
@@ -159,13 +158,13 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a);
diff := int(aligned_ptr - ptr);
if (size + diff) > space {
return nil;
return nil, .Out_Of_Memory;
}
aligned_mem = rawptr(aligned_ptr);
mem.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem;
return aligned_mem;
return mem.byte_slice(aligned_mem, size), nil;
}
aligned_free :: proc(p: rawptr) {
@@ -174,8 +173,10 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
}
}
aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> rawptr {
if p == nil do return nil;
aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> ([]byte, mem.Allocator_Error) {
if p == nil {
return nil, nil;
}
return aligned_alloc(new_size, new_alignment, p);
}
@@ -200,13 +201,13 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
if set != nil {
set^ = {.Alloc, .Free, .Resize, .Query_Features};
}
return set;
return nil, nil;
case .Query_Info:
return nil;
return nil, nil;
}
return nil;
return nil, nil;
}
heap_allocator :: proc() -> mem.Allocator {
+11
View File
@@ -0,0 +1,11 @@
// Package os provides a platform-independent interface to operating system functionality.
// The design is UNIX-like but with Odin-like error handling. Failing calls return values with a specific error type rather than error number.
//
// The package os interface is intended to be uniform across all operating systems.
// Features not generally available appear in the system-specific packages under core:sys/*.
//
//
// IMPORTANT NOTE from Bill: this is purely a mockup of what I want the new package os to be, and NON-FUNCTIONING.
// It is not complete but should give designers a better idea of the general interface and how to write things.
// This entire interface is subject to change.
package os2
+43
View File
@@ -0,0 +1,43 @@
package os2
// get_env retrieves the value of the environment variable named by the key
// It returns the value, which will be empty if the variable is not present
// To distinguish between an empty value and an unset value, use lookup_env
// NOTE: the value will be allocated with the supplied allocator
get_env :: proc(key: string, allocator := context.allocator) -> string {
value, _ := lookup_env(key, allocator);
return value;
}
// lookup_env gets the value of the environment variable named by the key
// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true
// Otherwise the returned value will be empty and the boolean will be false
// NOTE: the value will be allocated with the supplied allocator
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
return _lookup_env(key, allocator);
}
// set_env sets the value of the environment variable named by the key
// Returns true on success, false on failure
set_env :: proc(key, value: string) -> bool {
return _set_env(key, value);
}
// unset_env unsets a single environment variable
// Returns true on success, false on failure
unset_env :: proc(key: string) -> bool {
return _unset_env(key);
}
clear_env :: proc() {
_clear_env();
}
// environ returns a copy of strings representing the environment, in the form "key=value"
// NOTE: the slice of strings and the strings with be allocated using the supplied allocator
environ :: proc(allocator := context.allocator) -> []string {
return _environ(allocator);
}
+80
View File
@@ -0,0 +1,80 @@
//+private
package os2
import "core:mem"
import win32 "core:sys/windows"
_lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
if key == "" {
return;
}
wkey := win32.utf8_to_wstring(key);
b := make([dynamic]u16, 100, context.temp_allocator);
for {
n := win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)));
if n == 0 {
err := win32.GetLastError();
if err == win32.ERROR_ENVVAR_NOT_FOUND {
return "", false;
}
}
if n <= u32(len(b)) {
value = win32.utf16_to_utf8(b[:n], allocator);
found = true;
return;
}
resize(&b, len(b)*2);
}
}
_set_env :: proc(key, value: string) -> bool {
k := win32.utf8_to_wstring(key);
v := win32.utf8_to_wstring(value);
return bool(win32.SetEnvironmentVariableW(k, v));
}
_unset_env :: proc(key: string) -> bool {
k := win32.utf8_to_wstring(key);
return bool(win32.SetEnvironmentVariableW(k, nil));
}
_clear_env :: proc() {
envs := environ(context.temp_allocator);
for env in envs {
for j in 1..<len(env) {
if env[j] == '=' {
unset_env(env[0:j]);
break;
}
}
}
}
_environ :: proc(allocator := context.allocator) -> []string {
envs := win32.GetEnvironmentStringsW();
if envs == nil {
return nil;
}
defer win32.FreeEnvironmentStringsW(envs);
r := make([dynamic]string, 0, 50, allocator);
for from, i, p := 0, 0, envs; true; i += 1 {
c := (^u16)(uintptr(p) + uintptr(i*2))^;
if c == 0 {
if i <= from {
break;
}
w := mem.slice_ptr(mem.ptr_offset(p, from), i-from);
append(&r, win32.utf16_to_utf8(w, allocator));
from = i + 1;
}
}
return r[:];
}
+126
View File
@@ -0,0 +1,126 @@
package os2
Platform_Error_Min_Bits :: 32;
Error :: enum u64 {
None = 0,
// General Errors
Invalid_Argument,
Permission_Denied,
Exist,
Not_Exist,
Closed,
// Timeout Errors
Timeout,
// I/O Errors
// EOF is the error returned by `read` when no more input is available
EOF,
// Unexpected_EOF means that EOF was encountered in the middle of reading a fixed-sized block of data
Unexpected_EOF,
// Short_Write means that a write accepted fewer bytes than requested but failed to return an explicit error
Short_Write,
// Invalid_Write means that a write returned an impossible count
Invalid_Write,
// Short_Buffer means that a read required a longer buffer than was provided
Short_Buffer,
// No_Progress is returned by some implementations of `io.Reader` when many calls
// to `read` have failed to return any data or error.
// This is usually a signed of a broken `io.Reader` implementation
No_Progress,
Invalid_Whence,
Invalid_Offset,
Invalid_Unread,
Negative_Read,
Negative_Write,
Negative_Count,
Buffer_Full,
// Platform Specific Errors
Platform_Minimum = 1<<Platform_Error_Min_Bits,
}
Path_Error :: struct {
op: string,
path: string,
err: Error,
}
Link_Error :: struct {
op: string,
old: string,
new: string,
err: Error,
}
path_error_delete :: proc(perr: Maybe(Path_Error)) {
if err, ok := perr.?; ok {
context.allocator = error_allocator();
delete(err.op);
delete(err.path);
}
}
link_error_delete :: proc(lerr: Maybe(Link_Error)) {
if err, ok := lerr.?; ok {
context.allocator = error_allocator();
delete(err.op);
delete(err.old);
delete(err.new);
}
}
is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) {
if ferr >= .Platform_Minimum {
err = i32(u64(ferr)>>Platform_Error_Min_Bits);
ok = true;
}
return;
}
error_from_platform_error :: proc(errno: i32) -> Error {
return Error(u64(errno) << Platform_Error_Min_Bits);
}
error_string :: proc(ferr: Error) -> string {
#partial switch ferr {
case .None: return "";
case .Invalid_Argument: return "invalid argument";
case .Permission_Denied: return "permission denied";
case .Exist: return "file already exists";
case .Not_Exist: return "file does not exist";
case .Closed: return "file already closed";
case .Timeout: return "i/o timeout";
case .EOF: return "eof";
case .Unexpected_EOF: return "unexpected eof";
case .Short_Write: return "short write";
case .Invalid_Write: return "invalid write result";
case .Short_Buffer: return "short buffer";
case .No_Progress: return "multiple read calls return no data or error";
case .Invalid_Whence: return "invalid whence";
case .Invalid_Offset: return "invalid offset";
case .Invalid_Unread: return "invalid unread";
case .Negative_Read: return "negative read";
case .Negative_Write: return "negative write";
case .Negative_Count: return "negative count";
case .Buffer_Full: return "buffer full";
}
if errno, ok := is_platform_error(ferr); ok {
return _error_string(errno);
}
return "unknown error";
}
+14
View File
@@ -0,0 +1,14 @@
//+private
package os2
import win32 "core:sys/windows"
_error_string :: proc(errno: i32) -> string {
e := win32.DWORD(errno);
if e == 0 {
return "";
}
// TODO(bill): _error_string for windows
// FormatMessageW
return "";
}
+158
View File
@@ -0,0 +1,158 @@
package os2
import "core:io"
import "core:time"
Handle :: distinct uintptr;
Seek_From :: enum {
Start = 0, // seek relative to the origin of the file
Current = 1, // seek relative to the current offset
End = 2, // seek relative to the end
}
File_Mode :: distinct u32;
File_Mode_Dir :: File_Mode(1<<16);
File_Mode_Named_Pipe :: File_Mode(1<<17);
File_Mode_Device :: File_Mode(1<<18);
File_Mode_Char_Device :: File_Mode(1<<19);
File_Mode_Sym_Link :: File_Mode(1<<20);
O_RDONLY :: int( 0);
O_WRONLY :: int( 1);
O_RDWR :: int( 2);
O_APPEND :: int( 4);
O_CREATE :: int( 8);
O_EXCL :: int(16);
O_SYNC :: int(32);
O_TRUNC :: int(64);
stdin: Handle = 0; // OS-Specific
stdout: Handle = 1; // OS-Specific
stderr: Handle = 2; // OS-Specific
create :: proc(name: string) -> (Handle, Error) {
return _create(name);
}
open :: proc(name: string) -> (Handle, Error) {
return _open(name);
}
open_file :: proc(name: string, flag: int, perm: File_Mode) -> (Handle, Error) {
return _open_file(name, flag, perm);
}
close :: proc(fd: Handle) -> Error {
return _close(fd);
}
name :: proc(fd: Handle, allocator := context.allocator) -> string {
return _name(fd);
}
seek :: proc(fd: Handle, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
return _seek(fd, offset, whence);
}
read :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) {
return _read(fd, p);
}
read_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) {
return _read_at(fd, p, offset);
}
read_from :: proc(fd: Handle, r: io.Reader) -> (n: i64, err: Error) {
return _read_from(fd, r);
}
write :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) {
return _write(fd, p);
}
write_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) {
return _write_at(fd, p, offset);
}
write_to :: proc(fd: Handle, w: io.Writer) -> (n: i64, err: Error) {
return _write_to(fd, w);
}
file_size :: proc(fd: Handle) -> (n: i64, err: Error) {
return _file_size(fd);
}
sync :: proc(fd: Handle) -> Error {
return _sync(fd);
}
flush :: proc(fd: Handle) -> Error {
return _flush(fd);
}
truncate :: proc(fd: Handle, size: i64) -> Maybe(Path_Error) {
return _truncate(fd, size);
}
remove :: proc(name: string) -> Maybe(Path_Error) {
return _remove(name);
}
rename :: proc(old_path, new_path: string) -> Maybe(Path_Error) {
return _rename(old_path, new_path);
}
link :: proc(old_name, new_name: string) -> Maybe(Link_Error) {
return _link(old_name, new_name);
}
symlink :: proc(old_name, new_name: string) -> Maybe(Link_Error) {
return _symlink(old_name, new_name);
}
read_link :: proc(name: string) -> (string, Maybe(Path_Error)) {
return _read_link(name);
}
chdir :: proc(fd: Handle) -> Error {
return _chdir(fd);
}
chmod :: proc(fd: Handle, mode: File_Mode) -> Error {
return _chmod(fd, mode);
}
chown :: proc(fd: Handle, uid, gid: int) -> Error {
return _chown(fd, uid, gid);
}
lchown :: proc(name: string, uid, gid: int) -> Error {
return _lchown(name, uid, gid);
}
chtimes :: proc(name: string, atime, mtime: time.Time) -> Maybe(Path_Error) {
return _chtimes(name, atime, mtime);
}
exists :: proc(path: string) -> bool {
return _exists(path);
}
is_file :: proc(path: string) -> bool {
return _is_file(path);
}
is_dir :: proc(path: string) -> bool {
return _is_dir(path);
}
+98
View File
@@ -0,0 +1,98 @@
package os2
import "core:io"
file_to_stream :: proc(fd: Handle) -> (s: io.Stream) {
s.stream_data = rawptr(uintptr(fd));
s.stream_vtable = _file_stream_vtable;
return;
}
@(private)
error_to_io_error :: proc(ferr: Error) -> io.Error {
#partial switch ferr {
case .None: return .None;
case .EOF: return .EOF;
case .Unexpected_EOF: return .Unexpected_EOF;
case .Short_Write: return .Short_Write;
case .Invalid_Write: return .Invalid_Write;
case .Short_Buffer: return .Short_Buffer;
case .No_Progress: return .No_Progress;
case .Invalid_Whence: return .Invalid_Whence;
case .Invalid_Offset: return .Invalid_Offset;
case .Invalid_Unread: return .Invalid_Unread;
case .Negative_Read: return .Negative_Read;
case .Negative_Write: return .Negative_Write;
case .Negative_Count: return .Negative_Count;
case .Buffer_Full: return .Buffer_Full;
}
return .Unknown;
}
@(private)
_file_stream_vtable := &io.Stream_VTable{
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
fd := Handle(uintptr(s.stream_data));
ferr: Error;
n, ferr = read(fd, p);
err = error_to_io_error(ferr);
return;
},
impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
fd := Handle(uintptr(s.stream_data));
ferr: Error;
n, ferr = read_at(fd, p, offset);
err = error_to_io_error(ferr);
return;
},
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
fd := Handle(uintptr(s.stream_data));
ferr: Error;
n, ferr = write_to(fd, w);
err = error_to_io_error(ferr);
return;
},
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
fd := Handle(uintptr(s.stream_data));
ferr: Error;
n, ferr = write(fd, p);
err = error_to_io_error(ferr);
return;
},
impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
fd := Handle(uintptr(s.stream_data));
ferr: Error;
n, ferr = write_at(fd, p, offset);
err = error_to_io_error(ferr);
return;
},
impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
fd := Handle(uintptr(s.stream_data));
ferr: Error;
n, ferr = read_from(fd, r);
err = error_to_io_error(ferr);
return;
},
impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
fd := Handle(uintptr(s.stream_data));
n, ferr := seek(fd, offset, Seek_From(whence));
err := error_to_io_error(ferr);
return n, err;
},
impl_size = proc(s: io.Stream) -> i64 {
fd := Handle(uintptr(s.stream_data));
sz, _ := file_size(fd);
return sz;
},
impl_flush = proc(s: io.Stream) -> io.Error {
fd := Handle(uintptr(s.stream_data));
ferr := flush(fd);
return error_to_io_error(ferr);
},
impl_close = proc(s: io.Stream) -> io.Error {
fd := Handle(uintptr(s.stream_data));
ferr := close(fd);
return error_to_io_error(ferr);
},
};
+122
View File
@@ -0,0 +1,122 @@
package os2
import "core:mem"
import "core:strconv"
import "core:unicode/utf8"
write_string :: proc(fd: Handle, s: string) -> (n: int, err: Error) {
return write(fd, transmute([]byte)s);
}
write_byte :: proc(fd: Handle, b: byte) -> (n: int, err: Error) {
return write(fd, []byte{b});
}
write_rune :: proc(fd: Handle, r: rune) -> (n: int, err: Error) {
if r < utf8.RUNE_SELF {
return write_byte(fd, byte(r));
}
b: [4]byte;
b, n = utf8.encode_rune(r);
return write(fd, b[:n]);
}
write_encoded_rune :: proc(fd: Handle, r: rune) -> (n: int, err: Error) {
wrap :: proc(m: int, merr: Error, n: ^int, err: ^Error) -> bool {
n^ += m;
if merr != nil {
err^ = merr;
return true;
}
return false;
}
if wrap(write_byte(fd, '\''), &n, &err) { return; }
switch r {
case '\a': if wrap(write_string(fd, "\\a"), &n, &err) { return; }
case '\b': if wrap(write_string(fd, "\\b"), &n, &err) { return; }
case '\e': if wrap(write_string(fd, "\\e"), &n, &err) { return; }
case '\f': if wrap(write_string(fd, "\\f"), &n, &err) { return; }
case '\n': if wrap(write_string(fd, "\\n"), &n, &err) { return; }
case '\r': if wrap(write_string(fd, "\\r"), &n, &err) { return; }
case '\t': if wrap(write_string(fd, "\\t"), &n, &err) { return; }
case '\v': if wrap(write_string(fd, "\\v"), &n, &err) { return; }
case:
if r < 32 {
if wrap(write_string(fd, "\\x"), &n, &err) { return; }
b: [2]byte;
s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil);
switch len(s) {
case 0: if wrap(write_string(fd, "00"), &n, &err) { return; }
case 1: if wrap(write_rune(fd, '0'), &n, &err) { return; }
case 2: if wrap(write_string(fd, s), &n, &err) { return; }
}
} else {
if wrap(write_rune(fd, r), &n, &err) { return; }
}
}
_ = wrap(write_byte(fd, '\''), &n, &err);
return;
}
write_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (n: int, err: Error) {
s := transmute([]byte)mem.Raw_Slice{data, len};
return write(fd, s);
}
read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (n: int, err: Error) {
s := transmute([]byte)mem.Raw_Slice{data, len};
return read(fd, s);
}
read_entire_file :: proc(name: string, allocator := context.allocator) -> ([]byte, Error) {
f, ferr := open(name);
if ferr != nil {
return nil, ferr;
}
defer close(f);
size: int;
if size64, err := file_size(f); err == nil {
if i64(int(size64)) != size64 {
size = int(size64);
}
}
size += 1; // for EOF
// TODO(bill): Is this correct logic?
total: int;
data := make([]byte, size, allocator);
for {
n, err := read(f, data[total:]);
total += n;
if err != nil {
if err == .EOF {
err = nil;
}
return data[:total], err;
}
}
}
write_entire_file :: proc(name: string, data: []byte, perm: File_Mode, truncate := true) -> Error {
flags := O_WRONLY|O_CREATE;
if truncate {
flags |= O_TRUNC;
}
f, err := open_file(name, flags, perm);
if err != nil {
return err;
}
_, err = write(f, data);
if cerr := close(f); cerr != nil && err == nil {
err = cerr;
}
return err;
}
+136
View File
@@ -0,0 +1,136 @@
//+private
package os2
import "core:io"
import "core:time"
_create :: proc(name: string) -> (Handle, Error) {
return 0, .None;
}
_open :: proc(name: string) -> (Handle, Error) {
return 0, .None;
}
_open_file :: proc(name: string, flag: int, perm: File_Mode) -> (Handle, Error) {
return 0, .None;
}
_close :: proc(fd: Handle) -> Error {
return .None;
}
_name :: proc(fd: Handle, allocator := context.allocator) -> string {
return "";
}
_seek :: proc(fd: Handle, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
return;
}
_read :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) {
return;
}
_read_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) {
return;
}
_read_from :: proc(fd: Handle, r: io.Reader) -> (n: i64, err: Error) {
return;
}
_write :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) {
return;
}
_write_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) {
return;
}
_write_to :: proc(fd: Handle, w: io.Writer) -> (n: i64, err: Error) {
return;
}
_file_size :: proc(fd: Handle) -> (n: i64, err: Error) {
return;
}
_sync :: proc(fd: Handle) -> Error {
return .None;
}
_flush :: proc(fd: Handle) -> Error {
return .None;
}
_truncate :: proc(fd: Handle, size: i64) -> Maybe(Path_Error) {
return nil;
}
_remove :: proc(name: string) -> Maybe(Path_Error) {
return nil;
}
_rename :: proc(old_path, new_path: string) -> Maybe(Path_Error) {
return nil;
}
_link :: proc(old_name, new_name: string) -> Maybe(Link_Error) {
return nil;
}
_symlink :: proc(old_name, new_name: string) -> Maybe(Link_Error) {
return nil;
}
_read_link :: proc(name: string) -> (string, Maybe(Path_Error)) {
return "", nil;
}
_chdir :: proc(fd: Handle) -> Error {
return .None;
}
_chmod :: proc(fd: Handle, mode: File_Mode) -> Error {
return .None;
}
_chown :: proc(fd: Handle, uid, gid: int) -> Error {
return .None;
}
_lchown :: proc(name: string, uid, gid: int) -> Error {
return .None;
}
_chtimes :: proc(name: string, atime, mtime: time.Time) -> Maybe(Path_Error) {
return nil;
}
_exists :: proc(path: string) -> bool {
return false;
}
_is_file :: proc(path: string) -> bool {
return false;
}
_is_dir :: proc(path: string) -> bool {
return false;
}
_path_error_delete :: proc(perr: Maybe(Path_Error)) {
}
_link_error_delete :: proc(lerr: Maybe(Link_Error)) {
}
+21
View File
@@ -0,0 +1,21 @@
package os2
import "core:runtime"
heap_allocator :: proc() -> runtime.Allocator {
return runtime.Allocator{
procedure = heap_allocator_proc,
data = nil,
};
}
heap_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
return _heap_allocator_proc(allocator_data, mode, size, alignment, old_memory, old_size, flags, loc);
}
@(private)
error_allocator := heap_allocator;
+107
View File
@@ -0,0 +1,107 @@
//+private
package os2
import "core:runtime"
import "core:mem"
import win32 "core:sys/windows"
heap_alloc :: proc(size: int) -> rawptr {
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, 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);
}
return win32.HeapReAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, ptr, uint(new_size));
}
heap_free :: proc(ptr: rawptr) {
if ptr == nil {
return;
}
win32.HeapFree(win32.GetProcessHeap(), 0, ptr);
}
_heap_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
//
// 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) -> rawptr {
a := max(alignment, align_of(rawptr));
space := size + a - 1;
allocated_mem: rawptr;
if old_ptr != nil {
original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^;
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr));
} else {
allocated_mem = heap_alloc(space+size_of(rawptr));
}
aligned_mem := rawptr(mem.ptr_offset((^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 {
return nil;
}
aligned_mem = rawptr(aligned_ptr);
mem.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem;
return aligned_mem;
}
aligned_free :: proc(p: rawptr) {
if p != nil {
heap_free(mem.ptr_offset((^rawptr)(p), -1)^);
}
}
aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> rawptr {
if p == nil {
return nil;
}
return aligned_alloc(new_size, new_alignment, p);
}
switch mode {
case .Alloc:
return aligned_alloc(size, alignment);
case .Free:
aligned_free(old_memory);
case .Free_All:
// NOTE(tetra): Do nothing.
case .Resize:
if old_memory == nil {
return aligned_alloc(size, alignment);
}
return aligned_resize(old_memory, old_size, size, alignment);
case .Query_Features:
set := (^runtime.Allocator_Mode_Set)(old_memory);
if set != nil {
set^ = {.Alloc, .Free, .Resize, .Query_Features};
}
return set;
case .Query_Info:
return nil;
}
return nil;
}
+29
View File
@@ -0,0 +1,29 @@
package os2
Path_Separator :: _Path_Separator; // OS-Specific
Path_List_Separator :: _Path_List_Separator; // OS-Specific
is_path_separator :: proc(c: byte) -> bool {
return _is_path_separator(c);
}
mkdir :: proc(name: string, perm: File_Mode) -> Maybe(Path_Error) {
return _mkdir(name, perm);
}
mkdir_all :: proc(path: string, perm: File_Mode) -> Maybe(Path_Error) {
return _mkdir_all(path, perm);
}
remove_all :: proc(path: string) -> Maybe(Path_Error) {
return _remove_all(path);
}
getwd :: proc(allocator := context.allocator) -> (dir: string, err: Error) {
return _getwd(allocator);
}
setwd :: proc(dir: string) -> (err: Error) {
return _setwd(dir);
}
+31
View File
@@ -0,0 +1,31 @@
//+private
package os2
_Path_Separator :: '\\';
_Path_List_Separator :: ';';
_is_path_separator :: proc(c: byte) -> bool {
return c == '\\' || c == '/';
}
_mkdir :: proc(name: string, perm: File_Mode) -> Maybe(Path_Error) {
return nil;
}
_mkdir_all :: proc(path: string, perm: File_Mode) -> Maybe(Path_Error) {
// TODO(bill): _mkdir_all for windows
return nil;
}
_remove_all :: proc(path: string) -> Maybe(Path_Error) {
// TODO(bill): _remove_all for windows
return nil;
}
_getwd :: proc(allocator := context.allocator) -> (dir: string, err: Error) {
return "", nil;
}
_setwd :: proc(dir: string) -> (err: Error) {
return nil;
}
+5
View File
@@ -0,0 +1,5 @@
package os2
pipe :: proc() -> (r, w: Handle, err: Error) {
return _pipe();
}

Some files were not shown because too many files have changed in this diff Show More