Compare commits

..

422 Commits

Author SHA1 Message Date
gingerBill 74458ab096 Merge pull request #1989 from Kelimion/cmark
Add CMark bindings
2022-09-01 23:02:30 +01:00
Jeroen van Rijn c39b1a31db vendor:cmark -> vendor:commonmark 2022-09-01 23:44:38 +02:00
Jeroen van Rijn 635c7fa153 Merge branch 'master' into cmark 2022-09-01 23:42:15 +02:00
Jeroen van Rijn b7ac0a9e8d Merge pull request #2006 from thePHTest/sort_with_indices
fix sort_by_with_indices for zero and one length slices
2022-09-01 22:21:44 +02:00
Phil 3f3ae4b2b6 fix sort_by_with_indices for zero and one length slices: 2022-09-01 13:14:10 -07:00
Jeroen van Rijn c2423dc07f Merge pull request #2005 from thePHTest/sort_with_indices
have sort_with_indices allocate. Add a couple convenience procs for using the indices result to sort other slices.
2022-09-01 21:42:16 +02:00
Phil 1296630160 cleanup tests 2022-09-01 12:34:08 -07:00
Phil 63eec25044 add sort_by_indices overload and test 2022-09-01 12:32:33 -07:00
Phil 7a9b0731cf add tests for sort_by_indices 2022-09-01 12:13:15 -07:00
Phil d45661c405 cleanup slice sorting with indices changes 2022-09-01 11:51:45 -07:00
Phil 002bec256a have sort_with_indices allocate. Add a couple convenience procs for using the indices result to sort other slices. 2022-09-01 11:46:59 -07:00
Jeroen van Rijn 000861cba8 Merge pull request #2003 from Kelimion/tests
Add tests for `slice.sort_with_indices`
2022-09-01 18:19:47 +02:00
Jeroen van Rijn 36473b2774 Add test for core:slice. 2022-09-01 18:04:48 +02:00
gingerBill 4188f50105 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-09-01 16:33:50 +01:00
gingerBill 3e3b9ae2df Fix #1994 Returning to err: Maybe(Error) raises compiler assert 2022-09-01 16:33:44 +01:00
gingerBill e89f0de232 Merge pull request #1997 from thePHTest/sort_with_indices
add sort_with_indices family of procs
2022-09-01 16:27:00 +01:00
gingerBill 4858e16a11 Merge pull request #1976 from matias-eduardo/patch-2
Add Extended Window Styles (WS_EX)
2022-09-01 16:26:51 +01:00
gingerBill 902a6db0e1 Fix #2000 - allow #soa array iteration by pointer 2022-09-01 16:15:09 +01:00
gingerBill 19ae6122c7 Fix #2002 (allow array *= matrix) 2022-09-01 16:11:03 +01:00
gingerBill b82b91ea08 Fix #1974 by erring on untyped parameters to intrinsics.objc_send 2022-09-01 15:15:51 +01:00
gingerBill 636f0d7063 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-09-01 15:11:58 +01:00
gingerBill ed73441a4c Fix code generation related to Objective-C related x->y() calls 2022-09-01 15:11:53 +01:00
Jeroen van Rijn 4c2be6cd49 Merge pull request #2001 from Lperlind/staging/add-window-bindings
Kernel32: Add more window bindings and improve consistency
2022-09-01 15:57:58 +02:00
Lucas Perlind f3f51bd643 Kernel32: Add more window bindings and improve consistency 2022-09-01 22:57:27 +10:00
Jeroen van Rijn 4c5672119a Merge pull request #1998 from thePHTest/ease_fix
fix ease.odin compile error from missing comma
2022-09-01 00:14:22 +02:00
Phil 8482f943ea fix ease.odin compile error from missing comma 2022-08-31 14:14:06 -07:00
Phil 15aaf7dfa0 add sort_with_indices family of procs 2022-08-31 14:06:21 -07:00
Jeroen van Rijn 768abf83f6 Merge pull request #1993 from SyndaKitty/master
Fix for SDL `GetPreferredLocales`
2022-08-31 14:09:35 +02:00
gingerBill ca76d53452 Fix more typos 2022-08-31 11:07:31 +01:00
gingerBill b0904d6598 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-08-31 11:02:52 +01:00
gingerBill b0a09f7b9e Fix typo 2022-08-31 11:02:36 +01:00
SyndaKai eb4891bcc8 Fix for SDL GetPreferredLocales 2022-08-31 04:28:46 -04:00
Jeroen van Rijn 803fd8f037 Wrap CMark allocator as Odin allocator
```odin
	// Smaller allocation to larger allocation resize
	{
		// Allocated on CMark's allocator
		foo := make([dynamic]int, 13)

		for i in 0..<13 {
			foo[i] = i
		}
		fmt.println("Before resize:", foo)

		resize(&foo, 42)
		fmt.println("After resize:", foo)

		delete(foo)
	}
```
2022-08-30 16:35:18 +02:00
Jeroen van Rijn 67bdb5b1a3 [cmark] Add static library for Linux
Built on Ubuntu 20.04.3 LTS.

Remove Odin allocator wrapper.
2022-08-30 16:02:40 +02:00
Jeroen van Rijn acc635b535 [cmark] Move wrapped allocator next to get_default_allocator 2022-08-30 14:56:10 +02:00
Jeroen van Rijn 4e8ce87792 [cmark] Allow wrapping context.allocator 2022-08-30 14:53:48 +02:00
Jeroen van Rijn 2c8daa25dc [cmark] Add wrappers. 2022-08-30 13:55:53 +02:00
Jeroen van Rijn 054ee0a8b5 [cmark] Change to static library 2022-08-30 13:32:07 +02:00
Jeroen van Rijn d0cadaf1a6 Add CMark bindings 2022-08-30 00:54:12 +02:00
Jeroen van Rijn 317db2758a Revert "Fix #1985"
This reverts commit 25102d4792.
2022-08-29 14:50:18 +02:00
Jeroen van Rijn 25102d4792 Fix #1985 2022-08-29 14:47:00 +02:00
gingerBill d39f1c461e Merge pull request #1988 from colrdavidson/queue_peek
Add peek_front and peek_back
2022-08-29 12:02:17 +01:00
Colin Davidson 7a6fc3a93b Add bounds check for peeks 2022-08-29 02:03:12 -07:00
Colin Davidson 83c002c197 add peeks 2022-08-29 01:53:40 -07:00
gingerBill fc47b5dee0 Merge pull request #1987 from colrdavidson/more_queue
Add more queue helpers
2022-08-29 09:38:53 +01:00
Colin Davidson 6c2e0b09ba Add more queue helpers 2022-08-29 00:43:35 -07:00
Jeroen van Rijn 3d4698debe [TGA] Add B&W and RLE color-mapped. 2022-08-29 00:29:50 +02:00
Jeroen van Rijn 4a25cfb27c Merge pull request #1984 from Kelimion/tga
TGA improvements
2022-08-28 21:58:20 +02:00
Jeroen van Rijn 294bd6a446 [TGA] Add B5G5R5 15- and 16-bit support. 2022-08-28 21:48:51 +02:00
Jeroen van Rijn d0109db23b [TGA] Add support for Top-Right and Bottom-Right origins. 2022-08-28 19:41:42 +02:00
Jeroen van Rijn ee3ee66aae Merge pull request #1981 from benoitjacquier/master
TGA loader
2022-08-28 18:33:20 +02:00
Jeroen van Rijn f74e281efa Various changes to TGA reader
- Style changes
- Change ptr usage to slice indexing
- Add TGA Footer

Also, add `peek_data` with offset to `compress`.
2022-08-28 18:25:07 +02:00
Jeroen van Rijn c0cd02883f Merge pull request #1982 from Lperlind/staging/raw_windows_input
Add much of Win32's Raw Input API
2022-08-28 12:29:19 +02:00
Lucas Perlind d9adb0fd6b Add much of Win32's Raw Input API 2022-08-28 16:30:55 +10:00
Benoit Jacquier 6363013dd8 style fix 2022-08-27 19:45:14 +02:00
Benoit Jacquier 934131abf8 Add RLE supports for TGA loader 2022-08-27 19:30:34 +02:00
Benoit Jacquier 4e5337412a Merge remote-tracking branch 'origin/master' 2022-08-27 16:22:37 +02:00
Benoit Jacquier 00f2e911a7 Add support for basic TGA loading 2022-08-27 16:07:21 +02:00
gingerBill c82d7d3d87 Merge pull request #1979 from Despacito696969/master
Fixed marshal_to_writer not supporting i128
2022-08-27 00:58:54 +01:00
Despacito696969 ecfea027a0 Fixed marshal_to_writer not supporting i128
Previously json.marshal(i128(696969)) would print 0 as there was no `u = i128(i)` initialization.
2022-08-26 23:49:33 +02:00
gingerBill 96be494730 Fix append_nothing 2022-08-26 22:21:19 +01:00
gingerBill 12c8db927b Remove direct core:mem dependency in core:reflect 2022-08-26 16:46:19 +01:00
gingerBill 027ea587fc Unify mem and runtime logic 2022-08-26 16:45:40 +01:00
gingerBill 026900c7f0 Add optional n parameter to strconv.parse_* procedures to state how many bytes could be read 2022-08-26 16:31:42 +01:00
gingerBill ffa87f55c4 Add a return value to append that states the number of elements that were returned 2022-08-26 16:20:47 +01:00
gingerBill c9eed04b51 Add comment regarding the layout of a map to explain how it is iterated 2022-08-26 16:19:48 +01:00
gingerBill b50b6b9f33 Add reflect.get_union_variant 2022-08-26 16:19:16 +01:00
gingerBill 8fd5bef0bd Fix #1977 2022-08-26 12:14:04 +01:00
gingerBill d6b49994a2 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-08-26 12:11:34 +01:00
gingerBill 776927709b Check for using variables 2022-08-26 12:11:27 +01:00
matias 96e033b22c Add Extended Window Styles (WS_EX)
Featuring frequently used styles such as: `WS_EX_APPWINDOW`, `WS_EX_NOREDIRECTIONBITMAP`, `WS_EX_ACCEPTFILES`, `WS_EX_TOOLWINDOW`, `WS_EX_TOPMOST`, `WS_EX_NOACTIVATE`, `WS_EX_LAYERED`, and more.
2022-08-25 21:12:45 -04:00
Jeroen van Rijn 3469178dc1 Fix #1973. 2022-08-25 13:31:45 +02:00
gingerBill af1b3b6368 Correct check_expr_is_stack_variable 2022-08-24 16:03:04 +01:00
gingerBill d56789e5a7 Add very basic escape analysis on return values 2022-08-24 15:58:20 +01:00
gingerBill aeacf3a9d8 Correct max alignment handling throughout the llvm backend 2022-08-24 13:07:41 +01:00
gingerBill 4ba486baa2 Add extra max alignment parameter for metrics (specifically for SIMD) 2022-08-24 12:58:16 +01:00
gingerBill f1ffd90294 Fix #1966 2022-08-24 12:52:57 +01:00
gingerBill 777aa8b118 Merge pull request #1955 from jaspergeer/fix-endian-type-conversion
fix #1759 endian float type conversion errors
2022-08-24 12:49:57 +01:00
gingerBill cb9e16f4df Correct syscalls for linux_i386 2022-08-24 12:37:56 +01:00
gingerBill 2908923db9 Fix #1972 2022-08-24 12:18:42 +01:00
Jeroen van Rijn 8c1dfabb6b Fix append for size_of(E) == 0 2022-08-23 16:00:14 +02:00
gingerBill 7fe36de069 Correct Try SRWLock related procedures to return BOOLEAN rather than BOOL 2022-08-22 22:24:51 +01:00
Jeroen van Rijn 27d556735a Merge pull request #1969 from ashemedai/bug-1968-bsd-headers
fix: Expand OpenBSD include for wait.h to all Unix
2022-08-22 13:50:40 +02:00
Jeroen Ruigrok van der Werven b70d211f21 fix: Expand OpenBSD include for wait.h to all Unix
closes: #1968
2022-08-22 11:15:53 +02:00
gingerBill b3e3b6c656 Fix #1965 2022-08-20 11:17:47 +01:00
gingerBill 1734286252 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-08-19 16:57:44 +01:00
gingerBill c8c076f970 Fix #1963 2022-08-19 16:57:36 +01:00
Jeroen van Rijn e40b3ad338 Remove unused n from PQ; add reflect.is_bit_set 2022-08-18 18:00:25 +02:00
gingerBill afec321db2 Add core:sys/valgrind package for valgrind, memcheck, and callgrind 2022-08-17 16:09:03 +01:00
gingerBill 6e9f9e6f3c Fix typo 2022-08-17 15:57:56 +01:00
gingerBill f504b200a9 Improve unreachable generation by putting a trap before it 2022-08-17 15:54:45 +01:00
gingerBill 82e840a0ca EXPERIMENTAL intrinsics.valgrind_client_request 2022-08-17 13:52:13 +01:00
gingerBill 82765ca96e Fix RtlWaitOnAddress behaviour with SetLastError(RtlNtStatusToDosError(status)) if an error occurred 2022-08-17 13:12:45 +01:00
Jasper Geer 5387ec5f29 Remove erroneous byte swap 2022-08-16 19:22:52 -07:00
gingerBill f2908cbc5a Remove debug crap with inlining 2022-08-16 16:36:13 +01:00
gingerBill 5337b0b471 Revert "Remove debug message"
This reverts commit 22d16c20f8.

Revert "Correct global constant procedure initialization"

This reverts commit 697c839c84.

Revert "Clean up how procedures are typed in LLVM's dumb type system"

This reverts commit de8bd88d2a.
2022-08-16 16:16:36 +01:00
gingerBill e51afc3509 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-08-16 12:02:22 +01:00
gingerBill 2c004dbcc9 Improve matrix conversion rules 2022-08-16 12:02:14 +01:00
gingerBill e128ed7d26 Merge pull request #1953 from thePHTest/master
Change math/linalg/general.odin vector_length and vector_normalize to only accept vectors of float types rather than any numeric type
2022-08-16 01:17:39 +01:00
Phil 9064ebfe97 Change math/linalg/general.odin vector_length and vector_normalize to only accept vectors of float types rather than any numeric type 2022-08-15 16:45:54 -07:00
gingerBill 4f7bbe0e4a Merge pull request #1927 from IanLilleyT/debug_symbols_for_range_interval
Debug symbols for range-based for loop over number range
2022-08-15 16:56:01 +01:00
gingerBill 208f168564 Correct assignment 2022-08-15 16:31:59 +01:00
gingerBill 737bccbd5e Add math.divmod and math.floor_divmod 2022-08-15 16:31:43 +01:00
Ian Lilley f5431a046d using correct type for val1 2022-08-15 08:08:49 -04:00
gingerBill 5a9422b6bc Merge branch 'master' of https://github.com/odin-lang/Odin 2022-08-15 10:27:57 +01:00
gingerBill d30198c99a Merge pull request #1944 from odin-lang/load-improvements
Improvements to `#load`
2022-08-15 10:27:53 +01:00
gingerBill 0c8d59dd20 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-08-15 10:24:23 +01:00
gingerBill a460d140fe Merge pull request #1889 from thePHTest/master
fix glfw/native.odin foreign lib import
2022-08-15 10:24:19 +01:00
gingerBill 881d18ee88 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-08-15 10:23:56 +01:00
gingerBill d73a4aa34b Merge pull request #1938 from Skytrias/pretty-json-2
add pretty json output in marshal based on spec + options
2022-08-15 10:23:50 +01:00
gingerBill 5c298c1501 Minor text alignment 2022-08-15 10:22:32 +01:00
gingerBill a83ca2120e Remove unused import 2022-08-14 21:50:41 +01:00
gingerBill 81799f7f78 Fix #1950 2022-08-14 21:50:21 +01:00
Jeroen van Rijn 7973f7e750 Merge pull request #1949 from matias-eduardo/patch-1
Typo: `GetWindowLongW` to `SetWindowLongW`
2022-08-14 13:25:23 +02:00
matias 3dc62a67e0 Typo: "Get" -> "Set" 2022-08-14 07:16:54 -04:00
Jeroen van Rijn 081e36c909 Add additional mouse buttons to Raylib bindings. 2022-08-13 15:34:52 +02:00
Jeroen van Rijn 3a1d364f59 Merge pull request #1929 from fabiansperber/add-windows-definitions
Add windows & wgl definitions
2022-08-12 22:58:18 +02:00
Fabian Sperber e50648279d Add some wgl extensions
- fix upper case for wglSwapIntervalEXT to be the same as the exported name
2022-08-12 21:56:55 +02:00
Fabian Sperber 929af320da Add more windows bindings 2022-08-12 21:56:54 +02:00
gingerBill 8e7c7eeeba Fix lb_emit_ptr_offset 2022-08-12 13:48:10 +01:00
Michael Kutowski b739044e69 Update marshal.odin 2022-08-12 13:59:11 +02:00
Michael Kutowski 9e0107c9fc add new soa type, my bad wasnt on master 2022-08-12 13:50:19 +02:00
Michael Kutowski 107e016508 switch styling and opt.spaces max 2022-08-12 13:42:19 +02:00
gingerBill 22d16c20f8 Remove debug message 2022-08-12 12:29:32 +01:00
gingerBill 697c839c84 Correct global constant procedure initialization 2022-08-12 12:29:11 +01:00
gingerBill de8bd88d2a Clean up how procedures are typed in LLVM's dumb type system 2022-08-12 11:15:12 +01:00
Ian Lilley 595efba747 removed unnecessary ternary 2022-08-11 11:43:19 -04:00
gingerBill c041d15569 Merge pull request #1928 from IanLilleyT/logger_extra_space
Removed extra space from file_console_logger
2022-08-11 16:18:46 +01:00
gingerBill 57b20e634b Merge branch 'master' into pretty-json-2 2022-08-11 16:14:27 +01:00
gingerBill e285796fc1 Merge pull request #1940 from Lperlind/staging/d3d11-idebug
Add ID3D11Debug interface to d3d11 package
2022-08-11 16:13:09 +01:00
gingerBill a19494d3a7 Minor refactoring of lb_build_addr 2022-08-11 16:12:07 +01:00
gingerBill d2a362fd52 Clean up compound literal backend stuff some more 2022-08-11 16:07:55 +01:00
gingerBill 0f3562ef02 Improve compound literal generation for array-like types 2022-08-11 16:01:46 +01:00
gingerBill 03f683f9e7 Improve emit store for large constants 2022-08-11 15:19:27 +01:00
gingerBill cecadce86d Allow for chaining of '#load(path) or_else #load(path)' 2022-08-11 14:42:29 +01:00
gingerBill a7c3906003 #load(path, type)
where `type` can be `string` or `[]T` where `T` is a simple type
2022-08-11 14:30:14 +01:00
gingerBill 70dc0c15fd Improve type hint for #load to allow for string types 2022-08-11 13:43:35 +01:00
gingerBill 9eeed9d5bd Simplify #load_or for the time being 2022-08-11 13:35:24 +01:00
gingerBill a054c2934e Cache #load data and hashes 2022-08-11 13:32:45 +01:00
gingerBill 38102f14c1 Add #load(path) or_else default in favour of #load_or(path, default) 2022-08-11 13:01:54 +01:00
gingerBill 0997df4fcf Move builtin directives to a separate procedure 2022-08-11 12:39:05 +01:00
gingerBill a5a56e061c Fix to png example.odin 2022-08-11 11:10:05 +01:00
gingerBill 8b007ad55a Merge pull request #1943 from jaspergeer/fix-untyped-const-shift
fix #1840 invalid LLVM code gen for arithmetics between f32 and shifted untyped integer
2022-08-11 10:58:58 +01:00
Jasper Yujin Geer 57dd5ec4db Added back missing return statement 2022-08-10 18:25:29 -07:00
Jasper Yujin Geer 5b621d5be1 More accurate error message 2022-08-10 18:07:49 -07:00
Jasper Yujin Geer 7aee762f3a Throw error when untyped shift expressions have non-integral type hints 2022-08-10 17:39:21 -07:00
Jeroen van Rijn 4ee50c5a35 Merge pull request #1941 from jockus/fix-rc-missing-extension
Do not remove .rc extension from resource path to prevent expansion t…
2022-08-10 15:22:09 +02:00
Joakim Hentula 28f440dd9e Do not remove .rc extension from resource path to prevent expansion to full path assuming it's a directory if a folder with the same name exists in the same folder as the resource file 2022-08-10 14:03:04 +01:00
Lucas Perlind 43b78e51a4 Add ID3D11Debug interface to d3d11 package 2022-08-10 22:28:24 +10:00
gingerBill 84f9fb706b General clean up of LLVM*GEP2 code 2022-08-10 11:36:25 +01:00
gingerBill 812823cad8 Wrap all LLVMGetElementType uses 2022-08-09 21:17:32 +01:00
gingerBill 0655260378 Comment out a bit of code in lb_is_const_or_global 2022-08-09 21:13:52 +01:00
gingerBill cfc3723879 Remove other uses of LLVMGetElementType on pointer types 2022-08-09 21:12:31 +01:00
gingerBill 4c3281b3f2 Disallow Early CSE on -debug builds 2022-08-09 18:09:26 +01:00
gingerBill ff94c605e0 Minor change to lb_emit_store for storing nil to procedure variables 2022-08-09 18:08:30 +01:00
gingerBill cb0a59bb2c Eliminate use of LLVMGetElementType for pointers 2022-08-09 15:36:18 +01:00
gingerBill 076700bd22 Merge pull request #1933 from lerno/reduce_reliance_on_ptr_type
Removed use of deprecated functions. Cleaned up most deprecated use o…
2022-08-09 15:18:09 +01:00
Jeroen van Rijn bcccc8338f Merge pull request #1937 from IanLilleyT/rc_fix
Find rc.exe in Windows SDK
2022-08-09 16:03:17 +02:00
Michael Kutowski 425dec8bb8 add uint as hex option 2022-08-08 18:28:28 +02:00
gingerBill 838554460b Add basic "lock" around Log_Allocator to minimize errors with allocation logging loops 2022-08-08 15:25:11 +01:00
gingerBill 659c3c528d Update delete to pass size in bytes to free when possible 2022-08-08 15:16:18 +01:00
gingerBill 60aeab3c38 Update fmt.odin 2022-08-08 15:07:19 +01:00
gingerBill 5e3cf45df3 Add #soa pointer type to aid with refactoring to #soa data types
a: #soa[16]Foo
p := &a[6]
#assert(type_of(p) == #soa^#soa[16]Foo)
p^.x = 123
p.x = 123
2022-08-08 15:07:00 +01:00
gingerBill 4633591918 Remove unnecessary pointer 2022-08-08 14:28:36 +01:00
gingerBill 0e6a8b7c72 Make Growing_Arena reserve memory first and then commit as needed
It just committed all at once to begin with
2022-08-08 12:43:18 +01:00
gingerBill 147848ca20 Clean up naming conventions 2022-08-08 12:30:58 +01:00
gingerBill cde002c579 Fix i18n 2022-08-08 12:27:23 +01:00
gingerBill f23d93ba89 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-08-08 12:23:48 +01:00
gingerBill c97a8418dc Clean-up and unification for the allocation procedures 2022-08-08 12:23:19 +01:00
gingerBill 4aca9372a6 Improve resize call 2022-08-08 12:11:30 +01:00
gingerBill 4912ecc3ea Add log.Log_Allocator 2022-08-08 11:57:51 +01:00
Ian Lilley c1c8ceafc2 find windows sdk bin path for rc.exe 2022-08-07 17:52:29 -04:00
Christoffer Lerno 9c0a3b6c60 Removed use of deprecated functions. Cleaned up most deprecated use of LLVMGetElementType. 2022-08-07 19:26:28 +02:00
Jeroen van Rijn 7b539e3025 Merge pull request #1932 from blakeb211/fixed_math_log2
fixed math.log2
2022-08-06 14:55:23 +02:00
blake b2b0043875 fixed math.log2 2022-08-06 06:19:47 -04:00
Michael Kutowski 53e0d182af ...bad paste 2022-08-05 18:56:14 +02:00
Michael Kutowski a6fa41e290 marshal output options with pretty option and other config 2022-08-05 18:53:29 +02:00
Jeroen van Rijn edba99d636 Revert "Use sync.Mutex rather than a spin lock for the threading example"
This reverts commit 8d1f46d837.
2022-08-05 15:03:09 +02:00
gingerBill 35674959f2 Merge pull request #1931 from cuishuang/master
fix some typos
2022-08-05 13:16:48 +01:00
cui fliter dc8b7a0eb8 fix some typos
Signed-off-by: cui fliter <imcusg@gmail.com>
2022-08-05 20:10:20 +08:00
gingerBill 8d1f46d837 Use sync.Mutex rather than a spin lock for the threading example 2022-08-05 12:45:26 +01:00
gingerBill a2117d23b2 Change unreachable with panic 2022-08-05 12:41:25 +01:00
gingerBill a58e4d0359 Allow for foo() or_else unreachable() and other diverging procedures 2022-08-05 12:19:57 +01:00
gingerBill 576914aee1 Make unreachable() a built-in compiler-level procedure 2022-08-05 11:57:33 +01:00
gingerBill 8171f8209a Merge branch 'master' of https://github.com/odin-lang/Odin 2022-08-05 11:37:28 +01:00
gingerBill 64ff05303c Merge branch 'master' of https://github.com/odin-lang/Odin 2022-08-05 11:36:20 +01:00
gingerBill 6caab6225d Fix #1930 2022-08-05 11:36:12 +01:00
gingerBill 326411498a Merge pull request #1925 from neNasko1/fix-incorrect-behaviour-of-string-ordering
Fix incorrect behaviour of string ordering
2022-08-03 19:43:55 +01:00
gingerBill d50fcf0020 Merge pull request #1926 from IanLilleyT/env_leak
Memory leak in os2 user_config_dir and user_home_dir
2022-08-03 19:02:05 +01:00
Ian Lilley d354d36a3b removed extra space from file_console_logger 2022-08-03 13:22:12 -04:00
Ian Lilley 483a72ac61 fixed debug symbols for range interval 2022-08-03 12:36:52 -04:00
Ian Lilley dbec4b0d0e fixed memory leak from calling get_env 2022-08-03 11:49:42 -04:00
Atanas Dimitrov 4cb489b9e4 Fix sort.compare_strings for prefixes 2022-08-03 16:22:54 +03:00
Atanas Dimitrov 28ec50d567 Fix string orderings to account for prefix-equal strings 2022-08-03 16:09:36 +03:00
gingerBill 73beed0477 Merge pull request #1921 from Hyp-X/master
Update user32.odin
2022-08-02 12:15:44 +01:00
gingerBill e0ecdd4b24 Simplify logic of append of zero sized elements 2022-08-02 11:13:53 +01:00
Phil 0cb1a578d0 fix glfw/native.odin foreign lib import 2022-08-01 08:26:26 -07:00
gingerBill 5168cf03a9 Remove dead #maybe code 2022-08-01 15:38:50 +01:00
gingerBill b886ae6515 Simplify parser logic for field prefixes 2022-08-01 15:32:17 +01:00
gingerBill 277a973b98 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-08-01 15:24:23 +01:00
gingerBill 0ec4d97bfd Correct parser logic for is_token_field_prefix 2022-08-01 15:24:17 +01:00
Hyp-X e6236e5c3e Update user32.odin
Added UnregisterClassW procedure to windows\user32.odin
2022-08-01 15:59:00 +02:00
gingerBill e201280844 Merge pull request #1920 from Hyp-X/master
Update dxgi.odin
2022-08-01 14:40:39 +01:00
gingerBill 8e50a6c61b Merge pull request #1876 from mohd-akram/build-config-opts
Allow passing config options to build script
2022-08-01 14:33:35 +01:00
Hyp-X 7e6f5f89d0 Merge branch 'odin-lang:master' into master 2022-08-01 14:44:57 +02:00
gingerBill 97acc57649 Simplify field prefix handling 2022-08-01 13:17:10 +01:00
gingerBill 83c8c48ed7 Simplify ast parsing for prefixes 2022-08-01 13:07:49 +01:00
Hyp-X 0815b4d59f Update dxgi.odin
Fixed CreateDXGIFactory* last parameter to be ^rawptr instead of rawptr
2022-08-01 14:04:12 +02:00
gingerBill a0135080b3 Merge pull request #1917 from lerno/added_virtuals_macos
Added virtual memory headers for Darwin.
2022-07-30 19:38:37 +01:00
Christoffer Lerno f45e8e5d47 Added virtual memory headers for Darwin. 2022-07-29 20:01:26 +02:00
Jeroen van Rijn 98ba4beede Merge pull request #1916 from wjlroe/glfw-3-3-8-darwin-universal-static-libs
GLFW 3.3.8 Darwin universal and Windows static libs
2022-07-28 16:55:42 +02:00
William Roe a9304f2fef Update Windows GLFW libraries to 3.3.8
This matches the latest libraries committed for macOS/Darwin
2022-07-28 15:38:14 +01:00
William Roe a0697ab057 Fix build on Apple Silicon when linking to GLFW
This also updates the library to GLFW 3.3.8
This includes the lib-universal static library, rather than just the
Intel one (lib-x86_64), so software building with GLFW on Apple Silicon
will work again.
2022-07-28 15:29:30 +01:00
Jeroen van Rijn 2e895c72d3 Merge pull request #1447 from phillvancejr/master
Added vendor glfw support for Mac OS
2022-07-28 16:05:59 +02:00
Jeroen van Rijn 674ebe395f Merge branch 'master' into master 2022-07-28 16:01:18 +02:00
Jeroen van Rijn 96eecaab54 Merge pull request #1915 from JorriFransen/fix-simd_abs
Changed param count from 2 to 1 for simd_abs.
2022-07-28 15:36:16 +02:00
Jorri Fransen b1ae5bc9fe Changed param count from 2 to 1 for simd_abs. 2022-07-28 15:28:26 +02:00
Jeroen van Rijn 7258588ed5 Merge pull request #1914 from wjlroe/fix-json-unmarshal-without-assertions-bug
Fix bug unmarshalling JSON with assertions disabled
2022-07-28 15:03:47 +02:00
William Roe d913155972 Fix bug unmarshalling JSON with assertions disabled
When asserts are disabled, code within the assert isn't run. Having
expect_token within an assert means that the state of the Parser is
mutated when asserts are run, but not when they aren't.

There's already a wrapper procedure for this pattern, which I have
reused here.
2022-07-28 13:56:14 +01:00
pvance 9746e25784 Update stb_image_resize.odin 2022-07-28 04:27:42 -04:00
pvance d26cfd2141 Update bindings.odin 2022-07-28 04:26:22 -04:00
pvance 21f2c06f4b Update stb_image_resize.odin 2022-07-28 04:25:18 -04:00
pvance 727a25f41f Replace ODIN_OS strings with enum values 2022-07-28 04:23:24 -04:00
pvance 3f27cb2309 replace ODIN_OS string with enums 2022-07-28 04:22:00 -04:00
pvance f213622982 replace ODIN_OS string with enums 2022-07-28 04:20:37 -04:00
pvance 4aad835a66 replaced ODIN_OS string values with enums 2022-07-28 04:19:28 -04:00
Jeroen van Rijn 4af8a64580 Merge pull request #1907 from thePHTest/minor-fix
remove leftover print statement
2022-07-26 01:00:38 +02:00
Phil c9c3611b1d remove leftover print statement 2022-07-25 15:46:47 -07:00
gingerBill 220dfd7440 Minor clean up of wasi_api.odin 2022-07-25 17:00:32 +01:00
gingerBill bce8819ed5 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-07-25 12:12:32 +01:00
gingerBill 5f2b220a85 Fix minor issue with a lack of a trailing comma 2022-07-25 12:12:25 +01:00
gingerBill f174c805a9 Merge pull request #1905 from DanielGavin/glob-fix
Change open to be read only when opening directory
2022-07-25 10:43:28 +01:00
Daniel Gavin 25869b7504 Change open to be read only when opening directory 2022-07-25 11:34:03 +02:00
Mikkel Hjortshøj ecd81e8a53 Update stale.yml 2022-07-25 00:14:07 +02:00
gingerBill d7f9f7f170 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-07-24 23:07:41 +01:00
gingerBill 08f5259d77 Replace insert_at with inject_at and assign_at 2022-07-24 23:07:35 +01:00
Mikkel Hjortshøj a9f744cb64 Update stale.yml 2022-07-25 00:07:29 +02:00
Mikkel Hjortshøj b02e42c6dc Update stale.yml 2022-07-25 00:05:36 +02:00
Mikkel Hjortshøj cb0273b5d7 Update stale.yml 2022-07-24 23:58:43 +02:00
Mikkel Hjortshøj 8dbf45a65a Update stale.yml 2022-07-24 23:54:39 +02:00
gingerBill 9f64de9568 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-07-24 22:51:39 +01:00
gingerBill 0ebe9ba487 Fix #1901 2022-07-24 22:51:34 +01:00
Mikkel Hjortshøj efe00e1aa6 Create stale.yml 2022-07-24 23:48:34 +02:00
gingerBill 2bdbce55f9 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-07-24 22:46:07 +01:00
gingerBill 9614ca92f0 Fix #1834 2022-07-24 22:46:00 +01:00
gingerBill d30e59f539 Merge pull request #1844 from Ronaldr1985/fix-odin-on-openbsd
Added missing #includes on OpenBSD
2022-07-24 22:28:25 +01:00
Mikkel Hjortshøj a3afe617c2 Update ci.yml 2022-07-24 23:27:07 +02:00
gingerBill 69daac583e Merge pull request #1898 from DaseinPhaos/patch-1
fix `linalg.angle_from_quaternion`
2022-07-24 22:18:36 +01:00
gingerBill b28d4b753b Temp fix for lb_emit_store 2022-07-24 20:58:50 +01:00
gingerBill e6ab4f4856 Force memset instead of store zeroinitializer when the value is large 2022-07-24 20:22:50 +01:00
gingerBill c8ab1b7ee1 Add #by_ptr procedure attribute to enforce a parameter to be passed by pointer internally 2022-07-24 13:11:48 +01:00
gingerBill 9f10487678 Fix #1493 2022-07-24 12:59:34 +01:00
gingerBill 2542983d70 Fix #1793 2022-07-24 12:54:05 +01:00
gingerBill d492fb3501 Fix and improve x->y() behaviour to minimize duplicate evaluation 2022-07-24 12:39:59 +01:00
Ronald1985 1829aa1638 Undo changes to common.cpp and move the include of sys/wait.h to gb.h 2022-07-22 23:08:46 +01:00
Ronald1985 4cb4173ced Updated shabang in build_odin.sh so it runs on OpenBSD
This change doesn't break compatibility on Linux or Darwin
2022-07-22 23:01:38 +01:00
Luxko 00e704b216 fix linalg.angle_from_quaternion
fixes #1894 .2:
```odin
package laa
import "core:fmt"
import la "core:math/linalg"

main:: proc() {
    angle := f32(0.5)
    quat  := la.quaternion_angle_axis_f32(angle,la.Vector3f32{0,0,1})
    fmt.printf("retreived: %0.8f\n",  la.angle_from_quaternion(quat)) // should be 0.5, but wasn't
}
```
2022-07-23 00:59:45 +08:00
gingerBill 227ee0f705 Merge pull request #1895 from yeongjukang/patch-1
fix parameter for atan2 in procedure math.asin
2022-07-22 11:23:46 +01:00
gingerBill 17f47a7ab0 Merge pull request #1892 from esnunes/vendor-raylib-4.0-macos-arm64
Update `vendor:raylib` to support macos-arm64 (apple silicon)
2022-07-22 11:06:16 +01:00
Yeongju Kang 4e8bc0786d fix parameter for atan2 in procedure asin 2022-07-22 15:48:06 +09:00
gingerBill 3d3ccf061f Merge pull request #1893 from Tetralux/slash-join-slice
[path/slashpath] Change join() to take a slice instead of varargs
2022-07-21 18:59:04 +01:00
Tetralux 3a8adc6721 [path/slashpath] Change join() to take a slice instead of varargs
Achieves parity with filepath.join(), which was similarly changed a while back.
2022-07-21 17:49:31 +00:00
gingerBill e1748a5dd1 Add MAKE_WORD 2022-07-21 17:08:11 +01:00
Eduardo Nunes 59b4c889d3 Update vendor:raylib to support macos-arm64 (apple silicon) 2022-07-20 18:23:09 -03:00
gingerBill b6408d1b3f Merge branch 'master' of https://github.com/odin-lang/Odin 2022-07-19 16:17:45 +01:00
gingerBill 3db3047f47 Minor improvements 2022-07-19 16:17:23 +01:00
gingerBill 7420fbd95b Add custom event handling to wasm 2022-07-19 16:16:51 +01:00
gingerBill 7c990b3833 Merge pull request #1877 from fabiansperber/improve-debug-info
Improve debug info
2022-07-19 00:49:22 +01:00
Fabian Sperber 9c059f1a12 Fix debug info for type switch variable to be only visible in correct switch case
- use correct scopes for type switch
2022-07-19 00:46:28 +02:00
Fabian Sperber fb167d1d0a Fix loop var (init) becoming hidden while stepping on the for-line
- set debug location of jumps between block (similar to clang to the location of 'for')
- extend scope range to include all parts of the for-stmt
2022-07-19 00:46:27 +02:00
Fabian Sperber 0992239d86 Embed natvis in windows debug build for easier viewing of strings and Arrays 2022-07-19 00:46:27 +02:00
gingerBill 9eb3da0474 Remove import cycle on FreeBSD 2022-07-18 15:20:28 +01:00
gingerBill e91f8feedf Keep -vet happy 2022-07-18 15:12:54 +01:00
gingerBill 22a0c3fce1 Disallow proc() do stmt and only allow proc() { stmt } 2022-07-18 15:09:04 +01:00
gingerBill 6c7e5748a8 Integrate numerous debug fixes from #1877 2022-07-18 12:49:29 +01:00
gingerBill 0b0c6da8b0 Merge pull request #1873 from weshardee/master
fix -build-mode:shared on MacOS
2022-07-18 12:42:53 +01:00
gingerBill 78826071c0 Fix #1836 2022-07-16 23:14:09 +01:00
gingerBill e61b73d7ad Fix #1869 2022-07-16 22:30:57 +01:00
gingerBill f886632bf1 Fix #1880 2022-07-16 22:18:07 +01:00
gingerBill eafa5098aa Fix #1883 2022-07-16 18:03:43 +01:00
gingerBill 0571b80d37 Fix printing of bit_set types 2022-07-16 17:56:38 +01:00
gingerBill 80c10644dd Remove assert 2022-07-16 17:39:13 +01:00
gingerBill 041625381c Fix #1888 2022-07-16 17:36:03 +01:00
gingerBill 48f56d728b Merge branch 'master' of https://github.com/odin-lang/Odin 2022-07-16 16:12:24 +01:00
gingerBill 872d391cfb Mock up for the ability to enforce our own runtime.memset if necessary 2022-07-16 16:12:16 +01:00
Jeroen van Rijn 3e6ec65dd9 Fix murmur64a's tail handling.
Also, split up the murmur64 proc into murmur64a and murmur64b as they're distinct hashes with their own output.
2022-07-15 13:02:07 +02:00
gingerBill 157c87b2a2 Fix typo 2022-07-15 00:34:26 +01:00
gingerBill d3081bd889 Add buffer_read_ptr and buffer_write_ptr 2022-07-14 15:26:50 +01:00
gingerBill 2ae5bf4395 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-07-14 11:56:13 +01:00
gingerBill f28547cae1 Fix libc.putchar 2022-07-14 11:56:05 +01:00
Jeroen van Rijn 5332705e31 [hash] Give crc-64 a 64-bit seed. 2022-07-14 11:07:52 +02:00
gingerBill bfb082cda4 Merge pull request #1881 from odin-lang/strings-builder-cleanup
Rename strings.Builder procedures to be consistent with the rest of the core library
2022-07-13 16:09:19 +01:00
gingerBill 37d04198ab Add improved error message when doing ^x instead of &x for addressable variables 2022-07-11 12:31:01 +01:00
gingerBill ae9d540c1c Correct naming in tests 2022-07-11 11:55:49 +01:00
gingerBill c90b7c38f1 Rename strings.Builder procedures to be consistent with the rest of the core library 2022-07-11 11:50:08 +01:00
gingerBill 9e376fbda7 Update README.md 2022-07-10 00:52:59 +01:00
Wes Hardee 00739bf06d Merge branch 'odin-lang:master' into master 2022-07-09 18:09:21 -05:00
gingerBill e8148055ad Fix compilation on non-windows platforms 2022-07-09 23:55:02 +01:00
gingerBill dd0a20ab45 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-07-09 23:53:24 +01:00
gingerBill babbc304b8 Fix wasm compilation in windows 2022-07-09 23:53:18 +01:00
Mohamed Akram a8b44f33bd Allow passing config options to build script 2022-07-08 16:14:54 +04:00
gingerBill bd48561688 Merge pull request #1865 from thePHTest/ogl_bindless
Add OpenGL bindless textures ARB api to vendor
2022-07-05 20:03:15 +01:00
Wes Hardee 23842a8950 fix -build-mode:shared on MacOS 2022-06-30 12:40:32 -05:00
gingerBill 1676c643df Change MAX_STORE_SIZE 2022-06-30 15:47:19 +01:00
gingerBill e2bfb024de Merge branch 'master' of https://github.com/odin-lang/Odin 2022-06-30 15:42:11 +01:00
gingerBill 4d06a54c0c Use memmove if possible when emitting store over a certain size 2022-06-30 15:42:02 +01:00
Phil 0a8e6169d7 remove unneeded semicolons 2022-06-28 11:36:50 -07:00
Jeroen van Rijn 04ae87eaef Merge pull request #1868 from Skytrias/skytrias-ease-flux-fixes
math ease fixed flux map key deletion and generic in `flux_to`
2022-06-28 18:35:18 +02:00
Michael Kutowski b313d09c2c flux fixed key deletion and generic in flux_to 2022-06-28 18:27:52 +02:00
gingerBill c0d2359a91 Merge pull request #1826 from englerj/windows-bindings
Added additional Windows bindings.
2022-06-28 13:14:35 +01:00
gingerBill 51a2f09032 Merge pull request #1863 from OrigamiDev-Pete/master
Correct error reporting for Win32 file operations
2022-06-28 13:12:41 +01:00
gingerBill f60e8031f2 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-06-28 11:27:57 +01:00
gingerBill ea42613fec Improve error message with suggestion for #1866 2022-06-28 11:27:50 +01:00
Phil b89bb87759 Add OpenGL bindless textures ARB api to vendor 2022-06-27 14:50:24 -07:00
gingerBill 2cbb3d5a24 Merge pull request #1864 from JimmyLefevre/master
Change LPCSTR to LPCWSTR in windows _W procedures
2022-06-27 22:20:20 +01:00
Jimmy "Keeba" Lefevre 9e288b7ce8 Change LPCSTR to LPCWSTR in windows _W procedures 2022-06-27 21:48:02 +02:00
gingerBill 776b48c10d Fix GetEventState 2022-06-27 11:02:40 +01:00
gingerBill 199dae6cd5 Make SDL.*Event procedures return booleans instead of an integer where appropriate 2022-06-27 11:02:20 +01:00
Peter DV e58f45bef7 Correct error reporting 2022-06-27 11:26:34 +10:00
gingerBill abe122ecb7 Implement #1859 2022-06-26 13:13:07 +01:00
gingerBill f8744d87b0 Add shorthand flag to determine_type_from_polymorphic 2022-06-24 15:12:15 +01:00
Jeroen van Rijn 3e7f6b8751 Merge pull request #1857 from Pablo96/fix-core-lib-odin-parser-file-docs
Fix issue 1856
2022-06-23 23:22:36 +02:00
Pablo Narvaja 6ffe814ca7 assign package docs to file docs 2022-06-23 18:13:34 -03:00
Jeroen van Rijn eec9be71f6 Merge pull request #1854 from Kelimion/dll-entry-point
Add runtime.dll_forward_reason for Windows DLLs.
2022-06-21 18:02:47 +02:00
Jeroen van Rijn 888913c739 Add runtime.dll_forward_reason for Windows DLLs. 2022-06-21 17:29:55 +02:00
gingerBill 33d96fd28a Correct debug information of #simd vectors 2022-06-20 16:00:17 +01:00
gingerBill f1e8738af2 Merge pull request #1851 from DaseinPhaos/patch-1
Add missing field for `TEXTURE_COPY_LOCATION`
2022-06-19 16:21:21 +01:00
gingerBill 9c52a11b1b Merge pull request #1850 from ftphikari/master
sys/windows: add RegSetKeyValueW
2022-06-19 16:21:00 +01:00
Luxko d89c4606bd Add missing field for TEXTURE_COPY_LOCATION
Add missing anonymous union field for [D3D12_TEXTURE_COPY_LOCATION ](https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_texture_copy_location):
```C
union {
    D3D12_PLACED_SUBRESOURCE_FOOTPRINT PlacedFootprint;
    UINT                               SubresourceIndex;
  };
```
2022-06-19 19:06:31 +08:00
hikari c6903fbcd5 sys/windows: add constants and macro 2022-06-17 15:14:21 +03:00
ftphikari 13c8149046 Merge branch 'odin-lang:master' into master 2022-06-16 22:55:24 +03:00
hikari 665db0f778 sys/windows: add RegSetKeyValueW 2022-06-16 22:54:59 +03:00
gingerBill 173286de65 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-06-16 15:49:13 +01:00
gingerBill f2ecda8fec Rename to SJSON for the Simplified JSON notation variant 2022-06-16 15:49:08 +01:00
Jeroen van Rijn 6f1222e9bf Update strings.prefix_length to handle partial UTF-8 runes. 2022-06-16 16:12:15 +02:00
gingerBill 84a424f21e Add numerous procedure groups decode_rune, decode_last_rune, rune_count, full_rune 2022-06-16 15:07:07 +01:00
Jeroen van Rijn 9b7710488b Merge pull request #1849 from Kelimion/prefix_length
Add `strings.prefix_length` & `slice.prefix_length`
2022-06-16 12:42:35 +02:00
Jeroen van Rijn 9f413862e9 Add strings.prefix_length & slice.prefix_length 2022-06-16 12:34:13 +02:00
gingerBill b8802d7df7 Merge pull request #1848 from Astavie/require-fix
fix require flag on higher optimization modes
2022-06-16 10:59:49 +01:00
Astavie b13dad02a4 fix require flag on higher optimization modes 2022-06-15 21:30:29 +02:00
Jeroen van Rijn f045f8d805 Merge pull request #1845 from hfr4/patch-1
wrong variable names in functions
2022-06-14 18:32:25 +02:00
hfr4 30b7c8ad66 wrong variable names in functions 2022-06-14 18:20:38 +02:00
Ronald1985 89222a0ab2 Added missing #includes on OpenBSD 2022-06-14 09:55:06 +01:00
gingerBill 21e637d2b3 Allow %q with enums 2022-06-13 23:30:10 +01:00
gingerBill 6c196931d2 Increase record_level on displaying map values 2022-06-13 14:46:03 +01:00
gingerBill d7195b0798 Add missing commas 2022-06-13 11:12:31 +01:00
gingerBill b40998de9e Improve -strict-style for foreign import 2022-06-13 11:11:51 +01:00
gingerBill 8c0c327df9 Improvements to -strict-style and trailing commas 2022-06-13 11:00:13 +01:00
Josh Engler 50cbb8a1fc Fix tab spacing. 2022-06-12 15:55:38 -04:00
gingerBill ff9d058392 Minor changes to core:slice/heap; add to examples/all 2022-06-12 17:25:42 +01:00
gingerBill 1acc8f438b Merge pull request #1830 from graphitemaster/dale/max_heap
add max heap implementation to slice package
2022-06-12 17:21:04 +01:00
gingerBill c53426fcb4 Merge pull request #1818 from IanLilleyT/reserve_exceed
Dynamic array append reserves more space when it exceeds capacity
2022-06-12 17:19:08 +01:00
gingerBill 5187db525f Minor style change 2022-06-12 17:17:59 +01:00
gingerBill a8bd340267 Merge pull request #1395 from hdooley/master
factor out alloca generation into a helper
2022-06-12 17:17:12 +01:00
gingerBill ca1f419dc2 Merge pull request #1819 from IanLilleyT/no_query_info
Allocator: tidying up Query_Info return values when not part of Query_Features
2022-06-12 17:15:08 +01:00
gingerBill fae60a6b88 Merge pull request #1829 from totoki-kei/dev/totoki-kei/patch-testing
[testing] Fixed `@(test)` always fails unless `testing.set_fail_timeout` is called
2022-06-12 17:15:02 +01:00
gingerBill 108558ddfc Merge pull request #1841 from hasenj/hack-objc-window-init
HACK work around for creating a cocoa window
2022-06-12 17:08:10 +01:00
gingerBill 65b8cfae82 Minor code clean up 2022-06-12 16:49:15 +01:00
gingerBill 4055c31cf0 Update utf-32 printing 2022-06-12 16:48:06 +01:00
gingerBill e88af4e458 Support UTF-16 printing with []u16 and [^]u16 related types 2022-06-12 16:36:55 +01:00
gingerBill 79eb7b52d9 Merge pull request #1828 from JungerBoyo/patch-2
front() and back()
2022-06-12 16:17:39 +01:00
gingerBill 83b2bf44c4 Merge pull request #1842 from ftphikari/master
sys/windows: add a couple of constants
2022-06-12 16:12:43 +01:00
gingerBill 670f18ad1b Minor change to shrink 2022-06-12 16:12:24 +01:00
gingerBill 9a81716936 Merge pull request #1820 from Tetralux/shrink
[runtime] Add builtin `shrink` for dynamic arrays and maps
2022-06-12 16:11:18 +01:00
gingerBill f013499eea Merge pull request #1827 from JungerBoyo/patch-1
unset function
2022-06-12 16:09:09 +01:00
gingerBill d04f732e68 Add fmt:"s,0" to allow arrays and multi-pointers to be printed with NUL termination 2022-06-12 13:07:43 +01:00
gingerBill 35fd8e7f68 Move trailing comma requirement in field lists to -strict-style 2022-06-12 12:20:00 +01:00
gingerBill 38ff2a3ed9 Minor fix 2022-06-11 15:57:56 +01:00
gingerBill bd502d16bc Correct distinction between indirection and record level 2022-06-11 15:24:35 +01:00
gingerBill a11e17fbc3 Disable again 2022-06-11 15:18:51 +01:00
gingerBill 8b3b659433 Re-enable string quoting within records 2022-06-11 15:16:17 +01:00
gingerBill f33228fd6e Merge branch 'master' of https://github.com/odin-lang/Odin 2022-06-11 15:13:13 +01:00
gingerBill df5b693de8 Refactor fmt_value 2022-06-11 15:13:07 +01:00
Jeroen van Rijn ab98108441 [image/png] Construct test paths differently. 2022-06-11 16:07:56 +02:00
Josh Engler 6f80174f84 Merge branch 'master' into windows-bindings 2022-06-11 09:48:28 -04:00
hikari 0ebe81fce2 sys/windows: add GetTextExtentPoint32W 2022-06-11 12:27:18 +03:00
gingerBill c75dd14308 Fix typo 2022-06-11 02:03:54 +01:00
gingerBill c166b6a21d Support optional length parameter on struct tags for multi-pointers 2022-06-11 01:40:58 +01:00
gingerBill 6ed5cbee12 Refactor to fmt_array 2022-06-11 01:38:14 +01:00
gingerBill 5b200ccdf8 Correct #soa printing 2022-06-11 01:32:52 +01:00
gingerBill cf0f0c4b31 Clean up trailing comma 2022-06-11 01:23:41 +01:00
gingerBill 1399ecb41c Refactor with fmt_struct 2022-06-11 01:22:18 +01:00
gingerBill 339d6cfd41 Check for in_bad 2022-06-11 01:17:33 +01:00
gingerBill 7bded4f189 Default to q for strings within structs 2022-06-11 01:09:42 +01:00
gingerBill c7269b9ef0 Allow for fmt:"s" and fmt:"s,field_length" 2022-06-11 01:06:47 +01:00
gingerBill 85688015aa Add fmt:"-" to ignore fields in a struct 2022-06-11 00:55:10 +01:00
hikari 3c6cc575c6 sys/windows: add a couple of constants 2022-06-10 21:17:27 +03:00
gingerBill 0564cb6483 Merge pull request #1824 from ftphikari/master
sys/windows: add registry functions and create winerror.odin
2022-06-10 14:31:48 +01:00
Hasen Judy 4be92c7eb8 HACK work around for creating a cocoa window
See #1825 for more details.

This does not fix the underlying issue; it's merely a work around for
this specific manifestation of the bug.
2022-06-10 17:10:00 +09:00
hikari 88e9eb7d0c sys/windows: add functions and constants 2022-06-09 17:44:51 +03:00
hikari d19fc54c3d sys/windows: add macro 2022-06-08 21:18:11 +03:00
ftphikari 5d4291d9fa Merge branch 'odin-lang:master' into master 2022-06-08 19:55:42 +03:00
hikari b70cd03e9e sys/windows: add interfaces from shobjidl_core.h 2022-06-08 19:38:12 +03:00
hikari e91e5e1fe9 sys/windows: fix formatting 2022-06-07 20:16:32 +03:00
hikari ae57a49915 sys/windows: fix file picker dialog 2022-06-07 17:59:15 +03:00
gingerBill bfcb527b42 Merge pull request #1831 from Despacito696969/master
Fixed `slice.is_sorted_cmp`
2022-06-07 13:23:01 +01:00
Despacito696969 a343fb171d Fixed slice.is_sorted_cmp 2022-06-07 14:07:19 +02:00
Tetralux fa2296a124 [runtime] Add builtin shrink for dynamic arrays and maps
Asks the allocator to shrink the backing allocation to the current __length__, or a capacity
of the user's choosing.

Returns `(did_shrink: bool, err: mem.Allocator_Error)`.

```
shrink(&array) // shrinks to len(array)
shrink(&array, N) // shrink to N capacity

shrink(&map) // shrinks down to len(map)
shrink(&map, N) // shrink to N capacity
```
2022-06-04 23:29:47 +00:00
Dale Weiler a996cfc536 fix 2022-06-04 04:47:52 -04:00
Dale Weiler c1d55b9296 add max heap implementation to slice package 2022-06-04 04:31:55 -04:00
Totoki Kei 5c18cca1ca [core:testing] add global_fail_timeout_thread nil check
Fixed an issue where test would always fail unless set_fail_timeout was called.
2022-06-04 12:21:20 +09:00
JungerBoyo 78e6cd0c60 front() and back()
based on pop_front(), pop_back()
2022-06-04 00:12:34 +02:00
hikari 176954a6d8 sys/windows: add several functions and constants 2022-06-03 22:12:43 +03:00
JungerBoyo ad6b3bd95f unset function
unset function, clear single bits
2022-06-03 15:53:14 +02:00
Josh Engler 762895bc45 - Added EnumDisplaySettingsW and friends.
- Added additional file access/security/sharing attributes
2022-06-03 07:40:45 -04:00
hikari 592e9afa5f sys/windows: add RegSetValueExW 2022-06-03 07:57:21 +03:00
hikari a2e0373934 sys/windows: add registry functions and create winerror.odin 2022-06-03 06:44:42 +03:00
gingerBill fb49841b1d Remove strings dependency from core:sys/windows 2022-06-02 13:02:16 +01:00
gingerBill 01ea0d6f1e Merge branch 'master' of https://github.com/odin-lang/Odin 2022-06-02 12:11:29 +01:00
gingerBill bb7f291f5f Remove simd_rem; Disallow simd_div for integers 2022-06-02 12:10:43 +01:00
gingerBill 174fa9b490 Merge pull request #1817 from IanLilleyT/arena_allocation
Improved arena allocation with alignment
2022-06-02 10:53:23 +01:00
Ian Lilley dda2ed290a tidying up Query_Info return value when not in query features 2022-06-01 22:40:39 -04:00
Ian Lilley ee9908b09e Reserve more space when exceeding, not meeting, capacity 2022-06-01 22:01:00 -04:00
Ian Lilley 66de1856e3 tighter allocation for arena allocator 2022-06-01 21:37:05 -04:00
phillvancejr a315e7c962 Reverted experimental changes that were meant for a branch, in sync with master 2022-02-04 13:45:36 -05:00
phillvancejr 42364f2fce sync with main 2022-02-04 13:16:40 -05:00
phillvancejr 8f600798ef stb_image 2022-02-04 13:03:20 -05:00
phillvancejr f28c268d97 move libgflw3.a to lib/darwin 2022-01-24 10:03:38 -05:00
phillvancejr f1cff20249 moved mac os glfw static lib to lib/darwin subdirectory 2022-01-24 10:02:56 -05:00
Henry Dooley 865d88dd56 review feedback 2021-12-28 10:50:34 -06:00
Henry Dooley 069c05669f cast isize to unsigned int for llvm api, add defaulted name parameter to helper. 2021-12-25 16:46:02 -06:00
Henry Dooley 516065d7c2 factor out alloca generation into a helper 2021-12-25 16:28:09 -06:00
196 changed files with 10378 additions and 4610 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
name: CI name: CI
on: [push, pull_request] on: [push, pull_request, workflow_dispatch]
jobs: jobs:
build_linux: build_linux:
+43
View File
@@ -0,0 +1,43 @@
name: "Close Stale Issues & PRs"
on:
workflow_dispatch:
schedule:
- cron: "0 21 * * *"
permissions:
issues: write
pull-requests: write
jobs:
stale:
runs-on: ubuntu-latest
steps:
- name: Close Stale Issues
uses: actions/stale@v4.1.0
with:
# stale-issue-message: |
# Hello!
#
# I am marking this issue as stale as it has not received any engagement from the community or maintainers 120 days. That does not imply that the issue has no merit! If you feel strongly about this issue
# - open a PR referencing and resolving the issue;
# - leave a comment on it and discuss ideas how you could contribute towards resolving it;
# - leave a comment and describe in detail why this issue is critical for your use case;
# - open a new issue with updated details and a plan on resolving the issue.
#
# The motivation for this automation is to help prioritize issues in the backlog and not ignore, reject, or belittle anyone..
#
# stale-pr-message: |
# Hello!
#
# I am marking this PR as stale as it has not received any engagement from the community or maintainers 120 days. That does not imply that the issue has no merit! If you feel strongly about this issue
# - leave a comment on it and discuss ideas how you could contribute towards resolving it;
# - leave a comment and describe in detail why this issue is critical for your use case;
#
# The motivation for this automation is to help prioritize issues in the backlog and not ignore, reject, or belittle anyone..
days-before-stale: 120
days-before-close: 30
exempt-draft-pr: true
ascending: true
operations-per-run: 1000
exempt-issue-labels: "ignore"
+1 -1
View File
@@ -58,7 +58,7 @@ set libs= ^
set linker_flags= -incremental:no -opt:ref -subsystem:console set linker_flags= -incremental:no -opt:ref -subsystem:console
if %release_mode% EQU 0 ( rem Debug if %release_mode% EQU 0 ( rem Debug
set linker_flags=%linker_flags% -debug set linker_flags=%linker_flags% -debug /NATVIS:src\odin_compiler.natvis
) else ( rem Release ) else ( rem Release
set linker_flags=%linker_flags% -debug set linker_flags=%linker_flags% -debug
) )
+35 -23
View File
@@ -1,12 +1,20 @@
#!/bin/bash #!/usr/bin/env bash
set -eu set -eu
GIT_SHA=$(git rev-parse --short HEAD) : ${CXX=clang++}
: ${CPPFLAGS=}
: ${CXXFLAGS=}
: ${LDFLAGS=}
: ${ODIN_VERSION=dev-$(date +"%Y-%m")}
CPPFLAGS="$CPPFLAGS -DODIN_VERSION_RAW=\"$ODIN_VERSION\""
CXXFLAGS="$CXXFLAGS -std=c++14"
LDFLAGS="$LDFLAGS -pthread -lm -lstdc++"
GIT_SHA=$(git rev-parse --short HEAD || :)
if [ "$GIT_SHA" ]; then CPPFLAGS="$CPPFLAGS -DGIT_SHA=\"$GIT_SHA\""; fi
DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value" DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value"
LDFLAGS="-pthread -lm -lstdc++"
CFLAGS="-std=c++14 -DGIT_SHA=\"$GIT_SHA\""
CFLAGS="$CFLAGS -DODIN_VERSION_RAW=\"dev-$(date +"%Y-%m")\""
CC=clang
OS=$(uname) OS=$(uname)
panic() { panic() {
@@ -18,13 +26,13 @@ version() { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }';
config_darwin() { config_darwin() {
ARCH=$(uname -m) ARCH=$(uname -m)
LLVM_CONFIG=llvm-config : ${LLVM_CONFIG=llvm-config}
# allow for arm only llvm's with version 13 # allow for arm only llvm's with version 13
if [ ARCH == arm64 ]; then if [ ARCH == arm64 ]; then
MIN_LLVM_VERSION=("13.0.0") MIN_LLVM_VERSION=("13.0.0")
else else
# allow for x86 / amd64 all llvm versions begining from 11 # allow for x86 / amd64 all llvm versions beginning from 11
MIN_LLVM_VERSION=("11.1.0") MIN_LLVM_VERSION=("11.1.0")
fi fi
@@ -37,34 +45,38 @@ config_darwin() {
fi fi
LDFLAGS="$LDFLAGS -liconv -ldl" LDFLAGS="$LDFLAGS -liconv -ldl"
CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)" CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
LDFLAGS="$LDFLAGS -lLLVM-C" LDFLAGS="$LDFLAGS -lLLVM-C"
} }
config_freebsd() { config_freebsd() {
LLVM_CONFIG=/usr/local/bin/llvm-config11 : ${LLVM_CONFIG=/usr/local/bin/llvm-config11}
CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)" CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)" LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
} }
config_openbsd() { config_openbsd() {
LLVM_CONFIG=/usr/local/bin/llvm-config : ${LLVM_CONFIG=/usr/local/bin/llvm-config}
LDFLAGS="$LDFLAGS -liconv" LDFLAGS="$LDFLAGS -liconv"
CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)" CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)" LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
} }
config_linux() { config_linux() {
if which llvm-config > /dev/null 2>&1; then : ${LLVM_CONFIG=}
LLVM_CONFIG=llvm-config
elif which llvm-config-11 > /dev/null 2>&1; then if [ ! "$LLVM_CONFIG" ]; then
LLVM_CONFIG=llvm-config-11 if which llvm-config > /dev/null 2>&1; then
elif which llvm-config-11-64 > /dev/null 2>&1; then LLVM_CONFIG=llvm-config
LLVM_CONFIG=llvm-config-11-64 elif which llvm-config-11 > /dev/null 2>&1; then
else LLVM_CONFIG=llvm-config-11
panic "Unable to find LLVM-config" elif which llvm-config-11-64 > /dev/null 2>&1; then
LLVM_CONFIG=llvm-config-11-64
else
panic "Unable to find LLVM-config"
fi
fi fi
MIN_LLVM_VERSION=("11.0.0") MIN_LLVM_VERSION=("11.0.0")
@@ -74,7 +86,7 @@ config_linux() {
fi fi
LDFLAGS="$LDFLAGS -ldl" LDFLAGS="$LDFLAGS -ldl"
CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)" CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)" LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
} }
@@ -97,7 +109,7 @@ build_odin() {
esac esac
set -x set -x
$CC src/main.cpp src/libtommath.cpp $DISABLED_WARNINGS $CFLAGS $EXTRAFLAGS $LDFLAGS -o odin $CXX src/main.cpp src/libtommath.cpp $DISABLED_WARNINGS $CPPFLAGS $CXXFLAGS $EXTRAFLAGS $LDFLAGS -o odin
set +x set +x
} }
+1 -1
View File
@@ -66,7 +66,7 @@ scanner_destroy :: proc(s: ^Scanner) {
} }
// Returns the first non-EOF error that was encounted by the scanner // Returns the first non-EOF error that was encountered by the scanner
scanner_error :: proc(s: ^Scanner) -> Scanner_Error { scanner_error :: proc(s: ^Scanner) -> Scanner_Error {
switch s._err { switch s._err {
case .EOF, nil: case .EOF, nil:
+8
View File
@@ -161,6 +161,10 @@ buffer_write :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
return copy(b.buf[m:], p), nil return copy(b.buf[m:], p), nil
} }
buffer_write_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int) -> (n: int, err: io.Error) {
return buffer_write(b, ([^]byte)(ptr)[:size])
}
buffer_write_string :: proc(b: ^Buffer, s: string) -> (n: int, err: io.Error) { buffer_write_string :: proc(b: ^Buffer, s: string) -> (n: int, err: io.Error) {
b.last_read = .Invalid b.last_read = .Invalid
m, ok := _buffer_try_grow(b, len(s)) m, ok := _buffer_try_grow(b, len(s))
@@ -229,6 +233,10 @@ buffer_read :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
return return
} }
buffer_read_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int) -> (n: int, err: io.Error) {
return buffer_read(b, ([^]byte)(ptr)[:size])
}
buffer_read_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) { buffer_read_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
b.last_read = .Invalid b.last_read = .Invalid
+1 -1
View File
@@ -519,7 +519,7 @@ join_adjacent_string_literals :: proc(cpp: ^Preprocessor, initial_tok: ^Token) {
quote_string :: proc(s: string) -> []byte { quote_string :: proc(s: string) -> []byte {
b := strings.make_builder(0, len(s)+2) b := strings.builder_make(0, len(s)+2)
io.write_quoted_string(strings.to_writer(&b), s, '"') io.write_quoted_string(strings.to_writer(&b), s, '"')
return b.buf[:] return b.buf[:]
} }
+1 -1
View File
@@ -331,7 +331,7 @@ fmin :: proc{libc_fmin, libc_fminf}
fma :: proc{libc_fma, libc_fmaf} fma :: proc{libc_fma, libc_fmaf}
// But retain the 'f' suffix-variant functions as well so they can be used, // But retain the 'f' suffix-variant functions as well so they can be used,
// a trick is used here where we use explicit procedrual overloading of one // a trick is used here where we use explicit procedural overloading of one
// procedure. This is done because the foreign block is marked @(private) and // procedure. This is done because the foreign block is marked @(private) and
// aliasing functions does not remove privateness from the entity. // aliasing functions does not remove privateness from the entity.
acosf :: proc{libc_acosf} acosf :: proc{libc_acosf}
+1 -1
View File
@@ -196,7 +196,7 @@ foreign libc {
getc :: proc(stream: ^FILE) -> int --- getc :: proc(stream: ^FILE) -> int ---
getchar :: proc() -> int --- getchar :: proc() -> int ---
putc :: proc(c: int, stream: ^FILE) -> int --- putc :: proc(c: int, stream: ^FILE) -> int ---
putchar :: proc() -> int --- putchar :: proc(c: int) -> int ---
puts :: proc(s: cstring) -> int --- puts :: proc(s: cstring) -> int ---
ungetc :: proc(c: int, stream: ^FILE) -> int --- ungetc :: proc(c: int, stream: ^FILE) -> int ---
fread :: proc(ptr: rawptr, size: size_t, nmemb: size_t, stream: ^FILE) -> size_t --- fread :: proc(ptr: rawptr, size: size_t, nmemb: size_t, stream: ^FILE) -> size_t ---
+2 -2
View File
@@ -8,7 +8,7 @@ tss_dtor_t :: proc "c" (rawptr)
when ODIN_OS == .Windows { when ODIN_OS == .Windows {
foreign import libc { foreign import libc {
"system:libucrt.lib", "system:libucrt.lib",
"system:msvcprt.lib" "system:msvcprt.lib",
} }
thrd_success :: 0 // _Thrd_success thrd_success :: 0 // _Thrd_success
@@ -77,7 +77,7 @@ when ODIN_OS == .Windows {
when ODIN_OS == .Linux { when ODIN_OS == .Linux {
foreign import libc { foreign import libc {
"system:c", "system:c",
"system:pthread" "system:pthread",
} }
thrd_success :: 0 thrd_success :: 0
+56 -1
View File
@@ -294,6 +294,24 @@ peek_data_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid
} }
} }
@(optimization_mode="speed")
peek_data_at_offset_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
size :: size_of(T)
#no_bounds_check {
if len(z.input_data) >= size + offset {
buf := z.input_data[offset:][:size]
return (^T)(&buf[0])^, .None
}
}
if len(z.input_data) == 0 {
return T{}, .EOF
} else {
return T{}, .Short_Buffer
}
}
@(optimization_mode="speed") @(optimization_mode="speed")
peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid) -> (res: T, err: io.Error) { peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid) -> (res: T, err: io.Error) {
size :: size_of(T) size :: size_of(T)
@@ -321,7 +339,44 @@ peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid
return res, .None return res, .None
} }
peek_data :: proc{peek_data_from_memory, peek_data_from_stream} @(optimization_mode="speed")
peek_data_at_offset_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
size :: size_of(T)
// Get current position to return to.
cur_pos, e1 := z.input->impl_seek(0, .Current)
if e1 != .None {
return T{}, e1
}
// Seek to offset.
pos, e2 := z.input->impl_seek(offset, .Start)
if e2 != .None {
return T{}, e2
}
r, e3 := io.to_reader_at(z.input)
if !e3 {
return T{}, .Empty
}
when size <= 128 {
b: [size]u8
} else {
b := make([]u8, size, context.temp_allocator)
}
_, e4 := io.read_at(r, b[:], pos)
if e4 != .None {
return T{}, .Empty
}
// Return read head to original position.
z.input->impl_seek(cur_pos, .Start)
res = (^T)(&b[0])^
return res, .None
}
peek_data :: proc{peek_data_from_memory, peek_data_from_stream, peek_data_at_offset_from_memory, peek_data_at_offset_from_stream}
+27
View File
@@ -185,6 +185,33 @@ set :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator
return true return true
} }
/*
In:
- ba: ^Bit_Array - a pointer to the Bit Array
- index: The bit index. Can be an enum member.
Out:
- ok: Whether or not we managed to unset requested bit.
`unset` automatically resizes the Bit Array to accommodate the requested index if needed.
*/
unset :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (ok: bool) {
idx := int(index) - ba.bias
if ba == nil || int(index) < ba.bias { return false }
context.allocator = allocator
leg_index := idx >> INDEX_SHIFT
bit_index := idx & INDEX_MASK
resize_if_needed(ba, leg_index) or_return
ba.max_index = max(idx, ba.max_index)
ba.bits[leg_index] &= ~(1 << uint(bit_index))
return true
}
/* /*
A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative). A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
*/ */
@@ -85,7 +85,6 @@ _shift_down :: proc(pq: ^$Q/Priority_Queue($T), i0, n: int) -> bool {
_shift_up :: proc(pq: ^$Q/Priority_Queue($T), j: int) { _shift_up :: proc(pq: ^$Q/Priority_Queue($T), j: int) {
j := j j := j
queue := pq.queue[:] queue := pq.queue[:]
n := builtin.len(queue)
for 0 <= j { for 0 <= j {
i := (j-1)/2 i := (j-1)/2
if i == j || !pq.less(queue[j], queue[i]) { if i == j || !pq.less(queue[j], queue[i]) {
+29
View File
@@ -69,6 +69,23 @@ get :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> T {
idx := (uint(i)+q.offset)%builtin.len(q.data) idx := (uint(i)+q.offset)%builtin.len(q.data)
return q.data[idx] return q.data[idx]
} }
front :: proc(q: ^$Q/Queue($T)) -> T {
return q.data[q.offset]
}
front_ptr :: proc(q: ^$Q/Queue($T)) -> ^T {
return &q.data[q.offset]
}
back :: proc(q: ^$Q/Queue($T)) -> T {
idx := (q.offset+uint(q.len))%builtin.len(q.data)
return q.data[idx]
}
back_ptr :: proc(q: ^$Q/Queue($T)) -> ^T {
idx := (q.offset+uint(q.len))%builtin.len(q.data)
return &q.data[idx]
}
set :: proc(q: ^$Q/Queue($T), #any_int i: int, val: T, loc := #caller_location) { set :: proc(q: ^$Q/Queue($T), #any_int i: int, val: T, loc := #caller_location) {
runtime.bounds_check_error_loc(loc, i, builtin.len(q.data)) runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
@@ -82,6 +99,18 @@ get_ptr :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> ^
return &q.data[idx] return &q.data[idx]
} }
peek_front :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> ^T {
runtime.bounds_check_error_loc(loc, 0, builtin.len(q.data))
idx := q.offset%builtin.len(q.data)
return &q.data[idx]
}
peek_back :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> ^T {
runtime.bounds_check_error_loc(loc, int(q.len - 1), builtin.len(q.data))
idx := (uint(q.len - 1)+q.offset)%builtin.len(q.data)
return &q.data[idx]
}
// Push an element to the back of the queue // Push an element to the back of the queue
push_back :: proc(q: ^$Q/Queue($T), elem: T) -> bool { push_back :: proc(q: ^$Q/Queue($T), elem: T) -> bool {
if space(q^) == 0 { if space(q^) == 0 {
+1 -1
View File
@@ -81,7 +81,7 @@ The crypto package is not thread-safe at the moment. This may change in the futu
### Disclaimer ### Disclaimer
The algorithms were ported out of curiosity and due to interest in the field. The algorithms were ported out of curiosity and due to interest in the field.
We have not had any of the code verified by a third party or tested/fuzzed by any automatic means. We have not had any of the code verified by a third party or tested/fuzzed by any automatic means.
Whereever we were able to find official test vectors, those were used to verify the implementation. Wherever we were able to find official test vectors, those were used to verify the implementation.
We do not recommend using them in a production environment, without any additional testing and/or verification. We do not recommend using them in a production environment, without any additional testing and/or verification.
### ToDo ### ToDo
+1 -1
View File
@@ -30,6 +30,6 @@ equivalence.
For the most part, alterations to the base fiat-crypto generated code was For the most part, alterations to the base fiat-crypto generated code was
kept to a minimum, to aid auditability. This results in a somewhat kept to a minimum, to aid auditability. This results in a somewhat
ideosyncratic style, and in some cases minor performance penalties. idiosyncratic style, and in some cases minor performance penalties.
[1]: https://github.com/mit-plv/fiat-crypto [1]: https://github.com/mit-plv/fiat-crypto
+1 -1
View File
@@ -233,7 +233,7 @@ init :: proc(ctx: ^Context, key: []byte, c_rounds, d_rounds: int) {
} }
update :: proc(ctx: ^Context, data: []byte) { update :: proc(ctx: ^Context, data: []byte) {
assert(ctx.is_initialized, "crypto/siphash: Context is not initalized") assert(ctx.is_initialized, "crypto/siphash: Context is not initialized")
ctx.last_block = len(data) / 8 * 8 ctx.last_block = len(data) / 8 * 8
ctx.buf = data ctx.buf = data
i := 0 i := 0
+4 -4
View File
@@ -25,8 +25,8 @@ import "core:strings"
MAX_RUNE_CODEPOINT :: int(unicode.MAX_RUNE) MAX_RUNE_CODEPOINT :: int(unicode.MAX_RUNE)
write_rune :: strings.write_rune_builder write_rune :: strings.write_rune
write_string :: strings.write_string_builder write_string :: strings.write_string
Error :: enum u8 { Error :: enum u8 {
None = 0, None = 0,
@@ -94,8 +94,8 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
l := len(input) l := len(input)
if l == 0 { return "", .None } if l == 0 { return "", .None }
builder := strings.make_builder() builder := strings.builder_make()
defer strings.destroy_builder(&builder) defer strings.builder_destroy(&builder)
t := Tokenizer{src=input} t := Tokenizer{src=input}
in_data := false in_data := false
@@ -8,7 +8,7 @@ import "core:time"
doc_print :: proc(doc: ^xml.Document) { doc_print :: proc(doc: ^xml.Document) {
buf: strings.Builder buf: strings.Builder
defer strings.destroy_builder(&buf) defer strings.builder_destroy(&buf)
w := strings.to_writer(&buf) w := strings.to_writer(&buf)
xml.print(w, doc) xml.print(w, doc)
+1 -1
View File
@@ -107,7 +107,7 @@ Node :: struct {
/* Conventions */ /* Conventions */
/* ------------ /* ------------
Much of HxA's use is based on convention. HxA lets users store arbitrary data in its structure that can be parsed but whose semantic meaning does not need to be understood. Much of HxA's use is based on convention. HxA lets users store arbitrary data in its structure that can be parsed but whose semantic meaning does not need to be understood.
A few conventions are hard, and some are soft. Hard convention that a user HAS to follow in order to produce a valid file. Hard conventions simplify parsing becaus the parser can make some assumptions. Soft convenbtions are basicly recomendations of how to store common data. A few conventions are hard, and some are soft. Hard convention that a user HAS to follow in order to produce a valid file. Hard conventions simplify parsing becaus the parser can make some assumptions. Soft convenbtions are basically recomendations of how to store common data.
If you use HxA for something not covered by the conventions but need a convention for your use case. Please let us know so that we can add it! If you use HxA for something not covered by the conventions but need a convention for your use case. Please let us know so that we can add it!
*/ */
+218 -43
View File
@@ -17,25 +17,54 @@ Marshal_Error :: union #shared_nil {
io.Error, io.Error,
} }
marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) { // careful with MJSON maps & non quotes usage as keys without whitespace will lead to bad results
b := strings.make_builder(allocator) Marshal_Options :: struct {
// output based on spec
spec: Specification,
// use line breaks & tab|spaces
pretty: bool,
// spacing
use_spaces: bool,
spaces: int,
// state
indentation: int,
// option to output uint in JSON5 & MJSON
write_uint_as_hex: bool,
// mjson output options
mjson_keys_use_quotes: bool,
mjson_keys_use_equal_sign: bool,
// mjson state
mjson_skipped_first_braces_start: bool,
mjson_skipped_first_braces_end: bool,
}
marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) {
b := strings.builder_make(allocator)
defer if err != nil { defer if err != nil {
strings.destroy_builder(&b) strings.builder_destroy(&b)
} }
marshal_to_builder(&b, v) or_return opt := opt
marshal_to_builder(&b, v, &opt) or_return
if len(b.buf) != 0 { if len(b.buf) != 0 {
data = b.buf[:] data = b.buf[:]
} }
return data, nil return data, nil
} }
marshal_to_builder :: proc(b: ^strings.Builder, v: any) -> Marshal_Error { marshal_to_builder :: proc(b: ^strings.Builder, v: any, opt: ^Marshal_Options) -> Marshal_Error {
return marshal_to_writer(strings.to_writer(b), v) return marshal_to_writer(strings.to_writer(b), v, opt)
} }
marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) { marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: Marshal_Error) {
if v == nil { if v == nil {
io.write_string(w, "null") or_return io.write_string(w, "null") or_return
return return
@@ -49,13 +78,14 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
unreachable() unreachable()
case runtime.Type_Info_Integer: case runtime.Type_Info_Integer:
buf: [21]byte buf: [40]byte
u: u128 u: u128
switch i in a { switch i in a {
case i8: u = u128(i) case i8: u = u128(i)
case i16: u = u128(i) case i16: u = u128(i)
case i32: u = u128(i) case i32: u = u128(i)
case i64: u = u128(i) case i64: u = u128(i)
case i128: u = u128(i)
case int: u = u128(i) case int: u = u128(i)
case u8: u = u128(i) case u8: u = u128(i)
case u16: u = u128(i) case u16: u = u128(i)
@@ -82,7 +112,21 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
case u128be: u = u128(i) case u128be: u = u128(i)
} }
s := strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil) s: string
// allow uints to be printed as hex
if opt.write_uint_as_hex && (opt.spec == .JSON5 || opt.spec == .MJSON) {
switch i in a {
case u8, u16, u32, u64, u128:
s = strconv.append_bits_128(buf[:], u, 16, info.signed, 8*ti.size, "0123456789abcdef", { .Prefix })
case:
s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
}
} else {
s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
}
io.write_string(w, s) or_return io.write_string(w, s) or_return
@@ -147,6 +191,9 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
case runtime.Type_Info_Multi_Pointer: case runtime.Type_Info_Multi_Pointer:
return .Unsupported_Type return .Unsupported_Type
case runtime.Type_Info_Soa_Pointer:
return .Unsupported_Type
case runtime.Type_Info_Procedure: case runtime.Type_Info_Procedure:
return .Unsupported_Type return .Unsupported_Type
@@ -166,52 +213,48 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
return .Unsupported_Type return .Unsupported_Type
case runtime.Type_Info_Array: case runtime.Type_Info_Array:
io.write_byte(w, '[') or_return opt_write_start(w, opt, '[') or_return
for i in 0..<info.count { for i in 0..<info.count {
if i > 0 { io.write_string(w, ", ") or_return } opt_write_iteration(w, opt, i) or_return
data := uintptr(v.data) + uintptr(i*info.elem_size) data := uintptr(v.data) + uintptr(i*info.elem_size)
marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
} }
io.write_byte(w, ']') or_return opt_write_end(w, opt, ']') or_return
case runtime.Type_Info_Enumerated_Array: case runtime.Type_Info_Enumerated_Array:
index := runtime.type_info_base(info.index).variant.(runtime.Type_Info_Enum) index := runtime.type_info_base(info.index).variant.(runtime.Type_Info_Enum)
io.write_byte(w, '[') or_return opt_write_start(w, opt, '[') or_return
for i in 0..<info.count { for i in 0..<info.count {
if i > 0 { io.write_string(w, ", ") or_return } opt_write_iteration(w, opt, i) or_return
data := uintptr(v.data) + uintptr(i*info.elem_size) data := uintptr(v.data) + uintptr(i*info.elem_size)
marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
} }
io.write_byte(w, ']') or_return opt_write_end(w, opt, ']') or_return
case runtime.Type_Info_Dynamic_Array: case runtime.Type_Info_Dynamic_Array:
io.write_byte(w, '[') or_return opt_write_start(w, opt, '[') or_return
array := cast(^mem.Raw_Dynamic_Array)v.data array := cast(^mem.Raw_Dynamic_Array)v.data
for i in 0..<array.len { for i in 0..<array.len {
if i > 0 { io.write_string(w, ", ") or_return } opt_write_iteration(w, opt, i) or_return
data := uintptr(array.data) + uintptr(i*info.elem_size) data := uintptr(array.data) + uintptr(i*info.elem_size)
marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
} }
io.write_byte(w, ']') or_return opt_write_end(w, opt, ']') or_return
case runtime.Type_Info_Slice: case runtime.Type_Info_Slice:
io.write_byte(w, '[') or_return opt_write_start(w, opt, '[') or_return
slice := cast(^mem.Raw_Slice)v.data slice := cast(^mem.Raw_Slice)v.data
for i in 0..<slice.len { for i in 0..<slice.len {
if i > 0 { io.write_string(w, ", ") or_return } opt_write_iteration(w, opt, i) or_return
data := uintptr(slice.data) + uintptr(i*info.elem_size) data := uintptr(slice.data) + uintptr(i*info.elem_size)
marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
} }
io.write_byte(w, ']') or_return opt_write_end(w, opt, ']') or_return
case runtime.Type_Info_Map: case runtime.Type_Info_Map:
m := (^mem.Raw_Map)(v.data) m := (^mem.Raw_Map)(v.data)
opt_write_start(w, opt, '{') or_return
io.write_byte(w, '{') or_return
if m != nil { if m != nil {
if info.generated_struct == nil { if info.generated_struct == nil {
return .Unsupported_Type return .Unsupported_Type
@@ -223,31 +266,50 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
entry_size := ed.elem_size entry_size := ed.elem_size
for i in 0..<entries.len { for i in 0..<entries.len {
if i > 0 { io.write_string(w, ", ") or_return } opt_write_iteration(w, opt, i) or_return
data := uintptr(entries.data) + uintptr(i*entry_size) data := uintptr(entries.data) + uintptr(i*entry_size)
key := rawptr(data + entry_type.offsets[2]) key := rawptr(data + entry_type.offsets[2])
value := rawptr(data + entry_type.offsets[3]) value := rawptr(data + entry_type.offsets[3])
marshal_to_writer(w, any{key, info.key.id}) or_return // check for string type
io.write_string(w, ": ") or_return {
marshal_to_writer(w, any{value, info.value.id}) or_return v := any{key, info.key.id}
ti := runtime.type_info_base(type_info_of(v.id))
a := any{v.data, ti.id}
name: string
#partial switch info in ti.variant {
case runtime.Type_Info_String:
switch s in a {
case string: name = s
case cstring: name = string(s)
}
opt_write_key(w, opt, name) or_return
case: return .Unsupported_Type
}
}
marshal_to_writer(w, any{value, info.value.id}, opt) or_return
} }
} }
io.write_byte(w, '}') or_return
opt_write_end(w, opt, '}') or_return
case runtime.Type_Info_Struct: case runtime.Type_Info_Struct:
io.write_byte(w, '{') or_return opt_write_start(w, opt, '{') or_return
for name, i in info.names { for name, i in info.names {
if i > 0 { io.write_string(w, ", ") or_return } opt_write_iteration(w, opt, i) or_return
io.write_quoted_string(w, name) or_return opt_write_key(w, opt, name) or_return
io.write_string(w, ": ") or_return
id := info.types[i].id id := info.types[i].id
data := rawptr(uintptr(v.data) + info.offsets[i]) data := rawptr(uintptr(v.data) + info.offsets[i])
marshal_to_writer(w, any{data, id}) or_return marshal_to_writer(w, any{data, id}, opt) or_return
} }
io.write_byte(w, '}') or_return
opt_write_end(w, opt, '}') or_return
case runtime.Type_Info_Union: case runtime.Type_Info_Union:
tag_ptr := uintptr(v.data) + info.tag_offset tag_ptr := uintptr(v.data) + info.tag_offset
@@ -270,11 +332,11 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
io.write_string(w, "null") or_return io.write_string(w, "null") or_return
} else { } else {
id := info.variants[tag-1].id id := info.variants[tag-1].id
return marshal_to_writer(w, any{v.data, id}) return marshal_to_writer(w, any{v.data, id}, opt)
} }
case runtime.Type_Info_Enum: case runtime.Type_Info_Enum:
return marshal_to_writer(w, any{v.data, info.base.id}) return marshal_to_writer(w, any{v.data, info.base.id}, opt)
case runtime.Type_Info_Bit_Set: case runtime.Type_Info_Bit_Set:
is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool { is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool {
@@ -330,3 +392,116 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
return return
} }
// write key as quoted string or with optional quotes in mjson
opt_write_key :: proc(w: io.Writer, opt: ^Marshal_Options, name: string) -> (err: io.Error) {
switch opt.spec {
case .JSON, .JSON5:
io.write_quoted_string(w, name) or_return
io.write_string(w, ": ") or_return
case .MJSON:
if opt.mjson_keys_use_quotes {
io.write_quoted_string(w, name) or_return
} else {
io.write_string(w, name) or_return
}
if opt.mjson_keys_use_equal_sign {
io.write_string(w, " = ") or_return
} else {
io.write_string(w, ": ") or_return
}
}
return
}
// insert start byte and increase indentation on pretty
opt_write_start :: proc(w: io.Writer, opt: ^Marshal_Options, c: byte) -> (err: io.Error) {
// skip mjson starting braces
if opt.spec == .MJSON && !opt.mjson_skipped_first_braces_start {
opt.mjson_skipped_first_braces_start = true
return
}
io.write_byte(w, c) or_return
opt.indentation += 1
if opt.pretty {
io.write_byte(w, '\n') or_return
}
return
}
// insert comma seperation and write indentations
opt_write_iteration :: proc(w: io.Writer, opt: ^Marshal_Options, iteration: int) -> (err: io.Error) {
switch opt.spec {
case .JSON, .JSON5:
if iteration > 0 {
io.write_string(w, ", ") or_return
if opt.pretty {
io.write_byte(w, '\n') or_return
}
}
opt_write_indentation(w, opt) or_return
case .MJSON:
if iteration > 0 {
// on pretty no commas necessary
if opt.pretty {
io.write_byte(w, '\n') or_return
} else {
// comma seperation necessary for non pretty output!
io.write_string(w, ", ") or_return
}
}
opt_write_indentation(w, opt) or_return
}
return
}
// decrease indent, write spacing and insert end byte
opt_write_end :: proc(w: io.Writer, opt: ^Marshal_Options, c: byte) -> (err: io.Error) {
if opt.spec == .MJSON && opt.mjson_skipped_first_braces_start && !opt.mjson_skipped_first_braces_end {
if opt.indentation == 0 {
opt.mjson_skipped_first_braces_end = true
return
}
}
opt.indentation -= 1
if opt.pretty {
io.write_byte(w, '\n') or_return
opt_write_indentation(w, opt) or_return
}
io.write_byte(w, c) or_return
return
}
// writes current indentation level based on options
opt_write_indentation :: proc(w: io.Writer, opt: ^Marshal_Options) -> (err: io.Error) {
if !opt.pretty {
return
}
if opt.use_spaces {
spaces := opt.spaces == 0 ? 4 : opt.spaces
for _ in 0..<opt.indentation * spaces {
io.write_byte(w, ' ') or_return
}
} else {
for _ in 0..<opt.indentation {
io.write_byte(w, '\t') or_return
}
}
return
}
+1 -1
View File
@@ -40,7 +40,7 @@ parse_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers
return parse_object(&p) return parse_object(&p)
case .JSON5: case .JSON5:
return parse_value(&p) return parse_value(&p)
case .MJSON: case .SJSON:
#partial switch p.curr_token.kind { #partial switch p.curr_token.kind {
case .Ident, .String: case .Ident, .String:
return parse_object_body(&p, .EOF) return parse_object_body(&p, .EOF)
+3 -2
View File
@@ -33,8 +33,9 @@ package json
Specification :: enum { Specification :: enum {
JSON, JSON,
JSON5, // https://json5.org/ JSON5, // https://json5.org/
MJSON, // https://bitsquid.blogspot.com/2009/10/simplified-json-notation.html SJSON, // https://bitsquid.blogspot.com/2009/10/simplified-json-notation.html
Bitsquid = MJSON, Bitsquid = SJSON,
MJSON = SJSON,
} }
+2 -2
View File
@@ -325,7 +325,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
UNSUPPORTED_TYPE := Unsupported_Type_Error{v.id, p.curr_token} UNSUPPORTED_TYPE := Unsupported_Type_Error{v.id, p.curr_token}
if end_token == .Close_Brace { if end_token == .Close_Brace {
assert(expect_token(p, .Open_Brace) == nil) unmarshal_expect_token(p, .Open_Brace)
} }
v := v v := v
@@ -473,7 +473,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
} }
if end_token == .Close_Brace { if end_token == .Close_Brace {
assert(expect_token(p, .Close_Brace) == nil) unmarshal_expect_token(p, .Close_Brace)
} }
return return
} }
+1 -1
View File
@@ -84,7 +84,7 @@ example :: proc() {
doc_hash :: proc(doc: ^xml.Document, print := false) -> (crc32: u32) { doc_hash :: proc(doc: ^xml.Document, print := false) -> (crc32: u32) {
buf: strings.Builder buf: strings.Builder
defer strings.destroy_builder(&buf) defer strings.builder_destroy(&buf)
w := strings.to_writer(&buf) w := strings.to_writer(&buf)
xml.print(w, doc) xml.print(w, doc)
+649 -490
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -1,8 +1,8 @@
package hash package hash
@(optimization_mode="speed") @(optimization_mode="speed")
crc64_ecma_182 :: proc(data: []byte, seed := u32(0)) -> u64 #no_bounds_check { crc64_ecma_182 :: proc(data: []byte, seed := u64(0)) -> (result: u64) #no_bounds_check {
result := u64(seed) result = seed
#no_bounds_check for b in data { #no_bounds_check for b in data {
result = result<<8 ~ _crc64_table_ecma_182[((result>>56) ~ u64(b)) & 0xff] result = result<<8 ~ _crc64_table_ecma_182[((result>>56) ~ u64(b)) & 0xff]
} }
+101 -95
View File
@@ -172,108 +172,114 @@ murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 {
return h1 return h1
} }
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L96
@(optimization_mode="speed") @(optimization_mode="speed")
murmur64 :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 { murmur64a :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
when size_of(int) == 8 { m :: 0xc6a4a7935bd1e995
m :: 0xc6a4a7935bd1e995 r :: 47
r :: 47
h: u64 = seed ~ (u64(len(data)) * m) h: u64 = seed ~ (u64(len(data)) * m)
data64 := mem.slice_ptr(cast(^u64)raw_data(data), len(data)/size_of(u64)) data64 := mem.slice_data_cast([]u64, data)
for _, i in data64 { for _, i in data64 {
k := data64[i] k := data64[i]
k *= m k *= m
k ~= k>>r k ~= k>>r
k *= m k *= m
h ~= k h ~= k
h *= m
}
switch len(data)&7 {
case 7: h ~= u64(data[6]) << 48; fallthrough
case 6: h ~= u64(data[5]) << 40; fallthrough
case 5: h ~= u64(data[4]) << 32; fallthrough
case 4: h ~= u64(data[3]) << 24; fallthrough
case 3: h ~= u64(data[2]) << 16; fallthrough
case 2: h ~= u64(data[1]) << 8; fallthrough
case 1:
h ~= u64(data[0])
h *= m
}
h ~= h>>r
h *= m h *= m
h ~= h>>r
return h
} else {
m :: 0x5bd1e995
r :: 24
h1 := u32(seed) ~ u32(len(data))
h2 := u32(seed) >> 32
data32 := mem.slice_ptr(cast(^u32)raw_data(data), len(data)/size_of(u32))
len := len(data)
i := 0
for len >= 8 {
k1, k2: u32
k1 = data32[i]; i += 1
k1 *= m
k1 ~= k1>>r
k1 *= m
h1 *= m
h1 ~= k1
len -= 4
k2 = data32[i]; i += 1
k2 *= m
k2 ~= k2>>r
k2 *= m
h2 *= m
h2 ~= k2
len -= 4
}
if len >= 4 {
k1: u32
k1 = data32[i]; i += 1
k1 *= m
k1 ~= k1>>r
k1 *= m
h1 *= m
h1 ~= k1
len -= 4
}
// TODO(bill): Fix this
#no_bounds_check data8 := mem.slice_to_bytes(data32[i:])[:3]
switch len {
case 3:
h2 ~= u32(data8[2]) << 16
fallthrough
case 2:
h2 ~= u32(data8[1]) << 8
fallthrough
case 1:
h2 ~= u32(data8[0])
h2 *= m
}
h1 ~= h2>>18
h1 *= m
h2 ~= h1>>22
h2 *= m
h1 ~= h2>>17
h1 *= m
h2 ~= h1>>19
h2 *= m
return u64(h1)<<32 | u64(h2)
} }
offset := len(data64) * size_of(u64)
switch len(data)&7 {
case 7: h ~= u64(data[offset + 6]) << 48; fallthrough
case 6: h ~= u64(data[offset + 5]) << 40; fallthrough
case 5: h ~= u64(data[offset + 4]) << 32; fallthrough
case 4: h ~= u64(data[offset + 3]) << 24; fallthrough
case 3: h ~= u64(data[offset + 2]) << 16; fallthrough
case 2: h ~= u64(data[offset + 1]) << 8; fallthrough
case 1:
h ~= u64(data[offset + 0])
h *= m
}
h ~= h>>r
h *= m
h ~= h>>r
return h
}
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L140
@(optimization_mode="speed")
murmur64b :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
m :: 0x5bd1e995
r :: 24
h1 := u32(seed) ~ u32(len(data))
h2 := u32(seed) >> 32
data32 := mem.slice_ptr(cast(^u32)raw_data(data), len(data)/size_of(u32))
len := len(data)
i := 0
for len >= 8 {
k1, k2: u32
k1 = data32[i]; i += 1
k1 *= m
k1 ~= k1>>r
k1 *= m
h1 *= m
h1 ~= k1
len -= 4
k2 = data32[i]; i += 1
k2 *= m
k2 ~= k2>>r
k2 *= m
h2 *= m
h2 ~= k2
len -= 4
}
if len >= 4 {
k1: u32
k1 = data32[i]; i += 1
k1 *= m
k1 ~= k1>>r
k1 *= m
h1 *= m
h1 ~= k1
len -= 4
}
// TODO(bill): Fix this
#no_bounds_check data8 := mem.slice_to_bytes(data32[i:])[:3]
switch len {
case 3:
h2 ~= u32(data8[2]) << 16
fallthrough
case 2:
h2 ~= u32(data8[1]) << 8
fallthrough
case 1:
h2 ~= u32(data8[0])
h2 *= m
}
h1 ~= h2>>18
h1 *= m
h2 ~= h1>>22
h2 *= m
h1 ~= h2>>17
h1 *= m
h2 ~= h1>>19
h2 *= m
return u64(h1)<<32 | u64(h2)
} }
@(optimization_mode="speed") @(optimization_mode="speed")
+65 -7
View File
@@ -46,7 +46,7 @@ Image :: struct {
height: int, height: int,
channels: int, channels: int,
depth: int, // Channel depth in bits, typically 8 or 16 depth: int, // Channel depth in bits, typically 8 or 16
pixels: bytes.Buffer, pixels: bytes.Buffer `fmt:"-"`,
/* /*
Some image loaders/writers can return/take an optional background color. Some image loaders/writers can return/take an optional background color.
For convenience, we return them as u16 so we don't need to switch on the type For convenience, we return them as u16 so we don't need to switch on the type
@@ -61,6 +61,7 @@ Image_Metadata :: union #shared_nil {
^Netpbm_Info, ^Netpbm_Info,
^PNG_Info, ^PNG_Info,
^QOI_Info, ^QOI_Info,
^TGA_Info,
} }
@@ -168,6 +169,7 @@ Error :: union #shared_nil {
General_Image_Error :: enum { General_Image_Error :: enum {
None = 0, None = 0,
Unsupported_Option,
// File I/O // File I/O
Unable_To_Read_File, Unable_To_Read_File,
Unable_To_Write_File, Unable_To_Write_File,
@@ -376,10 +378,20 @@ QOI_Info :: struct {
header: QOI_Header, header: QOI_Header,
} }
TGA_Data_Type :: enum u8 {
No_Image_Data = 0,
Uncompressed_Color_Mapped = 1,
Uncompressed_RGB = 2,
Uncompressed_Black_White = 3,
Compressed_Color_Mapped = 9,
Compressed_RGB = 10,
Compressed_Black_White = 11,
}
TGA_Header :: struct #packed { TGA_Header :: struct #packed {
id_length: u8, id_length: u8,
color_map_type: u8, color_map_type: u8,
data_type_code: u8, data_type_code: TGA_Data_Type,
color_map_origin: u16le, color_map_origin: u16le,
color_map_length: u16le, color_map_length: u16le,
color_map_depth: u8, color_map_depth: u8,
@@ -390,6 +402,52 @@ TGA_Header :: struct #packed {
} }
#assert(size_of(TGA_Header) == 18) #assert(size_of(TGA_Header) == 18)
New_TGA_Signature :: "TRUEVISION-XFILE.\x00"
TGA_Footer :: struct #packed {
extension_area_offset: u32le,
developer_directory_offset: u32le,
signature: [18]u8 `fmt:"s,0"`, // Should match signature if New TGA.
}
#assert(size_of(TGA_Footer) == 26)
TGA_Extension :: struct #packed {
extension_size: u16le, // Size of this struct. If not 495 bytes it means it's an unsupported version.
author_name: [41]u8 `fmt:"s,0"`, // Author name, ASCII. Zero-terminated
author_comments: [324]u8 `fmt:"s,0"`, // Author comments, formatted as 4 lines of 80 character lines, each zero terminated.
datetime: struct {month, day, year, hour, minute, second: u16le},
job_name: [41]u8 `fmt:"s,0"`, // Author name, ASCII. Zero-terminated
job_time: struct {hour, minute, second: u16le},
software_id: [41]u8 `fmt:"s,0"`, // Software ID name, ASCII. Zero-terminated
software_version: struct #packed {
number: u16le, // Version number * 100
letter: u8 `fmt:"r"`, // " " if not used
},
key_color: [4]u8, // ARGB key color used at time of production
aspect_ratio: [2]u16le, // Numerator / Denominator
gamma: [2]u16le, // Numerator / Denominator, range should be 0.0..10.0
color_correction_offset: u32le, // 0 if no color correction information
postage_stamp_offset: u32le, // 0 if no thumbnail
scanline_offset: u32le, // 0 if no scanline table
attributes: TGA_Alpha_Kind,
}
#assert(size_of(TGA_Extension) == 495)
TGA_Alpha_Kind :: enum u8 {
None,
Undefined_Ignore,
Undefined_Retain,
Useful,
Premultiplied,
}
TGA_Info :: struct {
header: TGA_Header,
image_id: string,
footer: Maybe(TGA_Footer),
extension: Maybe(TGA_Extension),
}
// Function to help with image buffer calculations // Function to help with image buffer calculations
compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes := int(0)) -> (size: int) { compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes := int(0)) -> (size: int) {
size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height
@@ -469,7 +527,7 @@ return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok
} }
// Does the image have 1 or 2 channels, a valid bit depth (8 or 16), // Does the image have 1 or 2 channels, a valid bit depth (8 or 16),
// Is the pointer valid, are the dimenions valid? // Is the pointer valid, are the dimensions valid?
is_valid_grayscale_image :: proc(img: ^Image) -> (ok: bool) { is_valid_grayscale_image :: proc(img: ^Image) -> (ok: bool) {
// Were we actually given a valid image? // Were we actually given a valid image?
if img == nil { if img == nil {
@@ -489,7 +547,7 @@ is_valid_grayscale_image :: proc(img: ^Image) -> (ok: bool) {
// This returns 0 if any of the inputs is zero. // This returns 0 if any of the inputs is zero.
bytes_expected := compute_buffer_size(img.width, img.height, img.channels, img.depth) bytes_expected := compute_buffer_size(img.width, img.height, img.channels, img.depth)
// If the dimenions are invalid or the buffer size doesn't match the image characteristics, bail. // If the dimensions are invalid or the buffer size doesn't match the image characteristics, bail.
if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS { if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS {
return false return false
} }
@@ -498,7 +556,7 @@ is_valid_grayscale_image :: proc(img: ^Image) -> (ok: bool) {
} }
// Does the image have 3 or 4 channels, a valid bit depth (8 or 16), // Does the image have 3 or 4 channels, a valid bit depth (8 or 16),
// Is the pointer valid, are the dimenions valid? // Is the pointer valid, are the dimensions valid?
is_valid_color_image :: proc(img: ^Image) -> (ok: bool) { is_valid_color_image :: proc(img: ^Image) -> (ok: bool) {
// Were we actually given a valid image? // Were we actually given a valid image?
if img == nil { if img == nil {
@@ -518,7 +576,7 @@ is_valid_color_image :: proc(img: ^Image) -> (ok: bool) {
// This returns 0 if any of the inputs is zero. // This returns 0 if any of the inputs is zero.
bytes_expected := compute_buffer_size(img.width, img.height, img.channels, img.depth) bytes_expected := compute_buffer_size(img.width, img.height, img.channels, img.depth)
// If the dimenions are invalid or the buffer size doesn't match the image characteristics, bail. // If the dimensions are invalid or the buffer size doesn't match the image characteristics, bail.
if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS { if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS {
return false return false
} }
@@ -527,7 +585,7 @@ is_valid_color_image :: proc(img: ^Image) -> (ok: bool) {
} }
// Does the image have 1..4 channels, a valid bit depth (8 or 16), // Does the image have 1..4 channels, a valid bit depth (8 or 16),
// Is the pointer valid, are the dimenions valid? // Is the pointer valid, are the dimensions valid?
is_valid_image :: proc(img: ^Image) -> (ok: bool) { is_valid_image :: proc(img: ^Image) -> (ok: bool) {
// Were we actually given a valid image? // Were we actually given a valid image?
if img == nil { if img == nil {
+2 -2
View File
@@ -130,7 +130,7 @@ save_to_buffer :: proc(img: ^Image, custom_info: Info = {}, allocator := context
// we will write to a string builder // we will write to a string builder
data: strings.Builder data: strings.Builder
strings.init_builder(&data) strings.builder_init(&data)
// all PNM headers start with the format // all PNM headers start with the format
fmt.sbprintf(&data, "%s\n", header.format) fmt.sbprintf(&data, "%s\n", header.format)
@@ -409,7 +409,7 @@ _parse_header_pam :: proc(data: []byte, allocator := context.allocator) -> (head
// string buffer for the tupltype // string buffer for the tupltype
tupltype: strings.Builder tupltype: strings.Builder
strings.init_builder(&tupltype, context.temp_allocator); defer strings.destroy_builder(&tupltype) strings.builder_init(&tupltype, context.temp_allocator); defer strings.builder_destroy(&tupltype)
fmt.sbprint(&tupltype, "") fmt.sbprint(&tupltype, "")
// PAM uses actual lines, so we can iterate easily // PAM uses actual lines, so we can iterate easily
+1 -1
View File
@@ -219,7 +219,7 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b
defer close(fd) defer close(fd)
write_string(fd, write_string(fd,
fmt.tprintf("P6\n%v %v\n%v\n", width, height, (1 << uint(depth) - 1)), fmt.tprintf("P6\n%v %v\n%v\n", width, height, uint(1 << uint(depth) - 1)),
) )
if channels == 3 { if channels == 3 {
+3 -3
View File
@@ -1002,7 +1002,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
o16 = o16[out_image_channels:] o16 = o16[out_image_channels:]
} }
case: case:
unreachable("We should never seen # channels other than 1-4 inclusive.") panic("We should never seen # channels other than 1-4 inclusive.")
} }
img.pixels = t img.pixels = t
@@ -1195,7 +1195,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
o = o[out_image_channels:] o = o[out_image_channels:]
} }
case: case:
unreachable("We should never seen # channels other than 1-4 inclusive.") panic("We should never seen # channels other than 1-4 inclusive.")
} }
img.pixels = t img.pixels = t
@@ -1206,7 +1206,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
This may change if we ever don't expand 1, 2 and 4 bit images. But, those raw This may change if we ever don't expand 1, 2 and 4 bit images. But, those raw
returns will likely bypass this processing pipeline. returns will likely bypass this processing pipeline.
*/ */
unreachable("We should never see bit depths other than 8, 16 and 'Paletted' here.") panic("We should never see bit depths other than 8, 16 and 'Paletted' here.")
} }
return img, nil return img, nil
+340 -1
View File
@@ -4,6 +4,7 @@
List of contributors: List of contributors:
Jeroen van Rijn: Initial implementation. Jeroen van Rijn: Initial implementation.
Benoit Jacquier: tga loader
*/ */
@@ -14,11 +15,16 @@ import "core:mem"
import "core:image" import "core:image"
import "core:bytes" import "core:bytes"
import "core:os" import "core:os"
import "core:compress"
import "core:strings"
// TODO: alpha_premultiply support
Error :: image.Error Error :: image.Error
Image :: image.Image Image :: image.Image
Options :: image.Options Options :: image.Options
GA_Pixel :: image.GA_Pixel
RGB_Pixel :: image.RGB_Pixel RGB_Pixel :: image.RGB_Pixel
RGBA_Pixel :: image.RGBA_Pixel RGBA_Pixel :: image.RGBA_Pixel
@@ -57,7 +63,7 @@ save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}
} }
header := image.TGA_Header{ header := image.TGA_Header{
data_type_code = 0x02, // Color, uncompressed. data_type_code = .Uncompressed_RGB,
dimensions = {u16le(img.width), u16le(img.height)}, dimensions = {u16le(img.width), u16le(img.height)},
bits_per_pixel = u8(img.depth * img.channels), bits_per_pixel = u8(img.depth * img.channels),
image_descriptor = 1 << 5, // Origin is top left. image_descriptor = 1 << 5, // Origin is top left.
@@ -99,3 +105,336 @@ save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocato
} }
save :: proc{save_to_memory, save_to_file} save :: proc{save_to_memory, save_to_file}
load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
context.allocator = allocator
options := options
if .alpha_premultiply in options {
return nil, .Unsupported_Option
}
if .info in options {
options |= {.return_metadata, .do_not_decompress_image}
options -= {.info}
}
if .return_header in options && .return_metadata in options {
options -= {.return_header}
}
// First check for a footer.
filesize := compress.input_size(ctx) or_return
footer: image.TGA_Footer
have_valid_footer := false
extension: image.TGA_Extension
have_valid_extension := false
if filesize >= size_of(image.TGA_Header) + size_of(image.TGA_Footer) {
if f, f_err := compress.peek_data(ctx, image.TGA_Footer, filesize - i64(size_of(image.TGA_Footer))); f_err == .None {
if string(f.signature[:]) == image.New_TGA_Signature {
have_valid_footer = true
footer = f
if i64(footer.extension_area_offset) + i64(size_of(image.TGA_Extension)) < filesize {
if e, e_err := compress.peek_data(ctx, image.TGA_Extension, footer.extension_area_offset); e_err == .None {
if e.extension_size == size_of(image.TGA_Extension) {
have_valid_extension = true
extension = e
}
}
}
}
}
}
header := image.read_data(ctx, image.TGA_Header) or_return
// Header checks
rle_encoding := false
color_mapped := false
black_white := false
src_channels := 0
dest_depth := header.bits_per_pixel
dest_channels := 0
#partial switch header.data_type_code {
// Supported formats: RGB(A), RGB(A) RLE
case .Compressed_RGB:
rle_encoding = true
case .Uncompressed_RGB:
// Intentionally blank
case .Uncompressed_Black_White:
black_white = true
dest_depth = 24
case .Uncompressed_Color_Mapped:
color_mapped = true
case .Compressed_Color_Mapped:
color_mapped = true
rle_encoding = true
case .Compressed_Black_White:
black_white = true
rle_encoding = true
dest_depth = 24
case:
return nil, .Unsupported_Format
}
if color_mapped {
if header.color_map_type != 1 {
return nil, .Unsupported_Format
}
dest_depth = header.color_map_depth
// Expect LUT entry index to be 8 bits
if header.bits_per_pixel != 8 || header.color_map_origin != 0 || header.color_map_length > 256 {
return nil, .Unsupported_Format
}
}
switch dest_depth {
case 15: // B5G5R5
src_channels = 2
dest_channels = 3
if color_mapped {
src_channels = 1
}
case 16: // B5G5R5A1
src_channels = 2
dest_channels = 3 // Alpha bit is dodgy in TGA, so we ignore it.
if color_mapped {
src_channels = 1
}
case 24: // RGB8
src_channels = 1 if (color_mapped || black_white) else 3
dest_channels = 3
case 32: // RGBA8
src_channels = 4 if !color_mapped else 1
dest_channels = 4
case:
return nil, .Unsupported_Format
}
if header.image_descriptor & IMAGE_DESCRIPTOR_INTERLEAVING_MASK != 0 {
return nil, .Unsupported_Format
}
if int(header.dimensions[0]) * int(header.dimensions[1]) > image.MAX_DIMENSIONS {
return nil, .Image_Dimensions_Too_Large
}
if img == nil {
img = new(Image)
}
defer if err != nil {
destroy(img)
}
img.which = .TGA
img.channels = 4 if .alpha_add_if_missing in options else dest_channels
img.channels = 3 if .alpha_drop_if_present in options else img.channels
img.depth = 8
img.width = int(header.dimensions[0])
img.height = int(header.dimensions[1])
// Read Image ID if present
image_id := ""
if _id, e := compress.read_slice(ctx, int(header.id_length)); e != .None {
return img, .Corrupt
} else {
if .return_metadata in options {
id := strings.trim_right_null(string(_id))
image_id = strings.clone(id)
}
}
color_map := make([]RGBA_Pixel, header.color_map_length)
defer delete(color_map)
if color_mapped {
switch header.color_map_depth {
case 16:
for i in 0..<header.color_map_length {
if lut, lut_err := compress.read_data(ctx, GA_Pixel); lut_err != .None {
return img, .Corrupt
} else {
color_map[i].rg = lut
color_map[i].ba = 255
}
}
case 24:
for i in 0..<header.color_map_length {
if lut, lut_err := compress.read_data(ctx, RGB_Pixel); lut_err != .None {
return img, .Corrupt
} else {
color_map[i].rgb = lut
color_map[i].a = 255
}
}
case 32:
for i in 0..<header.color_map_length {
if lut, lut_err := compress.read_data(ctx, RGBA_Pixel); lut_err != .None {
return img, .Corrupt
} else {
color_map[i] = lut
}
}
}
}
if .return_metadata in options {
info := new(image.TGA_Info)
info.header = header
info.image_id = image_id
if have_valid_footer {
info.footer = footer
}
if have_valid_extension {
info.extension = extension
}
img.metadata = info
}
if .do_not_decompress_image in options {
return img, nil
}
if !resize(&img.pixels.buf, dest_channels * img.width * img.height) {
return img, .Unable_To_Allocate_Or_Resize
}
origin_is_top := header.image_descriptor & IMAGE_DESCRIPTOR_TOP_MASK != 0
origin_is_left := header.image_descriptor & IMAGE_DESCRIPTOR_RIGHT_MASK == 0
rle_repetition_count := 0
read_pixel := true
is_packet_rle := false
pixel: RGBA_Pixel
stride := img.width * dest_channels
line := 0 if origin_is_top else img.height - 1
for _ in 0..<img.height {
offset := line * stride + (0 if origin_is_left else (stride - dest_channels))
for _ in 0..<img.width {
// handle RLE decoding
if rle_encoding {
if rle_repetition_count == 0 {
rle_cmd, err := compress.read_u8(ctx)
if err != .None {
return img, .Corrupt
}
is_packet_rle = (rle_cmd >> 7) != 0
rle_repetition_count = 1 + int(rle_cmd & 0x7F)
read_pixel = true
} else if !is_packet_rle {
read_pixel = rle_repetition_count > 0
} else {
read_pixel = false
}
}
// Read pixel
if read_pixel {
src, src_err := compress.read_slice(ctx, src_channels)
if src_err != .None {
return img, .Corrupt
}
switch src_channels {
case 1:
// Color-mapped or Black & White
if black_white {
pixel = {src[0], src[0], src[0], 255}
} else if header.color_map_depth == 24 {
pixel = color_map[src[0]].bgra
} else if header.color_map_depth == 16 {
lut := color_map[src[0]]
v := u16(lut.r) | u16(lut.g) << 8
b := u8( v & 31) << 3
g := u8((v >> 5) & 31) << 3
r := u8((v >> 10) & 31) << 3
pixel = {r, g, b, 255}
}
case 2:
v := u16(src[0]) | u16(src[1]) << 8
b := u8( v & 31) << 3
g := u8((v >> 5) & 31) << 3
r := u8((v >> 10) & 31) << 3
pixel = {r, g, b, 255}
case 3:
pixel = {src[2], src[1], src[0], 255}
case 4:
pixel = {src[2], src[1], src[0], src[3]}
case:
return img, .Corrupt
}
}
// Write pixel
copy(img.pixels.buf[offset:], pixel[:dest_channels])
offset += dest_channels if origin_is_left else -dest_channels
rle_repetition_count -= 1
}
line += 1 if origin_is_top else -1
}
return img, nil
}
load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
ctx := &compress.Context_Memory_Input{
input_data = data,
}
img, err = load_from_context(ctx, options, allocator)
return img, err
}
load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
context.allocator = allocator
data, ok := os.read_entire_file(filename)
defer delete(data)
if ok {
return load_from_bytes(data, options)
} else {
return nil, .Unable_To_Read_File
}
}
load :: proc{load_from_file, load_from_bytes, load_from_context}
destroy :: proc(img: ^Image) {
if img == nil || img.width == 0 || img.height == 0 {
return
}
bytes.buffer_destroy(&img.pixels)
if v, ok := img.metadata.(^image.TGA_Info); ok {
delete(v.image_id)
free(v)
}
// Make destroy idempotent
img.width = 0
img.height = 0
free(img)
}
IMAGE_DESCRIPTOR_INTERLEAVING_MASK :: (1<<6) | (1<<7)
IMAGE_DESCRIPTOR_RIGHT_MASK :: 1<<4
IMAGE_DESCRIPTOR_TOP_MASK :: 1<<5
@(init, private)
_register :: proc() {
image.register(.TGA, load_from_bytes, destroy)
}
+4 -2
View File
@@ -194,8 +194,7 @@ constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
simd_add :: proc(a, b: #simd[N]T) -> #simd[N]T --- simd_add :: proc(a, b: #simd[N]T) -> #simd[N]T ---
simd_sub :: proc(a, b: #simd[N]T) -> #simd[N]T --- simd_sub :: proc(a, b: #simd[N]T) -> #simd[N]T ---
simd_mul :: proc(a, b: #simd[N]T) -> #simd[N]T --- simd_mul :: proc(a, b: #simd[N]T) -> #simd[N]T ---
simd_div :: proc(a, b: #simd[N]T) -> #simd[N]T --- simd_div :: proc(a, b: #simd[N]T) -> #simd[N]T where type_is_float(T) ---
simd_rem :: proc(a, b: #simd[N]T) -> #simd[N]T ---
// Keeps Odin's Behaviour // Keeps Odin's Behaviour
// (x << y) if y <= mask else 0 // (x << y) if y <= mask else 0
@@ -296,6 +295,9 @@ objc_register_selector :: proc($name: string) -> objc_SEL ---
objc_find_class :: proc($name: string) -> objc_Class --- objc_find_class :: proc($name: string) -> objc_Class ---
objc_register_class :: proc($name: string) -> objc_Class --- objc_register_class :: proc($name: string) -> objc_Class ---
valgrind_client_request :: proc(default: uintptr, request: uintptr, a0, a1, a2, a3, a4: uintptr) -> uintptr ---
// Internal compiler use only // Internal compiler use only
__entry_point :: proc() --- __entry_point :: proc() ---
+24
View File
@@ -247,6 +247,30 @@ write_quoted_string :: proc(w: Writer, str: string, quote: byte = '"', n_written
return return
} }
// writer append a quoted rune into the byte buffer, return the written size
write_quoted_rune :: proc(w: Writer, r: rune) -> (n: int) {
_write_byte :: #force_inline proc(w: Writer, c: byte) -> int {
err := write_byte(w, c)
return 1 if err == nil else 0
}
quote := byte('\'')
n += _write_byte(w, quote)
buf, width := utf8.encode_rune(r)
if width == 1 && r == utf8.RUNE_ERROR {
n += _write_byte(w, '\\')
n += _write_byte(w, 'x')
n += _write_byte(w, DIGITS_LOWER[buf[0]>>4])
n += _write_byte(w, DIGITS_LOWER[buf[0]&0xf])
} else {
i, _ := write_escaped_rune(w, r, quote)
n += i
}
n += _write_byte(w, quote)
return
}
Tee_Reader :: struct { Tee_Reader :: struct {
+2 -2
View File
@@ -56,7 +56,7 @@ create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logg
return Logger{file_console_logger_proc, data, lowest, opt} return Logger{file_console_logger_proc, data, lowest, opt}
} }
destroy_console_logger :: proc(log: ^Logger) { destroy_console_logger :: proc(log: Logger) {
free(log.data) free(log.data)
} }
@@ -95,7 +95,7 @@ file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string
fmt.sbprintf(&buf, "[%s] ", data.ident) fmt.sbprintf(&buf, "[%s] ", data.ident)
} }
//TODO(Hoej): When we have better atomics and such, make this thread-safe //TODO(Hoej): When we have better atomics and such, make this thread-safe
fmt.fprintf(h, "%s %s\n", strings.to_string(buf), text) fmt.fprintf(h, "%s%s\n", strings.to_string(buf), text)
} }
do_level_header :: proc(opts: Options, level: Level, str: ^strings.Builder) { do_level_header :: proc(opts: Options, level: Level, str: ^strings.Builder) {
+5 -6
View File
@@ -6,7 +6,6 @@ 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. // 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
/* /*
Logger_Level :: enum { Logger_Level :: enum {
Debug = 0, Debug = 0,
@@ -16,8 +15,8 @@ Logger_Level :: enum {
Fatal = 40, Fatal = 40,
} }
*/ */
Level :: runtime.Logger_Level
Option :: runtime.Logger_Option
/* /*
Option :: enum { Option :: enum {
Level, Level,
@@ -30,11 +29,12 @@ Option :: enum {
Terminal_Color Terminal_Color
} }
*/ */
Option :: runtime.Logger_Option
Options :: runtime.Logger_Options
/* /*
Options :: bit_set[Option]; Options :: bit_set[Option];
*/ */
Options :: runtime.Logger_Options
Full_Timestamp_Opts :: Options{ Full_Timestamp_Opts :: Options{
.Date, .Date,
@@ -52,12 +52,11 @@ Location_File_Opts :: Options{
} }
Logger_Proc :: runtime.Logger_Proc
/* /*
Logger_Proc :: #type proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location); Logger_Proc :: #type proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location);
*/ */
Logger_Proc :: runtime.Logger_Proc
Logger :: runtime.Logger
/* /*
Logger :: struct { Logger :: struct {
procedure: Logger_Proc, procedure: Logger_Proc,
@@ -66,6 +65,7 @@ Logger :: struct {
options: Logger_Options, options: Logger_Options,
} }
*/ */
Logger :: runtime.Logger
nil_logger_proc :: proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location) { nil_logger_proc :: proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
// Do nothing // Do nothing
@@ -75,7 +75,6 @@ nil_logger :: proc() -> Logger {
return Logger{nil_logger_proc, nil, Level.Debug, nil} return Logger{nil_logger_proc, nil, Level.Debug, nil}
} }
// TODO(bill): Should these be redesigned so that they are do not rely upon `package fmt`?
debugf :: proc(fmt_str: string, args: ..any, location := #caller_location) { debugf :: proc(fmt_str: string, args: ..any, location := #caller_location) {
logf(level=.Debug, fmt_str=fmt_str, args=args, location=location) logf(level=.Debug, fmt_str=fmt_str, args=args, location=location)
} }
+107
View File
@@ -0,0 +1,107 @@
package log
import "core:runtime"
Log_Allocator :: struct {
allocator: runtime.Allocator,
level: Level,
prefix: string,
locked: bool,
}
log_allocator_init :: proc(la: ^Log_Allocator, level: Level, allocator := context.allocator, prefix := "") {
la.allocator = allocator
la.level = level
la.prefix = prefix
la.locked = false
}
log_allocator :: proc(la: ^Log_Allocator) -> runtime.Allocator {
return runtime.Allocator{
procedure = log_allocator_proc,
data = la,
}
}
log_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, runtime.Allocator_Error) {
la := (^Log_Allocator)(allocator_data)
padding := " " if la.prefix != "" else ""
if !la.locked {
la.locked = true
defer la.locked = false
switch mode {
case .Alloc:
logf(
level=la.level,
fmt_str = "%s%s>>> ALLOCATOR(mode=.Alloc, size=%d, alignment=%d)",
args = {la.prefix, padding, size, alignment},
location = location,
)
case .Free:
if old_size != 0 {
logf(
level=la.level,
fmt_str = "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p, size=%d)",
args = {la.prefix, padding, old_memory, old_size},
location = location,
)
} else {
logf(
level=la.level,
fmt_str = "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p)",
args = {la.prefix, padding, old_memory},
location = location,
)
}
case .Free_All:
logf(
level=la.level,
fmt_str = "%s%s<<< ALLOCATOR(mode=.Free_All)",
args = {la.prefix, padding},
location = location,
)
case .Resize:
logf(
level=la.level,
fmt_str = "%s%s>>> ALLOCATOR(mode=.Resize, ptr=%p, old_size=%d, size=%d, alignment=%d)",
args = {la.prefix, padding, old_memory, old_size, size, alignment},
location = location,
)
case .Query_Features:
logf(
level=la.level,
fmt_str = "%s%ALLOCATOR(mode=.Query_Features)",
args = {la.prefix, padding},
location = location,
)
case .Query_Info:
logf(
level=la.level,
fmt_str = "%s%ALLOCATOR(mode=.Query_Info)",
args = {la.prefix, padding},
location = location,
)
}
}
data, err := la.allocator.procedure(la.allocator.data, mode, size, alignment, old_memory, old_size, location)
if !la.locked {
la.locked = true
defer la.locked = false
if err != nil {
logf(
level=la.level,
fmt_str = "%s%ALLOCATOR ERROR=%v",
args = {la.prefix, padding, error},
location = location,
)
}
}
return data, err
}
+1 -1
View File
@@ -449,7 +449,7 @@ internal_int_is_prime :: proc(a: ^Int, miller_rabin_trials := int(-1), miller_ra
in the loop is non-zero, although very low. in the loop is non-zero, although very low.
-- NOTE(Jeroen): This is not yet true in Odin, but I have some ideas. -- NOTE(Jeroen): This is not yet true in Odin, but I have some ideas.
If the BPSW test and/or the addtional Frobenious test have been If the BPSW test and/or the additional Frobenious test have been
performed instead of just the Miller-Rabin test with the bases 2 and 3, performed instead of just the Miller-Rabin test with the bases 2 and 3,
a single extra test should suffice, so such a very unlikely event will not do much harm. a single extra test should suffice, so such a very unlikely event will not do much harm.
+23 -11
View File
@@ -325,9 +325,9 @@ ease :: proc "contextless" (type: Ease, p: $T) -> T
// in case type was invalid // in case type was invalid
return 0 return 0
} }
Flux_Map :: struct($T: typeid) { Flux_Map :: struct($T: typeid) {
values: map[^T]Flux_Tween(T), values: map[^T]Flux_Tween(T),
keys_to_be_deleted: [dynamic]^T,
} }
Flux_Tween :: struct($T: typeid) { Flux_Tween :: struct($T: typeid) {
@@ -353,15 +353,17 @@ Flux_Tween :: struct($T: typeid) {
} }
// init flux map to a float type and a wanted cap // init flux map to a float type and a wanted cap
flux_init :: proc($T: typeid, cap := 8) -> Flux_Map(T) where intrinsics.type_is_float(T) { flux_init :: proc($T: typeid, value_capacity := 8) -> Flux_Map(T) where intrinsics.type_is_float(T) {
return { return {
make(map[^T]Flux_Tween(T), cap), values = make(map[^T]Flux_Tween(T), value_capacity),
keys_to_be_deleted = make([dynamic]^T, 0, value_capacity),
} }
} }
// delete map content // delete map content
flux_destroy :: proc(flux: Flux_Map($T)) where intrinsics.type_is_float(T) { flux_destroy :: proc(flux: Flux_Map($T)) where intrinsics.type_is_float(T) {
delete(flux.values) delete(flux.values)
delete(flux.keys_to_be_deleted)
} }
// clear map content, stops all animations // clear map content, stops all animations
@@ -374,8 +376,8 @@ flux_clear :: proc(flux: ^Flux_Map($T)) where intrinsics.type_is_float(T) {
// return value can be used to set callbacks // return value can be used to set callbacks
flux_to :: proc( flux_to :: proc(
flux: ^Flux_Map($T), flux: ^Flux_Map($T),
value: ^f32, value: ^T,
goal: f32, goal: T,
type: Ease = .Quadratic_Out, type: Ease = .Quadratic_Out,
duration: time.Duration = time.Second, duration: time.Duration = time.Second,
delay: f64 = 0, delay: f64 = 0,
@@ -413,6 +415,8 @@ flux_tween_init :: proc(tween: ^Flux_Tween($T), duration: time.Duration) where i
// calls callbacks in all stages, when they're filled // calls callbacks in all stages, when they're filled
// deletes tween from the map after completion // deletes tween from the map after completion
flux_update :: proc(flux: ^Flux_Map($T), dt: f64) where intrinsics.type_is_float(T) { flux_update :: proc(flux: ^Flux_Map($T), dt: f64) where intrinsics.type_is_float(T) {
clear(&flux.keys_to_be_deleted)
for key, tween in &flux.values { for key, tween in &flux.values {
delay_remainder := f64(0) delay_remainder := f64(0)
@@ -451,7 +455,8 @@ flux_update :: proc(flux: ^Flux_Map($T), dt: f64) where intrinsics.type_is_float
} }
if tween.progress >= 1 { if tween.progress >= 1 {
delete_key(&flux.values, key) // append keys to array that will be deleted after the loop
append(&flux.keys_to_be_deleted, key)
if tween.on_complete != nil { if tween.on_complete != nil {
tween.on_complete(flux, tween.data) tween.on_complete(flux, tween.data)
@@ -459,17 +464,24 @@ flux_update :: proc(flux: ^Flux_Map($T), dt: f64) where intrinsics.type_is_float
} }
} }
} }
// loop through keys that should be deleted from the map
if len(flux.keys_to_be_deleted) != 0 {
for key in flux.keys_to_be_deleted {
delete_key(&flux.values, key)
}
}
} }
// stop a specific key inside the map // stop a specific key inside the map
// returns true when it successfully removed the key // returns true when it successfully removed the key
flux_stop :: proc(flux: ^Flux_Map($T), key: ^T) -> bool where intrinsics.type_is_float(T) { flux_stop :: proc(flux: ^Flux_Map($T), key: ^T) -> bool where intrinsics.type_is_float(T) {
if key in flux.values { if key in flux.values {
delete_key(&flux.values, key) delete_key(&flux.values, key)
return true return true
} }
return false return false
} }
// returns the amount of time left for the tween animation, if the key exists in the map // returns the amount of time left for the tween animation, if the key exists in the map
+5 -5
View File
@@ -426,14 +426,14 @@ distance :: proc(p0, p1: $V/[$N]$E) -> E where IS_NUMERIC(E) {
} }
reflect :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) { reflect :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) {
b := n * (2 * dot(n, i)) b := N * (2 * dot(N, I))
return i - b return I - b
} }
refract :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) { refract :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) {
dv := dot(n, i) dv := dot(N, I)
k := 1 - eta*eta - (1 - dv*dv) k := 1 - eta*eta - (1 - dv*dv)
a := i * eta a := I * eta
b := n * eta*dv*math.sqrt(k) b := N * eta*dv*math.sqrt(k)
return (a - b) * E(int(k >= 0)) return (a - b) * E(int(k >= 0))
} }
+3 -3
View File
@@ -94,7 +94,7 @@ quaternion_cross :: proc(q1, q2: $Q) -> (q3: Q) where IS_QUATERNION(Q) {
vector_cross :: proc{scalar_cross, vector_cross2, vector_cross3} vector_cross :: proc{scalar_cross, vector_cross2, vector_cross3}
cross :: proc{scalar_cross, vector_cross2, vector_cross3, quaternion_cross} cross :: proc{scalar_cross, vector_cross2, vector_cross3, quaternion_cross}
vector_normalize :: proc(v: $T/[$N]$E) -> T where IS_NUMERIC(E) { vector_normalize :: proc(v: $T/[$N]$E) -> T where IS_FLOAT(E) {
return v / length(v) return v / length(v)
} }
quaternion_normalize :: proc(q: $Q) -> Q where IS_QUATERNION(Q) { quaternion_normalize :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
@@ -102,7 +102,7 @@ quaternion_normalize :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
} }
normalize :: proc{vector_normalize, quaternion_normalize} normalize :: proc{vector_normalize, quaternion_normalize}
vector_normalize0 :: proc(v: $T/[$N]$E) -> T where IS_NUMERIC(E) { vector_normalize0 :: proc(v: $T/[$N]$E) -> T where IS_FLOAT(E) {
m := length(v) m := length(v)
return 0 if m == 0 else v/m return 0 if m == 0 else v/m
} }
@@ -113,7 +113,7 @@ quaternion_normalize0 :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
normalize0 :: proc{vector_normalize0, quaternion_normalize0} normalize0 :: proc{vector_normalize0, quaternion_normalize0}
vector_length :: proc(v: $T/[$N]$E) -> E where IS_NUMERIC(E) { vector_length :: proc(v: $T/[$N]$E) -> E where IS_FLOAT(E) {
return math.sqrt(dot(v, v)) return math.sqrt(dot(v, v))
} }
+3 -3
View File
@@ -476,21 +476,21 @@ quaternion_angle_axis :: proc{
angle_from_quaternion_f16 :: proc(q: Quaternionf16) -> f16 { angle_from_quaternion_f16 :: proc(q: Quaternionf16) -> f16 {
if abs(q.w) > math.SQRT_THREE*0.5 { if abs(q.w) > math.SQRT_THREE*0.5 {
return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2 return math.asin(math.sqrt(q.x*q.x + q.y*q.y + q.z*q.z)) * 2
} }
return math.acos(q.w) * 2 return math.acos(q.w) * 2
} }
angle_from_quaternion_f32 :: proc(q: Quaternionf32) -> f32 { angle_from_quaternion_f32 :: proc(q: Quaternionf32) -> f32 {
if abs(q.w) > math.SQRT_THREE*0.5 { if abs(q.w) > math.SQRT_THREE*0.5 {
return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2 return math.asin(math.sqrt(q.x*q.x + q.y*q.y + q.z*q.z)) * 2
} }
return math.acos(q.w) * 2 return math.acos(q.w) * 2
} }
angle_from_quaternion_f64 :: proc(q: Quaternionf64) -> f64 { angle_from_quaternion_f64 :: proc(q: Quaternionf64) -> f64 {
if abs(q.w) > math.SQRT_THREE*0.5 { if abs(q.w) > math.SQRT_THREE*0.5 {
return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2 return math.asin(math.sqrt(q.x*q.x + q.y*q.y + q.z*q.z)) * 2
} }
return math.acos(q.w) * 2 return math.acos(q.w) * 2
+37 -11
View File
@@ -185,16 +185,23 @@ log :: proc{
log_f64, log_f64le, log_f64be, log_f64, log_f64le, log_f64be,
} }
log2_f16 :: logb_f16 log2_f16 :: proc "contextless" (x: f16) -> f16 { return log(f16(x), f16(2.0)) }
log2_f16le :: logb_f16le log2_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log_f16(f16(x), f16(2.0))) }
log2_f16be :: logb_f16be log2_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(log_f16(f16(x), f16(2.0))) }
log2_f32 :: logb_f32
log2_f32le :: logb_f32le log2_f32 :: proc "contextless" (x: f32) -> f32 { return log(f32(x), f32(2.0)) }
log2_f32be :: logb_f32be log2_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(log_f32(f32(x), f32(2.0))) }
log2_f64 :: logb_f64 log2_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(log_f32(f32(x), f32(2.0))) }
log2_f64le :: logb_f64le
log2_f64be :: logb_f64be log2_f64 :: proc "contextless" (x: f64) -> f64 { return log(f64(x), f64(2.0)) }
log2 :: logb log2_f64le :: proc "contextless" (x: f64le) -> f64le { return f64le(log_f64(f64(x), f64(2.0))) }
log2_f64be :: proc "contextless" (x: f64be) -> f64be { return f64be(log_f64(f64(x), f64(2.0))) }
log2 :: proc{
log2_f16, log2_f16le, log2_f16be,
log2_f32, log2_f32le, log2_f32be,
log2_f64, log2_f64le, log2_f64be,
}
log10_f16 :: proc "contextless" (x: f16) -> f16 { return ln(x)/LN10 } log10_f16 :: proc "contextless" (x: f16) -> f16 { return ln(x)/LN10 }
log10_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log10_f16(f16(x))) } log10_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log10_f16(f16(x))) }
@@ -607,6 +614,25 @@ floor_mod :: proc "contextless" (x, y: $T) -> T
return r return r
} }
divmod :: #force_inline proc "contextless" (x, y: $T) -> (div, mod: T)
where intrinsics.type_is_integer(T) {
div = x / y
mod = x % y
return
}
floor_divmod :: #force_inline proc "contextless" (x, y: $T) -> (div, mod: T)
where intrinsics.type_is_integer(T) {
div = x / y
mod = x % y
if (div > 0 && y < 0) || (mod < 0 && y > 0) {
div -= 1
mod += y
}
return
}
modf_f16 :: proc "contextless" (x: f16) -> (int: f16, frac: f16) { modf_f16 :: proc "contextless" (x: f16) -> (int: f16, frac: f16) {
shift :: F16_SHIFT shift :: F16_SHIFT
mask :: F16_MASK mask :: F16_MASK
@@ -1357,7 +1383,7 @@ atan :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
} }
asin :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) { asin :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
return atan2(x, 1 + sqrt(1 - x*x)) return atan2(x, sqrt(1 - x*x))
} }
acos :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) { acos :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
+9 -85
View File
@@ -61,114 +61,38 @@ DEFAULT_PAGE_SIZE ::
4 * 1024 4 * 1024
alloc :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr { alloc :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
if size == 0 { data, _ := runtime.mem_alloc(size, alignment, allocator, loc)
return nil
}
if allocator.procedure == nil {
return nil
}
data, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc)
_ = err
return raw_data(data) return raw_data(data)
} }
alloc_bytes :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) { alloc_bytes :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
if size == 0 { return runtime.mem_alloc(size, alignment, allocator, loc)
return nil, nil
}
if allocator.procedure == nil {
return nil, nil
}
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc)
} }
free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
if ptr == nil { return runtime.mem_free(ptr, allocator, loc)
return nil
}
if allocator.procedure == nil {
return nil
}
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, loc)
return err
} }
free_bytes :: proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { free_bytes :: proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
if bytes == nil { return runtime.mem_free_bytes(bytes, allocator, loc)
return nil
}
if allocator.procedure == nil {
return nil
}
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, raw_data(bytes), len(bytes), loc)
return err
} }
free_all :: proc(allocator := context.allocator, loc := #caller_location) -> Allocator_Error { free_all :: proc(allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
if allocator.procedure != nil { return runtime.mem_free_all(allocator, loc)
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free_All, 0, 0, nil, 0, loc)
return err
}
return nil
} }
resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr { resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
if allocator.procedure == nil { data, _ := runtime.mem_resize(ptr, old_size, new_size, alignment, allocator, loc)
return nil
}
if new_size == 0 {
if ptr != nil {
allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc)
}
return nil
} else if ptr == nil {
_, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc)
_ = err
return nil
}
data, err := allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc)
if err == .Mode_Not_Implemented {
data, err = allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc)
if err != nil {
return nil
}
runtime.copy(data, byte_slice(ptr, old_size))
_, err = allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc)
return raw_data(data)
}
return raw_data(data) return raw_data(data)
} }
resize_bytes :: proc(old_data: []byte, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) { resize_bytes :: proc(old_data: []byte, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
if allocator.procedure == nil { return runtime.mem_resize(raw_data(old_data), len(old_data), new_size, alignment, allocator, loc)
return nil, nil
}
ptr := raw_data(old_data)
old_size := len(old_data)
if new_size == 0 {
if ptr != nil {
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc)
return nil, err
}
return nil, nil
} else if ptr == nil {
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc)
}
data, err := allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc)
if err == .Mode_Not_Implemented {
data, err = allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc)
if err != nil {
return data, err
}
runtime.copy(data, old_data)
_, err = allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc)
}
return data, err
} }
query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: Allocator_Mode_Set) { query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: Allocator_Mode_Set) {
if allocator.procedure != nil { if allocator.procedure != nil {
allocator.procedure(allocator.data, Allocator_Mode.Query_Features, 0, 0, &set, 0, loc) allocator.procedure(allocator.data, .Query_Features, 0, 0, &set, 0, loc)
return set return set
} }
return nil return nil
@@ -177,7 +101,7 @@ query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: A
query_info :: proc(pointer: rawptr, allocator: Allocator, loc := #caller_location) -> (props: Allocator_Query_Info) { query_info :: proc(pointer: rawptr, allocator: Allocator, loc := #caller_location) -> (props: Allocator_Query_Info) {
props.pointer = pointer props.pointer = pointer
if allocator.procedure != nil { if allocator.procedure != nil {
allocator.procedure(allocator.data, Allocator_Mode.Query_Info, 0, 0, &props, 0, loc) allocator.procedure(allocator.data, .Query_Info, 0, 0, &props, 0, loc)
} }
return return
} }
+38 -15
View File
@@ -31,6 +31,14 @@ Arena_Temp_Memory :: struct {
} }
arena_init :: proc(a: ^Arena, data: []byte) {
a.data = data
a.offset = 0
a.peak_used = 0
a.temp_count = 0
}
@(deprecated="prefer 'mem.arena_init'")
init_arena :: proc(a: ^Arena, data: []byte) { init_arena :: proc(a: ^Arena, data: []byte) {
a.data = data a.data = data
a.offset = 0 a.offset = 0
@@ -52,15 +60,16 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
switch mode { switch mode {
case .Alloc: case .Alloc:
total_size := size + alignment #no_bounds_check end := &arena.data[arena.offset]
ptr := align_forward(end, uintptr(alignment))
total_size := size + ptr_sub((^byte)(ptr), (^byte)(end))
if arena.offset + total_size > len(arena.data) { if arena.offset + total_size > len(arena.data) {
return nil, .Out_Of_Memory return nil, .Out_Of_Memory
} }
#no_bounds_check end := &arena.data[arena.offset]
ptr := align_forward(end, uintptr(alignment))
arena.offset += total_size arena.offset += total_size
arena.peak_used = max(arena.peak_used, arena.offset) arena.peak_used = max(arena.peak_used, arena.offset)
zero(ptr, size) zero(ptr, size)
@@ -292,6 +301,14 @@ Stack :: struct {
peak_used: int, peak_used: int,
} }
stack_init :: proc(s: ^Stack, data: []byte) {
s.data = data
s.prev_offset = 0
s.curr_offset = 0
s.peak_used = 0
}
@(deprecated="prefer 'mem.stack_init'")
init_stack :: proc(s: ^Stack, data: []byte) { init_stack :: proc(s: ^Stack, data: []byte) {
s.data = data s.data = data
s.prev_offset = 0 s.prev_offset = 0
@@ -444,27 +461,34 @@ Small_Stack_Allocation_Header :: struct {
// Small_Stack is a stack-like allocator which uses the smallest possible header but at the cost of non-strict memory freeing order // Small_Stack is a stack-like allocator which uses the smallest possible header but at the cost of non-strict memory freeing order
Small_Stack :: struct { Small_Stack :: struct {
data: []byte, data: []byte,
offset: int, offset: int,
peak_used: int, peak_used: int,
} }
small_stack_init :: proc(s: ^Small_Stack, data: []byte) {
s.data = data
s.offset = 0
s.peak_used = 0
}
@(deprecated="prefer 'small_stack_init'")
init_small_stack :: proc(s: ^Small_Stack, data: []byte) { init_small_stack :: proc(s: ^Small_Stack, data: []byte) {
s.data = data s.data = data
s.offset = 0 s.offset = 0
s.peak_used = 0 s.peak_used = 0
} }
small_stack_allocator :: proc(stack: ^Small_Stack) -> Allocator { small_stack_allocator :: proc(stack: ^Small_Stack) -> Allocator {
return Allocator{ return Allocator{
procedure = small_stack_allocator_proc, procedure = small_stack_allocator_proc,
data = stack, data = stack,
} }
} }
small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int, size, alignment: int,
old_memory: rawptr, old_size: int, ocation := #caller_location) -> ([]byte, Allocator_Error) { old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, Allocator_Error) {
s := cast(^Small_Stack)allocator_data s := cast(^Small_Stack)allocator_data
if s.data == nil { if s.data == nil {
@@ -886,6 +910,10 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
} }
case .Free: case .Free:
delete_key(&data.allocation_map, old_memory) delete_key(&data.allocation_map, old_memory)
case .Free_All:
if data.clear_on_free_all {
clear_map(&data.allocation_map)
}
case .Resize: case .Resize:
if old_memory != result_ptr { if old_memory != result_ptr {
delete_key(&data.allocation_map, old_memory) delete_key(&data.allocation_map, old_memory)
@@ -898,11 +926,6 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
location = loc, location = loc,
} }
case .Free_All:
if data.clear_on_free_all {
clear_map(&data.allocation_map)
}
case .Query_Features: case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory) set := (^Allocator_Mode_Set)(old_memory)
if set != nil { if set != nil {
+8 -43
View File
@@ -54,48 +54,7 @@ compare :: proc "contextless" (a, b: []byte) -> int {
} }
compare_byte_ptrs :: proc "contextless" (a, b: ^byte, n: int) -> int #no_bounds_check { compare_byte_ptrs :: proc "contextless" (a, b: ^byte, n: int) -> int #no_bounds_check {
switch { return runtime.memory_compare(a, b, n)
case a == b:
return 0
case a == nil:
return -1
case b == nil:
return -1
case n == 0:
return 0
}
x := slice_ptr(a, n)
y := slice_ptr(b, n)
SU :: size_of(uintptr)
fast := n/SU + 1
offset := (fast-1)*SU
curr_block := 0
if n < SU {
fast = 0
}
la := slice_ptr((^uintptr)(a), fast)
lb := slice_ptr((^uintptr)(b), fast)
for /**/; curr_block < fast; curr_block += 1 {
if la[curr_block] ~ lb[curr_block] != 0 {
for pos := curr_block*SU; pos < n; pos += 1 {
if x[pos] ~ y[pos] != 0 {
return (int(x[pos]) - int(y[pos])) < 0 ? -1 : +1
}
}
}
}
for /**/; offset < n; offset += 1 {
if x[offset] ~ y[offset] != 0 {
return (int(x[offset]) - int(y[offset])) < 0 ? -1 : +1
}
}
return 0
} }
check_zero :: proc(data: []byte) -> bool { check_zero :: proc(data: []byte) -> bool {
@@ -109,6 +68,12 @@ check_zero_ptr :: proc(ptr: rawptr, len: int) -> bool {
case ptr == nil: case ptr == nil:
return true return true
} }
switch len {
case 1: return (^u8)(ptr)^ == 0
case 2: return intrinsics.unaligned_load((^u16)(ptr)) == 0
case 4: return intrinsics.unaligned_load((^u32)(ptr)) == 0
case 8: return intrinsics.unaligned_load((^u64)(ptr)) == 0
}
start := uintptr(ptr) start := uintptr(ptr)
start_aligned := align_forward_uintptr(start, align_of(uintptr)) start_aligned := align_forward_uintptr(start, align_of(uintptr))
@@ -152,7 +117,7 @@ slice_ptr :: proc "contextless" (ptr: ^$T, len: int) -> []T {
return ([^]T)(ptr)[:len] return ([^]T)(ptr)[:len]
} }
byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte { byte_slice :: #force_inline proc "contextless" (data: rawptr, #any_int len: int) -> []byte {
return ([^]u8)(data)[:max(len, 0)] return ([^]u8)(data)[:max(len, 0)]
} }
+1
View File
@@ -8,6 +8,7 @@ Raw_Cstring :: runtime.Raw_Cstring
Raw_Slice :: runtime.Raw_Slice Raw_Slice :: runtime.Raw_Slice
Raw_Dynamic_Array :: runtime.Raw_Dynamic_Array Raw_Dynamic_Array :: runtime.Raw_Dynamic_Array
Raw_Map :: runtime.Raw_Map Raw_Map :: runtime.Raw_Map
Raw_Soa_Pointer :: runtime.Raw_Soa_Pointer
Raw_Complex64 :: struct {real, imag: f32} Raw_Complex64 :: struct {real, imag: f32}
Raw_Complex128 :: struct {real, imag: f64} Raw_Complex128 :: struct {real, imag: f64}
+5
View File
@@ -1,5 +1,10 @@
package mem_virtual package mem_virtual
arena_init :: proc{
static_arena_init,
growing_arena_init,
}
arena_temp_begin :: proc{ arena_temp_begin :: proc{
static_arena_temp_begin, static_arena_temp_begin,
growing_arena_temp_begin, growing_arena_temp_begin,
+11 -4
View File
@@ -13,6 +13,13 @@ Growing_Arena :: struct {
DEFAULT_MINIMUM_BLOCK_SIZE :: 1<<20 // 1 MiB should be enough DEFAULT_MINIMUM_BLOCK_SIZE :: 1<<20 // 1 MiB should be enough
growing_arena_init :: proc(arena: ^Growing_Arena, reserved: uint = DEFAULT_MINIMUM_BLOCK_SIZE) -> (err: Allocator_Error) {
arena.curr_block = memory_block_alloc(0, reserved, {}) or_return
arena.total_used = 0
arena.total_reserved = arena.curr_block.reserved
return
}
growing_arena_alloc :: proc(arena: ^Growing_Arena, min_size: int, alignment: int) -> (data: []byte, err: Allocator_Error) { growing_arena_alloc :: proc(arena: ^Growing_Arena, min_size: int, alignment: int) -> (data: []byte, err: Allocator_Error) {
align_forward_offset :: proc "contextless" (arena: ^Growing_Arena, alignment: int) -> uint #no_bounds_check { align_forward_offset :: proc "contextless" (arena: ^Growing_Arena, alignment: int) -> uint #no_bounds_check {
alignment_offset := uint(0) alignment_offset := uint(0)
@@ -37,7 +44,7 @@ growing_arena_alloc :: proc(arena: ^Growing_Arena, min_size: int, alignment: int
block_size := max(size, arena.minimum_block_size) block_size := max(size, arena.minimum_block_size)
new_block := memory_block_alloc(block_size, block_size, {}) or_return new_block := memory_block_alloc(size, block_size, {}) or_return
new_block.prev = arena.curr_block new_block.prev = arena.curr_block
arena.curr_block = new_block arena.curr_block = new_block
arena.total_reserved += new_block.reserved arena.total_reserved += new_block.reserved
@@ -95,9 +102,9 @@ growing_arena_allocator :: proc(arena: ^Growing_Arena) -> mem.Allocator {
} }
growing_arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, growing_arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
size, alignment: int, size, alignment: int,
old_memory: rawptr, old_size: int, old_memory: rawptr, old_size: int,
location := #caller_location) -> (data: []byte, err: Allocator_Error) { location := #caller_location) -> (data: []byte, err: Allocator_Error) {
arena := (^Growing_Arena)(allocator_data) arena := (^Growing_Arena)(allocator_data)
switch mode { switch mode {
+148
View File
@@ -0,0 +1,148 @@
//+build darwin
//+private
package mem_virtual
foreign import libc "System.framework"
import "core:c"
PROT_NONE :: 0x0 /* [MC2] no permissions */
PROT_READ :: 0x1 /* [MC2] pages can be read */
PROT_WRITE :: 0x2 /* [MC2] pages can be written */
PROT_EXEC :: 0x4 /* [MC2] pages can be executed */
// Sharing options
MAP_SHARED :: 0x1 /* [MF|SHM] share changes */
MAP_PRIVATE :: 0x2 /* [MF|SHM] changes are private */
// Other flags
MAP_FIXED :: 0x0010 /* [MF|SHM] interpret addr exactly */
MAP_RENAME :: 0x0020 /* Sun: rename private pages to file */
MAP_NORESERVE :: 0x0040 /* Sun: don't reserve needed swap area */
MAP_RESERVED0080 :: 0x0080 /* previously unimplemented MAP_INHERIT */
MAP_NOEXTEND :: 0x0100 /* for MAP_FILE, don't change file size */
MAP_HASSEMAPHORE :: 0x0200 /* region may contain semaphores */
MAP_NOCACHE :: 0x0400 /* don't cache pages for this mapping */
MAP_JIT :: 0x0800 /* Allocate a region that will be used for JIT purposes */
// Mapping type
MAP_FILE :: 0x0000 /* map from file (default) */
MAP_ANONYMOUS :: 0x1000 /* allocated from memory, swap space */
/*
* The MAP_RESILIENT_* flags can be used when the caller wants to map some
* possibly unreliable memory and be able to access it safely, possibly
* getting the wrong contents rather than raising any exception.
* For safety reasons, such mappings have to be read-only (PROT_READ access
* only).
*
* MAP_RESILIENT_CODESIGN:
* accessing this mapping will not generate code-signing violations,
* even if the contents are tainted.
* MAP_RESILIENT_MEDIA:
* accessing this mapping will not generate an exception if the contents
* are not available (unreachable removable or remote media, access beyond
* end-of-file, ...). Missing contents will be replaced with zeroes.
*/
MAP_RESILIENT_CODESIGN :: 0x2000 /* no code-signing failures */
MAP_RESILIENT_MEDIA :: 0x4000 /* no backing-store failures */
MAP_32BIT :: 0x8000 /* Return virtual addresses <4G only */
// Flags used to support translated processes.
MAP_TRANSLATED_ALLOW_EXECUTE :: 0x20000 /* allow execute in translated processes */
MAP_UNIX03 :: 0x40000 /* UNIX03 compliance */
// Process memory locking
MCL_CURRENT :: 0x0001 /* [ML] Lock only current memory */
MCL_FUTURE :: 0x0002 /* [ML] Lock all future memory as well */
MADV_NORMAL :: 0 /* [MC1] no further special treatment */
MADV_RANDOM :: 1 /* [MC1] expect random page refs */
MADV_SEQUENTIAL :: 2 /* [MC1] expect sequential page refs */
MADV_WILLNEED :: 3 /* [MC1] will need these pages */
MADV_DONTNEED :: 4 /* [MC1] dont need these pages */
MADV_FREE :: 5 /* pages unneeded, discard contents */
MADV_ZERO_WIRED_PAGES :: 6 /* zero the wired pages that have not been unwired before the entry is deleted */
MADV_FREE_REUSABLE :: 7 /* pages can be reused (by anyone) */
MADV_FREE_REUSE :: 8 /* caller wants to reuse those pages */
MADV_CAN_REUSE :: 9
MADV_PAGEOUT :: 10 /* page out now (internal only) */
// msync() flags
MS_ASYNC :: 0x0001 /* [MF|SIO] return immediately */
MS_INVALIDATE :: 0x0002 /* [MF|SIO] invalidate all cached data */
MS_SYNC :: 0x0010 /* [MF|SIO] msync synchronously */
MS_KILLPAGES :: 0x0004 /* invalidate pages, leave mapped */
MS_DEACTIVATE :: 0x0008 /* deactivate pages, leave mapped */
// Return bits from mincore
MINCORE_INCORE :: 0x1 /* Page is incore */
MINCORE_REFERENCED :: 0x2 /* Page has been referenced by us */
MINCORE_MODIFIED :: 0x4 /* Page has been modified by us */
MINCORE_REFERENCED_OTHER :: 0x8 /* Page has been referenced */
MINCORE_MODIFIED_OTHER :: 0x10 /* Page has been modified */
MINCORE_PAGED_OUT :: 0x20 /* Page has been paged out */
MINCORE_COPIED :: 0x40 /* Page has been copied */
MINCORE_ANONYMOUS :: 0x80 /* Page belongs to an anonymous object */
// Allocation failure result
MAP_FAILED : rawptr = rawptr(~uintptr(0))
foreign libc {
@(link_name="mlockall") _mlockall :: proc(flags: c.int) -> c.int ---
@(link_name="munlockall") _munlockall :: proc() -> c.int ---
@(link_name="mlock") _mlock :: proc(addr: rawptr, len: c.size_t) -> c.int ---
@(link_name="mmap") _mmap :: proc(addr: rawptr, len: c.size_t, prot: c.int, flags: c.int, fd: c.int, offset: int) -> rawptr ---
@(link_name="mprotect") _mprotect :: proc(addr: rawptr, len: c.size_t, prot: c.int) -> c.int ---
@(link_name="msync") _msync :: proc(addr: rawptr, len: c.size_t) -> c.int ---
@(link_name="munlock") _munlock :: proc(addr: rawptr, len: c.size_t) -> c.int ---
@(link_name="munmap") _munmap :: proc(addr: rawptr, len: c.size_t) -> c.int ---
@(link_name="shm_open") _shm_open :: proc(name: cstring, oflag: c.int, #c_vararg args: ..any) -> c.int ---
@(link_name="shm_unlink") _shm_unlink :: proc(name: cstring) -> c.int ---
@(link_name="posix_madvise") _posix_madvise :: proc(addr: rawptr, len: c.size_t, advice: c.int) -> c.int ---
@(link_name="madvise") _madvise :: proc(addr: rawptr, len: c.size_t, advice: c.int) -> c.int ---
@(link_name="mincore") _mincore :: proc(addr: rawptr, len: c.size_t, vec: cstring) -> c.int ---
@(link_name="minherit") _minherit :: proc(addr: rawptr, len: c.size_t, inherit: c.int) -> c.int ---
}
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
result := _mmap(nil, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
if result == MAP_FAILED {
return nil, .Out_Of_Memory
}
return ([^]byte)(uintptr(result))[:size], nil
}
_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
result := _mprotect(data, size, PROT_READ|PROT_WRITE)
if result != 0 {
return .Out_Of_Memory
}
return nil
}
_decommit :: proc "contextless" (data: rawptr, size: uint) {
_mprotect(data, size, PROT_NONE)
_madvise(data, size, MADV_FREE)
}
_release :: proc "contextless" (data: rawptr, size: uint) {
_munmap(data, size)
}
_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
pflags: c.int
pflags = PROT_NONE
if .Read in flags { pflags |= PROT_READ }
if .Write in flags { pflags |= PROT_WRITE }
if .Execute in flags { pflags |= PROT_EXEC }
err := _mprotect(data, size, pflags)
return err != 0
}
_platform_memory_init :: proc() {
DEFAULT_PAGE_SIZE = 4096
// is power of two
assert(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0)
}
+1 -1
View File
@@ -48,7 +48,7 @@ munmap :: proc "contextless" (addr: rawptr, length: uint) -> c.int {
} }
mprotect :: proc "contextless" (addr: rawptr, length: uint, prot: c.int) -> c.int { mprotect :: proc "contextless" (addr: rawptr, length: uint, prot: c.int) -> c.int {
res := intrinsics.syscall(unix.SYS_mprotect, uintptr(addr), uintptr(length), uint(prot)) res := intrinsics.syscall(unix.SYS_mprotect, uintptr(addr), uintptr(length), uintptr(prot))
return c.int(res) return c.int(res)
} }
+37 -1
View File
@@ -552,13 +552,20 @@ unparen_expr :: proc(expr: ^Expr) -> (val: ^Expr) {
return return
} }
Field_Flags :: distinct bit_set[Field_Flag]
Field_Flag :: enum { Field_Flag :: enum {
Invalid,
Unknown,
Ellipsis, Ellipsis,
Using, Using,
No_Alias, No_Alias,
C_Vararg, C_Vararg,
Auto_Cast, Auto_Cast,
Any_Int, Any_Int,
Subtype,
By_Ptr,
Results, Results,
Tags, Tags,
@@ -566,11 +573,38 @@ Field_Flag :: enum {
Typeid_Token, Typeid_Token,
} }
Field_Flags :: distinct bit_set[Field_Flag] field_flag_strings := [Field_Flag]string{
.Invalid = "",
.Unknown = "",
.Ellipsis = "..",
.Using = "using",
.No_Alias = "#no_alias",
.C_Vararg = "#c_vararg",
.Auto_Cast = "auto_cast",
.Any_Int = "#any_int",
.Subtype = "#subtype",
.By_Ptr = "#by_ptr",
.Results = "results",
.Tags = "field tag",
.Default_Parameters = "default parameters",
.Typeid_Token = "typeid",
}
field_hash_flag_strings := []struct{key: string, flag: Field_Flag}{
{"no_alias", .No_Alias},
{"c_vararg", .C_Vararg},
{"any_int", .Any_Int},
{"subtype", .Subtype},
{"by_ptr", .By_Ptr},
}
Field_Flags_Struct :: Field_Flags{ Field_Flags_Struct :: Field_Flags{
.Using, .Using,
.Tags, .Tags,
.Subtype,
} }
Field_Flags_Record_Poly_Params :: Field_Flags{ Field_Flags_Record_Poly_Params :: Field_Flags{
.Typeid_Token, .Typeid_Token,
@@ -583,6 +617,7 @@ Field_Flags_Signature :: Field_Flags{
.C_Vararg, .C_Vararg,
.Auto_Cast, .Auto_Cast,
.Any_Int, .Any_Int,
.By_Ptr,
.Default_Parameters, .Default_Parameters,
} }
@@ -665,6 +700,7 @@ Proc_Type :: struct {
Pointer_Type :: struct { Pointer_Type :: struct {
using node: Expr, using node: Expr,
tag: ^Expr,
pointer: tokenizer.Pos, pointer: tokenizer.Pos,
elem: ^Expr, elem: ^Expr,
} }
+1
View File
@@ -286,6 +286,7 @@ clone_node :: proc(node: ^Node) -> ^Node {
r.results = auto_cast clone(r.results) r.results = auto_cast clone(r.results)
case ^Pointer_Type: case ^Pointer_Type:
r.elem = clone(r.elem) r.elem = clone(r.elem)
r.tag = clone(r.tag)
case ^Multi_Pointer_Type: case ^Multi_Pointer_Type:
r.elem = clone(r.elem) r.elem = clone(r.elem)
case ^Array_Type: case ^Array_Type:
+2
View File
@@ -186,6 +186,7 @@ Type_Kind :: enum u32le {
Relative_Slice = 21, Relative_Slice = 21,
Multi_Pointer = 22, Multi_Pointer = 22,
Matrix = 23, Matrix = 23,
Soa_Pointer = 24,
} }
Type_Elems_Cap :: 4 Type_Elems_Cap :: 4
@@ -245,6 +246,7 @@ Type :: struct {
// .Relative_Slice - 2 types: 0=slice type, 1=base integer // .Relative_Slice - 2 types: 0=slice type, 1=base integer
// .Multi_Pointer - 1 type: 0=element // .Multi_Pointer - 1 type: 0=element
// .Matrix - 1 type: 0=element // .Matrix - 1 type: 0=element
// .Soa_Pointer - 1 type: 0=element
types: Array(Type_Index), types: Array(Type_Index),
// Used by: // Used by:
+41 -78
View File
@@ -183,6 +183,7 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool {
pd.name = pkg_name.text pd.name = pkg_name.text
pd.comment = p.line_comment pd.comment = p.line_comment
p.file.pkg_decl = pd p.file.pkg_decl = pd
p.file.docs = docs
expect_semicolon(p, pd) expect_semicolon(p, pd)
@@ -244,12 +245,7 @@ peek_token :: proc(p: ^Parser, lookahead := 0) -> (tok: tokenizer.Token) {
return return
} }
skip_possible_newline :: proc(p: ^Parser) -> bool { skip_possible_newline :: proc(p: ^Parser) -> bool {
if .Optional_Semicolons not_in p.flags { if tokenizer.is_newline(p.curr_tok) {
return false
}
prev := p.curr_tok
if tokenizer.is_newline(prev) {
advance_token(p) advance_token(p)
return true return true
} }
@@ -1610,20 +1606,6 @@ new_ast_field :: proc(names: []^ast.Expr, type: ^ast.Expr, default_value: ^ast.E
return field return field
} }
Field_Prefix :: enum {
Invalid,
Unknown,
Using,
No_Alias,
C_Vararg,
Auto_Cast,
Any_Int,
}
Field_Prefixes :: distinct bit_set[Field_Prefix]
Expr_And_Flags :: struct { Expr_And_Flags :: struct {
expr: ^ast.Expr, expr: ^ast.Expr,
flags: ast.Field_Flags, flags: ast.Field_Flags,
@@ -1665,7 +1647,7 @@ convert_to_ident_list :: proc(p: ^Parser, list: []Expr_And_Flags, ignore_flags,
return idents[:] return idents[:]
} }
is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix { is_token_field_prefix :: proc(p: ^Parser) -> ast.Field_Flag {
#partial switch p.curr_tok.kind { #partial switch p.curr_tok.kind {
case .EOF: case .EOF:
return .Invalid return .Invalid
@@ -1676,17 +1658,15 @@ is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix {
advance_token(p) advance_token(p)
return .Auto_Cast return .Auto_Cast
case .Hash: case .Hash:
tok: tokenizer.Token
advance_token(p) advance_token(p)
defer advance_token(p) tok = p.curr_tok
#partial switch p.curr_tok.kind { advance_token(p)
case .Ident: if tok.kind == .Ident {
switch p.curr_tok.text { for kf in ast.field_hash_flag_strings {
case "no_alias": if kf.key == tok.text {
return .No_Alias return kf.flag
case "c_vararg": }
return .C_Vararg
case "any_int":
return .Any_Int
} }
} }
return .Unknown return .Unknown
@@ -1694,8 +1674,8 @@ is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix {
return .Invalid return .Invalid
} }
parse_field_prefixes :: proc(p: ^Parser) -> ast.Field_Flags { parse_field_prefixes :: proc(p: ^Parser) -> (flags: ast.Field_Flags) {
counts: [len(Field_Prefix)]int counts: [len(ast.Field_Flag)]int
for { for {
kind := is_token_field_prefix(p) kind := is_token_field_prefix(p)
@@ -1711,31 +1691,17 @@ parse_field_prefixes :: proc(p: ^Parser) -> ast.Field_Flags {
counts[kind] += 1 counts[kind] += 1
} }
flags: ast.Field_Flags for kind in ast.Field_Flag {
for kind in Field_Prefix {
count := counts[kind] count := counts[kind]
switch kind { if kind == .Invalid || kind == .Unknown {
case .Invalid, .Unknown: // Ignore // Ignore
case .Using: } else {
if count > 1 { error(p, p.curr_tok.pos, "multiple 'using' in this field list") } if count > 1 { error(p, p.curr_tok.pos, "multiple '%s' in this field list", ast.field_flag_strings[kind]) }
if count > 0 { flags += {.Using} } if count > 0 { flags += {kind} }
case .No_Alias:
if count > 1 { error(p, p.curr_tok.pos, "multiple '#no_alias' in this field list") }
if count > 0 { flags += {.No_Alias} }
case .C_Vararg:
if count > 1 { error(p, p.curr_tok.pos, "multiple '#c_vararg' in this field list") }
if count > 0 { flags += {.C_Vararg} }
case .Auto_Cast:
if count > 1 { error(p, p.curr_tok.pos, "multiple 'auto_cast' in this field list") }
if count > 0 { flags += {.Auto_Cast} }
case .Any_Int:
if count > 1 { error(p, p.curr_tok.pos, "multiple '#any_int' in this field list") }
if count > 0 { flags += {.Any_Int} }
} }
} }
return flags return
} }
check_field_flag_prefixes :: proc(p: ^Parser, name_count: int, allowed_flags, set_flags: ast.Field_Flags) -> (flags: ast.Field_Flags) { check_field_flag_prefixes :: proc(p: ^Parser, name_count: int, allowed_flags, set_flags: ast.Field_Flags) -> (flags: ast.Field_Flags) {
@@ -1747,19 +1713,13 @@ check_field_flag_prefixes :: proc(p: ^Parser, name_count: int, allowed_flags, se
for flag in ast.Field_Flag { for flag in ast.Field_Flag {
if flag not_in allowed_flags && flag in flags { if flag not_in allowed_flags && flag in flags {
switch flag { #partial switch flag {
case .Using: case .Unknown, .Invalid:
error(p, p.curr_tok.pos, "'using' is not allowed within this field list") // ignore
case .No_Alias:
error(p, p.curr_tok.pos, "'#no_alias' is not allowed within this field list")
case .C_Vararg:
error(p, p.curr_tok.pos, "'#c_vararg' is not allowed within this field list")
case .Auto_Cast:
error(p, p.curr_tok.pos, "'auto_cast' is not allowed within this field list")
case .Any_Int:
error(p, p.curr_tok.pos, "'#any_int' is not allowed within this field list")
case .Tags, .Ellipsis, .Results, .Default_Parameters, .Typeid_Token: case .Tags, .Ellipsis, .Results, .Default_Parameters, .Typeid_Token:
panic("Impossible prefixes") panic("Impossible prefixes")
case:
error(p, p.curr_tok.pos, "'%s' is not allowed within this field list", ast.field_flag_strings[flag])
} }
flags -= {flag} flags -= {flag}
} }
@@ -2270,7 +2230,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
return parse_call_expr(p, bd) return parse_call_expr(p, bd)
case "soa", "simd": case "soa":
bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name)) bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
bd.tok = tok bd.tok = tok
bd.name = name.text bd.name = name.text
@@ -2279,6 +2239,20 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
#partial switch t in type.derived_expr { #partial switch t in type.derived_expr {
case ^ast.Array_Type: t.tag = bd case ^ast.Array_Type: t.tag = bd
case ^ast.Dynamic_Array_Type: t.tag = bd case ^ast.Dynamic_Array_Type: t.tag = bd
case ^ast.Pointer_Type: t.tag = bd
case:
error(p, original_type.pos, "expected an array or pointer type after #%s", name.text)
}
return original_type
case "simd":
bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
bd.tok = tok
bd.name = name.text
original_type := parse_type(p)
type := ast.unparen_expr(original_type)
#partial switch t in type.derived_expr {
case ^ast.Array_Type: t.tag = bd
case: case:
error(p, original_type.pos, "expected an array type after #%s", name.text) error(p, original_type.pos, "expected an array type after #%s", name.text)
} }
@@ -2630,7 +2604,6 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
tok := expect_token(p, .Union) tok := expect_token(p, .Union)
poly_params: ^ast.Field_List poly_params: ^ast.Field_List
align: ^ast.Expr align: ^ast.Expr
is_maybe: bool
is_no_nil: bool is_no_nil: bool
is_shared_nil: bool is_shared_nil: bool
@@ -2655,10 +2628,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
} }
align = parse_expr(p, true) align = parse_expr(p, true)
case "maybe": case "maybe":
if is_maybe { error(p, tag.pos, "#%s functionality has now been merged with standard 'union' functionality", tag.text)
error(p, tag.pos, "duplicate union tag '#%s'", tag.text)
}
is_maybe = true
case "no_nil": case "no_nil":
if is_no_nil { if is_no_nil {
error(p, tag.pos, "duplicate union tag '#%s'", tag.text) error(p, tag.pos, "duplicate union tag '#%s'", tag.text)
@@ -2675,19 +2645,12 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
} }
p.expr_level = prev_level p.expr_level = prev_level
if is_no_nil && is_maybe {
error(p, p.curr_tok.pos, "#maybe and #no_nil cannot be applied together")
}
if is_no_nil && is_shared_nil { if is_no_nil && is_shared_nil {
error(p, p.curr_tok.pos, "#shared_nil and #no_nil cannot be applied together") error(p, p.curr_tok.pos, "#shared_nil and #no_nil cannot be applied together")
} }
if is_shared_nil && is_maybe {
error(p, p.curr_tok.pos, "#maybe and #shared_nil cannot be applied together")
}
union_kind := ast.Union_Type_Kind.Normal union_kind := ast.Union_Type_Kind.Normal
switch { switch {
case is_maybe: union_kind = .maybe
case is_no_nil: union_kind = .no_nil case is_no_nil: union_kind = .no_nil
case is_shared_nil: union_kind = .shared_nil case is_shared_nil: union_kind = .shared_nil
} }
+1 -1
View File
@@ -151,7 +151,7 @@ print :: proc(p: ^Printer, file: ^ast.File) -> string {
fix_lines(p) fix_lines(p)
builder := strings.make_builder(0, 5 * mem.Megabyte, p.allocator) builder := strings.builder_make(0, 5 * mem.Megabyte, p.allocator)
last_line := 0 last_line := 0
+3 -3
View File
@@ -71,7 +71,7 @@ push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int {
return 0 return 0
} else { } else {
builder := strings.make_builder(context.temp_allocator) builder := strings.builder_make(context.temp_allocator)
c_len := len(comment.text) c_len := len(comment.text)
trim_space := true trim_space := true
@@ -90,12 +90,12 @@ push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int {
continue continue
case c == '\r' && comment.text[min(c_len - 1, i + 1)] == '\n': case c == '\r' && comment.text[min(c_len - 1, i + 1)] == '\n':
append(&multilines, strings.to_string(builder)) append(&multilines, strings.to_string(builder))
builder = strings.make_builder(context.temp_allocator) builder = strings.builder_make(context.temp_allocator)
trim_space = true trim_space = true
i += 1 i += 1
case c == '\n': case c == '\n':
append(&multilines, strings.to_string(builder)) append(&multilines, strings.to_string(builder))
builder = strings.make_builder(context.temp_allocator) builder = strings.builder_make(context.temp_allocator)
trim_space = true trim_space = true
case c == '/' && comment.text[min(c_len - 1, i + 1)] == '*': case c == '/' && comment.text[min(c_len - 1, i + 1)] == '*':
strings.write_string(&builder, "/*") strings.write_string(&builder, "/*")
+5 -3
View File
@@ -1,6 +1,5 @@
package os package os
import "core:strings"
import "core:mem" import "core:mem"
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) { read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
@@ -51,10 +50,13 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
continue continue
} }
fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator) fullpath := make([]byte, len(dirpath)+1+len(filename))
copy(fullpath, dirpath)
copy(fullpath[len(dirpath):], "/")
copy(fullpath[len(dirpath)+1:], filename)
defer delete(fullpath, context.temp_allocator) defer delete(fullpath, context.temp_allocator)
fi_, err = stat(fullpath, allocator) fi_, err = stat(string(fullpath), allocator)
if err != ERROR_NONE { if err != ERROR_NONE {
for fi__ in dfi { for fi__ in dfi {
file_info_delete(fi__, allocator) file_info_delete(fi__, allocator)
+16 -16
View File
@@ -11,24 +11,24 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
return return
} }
wkey := win32.utf8_to_wstring(key) wkey := win32.utf8_to_wstring(key)
b := make([dynamic]u16, 100, context.temp_allocator) n := win32.GetEnvironmentVariableW(wkey, nil, 0)
for { if n == 0 {
n := win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b))) err := win32.GetLastError()
if n == 0 { if err == u32(ERROR_ENVVAR_NOT_FOUND) {
err := win32.GetLastError() return "", false
if err == u32(ERROR_ENVVAR_NOT_FOUND) {
return "", false
}
} }
if n <= u32(len(b)) {
value, _ = win32.utf16_to_utf8(b[:n], allocator)
found = true
return
}
resize(&b, len(b)*2)
} }
b := make([dynamic]u16, n, context.temp_allocator)
n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
if n == 0 {
err := win32.GetLastError()
if err == u32(ERROR_ENVVAR_NOT_FOUND) {
return "", false
}
}
value, _ = win32.utf16_to_utf8(b[:n], allocator)
found = true
return
} }
+31 -11
View File
@@ -384,21 +384,33 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
change_directory :: proc(path: string) -> Errno { change_directory :: proc(path: string) -> (err: Errno) {
wpath := win32.utf8_to_wstring(path, context.temp_allocator) wpath := win32.utf8_to_wstring(path, context.temp_allocator)
return Errno(win32.SetCurrentDirectoryW(wpath))
if !win32.SetCurrentDirectoryW(wpath) {
err = Errno(win32.GetLastError())
}
return
} }
make_directory :: proc(path: string, mode: u32 = 0) -> Errno { make_directory :: proc(path: string, mode: u32 = 0) -> (err: Errno) {
// Mode is unused on Windows, but is needed on *nix // Mode is unused on Windows, but is needed on *nix
wpath := win32.utf8_to_wstring(path, context.temp_allocator) wpath := win32.utf8_to_wstring(path, context.temp_allocator)
return Errno(win32.CreateDirectoryW(wpath, nil))
if !win32.CreateDirectoryW(wpath, nil) {
err = Errno(win32.GetLastError())
}
return
} }
remove_directory :: proc(path: string) -> Errno { remove_directory :: proc(path: string) -> (err: Errno) {
wpath := win32.utf8_to_wstring(path, context.temp_allocator) wpath := win32.utf8_to_wstring(path, context.temp_allocator)
return Errno(win32.RemoveDirectoryW(wpath))
if !win32.RemoveDirectoryW(wpath) {
err = Errno(win32.GetLastError())
}
return
} }
@@ -464,23 +476,31 @@ fix_long_path :: proc(path: string) -> string {
} }
link :: proc(old_name, new_name: string) -> Errno { link :: proc(old_name, new_name: string) -> (err: Errno) {
n := win32.utf8_to_wstring(fix_long_path(new_name)) n := win32.utf8_to_wstring(fix_long_path(new_name))
o := win32.utf8_to_wstring(fix_long_path(old_name)) o := win32.utf8_to_wstring(fix_long_path(old_name))
return Errno(win32.CreateHardLinkW(n, o, nil)) return Errno(win32.CreateHardLinkW(n, o, nil))
} }
unlink :: proc(path: string) -> Errno { unlink :: proc(path: string) -> (err: Errno) {
wpath := win32.utf8_to_wstring(path, context.temp_allocator) wpath := win32.utf8_to_wstring(path, context.temp_allocator)
return Errno(win32.DeleteFileW(wpath))
if !win32.DeleteFileW(wpath) {
err = Errno(win32.GetLastError())
}
return
} }
rename :: proc(old_path, new_path: string) -> Errno { rename :: proc(old_path, new_path: string) -> (err: Errno) {
from := win32.utf8_to_wstring(old_path, context.temp_allocator) from := win32.utf8_to_wstring(old_path, context.temp_allocator)
to := win32.utf8_to_wstring(new_path, context.temp_allocator) to := win32.utf8_to_wstring(new_path, context.temp_allocator)
return Errno(win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING))
if !win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) {
err = Errno(win32.GetLastError())
}
return
} }
+1 -1
View File
@@ -99,7 +99,7 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
return nil, nil return nil, nil
case .Query_Info: case .Query_Info:
return nil, nil return nil, .Mode_Not_Implemented
} }
return nil, nil return nil, nil
+10 -10
View File
@@ -6,19 +6,19 @@ import "core:runtime"
user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
#partial switch ODIN_OS { #partial switch ODIN_OS {
case .Windows: case .Windows:
dir = get_env("LocalAppData") dir = get_env("LocalAppData", allocator)
if dir != "" { if dir != "" {
dir = strings.clone_safe(dir, allocator) or_return dir = strings.clone_safe(dir, allocator) or_return
} }
case .Darwin: case .Darwin:
dir = get_env("HOME") dir = get_env("HOME", allocator)
if dir != "" { if dir != "" {
dir = strings.concatenate_safe({dir, "/Library/Caches"}, allocator) or_return dir = strings.concatenate_safe({dir, "/Library/Caches"}, allocator) or_return
} }
case: // All other UNIX systems case: // All other UNIX systems
dir = get_env("XDG_CACHE_HOME") dir = get_env("XDG_CACHE_HOME", allocator)
if dir == "" { if dir == "" {
dir = get_env("HOME") dir = get_env("HOME", allocator)
if dir == "" { if dir == "" {
return return
} }
@@ -34,19 +34,19 @@ user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error
user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
#partial switch ODIN_OS { #partial switch ODIN_OS {
case .Windows: case .Windows:
dir = get_env("AppData") dir = get_env("AppData", allocator)
if dir != "" { if dir != "" {
dir = strings.clone_safe(dir, allocator) or_return dir = strings.clone_safe(dir, allocator) or_return
} }
case .Darwin: case .Darwin:
dir = get_env("HOME") dir = get_env("HOME", allocator)
if dir != "" { if dir != "" {
dir = strings.concatenate_safe({dir, "/Library/Application Support"}, allocator) or_return dir = strings.concatenate_safe({dir, "/Library/Application Support"}, allocator) or_return
} }
case: // All other UNIX systems case: // All other UNIX systems
dir = get_env("XDG_CACHE_HOME") dir = get_env("XDG_CACHE_HOME", allocator)
if dir == "" { if dir == "" {
dir = get_env("HOME") dir = get_env("HOME", allocator)
if dir == "" { if dir == "" {
return return
} }
@@ -59,13 +59,13 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro
return return
} }
user_home_dir :: proc() -> (dir: string, err: Error) { user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
env := "HOME" env := "HOME"
#partial switch ODIN_OS { #partial switch ODIN_OS {
case .Windows: case .Windows:
env = "USERPROFILE" env = "USERPROFILE"
} }
if v := get_env(env); v != "" { if v := get_env(env, allocator); v != "" {
return v, nil return v, nil
} }
return "", .Invalid_Path return "", .Invalid_Path
+8 -8
View File
@@ -241,13 +241,13 @@ S_ISGID :: 0o2000 // Set group id on execution
S_ISVTX :: 0o1000 // Directory restrcted delete S_ISVTX :: 0o1000 // Directory restrcted delete
S_ISLNK :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFLNK S_ISLNK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFLNK }
S_ISREG :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFREG S_ISREG :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFREG }
S_ISDIR :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFDIR S_ISDIR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFDIR }
S_ISCHR :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFCHR S_ISCHR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFCHR }
S_ISBLK :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFBLK S_ISBLK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFBLK }
S_ISFIFO :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFIFO S_ISFIFO :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFIFO }
S_ISSOCK :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFSOCK S_ISSOCK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFSOCK }
F_OK :: 0 // Test for file existance F_OK :: 0 // Test for file existance
X_OK :: 1 // Test for execute permission X_OK :: 1 // Test for execute permission
@@ -257,7 +257,7 @@ R_OK :: 4 // Test for read permission
foreign libc { foreign libc {
@(link_name="__error") __errno_location :: proc() -> ^int --- @(link_name="__error") __errno_location :: proc() -> ^int ---
@(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle --- @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---
@(link_name="close") _unix_close :: proc(fd: Handle) -> c.int --- @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---
@(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
@(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
+1 -1
View File
@@ -311,7 +311,7 @@ _unix_seek :: proc(fd: Handle, offset: i64, whence: int) -> i64 {
low := uintptr(offset & 0xFFFFFFFF) low := uintptr(offset & 0xFFFFFFFF)
high := uintptr(offset >> 32) high := uintptr(offset >> 32)
result: i64 result: i64
res := i64(intrinsics.syscall(unix.SYS__llseek, uintptr(fd), high, low, &result, uintptr(whence))) res := i64(intrinsics.syscall(unix.SYS__llseek, uintptr(fd), high, low, uintptr(&result), uintptr(whence)))
return -1 if res < 0 else result return -1 if res < 0 else result
} }
} }
+1 -1
View File
@@ -271,7 +271,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string, allocator := cont
} }
d, derr := os.open(dir) d, derr := os.open(dir, os.O_RDONLY)
if derr != 0 { if derr != 0 {
return return
} }
+1 -1
View File
@@ -146,7 +146,7 @@ clean :: proc(path: string, allocator := context.allocator) -> string {
} }
// join joins numerous path elements into a single path // join joins numerous path elements into a single path
join :: proc(elems: ..string, allocator := context.allocator) -> string { join :: proc(elems: []string, allocator := context.allocator) -> string {
context.allocator = allocator context.allocator = allocator
for elem, i in elems { for elem, i in elems {
if elem != "" { if elem != "" {
+39 -24
View File
@@ -1,7 +1,6 @@
package reflect package reflect
import "core:runtime" import "core:runtime"
import "core:mem"
import "core:intrinsics" import "core:intrinsics"
_ :: intrinsics _ :: intrinsics
@@ -34,6 +33,7 @@ Type_Info_Simd_Vector :: runtime.Type_Info_Simd_Vector
Type_Info_Relative_Pointer :: runtime.Type_Info_Relative_Pointer Type_Info_Relative_Pointer :: runtime.Type_Info_Relative_Pointer
Type_Info_Relative_Slice :: runtime.Type_Info_Relative_Slice Type_Info_Relative_Slice :: runtime.Type_Info_Relative_Slice
Type_Info_Matrix :: runtime.Type_Info_Matrix Type_Info_Matrix :: runtime.Type_Info_Matrix
Type_Info_Soa_Pointer :: runtime.Type_Info_Soa_Pointer
Type_Info_Enum_Value :: runtime.Type_Info_Enum_Value Type_Info_Enum_Value :: runtime.Type_Info_Enum_Value
@@ -68,6 +68,7 @@ Type_Kind :: enum {
Relative_Pointer, Relative_Pointer,
Relative_Slice, Relative_Slice,
Matrix, Matrix,
Soa_Pointer,
} }
@@ -102,6 +103,7 @@ type_kind :: proc(T: typeid) -> Type_Kind {
case Type_Info_Relative_Pointer: return .Relative_Pointer case Type_Info_Relative_Pointer: return .Relative_Pointer
case Type_Info_Relative_Slice: return .Relative_Slice case Type_Info_Relative_Slice: return .Relative_Slice
case Type_Info_Matrix: return .Matrix case Type_Info_Matrix: return .Matrix
case Type_Info_Soa_Pointer: return .Soa_Pointer
} }
} }
@@ -194,6 +196,7 @@ typeid_elem :: proc(id: typeid) -> typeid {
} }
case Type_Info_Pointer: return v.elem.id case Type_Info_Pointer: return v.elem.id
case Type_Info_Multi_Pointer: return v.elem.id case Type_Info_Multi_Pointer: return v.elem.id
case Type_Info_Soa_Pointer: return v.elem.id
case Type_Info_Array: return v.elem.id case Type_Info_Array: return v.elem.id
case Type_Info_Enumerated_Array: return v.elem.id case Type_Info_Enumerated_Array: return v.elem.id
case Type_Info_Slice: return v.elem.id case Type_Info_Slice: return v.elem.id
@@ -220,7 +223,7 @@ align_of_typeid :: proc(T: typeid) -> int {
as_bytes :: proc(v: any) -> []byte { as_bytes :: proc(v: any) -> []byte {
if v != nil { if v != nil {
sz := size_of_typeid(v.id) sz := size_of_typeid(v.id)
return mem.slice_ptr((^byte)(v.data), sz) return ([^]byte)(v.data)[:sz]
} }
return nil return nil
} }
@@ -262,19 +265,19 @@ length :: proc(val: any) -> int {
return a.count return a.count
case Type_Info_Slice: case Type_Info_Slice:
return (^mem.Raw_Slice)(val.data).len return (^runtime.Raw_Slice)(val.data).len
case Type_Info_Dynamic_Array: case Type_Info_Dynamic_Array:
return (^mem.Raw_Dynamic_Array)(val.data).len return (^runtime.Raw_Dynamic_Array)(val.data).len
case Type_Info_Map: case Type_Info_Map:
return (^mem.Raw_Map)(val.data).entries.len return (^runtime.Raw_Map)(val.data).entries.len
case Type_Info_String: case Type_Info_String:
if a.is_cstring { if a.is_cstring {
return len((^cstring)(val.data)^) return len((^cstring)(val.data)^)
} else { } else {
return (^mem.Raw_String)(val.data).len return (^runtime.Raw_String)(val.data).len
} }
} }
return 0 return 0
@@ -297,10 +300,10 @@ capacity :: proc(val: any) -> int {
return a.count return a.count
case Type_Info_Dynamic_Array: case Type_Info_Dynamic_Array:
return (^mem.Raw_Dynamic_Array)(val.data).cap return (^runtime.Raw_Dynamic_Array)(val.data).cap
case Type_Info_Map: case Type_Info_Map:
return (^mem.Raw_Map)(val.data).entries.cap return (^runtime.Raw_Map)(val.data).entries.cap
} }
return 0 return 0
} }
@@ -340,14 +343,14 @@ index :: proc(val: any, i: int, loc := #caller_location) -> any {
return any{data, a.elem.id} return any{data, a.elem.id}
case Type_Info_Slice: case Type_Info_Slice:
raw := (^mem.Raw_Slice)(val.data) raw := (^runtime.Raw_Slice)(val.data)
runtime.bounds_check_error_loc(loc, i, raw.len) runtime.bounds_check_error_loc(loc, i, raw.len)
offset := uintptr(a.elem.size * i) offset := uintptr(a.elem.size * i)
data := rawptr(uintptr(raw.data) + offset) data := rawptr(uintptr(raw.data) + offset)
return any{data, a.elem.id} return any{data, a.elem.id}
case Type_Info_Dynamic_Array: case Type_Info_Dynamic_Array:
raw := (^mem.Raw_Dynamic_Array)(val.data) raw := (^runtime.Raw_Dynamic_Array)(val.data)
runtime.bounds_check_error_loc(loc, i, raw.len) runtime.bounds_check_error_loc(loc, i, raw.len)
offset := uintptr(a.elem.size * i) offset := uintptr(a.elem.size * i)
data := rawptr(uintptr(raw.data) + offset) data := rawptr(uintptr(raw.data) + offset)
@@ -356,7 +359,7 @@ index :: proc(val: any, i: int, loc := #caller_location) -> any {
case Type_Info_String: case Type_Info_String:
if a.is_cstring { return nil } if a.is_cstring { return nil }
raw := (^mem.Raw_String)(val.data) raw := (^runtime.Raw_String)(val.data)
runtime.bounds_check_error_loc(loc, i, raw.len) runtime.bounds_check_error_loc(loc, i, raw.len)
offset := uintptr(size_of(u8) * i) offset := uintptr(size_of(u8) * i)
data := rawptr(uintptr(raw.data) + offset) data := rawptr(uintptr(raw.data) + offset)
@@ -725,6 +728,17 @@ get_union_variant_raw_tag :: proc(a: any) -> i64 {
panic("expected a union to reflect.get_union_variant_raw_tag") panic("expected a union to reflect.get_union_variant_raw_tag")
} }
get_union_variant :: proc(a: any) -> any {
if a == nil {
return nil
}
id := union_variant_typeid(a)
if id == nil {
return nil
}
return any{a.data, id}
}
set_union_variant_raw_tag :: proc(a: any, tag: i64) { set_union_variant_raw_tag :: proc(a: any, tag: i64) {
if a == nil { return } if a == nil { return }
@@ -822,17 +836,17 @@ set_union_value :: proc(dst: any, value: any) -> bool {
ti := runtime.type_info_base(type_info_of(dst.id)) ti := runtime.type_info_base(type_info_of(dst.id))
if info, ok := ti.variant.(runtime.Type_Info_Union); ok { if info, ok := ti.variant.(runtime.Type_Info_Union); ok {
if value.id == nil { if value.id == nil {
mem.zero(dst.data, ti.size) intrinsics.mem_zero(dst.data, ti.size)
return true return true
} }
if ti.id == runtime.typeid_base(value.id) { if ti.id == runtime.typeid_base(value.id) {
mem.copy(dst.data, value.data, ti.size) intrinsics.mem_copy(dst.data, value.data, ti.size)
return true return true
} }
if type_info_union_is_pure_maybe(info) { if type_info_union_is_pure_maybe(info) {
if variant := info.variants[0]; variant.id == value.id { if variant := info.variants[0]; variant.id == value.id {
mem.copy(dst.data, value.data, variant.size) intrinsics.mem_copy(dst.data, value.data, variant.size)
return true return true
} }
return false return false
@@ -844,7 +858,7 @@ set_union_value :: proc(dst: any, value: any) -> bool {
if !info.no_nil { if !info.no_nil {
tag += 1 tag += 1
} }
mem.copy(dst.data, value.data, variant.size) intrinsics.mem_copy(dst.data, value.data, variant.size)
set_union_variant_raw_tag(dst, tag) set_union_variant_raw_tag(dst, tag)
return true return true
} }
@@ -1337,11 +1351,11 @@ as_raw_data :: proc(a: any) -> (value: rawptr, valid: bool) {
case Type_Info_Slice: case Type_Info_Slice:
valid = true valid = true
value = (^mem.Raw_Slice)(a.data).data value = (^runtime.Raw_Slice)(a.data).data
case Type_Info_Dynamic_Array: case Type_Info_Dynamic_Array:
valid = true valid = true
value = (^mem.Raw_Dynamic_Array)(a.data).data value = (^runtime.Raw_Dynamic_Array)(a.data).data
} }
return return
@@ -1383,7 +1397,7 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
} }
if .Simple_Compare in t.flags { if .Simple_Compare in t.flags {
return mem.compare_byte_ptrs((^byte)(a.data), (^byte)(b.data), t.size) == 0 return runtime.memory_compare(a.data, b.data, t.size) == 0
} }
t = runtime.type_info_core(t) t = runtime.type_info_core(t)
@@ -1419,8 +1433,9 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
Type_Info_Enum, Type_Info_Enum,
Type_Info_Simd_Vector, Type_Info_Simd_Vector,
Type_Info_Relative_Pointer, Type_Info_Relative_Pointer,
Type_Info_Soa_Pointer,
Type_Info_Matrix: Type_Info_Matrix:
return mem.compare_byte_ptrs((^byte)(a.data), (^byte)(b.data), t.size) == 0 return runtime.memory_compare(a.data, b.data, t.size) == 0
case Type_Info_String: case Type_Info_String:
if v.is_cstring { if v.is_cstring {
@@ -1474,8 +1489,8 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
if !including_indirect_array_recursion { if !including_indirect_array_recursion {
return false return false
} }
array_a := (^mem.Raw_Slice)(a.data) array_a := (^runtime.Raw_Slice)(a.data)
array_b := (^mem.Raw_Slice)(b.data) array_b := (^runtime.Raw_Slice)(b.data)
if array_a.len != array_b.len { if array_a.len != array_b.len {
return false return false
} }
@@ -1494,8 +1509,8 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
if !including_indirect_array_recursion { if !including_indirect_array_recursion {
return false return false
} }
array_a := (^mem.Raw_Dynamic_Array)(a.data) array_a := (^runtime.Raw_Dynamic_Array)(a.data)
array_b := (^mem.Raw_Dynamic_Array)(b.data) array_b := (^runtime.Raw_Dynamic_Array)(b.data)
if array_a.len != array_b.len { if array_a.len != array_b.len {
return false return false
} }
@@ -1503,7 +1518,7 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
return true return true
} }
if .Simple_Compare in v.elem.flags { if .Simple_Compare in v.elem.flags {
return mem.compare_byte_ptrs((^byte)(array_a.data), (^byte)(array_b.data), array_a.len * v.elem.size) == 0 return runtime.memory_compare((^byte)(array_a.data), (^byte)(array_b.data), array_a.len * v.elem.size) == 0
} }
for i in 0..<array_a.len { for i in 0..<array_a.len {
+20 -2
View File
@@ -68,6 +68,11 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
y := b.variant.(Type_Info_Multi_Pointer) or_return y := b.variant.(Type_Info_Multi_Pointer) or_return
return are_types_identical(x.elem, y.elem) return are_types_identical(x.elem, y.elem)
case Type_Info_Soa_Pointer:
y := b.variant.(Type_Info_Soa_Pointer) or_return
return are_types_identical(x.elem, y.elem)
case Type_Info_Procedure: case Type_Info_Procedure:
y := b.variant.(Type_Info_Procedure) or_return y := b.variant.(Type_Info_Procedure) or_return
switch { switch {
@@ -256,6 +261,11 @@ is_multi_pointer :: proc(info: ^Type_Info) -> bool {
_, ok := type_info_base(info).variant.(Type_Info_Multi_Pointer) _, ok := type_info_base(info).variant.(Type_Info_Multi_Pointer)
return ok return ok
} }
is_soa_pointer :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Soa_Pointer)
return ok
}
is_pointer_internally :: proc(info: ^Type_Info) -> bool { is_pointer_internally :: proc(info: ^Type_Info) -> bool {
if info == nil { return false } if info == nil { return false }
#partial switch v in info.variant { #partial switch v in info.variant {
@@ -292,6 +302,11 @@ is_dynamic_map :: proc(info: ^Type_Info) -> bool {
_, ok := type_info_base(info).variant.(Type_Info_Map) _, ok := type_info_base(info).variant.(Type_Info_Map)
return ok return ok
} }
is_bit_set :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Bit_Set)
return ok
}
is_slice :: proc(info: ^Type_Info) -> bool { is_slice :: proc(info: ^Type_Info) -> bool {
if info == nil { return false } if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Slice) _, ok := type_info_base(info).variant.(Type_Info_Slice)
@@ -437,6 +452,9 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) -
case Type_Info_Multi_Pointer: case Type_Info_Multi_Pointer:
io.write_string(w, "[^]", &n) or_return io.write_string(w, "[^]", &n) or_return
write_type(w, info.elem, &n) or_return write_type(w, info.elem, &n) or_return
case Type_Info_Soa_Pointer:
io.write_string(w, "#soa ^", &n) or_return
write_type(w, info.elem, &n) or_return
case Type_Info_Procedure: case Type_Info_Procedure:
io.write_string(w, "proc", &n) or_return io.write_string(w, "proc", &n) or_return
if info.params == nil { if info.params == nil {
@@ -573,11 +591,11 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) -
write_type(w, info.elem, &n) or_return write_type(w, info.elem, &n) or_return
case is_rune(info.elem): case is_rune(info.elem):
io.write_encoded_rune(w, rune(info.lower), true, &n) or_return io.write_encoded_rune(w, rune(info.lower), true, &n) or_return
io.write_string(w, "..", &n) or_return io.write_string(w, "..=", &n) or_return
io.write_encoded_rune(w, rune(info.upper), true, &n) or_return io.write_encoded_rune(w, rune(info.upper), true, &n) or_return
case: case:
io.write_i64(w, info.lower, 10, &n) or_return io.write_i64(w, info.lower, 10, &n) or_return
io.write_string(w, "..", &n) or_return io.write_string(w, "..=", &n) or_return
io.write_i64(w, info.upper, 10, &n) or_return io.write_i64(w, info.upper, 10, &n) or_return
} }
if info.underlying != nil { if info.underlying != nil {
+23
View File
@@ -176,6 +176,9 @@ Type_Info_Matrix :: struct {
column_count: int, column_count: int,
// Total element count = column_count * elem_stride // Total element count = column_count * elem_stride
} }
Type_Info_Soa_Pointer :: struct {
elem: ^Type_Info,
}
Type_Info_Flag :: enum u8 { Type_Info_Flag :: enum u8 {
Comparable = 0, Comparable = 0,
@@ -217,6 +220,7 @@ Type_Info :: struct {
Type_Info_Relative_Pointer, Type_Info_Relative_Pointer,
Type_Info_Relative_Slice, Type_Info_Relative_Slice,
Type_Info_Matrix, Type_Info_Matrix,
Type_Info_Soa_Pointer,
}, },
} }
@@ -267,6 +271,19 @@ type_table: []Type_Info
args__: []cstring args__: []cstring
when ODIN_OS == .Windows {
// NOTE(Jeroen): If we're a Windows DLL, fwdReason will be populated.
// This tells a DLL if it's first loaded, about to be unloaded, or a thread is joining/exiting.
DLL_Forward_Reason :: enum u32 {
Process_Detach = 0, // About to unload DLL
Process_Attach = 1, // Entry point
Thread_Attach = 2,
Thread_Detach = 3,
}
dll_forward_reason: DLL_Forward_Reason
}
// IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it) // IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it)
@@ -390,6 +407,12 @@ Raw_Cstring :: struct {
data: [^]byte, data: [^]byte,
} }
Raw_Soa_Pointer :: struct {
data: rawptr,
index: int,
}
/* /*
// Defined internally by the compiler // Defined internally by the compiler
+180 -60
View File
@@ -129,6 +129,9 @@ reserve :: proc{reserve_dynamic_array, reserve_map}
@builtin @builtin
resize :: proc{resize_dynamic_array} resize :: proc{resize_dynamic_array}
// Shrinks the capacity of a dynamic array or map down to the current length, or the given capacity.
@builtin
shrink :: proc{shrink_dynamic_array, shrink_map}
@builtin @builtin
free :: proc{mem_free} free :: proc{mem_free}
@@ -140,7 +143,7 @@ free_all :: proc{mem_free_all}
@builtin @builtin
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
return mem_free(raw_data(str), allocator, loc) return mem_free_with_size(raw_data(str), len(str), allocator, loc)
} }
@builtin @builtin
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
@@ -148,17 +151,24 @@ delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #cal
} }
@builtin @builtin
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> Allocator_Error { delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> Allocator_Error {
return mem_free(raw_data(array), array.allocator, loc) return mem_free_with_size(raw_data(array), cap(array)*size_of(E), array.allocator, loc)
} }
@builtin @builtin
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
return mem_free(raw_data(array), allocator, loc) return mem_free_with_size(raw_data(array), len(array)*size_of(E), allocator, loc)
} }
@builtin @builtin
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error { delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
Entry :: struct {
hash: uintptr,
next: int,
key: K,
value: V,
}
raw := transmute(Raw_Map)m raw := transmute(Raw_Map)m
err := delete_slice(raw.hashes, raw.entries.allocator, loc) err := delete_slice(raw.hashes, raw.entries.allocator, loc)
err1 := mem_free(raw.entries.data, raw.entries.allocator, loc) err1 := mem_free_with_size(raw.entries.data, raw.entries.cap*size_of(Entry), raw.entries.allocator, loc)
if err == nil { if err == nil {
err = err1 err = err1
} }
@@ -284,12 +294,30 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) {
} }
@builtin @builtin
reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int) { reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int, loc := #caller_location) {
if m != nil { if m != nil {
__dynamic_map_reserve(__get_map_header(m), capacity) __dynamic_map_reserve(__get_map_header(m), capacity, loc)
} }
} }
/*
Shrinks the capacity of a map down to the current length, or the given capacity.
If `new_cap` is negative, then `len(m)` is used.
Returns false if `cap(m) < new_cap`, or the allocator report failure.
If `len(m) < new_cap`, then `len(m)` will be left unchanged.
*/
@builtin
shrink_map :: proc(m: ^$T/map[$K]$V, new_cap := -1, loc := #caller_location) -> (did_shrink: bool) {
if m != nil {
new_cap := new_cap if new_cap >= 0 else len(m)
return __dynamic_map_shrink(__get_map_header(m), new_cap, loc)
}
return
}
// The delete_key built-in procedure deletes the element with the specified key (m[key]) from the map. // The delete_key built-in procedure deletes the element with the specified key (m[key]) from the map.
// If m is nil, or there is no such element, this procedure is a no-op // If m is nil, or there is no such element, this procedure is a no-op
@builtin @builtin
@@ -314,68 +342,80 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value:
@builtin @builtin
append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) { append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> int {
if array == nil { if array == nil {
return return 0
} }
when size_of(E) == 0 {
if cap(array) < len(array)+1 { array.len += 1
cap := 2 * cap(array) + max(8, 1) return 1
_ = reserve(array, cap, loc) } else {
} if cap(array) < len(array)+1 {
if cap(array)-len(array) > 0 { cap := 2 * cap(array) + max(8, 1)
a := (^Raw_Dynamic_Array)(array) _ = reserve(array, cap, loc)
when size_of(E) != 0 {
data := ([^]E)(a.data)
assert(condition=data != nil, loc=loc)
data[a.len] = arg
} }
a.len += 1 if cap(array)-len(array) > 0 {
a := (^Raw_Dynamic_Array)(array)
when size_of(E) != 0 {
data := ([^]E)(a.data)
assert(condition=data != nil, loc=loc)
data[a.len] = arg
}
a.len += 1
return 1
}
return 0
} }
} }
@builtin @builtin
append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) { append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> int {
if array == nil { if array == nil {
return return 0
} }
arg_len := len(args) arg_len := len(args)
if arg_len <= 0 { if arg_len <= 0 {
return return 0
} }
when size_of(E) == 0 {
if cap(array) < len(array)+arg_len { array.len += arg_len
cap := 2 * cap(array) + max(8, arg_len) return arg_len
_ = reserve(array, cap, loc) } else {
} if cap(array) < len(array)+arg_len {
arg_len = min(cap(array)-len(array), arg_len) cap := 2 * cap(array) + max(8, arg_len)
if arg_len > 0 { _ = reserve(array, cap, loc)
a := (^Raw_Dynamic_Array)(array)
when size_of(E) != 0 {
data := ([^]E)(a.data)
assert(condition=data != nil, loc=loc)
intrinsics.mem_copy(&data[a.len], raw_data(args), size_of(E) * arg_len)
} }
a.len += arg_len arg_len = min(cap(array)-len(array), arg_len)
if arg_len > 0 {
a := (^Raw_Dynamic_Array)(array)
when size_of(E) != 0 {
data := ([^]E)(a.data)
assert(condition=data != nil, loc=loc)
intrinsics.mem_copy(&data[a.len], raw_data(args), size_of(E) * arg_len)
}
a.len += arg_len
}
return arg_len
} }
} }
// The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type // The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type
@builtin @builtin
append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) { append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> int {
args := transmute([]E)arg args := transmute([]E)arg
append_elems(array=array, args=args, loc=loc) return append_elems(array=array, args=args, loc=loc)
} }
// The append_string built-in procedure appends multiple strings to the end of a [dynamic]u8 like type // The append_string built-in procedure appends multiple strings to the end of a [dynamic]u8 like type
@builtin @builtin
append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) { append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) -> (n: int) {
for arg in args { for arg in args {
append(array = array, args = transmute([]E)(arg), loc = loc) n += append(array = array, args = transmute([]E)(arg), loc = loc)
} }
return
} }
// The append built-in procedure appends elements to the end of a dynamic array // The append built-in procedure appends elements to the end of a dynamic array
@@ -383,16 +423,18 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_
@builtin @builtin
append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) { append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> int {
if array == nil { if array == nil {
return return 0
} }
prev_len := len(array)
resize(array, len(array)+1) resize(array, len(array)+1)
return len(array)-prev_len
} }
@builtin @builtin
insert_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool) #no_bounds_check { inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
if array == nil { if array == nil {
return return
} }
@@ -411,7 +453,7 @@ insert_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle
} }
@builtin @builtin
insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check { inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
if array == nil { if array == nil {
return return
} }
@@ -435,7 +477,7 @@ insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #c
} }
@builtin @builtin
insert_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check { inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check {
if array == nil { if array == nil {
return return
} }
@@ -456,7 +498,51 @@ insert_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string
return return
} }
@builtin insert_at :: proc{insert_at_elem, insert_at_elems, insert_at_elem_string} @builtin inject_at :: proc{inject_at_elem, inject_at_elems, inject_at_elem_string}
@builtin
assign_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
if index < len(array) {
array[index] = arg
ok = true
} else if resize(array, index+1, loc) {
array[index] = arg
ok = true
}
return
}
@builtin
assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
if index+len(args) < len(array) {
copy(array[index:], args)
ok = true
} else if resize(array, index+1+len(args), loc) {
copy(array[index:], args)
ok = true
}
return
}
@builtin
assign_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check {
if len(args) == 0 {
ok = true
} else if index+len(args) < len(array) {
copy(array[index:], args)
ok = true
} else if resize(array, index+1+len(args), loc) {
copy(array[index:], args)
ok = true
}
return
}
@builtin assign_at :: proc{assign_at_elem, assign_at_elems, assign_at_elem_string}
@@ -536,6 +622,54 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
return true return true
} }
/*
Shrinks the capacity of a dynamic array down to the current length, or the given capacity.
If `new_cap` is negative, then `len(array)` is used.
Returns false if `cap(array) < new_cap`, or the allocator report failure.
If `len(array) < new_cap`, then `len(array)` will be left unchanged.
*/
shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #caller_location) -> (did_shrink: bool) {
if array == nil {
return
}
a := (^Raw_Dynamic_Array)(array)
new_cap := new_cap if new_cap >= 0 else a.len
if new_cap > a.cap {
return
}
if a.allocator.procedure == nil {
a.allocator = context.allocator
}
assert(a.allocator.procedure != nil)
old_size := a.cap * size_of(E)
new_size := new_cap * size_of(E)
new_data, err := a.allocator.procedure(
a.allocator.data,
.Resize,
new_size,
align_of(E),
a.data,
old_size,
loc,
)
if err != nil {
return
}
a.data = raw_data(new_data)
a.len = min(new_cap, a.len)
a.cap = new_cap
return true
}
@builtin @builtin
map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (ptr: ^V) { map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (ptr: ^V) {
key, value := key, value key, value := key, value
@@ -666,17 +800,3 @@ unimplemented :: proc(message := "", loc := #caller_location) -> ! {
} }
p("not yet implemented", message, loc) p("not yet implemented", message, loc)
} }
@builtin
@(disabled=ODIN_DISABLE_ASSERT)
unreachable :: proc(message := "", loc := #caller_location) -> ! {
p := context.assertion_failure_proc
if p == nil {
p = default_assertion_failure_proc
}
if message != "" {
p("internal error", message, loc)
} else {
p("internal error", "entered unreachable code", loc)
}
}
+2 -2
View File
@@ -17,7 +17,7 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
_windows_default_free(old_memory) _windows_default_free(old_memory)
case .Free_All: case .Free_All:
// NOTE(tetra): Do nothing. return nil, .Mode_Not_Implemented
case .Resize: case .Resize:
data, err = _windows_default_resize(old_memory, old_size, size, alignment) data, err = _windows_default_resize(old_memory, old_size, size, alignment)
@@ -29,7 +29,7 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
} }
case .Query_Info: case .Query_Info:
// Do nothing return nil, .Mode_Not_Implemented
} }
return return
@@ -185,7 +185,7 @@ when ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR
} }
case .Query_Info: case .Query_Info:
// Nothing to give return nil, .Mode_Not_Implemented
} }
return return
+37 -4
View File
@@ -29,11 +29,15 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap:
new_size := cap * elem_size new_size := cap * elem_size
allocator := array.allocator allocator := array.allocator
new_data, err := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, loc) new_data, err := mem_resize(array.data, old_size, new_size, elem_align, allocator, loc)
if err != nil { if err != nil {
return false return false
} }
if new_data != nil || elem_size == 0 { if elem_size == 0 {
array.data = raw_data(new_data)
array.cap = cap
return true
} else if new_data != nil {
array.data = raw_data(new_data) array.data = raw_data(new_data)
array.cap = min(cap, len(new_data)/elem_size) array.cap = min(cap, len(new_data)/elem_size)
return true return true
@@ -41,6 +45,35 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap:
return false return false
} }
__dynamic_array_shrink :: proc(array_: rawptr, elem_size, elem_align: int, new_cap: int, loc := #caller_location) -> (did_shrink: bool) {
array := (^Raw_Dynamic_Array)(array_)
// NOTE(tetra, 2020-01-26): We set the allocator before earlying-out below, because user code is usually written
// assuming that appending/reserving will set the allocator, if it is not already set.
if array.allocator.procedure == nil {
array.allocator = context.allocator
}
assert(array.allocator.procedure != nil)
if new_cap > array.cap {
return
}
old_size := array.cap * elem_size
new_size := new_cap * elem_size
allocator := array.allocator
new_data, err := mem_resize(array.data, old_size, new_size, elem_align, allocator, loc)
if err != nil {
return
}
array.data = raw_data(new_data)
array.len = min(new_cap, array.len)
array.cap = new_cap
return true
}
__dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int, loc := #caller_location) -> bool { __dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int, loc := #caller_location) -> bool {
array := (^Raw_Dynamic_Array)(array_) array := (^Raw_Dynamic_Array)(array_)
@@ -65,7 +98,7 @@ __dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int,
ok := true ok := true
if array.cap <= array.len+item_count { if array.cap < array.len+item_count {
cap := 2 * array.cap + max(8, item_count) cap := 2 * array.cap + max(8, item_count)
ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc) ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc)
} }
@@ -86,7 +119,7 @@ __dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: in
array := (^Raw_Dynamic_Array)(array_) array := (^Raw_Dynamic_Array)(array_)
ok := true ok := true
if array.cap <= array.len+1 { if array.cap < array.len+1 {
cap := 2 * array.cap + max(8, 1) cap := 2 * array.cap + max(8, 1)
ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc) ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc)
} }
+18 -5
View File
@@ -194,12 +194,15 @@ __slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, l
new_size := new_count*size_of(T) new_size := new_count*size_of(T)
new_data, err := mem_resize(array.data, old_size, new_size, align_of(T), allocator, loc) new_data, err := mem_resize(array.data, old_size, new_size, align_of(T), allocator, loc)
if new_data == nil || err != nil { if err != nil {
return false return false
} }
array.data = new_data if new_data != nil || size_of(E) == 0 {
array.len = new_count array.data = raw_data(new_data)
return true array.len = new_count
return true
}
return false
} }
__dynamic_map_reset_entries :: proc(using header: Map_Header, loc := #caller_location) { __dynamic_map_reset_entries :: proc(using header: Map_Header, loc := #caller_location) {
@@ -207,7 +210,7 @@ __dynamic_map_reset_entries :: proc(using header: Map_Header, loc := #caller_loc
m.hashes[i] = -1 m.hashes[i] = -1
} }
for i in 0 ..< m.entries.len { for i in 0..<m.entries.len {
entry_header := __dynamic_map_get_entry(header, i) entry_header := __dynamic_map_get_entry(header, i)
entry_hash := __get_map_hash_from_entry(header, entry_header) entry_hash := __get_map_hash_from_entry(header, entry_header)
entry_header.next = -1 entry_header.next = -1
@@ -239,6 +242,16 @@ __dynamic_map_reserve :: proc(using header: Map_Header, cap: int, loc := #caller
} }
} }
__dynamic_map_shrink :: proc(using header: Map_Header, cap: int, loc := #caller_location) -> (did_shrink: bool) {
c := context
if m.entries.allocator.procedure != nil {
c.allocator = m.entries.allocator
}
context = c
return __dynamic_array_shrink(&m.entries, entry_size, entry_align, cap, loc)
}
__dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #caller_location) { __dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #caller_location) {
#force_inline __dynamic_map_reserve(header, new_count, loc) #force_inline __dynamic_map_reserve(header, new_count, loc)
} }
+9 -5
View File
@@ -8,15 +8,19 @@ when ODIN_BUILD_MODE == .Dynamic {
@(link_name="DllMain", linkage="strong", require) @(link_name="DllMain", linkage="strong", require)
DllMain :: proc "stdcall" (hinstDLL: rawptr, fdwReason: u32, lpReserved: rawptr) -> b32 { DllMain :: proc "stdcall" (hinstDLL: rawptr, fdwReason: u32, lpReserved: rawptr) -> b32 {
context = default_context() context = default_context()
switch fdwReason {
case 1: // DLL_PROCESS_ATTACH // Populate Windows DLL-specific global
dll_forward_reason = DLL_Forward_Reason(fdwReason)
switch dll_forward_reason {
case .Process_Attach:
#force_no_inline _startup_runtime() #force_no_inline _startup_runtime()
intrinsics.__entry_point() intrinsics.__entry_point()
case 0: // DLL_PROCESS_DETACH case .Process_Detach:
#force_no_inline _cleanup_runtime() #force_no_inline _cleanup_runtime()
case 2: // DLL_THREAD_ATTACH case .Thread_Attach:
break break
case 3: // DLL_THREAD_DETACH case .Thread_Detach:
break break
} }
return true return true
+61 -37
View File
@@ -103,7 +103,7 @@ mem_zero :: proc "contextless" (data: rawptr, len: int) -> rawptr {
if data == nil { if data == nil {
return nil return nil
} }
if len < 0 { if len <= 0 {
return data return data
} }
intrinsics.mem_zero(data, len) intrinsics.mem_zero(data, len)
@@ -111,22 +111,18 @@ mem_zero :: proc "contextless" (data: rawptr, len: int) -> rawptr {
} }
mem_copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr { mem_copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
if src == nil { if src != nil && dst != src && len > 0 {
return dst // NOTE(bill): This _must_ be implemented like C's memmove
intrinsics.mem_copy(dst, src, len)
} }
// NOTE(bill): This _must_ be implemented like C's memmove
intrinsics.mem_copy(dst, src, len)
return dst return dst
} }
mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr { mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
if src == nil { if src != nil && dst != src && len > 0 {
return dst // NOTE(bill): This _must_ be implemented like C's memcpy
intrinsics.mem_copy_non_overlapping(dst, src, len)
} }
// NOTE(bill): This _must_ be implemented like C's memcpy
intrinsics.mem_copy_non_overlapping(dst, src, len)
return dst return dst
} }
@@ -142,28 +138,38 @@ mem_alloc_bytes :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNM
return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc) return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc)
} }
mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (rawptr, Allocator_Error) { mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
if size == 0 { if size == 0 || allocator.procedure == nil {
return nil, nil return nil, nil
} }
if allocator.procedure == nil { return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc)
return nil, nil
}
data, err := allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc)
return raw_data(data), err
} }
mem_free :: #force_inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { mem_free :: #force_inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
if ptr == nil { if ptr == nil || allocator.procedure == nil {
return .None return nil
}
if allocator.procedure == nil {
return .None
} }
_, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, 0, loc) _, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, 0, loc)
return err return err
} }
mem_free_with_size :: #force_inline proc(ptr: rawptr, byte_count: int, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
if ptr == nil || allocator.procedure == nil {
return nil
}
_, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, byte_count, loc)
return err
}
mem_free_bytes :: #force_inline proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
if bytes == nil || allocator.procedure == nil {
return nil
}
_, err := allocator.procedure(allocator.data, .Free, 0, 0, raw_data(bytes), len(bytes), loc)
return err
}
mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #caller_location) -> (err: Allocator_Error) { mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #caller_location) -> (err: Allocator_Error) {
if allocator.procedure != nil { if allocator.procedure != nil {
_, err = allocator.procedure(allocator.data, .Free_All, 0, 0, nil, 0, loc) _, err = allocator.procedure(allocator.data, .Free_All, 0, 0, nil, 0, loc)
@@ -171,21 +177,34 @@ mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #calle
return return
} }
mem_resize :: #force_inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (new_ptr: rawptr, err: Allocator_Error) { mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
new_data: []byte if allocator.procedure == nil {
switch { return nil, nil
case allocator.procedure == nil:
return
case new_size == 0:
new_data, err = allocator.procedure(allocator.data, .Free, 0, 0, ptr, 0, loc)
case ptr == nil:
new_data, err = allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
case:
new_data, err = allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc)
} }
new_ptr = raw_data(new_data) if new_size == 0 {
return if ptr != nil {
_, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, old_size, loc)
return nil, err
}
return nil, nil
} else if ptr == nil {
return allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
} else if old_size == new_size && uintptr(ptr) % uintptr(alignment) == 0 {
return ([^]byte)(ptr)[:old_size], nil
}
data, err := allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc)
if err == .Mode_Not_Implemented {
data, err = allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
if err != nil {
return data, err
}
copy(data, ([^]byte)(ptr)[:old_size])
_, err = allocator.procedure(allocator.data, .Free, 0, 0, ptr, old_size, loc)
}
return data, err
} }
memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool { memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
switch { switch {
case n == 0: return true case n == 0: return true
@@ -341,7 +360,12 @@ string_eq :: proc "contextless" (lhs, rhs: string) -> bool {
string_cmp :: proc "contextless" (a, b: string) -> int { string_cmp :: proc "contextless" (a, b: string) -> int {
x := transmute(Raw_String)a x := transmute(Raw_String)a
y := transmute(Raw_String)b y := transmute(Raw_String)b
return memory_compare(x.data, y.data, min(x.len, y.len))
ret := memory_compare(x.data, y.data, min(x.len, y.len))
if ret == 0 && x.len != y.len {
return -1 if x.len < y.len else +1
}
return ret
} }
string_ne :: #force_inline proc "contextless" (a, b: string) -> bool { return !string_eq(a, b) } string_ne :: #force_inline proc "contextless" (a, b: string) -> bool { return !string_eq(a, b) }
+3
View File
@@ -228,6 +228,9 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
case Type_Info_Multi_Pointer: case Type_Info_Multi_Pointer:
print_string("[^]") print_string("[^]")
print_type(info.elem) print_type(info.elem)
case Type_Info_Soa_Pointer:
print_string("#soa ^")
print_type(info.elem)
case Type_Info_Procedure: case Type_Info_Procedure:
print_string("proc") print_string("proc")
if info.params == nil { if info.params == nil {
+18 -8
View File
@@ -6,7 +6,7 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
@(private="file") @(private="file")
@(default_calling_convention="stdcall") @(default_calling_convention="stdcall")
foreign lib { foreign lib {
RtlMoveMemory :: proc(dst, src: rawptr, length: int) --- RtlMoveMemory :: proc(dst, s: rawptr, length: int) ---
RtlFillMemory :: proc(dst: rawptr, length: int, fill: i32) --- RtlFillMemory :: proc(dst: rawptr, length: int, fill: i32) ---
} }
@@ -40,24 +40,34 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
@(link_name="memmove", linkage="strong", require) @(link_name="memmove", linkage="strong", require)
memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr { memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
if dst != src { d, s := ([^]byte)(dst), ([^]byte)(src)
d, s := ([^]byte)(dst), ([^]byte)(src) if d == s || len == 0 {
return dst
}
if d > s && uintptr(d)-uintptr(s) < uintptr(len) {
for i := len-1; i >= 0; i -= 1 { for i := len-1; i >= 0; i -= 1 {
d[i] = s[i] d[i] = s[i]
} }
return dst
} }
return dst
if s > d && uintptr(s)-uintptr(d) < uintptr(len) {
for i := 0; i < len; i += 1 {
d[i] = s[i]
}
return dst
}
return memcpy(dst, src, len)
} }
@(link_name="memcpy", linkage="strong", require) @(link_name="memcpy", linkage="strong", require)
memcpy :: proc "c" (dst, src: rawptr, len: int) -> rawptr { memcpy :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
if dst != src { d, s := ([^]byte)(dst), ([^]byte)(src)
d, s := ([^]byte)(dst), ([^]byte)(src) if d != s {
for i := len-1; i >= 0; i -= 1 { for i := 0; i < len; i += 1 {
d[i] = s[i] d[i] = s[i]
} }
} }
return dst return d
} }
} else { } else {
+1 -2
View File
@@ -61,8 +61,7 @@ b64x8 :: #simd[8]b64
add :: intrinsics.simd_add add :: intrinsics.simd_add
sub :: intrinsics.simd_sub sub :: intrinsics.simd_sub
mul :: intrinsics.simd_mul mul :: intrinsics.simd_mul
div :: intrinsics.simd_div div :: intrinsics.simd_div // floats only
rem :: intrinsics.simd_rem // integers only
// Keeps Odin's Behaviour // Keeps Odin's Behaviour
// (x << y) if y <= mask else 0 // (x << y) if y <= mask else 0
+231
View File
@@ -0,0 +1,231 @@
/*
Copyright 2022 Dale Weiler <weilercdale@gmail.com>.
Made available under Odin's BSD-3 license.
List of contributors:
Dale Weiler: Initial implementation
*/
// Package implements a generic max heap in-place on a slice for any type.
package heap
/*
Constructs a max heap in slice given by data with comparator. A max heap is
a range of elements which has the following properties:
1. With N = len(data), for all 0 < i < N, data[(i - 1) / 2] does not compare
less than data[i].
2. A new element can be added using push in O(log n) time.
3. The first element can be removed using pop in O(log n) time.
The comparator compares elements of type T and can be used to construct a
max heap (less than) or min heap (greater than) for T.
*/
make :: proc(data: []$T, less: proc(a, b: T) -> bool) {
// amoritize length lookup
length := len(data)
if length <= 1 do return
// start from data parent, no need to consider children
for start := (length - 2) / 2; start >= 0; start -= 1 {
sift_down(data, less, start)
}
}
/*
Inserts the element at the position len(data)-1 into the max heap with
comparator.
At most log(N) comparisons where N = len(data) will be performed.
*/
push :: proc(data: []$T, less: proc(a, b: T) -> bool) {
sift_up(data, less)
}
/*
Swaps the value in position data[0] and the value in data[len(data)-1] and
makes subrange [0, len(data)-1) into a heap. This has the effect of removing
the first element from the heap.
At most 2 * log(N) comparisons where N = len(data) will be performed.
*/
pop :: proc(data: []$T, less: proc(a, b: T) -> bool) {
length := len(data)
if length <= 1 do return
last := length
// create a hole at 0
top := data[0]
hole := floyd_sift_down(data, less)
last -= 1
if hole == last {
data[hole] = top
} else {
data[hole] = data[last]
hole += 1
data[last] = top
sift_up(data[:hole], less)
}
}
/*
Converts the max heap into a sorted range in ascending order. The resulting
slice will no longer be a heap after this.
At most 2 * N * log(N) comparisons where N = len(data) will be performed.
*/
sort :: proc(data: []$T, less: proc(a, b: T) -> bool) {
for n := len(data); n >= 1; n -= 1 {
pop(data[:n], less)
}
}
/*
Examines the slice and finds the largest range which is a max-heap. Elements
are compared with user-supplied comparison procedure.
This returns the upper bound of the largest range in the slice which is a
max heap. That is, the last index for which data is a max heap.
At most O(n) comparisons where N = len(data) will be performed.
*/
is_heap_until :: proc(data: []$T, less: proc(a, b: T) -> bool) -> int {
length := len(data)
a := 0
b := 1
for b < length {
if less(data[a], data[b]) {
return b
}
b += 1
if b == length || less(data[a], data[b]) {
return b
}
a += 1
b = 2 * a + 1
}
return length
}
/*
Checks if a given slice is a max heap.
At most O(n) comparisons where N = len(data) will be performed.
*/
is_heap :: #force_inline proc(data: []$T, less: proc(a, b: T) -> bool) -> bool {
return is_heap_until(data, less) == len(data)
}
@(private="file")
floyd_sift_down :: proc(data: []$T, less: proc(a, b: T) -> bool) -> int {
length := len(data)
assert(length >= 2)
hole := 0
child := 0
index := 0
for {
index += child + 1
child = 2 * child + 1
if child + 1 < length && less(data[index], data[index + 1]) {
child += 1
index += 1
}
data[hole] = data[index]
hole = index
if child > (length - 2) / 2 {
return hole
}
}
unreachable()
}
@(private="file")
sift_down :: proc(data: []$T, less: proc(a, b: T) -> bool, start: int) {
start := start
child := start
// amoritize length lookup
length := len(data)
// left child of start is at 2 * start + 1
// right child of start is at 2 * start + 2
if length < 2 || (length - 2) / 2 < child {
return
}
child = 2 * child + 1
if child + 1 < length && less(data[child], data[child + 1]) {
// right child exists and is greater than left child
child += 1
}
// check if in heap order
if less(data[child], data[start]) {
// start is larger than its largest child
return
}
top := data[start]
for {
// not in heap order, swap parent with its largest child
data[start] = data[child]
start = child
if (length - 2) / 2 < child {
break
}
// recompute child based off updated parent
child = 2 * child + 1
if child + 1 < length && less(data[child], data[child + 1]) {
// right child exists and is greater than left child
child += 1
}
// check if we are in heap order
if less(data[child], top) {
break
}
}
data[start] = top
}
@(private="file")
sift_up :: proc(data: []$T, less: proc(a, b: T) -> bool) {
// amoritize length lookup
length := len(data)
if length <= 1 do return
last := length
length = (length - 2) / 2
index := length
last -= 1
if less(data[index], data[last]) {
top := data[last]
for {
data[last] = data[index]
last = index
if length == 0 {
break
}
length = (length - 1) / 2
index = length
if !less(data[index], top) {
break
}
}
data[last] = top
}
}
+1 -3
View File
@@ -2,11 +2,9 @@ package slice
import "core:intrinsics" import "core:intrinsics"
import "core:runtime" import "core:runtime"
import "core:mem"
_ :: intrinsics _ :: intrinsics
_ :: runtime _ :: runtime
_ :: mem
map_keys :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (keys: []K) { map_keys :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (keys: []K) {
keys = make(type_of(keys), len(m), allocator) keys = make(type_of(keys), len(m), allocator)
@@ -52,7 +50,7 @@ map_entries :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries
map_entry_infos :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry_Info(K, V)) #no_bounds_check { map_entry_infos :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry_Info(K, V)) #no_bounds_check {
m := m m := m
rm := (^mem.Raw_Map)(&m) rm := (^runtime.Raw_Map)(&m)
info := runtime.type_info_base(type_info_of(M)).variant.(runtime.Type_Info_Map) info := runtime.type_info_base(type_info_of(M)).variant.(runtime.Type_Info_Map)
gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct) gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct)
+17 -2
View File
@@ -14,14 +14,14 @@ _ :: mem
Turn a pointer and a length into a slice. Turn a pointer and a length into a slice.
*/ */
from_ptr :: proc "contextless" (ptr: ^$T, count: int) -> []T { from_ptr :: proc "contextless" (ptr: ^$T, count: int) -> []T {
return ([^]T)(ptr)[:count] return ([^]T)(ptr)[:count]
} }
/* /*
Turn a pointer and a length into a byte slice. Turn a pointer and a length into a byte slice.
*/ */
bytes_from_ptr :: proc "contextless" (ptr: rawptr, byte_count: int) -> []byte { bytes_from_ptr :: proc "contextless" (ptr: rawptr, byte_count: int) -> []byte {
return ([^]byte)(ptr)[:byte_count] return ([^]byte)(ptr)[:byte_count]
} }
/* /*
@@ -170,6 +170,21 @@ simple_equal :: proc(a, b: $T/[]$E) -> bool where intrinsics.type_is_simple_comp
return mem.compare_ptrs(raw_data(a), raw_data(b), len(a)*size_of(E)) == 0 return mem.compare_ptrs(raw_data(a), raw_data(b), len(a)*size_of(E)) == 0
} }
/*
return the prefix length common between slices `a` and `b`.
slice.prefix_length([]u8{1, 2, 3, 4}, []u8{1}) -> 1
slice.prefix_length([]u8{1, 2, 3, 4}, []u8{1, 2, 3}) -> 3
slice.prefix_length([]u8{1, 2, 3, 4}, []u8{2, 3, 4}) -> 0
*/
prefix_length :: proc(a, b: $T/[]$E) -> (n: int) where intrinsics.type_is_comparable(E) {
_len := builtin.min(len(a), len(b))
#no_bounds_check for n < _len && a[n] == b[n] {
n += 1
}
return
}
has_prefix :: proc(array: $T/[]$E, needle: E) -> bool where intrinsics.type_is_comparable(E) { has_prefix :: proc(array: $T/[]$E, needle: E) -> bool where intrinsics.type_is_comparable(E) {
n := len(needle) n := len(needle)
+63 -1
View File
@@ -38,6 +38,52 @@ sort :: proc(data: $T/[]$E) where ORD(E) {
} }
} }
sort_by_indices :: proc{ sort_by_indices_allocate, _sort_by_indices}
sort_by_indices_allocate :: proc(data: $T/[]$E, indices: []int, allocator := context.allocator) -> (sorted: T) {
assert(len(data) == len(indices))
sorted = make([]int, len(data), allocator)
for v, i in indices {
sorted[i] = data[v]
}
return
}
_sort_by_indices :: proc(data, sorted: $T/[]$E, indices: []int) {
assert(len(data) == len(indices))
assert(len(data) == len(sorted))
for v, i in indices {
sorted[i] = data[v]
}
}
sort_by_indices_overwrite :: proc(data: $T/[]$E, indices: []int) {
assert(len(data) == len(indices))
temp := make([]int, len(data), context.allocator)
defer delete(temp)
for v, i in indices {
temp[i] = data[v]
}
swap_with_slice(data, temp)
}
// sort sorts a slice and returns a slice of the original indices
// This sort is not guaranteed to be stable
sort_with_indices :: proc(data: $T/[]$E, allocator := context.allocator) -> (indices: []int) where ORD(E) {
indices = make([]int, len(data), allocator)
when size_of(E) != 0 {
if n := len(data); n > 1 {
for _, idx in indices {
indices[idx] = idx
}
_quick_sort_general_with_indices(data, indices, 0, n, _max_depth(n), struct{}{}, .Ordered)
}
return indices
}
return indices
}
// sort_by sorts a slice with a given procedure to test whether two values are ordered "i < j" // sort_by sorts a slice with a given procedure to test whether two values are ordered "i < j"
// This sort is not guaranteed to be stable // This sort is not guaranteed to be stable
sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) { sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) {
@@ -48,6 +94,22 @@ sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) {
} }
} }
// sort_by sorts a slice with a given procedure to test whether two values are ordered "i < j"
// This sort is not guaranteed to be stable
sort_by_with_indices :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool, allocator := context.allocator) -> (indices : []int) {
indices = make([]int, len(data), allocator)
when size_of(E) != 0 {
if n := len(data); n > 1 {
for _, idx in indices {
indices[idx] = idx
}
_quick_sort_general_with_indices(data, indices, 0, n, _max_depth(n), less, .Less)
return indices
}
}
return indices
}
sort_by_cmp :: proc(data: $T/[]$E, cmp: proc(i, j: E) -> Ordering) { sort_by_cmp :: proc(data: $T/[]$E, cmp: proc(i, j: E) -> Ordering) {
when size_of(E) != 0 { when size_of(E) != 0 {
if n := len(data); n > 1 { if n := len(data); n > 1 {
@@ -103,7 +165,7 @@ is_sorted_by :: proc(array: $T/[]$E, less: proc(i, j: E) -> bool) -> bool {
is_sorted_by_cmp :: is_sorted_cmp is_sorted_by_cmp :: is_sorted_cmp
is_sorted_cmp :: proc(array: $T/[]$E, cmp: proc(i, j: E) -> Ordering) -> bool { is_sorted_cmp :: proc(array: $T/[]$E, cmp: proc(i, j: E) -> Ordering) -> bool {
for i := len(array)-1; i > 0; i -= 1 { for i := len(array)-1; i > 0; i -= 1 {
if cmp(array[i], array[i-1]) == .Equal { if cmp(array[i], array[i-1]) == .Less {
return false return false
} }
} }
+177
View File
@@ -198,3 +198,180 @@ _stable_sort_general :: proc(data: $T/[]$E, call: $P, $KIND: Sort_Kind) where (O
} }
} }
} }
_quick_sort_general_with_indices :: proc(data: $T/[]$E, indices: []int, a, b, max_depth: int, call: $P, $KIND: Sort_Kind) where (ORD(E) && KIND == .Ordered) || (KIND != .Ordered) #no_bounds_check {
less :: #force_inline proc(a, b: E, call: P) -> bool {
when KIND == .Ordered {
return a < b
} else when KIND == .Less {
return call(a, b)
} else when KIND == .Cmp {
return call(a, b) == .Less
} else {
#panic("unhandled Sort_Kind")
}
}
insertion_sort :: proc(data: $T/[]$E, indices: []int, a, b: int, call: P) #no_bounds_check {
for i in a+1..<b {
for j := i; j > a && less(data[j], data[j-1], call); j -= 1 {
swap(data, j, j-1)
swap(indices, j, j-1)
}
}
}
heap_sort :: proc(data: $T/[]$E, indices: []int, a, b: int, call: P) #no_bounds_check {
sift_down :: proc(data: T, indices: []int, lo, hi, first: int, call: P) #no_bounds_check {
root := lo
for {
child := 2*root + 1
if child >= hi {
break
}
if child+1 < hi && less(data[first+child], data[first+child+1], call) {
child += 1
}
if !less(data[first+root], data[first+child], call) {
return
}
swap(data, first+root, first+child)
swap(indices, first+root, first+child)
root = child
}
}
first, lo, hi := a, 0, b-a
for i := (hi-1)/2; i >= 0; i -= 1 {
sift_down(data, indices, i, hi, first, call)
}
for i := hi-1; i >= 0; i -= 1 {
swap(data, first, first+i)
swap(indices, first, first+i)
sift_down(data, indices, lo, i, first, call)
}
}
median3 :: proc(data: T, indices: []int, m1, m0, m2: int, call: P) #no_bounds_check {
if less(data[m1], data[m0], call) {
swap(data, m1, m0)
swap(indices, m1, m0)
}
if less(data[m2], data[m1], call) {
swap(data, m2, m1)
swap(indices, m2, m1)
if less(data[m1], data[m0], call) {
swap(data, m1, m0)
swap(indices, m1, m0)
}
}
}
do_pivot :: proc(data: T, indices: []int, lo, hi: int, call: P) -> (midlo, midhi: int) #no_bounds_check {
m := int(uint(lo+hi)>>1)
if hi-lo > 40 {
s := (hi-lo)/8
median3(data, indices, lo, lo+s, lo+s*2, call)
median3(data, indices, m, m-s, m+s, call)
median3(data, indices, hi-1, hi-1-s, hi-1-s*2, call)
}
median3(data, indices, lo, m, hi-1, call)
pivot := lo
a, c := lo+1, hi-1
for ; a < c && less(data[a], data[pivot], call); a += 1 {
}
b := a
for {
for ; b < c && !less(data[pivot], data[b], call); b += 1 { // data[b] <= pivot
}
for ; b < c && less(data[pivot], data[c-1], call); c -=1 { // data[c-1] > pivot
}
if b >= c {
break
}
swap(data, b, c-1)
swap(indices, b, c-1)
b += 1
c -= 1
}
protect := hi-c < 5
if !protect && hi-c < (hi-lo)/4 {
dups := 0
if !less(data[pivot], data[hi-1], call) {
swap(data, c, hi-1)
swap(indices, c, hi-1)
c += 1
dups += 1
}
if !less(data[b-1], data[pivot], call) {
b -= 1
dups += 1
}
if !less(data[m], data[pivot], call) {
swap(data, m, b-1)
swap(indices, m, b-1)
b -= 1
dups += 1
}
protect = dups > 1
}
if protect {
for {
for ; a < b && !less(data[b-1], data[pivot], call); b -= 1 {
}
for ; a < b && less(data[a], data[pivot], call); a += 1 {
}
if a >= b {
break
}
swap(data, a, b-1)
swap(indices, a, b-1)
a += 1
b -= 1
}
}
swap(data, pivot, b-1)
swap(indices, pivot, b-1)
return b-1, c
}
assert(len(data) == len(indices))
a, b, max_depth := a, b, max_depth
for b-a > 12 { // only use shell sort for lengths <= 12
if max_depth == 0 {
heap_sort(data, indices, a, b, call)
return
}
max_depth -= 1
mlo, mhi := do_pivot(data, indices, a, b, call)
if mlo-a < b-mhi {
_quick_sort_general_with_indices(data, indices, a, mlo, max_depth, call, KIND)
a = mhi
} else {
_quick_sort_general_with_indices(data, indices, mhi, b, max_depth, call, KIND)
b = mlo
}
}
if b-a > 1 {
// Shell short with gap 6
for i in a+6..<b {
if less(data[i], data[i-6], call) {
swap(data, i, i-6)
swap(indices, i, i-6)
}
}
insertion_sort(data, indices, a, b, call)
}
}
+6 -1
View File
@@ -684,5 +684,10 @@ compare_f64s :: proc(a, b: f64) -> int {
compare_strings :: proc(a, b: string) -> int { compare_strings :: proc(a, b: string) -> int {
x := transmute(mem.Raw_String)a x := transmute(mem.Raw_String)a
y := transmute(mem.Raw_String)b y := transmute(mem.Raw_String)b
return mem.compare_byte_ptrs(x.data, y.data, min(x.len, y.len))
ret := mem.compare_byte_ptrs(x.data, y.data, min(x.len, y.len))
if ret == 0 && x.len != y.len {
return -1 if x.len < y.len else +1
}
return ret
} }
+31 -18
View File
@@ -2,11 +2,13 @@ package strconv
import "core:unicode/utf8" import "core:unicode/utf8"
parse_bool :: proc(s: string) -> (result: bool = false, ok: bool) { parse_bool :: proc(s: string, n: ^int = nil) -> (result: bool = false, ok: bool) {
switch s { switch s {
case "1", "t", "T", "true", "TRUE", "True": case "1", "t", "T", "true", "TRUE", "True":
if n != nil { n^ = len(s) }
return true, true return true, true
case "0", "f", "F", "false", "FALSE", "False": case "0", "f", "F", "false", "FALSE", "False":
if n != nil { n^ = len(s) }
return false, true return false, true
} }
return return
@@ -32,10 +34,13 @@ _digit_value :: proc(r: rune) -> int {
// n, ok := strconv.parse_i64_of_base("-1234eeee", 10); // n, ok := strconv.parse_i64_of_base("-1234eeee", 10);
// assert(n == -1234 && ok); // assert(n == -1234 && ok);
// ``` // ```
parse_i64_of_base :: proc(str: string, base: int) -> (value: i64, ok: bool) { parse_i64_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: i64, ok: bool) {
assert(base <= 16, "base must be 1-16") assert(base <= 16, "base must be 1-16")
s := str s := str
defer if n != nil { n^ = len(str)-len(s) }
if s == "" { if s == "" {
return return
} }
@@ -87,8 +92,9 @@ parse_i64_of_base :: proc(str: string, base: int) -> (value: i64, ok: bool) {
// n, ok = strconv.parse_i64_maybe_prefixed("0xeeee"); // n, ok = strconv.parse_i64_maybe_prefixed("0xeeee");
// assert(n == 0xeeee && ok); // assert(n == 0xeeee && ok);
// ``` // ```
parse_i64_maybe_prefixed :: proc(str: string) -> (value: i64, ok: bool) { parse_i64_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: i64, ok: bool) {
s := str s := str
defer if n != nil { n^ = len(str)-len(s) }
if s == "" { if s == "" {
return return
} }
@@ -155,9 +161,10 @@ parse_i64 :: proc{parse_i64_maybe_prefixed, parse_i64_of_base}
// n, ok = strconv.parse_u64_of_base("5678eeee", 16); // n, ok = strconv.parse_u64_of_base("5678eeee", 16);
// assert(n == 0x5678eeee && ok); // assert(n == 0x5678eeee && ok);
// ``` // ```
parse_u64_of_base :: proc(str: string, base: int) -> (value: u64, ok: bool) { parse_u64_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: u64, ok: bool) {
assert(base <= 16, "base must be 1-16") assert(base <= 16, "base must be 1-16")
s := str s := str
defer if n != nil { n^ = len(str)-len(s) }
if s == "" { if s == "" {
return return
} }
@@ -198,8 +205,9 @@ parse_u64_of_base :: proc(str: string, base: int) -> (value: u64, ok: bool) {
// n, ok = strconv.parse_u64_maybe_prefixed("0xeeee"); // n, ok = strconv.parse_u64_maybe_prefixed("0xeeee");
// assert(n == 0xeeee && ok); // assert(n == 0xeeee && ok);
// ``` // ```
parse_u64_maybe_prefixed :: proc(str: string) -> (value: u64, ok: bool) { parse_u64_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u64, ok: bool) {
s := str s := str
defer if n != nil { n^ = len(str)-len(s) }
if s == "" { if s == "" {
return return
} }
@@ -259,11 +267,11 @@ parse_u64 :: proc{parse_u64_maybe_prefixed, parse_u64_of_base}
// n, ok = strconv.parse_int("0xffff"); // with prefix and inferred base // n, ok = strconv.parse_int("0xffff"); // with prefix and inferred base
// assert(n == 0xffff && ok); // assert(n == 0xffff && ok);
// ``` // ```
parse_int :: proc(s: string, base := 0) -> (value: int, ok: bool) { parse_int :: proc(s: string, base := 0, n: ^int = nil) -> (value: int, ok: bool) {
v: i64 = --- v: i64 = ---
switch base { switch base {
case 0: v, ok = parse_i64_maybe_prefixed(s) case 0: v, ok = parse_i64_maybe_prefixed(s, n)
case: v, ok = parse_i64_of_base(s, base) case: v, ok = parse_i64_of_base(s, base, n)
} }
value = int(v) value = int(v)
return return
@@ -289,11 +297,11 @@ parse_int :: proc(s: string, base := 0) -> (value: int, ok: bool) {
// n, ok = strconv.parse_uint("0xffff"); // with prefix and inferred base // n, ok = strconv.parse_uint("0xffff"); // with prefix and inferred base
// assert(n == 0xffff && ok); // assert(n == 0xffff && ok);
// ``` // ```
parse_uint :: proc(s: string, base := 0) -> (value: uint, ok: bool) { parse_uint :: proc(s: string, base := 0, n: ^int = nil) -> (value: uint, ok: bool) {
v: u64 = --- v: u64 = ---
switch base { switch base {
case 0: v, ok = parse_u64_maybe_prefixed(s) case 0: v, ok = parse_u64_maybe_prefixed(s, n)
case: v, ok = parse_u64_of_base(s, base) case: v, ok = parse_u64_of_base(s, base, n)
} }
value = uint(v) value = uint(v)
return return
@@ -309,10 +317,11 @@ parse_uint :: proc(s: string, base := 0) -> (value: uint, ok: bool) {
// n, ok := strconv.parse_i128_of_base("-1234eeee", 10); // n, ok := strconv.parse_i128_of_base("-1234eeee", 10);
// assert(n == -1234 && ok); // assert(n == -1234 && ok);
// ``` // ```
parse_i128_of_base :: proc(str: string, base: int) -> (value: i128, ok: bool) { parse_i128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: i128, ok: bool) {
assert(base <= 16, "base must be 1-16") assert(base <= 16, "base must be 1-16")
s := str s := str
defer if n != nil { n^ = len(str)-len(s) }
if s == "" { if s == "" {
return return
} }
@@ -364,8 +373,9 @@ parse_i128_of_base :: proc(str: string, base: int) -> (value: i128, ok: bool) {
// n, ok = strconv.parse_i128_maybe_prefixed("0xeeee"); // n, ok = strconv.parse_i128_maybe_prefixed("0xeeee");
// assert(n == 0xeeee && ok); // assert(n == 0xeeee && ok);
// ``` // ```
parse_i128_maybe_prefixed :: proc(str: string) -> (value: i128, ok: bool) { parse_i128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: i128, ok: bool) {
s := str s := str
defer if n != nil { n^ = len(str)-len(s) }
if s == "" { if s == "" {
return return
} }
@@ -432,9 +442,10 @@ parse_i128 :: proc{parse_i128_maybe_prefixed, parse_i128_of_base}
// n, ok = strconv.parse_u128_of_base("5678eeee", 16); // n, ok = strconv.parse_u128_of_base("5678eeee", 16);
// assert(n == 0x5678eeee && ok); // assert(n == 0x5678eeee && ok);
// ``` // ```
parse_u128_of_base :: proc(str: string, base: int) -> (value: u128, ok: bool) { parse_u128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: u128, ok: bool) {
assert(base <= 16, "base must be 1-16") assert(base <= 16, "base must be 1-16")
s := str s := str
defer if n != nil { n^ = len(str)-len(s) }
if s == "" { if s == "" {
return return
} }
@@ -475,8 +486,9 @@ parse_u128_of_base :: proc(str: string, base: int) -> (value: u128, ok: bool) {
// n, ok = strconv.parse_u128_maybe_prefixed("0xeeee"); // n, ok = strconv.parse_u128_maybe_prefixed("0xeeee");
// assert(n == 0xeeee && ok); // assert(n == 0xeeee && ok);
// ``` // ```
parse_u128_maybe_prefixed :: proc(str: string) -> (value: u128, ok: bool) { parse_u128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u128, ok: bool) {
s := str s := str
defer if n != nil { n^ = len(str)-len(s) }
if s == "" { if s == "" {
return return
} }
@@ -535,9 +547,9 @@ parse_u128 :: proc{parse_u128_maybe_prefixed, parse_u128_of_base}
// n, ok = strconv.parse_f32("12.34"); // n, ok = strconv.parse_f32("12.34");
// assert(n == 12.34 && ok); // assert(n == 12.34 && ok);
// ``` // ```
parse_f32 :: proc(s: string) -> (value: f32, ok: bool) { parse_f32 :: proc(s: string, n: ^int = nil) -> (value: f32, ok: bool) {
v: f64 = --- v: f64 = ---
v, ok = parse_f64(s) v, ok = parse_f64(s, n)
return f32(v), ok return f32(v), ok
} }
@@ -553,8 +565,9 @@ parse_f32 :: proc(s: string) -> (value: f32, ok: bool) {
// n, ok = strconv.parse_f32("12.34"); // n, ok = strconv.parse_f32("12.34");
// assert(n == 12.34 && ok); // assert(n == 12.34 && ok);
// ``` // ```
parse_f64 :: proc(str: string) -> (value: f64, ok: bool) { parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
s := str s := str
defer if n != nil { n^ = len(str)-len(s) }
if s == "" { if s == "" {
return return
} }
+49 -111
View File
@@ -1,6 +1,6 @@
package strings package strings
import "core:mem" import "core:runtime"
import "core:unicode/utf8" import "core:unicode/utf8"
import "core:strconv" import "core:strconv"
import "core:io" import "core:io"
@@ -17,50 +17,53 @@ Builder :: struct {
} }
// return a builder, default length 0 / cap 16 are done through make // return a builder, default length 0 / cap 16 are done through make
make_builder_none :: proc(allocator := context.allocator) -> Builder { builder_make_none :: proc(allocator := context.allocator) -> Builder {
return Builder{buf=make([dynamic]byte, allocator)} return Builder{buf=make([dynamic]byte, allocator)}
} }
// return a builder, with a set length `len` and cap 16 byte buffer // return a builder, with a set length `len` and cap 16 byte buffer
make_builder_len :: proc(len: int, allocator := context.allocator) -> Builder { builder_make_len :: proc(len: int, allocator := context.allocator) -> Builder {
return Builder{buf=make([dynamic]byte, len, allocator)} return Builder{buf=make([dynamic]byte, len, allocator)}
} }
// return a builder, with a set length `len` byte buffer and a custom `cap` // return a builder, with a set length `len` byte buffer and a custom `cap`
make_builder_len_cap :: proc(len, cap: int, allocator := context.allocator) -> Builder { builder_make_len_cap :: proc(len, cap: int, allocator := context.allocator) -> Builder {
return Builder{buf=make([dynamic]byte, len, cap, allocator)} return Builder{buf=make([dynamic]byte, len, cap, allocator)}
} }
// overload simple `make_builder_*` with or without len / cap parameters // overload simple `builder_make_*` with or without len / cap parameters
make_builder :: proc{ builder_make :: proc{
make_builder_none, builder_make_none,
make_builder_len, builder_make_len,
make_builder_len_cap, builder_make_len_cap,
} }
// initialize a builder, default length 0 / cap 16 are done through make // initialize a builder, default length 0 / cap 16 are done through make
// replaces the existing `buf` // replaces the existing `buf`
init_builder_none :: proc(b: ^Builder, allocator := context.allocator) { builder_init_none :: proc(b: ^Builder, allocator := context.allocator) -> ^Builder {
b.buf = make([dynamic]byte, allocator) b.buf = make([dynamic]byte, allocator)
return b
} }
// initialize a builder, with a set length `len` and cap 16 byte buffer // initialize a builder, with a set length `len` and cap 16 byte buffer
// replaces the existing `buf` // replaces the existing `buf`
init_builder_len :: proc(b: ^Builder, len: int, allocator := context.allocator) { builder_init_len :: proc(b: ^Builder, len: int, allocator := context.allocator) -> ^Builder {
b.buf = make([dynamic]byte, len, allocator) b.buf = make([dynamic]byte, len, allocator)
return b
} }
// initialize a builder, with a set length `len` byte buffer and a custom `cap` // initialize a builder, with a set length `len` byte buffer and a custom `cap`
// replaces the existing `buf` // replaces the existing `buf`
init_builder_len_cap :: proc(b: ^Builder, len, cap: int, allocator := context.allocator) { builder_init_len_cap :: proc(b: ^Builder, len, cap: int, allocator := context.allocator) -> ^Builder {
b.buf = make([dynamic]byte, len, cap, allocator) b.buf = make([dynamic]byte, len, cap, allocator)
return b
} }
// overload simple `init_builder_*` with or without len / ap parameters // overload simple `builder_init_*` with or without len / ap parameters
init_builder :: proc{ builder_init :: proc{
init_builder_none, builder_init_none,
init_builder_len, builder_init_len,
init_builder_len_cap, builder_init_len_cap,
} }
@(private) @(private)
@@ -103,18 +106,18 @@ to_writer :: proc(b: ^Builder) -> io.Writer {
} }
// delete and clear the builder byte buffer content // delete and clear the builder byte buffer content
destroy_builder :: proc(b: ^Builder) { builder_destroy :: proc(b: ^Builder) {
delete(b.buf) delete(b.buf)
clear(&b.buf) clear(&b.buf)
} }
// reserve the builfer byte buffer to a specific cap, when it's higher than before // reserve the builfer byte buffer to a specific cap, when it's higher than before
grow_builder :: proc(b: ^Builder, cap: int) { builder_grow :: proc(b: ^Builder, cap: int) {
reserve(&b.buf, cap) reserve(&b.buf, cap)
} }
// clear the builder byte buffer content // clear the builder byte buffer content
reset_builder :: proc(b: ^Builder) { builder_reset :: proc(b: ^Builder) {
clear(&b.buf) clear(&b.buf)
} }
@@ -129,12 +132,12 @@ reset_builder :: proc(b: ^Builder) {
strings.write_byte(&builder, 'b') -> "ab" strings.write_byte(&builder, 'b') -> "ab"
*/ */
builder_from_bytes :: proc(backing: []byte) -> Builder { builder_from_bytes :: proc(backing: []byte) -> Builder {
s := transmute(mem.Raw_Slice)backing s := transmute(runtime.Raw_Slice)backing
d := mem.Raw_Dynamic_Array{ d := runtime.Raw_Dynamic_Array{
data = s.data, data = s.data,
len = 0, len = 0,
cap = s.len, cap = s.len,
allocator = mem.nil_allocator(), allocator = runtime.nil_allocator(),
} }
return Builder{ return Builder{
buf = transmute([dynamic]byte)d, buf = transmute([dynamic]byte)d,
@@ -165,7 +168,7 @@ builder_space :: proc(b: Builder) -> int {
/* /*
appends a byte to the builder, returns the append diff appends a byte to the builder, returns the append diff
builder := strings.make_builder() builder := strings.builder_make()
strings.write_byte(&builder, 'a') // 1 strings.write_byte(&builder, 'a') // 1
strings.write_byte(&builder, 'b') // 1 strings.write_byte(&builder, 'b') // 1
strings.write_byte(&builder, 'c') // 1 strings.write_byte(&builder, 'c') // 1
@@ -181,7 +184,7 @@ write_byte :: proc(b: ^Builder, x: byte) -> (n: int) {
/* /*
appends a slice of bytes to the builder, returns the append diff appends a slice of bytes to the builder, returns the append diff
builder := strings.make_builder() builder := strings.builder_make()
bytes := [?]byte { 'a', 'b', 'c' } bytes := [?]byte { 'a', 'b', 'c' }
strings.write_bytes(&builder, bytes[:]) // 3 strings.write_bytes(&builder, bytes[:]) // 3
fmt.println(strings.to_string(builder)) // -> abc fmt.println(strings.to_string(builder)) // -> abc
@@ -196,77 +199,46 @@ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
/* /*
appends a single rune into the builder, returns written rune size and an `io.Error` appends a single rune into the builder, returns written rune size and an `io.Error`
builder := strings.make_builder() builder := strings.builder_make()
strings.write_rune_builder(&builder, 'ä') // 2 None strings.write_rune(&builder, 'ä') // 2 None
strings.write_rune_builder(&builder, 'b') // 1 None strings.write_rune(&builder, 'b') // 1 None
strings.write_rune_builder(&builder, 'c') // 1 None strings.write_rune(&builder, 'c') // 1 None
fmt.println(strings.to_string(builder)) // -> äbc fmt.println(strings.to_string(builder)) // -> äbc
*/ */
write_rune_builder :: proc(b: ^Builder, r: rune) -> (int, io.Error) { write_rune :: proc(b: ^Builder, r: rune) -> (int, io.Error) {
return io.write_rune(to_writer(b), r) return io.write_rune(to_writer(b), r)
} }
/* /*
appends a quoted rune into the builder, returns written size appends a quoted rune into the builder, returns written size
builder := strings.make_builder() builder := strings.builder_make()
strings.write_string(&builder, "abc") // 3 strings.write_string(&builder, "abc") // 3
strings.write_quoted_rune_builder(&builder, 'ä') // 4 strings.write_quoted_rune(&builder, 'ä') // 4
strings.write_string(&builder, "abc") // 3 strings.write_string(&builder, "abc") // 3
fmt.println(strings.to_string(builder)) // -> abc'ä'abc fmt.println(strings.to_string(builder)) // -> abc'ä'abc
*/ */
write_quoted_rune_builder :: proc(b: ^Builder, r: rune) -> (n: int) { write_quoted_rune :: proc(b: ^Builder, r: rune) -> (n: int) {
return write_quoted_rune(to_writer(b), r) return io.write_quoted_rune(to_writer(b), r)
} }
@(private)
_write_byte :: proc(w: io.Writer, c: byte) -> int {
err := io.write_byte(w, c)
return 1 if err == nil else 0
}
// writer append a quoted rune into the byte buffer, return the written size
write_quoted_rune :: proc(w: io.Writer, r: rune) -> (n: int) {
quote := byte('\'')
n += _write_byte(w, quote)
buf, width := utf8.encode_rune(r)
if width == 1 && r == utf8.RUNE_ERROR {
n += _write_byte(w, '\\')
n += _write_byte(w, 'x')
n += _write_byte(w, DIGITS_LOWER[buf[0]>>4])
n += _write_byte(w, DIGITS_LOWER[buf[0]&0xf])
} else {
i, _ := io.write_escaped_rune(w, r, quote)
n += i
}
n += _write_byte(w, quote)
return
}
// overload for `write_string_*` variants
write_string :: proc{
write_string_builder,
write_string_writer,
}
/* /*
appends a string to the builder, return the written byte size appends a string to the builder, return the written byte size
builder := strings.make_builder() builder := strings.builder_make()
strings.write_string(&builder, "a") // 1 strings.write_string(&builder, "a") // 1
strings.write_string(&builder, "bc") // 2 strings.write_string(&builder, "bc") // 2
strings.write_string(&builder, "xyz") // 3 strings.write_string(&builder, "xyz") // 3
fmt.println(strings.to_string(builder)) // -> abcxyz fmt.println(strings.to_string(builder)) // -> abcxyz
*/ */
write_string_builder :: proc(b: ^Builder, s: string) -> (n: int) { write_string :: proc(b: ^Builder, s: string) -> (n: int) {
return write_string_writer(to_writer(b), s) n0 := len(b.buf)
append(&b.buf, s)
n1 := len(b.buf)
return n1-n0
} }
// appends a string to the writer
write_string_writer :: proc(w: io.Writer, s: string) -> (n: int) {
n, _ = io.write(w, transmute([]byte)s)
return
}
// pops and returns the last byte in the builder // pops and returns the last byte in the builder
// returns 0 when the builder is empty // returns 0 when the builder is empty
@@ -276,7 +248,7 @@ pop_byte :: proc(b: ^Builder) -> (r: byte) {
} }
r = b.buf[len(b.buf)-1] r = b.buf[len(b.buf)-1]
d := cast(^mem.Raw_Dynamic_Array)&b.buf d := cast(^runtime.Raw_Dynamic_Array)&b.buf
d.len = max(d.len-1, 0) d.len = max(d.len-1, 0)
return return
} }
@@ -289,7 +261,7 @@ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) {
} }
r, width = utf8.decode_last_rune(b.buf[:]) r, width = utf8.decode_last_rune(b.buf[:])
d := cast(^mem.Raw_Dynamic_Array)&b.buf d := cast(^runtime.Raw_Dynamic_Array)&b.buf
d.len = max(d.len-width, 0) d.len = max(d.len-width, 0)
return return
} }
@@ -297,70 +269,36 @@ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) {
@(private) @(private)
DIGITS_LOWER := "0123456789abcdefx" DIGITS_LOWER := "0123456789abcdefx"
// overload for `write_quoted_string_*` variants
write_quoted_string :: proc{
write_quoted_string_builder,
write_quoted_string_writer,
}
/* /*
append a quoted string into the builder, return the written byte size append a quoted string into the builder, return the written byte size
builder := strings.make_builder() builder := strings.builder_make()
strings.write_quoted_string(&builder, "a") // 3 strings.write_quoted_string(&builder, "a") // 3
strings.write_quoted_string(&builder, "bc", '\'') // 4 strings.write_quoted_string(&builder, "bc", '\'') // 4
strings.write_quoted_string(&builder, "xyz") // 5 strings.write_quoted_string(&builder, "xyz") // 5
fmt.println(strings.to_string(builder)) // -> "a"'bc'xyz" fmt.println(strings.to_string(builder)) // -> "a"'bc'xyz"
*/ */
write_quoted_string_builder :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) { write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) {
n, _ = io.write_quoted_string(to_writer(b), str, quote) n, _ = io.write_quoted_string(to_writer(b), str, quote)
return return
} }
@(deprecated="prefer io.write_quoted_string")
write_quoted_string_writer :: proc(w: io.Writer, str: string, quote: byte = '"') -> (n: int) {
n, _ = io.write_quoted_string(w, str, quote)
return
}
// overload for `write_encoded_rune_*`
write_encoded_rune :: proc{
write_encoded_rune_builder,
write_encoded_rune_writer,
}
// appends a rune to the builder, optional `write_quote` boolean tag, returns the written rune size // appends a rune to the builder, optional `write_quote` boolean tag, returns the written rune size
write_encoded_rune_builder :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) { write_encoded_rune :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) {
n, _ = io.write_encoded_rune(to_writer(b), r, write_quote) n, _ = io.write_encoded_rune(to_writer(b), r, write_quote)
return return
} }
@(deprecated="prefer io.write_encoded_rune")
write_encoded_rune_writer :: proc(w: io.Writer, r: rune, write_quote := true) -> (n: int) {
n, _ = io.write_encoded_rune(w, r, write_quote)
return
}
// overload for `write_escaped_rune_*`
write_escaped_rune :: proc{
write_escaped_rune_builder,
write_escaped_rune_writer,
}
// appends a rune to the builder, fully written out in case of escaped runes e.g. '\a' will be written as such // appends a rune to the builder, fully written out in case of escaped runes e.g. '\a' will be written as such
// when `r` and `quote` match and `quote` is `\\` - they will be written as two slashes // when `r` and `quote` match and `quote` is `\\` - they will be written as two slashes
// `html_safe` flag in case the runes '<', '>', '&' should be encoded as digits e.g. `\u0026` // `html_safe` flag in case the runes '<', '>', '&' should be encoded as digits e.g. `\u0026`
write_escaped_rune_builder :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) { write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) {
n, _ = io.write_escaped_rune(to_writer(b), r, quote, html_safe) n, _ = io.write_escaped_rune(to_writer(b), r, quote, html_safe)
return return
} }
@(deprecated="prefer io.write_escaped_rune")
write_escaped_rune_writer :: proc(w: io.Writer, r: rune, quote: byte, html_safe := false) -> (n: int) {
n, _ = io.write_escaped_rune(w, r, quote, html_safe)
return
}
// writes a u64 value `i` in `base` = 10 into the builder, returns the written amount of characters // writes a u64 value `i` in `base` = 10 into the builder, returns the written amount of characters
write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) { write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) {
buf: [32]byte buf: [32]byte
+10 -10
View File
@@ -10,7 +10,7 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) ->
} }
b: Builder b: Builder
init_builder(&b, 0, 0, allocator) builder_init(&b, 0, 0, allocator)
s := s s := s
for c, i in s { for c, i in s {
@@ -20,7 +20,7 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) ->
_, w := utf8.decode_rune_in_string(s[i:]) _, w := utf8.decode_rune_in_string(s[i:])
if w == 1 { if w == 1 {
grow_builder(&b, len(s) + len(replacement)) builder_grow(&b, len(s) + len(replacement))
write_string(&b, s[:i]) write_string(&b, s[:i])
s = s[i:] s = s[i:]
break break
@@ -67,9 +67,9 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) ->
*/ */
to_lower :: proc(s: string, allocator := context.allocator) -> string { to_lower :: proc(s: string, allocator := context.allocator) -> string {
b: Builder b: Builder
init_builder(&b, 0, len(s), allocator) builder_init(&b, 0, len(s), allocator)
for r in s { for r in s {
write_rune_builder(&b, unicode.to_lower(r)) write_rune(&b, unicode.to_lower(r))
} }
return to_string(b) return to_string(b)
} }
@@ -83,9 +83,9 @@ to_lower :: proc(s: string, allocator := context.allocator) -> string {
*/ */
to_upper :: proc(s: string, allocator := context.allocator) -> string { to_upper :: proc(s: string, allocator := context.allocator) -> string {
b: Builder b: Builder
init_builder(&b, 0, len(s), allocator) builder_init(&b, 0, len(s), allocator)
for r in s { for r in s {
write_rune_builder(&b, unicode.to_upper(r)) write_rune(&b, unicode.to_upper(r))
} }
return to_string(b) return to_string(b)
} }
@@ -147,7 +147,7 @@ to_camel_case :: proc(s: string, allocator := context.allocator) -> string {
s := s s := s
s = trim_space(s) s = trim_space(s)
b: Builder b: Builder
init_builder(&b, 0, len(s), allocator) builder_init(&b, 0, len(s), allocator)
w := to_writer(&b) w := to_writer(&b)
string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) { string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) {
@@ -172,7 +172,7 @@ to_pascal_case :: proc(s: string, allocator := context.allocator) -> string {
s := s s := s
s = trim_space(s) s = trim_space(s)
b: Builder b: Builder
init_builder(&b, 0, len(s), allocator) builder_init(&b, 0, len(s), allocator)
w := to_writer(&b) w := to_writer(&b)
string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) { string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) {
@@ -203,7 +203,7 @@ to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allo
s := s s := s
s = trim_space(s) s = trim_space(s)
b: Builder b: Builder
init_builder(&b, 0, len(s), allocator) builder_init(&b, 0, len(s), allocator)
w := to_writer(&b) w := to_writer(&b)
adjust_case := unicode.to_upper if all_upper_case else unicode.to_lower adjust_case := unicode.to_upper if all_upper_case else unicode.to_lower
@@ -272,7 +272,7 @@ to_ada_case :: proc(s: string, allocator := context.allocator) -> string {
s := s s := s
s = trim_space(s) s = trim_space(s)
b: Builder b: Builder
init_builder(&b, 0, len(s), allocator) builder_init(&b, 0, len(s), allocator)
w := to_writer(&b) w := to_writer(&b)
prev, curr: rune prev, curr: rune
+13 -12
View File
@@ -1,6 +1,6 @@
package strings package strings
import "core:mem" import "core:runtime"
// custom string entry struct // custom string entry struct
Intern_Entry :: struct { Intern_Entry :: struct {
@@ -11,7 +11,7 @@ Intern_Entry :: struct {
// "intern" is a more memory efficient string map // "intern" is a more memory efficient string map
// `allocator` is used to allocate the actual `Intern_Entry` strings // `allocator` is used to allocate the actual `Intern_Entry` strings
Intern :: struct { Intern :: struct {
allocator: mem.Allocator, allocator: runtime.Allocator,
entries: map[string]^Intern_Entry, entries: map[string]^Intern_Entry,
} }
@@ -31,30 +31,31 @@ intern_destroy :: proc(m: ^Intern) {
// returns the `text` string from the intern map - gets set if it didnt exist yet // returns the `text` string from the intern map - gets set if it didnt exist yet
// the returned string lives as long as the map entry lives // the returned string lives as long as the map entry lives
intern_get :: proc(m: ^Intern, text: string) -> string { intern_get :: proc(m: ^Intern, text: string) -> (str: string, err: runtime.Allocator_Error) {
entry := _intern_get_entry(m, text) entry := _intern_get_entry(m, text) or_return
#no_bounds_check return string(entry.str[:entry.len]) #no_bounds_check return string(entry.str[:entry.len]), nil
} }
// returns the `text` cstring from the intern map - gets set if it didnt exist yet // returns the `text` cstring from the intern map - gets set if it didnt exist yet
// the returned cstring lives as long as the map entry lives // the returned cstring lives as long as the map entry lives
intern_get_cstring :: proc(m: ^Intern, text: string) -> cstring { intern_get_cstring :: proc(m: ^Intern, text: string) -> (str: cstring, err: runtime.Allocator_Error) {
entry := _intern_get_entry(m, text) entry := _intern_get_entry(m, text) or_return
return cstring(&entry.str[0]) return cstring(&entry.str[0]), nil
} }
// looks up wether the `text` string exists in the map, returns the entry // looks up wether the `text` string exists in the map, returns the entry
// sets & allocates the entry if it wasnt set yet // sets & allocates the entry if it wasnt set yet
_intern_get_entry :: proc(m: ^Intern, text: string) -> ^Intern_Entry #no_bounds_check { _intern_get_entry :: proc(m: ^Intern, text: string) -> (new_entry: ^Intern_Entry, err: runtime.Allocator_Error) #no_bounds_check {
if prev, ok := m.entries[text]; ok { if prev, ok := m.entries[text]; ok {
return prev return prev, nil
} }
if m.allocator.procedure == nil { if m.allocator.procedure == nil {
m.allocator = context.allocator m.allocator = context.allocator
} }
entry_size := int(offset_of(Intern_Entry, str)) + len(text) + 1 entry_size := int(offset_of(Intern_Entry, str)) + len(text) + 1
new_entry := (^Intern_Entry)(mem.alloc(entry_size, align_of(Intern_Entry), m.allocator)) bytes := runtime.mem_alloc(entry_size, align_of(Intern_Entry), m.allocator) or_return
new_entry = (^Intern_Entry)(raw_data(bytes))
new_entry.len = len(text) new_entry.len = len(text)
copy(new_entry.str[:new_entry.len], text) copy(new_entry.str[:new_entry.len], text)
@@ -62,5 +63,5 @@ _intern_get_entry :: proc(m: ^Intern, text: string) -> ^Intern_Entry #no_bounds_
key := string(new_entry.str[:new_entry.len]) key := string(new_entry.str[:new_entry.len])
m.entries[key] = new_entry m.entries[key] = new_entry
return new_entry return new_entry, nil
} }
+40 -8
View File
@@ -215,6 +215,38 @@ equal_fold :: proc(u, v: string) -> bool {
return s == t return s == t
} }
/*
return the prefix length common between strings `a` and `b`.
strings.prefix_length("testing", "test") -> 4
strings.prefix_length("testing", "te") -> 2
strings.prefix_length("telephone", "te") -> 2
strings.prefix_length("testing", "est") -> 0
*/
prefix_length :: proc(a, b: string) -> (n: int) {
_len := min(len(a), len(b))
// Scan for matches including partial codepoints.
#no_bounds_check for n < _len && a[n] == b[n] {
n += 1
}
// Now scan to ignore partial codepoints.
if n > 0 {
s := a[:n]
n = 0
for {
r0, w := utf8.decode_rune(s[n:])
if r0 != utf8.RUNE_ERROR {
n += w
} else {
break
}
}
}
return
}
/* /*
return true when the string `prefix` is contained at the start of the string `s` return true when the string `prefix` is contained at the start of the string `s`
@@ -1521,7 +1553,7 @@ split_multi_iterate :: proc(using sm: ^Split_Multi) -> (res: string, ok: bool) #
scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> string { scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> string {
str := s str := s
b: Builder b: Builder
init_builder(&b, 0, len(s), allocator) builder_init(&b, 0, len(s), allocator)
has_error := false has_error := false
cursor := 0 cursor := 0
@@ -1590,7 +1622,7 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) ->
} }
b: Builder b: Builder
init_builder(&b, allocator) builder_init(&b, allocator)
writer := to_writer(&b) writer := to_writer(&b)
str := s str := s
column: int column: int
@@ -1658,8 +1690,8 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte
pad_len := rune_count(pad) pad_len := rune_count(pad)
b: Builder b: Builder
init_builder(&b, allocator) builder_init(&b, allocator)
grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)) builder_grow(&b, len(str) + (remains/pad_len + 1)*len(pad))
w := to_writer(&b) w := to_writer(&b)
@@ -1681,8 +1713,8 @@ left_justify :: proc(str: string, length: int, pad: string, allocator := context
pad_len := rune_count(pad) pad_len := rune_count(pad)
b: Builder b: Builder
init_builder(&b, allocator) builder_init(&b, allocator)
grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)) builder_grow(&b, len(str) + (remains/pad_len + 1)*len(pad))
w := to_writer(&b) w := to_writer(&b)
@@ -1703,8 +1735,8 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex
pad_len := rune_count(pad) pad_len := rune_count(pad)
b: Builder b: Builder
init_builder(&b, allocator) builder_init(&b, allocator)
grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)) builder_grow(&b, len(str) + (remains/pad_len + 1)*len(pad))
w := to_writer(&b) w := to_writer(&b)
+8 -12
View File
@@ -3,24 +3,22 @@
package sync package sync
import "core:c" import "core:c"
import "core:os"
import "core:time" import "core:time"
UMTX_OP_WAIT :: 2 UMTX_OP_WAIT :: 2
UMTX_OP_WAKE :: 3 UMTX_OP_WAKE :: 3
ETIMEDOUT :: 60
foreign import libc "system:c" foreign import libc "system:c"
foreign libc { foreign libc {
_umtx_op :: proc "c" (obj: rawptr, op: c.int, val: c.ulong, uaddr: rawptr, uaddr2: rawptr) -> c.int --- _umtx_op :: proc "c" (obj: rawptr, op: c.int, val: c.ulong, uaddr: rawptr, uaddr2: rawptr) -> c.int ---
__error :: proc "c" () -> ^c.int ---
} }
_futex_wait :: proc(f: ^Futex, expected: u32) -> bool { _futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
timeout := os.Unix_File_Time{ timeout := [2]i64{14400, 0} // 4 hours
seconds = 5,
nanoseconds = 0,
}
for { for {
res := _umtx_op(f, UMTX_OP_WAIT, c.ulong(expected), nil, &timeout) res := _umtx_op(f, UMTX_OP_WAIT, c.ulong(expected), nil, &timeout)
@@ -28,7 +26,7 @@ _futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
return true return true
} }
if os.Errno(os.get_last_error()) == os.ETIMEDOUT { if __error()^ == ETIMEDOUT {
continue continue
} }
@@ -42,16 +40,14 @@ _futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Durati
return false return false
} }
res := _umtx_op(f, UMTX_OP_WAIT, c.ulong(expected), nil, &os.Unix_File_Time{ timeout := [2]i64{i64(duration/1e9), i64(duration%1e9)}
seconds = (os.time_t)(duration/1e9),
nanoseconds = (c.long)(duration%1e9),
})
res := _umtx_op(f, UMTX_OP_WAIT, c.ulong(expected), nil, &timeout)
if res != -1 { if res != -1 {
return true return true
} }
if os.Errno(os.get_last_error()) == os.ETIMEDOUT { if __error()^ == ETIMEDOUT {
return false return false
} }
+24 -2
View File
@@ -15,18 +15,40 @@ foreign import Ntdll "system:Ntdll.lib"
@(default_calling_convention="stdcall") @(default_calling_convention="stdcall")
foreign Ntdll { foreign Ntdll {
RtlWaitOnAddress :: proc(Address: rawptr, CompareAddress: rawptr, AddressSize: uint, Timeout: ^i64) -> i32 --- RtlWaitOnAddress :: proc(Address: rawptr, CompareAddress: rawptr, AddressSize: uint, Timeout: ^i64) -> i32 ---
RtlNtStatusToDosError :: proc(status: i32) -> u32 ---
SetLastError :: proc(err: u32) ---
} }
/*
NOTE(bill, 2022-08-17)
WaitOnAddress is implemented on top of RtlWaitOnAddress
BUT requires taking the return value of it and if it is non-zero
converting that status to a DOS error and then SetLastError
If this is not done, then things don't work as expected when
and error occurs
GODDAMN MICROSOFT!
*/
CustomWaitOnAddress :: proc "stdcall" (Address: rawptr, CompareAddress: rawptr, AddressSize: uint, Timeout: ^i64) -> bool {
status := RtlWaitOnAddress(Address, CompareAddress, AddressSize, Timeout)
if status != 0 {
SetLastError(RtlNtStatusToDosError(status))
}
return status == 0
}
_futex_wait :: proc(f: ^Futex, expect: u32) -> bool { _futex_wait :: proc(f: ^Futex, expect: u32) -> bool {
expect := expect expect := expect
return 0 == RtlWaitOnAddress(f, &expect, size_of(expect), nil) return CustomWaitOnAddress(f, &expect, size_of(expect), nil)
} }
_futex_wait_with_timeout :: proc(f: ^Futex, expect: u32, duration: time.Duration) -> bool { _futex_wait_with_timeout :: proc(f: ^Futex, expect: u32, duration: time.Duration) -> bool {
expect := expect expect := expect
// NOTE(bill): for some bizarre reason, this has be a negative number // NOTE(bill): for some bizarre reason, this has be a negative number
timeout := -i64(duration / 100) timeout := -i64(duration / 100)
return 0 == RtlWaitOnAddress(f, &expect, size_of(expect), &timeout) return CustomWaitOnAddress(f, &expect, size_of(expect), &timeout)
} }
_futex_signal :: proc(f: ^Futex) { _futex_signal :: proc(f: ^Futex) {
+8 -2
View File
@@ -2,8 +2,14 @@
//+private //+private
package sync package sync
import "core:os" import "core:c"
foreign import dl "system:dl"
foreign dl {
pthread_getthreadid_np :: proc "c" () -> c.int ---
}
_current_thread_id :: proc "contextless" () -> int { _current_thread_id :: proc "contextless" () -> int {
return os.current_thread_id() return int(pthread_getthreadid_np())
} }
+3 -3
View File
@@ -1568,7 +1568,7 @@ sys_gettid :: proc "contextless" () -> int {
} }
sys_getrandom :: proc "contextless" (buf: [^]byte, buflen: int, flags: uint) -> int { sys_getrandom :: proc "contextless" (buf: [^]byte, buflen: int, flags: uint) -> int {
return cast(int)intrinsics.syscall(SYS_getrandom, buf, cast(uintptr)(buflen), cast(uintptr)(flags)) return cast(int)intrinsics.syscall(SYS_getrandom, uintptr(buf), uintptr(buflen), uintptr(flags))
} }
sys_open :: proc "contextless" (path: cstring, flags: int, mode: int = 0o000) -> int { sys_open :: proc "contextless" (path: cstring, flags: int, mode: int = 0o000) -> int {
@@ -1622,7 +1622,7 @@ sys_lseek :: proc "contextless" (fd: int, offset: i64, whence: int) -> i64 {
low := uintptr(offset & 0xFFFFFFFF) low := uintptr(offset & 0xFFFFFFFF)
high := uintptr(offset >> 32) high := uintptr(offset >> 32)
result: i64 result: i64
res := i64(intrinsics.syscall(SYS__llseek, uintptr(fd), high, low, &result, uintptr(whence))) res := i64(intrinsics.syscall(SYS__llseek, uintptr(fd), high, low, uintptr(&result), uintptr(whence)))
return res if res < 0 else result return res if res < 0 else result
} }
} }
@@ -1748,7 +1748,7 @@ sys_unlink :: proc "contextless" (path: cstring) -> int {
} }
sys_unlinkat :: proc "contextless" (dfd: int, path: cstring, flag: int = 0) -> int { sys_unlinkat :: proc "contextless" (dfd: int, path: cstring, flag: int = 0) -> int {
return int(intrinsics.syscall(SYS_unlinkat, uintptr(dfd), uintptr(rawptr(path)), flag)) return int(intrinsics.syscall(SYS_unlinkat, uintptr(dfd), uintptr(rawptr(path)), uintptr(flag)))
} }
sys_rmdir :: proc "contextless" (path: cstring) -> int { sys_rmdir :: proc "contextless" (path: cstring) -> int {
+63
View File
@@ -0,0 +1,63 @@
//+build amd64
package sys_valgrind
import "core:intrinsics"
Callgrind_Client_Request :: enum uintptr {
Dump_Stats = 'C'<<24 | 'T'<<16,
Zero_Stats,
Toggle_Collect,
Dump_Stats_At,
Start_Instrumentation,
Stop_Instrumentation,
}
@(require_results)
callgrind_client_request_expr :: proc "c" (default: uintptr, request: Callgrind_Client_Request, a0, a1, a2, a3, a4: uintptr) -> uintptr {
return intrinsics.valgrind_client_request(default, uintptr(request), a0, a1, a2, a3, a4)
}
callgrind_client_request_stmt :: proc "c" (request: Callgrind_Client_Request, a0, a1, a2, a3, a4: uintptr) {
_ = intrinsics.valgrind_client_request(0, uintptr(request), a0, a1, a2, a3, a4)
}
// Dump current state of cost centres, and zero them afterwards.
dump_stats :: proc "c" () {
callgrind_client_request_stmt(.Dump_Stats, 0, 0, 0, 0, 0)
}
// Zero cost centres
zero_stats :: proc "c" () {
callgrind_client_request_stmt(.Zero_Stats, 0, 0, 0, 0, 0)
}
// Toggles collection state.
// The collection state specifies whether the happening of events should be noted or
// if they are to be ignored. Events are noted by increment of counters in a cost centre.
toggle_collect :: proc "c" () {
callgrind_client_request_stmt(.Toggle_Collect, 0, 0, 0, 0, 0)
}
// Dump current state of cost centres, and zero them afterwards.
// The argument is appended to a string stating the reason which triggered
// the dump. This string is written as a description field into the
// profile data dump.
dump_stats_at :: proc "c" (pos_str: rawptr) {
callgrind_client_request_stmt(.Dump_Stats_At, uintptr(pos_str), 0, 0, 0, 0)
}
// Start full callgrind instrumentation if not already switched on.
// When cache simulation is done, it will flush the simulated cache;
// this will lead to an artificial cache warmup phase afterwards with
// cache misses which would not have happened in reality.
start_instrumentation :: proc "c" () {
callgrind_client_request_stmt(.Start_Instrumentation, 0, 0, 0, 0, 0)
}
// Stop full callgrind instrumentation if not already switched off.
// This flushes Valgrinds translation cache, and does no additional instrumentation
// afterwards, which effectivly will run at the same speed as the "none" tool (ie. at minimal slowdown).
// Use this to bypass Callgrind aggregation for uninteresting code parts.
// To start Callgrind in this mode to ignore the setup phase, use the option "--instr-atstart=no".
stop_instrumentation :: proc "c" () {
callgrind_client_request_stmt(.Stop_Instrumentation, 0, 0, 0, 0, 0)
}
+169
View File
@@ -0,0 +1,169 @@
//+build amd64
package sys_valgrind
import "core:intrinsics"
Mem_Check_Client_Request :: enum uintptr {
Make_Mem_No_Access = 'M'<<24 | 'C'<<16,
Make_Mem_Undefined,
Make_Mem_Defined,
Discard,
Check_Mem_Is_Addressable,
Check_Mem_Is_Defined,
Do_Leak_Check,
Count_Leaks,
Get_Vbits,
Set_Vbits,
Create_Block,
Make_Mem_Defined_If_Addressable,
Count_Leak_Blocks,
Enable_Addr_Error_Reporting_In_Range,
Disable_Addr_Error_Reporting_In_Range,
}
@(require_results)
mem_check_client_request_expr :: proc "c" (default: uintptr, request: Mem_Check_Client_Request, a0, a1, a2, a3, a4: uintptr) -> uintptr {
return intrinsics.valgrind_client_request(default, uintptr(request), a0, a1, a2, a3, a4)
}
mem_check_client_request_stmt :: proc "c" (request: Mem_Check_Client_Request, a0, a1, a2, a3, a4: uintptr) {
_ = intrinsics.valgrind_client_request(0, uintptr(request), a0, a1, a2, a3, a4)
}
// Mark memory at `raw_data(qzz)` as unaddressable for `len(qzz)` bytes.
// Returns true when run on Valgrind and false otherwise.
make_mem_no_access :: proc "c" (qzz: []byte) -> bool {
return 0 != mem_check_client_request_expr(0, .Make_Mem_No_Access, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
}
// Mark memory at `raw_data(qzz)` as addressable but undefined for `len(qzz)` bytes.
// Returns true when run on Valgrind and false otherwise.
make_mem_undefined :: proc "c" (qzz: []byte) -> bool {
return 0 != mem_check_client_request_expr(0, .Make_Mem_Undefined, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
}
// Mark memory at `raw_data(qzz)` as addressable for `len(qzz)` bytes.
// Returns true when run on Valgrind and false otherwise.
make_mem_defined :: proc "c" (qzz: []byte) -> bool {
return 0 != mem_check_client_request_expr(0, .Make_Mem_Defined, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
}
// Check that memory at `raw_data(qzz)` is addressable for `len(qzz)` bytes.
// If suitable addressibility is not established, Valgrind prints an error
// message and returns the address of the first offending byte.
// Otherwise it returns zero.
check_mem_is_addressable :: proc "c" (qzz: []byte) -> uintptr {
return mem_check_client_request_expr(0, .Check_Mem_Is_Addressable, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
}
// Check that memory at `raw_data(qzz)` is addressable and defined for `len(qzz)` bytes.
// If suitable addressibility and definedness are not established,
// Valgrind prints an error message and returns the address of the first
// offending byte. Otherwise it returns zero.
check_mem_is_defined :: proc "c" (qzz: []byte) -> uintptr {
return mem_check_client_request_expr(0, .Check_Mem_Is_Defined, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
}
// Similar to `make_mem_defined(qzz)` except that addressability is not altered:
// bytes which are addressable are marked as defined, but those which
// are not addressable are left unchanged.
// Returns true when run on Valgrind and false otherwise.
make_mem_defined_if_addressable :: proc "c" (qzz: []byte) -> bool {
return 0 != mem_check_client_request_expr(0, .Make_Mem_Defined_If_Addressable, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
}
// Create a block-description handle.
// The description is an ascii string which is included in any messages
// pertaining to addresses within the specified memory range.
// Has no other effect on the properties of the memory range.
create_block :: proc "c" (qzz: []u8, desc: cstring) -> bool {
return 0 != mem_check_client_request_expr(0, .Create_Block, uintptr(raw_data(qzz)), uintptr(len(qzz)), uintptr(rawptr(desc)), 0, 0)
}
// Discard a block-description-handle. Returns true for an invalid handle, false for a valid handle.
discard :: proc "c" (blk_index: uintptr) -> bool {
return 0 != mem_check_client_request_expr(0, .Discard, 0, blk_index, 0, 0, 0)
}
// Do a full memory leak check (like `--leak-check=full`) mid-execution.
leak_check :: proc "c" () {
mem_check_client_request_stmt(.Do_Leak_Check, 0, 0, 0, 0, 0)
}
// Same as `leak_check()` but only showing the entries for which there was an increase
// in leaked bytes or leaked nr of blocks since the previous leak search.
added_leak_check :: proc "c" () {
mem_check_client_request_stmt(.Do_Leak_Check, 0, 1, 0, 0, 0)
}
// Same as `added_leak_check()` but showing entries with increased or decreased
// leaked bytes/blocks since previous leak search.
changed_leak_check :: proc "c" () {
mem_check_client_request_stmt(.Do_Leak_Check, 0, 2, 0, 0, 0)
}
// Do a summary memory leak check (like `--leak-check=summary`) mid-execution.
quick_leak_check :: proc "c" () {
mem_check_client_request_stmt(.Do_Leak_Check, 1, 0, 0, 0, 0)
}
Count_Result :: struct {
leaked: uint,
dubious: uint,
reachable: uint,
suppressed: uint,
}
count_leaks :: proc "c" () -> (res: Count_Result) {
mem_check_client_request_stmt(
.Count_Leaks,
uintptr(&res.leaked),
uintptr(&res.dubious),
uintptr(&res.reachable),
uintptr(&res.suppressed),
0,
)
return
}
count_leak_blocks :: proc "c" () -> (res: Count_Result) {
mem_check_client_request_stmt(
.Count_Leak_Blocks,
uintptr(&res.leaked),
uintptr(&res.dubious),
uintptr(&res.reachable),
uintptr(&res.suppressed),
0,
)
return
}
// Get the validity data for addresses zza and copy it
// into the provided zzvbits array. Return values:
// 0 - if not running on valgrind
// 1 - success
// 2 - [previously indicated unaligned arrays; these are now allowed]
// 3 - if any parts of zzsrc/zzvbits are not addressable.
// The metadata is not copied in cases 0, 2 or 3 so it should be
// impossible to segfault your system by using this call.
get_vbits :: proc(zza, zzvbits: []byte) -> u8 {
// assert requires a `context` thus these procedures cannot `proc "c"`
assert(len(zzvbits) >= len(zza)/8)
return u8(mem_check_client_request_expr(0, .Get_Vbits, uintptr(raw_data(zza)), uintptr(raw_data(zzvbits)), uintptr(len(zza)), 0, 0))
}
// Set the validity data for addresses zza, copying it
// from the provided zzvbits array. Return values:
// 0 - if not running on valgrind
// 1 - success
// 2 - [previously indicated unaligned arrays; these are now allowed]
// 3 - if any parts of zza/zzvbits are not addressable.
// The metadata is not copied in cases 0, 2 or 3 so it should be
// impossible to segfault your system by using this call.
set_vbits :: proc(zzvbits, zza: []byte) -> u8 {
// assert requires a `context` thus these procedures cannot `proc "c"`
assert(len(zzvbits) >= len(zza)/8)
return u8(mem_check_client_request_expr(0, .Set_Vbits, uintptr(raw_data(zza)), uintptr(raw_data(zzvbits)), uintptr(len(zza)), 0, 0))
}
// (Re-)enable reporting of addressing errors in the specified address range.
enable_addr_error_reporting_in_range :: proc "c" (qzz: []byte) -> uintptr {
return mem_check_client_request_expr(0, .Enable_Addr_Error_Reporting_In_Range, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
}
// Disable reporting of addressing errors in the specified address range.
disable_addr_error_reporting_in_range :: proc "c" (qzz: []byte) -> uintptr {
return mem_check_client_request_expr(0, .Disable_Addr_Error_Reporting_In_Range, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
}
+182
View File
@@ -0,0 +1,182 @@
//+build amd64
package sys_valgrind
import "core:intrinsics"
Client_Request :: enum uintptr {
Running_On_Valgrind = 4097,
Discard_Translations = 4098,
Client_Call0 = 4353,
Client_Call1 = 4354,
Client_Call2 = 4355,
Client_Call3 = 4356,
Count_Errors = 4609,
Gdb_Monitor_Command = 4610,
Malloc_Like_Block = 4865,
Resize_Inplace_Block = 4875,
Free_Like_Block = 4866,
Create_Mem_Pool = 4867,
Destroy_Mem_Pool = 4868,
Mem_Pool_Alloc = 4869,
Mem_Pool_Free = 4870,
Mem_Pool_Trim = 4871,
Move_Mem_Pool = 4872,
Mem_Pool_Change = 4873,
Mem_Pool_Exists = 4874,
Printf = 5121,
Printf_Backtrace = 5122,
Printf_Valist_By_Ref = 5123,
Printf_Backtrace_Valist_By_Ref = 5124,
Stack_Register = 5377,
Stack_Deregister = 5378,
Stack_Change = 5379,
Load_Pdb_Debug_Info = 5633,
Map_Ip_To_Src_Loc = 5889,
Change_Err_Disablement = 6145,
Vex_Init_For_Iri = 6401,
Inner_Threads = 6402,
}
@(require_results)
client_request_expr :: proc "c" (default: uintptr, request: Client_Request, a0, a1, a2, a3, a4: uintptr) -> uintptr {
return intrinsics.valgrind_client_request(default, uintptr(request), a0, a1, a2, a3, a4)
}
client_request_stmt :: proc "c" (request: Client_Request, a0, a1, a2, a3, a4: uintptr) {
_ = intrinsics.valgrind_client_request(0, uintptr(request), a0, a1, a2, a3, a4)
}
// Returns the number of Valgrinds this code is running under
// 0 - running natively
// 1 - running under Valgrind
// 2 - running under Valgrind which is running under another Valgrind
running_on_valgrind :: proc "c" () -> uintptr {
return client_request_expr(0, .Running_On_Valgrind, 0, 0, 0, 0, 0)
}
// Discard translation of code in the slice qzz. Useful if you are debugging a JIT-er or some such,
// since it provides a way to make sure valgrind will retranslate the invalidated area.
discard_translations :: proc "c" (qzz: []byte) {
client_request_stmt(.Discard_Translations, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
}
non_simd_call0 :: proc "c" (p: proc "c" (uintptr) -> uintptr) -> uintptr {
return client_request_expr(0, .Client_Call0, uintptr(rawptr(p)), 0, 0, 0, 0)
}
non_simd_call1 :: proc "c" (p: proc "c" (uintptr, uintptr) -> uintptr, a0: uintptr) -> uintptr {
return client_request_expr(0, .Client_Call1, uintptr(rawptr(p)), a0, 0, 0, 0)
}
non_simd_call2 :: proc "c" (p: proc "c" (uintptr, uintptr, uintptr) -> uintptr, a0, a1: uintptr) -> uintptr {
return client_request_expr(0, .Client_Call2, uintptr(rawptr(p)), a0, a1, 0, 0)
}
non_simd_call3 :: proc "c" (p: proc "c" (uintptr, uintptr, uintptr, uintptr) -> uintptr, a0, a1, a2: uintptr) -> uintptr {
return client_request_expr(0, .Client_Call3, uintptr(rawptr(p)), a0, a1, a2, 0)
}
// Counts the number of errors that have been recorded by a tool.
count_errrors :: proc "c" () -> uint {
return uint(client_request_expr(0, .Count_Errors, 0, 0, 0, 0, 0))
}
monitor_command :: proc "c" (command: cstring) -> bool {
return 0 != client_request_expr(0, .Gdb_Monitor_Command, uintptr(rawptr(command)), 0, 0, 0, 0)
}
malloc_like_block :: proc "c" (mem: []byte, rz_b: uintptr, is_zeroed: bool) {
client_request_stmt(.Malloc_Like_Block, uintptr(raw_data(mem)), uintptr(len(mem)), rz_b, uintptr(is_zeroed), 0)
}
resize_inplace_block :: proc "c" (old_mem: []byte, new_size: uint, rz_b: uintptr) {
client_request_stmt(.Resize_Inplace_Block, uintptr(raw_data(old_mem)), uintptr(len(old_mem)), uintptr(new_size), rz_b, 0)
}
free_like_block :: proc "c" (addr: rawptr, rz_b: uintptr) {
client_request_stmt(.Free_Like_Block, uintptr(addr), rz_b, 0, 0, 0)
}
Mem_Pool_Flags :: distinct bit_set[Mem_Pool_Flag; uintptr]
Mem_Pool_Flag :: enum uintptr {
Auto_Free = 0,
Meta_Pool = 1,
}
// Create a memory pool.
create_mem_pool :: proc "c" (pool: rawptr, rz_b: uintptr, is_zeroed: bool, flags: Mem_Pool_Flags) {
client_request_stmt(.Create_Mem_Pool, uintptr(pool), rz_b, uintptr(is_zeroed), transmute(uintptr)flags, 0)
}
// Destroy a memory pool.
destroy_mem_pool :: proc "c" (pool: rawptr) {
client_request_stmt(.Destroy_Mem_Pool, uintptr(pool), 0, 0, 0, 0)
}
// Associate a section of memory with a memory pool.
mem_pool_alloc :: proc "c" (pool: rawptr, mem: []byte) {
client_request_stmt(.Mem_Pool_Alloc, uintptr(pool), uintptr(raw_data(mem)), uintptr(len(mem)), 0, 0)
}
// Disassociate a section of memory from a memory pool.
mem_pool_free :: proc "c" (pool: rawptr, addr: rawptr) {
client_request_stmt(.Mem_Pool_Free, uintptr(pool), uintptr(addr), 0, 0, 0)
}
// Disassociate parts of a section of memory outside a particular range.
mem_pool_trim :: proc "c" (pool: rawptr, mem: []byte) {
client_request_stmt(.Mem_Pool_Trim, uintptr(pool), uintptr(raw_data(mem)), uintptr(len(mem)), 0, 0)
}
// Resize and/or move a section of memory associated with a memory pool.
move_mem_pool :: proc "c" (pool_a, pool_b: rawptr) {
client_request_stmt(.Move_Mem_Pool, uintptr(pool_a), uintptr(pool_b), 0, 0, 0)
}
// Resize and/or move a section of memory associated with a memory pool.
mem_pool_change :: proc "c" (pool: rawptr, addr_a: rawptr, mem: []byte) {
client_request_stmt(.Mem_Pool_Change, uintptr(pool), uintptr(addr_a), uintptr(raw_data(mem)), uintptr(len(mem)), 0)
}
// Return true if a memory pool exists
mem_pool_exists :: proc "c" (pool: rawptr) -> bool {
return 0 != client_request_expr(0, .Mem_Pool_Exists, uintptr(pool), 0, 0, 0, 0)
}
// Mark a section of memory as being a stack. Returns a stack id.
stack_register :: proc "c" (stack: []byte) -> (stack_id: uintptr) {
ptr := uintptr(raw_data(stack))
return client_request_expr(0, .Stack_Register, ptr, ptr+uintptr(len(stack)), 0, 0, 0)
}
// Unmark a section of memory associated with a stack id as being a stack.
stack_deregister :: proc "c" (id: uintptr) {
client_request_stmt(.Stack_Deregister, id, 0, 0, 0, 0)
}
// Change the start and end address of the stack id with the `new_stack` slice.
stack_change :: proc "c" (id: uint, new_stack: []byte) {
ptr := uintptr(raw_data(new_stack))
client_request_stmt(.Stack_Change, uintptr(id), ptr, ptr + uintptr(len(new_stack)), 0, 0)
}
// Disable error reporting for the current thread/
// It behaves in a stack-like way, meaning you can safely call this multiple times
// given that `enable_error_reporting()` is called the same number of times to
// re-enable the error reporting.
// The first call of this macro disables reporting.
// Subsequent calls have no effect except to increase the number of `enable_error_reporting()`
// calls needed to re-enable reporting.
// Child threads do not inherit this setting from their parents;
// they are always created with reporting enabled.
disable_error_reporting :: proc "c" () {
client_request_stmt(.Change_Err_Disablement, 1, 0, 0, 0, 0)
}
// Re-enable error reporting
enable_error_reporting :: proc "c" () {
client_request_stmt(.Change_Err_Disablement, ~uintptr(0), 0, 0, 0, 0)
}
inner_threads :: proc "c" (qzz: rawptr) {
client_request_stmt(.Inner_Threads, uintptr(qzz), 0, 0, 0, 0)
}
// Map a code address to a source file name and line number.
// `buf64` must point to a 64-byte buffer in the caller's address space.
// The result will be dumped in there and is guaranteed to be zero terminated.
// If no info is found, the first byte is set to zero.
map_ip_to_src_loc :: proc "c" (addr: rawptr, buf64: ^[64]byte) -> uintptr {
return client_request_expr(0, .Map_Ip_To_Src_Loc, uintptr(addr), uintptr(buf64), 0, 0, 0)
}
+171 -259
View File
@@ -1148,6 +1148,156 @@ foreign wasi {
*/ */
how: sdflags_t, how: sdflags_t,
) -> errno_t --- ) -> errno_t ---
/**
* Return a description of the given preopened file descriptor.
*/
fd_prestat_dir_name :: proc(
fd: fd_t,
/**
* A buffer into which to write the preopened directory name.
*/
path: string,
) -> errno_t ---
/**
* Create a directory.
* Note: This is similar to `mkdirat` in POSIX.
*/
path_create_directory :: proc(
fd: fd_t,
/**
* The path at which to create the directory.
*/
path: string,
) -> errno_t ---
/**
* Adjust the timestamps of a file or directory.
* Note: This is similar to `utimensat` in POSIX.
*/
path_filestat_set_times :: proc(
fd: fd_t,
/**
* Flags determining the method of how the path is resolved.
*/
flags: lookupflags_t,
/**
* The path of the file or directory to operate on.
*/
path: string,
/**
* The desired values of the data access timestamp.
*/
atim: timestamp_t,
/**
* The desired values of the data modification timestamp.
*/
mtim: timestamp_t,
/**
* A bitmask indicating which timestamps to adjust.
*/
fst_flags: fstflags_t,
) -> errno_t ---
/**
* Remove a directory.
* Return `errno::notempty` if the directory is not empty.
* Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX.
*/
path_remove_directory :: proc(
fd: fd_t,
/**
* The path to a directory to remove.
*/
path: string,
) -> errno_t ---
/**
* Create a hard link.
* Note: This is similar to `linkat` in POSIX.
*/
path_link :: proc(
old_fd: fd_t,
/**
* Flags determining the method of how the path is resolved.
*/
old_flags: lookupflags_t,
/**
* The source path from which to link.
*/
old_path: string,
/**
* The working directory at which the resolution of the new path starts.
*/
new_fd: fd_t,
/**
* The destination path at which to create the hard link.
*/
new_path: string,
) -> errno_t ---
/**
* Rename a file or directory.
* Note: This is similar to `renameat` in POSIX.
*/
path_rename :: proc(
fd: fd_t,
/**
* The source path of the file or directory to rename.
*/
old_path: string,
/**
* The working directory at which the resolution of the new path starts.
*/
new_fd: fd_t,
/**
* The destination path to which to rename the file or directory.
*/
new_path: string,
) -> errno_t ---
/**
* Create a symbolic link.
* Note: This is similar to `symlinkat` in POSIX.
*/
path_symlink :: proc(
/**
* The contents of the symbolic link.
*/
old_path: string,
fd: fd_t,
/**
* The destination path at which to create the symbolic link.
*/
new_path: string,
) -> errno_t ---
/**
* Unlink a file.
* Return `errno::isdir` if the path refers to a directory.
* Note: This is similar to `unlinkat(fd, path, 0)` in POSIX.
*/
path_unlink_file :: proc(
fd: fd_t,
/**
* The path to a file to unlink.
*/
path: string,
) -> errno_t ---
/**
* Write high-quality random data into a buffer.
* This function blocks when the implementation is unable to immediately
* provide sufficient high-quality random data.
* This function may execute slowly, so when large mounts of random data are
* required, it's advisable to use this function to seed a pseudo-random
* number generator, rather than to provide the random data directly.
*/
random_get :: proc(
/**
* The buffer to fill with random data.
*/
buf: []u8,
) -> errno_t ---
} }
/** /**
@@ -1250,7 +1400,7 @@ fd_pread :: proc "c" (
*/ */
offset: filesize_t, offset: filesize_t,
) -> (n: size_t, err: errno_t) { ) -> (n: size_t, err: errno_t) {
err = wasi_fd_pread(fd, raw_data(iovs), len(iovs), offset, &n) err = wasi_fd_pread(fd, iovs, offset, &n)
return return
} }
/** /**
@@ -1281,7 +1431,7 @@ fd_pwrite :: proc "c" (
*/ */
offset: filesize_t, offset: filesize_t,
) -> (n: size_t, err: errno_t) { ) -> (n: size_t, err: errno_t) {
err = wasi_fd_pwrite(fd, raw_data(iovs), len(iovs), offset, &n) err = wasi_fd_pwrite(fd, iovs, offset, &n)
return return
} }
/** /**
@@ -1297,7 +1447,7 @@ fd_read :: proc "c" (
*/ */
iovs: []iovec_t, iovs: []iovec_t,
) -> (n: size_t, err: errno_t) { ) -> (n: size_t, err: errno_t) {
err = wasi_fd_read(fd, raw_data(iovs), len(iovs), &n) err = wasi_fd_read(fd, iovs, &n)
return return
} }
/** /**
@@ -1324,7 +1474,7 @@ fd_readdir :: proc "c" (
*/ */
cookie: dircookie_t, cookie: dircookie_t,
) -> (n: size_t, err: errno_t) { ) -> (n: size_t, err: errno_t) {
err = wasi_fd_readdir(fd, raw_data(buf), len(buf), cookie, &n) err = wasi_fd_readdir(fd, buf, cookie, &n)
return return
} }
/** /**
@@ -1370,7 +1520,7 @@ fd_write :: proc "c" (
*/ */
iovs: []ciovec_t, iovs: []ciovec_t,
) -> (n: size_t, err: errno_t) { ) -> (n: size_t, err: errno_t) {
err = wasi_fd_write(fd, raw_data(iovs), len(iovs), &n) err = wasi_fd_write(fd, iovs, &n)
return return
} }
/** /**
@@ -1390,7 +1540,7 @@ path_filestat_get :: proc "c" (
*/ */
path: string, path: string,
) -> (offset: filestat_t, err: errno_t) { ) -> (offset: filestat_t, err: errno_t) {
err = wasi_path_filestat_get(fd, flags, raw_data(path), len(path), &offset) err = wasi_path_filestat_get(fd, flags, path, &offset)
return return
} }
/** /**
@@ -1432,7 +1582,7 @@ path_open :: proc "c" (
fs_rights_inheriting: rights_t, fs_rights_inheriting: rights_t,
fdflags: fdflags_t, fdflags: fdflags_t,
) -> (file: fd_t, err: errno_t) { ) -> (file: fd_t, err: errno_t) {
err = wasi_path_open(fd, dirflags, raw_data(path), len(path), oflags, fs_rights_base, fs_rights_inheriting, fdflags, &file) err = wasi_path_open(fd, dirflags, path, oflags, fs_rights_base, fs_rights_inheriting, fdflags, &file)
return return
} }
/** /**
@@ -1452,7 +1602,7 @@ path_readlink :: proc "c" (
*/ */
buf: []u8, buf: []u8,
) -> (n: size_t, err: errno_t) { ) -> (n: size_t, err: errno_t) {
err = wasi_path_readlink(fd, raw_data(path), len(path), raw_data(buf), len(buf), &n) err = wasi_path_readlink(fd, path, buf, &n)
return return
} }
/** /**
@@ -1495,7 +1645,7 @@ sock_recv :: proc "c" (
*/ */
ri_flags: riflags_t, ri_flags: riflags_t,
) -> (n: size_t, flags: roflags_t, err: errno_t) { ) -> (n: size_t, flags: roflags_t, err: errno_t) {
err = wasi_sock_recv(fd, raw_data(ri_data), len(ri_data), ri_flags, &n, &flags) err = wasi_sock_recv(fd, ri_data, ri_flags, &n, &flags)
return return
} }
/** /**
@@ -1516,172 +1666,11 @@ sock_send :: proc "c" (
*/ */
si_flags: siflags_t, si_flags: siflags_t,
) -> (n: size_t, err: errno_t) { ) -> (n: size_t, err: errno_t) {
err = wasi_sock_send(fd, raw_data(si_data), len(si_data), si_flags, &n) err = wasi_sock_send(fd, si_data, si_flags, &n)
return return
} }
/**
* Return a description of the given preopened file descriptor.
*/
fd_prestat_dir_name :: proc(
fd: fd_t,
/**
* A buffer into which to write the preopened directory name.
*/
path: string,
) -> errno_t {
return wasm_fd_prestat_dir_name(fd, raw_data(path), len(path))
}
/**
* Create a directory.
* Note: This is similar to `mkdirat` in POSIX.
*/
path_create_directory :: proc(
fd: fd_t,
/**
* The path at which to create the directory.
*/
path: string,
) -> errno_t {
return wasm_path_create_directory(fd, raw_data(path), len(path))
}
/**
* Adjust the timestamps of a file or directory.
* Note: This is similar to `utimensat` in POSIX.
*/
path_filestat_set_times :: proc(
fd: fd_t,
/**
* Flags determining the method of how the path is resolved.
*/
flags: lookupflags_t,
/**
* The path of the file or directory to operate on.
*/
path: string,
/**
* The desired values of the data access timestamp.
*/
atim: timestamp_t,
/**
* The desired values of the data modification timestamp.
*/
mtim: timestamp_t,
/**
* A bitmask indicating which timestamps to adjust.
*/
fst_flags: fstflags_t,
) -> errno_t {
return wasm_path_filestat_set_times(fd, flags, raw_data(path), len(path), atim, mtim, fst_flags)
}
/**
* Remove a directory.
* Return `errno::notempty` if the directory is not empty.
* Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX.
*/
path_remove_directory :: proc(
fd: fd_t,
/**
* The path to a directory to remove.
*/
path: string,
) -> errno_t {
return wasm_path_remove_directory(fd, raw_data(path), len(path))
}
/**
* Create a hard link.
* Note: This is similar to `linkat` in POSIX.
*/
path_link :: proc(
old_fd: fd_t,
/**
* Flags determining the method of how the path is resolved.
*/
old_flags: lookupflags_t,
/**
* The source path from which to link.
*/
old_path: string,
/**
* The working directory at which the resolution of the new path starts.
*/
new_fd: fd_t,
/**
* The destination path at which to create the hard link.
*/
new_path: string,
) -> errno_t {
return wasm_path_link(old_fd, old_flags, raw_data(old_path), len(old_path), new_fd, raw_data(new_path), len(new_path))
}
/**
* Rename a file or directory.
* Note: This is similar to `renameat` in POSIX.
*/
path_rename :: proc(
fd: fd_t,
/**
* The source path of the file or directory to rename.
*/
old_path: string,
/**
* The working directory at which the resolution of the new path starts.
*/
new_fd: fd_t,
/**
* The destination path to which to rename the file or directory.
*/
new_path: string,
) -> errno_t {
return wasm_path_rename(fd, raw_data(old_path), len(old_path), new_fd, raw_data(new_path), len(new_path))
}
/**
* Create a symbolic link.
* Note: This is similar to `symlinkat` in POSIX.
*/
path_symlink :: proc(
/**
* The contents of the symbolic link.
*/
old_path: string,
fd: fd_t,
/**
* The destination path at which to create the symbolic link.
*/
new_path: string,
) -> errno_t {
return wasm_path_symlink(raw_data(old_path), len(old_path), fd, raw_data(new_path), len(new_path))
}
/**
* Unlink a file.
* Return `errno::isdir` if the path refers to a directory.
* Note: This is similar to `unlinkat(fd, path, 0)` in POSIX.
*/
path_unlink_file :: proc(
fd: fd_t,
/**
* The path to a file to unlink.
*/
path: string,
) -> errno_t {
return wasm_path_unlink_file(fd, raw_data(path), len(path))
}
/**
* Write high-quality random data into a buffer.
* This function blocks when the implementation is unable to immediately
* provide sufficient high-quality random data.
* This function may execute slowly, so when large mounts of random data are
* required, it's advisable to use this function to seed a pseudo-random
* number generator, rather than to provide the random data directly.
*/
random_get :: proc(
/**
* The buffer to fill with random data.
*/
buf: []u8,
) -> errno_t {
return wasm_random_get(raw_data(buf), len(buf))
}
@@ -1722,8 +1711,7 @@ foreign wasi {
@(link_name="fd_pread") @(link_name="fd_pread")
wasi_fd_pread :: proc( wasi_fd_pread :: proc(
fd: fd_t, fd: fd_t,
iovs: [^]iovec_t, iovs: []iovec_t,
iovs_len: size_t,
offset: filesize_t, offset: filesize_t,
retptr0: ^size_t, retptr0: ^size_t,
) -> errno_t --- ) -> errno_t ---
@@ -1735,23 +1723,20 @@ foreign wasi {
@(link_name="fd_pwrite") @(link_name="fd_pwrite")
wasi_fd_pwrite :: proc( wasi_fd_pwrite :: proc(
fd: fd_t, fd: fd_t,
iovs: [^]ciovec_t, iovs: []ciovec_t,
iovs_len: size_t,
offset: filesize_t, offset: filesize_t,
retptr0: ^size_t, retptr0: ^size_t,
) -> errno_t --- ) -> errno_t ---
@(link_name="fd_read") @(link_name="fd_read")
wasi_fd_read :: proc( wasi_fd_read :: proc(
fd: fd_t, fd: fd_t,
iovs: [^]iovec_t, iovs: []iovec_t,
iovs_len: size_t,
retptr0: ^size_t, retptr0: ^size_t,
) -> errno_t --- ) -> errno_t ---
@(link_name="fd_readdir") @(link_name="fd_readdir")
wasi_fd_readdir :: proc( wasi_fd_readdir :: proc(
fd: fd_t, fd: fd_t,
buf: [^]u8, buf: []u8,
buf_len: size_t,
cookie: dircookie_t, cookie: dircookie_t,
retptr0: ^size_t, retptr0: ^size_t,
) -> errno_t --- ) -> errno_t ---
@@ -1770,8 +1755,7 @@ foreign wasi {
@(link_name="fd_write") @(link_name="fd_write")
wasi_fd_write :: proc( wasi_fd_write :: proc(
fd: fd_t, fd: fd_t,
iovs: [^]ciovec_t, iovs: []ciovec_t,
iovs_len: size_t,
retptr0: ^size_t, retptr0: ^size_t,
) -> errno_t --- ) -> errno_t ---
@(link_name="path_filestat_get") @(link_name="path_filestat_get")
@@ -1781,16 +1765,14 @@ foreign wasi {
/** /**
* The path of the file or directory to inspect. * The path of the file or directory to inspect.
*/ */
path: [^]u8, path: string,
path_len: size_t,
retptr0: ^filestat_t, retptr0: ^filestat_t,
) -> errno_t --- ) -> errno_t ---
@(link_name="path_open") @(link_name="path_open")
wasi_path_open :: proc( wasi_path_open :: proc(
fd: fd_t, fd: fd_t,
dirflags: lookupflags_t, dirflags: lookupflags_t,
path: [^]u8, path: string,
path_len: size_t,
oflags: oflags_t, oflags: oflags_t,
fs_rights_base: rights_t, fs_rights_base: rights_t,
fs_rights_inheriting: rights_t, fs_rights_inheriting: rights_t,
@@ -1800,10 +1782,8 @@ foreign wasi {
@(link_name="path_readlink") @(link_name="path_readlink")
wasi_path_readlink :: proc( wasi_path_readlink :: proc(
fd: fd_t, fd: fd_t,
path: [^]u8, path: string,
path_len: size_t, buf: []u8,
buf: [^]u8,
buf_len: size_t,
retptr0: ^size_t, retptr0: ^size_t,
) -> errno_t --- ) -> errno_t ---
@(link_name="poll_oneoff") @(link_name="poll_oneoff")
@@ -1816,8 +1796,7 @@ foreign wasi {
@(link_name="sock_recv") @(link_name="sock_recv")
wasi_sock_recv :: proc( wasi_sock_recv :: proc(
fd: fd_t, fd: fd_t,
ri_data: [^]iovec_t, ri_data: []iovec_t,
ri_data_len: size_t,
ri_flags: riflags_t, ri_flags: riflags_t,
retptr0: ^size_t, retptr0: ^size_t,
retptr1: ^roflags_t, retptr1: ^roflags_t,
@@ -1825,75 +1804,8 @@ foreign wasi {
@(link_name="sock_send") @(link_name="sock_send")
wasi_sock_send :: proc( wasi_sock_send :: proc(
fd: fd_t, fd: fd_t,
si_data: [^]ciovec_t, si_data: []ciovec_t,
si_data_len: size_t,
si_flags: siflags_t, si_flags: siflags_t,
retptr0: ^size_t, retptr0: ^size_t,
) -> errno_t --- ) -> errno_t ---
@(link_name="fd_prestat_dir_name")
wasm_fd_prestat_dir_name :: proc(
fd: fd_t,
path: [^]u8,
path_len: size_t,
) -> errno_t ---
@(link_name="path_create_directory")
wasm_path_create_directory :: proc(
fd: fd_t,
path: [^]u8,
path_len: size_t,
) -> errno_t ---
@(link_name="path_filestat_set_times")
wasm_path_filestat_set_times :: proc(
fd: fd_t,
flags: lookupflags_t,
path: [^]u8,
path_len: size_t,
atim: timestamp_t,
mtim: timestamp_t,
fst_flags: fstflags_t,
) -> errno_t ---
@(link_name="path_remove_directory")
wasm_path_remove_directory :: proc(
fd: fd_t,
path: [^]u8,
path_len: size_t,
) -> errno_t ---
@(link_name="path_link")
wasm_path_link :: proc(
old_fd: fd_t,
old_flags: lookupflags_t,
old_path: [^]u8,
old_path_len: size_t,
new_fd: fd_t,
new_path: [^]u8,
new_path_len: size_t,
) -> errno_t ---
@(link_name="path_rename")
wasm_path_rename :: proc(
fd: fd_t,
old_path: [^]u8,
old_path_len: size_t,
new_fd: fd_t,
new_path: [^]u8,
new_path_len: size_t,
) -> errno_t ---
@(link_name="path_symlink")
wasm_path_symlink :: proc(
old_path: [^]u8,
old_path_len: size_t,
fd: fd_t,
new_path: [^]u8,
new_path_len: size_t,
) -> errno_t ---
@(link_name="path_unlink_file")
wasm_path_unlink_file :: proc(
fd: fd_t,
path: [^]u8,
path_len: size_t,
) -> errno_t ---
@(link_name="random_get")
wasm_random_get :: proc(
buf: [^]u8,
buf_len: size_t,
) -> errno_t ---
} }
+58
View File
@@ -70,4 +70,62 @@ foreign advapi32 {
lpStartupInfo: LPSTARTUPINFO, lpStartupInfo: LPSTARTUPINFO,
lpProcessInformation: LPPROCESS_INFORMATION, lpProcessInformation: LPPROCESS_INFORMATION,
) -> BOOL --- ) -> BOOL ---
RegCreateKeyExW :: proc(
hKey: HKEY,
lpSubKey: LPCWSTR,
Reserved: DWORD,
lpClass: LPWSTR,
dwOptions: DWORD,
samDesired: REGSAM,
lpSecurityAttributes: LPSECURITY_ATTRIBUTES,
phkResult: PHKEY,
lpdwDisposition: LPDWORD,
) -> LSTATUS ---
RegOpenKeyW :: proc(
hKey: HKEY,
lpSubKey: LPCWSTR,
phkResult: PHKEY,
) -> LSTATUS ---
RegOpenKeyExW :: proc(
hKey: HKEY,
lpSubKey: LPCWSTR,
ulOptions: DWORD,
samDesired: REGSAM,
phkResult: PHKEY,
) -> LSTATUS ---
RegCloseKey :: proc(
hKey: HKEY,
) -> LSTATUS ---
RegGetValueW :: proc(
hkey: HKEY,
lpSubKey: LPCWSTR,
lpValue: LPCWSTR,
dwFlags: DWORD,
pdwType: LPDWORD,
pvData: PVOID,
pcbData: LPDWORD,
) -> LSTATUS ---
RegSetValueExW :: proc(
hKey: HKEY,
lpValueName: LPCWSTR,
Reserved: DWORD,
dwType: DWORD,
lpData: ^BYTE,
cbData: DWORD,
) -> LSTATUS ---
RegSetKeyValueW :: proc(
hKey: HKEY,
lpSubKey: LPCWSTR,
lpValueName: LPCWSTR,
dwType: DWORD,
lpData: LPCVOID,
cbData: DWORD,
) -> LSTATUS ---
} }

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