Compare commits

..

399 Commits

Author SHA1 Message Date
gingerBill 14c4fed94c v0.12.0 2020-01-18 15:32:15 +00:00
gingerBill c3205316ed Remove debug printing 2020-01-18 14:53:53 +00:00
gingerBill b542ef273d Begin work on -target:windows_386 2020-01-18 14:44:53 +00:00
gingerBill 404132de17 Fix microsoft_craziness.h to work correctly 2020-01-18 12:36:18 +00:00
gingerBill cd43f4c94c Add suggestions for indexing constant values with a variable index 2020-01-18 12:09:26 +00:00
gingerBill 23ff98dea0 Fix microsoft_craziness.h 2020-01-18 12:08:45 +00:00
gingerBill c3a8e232a5 Add new intrinsics for polymorphic records:
type_is_specialized_polymorphic_record, type_is_unspecialized_polymorphic_record, type_polymorphic_record_parameter_count, type_polymorphic_record_parameter_value
2020-01-18 11:27:41 +00:00
gingerBill 7f89f6b582 Add intrinsics.type_is_specialization_of 2020-01-17 23:30:38 +00:00
gingerBill 159150c6d9 Allow not_in as keyword over notin, but still allow notin to work 2020-01-16 10:00:14 +00:00
gingerBill 527b39ce2b Merge remote-tracking branch 'origin/master' 2020-01-16 09:07:52 +00:00
gingerBill 5af3c7b0e8 Fix constant slice checking, again 2020-01-16 09:05:17 +00:00
gingerBill 5db4bd9944 Fix #536 2020-01-15 12:01:29 +00:00
gingerBill 20b410f149 Fix #540 2020-01-15 11:56:42 +00:00
gingerBill ae7cbd5171 Add debug info for enumerated arrays 2020-01-14 17:37:45 +00:00
gingerBill 04f7225ea5 Exact value zero value for T{} of basic types 2020-01-14 17:36:37 +00:00
gingerBill f0c6f29f82 Merge branch 'master' of https://github.com/odin-lang/Odin 2020-01-12 13:53:57 +00:00
gingerBill 7d9a9a2283 Add extra check for opaque types 2020-01-12 13:53:51 +00:00
gingerBill ba85e432e7 Fix Proc Type ABI printing on System V 2020-01-12 13:43:45 +00:00
gingerBill cfba29002a Add extra set_procedure_abi_types sanity checks in IR 2020-01-11 20:29:46 +00:00
gingerBill 11c7b6a2e4 Fix len of type bug 2020-01-11 20:26:36 +00:00
gingerBill 47f9876b36 Merge branch 'master' of https://github.com/odin-lang/Odin 2020-01-11 20:20:20 +00:00
gingerBill ff31f9a900 Fix @thread_local IR printing 2020-01-11 20:19:52 +00:00
gingerBill ebc4867514 Fix #521 Explicit union tag values 2020-01-11 20:16:42 +00:00
gingerBill e1ccba3de5 Improve runtime/default_allocators.odin 2020-01-11 20:12:50 +00:00
gingerBill 28570f8fa4 Merge branch 'master' of https://github.com/odin-lang/Odin 2020-01-11 20:11:47 +00:00
gingerBill 24bd370e1b Fix Panic with runtime.type_info_base #532 2020-01-11 20:11:39 +00:00
Mikkel Hjortshøj 3e67ae7339 Merge pull request #534 from ThisDrunkDane/fix-log-mistake
Fix mistake in .Short_File_Path option in file_console_logger.odin
2020-01-09 19:19:23 +01:00
Mikkel Hjortshoej 0e52c37865 Don't change delimiter as location has changed to be unified no matter the platform 2020-01-08 21:29:46 +01:00
gingerBill d520b9a1ba Fix typo 2020-01-05 12:49:32 +00:00
gingerBill 5c7d6fcfd0 Improve minimum dependency for complex numbers and quaternion numbers. 2020-01-04 18:04:12 +00:00
gingerBill 5ae924f988 Make "none" calling convention ignore return_by_pointer flag 2020-01-04 12:42:16 +00:00
gingerBill cae1e02593 Add extra in set_procedure_abi_types ir_print.cpp 2020-01-04 12:10:28 +00:00
gingerBill b09297da81 Remove -Wno-writable-strings 2020-01-04 10:59:12 +00:00
gingerBill 9abdfaaf6c Merge pull request #531 from Tetralux/fix-make-and-reserve
Fix make and reserve
2020-01-03 10:51:42 +00:00
Tetralux b32ef9e47b Fix make and reserve
- Set the allocator, even if memory allocation fails.
  Right now it doesn't, which means that if allocation fails, it'll use
  the context allocator instead. This memory will be leaked if the user
  doesn't understand that this happened.

- Only set len and cap of the array returned from make iif the memory allocation
  succeeded.
  This means that reserve will return false if you do this:
  ```
  a := make([dynamic]int, failing_allocator);
  if !reserve(&a, 5) do return; // or whatever indicates failure
  ```
2020-01-03 10:40:45 +00:00
gingerBill b8324b0776 Fix behaviour for make to return nil when alloc returns nil 2020-01-03 10:17:30 +00:00
gingerBill d0ca045586 Merge pull request #530 from KTRosenberg/unix_yield_sem_fix
Fixes for yielding and semaphore posting on unix
2020-01-03 09:50:12 +00:00
KTRosenberg 673879d1d2 added note about pthread_yield 2020-01-02 16:44:30 -05:00
KTRosenberg 5d1c9583cb added the demo to the gitignore 2020-01-02 16:42:34 -05:00
KTRosenberg d017b5de9d replaced pthread_yield with ssched_yield, fixed semaphore post:q 2020-01-02 16:25:48 -05:00
gingerBill 93ead4bcb3 Fix typo 2020-01-02 15:41:32 +00:00
gingerBill bbe9b4dee0 Merge branch 'master' of https://github.com/odin-lang/Odin 2020-01-02 15:07:58 +00:00
gingerBill 3bd00fd6b7 Add thread.Pool with example in demo.odin; Update linalg to support handness changes for projection matrices 2020-01-02 15:07:12 +00:00
gingerBill 83fec387d4 Merge pull request #529 from Tetralux/patch-1
Fix `append_string`
2020-01-02 11:39:20 +00:00
Tetralux f6f10d10e8 Fix append_string 2020-01-02 11:38:25 +00:00
gingerBill 16a7c55334 Add x y z w fields to quaternion types; Improve linalg quaternion mathematics 2020-01-01 16:14:00 +00:00
gingerBill e9e2ab240d Merge pull request #528 from oskarnp/foreign-dylib-fix
Fix dylib foreign import
2020-01-01 10:42:09 +00:00
oskarn 842281ddd3 Fix dylib foreign import 2020-01-01 11:36:42 +01:00
gingerBill 978d7fcb99 Fix typeid information for enumerated arrays 2019-12-31 16:54:50 +00:00
gingerBill b267a5964d Fix memset for unix 2019-12-31 14:28:01 +00:00
gingerBill b288613307 Add extra check for ZeroInit instruction 2019-12-31 14:21:16 +00:00
gingerBill 4591353724 Use naive definition of memset for !windows 2019-12-31 14:17:21 +00:00
gingerBill 13107628f8 Make mem.set use llvm.memset.p0i8.iXX 2019-12-31 14:09:51 +00:00
gingerBill c407687a4c Fix new changes to runtime for unix 2019-12-31 14:04:19 +00:00
gingerBill 5a50ab7a99 Add new runtime files. 2019-12-31 13:54:42 +00:00
gingerBill 4578544007 Merge pull request #526 from sci4me/unix-dynlib
Implement dynlib core library for unix/darwin
2019-12-31 13:22:48 +00:00
gingerBill bdfef08214 Fix typos in demo.odin 2019-12-31 12:54:19 +00:00
gingerBill 42678848b2 Rename math/bits package name to math_bits 2019-12-31 12:27:31 +00:00
gingerBill ab52f8d795 Move definition of mem.Allocator and log.Logger to package runtime, to reduce import cycle magic 2019-12-31 12:15:19 +00:00
Scitoshi Nakayobro d79ee7d530 Implement dynlib core library for unix/darwin; not 100% about the build tags 2019-12-30 19:09:59 -05:00
gingerBill 7e271310ff Fix constant out of bounds bug 2019-12-29 22:53:37 +00:00
gingerBill f24de51c65 Add _tls_index and _fltused for windows -no-crt 2019-12-29 21:28:50 +00:00
gingerBill 2252d992d7 Add -disable-assert to disable the code generation of the built-in run-time 'assert' procedure 2019-12-29 21:10:27 +00:00
gingerBill 2d70a784d1 Add quaternion_look_at orientation procedure to package math/linalg 2019-12-29 20:35:27 +00:00
gingerBill a8a4dc1eb1 Make default context.temp_allocator thread safe when using package thread 2019-12-29 18:08:48 +00:00
gingerBill 9e9e905431 Add @(private="file") and @(private="package") 2019-12-29 15:39:20 +00:00
gingerBill 8ee41c20af Add more to package math 2019-12-28 23:48:15 +00:00
gingerBill 11c705508d Split linalg into general and specific parts 2019-12-28 23:09:43 +00:00
gingerBill 267ae0b4a2 Disallow enumerated array literals without field = value 2019-12-28 23:07:54 +00:00
gingerBill 1bc6e6a7cc Add linalg.vector_lerp, linalg.vector_unlerp 2019-12-28 23:07:31 +00:00
gingerBill 33a458c520 Update package math/linalg 2019-12-28 23:00:13 +00:00
gingerBill 6a7ccd8c0a Add new procedures for package math: atan2, asin, acos, atan, sin_bit, ldexp 2019-12-28 18:12:27 +00:00
gingerBill 9ba2926e7e Fix enumerated array contiguous error 2019-12-28 16:31:15 +00:00
gingerBill a50b2d5d04 Fix enumerated array literal check 2019-12-28 14:05:55 +00:00
gingerBill 7f9626e5c7 Improve 'cannot slice' error message 2019-12-27 17:53:05 +00:00
gingerBill 7140c95c55 Fix slicing of pointer to array 2019-12-27 17:47:55 +00:00
gingerBill ceef5db547 Support for and inline for for enumerated arrays 2019-12-27 17:16:43 +00:00
gingerBill 5ec8dd166a Add #partial tag for enumerated arrays to prevent common errors using non-contiguous enumerations 2019-12-27 16:55:32 +00:00
gingerBill 80a32a8182 Fix missing case 2019-12-27 16:01:58 +00:00
gingerBill c2b3056094 Fix enumerated array compound literals IR 2019-12-27 15:53:35 +00:00
gingerBill f99f351e01 Add constant literal expressions 2019-12-27 15:49:52 +00:00
gingerBill 880c7f01a8 Fix array lengths with enum value counts. 2019-12-27 13:55:18 +00:00
gingerBill 10f0961184 Enumerated arrays [Enum_Type]Elem_Type 2019-12-27 12:51:02 +00:00
gingerBill eea403d0ab Fix #514 2019-12-27 10:18:32 +00:00
gingerBill 2cc5c4eed3 Fix https://github.com/odin-lang/Odin/issues/522 2019-12-27 10:11:13 +00:00
gingerBill f308d8d73e Merge pull request #520 from SrMordred/memcpy
minor details ;)
2019-12-26 12:09:44 +00:00
Patric Dexheimer ff0bc3ccad minor details ;) 2019-12-24 11:50:26 -03:00
gingerBill 072979c6d2 Merge pull request #519 from Tetralux/remove-prints
remove errorneous prints
2019-12-24 12:54:00 +00:00
Tetralux a3d2c40da0 whoops 2019-12-24 12:51:27 +00:00
gingerBill 5b1312342e Fix runtime.mem_copy_non_overlapping to be like C's memcpy 2019-12-24 08:07:43 +00:00
gingerBill 85e31e1b69 Fix os.open 2019-12-23 18:10:09 +00:00
gingerBill fb0fb4767b Merge pull request #517 from SSStormy/fix-os-linux-fs-stuffs
Unix: Fix improper _unix_open binding; make write_entire_file set sane file permissions.
2019-12-23 18:07:59 +00:00
Justas Dabrila 38a9a2b7fc Linux: write_entire_file sets 644 permissions on open. 2019-12-23 16:35:10 +02:00
Justas Dabrila 005c6af302 Fix improper _unix_open binding that was ignoring the mode arg 2019-12-23 16:34:20 +02:00
gingerBill 1d14b3059e Fix Internal Compiler Error: Type_Info for 'XXX' could not be found #507 2019-12-22 14:16:56 +00:00
gingerBill cc2fa8f756 Fix thread/thread_unix.odin to use the new switch conventions 2019-12-22 12:11:54 +00:00
gingerBill d1c9fd4e01 Implement #complete switch by default, replace with #partial switch #511 2019-12-22 12:03:48 +00:00
gingerBill 4593730632 Fix Trying to get a pointer to a struct field that does not have a size generates an LLVM error #509 2019-12-22 11:40:30 +00:00
gingerBill f62a0891bd Fix Crash when compiling for loop with parens around array expression #506 2019-12-22 11:33:33 +00:00
gingerBill 4f2d4716ad Fix Internal error when accessing polymorphic struct parameters (also provide suggestions?) #513 2019-12-22 10:40:34 +00:00
gingerBill 022b793a7d Merge branch 'master' of https://github.com/odin-lang/Odin 2019-12-22 10:28:55 +00:00
gingerBill 7267004a55 Remove import "core:runtime" in integers.odin to make -vet happy 2019-12-22 10:28:48 +00:00
vassvik 786c9dfe07 Add the ability to toggle release mode externally when calling build.bat
The following cases builds Odin with optimizations:

    build.bat 1
    build.bat release

Any other options, or the lack thereof, will build without optimizations.
2019-12-22 11:19:42 +01:00
gingerBill 81b24594ce Add udivmod128.odin 2019-12-21 19:49:09 +00:00
gingerBill 995ba0df9a Fix using on array in struct 2019-12-21 14:42:08 +00:00
gingerBill 08392d885e Add strconv/integers.odin 2019-12-21 14:01:29 +00:00
gingerBill d462dbb5be Deprecate using import 2019-12-21 12:11:16 +00:00
gingerBill 19c32ecb81 Add extra to -help output 2019-12-21 11:27:55 +00:00
gingerBill 494b1e7eaa Add -help which prints information about the compiler flags 2019-12-21 11:22:46 +00:00
gingerBill c43d17bfec Merge pull request #505 from Tetralux/patch-2
Fix mem.Arena
2019-12-16 08:05:39 +00:00
Tetralux c9723e2dc0 Fix mem.Arena 2019-12-15 21:36:40 +00:00
gingerBill 4ba579bc25 Also allow #no_bounds_check on an expression #499 2019-12-15 11:41:21 +00:00
gingerBill 58d4d424c6 Replace #vector[N]T with #simd[N]T to reduce confusion #498 2019-12-15 11:30:09 +00:00
gingerBill 89ccb5b99f Add assert into scratch_allocator_proc to prevent initialization cycles #504 2019-12-15 11:20:06 +00:00
gingerBill 7f5021c8e9 Disallow procedure calls with an associated deferred procedure to be used in logical binary expressions (short-circuiting) 2019-12-15 11:10:50 +00:00
gingerBill 8bec324779 Fix Duplicate integer switch case values incorrectly consider its absolute value #502
(Hashing proc was wrong for big ints)
2019-12-15 09:56:11 +00:00
gingerBill e6f26b9931 Fix Unable to initialize a typeid field in a struct literal #501 2019-12-15 09:48:05 +00:00
gingerBill b8c534eba9 Merge pull request #500 from SSStormy/fix-string_to_enum_value
Fix 'fmt.string_to_enum_value' not compiling
2019-12-15 09:38:37 +00:00
Justas Dabrila 95d3f43e15 Fix 'string_to_enum_value' not compiling 2019-12-14 01:08:47 +02:00
vassvik 2d97e1dee3 Fix NaN checks in core:math.classify
Currently the classify procedures checks for NaNs using the check `x != x`, which is always false for NaNs and therefore that case is never entered. Using `!(x == x)` will work on the other hand.
2019-12-12 19:12:12 +01:00
gingerBill be2dfd42fd Merge pull request #493 from ThisDrunkDane/master
Fix #399 by removing unused parameter
2019-12-08 14:35:18 +00:00
Mikkel Hjortshoej 851118faf4 Fix #399 by removing unused parameter 2019-12-08 02:09:03 +01:00
gingerBill 53cd7a3d0c Fix transmute from uintptr to/from proc 2019-12-07 15:44:28 +00:00
gingerBill f170648629 Fix issue with -thread-count flag with <= 0 count 2019-12-03 18:23:14 +00:00
gingerBill 42def957d5 Fix append_elem_string, again 2019-12-03 09:35:24 +00:00
gingerBill 6433a0d31e Fix append_elem_string 2019-12-03 09:16:11 +00:00
gingerBill 359e5d9e15 Fix append_elem_string 2019-12-03 09:07:15 +00:00
gingerBill e229885b2b Remove addressing mode Addressing_Immutable 2019-12-01 19:11:00 +00:00
gingerBill ee78374281 Fix typo 2019-12-01 18:57:43 +00:00
gingerBill ebe152a155 Disable aligned heap allocations hack in os.heap_allocator_proc 2019-12-01 18:53:27 +00:00
gingerBill 46582a45bd Fix IR string interning type 2019-12-01 18:18:03 +00:00
gingerBill 6b5ea011e7 Add strings.ptr_from_string 2019-12-01 18:06:49 +00:00
gingerBill 9503440eb0 Add strings.unsafe_string_to_cstring 2019-12-01 17:45:07 +00:00
gingerBill 3fa4c5043a Fix crash caused by not checking for correct SOA kind on polymorphic parameters 2019-12-01 16:05:48 +00:00
gingerBill 9db81498d8 Make the string type elements "immutable", akin to char const * in C
Allows for extra security and optimization benefits
2019-12-01 14:10:59 +00:00
gingerBill 7fbe0a6f23 Fix nil comparisons for soa slices and dynamic arrays 2019-12-01 11:56:08 +00:00
gingerBill 3fd5c3cd85 Merge pull request #458 from Tetralux/linux-threads
Implement core:thread and core:sync on Unix using pthreads
2019-12-01 11:33:23 +00:00
Tetralux 99121d6ff2 Implement core:thread and core:sync on Unix using pthreads
Also do some cleanup and refactoring of the thread, sync and time APIs.

- remove 'semaphore_release' because 'post' and 'wait' is easier to understand

- change 'semaphore_wait' to '*_wait_for' to match Condition

- pthreads can be given a stack, but doing so requires the user to set up the guard
  pages manually. BE WARNED. The alignment requirements of the stack are also
  platform-dependant; it may need to be page size aligned on some systems.
  Unclear which systems, however. See 'os.get_page_size', and 'mem.make_aligned'.
  HOWEVER: I was unable to get custom stacks with guard pages working reliably,
  so while you can do it, the API does not support it.

- add 'os.get_page_size', 'mem.make_aligned', and 'mem.new_aligned'.

- removed thread return values because windows and linux are not consistent; windows returns 'i32'
  and pthreads return 'void*'; besides which, if you really wanted to communicate how the
  thread exited, you probably wouldn't do it with the thread's exit code.

- fixed 'thread.is_done' on Windows; it didn't report true immediately after calling 'thread.join'.

- moved time related stuff out of 'core:os' to 'core:time'.

- add 'mem.align_backward'

- fixed default allocator alignment
  The heap on Windows, and calloc on Linux, both have no facility to request alignment.
  It's a bit of hack, but the heap_allocator now overallocates; `size + alignment` bytes,
  and aligns things to at least 2.
  It does both of these things to ensure that there is at least two bytes before the payload,
  which it uses to store how much padding it needed to insert in order to fulfil the alignment
  requested.

- make conditions more sane by matching the Windows behaviour.
  The fact that they were signalled now lingers until a thread tries to wait,
  causing them to just pass by uninterrupted, without sleeping or locking the
  underlying mutex, as it would otherwise need to do.
  This means that a thread no longer has to be waiting in order to be signalled, which
  avoids timing bugs that causes deadlocks that are hard to debug and fix.
  See the comment on the `sync.Condition.flag` field.

- add thread priority: `thread.create(worker_proc, .High)`
2019-12-01 00:46:23 +00:00
gingerBill 0c0c83ee29 Disable find_visual_studio_and_windows_sdk_utf8 temporarily 2019-11-28 21:07:06 +00:00
gingerBill b75d59d6e0 Make sort.merge_sort in place; Add sort.heap_sort 2019-11-27 16:06:07 +00:00
gingerBill 71c8a3456e Update package odin/parser for #soa and #vector 2019-11-27 15:23:54 +00:00
gingerBill 37e3e081c6 Update microsoft_craziness.h to work correctly with the rest of the codebase (and not use WIN32_LEAN_AND_MEAN) 2019-11-27 15:18:32 +00:00
gingerBill 5ea9fc3fb0 Merge pull request #478 from castano/auto-search-sdk
Allow running Odin compiler without using visual studio command prompt or setting up environgment variables.
2019-11-26 22:29:08 +00:00
Mikkel Hjortshøj 53f65224f6 Merge pull request #459 from zhibog/master
Base32 added for core:encoding
2019-11-26 19:45:52 +01:00
gingerBill 902d313c6a Fix issue with os.write on *nix with writing nothing 2019-11-24 10:08:08 +00:00
gingerBill 45d844f9d2 Disable #soa compound literals 2019-11-21 20:05:45 +00:00
gingerBill 9b58781122 #soa[dynamic]Type (Experimental) 2019-11-21 19:36:07 +00:00
gingerBill b74f8f2047 Fix SOA entity usage error on -vet 2019-11-21 18:21:27 +00:00
gingerBill 88c90cf99a Update demo.odin 2019-11-21 14:41:33 +00:00
gingerBill 321dcc60e3 Update fmt.odin 2019-11-21 14:41:12 +00:00
gingerBill 400f12f31f Update demo.odin 2019-11-21 14:23:59 +00:00
gingerBill 2c5a84bb78 #soa[]Type (Experimental) 2019-11-21 00:07:21 +00:00
gingerBill e01d8a04a9 Fix String causes a crash when used in a polymorphic type #483 2019-11-20 22:07:12 +00:00
gingerBill 69afa33fa5 Fix Implicit Selector Expressions do not work at the global/package scope #484 2019-11-20 21:56:37 +00:00
gingerBill 44e0e96612 Prepare SOA Struct code for slices and dynamic arrays *to be implemented* 2019-11-19 23:54:36 +00:00
gingerBill 0839dccfdc Fix Compiler panic with SIMD Vector debug information #481 2019-11-19 23:24:49 +00:00
zhibog 2484f4d04b Removed CSV stuff. 2019-11-17 20:09:00 +01:00
gingerBill d22e5b697d Add new #soa and #vector syntax 2019-11-17 10:30:37 -08:00
gingerBill 301ee664e9 Add Handmade Seattle 2019 Demos 2019-11-16 06:59:48 -08:00
gingerBill 9b4d4a2c61 Minor fix to os.write on darwin 2019-11-16 06:59:14 -08:00
gingerBill d9647174a3 Merge pull request #479 from ThisDrunkDane/master
Fix CI badge on README.md
2019-11-14 22:57:08 +00:00
gingerBill 4d29b64196 Merge pull request #475 from hasenj/reverse
fix string reversal
2019-11-14 22:52:01 +00:00
gingerBill 8be1b2e1b1 Merge pull request #476 from castano/symlink-fix
Add support for running odin through a symlink.
2019-11-14 22:50:17 +00:00
Mikkel Hjortshoej b85258a9fc Add event query param to CI badge 2019-11-14 14:47:01 -08:00
gingerBill 07897ed78e Fix System V bitcast/transmute bug 2019-11-14 14:45:23 -08:00
Mikkel Hjortshoej 8115386217 Add branch query to CI badge 2019-11-14 14:39:11 -08:00
castano f8dd4816ff Integrate microsoft_craziness better. 2019-11-14 09:30:05 -08:00
castano 928a445a14 Fix definition. GB_SYSTEM_WINDOWS is not defined yet. Ideally microsoft_crazines.h should be included after common, but conflicts with defer macro. 2019-11-14 09:29:59 -08:00
castano 42cd78497a Use Jon's single file lib to find the latest compiler and SDK paths instead of relying on environment variables.
This allows you to run the odin compiler without having to use the visual studio command prompt or setting up environment variables. It would be nice to not rely on the automatic search exclusively, but allow specifying the desired compiler or SDK version.

This change introduces various library dependencies in order to interact with COM objects. Not sure if there's a way around that.
2019-11-14 09:29:45 -08:00
castano 813f5d211f Fix linux build. 2019-11-14 09:22:37 -08:00
castano 71d129a709 Add support for running odin through a symlink. Use realpath when obtaining root dir. 2019-11-14 00:50:26 -08:00
Hasen Judy c35762528c fix string reversal 2019-11-13 23:36:52 +09:00
gingerBill aa796a1032 v0.11.1 2019-11-12 21:20:40 +00:00
gingerBill 5bf71ba75c big_int addition overflow rules 2019-11-11 22:47:25 +00:00
gingerBill 967981aacd Add -show-more-timings 2019-11-10 21:49:02 +00:00
gingerBill 33d05a07de Fix big_int_or and big_int_xor 2019-11-10 20:06:04 +00:00
gingerBill 8da8301b09 Fix big subtraction for very large numbers on edges of overflow. 2019-11-10 19:55:26 +00:00
gingerBill 536cceeef9 Add intrinsics.type_is_unsigned 2019-11-10 18:51:21 +00:00
gingerBill 098684a6fe Add 128-bit random procedures to package math/rand 2019-11-10 18:47:16 +00:00
zhibog 694ee02247 Missed one 2019-11-09 18:06:33 +01:00
zhibog 0451c88ab6 Fixed indenting 2019-11-09 18:04:30 +01:00
gingerBill 2ef7bfc06e Remove debug message 2019-11-09 10:45:03 +00:00
gingerBill 7bfdb4f9f4 Fix Compile time assert on non-constant boolean parameters #467 2019-11-09 10:44:42 +00:00
gingerBill 0a35b13411 Fix inline for bug for #468 2019-11-09 10:24:27 +00:00
zhibog 803f6a6651 Added procs to read and write just the data, without any file loading / writing 2019-11-08 22:17:24 +01:00
zhibog 672cfd51c3 Added names to return values 2019-11-08 20:21:18 +01:00
zhibog 80cdf8b6a8 Should be row_count obviously 2019-11-08 20:16:56 +01:00
zhibog dc2d5239c5 Removed comments regarding RFC test vectors 2019-11-08 20:06:58 +01:00
gingerBill dacfc9de15 Fix //+build for ! e.g. //+build !windows amd64, linux !amd64 2019-11-05 20:09:09 +00:00
gingerBill 689aa4d734 Fix //+build system 2019-11-05 20:02:11 +00:00
gingerBill 8a46b493fd Fix Slice passed incorrectly in LLVM IR to procedure called via procedure pointer #465 2019-11-05 19:40:08 +00:00
gingerBill 86abdc0603 Merge branch 'master' of https://github.com/odin-lang/Odin 2019-11-05 19:37:30 +00:00
gingerBill a634444f99 Fix "Polymorphic parameter declared in return type doesn't compile #464" by giving a conversion error (code wasn't handling polymorphic result types as intended) 2019-11-05 19:37:19 +00:00
vassvik 04a25b11ad Fix incorrect math.linalg.cross3 indices. 2019-11-04 17:39:26 +01:00
gingerBill 40546fbde2 Use runtime.mem_copy in package me 2019-11-03 19:59:41 +00:00
gingerBill c1176c2bcb Fix typeid comparison bug; Add extra messages for pointer address errors 2019-11-03 19:49:21 +00:00
gingerBill 57853fe1b1 Add SOA Struct Layout (experimental) to demo.odin 2019-11-03 12:52:13 +00:00
gingerBill 013b3b9fb3 Fix for -vet 2019-11-03 11:42:00 +00:00
gingerBill dc0f04e53b Fix typo in fmt 2019-11-03 00:50:47 +00:00
gingerBill 40606d9272 Fix fmt printing of anonymous SOA structs 2019-11-03 00:47:27 +00:00
gingerBill ebf7926fa4 SOA support of Structures and Arrays; Runtime information for SOA structs; fmt printing support for SOA structs 2019-11-03 00:32:22 +00:00
gingerBill dfb3101ecf SOA Struct support intrinsics.soa_struct 2019-11-02 21:08:51 +00:00
gingerBill e3d3a81617 multivalued procedure calls allows in for in to allow a pseudo-iterator; @thread_local for variables in procedure 2019-11-02 16:36:46 +00:00
gingerBill dbdbbcd60f Fix range in statement bug caused by incorrectly assigned addressing mode #461 2019-11-02 10:57:42 +00:00
gingerBill f9aaff99c6 Fix linalg.mul; add linalg.Matrix1xN 2019-11-02 10:30:11 +00:00
zhibog 4b718aae75 Added an implementation for reading and writing csv files 2019-11-01 22:35:46 +01:00
zhibog 20db0e7f09 Added Base32 + official test vectors from the RFC 2019-11-01 22:35:09 +01:00
zhibog 614ea5c168 Added official test vectors from the RFC 2019-11-01 22:34:19 +01:00
gingerBill 57565b78a6 v0.11.0 2019-11-01 19:45:39 +00:00
gingerBill b5b085914a Merge pull request #404 from hazeycode/master
Impl time for macOS
2019-11-01 19:44:50 +00:00
Chris Heyes 044e64beb0 Add missing foreign import to time_darwin 2019-11-01 19:39:26 +00:00
Chris Heyes adca5b57cd Move time code from os to time package 2019-11-01 19:33:48 +00:00
gingerBill 3b898e5224 Add @force to foreign import 2019-11-01 19:26:22 +00:00
Chris Heyes 153e7525b5 Merge remote-tracking branch 'upstream/master' 2019-11-01 19:18:33 +00:00
gingerBill 44a303e577 Add dummy packages purely for documentation of builtin and intrinsics 2019-11-01 19:00:23 +00:00
gingerBill d63878d0dd Merge pull request #437 from nakst/master
updated os_essence.odin
2019-11-01 10:20:29 +00:00
gingerBill a20c31d6b5 Fix polymorphic record parameter determination bug caused by polymorphic constants not being handled correctly #447 2019-10-31 22:58:38 +00:00
gingerBill 560bdc339b Fix stack overflow bug caused by polymorphic record with polymorphic constant parameters. #447
DOES NOT FIX THE UNDERLYING BUG
2019-10-31 22:39:12 +00:00
gingerBill 01dfb1dac8 Fix double calling of lhs of logical binary expressions 2019-10-31 20:17:50 +00:00
gingerBill ee8d3e03f8 Delay determination of procedure abi types until as late as possible to prevent type undetermination in self-referential data types #454 2019-10-31 18:25:39 +00:00
gingerBill 4aad45e3e7 Merge pull request #453 from kevinw/master
Remove unused variables in utf8.odin.
2019-10-29 11:47:37 +00:00
gingerBill fe5c642d9f Fix runtime.cstring_len 2019-10-29 08:47:05 +00:00
Kevin Watters b5fdb3f855 Remove unused variables in utf8.odin. 2019-10-28 11:13:47 -04:00
gingerBill 416ff149bd Fix procedure group "best fit" algorithm for polymorphic procedures 2019-10-27 19:42:21 +00:00
gingerBill 233c6e2d3a Merge branch 'master' of https://github.com/odin-lang/Odin 2019-10-27 18:43:53 +00:00
gingerBill a29a6d9285 Fix typos in package linalg; Fix norm_float64 in package rand 2019-10-27 18:43:40 +00:00
Mikkel Hjortshøj a7a31e4c23 Set CI shell on windows to cmd.exe 2019-10-27 18:12:23 +01:00
Mikkel Hjortshøj 72cad591bd Update ci.yml 2019-10-27 18:07:14 +01:00
Mikkel Hjortshøj 684bf7aa61 Update ci.yml 2019-10-27 18:04:14 +01:00
Mikkel Hjortshøj d760b95e4f Update ci.yml 2019-10-27 18:02:39 +01:00
gingerBill 5e81fc72b9 New package math and package math/linalg 2019-10-27 10:35:35 +00:00
gingerBill 0977ac111a Fix typo in package utf8; add wchar_t to package c 2019-10-27 08:34:20 +00:00
gingerBill 2db16d6a32 Add for package utf8: rune_at_pos, rune_string_at_pos, rune_at, rune_offset 2019-10-26 15:05:36 +01:00
gingerBill 5aa46d31b2 Move package decimal to be a subpackage of package strconv 2019-10-26 14:46:21 +01:00
gingerBill 14e8b299b7 Fix slice and dynamic array lengths determined from ranged compound literals 2019-10-26 14:43:06 +01:00
gingerBill c7cb754514 Fix typos 2019-10-26 14:37:27 +01:00
gingerBill a5e42a0465 Add ranged_fields_for_array_compound_literals 2019-10-26 14:36:28 +01:00
gingerBill d808f9eccf Add range_cache.cpp 2019-10-26 14:29:04 +01:00
gingerBill 1734eb949f Update utf8.accept_sizes to use ranged fields 2019-10-26 14:18:38 +01:00
gingerBill 7fae890ef9 Allow ranges for array-like compound literals 2019-10-26 14:06:29 +01:00
gingerBill 94879ed149 Fix Compiler assertion when applying using to _ procedure parameter. #451 2019-10-26 12:14:04 +01:00
gingerBill 2c75fe2314 Allow for cycles in record polymorphic parameters but not in actualized fields 2019-10-26 11:50:42 +01:00
gingerBill 1da0668653 Add utf8.rune_index 2019-10-26 11:50:01 +01:00
gingerBill c60fb10a6a Move old demos and old stuff to /misc 2019-10-26 10:40:17 +01:00
gingerBill 7140f42915 Modify runtime to reduce dependencies on other packages 2019-10-23 21:43:13 +01:00
gingerBill ad92fbfd4e Merge branch 'master' of https://github.com/odin-lang/Odin 2019-10-23 21:42:42 +01:00
gingerBill 1416946757 Add token.odin 2019-10-23 21:42:28 +01:00
gingerBill 6a8c4ee04c Merge pull request #450 from ThisDrunkDane/master
Setup Github Actions instead of Travis and Appveyor
2019-10-21 22:17:05 +01:00
Mikkel Hjortshøj 516df9123d Update README.md 2019-10-21 23:08:16 +02:00
Mikkel Hjortshøj 818d6dbbea Update README.md with new CI badge 2019-10-21 23:05:18 +02:00
Mikkel Hjortshoej 286c5b7b24 Remove old CI runners 2019-10-21 23:01:32 +02:00
Mikkel Hjortshøj c0e8113f6f Add github CI action 2019-10-21 23:00:24 +02:00
gingerBill e15dfa8eb6 Fix missing check for zero elements 2019-10-20 18:26:30 +01:00
gingerBill f12ded54f2 Support for named indices for array-like compound literals {3 = a, 1 = b} 2019-10-20 13:11:28 +01:00
gingerBill b4951c9b39 Merge branch 'master' of https://github.com/odin-lang/Odin 2019-10-20 10:50:30 +01:00
gingerBill 9f0a28017d Fix typo in string_to_string16 #444 2019-10-20 10:50:18 +01:00
Mikkel Hjortshøj d7c8a3a9dd Merge pull request #445 from Tetralux/patch-2
Clarify that you can pass a directory to `odin build`
2019-10-19 18:57:23 +02:00
Tetralux 10b109e25f Clarify that you can pass a directory to odin build
Changes the usage information to this:
```
D:\Software\odin\odin.exe is a tool for managing Odin source code
Usage:
        D:\Software\odin\odin.exe command [arguments]
Commands:
        build     compile .odin file, or directory of .odin files, as an executable.
                  one must contain the program's entry point, all must be in the same package.
        run       same as 'build', but also then runs the newly compiled executable.
        check     parse and type check .odin file
        query     parse, type check, and output a .json file containing information about the program
        docs      generate documentation for a .odin file
        version   print version
```
2019-10-19 17:55:05 +01:00
gingerBill 2afe4bea67 Add instrincs.type_is_valid_map_key 2019-10-15 22:43:04 +01:00
gingerBill 12ae5ed09e Fix missing typeid conversion case for variadic parameters 2019-10-15 20:47:27 +01:00
gingerBill 0bdc3b4f21 Fix Values coerce to typeid #443 2019-10-15 17:54:37 +01:00
gingerBill eaabb888d4 Update Odin Logo 2019-10-15 17:42:55 +01:00
gingerBill b53fe14c22 Change error to syntax_error in parser 2019-10-13 16:06:41 +01:00
gingerBill 45683703ea Fix System V for certain structs 2019-10-13 14:29:56 +01:00
gingerBill 03053a18ce Fix IR generation bug for nested foreign procedure declaration 2019-10-13 12:51:47 +01:00
gingerBill 2a6d9e8927 #panic; Minor change to demo.odin; Fix #assert bug at file scope 2019-10-13 12:38:23 +01:00
gingerBill fa81061db0 Minor fix to Odin types with System V ABI 2019-10-10 21:50:20 +01:00
gingerBill 39b3c8c80f Update System V ABI to for more Odin types 2019-10-10 21:39:46 +01:00
gingerBill 3139151935 Minor fix to systemv_distribute_struct_fields 2019-10-10 21:24:32 +01:00
gingerBill 672a8f5dbd Add Odin types for System V ABI 2019-10-10 21:13:06 +01:00
gingerBill 8672ff1c55 Fix typo in System V ABI determination 2019-10-10 20:57:31 +01:00
gingerBill abfa894566 Fix general IR parameter case 2019-10-10 20:52:07 +01:00
gingerBill 5b52fed268 Correct (experimental) System V AMD64 ABI support 2019-10-10 20:41:16 +01:00
Mikkel Hjortshøj 94a638b436 Update README.md
Remove license badge (Github shows it themselves now) and add discord badge
2019-10-10 11:10:10 +02:00
gingerBill 1b8c3ca22a Fix typos and make demo work with -vet 2019-10-08 20:28:45 +01:00
gingerBill 71b32ae117 Update demo.odin 2019-10-06 19:20:00 +01:00
gingerBill 939459b635 Change implicit semicolon rules for record types within procedure bodies; Update package odin/* 2019-10-06 19:16:55 +01:00
gingerBill 562b518394 Change print*_err to eprint* in core library 2019-10-06 18:54:29 +01:00
gingerBill d62503d031 Change precedence for in and notin to match + - | ~ 2019-10-06 18:14:02 +01:00
gingerBill 4e8a801b35 strings.split; strings.index; eprint* over print*_err; 2019-10-06 18:13:15 +01:00
gingerBill e1b711b3b3 Update demo.odin with more information 2019-10-06 15:07:16 +01:00
gingerBill 6c69e8c043 Make typeid semantics consistent across variables and constants 2019-10-06 14:55:25 +01:00
gingerBill 7fa2d25eea Fix #complete switch with pointer case doesn't compile #416 2019-10-05 10:22:41 +01:00
gingerBill dae514a2c9 Fix Using any in if statement asserts compiler #441 2019-10-05 09:40:05 +01:00
gingerBill 068993a819 Remove os_osx.odin 2019-10-01 20:55:15 +01:00
gingerBill 66ae4e5afc Change ODIN_OS string for osx from "osx" to "darwin" to allow for other platforms 2019-10-01 20:38:50 +01:00
gingerBill 218d1131e8 Change how foreign imports work for mac 2019-09-29 09:25:33 +01:00
gingerBill f4f6e9ad49 Fix -debug crash on windows caused by missing debug info for files. 2019-09-25 21:07:56 +01:00
gingerBill 48ab7f876c Fix Implicit Selector Expressions do not work for parameteric struct parameters. #438 2019-09-25 20:52:47 +01:00
nakst 1e53a6fa96 updated os_essence.odin 2019-09-18 16:19:19 +01:00
gingerBill 4cef160c87 Merge branch 'master' of https://github.com/odin-lang/Odin 2019-09-17 19:47:13 +01:00
gingerBill 68582c5ad1 Add suggestions to errors on casts and assignments. 2019-09-17 19:47:04 +01:00
gingerBill a9a2dafca5 Merge pull request #430 from nakst/master
New Essence OS layer; cross-compiling improvements
2019-09-09 14:39:35 +01:00
gingerBill da3467c25f Merge pull request #434 from odin-lang/ThisDrunkDane-patch-1
Make `odin run` return the process exit code
2019-09-08 20:28:10 +01:00
gingerBill 5fc42bf9c9 Update demo.odin 2019-09-08 19:15:12 +01:00
gingerBill 42bbd31df1 Disallow where clauses on non-polymorphic records 2019-09-08 19:03:57 +01:00
gingerBill c7cc38b7d8 Remove assert 2019-09-08 15:47:57 +01:00
gingerBill 4afc78efc6 Add where clauses to struct and union 2019-09-08 12:12:41 +01:00
Mikkel Hjortshøj bc34083c9c Also return on unix 2019-09-08 01:10:54 +02:00
Mikkel Hjortshøj 08dd8414c1 Make odin run return the process exit code 2019-09-08 01:09:04 +02:00
gingerBill d54255505a Fix Compiler does not complain about missing semicolon #433 2019-09-04 18:10:02 +01:00
gingerBill d4914c3546 Fix Ir panic on using append() from within anonymous function #432 2019-09-04 18:06:02 +01:00
gingerBill 772c8779fa Clean up thread pool code 2019-09-03 22:11:21 +01:00
gingerBill c92b2e9612 Fix semaphore posting 2019-09-03 21:17:46 +01:00
gingerBill 1af143b749 Fix debug mode for build.bat 2019-09-02 21:16:17 +01:00
gingerBill 1370c10d1b Fix Converting addresses to function pointers produces llvm-opt error #431 2019-09-02 18:59:07 +01:00
gingerBill 1348d8a8cd Improve thread pool (volatile hints, etc) 2019-09-02 18:49:23 +01:00
gingerBill 495aaacb81 Fix build.bat 2019-09-02 18:35:00 +01:00
nakst 22e982c8fb New Essence OS layer; cross-compiling improvements 2019-09-02 16:46:50 +01:00
gingerBill 6d614ef07c Remove thread naming on thread pool 2019-09-01 23:16:01 +01:00
gingerBill 723f351a6d Remove custom allocator for thread pool 2019-09-01 23:13:29 +01:00
gingerBill c93872cc13 Thread pool fixes 2019-09-01 22:57:53 +01:00
gingerBill 97dece15d7 Minor changes 2019-09-01 22:18:55 +01:00
gingerBill 657103c4cf ThreadPool for the parser 2019-09-01 20:02:39 +01:00
gingerBill b9d3129fb3 where clauses for procedure literals 2019-08-31 20:13:28 +01:00
gingerBill b311540b16 Make require_results an attribute rather than a suffix tag for procedures 2019-08-31 14:48:56 +01:00
gingerBill 07ced1cf0e Fix variable dependency ordering issues caused by procedure literals 2019-08-31 11:12:41 +01:00
gingerBill a1d4ea7718 Merge pull request #425 from thebirk/parser-threading
Fixed parser creating a new thread for each file.
2019-08-29 20:44:48 +01:00
thebirk f921a91fc8 Properly removed the semaphore. 2019-08-29 20:35:12 +02:00
thebirk 4dade34603 Removed unused semaphore on Parser. 2019-08-29 20:34:09 +02:00
thebirk d76249d90b Cleaned up parse_packages and the worker proc. 2019-08-29 20:27:38 +02:00
gingerBill d118fc569a Add intrinsincs.type_is_quaternion 2019-08-29 16:45:36 +01:00
gingerBill 2dc39a5cbd Improve demo.odin 2019-08-29 15:25:46 +01:00
gingerBill c89fc35e94 Fix global variable initialization ordering
(related to #427)
2019-08-29 14:36:42 +01:00
gingerBill 614d209824 Add debug information for quaternion types (fixes #428) 2019-08-29 10:39:03 +01:00
gingerBill f1a7b31209 Fix nested raw_union with using #428 2019-08-28 13:34:55 +01:00
thebirk 6a8b3fee38 Removed gb_thread_set_name because it segfaults on linux. 2019-08-26 20:23:52 +02:00
thebirk 4551521b2c Im just trying things at this point, Bill should just squash this PR at merge time ;) 2019-08-26 19:51:33 +02:00
thebirk 97dfcffa76 Fixed error where the parser would end early. 2019-08-26 19:09:52 +02:00
gingerBill 6d3feb4531 Fix typo in tokenizer (no actual bug) 2019-08-26 16:18:26 +01:00
thebirk c44d25d14f Fixed parser creating a new thread for each file. 2019-08-26 16:47:41 +02:00
gingerBill 25dd00cd0b Add complex/quaternion raw layouts to mem/raw.odin 2019-08-26 14:40:04 +01:00
gingerBill 01c10aa944 inline for loops (only for 'in' based for loops) 2019-08-26 13:54:35 +01:00
gingerBill 4908d1ebdd Update odin_tokenizer to support quaternion literals 2019-08-26 11:56:04 +01:00
gingerBill 7bc146e6fd Built-in Quaternions (Not just an April Fool's Joke any more) 2019-08-26 11:33:05 +01:00
gingerBill 59ab51acec Fix typo 2019-08-23 11:54:23 +01:00
gingerBill cf23954297 Improve #assert to show the procedure and signature it was called with; Allow the ability to print ExactValue correct now. 2019-08-23 11:51:04 +01:00
gingerBill d1cc6534cd Remove the rule that made any declaration prefixed with an underscore private to that package. 2019-08-23 10:32:05 +01:00
gingerBill 1b454c7ded Merge branch 'master' of https://github.com/odin-lang/Odin 2019-08-23 10:24:25 +01:00
gingerBill 150d4e343d Fix ~(1 << x) type inference bug 2019-08-23 10:24:18 +01:00
gingerBill 28ada801a0 Merge pull request #419 from zhibog/master
Added an implementation for Base64. Also provides the ability to supp…
2019-08-16 19:42:03 +01:00
zhibog a7676bff6e Added an implementation for Base64. Also provides the ability to supply your own alphabet and decoding table. 2019-08-15 22:05:06 +02:00
gingerBill 4369298e96 Add reflect/types.odin 2019-08-13 23:21:51 +01:00
gingerBill a58c29582e Add new stuff to package reflect; fix -vet for odin_parser 2019-08-13 23:21:33 +01:00
gingerBill 3ad20a2d2d Remove package types and merge with package reflect 2019-08-13 22:59:07 +01:00
gingerBill b86dfa7af7 Fix compiler crash with #defined #417 2019-08-13 22:51:04 +01:00
gingerBill 980890ee8a Make -vet happy on *nix 2019-08-13 22:39:53 +01:00
gingerBill 0a63690b39 Fix typo in ? array lengths error 2019-08-13 22:34:02 +01:00
gingerBill 0076a4df62 Fix compound literal printing for structs with custom alignment requirements 2019-08-13 22:33:05 +01:00
gingerBill 4c065a7e99 Keep -vet happy 2019-08-13 22:27:52 +01:00
gingerBill 04036aba9c package reflect; fix substring type bug; fix scoping rules for using on procedure parameter 2019-08-11 23:58:49 +01:00
gingerBill b08aa857b3 Remove dead keywords in odin_token 2019-08-09 23:01:22 +01:00
gingerBill 2d26278a65 Make structs with the same fields but with different tags distinct types 2019-08-09 22:52:19 +01:00
gingerBill 27a3c5449a Fix global variable initialization for certain types. 2019-08-09 22:35:48 +01:00
gingerBill 9c63212824 Struct field tags 2019-08-09 21:59:58 +01:00
gingerBill 65d41d4248 Fix bit_field comparison against nil #414 2019-08-09 20:31:11 +01:00
gingerBill b04231dd95 Improve implementation of intrinsics.type_* procedures 2019-08-04 14:54:23 +01:00
gingerBill 37633c1d2a intrinsics.type_* constant evaluation procedures 2019-08-04 11:02:00 +01:00
gingerBill 5877017d30 Add error message for non-constant polymorphic name parameters 2019-08-03 10:15:31 +01:00
gingerBill 132fdf14b8 Fix min, max, clamp final type bug 2019-08-03 10:07:09 +01:00
gingerBill e7d3001dd1 Fix constant default value error #408 (typo) 2019-07-29 18:33:06 +01:00
gingerBill f163181204 Add extra hints for LLVM for implicit reference parameters 2019-07-29 10:43:07 +01:00
gingerBill 2c5c8192f8 Fix parsing for procedure literals expression statements; improve assert performance; other minor fixes 2019-07-28 22:58:56 +01:00
gingerBill 162c87b1b8 Minor code clean-up 2019-07-28 18:44:50 +01:00
gingerBill 77734ea967 Improve the performance of simple array comparisons 2019-07-27 11:59:50 +01:00
gingerBill 912fc2890b Fix array comparisons and fix f32 literal LLVM issue regarding accurate representation 2019-07-27 11:33:22 +01:00
gingerBill 14059583cd Fix array comparisons 2019-07-27 10:44:40 +01:00
gingerBill f3bffb9810 Improvement to the Odin calling conventions to pass certain things by "implicit reference" (const & in C++) 2019-07-27 10:20:11 +01:00
gingerBill 540730c0be Merge branch 'master' of https://github.com/odin-lang/Odin 2019-07-27 00:45:44 +01:00
gingerBill 40f0e74b8c Change scoping rules to allow for shadowing of procedure parameters but not named return values 2019-07-27 00:45:36 +01:00
Mikkel Hjortshøj 5ca0cd60d0 Merge pull request #406 from JoshuaManton/master
Fix `scale_f32` and `scale_vec3` returning the wrong variable
2019-07-20 22:21:58 +02:00
Joshua Mark Manton 96f0a08725 Fix scale_f32 and scale_vec3 from returning the wrong variable. 2019-07-20 13:15:51 -07:00
Chris Heyes d85893954d Impl time for macOS 2019-07-16 23:49:53 +01:00
gingerBill d26033eb23 Revert demo.odin 2019-07-15 22:41:25 +01:00
gingerBill c7a70be824 Fix __get_map_key 2019-07-15 22:38:51 +01:00
gingerBill 08c490d9ac Fix bounds checking on slices for constant parameters 2019-07-15 22:26:51 +01:00
gingerBill 8ee7ee7120 Fix core library for the new procedure parameter addressing mode 2019-07-15 22:16:27 +01:00
gingerBill d471a59041 IR fix array comparisons 2019-07-15 21:32:38 +01:00
gingerBill f25818e923 Make procedure parameters just named values rather than copied variables 2019-07-15 21:18:37 +01:00
gingerBill 3d531be711 Improve type hinting for named parameters in call expressions 2019-07-13 15:38:50 +01:00
gingerBill 56d365a4e7 Improve type inference for procedure group parameters 2019-07-13 13:34:21 +01:00
gingerBill 308300c1fc Add extra error handling for parsing slices 2019-07-09 11:18:50 +01:00
gingerBill 927d6814f2 slice_data_cast 2019-07-09 11:09:46 +01:00
gingerBill 7c99f52187 Add minimum requirement of 2 variants for #no_nil 2019-07-09 10:49:45 +01:00
gingerBill 4ab9edeb53 union #no_nil 2019-07-09 10:28:13 +01:00
gingerBill c5b3d7a736 Update package odin_parser 2019-07-07 16:20:58 +01:00
gingerBill d7172e168e Fix target list branch rules for name-labelled block/if statements 2019-07-07 16:06:41 +01:00
gingerBill d99ffe604f Fix unions with zero variants 2019-07-07 14:38:11 +01:00
gingerBill b77c79294c Merge branch 'master' of https://github.com/odin-lang/Odin 2019-07-07 14:14:36 +01:00
gingerBill 8e722274f0 Disallow blank identifier polymorphic types $_ 2019-07-07 14:14:28 +01:00
gingerBill ebe7fc23a5 Merge pull request #400 from asmoaesl/patch-1
Correct two typos
2019-06-29 12:09:46 +01:00
Luke I. Wilson 4d40f564ef Correct two typos 2019-06-28 20:42:59 -05:00
gingerBill fd62959bf4 Fix procedure constant declaration value type assignment checking 2019-06-21 23:11:14 +01:00
gingerBill 8b8cada33e Fix procedure group compiler assert with no matching arguments #393 2019-06-21 22:55:00 +01:00
gingerBill aaa24894b6 Fix double-pointer indexing bug #396 2019-06-21 22:50:29 +01:00
gingerBill 2af19c496e Fix comparison for bit field values #386 2019-06-21 22:48:37 +01:00
gingerBill fea34b32ea Merge branch 'master' of https://github.com/odin-lang/Odin 2019-06-21 22:40:20 +01:00
gingerBill b891c0feab Fix ranges in switch statement for strings 2019-06-21 22:40:11 +01:00
gingerBill 9f039c323e Update FUNDING.yml 2019-06-20 09:29:18 +01:00
134 changed files with 23098 additions and 5353 deletions
-1
View File
@@ -1,4 +1,3 @@
# These are supported funding model platforms
github: [gingerBill]
patreon: gingerbill
+57
View File
@@ -0,0 +1,57 @@
name: CI
on: [push, pull_request]
jobs:
build_unix:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macOS-latest]
steps:
- uses: actions/checkout@v1
- name: (macOS) Download LLVM and setup PATH
if: startsWith(matrix.os, 'macOS')
run: |
brew install llvm
echo ::add-path::/usr/local/opt/llvm/bin
echo ::set-env name=CPATH::`xcrun --show-sdk-path`/usr/include
- name: (Linux) Download LLVM
if: startsWith(matrix.os, 'ubuntu')
run: |
sudo apt-get install llvm
- name: build odin
run: make release
- name: Odin run
run: ./odin run examples/demo/demo.odin
- name: Odin check
run: ./odin check examples/demo/demo.odin -vet
build_windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v1
- name: Install cURL
run: choco install curl
- name: Download and unpack LLVM bins
shell: cmd
run: |
cd bin
curl -sL https://github.com/odin-lang/Odin/releases/download/llvm-windows/llvm-binaries.zip --output llvm-binaries.zip
7z x llvm-binaries.zip > nul
- name: build Odin
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
./build_ci.bat
- name: Odin run
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin run examples/demo/demo.odin
- name: Odin check
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin check examples/demo/demo.odin -vet
+3
View File
@@ -23,6 +23,7 @@ bld/
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
demo
# MSTest test Results
[Tt]est[Rr]esult*/
@@ -271,3 +272,5 @@ shared/
* .ll
*.bc
*.ll
*.sublime-workspace
-24
View File
@@ -1,24 +0,0 @@
language: cpp
git:
depth: false
os:
- linux
- osx
compiler:
- clang
addons:
homebrew:
packages:
- llvm
script:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export PATH="/usr/local/opt/llvm/bin:$PATH" ; fi
- make release
- ./odin run examples/demo/demo.odin
- ./odin check examples/demo/demo.odin -vet
notifications:
email: false
+1 -1
View File
@@ -1,4 +1,4 @@
DISABLED_WARNINGS=-Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined -Wno-writable-strings
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
CC=clang
+6 -9
View File
@@ -10,15 +10,12 @@
<a href="https://github.com/odin-lang/odin/releases/latest">
<img src="https://img.shields.io/badge/platforms-Windows%20|%20Linux%20|%20macOS-green.svg">
</a>
<a href="https://github.com/odin-lang/odin/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/odin-lang/odin.svg">
</a>
<br>
<a href="https://ci.appveyor.com/project/ThisDrunkDane/odin-vf0ap">
<img src="https://ci.appveyor.com/api/projects/status/qss6l921c0eu85u6/branch/master?svg=true">
<a href="https://discord.gg/hnwN2Rj">
<img src="https://img.shields.io/discord/568138951836172421?logo=discord">
</a>
<a href="https://travis-ci.org/odin-lang/Odin">
<img src="https://travis-ci.org/odin-lang/Odin.svg?branch=master">
<a href="https://github.com/odin-lang/odin/actions">
<img src="https://github.com/odin-lang/odin/workflows/CI/badge.svg?branch=master&event=push">
</a>
</p>
@@ -71,9 +68,9 @@ Instructions for downloading and install the Odin compiler and libraries.
An overview of the Odin programming language.
#### [Frequently Asked Questsions (FAQ)](https://odin-lang.org/docs/faq)
#### [Frequently Asked Questions (FAQ)](https://odin-lang.org/docs/faq)
Answers to common questsions about Odin.
Answers to common questions about Odin.
#### [The Odin Wiki](https://github.com/odin-lang/Odin/wiki)
-19
View File
@@ -1,19 +0,0 @@
image:
- Visual Studio 2017
shallow_clone: true
platform: x64
install:
- cd bin
- appveyor DownloadFile https://github.com/odin-lang/Odin/releases/download/llvm-windows/llvm-binaries.zip
- 7z x llvm-binaries.zip > nul
- cd ..
build_script:
- call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
- ./build_ci.bat
test_script:
- odin run examples/demo/demo.odin
- odin check examples/demo/demo.odin -vet
+9 -3
View File
@@ -4,8 +4,15 @@
set exe_name=odin.exe
:: Debug = 0, Release = 1
set release_mode=0
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -GS- -EHsc- -GR-
if "%1" == "1" (
set release_mode=1
) else if "%1" == "release" (
set release_mode=1
) else (
set release_mode=0
)
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
if %release_mode% EQU 0 ( rem Debug
set compiler_flags=%compiler_flags% -Od -MDd -Z7
@@ -39,7 +46,6 @@ set linker_settings=%libs% %linker_flags%
del *.pdb > NUL 2> NUL
del *.ilk > NUL 2> NUL
cl %compiler_settings% "src\main.cpp" ^
/link %linker_settings% -OUT:%exe_name% ^
&& odin run examples/demo/demo.odin
+1 -1
View File
@@ -2,7 +2,7 @@
release_mode=$1
warnings_to_disable="-std=c++11 -Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined -Wno-writable-strings"
warnings_to_disable="-std=c++11 -Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined"
libraries="-pthread -ldl -lm -lstdc++"
other_args=""
compiler="clang"
+105
View File
@@ -0,0 +1,105 @@
// This is purely for documentation
package builtin
nil :: nil;
false :: 0!==0;
true :: 0==0;
ODIN_OS :: ODIN_OS;
ODIN_ARCH :: ODIN_ARCH;
ODIN_ENDIAN :: ODIN_ENDIAN;
ODIN_VENDOR :: ODIN_VENDOR;
ODIN_VERSION :: ODIN_VERSION;
ODIN_ROOT :: ODIN_ROOT;
ODIN_DEBUG :: ODIN_DEBUG;
byte :: u8; // alias
bool :: bool;
b8 :: b8;
b16 :: b16;
b32 :: b32;
b64 :: b64;
i8 :: i8;
u8 :: u8;
i16 :: i16;
u16 :: u16;
i32 :: i32;
u32 :: u32;
i64 :: i64;
u64 :: u64;
i128 :: i128;
u128 :: u128;
rune :: rune;
f16 :: f16;
f32 :: f32;
f64 :: f64;
complex32 :: complex32;
complex64 :: complex64;
complex128 :: complex128;
quaternion128 :: quaternion128;
quaternion256 :: quaternion256;
int :: int;
uint :: uint;
uintptr :: uintptr;
rawptr :: rawptr;
string :: string;
cstring :: cstring;
any :: any;
typeid :: typeid;
// Endian Specific Types
i16le :: i16le;
u16le :: u16le;
i32le :: i32le;
u32le :: u32le;
i64le :: i64le;
u64le :: u64le;
i128le :: i128le;
u128le :: u128le;
i16be :: i16be;
u16be :: u16be;
i32be :: i32be;
u32be :: u32be;
i64be :: i64be;
u64be :: u64be;
i128be :: i128be;
u128be :: u128be;
// Procedures
len :: proc(array: Array_Type) -> int ---
cap :: proc(array: Array_Type) -> int ---
size_of :: proc($T: typeid) -> int ---
align_of :: proc($T: typeid) -> int ---
offset_of :: proc($T: typeid) -> uintptr ---
type_of :: proc(x: expr) -> type ---
type_info_of :: proc($T: typeid) -> ^runtime.Type_Info ---
typeid_of :: proc($T: typeid) -> typeid ---
swizzle :: proc(x: [N]T, indices: ..int) -> [len(indices)]T ---
complex :: proc(real, imag: Float) -> Complex_Type ---
quaternion :: proc(real, imag, jmag, kmag: Float) -> Quaternion_Type ---
real :: proc(value: Complex_Or_Quaternion) -> Float ---
imag :: proc(value: Complex_Or_Quaternion) -> Float ---
jmag :: proc(value: Quaternion) -> Float ---
kmag :: proc(value: Quaternion) -> Float ---
conj :: proc(value: Complex_Or_Quaternion) -> Complex_Or_Quaternion ---
expand_to_tuple :: proc(value: Struct_Or_Array) -> (A, B, C, ...) ---
min :: proc(values: ..T) -> T ---
max :: proc(values: ..T) -> T ---
abs :: proc(value: T) -> T ---
clamp :: proc(value, minimum, maximum: T) -> T ---
+2
View File
@@ -31,3 +31,5 @@ ssize_t :: b.int;
ptrdiff_t :: b.int;
uintptr_t :: b.uintptr;
intptr_t :: b.int;
wchar_t :: (ODIN_OS == "windows") ? b.u16 : b.u32;
+21
View File
@@ -0,0 +1,21 @@
// +build linux, darwin
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;
lib := os.dlopen(path, flags);
return Library(lib), lib != nil;
}
unload_library :: proc(library: Library) {
os.dlclose(rawptr(library));
}
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
ptr = os.dlsym(rawptr(library), symbol);
found = ptr != nil;
return;
}
+2 -1
View File
@@ -1,3 +1,4 @@
// +build windows
package dynlib
import "core:sys/win32"
@@ -19,6 +20,6 @@ unload_library :: proc(library: Library) -> bool {
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
c_str := strings.clone_to_cstring(symbol, context.temp_allocator);
ptr = win32.get_proc_address(cast(win32.Hmodule)library, c_str);
found == ptr != nil;
found = ptr != nil;
return;
}
+145
View File
@@ -0,0 +1,145 @@
package base32
// @note(zh): Encoding utility for Base32
// A secondary param can be used to supply a custom alphabet to
// @link(encode) and a matching decoding table to @link(decode).
// If none is supplied it just uses the standard Base32 alphabet.
// Incase your specific version does not use padding, you may
// truncate it from the encoded output.
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'
};
PADDING :: '=';
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, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0,
0, 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, 0, 0, 0, 0, 0,
0, 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, 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, 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 {
out_length := (len(data) + 4) / 5 * 8;
out := make([]byte, out_length);
_encode(out, data);
return string(out);
}
@private
_encode :: inline proc "contextless"(out, data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) {
out := out;
data := data;
for len(data) > 0 {
carry: byte;
switch len(data) {
case:
out[7] = ENC_TABLE[data[4] & 0x1f];
carry = data[4] >> 5;
fallthrough;
case 4:
out[6] = ENC_TABLE[carry | (data[3] << 3) & 0x1f];
out[5] = ENC_TABLE[(data[3] >> 2) & 0x1f];
carry = data[3] >> 7;
fallthrough;
case 3:
out[4] = ENC_TABLE[carry | (data[2] << 1) & 0x1f];
carry = (data[2] >> 4) & 0x1f;
fallthrough;
case 2:
out[3] = ENC_TABLE[carry | (data[1] << 4) & 0x1f];
out[2] = ENC_TABLE[(data[1] >> 1) & 0x1f];
carry = (data[1] >> 6) & 0x1f;
fallthrough;
case 1:
out[1] = ENC_TABLE[carry | (data[0] << 2) & 0x1f];
out[0] = ENC_TABLE[data[0] >> 3];
}
if len(data) < 5 {
out[7] = byte(PADDING);
if len(data) < 4 {
out[6] = byte(PADDING);
out[5] = byte(PADDING);
if len(data) < 3 {
out[4] = byte(PADDING);
if len(data) < 2 {
out[3] = byte(PADDING);
out[2] = byte(PADDING);
}
}
}
break;
}
data = data[5:];
out = out[8:];
}
}
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check{
if len(data) == 0 do return []byte{};
outi := 0;
olen := len(data);
data := data;
out := make([]byte, len(data) / 8 * 5, allocator);
end := false;
for len(data) > 0 && !end {
dbuf : [8]byte;
dlen := 8;
for j := 0; j < 8; {
if len(data) == 0 {
dlen, end = j, true;
break;
}
input := data[0];
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");
dlen, end = j, true;
assert(dlen != 1 && dlen != 3 && dlen != 6, "Corrupted input");
break;
}
dbuf[j] = DEC_TABLE[input];
assert(dbuf[j] != 0xff, "Corrupted input");
j += 1;
}
switch dlen {
case 8:
out[outi + 4] = dbuf[6] << 5 | dbuf[7];
fallthrough;
case 7:
out[outi + 3] = dbuf[4] << 7 | dbuf[5] << 2 | dbuf[6] >> 3;
fallthrough;
case 5:
out[outi + 2] = dbuf[3] << 4 | dbuf[4] >> 1;
fallthrough;
case 4:
out[outi + 1] = dbuf[1] << 6 | dbuf[2] << 1 | dbuf[3] >> 4;
fallthrough;
case 2:
out[outi + 0] = dbuf[0] << 3 | dbuf[1] >> 2;
}
outi += 5;
}
return out;
}
+93
View File
@@ -0,0 +1,93 @@
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).
// 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', '+', '/'
};
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
};
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string #no_bounds_check {
length := len(data);
if length == 0 do return "";
out_length := ((4 * length / 3) + 3) &~ 3;
out := make([]byte, out_length, allocator);
c0, c1, c2, block: int;
for i, d := 0, 0; i < length; i, d = i + 3, d + 4 {
c0, c1, c2 = int(data[i]), 0, 0;
if i + 1 < length do c1 = int(data[i + 1]);
if i + 2 < length do c2 = int(data[i + 2]);
block = (c0 << 16) | (max(c1, 0) << 8) | max(c2, 0);
out[d] = ENC_TBL[block >> 18 & 63];
out[d + 1] = ENC_TBL[block >> 12 & 63];
out[d + 2] = c1 == 0 ? PADDING : ENC_TBL[block >> 6 & 63];
out[d + 3] = c2 == 0 ? PADDING : ENC_TBL[block & 63];
}
return string(out);
}
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check{
length := len(data);
if length == 0 do return []byte{};
pad_count := data[length - 1] == PADDING ? (data[length - 2] == PADDING ? 2 : 1) : 0;
out_length := ((length * 6) >> 3) - pad_count;
out := make([]byte, out_length, allocator);
c0, c1, c2, c3: int;
b0, b1, b2: int;
for i, j := 0, 0; i < length; i, j = i + 4, j + 3 {
c0 = DEC_TBL[data[i]];
c1 = DEC_TBL[data[i + 1]];
c2 = DEC_TBL[data[i + 2]];
c3 = DEC_TBL[data[i + 3]];
b0 = (c0 << 2) | (c1 >> 4);
b1 = (c1 << 4) | (c2 >> 2);
b2 = (c2 << 6) | c3;
out[j] = byte(b0);
out[j + 1] = byte(b1);
out[j + 2] = byte(b2);
}
return out;
}
+123 -121
View File
@@ -91,7 +91,7 @@ print :: proc(p: ^Parser, pretty := false) {
}
create_from_string :: proc(src: string) -> (^Parser, bool) {
return init(cast([]byte)src);
return init(transmute([]byte)src);
}
@@ -106,11 +106,11 @@ create_from_tokenizer :: proc(t: ^Tokenizer) -> (^Parser, bool) {
p := new(Parser);
for {
tok := scan(t);
if tok.kind == Kind.Illegal {
if tok.kind == .Illegal {
return p, false;
}
append(&p.tokens, tok);
if tok.kind == Kind.EOF {
if tok.kind == .EOF {
break;
}
}
@@ -120,7 +120,7 @@ create_from_tokenizer :: proc(t: ^Tokenizer) -> (^Parser, bool) {
}
if len(p.tokens) == 0 {
tok := Token{kind = Kind.EOF};
tok := Token{kind = .EOF};
tok.line, tok.column = 1, 1;
append(&p.tokens, tok);
return p, true;
@@ -134,8 +134,8 @@ create_from_tokenizer :: proc(t: ^Tokenizer) -> (^Parser, bool) {
p.dict_stack = make([dynamic]^Dict, 0, 4);
append(&p.dict_stack, &p.root);
for p.curr_token.kind != Kind.EOF &&
p.curr_token.kind != Kind.Illegal &&
for p.curr_token.kind != .EOF &&
p.curr_token.kind != .Illegal &&
p.curr_token_index < len(p.tokens) {
if !parse_assignment(p) {
break;
@@ -147,7 +147,7 @@ create_from_tokenizer :: proc(t: ^Tokenizer) -> (^Parser, bool) {
destroy :: proc(p: ^Parser) {
destroy_value :: proc(value: Value) {
switch v in value {
#partial switch v in value {
case Array:
for elem in v do destroy_value(elem);
delete(v);
@@ -168,9 +168,9 @@ destroy :: proc(p: ^Parser) {
}
error :: proc(p: ^Parser, pos: Pos, msg: string, args: ..any) {
fmt.printf_err("%s(%d:%d) Error: ", pos.file, pos.line, pos.column);
fmt.printf_err(msg, ..args);
fmt.println_err();
fmt.eprintf("%s(%d:%d) Error: ", pos.file, pos.line, pos.column);
fmt.eprintf(msg, ..args);
fmt.eprintln();
p.error_count += 1;
}
@@ -190,7 +190,7 @@ next_token :: proc(p: ^Parser) -> Token {
return prev;
}
unquote_char :: proc(s: string, quote: byte) -> (r: rune, multiple_bytes: bool, tail_string: string, success: bool) {
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');
@@ -201,18 +201,19 @@ unquote_char :: proc(s: string, quote: byte) -> (r: rune, multiple_bytes: bool,
}
w: int;
if s[0] == quote && quote == '"' {
if str[0] == quote && quote == '"' {
return;
} else if s[0] >= 0x80 {
r, w = utf8.decode_rune_in_string(s);
return r, true, s[w:], true;
} else if s[0] != '\\' {
return rune(s[0]), false, s[1:], true;
} 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(s) <= 1 {
if len(str) <= 1 {
return;
}
s := str;
c := s[1];
s = s[2:];
@@ -286,7 +287,7 @@ unquote_char :: proc(s: string, quote: byte) -> (r: rune, multiple_bytes: bool,
unquote_string :: proc(p: ^Parser, t: Token) -> (string, bool) {
if t.kind != Kind.String {
if t.kind != .String {
return t.lit, true;
}
s := t.lit;
@@ -367,8 +368,8 @@ expect_operator :: proc(p: ^Parser) -> Token {
fix_advance :: proc(p: ^Parser) {
for {
switch t := p.curr_token; t.kind {
case Kind.EOF, Kind.Semicolon:
#partial switch t := p.curr_token; t.kind {
case .EOF, .Semicolon:
return;
}
next_token(p);
@@ -376,7 +377,7 @@ fix_advance :: proc(p: ^Parser) {
}
copy_value :: proc(value: Value) -> Value {
switch v in value {
#partial switch v in value {
case Array:
a := make(Array, len(v));
for elem, idx in v {
@@ -406,79 +407,79 @@ lookup_value :: proc(p: ^Parser, name: string) -> (Value, bool) {
parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
tok := p.curr_token;
switch p.curr_token.kind {
case Kind.Ident:
#partial switch p.curr_token.kind {
case .Ident:
next_token(p);
v, ok := lookup_value(p, tok.lit);
if !ok do error(p, tok.pos, "Undeclared identifier %s", tok.lit);
return v, tok.pos;
case Kind.True:
case .True:
next_token(p);
return true, tok.pos;
case Kind.False:
case .False:
next_token(p);
return false, tok.pos;
case Kind.Nil:
case .Nil:
next_token(p);
return Nil_Value{}, tok.pos;
case Kind.Integer:
case .Integer:
next_token(p);
return strconv.parse_i64(tok.lit), tok.pos;
case Kind.Float:
case .Float:
next_token(p);
return strconv.parse_f64(tok.lit), tok.pos;
case Kind.String:
case .String:
next_token(p);
str, ok := unquote_string(p, tok);
if !ok do error(p, tok.pos, "Unable to unquote string");
return string(str), tok.pos;
case Kind.Open_Paren:
expect_token(p, Kind.Open_Paren);
case .Open_Paren:
expect_token(p, .Open_Paren);
expr, _ := parse_expr(p);
expect_token(p, Kind.Close_Paren);
expect_token(p, .Close_Paren);
return expr, tok.pos;
case Kind.Open_Bracket:
expect_token(p, Kind.Open_Bracket);
case .Open_Bracket:
expect_token(p, .Open_Bracket);
elems := make([dynamic]Value, 0, 4);
for p.curr_token.kind != Kind.Close_Bracket &&
p.curr_token.kind != Kind.EOF {
for p.curr_token.kind != .Close_Bracket &&
p.curr_token.kind != .EOF {
elem, _ := parse_expr(p);
append(&elems, elem);
if p.curr_token.kind == Kind.Semicolon && p.curr_token.lit == "\n" {
if p.curr_token.kind == .Semicolon && p.curr_token.lit == "\n" {
next_token(p);
} else if !allow_token(p, Kind.Comma) {
} else if !allow_token(p, .Comma) {
break;
}
}
expect_token(p, Kind.Close_Bracket);
expect_token(p, .Close_Bracket);
return Array(elems[:]), tok.pos;
case Kind.Open_Brace:
expect_token(p, Kind.Open_Brace);
case .Open_Brace:
expect_token(p, .Open_Brace);
dict := Dict{};
append(&p.dict_stack, &dict);
defer pop(&p.dict_stack);
for p.curr_token.kind != Kind.Close_Brace &&
p.curr_token.kind != Kind.EOF {
for p.curr_token.kind != .Close_Brace &&
p.curr_token.kind != .EOF {
name_tok := p.curr_token;
if !allow_token(p, Kind.Ident) && !allow_token(p, Kind.String) {
name_tok = expect_token(p, Kind.Ident);
if !allow_token(p, .Ident) && !allow_token(p, .String) {
name_tok = expect_token(p, .Ident);
}
name, ok := unquote_string(p, name_tok);
if !ok do error(p, tok.pos, "Unable to unquote string");
expect_token(p, Kind.Assign);
expect_token(p, .Assign);
elem, _ := parse_expr(p);
if _, ok2 := dict[name]; ok2 {
@@ -487,13 +488,13 @@ parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
dict[name] = elem;
}
if p.curr_token.kind == Kind.Semicolon && p.curr_token.lit == "\n" {
if p.curr_token.kind == .Semicolon && p.curr_token.lit == "\n" {
next_token(p);
} else if !allow_token(p, Kind.Comma) {
} else if !allow_token(p, .Comma) {
break;
}
}
expect_token(p, Kind.Close_Brace);
expect_token(p, .Close_Brace);
return dict, tok.pos;
}
@@ -502,14 +503,14 @@ parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
parse_atom_expr :: proc(p: ^Parser, operand: Value, pos: Pos) -> (Value, Pos) {
loop := true;
for loop {
switch p.curr_token.kind {
case Kind.Period:
for operand := operand; loop; {
#partial switch p.curr_token.kind {
case .Period:
next_token(p);
tok := next_token(p);
switch tok.kind {
case Kind.Ident:
#partial switch tok.kind {
case .Ident:
d, ok := operand.(Dict);
if !ok || d == nil {
error(p, tok.pos, "Expected a dictionary");
@@ -530,13 +531,13 @@ parse_atom_expr :: proc(p: ^Parser, operand: Value, pos: Pos) -> (Value, Pos) {
operand = nil;
}
case Kind.Open_Bracket:
expect_token(p, Kind.Open_Bracket);
case .Open_Bracket:
expect_token(p, .Open_Bracket);
index, index_pos := parse_expr(p);
expect_token(p, Kind.Close_Bracket);
expect_token(p, .Close_Bracket);
switch a in operand {
#partial switch a in operand {
case Array:
i, ok := index.(i64);
if !ok {
@@ -586,22 +587,22 @@ parse_atom_expr :: proc(p: ^Parser, operand: Value, pos: Pos) -> (Value, Pos) {
parse_unary_expr :: proc(p: ^Parser) -> (Value, Pos) {
op := p.curr_token;
switch p.curr_token.kind {
case Kind.At:
#partial switch p.curr_token.kind {
case .At:
next_token(p);
tok := expect_token(p, Kind.String);
tok := expect_token(p, .String);
v, ok := lookup_value(p, tok.lit);
if !ok do error(p, tok.pos, "Undeclared identifier %s", tok.lit);
return parse_atom_expr(p, v, tok.pos);
case Kind.Add, Kind.Sub:
case .Add, .Sub:
next_token(p);
// TODO(bill): Calcuate values as you go!
expr, pos := parse_unary_expr(p);
switch e in expr {
case i64: if op.kind == Kind.Sub do return -e, pos;
case f64: if op.kind == Kind.Sub do return -e, pos;
#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:
error(p, op.pos, "Unary operator %s can only be used on integers or floats", op.lit);
return nil, op.pos;
@@ -609,7 +610,7 @@ parse_unary_expr :: proc(p: ^Parser) -> (Value, Pos) {
return expr, op.pos;
case Kind.Not:
case .Not:
next_token(p);
expr, _ := parse_unary_expr(p);
if v, ok := expr.(bool); ok {
@@ -624,7 +625,7 @@ parse_unary_expr :: proc(p: ^Parser) -> (Value, Pos) {
value_order :: proc(v: Value) -> int {
switch _ in v {
#partial switch _ in v {
case bool, string:
return 1;
case i64:
@@ -640,13 +641,13 @@ match_values :: proc(left, right: ^Value) -> bool {
return match_values(right, left);
}
switch x in left^ {
#partial switch x in left^ {
case:
right^ = left^;
case bool, string:
return true;
case i64:
switch y in right^ {
#partial switch y in right^ {
case i64:
return true;
case f64:
@@ -655,7 +656,7 @@ match_values :: proc(left, right: ^Value) -> bool {
}
case f64:
switch y in right {
#partial switch y in right {
case f64:
return true;
}
@@ -664,78 +665,79 @@ match_values :: proc(left, right: ^Value) -> bool {
return false;
}
calculate_binary_value :: proc(p: ^Parser, op: Kind, x, y: 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;
match_values(&x, &y);
switch a in x {
#partial switch a in x {
case: return x, true;
case bool:
b, ok := y.(bool);
if !ok do return nil, false;
switch op {
case Kind.Eq: return a == b, true;
case Kind.NotEq: return a != b, true;
case Kind.And: return a && b, true;
case Kind.Or: return a || b, true;
#partial switch op {
case .Eq: return a == b, true;
case .NotEq: return a != b, true;
case .And: return a && b, true;
case .Or: return a || b, true;
}
case i64:
b, ok := y.(i64);
if !ok do return nil, false;
switch op {
case Kind.Add: return a + b, true;
case Kind.Sub: return a - b, true;
case Kind.Mul: return a * b, true;
case Kind.Quo: return a / b, true;
case Kind.Rem: return a % b, true;
case Kind.Eq: return a == b, true;
case Kind.NotEq: return a != b, true;
case Kind.Lt: return a < b, true;
case Kind.Gt: return a > b, true;
case Kind.LtEq: return a <= b, true;
case Kind.GtEq: return a >= b, true;
#partial switch op {
case .Add: return a + b, true;
case .Sub: return a - b, true;
case .Mul: return a * b, true;
case .Quo: return a / b, true;
case .Rem: return a % b, true;
case .Eq: return a == b, true;
case .NotEq: return a != b, true;
case .Lt: return a < b, true;
case .Gt: return a > b, true;
case .LtEq: return a <= b, true;
case .GtEq: return a >= b, true;
}
case f64:
b, ok := y.(f64);
if !ok do return nil, false;
switch op {
case Kind.Add: return a + b, true;
case Kind.Sub: return a - b, true;
case Kind.Mul: return a * b, true;
case Kind.Quo: return a / b, true;
case Kind.Eq: return a == b, true;
case Kind.NotEq: return a != b, true;
case Kind.Lt: return a < b, true;
case Kind.Gt: return a > b, true;
case Kind.LtEq: return a <= b, true;
case Kind.GtEq: return a >= b, true;
#partial switch op {
case .Add: return a + b, true;
case .Sub: return a - b, true;
case .Mul: return a * b, true;
case .Quo: return a / b, true;
case .Eq: return a == b, true;
case .NotEq: return a != b, true;
case .Lt: return a < b, true;
case .Gt: return a > b, true;
case .LtEq: return a <= b, true;
case .GtEq: return a >= b, true;
}
case string:
b, ok := y.(string);
if !ok do return nil, false;
switch op {
case Kind.Add:
#partial switch op {
case .Add:
n := len(a) + len(b);
data := make([]byte, n);
copy(data[:], cast([]byte)a);
copy(data[len(a):], cast([]byte)b);
copy(data[:], a);
copy(data[len(a):], b);
s := string(data);
append(&p.allocated_strings, s);
return s, true;
case Kind.Eq: return a == b, true;
case Kind.NotEq: return a != b, true;
case Kind.Lt: return a < b, true;
case Kind.Gt: return a > b, true;
case Kind.LtEq: return a <= b, true;
case Kind.GtEq: return a >= b, true;
case .Eq: return a == b, true;
case .NotEq: return a != b, true;
case .Lt: return a < b, true;
case .Gt: return a > b, true;
case .LtEq: return a <= b, true;
case .GtEq: return a >= b, true;
}
}
@@ -753,10 +755,10 @@ parse_binary_expr :: proc(p: ^Parser, prec_in: int) -> (Value, Pos) {
}
expect_operator(p);
if op.kind == Kind.Question {
if op.kind == .Question {
cond := expr;
x, _ := parse_expr(p);
expect_token(p, Kind.Colon);
expect_token(p, .Colon);
y, _ := parse_expr(p);
if t, ok := cond.(bool); ok {
@@ -789,13 +791,13 @@ parse_expr :: proc(p: ^Parser) -> (Value, Pos) {
expect_semicolon :: proc(p: ^Parser) {
kind := p.curr_token.kind;
switch kind {
case Kind.Comma:
#partial switch kind {
case .Comma:
error(p, p.curr_token.pos, "Expected ';', got ','");
next_token(p);
case Kind.Semicolon:
case .Semicolon:
next_token(p);
case Kind.EOF:
case .EOF:
// okay
case:
error(p, p.curr_token.pos, "Expected ';', got %s", p.curr_token.lit);
@@ -809,17 +811,17 @@ parse_assignment :: proc(p: ^Parser) -> bool {
return p.dict_stack[len(p.dict_stack)-1];
}
if p.curr_token.kind == Kind.Semicolon {
if p.curr_token.kind == .Semicolon {
next_token(p);
return true;
}
if p.curr_token.kind == Kind.EOF {
if p.curr_token.kind == .EOF {
return false;
}
tok := p.curr_token;
if allow_token(p, Kind.Ident) || allow_token(p, Kind.String) {
expect_token(p, Kind.Assign);
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");
expr, _ := parse_expr(p);
+13 -10
View File
@@ -137,7 +137,7 @@ kind_to_string := [len(Kind)]string{
};
precedence :: proc(op: Kind) -> int {
switch op {
#partial switch op {
case Question:
return 1;
case Or:
@@ -183,9 +183,9 @@ tokenizer_init :: proc(t: ^Tokenizer, src: []byte, file := "") {
}
token_error :: proc(t: ^Tokenizer, msg: string, args: ..any) {
fmt.printf_err("%s(%d:%d) Error: ", t.file, t.line_count, t.read_offset-t.line_offset+1);
fmt.printf_err(msg, ..args);
fmt.println_err();
fmt.eprintf("%s(%d:%d) Error: ", t.file, t.line_count, t.read_offset-t.line_offset+1);
fmt.eprintf(msg, ..args);
fmt.eprintln();
t.error_count += 1;
}
@@ -286,9 +286,10 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Kind, string) {
advance_to_next_rune(t);
}
}
scan_exponent :: proc(t: ^Tokenizer, tok: Kind, offset: int) -> (Kind, string) {
scan_exponent :: proc(t: ^Tokenizer, tok: Kind, offset: int) -> (kind: Kind, text: string) {
kind = tok;
if t.curr_rune == 'e' || t.curr_rune == 'E' {
tok = Float;
kind = Float;
advance_to_next_rune(t);
if t.curr_rune == '-' || t.curr_rune == '+' {
advance_to_next_rune(t);
@@ -299,16 +300,18 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Kind, string) {
token_error(t, "Illegal floating point exponent");
}
}
return tok, string(t.src[offset : t.offset]);
text = string(t.src[offset : t.offset]);
return;
}
scan_fraction :: proc(t: ^Tokenizer, tok: Kind, offset: int) -> (Kind, string) {
scan_fraction :: proc(t: ^Tokenizer, tok: Kind, offset: int) -> (kind: Kind, text: string) {
kind = tok;
if t.curr_rune == '.' {
tok = Float;
kind = Float;
advance_to_next_rune(t);
scan_mantissa(t, 10);
}
return scan_exponent(t, tok, offset);
return scan_exponent(t, kind, offset);
}
offset := t.offset;
+15 -16
View File
@@ -5,7 +5,7 @@ import "core:math/bits"
import "core:runtime"
import "core:strconv"
import "core:strings"
import "core:types"
import "core:reflect"
Marshal_Error :: enum {
None,
@@ -40,7 +40,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
ti := type_info_base(type_info_of(v.id));
a := any{v.data, ti.id};
switch info in ti.variant {
#partial switch info in ti.variant {
case Type_Info_Named:
panic("Unreachable");
@@ -95,17 +95,17 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
buf: [386]byte;
str := strconv.append_float(buf[1:], val, 'f', 2*ti.size, 8*ti.size);
str = string(buf[:len(str)+1]);
if str[1] == '+' || str[1] == '-' {
str = str[1:];
s := buf[:len(str)+1];
if s[1] == '+' || s[1] == '-' {
s = s[1:];
} else {
str[0] = '+';
s[0] = '+';
}
if str[0] == '+' {
str = str[1:];
if s[0] == '+' {
s = s[1:];
}
write_string(b, str);
write_string(b, string(s));
case Type_Info_Complex:
return Marshal_Error.Unsupported_Type;
@@ -194,7 +194,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
data := uintptr(entries.data) + uintptr(i*entry_size);
header := cast(^Map_Entry_Header)data;
if types.is_string(info.key) {
if reflect.is_string(info.key) {
marshal_arg(b, header.key.str);
} else {
marshal_arg(b, any{rawptr(&header.key.hash), info.key.id});
@@ -281,14 +281,13 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
if ti == nil {
return false;
}
ti = runtime.type_info_base(ti);
switch info in ti.variant {
t := runtime.type_info_base(ti);
#partial switch info in t.variant {
case runtime.Type_Info_Integer:
using runtime.Type_Info_Endianness;
switch info.endianness {
case Platform: return false;
case Little: return ODIN_ENDIAN != "little";
case Big: return ODIN_ENDIAN != "big";
case .Platform: return false;
case .Little: return ODIN_ENDIAN != "little";
case .Big: return ODIN_ENDIAN != "big";
}
}
return false;
+4 -4
View File
@@ -70,7 +70,7 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
defer value.end = token_end_pos(p.prev_token);
token := p.curr_token;
switch token.kind {
#partial switch token.kind {
case Kind.Null:
value.value = Null{};
advance_token(p);
@@ -105,7 +105,7 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
case:
if p.spec == Specification.JSON5 {
switch token.kind {
#partial switch token.kind {
case Kind.Infinity:
inf: u64 = 0x7ff0000000000000;
if token.text[0] == '-' {
@@ -174,7 +174,7 @@ parse_array :: proc(p: ^Parser) -> (value: Value, err: Error) {
clone_string :: proc(s: string, allocator: mem.Allocator) -> string {
n := len(s);
b := make([]byte, n+1, allocator);
copy(b, cast([]byte)s);
copy(b, s);
b[n] = 0;
return string(b[:n]);
}
@@ -349,7 +349,7 @@ unquote_string :: proc(token: Token, spec: Specification, allocator := context.a
}
b := make([]byte, len(s) + 2*utf8.UTF_MAX, allocator);
w := copy(b, cast([]byte)s[0:i]);
w := copy(b, s[0:i]);
loop: for i < len(s) {
c := s[i];
switch {
+4 -2
View File
@@ -333,7 +333,8 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
is_valid_number :: proc(s: string, spec: Specification) -> bool {
is_valid_number :: proc(str: string, spec: Specification) -> bool {
s := str;
if s == "" {
return false;
}
@@ -395,7 +396,8 @@ is_valid_number :: proc(s: string, spec: Specification) -> bool {
return s == "";
}
is_valid_string_literal :: proc(s: string, spec: Specification) -> bool {
is_valid_string_literal :: proc(str: string, spec: Specification) -> bool {
s := str;
if len(s) < 2 {
return false;
}
+1 -1
View File
@@ -56,7 +56,7 @@ Error :: enum {
destroy_value :: proc(value: Value) {
switch v in value.value {
#partial switch v in value.value {
case Object:
for key, elem in v {
delete(key);
+8 -9
View File
@@ -91,28 +91,27 @@ validate_array :: proc(p: ^Parser) -> bool {
validate_value :: proc(p: ^Parser) -> bool {
token := p.curr_token;
using Kind;
switch token.kind {
case Null, False, True:
#partial switch token.kind {
case .Null, .False, .True:
advance_token(p);
return true;
case Integer, Float:
case .Integer, .Float:
advance_token(p);
return true;
case String:
case .String:
advance_token(p);
return is_valid_string_literal(token.text, p.spec);
case Open_Brace:
case .Open_Brace:
return validate_object(p);
case Open_Bracket:
case .Open_Bracket:
return validate_array(p);
case:
if p.spec == Specification.JSON5 {
switch token.kind {
case Infinity, NaN:
#partial switch token.kind {
case .Infinity, .NaN:
advance_token(p);
return true;
}
+354 -310
View File
@@ -5,9 +5,9 @@ import "core:os"
import "core:mem"
import "core:math/bits"
import "core:unicode/utf8"
import "core:types"
import "core:strconv"
import "core:strings"
import "core:reflect"
@private
@@ -59,12 +59,18 @@ fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
// print* procedures return the number of bytes written
print :: proc(args: ..any) -> int { return fprint(context.stdout, ..args); }
print_err :: proc(args: ..any) -> int { return fprint(context.stderr, ..args); }
println :: proc(args: ..any) -> int { return fprintln(context.stdout, ..args); }
println_err :: proc(args: ..any) -> int { return fprintln(context.stderr, ..args); }
printf :: proc(fmt: string, args: ..any) -> int { return fprintf(context.stdout, fmt, ..args); }
printf_err :: proc(fmt: string, args: ..any) -> int { return fprintf(context.stderr, fmt, ..args); }
print :: proc(args: ..any) -> int { return fprint(context.stdout, ..args); }
println :: proc(args: ..any) -> int { return fprintln(context.stdout, ..args); }
printf :: proc(fmt: string, args: ..any) -> int { return fprintf(context.stdout, fmt, ..args); }
eprint :: proc(args: ..any) -> int { return fprint(context.stderr, ..args); }
eprintln :: proc(args: ..any) -> int { return fprintln(context.stderr, ..args); }
eprintf :: proc(fmt: string, args: ..any) -> int { return fprintf(context.stderr, fmt, ..args); }
@(deprecated="prefer eprint") print_err :: proc(args: ..any) -> int { return eprint(..args); }
@(deprecated="prefer eprintf") printf_err :: proc(fmt: string, args: ..any) -> int { return eprintf(fmt, ..args); }
@(deprecated="prefer eprintln") println_err :: proc(args: ..any) -> int { return eprintln(..args); }
// aprint* procedures return a string that was allocated with the current context
@@ -143,7 +149,7 @@ panicf :: proc "contextless" (fmt: string, args: ..any, loc := #caller_location)
fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info) {
data: [DEFAULT_BUFFER_SIZE]byte;
buf := strings.builder_from_slice(data[:]);
write_type(&buf, info);
reflect.write_type(&buf, info);
os.write_string(fd, strings.to_string(buf));
}
@@ -156,7 +162,7 @@ sbprint :: proc(buf: ^strings.Builder, args: ..any) -> string {
fi.buf = buf;
for arg, i in args {
is_string := arg != nil && types.is_string(type_info_of(arg.id));
is_string := arg != nil && reflect.is_string(type_info_of(arg.id));
if i > 0 && !is_string && !prev_string {
strings.write_byte(buf, ' ');
}
@@ -399,7 +405,7 @@ fmt_bad_verb :: proc(using fi: ^Info, verb: rune) {
strings.write_rune(buf, verb);
strings.write_byte(buf, '(');
if arg.id != nil {
write_typeid(buf, arg.id);
reflect.write_typeid(buf, arg.id);
strings.write_byte(buf, '=');
fmt_value(fi, arg, 'v');
} else {
@@ -648,32 +654,32 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
buf: [386]byte;
str := strconv.append_float(buf[1:], v, 'f', prec, bit_size);
str = string(buf[:len(str)+1]);
if str[1] == '+' || str[1] == '-' {
str = str[1:];
b := buf[:len(str)+1];
if b[1] == '+' || b[1] == '-' {
b = b[1:];
} else {
str[0] = '+';
b[0] = '+';
}
if fi.space && !fi.plus && str[0] == '+' {
str[0] = ' ';
if fi.space && !fi.plus && b[0] == '+' {
b[0] = ' ';
}
if len(str) > 1 && (str[1] == 'N' || str[1] == 'I') {
strings.write_string(fi.buf, str);
if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') {
strings.write_string(fi.buf, string(b));
return;
}
if fi.plus || str[0] != '+' {
if fi.zero && fi.width_set && fi.width > len(str) {
strings.write_byte(fi.buf, str[0]);
fmt_write_padding(fi, fi.width - len(str));
strings.write_string(fi.buf, str[1:]);
if fi.plus || b[0] != '+' {
if fi.zero && fi.width_set && fi.width > len(b) {
strings.write_byte(fi.buf, b[0]);
fmt_write_padding(fi, fi.width - len(b));
strings.write_string(fi.buf, string(b[1:]));
} else {
_pad(fi, str);
_pad(fi, string(b));
}
} else {
_pad(fi, str[1:]);
_pad(fi, string(b[1:]));
}
case 'e', 'E':
@@ -682,32 +688,32 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
buf: [386]byte;
str := strconv.append_float(buf[1:], v, 'e', prec, bit_size);
str = string(buf[:len(str)+1]);
if str[1] == '+' || str[1] == '-' {
str = str[1:];
b := buf[:len(str)+1];
if b[1] == '+' || b[1] == '-' {
b = b[1:];
} else {
str[0] = '+';
b[0] = '+';
}
if fi.space && !fi.plus && str[0] == '+' {
str[0] = ' ';
if fi.space && !fi.plus && b[0] == '+' {
b[0] = ' ';
}
if len(str) > 1 && (str[1] == 'N' || str[1] == 'I') {
strings.write_string(fi.buf, str);
if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') {
strings.write_string(fi.buf, string(b));
return;
}
if fi.plus || str[0] != '+' {
if fi.zero && fi.width_set && fi.width > len(str) {
strings.write_byte(fi.buf, str[0]);
fmt_write_padding(fi, fi.width - len(str));
strings.write_string(fi.buf, str[1:]);
if fi.zero && fi.width_set && fi.width > len(b) {
strings.write_byte(fi.buf, b[0]);
fmt_write_padding(fi, fi.width - len(b));
strings.write_string(fi.buf, string(b[1:]));
} else {
_pad(fi, str);
_pad(fi, string(b));
}
} else {
_pad(fi, str[1:]);
_pad(fi, string(b[1:]));
}
case 'h', 'H':
@@ -783,15 +789,16 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) {
}
}
enum_value_to_string :: proc(v: any) -> (string, bool) {
enum_value_to_string :: proc(val: any) -> (string, bool) {
v := val;
v.id = runtime.typeid_base(v.id);
type_info := type_info_of(v.id);
switch e in type_info.variant {
#partial switch e in type_info.variant {
case: return "", false;
case runtime.Type_Info_Enum:
get_str :: proc(i: $T, e: runtime.Type_Info_Enum) -> (string, bool) {
if types.is_string(e.base) {
if reflect.is_string(e.base) {
for val, idx in e.values {
if v, ok := val.(T); ok && v == i {
return e.names[idx], true;
@@ -830,8 +837,8 @@ enum_value_to_string :: proc(v: any) -> (string, bool) {
}
string_to_enum_value :: proc($T: typeid, s: string) -> (T, bool) {
ti := type_info_base(type_info_of(T));
if e, ok := ti.variant.(Type_Info_Enum); ok {
ti := runtime.type_info_base(type_info_of(T));
if e, ok := ti.variant.(runtime.Type_Info_Enum); ok {
for str, idx in e.names {
if s == str {
// NOTE(bill): Unsafe cast
@@ -850,7 +857,7 @@ fmt_enum :: proc(fi: ^Info, v: any, verb: rune) {
}
type_info := type_info_of(v.id);
switch e in type_info.variant {
#partial switch e in type_info.variant {
case: fmt_bad_verb(fi, verb);
case runtime.Type_Info_Enum:
switch verb {
@@ -866,6 +873,49 @@ fmt_enum :: proc(fi: ^Info, v: any, verb: rune) {
}
stored_enum_value_to_string :: proc(enum_type: ^runtime.Type_Info, ev: runtime.Type_Info_Enum_Value, offset: int = 0) -> (string, bool) {
et := runtime.type_info_base(enum_type);
#partial switch e in et.variant {
case: return "", false;
case runtime.Type_Info_Enum:
get_str :: proc(i: $T, e: runtime.Type_Info_Enum) -> (string, bool) {
if reflect.is_string(e.base) {
for val, idx in e.values {
if v, ok := val.(T); ok && v == i {
return e.names[idx], true;
}
}
} else if len(e.values) == 0 {
return "", true;
} else {
for val, idx in e.values {
if v, ok := val.(T); ok && v == i {
return e.names[idx], true;
}
}
}
return "", false;
}
switch v in ev {
case rune: return get_str(v + auto_cast offset, e);
case i8: return get_str(v + auto_cast offset, e);
case i16: return get_str(v + auto_cast offset, e);
case i32: return get_str(v + auto_cast offset, e);
case i64: return get_str(v + auto_cast offset, e);
case int: return get_str(v + auto_cast offset, e);
case u8: return get_str(v + auto_cast offset, e);
case u16: return get_str(v + auto_cast offset, e);
case u32: return get_str(v + auto_cast offset, e);
case u64: return get_str(v + auto_cast offset, e);
case uint: return get_str(v + auto_cast offset, e);
case uintptr: return get_str(v + auto_cast offset, e);
}
}
return "", false;
}
enum_value_to_u64 :: proc(ev: runtime.Type_Info_Enum_Value) -> u64 {
switch i in ev {
@@ -885,13 +935,31 @@ enum_value_to_u64 :: proc(ev: runtime.Type_Info_Enum_Value) -> u64 {
return 0;
}
enum_value_to_i64 :: proc(ev: runtime.Type_Info_Enum_Value) -> i64 {
switch i in ev {
case rune: return i64(i);
case i8: return i64(i);
case i16: return i64(i);
case i32: return i64(i);
case i64: return i64(i);
case int: return i64(i);
case u8: return i64(i);
case u16: return i64(i);
case u32: return i64(i);
case u64: return i64(i);
case uint: return i64(i);
case uintptr: return i64(i);
}
return 0;
}
fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool {
if ti == nil {
return false;
}
ti = runtime.type_info_base(ti);
switch info in ti.variant {
t := runtime.type_info_base(ti);
#partial switch info in t.variant {
case runtime.Type_Info_Integer:
switch info.endianness {
case .Platform: return false;
@@ -905,7 +973,7 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
byte_swap :: bits.byte_swap;
type_info := type_info_of(v.id);
switch info in type_info.variant {
#partial switch info in type_info.variant {
case runtime.Type_Info_Named:
val := v;
val.id = info.base.id;
@@ -946,7 +1014,7 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
if name != "" {
strings.write_string(fi.buf, name);
} else {
write_type(fi.buf, type_info);
reflect.write_type(fi.buf, type_info);
}
strings.write_byte(fi.buf, '{');
defer strings.write_byte(fi.buf, '}');
@@ -968,14 +1036,15 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
continue loop;
}
}
strings.write_i64(fi.buf, i64(i), 10);
v := i64(i) + info.lower;
strings.write_i64(fi.buf, v, 10);
commas += 1;
}
}
}
fmt_bit_field :: proc(fi: ^Info, v: any, bit_field_name: string = "") {
type_info := type_info_of(v.id);
switch info in type_info.variant {
#partial switch info in type_info.variant {
case runtime.Type_Info_Named:
val := v;
val.id = info.base.id;
@@ -1041,18 +1110,18 @@ fmt_opaque :: proc(fi: ^Info, v: any) {
if ot, ok := rt.type_info_base(type_info).variant.(rt.Type_Info_Opaque); ok {
elem := rt.type_info_base(ot.elem);
if elem == nil do return;
write_type(fi.buf, type_info);
reflect.write_type(fi.buf, type_info);
strings.write_byte(fi.buf, '{');
defer strings.write_byte(fi.buf, '}');
switch in elem.variant {
#partial switch in elem.variant {
case rt.Type_Info_Integer, rt.Type_Info_Pointer, rt.Type_Info_Float:
fmt_value(fi, any{v.data, elem.id}, 'v');
case:
// Okay
}
} else {
write_type(fi.buf, type_info);
reflect.write_type(fi.buf, type_info);
strings.write_byte(fi.buf, '{');
strings.write_byte(fi.buf, '}');
}
@@ -1066,8 +1135,11 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
type_info := type_info_of(v.id);
switch info in type_info.variant {
case runtime.Type_Info_Any: // Ignore
case runtime.Type_Info_Tuple: // Ignore
case runtime.Type_Info_Named:
switch b in info.base.variant {
#partial switch b in info.base.variant {
case runtime.Type_Info_Struct:
if verb != 'v' {
fmt_bad_verb(fi, verb);
@@ -1078,8 +1150,11 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
strings.write_string(fi.buf, "{}");
return;
};
is_soa := b.soa_kind != .None;
strings.write_string(fi.buf, info.name);
strings.write_byte(fi.buf, '{');
strings.write_byte(fi.buf, is_soa ? '[' : '{');
hash := fi.hash; defer fi.hash = hash;
indent := fi.indent; defer fi.indent -= 1;
@@ -1088,30 +1163,73 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
fi.indent += 1;
if hash do strings.write_byte(fi.buf, '\n');
field_count := -1;
for name, i in b.names {
// if len(name) > 0 && name[0] == '_' do continue;
field_count += 1;
if !hash && field_count > 0 do strings.write_string(fi.buf, ", ");
if hash do for in 0..<fi.indent do strings.write_byte(fi.buf, '\t');
strings.write_string(fi.buf, name);
strings.write_string(fi.buf, " = ");
if t := b.types[i]; types.is_any(t) {
strings.write_string(fi.buf, "any{}");
} else {
data := rawptr(uintptr(v.data) + b.offsets[i]);
fmt_arg(fi, any{data, t.id}, 'v');
}
if hash do strings.write_string(fi.buf, ",\n");
defer {
if hash do for in 0..<indent do strings.write_byte(fi.buf, '\t');
strings.write_byte(fi.buf, is_soa ? ']' : '}');
}
if hash do for in 0..<indent do strings.write_byte(fi.buf, '\t');
strings.write_byte(fi.buf, '}');
if is_soa {
fi.indent += 1;
defer fi.indent -= 1;
base_type_name: string;
if v, ok := b.soa_base_type.variant.(runtime.Type_Info_Named); ok {
base_type_name = v.name;
}
for index in 0..<uintptr(b.soa_len) {
if !hash && index > 0 do strings.write_string(fi.buf, ", ");
field_count := -1;
if !hash && field_count > 0 do strings.write_string(fi.buf, ", ");
strings.write_string(fi.buf, base_type_name);
strings.write_byte(fi.buf, '{');
defer strings.write_byte(fi.buf, '}');
for name, i in b.names {
field_count += 1;
if !hash && field_count > 0 do strings.write_string(fi.buf, ", ");
if hash do for in 0..<fi.indent do strings.write_byte(fi.buf, '\t');
strings.write_string(fi.buf, name);
strings.write_string(fi.buf, " = ");
t := b.types[i].variant.(runtime.Type_Info_Array).elem;
t_size := uintptr(t.size);
if reflect.is_any(t) {
strings.write_string(fi.buf, "any{}");
} else {
data := rawptr(uintptr(v.data) + b.offsets[i] + index*t_size);
fmt_arg(fi, any{data, t.id}, 'v');
}
if hash do strings.write_string(fi.buf, ",\n");
}
}
} else {
field_count := -1;
for name, i in b.names {
field_count += 1;
if !hash && field_count > 0 do strings.write_string(fi.buf, ", ");
if hash do for in 0..<fi.indent do strings.write_byte(fi.buf, '\t');
strings.write_string(fi.buf, name);
strings.write_string(fi.buf, " = ");
if t := b.types[i]; reflect.is_any(t) {
strings.write_string(fi.buf, "any{}");
} else {
data := rawptr(uintptr(v.data) + b.offsets[i]);
fmt_arg(fi, any{data, t.id}, 'v');
}
if hash do strings.write_string(fi.buf, ",\n");
}
}
case runtime.Type_Info_Bit_Set:
fmt_bit_set(fi, v);
@@ -1128,18 +1246,19 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case runtime.Type_Info_Rune: fmt_arg(fi, v, verb);
case runtime.Type_Info_Float: fmt_arg(fi, v, verb);
case runtime.Type_Info_Complex: fmt_arg(fi, v, verb);
case runtime.Type_Info_Quaternion: fmt_arg(fi, v, verb);
case runtime.Type_Info_String: fmt_arg(fi, v, verb);
case runtime.Type_Info_Pointer:
if v.id == typeid_of(^runtime.Type_Info) {
write_type(fi.buf, (^^runtime.Type_Info)(v.data)^);
reflect.write_type(fi.buf, (^^runtime.Type_Info)(v.data)^);
} else {
ptr := (^rawptr)(v.data)^;
if verb != 'p' && info.elem != nil {
a := any{ptr, info.elem.id};
elem := runtime.type_info_base(info.elem);
if elem != nil do switch e in elem.variant {
if elem != nil do #partial switch e in elem.variant {
case runtime.Type_Info_Array,
runtime.Type_Info_Slice,
runtime.Type_Info_Dynamic_Array,
@@ -1184,6 +1303,25 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb);
}
case runtime.Type_Info_Enumerated_Array:
strings.write_byte(fi.buf, '[');
defer strings.write_byte(fi.buf, ']');
for i in 0..<info.count {
if i > 0 do strings.write_string(fi.buf, ", ");
idx, ok := stored_enum_value_to_string(info.index, info.min_value, i);
if ok {
strings.write_byte(fi.buf, '.');
strings.write_string(fi.buf, idx);
} else {
strings.write_i64(fi.buf, enum_value_to_i64(info.min_value)+i64(i));
}
strings.write_string(fi.buf, " = ");
data := uintptr(v.data) + uintptr(i*info.elem_size);
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb);
}
case runtime.Type_Info_Dynamic_Array:
if verb == 'p' {
slice := cast(^mem.Raw_Dynamic_Array)v.data;
@@ -1255,7 +1393,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
data := uintptr(entries.data) + uintptr(i*entry_size);
header := cast(^runtime.Map_Entry_Header)data;
if types.is_string(info.key) {
if reflect.is_string(info.key) {
strings.write_string(fi.buf, header.key.str);
} else {
fi := Info{buf = fi.buf};
@@ -1275,8 +1413,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
return;
}
strings.write_byte(fi.buf, '{');
defer strings.write_byte(fi.buf, '}');
is_soa := info.soa_kind != .None;
strings.write_byte(fi.buf, is_soa ? '[' : '{');
defer strings.write_byte(fi.buf, is_soa ? ']' : '}');
fi.indent += 1; defer fi.indent -= 1;
hash := fi.hash; defer fi.hash = hash;
@@ -1285,27 +1425,106 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
if hash do strings.write_byte(fi.buf, '\n');
for _, i in info.names {
if !hash && i > 0 do strings.write_string(fi.buf, ", ");
if hash {
for in 0..<fi.indent {
strings.write_byte(fi.buf, '\t');
if is_soa {
fi.indent += 1;
defer fi.indent -= 1;
base_type_name: string;
if v, ok := info.soa_base_type.variant.(runtime.Type_Info_Named); ok {
base_type_name = v.name;
}
actual_field_count := len(info.names);
n := uintptr(info.soa_len);
if info.soa_kind == .Slice {
actual_field_count = len(info.names)-1; // len
n = uintptr((^int)(uintptr(v.data) + info.offsets[actual_field_count])^);
} else if info.soa_kind == .Dynamic {
actual_field_count = len(info.names)-3; // len, cap, allocator
n = uintptr((^int)(uintptr(v.data) + info.offsets[actual_field_count])^);
}
for index in 0..<n {
if !hash && index > 0 do strings.write_string(fi.buf, ", ");
field_count := -1;
if !hash && field_count > 0 do strings.write_string(fi.buf, ", ");
strings.write_string(fi.buf, base_type_name);
strings.write_byte(fi.buf, '{');
defer strings.write_byte(fi.buf, '}');
for i in 0..<actual_field_count {
name := info.names[i];
field_count += 1;
if !hash && field_count > 0 do strings.write_string(fi.buf, ", ");
if hash do for in 0..<fi.indent do strings.write_byte(fi.buf, '\t');
strings.write_string(fi.buf, name);
strings.write_string(fi.buf, " = ");
if info.soa_kind == .Fixed {
t := info.types[i].variant.(runtime.Type_Info_Array).elem;
t_size := uintptr(t.size);
if reflect.is_any(t) {
strings.write_string(fi.buf, "any{}");
} else {
data := rawptr(uintptr(v.data) + info.offsets[i] + index*t_size);
fmt_arg(fi, any{data, t.id}, 'v');
}
} else {
t := info.types[i].variant.(runtime.Type_Info_Pointer).elem;
t_size := uintptr(t.size);
if reflect.is_any(t) {
strings.write_string(fi.buf, "any{}");
} else {
field_ptr := (^^byte)(uintptr(v.data) + info.offsets[i])^;
data := rawptr(uintptr(field_ptr) + index*t_size);
fmt_arg(fi, any{data, t.id}, 'v');
}
}
if hash do strings.write_string(fi.buf, ",\n");
}
}
} else {
field_count := -1;
for name, i in info.names {
field_count += 1;
strings.write_string(fi.buf, info.names[i]);
strings.write_string(fi.buf, " = ");
if !hash && field_count > 0 do strings.write_string(fi.buf, ", ");
if hash do for in 0..<fi.indent do strings.write_byte(fi.buf, '\t');
if t := info.types[i]; types.is_any(t) {
strings.write_string(fi.buf, "any{}");
} else {
data := uintptr(v.data) + info.offsets[i];
fmt_arg(fi, any{rawptr(data), t.id}, 'v');
strings.write_string(fi.buf, name);
strings.write_string(fi.buf, " = ");
if t := info.types[i]; reflect.is_any(t) {
strings.write_string(fi.buf, "any{}");
} else {
data := rawptr(uintptr(v.data) + info.offsets[i]);
fmt_arg(fi, any{data, t.id}, 'v');
}
if hash do strings.write_string(fi.buf, ",\n");
}
if hash do strings.write_string(fi.buf, ",\n");
}
case runtime.Type_Info_Union:
if type_info.size == 0 {
strings.write_string(fi.buf, "nil");
return;
}
tag_ptr := uintptr(v.data) + info.tag_offset;
tag_any := any{rawptr(tag_ptr), info.tag_type.id};
@@ -1321,8 +1540,14 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case i64: tag = i64(i);
case: panic("Invalid union tag type");
}
assert(tag >= 0);
if v.data == nil || tag == 0 {
if v.data == nil {
strings.write_string(fi.buf, "nil");
} else if info.no_nil {
id := info.variants[tag].id;
fmt_arg(fi, any{v.data, id}, verb);
} else if tag == 0 {
strings.write_string(fi.buf, "nil");
} else {
id := info.variants[tag-1].id;
@@ -1337,14 +1562,14 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
if ptr == nil {
strings.write_string(fi.buf, "nil");
} else {
write_typeid(fi.buf, v.id);
reflect.write_typeid(fi.buf, v.id);
strings.write_string(fi.buf, " @ ");
fmt_pointer(fi, ptr, 'p');
}
case runtime.Type_Info_Type_Id:
id := (^typeid)(v.data)^;
write_typeid(fi.buf, id);
reflect.write_typeid(fi.buf, id);
case runtime.Type_Info_Bit_Field:
fmt_bit_field(fi, v);
@@ -1374,6 +1599,31 @@ fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) {
}
}
fmt_quaternion :: proc(fi: ^Info, q: quaternion256, bits: int, verb: rune) {
switch verb {
case 'f', 'F', 'v', 'h', 'H':
r, i, j, k := real(q), imag(q), jmag(q), kmag(q);
fmt_float(fi, r, bits/4, verb);
if !fi.plus && i >= 0 do strings.write_rune(fi.buf, '+');
fmt_float(fi, i, bits/4, verb);
strings.write_rune(fi.buf, 'i');
if !fi.plus && j >= 0 do strings.write_rune(fi.buf, '+');
fmt_float(fi, j, bits/4, verb);
strings.write_rune(fi.buf, 'j');
if !fi.plus && k >= 0 do strings.write_rune(fi.buf, '+');
fmt_float(fi, k, bits/4, verb);
strings.write_rune(fi.buf, 'k');
case:
fmt_bad_verb(fi, verb);
return;
}
}
fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
if arg == nil {
strings.write_string(fi.buf, "<nil>");
@@ -1386,7 +1636,7 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
switch a in arg {
case ^runtime.Type_Info: ti = a;
}
write_type(fi.buf, ti);
reflect.write_type(fi.buf, ti);
return;
}
@@ -1422,6 +1672,9 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
case complex64: fmt_complex(fi, complex128(a), 64, verb);
case complex128: fmt_complex(fi, a, 128, verb);
case quaternion128: fmt_quaternion(fi, quaternion256(a), 128, verb);
case quaternion256: fmt_quaternion(fi, a, 256, verb);
case i8: fmt_int(fi, u64(a), true, 8, verb);
case u8: fmt_int(fi, u64(a), false, 8, verb);
case i16: fmt_int(fi, u64(a), true, 16, verb);
@@ -1437,7 +1690,7 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
case string: fmt_string(fi, a, verb);
case cstring: fmt_cstring(fi, a, verb);
case typeid: write_typeid(fi.buf, a);
case typeid: reflect.write_typeid(fi.buf, a);
case i16le: fmt_int(fi, u64(a), true, 16, verb);
case u16le: fmt_int(fi, u64(a), false, 16, verb);
@@ -1470,212 +1723,3 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
write_typeid :: proc(buf: ^strings.Builder, id: typeid) {
write_type(buf, type_info_of(id));
}
write_type :: proc(buf: ^strings.Builder, ti: ^runtime.Type_Info) {
using strings;
if ti == nil {
write_string(buf, "nil");
return;
}
switch info in ti.variant {
case runtime.Type_Info_Named:
write_string(buf, info.name);
case runtime.Type_Info_Integer:
switch ti.id {
case int: write_string(buf, "int");
case uint: write_string(buf, "uint");
case uintptr: write_string(buf, "uintptr");
case:
write_byte(buf, info.signed ? 'i' : 'u');
write_i64(buf, i64(8*ti.size), 10);
switch info.endianness {
case runtime.Type_Info_Endianness.Little:
write_string(buf, "le");
case runtime.Type_Info_Endianness.Big:
write_string(buf, "be");
}
}
case runtime.Type_Info_Rune:
write_string(buf, "rune");
case runtime.Type_Info_Float:
write_byte(buf, 'f');
write_i64(buf, i64(8*ti.size), 10);
case runtime.Type_Info_Complex:
write_string(buf, "complex");
write_i64(buf, i64(8*ti.size), 10);
case runtime.Type_Info_String:
if info.is_cstring {
write_string(buf, "cstring");
} else {
write_string(buf, "string");
}
case runtime.Type_Info_Boolean:
switch ti.id {
case bool: write_string(buf, "bool");
case:
write_byte(buf, 'b');
write_i64(buf, i64(8*ti.size), 10);
}
case runtime.Type_Info_Any:
write_string(buf, "any");
case runtime.Type_Info_Type_Id:
write_string(buf, "typeid");
case runtime.Type_Info_Pointer:
if info.elem == nil {
write_string(buf, "rawptr");
} else {
write_string(buf, "^");
write_type(buf, info.elem);
}
case runtime.Type_Info_Procedure:
write_string(buf, "proc");
if info.params == nil {
write_string(buf, "()");
} else {
t := info.params.variant.(runtime.Type_Info_Tuple);
write_string(buf, "(");
for t, i in t.types {
if i > 0 do write_string(buf, ", ");
write_type(buf, t);
}
write_string(buf, ")");
}
if info.results != nil {
write_string(buf, " -> ");
write_type(buf, info.results);
}
case runtime.Type_Info_Tuple:
count := len(info.names);
if count != 1 do write_string(buf, "(");
for name, i in info.names {
if i > 0 do write_string(buf, ", ");
t := info.types[i];
if len(name) > 0 {
write_string(buf, name);
write_string(buf, ": ");
}
write_type(buf, t);
}
if count != 1 do write_string(buf, ")");
case runtime.Type_Info_Array:
write_string(buf, "[");
write_i64(buf, i64(info.count), 10);
write_string(buf, "]");
write_type(buf, info.elem);
case runtime.Type_Info_Dynamic_Array:
write_string(buf, "[dynamic]");
write_type(buf, info.elem);
case runtime.Type_Info_Slice:
write_string(buf, "[]");
write_type(buf, info.elem);
case runtime.Type_Info_Map:
write_string(buf, "map[");
write_type(buf, info.key);
write_byte(buf, ']');
write_type(buf, info.value);
case runtime.Type_Info_Struct:
write_string(buf, "struct ");
if info.is_packed do write_string(buf, "#packed ");
if info.is_raw_union do write_string(buf, "#raw_union ");
if info.custom_align {
write_string(buf, "#align ");
write_i64(buf, i64(ti.align), 10);
write_byte(buf, ' ');
}
write_byte(buf, '{');
for name, i in info.names {
if i > 0 do write_string(buf, ", ");
write_string(buf, name);
write_string(buf, ": ");
write_type(buf, info.types[i]);
}
write_byte(buf, '}');
case runtime.Type_Info_Union:
write_string(buf, "union ");
if info.custom_align {
write_string(buf, "#align ");
write_i64(buf, i64(ti.align), 10);
write_byte(buf, ' ');
}
write_byte(buf, '{');
for variant, i in info.variants {
if i > 0 do write_string(buf, ", ");
write_type(buf, variant);
}
write_byte(buf, '}');
case runtime.Type_Info_Enum:
write_string(buf, "enum ");
write_type(buf, info.base);
write_string(buf, " {");
for name, i in info.names {
if i > 0 do write_string(buf, ", ");
write_string(buf, name);
}
write_byte(buf, '}');
case runtime.Type_Info_Bit_Field:
write_string(buf, "bit_field ");
if ti.align != 1 {
write_string(buf, "#align ");
write_i64(buf, i64(ti.align), 10);
write_byte(buf, ' ');
}
write_string(buf, " {");
for name, i in info.names {
if i > 0 do write_string(buf, ", ");
write_string(buf, name);
write_string(buf, ": ");
write_i64(buf, i64(info.bits[i]), 10);
}
write_byte(buf, '}');
case runtime.Type_Info_Bit_Set:
write_string(buf, "bit_set[");
switch {
case types.is_enum(info.elem):
write_type(buf, info.elem);
case types.is_rune(info.elem):
write_encoded_rune(buf, rune(info.lower));
write_string(buf, "..");
write_encoded_rune(buf, rune(info.upper));
case:
write_i64(buf, info.lower, 10);
write_string(buf, "..");
write_i64(buf, info.upper, 10);
}
if info.underlying != nil {
write_string(buf, "; ");
write_type(buf, info.underlying);
}
write_byte(buf, ']');
case runtime.Type_Info_Opaque:
write_string(buf, "opaque ");
write_type(buf, info.elem);
case runtime.Type_Info_Simd_Vector:
if info.is_x86_mmx {
write_string(buf, "intrinsics.x86_mmx");
} else {
write_string(buf, "intrinsics.vector(");
write_i64(buf, i64(info.count));
write_string(buf, ", ");
write_type(buf, info.elem);
write_byte(buf, ')');
}
}
}
+132
View File
@@ -0,0 +1,132 @@
// This is purely for documentation
package intrinsics
x86_mmx :: x86_mmx; // Specialized SIMD Vector type
simd_vector :: proc($N: int, $T: typeid) -> type/#simd[N]T
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_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_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) ---
atomic_cxchg_rel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchg_acqrel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchg_relaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchg_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchg_failacq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchg_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchg_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchgweak :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchgweak_acq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchgweak_rel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchgweak_acqrel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchgweak_relaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchgweak_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchgweak_failacq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchgweak_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchgweak_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
// Constant type tests
type_base_type :: proc($T: typeid) -> type ---
type_core_type :: proc($T: typeid) -> type ---
type_elem_type :: proc($T: typeid) -> type ---
type_is_boolean :: proc($T: typeid) -> bool ---
type_is_integer :: proc($T: typeid) -> bool ---
type_is_rune :: proc($T: typeid) -> bool ---
type_is_float :: proc($T: typeid) -> bool ---
type_is_complex :: proc($T: typeid) -> bool ---
type_is_quaternion :: proc($T: typeid) -> bool ---
type_is_string :: proc($T: typeid) -> bool ---
type_is_typeid :: proc($T: typeid) -> bool ---
type_is_any :: proc($T: typeid) -> bool ---
type_is_endian_little :: proc($T: typeid) -> bool ---
type_is_endian_big :: proc($T: typeid) -> bool ---
type_is_unsigned :: proc($T: typeid) -> bool ---
type_is_numeric :: proc($T: typeid) -> bool ---
type_is_ordered :: proc($T: typeid) -> bool ---
type_is_ordered_numeric :: proc($T: typeid) -> bool ---
type_is_indexable :: proc($T: typeid) -> bool ---
type_is_sliceable :: proc($T: typeid) -> bool ---
type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp
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 ---
type_is_dynamic_array :: proc($T: typeid) -> bool ---
type_is_map :: proc($T: typeid) -> bool ---
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 ---
type_is_specialization_of :: proc($T, $S: typeid) -> bool ---
type_has_nil :: proc($T: typeid) -> 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) ---
+17 -18
View File
@@ -14,18 +14,18 @@ Level_Headers := []string{
};
Default_Console_Logger_Opts :: Options{
Option.Level,
Option.Terminal_Color,
Option.Short_File_Path,
Option.Line,
Option.Procedure,
.Level,
.Terminal_Color,
.Short_File_Path,
.Line,
.Procedure,
} | Full_Timestamp_Opts;
Default_File_Logger_Opts :: Options{
Option.Level,
Option.Short_File_Path,
Option.Line,
Option.Procedure,
.Level,
.Short_File_Path,
.Line,
.Procedure,
} | Full_Timestamp_Opts;
@@ -109,10 +109,10 @@ do_level_header :: proc(opts : Options, level : Level, str : ^strings.Builder) {
case Level.Error, Level.Fatal : col = RED;
}
if Option.Level in opts {
if Option.Terminal_Color in opts do fmt.sbprint(str, col);
if .Level in opts {
if .Terminal_Color in opts do fmt.sbprint(str, col);
fmt.sbprint(str, Level_Headers[level]);
if Option.Terminal_Color in opts do fmt.sbprint(str, RESET);
if .Terminal_Color in opts do fmt.sbprint(str, RESET);
}
}
@@ -120,22 +120,21 @@ do_location_header :: proc(opts : Options, buf : ^strings.Builder, location := #
if Location_Header_Opts & opts != nil do fmt.sbprint(buf, "["); else do return;
file := location.file_path;
if Option.Short_File_Path in opts {
when os.OS == "windows" do delimiter := '\\'; else do delimiter := '/';
if .Short_File_Path in opts {
last := 0;
for r, i in location.file_path do if r == delimiter do last = i+1;
for r, i in location.file_path do if r == '/' do last = i+1;
file = location.file_path[last:];
}
if Location_File_Opts & opts != nil do fmt.sbprint(buf, file);
if Option.Procedure in opts {
if .Procedure in opts {
if Location_File_Opts & opts != nil do fmt.sbprint(buf, ".");
fmt.sbprintf(buf, "%s()", location.procedure);
}
if Option.Line in opts {
if Location_File_Opts & opts != nil || Option.Procedure in opts do fmt.sbprint(buf, ":");
if .Line in opts {
if Location_File_Opts & opts != nil || .Procedure in opts do fmt.sbprint(buf, ":");
fmt.sbprint(buf, location.line);
}
+58 -32
View File
@@ -1,7 +1,13 @@
package log
import "core:runtime"
import "core:fmt"
// 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.
Level :: runtime.Logger_Level;
/*
Level :: enum {
Debug,
Info,
@@ -9,63 +15,82 @@ Level :: enum {
Error,
Fatal,
}
*/
Option :: runtime.Logger_Option;
/*
Option :: enum {
Level,
Date,
Time,
Short_File_Path,
Long_File_Path,
Line,
Procedure,
Terminal_Color
Level,
Date,
Time,
Short_File_Path,
Long_File_Path,
Line,
Procedure,
Terminal_Color
}
*/
Options :: runtime.Logger_Options;
/*
Options :: bit_set[Option];
*/
Full_Timestamp_Opts :: Options{
Option.Date,
Option.Time
.Date,
.Time
};
Location_Header_Opts :: Options{
Option.Short_File_Path,
Option.Long_File_Path,
Option.Line,
Option.Procedure,
.Short_File_Path,
.Long_File_Path,
.Line,
.Procedure,
};
Location_File_Opts :: Options{
Option.Short_File_Path,
Option.Long_File_Path
.Short_File_Path,
.Long_File_Path
};
Logger_Proc :: #type proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location);
Logger_Proc :: runtime.Logger_Proc;
/*
Logger_Proc :: #type proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location);
*/
Logger :: runtime.Logger;
/*
Logger :: struct {
procedure: Logger_Proc,
data: rawptr,
options: Options,
options: Options,
}
*/
Multi_Logger_Data :: struct {
loggers : []Logger,
loggers : []Logger,
}
create_multi_logger :: proc(logs: ..Logger) -> Logger {
data := new(Multi_Logger_Data);
data.loggers = make([]Logger, len(logs));
copy(data.loggers, logs);
return Logger{multi_logger_proc, data, nil};
data := new(Multi_Logger_Data);
data.loggers = make([]Logger, len(logs));
copy(data.loggers, logs);
return Logger{multi_logger_proc, data, nil};
}
destroy_multi_logger ::proc(log : ^Logger) {
free(log.data);
log^ = nil_logger();
destroy_multi_logger :: proc(log : ^Logger) {
free(log.data);
log^ = nil_logger();
}
multi_logger_proc :: proc(logger_data: rawptr, level: Level, text: string,
options: Options, location := #caller_location) {
data := cast(^Multi_Logger_Data)logger_data;
if data.loggers == nil || len(data.loggers) == 0 do return;
for log in data.loggers do log.procedure(log.data, level, text, log.options, location);
data := cast(^Multi_Logger_Data)logger_data;
if data.loggers == nil || len(data.loggers) == 0 {
return;
}
for log in data.loggers {
log.procedure(log.data, level, text, log.options, location);
}
}
nil_logger_proc :: proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
@@ -76,6 +101,7 @@ nil_logger :: proc() -> Logger {
return Logger{nil_logger_proc, nil, nil};
}
// TODO(bill): Should these be redesigned so that they are do not rely upon `package fmt`?
debug :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Debug, fmt_str=fmt_str, args=args, location=location);
info :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Info, fmt_str=fmt_str, args=args, location=location);
warn :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Warning, fmt_str=fmt_str, args=args, location=location);
@@ -83,7 +109,7 @@ error :: proc(fmt_str : string, args : ..any, location := #caller_location) do l
fatal :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Fatal, fmt_str=fmt_str, args=args, location=location);
logf :: proc(level : Level, fmt_str : string, args : ..any, location := #caller_location) {
logger := context.logger;
str := len(args) > 0 ? fmt.tprintf(fmt_str, ..args) : fmt.tprint(fmt_str); //NOTE(Hoej): While tprint isn't thread-safe, no logging is.
logger.procedure(logger.data, level, str, logger.options, location);
logger := context.logger;
str := len(args) > 0 ? fmt.tprintf(fmt_str, ..args) : fmt.tprint(fmt_str); //NOTE(Hoej): While tprint isn't thread-safe, no logging is.
logger.procedure(logger.data, level, str, logger.options, location);
}
+1 -1
View File
@@ -1,4 +1,4 @@
package bits
package math_bits
import "core:os"
+401
View File
@@ -0,0 +1,401 @@
package linalg
import "core:math"
import "intrinsics"
// Generic
@private IS_NUMERIC :: intrinsics.type_is_numeric;
@private IS_QUATERNION :: intrinsics.type_is_quaternion;
@private IS_ARRAY :: intrinsics.type_is_array;
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;
}
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;
}
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};
quaternion_inverse :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
return conj(q) * quaternion(1.0/dot(q, q), 0, 0, 0);
}
vector_cross2 :: proc(a, b: $T/[2]$E) -> E where IS_NUMERIC(E) {
return a[0]*b[1] - b[0]*a[1];
}
vector_cross3 :: proc(a, b: $T/[3]$E) -> (c: T) where IS_NUMERIC(E) {
c[0] = a[1]*b[2] - b[1]*a[2];
c[1] = a[2]*b[0] - b[2]*a[0];
c[2] = a[0]*b[1] - b[0]*a[1];
return;
}
vector_cross :: proc{vector_cross2, vector_cross3};
cross :: vector_cross;
vector_normalize :: proc(v: $T/[$N]$E) -> T where IS_NUMERIC(E) {
return v / length(v);
}
quaternion_normalize :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
return q/abs(q);
}
normalize :: proc{vector_normalize, quaternion_normalize};
vector_normalize0 :: proc(v: $T/[$N]$E) -> T where IS_NUMERIC(E) {
m := length(v);
return m == 0 ? 0 : v/m;
}
quaternion_normalize0 :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
m := abs(q);
return m == 0 ? 0 : q/m;
}
normalize0 :: proc{vector_normalize0, quaternion_normalize0};
vector_length :: proc(v: $T/[$N]$E) -> E where IS_NUMERIC(E) {
return math.sqrt(dot(v, v));
}
vector_length2 :: proc(v: $T/[$N]$E) -> E where IS_NUMERIC(E) {
return dot(v, v);
}
quaternion_length :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
return abs(q);
}
quaternion_length2 :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
return dot(q, q);
}
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;
}
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] = x[i] < edge[i] ? 0 : 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);
return m;
}
trace :: proc(m: $T/[$N][N]$E) -> (tr: E) {
for i in 0..<N {
tr += m[i][i];
}
return;
}
transpose :: proc(a: $T/[$N][$M]$E) -> (m: T) {
for j in 0..<M {
for i in 0..<N {
m[j][i] = a[i][j];
}
}
return;
}
matrix_mul :: proc(a, b: $M/[$N][N]$E) -> (c: M)
where !IS_ARRAY(E),
IS_NUMERIC(E) {
for i in 0..<N {
for k in 0..<N {
for j in 0..<N {
c[k][i] += a[j][i] * b[k][j];
}
}
}
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),
I != K {
for k in 0..<K {
for j in 0..<J {
for i in 0..<I {
c[k][i] += a[j][i] * b[k][j];
}
}
}
return;
}
matrix_mul_vector :: proc(a: $A/[$I][$J]$E, b: $B/[I]E) -> (c: B)
where !IS_ARRAY(E),
IS_NUMERIC(E) {
for i in 0..<I {
for j in 0..<J {
c[i] += a[i][j] * b[i];
}
}
return;
}
quaternion128_mul_vector3 :: proc(q: $Q/quaternion128, v: $V/[3]$F/f32) -> V {
Raw_Quaternion :: struct {xyz: [3]f32, r: f32};
q := transmute(Raw_Quaternion)q;
v := transmute([3]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};
q := transmute(Raw_Quaternion)q;
v := transmute([3]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};
mul :: proc{
matrix_mul,
matrix_mul_differ,
matrix_mul_vector,
quaternion128_mul_vector3,
quaternion256_mul_vector3,
};
vector_to_ptr :: proc(v: ^$V/[$N]$E) -> ^E where IS_NUMERIC(E) {
return &v[0];
}
matrix_to_ptr :: proc(m: ^$A/[$I][$J]$E) -> ^E where IS_NUMERIC(E) {
return &m[0][0];
}
+786
View File
@@ -0,0 +1,786 @@
package linalg
import "core:math"
import "intrinsics"
// Specific
Float :: f32;
FLOAT_EPSILON :: size_of(Float) == 4 ? 1e-7 : 1e-15;
Vector2 :: distinct [2]Float;
Vector3 :: distinct [3]Float;
Vector4 :: distinct [4]Float;
Matrix1x1 :: distinct [1][1]Float;
Matrix1x2 :: distinct [1][2]Float;
Matrix1x3 :: distinct [1][3]Float;
Matrix1x4 :: distinct [1][4]Float;
Matrix2x1 :: distinct [2][1]Float;
Matrix2x2 :: distinct [2][2]Float;
Matrix2x3 :: distinct [2][3]Float;
Matrix2x4 :: distinct [2][4]Float;
Matrix3x1 :: distinct [3][1]Float;
Matrix3x2 :: distinct [3][2]Float;
Matrix3x3 :: distinct [3][3]Float;
Matrix3x4 :: distinct [3][4]Float;
Matrix4x1 :: distinct [4][1]Float;
Matrix4x2 :: distinct [4][2]Float;
Matrix4x3 :: distinct [4][3]Float;
Matrix4x4 :: distinct [4][4]Float;
Matrix1 :: Matrix1x1;
Matrix2 :: Matrix2x2;
Matrix3 :: Matrix3x3;
Matrix4 :: Matrix4x4;
Quaternion :: distinct (size_of(Float) == size_of(f32) ? quaternion128 : quaternion256);
MATRIX1_IDENTITY :: Matrix1{{1}};
MATRIX2_IDENTITY :: Matrix2{{1, 0}, {0, 1}};
MATRIX3_IDENTITY :: Matrix3{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
MATRIX4_IDENTITY :: Matrix4{{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}};
QUATERNION_IDENTITY :: Quaternion(1);
VECTOR3_X_AXIS :: Vector3{1, 0, 0};
VECTOR3_Y_AXIS :: Vector3{0, 1, 0};
VECTOR3_Z_AXIS :: Vector3{0, 0, 1};
radians :: proc(degrees: Float) -> Float {
return math.TAU * degrees / 360.0;
}
degrees :: proc(radians: Float) -> Float {
return 360.0 * radians / math.TAU;
}
vector2_orthogonal :: proc(v: Vector2) -> Vector2 {
return {-v.y, v.x};
}
vector3_orthogonal :: proc(v: Vector3) -> Vector3 {
x := abs(v.x);
y := abs(v.y);
z := abs(v.z);
other: Vector3 = x < y ? (x < z ? {1, 0, 0} : {0, 0, 1}) : (y < z ? {0, 1, 0} : {0, 0, 1});
return normalize(cross(v, other));
}
vector4_srgb_to_linear :: proc(col: Vector4) -> Vector4 {
r := math.pow(col.x, 2.2);
g := math.pow(col.y, 2.2);
b := math.pow(col.z, 2.2);
a := col.w;
return {r, g, b, a};
}
vector4_linear_to_srgb :: proc(col: Vector4) -> Vector4 {
a :: 2.51;
b :: 0.03;
c :: 2.43;
d :: 0.59;
e :: 0.14;
x := col.x;
y := col.y;
z := col.z;
x = (x * (a * x + b)) / (x * (c * x + d) + e);
y = (y * (a * y + b)) / (y * (c * y + d) + e);
z = (z * (a * z + b)) / (z * (c * z + d) + e);
x = math.pow(clamp(x, 0, 1), 1.0 / 2.2);
y = math.pow(clamp(y, 0, 1), 1.0 / 2.2);
z = math.pow(clamp(z, 0, 1), 1.0 / 2.2);
return {x, y, z, col.w};
}
vector4_hsl_to_rgb :: proc(h, s, l: Float, a: Float = 1) -> Vector4 {
hue_to_rgb :: proc(p, q, t0: Float) -> Float {
t := math.mod(t0, 1.0);
switch {
case t < 1.0/6.0: return p + (q - p) * 6.0 * t;
case t < 1.0/2.0: return q;
case t < 2.0/3.0: return p + (q - p) * 6.0 * (2.0/3.0 - t);
}
return p;
}
r, g, b: Float;
if s == 0 {
r = l;
g = l;
b = l;
} else {
q := l < 0.5 ? l * (1+s) : l+s - l*s;
p := 2*l - q;
r = hue_to_rgb(p, q, h + 1.0/3.0);
g = hue_to_rgb(p, q, h);
b = hue_to_rgb(p, q, h - 1.0/3.0);
}
return {r, g, b, a};
}
vector4_rgb_to_hsl :: proc(col: Vector4) -> Vector4 {
r := col.x;
g := col.y;
b := col.z;
a := col.w;
v_min := min(r, g, b);
v_max := max(r, g, b);
h, s, l: Float;
h = 0.0;
s = 0.0;
l = (v_min + v_max) * 0.5;
if v_max != v_min {
d: = v_max - v_min;
s = l > 0.5 ? d / (2.0 - v_max - v_min) : d / (v_max + v_min);
switch {
case v_max == r:
h = (g - b) / d + (g < b ? 6.0 : 0.0);
case v_max == g:
h = (b - r) / d + 2.0;
case v_max == b:
h = (r - g) / d + 4.0;
}
h *= 1.0/6.0;
}
return {h, s, l, a};
}
quaternion_angle_axis :: proc(angle_radians: Float, axis: Vector3) -> Quaternion {
t := angle_radians*0.5;
w := math.cos(t);
v := normalize(axis) * math.sin(t);
return quaternion(w, v.x, v.y, v.z);
}
quaternion_from_euler_angles :: proc(roll, pitch, yaw: Float) -> Quaternion {
x, y, z := roll, pitch, yaw;
a, b, c := x, y, z;
ca, sa := math.cos(a*0.5), math.sin(a*0.5);
cb, sb := math.cos(b*0.5), math.sin(b*0.5);
cc, sc := math.cos(c*0.5), math.sin(c*0.5);
q: Quaternion;
q.x = sa*cb*cc - ca*sb*sc;
q.y = ca*sb*cc + sa*cb*sc;
q.z = ca*cb*sc - sa*sb*cc;
q.w = ca*cb*cc + sa*sb*sc;
return q;
}
euler_angles_from_quaternion :: proc(q: Quaternion) -> (roll, pitch, yaw: Float) {
// roll, x-axis rotation
sinr_cosp: Float = 2 * (q.w * q.x + q.y * q.z);
cosr_cosp: Float = 1 - 2 * (q.x * q.x + q.y * q.y);
roll = math.atan2(sinr_cosp, cosr_cosp);
// pitch, y-axis rotation
sinp: Float = 2 * (q.w * q.y - q.z * q.x);
if abs(sinp) >= 1 {
pitch = math.copy_sign(math.TAU * 0.25, sinp);
} else {
pitch = math.asin(sinp);
}
// yaw, z-axis rotation
siny_cosp: Float = 2 * (q.w * q.z + q.x * q.y);
cosy_cosp: Float = 1 - 2 * (q.y * q.y + q.z * q.z);
yaw = math.atan2(siny_cosp, cosy_cosp);
return;
}
quaternion_from_forward_and_up :: proc(forward, up: Vector3) -> Quaternion {
f := normalize(forward);
s := normalize(cross(f, up));
u := cross(s, f);
m := Matrix3{
{+s.x, +u.x, -f.x},
{+s.y, +u.y, -f.y},
{+s.z, +u.z, -f.z},
};
tr := trace(m);
q: Quaternion;
switch {
case tr > 0:
S := 2 * math.sqrt(1 + tr);
q.w = 0.25 * S;
q.x = (m[2][1] - m[1][2]) / S;
q.y = (m[0][2] - m[2][0]) / S;
q.z = (m[1][0] - m[0][1]) / S;
case (m[0][0] > m[1][1]) && (m[0][0] > m[2][2]):
S := 2 * math.sqrt(1 + m[0][0] - m[1][1] - m[2][2]);
q.w = (m[2][1] - m[1][2]) / S;
q.x = 0.25 * S;
q.y = (m[0][1] + m[1][0]) / S;
q.z = (m[0][2] + m[2][0]) / S;
case m[1][1] > m[2][2]:
S := 2 * math.sqrt(1 + m[1][1] - m[0][0] - m[2][2]);
q.w = (m[0][2] - m[2][0]) / S;
q.x = (m[0][1] + m[1][0]) / S;
q.y = 0.25 * S;
q.z = (m[1][2] + m[2][1]) / S;
case:
S := 2 * math.sqrt(1 + m[2][2] - m[0][0] - m[1][1]);
q.w = (m[1][0] - m[0][1]) / S;
q.x = (m[0][2] - m[2][0]) / S;
q.y = (m[1][2] + m[2][1]) / S;
q.z = 0.25 * S;
}
return normalize(q);
}
quaternion_look_at :: proc(eye, centre: Vector3, up: Vector3) -> Quaternion {
return quaternion_from_forward_and_up(centre-eye, up);
}
quaternion_nlerp :: proc(a, b: Quaternion, t: Float) -> Quaternion {
c := a + (b-a)*quaternion(t, 0, 0, 0);
return normalize(c);
}
quaternion_slerp :: proc(x, y: Quaternion, t: Float) -> Quaternion {
a, b := x, y;
cos_angle := dot(a, b);
if cos_angle < 0 {
b = -b;
cos_angle = -cos_angle;
}
if cos_angle > 1 - FLOAT_EPSILON {
return a + (b-a)*quaternion(t, 0, 0, 0);
}
angle := math.acos(cos_angle);
sin_angle := math.sin(angle);
factor_a, factor_b: Quaternion;
factor_a = quaternion(math.sin((1-t) * angle) / sin_angle, 0, 0, 0);
factor_b = quaternion(math.sin(t * angle) / sin_angle, 0, 0, 0);
return factor_a * a + factor_b * b;
}
quaternion_from_matrix4 :: proc(m: Matrix4) -> Quaternion {
four_x_squared_minus_1, four_y_squared_minus_1,
four_z_squared_minus_1, four_w_squared_minus_1,
four_biggest_squared_minus_1: Float;
/* xyzw */
/* 0123 */
biggest_index := 3;
biggest_value, mult: Float;
four_x_squared_minus_1 = m[0][0] - m[1][1] - m[2][2];
four_y_squared_minus_1 = m[1][1] - m[0][0] - m[2][2];
four_z_squared_minus_1 = m[2][2] - m[0][0] - m[1][1];
four_w_squared_minus_1 = m[0][0] + m[1][1] + m[2][2];
four_biggest_squared_minus_1 = four_w_squared_minus_1;
if four_x_squared_minus_1 > four_biggest_squared_minus_1 {
four_biggest_squared_minus_1 = four_x_squared_minus_1;
biggest_index = 0;
}
if four_y_squared_minus_1 > four_biggest_squared_minus_1 {
four_biggest_squared_minus_1 = four_y_squared_minus_1;
biggest_index = 1;
}
if four_z_squared_minus_1 > four_biggest_squared_minus_1 {
four_biggest_squared_minus_1 = four_z_squared_minus_1;
biggest_index = 2;
}
biggest_value = math.sqrt(four_biggest_squared_minus_1 + 1) * 0.5;
mult = 0.25 / biggest_value;
switch biggest_index {
case 0:
return quaternion(
biggest_value,
(m[0][1] + m[1][0]) * mult,
(m[2][0] + m[0][2]) * mult,
(m[1][2] - m[2][1]) * mult,
);
case 1:
return quaternion(
(m[0][1] + m[1][0]) * mult,
biggest_value,
(m[1][2] + m[2][1]) * mult,
(m[2][0] - m[0][2]) * mult,
);
case 2:
return quaternion(
(m[2][0] + m[0][2]) * mult,
(m[1][2] + m[2][1]) * mult,
biggest_value,
(m[0][1] - m[1][0]) * mult,
);
case 3:
return quaternion(
(m[1][2] - m[2][1]) * mult,
(m[2][0] - m[0][2]) * mult,
(m[0][1] - m[1][0]) * mult,
biggest_value,
);
}
return 0;
}
quaternion_from_matrix3 :: proc(m: Matrix3) -> Quaternion {
four_x_squared_minus_1, four_y_squared_minus_1,
four_z_squared_minus_1, four_w_squared_minus_1,
four_biggest_squared_minus_1: Float;
/* xyzw */
/* 0123 */
biggest_index := 3;
biggest_value, mult: Float;
four_x_squared_minus_1 = m[0][0] - m[1][1] - m[2][2];
four_y_squared_minus_1 = m[1][1] - m[0][0] - m[2][2];
four_z_squared_minus_1 = m[2][2] - m[0][0] - m[1][1];
four_w_squared_minus_1 = m[0][0] + m[1][1] + m[2][2];
four_biggest_squared_minus_1 = four_w_squared_minus_1;
if four_x_squared_minus_1 > four_biggest_squared_minus_1 {
four_biggest_squared_minus_1 = four_x_squared_minus_1;
biggest_index = 0;
}
if four_y_squared_minus_1 > four_biggest_squared_minus_1 {
four_biggest_squared_minus_1 = four_y_squared_minus_1;
biggest_index = 1;
}
if four_z_squared_minus_1 > four_biggest_squared_minus_1 {
four_biggest_squared_minus_1 = four_z_squared_minus_1;
biggest_index = 2;
}
biggest_value = math.sqrt(four_biggest_squared_minus_1 + 1) * 0.5;
mult = 0.25 / biggest_value;
switch biggest_index {
case 0:
return quaternion(
biggest_value,
(m[0][1] + m[1][0]) * mult,
(m[2][0] + m[0][2]) * mult,
(m[1][2] - m[2][1]) * mult,
);
case 1:
return quaternion(
(m[0][1] + m[1][0]) * mult,
biggest_value,
(m[1][2] + m[2][1]) * mult,
(m[2][0] - m[0][2]) * mult,
);
case 2:
return quaternion(
(m[2][0] + m[0][2]) * mult,
(m[1][2] + m[2][1]) * mult,
biggest_value,
(m[0][1] - m[1][0]) * mult,
);
case 3:
return quaternion(
(m[1][2] - m[2][1]) * mult,
(m[2][0] - m[0][2]) * mult,
(m[0][1] - m[1][0]) * mult,
biggest_value,
);
}
return 0;
}
quaternion_between_two_vector3 :: proc(from, to: Vector3) -> Quaternion {
x := normalize(from);
y := normalize(to);
cos_theta := dot(x, y);
if abs(cos_theta + 1) < 2*FLOAT_EPSILON {
v := vector3_orthogonal(x);
return quaternion(0, v.x, v.y, v.z);
}
v := cross(x, y);
w := cos_theta + 1;
return Quaternion(normalize(quaternion(w, v.x, v.y, v.z)));
}
matrix2_inverse_transpose :: proc(m: Matrix2) -> Matrix2 {
c: Matrix2;
d := m[0][0]*m[1][1] - m[1][0]*m[0][1];
id := 1.0/d;
c[0][0] = +m[1][1] * id;
c[0][1] = -m[0][1] * id;
c[1][0] = -m[1][0] * id;
c[1][1] = +m[0][0] * id;
return c;
}
matrix2_determinant :: proc(m: Matrix2) -> Float {
return m[0][0]*m[1][1] - m[1][0]*m[0][1];
}
matrix2_inverse :: proc(m: Matrix2) -> Matrix2 {
c: Matrix2;
d := m[0][0]*m[1][1] - m[1][0]*m[0][1];
id := 1.0/d;
c[0][0] = +m[1][1] * id;
c[1][0] = -m[0][1] * id;
c[0][1] = -m[1][0] * id;
c[1][1] = +m[0][0] * id;
return c;
}
matrix2_adjoint :: proc(m: Matrix2) -> Matrix2 {
c: Matrix2;
c[0][0] = +m[1][1];
c[0][1] = -m[1][0];
c[1][0] = -m[0][1];
c[1][1] = +m[0][0];
return c;
}
matrix3_from_quaternion :: proc(q: Quaternion) -> Matrix3 {
xx := q.x * q.x;
xy := q.x * q.y;
xz := q.x * q.z;
xw := q.x * q.w;
yy := q.y * q.y;
yz := q.y * q.z;
yw := q.y * q.w;
zz := q.z * q.z;
zw := q.z * q.w;
m: Matrix3;
m[0][0] = 1 - 2 * (yy + zz);
m[1][0] = 2 * (xy - zw);
m[2][0] = 2 * (xz + yw);
m[0][1] = 2 * (xy + zw);
m[1][1] = 1 - 2 * (xx + zz);
m[2][1] = 2 * (yz - xw);
m[0][2] = 2 * (xz - yw);
m[1][2] = 2 * (yz + xw);
m[2][2] = 1 - 2 * (xx + yy);
return m;
}
matrix3_inverse :: proc(m: Matrix3) -> Matrix3 {
return transpose(matrix3_inverse_transpose(m));
}
matrix3_determinant :: proc(m: Matrix3) -> Float {
a := +m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2]);
b := -m[1][0] * (m[0][1] * m[2][2] - m[2][1] * m[0][2]);
c := +m[2][0] * (m[0][1] * m[1][2] - m[1][1] * m[0][2]);
return a + b + c;
}
matrix3_adjoint :: proc(m: Matrix3) -> Matrix3 {
adjoint: Matrix3;
adjoint[0][0] = +(m[1][1] * m[2][2] - m[1][2] * m[2][1]);
adjoint[1][0] = -(m[0][1] * m[2][2] - m[0][2] * m[2][1]);
adjoint[2][0] = +(m[0][1] * m[1][2] - m[0][2] * m[1][1]);
adjoint[0][1] = -(m[1][0] * m[2][2] - m[1][2] * m[2][0]);
adjoint[1][1] = +(m[0][0] * m[2][2] - m[0][2] * m[2][0]);
adjoint[2][1] = -(m[0][0] * m[1][2] - m[0][2] * m[1][0]);
adjoint[0][2] = +(m[1][0] * m[2][1] - m[1][1] * m[2][0]);
adjoint[1][2] = -(m[0][0] * m[2][1] - m[0][1] * m[2][0]);
adjoint[2][2] = +(m[0][0] * m[1][1] - m[0][1] * m[1][0]);
return adjoint;
}
matrix3_inverse_transpose :: proc(m: Matrix3) -> Matrix3 {
inverse_transpose: Matrix3;
adjoint := matrix3_adjoint(m);
determinant := matrix3_determinant(m);
inv_determinant := 1.0 / determinant;
for i in 0..<3 {
for j in 0..<3 {
inverse_transpose[i][j] = adjoint[i][j] * inv_determinant;
}
}
return inverse_transpose;
}
matrix3_scale :: proc(s: Vector3) -> Matrix3 {
m: Matrix3;
m[0][0] = s[0];
m[1][1] = s[1];
m[2][2] = s[2];
return m;
}
matrix3_rotate :: proc(angle_radians: Float, v: Vector3) -> Matrix3 {
c := math.cos(angle_radians);
s := math.sin(angle_radians);
a := normalize(v);
t := a * (1-c);
rot: Matrix3 = ---;
rot[0][0] = c + t[0]*a[0];
rot[0][1] = 0 + t[0]*a[1] + s*a[2];
rot[0][2] = 0 + t[0]*a[2] - s*a[1];
rot[1][0] = 0 + t[1]*a[0] - s*a[2];
rot[1][1] = c + t[1]*a[1];
rot[1][2] = 0 + t[1]*a[2] + s*a[0];
rot[2][0] = 0 + t[2]*a[0] + s*a[1];
rot[2][1] = 0 + t[2]*a[1] - s*a[0];
rot[2][2] = c + t[2]*a[2];
return rot;
}
matrix3_look_at :: proc(eye, centre, up: Vector3) -> Matrix3 {
f := normalize(centre - eye);
s := normalize(cross(f, up));
u := cross(s, f);
return Matrix3{
{+s.x, +u.x, -f.x},
{+s.y, +u.y, -f.y},
{+s.z, +u.z, -f.z},
};
}
matrix4_from_quaternion :: proc(q: Quaternion) -> Matrix4 {
m := identity(Matrix4);
xx := q.x * q.x;
xy := q.x * q.y;
xz := q.x * q.z;
xw := q.x * q.w;
yy := q.y * q.y;
yz := q.y * q.z;
yw := q.y * q.w;
zz := q.z * q.z;
zw := q.z * q.w;
m[0][0] = 1 - 2 * (yy + zz);
m[1][0] = 2 * (xy - zw);
m[2][0] = 2 * (xz + yw);
m[0][1] = 2 * (xy + zw);
m[1][1] = 1 - 2 * (xx + zz);
m[2][1] = 2 * (yz - xw);
m[0][2] = 2 * (xz - yw);
m[1][2] = 2 * (yz + xw);
m[2][2] = 1 - 2 * (xx + yy);
return m;
}
matrix4_from_trs :: proc(t: Vector3, r: Quaternion, s: Vector3) -> Matrix4 {
translation := matrix4_translate(t);
rotation := matrix4_from_quaternion(r);
scale := matrix4_scale(s);
return mul(translation, mul(rotation, scale));
}
matrix4_inverse :: proc(m: Matrix4) -> Matrix4 {
return transpose(matrix4_inverse_transpose(m));
}
matrix4_minor :: proc(m: Matrix4, c, r: int) -> Float {
cut_down: Matrix3;
for i in 0..<3 {
col := i < c ? i : i+1;
for j in 0..<3 {
row := j < r ? j : j+1;
cut_down[i][j] = m[col][row];
}
}
return matrix3_determinant(cut_down);
}
matrix4_cofactor :: proc(m: Matrix4, c, r: int) -> Float {
sign, minor: Float;
sign = (c + r) % 2 == 0 ? 1 : -1;
minor = matrix4_minor(m, c, r);
return sign * minor;
}
matrix4_adjoint :: proc(m: Matrix4) -> Matrix4 {
adjoint: Matrix4;
for i in 0..<4 {
for j in 0..<4 {
adjoint[i][j] = matrix4_cofactor(m, i, j);
}
}
return adjoint;
}
matrix4_determinant :: proc(m: Matrix4) -> Float {
adjoint := matrix4_adjoint(m);
determinant: Float = 0;
for i in 0..<4 {
determinant += m[i][0] * adjoint[i][0];
}
return determinant;
}
matrix4_inverse_transpose :: proc(m: Matrix4) -> Matrix4 {
adjoint := matrix4_adjoint(m);
determinant: Float = 0;
for i in 0..<4 {
determinant += m[i][0] * adjoint[i][0];
}
inv_determinant := 1.0 / determinant;
inverse_transpose: Matrix4;
for i in 0..<4 {
for j in 0..<4 {
inverse_transpose[i][j] = adjoint[i][j] * inv_determinant;
}
}
return inverse_transpose;
}
matrix4_translate :: proc(v: Vector3) -> Matrix4 {
m := identity(Matrix4);
m[3][0] = v[0];
m[3][1] = v[1];
m[3][2] = v[2];
return m;
}
matrix4_rotate :: proc(angle_radians: Float, v: Vector3) -> Matrix4 {
c := math.cos(angle_radians);
s := math.sin(angle_radians);
a := normalize(v);
t := a * (1-c);
rot := identity(Matrix4);
rot[0][0] = c + t[0]*a[0];
rot[0][1] = 0 + t[0]*a[1] + s*a[2];
rot[0][2] = 0 + t[0]*a[2] - s*a[1];
rot[0][3] = 0;
rot[1][0] = 0 + t[1]*a[0] - s*a[2];
rot[1][1] = c + t[1]*a[1];
rot[1][2] = 0 + t[1]*a[2] + s*a[0];
rot[1][3] = 0;
rot[2][0] = 0 + t[2]*a[0] + s*a[1];
rot[2][1] = 0 + t[2]*a[1] - s*a[0];
rot[2][2] = c + t[2]*a[2];
rot[2][3] = 0;
return rot;
}
matrix4_scale :: proc(v: Vector3) -> Matrix4 {
m: Matrix4;
m[0][0] = v[0];
m[1][1] = v[1];
m[2][2] = v[2];
m[3][3] = 1;
return m;
}
matrix4_look_at :: proc(eye, centre, up: Vector3) -> Matrix4 {
f := normalize(centre - eye);
s := normalize(cross(f, up));
u := cross(s, f);
return Matrix4{
{+s.x, +u.x, -f.x, 0},
{+s.y, +u.y, -f.y, 0},
{+s.z, +u.z, -f.z, 0},
{-dot(s, eye), -dot(u, eye), +dot(f, eye), 1},
};
}
matrix4_perspective :: proc(fovy, aspect, near, far: Float, flip_z_axis := true) -> (m: Matrix4) {
tan_half_fovy := math.tan(0.5 * fovy);
m[0][0] = 1 / (aspect*tan_half_fovy);
m[1][1] = 1 / (tan_half_fovy);
m[2][2] = +(far + near) / (far - near);
m[2][3] = +1;
m[3][2] = -2*far*near / (far - near);
if flip_z_axis {
m[2] = -m[2];
}
return;
}
matrix_ortho3d :: proc(left, right, bottom, top, near, far: Float, flip_z_axis := true) -> (m: Matrix4) {
m[0][0] = +2 / (right - left);
m[1][1] = +2 / (top - bottom);
m[2][2] = +2 / (far - near);
m[3][0] = -(right + left) / (right - left);
m[3][1] = -(top + bottom) / (top - bottom);
m[3][2] = -(far + near) / (far- near);
m[3][3] = 1;
if flip_z_axis {
m[2] = -m[2];
}
return;
}
matrix4_infinite_perspective :: proc(fovy, aspect, near: Float, flip_z_axis := true) -> (m: Matrix4) {
tan_half_fovy := math.tan(0.5 * fovy);
m[0][0] = 1 / (aspect*tan_half_fovy);
m[1][1] = 1 / (tan_half_fovy);
m[2][2] = +1;
m[2][3] = +1;
m[3][2] = -2*near;
if flip_z_axis {
m[2] = -m[2];
}
return;
}
+579 -359
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -128,8 +128,8 @@ norm_float64 :: proc(r: ^Rand = global_rand_ptr) -> f64 {
if i == 0 {
for {
x = -math.log(float64(r)) * (1.0/ rn);
y := -math.log(float64(r));
x = -math.ln(float64(r)) * (1.0/ rn);
y := -math.ln(float64(r));
if y+y >= x*x {
break;
}
+24 -2
View File
@@ -47,8 +47,17 @@ uint64 :: proc(r: ^Rand = global_rand_ptr) -> u64 {
return (a<<32) | b;
}
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); }
uint128 :: proc(r: ^Rand = global_rand_ptr) -> u128 {
a := u128(_random(r));
b := u128(_random(r));
c := u128(_random(r));
d := u128(_random(r));
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_max :: proc(n: i32, r: ^Rand = global_rand_ptr) -> i32 {
if n <= 0 do panic("Invalid argument to int31_max");
@@ -76,6 +85,19 @@ 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");
if n&(n-1) == 0 {
return int127(r) & (n-1);
}
max := i128((1<<63) - 1 - (1<<63)&u128(n));
v := int127(r);
for v > max {
v = int127(r);
}
return v % n;
}
int_max :: proc(n: int, r: ^Rand = global_rand_ptr) -> int {
if n <= 0 do panic("Invalid argument to int_max");
when size_of(int) == 4 {
+26 -9
View File
@@ -2,26 +2,33 @@ package mem
import "core:runtime"
DEFAULT_ALIGNMENT :: 2*align_of(rawptr);
// 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.
Allocator_Mode :: runtime.Allocator_Mode;
/*
Allocator_Mode :: enum byte {
Alloc,
Free,
Free_All,
Resize,
}
*/
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;
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64 = 0, location := #caller_location) -> rawptr;
*/
Allocator :: runtime.Allocator;
/*
Allocator :: struct {
procedure: Allocator_Proc,
data: rawptr,
}
*/
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;
@@ -84,7 +91,10 @@ delete :: proc{
new :: inline proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T {
ptr := (^T)(alloc(size_of(T), align_of(T), allocator, loc));
return new_aligned(T, align_of(T), allocator, loc);
}
new_aligned :: inline 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{};
return ptr;
}
@@ -95,9 +105,13 @@ new_clone :: inline proc(data: $T, allocator := context.allocator, loc := #calle
}
make_slice :: proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
make_slice :: inline 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 {
runtime.make_slice_error_loc(loc, len);
data := alloc(size_of(E)*len, align_of(E), allocator, loc);
data := alloc(size_of(E)*len, alignment, allocator, loc);
if data == nil do return nil;
s := Raw_Slice{data, len};
return transmute(T)s;
}
@@ -111,6 +125,9 @@ make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, auto_cast len: int, a
runtime.make_dynamic_array_error_loc(loc, len, cap);
data := alloc(size_of(E)*cap, align_of(E), allocator, loc);
s := Raw_Dynamic_Array{data, len, cap, allocator};
if data == nil {
s.len, s.cap = 0, 0;
}
return transmute(T)s;
}
make_map :: proc($T: typeid/map[$K]$E, auto_cast cap: int = 16, allocator := context.allocator, loc := #caller_location) -> T {
+37 -21
View File
@@ -1,7 +1,5 @@
package mem
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 {
@@ -57,7 +55,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return nil;
}
#no_bounds_check end := &arena.data[len(arena.data)];
#no_bounds_check end := &arena.data[arena.offset];
ptr := align_forward(end, uintptr(alignment));
arena.offset += total_size;
@@ -101,6 +99,7 @@ Scratch_Allocator :: struct {
prev_offset: int,
backup_allocator: Allocator,
leaked_allocations: [dynamic]rawptr,
default_to_default_allocator: bool,
}
scratch_allocator_init :: proc(scratch: ^Scratch_Allocator, data: []byte, backup_allocator := context.allocator) {
@@ -110,6 +109,18 @@ scratch_allocator_init :: proc(scratch: ^Scratch_Allocator, data: []byte, backup
scratch.backup_allocator = backup_allocator;
}
scratch_allocator_destroy :: proc(using scratch: ^Scratch_Allocator) {
if scratch == nil {
return;
}
for ptr in leaked_allocations {
free(ptr, backup_allocator);
}
delete(leaked_allocations);
delete(data, backup_allocator);
scratch^ = {};
}
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 {
@@ -118,11 +129,15 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
if scratch.data == nil {
DEFAULT_SCRATCH_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));
}
switch mode {
case Allocator_Mode.Alloc:
case .Alloc:
switch {
case scratch.curr_offset+size <= len(scratch.data):
offset := align_forward_uintptr(uintptr(scratch.curr_offset), uintptr(alignment));
@@ -154,7 +169,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return ptr;
case Allocator_Mode.Free:
case .Free:
last_ptr := rawptr(&scratch.data[scratch.prev_offset]);
if old_memory == last_ptr {
full_size := scratch.curr_offset - scratch.prev_offset;
@@ -164,7 +179,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
}
// NOTE(bill): It's scratch memory, don't worry about freeing
case Allocator_Mode.Free_All:
case .Free_All:
scratch.curr_offset = 0;
scratch.prev_offset = 0;
for ptr in scratch.leaked_allocations {
@@ -172,7 +187,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
}
clear(&scratch.leaked_allocations);
case Allocator_Mode.Resize:
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;
@@ -377,7 +392,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return nil;
}
alignment = clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2);
align := clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2);
raw_alloc :: proc(s: ^Small_Stack, size, alignment: int) -> rawptr {
curr_addr := uintptr(&s.data[0]) + uintptr(s.offset);
@@ -400,7 +415,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
switch mode {
case .Alloc:
return raw_alloc(s, size, alignment);
return raw_alloc(s, size, align);
case .Free:
if old_memory == nil {
return nil;
@@ -429,7 +444,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
case .Resize:
if old_memory == nil {
return raw_alloc(s, size, alignment);
return raw_alloc(s, size, align);
}
if size == 0 {
return nil;
@@ -452,7 +467,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return old_memory;
}
ptr := raw_alloc(s, size, alignment);
ptr := raw_alloc(s, size, align);
copy(ptr, old_memory, min(old_size, size));
return ptr;
}
@@ -493,13 +508,13 @@ dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
pool := (^Dynamic_Pool)(allocator_data);
switch mode {
case Allocator_Mode.Alloc:
case .Alloc:
return dynamic_pool_alloc(pool, size);
case Allocator_Mode.Free:
case .Free:
panic("Allocator_Mode.Free is not supported for a pool");
case Allocator_Mode.Free_All:
case .Free_All:
dynamic_pool_free_all(pool);
case Allocator_Mode.Resize:
case .Resize:
panic("Allocator_Mode.Resize is not supported for a pool");
if old_size >= size {
return old_memory;
@@ -568,9 +583,10 @@ dynamic_pool_alloc :: proc(using pool: ^Dynamic_Pool, bytes: int) -> rawptr {
}
extra := alignment - (bytes % alignment);
bytes += extra;
if bytes >= out_band_size {
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,
block_size, alignment,
@@ -581,7 +597,7 @@ dynamic_pool_alloc :: proc(using pool: ^Dynamic_Pool, bytes: int) -> rawptr {
return memory;
}
if bytes_left < bytes {
if bytes_left < n {
cycle_new_block(pool);
if current_block == nil {
return nil;
@@ -589,8 +605,8 @@ dynamic_pool_alloc :: proc(using pool: ^Dynamic_Pool, bytes: int) -> rawptr {
}
memory := current_pos;
current_pos = ptr_offset((^byte)(current_pos), bytes);
bytes_left -= bytes;
current_pos = ptr_offset((^byte)(current_pos), n);
bytes_left -= n;
return memory;
}
+40 -45
View File
@@ -1,5 +1,7 @@
package mem
import "core:runtime"
foreign _ {
@(link_name = "llvm.bswap.i16") swap16 :: proc(b: u16) -> u16 ---;
@(link_name = "llvm.bswap.i32") swap32 :: proc(b: u32) -> u32 ---;
@@ -10,8 +12,6 @@ swap :: proc{swap16, swap32, swap64};
set :: proc "contextless" (data: rawptr, value: byte, len: int) -> rawptr {
if data == nil do return nil;
if len < 0 do return data;
foreign _ {
when size_of(rawptr) == 8 {
@(link_name="llvm.memset.p0i8.i64")
@@ -21,7 +21,8 @@ set :: proc "contextless" (data: rawptr, value: byte, len: int) -> rawptr {
llvm_memset :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) ---;
}
}
llvm_memset(data, byte(value), len, 1, false);
llvm_memset(data, value, len, 1, false);
return data;
}
zero :: inline proc "contextless" (data: rawptr, len: int) -> rawptr {
@@ -38,34 +39,10 @@ zero_slice :: proc "contextless" (data: $T/[]$E) {
copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
if src == nil do return dst;
// NOTE(bill): This _must_ be implemented like C's memmove
foreign _ {
when size_of(rawptr) == 8 {
@(link_name="llvm.memmove.p0i8.p0i8.i64")
llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
} else {
@(link_name="llvm.memmove.p0i8.p0i8.i32")
llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
}
}
llvm_memmove(dst, src, len, 1, false);
return dst;
return runtime.mem_copy(dst, src, len);
}
copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
if src == nil do return dst;
// NOTE(bill): This _must_ be implemented like C's memcpy
foreign _ {
when size_of(rawptr) == 8 {
@(link_name="llvm.memcpy.p0i8.p0i8.i64")
llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
} else {
@(link_name="llvm.memcpy.p0i8.p0i8.i32")
llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
}
}
llvm_memcpy(dst, src, len, 1, false);
return dst;
return runtime.mem_copy_non_overlapping(dst, src, len);
}
compare :: inline proc "contextless" (a, b: []byte) -> int {
return compare_byte_ptrs(&a[0], &b[0], min(len(a), len(b)));
@@ -129,6 +106,16 @@ slice_to_bytes :: inline proc "contextless" (slice: $E/[]$T) -> []byte {
return transmute([]byte)s;
}
slice_data_cast :: inline proc "contextless" ($T: typeid/[]$A, slice: $S/[]$B) -> T {
when size_of(A) == 0 || size_of(B) == 0 {
return nil;
} else {
s := transmute(Raw_Slice)slice;
s.len = (len(slice) * size_of(B)) / size_of(A);
return transmute(T)s;
}
}
buffer_from_slice :: inline proc(backing: $T/[]$E) -> [dynamic]E {
s := transmute(Raw_Slice)backing;
@@ -163,27 +150,19 @@ is_power_of_two :: inline proc(x: uintptr) -> bool {
return (x & (x-1)) == 0;
}
align_forward :: proc(ptr: rawptr, align: uintptr) -> rawptr {
assert(is_power_of_two(align));
a := uintptr(align);
p := uintptr(ptr);
modulo := p & (a-1);
if modulo != 0 do p += a - modulo;
return rawptr(p);
align_forward :: inline proc(ptr: rawptr, align: uintptr) -> rawptr {
return rawptr(align_forward_uintptr(uintptr(ptr), align));
}
align_forward_uintptr :: proc(ptr, align: uintptr) -> uintptr {
assert(is_power_of_two(align));
a := uintptr(align);
p := uintptr(ptr);
modulo := p & (a-1);
if modulo != 0 do p += a - modulo;
return uintptr(p);
p := ptr;
modulo := p & (align-1);
if modulo != 0 do p += align - modulo;
return p;
}
align_forward_int :: inline proc(ptr, align: int) -> int {
return int(align_forward_uintptr(uintptr(ptr), uintptr(align)));
}
@@ -191,6 +170,24 @@ align_forward_uint :: inline proc(ptr, align: uint) -> uint {
return uint(align_forward_uintptr(uintptr(ptr), uintptr(align)));
}
align_backward :: inline proc(ptr: rawptr, align: uintptr) -> rawptr {
return rawptr(align_backward_uintptr(uintptr(ptr), align));
}
align_backward_uintptr :: proc(ptr, align: uintptr) -> uintptr {
assert(is_power_of_two(align));
ptr := rawptr(ptr - align);
return uintptr(align_forward(ptr, align));
}
align_backward_int :: inline proc(ptr, align: int) -> int {
return int(align_backward_uintptr(uintptr(ptr), uintptr(align)));
}
align_backward_uint :: inline proc(ptr, align: uint) -> uint {
return uint(align_backward_uintptr(uintptr(ptr), uintptr(align)));
}
context_from_allocator :: proc(a: Allocator) -> type_of(context) {
context.allocator = a;
return context;
@@ -238,5 +235,3 @@ calc_padding_with_header :: proc(ptr: uintptr, align: uintptr, header_size: int)
return int(padding);
}
+9 -3
View File
@@ -31,19 +31,25 @@ Raw_Map :: struct {
entries: Raw_Dynamic_Array,
}
Raw_Complex64 :: struct {real, imag: f32};
Raw_Complex128 :: struct {real, imag: f64};
Raw_Quaternion128 :: struct {imag, jmag, kmag: f32, real: f32};
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 {
return transmute(any)Raw_Any{data, id};
}
raw_string_data :: inline proc(s: $T/string) -> ^byte {
return (^Raw_String)(&s).data;
return (transmute(Raw_String)s).data;
}
raw_slice_data :: inline proc(a: $T/[]$E) -> ^E {
return cast(^E)(^Raw_Slice)(&a).data;
return cast(^E)(transmute(Raw_Slice)a).data;
}
raw_dynamic_array_data :: inline proc(a: $T/[dynamic]$E) -> ^E {
return cast(^E)(^Raw_Dynamic_Array)(&a).data;
return cast(^E)(transmute(Raw_Dynamic_Array)a).data;
}
raw_data :: proc{raw_string_data, raw_slice_data, raw_dynamic_array_data};
+125 -112
View File
@@ -1,11 +1,10 @@
package odin_ast
import "core:odin/token"
import "core:odin/tokenizer"
Proc_Tag :: enum {
Bounds_Check,
No_Bounds_Check,
Require_Results,
}
Proc_Tags :: distinct bit_set[Proc_Tag; u32];
@@ -34,12 +33,12 @@ Node_State_Flags :: distinct bit_set[Node_State_Flag];
Comment_Group :: struct {
list: []token.Token,
list: []tokenizer.Token,
}
Node :: struct {
pos: token.Pos,
end: token.Pos,
pos: tokenizer.Pos,
end: tokenizer.Pos,
derived: any,
state_flags: Node_State_Flags,
}
@@ -68,29 +67,29 @@ Ident :: struct {
Implicit :: struct {
using node: Expr,
tok: token.Token,
tok: tokenizer.Token,
}
Undef :: struct {
using node: Expr,
tok: token.Kind,
tok: tokenizer.Token_Kind,
}
Basic_Lit :: struct {
using node: Expr,
tok: token.Token,
tok: tokenizer.Token,
}
Basic_Directive :: struct {
using node: Expr,
tok: token.Token,
tok: tokenizer.Token,
name: string,
}
Ellipsis :: struct {
using node: Expr,
tok: token.Kind,
tok: tokenizer.Token_Kind,
expr: ^Expr,
}
@@ -100,42 +99,44 @@ Proc_Lit :: struct {
body: ^Stmt,
tags: Proc_Tags,
inlining: Proc_Inlining,
where_token: tokenizer.Token,
where_clauses: []^Expr,
}
Comp_Lit :: struct {
using node: Expr,
type: ^Expr,
open: token.Pos,
open: tokenizer.Pos,
elems: []^Expr,
close: token.Pos,
close: tokenizer.Pos,
}
Tag_Expr :: struct {
using node: Expr,
op: token.Token,
op: tokenizer.Token,
name: string,
expr: ^Expr,
}
Unary_Expr :: struct {
using node: Expr,
op: token.Token,
op: tokenizer.Token,
expr: ^Expr,
}
Binary_Expr :: struct {
using node: Expr,
left: ^Expr,
op: token.Token,
op: tokenizer.Token,
right: ^Expr,
}
Paren_Expr :: struct {
using node: Expr,
open: token.Pos,
open: tokenizer.Pos,
expr: ^Expr,
close: token.Pos,
close: tokenizer.Pos,
}
Selector_Expr :: struct {
@@ -152,74 +153,74 @@ Implicit_Selector_Expr :: struct {
Index_Expr :: struct {
using node: Expr,
expr: ^Expr,
open: token.Pos,
open: tokenizer.Pos,
index: ^Expr,
close: token.Pos,
close: tokenizer.Pos,
}
Deref_Expr :: struct {
using node: Expr,
expr: ^Expr,
op: token.Token,
op: tokenizer.Token,
}
Slice_Expr :: struct {
using node: Expr,
expr: ^Expr,
open: token.Pos,
open: tokenizer.Pos,
low: ^Expr,
interval: token.Token,
interval: tokenizer.Token,
high: ^Expr,
close: token.Pos,
close: tokenizer.Pos,
}
Call_Expr :: struct {
using node: Expr,
inlining: Proc_Inlining,
expr: ^Expr,
open: token.Pos,
open: tokenizer.Pos,
args: []^Expr,
ellipsis: token.Token,
close: token.Pos,
ellipsis: tokenizer.Token,
close: tokenizer.Pos,
}
Field_Value :: struct {
using node: Expr,
field: ^Expr,
sep: token.Pos,
sep: tokenizer.Pos,
value: ^Expr,
}
Ternary_Expr :: struct {
using node: Expr,
cond: ^Expr,
op1: token.Token,
op1: tokenizer.Token,
x: ^Expr,
op2: token.Token,
op2: tokenizer.Token,
y: ^Expr,
}
Type_Assertion :: struct {
using node: Expr,
expr: ^Expr,
dot: token.Pos,
open: token.Pos,
dot: tokenizer.Pos,
open: tokenizer.Pos,
type: ^Expr,
close: token.Pos,
close: tokenizer.Pos,
}
Type_Cast :: struct {
using node: Expr,
tok: token.Token,
open: token.Pos,
tok: tokenizer.Token,
open: tokenizer.Pos,
type: ^Expr,
close: token.Pos,
close: tokenizer.Pos,
expr: ^Expr,
}
Auto_Cast :: struct {
using node: Expr,
op: token.Token,
op: tokenizer.Token,
expr: ^Expr,
}
@@ -234,7 +235,7 @@ Bad_Stmt :: struct {
Empty_Stmt :: struct {
using node: Stmt,
semicolon: token.Pos, // Position of the following ';'
semicolon: tokenizer.Pos, // Position of the following ';'
}
Expr_Stmt :: struct {
@@ -244,7 +245,7 @@ Expr_Stmt :: struct {
Tag_Stmt :: struct {
using node: Stmt,
op: token.Token,
op: tokenizer.Token,
name: string,
stmt: ^Stmt,
}
@@ -252,7 +253,7 @@ Tag_Stmt :: struct {
Assign_Stmt :: struct {
using node: Stmt,
lhs: []^Expr,
op: token.Token,
op: tokenizer.Token,
rhs: []^Expr,
}
@@ -260,15 +261,15 @@ Assign_Stmt :: struct {
Block_Stmt :: struct {
using node: Stmt,
label: ^Expr,
open: token.Pos,
open: tokenizer.Pos,
stmts: []^Stmt,
close: token.Pos,
close: tokenizer.Pos,
}
If_Stmt :: struct {
using node: Stmt,
label: ^Expr,
if_pos: token.Pos,
if_pos: tokenizer.Pos,
init: ^Stmt,
cond: ^Expr,
body: ^Stmt,
@@ -277,7 +278,7 @@ If_Stmt :: struct {
When_Stmt :: struct {
using node: Stmt,
when_pos: token.Pos,
when_pos: tokenizer.Pos,
cond: ^Expr,
body: ^Stmt,
else_stmt: ^Stmt,
@@ -296,7 +297,7 @@ Defer_Stmt :: struct {
For_Stmt :: struct {
using node: Stmt,
label: ^Expr,
for_pos: token.Pos,
for_pos: tokenizer.Pos,
init: ^Stmt,
cond: ^Expr,
post: ^Stmt,
@@ -306,10 +307,10 @@ For_Stmt :: struct {
Range_Stmt :: struct {
using node: Stmt,
label: ^Expr,
for_pos: token.Pos,
for_pos: tokenizer.Pos,
val0: ^Expr,
val1: ^Expr,
in_pos: token.Pos,
in_pos: tokenizer.Pos,
expr: ^Expr,
body: ^Stmt,
}
@@ -317,35 +318,35 @@ Range_Stmt :: struct {
Case_Clause :: struct {
using node: Stmt,
case_pos: token.Pos,
case_pos: tokenizer.Pos,
list: []^Expr,
terminator: token.Token,
terminator: tokenizer.Token,
body: []^Stmt,
}
Switch_Stmt :: struct {
using node: Stmt,
label: ^Expr,
switch_pos: token.Pos,
switch_pos: tokenizer.Pos,
init: ^Stmt,
cond: ^Expr,
body: ^Stmt,
complete: bool,
partial: bool,
}
Type_Switch_Stmt :: struct {
using node: Stmt,
label: ^Expr,
switch_pos: token.Pos,
switch_pos: tokenizer.Pos,
tag: ^Stmt,
expr: ^Expr,
body: ^Stmt,
complete: bool,
partial: bool,
}
Branch_Stmt :: struct {
using node: Stmt,
tok: token.Token,
tok: tokenizer.Token,
label: ^Ident,
}
@@ -376,7 +377,7 @@ Value_Decl :: struct {
Package_Decl :: struct {
using node: Decl,
docs: ^Comment_Group,
token: token.Token,
token: tokenizer.Token,
name: string,
comment: ^Comment_Group,
}
@@ -385,9 +386,9 @@ Import_Decl :: struct {
using node: Decl,
docs: ^Comment_Group,
is_using: bool,
import_tok: token.Token,
name: token.Token,
relpath: token.Token,
import_tok: tokenizer.Token,
name: tokenizer.Token,
relpath: tokenizer.Token,
fullpath: string,
comment: ^Comment_Group,
}
@@ -396,7 +397,7 @@ Foreign_Block_Decl :: struct {
using node: Decl,
docs: ^Comment_Group,
attributes: [dynamic]^Attribute, // dynamic as parsing will add to them lazily
tok: token.Token,
tok: tokenizer.Token,
foreign_library: ^Expr,
body: ^Stmt,
}
@@ -404,27 +405,29 @@ Foreign_Block_Decl :: struct {
Foreign_Import_Decl :: struct {
using node: Decl,
docs: ^Comment_Group,
foreign_tok: token.Token,
import_tok: token.Token,
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,
}
// Other things
unparen_expr :: proc(expr: ^Expr) -> ^Expr {
unparen_expr :: proc(expr: ^Expr) -> (val: ^Expr) {
val = expr;
if expr == nil {
return nil;
return;
}
for {
e, ok := expr.derived.(Paren_Expr);
e, ok := val.derived.(Paren_Expr);
if !ok do break;
expr = e.expr;
val = e.expr;
}
return expr;
return;
}
Field_Flag :: enum {
@@ -434,7 +437,9 @@ Field_Flag :: enum {
C_Vararg,
Auto_Cast,
In,
Results,
Tags,
Default_Parameters,
Typeid_Token,
}
@@ -442,18 +447,19 @@ Field_Flag :: enum {
Field_Flags :: distinct bit_set[Field_Flag];
Field_Flags_Struct :: Field_Flags{
Field_Flag.Using,
.Using,
.Tags,
};
Field_Flags_Record_Poly_Params :: Field_Flags{
Field_Flag.Typeid_Token,
.Typeid_Token,
};
Field_Flags_Signature :: Field_Flags{
Field_Flag.Ellipsis,
Field_Flag.Using,
Field_Flag.No_Alias,
Field_Flag.C_Vararg,
Field_Flag.Auto_Cast,
Field_Flag.Default_Parameters,
.Ellipsis,
.Using,
.No_Alias,
.C_Vararg,
.Auto_Cast,
.Default_Parameters,
};
Field_Flags_Signature_Params :: Field_Flags_Signature | {Field_Flag.Typeid_Token};
@@ -462,18 +468,18 @@ Field_Flags_Signature_Results :: Field_Flags_Signature;
Proc_Group :: struct {
using node: Expr,
tok: token.Token,
open: token.Pos,
tok: tokenizer.Token,
open: tokenizer.Pos,
args: []^Expr,
close: token.Pos,
close: tokenizer.Pos,
}
Attribute :: struct {
using node: Node,
tok: token.Kind,
open: token.Pos,
tok: tokenizer.Token_Kind,
open: tokenizer.Pos,
elems: []^Expr,
close: token.Pos,
close: tokenizer.Pos,
}
Field :: struct {
@@ -482,56 +488,57 @@ Field :: struct {
names: []^Expr, // Could be polymorphic
type: ^Expr,
default_value: ^Expr,
tag: tokenizer.Token,
flags: Field_Flags,
comment: ^Comment_Group,
}
Field_List :: struct {
using node: Node,
open: token.Pos,
open: tokenizer.Pos,
list: []^Field,
close: token.Pos,
close: tokenizer.Pos,
}
// Types
Typeid_Type :: struct {
using node: Expr,
tok: token.Kind,
tok: tokenizer.Token_Kind,
specialization: ^Expr,
}
Helper_Type :: struct {
using node: Expr,
tok: token.Kind,
tok: tokenizer.Token_Kind,
type: ^Expr,
}
Distinct_Type :: struct {
using node: Expr,
tok: token.Kind,
tok: tokenizer.Token_Kind,
type: ^Expr,
}
Opaque_Type :: struct {
using node: Expr,
tok: token.Kind,
tok: tokenizer.Token_Kind,
type: ^Expr,
}
Poly_Type :: struct {
using node: Expr,
dollar: token.Pos,
dollar: tokenizer.Pos,
type: ^Ident,
specialization: ^Expr,
}
Proc_Type :: struct {
using node: Expr,
tok: token.Token,
tok: tokenizer.Token,
calling_convention: Proc_Calling_Convention,
params: ^Field_List,
arrow: token.Pos,
arrow: tokenizer.Pos,
results: ^Field_List,
tags: Proc_Tags,
generic: bool,
@@ -540,77 +547,83 @@ Proc_Type :: struct {
Pointer_Type :: struct {
using node: Expr,
pointer: token.Pos,
pointer: tokenizer.Pos,
elem: ^Expr,
}
Array_Type :: struct {
using node: Expr,
open: token.Pos,
open: tokenizer.Pos,
tag: ^Expr,
len: ^Expr, // Ellipsis node for [?]T arrray types, nil for slice types
close: token.Pos,
close: tokenizer.Pos,
elem: ^Expr,
}
Dynamic_Array_Type :: struct {
using node: Expr,
open: token.Pos,
dynamic_pos: token.Pos,
close: token.Pos,
tag: ^Expr,
open: tokenizer.Pos,
dynamic_pos: tokenizer.Pos,
close: tokenizer.Pos,
elem: ^Expr,
}
Struct_Type :: struct {
using node: Expr,
tok_pos: token.Pos,
poly_params: ^Field_List,
align: ^Expr,
is_packed: bool,
is_raw_union: bool,
fields: ^Field_List,
name_count: int,
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,
}
Union_Type :: struct {
using node: Expr,
tok_pos: token.Pos,
tok_pos: tokenizer.Pos,
poly_params: ^Field_List,
align: ^Expr,
variants: []^Expr,
where_token: tokenizer.Token,
where_clauses: []^Expr,
}
Enum_Type :: struct {
using node: Expr,
tok_pos: token.Pos,
tok_pos: tokenizer.Pos,
base_type: ^Expr,
open: token.Pos,
open: tokenizer.Pos,
fields: []^Expr,
close: token.Pos,
close: tokenizer.Pos,
is_using: bool,
}
Bit_Field_Type :: struct {
using node: Expr,
tok_pos: token.Pos,
tok_pos: tokenizer.Pos,
align: ^Expr,
open: token.Pos,
open: tokenizer.Pos,
fields: []^Field_Value, // Field_Value with ':' rather than '='
close: token.Pos,
close: tokenizer.Pos,
}
Bit_Set_Type :: struct {
using node: Expr,
tok_pos: token.Pos,
open: token.Pos,
tok_pos: tokenizer.Pos,
open: tokenizer.Pos,
elem: ^Expr,
underlying: ^Expr,
close: token.Pos,
close: tokenizer.Pos,
}
Map_Type :: struct {
using node: Expr,
tok_pos: token.Pos,
tok_pos: tokenizer.Pos,
key: ^Expr,
value: ^Expr,
}
+2 -2
View File
@@ -2,9 +2,9 @@ package odin_ast
import "core:mem"
import "core:fmt"
import "core:odin/token"
import "core:odin/tokenizer"
new :: proc($T: typeid, pos, end: token.Pos) -> ^T {
new :: proc($T: typeid, pos, end: tokenizer.Pos) -> ^T {
n := mem.new(T);
n.pos = pos;
n.end = end;
+2 -2
View File
@@ -1,6 +1,6 @@
package odin_ast
import "core:odin/token"
import "core:odin/tokenizer"
Package_Kind :: enum {
Normal,
@@ -26,7 +26,7 @@ File :: struct {
src: []byte,
pkg_decl: ^Package_Decl,
pkg_token: token.Token,
pkg_token: tokenizer.Token,
pkg_name: string,
decls: [dynamic]^Stmt,
File diff suppressed because it is too large Load Diff
@@ -1,9 +1,9 @@
package odin_token
package odin_tokenizer
import "core:strings"
Token :: struct {
kind: Kind,
kind: Token_Kind,
text: string,
pos: Pos,
}
@@ -28,7 +28,7 @@ pos_compare :: proc(lhs, rhs: Pos) -> int {
return strings.compare(lhs.file, rhs.file);
}
using Kind :: enum u32 {
Token_Kind :: enum u32 {
Invalid,
EOF,
Comment,
@@ -118,12 +118,13 @@ using Kind :: enum u32 {
Package,
Typeid,
When,
Where,
If,
Else,
For,
Switch,
In,
Notin,
Not_In,
Do,
Case,
Break,
@@ -154,9 +155,6 @@ using Kind :: enum u32 {
Offset_Of,
Type_Of,
Const,
Asm,
Yield,
Await,
B_Keyword_End,
COUNT,
@@ -165,7 +163,7 @@ using Kind :: enum u32 {
// ... Custom keywords
};
tokens := [Kind.COUNT]string {
tokens := [Token_Kind.COUNT]string {
"Invalid",
"EOF",
"Comment",
@@ -255,12 +253,13 @@ tokens := [Kind.COUNT]string {
"package",
"typeid",
"when",
"where",
"if",
"else",
"for",
"switch",
"in",
"notin",
"not_in",
"do",
"case",
"break",
@@ -291,20 +290,17 @@ tokens := [Kind.COUNT]string {
"offset_of",
"type_of",
"const",
"asm",
"yield",
"await",
"",
};
custom_keyword_tokens: []string;
to_string :: proc(kind: Kind) -> string {
if Invalid <= kind && kind < COUNT {
to_string :: proc(kind: Token_Kind) -> string {
if Token_Kind.Invalid <= kind && kind < Token_Kind.COUNT {
return tokens[kind];
}
if B_Custom_Keyword_Begin < kind {
n := int(u16(kind)-u16(B_Custom_Keyword_Begin));
if Token_Kind.B_Custom_Keyword_Begin < kind {
n := int(u16(kind)-u16(Token_Kind.B_Custom_Keyword_Begin));
if n < len(custom_keyword_tokens) {
return custom_keyword_tokens[n];
}
@@ -313,24 +309,26 @@ to_string :: proc(kind: Kind) -> string {
return "Invalid";
}
is_literal :: proc(kind: Kind) -> bool { return B_Literal_Begin < kind && kind < B_Literal_End; }
is_operator :: proc(kind: Kind) -> bool {
switch kind {
case B_Operator_Begin..B_Operator_End:
is_literal :: proc(kind: Token_Kind) -> bool {
return Token_Kind.B_Literal_Begin < kind && kind < Token_Kind.B_Literal_End;
}
is_operator :: proc(kind: Token_Kind) -> bool {
#partial switch kind {
case .B_Operator_Begin .. .B_Operator_End:
return true;
case In, Notin:
case .In, .Not_In:
return true;
}
return false;
}
is_assignment_operator :: proc(kind: Kind) -> bool {
return B_Assign_Op_Begin < kind && kind < B_Assign_Op_End || kind == Eq;
is_assignment_operator :: proc(kind: Token_Kind) -> bool {
return Token_Kind.B_Assign_Op_Begin < kind && kind < Token_Kind.B_Assign_Op_End || kind == Token_Kind.Eq;
}
is_keyword :: proc(kind: Kind) -> bool {
is_keyword :: proc(kind: Token_Kind) -> bool {
switch {
case B_Keyword_Begin < kind && kind < B_Keyword_End:
case Token_Kind.B_Keyword_Begin < kind && kind < Token_Kind.B_Keyword_End:
return true;
case B_Custom_Keyword_Begin < kind:
case Token_Kind.B_Custom_Keyword_Begin < kind:
return true;
}
return false;
+84 -86
View File
@@ -1,10 +1,9 @@
package odin_tokenizer
import "core:fmt"
import "core:odin/token"
import "core:unicode/utf8"
Error_Handler :: #type proc(pos: token.Pos, fmt: string, args: ..any);
Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any);
Tokenizer :: struct {
// Immutable data
@@ -41,11 +40,11 @@ init :: proc(t: ^Tokenizer, src: []byte, path: string, err: Error_Handler = defa
}
@(private)
offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> token.Pos {
offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> Pos {
line := t.line_count;
column := offset - t.line_offset + 1;
return token.Pos {
return Pos {
file = t.path,
offset = offset,
line = line,
@@ -53,10 +52,10 @@ offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> token.Pos {
};
}
default_error_handler :: proc(pos: token.Pos, msg: string, args: ..any) {
fmt.printf_err("%s(%d:%d) ", pos.file, pos.line, pos.column);
fmt.printf_err(msg, ..args);
fmt.printf_err("\n");
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");
}
error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
@@ -98,9 +97,9 @@ advance_rune :: proc(using t: ^Tokenizer) {
}
}
peek_byte :: proc(using t: ^Tokenizer, offset := 0) -> byte {
if read_offset+offset < len(src) {
return src[read_offset+offset];
peek_byte :: proc(t: ^Tokenizer, offset := 0) -> byte {
if t.read_offset+offset < len(t.src) {
return t.src[t.read_offset+offset];
}
return 0;
}
@@ -322,15 +321,15 @@ scan_rune :: proc(t: ^Tokenizer) -> string {
return string(t.src[offset : t.offset]);
}
scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (token.Kind, string) {
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 || t.ch == '_' {
advance_rune(t);
}
}
scan_exponent :: proc(t: ^Tokenizer, kind: ^token.Kind) {
scan_exponent :: proc(t: ^Tokenizer, kind: ^Token_Kind) {
if t.ch == 'e' || t.ch == 'E' {
kind^ = token.Float;
kind^ = .Float;
advance_rune(t);
if t.ch == '-' || t.ch == '+' {
advance_rune(t);
@@ -343,17 +342,18 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (token.Kind, str
}
// NOTE(bill): This needs to be here for sanity's sake
if t.ch == 'i' {
kind^ = token.Imag;
switch t.ch {
case 'i', 'j', 'k':
kind^ = .Imag;
advance_rune(t);
}
}
scan_fraction :: proc(t: ^Tokenizer, kind: ^token.Kind) -> (early_exit: bool) {
scan_fraction :: proc(t: ^Tokenizer, kind: ^Token_Kind) -> (early_exit: bool) {
if t.ch == '.' && peek_byte(t) == '.' {
return true;
}
if t.ch == '.' {
kind^ = token.Float;
kind^ = .Float;
advance_rune(t);
scan_mantissa(t, 10);
}
@@ -362,21 +362,22 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (token.Kind, str
offset := t.offset;
kind := token.Integer;
kind := Token_Kind.Integer;
seen_point := seen_decimal_point;
if seen_decimal_point {
if seen_point {
offset -= 1;
kind = token.Float;
kind = .Float;
scan_mantissa(t, 10);
scan_exponent(t, &kind);
} else {
if t.ch == '0' {
int_base :: inline proc(t: ^Tokenizer, kind: ^token.Kind, base: int, msg: string) {
int_base :: inline proc(t: ^Tokenizer, kind: ^Token_Kind, base: int, msg: string) {
prev := t.offset;
advance_rune(t);
scan_mantissa(t, base);
if t.offset - prev <= 1 {
kind^ = token.Invalid;
kind^ = .Invalid;
error(t, t.offset, msg);
}
}
@@ -393,7 +394,7 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (token.Kind, str
advance_rune(t);
scan_mantissa(t, 16);
if t.offset - prev <= 1 {
kind = token.Invalid;
kind = .Invalid;
error(t, t.offset, "illegal hexadecimal floating-point number");
} else {
sub := t.src[prev+1 : t.offset];
@@ -412,10 +413,10 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (token.Kind, str
}
case:
seen_decimal_point = false;
seen_point = false;
scan_mantissa(t, 10);
if t.ch == '.' {
seen_decimal_point = true;
seen_point = true;
if scan_fraction(t, &kind) {
return kind, string(t.src[offset : t.offset]);
}
@@ -438,15 +439,15 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (token.Kind, str
}
scan :: proc(t: ^Tokenizer) -> token.Token {
switch2 :: proc(t: ^Tokenizer, tok0, tok1: token.Kind) -> token.Kind {
scan :: proc(t: ^Tokenizer) -> Token {
switch2 :: proc(t: ^Tokenizer, tok0, tok1: Token_Kind) -> Token_Kind {
if t.ch == '=' {
advance_rune(t);
return tok1;
}
return tok0;
}
switch3 :: proc(t: ^Tokenizer, tok0, tok1: token.Kind, ch2: rune, tok2: token.Kind) -> token.Kind {
switch3 :: proc(t: ^Tokenizer, tok0, tok1: Token_Kind, ch2: rune, tok2: Token_Kind) -> Token_Kind {
if t.ch == '=' {
advance_rune(t);
return tok1;
@@ -457,7 +458,7 @@ scan :: proc(t: ^Tokenizer) -> token.Token {
}
return tok0;
}
switch4 :: proc(t: ^Tokenizer, tok0, tok1: token.Kind, ch2: rune, tok2, tok3: token.Kind) -> token.Kind {
switch4 :: proc(t: ^Tokenizer, tok0, tok1: Token_Kind, ch2: rune, tok2, tok3: Token_Kind) -> Token_Kind {
if t.ch == '=' {
advance_rune(t);
return tok1;
@@ -478,28 +479,31 @@ scan :: proc(t: ^Tokenizer) -> token.Token {
offset := t.offset;
kind: token.Kind;
kind: Token_Kind;
lit: string;
pos := offset_to_pos(t, offset);
switch ch := t.ch; true {
case is_letter(ch):
lit = scan_identifier(t);
kind = token.Ident;
kind = .Ident;
check_keyword: if len(lit) > 1 {
// TODO(bill): Maybe have a hash table lookup rather than this linear search
for i in token.B_Keyword_Begin .. token.B_Keyword_End {
if lit == token.tokens[i] {
kind = token.Kind(i);
for i in Token_Kind.B_Keyword_Begin .. Token_Kind.B_Keyword_End {
if lit == tokens[i] {
kind = Token_Kind(i);
break check_keyword;
}
}
for keyword, i in token.custom_keyword_tokens {
for keyword, i in custom_keyword_tokens {
if lit == keyword {
kind = token.Kind(i+1)+token.B_Custom_Keyword_Begin;
kind = Token_Kind(i+1) + .B_Custom_Keyword_Begin;
break check_keyword;
}
}
if kind == .Ident && lit == "notin" {
kind = .Not_In;
}
}
case '0' <= ch && ch <= '9':
kind, lit = scan_number(t, false);
@@ -507,115 +511,109 @@ scan :: proc(t: ^Tokenizer) -> token.Token {
advance_rune(t);
switch ch {
case -1:
kind = token.EOF;
kind = .EOF;
case '"':
kind = token.String;
kind = .String;
lit = scan_string(t);
case '\'':
kind = token.Rune;
kind = .Rune;
lit = scan_rune(t);
case '`':
kind = token.String;
kind = .String;
lit = scan_raw_string(t);
case '=':
if t.ch == '>' {
advance_rune(t);
kind = token.Double_Arrow_Right;
kind = .Double_Arrow_Right;
} else {
kind = switch2(t, token.Eq, token.Cmp_Eq);
kind = switch2(t, .Eq, .Cmp_Eq);
}
case '!': kind = switch2(t, token.Not, token.Not_Eq);
case '!': kind = switch2(t, .Not, .Not_Eq);
case '#':
kind = token.Hash;
kind = .Hash;
if t.ch == '!' {
kind = token.Comment;
kind = .Comment;
lit = scan_comment(t);
}
case '?': kind = token.Question;
case '@': kind = token.At;
case '$': kind = token.Dollar;
case '^': kind = token.Pointer;
case '+': kind = switch2(t, token.Add, token.Add_Eq);
case '?': kind = .Question;
case '@': kind = .At;
case '$': kind = .Dollar;
case '^': kind = .Pointer;
case '+': kind = switch2(t, .Add, .Add_Eq);
case '-':
if t.ch == '>' {
advance_rune(t);
kind = token.Arrow_Right;
kind = .Arrow_Right;
} else if t.ch == '-' && peek_byte(t) == '-' {
advance_rune(t);
advance_rune(t);
kind = token.Undef;
kind = .Undef;
} else {
kind = switch2(t, token.Sub, token.Sub_Eq);
kind = switch2(t, .Sub, .Sub_Eq);
}
case '*': kind = switch2(t, token.Mul, token.Mul_Eq);
case '*': kind = switch2(t, .Mul, .Mul_Eq);
case '/':
if t.ch == '/' || t.ch == '*' {
kind = token.Comment;
kind = .Comment;
lit = scan_comment(t);
} else {
kind = switch2(t, token.Quo, token.Quo_Eq);
kind = switch2(t, .Quo, .Quo_Eq);
}
case '%': kind = switch4(t, token.Mod, token.Mod_Eq, '%', token.Mod_Mod, token.Mod_Mod_Eq);
case '%': kind = switch4(t, .Mod, .Mod_Eq, '%', .Mod_Mod, .Mod_Mod_Eq);
case '&':
if t.ch == '~' {
advance_rune(t);
kind = switch2(t, token.And_Not, token.And_Not_Eq);
kind = switch2(t, .And_Not, .And_Not_Eq);
} else {
kind = switch3(t, token.And, token.And_Eq, '&', token.Cmp_And);
kind = switch3(t, .And, .And_Eq, '&', .Cmp_And);
}
case '|': kind = switch3(t, token.Or, token.Or_Eq, '|', token.Cmp_Or);
case '~': kind = token.Xor;
case '|': kind = switch3(t, .Or, .Or_Eq, '|', .Cmp_Or);
case '~': kind = .Xor;
case '<':
if t.ch == '-' {
advance_rune(t);
kind = token.Arrow_Left;
kind = .Arrow_Left;
} else {
kind = switch4(t, token.Lt, token.Lt_Eq, '<', token.Shl, token.Shl_Eq);
kind = switch4(t, .Lt, .Lt_Eq, '<', .Shl, .Shl_Eq);
}
case '>': kind = switch4(t, token.Gt, token.Gt_Eq, '>', token.Shr,token.Shr_Eq);
case '≠': kind = token.Not_Eq;
case '≤': kind = token.Lt_Eq;
case '≥': kind = token.Gt_Eq;
case '∈': kind = token.In;
case '∉': kind = token.Notin;
case '>': kind = switch4(t, .Gt, .Gt_Eq, '>', .Shr,.Shr_Eq);
case '.':
if '0' <= t.ch && t.ch <= '9' {
kind, lit = scan_number(t, true);
} else {
kind = token.Period;
kind = .Period;
if t.ch == '.' {
advance_rune(t);
kind = token.Ellipsis;
kind = .Ellipsis;
if t.ch == '<' {
advance_rune(t);
kind = token.Range_Half;
kind = .Range_Half;
}
}
}
case ':': kind = token.Colon;
case ',': kind = token.Comma;
case ';': kind = token.Semicolon;
case '(': kind = token.Open_Paren;
case ')': kind = token.Close_Paren;
case '[': kind = token.Open_Bracket;
case ']': kind = token.Close_Bracket;
case '{': kind = token.Open_Brace;
case '}': kind = token.Close_Brace;
case ':': kind = .Colon;
case ',': kind = .Comma;
case ';': kind = .Semicolon;
case '(': kind = .Open_Paren;
case ')': kind = .Close_Paren;
case '[': kind = .Open_Bracket;
case ']': kind = .Close_Bracket;
case '{': kind = .Open_Brace;
case '}': kind = .Close_Brace;
case '\\': kind = token.Back_Slash;
case '\\': kind = .Back_Slash;
case:
if ch != utf8.RUNE_BOM {
error(t, t.offset, "illegal character '%r': %d", ch, ch);
}
kind = token.Invalid;
kind = .Invalid;
}
}
if lit == "" {
lit = string(t.src[offset : t.offset]);
}
return token.Token{kind, lit, pos};
return Token{kind, lit, pos};
}
+75 -8
View File
@@ -5,7 +5,7 @@ import "core:strconv"
import "core:unicode/utf8"
write_string :: proc(fd: Handle, str: string) -> (int, Errno) {
return write(fd, cast([]byte)str);
return write(fd, transmute([]byte)str);
}
write_byte :: proc(fd: Handle, b: byte) -> (int, Errno) {
@@ -99,7 +99,14 @@ write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (succ
if truncate {
flags |= O_TRUNC;
}
fd, err := open(name, flags, 0);
mode: int = 0;
when OS == "linux" {
// NOTE(justasd): 644 (owner read, write; group read; others read)
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
}
fd, err := open(name, flags, mode);
if err != 0 {
return false;
}
@@ -119,16 +126,56 @@ read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) {
return read(fd, s);
}
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 {
/*
//
// NOTE(tetra, 2019-11-10): The heap doesn't respect alignment.
// HACK: Overallocate, align forwards, and then use the two bytes immediately before
// the address we return, to store the padding we inserted.
// This allows us to pass the original pointer we got back from the heap to `free` later.
//
align_and_store_padding :: proc(ptr: rawptr, alignment: int) -> rawptr {
ptr := mem.ptr_offset(cast(^u8) ptr, 2);
new_ptr := cast(^u8) mem.align_forward(ptr, uintptr(alignment));
offset := mem.ptr_sub(new_ptr, cast(^u8) ptr) + 2;
assert(offset < int(max(u16)));
(^[2]u8)(mem.ptr_offset(new_ptr, -2))^ = transmute([2]u8) u16(offset);
return new_ptr;
}
recover_original_pointer :: proc(ptr: rawptr) -> rawptr {
ptr := cast(^u8) ptr;
offset := transmute(u16) (^[2]u8)(mem.ptr_offset(ptr, -2))^;
ptr = mem.ptr_offset(ptr, -int(offset));
return ptr;
}
aligned_heap_alloc :: proc(size: int, alignment: int) -> rawptr {
// NOTE(tetra): Alignment 1 will mean we only have one extra byte.
// This is not enough for a u16 - so we ensure there is at least two bytes extra.
// This also means that the pointer is always aligned to at least 2.
extra := alignment;
if extra <= 1 do extra = 2;
orig := cast(^u8) heap_alloc(size + extra);
if orig == nil do return nil;
ptr := align_and_store_padding(orig, alignment);
assert(recover_original_pointer(ptr) == orig);
return ptr;
}
switch mode {
case .Alloc:
return heap_alloc(size);
return aligned_heap_alloc(size, alignment);
case .Free:
heap_free(old_memory);
if old_memory != nil {
ptr := recover_original_pointer(old_memory);
heap_free(ptr);
}
return nil;
case .Free_All:
@@ -136,11 +183,31 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
case .Resize:
if old_memory == nil {
return heap_alloc(size);
return aligned_heap_alloc(size, alignment);
}
ptr := heap_resize(old_memory, size);
ptr := recover_original_pointer(old_memory);
ptr = heap_resize(ptr, size);
assert(ptr != nil);
return ptr;
return align_and_store_padding(ptr, alignment);
}
return nil;
*/
switch mode {
case .Alloc:
return heap_alloc(size);
case .Free:
if old_memory != nil {
heap_free(old_memory);
}
return nil;
case .Free_All:
// NOTE(bill): Does nothing
case .Resize:
return heap_resize(old_memory, size);
}
return nil;
+154 -5
View File
@@ -6,7 +6,7 @@ foreign import libc "system:c"
import "core:runtime"
import "core:strings"
OS :: "osx";
OS :: "darwin";
Handle :: distinct i32;
File_Time :: distinct u64;
@@ -14,6 +14,142 @@ Errno :: distinct int;
INVALID_HANDLE :: ~Handle(0);
ERROR_NONE: Errno : 0;
EPERM: Errno : 1; /* Operation not permitted */
ENOENT: Errno : 2; /* No such file or directory */
ESRCH: Errno : 3; /* No such process */
EINTR: Errno : 4; /* Interrupted system call */
EIO: Errno : 5; /* Input/output error */
ENXIO: Errno : 6; /* Device not configured */
E2BIG: Errno : 7; /* Argument list too long */
ENOEXEC: Errno : 8; /* Exec format error */
EBADF: Errno : 9; /* Bad file descriptor */
ECHILD: Errno : 10; /* No child processes */
EDEADLK: Errno : 11; /* Resource deadlock avoided */
ENOMEM: Errno : 12; /* Cannot allocate memory */
EACCES: Errno : 13; /* Permission denied */
EFAULT: Errno : 14; /* Bad address */
ENOTBLK: Errno : 15; /* Block device required */
EBUSY: Errno : 16; /* Device / Resource busy */
EEXIST: Errno : 17; /* File exists */
EXDEV: Errno : 18; /* Cross-device link */
ENODEV: Errno : 19; /* Operation not supported by device */
ENOTDIR: Errno : 20; /* Not a directory */
EISDIR: Errno : 21; /* Is a directory */
EINVAL: Errno : 22; /* Invalid argument */
ENFILE: Errno : 23; /* Too many open files in system */
EMFILE: Errno : 24; /* Too many open files */
ENOTTY: Errno : 25; /* Inappropriate ioctl for device */
ETXTBSY: Errno : 26; /* Text file busy */
EFBIG: Errno : 27; /* File too large */
ENOSPC: Errno : 28; /* No space left on device */
ESPIPE: Errno : 29; /* Illegal seek */
EROFS: Errno : 30; /* Read-only file system */
EMLINK: Errno : 31; /* Too many links */
EPIPE: Errno : 32; /* Broken pipe */
/* math software */
EDOM: Errno : 33; /* Numerical argument out of domain */
ERANGE: Errno : 34; /* Result too large */
/* non-blocking and interrupt i/o */
EAGAIN: Errno : 35; /* Resource temporarily unavailable */
EWOULDBLOCK: Errno : EAGAIN; /* Operation would block */
EINPROGRESS: Errno : 36; /* Operation now in progress */
EALREADY: Errno : 37; /* Operation already in progress */
/* ipc/network software -- argument errors */
ENOTSOCK: Errno : 38; /* Socket operation on non-socket */
EDESTADDRREQ: Errno : 39; /* Destination address required */
EMSGSIZE: Errno : 40; /* Message too long */
EPROTOTYPE: Errno : 41; /* Protocol wrong type for socket */
ENOPROTOOPT: Errno : 42; /* Protocol not available */
EPROTONOSUPPORT: Errno : 43; /* Protocol not supported */
ESOCKTNOSUPPORT: Errno : 44; /* Socket type not supported */
ENOTSUP: Errno : 45; /* Operation not supported */
EPFNOSUPPORT: Errno : 46; /* Protocol family not supported */
EAFNOSUPPORT: Errno : 47; /* Address family not supported by protocol family */
EADDRINUSE: Errno : 48; /* Address already in use */
EADDRNOTAVAIL: Errno : 49; /* Can't assign requested address */
/* ipc/network software -- operational errors */
ENETDOWN: Errno : 50; /* Network is down */
ENETUNREACH: Errno : 51; /* Network is unreachable */
ENETRESET: Errno : 52; /* Network dropped connection on reset */
ECONNABORTED: Errno : 53; /* Software caused connection abort */
ECONNRESET: Errno : 54; /* Connection reset by peer */
ENOBUFS: Errno : 55; /* No buffer space available */
EISCONN: Errno : 56; /* Socket is already connected */
ENOTCONN: Errno : 57; /* Socket is not connected */
ESHUTDOWN: Errno : 58; /* Can't send after socket shutdown */
ETOOMANYREFS: Errno : 59; /* Too many references: can't splice */
ETIMEDOUT: Errno : 60; /* Operation timed out */
ECONNREFUSED: Errno : 61; /* Connection refused */
ELOOP: Errno : 62; /* Too many levels of symbolic links */
ENAMETOOLONG: Errno : 63; /* File name too long */
/* should be rearranged */
EHOSTDOWN: Errno : 64; /* Host is down */
EHOSTUNREACH: Errno : 65; /* No route to host */
ENOTEMPTY: Errno : 66; /* Directory not empty */
/* quotas & mush */
EPROCLIM: Errno : 67; /* Too many processes */
EUSERS: Errno : 68; /* Too many users */
EDQUOT: Errno : 69; /* Disc quota exceeded */
/* Network File System */
ESTALE: Errno : 70; /* Stale NFS file handle */
EREMOTE: Errno : 71; /* Too many levels of remote in path */
EBADRPC: Errno : 72; /* RPC struct is bad */
ERPCMISMATCH: Errno : 73; /* RPC version wrong */
EPROGUNAVAIL: Errno : 74; /* RPC prog. not avail */
EPROGMISMATCH: Errno : 75; /* Program version wrong */
EPROCUNAVAIL: Errno : 76; /* Bad procedure for program */
ENOLCK: Errno : 77; /* No locks available */
ENOSYS: Errno : 78; /* Function not implemented */
EFTYPE: Errno : 79; /* Inappropriate file type or format */
EAUTH: Errno : 80; /* Authentication error */
ENEEDAUTH: Errno : 81; /* Need authenticator */
/* Intelligent device errors */
EPWROFF: Errno : 82; /* Device power is off */
EDEVERR: Errno : 83; /* Device error, e.g. paper out */
EOVERFLOW: Errno : 84; /* Value too large to be stored in data type */
/* Program loading errors */
EBADEXEC: Errno : 85; /* Bad executable */
EBADARCH: Errno : 86; /* Bad CPU type in executable */
ESHLIBVERS: Errno : 87; /* Shared library version mismatch */
EBADMACHO: Errno : 88; /* Malformed Macho file */
ECANCELED: Errno : 89; /* Operation canceled */
EIDRM: Errno : 90; /* Identifier removed */
ENOMSG: Errno : 91; /* No message of desired type */
EILSEQ: Errno : 92; /* Illegal byte sequence */
ENOATTR: Errno : 93; /* Attribute not found */
EBADMSG: Errno : 94; /* Bad message */
EMULTIHOP: Errno : 95; /* Reserved */
ENODATA: Errno : 96; /* No message available on STREAM */
ENOLINK: Errno : 97; /* Reserved */
ENOSR: Errno : 98; /* No STREAM resources */
ENOSTR: Errno : 99; /* Not a STREAM */
EPROTO: Errno : 100; /* Protocol error */
ETIME: Errno : 101; /* STREAM ioctl timeout */
ENOPOLICY: Errno : 103; /* No such policy registered */
ENOTRECOVERABLE: Errno : 104; /* State not recoverable */
EOWNERDEAD: Errno : 105; /* Previous owner died */
EQFULL: Errno : 106; /* Interface output queue is full */
ELAST: Errno : 106; /* Must be equal largest errno */
O_RDONLY :: 0x00000;
O_WRONLY :: 0x00001;
O_RDWR :: 0x00002;
@@ -133,6 +269,7 @@ foreign libc {
@(link_name="write") _unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> int ---;
@(link_name="lseek") _unix_lseek :: proc(fs: Handle, offset: int, whence: int) -> int ---;
@(link_name="gettid") _unix_gettid :: proc() -> u64 ---;
@(link_name="getpagesize") _unix_getpagesize :: proc() -> i32 ---;
@(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^Stat) -> int ---;
@(link_name="access") _unix_access :: proc(path: cstring, mask: int) -> int ---;
@@ -169,6 +306,9 @@ close :: proc(fd: Handle) {
write :: proc(fd: Handle, data: []u8) -> (int, Errno) {
assert(fd != -1);
if len(data) == 0 {
return 0, 0;
}
bytes_written := _unix_write(fd, &data[0], len(data));
if(bytes_written == -1) {
return 0, 1;
@@ -258,7 +398,6 @@ exit :: inline proc(code: int) -> ! {
_unix_exit(code);
}
current_thread_id :: proc "contextless" () -> int {
// return int(_unix_gettid());
return 0;
@@ -285,11 +424,21 @@ dlerror :: proc() -> string {
return string(_unix_dlerror());
}
get_page_size :: proc() -> int {
// NOTE(tetra): The page size never changes, so why do anything complicated
// if we don't have to.
@static page_size := -1;
if page_size != -1 do return page_size;
page_size = int(_unix_getpagesize());
return page_size;
}
_alloc_command_line_arguments :: proc() -> []string {
args := make([]string, len(runtime.args__));
res := make([]string, len(runtime.args__));
for arg, i in runtime.args__ {
args[i] = string(arg);
res[i] = string(arg);
}
return args;
return res;
}
+2422 -162
View File
File diff suppressed because it is too large Load Diff
+150 -74
View File
@@ -15,37 +15,138 @@ Syscall :: distinct int;
INVALID_HANDLE :: ~Handle(0);
ERROR_NONE: Errno : 0;
EPERM: Errno : 1;
ENOENT: Errno : 2;
EINTR: Errno : 4;
EIO: Errno : 5;
ENXIO: Errno : 6;
EBADF: Errno : 9;
EAGAIN: Errno : 11;
EWOULDBLOCK: Errno : EAGAIN;
ENOMEM: Errno : 12;
EACCES: Errno : 13;
EFAULT: Errno : 14;
EEXIST: Errno : 17;
ENODEV: Errno : 19;
ENOTDIR: Errno : 20;
EISDIR: Errno : 21;
EINVAL: Errno : 22;
ENFILE: Errno : 23;
EMFILE: Errno : 24;
ETXTBSY: Errno : 26;
EFBIG: Errno : 27;
ENOSPC: Errno : 28;
ESPIPE: Errno : 29;
EROFS: Errno : 30;
EPIPE: Errno : 32;
ENAMETOOLONG: Errno : 36;
ELOOP: Errno : 40;
EOVERFLOW: Errno : 75;
EDESTADDRREQ: Errno : 89;
EOPNOTSUPP: Errno : 95;
EDQUOT: Errno : 122;
ERROR_NONE: Errno : 0;
EPERM: Errno : 1;
ENOENT: Errno : 2;
ESRCH: Errno : 3;
EINTR: Errno : 4;
EIO: Errno : 5;
ENXIO: Errno : 6;
EBADF: Errno : 9;
EAGAIN: Errno : 11;
ENOMEM: Errno : 12;
EACCES: Errno : 13;
EFAULT: Errno : 14;
EEXIST: Errno : 17;
ENODEV: Errno : 19;
ENOTDIR: Errno : 20;
EISDIR: Errno : 21;
EINVAL: Errno : 22;
ENFILE: Errno : 23;
EMFILE: Errno : 24;
ETXTBSY: Errno : 26;
EFBIG: Errno : 27;
ENOSPC: Errno : 28;
ESPIPE: Errno : 29;
EROFS: Errno : 30;
EPIPE: Errno : 32;
EDEADLK: Errno : 35; /* Resource deadlock would occur */
ENAMETOOLONG: Errno : 36; /* File name too long */
ENOLCK: Errno : 37; /* No record locks available */
ENOSYS: Errno : 38; /* Invalid system call number */
ENOTEMPTY: Errno : 39; /* Directory not empty */
ELOOP: Errno : 40; /* Too many symbolic links encountered */
EWOULDBLOCK: Errno : EAGAIN; /* Operation would block */
ENOMSG: Errno : 42; /* No message of desired type */
EIDRM: Errno : 43; /* Identifier removed */
ECHRNG: Errno : 44; /* Channel number out of range */
EL2NSYNC: Errno : 45; /* Level 2 not synchronized */
EL3HLT: Errno : 46; /* Level 3 halted */
EL3RST: Errno : 47; /* Level 3 reset */
ELNRNG: Errno : 48; /* Link number out of range */
EUNATCH: Errno : 49; /* Protocol driver not attached */
ENOCSI: Errno : 50; /* No CSI structure available */
EL2HLT: Errno : 51; /* Level 2 halted */
EBADE: Errno : 52; /* Invalid exchange */
EBADR: Errno : 53; /* Invalid request descriptor */
EXFULL: Errno : 54; /* Exchange full */
ENOANO: Errno : 55; /* No anode */
EBADRQC: Errno : 56; /* Invalid request code */
EBADSLT: Errno : 57; /* Invalid slot */
EDEADLOCK: Errno : EDEADLK;
EBFONT: Errno : 59; /* Bad font file format */
ENOSTR: Errno : 60; /* Device not a stream */
ENODATA: Errno : 61; /* No data available */
ETIME: Errno : 62; /* Timer expired */
ENOSR: Errno : 63; /* Out of streams resources */
ENONET: Errno : 64; /* Machine is not on the network */
ENOPKG: Errno : 65; /* Package not installed */
EREMOTE: Errno : 66; /* Object is remote */
ENOLINK: Errno : 67; /* Link has been severed */
EADV: Errno : 68; /* Advertise error */
ESRMNT: Errno : 69; /* Srmount error */
ECOMM: Errno : 70; /* Communication error on send */
EPROTO: Errno : 71; /* Protocol error */
EMULTIHOP: Errno : 72; /* Multihop attempted */
EDOTDOT: Errno : 73; /* RFS specific error */
EBADMSG: Errno : 74; /* Not a data message */
EOVERFLOW: Errno : 75; /* Value too large for defined data type */
ENOTUNIQ: Errno : 76; /* Name not unique on network */
EBADFD: Errno : 77; /* File descriptor in bad state */
EREMCHG: Errno : 78; /* Remote address changed */
ELIBACC: Errno : 79; /* Can not access a needed shared library */
ELIBBAD: Errno : 80; /* Accessing a corrupted shared library */
ELIBSCN: Errno : 81; /* .lib section in a.out corrupted */
ELIBMAX: Errno : 82; /* Attempting to link in too many shared libraries */
ELIBEXEC: Errno : 83; /* Cannot exec a shared library directly */
EILSEQ: Errno : 84; /* Illegal byte sequence */
ERESTART: Errno : 85; /* Interrupted system call should be restarted */
ESTRPIPE: Errno : 86; /* Streams pipe error */
EUSERS: Errno : 87; /* Too many users */
ENOTSOCK: Errno : 88; /* Socket operation on non-socket */
EDESTADDRREQ: Errno : 89; /* Destination address required */
EMSGSIZE: Errno : 90; /* Message too long */
EPROTOTYPE: Errno : 91; /* Protocol wrong type for socket */
ENOPROTOOPT: Errno : 92; /* Protocol not available */
EPROTONOSUPPORT: Errno : 93; /* Protocol not supported */
ESOCKTNOSUPPORT: Errno : 94; /* Socket type not supported */
EOPNOTSUPP: Errno : 95; /* Operation not supported on transport endpoint */
EPFNOSUPPORT: Errno : 96; /* Protocol family not supported */
EAFNOSUPPORT: Errno : 97; /* Address family not supported by protocol */
EADDRINUSE: Errno : 98; /* Address already in use */
EADDRNOTAVAIL: Errno : 99; /* Cannot assign requested address */
ENETDOWN: Errno : 100; /* Network is down */
ENETUNREACH: Errno : 101; /* Network is unreachable */
ENETRESET: Errno : 102; /* Network dropped connection because of reset */
ECONNABORTED: Errno : 103; /* Software caused connection abort */
ECONNRESET: Errno : 104; /* Connection reset by peer */
ENOBUFS: Errno : 105; /* No buffer space available */
EISCONN: Errno : 106; /* Transport endpoint is already connected */
ENOTCONN: Errno : 107; /* Transport endpoint is not connected */
ESHUTDOWN: Errno : 108; /* Cannot send after transport endpoint shutdown */
ETOOMANYREFS: Errno : 109; /* Too many references: cannot splice */
ETIMEDOUT: Errno : 110; /* Connection timed out */
ECONNREFUSED: Errno : 111; /* Connection refused */
EHOSTDOWN: Errno : 112; /* Host is down */
EHOSTUNREACH: Errno : 113; /* No route to host */
EALREADY: Errno : 114; /* Operation already in progress */
EINPROGRESS: Errno : 115; /* Operation now in progress */
ESTALE: Errno : 116; /* Stale file handle */
EUCLEAN: Errno : 117; /* Structure needs cleaning */
ENOTNAM: Errno : 118; /* Not a XENIX named type file */
ENAVAIL: Errno : 119; /* No XENIX semaphores available */
EISNAM: Errno : 120; /* Is a named type file */
EREMOTEIO: Errno : 121; /* Remote I/O error */
EDQUOT: Errno : 122; /* Quota exceeded */
ENOMEDIUM: Errno : 123; /* No medium found */
EMEDIUMTYPE: Errno : 124; /* Wrong medium type */
ECANCELED: Errno : 125; /* Operation Canceled */
ENOKEY: Errno : 126; /* Required key not available */
EKEYEXPIRED: Errno : 127; /* Key has expired */
EKEYREVOKED: Errno : 128; /* Key has been revoked */
EKEYREJECTED: Errno : 129; /* Key was rejected by service */
/* for robust mutexes */
EOWNERDEAD: Errno : 130; /* Owner died */
ENOTRECOVERABLE: Errno : 131; /* State not recoverable */
ERFKILL: Errno : 132; /* Operation not possible due to RF-kill */
EHWPOISON: Errno : 133; /* Memory page has hardware error */
O_RDONLY :: 0x00000;
O_WRONLY :: 0x00001;
@@ -152,34 +253,19 @@ X_OK :: 1; // Test for execute permission
W_OK :: 2; // Test for write permission
R_OK :: 4; // Test for read permission
TimeSpec :: struct {
tv_sec : i64, /* seconds */
tv_nsec : i64, /* nanoseconds */
};
CLOCK_REALTIME :: 0;
CLOCK_MONOTONIC :: 1;
CLOCK_PROCESS_CPUTIME_ID :: 2;
CLOCK_THREAD_CPUTIME_ID :: 3;
CLOCK_MONOTONIC_RAW :: 4;
CLOCK_REALTIME_COARSE :: 5;
CLOCK_MONOTONIC_COARSE :: 6;
CLOCK_BOOTTIME :: 7;
CLOCK_REALTIME_ALARM :: 8;
CLOCK_BOOTTIME_ALARM :: 9;
SYS_GETTID: Syscall : 186;
foreign libc {
@(link_name="__errno_location") __errno_location :: proc() -> ^int ---;
@(link_name="syscall") syscall :: proc(number: Syscall, #c_vararg args: ..any) -> int ---;
@(link_name="open") _unix_open :: proc(path: cstring, flags: int, #c_vararg mode: ..any) -> Handle ---;
@(link_name="open") _unix_open :: proc(path: cstring, flags: int, mode: int) -> Handle ---;
@(link_name="close") _unix_close :: proc(fd: Handle) -> int ---;
@(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: int) -> int ---;
@(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: int) -> int ---;
@(link_name="lseek64") _unix_seek :: proc(fd: Handle, offset: i64, whence: i32) -> i64 ---;
@(link_name="gettid") _unix_gettid :: proc() -> u64 ---;
@(link_name="getpagesize") _unix_getpagesize :: proc() -> i32 ---;
@(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^Stat) -> int ---;
@(link_name="fstat") _unix_fstat :: proc(fd: Handle, stat: ^Stat) -> int ---;
@(link_name="access") _unix_access :: proc(path: cstring, mask: int) -> int ---;
@@ -190,10 +276,6 @@ foreign libc {
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr ---;
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---;
@(link_name="clock_gettime") _unix_clock_gettime :: proc(clock_id: u64, timespec: ^TimeSpec) ---;
@(link_name="nanosleep") _unix_nanosleep :: proc(requested: ^TimeSpec, remaining: ^TimeSpec) -> int ---;
@(link_name="sleep") _unix_sleep :: proc(seconds: u64) -> int ---;
@(link_name="exit") _unix_exit :: proc(status: int) -> ! ---;
}
foreign dl {
@@ -238,6 +320,9 @@ read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
}
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
if len(data) == 0 {
return 0, ERROR_NONE;
}
bytes_written := _unix_write(fd, &data[0], len(data));
if bytes_written == -1 {
return -1, Errno(get_last_error());
@@ -346,25 +431,6 @@ exit :: proc(code: int) -> ! {
_unix_exit(code);
}
clock_gettime :: proc(clock_id: u64) -> TimeSpec {
ts : TimeSpec;
_unix_clock_gettime(clock_id, &ts);
return ts;
}
sleep :: proc(seconds: u64) -> int {
return _unix_sleep(seconds);
}
nanosleep :: proc(nanoseconds: i64) -> int {
assert(nanoseconds <= 999999999);
requested, remaining : TimeSpec;
requested = TimeSpec{tv_nsec = nanoseconds};
return _unix_nanosleep(&requested, &remaining);
}
current_thread_id :: proc "contextless" () -> int {
return syscall(SYS_GETTID);
}
@@ -390,11 +456,21 @@ dlerror :: proc() -> string {
return string(_unix_dlerror());
}
get_page_size :: proc() -> int {
// NOTE(tetra): The page size never changes, so why do anything complicated
// if we don't have to.
@static page_size := -1;
if page_size != -1 do return page_size;
page_size = int(_unix_getpagesize());
return page_size;
}
_alloc_command_line_arguments :: proc() -> []string {
args := make([]string, len(runtime.args__));
res := make([]string, len(runtime.args__));
for arg, i in runtime.args__ {
args[i] = string(arg);
res[i] = string(arg);
}
return args;
return res;
}
+14 -3
View File
@@ -65,7 +65,7 @@ is_path_separator :: proc(r: rune) -> bool {
return r == '/' || r == '\\';
}
open :: proc(path: string, mode: int = O_RDONLY, perm: u32 = 0) -> (Handle, Errno) {
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) {
if len(path) == 0 do return INVALID_HANDLE, ERROR_FILE_NOT_FOUND;
access: u32;
@@ -210,7 +210,6 @@ get_std_handle :: proc(h: int) -> Handle {
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
file_info: win32.By_Handle_File_Information;
if !win32.get_file_information_by_handle(win32.Handle(fd), &file_info) {
@@ -253,6 +252,18 @@ heap_free :: proc(ptr: rawptr) {
win32.heap_free(win32.get_process_heap(), 0, ptr);
}
get_page_size :: proc() -> int {
// NOTE(tetra): The page size never changes, so why do anything complicated
// if we don't have to.
@static page_size := -1;
if page_size != -1 do return page_size;
info: win32.System_Info;
win32.get_system_info(&info);
page_size = int(info.page_size);
return page_size;
}
exit :: proc(code: int) -> ! {
win32.exit_process(u32(code));
@@ -322,4 +333,4 @@ is_windows_8_1 :: proc() -> bool {
is_windows_10 :: proc() -> bool {
osvi := get_windows_version_ansi();
return (osvi.major_version == 10 && osvi.minor_version == 0);
}
}
+398
View File
@@ -0,0 +1,398 @@
package reflect
import "core:runtime"
import "core:mem"
Type_Kind :: enum {
Invalid,
Named,
Integer,
Rune,
Float,
Complex,
Quaternion,
String,
Boolean,
Any,
Type_Id,
Pointer,
Procedure,
Array,
Enumerated_Array,
Dynamic_Array,
Slice,
Tuple,
Struct,
Union,
Enum,
Map,
Bit_Field,
Bit_Set,
Opaque,
Simd_Vector,
}
type_kind :: proc(T: typeid) -> Type_Kind {
ti := type_info_of(T);
if ti != nil {
switch _ in ti.variant {
case runtime.Type_Info_Named: return .Named;
case runtime.Type_Info_Integer: return .Integer;
case runtime.Type_Info_Rune: return .Rune;
case runtime.Type_Info_Float: return .Float;
case runtime.Type_Info_Complex: return .Complex;
case runtime.Type_Info_Quaternion: return .Quaternion;
case runtime.Type_Info_String: return .String;
case runtime.Type_Info_Boolean: return .Boolean;
case runtime.Type_Info_Any: return .Any;
case runtime.Type_Info_Type_Id: return .Type_Id;
case runtime.Type_Info_Pointer: return .Pointer;
case runtime.Type_Info_Procedure: return .Procedure;
case runtime.Type_Info_Array: return .Array;
case runtime.Type_Info_Enumerated_Array: return .Enumerated_Array;
case runtime.Type_Info_Dynamic_Array: return .Dynamic_Array;
case runtime.Type_Info_Slice: return .Slice;
case runtime.Type_Info_Tuple: return .Tuple;
case runtime.Type_Info_Struct: return .Struct;
case runtime.Type_Info_Union: return .Union;
case runtime.Type_Info_Enum: return .Enum;
case runtime.Type_Info_Map: return .Map;
case runtime.Type_Info_Bit_Field: return .Bit_Field;
case runtime.Type_Info_Bit_Set: return .Bit_Set;
case runtime.Type_Info_Opaque: return .Opaque;
case runtime.Type_Info_Simd_Vector: return .Simd_Vector;
}
}
return .Invalid;
}
// TODO(bill): Better name
underlying_type_kind :: proc(T: typeid) -> Type_Kind {
return type_kind(runtime.typeid_base(T));
}
// TODO(bill): Better name
backing_type_kind :: proc(T: typeid) -> Type_Kind {
return type_kind(runtime.typeid_core(T));
}
size_of_typeid :: proc(T: typeid) -> int {
if ti := type_info_of(T); ti != nil {
return ti.size;
}
return 0;
}
align_of_typeid :: proc(T: typeid) -> int {
if ti := type_info_of(T); ti != nil {
return ti.align;
}
return 1;
}
to_bytes :: proc(v: any) -> []byte {
if v != nil {
sz := size_of_typeid(v.id);
return mem.slice_ptr((^byte)(v.data), sz);
}
return nil;
}
any_data :: inline proc(v: any) -> (data: rawptr, id: typeid) {
return v.data, v.id;
}
is_nil :: proc(v: any) -> bool {
data := to_bytes(v);
if data != nil {
return true;
}
for v in data do if v != 0 {
return false;
}
return true;
}
length :: proc(val: any) -> int {
if val == nil do return 0;
v := val;
v.id = runtime.typeid_base(v.id);
switch a in v {
case runtime.Type_Info_Array:
return a.count;
case runtime.Type_Info_Slice:
return (^mem.Raw_Slice)(v.data).len;
case runtime.Type_Info_Dynamic_Array:
return (^mem.Raw_Dynamic_Array)(v.data).len;
case runtime.Type_Info_String:
if a.is_cstring {
return len((^cstring)(v.data)^);
} else {
return (^mem.Raw_String)(v.data).len;
}
}
return 0;
}
index :: proc(val: any, i: int, loc := #caller_location) -> any {
if val == nil do return nil;
v := val;
v.id = runtime.typeid_base(v.id);
switch a in v {
case runtime.Type_Info_Array:
runtime.bounds_check_error_loc(loc, i, a.count);
offset := uintptr(a.elem.size * i);
data := rawptr(uintptr(v.data) + offset);
return any{data, a.elem.id};
case runtime.Type_Info_Slice:
raw := (^mem.Raw_Slice)(v.data);
runtime.bounds_check_error_loc(loc, i, raw.len);
offset := uintptr(a.elem.size * i);
data := rawptr(uintptr(raw.data) + offset);
return any{data, a.elem.id};
case runtime.Type_Info_Dynamic_Array:
raw := (^mem.Raw_Dynamic_Array)(v.data);
runtime.bounds_check_error_loc(loc, i, raw.len);
offset := uintptr(a.elem.size * i);
data := rawptr(uintptr(raw.data) + offset);
return any{data, a.elem.id};
case runtime.Type_Info_String:
if a.is_cstring do return nil;
raw := (^mem.Raw_String)(v.data);
runtime.bounds_check_error_loc(loc, i, raw.len);
offset := uintptr(size_of(u8) * i);
data := rawptr(uintptr(raw.data) + offset);
return any{data, typeid_of(u8)};
}
return nil;
}
Struct_Tag :: distinct string;
Struct_Field :: struct {
name: string,
type: typeid,
tag: Struct_Tag,
offset: uintptr,
}
struct_field_at :: proc(T: typeid, i: int) -> (field: Struct_Field) {
ti := runtime.type_info_base(type_info_of(T));
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
if 0 <= i && i < len(s.names) {
field.name = s.names[i];
field.type = s.types[i].id;
field.tag = Struct_Tag(s.tags[i]);
field.offset = s.offsets[i];
}
}
return;
}
struct_field_by_name :: proc(T: typeid, name: string) -> (field: Struct_Field) {
ti := runtime.type_info_base(type_info_of(T));
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
for fname, i in s.names {
if fname == name {
field.name = s.names[i];
field.type = s.types[i].id;
field.tag = Struct_Tag(s.tags[i]);
field.offset = s.offsets[i];
break;
}
}
}
return;
}
struct_field_value_by_name :: proc(a: any, field: string, recurse := false) -> any {
if a == nil do return nil;
ti := runtime.type_info_base(type_info_of(a.id));
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
for name, i in s.names {
if name == field {
return any{
rawptr(uintptr(a.data) + s.offsets[i]),
s.types[i].id,
};
}
if recurse && s.usings[i] {
f := any{
rawptr(uintptr(a.data) + s.offsets[i]),
s.types[i].id,
};
if res := struct_field_value_by_name(f, field, recurse); res != nil {
return res;
}
}
}
}
return nil;
}
struct_field_names :: proc(T: typeid) -> []string {
ti := runtime.type_info_base(type_info_of(T));
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
return s.names;
}
return nil;
}
struct_field_types :: proc(T: typeid) -> []^runtime.Type_Info {
ti := runtime.type_info_base(type_info_of(T));
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
return s.types;
}
return nil;
}
struct_field_tags :: proc(T: typeid) -> []Struct_Tag {
ti := runtime.type_info_base(type_info_of(T));
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
return transmute([]Struct_Tag)s.tags;
}
return nil;
}
struct_field_offsets :: proc(T: typeid) -> []uintptr {
ti := runtime.type_info_base(type_info_of(T));
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
return s.offsets;
}
return nil;
}
struct_tag_get :: proc(tag: Struct_Tag, key: string) -> (value: string) {
value, _ = struct_tag_lookup(tag, key);
return;
}
struct_tag_lookup :: proc(tag: Struct_Tag, key: string) -> (value: string, ok: bool) {
for t := tag; t != ""; /**/ {
i := 0;
for i < len(t) && t[i] == ' ' { // Skip whitespace
i += 1;
}
t = t[i:];
if len(t) == 0 do break;
i = 0;
loop: for i < len(t) {
switch t[i] {
case ':', '"':
break loop;
case 0x00 ..< ' ', 0x7f .. 0x9f: // break if control character is found
break loop;
}
i += 1;
}
if i == 0 do break;
if i+1 >= len(t) do break;
if t[i] != ':' || t[i+1] != '"' {
break;
}
name := string(t[:i]);
t = t[i+1:];
i = 1;
for i < len(t) && t[i] != '"' { // find closing quote
if t[i] == '\\' do i += 1; // Skip escaped characters
i += 1;
}
if i >= len(t) do break;
val := string(t[:i+1]);
t = t[i+1:];
if key == name {
return val[1:i], true;
}
}
return;
}
enum_string :: proc(a: any) -> string {
if a == nil do return "";
ti := runtime.type_info_base(type_info_of(a.id));
if e, ok := ti.variant.(runtime.Type_Info_Enum); ok {
for _, i in e.values {
value := &e.values[i];
n := mem.compare_byte_ptrs((^byte)(a.data), (^byte)(value), ti.size);
if n == 0 {
return e.names[i];
}
}
} else {
panic("expected an enum to reflect.enum_string");
}
return "";
}
union_variant_type_info :: proc(a: any) -> ^runtime.Type_Info {
id := union_variant_typeid(a);
return type_info_of(id);
}
union_variant_typeid :: proc(a: any) -> typeid {
if a == nil do return nil;
ti := runtime.type_info_base(type_info_of(a.id));
if info, ok := ti.variant.(runtime.Type_Info_Union); ok {
tag_ptr := uintptr(a.data) + info.tag_offset;
tag_any := any{rawptr(tag_ptr), info.tag_type.id};
tag: i64 = ---;
switch i in tag_any {
case u8: tag = i64(i);
case i8: tag = i64(i);
case u16: tag = i64(i);
case i16: tag = i64(i);
case u32: tag = i64(i);
case i32: tag = i64(i);
case u64: tag = i64(i);
case i64: tag = i64(i);
case: unimplemented();
}
if a.data != nil && tag != 0 {
return info.variants[tag-1].id;
}
} else {
panic("expected a union to reflect.union_variant_typeid");
}
return nil;
}
@@ -1,6 +1,7 @@
package types
package reflect
import rt "core:runtime"
import "core:strings"
are_types_identical :: proc(a, b: ^rt.Type_Info) -> bool {
if a == b do return true;
@@ -39,6 +40,14 @@ are_types_identical :: proc(a, b: ^rt.Type_Info) -> bool {
_, ok := b.variant.(rt.Type_Info_Complex);
return ok;
case rt.Type_Info_Quaternion:
_, ok := b.variant.(rt.Type_Info_Quaternion);
return ok;
case rt.Type_Info_Type_Id:
_, ok := b.variant.(rt.Type_Info_Type_Id);
return ok;
case rt.Type_Info_String:
_, ok := b.variant.(rt.Type_Info_String);
return ok;
@@ -73,6 +82,13 @@ are_types_identical :: proc(a, b: ^rt.Type_Info) -> bool {
if x.count != y.count do return false;
return are_types_identical(x.elem, y.elem);
case rt.Type_Info_Enumerated_Array:
y, ok := b.variant.(rt.Type_Info_Enumerated_Array);
if !ok do return false;
if x.count != y.count do return false;
return are_types_identical(x.index, y.index) &&
are_types_identical(x.elem, y.elem);
case rt.Type_Info_Dynamic_Array:
y, ok := b.variant.(rt.Type_Info_Dynamic_Array);
if !ok do return false;
@@ -108,9 +124,11 @@ are_types_identical :: proc(a, b: ^rt.Type_Info) -> bool {
for _, i in x.types {
xn, yn := x.names[i], y.names[i];
xt, yt := x.types[i], y.types[i];
xl, yl := x.tags[i], y.tags[i];
if xn != yn do return false;
if !are_types_identical(xt, yt) do return false;
if xl != yl do return false;
}
return true;
@@ -159,6 +177,11 @@ are_types_identical :: proc(a, b: ^rt.Type_Info) -> bool {
y, ok := b.variant.(rt.Type_Info_Opaque);
if !ok do return false;
return x.elem == y.elem;
case rt.Type_Info_Simd_Vector:
y, ok := b.variant.(rt.Type_Info_Simd_Vector);
if !ok do return false;
return x.count == y.count && x.elem == y.elem;
}
return false;
@@ -166,7 +189,7 @@ are_types_identical :: proc(a, b: ^rt.Type_Info) -> bool {
is_signed :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
switch i in rt.type_info_base(info).variant {
#partial switch i in rt.type_info_base(info).variant {
case rt.Type_Info_Integer: return i.signed;
case rt.Type_Info_Float: return true;
}
@@ -272,3 +295,244 @@ is_simd_vector :: proc(info: ^rt.Type_Info) -> bool {
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Simd_Vector);
return ok;
}
write_typeid :: proc(buf: ^strings.Builder, id: typeid) {
write_type(buf, type_info_of(id));
}
write_type :: proc(buf: ^strings.Builder, ti: ^rt.Type_Info) {
using strings;
if ti == nil {
write_string(buf, "nil");
return;
}
switch info in ti.variant {
case rt.Type_Info_Named:
write_string(buf, info.name);
case rt.Type_Info_Integer:
switch ti.id {
case int: write_string(buf, "int");
case uint: write_string(buf, "uint");
case uintptr: write_string(buf, "uintptr");
case:
write_byte(buf, info.signed ? 'i' : 'u');
write_i64(buf, i64(8*ti.size), 10);
switch info.endianness {
case .Platform: // Okay
case .Little: write_string(buf, "le");
case .Big: write_string(buf, "be");
}
}
case rt.Type_Info_Rune:
write_string(buf, "rune");
case rt.Type_Info_Float:
write_byte(buf, 'f');
write_i64(buf, i64(8*ti.size), 10);
case rt.Type_Info_Complex:
write_string(buf, "complex");
write_i64(buf, i64(8*ti.size), 10);
case rt.Type_Info_Quaternion:
write_string(buf, "quaternion");
write_i64(buf, i64(8*ti.size), 10);
case rt.Type_Info_String:
if info.is_cstring {
write_string(buf, "cstring");
} else {
write_string(buf, "string");
}
case rt.Type_Info_Boolean:
switch ti.id {
case bool: write_string(buf, "bool");
case:
write_byte(buf, 'b');
write_i64(buf, i64(8*ti.size), 10);
}
case rt.Type_Info_Any:
write_string(buf, "any");
case rt.Type_Info_Type_Id:
write_string(buf, "typeid");
case rt.Type_Info_Pointer:
if info.elem == nil {
write_string(buf, "rawptr");
} else {
write_string(buf, "^");
write_type(buf, info.elem);
}
case rt.Type_Info_Procedure:
write_string(buf, "proc");
if info.params == nil {
write_string(buf, "()");
} else {
t := info.params.variant.(rt.Type_Info_Tuple);
write_string(buf, "(");
for t, i in t.types {
if i > 0 do write_string(buf, ", ");
write_type(buf, t);
}
write_string(buf, ")");
}
if info.results != nil {
write_string(buf, " -> ");
write_type(buf, info.results);
}
case rt.Type_Info_Tuple:
count := len(info.names);
if count != 1 do write_string(buf, "(");
for name, i in info.names {
if i > 0 do write_string(buf, ", ");
t := info.types[i];
if len(name) > 0 {
write_string(buf, name);
write_string(buf, ": ");
}
write_type(buf, t);
}
if count != 1 do write_string(buf, ")");
case rt.Type_Info_Array:
write_string(buf, "[");
write_i64(buf, i64(info.count), 10);
write_string(buf, "]");
write_type(buf, info.elem);
case rt.Type_Info_Enumerated_Array:
write_string(buf, "[");
write_type(buf, info.index);
write_string(buf, "]");
write_type(buf, info.elem);
case rt.Type_Info_Dynamic_Array:
write_string(buf, "[dynamic]");
write_type(buf, info.elem);
case rt.Type_Info_Slice:
write_string(buf, "[]");
write_type(buf, info.elem);
case rt.Type_Info_Map:
write_string(buf, "map[");
write_type(buf, info.key);
write_byte(buf, ']');
write_type(buf, info.value);
case rt.Type_Info_Struct:
switch info.soa_kind {
case .None: // Ignore
case .Fixed:
write_string(buf, "#soa[");
write_i64(buf, i64(info.soa_len));
write_byte(buf, ']');
write_type(buf, info.soa_base_type);
return;
case .Slice:
write_string(buf, "#soa[]");
write_type(buf, info.soa_base_type);
return;
case .Dynamic:
write_string(buf, "#soa[dynamic]");
write_type(buf, info.soa_base_type);
return;
}
write_string(buf, "struct ");
if info.is_packed do write_string(buf, "#packed ");
if info.is_raw_union do write_string(buf, "#raw_union ");
if info.custom_align {
write_string(buf, "#align ");
write_i64(buf, i64(ti.align), 10);
write_byte(buf, ' ');
}
write_byte(buf, '{');
for name, i in info.names {
if i > 0 do write_string(buf, ", ");
write_string(buf, name);
write_string(buf, ": ");
write_type(buf, info.types[i]);
}
write_byte(buf, '}');
case rt.Type_Info_Union:
write_string(buf, "union ");
if info.custom_align {
write_string(buf, "#align ");
write_i64(buf, i64(ti.align), 10);
write_byte(buf, ' ');
}
write_byte(buf, '{');
for variant, i in info.variants {
if i > 0 do write_string(buf, ", ");
write_type(buf, variant);
}
write_byte(buf, '}');
case rt.Type_Info_Enum:
write_string(buf, "enum ");
write_type(buf, info.base);
write_string(buf, " {");
for name, i in info.names {
if i > 0 do write_string(buf, ", ");
write_string(buf, name);
}
write_byte(buf, '}');
case rt.Type_Info_Bit_Field:
write_string(buf, "bit_field ");
if ti.align != 1 {
write_string(buf, "#align ");
write_i64(buf, i64(ti.align), 10);
write_byte(buf, ' ');
}
write_string(buf, " {");
for name, i in info.names {
if i > 0 do write_string(buf, ", ");
write_string(buf, name);
write_string(buf, ": ");
write_i64(buf, i64(info.bits[i]), 10);
}
write_byte(buf, '}');
case rt.Type_Info_Bit_Set:
write_string(buf, "bit_set[");
switch {
case is_enum(info.elem):
write_type(buf, info.elem);
case is_rune(info.elem):
write_encoded_rune(buf, rune(info.lower));
write_string(buf, "..");
write_encoded_rune(buf, rune(info.upper));
case:
write_i64(buf, info.lower, 10);
write_string(buf, "..");
write_i64(buf, info.upper, 10);
}
if info.underlying != nil {
write_string(buf, "; ");
write_type(buf, info.underlying);
}
write_byte(buf, ']');
case rt.Type_Info_Opaque:
write_string(buf, "opaque ");
write_type(buf, info.elem);
case rt.Type_Info_Simd_Vector:
if info.is_x86_mmx {
write_string(buf, "intrinsics.x86_mmx");
} else {
write_string(buf, "#simd[");
write_i64(buf, i64(info.count));
write_byte(buf, ']');
write_type(buf, info.elem);
}
}
}
+525 -121
View File
File diff suppressed because it is too large Load Diff
+143
View File
@@ -0,0 +1,143 @@
package runtime
import "core:os"
default_allocator_proc :: os.heap_allocator_proc;
default_allocator :: proc() -> Allocator {
return os.heap_allocator();
}
Default_Temp_Allocator :: struct {
data: []byte,
curr_offset: int,
prev_offset: int,
backup_allocator: Allocator,
leaked_allocations: [dynamic]rawptr,
}
default_temp_allocator_init :: proc(allocator: ^Default_Temp_Allocator, data: []byte, backup_allocator := context.allocator) {
allocator.data = data;
allocator.curr_offset = 0;
allocator.prev_offset = 0;
allocator.backup_allocator = backup_allocator;
allocator.leaked_allocations.allocator = backup_allocator;
}
default_temp_allocator_destroy :: proc(using allocator: ^Default_Temp_Allocator) {
if allocator == nil {
return;
}
for ptr in leaked_allocations {
free(ptr, backup_allocator);
}
delete(leaked_allocations);
delete(data, backup_allocator);
allocator^ = {};
}
default_temp_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 {
allocator := (^Default_Temp_Allocator)(allocator_data);
if allocator.data == nil {
DEFAULT_SCRATCH_BACKING_SIZE :: 1<<22;
a := context.allocator;
if !(context.allocator.procedure != default_temp_allocator_proc &&
context.allocator.data != allocator_data) {
a = default_allocator();
}
default_temp_allocator_init(allocator, make([]byte, 1<<22, a), a);
}
switch mode {
case .Alloc:
switch {
case allocator.curr_offset+size <= len(allocator.data):
offset := align_forward_uintptr(uintptr(allocator.curr_offset), uintptr(alignment));
ptr := &allocator.data[offset];
mem_zero(ptr, size);
allocator.prev_offset = int(offset);
allocator.curr_offset = int(offset) + size;
return ptr;
case size <= len(allocator.data):
offset := align_forward_uintptr(uintptr(0), uintptr(alignment));
ptr := &allocator.data[offset];
mem_zero(ptr, size);
allocator.prev_offset = int(offset);
allocator.curr_offset = int(offset) + size;
return ptr;
}
// TODO(bill): Should leaks be notified about? Should probably use a logging system that is built into the context system
a := allocator.backup_allocator;
if a.procedure == nil {
a = context.allocator;
allocator.backup_allocator = a;
}
ptr := mem_alloc(size, alignment, a, loc);
if allocator.leaked_allocations == nil {
allocator.leaked_allocations = make([dynamic]rawptr, a);
}
append(&allocator.leaked_allocations, ptr);
return ptr;
case .Free:
if len(allocator.data) == 0 {
return nil;
}
last_ptr := rawptr(&allocator.data[allocator.prev_offset]);
if old_memory == last_ptr {
full_size := allocator.curr_offset - allocator.prev_offset;
allocator.curr_offset = allocator.prev_offset;
mem_zero(last_ptr, full_size);
return nil;
} else {
#no_bounds_check start, end := &allocator.data[0], &allocator.data[allocator.curr_offset];
if start <= old_memory && old_memory < end {
// NOTE(bill): Cannot free this pointer
return nil;
}
if len(allocator.leaked_allocations) != 0 {
for ptr, i in allocator.leaked_allocations {
if ptr == old_memory {
free(ptr, allocator.backup_allocator);
ordered_remove(&allocator.leaked_allocations, i);
return nil;
}
}
}
}
// NOTE(bill): It's a temporary memory, don't worry about freeing
case .Free_All:
allocator.curr_offset = 0;
allocator.prev_offset = 0;
for ptr in allocator.leaked_allocations {
free(ptr, allocator.backup_allocator);
}
clear(&allocator.leaked_allocations);
case .Resize:
last_ptr := rawptr(&allocator.data[allocator.prev_offset]);
if old_memory == last_ptr && len(allocator.data)-allocator.prev_offset >= size {
allocator.curr_offset = allocator.prev_offset+size;
return old_memory;
}
return default_temp_allocator_proc(allocator_data, Allocator_Mode.Alloc, size, alignment, old_memory, old_size, flags, loc);
}
return nil;
}
default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator {
return Allocator{
procedure = default_temp_allocator_proc,
data = allocator,
};
}
+402 -26
View File
@@ -1,16 +1,138 @@
package runtime
import "core:mem"
import "core:os"
import "core:unicode/utf8"
ptr_offset :: inline proc "contextless" (ptr: $P/^$T, n: int) -> P {
new := int(uintptr(ptr)) + size_of(T)*n;
return P(uintptr(new));
}
is_power_of_two_int :: inline proc(x: int) -> bool {
if x <= 0 do return false;
return (x & (x-1)) == 0;
}
align_forward_int :: inline proc(ptr, align: int) -> int {
assert(is_power_of_two_int(align));
p := ptr;
modulo := p & (align-1);
if modulo != 0 do p += align - modulo;
return p;
}
is_power_of_two_uintptr :: inline proc(x: uintptr) -> bool {
if x <= 0 do return false;
return (x & (x-1)) == 0;
}
align_forward_uintptr :: inline proc(ptr, align: uintptr) -> uintptr {
assert(is_power_of_two_uintptr(align));
p := ptr;
modulo := p & (align-1);
if modulo != 0 do p += align - modulo;
return p;
}
mem_zero :: proc "contextless" (data: rawptr, len: int) -> rawptr {
if data == nil do return nil;
if len < 0 do return data;
when !#defined(memset) {
foreign _ {
when size_of(rawptr) == 8 {
@(link_name="llvm.memset.p0i8.i64")
memset :: proc(dst: rawptr, val: byte, len: int, align: i32 = 1, is_volatile: bool = false) ---;
} else {
@(link_name="llvm.memset.p0i8.i32")
memset :: proc(dst: rawptr, val: byte, len: int, align: i32 = 1, is_volatile: bool = false) ---;
}
}
}
memset(data, 0, len);
return data;
}
mem_copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
if src == nil do return dst;
// NOTE(bill): This _must_ be implemented like C's memmove
foreign _ {
when size_of(rawptr) == 8 {
@(link_name="llvm.memmove.p0i8.p0i8.i64")
llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
} else {
@(link_name="llvm.memmove.p0i8.p0i8.i32")
llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
}
}
llvm_memmove(dst, src, len, 1, false);
return dst;
}
mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
if src == nil do return dst;
// NOTE(bill): This _must_ be implemented like C's memcpy
foreign _ {
when size_of(rawptr) == 8 {
@(link_name="llvm.memcpy.p0i8.p0i8.i64")
llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
} else {
@(link_name="llvm.memcpy.p0i8.p0i8.i32")
llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
}
}
llvm_memcpy(dst, src, len, 1, false);
return dst;
}
DEFAULT_ALIGNMENT :: 2*align_of(rawptr);
mem_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, .Alloc, size, alignment, nil, 0, 0, loc);
}
mem_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, .Free, 0, 0, ptr, 0, 0, loc);
}
mem_free_all :: inline proc(allocator := context.allocator, loc := #caller_location) {
if allocator.procedure != nil {
allocator.procedure(allocator.data, .Free_All, 0, 0, nil, 0, 0, loc);
}
}
mem_resize :: inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
switch {
case allocator.procedure == nil:
return nil;
case new_size == 0:
allocator.procedure(allocator.data, .Free, 0, 0, ptr, 0, 0, loc);
return nil;
case ptr == nil:
return allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, 0, loc);
}
return allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, 0, loc);
}
print_u64 :: proc(fd: os.Handle, u: u64) {
print_u64 :: proc(fd: os.Handle, x: u64) {
digits := "0123456789";
a: [129]byte;
i := len(a);
b := u64(10);
u := x;
for u >= b {
i -= 1; a[i] = digits[u % b];
u /= b;
@@ -20,10 +142,11 @@ print_u64 :: proc(fd: os.Handle, u: u64) {
os.write(fd, a[i:]);
}
print_i64 :: proc(fd: os.Handle, u: i64) {
print_i64 :: proc(fd: os.Handle, x: i64) {
digits := "0123456789";
b :: i64(10);
u := x;
neg := u < 0;
u = abs(u);
@@ -79,6 +202,9 @@ print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
case Type_Info_Complex:
os.write_string(fd, "complex");
print_u64(fd, u64(8*ti.size));
case Type_Info_Quaternion:
os.write_string(fd, "quaternion");
print_u64(fd, u64(8*ti.size));
case Type_Info_String:
os.write_string(fd, "string");
case Type_Info_Boolean:
@@ -106,7 +232,7 @@ print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
os.write_string(fd, "()");
} else {
t := info.params.variant.(Type_Info_Tuple);
os.write_string(fd, "(");
os.write_byte(fd, '(');
for t, i in t.types {
if i > 0 do os.write_string(fd, ", ");
print_type(fd, t);
@@ -119,7 +245,7 @@ print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
}
case Type_Info_Tuple:
count := len(info.names);
if count != 1 do os.write_string(fd, "(");
if count != 1 do os.write_byte(fd, '(');
for name, i in info.names {
if i > 0 do os.write_string(fd, ", ");
@@ -134,10 +260,18 @@ print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
if count != 1 do os.write_string(fd, ")");
case Type_Info_Array:
os.write_string(fd, "[");
os.write_byte(fd, '[');
print_u64(fd, u64(info.count));
os.write_string(fd, "]");
os.write_byte(fd, ']');
print_type(fd, info.elem);
case Type_Info_Enumerated_Array:
os.write_byte(fd, '[');
print_type(fd, info.index);
os.write_byte(fd, ']');
print_type(fd, info.elem);
case Type_Info_Dynamic_Array:
os.write_string(fd, "[dynamic]");
print_type(fd, info.elem);
@@ -152,6 +286,24 @@ print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
print_type(fd, info.value);
case Type_Info_Struct:
switch info.soa_kind {
case .None: // Ignore
case .Fixed:
os.write_string(fd, "#soa[");
print_u64(fd, u64(info.soa_len));
os.write_byte(fd, ']');
print_type(fd, info.soa_base_type);
return;
case .Slice:
os.write_string(fd, "#soa[]");
print_type(fd, info.soa_base_type);
return;
case .Dynamic:
os.write_string(fd, "#soa[dynamic]");
print_type(fd, info.soa_base_type);
return;
}
os.write_string(fd, "struct ");
if info.is_packed do os.write_string(fd, "#packed ");
if info.is_raw_union do os.write_string(fd, "#raw_union ");
@@ -170,7 +322,15 @@ print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
os.write_byte(fd, '}');
case Type_Info_Union:
os.write_string(fd, "union {");
os.write_string(fd, "union ");
if info.custom_align {
os.write_string(fd, "#align ");
print_u64(fd, u64(ti.align));
}
if info.no_nil {
os.write_string(fd, "#no_nil ");
}
os.write_byte(fd, '{');
for variant, i in info.variants {
if i > 0 do os.write_string(fd, ", ");
print_type(fd, variant);
@@ -206,7 +366,7 @@ print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
case Type_Info_Bit_Set:
os.write_string(fd, "bit_set[");
switch elem in type_info_base(info.elem).variant {
#partial switch elem in type_info_base(info.elem).variant {
case Type_Info_Enum:
print_type(fd, info.elem);
case Type_Info_Rune:
@@ -232,26 +392,101 @@ print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
if info.is_x86_mmx {
os.write_string(fd, "intrinsics.x86_mmx");
} else {
os.write_string(fd, "intrinsics.vector(");
os.write_string(fd, "#simd[");
print_u64(fd, u64(info.count));
os.write_string(fd, ", ");
os.write_byte(fd, ']');
print_type(fd, info.elem);
os.write_byte(fd, ')');
}
}
}
memory_compare :: proc "contextless" (a, b: rawptr, n: int) -> int #no_bounds_check {
x := uintptr(a);
y := uintptr(b);
n := uintptr(n);
SU :: size_of(uintptr);
fast := uintptr(n/SU + 1);
offset := (fast-1)*SU;
curr_block := uintptr(0);
if n < SU {
fast = 0;
}
for /**/; curr_block < fast; curr_block += 1 {
va := (^uintptr)(x + curr_block * size_of(uintptr))^;
vb := (^uintptr)(y + curr_block * size_of(uintptr))^;
if va ~ vb != 0 {
for pos := curr_block*SU; pos < n; pos += 1 {
a := (^byte)(x+pos)^;
b := (^byte)(y+pos)^;
if a ~ b != 0 {
return (int(a) - int(b)) < 0 ? -1 : +1;
}
}
}
}
for /**/; offset < n; offset += 1 {
a := (^byte)(x+offset)^;
b := (^byte)(y+offset)^;
if a ~ b != 0 {
return (int(a) - int(b)) < 0 ? -1 : +1;
}
}
return 0;
}
memory_compare_zero :: proc "contextless" (a: rawptr, n: int) -> int #no_bounds_check {
x := uintptr(a);
n := uintptr(n);
SU :: size_of(uintptr);
fast := uintptr(n/SU + 1);
offset := (fast-1)*SU;
curr_block := uintptr(0);
if n < SU {
fast = 0;
}
for /**/; curr_block < fast; curr_block += 1 {
va := (^uintptr)(x + curr_block * size_of(uintptr))^;
if va ~ 0 != 0 {
for pos := curr_block*SU; pos < n; pos += 1 {
a := (^byte)(x+pos)^;
if a ~ 0 != 0 {
return int(a) < 0 ? -1 : +1;
}
}
}
}
for /**/; offset < n; offset += 1 {
a := (^byte)(x+offset)^;
if a ~ 0 != 0 {
return int(a) < 0 ? -1 : +1;
}
}
return 0;
}
string_eq :: proc "contextless" (a, b: string) -> bool {
x := transmute(Raw_String)a;
y := transmute(Raw_String)b;
switch {
case len(a) != len(b): return false;
case len(a) == 0: return true;
case &a[0] == &b[0]: return true;
case x.len != y.len: return false;
case x.len == 0: return true;
case x.data == y.data: return true;
}
return string_cmp(a, b) == 0;
}
string_cmp :: proc "contextless" (a, b: string) -> int {
return mem.compare_byte_ptrs(&a[0], &b[0], min(len(a), len(b)));
x := transmute(Raw_String)a;
y := transmute(Raw_String)b;
return memory_compare(x.data, y.data, min(x.len, y.len));
}
string_ne :: inline proc "contextless" (a, b: string) -> bool { return !string_eq(a, b); }
@@ -261,18 +496,19 @@ string_le :: inline proc "contextless" (a, b: string) -> bool { return string_cm
string_ge :: inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) >= 0; }
cstring_len :: proc "contextless" (s: cstring) -> int {
n := 0;
for p := (^byte)(s); p != nil && p^ != 0; p = mem.ptr_offset(p, 1) {
n += 1;
p0 := uintptr((^byte)(s));
p := p0;
for p != 0 && (^byte)(p)^ != 0 {
p += 1;
}
return n;
return int(p - p0);
}
cstring_to_string :: proc "contextless" (s: cstring) -> string {
if s == nil do return "";
ptr := (^byte)(s);
n := cstring_len(s);
return transmute(string)mem.Raw_String{ptr, n};
return transmute(string)Raw_String{ptr, n};
}
@@ -283,6 +519,11 @@ complex128_eq :: inline proc "contextless" (a, b: complex128) -> bool { return r
complex128_ne :: inline proc "contextless" (a, b: complex128) -> bool { return real(a) != real(b) || imag(a) != imag(b); }
quaternion128_eq :: inline proc "contextless" (a, b: quaternion128) -> bool { return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b); }
quaternion128_ne :: inline proc "contextless" (a, b: quaternion128) -> bool { return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b); }
quaternion256_eq :: inline proc "contextless" (a, b: quaternion256) -> bool { return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b); }
quaternion256_ne :: inline proc "contextless" (a, b: quaternion256) -> bool { return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b); }
bounds_check_error :: proc "contextless" (file: string, line, column: int, index, count: int) {
@@ -356,8 +597,84 @@ type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column
handle_error(file, line, column, from, to);
}
string_decode_rune :: inline proc "contextless" (s: string) -> (rune, int) {
return utf8.decode_rune_in_string(s);
// NOTE(bill): Duplicated here to remove dependency on package unicode/utf8
@static accept_sizes := [256]u8{
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x00-0x0f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x10-0x1f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x20-0x2f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x30-0x3f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x40-0x4f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x50-0x5f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x60-0x6f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x70-0x7f
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0x80-0x8f
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0x90-0x9f
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xa0-0xaf
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xb0-0xbf
0xf1, 0xf1, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xc0-0xcf
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xd0-0xdf
0x13, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x23, 0x03, 0x03, // 0xe0-0xef
0x34, 0x04, 0x04, 0x04, 0x44, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xf0-0xff
};
Accept_Range :: struct {lo, hi: u8};
@static accept_ranges := [5]Accept_Range{
{0x80, 0xbf},
{0xa0, 0xbf},
{0x80, 0x9f},
{0x90, 0xbf},
{0x80, 0x8f},
};
MASKX :: 0b0011_1111;
MASK2 :: 0b0001_1111;
MASK3 :: 0b0000_1111;
MASK4 :: 0b0000_0111;
LOCB :: 0b1000_0000;
HICB :: 0b1011_1111;
RUNE_ERROR :: '\ufffd';
n := len(s);
if n < 1 {
return RUNE_ERROR, 0;
}
s0 := s[0];
x := accept_sizes[s0];
if x >= 0xF0 {
mask := rune(x) << 31 >> 31; // NOTE(bill): Create 0x0000 or 0xffff.
return rune(s[0])&~mask | RUNE_ERROR&mask, 1;
}
sz := x & 7;
accept := accept_ranges[x>>4];
if n < int(sz) {
return RUNE_ERROR, 1;
}
b1 := s[1];
if b1 < accept.lo || accept.hi < b1 {
return RUNE_ERROR, 1;
}
if sz == 2 {
return rune(s0&MASK2)<<6 | rune(b1&MASKX), 2;
}
b2 := s[2];
if b2 < LOCB || HICB < b2 {
return RUNE_ERROR, 1;
}
if sz == 3 {
return rune(s0&MASK3)<<12 | rune(b1&MASKX)<<6 | rune(b2&MASKX), 3;
}
b3 := s[3];
if b3 < LOCB || HICB < b3 {
return RUNE_ERROR, 1;
}
return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4;
}
bounds_check_error_loc :: inline proc "contextless" (using loc := #caller_location, index, count: int) {
@@ -472,9 +789,16 @@ abs_complex128 :: inline proc "contextless" (x: complex128) -> f64 {
r, i := real(x), imag(x);
return _sqrt_f64(r*r + i*i);
}
abs_quaternion128 :: inline proc "contextless" (x: quaternion128) -> f32 {
r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
return _sqrt_f32(r*r + i*i + j*j + k*k);
}
abs_quaternion256 :: inline proc "contextless" (x: quaternion256) -> f64 {
r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
return _sqrt_f64(r*r + i*i + j*j + k*k);
}
quo_complex64 :: proc(n, m: complex64) -> complex64 {
quo_complex64 :: proc "contextless" (n, m: complex64) -> complex64 {
e, f: f32;
if abs(real(m)) >= abs(imag(m)) {
@@ -492,7 +816,7 @@ quo_complex64 :: proc(n, m: complex64) -> complex64 {
return complex(e, f);
}
quo_complex128 :: proc(n, m: complex128) -> complex128 {
quo_complex128 :: proc "contextless" (n, m: complex128) -> complex128 {
e, f: f64;
if abs(real(m)) >= abs(imag(m)) {
@@ -509,3 +833,55 @@ quo_complex128 :: proc(n, m: complex128) -> complex128 {
return complex(e, f);
}
mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q);
r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r);
t0 := r0*q0 - r1*q1 - r2*q2 - r3*q3;
t1 := r0*q1 + r1*q0 - r2*q3 + r3*q2;
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1;
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0;
return quaternion(t0, t1, t2, t3);
}
mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q);
r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r);
t0 := r0*q0 - r1*q1 - r2*q2 - r3*q3;
t1 := r0*q1 + r1*q0 - r2*q3 + r3*q2;
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1;
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0;
return quaternion(t0, t1, t2, t3);
}
quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q);
r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r);
invmag2 := 1.0 / (r0*r0 + r1*r1 + r2*r2 + r3*r3);
t0 := (r0*q0 + r1*q1 + r2*q2 + r3*q3) * invmag2;
t1 := (r0*q1 - r1*q0 - r2*q3 - r3*q2) * invmag2;
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2;
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2;
return quaternion(t0, t1, t2, t3);
}
quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q);
r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r);
invmag2 := 1.0 / (r0*r0 + r1*r1 + r2*r2 + r3*r3);
t0 := (r0*q0 + r1*q1 + r2*q2 + r3*q3) * invmag2;
t1 := (r0*q1 - r1*q0 - r2*q3 - r3*q2) * invmag2;
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2;
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2;
return quaternion(t0, t1, t2, t3);
}
-178
View File
@@ -1,183 +1,5 @@
package runtime
@(default_calling_convention="none")
foreign {
@(link_name="llvm.cttz.i8") _ctz_u8 :: proc(i: u8, is_zero_undef := false) -> u8 ---
@(link_name="llvm.cttz.i16") _ctz_u16 :: proc(i: u16, is_zero_undef := false) -> u16 ---
@(link_name="llvm.cttz.i32") _ctz_u32 :: proc(i: u32, is_zero_undef := false) -> u32 ---
@(link_name="llvm.cttz.i64") _ctz_u64 :: proc(i: u64, is_zero_undef := false) -> u64 ---
}
_ctz :: proc{
_ctz_u8,
_ctz_u16,
_ctz_u32,
_ctz_u64,
};
@(default_calling_convention="none")
foreign {
@(link_name="llvm.ctlz.i8") _clz_u8 :: proc(i: u8, is_zero_undef := false) -> u8 ---
@(link_name="llvm.ctlz.i16") _clz_u16 :: proc(i: u16, is_zero_undef := false) -> u16 ---
@(link_name="llvm.ctlz.i32") _clz_u32 :: proc(i: u32, is_zero_undef := false) -> u32 ---
@(link_name="llvm.ctlz.i64") _clz_u64 :: proc(i: u64, is_zero_undef := false) -> u64 ---
}
_clz :: proc{
_clz_u8,
_clz_u16,
_clz_u32,
_clz_u64,
};
udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
n := transmute([2]u64)a;
d := transmute([2]u64)b;
q, r: [2]u64 = ---, ---;
sr: u32 = 0;
low :: ODIN_ENDIAN == "big" ? 1 : 0;
high :: 1 - low;
U64_BITS :: 8*size_of(u64);
U128_BITS :: 8*size_of(u128);
// Special Cases
if n[high] == 0 {
if d[high] == 0 {
if rem != nil {
rem^ = u128(n[low] % d[low]);
}
return u128(n[low] / d[low]);
}
if rem != nil {
rem^ = u128(n[low]);
}
return 0;
}
if d[low] == 0 {
if d[high] == 0 {
if rem != nil {
rem^ = u128(n[high] % d[low]);
}
return u128(n[high] / d[low]);
}
if n[low] == 0 {
if rem != nil {
r[high] = n[high] % d[high];
r[low] = 0;
rem^ = transmute(u128)r;
}
return u128(n[high] / d[high]);
}
if d[high] & (d[high]-1) == 0 {
if rem != nil {
r[low] = n[low];
r[high] = n[high] & (d[high] - 1);
rem^ = transmute(u128)r;
}
return u128(n[high] >> _ctz(d[high]));
}
sr = transmute(u32)(i32(_clz(d[high])) - i32(_clz(n[high])));
if sr > U64_BITS - 2 {
if rem != nil {
rem^ = a;
}
return 0;
}
sr += 1;
q[low] = 0;
q[high] = n[low] << u64(U64_BITS - sr);
r[high] = n[high] >> sr;
r[low] = (n[high] << (U64_BITS - sr)) | (n[low] >> sr);
} else {
if d[high] == 0 {
if d[low] & (d[low] - 1) == 0 {
if rem != nil {
rem^ = u128(n[low] & (d[low] - 1));
}
if d[low] == 1 {
return a;
}
sr = u32(_ctz(d[low]));
q[high] = n[high] >> sr;
q[low] = (n[high] << (U64_BITS-sr)) | (n[low] >> sr);
return transmute(u128)q;
}
sr = 1 + U64_BITS + u32(_clz(d[low])) - u32(_clz(n[high]));
switch {
case sr == U64_BITS:
q[low] = 0;
q[high] = n[low];
r[high] = 0;
r[low] = n[high];
case sr < U64_BITS:
q[low] = 0;
q[high] = n[low] << (U64_BITS - sr);
r[high] = n[high] >> sr;
r[low] = (n[high] << (U64_BITS - sr)) | (n[low] >> sr);
case:
q[low] = n[low] << (U128_BITS - sr);
q[high] = (n[high] << (U128_BITS - sr)) | (n[low] >> (sr - U64_BITS));
r[high] = 0;
r[low] = n[high] >> (sr - U64_BITS);
}
} else {
sr = transmute(u32)(i32(_clz(d[high])) - i32(_clz(n[high])));
if sr > U64_BITS - 1 {
if rem != nil {
rem^ = a;
}
return 0;
}
sr += 1;
q[low] = 0;
if sr == U64_BITS {
q[high] = n[low];
r[high] = 0;
r[low] = n[high];
} else {
r[high] = n[high] >> sr;
r[low] = (n[high] << (U64_BITS - sr)) | (n[low] >> sr);
q[high] = n[low] << (U64_BITS - sr);
}
}
}
carry: u32 = 0;
r_all: u128 = ---;
for ; sr > 0; sr -= 1 {
r[high] = (r[high] << 1) | (r[low] >> (U64_BITS - 1));
r[low] = (r[low] << 1) | (q[high] >> (U64_BITS - 1));
q[high] = (q[high] << 1) | (q[low] >> (U64_BITS - 1));
q[low] = (q[low] << 1) | u64(carry);
r_all = transmute(u128)r;
s := i128(b - r_all - 1) >> (U128_BITS - 1);
carry = u32(s & 1);
r_all -= b & transmute(u128)s;
r = transmute([2]u64)r_all;
}
q_all := ((transmute(u128)q) << 1) | u128(carry);
if rem != nil {
rem^ = r_all;
}
return q_all;
}
@(link_name="__umodti3")
umodti3 :: proc "c" (a, b: i128) -> i128 {
s_a := a >> (128 - 1);
+15
View File
@@ -0,0 +1,15 @@
//+build linux, darwin
package runtime
@(link_name="memset")
memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
b := byte(val);
p_start := uintptr(ptr);
p_end := p_start + uintptr(max(len, 0));
for p := p_start; p < p_end; p += 1 {
(^byte)(p)^ = b;
}
return ptr;
}
+59
View File
@@ -0,0 +1,59 @@
package runtime
foreign import kernel32 "system:Kernel32.lib"
@private
@(link_name="_tls_index")
_tls_index: u32;
@private
@(link_name="_fltused")
_fltused: i32 = 0x9875;
@(link_name="memcpy")
memcpy :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
foreign kernel32 {
RtlCopyMemory :: proc "c" (dst, src: rawptr, len: int) ---
}
RtlCopyMemory(dst, src, len);
return dst;
}
@(link_name="memmove")
memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
foreign kernel32 {
RtlMoveMemory :: proc "c" (dst, src: rawptr, len: int) ---
}
RtlMoveMemory(dst, src, len);
return dst;
}
@(link_name="memset")
memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
d := uintptr(ptr);
b := byte(val);
for i in 0..<uintptr(len) {
(^byte)(d+i)^ = b;
}
return ptr;
}
// @(link_name="memcmp")
// memcmp :: proc "c" (dst, src: rawptr, len: int) -> i32 {
// if dst == nil || src == nil {
// return 0;
// }
// if dst == src {
// return 0;
// }
// d, s := uintptr(dst), uintptr(src);
// n := uintptr(len);
// for i := uintptr(0); i < n; i += 1 {
// x, y := (^byte)(d+i)^, (^byte)(s+i)^;
// if x != y {
// return x < y ? -1 : +1;
// }
// }
// return 0;
// }
+8
View File
@@ -2,6 +2,14 @@ package runtime
foreign import kernel32 "system:Kernel32.lib"
@private
@(link_name="_tls_index")
_tls_index: u32;
@private
@(link_name="_fltused")
_fltused: i32 = 0x9875;
@(link_name="memcpy")
memcpy :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
foreign kernel32 {
+180
View File
@@ -0,0 +1,180 @@
package runtime
@(default_calling_convention="none")
foreign {
@(link_name="llvm.cttz.i8") _ctz_u8 :: proc(i: u8, is_zero_undef := false) -> u8 ---
@(link_name="llvm.cttz.i16") _ctz_u16 :: proc(i: u16, is_zero_undef := false) -> u16 ---
@(link_name="llvm.cttz.i32") _ctz_u32 :: proc(i: u32, is_zero_undef := false) -> u32 ---
@(link_name="llvm.cttz.i64") _ctz_u64 :: proc(i: u64, is_zero_undef := false) -> u64 ---
}
_ctz :: proc{
_ctz_u8,
_ctz_u16,
_ctz_u32,
_ctz_u64,
};
@(default_calling_convention="none")
foreign {
@(link_name="llvm.ctlz.i8") _clz_u8 :: proc(i: u8, is_zero_undef := false) -> u8 ---
@(link_name="llvm.ctlz.i16") _clz_u16 :: proc(i: u16, is_zero_undef := false) -> u16 ---
@(link_name="llvm.ctlz.i32") _clz_u32 :: proc(i: u32, is_zero_undef := false) -> u32 ---
@(link_name="llvm.ctlz.i64") _clz_u64 :: proc(i: u64, is_zero_undef := false) -> u64 ---
}
_clz :: proc{
_clz_u8,
_clz_u16,
_clz_u32,
_clz_u64,
};
udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
n := transmute([2]u64)a;
d := transmute([2]u64)b;
q, r: [2]u64 = ---, ---;
sr: u32 = 0;
low :: ODIN_ENDIAN == "big" ? 1 : 0;
high :: 1 - low;
U64_BITS :: 8*size_of(u64);
U128_BITS :: 8*size_of(u128);
// Special Cases
if n[high] == 0 {
if d[high] == 0 {
if rem != nil {
res := n[low] % d[low];
rem^ = u128(res);
}
return u128(n[low] / d[low]);
}
if rem != nil {
rem^ = u128(n[low]);
}
return 0;
}
if d[low] == 0 {
if d[high] == 0 {
if rem != nil {
rem^ = u128(n[high] % d[low]);
}
return u128(n[high] / d[low]);
}
if n[low] == 0 {
if rem != nil {
r[high] = n[high] % d[high];
r[low] = 0;
rem^ = transmute(u128)r;
}
return u128(n[high] / d[high]);
}
if d[high] & (d[high]-1) == 0 {
if rem != nil {
r[low] = n[low];
r[high] = n[high] & (d[high] - 1);
rem^ = transmute(u128)r;
}
return u128(n[high] >> _ctz(d[high]));
}
sr = transmute(u32)(i32(_clz(d[high])) - i32(_clz(n[high])));
if sr > U64_BITS - 2 {
if rem != nil {
rem^ = a;
}
return 0;
}
sr += 1;
q[low] = 0;
q[high] = n[low] << u64(U64_BITS - sr);
r[high] = n[high] >> sr;
r[low] = (n[high] << (U64_BITS - sr)) | (n[low] >> sr);
} else {
if d[high] == 0 {
if d[low] & (d[low] - 1) == 0 {
if rem != nil {
rem^ = u128(n[low] & (d[low] - 1));
}
if d[low] == 1 {
return a;
}
sr = u32(_ctz(d[low]));
q[high] = n[high] >> sr;
q[low] = (n[high] << (U64_BITS-sr)) | (n[low] >> sr);
return transmute(u128)q;
}
sr = 1 + U64_BITS + u32(_clz(d[low])) - u32(_clz(n[high]));
switch {
case sr == U64_BITS:
q[low] = 0;
q[high] = n[low];
r[high] = 0;
r[low] = n[high];
case sr < U64_BITS:
q[low] = 0;
q[high] = n[low] << (U64_BITS - sr);
r[high] = n[high] >> sr;
r[low] = (n[high] << (U64_BITS - sr)) | (n[low] >> sr);
case:
q[low] = n[low] << (U128_BITS - sr);
q[high] = (n[high] << (U128_BITS - sr)) | (n[low] >> (sr - U64_BITS));
r[high] = 0;
r[low] = n[high] >> (sr - U64_BITS);
}
} else {
sr = transmute(u32)(i32(_clz(d[high])) - i32(_clz(n[high])));
if sr > U64_BITS - 1 {
if rem != nil {
rem^ = a;
}
return 0;
}
sr += 1;
q[low] = 0;
if sr == U64_BITS {
q[high] = n[low];
r[high] = 0;
r[low] = n[high];
} else {
r[high] = n[high] >> sr;
r[low] = (n[high] << (U64_BITS - sr)) | (n[low] >> sr);
q[high] = n[low] << (U64_BITS - sr);
}
}
}
carry: u32 = 0;
r_all: u128 = ---;
for ; sr > 0; sr -= 1 {
r[high] = (r[high] << 1) | (r[low] >> (U64_BITS - 1));
r[low] = (r[low] << 1) | (q[high] >> (U64_BITS - 1));
q[high] = (q[high] << 1) | (q[low] >> (U64_BITS - 1));
q[low] = (q[low] << 1) | u64(carry);
r_all = transmute(u128)r;
s := i128(b - r_all - 1) >> (U128_BITS - 1);
carry = u32(s & 1);
r_all -= b & transmute(u128)s;
r = transmute([2]u64)r_all;
}
q_all := ((transmute(u128)q) << 1) | u128(carry);
if rem != nil {
rem^ = r_all;
}
return q_all;
}
+134 -71
View File
@@ -1,6 +1,7 @@
package sort
import "core:mem"
import "intrinsics"
bubble_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
assert(f != nil);
@@ -26,7 +27,7 @@ bubble_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
}
}
bubble_sort :: proc(array: $A/[]$T) {
bubble_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
count := len(array);
init_j, last_j := 0, count-1;
@@ -73,7 +74,7 @@ quick_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
quick_sort_proc(a[i:n], f);
}
quick_sort :: proc(array: $A/[]$T) {
quick_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
a := array;
n := len(a);
if n < 2 do return;
@@ -96,99 +97,159 @@ quick_sort :: proc(array: $A/[]$T) {
quick_sort(a[i:n]);
}
_log2 :: proc(n: int) -> int {
_log2 :: proc(x: int) -> int {
res := 0;
for ; n != 0; n >>= 1 do res += 1;
for n := x; n != 0; n >>= 1 do res += 1;
return res;
}
merge_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
merge_slices :: proc(arr1, arr2, out: A, f: proc(T, T) -> int) {
N1, N2 := len(arr1), len(arr2);
i, j := 0, 0;
for k in 0..<N1+N2 {
if j == N2 || i < N1 && j < N2 && f(arr1[i], arr2[j]) < 0 {
out[k] = arr1[i];
i += 1;
merge_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) where intrinsics.type_is_ordered(T) {
merge :: proc(a: A, start, mid, end: int, f: proc(T, T) -> int) {
s, m := start, mid;
s2 := m + 1;
if f(a[m], a[s2]) <= 0 {
return;
}
for s <= m && s2 <= end {
if f(a[s], a[s2]) <= 0 {
s += 1;
} else {
out[k] = arr2[j];
j += 1;
v := a[s2];
i := s2;
for i != s {
a[i] = a[i-1];
i -= 1;
}
a[s] = v;
s += 1;
m += 1;
s2 += 1;
}
}
}
internal_sort :: proc(a: A, l, r: int, f: proc(T, T) -> int) {
if l < r {
m := l + (r - l) / 2;
assert(f != nil);
arr1 := array;
N := len(arr1);
arr2 := make([]T, N);
defer free(arr2);
a, b, m, M := N/2, N, 1, _log2(N);
for i in 0..M {
for j in 0..<a {
k := 2*j*m;
merge_slices(arr1[k:k+m], arr1[k+m:k+m+m], arr2[k:], f);
internal_sort(a, l, m, f);
internal_sort(a, m+1, r, f);
merge(a, l, m, r, f);
}
if N-b > m {
k := 2*a*m;
merge_slices(arr1[k:k+m], arr1[k+m : k+m+(N-b)&(m-1)], arr2[k:], f);
} else {
copy(arr2[b:N], arr1[b:N]);
}
arr1, arr2 = arr2, arr1;
m <<= 1;
a >>= 1;
b = a << uint(i) << 2;
}
if M & 1 == 0 do copy(arr2, arr1);
internal_sort(array, 0, len(array)-1, f);
}
merge_sort :: proc(array: $A/[]$T) {
merge_slices :: proc(arr1, arr2, out: A) {
N1, N2 := len(arr1), len(arr2);
i, j := 0, 0;
for k in 0..<N1+N2 {
if j == N2 || i < N1 && j < N2 && arr1[i] < arr2[j] {
out[k] = arr1[i];
i += 1;
merge_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
merge :: proc(a: A, start, mid, end: int) {
s, m := start, mid;
s2 := m + 1;
if a[m] <= a[s2] {
return;
}
for s <= m && s2 <= end {
if a[s] <= a[s2] {
s += 1;
} else {
out[k] = arr2[j];
j += 1;
v := a[s2];
i := s2;
for i != s {
a[i] = a[i-1];
i -= 1;
}
a[s] = v;
s += 1;
m += 1;
s2 += 1;
}
}
}
internal_sort :: proc(a: A, l, r: int) {
if l < r {
m := l + (r - l) / 2;
arr1 := array;
N := len(arr1);
arr2 := make([]T, N);
defer free(arr2);
a, b, m, M := N/2, N, 1, _log2(N);
for i in 0..M {
for j in 0..<a {
k := 2*j*m;
merge_slices(arr1[k:k+m], arr1[k+m:k+m+m], arr2[k:]);
internal_sort(a, l, m);
internal_sort(a, m+1, r);
merge(a, l, m, r);
}
if N-b > m {
k := 2*a*m;
merge_slices(arr1[k:k+m], arr1[k+m : k+m+(N-b)&(m-1)], arr2[k:]);
} else {
copy(arr2[b:N], arr1[b:N]);
}
arr1, arr2 = arr2, arr1;
m <<= 1;
a >>= 1;
b = a << uint(i) << 2;
}
if M & 1 == 0 do copy(arr2, arr1);
internal_sort(array, 0, len(array)-1);
}
heap_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
sift_proc :: proc(a: A, pi: int, n: int, f: proc(T, T) -> int) #no_bounds_check {
p := pi;
v := a[p];
m := p*2 + 1;
for m <= n {
if (m < n) && f(a[m+1], a[m]) > 0 {
m += 1;
}
if f(v, a[m]) >= 0 {
break;
}
a[p] = a[m];
p = m;
m += m+1;
a[p] = v;
}
}
n := len(array);
if n == 0 do return;
for i := n/2; i >= 0; i -= 1 {
sift_proc(array, i, n-1, f);
}
for i := n-1; i >= 1; i -= 1 {
array[0], array[i] = array[i], array[0];
sift_proc(array, 0, i-1, f);
}
}
heap_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
sift :: proc(a: A, pi: int, n: int) #no_bounds_check {
p := pi;
v := a[p];
m := p*2 + 1;
for m <= n {
if (m < n) && (a[m+1] > a[m]) {
m += 1;
}
if v >= a[m] {
break;
}
a[p] = a[m];
p = m;
m += m+1;
a[p] = v;
}
}
n := len(array);
if n == 0 do return;
for i := n/2; i >= 0; i -= 1 {
sift(array, i, n-1);
}
for i := n-1; i >= 1; i -= 1 {
array[0], array[i] = array[i], array[0];
sift(array, 0, i-1);
}
}
compare_bools :: proc(a, b: bool) -> int {
switch {
case !a && b: return -1;
@@ -221,5 +282,7 @@ compare_f64s :: proc(a, b: f64) -> int {
return 0;
}
compare_strings :: proc(a, b: string) -> int {
return mem.compare_byte_ptrs(&a[0], &b[0], min(len(a), len(b)));
x := transmute(mem.Raw_String)a;
y := transmute(mem.Raw_String)b;
return mem.compare_byte_ptrs(x.data, y.data, min(x.len, y.len));
}
@@ -1,6 +1,6 @@
// Multiple precision decimal numbers
// NOTE: This is only for floating point printing and nothing else
package decimal
package strconv_decimal
Decimal :: struct {
digits: [384]byte, // big-endian digits
@@ -20,29 +20,29 @@ decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string {
// TODO(bill): make this work with a buffer that's not big enough
assert(len(buf) >= n);
buf = buf[0:n];
b := buf[0:n];
if a.count == 0 {
buf[0] = '0';
return string(buf[0:1]);
b[0] = '0';
return string(b[0:1]);
}
w := 0;
if a.decimal_point <= 0 {
buf[w] = '0'; w += 1;
buf[w] = '.'; w += 1;
w += digit_zero(buf[w : w-a.decimal_point]);
w += copy(buf[w:], a.digits[0:a.count]);
b[w] = '0'; w += 1;
b[w] = '.'; w += 1;
w += digit_zero(b[w : w-a.decimal_point]);
w += copy(b[w:], a.digits[0:a.count]);
} else if a.decimal_point < a.count {
w += copy(buf[w:], a.digits[0:a.decimal_point]);
buf[w] = '.'; w += 1;
w += copy(buf[w:], a.digits[a.decimal_point : a.count]);
w += copy(b[w:], a.digits[0:a.decimal_point]);
b[w] = '.'; w += 1;
w += copy(b[w:], a.digits[a.decimal_point : a.count]);
} else {
w += copy(buf[w:], a.digits[0:a.count]);
w += digit_zero(buf[w : w+a.decimal_point-a.count]);
w += copy(b[w:], a.digits[0:a.count]);
w += digit_zero(b[w : w+a.decimal_point-a.count]);
}
return string(buf[0:w]);
return string(b[0:w]);
}
// trim trailing zeros
@@ -56,10 +56,10 @@ trim :: proc(a: ^Decimal) {
}
assign :: proc(a: ^Decimal, i: u64) {
assign :: proc(a: ^Decimal, idx: u64) {
buf: [64]byte;
n := 0;
for i > 0 {
for i := idx; i > 0; {
j := i/10;
i -= 10*j;
buf[n] = byte('0'+i);
@@ -175,11 +175,11 @@ shift_left :: proc(a: ^Decimal, k: uint) {
trim(a);
}
shift :: proc(a: ^Decimal, k: int) {
shift :: proc(a: ^Decimal, i: int) {
uint_size :: 8*size_of(uint);
max_shift :: uint_size-4;
switch {
switch k := i; {
case a.count == 0:
// no need to update
case k > 0:
+23 -179
View File
@@ -1,13 +1,6 @@
package strconv
using import "core:decimal"
Int_Flag :: enum {
Prefix,
Plus,
Space,
}
Int_Flags :: bit_set[Int_Flag];
import "decimal"
Decimal_Slice :: struct {
digits: []byte,
@@ -28,7 +21,7 @@ _f32_info := Float_Info{23, 8, -127};
_f64_info := Float_Info{52, 11, -1023};
generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> []byte {
generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, precision, bit_size: int) -> []byte {
bits: u64;
flt: ^Float_Info;
switch bit_size {
@@ -56,7 +49,7 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> [
} else {
s = "+Inf";
}
n := copy(buf, cast([]byte)s);
n := copy(buf, s);
return buf[:n];
case 0: // denormalized
@@ -68,11 +61,12 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> [
exp += flt.bias;
d_: Decimal;
d_: decimal.Decimal;
d := &d_;
assign(d, mant);
shift(d, exp - int(flt.mantbits));
decimal.assign(d, mant);
decimal.shift(d, exp - int(flt.mantbits));
digs: Decimal_Slice;
prec := precision;
shortest := prec < 0;
if shortest {
round_shortest(d, mant, exp, flt);
@@ -84,13 +78,13 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> [
}
} else {
switch fmt {
case 'e', 'E': round(d, prec+1);
case 'f', 'F': round(d, d.decimal_point+prec);
case 'e', 'E': decimal.round(d, prec+1);
case 'f', 'F': decimal.round(d, d.decimal_point+prec);
case 'g', 'G':
if prec == 0 {
prec = 1;
}
round(d, prec);
decimal.round(d, prec);
}
digs = Decimal_Slice{digits = d.digits[:], count = d.count, decimal_point = d.decimal_point};
@@ -100,11 +94,11 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> [
format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slice, prec: int, fmt: byte) -> []byte {
format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slice, precision: int, fmt: byte) -> []byte {
Buffer :: struct {
b: []byte,
n: int,
}
};
to_bytes :: proc(b: Buffer) -> []byte do return b.b[:b.n];
add_bytes :: proc(buf: ^Buffer, bytes: ..byte) {
@@ -112,6 +106,7 @@ format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slic
}
b := Buffer{b = buf};
prec := precision;
switch fmt {
case 'f', 'F':
@@ -218,7 +213,7 @@ format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slic
}
round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) {
round_shortest :: proc(d: ^decimal.Decimal, mant: u64, exp: int, flt: ^Float_Info) {
if mant == 0 { // If mantissa is zero, the number is zero
d.count = 0;
return;
@@ -236,9 +231,9 @@ round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) {
return;
}
upper_: Decimal; upper := &upper_;
assign(upper, 2*mant - 1);
shift(upper, exp - int(flt.mantbits) - 1);
upper_: decimal.Decimal; upper := &upper_;
decimal.assign(upper, 2*mant - 1);
decimal.shift(upper, exp - int(flt.mantbits) - 1);
mantlo: u64;
explo: int;
@@ -249,9 +244,9 @@ round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) {
mantlo = 2*mant - 1;
explo = exp-1;
}
lower_: Decimal; lower := &lower_;
assign(lower, 2*mantlo + 1);
shift(lower, explo - int(flt.mantbits) - 1);
lower_: decimal.Decimal; lower := &lower_;
decimal.assign(lower, 2*mantlo + 1);
decimal.shift(lower, explo - int(flt.mantbits) - 1);
inclusive := mant%2 == 0;
@@ -270,168 +265,17 @@ round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) {
ok_round_up := m != u && (inclusive || m+1 < u || i+1 < upper.count);
if ok_round_down && ok_round_up {
round(d, i+1);
decimal.round(d, i+1);
return;
}
if ok_round_down {
round_down(d, i+1);
decimal.round_down(d, i+1);
return;
}
if ok_round_up {
round_up(d, i+1);
decimal.round_up(d, i+1);
return;
}
}
}
MAX_BASE :: 32;
digits := "0123456789abcdefghijklmnopqrstuvwxyz";
is_integer_negative :: proc(u: u64, is_signed: bool, bit_size: int) -> (unsigned: u64, neg: bool) {
if is_signed {
switch bit_size {
case 8:
i := i8(u);
neg = i < 0;
u = u64(abs(i64(i)));
case 16:
i := i16(u);
neg = i < 0;
u = u64(abs(i64(i)));
case 32:
i := i32(u);
neg = i < 0;
u = u64(abs(i64(i)));
case 64:
i := i64(u);
neg = i < 0;
u = u64(abs(i64(i)));
case:
panic("is_integer_negative: Unknown integer size");
}
}
return u, neg;
}
append_bits :: proc(buf: []byte, u: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
if base < 2 || base > MAX_BASE {
panic("strconv: illegal base passed to append_bits");
}
neg: bool;
a: [129]byte;
i := len(a);
u, neg = is_integer_negative(u, is_signed, bit_size);
b := u64(base);
for u >= b {
i-=1; a[i] = digits[u % b];
u /= b;
}
i-=1; a[i] = digits[u % b];
if .Prefix in flags {
ok := true;
switch base {
case 2: i-=1; a[i] = 'b';
case 8: i-=1; a[i] = 'o';
case 10: i-=1; a[i] = 'd';
case 12: i-=1; a[i] = 'z';
case 16: i-=1; a[i] = 'x';
case: ok = false;
}
if ok {
i-=1; a[i] = '0';
}
}
switch {
case neg:
i-=1; a[i] = '-';
case .Plus in flags:
i-=1; a[i] = '+';
case .Space in flags:
i-=1; a[i] = ' ';
}
out := a[i:];
copy(buf, out);
return string(buf[0:len(out)]);
}
is_integer_negative_128 :: proc(u: u128, is_signed: bool, bit_size: int) -> (unsigned: u128, neg: bool) {
if is_signed {
switch bit_size {
case 8:
i := i8(u);
neg = i < 0;
u = u128(abs(i128(i)));
case 16:
i := i16(u);
neg = i < 0;
u = u128(abs(i128(i)));
case 32:
i := i32(u);
neg = i < 0;
u = u128(abs(i128(i)));
case 64:
i := i64(u);
neg = i < 0;
u = u128(abs(i128(i)));
case 128:
i := i128(u);
neg = i < 0;
u = u128(abs(i128(i)));
case:
panic("is_integer_negative: Unknown integer size");
}
}
return u, neg;
}
append_bits_128 :: proc(buf: []byte, u: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
if base < 2 || base > MAX_BASE {
panic("strconv: illegal base passed to append_bits");
}
neg: bool;
a: [140]byte;
i := len(a);
u, neg = is_integer_negative_128(u, is_signed, bit_size);
b := u128(base);
for u >= b {
i-=1; a[i] = digits[u % b];
u /= b;
}
i-=1; a[i] = digits[u % b];
if .Prefix in flags {
ok := true;
switch base {
case 2: i-=1; a[i] = 'b';
case 8: i-=1; a[i] = 'o';
case 10: i-=1; a[i] = 'd';
case 12: i-=1; a[i] = 'z';
case 16: i-=1; a[i] = 'x';
case: ok = false;
}
if ok {
i-=1; a[i] = '0';
}
}
switch {
case neg:
i-=1; a[i] = '-';
case .Plus in flags:
i-=1; a[i] = '+';
case .Space in flags:
i-=1; a[i] = ' ';
}
out := a[i:];
copy(buf, out);
return string(buf[0:len(out)]);
}
+167
View File
@@ -0,0 +1,167 @@
package strconv
Int_Flag :: enum {
Prefix,
Plus,
Space,
}
Int_Flags :: bit_set[Int_Flag];
MAX_BASE :: 32;
digits := "0123456789abcdefghijklmnopqrstuvwxyz";
is_integer_negative :: proc(x: u64, is_signed: bool, bit_size: int) -> (u: u64, neg: bool) {
u = x;
if is_signed {
switch bit_size {
case 8:
i := i8(u);
neg = i < 0;
u = u64(abs(i64(i)));
case 16:
i := i16(u);
neg = i < 0;
u = u64(abs(i64(i)));
case 32:
i := i32(u);
neg = i < 0;
u = u64(abs(i64(i)));
case 64:
i := i64(u);
neg = i < 0;
u = u64(abs(i64(i)));
case:
panic("is_integer_negative: Unknown integer size");
}
}
return;
}
append_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
if base < 2 || base > MAX_BASE {
panic("strconv: illegal base passed to append_bits");
}
a: [129]byte;
i := len(a);
u, neg := is_integer_negative(x, is_signed, bit_size);
b := u64(base);
for u >= b {
i-=1; a[i] = digits[u % b];
u /= b;
}
i-=1; a[i] = digits[u % b];
if .Prefix in flags {
ok := true;
switch base {
case 2: i-=1; a[i] = 'b';
case 8: i-=1; a[i] = 'o';
case 10: i-=1; a[i] = 'd';
case 12: i-=1; a[i] = 'z';
case 16: i-=1; a[i] = 'x';
case: ok = false;
}
if ok {
i-=1; a[i] = '0';
}
}
switch {
case neg:
i-=1; a[i] = '-';
case .Plus in flags:
i-=1; a[i] = '+';
case .Space in flags:
i-=1; a[i] = ' ';
}
out := a[i:];
copy(buf, out);
return string(buf[0:len(out)]);
}
is_integer_negative_128 :: proc(x: u128, is_signed: bool, bit_size: int) -> (u: u128, neg: bool) {
u = x;
if is_signed {
switch bit_size {
case 8:
i := i8(u);
neg = i < 0;
u = u128(abs(i128(i)));
case 16:
i := i16(u);
neg = i < 0;
u = u128(abs(i128(i)));
case 32:
i := i32(u);
neg = i < 0;
u = u128(abs(i128(i)));
case 64:
i := i64(u);
neg = i < 0;
u = u128(abs(i128(i)));
case 128:
i := i128(u);
neg = i < 0;
u = u128(abs(i128(i)));
case:
panic("is_integer_negative: Unknown integer size");
}
}
return;
}
// import "core:runtime"
append_bits_128 :: proc(buf: []byte, x: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
if base < 2 || base > MAX_BASE {
panic("strconv: illegal base passed to append_bits");
}
a: [140]byte;
i := len(a);
u, neg := is_integer_negative_128(x, is_signed, bit_size);
b := u128(base);
for u >= b && i >= 0 {
i-=1;
// rem: u128;
// u = runtime.udivmod128(u, b, &rem);
// u /= b;
rem := u % b;
u /= b;
idx := u32(rem);
a[i] = digits[idx];
}
i-=1; a[i] = digits[u64(u % b)];
if .Prefix in flags {
ok := true;
switch base {
case 2: i-=1; a[i] = 'b';
case 8: i-=1; a[i] = 'o';
case 10: i-=1; a[i] = 'd';
case 12: i-=1; a[i] = 'z';
case 16: i-=1; a[i] = 'x';
case: ok = false;
}
if ok {
i-=1; a[i] = '0';
}
}
switch {
case neg:
i-=1; a[i] = '-';
case .Plus in flags:
i-=1; a[i] = '+';
case .Space in flags:
i-=1; a[i] = ' ';
}
out := a[i:];
copy(buf, out);
return string(buf[0:len(out)]);
}
+14 -7
View File
@@ -23,7 +23,8 @@ _digit_value :: proc(r: rune) -> int {
return v;
}
parse_i64 :: proc(s: string) -> i64 {
parse_i64 :: proc(str: string) -> i64 {
s := str;
neg := false;
if len(s) > 1 {
switch s[0] {
@@ -66,7 +67,8 @@ parse_i64 :: proc(s: string) -> i64 {
return value;
}
parse_u64 :: proc(s: string) -> u64 {
parse_u64 :: proc(str: string) -> u64 {
s := str;
neg := false;
if len(s) > 1 && s[0] == '+' {
s = s[1:];
@@ -102,7 +104,7 @@ parse_u64 :: proc(s: string) -> u64 {
parse_int :: proc(s: string) -> int {
return int(parse_i64(s));
}
parse_uint :: proc(s: string, base: int) -> uint {
parse_uint :: proc(s: string) -> uint {
return uint(parse_u64(s));
}
@@ -185,8 +187,8 @@ parse_f64 :: proc(s: string) -> f64 {
append_bool :: proc(buf: []byte, b: bool) -> string {
n := 0;
if b do n = copy(buf, cast([]byte)"true");
else do n = copy(buf, cast([]byte)"false");
if b do n = copy(buf, "true");
else do n = copy(buf, "false");
return string(buf[:n]);
}
@@ -203,13 +205,17 @@ itoa :: proc(buf: []byte, i: int) -> string {
atoi :: proc(s: string) -> int {
return parse_int(s);
}
atof :: proc(s: string) -> f64 {
return parse_f64(s);
}
ftoa :: append_float;
append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string {
return string(generic_ftoa(buf, f, fmt, prec, bit_size));
}
quote :: proc(buf: []byte, s: string) -> string {
quote :: proc(buf: []byte, str: string) -> string {
write_byte :: inline proc(buf: []byte, i: ^int, bytes: ..byte) {
if i^ >= len(buf) do return;
n := copy(buf[i^:], bytes[:]);
@@ -222,6 +228,7 @@ quote :: proc(buf: []byte, s: string) -> string {
c :: '"';
i := 0;
s := str;
write_byte(buf, &i, c);
for width := 0; len(s) > 0; s = s[width:] {
@@ -253,7 +260,7 @@ quote_rune :: proc(buf: []byte, r: rune) -> string {
}
write_string :: inline proc(buf: []byte, i: ^int, s: string) {
if i^ < len(buf) {
n := copy(buf[i^:], cast([]byte)s);
n := copy(buf[i^:], s);
i^ += n;
}
}
+16 -12
View File
@@ -21,6 +21,10 @@ grow_builder :: proc(b: ^Builder, cap: int) {
reserve(&b.buf, cap);
}
reset_builder :: proc(b: ^Builder) {
clear(&b.buf);
}
builder_from_slice :: proc(backing: []byte) -> Builder {
s := transmute(mem.Raw_Slice)backing;
d := mem.Raw_Dynamic_Array{
@@ -58,7 +62,7 @@ write_rune :: proc(b: ^Builder, r: rune) -> int {
}
write_string :: proc(b: ^Builder, s: string) {
write_bytes(b, cast([]byte)s);
write_bytes(b, transmute([]byte)s);
}
write_bytes :: proc(b: ^Builder, x: []byte) {
@@ -68,9 +72,9 @@ write_bytes :: proc(b: ^Builder, x: []byte) {
@(private, static)
DIGITS_LOWER := "0123456789abcdefx";
write_quoted_string :: proc(b: ^Builder, s: string, quote: byte = '"') {
write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') {
write_byte(b, quote);
for width := 0; len(s) > 0; s = s[width:] {
for width, s := 0, str; len(s) > 0; s = s[width:] {
r := rune(s[0]);
width = 1;
if r >= utf8.RUNE_SELF {
@@ -166,27 +170,27 @@ write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false
case '\t': write_string(b, `\t`);
case '\v': write_string(b, `\v`);
case:
switch {
case r < ' ':
switch c := r; {
case c < ' ':
write_byte(b, '\\');
write_byte(b, 'x');
write_byte(b, DIGITS_LOWER[byte(r)>>4]);
write_byte(b, DIGITS_LOWER[byte(r)&0xf]);
write_byte(b, DIGITS_LOWER[byte(c)>>4]);
write_byte(b, DIGITS_LOWER[byte(c)&0xf]);
case r > utf8.MAX_RUNE:
r = 0xfffd;
case c > utf8.MAX_RUNE:
c = 0xfffd;
fallthrough;
case r < 0x10000:
case c < 0x10000:
write_byte(b, '\\');
write_byte(b, 'u');
for s := 12; s >= 0; s -= 4 {
write_byte(b, DIGITS_LOWER[r>>uint(s) & 0xf]);
write_byte(b, DIGITS_LOWER[c>>uint(s) & 0xf]);
}
case:
write_byte(b, '\\');
write_byte(b, 'U');
for s := 28; s >= 0; s -= 4 {
write_byte(b, DIGITS_LOWER[r>>uint(s) & 0xf]);
write_byte(b, DIGITS_LOWER[c>>uint(s) & 0xf]);
}
}
}
+199 -36
View File
@@ -5,14 +5,14 @@ import "core:unicode/utf8"
clone :: proc(s: string, allocator := context.allocator) -> string {
c := make([]byte, len(s)+1, allocator);
copy(c, cast([]byte)s);
copy(c, s);
c[len(s)] = 0;
return string(c[:len(s)]);
}
clone_to_cstring :: proc(s: string, allocator := context.allocator) -> cstring {
c := make([]byte, len(s)+1, allocator);
copy(c, cast([]byte)s);
copy(c, s);
c[len(s)] = 0;
return cstring(&c[0]);
}
@@ -20,7 +20,7 @@ clone_to_cstring :: proc(s: string, allocator := context.allocator) -> cstring {
@(deprecated="Please use 'strings.clone'")
new_string :: proc(s: string, allocator := context.allocator) -> string {
c := make([]byte, len(s)+1, allocator);
copy(c, cast([]byte)s);
copy(c, s);
c[len(s)] = 0;
return string(c[:len(s)]);
}
@@ -28,7 +28,7 @@ new_string :: proc(s: string, allocator := context.allocator) -> string {
@(deprecated="Please use 'strings.clone_to_cstring'")
new_cstring :: proc(s: string, allocator := context.allocator) -> cstring {
c := make([]byte, len(s)+1, allocator);
copy(c, cast([]byte)s);
copy(c, s);
c[len(s)] = 0;
return cstring(&c[0]);
}
@@ -42,8 +42,18 @@ string_from_ptr :: proc(ptr: ^byte, len: int) -> string {
return transmute(string)mem.Raw_String{ptr, len};
}
ptr_from_string :: proc(str: string) -> ^byte {
d := transmute(mem.Raw_String)str;
return d.data;
}
unsafe_string_to_cstring :: proc(str: string) -> cstring {
d := transmute(mem.Raw_String)str;
return cstring(d.data);
}
compare :: proc(lhs, rhs: string) -> int {
return mem.compare(cast([]byte)lhs, cast([]byte)rhs);
return mem.compare(transmute([]byte)lhs, transmute([]byte)rhs);
}
contains_rune :: proc(s: string, r: rune) -> int {
@@ -67,7 +77,8 @@ rune_count :: proc(s: string) -> int {
}
equal_fold :: proc(s, t: string) -> bool {
equal_fold :: proc(u, v: string) -> bool {
s, t := u, v;
loop: for s != "" && t != "" {
sr, tr: rune;
if s[0] < utf8.RUNE_SELF {
@@ -129,10 +140,10 @@ join :: proc(a: []string, sep: string, allocator := context.allocator) -> string
}
b := make([]byte, n, allocator);
i := copy(b, cast([]byte)a[0]);
i := copy(b, a[0]);
for s in a[1:] {
i += copy(b[i:], cast([]byte)sep);
i += copy(b[i:], cast([]byte)s);
i += copy(b[i:], sep);
i += copy(b[i:], s);
}
return string(b);
}
@@ -149,11 +160,78 @@ concatenate :: proc(a: []string, allocator := context.allocator) -> string {
b := make([]byte, n, allocator);
i := 0;
for s in a {
i += copy(b[i:], cast([]byte)s);
i += copy(b[i:], s);
}
return string(b);
}
@private
_split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocator) -> []string {
s, n := s_, n_;
if n == 0 {
return nil;
}
if sep == "" {
l := utf8.rune_count_in_string(s);
if n < 0 || n > l {
n = l;
}
res := make([dynamic]string, n, allocator);
for i := 0; i < n-1; i += 1 {
_, w := utf8.decode_rune_in_string(s);
res[i] = s[:w];
s = s[w:];
}
if n > 0 {
res[n-1] = s;
}
return res[:];
}
if n < 0 {
n = count(s, sep) + 1;
}
res := make([dynamic]string, n, allocator);
n -= 1;
i := 0;
for ; i < n; i += 1 {
m := index(s, sep);
if m < 0 {
break;
}
res[i] = s[:m+sep_save];
s = s[m+len(sep):];
}
res[i] = s;
return res[:i+1];
}
split :: inline proc(s, sep: string, allocator := context.allocator) -> []string {
return _split(s, sep, 0, -1, allocator);
}
split_n :: inline proc(s, sep: string, n: int, allocator := context.allocator) -> []string {
return _split(s, sep, 0, n, allocator);
}
split_after :: inline proc(s, sep: string, allocator := context.allocator) -> []string {
return _split(s, sep, len(sep), -1, allocator);
}
split_after_n :: inline proc(s, sep: string, n: int, allocator := context.allocator) -> []string {
return _split(s, sep, len(sep), n, allocator);
}
index_byte :: proc(s: string, c: byte) -> int {
for i := 0; i < len(s); i += 1 {
if s[i] == c do return i;
@@ -169,7 +247,25 @@ last_index_byte :: proc(s: string, c: byte) -> int {
return -1;
}
@private PRIME_RABIN_KARP :: 16777619;
index :: proc(s, substr: string) -> int {
hash_str_rabin_karp :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) {
for i := 0; i < len(s); i += 1 {
hash = hash*PRIME_RABIN_KARP + u32(s[i]);
}
sq := u32(PRIME_RABIN_KARP);
for i := len(s); i > 0; i >>= 1 {
if (i & 1) != 0 {
pow *= sq;
}
sq *= sq;
}
return;
}
n := len(substr);
switch {
case n == 0:
@@ -185,9 +281,68 @@ index :: proc(s, substr: string) -> int {
return -1;
}
for i := 0; i < len(s)-n+1; i += 1 {
x := s[i:i+n];
if x == substr {
hash, pow := hash_str_rabin_karp(substr);
h: u32;
for i := 0; i < n; i += 1 {
h = h*PRIME_RABIN_KARP + u32(s[i]);
}
if h == hash && s[:n] == substr {
return 0;
}
for i := n; i < len(s); /**/ {
h *= PRIME_RABIN_KARP;
h += u32(s[i]);
h -= pow * u32(s[i-n]);
i += 1;
if h == hash && s[i-n:i] == substr {
return i - n;
}
}
return -1;
}
last_index :: proc(s, substr: string) -> int {
hash_str_rabin_karp_reverse :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) {
for i := len(s) - 1; i >= 0; i -= 1 {
hash = hash*PRIME_RABIN_KARP + u32(s[i]);
}
sq := u32(PRIME_RABIN_KARP);
for i := len(s); i > 0; i >>= 1 {
if (i & 1) != 0 {
pow *= sq;
}
sq *= sq;
}
return;
}
n := len(substr);
switch {
case n == 0:
return len(s);
case n == 1:
return last_index_byte(s, substr[0]);
case n == len(s):
return substr == s ? 0 : -1;
case n > len(s):
return -1;
}
hash, pow := hash_str_rabin_karp_reverse(substr);
last := len(s) - n;
h: u32;
for i := len(s)-1; i >= last; i -= 1 {
h = h*PRIME_RABIN_KARP + u32(s[i]);
}
if h == hash && s[last:] == substr {
return last;
}
for i := last-1; i >= 0; i -= 1 {
h *= PRIME_RABIN_KARP;
h += u32(s[i]);
h -= pow * u32(s[i+n]);
if h == hash && s[i:i+n] == substr {
return i;
}
}
@@ -250,13 +405,14 @@ count :: proc(s, substr: string) -> int {
// TODO(bill): Use a non-brute for approach
n := 0;
str := s;
for {
i := index(s, substr);
i := index(str, substr);
if i == -1 {
return n;
}
n += 1;
s = s[i+len(substr):];
str = str[i+len(substr):];
}
return n;
}
@@ -270,7 +426,7 @@ repeat :: proc(s: string, count: int, allocator := context.allocator) -> string
}
b := make([]byte, len(s)*count, allocator);
i := copy(b, cast([]byte)s);
i := copy(b, s);
for i < len(b) { // 2^N trick to reduce the need to copy
copy(b[i:], b[:i]);
i *= 2;
@@ -289,22 +445,22 @@ replace :: proc(s, old, new: string, n: int, allocator := context.allocator) ->
output = s;
return;
}
byte_count := n;
if m := count(s, old); m == 0 {
was_allocation = false;
output = s;
return;
} else if n < 0 || m < n {
n = m;
byte_count = m;
}
t := make([]byte, len(s) + n*(len(new) - len(old)), allocator);
t := make([]byte, len(s) + byte_count*(len(new) - len(old)), allocator);
was_allocation = true;
w := 0;
start := 0;
for i := 0; i < n; i += 1 {
for i := 0; i < byte_count; i += 1 {
j := start;
if len(old) == 0 {
if i > 0 {
@@ -314,11 +470,11 @@ replace :: proc(s, old, new: string, n: int, allocator := context.allocator) ->
} else {
j += index(s[start:], old);
}
w += copy(t[w:], cast([]byte)s[start:j]);
w += copy(t[w:], cast([]byte)new);
w += copy(t[w:], s[start:j]);
w += copy(t[w:], new);
start = j + len(old);
}
w += copy(t[w:], cast([]byte)s[start:]);
w += copy(t[w:], s[start:]);
output = string(t[0:w]);
return;
}
@@ -475,14 +631,16 @@ trim_left :: proc(s: string, cutset: string) -> string {
if s == "" || cutset == "" {
return s;
}
return trim_left_proc_with_state(s, is_in_cutset, &cutset);
state := cutset;
return trim_left_proc_with_state(s, is_in_cutset, &state);
}
trim_right :: proc(s: string, cutset: string) -> string {
if s == "" || cutset == "" {
return s;
}
return trim_right_proc_with_state(s, is_in_cutset, &cutset);
state := cutset;
return trim_right_proc_with_state(s, is_in_cutset, &state);
}
trim :: proc(s: string, cutset: string) -> string {
@@ -515,7 +673,8 @@ trim_null :: proc(s: string) -> string {
// scrub scruvs invalid utf-8 characters and replaces them with the replacement string
// Adjacent invalid bytes are only replaced once
scrub :: proc(str: string, replacement: string, allocator := context.allocator) -> string {
scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> string {
str := s;
b := make_builder(allocator);;
grow_builder(&b, len(str));
@@ -547,30 +706,33 @@ scrub :: proc(str: string, replacement: string, allocator := context.allocator)
}
reverse :: proc(str: string, allocator := context.allocator) -> string {
reverse :: proc(s: string, allocator := context.allocator) -> string {
str := s;
n := len(str);
buf := make([]byte, n);
i := 0;
i := n;
for len(str) > 0 {
_, w := utf8.decode_rune_in_string(str);
copy(buf[i:], cast([]byte)str[:w]);
i -= w;
copy(buf[i:], str[:w]);
str = str[w:];
}
return string(buf);
}
expand_tabs :: proc(str: string, tab_size: int, allocator := context.allocator) -> string {
expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> string {
if tab_size <= 0 {
panic("tab size must be positive");
}
if str == "" {
if s == "" {
return "";
}
b := make_builder(allocator);
str := s;
column: int;
for len(str) > 0 {
@@ -683,11 +845,12 @@ write_pad_string :: proc(b: ^Builder, pad: string, pad_len, remains: int) {
write_string(b, pad);
}
remains = remains % pad_len;
n := remains % pad_len;
p := pad;
if remains != 0 do for i := 0; i < remains; i += 1 {
r, w := utf8.decode_rune_in_string(pad);
for i := 0; i < n; i += 1 {
r, w := utf8.decode_rune_in_string(p);
write_rune(b, r);
pad = pad[w:];
p = p[w:];
}
}
+11 -11
View File
@@ -11,7 +11,7 @@ Ordering :: enum {
}
strongest_failure_ordering :: inline proc "contextless" (order: Ordering) -> Ordering {
#complete switch order {
switch order {
case .Relaxed: return .Relaxed;
case .Release: return .Relaxed;
case .Acquire: return .Acquire;
@@ -22,7 +22,7 @@ strongest_failure_ordering :: inline proc "contextless" (order: Ordering) -> Ord
}
fence :: inline proc "contextless" ($order: Ordering) {
#complete switch order {
switch order {
case .Relaxed: panic("there is no such thing as a relaxed fence");
case .Release: intrinsics.atomic_fence_rel();
case .Acquire: intrinsics.atomic_fence_acq();
@@ -34,7 +34,7 @@ fence :: inline proc "contextless" ($order: Ordering) {
atomic_store :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) {
#complete switch order {
switch order {
case .Relaxed: intrinsics.atomic_store_relaxed(dst, val);
case .Release: intrinsics.atomic_store_rel(dst, val);
case .Sequentially_Consistent: intrinsics.atomic_store(dst, val);
@@ -45,7 +45,7 @@ atomic_store :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) {
}
atomic_load :: inline proc "contextless" (dst: ^$T, $order: Ordering) -> T {
#complete switch order {
switch order {
case .Relaxed: return intrinsics.atomic_load_relaxed(dst);
case .Acquire: return intrinsics.atomic_load_acq(dst);
case .Sequentially_Consistent: return intrinsics.atomic_load(dst);
@@ -57,7 +57,7 @@ atomic_load :: inline proc "contextless" (dst: ^$T, $order: Ordering) -> T {
}
atomic_swap :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
#complete switch order {
switch order {
case .Relaxed: return intrinsics.atomic_xchg_relaxed(dst, val);
case .Release: return intrinsics.atomic_xchg_rel(dst, val);
case .Acquire: return intrinsics.atomic_xchg_acq(dst, val);
@@ -138,7 +138,7 @@ atomic_compare_exchange_weak :: inline proc "contextless" (dst: ^$T, old, new: T
atomic_add :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
#complete switch order {
switch order {
case .Relaxed: return intrinsics.atomic_add_relaxed(dst, val);
case .Release: return intrinsics.atomic_add_rel(dst, val);
case .Acquire: return intrinsics.atomic_add_acq(dst, val);
@@ -150,7 +150,7 @@ atomic_add :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) ->
}
atomic_sub :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
#complete switch order {
switch order {
case .Relaxed: return intrinsics.atomic_sub_relaxed(dst, val);
case .Release: return intrinsics.atomic_sub_rel(dst, val);
case .Acquire: return intrinsics.atomic_sub_acq(dst, val);
@@ -162,7 +162,7 @@ atomic_sub :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) ->
}
atomic_and :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
#complete switch order {
switch order {
case .Relaxed: return intrinsics.atomic_and_relaxed(dst, val);
case .Release: return intrinsics.atomic_and_rel(dst, val);
case .Acquire: return intrinsics.atomic_and_acq(dst, val);
@@ -174,7 +174,7 @@ atomic_and :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) ->
}
atomic_nand :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
#complete switch order {
switch order {
case .Relaxed: return intrinsics.atomic_nand_relaxed(dst, val);
case .Release: return intrinsics.atomic_nand_rel(dst, val);
case .Acquire: return intrinsics.atomic_nand_acq(dst, val);
@@ -186,7 +186,7 @@ atomic_nand :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) ->
}
atomic_or :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
#complete switch order {
switch order {
case .Relaxed: return intrinsics.atomic_or_relaxed(dst, val);
case .Release: return intrinsics.atomic_or_rel(dst, val);
case .Acquire: return intrinsics.atomic_or_acq(dst, val);
@@ -198,7 +198,7 @@ atomic_or :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T
}
atomic_xor :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
#complete switch order {
switch order {
case .Relaxed: return intrinsics.atomic_xor_relaxed(dst, val);
case .Release: return intrinsics.atomic_xor_rel(dst, val);
case .Acquire: return intrinsics.atomic_xor_acq(dst, val);
+27
View File
@@ -0,0 +1,27 @@
package sync
foreign {
@(link_name="llvm.x86.sse2.pause")
yield_processor :: proc() ---
}
Ticket_Mutex :: struct {
ticket: u64,
serving: u64,
}
ticket_mutex_init :: proc(m: ^Ticket_Mutex) {
atomic_store(&m.ticket, 0, .Relaxed);
atomic_store(&m.serving, 0, .Relaxed);
}
ticket_mutex_lock :: inline proc(m: ^Ticket_Mutex) {
ticket := atomic_add(&m.ticket, 1, .Relaxed);
for ticket != m.serving {
yield_processor();
}
}
ticket_mutex_unlock :: inline proc(m: ^Ticket_Mutex) {
atomic_add(&m.serving, 1, .Relaxed);
}
+41
View File
@@ -0,0 +1,41 @@
package sync
import "core:sys/darwin"
import "core:c"
// The Darwin docs say it best:
// A semaphore is much like a lock, except that a finite number of threads can hold it simultaneously.
// Semaphores can be thought of as being much like piles of tokens; multiple threads can take these tokens,
// but when there are none left, a thread must wait until another thread returns one.
Semaphore :: struct #align 16 {
handle: darwin.semaphore_t,
}
// TODO(tetra): Only marked with alignment because we cannot mark distinct integers with alignments.
// See core/sys/unix/pthread_linux.odin/pthread_t.
semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
ct := darwin.mach_task_self();
res := darwin.semaphore_create(ct, &s.handle, 0, c.int(initial_count));
assert(res == 0);
}
semaphore_destroy :: proc(s: ^Semaphore) {
ct := darwin.mach_task_self();
res := darwin.semaphore_destroy(ct, s.handle);
assert(res == 0);
s.handle = {};
}
semaphore_post :: proc(s: ^Semaphore, count := 1) {
// NOTE: SPEED: If there's one syscall to do this, we should use it instead of the loop.
for in 0..count-1 {
res := darwin.semaphore_signal(s.handle);
assert(res == 0);
}
}
semaphore_wait_for :: proc(s: ^Semaphore) {
res := darwin.semaphore_wait(s.handle);
assert(res == 0);
}
+18 -85
View File
@@ -1,98 +1,31 @@
package sync
/*
import "core:sys/unix"
import "core:atomics"
import "core:os"
Semaphore :: struct {
// _handle: win32.Handle,
// The Darwin docs say it best:
// A semaphore is much like a lock, except that a finite number of threads can hold it simultaneously.
// Semaphores can be thought of as being much like piles of tokens; multiple threads can take these tokens,
// but when there are none left, a thread must wait until another thread returns one.
Semaphore :: struct #align 16 {
handle: unix.sem_t,
}
Mutex :: struct {
_semaphore: Semaphore,
_counter: i32,
_owner: i32,
_recursion: i32,
}
current_thread_id :: proc() -> i32 {
return i32(os.current_thread_id());
}
semaphore_init :: proc(s: ^Semaphore) {
// s._handle = win32.CreateSemaphoreA(nil, 0, 1<<31-1, nil);
semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
assert(unix.sem_init(&s.handle, 0, u32(initial_count)) == 0);
}
semaphore_destroy :: proc(s: ^Semaphore) {
// win32.CloseHandle(s._handle);
assert(unix.sem_destroy(&s.handle) == 0);
s.handle = {};
}
semaphore_post :: proc(s: ^Semaphore, count: int) {
// win32.ReleaseSemaphore(s._handle, cast(i32)count, nil);
semaphore_post :: proc(s: ^Semaphore, count := 1) {
// NOTE: SPEED: If there's one syscall to do this, we should use it instead of the loop.
for in 0..count-1 {
assert(unix.sem_post(&s.handle) == 0);
}
}
semaphore_release :: inline proc(s: ^Semaphore) {
semaphore_post(s, 1);
semaphore_wait_for :: proc(s: ^Semaphore) {
assert(unix.sem_wait(&s.handle) == 0);
}
semaphore_wait :: proc(s: ^Semaphore) {
// win32.WaitForSingleObject(s._handle, win32.INFINITE);
}
mutex_init :: proc(m: ^Mutex) {
atomics.store(&m._counter, 0);
atomics.store(&m._owner, current_thread_id());
semaphore_init(&m._semaphore);
m._recursion = 0;
}
mutex_destroy :: proc(m: ^Mutex) {
semaphore_destroy(&m._semaphore);
}
mutex_lock :: proc(m: ^Mutex) {
thread_id := current_thread_id();
if atomics.fetch_add(&m._counter, 1) > 0 {
if thread_id != atomics.load(&m._owner) {
semaphore_wait(&m._semaphore);
}
}
atomics.store(&m._owner, thread_id);
m._recursion += 1;
}
mutex_try_lock :: proc(m: ^Mutex) -> bool {
thread_id := current_thread_id();
if atomics.load(&m._owner) == thread_id {
atomics.fetch_add(&m._counter, 1);
} else {
expected: i32 = 0;
if atomics.load(&m._counter) != 0 {
return false;
}
if atomics.compare_exchange(&m._counter, expected, 1) == 0 {
return false;
}
atomics.store(&m._owner, thread_id);
}
m._recursion += 1;
return true;
}
mutex_unlock :: proc(m: ^Mutex) {
recursion: i32;
thread_id := current_thread_id();
assert(thread_id == atomics.load(&m._owner));
m._recursion -= 1;
recursion = m._recursion;
if recursion == 0 {
atomics.store(&m._owner, thread_id);
}
if atomics.fetch_add(&m._counter, -1) > 1 {
if recursion == 0 {
semaphore_release(&m._semaphore);
}
}
}
*/
+99
View File
@@ -0,0 +1,99 @@
// +build linux, darwin
package sync
import "core:sys/unix"
// A lock that can only be held by one thread at once.
Mutex :: struct {
handle: unix.pthread_mutex_t,
}
// Blocks until signalled, and then lets past exactly
// one thread.
Condition :: struct {
handle: unix.pthread_cond_t,
// NOTE(tetra, 2019-11-11): Used to mimic the more sane behavior of Windows' AutoResetEvent.
// This means that you may signal the condition before anyone is waiting to cause the
// next thread that tries to wait to just pass by uninterrupted, without sleeping.
// Without this, signalling a condition will only wake up a thread which is already waiting,
// but not one that is about to wait, which can cause your program to become out of sync in
// ways that are hard to debug or fix.
flag: bool, // atomically mutated
mutex: Mutex,
}
mutex_init :: proc(m: ^Mutex) {
// NOTE(tetra, 2019-11-01): POSIX OOM if we cannot init the attrs or the mutex.
attrs: unix.pthread_mutexattr_t;
assert(unix.pthread_mutexattr_init(&attrs) == 0);
defer unix.pthread_mutexattr_destroy(&attrs); // ignores destruction error
assert(unix.pthread_mutex_init(&m.handle, &attrs) == 0);
}
mutex_destroy :: proc(m: ^Mutex) {
assert(unix.pthread_mutex_destroy(&m.handle) == 0);
m.handle = {};
}
mutex_lock :: proc(m: ^Mutex) {
assert(unix.pthread_mutex_lock(&m.handle) == 0);
}
// Returns false if someone else holds the lock.
mutex_try_lock :: proc(m: ^Mutex) -> bool {
return unix.pthread_mutex_trylock(&m.handle) == 0;
}
mutex_unlock :: proc(m: ^Mutex) {
assert(unix.pthread_mutex_unlock(&m.handle) == 0);
}
condition_init :: proc(c: ^Condition) {
// NOTE(tetra, 2019-11-01): POSIX OOM if we cannot init the attrs or the condition.
attrs: unix.pthread_condattr_t;
assert(unix.pthread_condattr_init(&attrs) == 0);
defer unix.pthread_condattr_destroy(&attrs); // ignores destruction error
assert(unix.pthread_cond_init(&c.handle, &attrs) == 0);
mutex_init(&c.mutex);
c.flag = false;
}
condition_destroy :: proc(c: ^Condition) {
assert(unix.pthread_cond_destroy(&c.handle) == 0);
mutex_destroy(&c.mutex);
c.handle = {};
}
// Awaken exactly one thread who is waiting on the condition.
condition_signal :: proc(c: ^Condition) {
mutex_lock(&c.mutex);
defer mutex_unlock(&c.mutex);
atomic_swap(&c.flag, true, .Sequentially_Consistent);
assert(unix.pthread_cond_signal(&c.handle) == 0);
}
// Wait for the condition to be signalled.
// Does not block if the condition has been signalled and no one
// has waited on it yet.
condition_wait_for :: proc(c: ^Condition) {
mutex_lock(&c.mutex);
defer mutex_unlock(&c.mutex);
// NOTE(tetra): If a thread comes by and steals the flag immediately after the signal occurs,
// the thread that gets signalled and wakes up, discovers that the flag was taken and goes
// back to sleep.
// Though this overall behavior is the most sane, there may be a better way to do this that means that
// the first thread to wait, gets the flag first.
if atomic_swap(&c.flag, false, .Sequentially_Consistent) do return;
for {
assert(unix.pthread_cond_wait(&c.handle, &c.mutex.handle) == 0);
if atomic_swap(&c.flag, false, .Sequentially_Consistent) do break;
}
}
+22 -47
View File
@@ -1,51 +1,40 @@
// +build windows
package sync
import "core:sys/win32"
foreign {
@(link_name="llvm.x86.sse2.pause")
yield_processor :: proc() ---
}
Semaphore :: struct {
_handle: win32.Handle,
}
// A lock that can only be held by one thread at once.
Mutex :: struct {
_critical_section: win32.Critical_Section,
}
// Blocks until signalled.
// When signalled, awakens exactly one waiting thread.
Condition :: struct {
event: win32.Handle,
}
Ticket_Mutex :: struct {
ticket: u64,
serving: u64,
// When waited upon, blocks until the internal count is greater than zero, then subtracts one.
// Posting to the semaphore increases the count by one, or the provided amount.
Semaphore :: struct {
_handle: win32.Handle,
}
current_thread_id :: proc() -> i32 {
return i32(win32.get_current_thread_id());
}
semaphore_init :: proc(s: ^Semaphore) {
s._handle = win32.create_semaphore_w(nil, 0, 1<<31-1, nil);
semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
s._handle = win32.create_semaphore_w(nil, i32(initial_count), 1<<31-1, nil);
}
semaphore_destroy :: proc(s: ^Semaphore) {
win32.close_handle(s._handle);
}
semaphore_post :: proc(s: ^Semaphore, count: int) {
semaphore_post :: proc(s: ^Semaphore, count := 1) {
win32.release_semaphore(s._handle, i32(count), nil);
}
semaphore_release :: inline proc(s: ^Semaphore) {
semaphore_post(s, 1);
}
semaphore_wait :: proc(s: ^Semaphore) {
semaphore_wait_for :: proc(s: ^Semaphore) {
// NOTE(tetra, 2019-10-30): wait_for_single_object decrements the count before it returns.
result := win32.wait_for_single_object(s._handle, win32.INFINITE);
assert(result != win32.WAIT_FAILED);
}
@@ -73,10 +62,19 @@ mutex_unlock :: proc(m: ^Mutex) {
condition_init :: proc(using c: ^Condition) {
// create an auto-reset event.
// NOTE(tetra, 2019-10-30): this will, when signalled, signal exactly one waiting thread
// and then reset itself automatically.
event = win32.create_event_w(nil, false, false, nil);
assert(event != nil);
}
condition_destroy :: proc(using c: ^Condition) {
if event != nil {
win32.close_handle(event);
}
}
condition_signal :: proc(using c: ^Condition) {
ok := win32.set_event(event);
assert(bool(ok));
@@ -86,26 +84,3 @@ condition_wait_for :: proc(using c: ^Condition) {
result := win32.wait_for_single_object(event, win32.INFINITE);
assert(result != win32.WAIT_FAILED);
}
condition_destroy :: proc(using c: ^Condition) {
if event != nil {
win32.close_handle(event);
}
}
ticket_mutex_init :: proc(m: ^Ticket_Mutex) {
atomic_store(&m.ticket, 0, Ordering.Relaxed);
atomic_store(&m.serving, 0, Ordering.Relaxed);
}
ticket_mutex_lock :: inline proc(m: ^Ticket_Mutex) {
ticket := atomic_add(&m.ticket, 1, Ordering.Relaxed);
for ticket != m.serving {
yield_processor();
}
}
ticket_mutex_unlock :: inline proc(m: ^Ticket_Mutex) {
atomic_add(&m.serving, 1, Ordering.Relaxed);
}
+29
View File
@@ -0,0 +1,29 @@
package darwin;
foreign import "system:pthread"
import "core:c"
// NOTE(tetra): Unclear whether these should be aligned 16 or not.
// However all other sync primitives are aligned for robustness.
// I cannot currently align these though.
// See core/sys/unix/pthread_linux.odin/pthread_t.
task_t :: distinct u64;
semaphore_t :: distinct u64;
kern_return_t :: distinct u64;
thread_act_t :: distinct u64;
@(default_calling_convention="c")
foreign pthread {
mach_task_self :: proc() -> task_t ---;
semaphore_create :: proc(task: task_t, semaphore: ^semaphore_t, policy, value: c.int) -> kern_return_t ---;
semaphore_destroy :: proc(task: task_t, semaphore: semaphore_t) -> kern_return_t ---;
semaphore_signal :: proc(semaphore: semaphore_t) -> kern_return_t ---;
semaphore_signal_all :: proc(semaphore: semaphore_t) -> kern_return_t ---;
semaphore_signal_thread :: proc(semaphore: semaphore_t, thread: thread_act_t) -> kern_return_t ---;
semaphore_wait :: proc(semaphore: semaphore_t) -> kern_return_t ---;
}
-24
View File
@@ -1,24 +0,0 @@
ENTRY(_start)
SECTIONS
{
. = 0x100000;
.text BLOCK(4K) : ALIGN(4K)
{
*(.text)
}
.rodata BLOCK(4K) : ALIGN(4K)
{
*(.rodata)
}
.data BLOCK(4K) : ALIGN(4K)
{
*(.data)
}
.bss BLOCK(4K) : ALIGN(4K)
{
*(COMMON)
*(.bss)
}
}
+80
View File
@@ -0,0 +1,80 @@
package unix;
import "core:c"
// NOTE(tetra): No 32-bit Macs.
// Source: _pthread_types.h on my Mac.
PTHREAD_SIZE :: 8176;
PTHREAD_ATTR_SIZE :: 56;
PTHREAD_MUTEXATTR_SIZE :: 8;
PTHREAD_MUTEX_SIZE :: 56;
PTHREAD_CONDATTR_SIZE :: 8;
PTHREAD_COND_SIZE :: 40;
PTHREAD_ONCE_SIZE :: 8;
PTHREAD_RWLOCK_SIZE :: 192;
PTHREAD_RWLOCKATTR_SIZE :: 16;
pthread_t :: opaque struct #align 16 {
sig: c.long,
cleanup_stack: rawptr,
_: [PTHREAD_SIZE] c.char,
};
pthread_attr_t :: opaque struct #align 16 {
sig: c.long,
_: [PTHREAD_ATTR_SIZE] c.char,
};
pthread_cond_t :: opaque struct #align 16 {
sig: c.long,
_: [PTHREAD_COND_SIZE] c.char,
};
pthread_condattr_t :: opaque struct #align 16 {
sig: c.long,
_: [PTHREAD_CONDATTR_SIZE] c.char,
};
pthread_mutex_t :: opaque struct #align 16 {
sig: c.long,
_: [PTHREAD_MUTEX_SIZE] c.char,
};
pthread_mutexattr_t :: opaque struct #align 16 {
sig: c.long,
_: [PTHREAD_MUTEXATTR_SIZE] c.char,
};
pthread_once_t :: opaque struct #align 16 {
sig: c.long,
_: [PTHREAD_ONCE_SIZE] c.char,
};
pthread_rwlock_t :: opaque struct #align 16 {
sig: c.long,
_: [PTHREAD_RWLOCK_SIZE] c.char,
};
pthread_rwlockattr_t :: opaque struct #align 16 {
sig: c.long,
_: [PTHREAD_RWLOCKATTR_SIZE] c.char,
};
SCHED_OTHER :: 1; // Avoid if you are writing portable software.
SCHED_FIFO :: 4;
SCHED_RR :: 2; // Round robin.
SCHED_PARAM_SIZE :: 4;
sched_param :: struct {
sched_priority: c.int,
_: [SCHED_PARAM_SIZE] c.char,
};
// Source: https://github.com/apple/darwin-libpthread/blob/03c4628c8940cca6fd6a82957f683af804f62e7f/pthread/pthread.h#L138
PTHREAD_CREATE_JOINABLE :: 1;
PTHREAD_CREATE_DETACHED :: 2;
PTHREAD_INHERIT_SCHED :: 1;
PTHREAD_EXPLICIT_SCHED :: 2;
PTHREAD_PROCESS_SHARED :: 1;
PTHREAD_PROCESS_PRIVATE :: 2;
+110
View File
@@ -0,0 +1,110 @@
package unix;
import "core:c"
// TODO(tetra): For robustness, I'd like to mark this with align 16.
// I cannot currently do this.
// And at the time of writing there is a bug with putting it
// as the only field in a struct.
pthread_t :: distinct u64;
// pthread_t :: struct #align 16 { x: u64 };
// NOTE(tetra): Got all the size constants from pthreadtypes-arch.h on my
// Linux machine.
PTHREAD_COND_T_SIZE :: 48;
PTHREAD_MUTEXATTR_T_SIZE :: 4;
PTHREAD_CONDATTR_T_SIZE :: 4;
PTHREAD_RWLOCKATTR_T_SIZE :: 8;
PTHREAD_BARRIERATTR_T_SIZE :: 4;
// WARNING: The sizes of these things are different yet again
// on non-X86!
when size_of(int) == 8 {
PTHREAD_ATTR_T_SIZE :: 56;
PTHREAD_MUTEX_T_SIZE :: 40;
PTHREAD_RWLOCK_T_SIZE :: 56;
PTHREAD_BARRIER_T_SIZE :: 32;
} else when size_of(int) == 4 {
PTHREAD_ATTR_T_SIZE :: 32;
PTHREAD_MUTEX_T_SIZE :: 32;
PTHREAD_RWLOCK_T_SIZE :: 44;
PTHREAD_BARRIER_T_SIZE :: 20;
}
pthread_cond_t :: opaque struct #align 16 {
_: [PTHREAD_COND_T_SIZE] c.char,
};
pthread_mutex_t :: opaque struct #align 16 {
_: [PTHREAD_MUTEX_T_SIZE] c.char,
};
pthread_rwlock_t :: opaque struct #align 16 {
_: [PTHREAD_RWLOCK_T_SIZE] c.char,
};
pthread_barrier_t :: opaque struct #align 16 {
_: [PTHREAD_BARRIER_T_SIZE] c.char,
};
pthread_attr_t :: opaque struct #align 16 {
_: [PTHREAD_ATTR_T_SIZE] c.char,
};
pthread_condattr_t :: opaque struct #align 16 {
_: [PTHREAD_CONDATTR_T_SIZE] c.char,
};
pthread_mutexattr_t :: opaque struct #align 16 {
_: [PTHREAD_MUTEXATTR_T_SIZE] c.char,
};
pthread_rwlockattr_t :: opaque struct #align 16 {
_: [PTHREAD_RWLOCKATTR_T_SIZE] c.char,
};
pthread_barrierattr_t :: opaque struct #align 16 {
_: [PTHREAD_BARRIERATTR_T_SIZE] c.char,
};
// TODO(tetra, 2019-11-01): Maybe make `enum c.int`s for these?
PTHREAD_CREATE_JOINABLE :: 0;
PTHREAD_CREATE_DETACHED :: 1;
PTHREAD_INHERIT_SCHED :: 0;
PTHREAD_EXPLICIT_SCHED :: 1;
PTHREAD_PROCESS_PRIVATE :: 0;
PTHREAD_PROCESS_SHARED :: 1;
SCHED_OTHER :: 0;
SCHED_FIFO :: 1;
SCHED_RR :: 2; // Round robin.
sched_param :: struct {
sched_priority: c.int,
}
sem_t :: struct #align 16 {
_: [SEM_T_SIZE] c.char,
}
when size_of(int) == 8 {
SEM_T_SIZE :: 32;
} else when size_of(int) == 4 {
SEM_T_SIZE :: 16;
}
foreign import "system:pthread"
@(default_calling_convention="c")
foreign pthread {
// create named semaphore.
// used in process-shared semaphores.
sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t ---;
sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int ---;
sem_destroy :: proc(sem: ^sem_t) -> c.int ---;
sem_post :: proc(sem: ^sem_t) -> c.int ---;
sem_wait :: proc(sem: ^sem_t) -> c.int ---;
sem_trywait :: proc(sem: ^sem_t) -> c.int ---;
// sem_timedwait :: proc(sem: ^sem_t, timeout: time.TimeSpec) -> c.int ---;
// NOTE: unclear whether pthread_yield is well-supported on Linux systems,
// see https://linux.die.net/man/3/pthread_yield
pthread_yield :: proc() -> c.int ---;
}
+110
View File
@@ -0,0 +1,110 @@
package unix;
foreign import "system:pthread"
import "core:c"
import "core:time"
//
// On success, these functions return 0.
//
@(default_calling_convention="c")
foreign pthread {
pthread_create :: proc(t: ^pthread_t, attrs: ^pthread_attr_t, routine: proc(data: rawptr) -> rawptr, arg: rawptr) -> c.int ---;
// retval is a pointer to a location to put the return value of the thread proc.
pthread_join :: proc(t: pthread_t, retval: rawptr) -> c.int ---;
pthread_self :: proc() -> pthread_t ---;
pthread_equal :: proc(a, b: pthread_t) -> b32 ---;
sched_get_priority_min :: proc(policy: c.int) -> c.int ---;
sched_get_priority_max :: proc(policy: c.int) -> c.int ---;
// NOTE: POSIX says this can fail with OOM.
pthread_attr_init :: proc(attrs: ^pthread_attr_t) -> c.int ---;
pthread_attr_destroy :: proc(attrs: ^pthread_attr_t) -> c.int ---;
pthread_attr_getschedparam :: proc(attrs: ^pthread_attr_t, param: ^sched_param) -> c.int ---;
pthread_attr_setschedparam :: proc(attrs: ^pthread_attr_t, param: ^sched_param) -> c.int ---;
pthread_attr_getschedpolicy :: proc(t: ^pthread_attr_t, policy: ^c.int) -> c.int ---;
pthread_attr_setschedpolicy :: proc(t: ^pthread_attr_t, policy: c.int) -> c.int ---;
// states: PTHREAD_CREATE_DETACHED, PTHREAD_CREATE_JOINABLE
pthread_attr_setdetachstate :: proc(attrs: ^pthread_attr_t, detach_state: c.int) -> c.int ---;
// scheds: PTHREAD_INHERIT_SCHED, PTHREAD_EXPLICIT_SCHED
pthread_attr_setinheritsched :: proc(attrs: ^pthread_attr_t, sched: c.int) -> c.int ---;
// NOTE(tetra, 2019-11-06): WARNING: Different systems have different alignment requirements.
// For maximum usefulness, use the OS's page size.
// ALSO VERY MAJOR WARNING: `stack_ptr` must be the LAST byte of the stack on systems
// where the stack grows downwards, which is the common case, so far as I know.
// On systems where it grows upwards, give the FIRST byte instead.
// ALSO SLIGHTLY LESS MAJOR WARNING: Using this procedure DISABLES automatically-provided
// guard pages. If you are using this procedure, YOU must set them up manually.
// If you forget to do this, you WILL get stack corruption bugs if you do not EXTREMELY
// know what you are doing!
pthread_attr_setstack :: proc(attrs: ^pthread_attr_t, stack_ptr: rawptr, stack_size: u64) -> c.int ---;
pthread_attr_getstack :: proc(attrs: ^pthread_attr_t, stack_ptr: ^rawptr, stack_size: ^u64) -> c.int ---;
sched_yield :: proc() -> c.int ---;
}
@(default_calling_convention="c")
foreign pthread {
// NOTE: POSIX says this can fail with OOM.
pthread_cond_init :: proc(cond: ^pthread_cond_t, attrs: ^pthread_condattr_t) -> c.int ---;
pthread_cond_destroy :: proc(cond: ^pthread_cond_t) -> c.int ---;
pthread_cond_signal :: proc(cond: ^pthread_cond_t) -> c.int ---;
// same as signal, but wakes up _all_ threads that are waiting
pthread_cond_broadcast :: proc(cond: ^pthread_cond_t) -> c.int ---;
// assumes the mutex is pre-locked
pthread_cond_wait :: proc(cond: ^pthread_cond_t, mutex: ^pthread_mutex_t) -> c.int ---;
pthread_cond_timedwait :: proc(cond: ^pthread_cond_t, mutex: ^pthread_mutex_t, timeout: ^time.TimeSpec) -> c.int ---;
pthread_condattr_init :: proc(attrs: ^pthread_condattr_t) -> c.int ---;
pthread_condattr_destroy :: proc(attrs: ^pthread_condattr_t) -> c.int ---;
// p-shared = "process-shared" - i.e: is this condition shared among multiple processes?
// values: PTHREAD_PROCESS_PRIVATE, PTHREAD_PROCESS_SHARED
pthread_condattr_setpshared :: proc(attrs: ^pthread_condattr_t, value: c.int) -> c.int ---;
pthread_condattr_getpshared :: proc(attrs: ^pthread_condattr_t, result: ^c.int) -> c.int ---;
}
@(default_calling_convention="c")
foreign pthread {
// NOTE: POSIX says this can fail with OOM.
pthread_mutex_init :: proc(mutex: ^pthread_mutex_t, attrs: ^pthread_mutexattr_t) -> c.int ---;
pthread_mutex_destroy :: proc(mutex: ^pthread_mutex_t) -> c.int ---;
pthread_mutex_trylock :: proc(mutex: ^pthread_mutex_t) -> c.int ---;
pthread_mutex_lock :: proc(mutex: ^pthread_mutex_t) -> c.int ---;
pthread_mutex_timedlock :: proc(mutex: ^pthread_mutex_t, timeout: ^time.TimeSpec) -> c.int ---;
pthread_mutex_unlock :: proc(mutex: ^pthread_mutex_t) -> c.int ---;
pthread_mutexattr_init :: proc(attrs: ^pthread_mutexattr_t) -> c.int ---;
pthread_mutexattr_destroy :: proc(attrs: ^pthread_mutexattr_t) -> c.int ---;
// p-shared = "process-shared" - i.e: is this mutex shared among multiple processes?
// values: PTHREAD_PROCESS_PRIVATE, PTHREAD_PROCESS_SHARED
pthread_mutexattr_setpshared :: proc(attrs: ^pthread_mutexattr_t, value: c.int) -> c.int ---;
pthread_mutexattr_getpshared :: proc(attrs: ^pthread_mutexattr_t, result: ^c.int) -> c.int ---;
}
+4 -5
View File
@@ -89,7 +89,6 @@ _open_file_dialog :: proc(title: string, dir: string,
// Filters need to be passed as a pair of strings (title, filter)
filter_len := u32(len(filters));
if filter_len % 2 != 0 do return "", false;
default_filter = clamp(default_filter, 1, filter_len / 2);
filter: string;
filter = strings.join(filters, "\u0000", context.temp_allocator);
@@ -102,7 +101,7 @@ _open_file_dialog :: proc(title: string, dir: string,
title = utf8_to_wstring(title, context.temp_allocator),
filter = utf8_to_wstring(filter, context.temp_allocator),
initial_dir = utf8_to_wstring(dir, context.temp_allocator),
filter_index = u32(default_filter),
filter_index = u32(clamp(default_filter, 1, filter_len / 2)),
def_ext = utf8_to_wstring(default_ext, context.temp_allocator),
flags = u32(flags),
};
@@ -121,7 +120,7 @@ _open_file_dialog :: proc(title: string, dir: string,
return "", false;
}
file_name := ucs2_to_utf8(file_buf[:], allocator);
file_name := utf16_to_utf8(file_buf[:], allocator);
path = strings.trim_right_null(file_name);
return;
}
@@ -143,7 +142,7 @@ select_file_to_save :: proc(title := SAVE_TITLE, dir := ".",
return;
}
// TODO: Implement convenience function for select_file_to_open with ALLOW_MULTI_SELECT that takes
// TODO: Implement convenience function for select_file_to_open with ALLOW_MULTI_SELECT that takes
// it output of the form "path\u0000\file1u\0000file2" and turns it into []string with the path + file pre-concatenated for you.
OFN_ALLOWMULTISELECT :: 0x00000200; // NOTE(Jeroen): Without OFN_EXPLORER it uses the Win3 dialog.
@@ -186,4 +185,4 @@ CDERR_LOCKRESFAILURE :: 0x00000008;
CDERR_MEMALLOCFAILURE :: 0x00000009;
CDERR_MEMLOCKFAILURE :: 0x0000000A;
CDERR_NOHOOK :: 0x0000000B;
CDERR_REGISTERMSGFAIL :: 0x0000000C;
CDERR_REGISTERMSGFAIL :: 0x0000000C;
+2 -2
View File
@@ -9,6 +9,6 @@ foreign {
get_cwd :: proc(allocator := context.temp_allocator) -> string {
buffer := make([]u16, MAX_PATH_WIDE, allocator);
_get_cwd_wide(Wstring(&buffer[0]), MAX_PATH_WIDE);
file := ucs2_to_utf8(buffer[:], allocator);
file := utf16_to_utf8(buffer[:], allocator);
return strings.trim_right_null(file);
}
}
+26 -5
View File
@@ -300,6 +300,25 @@ File_Notify_Information :: struct {
file_name: [1]u16,
}
// https://docs.microsoft.com/en-gb/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info
System_Info :: struct {
using _: struct #raw_union {
oem_id: u32,
using _: struct #raw_union {
processor_architecture: u16,
_: u16, // reserved
},
},
page_size: u32,
minimum_application_address: rawptr,
maximum_application_address: rawptr,
active_processor_mask: u32,
number_of_processors: u32,
processor_type: u32,
allocation_granularity: u32,
processor_level: u16,
processor_revision: u16,
}
// https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_osversioninfoexa
OS_Version_Info_Ex_A :: struct {
@@ -728,19 +747,21 @@ CP_UTF8 :: 65001; // UTF-8 translation
MB_ERR_INVALID_CHARS :: 8;
WC_ERR_INVALID_CHARS :: 128;
utf8_to_ucs2 :: proc(s: string, allocator := context.temp_allocator) -> []u16 {
utf8_to_utf16 :: proc(s: string, allocator := context.temp_allocator) -> []u16 {
if len(s) < 1 {
return nil;
}
n := multi_byte_to_wide_char(CP_UTF8, MB_ERR_INVALID_CHARS, cstring(&s[0]), i32(len(s)), nil, 0);
b := transmute([]byte)s;
cstr := cstring(&b[0]);
n := multi_byte_to_wide_char(CP_UTF8, MB_ERR_INVALID_CHARS, cstr, i32(len(s)), nil, 0);
if n == 0 {
return nil;
}
text := make([]u16, n+1, allocator);
n1 := multi_byte_to_wide_char(CP_UTF8, MB_ERR_INVALID_CHARS, cstring(&s[0]), i32(len(s)), Wstring(&text[0]), i32(n));
n1 := multi_byte_to_wide_char(CP_UTF8, MB_ERR_INVALID_CHARS, cstr, i32(len(s)), Wstring(&text[0]), i32(n));
if n1 == 0 {
delete(text, allocator);
return nil;
@@ -751,13 +772,13 @@ utf8_to_ucs2 :: proc(s: string, allocator := context.temp_allocator) -> []u16 {
return text[:len(text)-1];
}
utf8_to_wstring :: proc(s: string, allocator := context.temp_allocator) -> Wstring {
if res := utf8_to_ucs2(s, allocator); res != nil {
if res := utf8_to_utf16(s, allocator); res != nil {
return Wstring(&res[0]);
}
return nil;
}
ucs2_to_utf8 :: proc(s: []u16, allocator := context.temp_allocator) -> string {
utf16_to_utf8 :: proc(s: []u16, allocator := context.temp_allocator) -> string {
if len(s) < 1 {
return "";
}
+2 -1
View File
@@ -23,7 +23,7 @@ foreign kernel32 {
@(link_name="GetModuleFileNameA") get_module_file_name_a :: proc(module: Hmodule, filename: cstring, size: u32) -> u32 ---;
@(link_name="GetModuleFileNameW") get_module_file_name_w :: proc(module: Hmodule, filename: Wstring, size: u32) -> u32 ---;
@(link_name="Sleep") sleep :: proc(ms: i32) -> i32 ---;
@(link_name="Sleep") sleep :: proc(ms: u32) ---;
@(link_name="QueryPerformanceFrequency") query_performance_frequency :: proc(result: ^i64) -> i32 ---;
@(link_name="QueryPerformanceCounter") query_performance_counter :: proc(result: ^i64) -> i32 ---;
@(link_name="OutputDebugStringA") output_debug_string_a :: proc(c_str: cstring) ---;
@@ -31,6 +31,7 @@ foreign kernel32 {
@(link_name="GetCommandLineA") get_command_line_a :: proc() -> cstring ---;
@(link_name="GetCommandLineW") get_command_line_w :: proc() -> Wstring ---;
@(link_name="GetSystemMetrics") get_system_metrics :: proc(index: i32) -> i32 ---;
@(link_name="GetSystemInfo") get_system_info :: proc(info: ^System_Info) ---;
@(link_name="GetVersionExA") get_version :: proc(osvi: ^OS_Version_Info_Ex_A) ---;
@(link_name="GetCurrentThreadId") get_current_thread_id :: proc() -> u32 ---;
+1 -1
View File
@@ -182,7 +182,7 @@ foreign user32 {
@(link_name="DestroyIcon") destroy_icon :: proc(icon: Hicon) -> Bool ---;
@(link_name="LoadCursorA") load_cursor_a :: proc(instance: Hinstance, cursor_name: cstring) -> Hcursor ---;
@(link_name="LoadCursorW") load_cursor_w :: proc(instance: Hinstance, cursor_name: cstring) -> Hcursor ---;
@(link_name="LoadCursorW") load_cursor_w :: proc(instance: Hinstance, cursor_name: Wstring) -> Hcursor ---;
@(link_name="GetCursor") get_cursor :: proc() -> Hcursor ---;
@(link_name="SetCursor") set_cursor :: proc(cursor: Hcursor) -> Hcursor ---;
+15
View File
@@ -0,0 +1,15 @@
package thread
import "core:runtime"
Thread_Proc :: #type proc(^Thread);
Thread :: struct {
using specific: Thread_Os_Specific,
procedure: Thread_Proc,
data: rawptr,
user_index: int,
init_context: runtime.Context,
use_init_context: bool,
}
+147
View File
@@ -0,0 +1,147 @@
package thread
import "intrinsics"
import "core:sync"
import "core:mem"
Task_Status :: enum i32 {
Ready,
Busy,
Waiting,
Term,
}
Task_Proc :: #type proc(task: ^Task);
Task :: struct {
procedure: Task_Proc,
data: rawptr,
user_index: int,
}
Task_Id :: distinct i32;
INVALID_TASK_ID :: Task_Id(-1);
Pool :: struct {
allocator: mem.Allocator,
mutex: sync.Mutex,
sem_available: sync.Semaphore,
processing_task_count: int, // atomic
is_running: bool,
threads: []^Thread,
tasks: [dynamic]Task,
}
pool_init :: proc(pool: ^Pool, thread_count: int, allocator := context.allocator) {
worker_thread_internal :: proc(t: ^Thread) {
pool := (^Pool)(t.data);
for pool.is_running {
sync.semaphore_wait_for(&pool.sem_available);
if task, ok := pool_try_and_pop_task(pool); ok {
pool_do_work(pool, &task);
}
}
sync.semaphore_post(&pool.sem_available, 1);
}
context.allocator = allocator;
pool.allocator = allocator;
pool.tasks = make([dynamic]Task);
pool.threads = make([]^Thread, thread_count);
sync.mutex_init(&pool.mutex);
sync.semaphore_init(&pool.sem_available);
pool.is_running = true;
for _, i in pool.threads {
t := create(worker_thread_internal);
t.user_index = i;
t.data = pool;
pool.threads[i] = t;
}
}
pool_destroy :: proc(pool: ^Pool) {
delete(pool.tasks);
delete(pool.threads, pool.allocator);
sync.mutex_destroy(&pool.mutex);
sync.semaphore_destroy(&pool.sem_available);
}
pool_start :: proc(pool: ^Pool) {
for t in pool.threads {
start(t);
}
}
pool_join :: proc(pool: ^Pool) {
pool.is_running = false;
sync.semaphore_post(&pool.sem_available, len(pool.threads));
yield();
for t in pool.threads {
join(t);
}
}
pool_add_task :: proc(pool: ^Pool, procedure: Task_Proc, data: rawptr, user_index: int = 0) {
sync.mutex_lock(&pool.mutex);
defer sync.mutex_unlock(&pool.mutex);
task: Task;
task.procedure = procedure;
task.data = data;
task.user_index = user_index;
append(&pool.tasks, task);
sync.semaphore_post(&pool.sem_available, 1);
}
pool_try_and_pop_task :: proc(pool: ^Pool) -> (task: Task, got_task: bool = false) {
if sync.mutex_try_lock(&pool.mutex) {
if len(pool.tasks) != 0 {
intrinsics.atomic_add(&pool.processing_task_count, 1);
task = pool.tasks[0];
got_task = true;
ordered_remove(&pool.tasks, 0);
}
sync.mutex_unlock(&pool.mutex);
}
return;
}
pool_do_work :: proc(pool: ^Pool, task: ^Task) {
task.procedure(task);
intrinsics.atomic_sub(&pool.processing_task_count, 1);
}
pool_wait_and_process :: proc(pool: ^Pool) {
for len(pool.tasks) != 0 || intrinsics.atomic_load(&pool.processing_task_count) != 0 {
if task, ok := pool_try_and_pop_task(pool); ok {
pool_do_work(pool, &task);
}
// Safety kick
if len(pool.tasks) != 0 && intrinsics.atomic_load(&pool.processing_task_count) == 0 {
sync.mutex_lock(&pool.mutex);
sync.semaphore_post(&pool.sem_available, len(pool.tasks));
sync.mutex_unlock(&pool.mutex);
}
yield();
}
pool_join(pool);
}
+162
View File
@@ -0,0 +1,162 @@
// +build linux, darwin
package thread;
import "core:runtime"
import "core:sync"
import "core:sys/unix"
// NOTE(tetra): Aligned here because of core/unix/pthread_linux.odin/pthread_t.
// Also see core/sys/darwin/mach_darwin.odin/semaphore_t.
Thread_Os_Specific :: struct #align 16 {
unix_thread: unix.pthread_t, // NOTE: very large on Darwin, small on Linux.
// NOTE: pthread has a proc to query this, but it is marked
// as non-portable ("np") so we do this instead.
done: bool,
// since libpthread doesn't seem to have a way to create a thread
// in a suspended state, we have it wait on this gate, which we
// signal to start it.
// destroyed after thread is started.
start_gate: sync.Condition,
// if true, the thread has been started and the start_gate has been destroyed.
started: bool,
// NOTE: with pthreads, it is undefined behavior for multiple threads
// to call join on the same thread at the same time.
// this value is atomically updated to detect this.
// See the comment in `join`.
already_joined: bool,
}
Thread_Priority :: enum {
Normal,
Low,
High,
}
//
// Creates a thread which will run the given procedure.
// It then waits for `start` to be called.
//
// You may provide a slice of bytes to use as the stack for the new thread,
// but if you do, you are expected to set up the guard pages yourself.
//
// The stack must also be aligned appropriately for the platform.
// We require it's at least 16 bytes aligned to help robustness; other
// platforms may require page-size alignment.
// Note also that pthreads requires the stack is at least 6 OS pages in size:
// 4 are required by pthreads, and two extra for guards pages that will be applied.
//
create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
__linux_thread_entry_proc :: proc "c" (t: rawptr) -> rawptr {
t := (^Thread)(t);
sync.condition_wait_for(&t.start_gate);
sync.condition_destroy(&t.start_gate);
t.start_gate = {};
c := context;
if t.use_init_context {
c = t.init_context;
}
context = c;
t.procedure(t);
if !t.use_init_context {
if context.temp_allocator.data == &runtime.global_default_temp_allocator_data {
runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data);
}
}
sync.atomic_store(&t.done, true, .Sequentially_Consistent);
return nil;
}
attrs: unix.pthread_attr_t;
if unix.pthread_attr_init(&attrs) != 0 do return nil; // NOTE(tetra, 2019-11-01): POSIX OOM.
defer unix.pthread_attr_destroy(&attrs);
// NOTE(tetra, 2019-11-01): These only fail if their argument is invalid.
assert(unix.pthread_attr_setdetachstate(&attrs, unix.PTHREAD_CREATE_JOINABLE) == 0);
assert(unix.pthread_attr_setinheritsched(&attrs, unix.PTHREAD_EXPLICIT_SCHED) == 0);
thread := new(Thread);
if thread == nil do return nil;
// Set thread priority.
policy: i32;
res := unix.pthread_attr_getschedpolicy(&attrs, &policy);
assert(res == 0);
params: unix.sched_param;
res = unix.pthread_attr_getschedparam(&attrs, &params);
assert(res == 0);
low := unix.sched_get_priority_min(policy);
high := unix.sched_get_priority_max(policy);
switch priority {
case .Normal: // Okay
case .Low: params.sched_priority = low + 1;
case .High: params.sched_priority = high;
}
res = unix.pthread_attr_setschedparam(&attrs, &params);
assert(res == 0);
sync.condition_init(&thread.start_gate);
if unix.pthread_create(&thread.unix_thread, &attrs, __linux_thread_entry_proc, thread) != 0 {
free(thread);
return nil;
}
thread.procedure = procedure;
return thread;
}
start :: proc(t: ^Thread) {
if sync.atomic_swap(&t.started, true, .Sequentially_Consistent) do return;
sync.condition_signal(&t.start_gate);
}
is_done :: proc(t: ^Thread) -> bool {
return sync.atomic_load(&t.done, .Sequentially_Consistent);
}
join :: proc(t: ^Thread) {
if unix.pthread_equal(unix.pthread_self(), t.unix_thread) do return;
// if unix.pthread_self().x == t.unix_thread.x do return;
// NOTE(tetra): It's apparently UB for multiple threads to join the same thread
// at the same time.
// If someone else already did, spin until the thread dies.
// See note on `already_joined` field.
// TODO(tetra): I'm not sure if we should do this, or panic, since I'm not
// sure it makes sense to need to join from multiple threads?
if sync.atomic_swap(&t.already_joined, true, .Sequentially_Consistent) {
for {
if sync.atomic_load(&t.done, .Sequentially_Consistent) do return;
sync.yield_processor();
}
}
// NOTE(tetra): If we're already dead, don't bother calling to pthread_join as that
// will just return 3 (ESRCH).
// We do this instead because I don't know if there is a danger
// that you may join a different thread from the one you called join on,
// if the thread handle is reused.
if sync.atomic_load(&t.done, .Sequentially_Consistent) do return;
ret := unix.pthread_join(t.unix_thread, nil);
assert(ret == 0, "cannot join thread");
assert(sync.atomic_load(&t.done, .Sequentially_Consistent), "thread not done after join");
}
destroy :: proc(t: ^Thread) {
join(t);
t.unix_thread = {};
free(t);
}
yield :: proc() {
unix.sched_yield();
}
+40 -17
View File
@@ -1,27 +1,30 @@
package thread
import "core:runtime"
import "core:sync"
import "core:sys/win32"
Thread_Proc :: #type proc(^Thread) -> int;
Thread_Os_Specific :: struct {
win32_thread: win32.Handle,
win32_thread_id: u32,
done: bool, // see note in `is_done`
}
Thread :: struct {
using specific: Thread_Os_Specific,
procedure: Thread_Proc,
data: rawptr,
user_index: int,
THREAD_PRIORITY_IDLE :: -15;
THREAD_PRIORITY_LOWEST :: -2;
THREAD_PRIORITY_BELOW_NORMAL :: -1;
THREAD_PRIORITY_NORMAL :: 0;
THREAD_PRIORITY_ABOVE_NORMAL :: 1;
THREAD_PRIORITY_HIGHEST :: 2;
THREAD_PRIORITY_TIME_CRITICAL :: 15;
init_context: runtime.Context,
use_init_context: bool,
Thread_Priority :: enum i32 {
Normal = THREAD_PRIORITY_NORMAL,
Low = THREAD_PRIORITY_LOWEST,
High = THREAD_PRIORITY_HIGHEST,
}
create :: proc(procedure: Thread_Proc) -> ^Thread {
create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
win32_thread_id: u32;
__windows_thread_entry_proc :: proc "c" (t: ^Thread) -> i32 {
@@ -31,7 +34,16 @@ create :: proc(procedure: Thread_Proc) -> ^Thread {
}
context = c;
return i32(t.procedure(t));
t.procedure(t);
if !t.use_init_context {
if context.temp_allocator.data == &runtime.global_default_temp_allocator_data {
runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data);
}
}
sync.atomic_store(&t.done, true, .Sequentially_Consistent);
return 0;
}
@@ -47,6 +59,9 @@ create :: proc(procedure: Thread_Proc) -> ^Thread {
thread.win32_thread = win32_thread;
thread.win32_thread_id = win32_thread_id;
ok := win32.set_thread_priority(win32_thread, i32(priority));
assert(ok == true);
return thread;
}
@@ -55,14 +70,18 @@ start :: proc(using thread: ^Thread) {
}
is_done :: proc(using thread: ^Thread) -> bool {
res := win32.wait_for_single_object(win32_thread, 0);
return res != win32.WAIT_TIMEOUT;
// NOTE(tetra, 2019-10-31): Apparently using wait_for_single_object and
// checking if it didn't time out immediately, is not good enough,
// so we do it this way instead.
return sync.atomic_load(&done, .Sequentially_Consistent);
}
join :: proc(using thread: ^Thread) {
win32.wait_for_single_object(win32_thread, win32.INFINITE);
win32.close_handle(win32_thread);
win32_thread = win32.INVALID_HANDLE;
if win32_thread != win32.INVALID_HANDLE {
win32.wait_for_single_object(win32_thread, win32.INFINITE);
win32.close_handle(win32_thread);
win32_thread = win32.INVALID_HANDLE;
}
}
destroy :: proc(thread: ^Thread) {
@@ -73,3 +92,7 @@ destroy :: proc(thread: ^Thread) {
terminate :: proc(using thread : ^Thread, exit_code : u32) {
win32.terminate_thread(win32_thread, exit_code);
}
yield :: proc() {
win32.sleep(0);
}
+2
View File
@@ -0,0 +1,2 @@
package time
IS_SUPPORTED :: false;
-44
View File
@@ -1,44 +0,0 @@
package time
import "core:os";
// NOTE(Jeroen): The times returned are in UTC
IS_SUPPORTED :: true;
now :: proc() -> Time {
time_spec_now := os.clock_gettime(os.CLOCK_REALTIME);
ns := time_spec_now.tv_sec * 1e9 + time_spec_now.tv_nsec;
return Time{_nsec=ns};
}
boot_time :: proc() -> Time {
ts_now := os.clock_gettime(os.CLOCK_REALTIME);
ts_boottime := os.clock_gettime(os.CLOCK_BOOTTIME);
ns := (ts_now.tv_sec - ts_boottime.tv_sec) * 1e9 + ts_now.tv_nsec - ts_boottime.tv_nsec;
return Time{_nsec=ns};
}
seconds_since_boot :: proc() -> f64 {
ts_boottime := os.clock_gettime(os.CLOCK_BOOTTIME);
return f64(ts_boottime.tv_sec) + f64(ts_boottime.tv_nsec) / 1e9;
}
sleep :: proc(d: Duration) {
ds := duration_seconds(d);
seconds := u64(ds);
nanoseconds := i64((ds - f64(seconds)) * 1e9);
if seconds > 0 do os.sleep(seconds);
if nanoseconds > 0 do os.nanosleep(nanoseconds);
}
nanosleep :: proc(d: Duration) {
// NOTE(Jeroen): os.nanosleep returns -1 on failure, 0 on success
// duration needs to be [0, 999999999] nanoseconds.
os.nanosleep(i64(d));
}
-3
View File
@@ -1,3 +0,0 @@
package time
IS_SUPPORTED :: false;
+80
View File
@@ -0,0 +1,80 @@
//+build linux, darwin
package time
IS_SUPPORTED :: true; // NOTE: Times on Darwin are UTC.
foreign import libc "system:c"
@(default_calling_convention="c")
foreign libc {
@(link_name="clock_gettime") _unix_clock_gettime :: proc(clock_id: u64, timespec: ^TimeSpec) -> i32 ---;
@(link_name="sleep") _unix_sleep :: proc(seconds: u32) -> i32 ---;
@(link_name="nanosleep") _unix_nanosleep :: proc(requested: ^TimeSpec, remaining: ^TimeSpec) -> i32 ---;
}
TimeSpec :: struct {
tv_sec : i64, /* seconds */
tv_nsec : i64, /* nanoseconds */
};
CLOCK_REALTIME :: 0; // NOTE(tetra): May jump in time, when user changes the system time.
CLOCK_MONOTONIC :: 1; // NOTE(tetra): May stand still while system is asleep.
CLOCK_PROCESS_CPUTIME_ID :: 2;
CLOCK_THREAD_CPUTIME_ID :: 3;
CLOCK_MONOTONIC_RAW :: 4; // NOTE(tetra): "RAW" means: Not adjusted by NTP.
CLOCK_REALTIME_COARSE :: 5; // NOTE(tetra): "COARSE" clocks are apparently much faster, but not "fine-grained."
CLOCK_MONOTONIC_COARSE :: 6;
CLOCK_BOOTTIME :: 7; // NOTE(tetra): Same as MONOTONIC, except also including time system was asleep.
CLOCK_REALTIME_ALARM :: 8;
CLOCK_BOOTTIME_ALARM :: 9;
// TODO(tetra, 2019-11-05): The original implementation of this package for Darwin used this constants.
// I do not know if Darwin programmers are used to the existance of these constants or not, so
// I'm leaving aliases to them for now.
CLOCK_SYSTEM :: CLOCK_REALTIME;
CLOCK_CALENDAR :: CLOCK_MONOTONIC;
clock_gettime :: proc(clock_id: u64) -> TimeSpec {
ts : TimeSpec; // NOTE(tetra): Do we need to initialize this?
_unix_clock_gettime(clock_id, &ts);
return ts;
}
now :: proc() -> Time {
time_spec_now := clock_gettime(CLOCK_REALTIME);
ns := time_spec_now.tv_sec * 1e9 + time_spec_now.tv_nsec;
return Time{_nsec=ns};
}
boot_time :: proc() -> Time {
ts_now := clock_gettime(CLOCK_REALTIME);
ts_boottime := clock_gettime(CLOCK_BOOTTIME);
ns := (ts_now.tv_sec - ts_boottime.tv_sec) * 1e9 + ts_now.tv_nsec - ts_boottime.tv_nsec;
return Time{_nsec=ns};
}
seconds_since_boot :: proc() -> f64 {
ts_boottime := clock_gettime(CLOCK_BOOTTIME);
return f64(ts_boottime.tv_sec) + f64(ts_boottime.tv_nsec) / 1e9;
}
sleep :: proc(d: Duration) {
ds := duration_seconds(d);
seconds := u32(ds);
nanoseconds := i64((ds - f64(seconds)) * 1e9);
if seconds > 0 do _unix_sleep(seconds);
if nanoseconds > 0 do nanosleep(nanoseconds);
}
nanosleep :: proc(nanoseconds: i64) -> int {
// NOTE(tetra): Should we remove this assert? We are measuring nanoseconds after all...
assert(nanoseconds <= 999999999);
requested := TimeSpec{tv_nsec = nanoseconds};
remaining: TimeSpec; // NOTE(tetra): Do we need to initialize this?
return int(_unix_nanosleep(&requested, &remaining));
}
+1 -3
View File
@@ -17,8 +17,6 @@ now :: proc() -> Time {
return Time{_nsec=ns};
}
sleep :: proc(d: Duration) {
win32.sleep(i32(d/Millisecond));
win32.sleep(u32(d/Millisecond));
}
+2 -1
View File
@@ -21,7 +21,8 @@ decode_surrogate_pair :: proc(r1, r2: rune) -> rune {
}
encode_surrogate_pair :: proc(r: rune) -> (r1, r2: rune) {
encode_surrogate_pair :: proc(c: rune) -> (r1, r2: rune) {
r := c;
if r < _surr_self || r > MAX_RUNE {
return REPLACEMENT_CHAR, REPLACEMENT_CHAR;
}
+66 -21
View File
@@ -41,26 +41,22 @@ accept_ranges := [5]Accept_Range{
};
accept_sizes := [256]u8{
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x00-0x0f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x10-0x1f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x20-0x2f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x30-0x3f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x40-0x4f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x50-0x5f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x60-0x6f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x70-0x7f
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0x80-0x8f
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0x90-0x9f
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xa0-0xaf
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xb0-0xbf
0xf1, 0xf1, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xc0-0xcf
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xd0-0xdf
0x13, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x23, 0x03, 0x03, // 0xe0-0xef
0x34, 0x04, 0x04, 0x04, 0x44, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xf0-0xff
0x00..0x7f = 0xf0,
0x80..0xc1 = 0xf1,
0xc2..0xdf = 0x02,
0xe0 = 0x13,
0xe1..0xec = 0x03,
0xed = 0x23,
0xee..0xef = 0x03,
0xf0 = 0x34,
0xf1..0xf3 = 0x04,
0xf4 = 0x44,
0xf5..0xff = 0xf1,
};
encode_rune :: proc(r: rune) -> ([4]u8, int) {
encode_rune :: proc(c: rune) -> ([4]u8, int) {
r := c;
buf: [4]u8;
i := u32(r);
mask :: u8(0x3f);
@@ -94,7 +90,7 @@ encode_rune :: proc(r: rune) -> ([4]u8, int) {
return buf, 4;
}
decode_rune_in_string :: inline proc(s: string) -> (rune, int) do return decode_rune(cast([]u8)s);
decode_rune_in_string :: inline proc(s: string) -> (rune, int) do return decode_rune(transmute([]u8)s);
decode_rune :: proc(s: []u8) -> (rune, int) {
n := len(s);
if n < 1 {
@@ -134,7 +130,7 @@ decode_rune :: proc(s: []u8) -> (rune, int) {
decode_last_rune_in_string :: inline proc(s: string) -> (rune, int) do return decode_last_rune(cast([]u8)s);
decode_last_rune_in_string :: inline proc(s: string) -> (rune, int) do return decode_last_rune(transmute([]u8)s);
decode_last_rune :: proc(s: []u8) -> (rune, int) {
r: rune;
size: int;
@@ -165,9 +161,58 @@ decode_last_rune :: proc(s: []u8) -> (rune, int) {
return r, size;
}
rune_at_pos :: proc(s: string, pos: int) -> rune {
if pos < 0 {
return RUNE_ERROR;
}
i := 0;
for r in s {
if i == pos {
return r;
}
i += 1;
}
return RUNE_ERROR;
}
rune_string_at_pos :: proc(s: string, pos: int) -> string {
if pos < 0 {
return "";
}
i := 0;
for c, offset in s {
if i == pos {
w := rune_size(c);
return s[offset:][:w];
}
i += 1;
}
return "";
}
rune_at :: proc(s: string, byte_index: int) -> rune {
r, _ := decode_rune_in_string(s[byte_index:]);
return r;
}
// Returns the byte position of rune at position pos in s with an optional start byte position.
// Returns -1 if it runs out of the string.
rune_offset :: proc(s: string, pos: int, start: int = 0) -> int {
if pos < 0 {
return -1;
}
i := 0;
for _, offset in s[start:] {
if i == pos {
return offset+start;
}
i += 1;
}
return -1;
}
valid_rune :: proc(r: rune) -> bool {
if r < 0 {
@@ -215,7 +260,7 @@ valid_string :: proc(s: string) -> bool {
rune_start :: inline proc(b: u8) -> bool do return b&0xc0 != 0x80;
rune_count_in_string :: inline proc(s: string) -> int do return rune_count(cast([]u8)s);
rune_count_in_string :: inline proc(s: string) -> int do return rune_count(transmute([]u8)s);
rune_count :: proc(s: []u8) -> int {
count := 0;
n := len(s);
+1278 -304
View File
File diff suppressed because it is too large Load Diff
+7
View File
@@ -0,0 +1,7 @@
package basic
import "core:fmt"
main :: proc() {
fmt.println("Hellope!");
}
+67
View File
@@ -0,0 +1,67 @@
package eca
import "core:fmt"
import "core:math/rand"
import "core:time"
import "intrinsics"
elementary_cellular_automata :: proc(state: $T, rule: u8, generations: int, pause: time.Duration = 0)
where intrinsics.type_is_integer(T),
intrinsics.type_is_unsigned(T) {
N :: 8*size_of(state);
output :: proc(state: T) {
buf: [N]byte;
for i in 0..<T(N) {
c := byte('#');
// c := byte(rand.int_max(26) + 'A' + ('a'-'A')*rand.int_max(2));
buf[N-1-i] = state & (1<<i) != 0 ? c : ' ';
}
fmt.println(string(buf[:]));
}
bit :: proc(x, i: T) -> T {
return (x >> i) & 0x1;
}
set :: proc(x: ^T, cell, k: T, rule: u8) {
x^ &~= 1<<cell;
if rule>>k&1 != 0 {
x^ |= 1<<cell;
}
}
a := state;
a1 := T(0);
output(a);
last := T(N-1);
for r in 0..<generations {
if pause > 0 do time.sleep(pause);
k := bit(a, last) | bit(a, 0)<<1 | bit(a, 1)<<2;
set(&a1, 0, k, rule);
a1 |= (1<<0) * T(rule>>k&1);
for c in 1..<last {
k = k>>1 | bit(a, c+1)<<2;
set(&a1, c, k, rule);
}
set(&a1, last, k>>1|bit(a, 0)<<2, rule);
a, a1 = a1, a;
output(a);
if a == a1 {
return;
}
}
}
main :: proc() {
elementary_cellular_automata(
state=rand.uint128(),
rule=30,
generations=5000,
pause=100*time.Millisecond,
);
}
File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 75 KiB

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