Compare commits

..

116 Commits

Author SHA1 Message Date
gingerBill 9e6e769141 Add sanity checks to checker 2021-08-02 18:32:17 +01:00
Jeroen van Rijn c0f746a251 Merge pull request #1065 from Kelimion/string_cut
Add `strings.cut`, which returns a substring.
2021-08-02 18:09:59 +02:00
gingerBill ccbdf086ff Add @(warning=<string>) 2021-08-02 16:58:50 +01:00
Jeroen van Rijn d260ca6738 string.cut uses context.allocator by default. 2021-08-02 17:58:42 +02:00
gingerBill b0e64ca7e8 Prepare tokenizer for optimizations 2021-08-02 16:47:32 +01:00
gingerBill 7f3d4cb504 Remove the literal conversion logic to the parser from the tokenizer 2021-08-02 00:53:45 +01:00
gingerBill 97be36d18a Replace line with column_minus_one in Tokenizer; Remove dead code 2021-08-02 00:26:54 +01:00
gingerBill be76da2c90 Begin optimizing tokenizer; Replace gb_utf8_decode with utf8_decode (CC but easier to change later) 2021-08-01 23:56:17 +01:00
gingerBill b1a8357f50 Clean up a bit of the tokenizer code so that the semicolon insertion rules are in one place 2021-08-01 22:41:00 +01:00
Jeroen van Rijn 0dc900ba34 Add strings.cut, which returns a substring. 2021-08-01 19:41:20 +02:00
gingerBill 700624119b Give begin_error_block its own recursive mutex 2021-07-29 12:35:11 +01:00
gingerBill af32aba7fc Modify MPMCQueue behaviour to use i32 over isize; Correct cache line padding within MPMCQueue 2021-07-28 00:59:30 +01:00
gingerBill 541c79c01a Add mutex for @(builtin) attributes 2021-07-28 00:27:16 +01:00
gingerBill 358226468d EXPERIMENT: Set DEFAULT_TO_THREADED_CHECKER on all platforms 2021-07-27 23:55:55 +01:00
gingerBill d1e5f34f76 Use DEFAULT_TO_THREADED_CHECKER 2021-07-27 23:55:19 +01:00
gingerBill 416dd93bf7 Add accidentally removed call 2021-07-27 23:39:35 +01:00
gingerBill 7c80577160 Correct race condition on untyped expr info map logic on global evaluations 2021-07-27 23:37:55 +01:00
gingerBill a5d6fda433 Define which mutexes are blocking and recursive explicitly 2021-07-27 23:14:01 +01:00
gingerBill 4bc3796f9b Short circuit check_with_workers logic on worker_count == 0 2021-07-27 23:00:51 +01:00
gingerBill 5e12f5a746 Unify threading logic 2021-07-27 22:55:32 +01:00
gingerBill 4080ba4026 Remove global semaphore and place in CheckerInfo 2021-07-27 21:42:43 +01:00
gingerBill e17593be94 Remove dead mutex 2021-07-27 21:14:06 +01:00
gingerBill 44aa69748c Correct logic for check_import_entities - collect file decls 2021-07-27 21:13:03 +01:00
gingerBill 9cd5ea59dd Big simplification and improvement of the entity collection system, reducing unneeded steps for packages 2021-07-27 20:45:50 +01:00
gingerBill 116e98b378 Improve default scope size 2021-07-27 10:59:39 +01:00
gingerBill ae25787f48 Fix syntax error for a missing package name 2021-07-26 11:43:17 +01:00
gingerBill 08dc829b70 Manually short circuit in lb_build_if_stmt for constant conditions 2021-07-26 10:59:07 +01:00
gingerBill 3ac674cf02 Fix typo 2021-07-25 13:12:09 +01:00
gingerBill 6cd06ab95f Minor fix 2021-07-25 13:07:31 +01:00
gingerBill 99080d41f3 INTERNAL USE ONLY: //+lazy build flag 2021-07-25 13:06:09 +01:00
gingerBill 92f3567ee6 Default to using a threaded checker on Windows; Add -no-threaded-checker for Windows 2021-07-24 18:00:19 +01:00
Jeroen van Rijn 481fc8a5b6 Merge pull request #1060 from Kelimion/win_sys
Allow `core:sys/windows` to build on Windows only
2021-07-15 12:15:10 +02:00
Jeroen van Rijn 5dfff51a40 Allow core:sys/windows to build on Windows only 2021-07-15 12:13:57 +02:00
gingerBill 2938ec028f Remove dead code 2021-07-15 00:37:59 +01:00
gingerBill 981b9fb7a8 Remove need for scope mutex, make Scope singly linked list tree with atomic pointers 2021-07-14 23:49:35 +01:00
gingerBill 10f4d8df32 Override libtommath allocation procedures 2021-07-14 23:36:23 +01:00
gingerBill e15858e2be Remove random load balancing in thread_proc_body 2021-07-14 22:53:02 +01:00
gingerBill 6d8302825c Add Greed Work Stealing and Random Load Balancing for check_procedure_bodies 2021-07-14 01:13:39 +01:00
gingerBill fae8bf96dd Muilthread check_collect_entities_all 2021-07-14 00:46:03 +01:00
gingerBill bc59dc6389 Remove duplicate code 2021-07-14 00:36:48 +01:00
gingerBill bd8e2f82be Replace non-recursive mutexes with BlockingMutex; Minor improves to initialization improves 2021-07-14 00:34:34 +01:00
gingerBill 69027b6840 Remove dead mutexes 2021-07-13 23:10:12 +01:00
gingerBill 7a9b7af078 Reduce mutex usage and convert things to queues from arrays 2021-07-13 23:09:24 +01:00
gingerBill 31c7afce1b Minor code clean up to reuse the ProcInfo consumption code 2021-07-13 21:19:13 +01:00
gingerBill bab1873416 Require throughput pass results with -show-more-timings -show-debug-messages rather than just the former flag 2021-07-13 18:39:11 +01:00
gingerBill 5e2950e9fb Move asserts around 2021-07-13 18:35:23 +01:00
gingerBill 74c019f271 Correct lb_big_int_to_llvm 2021-07-13 18:32:53 +01:00
gingerBill a745bb8f42 Add extra message to assert 2021-07-13 18:21:53 +01:00
gingerBill da9870c77d Do manual byte swapping for endianness in lb_big_int_to_llvm 2021-07-13 18:15:47 +01:00
gingerBill 698eeaf7c3 Add (internal flag) -show-debug-messages 2021-07-13 17:40:06 +01:00
gingerBill bd954d9990 Minor code reorganization 2021-07-13 17:28:39 +01:00
gingerBill 4ded42a33b Split up cycle check and adding type info timings 2021-07-13 17:24:20 +01:00
gingerBill 1877965ac3 Short on -threaded-checker 2021-07-13 17:04:08 +01:00
gingerBill cec2309504 Big improvement to the -threaded-checker code, unifying the logic and simplify behaviour 2021-07-13 16:58:40 +01:00
gingerBill ed5a4afc8c Temporarily disable -threaded-checker; Restructure the untyped-expr-info system to be much more thread-friendly 2021-07-13 15:54:56 +01:00
gingerBill f29b51efdd Fix gb_shuffle 2021-07-13 13:09:55 +01:00
gingerBill 3930a32b0c enum Addressing_Mode : u8; 2021-07-12 16:45:54 +01:00
gingerBill 76707e1d2f Add sanity casts for 32/64 bit correctness 2021-07-12 11:03:12 +01:00
gingerBill ff2e5c3efe Simplify big_int_not for negative inputs 2021-07-11 17:43:56 +01:00
gingerBill 3600b2e209 Merge pull request #1057 from odin-lang/new-big-int-library-integration
New Big Int Library Integration
2021-07-11 17:20:57 +01:00
gingerBill eb36a0f3b1 Remove dead file 2021-07-11 16:35:04 +01:00
gingerBill b397254696 Rename libtommath.c to libtommath.cpp 2021-07-11 16:33:58 +01:00
gingerBill 9a37d3b6e5 Add -Wno-unused-value 2021-07-11 16:29:51 +01:00
gingerBill 51c4a19234 Fix tab and space issue in Makefile
FUCK DO I HATE Makefile whitespace sensitivity between spaces and tabs
2021-07-11 16:26:21 +01:00
gingerBill a1a1668dcf Update build.bat and Makefile 2021-07-11 16:23:25 +01:00
gingerBill e308098f18 Add libtommath.c 2021-07-11 16:19:20 +01:00
gingerBill 63b572a0ab Clean up big int to LLVM integer code 2021-07-11 16:18:30 +01:00
gingerBill e90e7d4af9 Change mp_clear calls to big_int_dealloc 2021-07-11 16:10:04 +01:00
gingerBill 460e14e586 Change the compiler's big integer library to use libTomMath
This now replaces Bill's crappy big int implementation
2021-07-11 16:08:16 +01:00
gingerBill ebcabb8a27 Add sanity conversion check for integer to quaternion 2021-07-11 00:51:56 +01:00
gingerBill 257b749e9d Minimize mutex usage in update_expr_type 2021-07-11 00:21:31 +01:00
gingerBill d9e6ade030 Add experimental support for a threaded semantic checker to -threaded-checker 2021-07-10 23:51:37 +01:00
gingerBill 690374d4de Fix typo 2021-07-10 23:07:42 +01:00
gingerBill adb25d9d19 Convert constant tag to the correct type for LLVMAddCase 2021-07-10 22:29:52 +01:00
gingerBill 2949e4b0c7 Fix floattidf typo 2021-07-10 22:23:22 +01:00
gingerBill 6de0181c75 Minor improvements to Map and StringMap 2021-07-10 21:51:39 +01:00
gingerBill 8a6b743d2a Simplify thread_pool_wait_to_process 2021-07-10 21:50:19 +01:00
gingerBill ed8a6f872d Move things around for sanity checking for multithread preparation 2021-07-10 21:29:49 +01:00
gingerBill 0a61d4bf2b Use next_pow2_isize 2021-07-10 19:57:54 +01:00
gingerBill 332461c0d2 Add prototypes for next_pow2 2021-07-10 19:52:26 +01:00
gingerBill d8abe7fc4d Implement MPMCQueue for procedure body checking
This is preparation for basic multithreading in the semantic checker
2021-07-10 19:50:34 +01:00
gingerBill ec9667ef5a Remove debug code 2021-07-10 17:11:54 +01:00
gingerBill 9f7154a039 Prepare for multithreading the semantic checker by giving mutexes to variables of contention
NOTE(bill): I know this is dodgy, but I want to make sure it is correct logic before improve those data structures
2021-07-10 15:14:25 +01:00
gingerBill 4a932616fc Improve CheckerContext usage 2021-07-10 13:02:13 +01:00
gingerBill 73fe36f19c Remove dead variable 2021-07-10 11:59:41 +01:00
gingerBill 4167ae95ae Fix #1050 2021-07-10 11:48:28 +01:00
gingerBill 13c3c5be95 Fix #1028 2021-07-10 11:46:22 +01:00
gingerBill 3afec0bcbe Fix #1054 2021-07-10 11:42:21 +01:00
gingerBill 8b1bfc80fb Fix #1051 2021-07-10 11:27:38 +01:00
gingerBill 3662275119 Allow x in ptr_to_map_or_bit_set 2021-07-10 11:18:19 +01:00
gingerBill 141573c18c Enable Damerau-Levenshtein 2021-07-10 11:09:24 +01:00
gingerBill e692efbe09 Improve update expr type semantics for ternary expressions 2021-07-10 11:08:51 +01:00
gingerBill f6c1a5bf6e Merge branch 'master' of https://github.com/odin-lang/Odin 2021-07-09 15:33:30 +01:00
gingerBill 6afc28f827 Use builtin.min and builtin.max in package slice 2021-07-09 15:33:25 +01:00
gingerBill df6681ad4e Merge pull request #1055 from streof/fix-typos-container-package
Fix typos container package
2021-07-09 13:27:03 +01:00
streof 114efbc57c Fix error: Cannot assign to a procedure parameter 2021-07-09 14:03:49 +02:00
streof 2c71494ad1 Fix error: Undeclared name: p 2021-07-09 13:54:27 +02:00
gingerBill 35230b1a11 Add "Suggestion: Did you mean?" for selector expression typos 2021-07-08 23:15:07 +01:00
gingerBill 7acbf8b7b9 Add slice.min and add slice.max 2021-07-08 11:23:07 +01:00
gingerBill f7413ca974 Fix thread_unix.odin 2021-07-05 16:36:07 +01:00
gingerBill 9b3a0251ca Use or_else in the core library when it makes sense 2021-07-05 16:33:01 +01:00
gingerBill 3b9ca8535f Fix comments 2021-07-05 16:26:11 +01:00
gingerBill a98eee145d Remove try; Replace try x else y with or_else(x, y) 2021-07-05 16:23:13 +01:00
gingerBill c6b9b3b9a4 Fix try parsing for expression statements 2021-07-04 22:52:12 +01:00
gingerBill a4be1a5e4c delete_key now returns the deleted key and deleted value (if found) 2021-07-04 18:52:47 +01:00
gingerBill ee908c00de Add documentation for the overview of package fmt 2021-07-04 18:21:41 +01:00
gingerBill 46264032aa Improve error messages for try expressions 2021-07-04 12:50:44 +01:00
gingerBill 4b831dbddd Try try and or_else built-in procedures with operators try and try else 2021-07-04 12:37:21 +01:00
gingerBill a01d6dcea7 Refactor return logic to be more reusable with lb_emit_try 2021-07-04 01:57:38 +01:00
gingerBill 01a15f78e6 Simplify lb_emit_try return logic 2021-07-04 01:50:37 +01:00
gingerBill 5f71c41582 Fix lb_emit_try 2021-07-04 01:47:43 +01:00
gingerBill e8f2c5a48a [Experimental] Add 'try' and `or_else' built-in procedures 2021-07-04 01:38:43 +01:00
gingerBill 1c76577918 Add slice.sort_by_cmp Ordering based sorting algorithms 2021-07-03 15:16:44 +01:00
gingerBill 4285b58aaa Add #no_bounds_check to linalg procedures 2021-07-03 14:38:41 +01:00
gingerBill 9cc366de97 Be more consistent with runtime intrinsics usage 2021-07-03 14:38:15 +01:00
gingerBill 212d294b84 Deprecate sort.slice and sort.reverse_slice 2021-07-03 14:37:55 +01:00
227 changed files with 14765 additions and 3604 deletions
+28 -25
View File
@@ -1,5 +1,5 @@
GIT_SHA=$(shell git rev-parse --short HEAD)
DISABLED_WARNINGS=-Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined
DISABLED_WARNINGS=-Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined -Wno-unused-value
LDFLAGS=-pthread -ldl -lm -lstdc++
CFLAGS=-std=c++14 -DGIT_SHA=\"$(GIT_SHA)\"
CFLAGS:=$(CFLAGS) -DODIN_VERSION_RAW=\"dev-$(shell date +"%Y-%m")\"
@@ -8,31 +8,31 @@ CC=clang
OS=$(shell uname)
ifeq ($(OS), Darwin)
LLVM_CONFIG=llvm-config
ifneq ($(shell llvm-config --version | grep '^11\.'),)
LLVM_CONFIG=llvm-config
else
$(error "Requirement: llvm-config must be version 11")
endif
LLVM_CONFIG=llvm-config
ifneq ($(shell llvm-config --version | grep '^11\.'),)
LLVM_CONFIG=llvm-config
else
$(error "Requirement: llvm-config must be version 11")
endif
LDFLAGS:=$(LDFLAGS) -liconv
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) -lLLVM-C
LDFLAGS:=$(LDFLAGS) -liconv
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) -lLLVM-C
endif
ifeq ($(OS), Linux)
LLVM_CONFIG=llvm-config-11
ifneq ($(shell which llvm-config-11 2>/dev/null),)
LLVM_CONFIG=llvm-config-11
else
ifneq ($(shell llvm-config --version | grep '^11\.'),)
LLVM_CONFIG=llvm-config
else
$(error "Requirement: llvm-config must be version 11")
endif
endif
LLVM_CONFIG=llvm-config-11
ifneq ($(shell which llvm-config-11 2>/dev/null),)
LLVM_CONFIG=llvm-config-11
else
ifneq ($(shell llvm-config --version | grep '^11\.'),)
LLVM_CONFIG=llvm-config
else
$(error "Requirement: llvm-config must be version 11")
endif
endif
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
endif
all: debug demo
@@ -41,13 +41,16 @@ demo:
./odin run examples/demo/demo.odin
debug:
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -g $(LDFLAGS) -o odin
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -g $(LDFLAGS) -o odin
release:
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 $(LDFLAGS) -o odin
release_native:
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin
nightly:
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 $(LDFLAGS) -o odin
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 $(LDFLAGS) -o odin
+2 -2
View File
@@ -46,7 +46,7 @@ if %release_mode% EQU 0 ( rem Debug
set compiler_warnings= ^
-W4 -WX ^
-wd4100 -wd4101 -wd4127 -wd4189 ^
-wd4100 -wd4101 -wd4127 -wd4146 -wd4189 ^
-wd4201 -wd4204 ^
-wd4456 -wd4457 -wd4480 ^
-wd4512
@@ -70,7 +70,7 @@ set linker_settings=%libs% %linker_flags%
del *.pdb > NUL 2> NUL
del *.ilk > NUL 2> NUL
cl %compiler_settings% "src\main.cpp" /link %linker_settings% -OUT:%exe_name%
cl %compiler_settings% "src\main.cpp" "src\libtommath.cpp" /link %linker_settings% -OUT:%exe_name%
if %errorlevel% neq 0 goto end_of_build
if %release_mode% EQU 0 odin run examples/demo/demo.odin
+2 -1
View File
@@ -26,6 +26,7 @@ ring_prev :: proc(r: ^$R/Ring) -> ^R {
ring_move :: proc(r: ^$R/Ring, n: int) -> ^R {
r := r;
if r.next == nil {
return ring_init(r);
}
@@ -64,7 +65,7 @@ ring_len :: proc(r: ^$R/Ring) -> int {
n := 0;
if r != nil {
n = 1;
for p := ring_next(&p); p != r; p = p.next {
for p := ring_next(r); p != r; p = p.next {
n += 1;
}
}
+141
View File
@@ -0,0 +1,141 @@
/*
package fmt implemented formatted I/O with procedures similar to C's printf and Python's format.
The format 'verbs' are derived from C's but simpler.
Printing
The verbs:
General:
%v the value in a default format
%#v an expanded format of %v with newlines and indentation
%T an Odin-syntax representation of the type of the value
%% a literal percent sign; consumes no value
{{ a literal open brace; consumes no value
}} a literal close brace; consumes no value
{:v} equivalent to %v (Python-like formatting syntax)
Boolean:
%t the word "true" or "false"
Integer:
%b base 2
%c the character represented by the corresponding Unicode code point
%r synonym for %c
%o base 8
%d base 10
%i base 10
%z base 12
%x base 16, with lower-case letters for a-f
%X base 16, with upper-case letters for A-F
%U Unicode format: U+1234; same as "U+%04X"
Floating-point, complex numbers, and quaternions:
%e scientific notation, e.g. -1.23456e+78
%E scientific notation, e.g. -1.23456E+78
%f decimal point but no exponent, e.g. 123.456
%F synonym for %f
%h hexadecimal (lower-case) representation with 0h prefix (0h01234abcd)
%H hexadecimal (upper-case) representation with 0H prefix (0h01234ABCD)
String and slice of bytes
%s the uninterpreted bytes of the string or slice
%q a double-quoted string safely escaped with Odin syntax
%x base 16, lower-case, two characters per byte
%X base 16, upper-case, two characters per byte
Slice and dynamic array:
%p address of the 0th element in base 16 notation (upper-case), with leading 0x
Pointer:
%p base 16 notation (upper-case), with leading 0x
The %b, %d, %o, %z, %x, %X verbs also work with pointers,
treating it as if it was an integer
Enums:
%s prints the name of the enum field
The %i, %d, %f verbs also work with enums,
treating it as if it was a number
For compound values, the elements are printed using these rules recursively; laid out like the following:
struct: {name0 = field0, name1 = field1, ...}
array [elem0, elem1, elem2, ...]
enumerated array [key0 = elem0, key1 = elem1, key2 = elem2, ...]
maps: map[key0 = value0, key1 = value1, ...]
bit sets {key0 = elem0, key1 = elem1, ...}
pointer to above: &{}, &[], &map[]
Width is specified by an optional decimal number immediately preceding the verb.
If not present, the width is whatever is necessary to represent the value.
Precision is specified after the (optional) width followed by a period followed by a decimal number.
If no period is present, a default precision is used.
A period with no following number specifies a precision of 0.
Examples:
%f default width, default precision
%8f width 8, default precision
%.3f default width, precision 2
%8.3f width 8, precision 3
%8.f width 8, precision 0
Width and precision are measured in units of Unicode code points (runes).
n.b. C's printf uses units of bytes
Other flags:
+ always print a sign for numeric values
- pad with spaces on the right rather the left (left-justify the field)
# alternate format:
add leading 0b for binary (%#b)
add leading 0o for octal (%#o)
add leading 0z for dozenal (%#z)
add leading 0x or 0X for hexadecimal (%#x or %#X)
remove leading 0x for %p (%#p)
' ' (space) leave a space for elided sign in numbers (% d)
0 pad with leading zeros rather than spaces
Flags are ignored by verbs that don't expect them
For each printf-like procedure, there is a print function that takes no
format, and is equivalent to doing %v for every value and inserts a separator
between each value (default is a single space).
Another procedure println which has the same functionality as print but appends a newline.
Explicit argument indices:
In printf-like procedures, the default behaviour is for each formatting verb to format successive
arguments passed in the call. However, the notation [n] immediately before the verb indicates that
the nth zero-index argument is to be formatted instead.
The same notation before an '*' for a width or precision selecting the argument index holding the value.
Python-like syntax with argument indices differs for the selecting the argument index: {N:v}
Examples:
fmt.printf("%[1]d %[0]d\n", 13, 37); // C-like syntax
fmt.printf("{1:d} {0:d}\n", 13, 37); // Python-like syntax
prints "37 13", whilst:
fmt.printf("%[2]*.[1]*[0]f\n", 17.0, 2, 6); // C-like syntax
fmt.printf("%{0:[2]*.[1]*f}\n", 17.0, 2, 6); // Python-like syntax
equivalent to:
fmt.printf("%6.2f\n", 17.0, 2, 6); // C-like syntax
fmt.printf("{:6.2f}\n", 17.0, 2, 6); // Python-like syntax
prints "17.00"
Format errors:
If an invalid argument is given for a verb, such as providing a string to %d, the generated string
will contain a description of the problem. For example:
Bad enum value:
%!(BAD ENUM VALUE)
Too many arguments:
%!(EXTRA <value>, <value>, ...)
Too few arguments:
%!(MISSING ARGUMENT)
Invalid width or precision
%!(BAD WIDTH)
%!(BAD PRECISION)
Missing verb:
%!(NO VERB)
Invalid or invalid use of argument index:
%!(BAD ARGUMENT NUMBER)
Missing close brace when using Python-like formatting syntax:
%!(MISSING CLOSE BRACE)
*/
package fmt
+2 -1
View File
@@ -1013,6 +1013,7 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) {
case 'b': _fmt_int(fi, u, 2, false, 8*size_of(rawptr), __DIGITS_UPPER);
case 'o': _fmt_int(fi, u, 8, false, 8*size_of(rawptr), __DIGITS_UPPER);
case 'i', 'd': _fmt_int(fi, u, 10, false, 8*size_of(rawptr), __DIGITS_UPPER);
case 'z': _fmt_int(fi, u, 12, false, 8*size_of(rawptr), __DIGITS_UPPER);
case 'x': _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER);
case 'X': _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER);
@@ -1082,7 +1083,7 @@ fmt_enum :: proc(fi: ^Info, v: any, verb: rune) {
case 's', 'v':
str, ok := enum_value_to_string(v);
if !ok {
str = "!%(BAD ENUM VALUE)";
str = "%!(BAD ENUM VALUE)";
}
io.write_string(fi.writer, str);
}
+10 -15
View File
@@ -41,7 +41,7 @@ scalar_dot :: proc(a, b: $T) -> T where IS_FLOAT(T), !IS_ARRAY(T) {
return a * b;
}
vector_dot :: proc(a, b: $T/[$N]$E) -> (c: E) where IS_NUMERIC(E) {
vector_dot :: proc(a, b: $T/[$N]$E) -> (c: E) where IS_NUMERIC(E) #no_bounds_check {
for i in 0..<N {
c += a[i] * b[i];
}
@@ -60,7 +60,7 @@ quaternion256_dot :: proc(a, b: $T/quaternion256) -> (c: f64) {
dot :: proc{scalar_dot, vector_dot, quaternion64_dot, quaternion128_dot, quaternion256_dot};
inner_product :: dot;
outer_product :: proc(a: $A/[$M]$E, b: $B/[$N]E) -> (out: [M][N]E) where IS_NUMERIC(E) {
outer_product :: proc(a: $A/[$M]$E, b: $B/[$N]E) -> (out: [M][N]E) where IS_NUMERIC(E) #no_bounds_check {
for i in 0..<M {
for j in 0..<N {
out[i][j] = a[i]*b[j];
@@ -156,7 +156,7 @@ projection :: proc(x, normal: $T/[$N]$E) -> T where IS_NUMERIC(E) {
return dot(x, normal) / dot(normal, normal) * normal;
}
identity :: proc($T: typeid/[$N][N]$E) -> (m: T) {
identity :: proc($T: typeid/[$N][N]$E) -> (m: T) #no_bounds_check {
for i in 0..<N {
m[i][i] = E(1);
}
@@ -170,7 +170,7 @@ trace :: proc(m: $T/[$N][N]$E) -> (tr: E) {
return;
}
transpose :: proc(a: $T/[$N][$M]$E) -> (m: (T when N == M else [M][N]E)) {
transpose :: proc(a: $T/[$N][$M]$E) -> (m: (T when N == M else [M][N]E)) #no_bounds_check {
for j in 0..<M {
for i in 0..<N {
m[j][i] = a[i][j];
@@ -180,8 +180,7 @@ transpose :: proc(a: $T/[$N][$M]$E) -> (m: (T when N == M else [M][N]E)) {
}
matrix_mul :: proc(a, b: $M/[$N][N]$E) -> (c: M)
where !IS_ARRAY(E),
IS_NUMERIC(E) {
where !IS_ARRAY(E), IS_NUMERIC(E) #no_bounds_check {
for i in 0..<N {
for k in 0..<N {
for j in 0..<N {
@@ -193,8 +192,7 @@ matrix_mul :: proc(a, b: $M/[$N][N]$E) -> (c: M)
}
matrix_comp_mul :: proc(a, b: $M/[$J][$I]$E) -> (c: M)
where !IS_ARRAY(E),
IS_NUMERIC(E) {
where !IS_ARRAY(E), IS_NUMERIC(E) #no_bounds_check {
for j in 0..<J {
for i in 0..<I {
c[j][i] = a[j][i] * b[j][i];
@@ -204,9 +202,7 @@ matrix_comp_mul :: proc(a, b: $M/[$J][$I]$E) -> (c: M)
}
matrix_mul_differ :: proc(a: $A/[$J][$I]$E, b: $B/[$K][J]E) -> (c: [K][I]E)
where !IS_ARRAY(E),
IS_NUMERIC(E),
I != K {
where !IS_ARRAY(E), IS_NUMERIC(E), I != K #no_bounds_check {
for k in 0..<K {
for j in 0..<J {
for i in 0..<I {
@@ -219,8 +215,7 @@ matrix_mul_differ :: proc(a: $A/[$J][$I]$E, b: $B/[$K][J]E) -> (c: [K][I]E)
matrix_mul_vector :: proc(a: $A/[$I][$J]$E, b: $B/[I]E) -> (c: B)
where !IS_ARRAY(E),
IS_NUMERIC(E) {
where !IS_ARRAY(E), IS_NUMERIC(E) #no_bounds_check {
for i in 0..<I {
for j in 0..<J {
c[j] += a[i][j] * b[i];
@@ -328,14 +323,14 @@ cubic :: proc(v1, v2, v3, v4: $T/[$N]$E, s: E) -> T {
array_cast :: proc(v: $A/[$N]$T, $Elem_Type: typeid) -> (w: [N]Elem_Type) {
array_cast :: proc(v: $A/[$N]$T, $Elem_Type: typeid) -> (w: [N]Elem_Type) #no_bounds_check {
for i in 0..<N {
w[i] = Elem_Type(v[i]);
}
return;
}
matrix_cast :: proc(v: $A/[$M][$N]$T, $Elem_Type: typeid) -> (w: [M][N]Elem_Type) {
matrix_cast :: proc(v: $A/[$M][$N]$T, $Elem_Type: typeid) -> (w: [M][N]Elem_Type) #no_bounds_check {
for i in 0..<M {
for j in 0..<N {
w[i][j] = Elem_Type(v[i][j]);
+2 -5
View File
@@ -57,11 +57,8 @@ link_error_delete :: proc(lerr: Maybe(Link_Error)) {
is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) {
v: Platform_Error;
if v, ok = ferr.(Platform_Error); ok {
err = v.err;
}
return;
v := or_else(ferr.(Platform_Error), {});
return v.err, v.err != 0;
}
+1 -5
View File
@@ -13,11 +13,7 @@ error_to_io_error :: proc(ferr: Error) -> io.Error {
if ferr == nil {
return .None;
}
err, ok := ferr.(io.Error);
if !ok {
err = .Unknown;
}
return err;
return or_else(ferr.(io.Error), .Unknown);
}
+3 -3
View File
@@ -427,7 +427,7 @@ typeid_base :: proc "contextless" (id: typeid) -> typeid {
return ti.id;
}
typeid_core :: proc "contextless" (id: typeid) -> typeid {
ti := type_info_base_without_enum(type_info_of(id));
ti := type_info_core(type_info_of(id));
return ti.id;
}
typeid_base_without_enum :: typeid_core;
@@ -492,6 +492,6 @@ default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code
print_string(message);
}
print_byte('\n');
// debug_trap();
trap();
// intrinsics.debug_trap();
intrinsics.trap();
}
+13 -2
View File
@@ -270,11 +270,22 @@ reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int) {
// 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
@builtin
delete_key :: proc(m: ^$T/map[$K]$V, key: K) {
delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value: V) {
if m != nil {
key := key;
__dynamic_map_delete_key(__get_map_header(m), __get_map_hash(&key));
h := __get_map_header(m);
hash := __get_map_hash(&key);
fr := __dynamic_map_find(h, hash);
if fr.entry_index >= 0 {
entry := __dynamic_map_get_entry(h, fr.entry_index);
deleted_key = (^K)(uintptr(entry)+h.key_offset)^;
deleted_value = (^V)(uintptr(entry)+h.value_offset)^;
__dynamic_map_erase(h, fr);
}
}
return;
}
+2 -2
View File
@@ -716,7 +716,7 @@ floattidf :: proc(a: i128) -> f64 {
a <<= u128(DBL_MANT_DIG - sd);
}
fb: [2]u32;
fb[1] = (u32(s) & 0x80000000) | // sign
fb[0] = (u32(s) & 0x80000000) | // sign
((e + 1023) << 20) | // exponent
u32((u64(a) >> 32) & 0x000FFFFF); // mantissa-high
fb[1] = u32(a); // mantissa-low
@@ -757,7 +757,7 @@ floattidf_unsigned :: proc(a: u128) -> f64 {
a <<= u128(DBL_MANT_DIG - sd);
}
fb: [2]u32;
fb[1] = (0) | // sign
fb[0] = (0) | // sign
((e + 1023) << 20) | // exponent
u32((u64(a) >> 32) & 0x000FFFFF); // mantissa-high
fb[1] = u32(a); // mantissa-low
+24
View File
@@ -1,10 +1,12 @@
package slice
import "intrinsics"
import "builtin"
import "core:math/bits"
import "core:mem"
_ :: intrinsics;
_ :: builtin;
_ :: bits;
_ :: mem;
@@ -292,6 +294,28 @@ filter :: proc(s: $S/[]$U, f: proc(U) -> bool, allocator := context.allocator) -
min :: proc(s: $S/[]$T) -> (res: T, ok: bool) where intrinsics.type_is_ordered(T) #optional_ok {
if len(s) != 0 {
res = s[0];
ok = true;
for v in s[1:] {
res = builtin.min(res, v);
}
}
return;
}
max :: proc(s: $S/[]$T) -> (res: T, ok: bool) where intrinsics.type_is_ordered(T) #optional_ok {
if len(s) != 0 {
res = s[0];
ok = true;
for v in s[1:] {
res = builtin.max(res, v);
}
}
return;
}
dot_product :: proc(a, b: $S/[]$T) -> T
where intrinsics.type_is_numeric(T) {
if len(a) != len(b) {
+229 -8
View File
@@ -5,6 +5,33 @@ _ :: intrinsics;
ORD :: intrinsics.type_is_ordered;
Ordering :: enum {
Less = -1,
Equal = 0,
Greater = +1,
}
cmp :: proc(a, b: $E) -> Ordering where ORD(E) {
switch {
case a < b:
return .Less;
case a > b:
return .Greater;
}
return .Equal;
}
cmp_proc :: proc($E: typeid) -> (proc(E, E) -> Ordering) where ORD(E) {
return proc(a, b: E) -> Ordering {
switch {
case a < b:
return .Less;
case a > b:
return .Greater;
}
return .Equal;
};
}
// sort sorts a slice
// This sort is not guaranteed to be stable
@@ -21,7 +48,15 @@ sort :: proc(data: $T/[]$E) where ORD(E) {
sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) {
when size_of(E) != 0 {
if n := len(data); n > 1 {
_quick_sort_proc(data, 0, n, _max_depth(n), less);
_quick_sort_less(data, 0, n, _max_depth(n), less);
}
}
}
sort_by_cmp :: proc(data: $T/[]$E, cmp: proc(i, j: E) -> Ordering) {
when size_of(E) != 0 {
if n := len(data); n > 1 {
_quick_sort_cmp(data, 0, n, _max_depth(n), cmp);
}
}
}
@@ -44,6 +79,16 @@ is_sorted_by :: proc(array: $T/[]$E, less: proc(i, j: E) -> bool) -> bool {
return true;
}
is_sorted_cmp :: proc(array: $T/[]$E, cmp: proc(i, j: E) -> Ordering) -> bool {
for i := len(array)-1; i > 0; i -= 1 {
if cmp(array[i], array[i-1]) == .Equal {
return false;
}
}
return true;
}
reverse_sort :: proc(data: $T/[]$E) where ORD(E) {
sort_by(data, proc(i, j: E) -> bool {
@@ -52,6 +97,23 @@ reverse_sort :: proc(data: $T/[]$E) where ORD(E) {
}
reverse_sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) where ORD(E) {
context._internal = rawptr(less);
sort_by(data, proc(i, j: E) -> bool {
k := (proc(i, j: E) -> bool)(context._internal);
return k(j, i);
});
}
reverse_sort_by_cmp :: proc(data: $T/[]$E, cmp: proc(i, j: E) -> Ordering) where ORD(E) {
context._internal = rawptr(cmp);
sort_by_cmp(data, proc(i, j: E) -> Ordering {
k := (proc(i, j: E) -> Ordering)(context._internal);
return k(j, i);
});
}
// TODO(bill): Should `sort_by_key` exist or is `sort_by` more than enough?
sort_by_key :: proc(data: $T/[]$E, key: proc(E) -> $K) where ORD(K) {
context._internal = rawptr(key);
@@ -250,7 +312,7 @@ _heap_sort :: proc(data: $T/[]$E, a, b: int) where ORD(E) {
@(private)
_quick_sort_proc :: proc(data: $T/[]$E, a, b, max_depth: int, less: proc(i, j: E) -> bool) {
_quick_sort_less :: proc(data: $T/[]$E, a, b, max_depth: int, less: proc(i, j: E) -> bool) {
median3 :: proc(data: T, m1, m0, m2: int, less: proc(i, j: E) -> bool) {
if less(data[m1], data[m0]) {
swap(data, m1, m0);
@@ -337,16 +399,16 @@ _quick_sort_proc :: proc(data: $T/[]$E, a, b, max_depth: int, less: proc(i, j: E
if b-a > 12 { // only use shell sort for lengths <= 12
if max_depth == 0 {
_heap_sort_proc(data, a, b, less);
_heap_sort_less(data, a, b, less);
return;
}
max_depth -= 1;
mlo, mhi := do_pivot(data, a, b, less);
if mlo-a < b-mhi {
_quick_sort_proc(data, a, mlo, max_depth, less);
_quick_sort_less(data, a, mlo, max_depth, less);
a = mhi;
} else {
_quick_sort_proc(data, mhi, b, max_depth, less);
_quick_sort_less(data, mhi, b, max_depth, less);
b = mlo;
}
}
@@ -357,12 +419,12 @@ _quick_sort_proc :: proc(data: $T/[]$E, a, b, max_depth: int, less: proc(i, j: E
swap(data, i, i-6);
}
}
_insertion_sort_proc(data, a, b, less);
_insertion_sort_less(data, a, b, less);
}
}
@(private)
_insertion_sort_proc :: proc(data: $T/[]$E, a, b: int, less: proc(i, j: E) -> bool) {
_insertion_sort_less :: proc(data: $T/[]$E, a, b: int, less: proc(i, j: E) -> bool) {
for i in a+1..<b {
for j := i; j > a && less(data[j], data[j-1]); j -= 1 {
swap(data, j, j-1);
@@ -371,7 +433,7 @@ _insertion_sort_proc :: proc(data: $T/[]$E, a, b: int, less: proc(i, j: E) -> bo
}
@(private)
_heap_sort_proc :: proc(data: $T/[]$E, a, b: int, less: proc(i, j: E) -> bool) {
_heap_sort_less :: proc(data: $T/[]$E, a, b: int, less: proc(i, j: E) -> bool) {
sift_down :: proc(data: T, lo, hi, first: int, less: proc(i, j: E) -> bool) {
root := lo;
for {
@@ -405,3 +467,162 @@ _heap_sort_proc :: proc(data: $T/[]$E, a, b: int, less: proc(i, j: E) -> bool) {
@(private)
_quick_sort_cmp :: proc(data: $T/[]$E, a, b, max_depth: int, cmp: proc(i, j: E) -> Ordering) {
median3 :: proc(data: T, m1, m0, m2: int, cmp: proc(i, j: E) -> Ordering) {
if cmp(data[m1], data[m0]) == .Less {
swap(data, m1, m0);
}
if cmp(data[m2], data[m1]) == .Less {
swap(data, m2, m1);
if cmp(data[m1], data[m0]) == .Less {
swap(data, m1, m0);
}
}
}
do_pivot :: proc(data: T, lo, hi: int, cmp: proc(i, j: E) -> Ordering) -> (midlo, midhi: int) {
m := int(uint(lo+hi)>>1);
if hi-lo > 40 {
s := (hi-lo)/8;
median3(data, lo, lo+s, lo+s*2, cmp);
median3(data, m, m-s, m+s, cmp);
median3(data, hi-1, hi-1-s, hi-1-s*2, cmp);
}
median3(data, lo, m, hi-1, cmp);
pivot := lo;
a, c := lo+1, hi-1;
for ; a < c && cmp(data[a], data[pivot]) == .Less; a += 1 {
}
b := a;
for {
for ; b < c && cmp(data[pivot], data[b]) >= .Equal; b += 1 { // data[b] <= pivot
}
for ; b < c && cmp(data[pivot], data[c-1]) == .Less; c -=1 { // data[c-1] > pivot
}
if b >= c {
break;
}
swap(data, b, c-1);
b += 1;
c -= 1;
}
protect := hi-c < 5;
if !protect && hi-c < (hi-lo)/4 {
dups := 0;
if cmp(data[pivot], data[hi-1]) != .Less {
swap(data, c, hi-1);
c += 1;
dups += 1;
}
if cmp(data[b-1], data[pivot]) != .Less {
b -= 1;
dups += 1;
}
if cmp(data[m], data[pivot]) != .Less {
swap(data, m, b-1);
b -= 1;
dups += 1;
}
protect = dups > 1;
}
if protect {
for {
for ; a < b && cmp(data[b-1], data[pivot]) >= .Equal; b -= 1 {
}
for ; a < b && cmp(data[a], data[pivot]) == .Less; a += 1 {
}
if a >= b {
break;
}
swap(data, a, b-1);
a += 1;
b -= 1;
}
}
swap(data, pivot, b-1);
return b-1, c;
}
a, b, max_depth := a, b, max_depth;
if b-a > 12 { // only use shell sort for lengths <= 12
if max_depth == 0 {
_heap_sort_cmp(data, a, b, cmp);
return;
}
max_depth -= 1;
mlo, mhi := do_pivot(data, a, b, cmp);
if mlo-a < b-mhi {
_quick_sort_cmp(data, a, mlo, max_depth, cmp);
a = mhi;
} else {
_quick_sort_cmp(data, mhi, b, max_depth, cmp);
b = mlo;
}
}
if b-a > 1 {
// Shell short with gap 6
for i in a+6..<b {
if cmp(data[i], data[i-6]) == .Less {
swap(data, i, i-6);
}
}
_insertion_sort_cmp(data, a, b, cmp);
}
}
@(private)
_insertion_sort_cmp :: proc(data: $T/[]$E, a, b: int, cmp: proc(i, j: E) -> Ordering) {
for i in a+1..<b {
for j := i; j > a && cmp(data[j], data[j-1]) == .Less; j -= 1 {
swap(data, j, j-1);
}
}
}
@(private)
_heap_sort_cmp :: proc(data: $T/[]$E, a, b: int, cmp: proc(i, j: E) -> Ordering) {
sift_down :: proc(data: T, lo, hi, first: int, cmp: proc(i, j: E) -> Ordering) {
root := lo;
for {
child := 2*root + 1;
if child >= hi {
break;
}
if child+1 < hi && cmp(data[first+child], data[first+child+1]) == .Less {
child += 1;
}
if cmp(data[first+root], data[first+child]) >= .Equal {
return;
}
swap(data, 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, i, hi, first, cmp);
}
for i := hi-1; i >= 0; i -= 1 {
swap(data, first, first+i);
sift_down(data, lo, i, first, cmp);
}
}
+9 -54
View File
@@ -1,6 +1,7 @@
package sort
import "core:mem"
import _slice "core:slice"
import "intrinsics"
_ :: intrinsics;
@@ -29,9 +30,11 @@ sort :: proc(it: Interface) {
}
@(deprecated="use slice.sort")
slice :: proc(array: $T/[]$E) where ORD(E) {
s := array;
sort(slice_interface(&s));
_slice.sort(array);
// s := array;
// sort(slice_interface(&s));
}
slice_interface :: proc(s: ^$T/[]$E) -> Interface where ORD(E) {
@@ -76,7 +79,10 @@ reverse_sort :: proc(it: Interface) {
sort(reverse_interface(&it));
}
@(deprecated="use slice.reverse")
reverse_slice :: proc(array: $T/[]$E) where ORD(E) {
_slice.reverse(array);
/*
s := array;
sort(Interface{
collection = rawptr(&s),
@@ -93,6 +99,7 @@ reverse_slice :: proc(array: $T/[]$E) where ORD(E) {
s[i], s[j] = s[j], s[i];
},
});
*/
}
@@ -678,55 +685,3 @@ compare_strings :: proc(a, b: string) -> int {
y := transmute(mem.Raw_String)b;
return mem.compare_byte_ptrs(x.data, y.data, min(x.len, y.len));
}
@(deprecated="use slice.binary_search")
binary_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
where intrinsics.type_is_ordered(T) #no_bounds_check {
n := len(array);
switch n {
case 0:
return -1, false;
case 1:
if array[0] == key {
return 0, true;
}
return -1, false;
}
lo, hi := 0, n-1;
for array[hi] != array[lo] && key >= array[lo] && key <= array[hi] {
when intrinsics.type_is_ordered_numeric(T) {
// NOTE(bill): This is technically interpolation search
m := lo + int((key - array[lo]) * T(hi - lo) / (array[hi] - array[lo]));
} else {
m := (lo + hi)/2;
}
switch {
case array[m] < key:
lo = m + 1;
case key < array[m]:
hi = m - 1;
case:
return m, true;
}
}
if key == array[lo] {
return lo, true;
}
return -1, false;
}
@(deprecated="use slice.linear_search")
linear_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
where intrinsics.type_is_comparable(T) #no_bounds_check {
for x, i in array {
if x == key {
return i, true;
}
}
return -1, false;
}
+34
View File
@@ -165,6 +165,40 @@ concatenate :: proc(a: []string, allocator := context.allocator) -> string {
return string(b);
}
/*
`rune_offset` and `rune_length` are in runes, not bytes.
If `rune_length` <= 0, then it'll return the remainder of the string starting with `rune_offset`.
*/
cut :: proc(s: string, rune_offset := int(0), rune_length := int(0), allocator := context.allocator) -> (res: string) {
s := s; rune_length := rune_length;
l := utf8.rune_count_in_string(s);
if rune_offset >= l { return ""; }
if rune_offset == 0 && rune_length <= 0 {
return clone(s, allocator);
}
if rune_length == 0 { rune_length = l; }
bytes_needed := min(rune_length * 4, len(s));
buf := make([]u8, bytes_needed, allocator);
byte_offset := 0;
for i := 0; i < l; i += 1 {
_, w := utf8.decode_rune_in_string(s);
if i >= rune_offset {
for j := 0; j < w; j += 1 {
buf[byte_offset+j] = s[j];
}
byte_offset += w;
}
if rune_length > 0 {
if i == rune_offset + rune_length - 1 { break; }
}
s = s[w:];
}
return string(buf[:byte_offset]);
}
@private
_split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocator) -> []string {
s, n := s_, n_;
+1
View File
@@ -1,3 +1,4 @@
// +build windows
package win32
import "core:strings";
+1
View File
@@ -1,3 +1,4 @@
// +build windows
package sys_windows
foreign import advapi32 "system:Advapi32.lib"
+1
View File
@@ -1,3 +1,4 @@
// +build windows
package sys_windows
foreign import bcrypt "system:Bcrypt.lib"
+1
View File
@@ -1,3 +1,4 @@
// +build windows
package sys_windows
foreign import kernel32 "system:Kernel32.lib"
+1
View File
@@ -1,3 +1,4 @@
// +build windows
package sys_windows
foreign import netapi32 "system:Netapi32.lib"
+1
View File
@@ -1,3 +1,4 @@
// +build windows
package sys_windows
foreign import ntdll_lib "system:ntdll.lib"
+1
View File
@@ -1,3 +1,4 @@
// +build windows
package sys_windows
foreign import shell32 "system:Shell32.lib"
+1
View File
@@ -1,3 +1,4 @@
// +build windows
package sys_windows
foreign import Synchronization "system:Synchronization.lib"
+3
View File
@@ -1,3 +1,4 @@
// +build windows
package sys_windows
import "core:c"
@@ -396,6 +397,8 @@ when size_of(uintptr) == 4 {
szDescription: [WSADESCRIPTION_LEN + 1]u8,
szSystemStatus: [WSASYS_STATUS_LEN + 1]u8,
}
} else {
#panic("unknown word size");
}
WSABUF :: struct {
+1
View File
@@ -1,3 +1,4 @@
// +build windows
package sys_windows
foreign import userenv "system:Userenv.lib"
+1
View File
@@ -1,3 +1,4 @@
// +build windows
package sys_windows
import "core:strings"
+1
View File
@@ -1,3 +1,4 @@
// +build windows
package sys_windows
foreign import ws2_32 "system:Ws2_32.lib"
+1 -6
View File
@@ -68,12 +68,7 @@ Thread_Os_Specific :: struct {
thread_create :: proc(procedure: Thread_Proc) -> ^Thread {
__windows_thread_entry_proc :: proc "stdcall" (t_: rawptr) -> win32.DWORD {
t := (^Thread)(t_);
context = runtime.default_context();
c := context;
if ic, ok := t.init_context.?; ok {
c = ic;
}
context = c;
context = or_else(t.init_context.?, runtime.default_context());
t.procedure(t);
+1 -6
View File
@@ -39,7 +39,6 @@ Thread_Os_Specific :: struct #align 16 {
_create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
__linux_thread_entry_proc :: proc "c" (t: rawptr) -> rawptr {
context = runtime.default_context();
t := (^Thread)(t);
sync.condition_wait_for(&t.start_gate);
sync.condition_destroy(&t.start_gate);
@@ -47,11 +46,7 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
t.start_gate = {};
t.start_mutex = {};
c := context;
if ic, ok := t.init_context.?; ok {
c = ic;
}
context = c;
context = or_else(t.init_context.?, runtime.default_context());
t.procedure(t);
+1 -6
View File
@@ -23,12 +23,7 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
__windows_thread_entry_proc :: proc "stdcall" (t_: rawptr) -> win32.DWORD {
t := (^Thread)(t_);
context = runtime.default_context();
c := context;
if ic, ok := t.init_context.?; ok {
c = ic;
}
context = c;
context = or_else(t.init_context.?, runtime.default_context());
t.procedure(t);
+35
View File
@@ -1999,6 +1999,40 @@ relative_data_types :: proc() {
fmt.println(rel_slice[1]);
}
or_else_procedure :: proc() {
fmt.println("\n#'or_else'");
// IMPORTANT NOTE: 'or_else' is experimental features and subject to change/removal
{
// 'or_else' does a similar value check as 'try' but instead of doing an
// early return, it will give a default value to be used instead
m: map[string]int;
i: int;
ok: bool;
if i, ok = m["hellope"]; !ok {
i = 123;
}
// The above can be mapped to 'or_else'
i = or_else(m["hellope"], 123);
assert(i == 123);
}
{
// 'or_else' can be used with type assertions too, as they
// have optional ok semantics
v: union{int, f64};
i: int;
i = or_else(v.(int), 123);
i = or_else(v.?, 123); // Type inference magic
assert(i == 123);
m: Maybe(int);
i = or_else(m.?, 456);
assert(i == 456);
}
}
main :: proc() {
when true {
the_basics();
@@ -2031,5 +2065,6 @@ main :: proc() {
union_maybe();
explicit_context_definition();
relative_data_types();
or_else_procedure();
}
}
+13
View File
@@ -89,6 +89,19 @@ template <typename T>
Slice<T> slice_from_array(Array<T> const &a) {
return {a.data, a.count};
}
template <typename T>
Slice<T> slice_array(Array<T> const &array, isize lo, isize hi) {
GB_ASSERT(0 <= lo && lo <= hi && hi <= array.count);
Slice<T> out = {};
isize len = hi-lo;
if (len > 0) {
out.data = array.data+lo;
out.count = len;
}
return out;
}
template <typename T>
Slice<T> slice_clone(gbAllocator const &allocator, Slice<T> const &a) {
T *data = cast(T *)gb_alloc_copy_align(allocator, a.data, a.count*gb_size_of(T), gb_align_of(T));
+174 -1097
View File
File diff suppressed because it is too large Load Diff
+24 -10
View File
@@ -3,6 +3,11 @@
#include <sys/sysctl.h>
#endif
// #if defined(GB_SYSTEM_WINDOWS)
#define DEFAULT_TO_THREADED_CHECKER
// #endif
enum TargetOsKind {
TargetOs_Invalid,
@@ -203,11 +208,16 @@ struct BuildContext {
bool warnings_as_errors;
bool show_error_line;
bool ignore_lazy;
bool use_subsystem_windows;
bool ignore_microsoft_magic;
bool linker_map_file;
bool use_separate_modules;
bool threaded_checker;
bool show_debug_messages;
u32 cmd_doc_flags;
Array<String> extra_packages;
@@ -513,8 +523,8 @@ String internal_odin_root_dir(void) {
}
len += 1; // NOTE(bill): It needs an extra 1 for some reason
gb_mutex_lock(&string_buffer_mutex);
defer (gb_mutex_unlock(&string_buffer_mutex));
mutex_lock(&string_buffer_mutex);
defer (mutex_unlock(&string_buffer_mutex));
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
defer (gb_temp_arena_memory_end(tmp));
@@ -571,8 +581,8 @@ String internal_odin_root_dir(void) {
}
}
gb_mutex_lock(&string_buffer_mutex);
defer (gb_mutex_unlock(&string_buffer_mutex));
mutex_lock(&string_buffer_mutex);
defer (mutex_unlock(&string_buffer_mutex));
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
defer (gb_temp_arena_memory_end(tmp));
@@ -648,8 +658,8 @@ String internal_odin_root_dir(void) {
array_resize(&path_buf, 2*path_buf.count + 300);
}
gb_mutex_lock(&string_buffer_mutex);
defer (gb_mutex_unlock(&string_buffer_mutex));
mutex_lock(&string_buffer_mutex);
defer (mutex_unlock(&string_buffer_mutex));
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
defer (gb_temp_arena_memory_end(tmp));
@@ -678,8 +688,8 @@ String internal_odin_root_dir(void) {
#if defined(GB_SYSTEM_WINDOWS)
String path_to_fullpath(gbAllocator a, String s) {
String result = {};
gb_mutex_lock(&string_buffer_mutex);
defer (gb_mutex_unlock(&string_buffer_mutex));
mutex_lock(&string_buffer_mutex);
defer (mutex_unlock(&string_buffer_mutex));
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
defer (gb_temp_arena_memory_end(tmp));
@@ -706,9 +716,9 @@ String path_to_fullpath(gbAllocator a, String s) {
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
String path_to_fullpath(gbAllocator a, String s) {
char *p;
gb_mutex_lock(&string_buffer_mutex);
mutex_lock(&string_buffer_mutex);
p = realpath(cast(char *)s.text, 0);
gb_mutex_unlock(&string_buffer_mutex);
mutex_unlock(&string_buffer_mutex);
if(p == nullptr) return String{};
return make_string_c(p);
}
@@ -820,6 +830,10 @@ void init_build_context(TargetMetrics *cross_target) {
bc->max_align = metrics->max_align;
bc->link_flags = str_lit(" ");
#if defined(DEFAULT_TO_THREADED_CHECKER)
bc->threaded_checker = true;
#endif
// NOTE(zangent): The linker flags to set the build architecture are different
// across OSs. It doesn't make sense to allocate extra data on the heap
+147 -24
View File
@@ -48,6 +48,70 @@ BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_boolean_end -
};
void check_try_split_types(CheckerContext *c, Operand *x, String const &name, Type **left_type_, Type **right_type_) {
Type *left_type = nullptr;
Type *right_type = nullptr;
if (x->type->kind == Type_Tuple) {
auto const &vars = x->type->Tuple.variables;
auto lhs = array_slice(vars, 0, vars.count-1);
auto rhs = vars[vars.count-1];
if (lhs.count == 1) {
left_type = lhs[0]->type;
} else if (lhs.count != 0) {
left_type = alloc_type_tuple();
left_type->Tuple.variables = array_make_from_ptr(lhs.data, lhs.count, lhs.count);
}
right_type = rhs->type;
} else {
check_promote_optional_ok(c, x, &left_type, &right_type);
}
if (left_type_) *left_type_ = left_type;
if (right_type_) *right_type_ = right_type;
if (!is_type_boolean(right_type)) {
gbString str = type_to_string(right_type);
error(x->expr, "'%.*s' expects an \"optional ok\" like value, got %s", LIT(name), str);
gb_string_free(str);
}
// if (!type_has_nil(right_type) && !is_type_boolean(right_type)) {
// gbString str = type_to_string(right_type);
// error(x->expr, "'%.*s' expects an \"optional ok\" like value, or an n-valued expression where the last value is either a boolean or can be compared against 'nil', got %s", LIT(name), str);
// gb_string_free(str);
// }
}
void check_try_expr_no_value_error(CheckerContext *c, String const &name, Operand const &x, Type *type_hint) {
// TODO(bill): better error message
gbString t = type_to_string(x.type);
error(x.expr, "'%.*s' does not return a value, value is of type %s", LIT(name), t);
if (is_type_union(type_deref(x.type))) {
Type *bsrc = base_type(type_deref(x.type));
gbString th = nullptr;
if (type_hint != nullptr) {
GB_ASSERT(bsrc->kind == Type_Union);
for_array(i, bsrc->Union.variants) {
Type *vt = bsrc->Union.variants[i];
if (are_types_identical(vt, type_hint)) {
th = type_to_string(type_hint);
break;
}
}
}
gbString expr_str = expr_to_string(x.expr);
if (th != nullptr) {
error_line("\tSuggestion: was a type assertion such as %s.(%s) or %s.? wanted?\n", expr_str, th, expr_str);
} else {
error_line("\tSuggestion: was a type assertion such as %s.(T) or %s.? wanted?\n", expr_str, expr_str);
}
gb_string_free(th);
gb_string_free(expr_str);
}
gb_string_free(t);
}
bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
ast_node(ce, CallExpr, call);
@@ -86,6 +150,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
// NOTE(bill): The first arg may be a Type, this will be checked case by case
break;
case BuiltinProc_or_else:
// NOTE(bill): The arguments may be multi-expr
break;
case BuiltinProc_DIRECTIVE: {
ast_node(bd, BasicDirective, ce->proc);
String name = bd->name.string;
@@ -174,7 +242,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
String original_string = o.value.value_string;
gbMutex *ignore_mutex = nullptr;
BlockingMutex *ignore_mutex = nullptr;
String path = {};
bool ok = determine_path_from_string(ignore_mutex, call, base_dir, original_string, &path);
@@ -512,6 +580,11 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
error(ce->args[0],
"'%s' has no field named '%.*s'", type_str, LIT(arg->token.string));
gb_string_free(type_str);
Type *bt = base_type(type);
if (bt->kind == Type_Struct) {
check_did_you_mean_type(arg->token.string, bt->Struct.fields);
}
return false;
}
if (sel.indirect) {
@@ -672,7 +745,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
return false;
}
if (op.value.value_integer.neg) {
if (big_int_is_neg(&op.value.value_integer)) {
error(op.expr, "Negative 'swizzle' index");
return false;
}
@@ -722,10 +795,12 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
if (x.mode == Addressing_Constant &&
y.mode == Addressing_Constant) {
if (is_type_numeric(x.type) && exact_value_imag(x.value).value_float == 0) {
x.value = exact_value_to_float(x.value);
y.value = exact_value_to_float(y.value);
if (is_type_numeric(x.type) && x.value.kind == ExactValue_Float) {
x.type = t_untyped_float;
}
if (is_type_numeric(y.type) && exact_value_imag(y.value).value_float == 0) {
if (is_type_numeric(y.type) && y.value.kind == ExactValue_Float) {
y.type = t_untyped_float;
}
}
@@ -809,16 +884,20 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
y.mode == Addressing_Constant &&
z.mode == Addressing_Constant &&
w.mode == Addressing_Constant) {
if (is_type_numeric(x.type) && exact_value_imag(x.value).value_float == 0) {
x.value = exact_value_to_float(x.value);
y.value = exact_value_to_float(y.value);
z.value = exact_value_to_float(z.value);
w.value = exact_value_to_float(w.value);
if (is_type_numeric(x.type) && x.value.kind == ExactValue_Float) {
x.type = t_untyped_float;
}
if (is_type_numeric(y.type) && exact_value_imag(y.value).value_float == 0) {
if (is_type_numeric(y.type) && y.value.kind == ExactValue_Float) {
y.type = t_untyped_float;
}
if (is_type_numeric(z.type) && exact_value_imag(z.value).value_float == 0) {
if (is_type_numeric(z.type) && z.value.kind == ExactValue_Float) {
z.type = t_untyped_float;
}
if (is_type_numeric(w.type) && exact_value_imag(w.value).value_float == 0) {
if (is_type_numeric(w.type) && w.value.kind == ExactValue_Float) {
w.type = t_untyped_float;
}
}
@@ -1041,7 +1120,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
// TODO(bill): Should I copy each of the entities or is this good enough?
gb_memmove_array(tuple->Tuple.variables.data, type->Struct.fields.data, variable_count);
} else if (is_type_array(type)) {
isize variable_count = type->Array.count;
isize variable_count = cast(isize)type->Array.count;
array_init(&tuple->Tuple.variables, a, variable_count);
for (isize i = 0; i < variable_count; i++) {
tuple->Tuple.variables[i] = alloc_entity_array_elem(nullptr, blank_token, type->Array.elem, cast(i32)i);
@@ -1411,7 +1490,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (operand->mode == Addressing_Constant) {
switch (operand->value.kind) {
case ExactValue_Integer:
operand->value.value_integer.neg = false;
mp_abs(&operand->value.value_integer, &operand->value.value_integer);
break;
case ExactValue_Float:
operand->value.value_float = gb_abs(operand->value.value_float);
@@ -1559,7 +1638,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
}
StringSet name_set = {};
string_set_init(&name_set, temporary_allocator(), 2*ce->args.count);
string_set_init(&name_set, heap_allocator(), 2*ce->args.count);
for_array(i, ce->args) {
String name = {};
@@ -1613,7 +1692,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
Ast *dummy_node_struct = alloc_ast_node(nullptr, Ast_Invalid);
Ast *dummy_node_soa = alloc_ast_node(nullptr, Ast_Invalid);
Scope *s = create_scope(builtin_pkg->scope);
Scope *s = create_scope(c->info, builtin_pkg->scope);
auto fields = array_make<Entity *>(permanent_allocator(), 0, types.count);
for_array(i, types) {
@@ -1713,6 +1792,46 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
case BuiltinProc_or_else: {
GB_ASSERT(ce->args.count == 2);
Ast *arg = ce->args[0];
Ast *default_value = ce->args[1];
Operand x = {};
Operand y = {};
check_multi_expr_with_type_hint(c, &x, arg, type_hint);
if (x.mode == Addressing_Invalid) {
operand->mode = Addressing_Value;
operand->type = t_invalid;
return false;
}
check_multi_expr_with_type_hint(c, &y, default_value, x.type);
error_operand_no_value(&y);
if (y.mode == Addressing_Invalid) {
operand->mode = Addressing_Value;
operand->type = t_invalid;
return false;
}
Type *left_type = nullptr;
Type *right_type = nullptr;
check_try_split_types(c, &x, builtin_name, &left_type, &right_type);
add_type_and_value(&c->checker->info, arg, x.mode, x.type, x.value);
if (left_type != nullptr) {
check_assignment(c, &y, left_type, builtin_name);
} else {
check_try_expr_no_value_error(c, builtin_name, x, type_hint);
}
if (left_type == nullptr) {
left_type = t_invalid;
}
operand->mode = Addressing_Value;
operand->type = left_type;
return true;
}
case BuiltinProc_simd_vector: {
Operand x = {};
@@ -1724,7 +1843,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
operand->type = t_invalid;
return false;
}
if (x.value.value_integer.neg) {
if (big_int_is_neg(&x.value.value_integer)) {
error(call, "Negative vector element length");
operand->mode = Addressing_Type;
operand->type = t_invalid;
@@ -1764,7 +1883,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
operand->type = t_invalid;
return false;
}
if (x.value.value_integer.neg) {
if (big_int_is_neg(&x.value.value_integer)) {
error(call, "Negative array element length");
operand->mode = Addressing_Type;
operand->type = t_invalid;
@@ -1797,14 +1916,14 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (is_type_array(elem)) {
Type *old_array = base_type(elem);
soa_struct = alloc_type_struct();
soa_struct->Struct.fields = array_make<Entity *>(heap_allocator(), old_array->Array.count);
soa_struct->Struct.tags = array_make<String>(heap_allocator(), old_array->Array.count);
soa_struct->Struct.fields = array_make<Entity *>(heap_allocator(), cast(isize)old_array->Array.count);
soa_struct->Struct.tags = array_make<String>(heap_allocator(), cast(isize)old_array->Array.count);
soa_struct->Struct.node = operand->expr;
soa_struct->Struct.soa_kind = StructSoa_Fixed;
soa_struct->Struct.soa_elem = elem;
soa_struct->Struct.soa_count = count;
scope = create_scope(c->scope);
scope = create_scope(c->info, c->scope);
soa_struct->Struct.scope = scope;
String params_xyzw[4] = {
@@ -1814,14 +1933,14 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
str_lit("w")
};
for (i64 i = 0; i < old_array->Array.count; i++) {
for (isize i = 0; i < cast(isize)old_array->Array.count; i++) {
Type *array_type = alloc_type_array(old_array->Array.elem, count);
Token token = {};
token.string = params_xyzw[i];
Entity *new_field = alloc_entity_field(scope, token, array_type, false, cast(i32)i);
soa_struct->Struct.fields[i] = new_field;
add_entity(c->checker, scope, nullptr, new_field);
add_entity(c, scope, nullptr, new_field);
add_entity_use(c, nullptr, new_field);
}
@@ -1837,7 +1956,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
soa_struct->Struct.soa_elem = elem;
soa_struct->Struct.soa_count = count;
scope = create_scope(old_struct->Struct.scope->parent);
scope = create_scope(c->info, old_struct->Struct.scope->parent);
soa_struct->Struct.scope = scope;
for_array(i, old_struct->Struct.fields) {
@@ -1846,7 +1965,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
Type *array_type = alloc_type_array(old_field->type, count);
Entity *new_field = alloc_entity_field(scope, old_field->token, array_type, false, old_field->Variable.field_src_index);
soa_struct->Struct.fields[i] = new_field;
add_entity(c->checker, scope, nullptr, new_field);
add_entity(c, scope, nullptr, new_field);
} else {
soa_struct->Struct.fields[i] = old_field;
}
@@ -1858,7 +1977,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
Token token = {};
token.string = str_lit("Base_Type");
Entity *base_type_entity = alloc_entity_type_name(scope, token, elem, EntityState_Resolved);
add_entity(c->checker, scope, nullptr, base_type_entity);
add_entity(c, scope, nullptr, base_type_entity);
add_type_info_type(c, soa_struct);
@@ -2772,7 +2891,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (bt->kind == Type_Proc) {
count = bt->Proc.param_count;
if (index < count) {
param = bt->Proc.params->Tuple.variables[index];
param = bt->Proc.params->Tuple.variables[cast(isize)index];
}
}
@@ -2831,7 +2950,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (bt->kind == Type_Proc) {
count = bt->Proc.result_count;
if (index < count) {
param = bt->Proc.results->Tuple.variables[index];
param = bt->Proc.results->Tuple.variables[cast(isize)index];
}
}
@@ -2974,6 +3093,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
error(ce->args[0],
"'%s' has no field named '%.*s'", type_str, LIT(field_name));
gb_string_free(type_str);
if (bt->kind == Type_Struct) {
check_did_you_mean_type(field_name, bt->Struct.fields);
}
return false;
}
if (sel.indirect) {
+68 -66
View File
@@ -313,7 +313,7 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
if (is_blank_ident(name)) {
continue;
}
add_entity(ctx->checker, parent, nullptr, f);
add_entity(ctx, parent, nullptr, f);
}
}
}
@@ -724,6 +724,7 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
e->Procedure.is_export = ac.is_export;
e->deprecated_message = ac.deprecated_message;
e->warning_message = ac.warning_message;
ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
if (ac.has_disabled_proc) {
if (ac.disabled_proc) {
@@ -786,7 +787,7 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
GB_ASSERT(pl->body->kind == Ast_BlockStmt);
if (!pt->is_polymorphic) {
check_procedure_later(ctx->checker, ctx->file, e->token, d, proc_type, pl->body, pl->tags);
check_procedure_later(ctx, ctx->file, e->token, d, proc_type, pl->body, pl->tags);
}
} else if (!is_foreign) {
if (e->Procedure.is_export) {
@@ -808,7 +809,7 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
if (ac.deferred_procedure.entity != nullptr) {
e->Procedure.deferred_procedure = ac.deferred_procedure;
array_add(&ctx->checker->procs_with_deferred_to_check, e);
mpmc_enqueue(&ctx->checker->procs_with_deferred_to_check, e);
}
if (is_foreign) {
@@ -821,6 +822,8 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
init_entity_foreign_library(ctx, e);
mutex_lock(&ctx->info->foreign_mutex);
auto *fp = &ctx->info->foreigns;
StringHashKey key = string_hash_string(name);
Entity **found = string_map_get(fp, key);
@@ -847,12 +850,16 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
} else {
string_map_set(fp, key, e);
}
mutex_unlock(&ctx->info->foreign_mutex);
} else {
String name = e->token.string;
if (e->Procedure.link_name.len > 0) {
name = e->Procedure.link_name;
}
if (e->Procedure.link_name.len > 0 || is_export) {
mutex_lock(&ctx->info->foreign_mutex);
auto *fp = &ctx->info->foreigns;
StringHashKey key = string_hash_string(name);
Entity **found = string_map_get(fp, key);
@@ -869,6 +876,8 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
} else {
string_map_set(fp, key, e);
}
mutex_unlock(&ctx->info->foreign_mutex);
}
}
}
@@ -893,7 +902,7 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr,
}
if (ac.require_declaration) {
array_add(&ctx->info->required_global_variables, e);
mpmc_enqueue(&ctx->info->required_global_variable_queue, e);
}
@@ -1107,71 +1116,63 @@ void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_
if (e->state == EntityState_Resolved) {
return;
}
if (e->flags & EntityFlag_Lazy) {
mutex_lock(&ctx->info->lazy_mutex);
}
String name = e->token.string;
if (e->type != nullptr || e->state != EntityState_Unresolved) {
error(e->token, "Illegal declaration cycle of `%.*s`", LIT(name));
return;
}
GB_ASSERT(e->state == EntityState_Unresolved);
#if 0
char buf[256] = {};
isize n = gb_snprintf(buf, 256, "%.*s %d", LIT(name), e->kind);
Timings timings = {};
timings_init(&timings, make_string(cast(u8 *)buf, n-1), 16);
defer ({
timings_print_all(&timings);
timings_destroy(&timings);
});
#define TIME_SECTION(str) timings_start_section(&timings, str_lit(str))
#else
#define TIME_SECTION(str)
#endif
if (d == nullptr) {
d = decl_info_of_entity(e);
} else {
GB_ASSERT(e->state == EntityState_Unresolved);
if (d == nullptr) {
// TODO(bill): Err here?
e->type = t_invalid;
e->state = EntityState_Resolved;
set_base_type(named_type, t_invalid);
return;
// GB_PANIC("'%.*s' should been declared!", LIT(name));
d = decl_info_of_entity(e);
if (d == nullptr) {
// TODO(bill): Err here?
e->type = t_invalid;
e->state = EntityState_Resolved;
set_base_type(named_type, t_invalid);
goto end;
}
}
CheckerContext c = *ctx;
c.scope = d->scope;
c.decl = d;
c.type_level = 0;
e->parent_proc_decl = c.curr_proc_decl;
e->state = EntityState_InProgress;
switch (e->kind) {
case Entity_Variable:
check_global_variable_decl(&c, e, d->type_expr, d->init_expr);
break;
case Entity_Constant:
check_const_decl(&c, e, d->type_expr, d->init_expr, named_type);
break;
case Entity_TypeName: {
check_type_decl(&c, e, d->init_expr, named_type);
break;
}
case Entity_Procedure:
check_proc_decl(&c, e, d);
break;
case Entity_ProcGroup:
check_proc_group_decl(&c, e, d);
break;
}
e->state = EntityState_Resolved;
}
CheckerContext c = *ctx;
c.scope = d->scope;
c.decl = d;
c.type_level = 0;
e->parent_proc_decl = c.curr_proc_decl;
e->state = EntityState_InProgress;
switch (e->kind) {
case Entity_Variable:
check_global_variable_decl(&c, e, d->type_expr, d->init_expr);
break;
case Entity_Constant:
check_const_decl(&c, e, d->type_expr, d->init_expr, named_type);
break;
case Entity_TypeName: {
check_type_decl(&c, e, d->init_expr, named_type);
break;
end:;
// NOTE(bill): Add it to the list of checked entities
if (e->flags & EntityFlag_Lazy) {
array_add(&ctx->info->entities, e);
mutex_unlock(&ctx->info->lazy_mutex);
}
case Entity_Procedure:
check_proc_decl(&c, e, d);
break;
case Entity_ProcGroup:
check_proc_group_decl(&c, e, d);
break;
}
e->state = EntityState_Resolved;
#undef TIME_SECTION
}
@@ -1265,7 +1266,8 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
Entity *uvar = using_entities[i].uvar;
Entity *prev = scope_insert(ctx->scope, uvar);
if (prev != nullptr) {
error(e->token, "Namespace collision while 'using' '%.*s' of: %.*s", LIT(e->token.string), LIT(prev->token.string));
error(e->token, "Namespace collision while 'using' procedure argument '%.*s' of: %.*s", LIT(e->token.string), LIT(prev->token.string));
error_line("%.*s != %.*s\n", LIT(uvar->token.string), LIT(prev->token.string));
break;
}
}
@@ -1317,6 +1319,8 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
if (ps->flags & (ScopeFlag_File & ScopeFlag_Pkg & ScopeFlag_Global)) {
return;
} else {
mutex_lock(&ctx->info->deps_mutex);
// NOTE(bill): Add the dependencies from the procedure literal (lambda)
// But only at the procedure level
for_array(i, decl->deps.entries) {
@@ -1327,10 +1331,8 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
Type *t = decl->type_info_deps.entries[i].ptr;
ptr_set_add(&decl->parent->type_info_deps, t);
}
mutex_unlock(&ctx->info->deps_mutex);
}
}
}
+259 -159
View File
@@ -48,8 +48,8 @@ struct CallArgumentData {
};
struct PolyProcData {
Entity * gen_entity;
ProcInfo proc_info;
Entity * gen_entity;
ProcInfo *proc_info;
};
struct ValidIndexAndScore {
@@ -73,6 +73,7 @@ typedef CALL_ARGUMENT_CHECKER(CallArgumentCheckerType);
void check_expr (CheckerContext *c, Operand *operand, Ast *expression);
void check_multi_expr (CheckerContext *c, Operand *operand, Ast *expression);
void check_multi_expr_or_type (CheckerContext *c, Operand *operand, Ast *expression);
void check_multi_expr_with_type_hint(CheckerContext *c, Operand *o, Ast *e, Type *type_hint);
void check_expr_or_type (CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint);
ExprKind check_expr_base (CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint);
void check_expr_with_type_hint (CheckerContext *c, Operand *o, Ast *e, Type *t);
@@ -86,7 +87,7 @@ void check_not_tuple (CheckerContext *c, Operand *operand);
void convert_to_typed (CheckerContext *c, Operand *operand, Type *target_type);
gbString expr_to_string (Ast *expression);
void check_proc_body (CheckerContext *c, Token token, DeclInfo *decl, Type *type, Ast *body);
void update_expr_type (CheckerContext *c, Ast *e, Type *type, bool final);
void update_untyped_expr_type (CheckerContext *c, Ast *e, Type *type, bool final);
bool check_is_terminating (Ast *node, String const &label);
bool check_has_break (Ast *stmt, String const &label, bool implicit);
void check_stmt (CheckerContext *c, Ast *node, u32 flags);
@@ -111,6 +112,8 @@ Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_typ_expr, As
bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint);
void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_);
Entity *entity_from_expr(Ast *expr) {
expr = unparen_expr(expr);
switch (expr->kind) {
@@ -212,6 +215,7 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
// //
///////////////////////////////////////////////////////////////////////////////
CheckerInfo *info = c->info;
if (base_entity == nullptr) {
return false;
@@ -220,11 +224,14 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
if (!is_type_proc(base_entity->type)) {
return false;
}
String name = base_entity->token.string;
Type *src = base_type(base_entity->type);
Type *dst = nullptr;
if (type != nullptr) dst = base_type(type);
if (type != nullptr) {
dst = base_type(type);
}
if (param_operands == nullptr) {
GB_ASSERT(dst != nullptr);
@@ -233,6 +240,8 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
GB_ASSERT(dst == nullptr);
}
mutex_lock(&info->gen_procs_mutex);
defer (mutex_unlock(&info->gen_procs_mutex));
if (!src->Proc.is_polymorphic || src->Proc.is_poly_specialized) {
return false;
@@ -276,10 +285,9 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
});
CheckerContext nctx = *c;
Scope *scope = create_scope(base_entity->scope);
Scope *scope = create_scope(c->info, base_entity->scope);
scope->flags |= ScopeFlag_Proc;
nctx.scope = scope;
nctx.allow_polymorphic_types = true;
@@ -291,9 +299,10 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
}
auto *pt = &src->Proc;
// NOTE(bill): This is slightly memory leaking if the type already exists
// Maybe it's better to check with the previous types first?
Type *final_proc_type = alloc_type_proc(scope, nullptr, 0, nullptr, 0, false, pt->calling_convention);
@@ -303,7 +312,7 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
return false;
}
auto *found_gen_procs = map_get(&nctx.info->gen_procs, hash_pointer(base_entity->identifier));
auto *found_gen_procs = map_get(&info->gen_procs, hash_pointer(base_entity->identifier));
if (found_gen_procs) {
auto procs = *found_gen_procs;
for_array(i, procs) {
@@ -400,22 +409,22 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
}
}
ProcInfo proc_info = {};
proc_info.file = file;
proc_info.token = token;
proc_info.decl = d;
proc_info.type = final_proc_type;
proc_info.body = pl->body;
proc_info.tags = tags;
proc_info.generated_from_polymorphic = true;
proc_info.poly_def_node = poly_def_node;
ProcInfo *proc_info = gb_alloc_item(permanent_allocator(), ProcInfo);
proc_info->file = file;
proc_info->token = token;
proc_info->decl = d;
proc_info->type = final_proc_type;
proc_info->body = pl->body;
proc_info->tags = tags;
proc_info->generated_from_polymorphic = true;
proc_info->poly_def_node = poly_def_node;
if (found_gen_procs) {
array_add(found_gen_procs, entity);
} else {
auto array = array_make<Entity *>(heap_allocator());
array_add(&array, entity);
map_set(&nctx.checker->info.gen_procs, hash_pointer(base_entity->identifier), array);
map_set(&info->gen_procs, hash_pointer(base_entity->identifier), array);
}
GB_ASSERT(entity != nullptr);
@@ -426,7 +435,7 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
}
// NOTE(bill): Check the newly generated procedure body
check_procedure_later(nctx.checker, proc_info);
check_procedure_later(&nctx, proc_info);
return true;
}
@@ -1490,13 +1499,13 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ
big_int_from_i64(&bi127, 127);
big_int_shl_eq(&umax, &bi128);
big_int_sub_eq(&umax, &BIG_INT_ONE);
mp_decr(&umax);
big_int_shl_eq(&imin, &bi127);
big_int_neg(&imin, &imin);
big_int_shl_eq(&imax, &bi127);
big_int_sub_eq(&imax, &BIG_INT_ONE);
mp_decr(&imax);
}
switch (type->Basic.kind) {
@@ -1542,7 +1551,7 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ
{
// return 0ull <= i && i <= umax;
int b = big_int_cmp(&i, &umax);
return !i.neg && (b <= 0);
return !i.sign && (b <= 0);
}
case Basic_UntypedInteger:
@@ -1725,7 +1734,12 @@ void check_cast_error_suggestion(CheckerContext *c, Operand *o, Type *type) {
void check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) {
GB_ASSERT(o->mode == Addressing_Constant);
if (!is_type_constant_type(type) || !check_representable_as_constant(ctx, o->value, type, &o->value)) {
ExactValue out_value = o->value;
if (is_type_constant_type(type) && check_representable_as_constant(ctx, o->value, type, &out_value)) {
o->value = out_value;
} else {
o->value = out_value;
gbString a = expr_to_string(o->expr);
gbString b = type_to_string(type);
gbString c = type_to_string(o->type);
@@ -1740,7 +1754,7 @@ void check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) {
if (!is_type_integer(o->type) && is_type_integer(type)) {
error(o->expr, "'%s' truncated to '%s'", a, b);
} else {
error(o->expr, "Cannot convert '%s' to '%s' from '%s", a, b, c);
error(o->expr, "Cannot convert numeric value '%s' to '%s' from '%s", a, b, c);
check_assignment_error_suggestion(ctx, o, type);
}
} else {
@@ -1784,10 +1798,6 @@ bool check_is_not_addressable(CheckerContext *c, Operand *o) {
void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) {
switch (op.kind) {
case Token_And: { // Pointer address
if (node->kind == Ast_TypeAssertion) {
gb_printf_err("%s\n", expr_to_string(node));
}
if (check_is_not_addressable(c, o)) {
if (ast_node_expect(node, Ast_UnaryExpr)) {
ast_node(ue, UnaryExpr, node);
@@ -2085,8 +2095,8 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) {
} else {
x->mode = Addressing_Value;
update_expr_type(c, x->expr, default_type(x->type), true);
update_expr_type(c, y->expr, default_type(y->type), true);
update_untyped_expr_type(c, x->expr, default_type(x->type), true);
update_untyped_expr_type(c, y->expr, default_type(y->type), true);
i64 size = 0;
if (!is_type_untyped(x->type)) size = gb_max(size, type_size_of(x->type));
@@ -2186,7 +2196,7 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ
}
BigInt max_shift = {};
big_int_from_u64(&max_shift, 128);
big_int_from_u64(&max_shift, MAX_BIG_INT_SHIFT);
if (big_int_cmp(&y_val.value_integer, &max_shift) > 0) {
gbString err_str = expr_to_string(y->expr);
@@ -2212,9 +2222,8 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ
TokenPos pos = ast_token(x->expr).pos;
if (x_is_untyped) {
ExprInfo *info = check_get_expr_info(&c->checker->info, x->expr);
if (info != nullptr) {
info->is_lhs = true;
if (x->expr != nullptr) {
x->expr->tav.is_lhs = true;
}
x->mode = Addressing_Value;
if (type_hint && is_type_integer(type_hint)) {
@@ -2225,7 +2234,7 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ
}
}
if (y->mode == Addressing_Constant && y->value.value_integer.neg) {
if (y->mode == Addressing_Constant && big_int_is_neg(&y->value.value_integer)) {
gbString err_str = expr_to_string(y->expr);
error(node, "Shift amount cannot be negative: '%s'", err_str);
gb_string_free(err_str);
@@ -2467,7 +2476,7 @@ void check_cast(CheckerContext *c, Operand *x, Type *type) {
if (is_const_expr && !is_type_constant_type(type)) {
final_type = default_type(x->type);
}
update_expr_type(c, x->expr, final_type, true);
update_untyped_expr_type(c, x->expr, final_type, true);
}
if (build_context.vet_extra) {
@@ -2506,6 +2515,25 @@ bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) {
return false;
}
Type *dst_bt = base_type(t);
if (dst_bt == nullptr || dst_bt == t_invalid) {
GB_ASSERT(global_error_collector.count != 0);
o->mode = Addressing_Invalid;
o->expr = node;
return false;
}
Type *src_bt = base_type(o->type);
if (src_bt == nullptr || src_bt == t_invalid) {
// NOTE(bill): this should be an error
GB_ASSERT(global_error_collector.count != 0);
o->mode = Addressing_Value;
o->expr = node;
o->type = t;
return true;
}
i64 srcz = type_size_of(o->type);
i64 dstz = type_size_of(t);
if (srcz != dstz) {
@@ -2594,15 +2622,16 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
case Token_in:
case Token_not_in:
{
// IMPORTANT NOTE(bill): This uses right-left evaluation in type checking only no in
check_expr(c, y, be->right);
Type *rhs_type = type_deref(y->type);
if (is_type_bit_set(y->type)) {
Type *elem = base_type(y->type)->BitSet.elem;
if (is_type_bit_set(rhs_type)) {
Type *elem = base_type(rhs_type)->BitSet.elem;
check_expr_with_type_hint(c, x, be->left, elem);
} else if (is_type_map(y->type)) {
Type *key = base_type(y->type)->Map.key;
} else if (is_type_map(rhs_type)) {
Type *key = base_type(rhs_type)->Map.key;
check_expr_with_type_hint(c, x, be->left, key);
} else {
check_expr(c, x, be->left);
@@ -2617,8 +2646,8 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
return;
}
if (is_type_map(y->type)) {
Type *yt = base_type(y->type);
if (is_type_map(rhs_type)) {
Type *yt = base_type(rhs_type);
if (op.kind == Token_in) {
check_assignment(c, x, yt->Map.key, str_lit("map 'in'"));
} else {
@@ -2626,8 +2655,8 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
}
add_package_dependency(c, "runtime", "__dynamic_map_get");
} else if (is_type_bit_set(y->type)) {
Type *yt = base_type(y->type);
} else if (is_type_bit_set(rhs_type)) {
Type *yt = base_type(rhs_type);
if (op.kind == Token_in) {
check_assignment(c, x, yt->BitSet.elem, str_lit("bit_set 'in'"));
@@ -2676,6 +2705,7 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
x->expr = node;
return;
}
default:
if (is_ise_expr(be->left)) {
@@ -2882,80 +2912,78 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
}
void update_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) {
void update_untyped_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) {
GB_ASSERT(e != nullptr);
ExprInfo *found = check_get_expr_info(&c->checker->info, e);
if (found == nullptr) {
ExprInfo *old = check_get_expr_info(c, e);
if (old == nullptr) {
if (type != nullptr && type != t_invalid) {
if (e->tav.type == nullptr || e->tav.type == t_invalid) {
add_type_and_value(&c->checker->info, e, e->tav.mode, type ? type : e->tav.type, e->tav.value);
add_type_and_value(c->info, e, e->tav.mode, type ? type : e->tav.type, e->tav.value);
}
}
return;
}
ExprInfo old = *found;
switch (e->kind) {
case_ast_node(ue, UnaryExpr, e);
if (old.value.kind != ExactValue_Invalid) {
if (old->value.kind != ExactValue_Invalid) {
// NOTE(bill): if 'e' is constant, the operands will be constant too.
// They don't need to be updated as they will be updated later and
// checked at the end of general checking stage.
break;
}
update_expr_type(c, ue->expr, type, final);
update_untyped_expr_type(c, ue->expr, type, final);
case_end;
case_ast_node(be, BinaryExpr, e);
if (old.value.kind != ExactValue_Invalid) {
if (old->value.kind != ExactValue_Invalid) {
// See above note in UnaryExpr case
break;
}
if (token_is_comparison(be->op.kind)) {
// NOTE(bill): Do nothing as the types are fine
} else if (token_is_shift(be->op.kind)) {
update_expr_type(c, be->left, type, final);
update_untyped_expr_type(c, be->left, type, final);
} else {
update_expr_type(c, be->left, type, final);
update_expr_type(c, be->right, type, final);
update_untyped_expr_type(c, be->left, type, final);
update_untyped_expr_type(c, be->right, type, final);
}
case_end;
case_ast_node(te, TernaryIfExpr, e);
if (old.value.kind != ExactValue_Invalid) {
if (old->value.kind != ExactValue_Invalid) {
// See above note in UnaryExpr case
break;
}
update_expr_type(c, te->x, type, final);
update_expr_type(c, te->y, type, final);
update_untyped_expr_type(c, te->x, type, final);
update_untyped_expr_type(c, te->y, type, final);
case_end;
case_ast_node(te, TernaryWhenExpr, e);
if (old.value.kind != ExactValue_Invalid) {
if (old->value.kind != ExactValue_Invalid) {
// See above note in UnaryExpr case
break;
}
update_expr_type(c, te->x, type, final);
update_expr_type(c, te->y, type, final);
update_untyped_expr_type(c, te->x, type, final);
update_untyped_expr_type(c, te->y, type, final);
case_end;
case_ast_node(pe, ParenExpr, e);
update_expr_type(c, pe->expr, type, final);
update_untyped_expr_type(c, pe->expr, type, final);
case_end;
}
if (!final && is_type_untyped(type)) {
old.type = base_type(type);
check_set_expr_info(&c->checker->info, e, old);
old->type = base_type(type);
return;
}
// We need to remove it and then give it a new one
check_remove_expr_info(&c->checker->info, e);
check_remove_expr_info(c, e);
if (old.is_lhs && !is_type_integer(type)) {
if (old->is_lhs && !is_type_integer(type)) {
gbString expr_str = expr_to_string(e);
gbString type_str = type_to_string(type);
error(e, "Shifted operand %s must be an integer, got %s", expr_str, type_str);
@@ -2964,11 +2992,12 @@ void update_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) {
return;
}
add_type_and_value(&c->checker->info, e, old.mode, type, old.value);
add_type_and_value(c->info, e, old->mode, type, old->value);
}
void update_expr_value(CheckerContext *c, Ast *e, ExactValue value) {
ExprInfo *found = check_get_expr_info(&c->checker->info, e);
void update_untyped_expr_value(CheckerContext *c, Ast *e, ExactValue value) {
GB_ASSERT(e != nullptr);
ExprInfo *found = check_get_expr_info(c, e);
if (found) {
found->value = value;
}
@@ -2988,7 +3017,7 @@ void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_typ
}
}
}
error(operand->expr, "Cannot convert '%s' to '%s' from '%s'%s", expr_str, type_str, from_type_str, extra_text);
error(operand->expr, "Cannot convert untyped value '%s' to '%s' from '%s'%s", expr_str, type_str, from_type_str, extra_text);
gb_string_free(from_type_str);
gb_string_free(type_str);
@@ -3031,7 +3060,7 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
if (is_type_numeric(operand->type) && is_type_numeric(target_type)) {
if (x_kind < y_kind) {
operand->type = target_type;
update_expr_type(c, operand->expr, target_type, false);
update_untyped_expr_type(c, operand->expr, target_type, false);
}
} else if (x_kind != y_kind) {
operand->mode = Addressing_Invalid;
@@ -3053,7 +3082,7 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
if (operand->mode == Addressing_Invalid) {
return;
}
update_expr_value(c, operand->expr, operand->value);
update_untyped_expr_value(c, operand->expr, operand->value);
} else {
switch (operand->type->Basic.kind) {
case Basic_UntypedBool:
@@ -3200,7 +3229,9 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
if (i > 0 && count > 2) error_line(", ");
if (i == count-1) {
if (count == 2) error_line(" ");
error_line("or ");
if (count > 1) {
error_line("or ");
}
}
gbString str = type_to_string(v);
error_line("'%s'", str);
@@ -3228,7 +3259,7 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
break;
}
update_expr_type(c, operand->expr, target_type, true);
update_untyped_expr_type(c, operand->expr, target_type, true);
operand->type = target_type;
}
@@ -3271,7 +3302,7 @@ bool check_index_value(CheckerContext *c, bool open_range, Ast *index_value, i64
if (operand.mode == Addressing_Constant &&
(c->state_flags & StateFlag_no_bounds_check) == 0) {
BigInt i = exact_value_to_integer(operand.value).value_integer;
if (i.neg && !is_type_enum(index_type)) {
if (i.sign && !is_type_enum(index_type)) {
gbString expr_str = expr_to_string(operand.expr);
error(operand.expr, "Index '%s' cannot be a negative value", expr_str);
gb_string_free(expr_str);
@@ -3317,7 +3348,7 @@ bool check_index_value(CheckerContext *c, bool open_range, Ast *index_value, i64
} else { // NOTE(bill): Do array bound checking
i64 v = -1;
if (i.len <= 1) {
if (i.used <= 1) {
v = big_int_to_i64(&i);
}
if (value) *value = v;
@@ -3573,6 +3604,40 @@ ExactValue get_constant_field(CheckerContext *c, Operand const *operand, Selecti
if (success_) *success_ = true;
return empty_exact_value;
}
void check_did_you_mean_print(DidYouMeanAnswers *d) {
auto results = did_you_mean_results(d);
if (results.count != 0) {
error_line("\tSuggestion: Did you mean?\n");
for_array(i, results) {
String const &target = results[i].target;
error_line("\t\t%.*s\n", LIT(target));
// error_line("\t\t%.*s %td\n", LIT(target), results[i].distance);
}
}
}
void check_did_you_mean_type(String const &name, Array<Entity *> const &fields) {
DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), fields.count, name);
defer (did_you_mean_destroy(&d));
for_array(i, fields) {
did_you_mean_append(&d, fields[i]->token.string);
}
check_did_you_mean_print(&d);
}
void check_did_you_mean_scope(String const &name, Scope *scope) {
DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), scope->elements.entries.count, name);
defer (did_you_mean_destroy(&d));
for_array(i, scope->elements.entries) {
Entity *e = scope->elements.entries[i].value;
did_you_mean_append(&d, e->token.string);
}
check_did_you_mean_print(&d);
}
Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *type_hint) {
ast_node(se, SelectorExpr, node);
@@ -3638,6 +3703,8 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
error(op_expr, "'%.*s' is not declared by '%.*s'", LIT(entity_name), LIT(import_name));
operand->mode = Addressing_Invalid;
operand->expr = node;
check_did_you_mean_scope(entity_name, import_scope);
return nullptr;
}
@@ -3785,7 +3852,9 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
Type *swizzle_array_type = nullptr;
Type *bth = base_type(type_hint);
if (bth != nullptr && bth->kind == Type_Array && bth->Array.count == index_count) {
if (bth != nullptr && bth->kind == Type_Array &&
bth->Array.count == index_count &&
are_types_identical(bth->Array.elem, array_type->Array.elem)) {
swizzle_array_type = type_hint;
} else {
swizzle_array_type = alloc_type_array(array_type->Array.elem, index_count);
@@ -3815,6 +3884,17 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
gbString type_str = type_to_string(operand->type);
gbString sel_str = expr_to_string(selector);
error(op_expr, "'%s' of type '%s' has no field '%s'", op_str, type_str, sel_str);
if (operand->type != nullptr && selector->kind == Ast_Ident) {
String const &name = selector->Ident.token.string;
Type *bt = base_type(operand->type);
if (bt->kind == Type_Struct) {
check_did_you_mean_type(name, bt->Struct.fields);
} else if (bt->kind == Type_Enum) {
check_did_you_mean_type(name, bt->Enum.fields);
}
}
gb_string_free(sel_str);
gb_string_free(type_str);
gb_string_free(op_str);
@@ -4045,26 +4125,7 @@ bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs,
val1.mode = Addressing_Value;
val1.type = t_untyped_bool;
if (expr->kind == Ast_CallExpr) {
Type *pt = base_type(type_of_expr(expr->CallExpr.proc));
if (is_type_proc(pt)) {
do_normal = false;
Type *tuple = pt->Proc.results;
add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
if (pt->Proc.result_count >= 2) {
Type *t1 = tuple->Tuple.variables[1]->type;
val1.type = t1;
}
expr->CallExpr.optional_ok_one = false;
}
}
if (do_normal) {
Type *tuple = make_optional_ok_type(o.type);
add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
}
check_promote_optional_ok(c, &o, nullptr, &val1.type);
if (expr->kind == Ast_TypeAssertion &&
(o.mode == Addressing_OptionalOk || o.mode == Addressing_OptionalOkPtr)) {
@@ -4170,26 +4231,7 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count,
val1.mode = Addressing_Value;
val1.type = t_untyped_bool;
if (expr->kind == Ast_CallExpr) {
Type *pt = base_type(type_of_expr(expr->CallExpr.proc));
if (is_type_proc(pt)) {
do_normal = false;
Type *tuple = pt->Proc.results;
add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
if (pt->Proc.result_count >= 2) {
Type *t1 = tuple->Tuple.variables[1]->type;
val1.type = t1;
}
expr->CallExpr.optional_ok_one = false;
}
}
if (do_normal) {
Type *tuple = make_optional_ok_type(o.type);
add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
}
check_promote_optional_ok(c, &o, nullptr, &val1.type);
if (expr->kind == Ast_TypeAssertion &&
(o.mode == Addressing_OptionalOk || o.mode == Addressing_OptionalOkPtr)) {
@@ -4357,9 +4399,8 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
}
} else {
// NOTE(bill): Generate the procedure type for this generic instance
PolyProcData poly_proc_data = {};
if (pt->is_polymorphic && !pt->is_poly_specialized) {
PolyProcData poly_proc_data = {};
if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &operands, call, &poly_proc_data)) {
gen_entity = poly_proc_data.gen_entity;
GB_ASSERT(is_type_proc(gen_entity->type));
@@ -4441,7 +4482,7 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
add_type_info_type(c, o.type);
add_type_and_value(c->info, o.expr, Addressing_Value, e->type, exact_value_typeid(o.type));
} else if (show_error && is_type_untyped(o.type)) {
update_expr_type(c, o.expr, t, true);
update_untyped_expr_type(c, o.expr, t, true);
}
}
@@ -4492,7 +4533,7 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
add_type_info_type(c, o.type);
add_type_and_value(c->info, o.expr, Addressing_Value, t, exact_value_typeid(o.type));
} else if (show_error && is_type_untyped(o.type)) {
update_expr_type(c, o.expr, t, true);
update_untyped_expr_type(c, o.expr, t, true);
}
}
}
@@ -4985,7 +5026,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
add_entity_use(c, ident, entity_to_use);
if (entity_to_use != nullptr) {
update_expr_type(c, operand->expr, entity_to_use->type, true);
update_untyped_expr_type(c, operand->expr, entity_to_use->type, true);
}
return data;
}
@@ -5259,7 +5300,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
add_entity_use(c, ident, entity_to_use);
if (entity_to_use != nullptr) {
update_expr_type(c, operand->expr, entity_to_use->type, true);
update_untyped_expr_type(c, operand->expr, entity_to_use->type, true);
}
if (data.gen_entity != nullptr) {
@@ -5292,7 +5333,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
add_entity_use(c, ident, entity_to_use);
if (entity_to_use != nullptr) {
update_expr_type(c, operand->expr, entity_to_use->type, true);
update_untyped_expr_type(c, operand->expr, entity_to_use->type, true);
}
if (data.gen_entity != nullptr) {
@@ -5495,7 +5536,7 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
return err;
}
while (ordered_operands.count >= 0) {
while (ordered_operands.count > 0) {
if (ordered_operands[ordered_operands.count-1].expr != nullptr) {
break;
}
@@ -5544,8 +5585,9 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
}
}
isize oo_count = gb_min(param_count, ordered_operands.count);
i64 score = 0;
for (isize i = 0; i < param_count; i++) {
for (isize i = 0; i < oo_count; i++) {
Entity *e = tuple->variables[i];
Operand *o = &ordered_operands[i];
if (o->mode == Addressing_Invalid) {
@@ -5657,7 +5699,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
operand->builtin_id = BuiltinProc_DIRECTIVE;
operand->expr = proc;
operand->type = t_invalid;
add_type_and_value(&c->checker->info, proc, operand->mode, operand->type, operand->value);
add_type_and_value(c->info, proc, operand->mode, operand->type, operand->value);
} else {
GB_PANIC("Unhandled #%.*s", LIT(name));
}
@@ -5720,7 +5762,6 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
operand->type = t_invalid;;
return Expr_Expr;
}
auto err = check_polymorphic_record_type(c, operand, call);
if (err == 0) {
Ast *ident = operand->expr;
@@ -5732,7 +5773,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
GB_ASSERT(ot->kind == Type_Named);
Entity *e = ot->Named.type_name;
add_entity_use(c, ident, e);
add_type_and_value(&c->checker->info, call, Addressing_Type, ot, empty_exact_value);
add_type_and_value(c->info, call, Addressing_Type, ot, empty_exact_value);
} else {
operand->mode = Addressing_Invalid;
operand->type = t_invalid;
@@ -5753,7 +5794,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
arg = arg->FieldValue.value;
// NOTE(bill): Carry on the cast regardless
}
check_expr(c, operand, arg);
check_expr_with_type_hint(c, operand, arg, t);
if (operand->mode != Addressing_Invalid) {
if (is_type_polymorphic(t)) {
error(call, "A polymorphic type cannot be used in a type conversion");
@@ -5764,6 +5805,10 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
}
}
operand->type = t;
operand->expr = call;
if (operand->mode != Addressing_Invalid) {
update_untyped_expr_type(c, arg, t, false);
}
break;
}
}
@@ -6124,8 +6169,8 @@ bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValu
return false;
}
add_type_and_value(&c->checker->info, ie->left, x->mode, x->type, x->value);
add_type_and_value(&c->checker->info, ie->right, y->mode, y->type, y->value);
add_type_and_value(c->info, ie->left, x->mode, x->type, x->value);
add_type_and_value(c->info, ie->right, y->mode, y->type, y->value);
return true;
}
@@ -6215,9 +6260,14 @@ ExprKind check_implicit_selector_expr(CheckerContext *c, Operand *o, Ast *node,
String name = ise->selector->Ident.token.string;
if (is_type_enum(th)) {
Type *bt = base_type(th);
GB_ASSERT(bt->kind == Type_Enum);
gbString typ = type_to_string(th);
error(node, "Undeclared name %.*s for type '%s'", LIT(name), typ);
gb_string_free(typ);
defer (gb_string_free(typ));
error(node, "Undeclared name '%.*s' for type '%s'", LIT(name), typ);
check_did_you_mean_type(name, bt->Enum.fields);
} else {
gbString typ = type_to_string(th);
gbString str = expr_to_string(node);
@@ -6231,6 +6281,44 @@ ExprKind check_implicit_selector_expr(CheckerContext *c, Operand *o, Ast *node,
return Expr_Expr;
}
void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_) {
switch (x->mode) {
case Addressing_MapIndex:
case Addressing_OptionalOk:
case Addressing_OptionalOkPtr:
if (val_type_) *val_type_ = x->type;
break;
default:
if (ok_type_) *ok_type_ = x->type;
return;
}
Ast *expr = unparen_expr(x->expr);
if (expr->kind == Ast_CallExpr) {
Type *pt = base_type(type_of_expr(expr->CallExpr.proc));
if (is_type_proc(pt)) {
Type *tuple = pt->Proc.results;
add_type_and_value(c->info, x->expr, x->mode, tuple, x->value);
if (pt->Proc.result_count >= 2) {
if (ok_type_) *ok_type_ = tuple->Tuple.variables[1]->type;
}
expr->CallExpr.optional_ok_one = false;
x->type = tuple;
return;
}
}
Type *tuple = make_optional_ok_type(x->type);
if (ok_type_) *ok_type_ = tuple->Tuple.variables[1]->type;
add_type_and_value(c->info, x->expr, x->mode, tuple, x->value);
x->type = tuple;
GB_ASSERT(is_type_tuple(type_of_expr(x->expr)));
}
ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
u32 prev_state_flags = c->state_flags;
defer (c->state_flags = prev_state_flags);
@@ -6389,7 +6477,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
}
pl->decl = decl;
check_procedure_later(ctx.checker, ctx.file, empty_token, decl, type, pl->body, pl->tags);
check_procedure_later(&ctx, ctx.file, empty_token, decl, type, pl->body, pl->tags);
}
check_close_scope(&ctx);
@@ -6450,20 +6538,13 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
o->type = type;
o->mode = Addressing_Value;
// if (cond.mode == Addressing_Constant && is_type_boolean(cond.type) &&
// x.mode == Addressing_Constant &&
// y.mode == Addressing_Constant) {
// o->mode = Addressing_Constant;
// if (cond.value.value_bool) {
// o->value = x.value;
// } else {
// o->value = y.value;
// }
// }
if (type_hint != nullptr && is_type_untyped(type)) {
if (check_cast_internal(c, &x, type_hint) &&
check_cast_internal(c, &y, type_hint)) {
update_untyped_expr_type(c, node, type_hint, !is_type_untyped(type_hint));
o->type = type_hint;
}
}
case_end;
case_ast_node(te, TernaryWhenExpr, node);
@@ -7600,8 +7681,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
}
case_end;
case_ast_node(se, SelectorExpr, node);
check_selector(c, o, node, type_hint);
node->viral_state_flags |= se->expr->viral_state_flags;
@@ -8025,7 +8104,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
o->mode = Addressing_Constant;
o->type = t;
o->value = exact_value_string(substring(s, indices[0], indices[1]));
o->value = exact_value_string(substring(s, cast(isize)indices[0], cast(isize)indices[1]));
}
case_end;
@@ -8100,7 +8179,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
error(x.expr, "Expected a constant string for the inline asm constraints parameter");
}
Scope *scope = create_scope(c->scope);
Scope *scope = create_scope(c->info, c->scope);
scope->flags |= ScopeFlag_Proc;
Type *params = alloc_type_tuple();
@@ -8158,9 +8237,9 @@ ExprKind check_expr_base(CheckerContext *c, Operand *o, Ast *node, Type *type_hi
gb_string_free(xs);
}
if (o->type != nullptr && is_type_untyped(o->type)) {
add_untyped(&c->checker->info, node, false, o->mode, o->type, o->value);
add_untyped(c, node, o->mode, o->type, o->value);
}
add_type_and_value(&c->checker->info, node, o->mode, o->type, o->value);
add_type_and_value(c->info, node, o->mode, o->type, o->value);
return kind;
}
@@ -8192,6 +8271,21 @@ void check_multi_expr(CheckerContext *c, Operand *o, Ast *e) {
o->mode = Addressing_Invalid;
}
void check_multi_expr_with_type_hint(CheckerContext *c, Operand *o, Ast *e, Type *type_hint) {
check_expr_base(c, o, e, type_hint);
switch (o->mode) {
default:
return; // NOTE(bill): Valid
case Addressing_NoValue:
error_operand_no_value(o);
break;
case Addressing_Type:
error_operand_not_expression(o);
break;
}
o->mode = Addressing_Invalid;
}
void check_not_tuple(CheckerContext *c, Operand *o) {
if (o->mode == Addressing_Value) {
// NOTE(bill): Tuples are not first class thus never named
@@ -8472,9 +8566,15 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
case_ast_node(ta, TypeAssertion, node);
str = write_expr_to_string(str, ta->expr, shorthand);
str = gb_string_appendc(str, ".(");
str = write_expr_to_string(str, ta->type, shorthand);
str = gb_string_append_rune(str, ')');
if (ta->type != nullptr &&
ta->type->kind == Ast_UnaryExpr &&
ta->type->UnaryExpr.op.kind == Token_Question) {
str = gb_string_appendc(str, ".?");
} else {
str = gb_string_appendc(str, ".(");
str = write_expr_to_string(str, ta->type, shorthand);
str = gb_string_append_rune(str, ')');
}
case_end;
case_ast_node(tc, TypeCast, node);
+12 -11
View File
@@ -558,7 +558,7 @@ void check_label(CheckerContext *ctx, Ast *label, Ast *parent) {
}
Entity *e = alloc_entity_label(ctx->scope, l->name->Ident.token, t_invalid, label, parent);
add_entity(ctx->checker, ctx->scope, l->name, e);
add_entity(ctx, ctx->scope, l->name, e);
e->parent_proc_decl = ctx->curr_proc_decl;
if (ok) {
@@ -587,7 +587,7 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
Entity *found = scope_insert(ctx->scope, f);
if (found != nullptr) {
gbString expr_str = expr_to_string(expr);
error(us->token, "Namespace collision while 'using' '%s' of: %.*s", expr_str, LIT(found->token.string));
error(us->token, "Namespace collision while 'using' enum '%s' of: %.*s", expr_str, LIT(found->token.string));
gb_string_free(expr_str);
return false;
}
@@ -611,7 +611,7 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
if (found != nullptr) {
gbString expr_str = expr_to_string(expr);
error(us->token,
"Namespace collision while 'using' '%s' of: %.*s\n"
"Namespace collision while 'using' import name '%s' of: %.*s\n"
"\tat %s\n"
"\tat %s",
expr_str, LIT(found->token.string),
@@ -861,7 +861,7 @@ void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
}
for (isize i = 0; i < entity_count; i++) {
add_entity(ctx->checker, ctx->scope, entities[i]->identifier, entities[i]);
add_entity(ctx, ctx->scope, entities[i]->identifier, entities[i]);
}
@@ -1103,7 +1103,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
if (y.mode != Addressing_Constant) {
continue;
}
update_untyped_expr_type(ctx, z.expr, x.type, !is_type_untyped(x.type));
add_constant_switch_case(ctx, &seen, y);
}
}
@@ -1344,7 +1344,7 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
if (!is_reference) {
tag_var->flags |= EntityFlag_Value;
}
add_entity(ctx->checker, ctx->scope, lhs, tag_var);
add_entity(ctx, ctx->scope, lhs, tag_var);
add_entity_use(ctx, lhs, tag_var);
add_implicit_entity(ctx, stmt, tag_var);
}
@@ -1667,7 +1667,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
GB_ASSERT(ctx->curr_proc_sig != nullptr);
if (ctx->in_defer) {
error(rs->token, "You cannot 'return' within a defer statement");
error(rs->token, "'return' cannot be used within a defer statement");
break;
}
@@ -1706,7 +1706,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
Operand *o = &operands[i];
check_assignment(ctx, o, e->type, str_lit("return statement"));
if (is_type_untyped(o->type)) {
update_expr_type(ctx, o->expr, e->type, true);
update_untyped_expr_type(ctx, o->expr, e->type, true);
}
}
}
@@ -1884,7 +1884,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
error(operand.expr, "Cannot iterate over '%s' of type '%s'", s, t);
if (rs->vals.count == 1) {
if (is_type_map(operand.type) || is_type_bit_set(operand.type)) {
Type *t = type_deref(operand.type);
if (is_type_map(t) || is_type_bit_set(t)) {
gbString v = expr_to_string(rs->vals[0]);
defer (gb_string_free(v));
error_line("\tSuggestion: place parentheses around the expression\n");
@@ -1965,7 +1966,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
Entity *e = entities[i];
DeclInfo *d = decl_info_of_entity(e);
GB_ASSERT(d == nullptr);
add_entity(ctx->checker, ctx->scope, e->identifier, e);
add_entity(ctx, ctx->scope, e->identifier, e);
d = make_decl_info(ctx->scope, ctx->decl);
add_entity_and_decl_info(ctx, e->identifier, e, d);
}
@@ -2285,7 +2286,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
}
}
}
add_entity(ctx->checker, ctx->scope, e->identifier, e);
add_entity(ctx, ctx->scope, e->identifier, e);
}
if (vd->is_using != 0) {
+39 -31
View File
@@ -23,7 +23,7 @@ void populate_using_array_index(CheckerContext *ctx, Ast *node, AstField *field,
tok.pos = ast_token(field->type).pos;
}
Entity *f = alloc_entity_array_elem(nullptr, tok, t->Array.elem, idx);
add_entity(ctx->checker, ctx->scope, nullptr, f);
add_entity(ctx, ctx->scope, nullptr, f);
}
}
@@ -52,7 +52,7 @@ void populate_using_entity_scope(CheckerContext *ctx, Ast *node, AstField *field
error(e->token, "'%.*s' is already declared", LIT(name));
}
} else {
add_entity(ctx->checker, ctx->scope, nullptr, f);
add_entity(ctx, ctx->scope, nullptr, f);
if (f->flags & EntityFlag_Using) {
populate_using_entity_scope(ctx, node, field, f->type);
}
@@ -157,7 +157,7 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Array<Entity *> *fields
Token name_token = name->Ident.token;
Entity *field = alloc_entity_field(ctx->scope, name_token, type, is_using, field_src_index);
add_entity(ctx->checker, ctx->scope, name, field);
add_entity(ctx, ctx->scope, name, field);
array_add(fields, field);
array_add(tags, p->tag.string);
@@ -207,7 +207,7 @@ bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_) {
if (is_type_untyped(type) || is_type_integer(type)) {
if (o.value.kind == ExactValue_Integer) {
BigInt v = o.value.value_integer;
if (v.len > 1) {
if (v.used > 1) {
gbAllocator a = heap_allocator();
String str = big_int_to_string(a, &v);
error(node, "#align too large, %.*s", LIT(str));
@@ -236,7 +236,10 @@ bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_) {
Entity *find_polymorphic_record_entity(CheckerContext *ctx, Type *original_type, isize param_count, Array<Operand> const &ordered_operands, bool *failure) {
auto *found_gen_types = map_get(&ctx->checker->info.gen_types, hash_pointer(original_type));
mutex_lock(&ctx->info->gen_types_mutex);
defer (mutex_unlock(&ctx->info->gen_types_mutex));
auto *found_gen_types = map_get(&ctx->info->gen_types, hash_pointer(original_type));
if (found_gen_types != nullptr) {
for_array(i, *found_gen_types) {
Entity *e = (*found_gen_types)[i];
@@ -315,14 +318,16 @@ void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, Type *named_t
named_type->Named.type_name = e;
auto *found_gen_types = map_get(&ctx->checker->info.gen_types, hash_pointer(original_type));
mutex_lock(&ctx->info->gen_types_mutex);
auto *found_gen_types = map_get(&ctx->info->gen_types, hash_pointer(original_type));
if (found_gen_types) {
array_add(found_gen_types, e);
} else {
auto array = array_make<Entity *>(heap_allocator());
array_add(&array, e);
map_set(&ctx->checker->info.gen_types, hash_pointer(original_type), array);
map_set(&ctx->info->gen_types, hash_pointer(original_type), array);
}
mutex_unlock(&ctx->info->gen_types_mutex);
}
Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *polymorphic_params,
@@ -483,7 +488,7 @@ Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *polymorphic_para
}
e->state = EntityState_Resolved;
add_entity(ctx->checker, scope, name, e);
add_entity(ctx, scope, name, e);
array_add(&entities, e);
}
}
@@ -795,7 +800,7 @@ void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *named_type, Ast
if (scope_lookup_current(ctx->scope, name) != nullptr) {
error(ident, "'%.*s' is already declared in this enumeration", LIT(name));
} else {
add_entity(ctx->checker, ctx->scope, nullptr, e);
add_entity(ctx, ctx->scope, nullptr, e);
array_add(&fields, e);
// TODO(bill): Should I add a use for the enum value?
add_entity_use(ctx, field, e);
@@ -1255,7 +1260,10 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type *
param_value.kind = ParameterValue_Constant;
param_value.value = o.value;
} else {
error(o.expr, "Invalid constant parameter");
gbString s = expr_to_string(o.expr);
error(o.expr, "Invalid constant parameter, got '%s'", s);
// error(o.expr, "Invalid constant parameter, got '%s' %d %d", s, o.mode, o.value.kind);
gb_string_free(s);
}
}
}
@@ -1622,7 +1630,7 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
}
param->state = EntityState_Resolved; // NOTE(bill): This should have be resolved whilst determining it
add_entity(ctx->checker, scope, name, param);
add_entity(ctx, scope, name, param);
if (is_using) {
add_entity_use(ctx, name, param);
}
@@ -1749,7 +1757,7 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) {
param->flags |= EntityFlag_Result;
param->Variable.param_value = param_value;
array_add(&variables, param);
add_entity(ctx->checker, scope, name, param);
add_entity(ctx, scope, name, param);
// NOTE(bill): Removes `declared but not used` when using -vet
add_entity_use(ctx, name, param);
}
@@ -1990,16 +1998,16 @@ i64 check_array_count(CheckerContext *ctx, Operand *o, Ast *e) {
if (is_type_untyped(type) || is_type_integer(type)) {
if (o->value.kind == ExactValue_Integer) {
BigInt count = o->value.value_integer;
if (o->value.value_integer.neg) {
if (big_int_is_neg(&o->value.value_integer)) {
gbAllocator a = heap_allocator();
String str = big_int_to_string(a, &count);
error(e, "Invalid negative array count, %.*s", LIT(str));
gb_free(a, str.text);
return 0;
}
switch (count.len) {
switch (count.used) {
case 0: return 0;
case 1: return count.d.word;
case 1: return big_int_to_u64(&count);
}
gbAllocator a = heap_allocator();
String str = big_int_to_string(a, &count);
@@ -2039,7 +2047,7 @@ void init_map_entry_type(Type *type) {
}
*/
Ast *dummy_node = alloc_ast_node(nullptr, Ast_Invalid);
Scope *s = create_scope(builtin_pkg->scope);
Scope *s = create_scope(nullptr, builtin_pkg->scope);
auto fields = array_make<Entity *>(permanent_allocator(), 0, 4);
array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("hash")), t_uintptr, false, cast(i32)fields.count, EntityState_Resolved));
@@ -2073,7 +2081,7 @@ void init_map_internal_types(Type *type) {
}
*/
Ast *dummy_node = alloc_ast_node(nullptr, Ast_Invalid);
Scope *s = create_scope(builtin_pkg->scope);
Scope *s = create_scope(nullptr, builtin_pkg->scope);
Type *hashes_type = alloc_type_slice(t_int);
Type *entries_type = alloc_type_dynamic_array(type->Map.entry_type);
@@ -2206,11 +2214,11 @@ Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *el
soa_struct->Struct.soa_count = 0;
soa_struct->Struct.is_polymorphic = true;
scope = create_scope(ctx->scope);
scope = create_scope(ctx->info, ctx->scope);
soa_struct->Struct.scope = scope;
} else if (is_type_array(elem)) {
Type *old_array = base_type(elem);
field_count = old_array->Array.count;
field_count = cast(isize)old_array->Array.count;
soa_struct = alloc_type_struct();
soa_struct->Struct.fields = array_make<Entity *>(heap_allocator(), field_count+extra_field_count);
@@ -2220,7 +2228,7 @@ Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *el
soa_struct->Struct.soa_elem = elem;
soa_struct->Struct.soa_count = count;
scope = create_scope(ctx->scope);
scope = create_scope(ctx->info, ctx->scope, 8);
soa_struct->Struct.scope = scope;
String params_xyzw[4] = {
@@ -2230,7 +2238,7 @@ Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *el
str_lit("w")
};
for (i64 i = 0; i < old_array->Array.count; i++) {
for (isize i = 0; i < cast(isize)old_array->Array.count; i++) {
Type *field_type = nullptr;
if (soa_kind == StructSoa_Fixed) {
GB_ASSERT(count >= 0);
@@ -2243,7 +2251,7 @@ Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *el
Entity *new_field = alloc_entity_field(scope, token, field_type, false, cast(i32)i);
soa_struct->Struct.fields[i] = new_field;
add_entity(ctx->checker, scope, nullptr, new_field);
add_entity(ctx, scope, nullptr, new_field);
add_entity_use(ctx, nullptr, new_field);
}
@@ -2262,7 +2270,7 @@ Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *el
soa_struct->Struct.soa_elem = elem;
soa_struct->Struct.soa_count = count;
scope = create_scope(old_struct->Struct.scope->parent);
scope = create_scope(ctx->info, old_struct->Struct.scope->parent);
soa_struct->Struct.scope = scope;
for_array(i, old_struct->Struct.fields) {
@@ -2277,7 +2285,7 @@ Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *el
}
Entity *new_field = alloc_entity_field(scope, old_field->token, field_type, false, old_field->Variable.field_src_index);
soa_struct->Struct.fields[i] = new_field;
add_entity(ctx->checker, scope, nullptr, new_field);
add_entity(ctx, scope, nullptr, new_field);
add_entity_use(ctx, nullptr, new_field);
} else {
soa_struct->Struct.fields[i] = old_field;
@@ -2290,13 +2298,13 @@ Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *el
if (soa_kind != StructSoa_Fixed) {
Entity *len_field = alloc_entity_field(scope, empty_token, t_int, false, cast(i32)field_count+0);
soa_struct->Struct.fields[field_count+0] = len_field;
add_entity(ctx->checker, scope, nullptr, len_field);
add_entity(ctx, scope, nullptr, len_field);
add_entity_use(ctx, nullptr, len_field);
if (soa_kind == StructSoa_Dynamic) {
Entity *cap_field = alloc_entity_field(scope, empty_token, t_int, false, cast(i32)field_count+1);
soa_struct->Struct.fields[field_count+1] = cap_field;
add_entity(ctx->checker, scope, nullptr, cap_field);
add_entity(ctx, scope, nullptr, cap_field);
add_entity_use(ctx, nullptr, cap_field);
Token token = {};
@@ -2304,7 +2312,7 @@ Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *el
init_mem_allocator(ctx->checker);
Entity *allocator_field = alloc_entity_field(scope, token, t_allocator, false, cast(i32)field_count+2);
soa_struct->Struct.fields[field_count+2] = allocator_field;
add_entity(ctx->checker, scope, nullptr, allocator_field);
add_entity(ctx, scope, nullptr, allocator_field);
add_entity_use(ctx, nullptr, allocator_field);
}
}
@@ -2312,7 +2320,7 @@ Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *el
Token token = {};
token.string = str_lit("Base_Type");
Entity *base_type_entity = alloc_entity_type_name(scope, token, elem, EntityState_Resolved);
add_entity(ctx->checker, scope, nullptr, base_type_entity);
add_entity(ctx, scope, nullptr, base_type_entity);
add_type_info_type(ctx, soa_struct);
@@ -2425,8 +2433,8 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
t->Generic.entity = e;
e->TypeName.is_type_alias = true;
e->state = EntityState_Resolved;
add_entity(ctx->checker, ps, ident, e);
add_entity(ctx->checker, s, ident, e);
add_entity(ctx, ps, ident, e);
add_entity(ctx, s, ident, e);
} else {
error(ident, "Invalid use of a polymorphic parameter '$%.*s'", LIT(token.string));
*type = t_invalid;
@@ -2796,7 +2804,7 @@ Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) {
#endif
if (is_type_typed(type)) {
add_type_and_value(&ctx->checker->info, e, Addressing_Type, type, empty_exact_value);
add_type_and_value(ctx->info, e, Addressing_Type, type, empty_exact_value);
} else {
gbString name = type_to_string(type);
error(e, "Invalid type definition of %s", name);
+1036 -511
View File
File diff suppressed because it is too large Load Diff
+86 -42
View File
@@ -9,23 +9,23 @@ struct Checker;
struct CheckerInfo;
struct CheckerContext;
enum AddressingMode;
enum AddressingMode : u8;
struct TypeAndValue;
// ExprInfo stores information used for "untyped" expressions
struct ExprInfo {
AddressingMode mode;
bool is_lhs; // Debug info
Type * type;
ExactValue value;
bool is_lhs; // Debug info
};
gb_inline ExprInfo make_expr_info(AddressingMode mode, Type *type, ExactValue value, bool is_lhs) {
ExprInfo ei = {};
ei.mode = mode;
ei.type = type;
ei.value = value;
ei.is_lhs = is_lhs;
gb_inline ExprInfo *make_expr_info(AddressingMode mode, Type *type, ExactValue const &value, bool is_lhs) {
ExprInfo *ei = gb_alloc_item(permanent_allocator(), ExprInfo);
ei->mode = mode;
ei->type = type;
ei->value = value;
ei->is_lhs = is_lhs;
return ei;
}
@@ -113,6 +113,7 @@ struct AttributeContext {
isize init_expr_list_count;
String thread_local_model;
String deprecated_message;
String warning_message;
DeferredProcedure deferred_procedure;
u32 optimization_mode; // ProcedureOptimizationMode
};
@@ -144,6 +145,7 @@ struct DeclInfo {
Type * gen_proc_type; // Precalculated
bool is_using;
bool where_clauses_evaluated;
bool proc_checked;
CommentGroup *comment;
CommentGroup *docs;
@@ -186,14 +188,10 @@ enum { DEFAULT_SCOPE_CAPACITY = 29 };
struct Scope {
Ast * node;
Scope * parent;
Scope * prev;
Scope * next;
Scope * first_child;
Scope * last_child;
StringMap<Entity *> elements;
std::atomic<Scope *> next;
std::atomic<Scope *> head_child;
Array<Ast *> delayed_directives;
Array<Ast *> delayed_imports;
StringMap<Entity *> elements;
PtrSet<Scope *> imported;
i32 flags; // ScopeFlag
@@ -258,26 +256,21 @@ struct AtomOpMapEntry {
struct CheckerContext;
struct UntypedExprInfo {
Ast *expr;
ExprInfo *info;
};
typedef Map<ExprInfo *> UntypedExprInfoMap; // Key: Ast *
typedef MPMCQueue<ProcInfo *> ProcBodyQueue;
// CheckerInfo stores all the symbol information for a type-checked program
struct CheckerInfo {
Checker *checker;
Map<ExprInfo> untyped; // Key: Ast * | Expression -> ExprInfo
// NOTE(bill): This needs to be a map and not on the Ast
// as it needs to be iterated across
StringMap<AstFile *> files; // Key (full path)
StringMap<AstPackage *> packages; // Key (full path)
StringMap<Entity *> foreigns;
Array<Entity *> definitions;
Array<Entity *> entities;
Array<DeclInfo *> variable_init_order;
Map<Array<Entity *> > gen_procs; // Key: Ast * | Identifier -> Entity
Map<Array<Entity *> > gen_types; // Key: Type *
Array<Type *> type_info_types;
Map<isize> type_info_map; // Key: Type *
Array<DeclInfo *> variable_init_order;
AstPackage * builtin_package;
AstPackage * runtime_package;
@@ -287,15 +280,57 @@ struct CheckerInfo {
PtrSet<Entity *> minimum_dependency_set;
PtrSet<isize> minimum_dependency_type_info_set;
Array<Entity *> required_foreign_imports_through_force;
Array<Entity *> required_global_variables;
Map<AtomOpMapEntry> atom_op_map; // Key: Ast *
Array<Entity *> testing_procedures;
bool allow_identifier_uses;
Array<Ast *> identifier_uses; // only used by 'odin query'
Array<Entity *> definitions;
Array<Entity *> entities;
Array<Entity *> required_foreign_imports_through_force;
// Below are accessed within procedures
// NOTE(bill): If the semantic checker (check_proc_body) is to ever to be multithreaded,
// these variables will be of contention
gbSemaphore collect_semaphore;
UntypedExprInfoMap global_untyped; // NOTE(bill): This needs to be a map and not on the Ast
// as it needs to be iterated across afterwards
BlockingMutex global_untyped_mutex;
BlockingMutex builtin_mutex;
// NOT recursive & Only used at the end of `check_proc_body`
// This is a possible source of contention but probably not
// too much of a problem in practice
BlockingMutex deps_mutex;
RecursiveMutex lazy_mutex; // Mutex required for lazy type checking of specific files
RecursiveMutex gen_procs_mutex;
RecursiveMutex gen_types_mutex;
Map<Array<Entity *> > gen_procs; // Key: Ast * | Identifier -> Entity
Map<Array<Entity *> > gen_types; // Key: Type *
BlockingMutex type_info_mutex; // NOT recursive
Array<Type *> type_info_types;
Map<isize> type_info_map; // Key: Type *
BlockingMutex foreign_mutex; // NOT recursive
StringMap<Entity *> foreigns;
// only used by 'odin query'
bool allow_identifier_uses;
BlockingMutex identifier_uses_mutex;
Array<Ast *> identifier_uses;
// NOTE(bill): These are actually MPSC queues
// TODO(bill): Convert them to be MPSC queues
MPMCQueue<Entity *> definition_queue;
MPMCQueue<Entity *> entity_queue;
MPMCQueue<Entity *> required_global_variable_queue;
MPMCQueue<Entity *> required_foreign_imports_through_force_queue;
};
struct CheckerContext {
@@ -322,6 +357,8 @@ struct CheckerContext {
CheckerPolyPath *poly_path;
isize poly_level; // TODO(bill): Actually handle correctly
UntypedExprInfoMap *untyped;
#define MAX_INLINE_FOR_DEPTH 1024ll
i64 inline_for_depth;
@@ -335,17 +372,24 @@ struct CheckerContext {
Scope * polymorphic_scope;
Ast *assignment_lhs_hint;
ProcBodyQueue *procs_to_check_queue;
};
struct Checker {
Parser * parser;
CheckerInfo info;
Array<ProcInfo> procs_to_check;
Array<Entity *> procs_with_deferred_to_check;
CheckerContext builtin_ctx;
CheckerContext *curr_ctx;
CheckerContext init_ctx;
MPMCQueue<Entity *> procs_with_deferred_to_check;
ProcBodyQueue procs_to_check_queue;
gbSemaphore procs_to_check_semaphore;
// TODO(bill): Technically MPSC queue
MPMCQueue<UntypedExprInfo> global_untyped_queue;
};
@@ -383,11 +427,9 @@ void scope_lookup_parent (Scope *s, String const &name, Scope **scope_, Entit
Entity *scope_insert (Scope *s, Entity *entity);
ExprInfo *check_get_expr_info (CheckerInfo *i, Ast *expr);
void check_set_expr_info (CheckerInfo *i, Ast *expr, ExprInfo info);
void check_remove_expr_info (CheckerInfo *i, Ast *expr);
void add_untyped (CheckerInfo *i, Ast *expression, bool lhs, AddressingMode mode, Type *basic_type, ExactValue value);
void add_type_and_value (CheckerInfo *i, Ast *expression, AddressingMode mode, Type *type, ExactValue value);
ExprInfo *check_get_expr_info (CheckerContext *c, Ast *expr);
void add_untyped (CheckerContext *c, Ast *expression, AddressingMode mode, Type *basic_type, ExactValue value);
void add_entity_use (CheckerContext *c, Ast *identifier, Entity *entity);
void add_implicit_entity (CheckerContext *c, Ast *node, Entity *e);
void add_entity_and_decl_info(CheckerContext *c, Ast *identifier, Entity *e, DeclInfo *d, bool is_exported=true);
@@ -420,3 +462,5 @@ Type *check_poly_path_pop (CheckerContext *c);
void init_core_context(Checker *c);
void init_mem_allocator(Checker *c);
void add_untyped_expressions(CheckerInfo *cinfo, UntypedExprInfoMap *untyped);
+4
View File
@@ -33,6 +33,8 @@ enum BuiltinProcId {
BuiltinProc_soa_zip,
BuiltinProc_soa_unzip,
BuiltinProc_or_else,
BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures
// "Intrinsics"
@@ -263,6 +265,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("soa_zip"), 1, true, Expr_Expr, BuiltinProcPkg_builtin},
{STR_LIT("soa_unzip"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
{STR_LIT("or_else"), 2, false, Expr_Expr, BuiltinProcPkg_builtin},
{STR_LIT(""), 0, true, Expr_Expr, BuiltinProcPkg_builtin}, // DIRECTIVE
+202 -6
View File
@@ -27,6 +27,65 @@
#include <math.h>
#include <string.h>
#include <atomic> // Because I wanted the C++11 memory order semantics, of which gb.h does not offer (because it was a C89 library)
#if defined(GB_SYSTEM_WINDOWS)
struct BlockingMutex {
SRWLOCK srwlock;
};
void mutex_init(BlockingMutex *m) {
}
void mutex_destroy(BlockingMutex *m) {
}
void mutex_lock(BlockingMutex *m) {
AcquireSRWLockExclusive(&m->srwlock);
}
bool mutex_try_lock(BlockingMutex *m) {
return !!TryAcquireSRWLockExclusive(&m->srwlock);
}
void mutex_unlock(BlockingMutex *m) {
ReleaseSRWLockExclusive(&m->srwlock);
}
#else
typedef gbMutex BlockingMutex;
void mutex_init(BlockingMutex *m) {
gb_mutex_init(m);
}
void mutex_destroy(BlockingMutex *m) {
gb_mutex_destroy(m);
}
void mutex_lock(BlockingMutex *m) {
gb_mutex_lock(m);
}
bool mutex_try_lock(BlockingMutex *m) {
return !!gb_mutex_try_lock(m);
}
void mutex_unlock(BlockingMutex *m) {
gb_mutex_unlock(m);
}
#endif
struct RecursiveMutex {
gbMutex mutex;
};
void mutex_init(RecursiveMutex *m) {
gb_mutex_init(&m->mutex);
}
void mutex_destroy(RecursiveMutex *m) {
gb_mutex_destroy(&m->mutex);
}
void mutex_lock(RecursiveMutex *m) {
gb_mutex_lock(&m->mutex);
}
bool mutex_try_lock(RecursiveMutex *m) {
return !!gb_mutex_try_lock(&m->mutex);
}
void mutex_unlock(RecursiveMutex *m) {
gb_mutex_unlock(&m->mutex);
}
gb_inline void zero_size(void *ptr, isize len) {
memset(ptr, 0, len);
@@ -35,6 +94,11 @@ gb_inline void zero_size(void *ptr, isize len) {
#define zero_item(ptr) zero_size((ptr), gb_size_of(ptr))
i32 next_pow2(i32 n);
i64 next_pow2(i64 n);
isize next_pow2_isize(isize n);
void debugf(char const *fmt, ...);
template <typename U, typename V>
gb_inline U bit_cast(V &v) { return reinterpret_cast<U &>(v); }
@@ -167,6 +231,7 @@ GB_ALLOCATOR_PROC(heap_allocator_proc) {
#include "unicode.cpp"
#include "array.cpp"
#include "string.cpp"
#include "queue.cpp"
#define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++)
@@ -325,17 +390,17 @@ gb_global u64 const unsigned_integer_maxs[] = {
bool add_overflow_u64(u64 x, u64 y, u64 *result) {
*result = x + y;
return *result < x || *result < y;
*result = x + y;
return *result < x || *result < y;
}
bool sub_overflow_u64(u64 x, u64 y, u64 *result) {
*result = x - y;
return *result > x;
*result = x - y;
return *result > x;
}
void mul_overflow_u64(u64 x, u64 y, u64 *lo, u64 *hi) {
#if defined(GB_COMPILER_MSVC)
#if defined(GB_COMPILER_MSVC) && defined(GB_ARCH_64_BIT)
*lo = _umul128(x, y, hi);
#else
// URL(bill): https://stackoverflow.com/questions/25095741/how-can-i-multiply-64-bit-operands-and-get-128-bit-result-portably#25096197
@@ -393,6 +458,7 @@ gb_global Arena permanent_arena = {};
void arena_init(Arena *arena, gbAllocator backing, isize block_size=ARENA_DEFAULT_BLOCK_SIZE) {
arena->backing = backing;
arena->block_size = block_size;
arena->use_mutex = true;
array_init(&arena->blocks, backing, 0, 2);
gb_mutex_init(&arena->mutex);
}
@@ -515,6 +581,7 @@ struct Temp_Allocator {
isize curr_offset;
gbAllocator backup_allocator;
Array<void *> leaked_allocations;
gbMutex mutex;
};
gb_global Temp_Allocator temporary_allocator_data = {};
@@ -525,6 +592,7 @@ void temp_allocator_init(Temp_Allocator *s, isize size) {
s->len = size;
s->curr_offset = 0;
s->leaked_allocations.allocator = s->backup_allocator;
gb_mutex_init(&s->mutex);
}
void *temp_allocator_alloc(Temp_Allocator *s, isize size, isize alignment) {
@@ -567,6 +635,9 @@ GB_ALLOCATOR_PROC(temp_allocator_proc) {
Temp_Allocator *s = cast(Temp_Allocator *)allocator_data;
GB_ASSERT_NOT_NULL(s);
gb_mutex_lock(&s->mutex);
defer (gb_mutex_unlock(&s->mutex));
switch (type) {
case gbAllocation_Alloc:
return temp_allocator_alloc(s, size, alignment);
@@ -592,7 +663,8 @@ GB_ALLOCATOR_PROC(temp_allocator_proc) {
gbAllocator temporary_allocator() {
return {temp_allocator_proc, &temporary_allocator_data};
return permanent_allocator();
// return {temp_allocator_proc, &temporary_allocator_data};
}
@@ -675,6 +747,36 @@ i64 next_pow2(i64 n) {
n++;
return n;
}
isize next_pow2_isize(isize n) {
if (n <= 0) {
return 0;
}
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
#if defined(GB_ARCH_64_BIT)
n |= n >> 32;
#endif
n++;
return n;
}
u32 next_pow2_u32(u32 n) {
if (n == 0) {
return 0;
}
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
n++;
return n;
}
i32 bit_set_count(u32 x) {
x -= ((x >> 1) & 0x55555555);
@@ -1174,3 +1276,97 @@ ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
#else
#error Implement read_directory
#endif
#define USE_DAMERAU_LEVENSHTEIN 1
isize levenstein_distance_case_insensitive(String const &a, String const &b) {
isize w = a.len+1;
isize h = b.len+1;
isize *matrix = gb_alloc_array(temporary_allocator(), isize, w*h);
for (isize i = 0; i <= a.len; i++) {
matrix[i*w + 0] = i;
}
for (isize i = 0; i <= b.len; i++) {
matrix[0*w + i] = i;
}
for (isize i = 1; i <= a.len; i++) {
char a_c = gb_char_to_lower(cast(char)a.text[i-1]);
for (isize j = 1; j <= b.len; j++) {
char b_c = gb_char_to_lower(cast(char)b.text[j-1]);
if (a_c == b_c) {
matrix[i*w + j] = matrix[(i-1)*w + j-1];
} else {
isize remove = matrix[(i-1)*w + j] + 1;
isize insert = matrix[i*w + j-1] + 1;
isize substitute = matrix[(i-1)*w + j-1] + 1;
isize minimum = remove;
if (insert < minimum) {
minimum = insert;
}
if (substitute < minimum) {
minimum = substitute;
}
// Damerau-Levenshtein (transposition extension)
#if USE_DAMERAU_LEVENSHTEIN
if (i > 1 && j > 1) {
isize transpose = matrix[(i-2)*w + j-2] + 1;
if (transpose < minimum) {
minimum = transpose;
}
}
#endif
matrix[i*w + j] = minimum;
}
}
}
return matrix[a.len*w + b.len];
}
struct DistanceAndTarget {
isize distance;
String target;
};
struct DidYouMeanAnswers {
Array<DistanceAndTarget> distances;
String key;
};
enum {MAX_SMALLEST_DID_YOU_MEAN_DISTANCE = 3-USE_DAMERAU_LEVENSHTEIN};
DidYouMeanAnswers did_you_mean_make(gbAllocator allocator, isize cap, String const &key) {
DidYouMeanAnswers d = {};
array_init(&d.distances, allocator, 0, cap);
d.key = key;
return d;
}
void did_you_mean_destroy(DidYouMeanAnswers *d) {
array_free(&d->distances);
}
void did_you_mean_append(DidYouMeanAnswers *d, String const &target) {
if (target.len == 0 || target == "_") {
return;
}
DistanceAndTarget dat = {};
dat.target = target;
dat.distance = levenstein_distance_case_insensitive(d->key, target);
array_add(&d->distances, dat);
}
Slice<DistanceAndTarget> did_you_mean_results(DidYouMeanAnswers *d) {
gb_sort_array(d->distances.data, d->distances.count, gb_isize_cmp(gb_offset_of(DistanceAndTarget, distance)));
isize count = 0;
for (isize i = 0; i < d->distances.count; i++) {
isize distance = d->distances[i].distance;
if (distance > MAX_SMALLEST_DID_YOU_MEAN_DISTANCE) {
break;
}
count += 1;
}
return slice_array(d->distances, 0, count);
}
+1 -1
View File
@@ -187,7 +187,7 @@ T *odin_doc_get_item(OdinDocWriter *w, OdinDocWriterItemTracker<T> *t, u32 index
if (w->state != OdinDocWriterState_Writing) {
return nullptr;
}
GB_ASSERT(index < t->len);
GB_ASSERT(index < cast(u32)t->len);
uintptr data = cast(uintptr)w->data + cast(uintptr)(t->offset + gb_size_of(T)*index);
return cast(T *)data;
}
+5 -2
View File
@@ -66,6 +66,8 @@ enum EntityFlag : u64 {
EntityFlag_Disabled = 1ull<<24,
EntityFlag_Cold = 1ull<<25, // procedure is rarely called
EntityFlag_Lazy = 1ull<<26, // Lazily type checked
EntityFlag_Test = 1ull<<30,
EntityFlag_Overridden = 1ull<<63,
@@ -132,8 +134,9 @@ struct Entity {
lbModule * code_gen_module;
lbProcedure *code_gen_procedure;
isize order_in_src;
u64 order_in_src;
String deprecated_message;
String warning_message;
// IMPORTANT NOTE(bill): This must be a discriminated union because of patching
// later entity kinds
@@ -225,7 +228,7 @@ bool is_entity_exported(Entity *e, bool allow_builtin = false) {
if (e->flags & EntityFlag_NotExported) {
return false;
}
if (e->file != nullptr && e->file->is_private) {
if (e->file != nullptr && (e->file->flags & AstFile_IsPrivate) != 0) {
return false;
}
+20 -21
View File
@@ -27,18 +27,18 @@ Quaternion256 quaternion256_inverse(Quaternion256 x) {
enum ExactValueKind {
ExactValue_Invalid,
ExactValue_Invalid = 0,
ExactValue_Bool,
ExactValue_String,
ExactValue_Integer,
ExactValue_Float,
ExactValue_Complex,
ExactValue_Quaternion,
ExactValue_Pointer,
ExactValue_Compound, // TODO(bill): Is this good enough?
ExactValue_Procedure, // TODO(bill): Is this good enough?
ExactValue_Typeid,
ExactValue_Bool = 1,
ExactValue_String = 2,
ExactValue_Integer = 3,
ExactValue_Float = 4,
ExactValue_Complex = 5,
ExactValue_Quaternion = 6,
ExactValue_Pointer = 7,
ExactValue_Compound = 8, // TODO(bill): Is this good enough?
ExactValue_Procedure = 9, // TODO(bill): Is this good enough?
ExactValue_Typeid = 10,
ExactValue_Count,
};
@@ -75,8 +75,8 @@ HashKey hash_exact_value(ExactValue v) {
}
case ExactValue_Integer:
{
HashKey key = hashing_proc(big_int_ptr(&v.value_integer), v.value_integer.len * gb_size_of(u64));
u8 last = (u8)v.value_integer.neg;
HashKey key = hashing_proc(v.value_integer.dp, gb_size_of(*v.value_integer.dp) * v.value_integer.used);
u8 last = (u8)v.value_integer.sign;
key.key = (key.key ^ last) * 0x100000001b3ll;
return key;
}
@@ -297,13 +297,13 @@ ExactValue exact_value_float_from_string(String string) {
}
ExactValue exact_value_from_basic_literal(Token token) {
switch (token.kind) {
case Token_String: return exact_value_string(token.string);
case Token_Integer: return exact_value_integer_from_string(token.string);
case Token_Float: return exact_value_float_from_string(token.string);
ExactValue exact_value_from_basic_literal(TokenKind kind, String const &string) {
switch (kind) {
case Token_String: return exact_value_string(string);
case Token_Integer: return exact_value_integer_from_string(string);
case Token_Float: return exact_value_float_from_string(string);
case Token_Imag: {
String str = token.string;
String str = string;
Rune last_rune = cast(Rune)str[str.len-1];
str.len--; // Ignore the 'i|j|k'
f64 imag = float_from_string(str);
@@ -317,7 +317,7 @@ ExactValue exact_value_from_basic_literal(Token token) {
}
case Token_Rune: {
Rune r = GB_RUNE_INVALID;
gb_utf8_decode(token.string.text, token.string.len, &r);
utf8_decode(string.text, string.len, &r);
return exact_value_i64(r);
}
default:
@@ -719,7 +719,6 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
case Token_Shr: big_int_shr(&c, a, b); break;
default: goto error;
}
big_int_normalize(&c);
ExactValue res = {ExactValue_Integer};
res.value_integer = c;
return res;
+4 -2
View File
@@ -6162,7 +6162,7 @@ void gb_shuffle(void *base, isize count, isize size) {
a = cast(u8 *)base + (count-1) * size;
for (i = count; i > 1; i--) {
j = gb_random_gen_isize(&random) % i;
j = cast(usize)gb_random_gen_u64(&random) % i;
gb_memswap(a, cast(u8 *)base + j*size, size);
a -= size;
}
@@ -8232,7 +8232,9 @@ gbFileContents gb_file_read_contents(gbAllocator a, b32 zero_terminate, char con
if (gb_file_open(&file, filepath) == gbFileError_None) {
isize file_size = cast(isize)gb_file_size(&file);
if (file_size > 0) {
result.data = gb_alloc(a, zero_terminate ? file_size+1 : file_size);
isize total_size = file_size + !!zero_terminate;
total_size = (total_size+15)&~15;
result.data = gb_alloc(a, total_size);
result.size = file_size;
gb_file_read_at(&file, result.data, result.size, 0);
if (zero_terminate) {
-744
View File
@@ -1,744 +0,0 @@
#if defined(GB_COMPILER_MSVC) && defined(GB_ARCH_64_BIT) && defined(GB_CPU_X86)
#define MSVC_AMD64_INTRINSICS
#include <intrin.h>
#pragma intrinsic(_mul128)
#endif
#define BIT128_U64_HIGHBIT 0x8000000000000000ull
#define BIT128_U64_BITS62 0x7fffffffffffffffull
#define BIT128_U64_ALLBITS 0xffffffffffffffffull
typedef struct u128 { u64 lo; u64 hi; } u128;
typedef struct i128 { u64 lo; i64 hi; } i128;
static u128 const U128_ZERO = {0, 0};
static u128 const U128_ONE = {1, 0};
static i128 const I128_ZERO = {0, 0};
static i128 const I128_ONE = {1, 0};
static u128 const U128_NEG_ONE = {BIT128_U64_ALLBITS, BIT128_U64_ALLBITS};
static i128 const I128_NEG_ONE = {BIT128_U64_ALLBITS, cast(i64)BIT128_U64_ALLBITS};
u128 u128_lo_hi (u64 lo, u64 hi);
u128 u128_from_u32 (u32 u);
u128 u128_from_u64 (u64 u);
u128 u128_from_i64 (i64 u);
u128 u128_from_f32 (f32 f);
u128 u128_from_f64 (f64 f);
u128 u128_from_string(String string);
i128 i128_lo_hi (u64 lo, i64 hi);
i128 i128_from_u32 (u32 u);
i128 i128_from_u64 (u64 u);
i128 i128_from_i64 (i64 u);
i128 i128_from_f32 (f32 f);
i128 i128_from_f64 (f64 f);
i128 i128_from_string(String string);
u64 u128_to_u64(u128 a);
i64 u128_to_i64(u128 a);
f64 u128_to_f64(u128 a);
i128 u128_to_i128(u128 a);
u64 i128_to_u64(i128 a);
i64 i128_to_i64(i128 a);
f64 i128_to_f64(i128 a);
u128 i128_to_u128(i128 a);
String u128_to_string(u128 a, char *buf, isize len);
String i128_to_string(i128 a, char *buf, isize len);
i32 u128_cmp (u128 a, u128 b);
bool u128_eq (u128 a, u128 b);
bool u128_ne (u128 a, u128 b);
bool u128_lt (u128 a, u128 b);
bool u128_gt (u128 a, u128 b);
bool u128_le (u128 a, u128 b);
bool u128_ge (u128 a, u128 b);
u128 u128_add (u128 a, u128 b);
u128 u128_not (u128 a);
u128 u128_neg (u128 a);
u128 u128_sub (u128 a, u128 b);
u128 u128_and (u128 a, u128 b);
u128 u128_or (u128 a, u128 b);
u128 u128_xor (u128 a, u128 b);
u128 u128_and_not(u128 a, u128 b);
u128 u128_shl (u128 a, u32 n);
u128 u128_shr (u128 a, u32 n);
u128 u128_mul (u128 a, u128 b);
void u128_divide (u128 num, u128 den, u128 *quo, u128 *rem);
u128 u128_quo (u128 a, u128 b);
u128 u128_mod (u128 a, u128 b);
i128 i128_abs (i128 a);
i32 i128_cmp (i128 a, i128 b);
bool i128_eq (i128 a, i128 b);
bool i128_ne (i128 a, i128 b);
bool i128_lt (i128 a, i128 b);
bool i128_gt (i128 a, i128 b);
bool i128_le (i128 a, i128 b);
bool i128_ge (i128 a, i128 b);
i128 i128_add (i128 a, i128 b);
i128 i128_not (i128 a);
i128 i128_neg (i128 a);
i128 i128_sub (i128 a, i128 b);
i128 i128_and (i128 a, i128 b);
i128 i128_or (i128 a, i128 b);
i128 i128_xor (i128 a, i128 b);
i128 i128_and_not(i128 a, i128 b);
i128 i128_shl (i128 a, u32 n);
i128 i128_shr (i128 a, u32 n);
i128 i128_mul (i128 a, i128 b);
void i128_divide (i128 num, i128 den, i128 *quo, i128 *rem);
i128 i128_quo (i128 a, i128 b);
i128 i128_mod (i128 a, i128 b);
bool operator==(u128 const &a, u128 const &b) { return u128_eq(a, b); }
bool operator!=(u128 const &a, u128 const &b) { return u128_ne(a, b); }
bool operator< (u128 const &a, u128 const &b) { return u128_lt(a, b); }
bool operator> (u128 const &a, u128 const &b) { return u128_gt(a, b); }
bool operator<=(u128 const &a, u128 const &b) { return u128_le(a, b); }
bool operator>=(u128 const &a, u128 const &b) { return u128_ge(a, b); }
u128 operator+ (u128 const &a, u128 const &b) { return u128_add(a, b); }
u128 operator- (u128 const &a, u128 const &b) { return u128_sub(a, b); }
u128 operator* (u128 const &a, u128 const &b) { return u128_mul(a, b); }
u128 operator/ (u128 const &a, u128 const &b) { return u128_quo(a, b); }
u128 operator% (u128 const &a, u128 const &b) { return u128_mod(a, b); }
u128 operator& (u128 const &a, u128 const &b) { return u128_and(a, b); }
u128 operator| (u128 const &a, u128 const &b) { return u128_or (a, b); }
u128 operator^ (u128 const &a, u128 const &b) { return u128_xor(a, b); }
u128 operator~ (u128 const &a) { return u128_not(a); }
u128 operator+ (u128 const &a) { return a; }
u128 operator- (u128 const &a) { return u128_neg(a); }
u128 operator<<(u128 const &a, u32 const &b) { return u128_shl(a, b); }
u128 operator>>(u128 const &a, u32 const &b) { return u128_shr(a, b); }
bool operator==(i128 const &a, i128 const &b) { return i128_eq(a, b); }
bool operator!=(i128 const &a, i128 const &b) { return i128_ne(a, b); }
bool operator< (i128 const &a, i128 const &b) { return i128_lt(a, b); }
bool operator> (i128 const &a, i128 const &b) { return i128_gt(a, b); }
bool operator<=(i128 const &a, i128 const &b) { return i128_le(a, b); }
bool operator>=(i128 const &a, i128 const &b) { return i128_ge(a, b); }
i128 operator+ (i128 const &a, i128 const &b) { return i128_add(a, b); }
i128 operator- (i128 const &a, i128 const &b) { return i128_sub(a, b); }
i128 operator* (i128 const &a, i128 const &b) { return i128_mul(a, b); }
i128 operator/ (i128 const &a, i128 const &b) { return i128_quo(a, b); }
i128 operator% (i128 const &a, i128 const &b) { return i128_mod(a, b); }
i128 operator& (i128 const &a, i128 const &b) { return i128_and(a, b); }
i128 operator| (i128 const &a, i128 const &b) { return i128_or (a, b); }
i128 operator^ (i128 const &a, i128 const &b) { return i128_xor(a, b); }
i128 operator~ (i128 const &a) { return i128_not(a); }
i128 operator+ (i128 const &a) { return a; }
i128 operator- (i128 const &a) { return i128_neg(a); }
i128 operator<<(i128 const &a, u32 b) { return i128_shl(a, b); }
i128 operator>>(i128 const &a, u32 b) { return i128_shr(a, b); }
////////////////////////////////////////////////////////////////
u64 bit128__digit_value(Rune r) {
if ('0' <= r && r <= '9') {
return r - '0';
} else if ('a' <= r && r <= 'f') {
return r - 'a' + 10;
} else if ('A' <= r && r <= 'F') {
return r - 'A' + 10;
}
return 16; // NOTE(bill): Larger than highest possible
}
u128 u128_lo_hi(u64 lo, u64 hi) {
u128 r = {};
r.lo = lo;
r.hi = hi;
return r;
}
u128 u128_from_u32(u32 u) { return u128_lo_hi(cast(u64)u, 0); }
u128 u128_from_u64(u64 u) { return u128_lo_hi(cast(u64)u, 0); }
u128 u128_from_i64(i64 u) { return u128_lo_hi(cast(u64)u, u < 0 ? -1 : 0); }
u128 u128_from_f32(f32 f) { return u128_lo_hi(cast(u64)f, 0); }
u128 u128_from_f64(f64 f) { return u128_lo_hi(cast(u64)f, 0); }
u128 u128_from_string(String string) {
// TODO(bill): Allow for numbers with underscores in them
u64 base = 10;
bool has_prefix = false;
if (string.len > 2 && string[0] == '0') {
switch (string[1]) {
case 'b': base = 2; has_prefix = true; break;
case 'o': base = 8; has_prefix = true; break;
case 'd': base = 10; has_prefix = true; break;
case 'z': base = 12; has_prefix = true; break;
case 'x': base = 16; has_prefix = true; break;
}
}
u8 *text = string.text;
isize len = string.len;
if (has_prefix) {
text += 2;
len -= 2;
}
u128 base_ = u128_from_u64(base);
u128 result = {0};
for (isize i = 0; i < len; i++) {
Rune r = cast(Rune)text[i];
if (r == '_') {
continue;
}
u64 v = bit128__digit_value(r);
if (v >= base) {
break;
}
result = u128_mul(result, base_);
result = u128_add(result, u128_from_u64(v));
}
return result;
}
i128 i128_lo_hi(u64 lo, i64 hi) {
i128 i;
i.lo = lo;
i.hi = hi;
return i;
}
i128 i128_from_u32(u32 u) { return i128_lo_hi(cast(u64)u, 0); }
i128 i128_from_u64(u64 u) { return i128_lo_hi(cast(u64)u, 0); }
i128 i128_from_i64(i64 u) { return i128_lo_hi(cast(u64)u, u < 0 ? -1 : 0); }
i128 i128_from_f32(f32 f) { return i128_lo_hi(cast(u64)f, 0); }
i128 i128_from_f64(f64 f) { return i128_lo_hi(cast(u64)f, 0); }
i128 i128_from_string(String string) {
// TODO(bill): Allow for numbers with underscores in them
u64 base = 10;
bool has_prefix = false;
if (string.len > 2 && string[0] == '0') {
switch (string[1]) {
case 'b': base = 2; has_prefix = true; break;
case 'o': base = 8; has_prefix = true; break;
case 'd': base = 10; has_prefix = true; break;
case 'z': base = 12; has_prefix = true; break;
case 'x': base = 16; has_prefix = true; break;
}
}
u8 *text = string.text;
isize len = string.len;
if (has_prefix) {
text += 2;
len -= 2;
}
i128 base_ = i128_from_u64(base);
i128 result = {0};
for (isize i = 0; i < len; i++) {
Rune r = cast(Rune)text[i];
if (r == '_') {
continue;
}
u64 v = bit128__digit_value(r);
if (v >= base) {
break;
}
result = i128_mul(result, base_);
result = i128_add(result, i128_from_u64(v));
}
return result;
}
u64 u128_to_u64(u128 a) {
return (a.lo&BIT128_U64_BITS62) | (a.hi&BIT128_U64_HIGHBIT);
}
i64 u128_to_i64(u128 a) {
return a.lo;
}
f64 u128_to_f64(u128 a) {
if (a.hi >= 0) {
return (cast(f64)a.hi * 18446744073709551616.0) + cast(f64)a.lo;
}
i64 h = cast(i64)a.hi;
u64 l = a.lo;
h = ~h;
l = ~l;
l += 1;
if (l == 0) {
h += 1;
}
return -((cast(f64)h * 18446744073709551616.0) + cast(f64)l);
}
i128 u128_to_i128(u128 a) {
return bit_cast<i128>(a);
}
u64 i128_to_u64(i128 a) {
return (a.lo&BIT128_U64_BITS62) | (a.hi&BIT128_U64_HIGHBIT);
}
i64 i128_to_i64(i128 a) {
return cast(i64)a.lo;
}
f64 i128_to_f64(i128 a) {
if (a.hi >= 0) {
return (cast(f64)a.hi * 18446744073709551616.0) + cast(f64)a.lo;
}
i64 h = a.hi;
u64 l = a.lo;
h = ~h;
l = ~l;
l += 1;
if (l == 0) {
h += 1;
}
return -((cast(f64)h * 18446744073709551616.0) + cast(f64)l);
}
u128 i128_to_u128(i128 a) {
return bit_cast<u128>(a);
}
String u128_to_string(u128 v, char *out_buf, isize out_buf_len) {
char buf[200] = {0};
isize i = gb_size_of(buf);
u128 b = u128_from_u64(10);;
while (u128_ge(v, b)) {
buf[--i] = gb__num_to_char_table[u128_to_i64(u128_mod(v, b))];
v = u128_quo(v, b);
}
buf[--i] = gb__num_to_char_table[u128_to_i64(u128_mod(v, b))];
isize len = gb_min(gb_size_of(buf)-i, out_buf_len);
gb_memmove(out_buf, &buf[i], len);
return make_string(cast(u8 *)out_buf, len);
}
String i128_to_string(i128 a, char *out_buf, isize out_buf_len) {
char buf[200] = {0};
isize i = gb_size_of(buf);
bool negative = false;
if (i128_lt(a, I128_ZERO)) {
negative = true;
a = i128_neg(a);
}
u128 v = bit_cast<u128>(a);
u128 b = u128_from_u64(10);;
while (u128_ge(v, b)) {
buf[--i] = gb__num_to_char_table[u128_to_i64(u128_mod(v, b))];
v = u128_quo(v, b);
}
buf[--i] = gb__num_to_char_table[u128_to_i64(u128_mod(v, b))];
if (negative) {
buf[--i] = '-';
}
isize len = gb_min(gb_size_of(buf)-i, out_buf_len);
gb_memmove(out_buf, &buf[i], len);
return make_string(cast(u8 *)out_buf, len);
}
////////////////////////////////////////////////////////////////
i32 u128_cmp(u128 a, u128 b) {
if (a.hi == b.hi && b.lo == b.lo) {
return 0;
}
if (a.hi == b.hi) {
return a.lo < b.lo ? -1 : +1;
}
return a.hi < b.hi ? -1 : +1;
}
bool u128_eq(u128 a, u128 b) { return a.hi == b.hi && a.lo == b.lo; }
bool u128_ne(u128 a, u128 b) { return !u128_eq(a, b); }
bool u128_lt(u128 a, u128 b) { return a.hi == b.hi ? a.lo < b.lo : a.hi < b.hi; }
bool u128_gt(u128 a, u128 b) { return a.hi == b.hi ? a.lo > b.lo : a.hi > b.hi; }
bool u128_le(u128 a, u128 b) { return !u128_gt(a, b); }
bool u128_ge(u128 a, u128 b) { return !u128_lt(a, b); }
u128 u128_add(u128 a, u128 b) {
u128 old_a = a;
a.lo += b.lo;
a.hi += b.hi;
if (a.lo < old_a.lo) {
a.hi += 1;
}
return a;
}
u128 u128_not(u128 a) { return u128_lo_hi(~a.lo, ~a.hi); }
u128 u128_neg(u128 a) {
return u128_add(u128_not(a), u128_from_u64(1));
}
u128 u128_sub(u128 a, u128 b) {
return u128_add(a, u128_neg(b));
}
u128 u128_and(u128 a, u128 b) { return u128_lo_hi(a.lo&b.lo, a.hi&b.hi); }
u128 u128_or (u128 a, u128 b) { return u128_lo_hi(a.lo|b.lo, a.hi|b.hi); }
u128 u128_xor(u128 a, u128 b) { return u128_lo_hi(a.lo^b.lo, a.hi^b.hi); }
u128 u128_and_not(u128 a, u128 b) { return u128_lo_hi(a.lo&(~b.lo), a.hi&(~b.hi)); }
u128 u128_shl(u128 a, u32 n) {
if (n >= 128) {
return u128_lo_hi(0, 0);
}
#if 0 && defined(MSVC_AMD64_INTRINSICS)
a.hi = __shiftleft128(a.lo, a.hi, n);
a.lo = a.lo << n;
return a;
#else
if (n >= 64) {
n -= 64;
a.hi = a.lo;
a.lo = 0;
}
if (n != 0) {
u64 mask = ~(BIT128_U64_ALLBITS >> n);
a.hi <<= n;
a.hi |= (a.lo&mask) >> (64 - n);
a.lo <<= n;
}
return a;
#endif
}
u128 u128_shr(u128 a, u32 n) {
if (n >= 128) {
return u128_lo_hi(0, 0);
}
#if 0 && defined(MSVC_AMD64_INTRINSICS)
a.lo = __shiftright128(a.lo, a.hi, n);
a.hi = a.hi >> n;
return a;
#else
if (n >= 64) {
n -= 64;
a.lo = a.hi;
a.hi = 0;
}
if (n != 0) {
u64 mask = ~(BIT128_U64_ALLBITS << n);
a.lo >>= n;
a.lo |= (a.hi&mask) << (64 - n);
a.hi >>= n;
}
return a;
#endif
}
u128 u128_mul(u128 a, u128 b) {
if (a.lo == 0 && a.hi == 0) {
return u128_from_u64(0);
} else if (b.lo == 0 && b.hi == 0) {
return u128_from_u64(0);
}
if (u128_eq(a, U128_ONE)) {
return b;
}
if (u128_eq(b, U128_ONE)) {
return a;
}
#if defined(MSVC_AMD64_INTRINSICS)
if (a.hi == 0 && b.hi == 0) {
a.lo = _umul128(a.lo, b.lo, &a.hi);
return a;
}
#endif
u128 res = {0};
u128 t = b;
for (u32 i = 0; i < 128; i++) {
if ((t.lo&1) != 0) {
res = u128_add(res, u128_shl(a, i));
}
t = u128_shr(t, 1);
}
return res;
}
bool u128_hibit(u128 const &d) { return (d.hi & BIT128_U64_HIGHBIT) != 0; }
bool i128_hibit(i128 const &d) { return d.hi < 0; }
void u128_divide(u128 a, u128 b, u128 *quo, u128 *rem) {
if (u128_eq(b, U128_ZERO)) {
if (quo) *quo = u128_from_u64(a.lo/b.lo);
if (rem) *rem = U128_ZERO;
return;
}
if (a.hi == 0 && b.hi == 0) {
if (quo) *quo = u128_from_u64(a.lo/b.lo);
if (rem) *rem = u128_from_u64(a.lo%b.lo);
return;
}
u128 r = a;
u128 d = b;
u128 x = U128_ONE;
u128 q = U128_ZERO;
while (u128_ge(r, d) && !u128_hibit(d)) {
x = u128_shl(x, 1);
d = u128_shl(d, 1);
}
while (u128_ne(x, U128_ZERO)) {
if (u128_ge(r, d)) {
r = u128_sub(r, d);
q = u128_or(q, x);
}
x = u128_shr(x, 1);
d = u128_shr(d, 1);
}
if (quo) *quo = q;
if (rem) *rem = r;
}
u128 u128_quo(u128 a, u128 b) {
if (a.hi == 0 && b.hi == 0) {
return u128_from_u64(a.lo/b.lo);
}
u128 res = {0};
u128_divide(a, b, &res, nullptr);
return res;
}
u128 u128_mod(u128 a, u128 b) {
if (a.hi == 0 && b.hi == 0) {
return u128_from_u64(a.lo%b.lo);
}
u128 res = {0};
u128_divide(a, b, nullptr, &res);
return res;
}
////////////////////////////////////////////////////////////////
i128 i128_abs(i128 a) {
if ((a.hi&BIT128_U64_HIGHBIT) != 0) {
return i128_neg(a);
}
return a;
}
i32 i128_cmp(i128 a, i128 b) {
if (a.hi == b.hi && b.lo == b.lo) {
return 0;
}
if (a.hi == b.hi) {
return a.lo < b.lo ? -1 : +1;
}
return a.hi < b.hi ? -1 : +1;
}
bool i128_eq(i128 a, i128 b) { return a.hi == b.hi && a.lo == b.lo; }
bool i128_ne(i128 a, i128 b) { return !i128_eq(a, b); }
bool i128_lt(i128 a, i128 b) { return a.hi == b.hi ? a.lo < b.lo : a.hi < b.hi; }
bool i128_gt(i128 a, i128 b) { return a.hi == b.hi ? a.lo > b.lo : a.hi > b.hi; }
bool i128_le(i128 a, i128 b) { return a.hi == b.hi ? a.lo <= b.lo : a.hi <= b.hi; }
bool i128_ge(i128 a, i128 b) { return a.hi == b.hi ? a.lo >= b.lo : a.hi >= b.hi; }
i128 i128_add(i128 a, i128 b) {
i128 old_a = a;
a.lo += b.lo;
a.hi += b.hi;
if (a.lo < old_a.lo) {
a.hi += 1;
}
return a;
}
i128 i128_not(i128 a) { return i128_lo_hi(~a.lo, ~a.hi); }
i128 i128_neg(i128 a) {
return i128_add(i128_not(a), i128_from_u64(1));
}
i128 i128_sub(i128 a, i128 b) {
return i128_add(a, i128_neg(b));
}
i128 i128_and(i128 a, i128 b) { return i128_lo_hi(a.lo&b.lo, a.hi&b.hi); }
i128 i128_or (i128 a, i128 b) { return i128_lo_hi(a.lo|b.lo, a.hi|b.hi); }
i128 i128_xor(i128 a, i128 b) { return i128_lo_hi(a.lo^b.lo, a.hi^b.hi); }
i128 i128_and_not(i128 a, i128 b) { return i128_lo_hi(a.lo&(~b.lo), a.hi&(~b.hi)); }
i128 i128_shl(i128 a, u32 n) {
if (n >= 128) {
return i128_lo_hi(0, 0);
}
#if 0 && defined(MSVC_AMD64_INTRINSICS)
a.hi = __shiftleft128(a.lo, a.hi, n);
a.lo = a.lo << n;
return a;
#else
if (n >= 64) {
n -= 64;
a.hi = a.lo;
a.lo = 0;
}
if (n != 0) {
u64 mask = ~(BIT128_U64_ALLBITS >> n);
a.hi <<= n;
a.hi |= (a.lo&mask) >> (64 - n);
a.lo <<= n;
}
return a;
#endif
}
i128 i128_shr(i128 a, u32 n) {
if (n >= 128) {
return i128_lo_hi(0, 0);
}
#if 0 && defined(MSVC_AMD64_INTRINSICS)
a.lo = __shiftright128(a.lo, a.hi, n);
a.hi = a.hi >> n;
return a;
#else
if (n >= 64) {
n -= 64;
a.lo = a.hi;
a.hi = 0;
}
if (n != 0) {
u64 mask = ~(BIT128_U64_ALLBITS << n);
a.lo >>= n;
a.lo |= (a.hi&mask) << (64 - n);
a.hi >>= n;
}
return a;
#endif
}
i128 i128_mul(i128 a, i128 b) {
if (a.lo == 0 && a.hi == 0) {
return i128_from_u64(0);
} else if (b.lo == 0 && b.hi == 0) {
return i128_from_u64(0);
}
if (i128_eq(a, I128_ONE)) {
return b;
}
if (i128_eq(b, I128_ONE)) {
return a;
}
#if defined(MSVC_AMD64_INTRINSICS)
if (a.hi == 0 && b.hi == 0) {
a.lo = _mul128(a.lo, b.lo, &a.hi);
return a;
}
#endif
i128 res = {0};
i128 t = b;
for (u32 i = 0; i < 128; i++) {
if ((t.lo&1) != 0) {
res = i128_add(res, i128_shl(a, i));
}
t = i128_shr(t, 1);
}
return res;
}
void i128_divide(i128 a, i128 b, i128 *quo_, i128 *rem_) {
// IMPORTANT TODO(bill): Optimize this i128 division calculation
i128 iquo = {0};
i128 irem = {0};
if (a.hi == 0 && b.hi == 0) {
u64 q = a.lo / b.lo;
u64 r = a.lo % b.lo;
iquo = i128_from_u64(q);
irem = i128_from_u64(r);
} else if ((~a.hi) == 0 && (~b.hi) == 0) {
i64 x = i128_to_i64(a);
i64 y = i128_to_i64(b);
i64 q = x / y;
i64 r = x % y;
iquo = i128_from_i64(q);
irem = i128_from_i64(r);
} else if (a.hi > 0 || b.hi > 0) {
u128 q, r = {0};
u128_divide(bit_cast<u128>(a), bit_cast<u128>(b), &q, &r);
iquo = bit_cast<i128>(q);
irem = bit_cast<i128>(r);
} else if (i128_eq(b, I128_ZERO)) {
iquo = i128_from_u64(a.lo/b.lo);
} else {
i32 rem_sign = 1;
i32 quo_sign = 1;
if (i128_lt(a, I128_ZERO)) {
a = i128_neg(a);
rem_sign = -1;
}
if (i128_lt(b, I128_ZERO)) {
b = i128_neg(b);
quo_sign = -1;
}
quo_sign *= rem_sign;
iquo = a;
for (isize i = 0; i < 128; i++) {
irem = i128_shl(irem, 1);
if (i128_lt(iquo, I128_ZERO)) {
irem.lo |= 1;
}
iquo = i128_shl(iquo, 1);
if (i128_ge(irem, b)) {
irem = i128_sub(irem, b);
iquo = i128_add(iquo, I128_ONE);
}
}
if (quo_sign < 0) iquo = i128_neg(iquo);
if (rem_sign < 0) irem = i128_neg(irem);
}
if (quo_) *quo_ = iquo;
if (rem_) *rem_ = irem;
}
i128 i128_quo(i128 a, i128 b) {
i128 res = {0};
i128_divide(a, b, &res, nullptr);
return res;
}
i128 i128_mod(i128 a, i128 b) {
i128 res = {0};
i128_divide(a, b, nullptr, &res);
return res;
}
+154
View File
@@ -0,0 +1,154 @@
#include "libtommath/mp_2expt.c"
#include "libtommath/mp_abs.c"
#include "libtommath/mp_add.c"
#include "libtommath/mp_add_d.c"
#include "libtommath/mp_addmod.c"
#include "libtommath/mp_and.c"
#include "libtommath/mp_clamp.c"
#include "libtommath/mp_clear.c"
#include "libtommath/mp_clear_multi.c"
#include "libtommath/mp_cmp.c"
#include "libtommath/mp_cmp_d.c"
#include "libtommath/mp_cmp_mag.c"
#include "libtommath/mp_cnt_lsb.c"
#include "libtommath/mp_complement.c"
#include "libtommath/mp_copy.c"
#include "libtommath/mp_count_bits.c"
#include "libtommath/mp_cutoffs.c"
#include "libtommath/mp_div.c"
#include "libtommath/mp_div_2.c"
#include "libtommath/mp_div_2d.c"
#include "libtommath/mp_div_d.c"
#include "libtommath/mp_dr_is_modulus.c"
#include "libtommath/mp_dr_reduce.c"
#include "libtommath/mp_dr_setup.c"
#include "libtommath/mp_error_to_string.c"
#include "libtommath/mp_exch.c"
#include "libtommath/mp_expt_n.c"
#include "libtommath/mp_exptmod.c"
#include "libtommath/mp_exteuclid.c"
#include "libtommath/mp_fread.c"
#include "libtommath/mp_from_sbin.c"
#include "libtommath/mp_from_ubin.c"
#include "libtommath/mp_fwrite.c"
#include "libtommath/mp_gcd.c"
#include "libtommath/mp_get_double.c"
#include "libtommath/mp_get_i32.c"
#include "libtommath/mp_get_i64.c"
#include "libtommath/mp_get_l.c"
#include "libtommath/mp_get_mag_u32.c"
#include "libtommath/mp_get_mag_u64.c"
#include "libtommath/mp_get_mag_ul.c"
#include "libtommath/mp_grow.c"
#include "libtommath/mp_init.c"
#include "libtommath/mp_init_copy.c"
#include "libtommath/mp_init_i32.c"
#include "libtommath/mp_init_i64.c"
#include "libtommath/mp_init_l.c"
#include "libtommath/mp_init_multi.c"
#include "libtommath/mp_init_set.c"
#include "libtommath/mp_init_size.c"
#include "libtommath/mp_init_u32.c"
#include "libtommath/mp_init_u64.c"
#include "libtommath/mp_init_ul.c"
#include "libtommath/mp_invmod.c"
#include "libtommath/mp_is_square.c"
#include "libtommath/mp_kronecker.c"
#include "libtommath/mp_lcm.c"
#include "libtommath/mp_log_n.c"
#include "libtommath/mp_lshd.c"
#include "libtommath/mp_mod.c"
#include "libtommath/mp_mod_2d.c"
#include "libtommath/mp_montgomery_calc_normalization.c"
#include "libtommath/mp_montgomery_reduce.c"
#include "libtommath/mp_montgomery_setup.c"
#include "libtommath/mp_mul.c"
#include "libtommath/mp_mul_2.c"
#include "libtommath/mp_mul_2d.c"
#include "libtommath/mp_mul_d.c"
#include "libtommath/mp_mulmod.c"
#include "libtommath/mp_neg.c"
#include "libtommath/mp_or.c"
#include "libtommath/mp_pack.c"
#include "libtommath/mp_pack_count.c"
// #include "libtommath/mp_prime_fermat.c"
// #include "libtommath/mp_prime_frobenius_underwood.c"
// #include "libtommath/mp_prime_is_prime.c"
// #include "libtommath/mp_prime_miller_rabin.c"
// #include "libtommath/mp_prime_next_prime.c"
// #include "libtommath/mp_prime_rabin_miller_trials.c"
// #include "libtommath/mp_prime_rand.c"
// #include "libtommath/mp_prime_strong_lucas_selfridge.c"
#include "libtommath/mp_radix_size.c"
#include "libtommath/mp_radix_size_overestimate.c"
// #include "libtommath/mp_rand.c"
// #include "libtommath/mp_rand_source.c"
#include "libtommath/mp_read_radix.c"
#include "libtommath/mp_reduce.c"
#include "libtommath/mp_reduce_2k.c"
#include "libtommath/mp_reduce_2k_l.c"
#include "libtommath/mp_reduce_2k_setup.c"
#include "libtommath/mp_reduce_2k_setup_l.c"
#include "libtommath/mp_reduce_is_2k.c"
#include "libtommath/mp_reduce_is_2k_l.c"
#include "libtommath/mp_reduce_setup.c"
#include "libtommath/mp_root_n.c"
#include "libtommath/mp_rshd.c"
#include "libtommath/mp_sbin_size.c"
#include "libtommath/mp_set.c"
#include "libtommath/mp_set_double.c"
#include "libtommath/mp_set_i32.c"
#include "libtommath/mp_set_i64.c"
#include "libtommath/mp_set_l.c"
#include "libtommath/mp_set_u32.c"
#include "libtommath/mp_set_u64.c"
#include "libtommath/mp_set_ul.c"
#include "libtommath/mp_shrink.c"
#include "libtommath/mp_signed_rsh.c"
#include "libtommath/mp_sqrmod.c"
#include "libtommath/mp_sqrt.c"
#include "libtommath/mp_sqrtmod_prime.c"
#include "libtommath/mp_sub.c"
#include "libtommath/mp_sub_d.c"
#include "libtommath/mp_submod.c"
#include "libtommath/mp_to_radix.c"
#include "libtommath/mp_to_sbin.c"
#include "libtommath/mp_to_ubin.c"
#include "libtommath/mp_ubin_size.c"
#include "libtommath/mp_unpack.c"
#include "libtommath/mp_xor.c"
#include "libtommath/mp_zero.c"
#include "libtommath/s_mp_add.c"
#include "libtommath/s_mp_copy_digs.c"
#include "libtommath/s_mp_div_3.c"
#include "libtommath/s_mp_div_recursive.c"
#include "libtommath/s_mp_div_school.c"
#include "libtommath/s_mp_div_small.c"
#include "libtommath/s_mp_exptmod.c"
#include "libtommath/s_mp_exptmod_fast.c"
#include "libtommath/s_mp_get_bit.c"
#include "libtommath/s_mp_invmod.c"
#include "libtommath/s_mp_invmod_odd.c"
#include "libtommath/s_mp_log.c"
#include "libtommath/s_mp_log_2expt.c"
#include "libtommath/s_mp_log_d.c"
#include "libtommath/s_mp_montgomery_reduce_comba.c"
#include "libtommath/s_mp_mul.c"
#include "libtommath/s_mp_mul_balance.c"
#include "libtommath/s_mp_mul_comba.c"
#include "libtommath/s_mp_mul_high.c"
#include "libtommath/s_mp_mul_high_comba.c"
#include "libtommath/s_mp_mul_karatsuba.c"
#include "libtommath/s_mp_mul_toom.c"
#include "libtommath/s_mp_prime_is_divisible.c"
#include "libtommath/s_mp_prime_tab.c"
#include "libtommath/s_mp_radix_map.c"
#include "libtommath/s_mp_radix_size_overestimate.c"
// #include "libtommath/s_mp_rand_platform.c"
#include "libtommath/s_mp_sqr.c"
#include "libtommath/s_mp_sqr_comba.c"
#include "libtommath/s_mp_sqr_karatsuba.c"
#include "libtommath/s_mp_sqr_toom.c"
#include "libtommath/s_mp_sub.c"
#include "libtommath/s_mp_zero_buf.c"
#include "libtommath/s_mp_zero_digs.c"
+26
View File
@@ -0,0 +1,26 @@
The LibTom license
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
+44
View File
@@ -0,0 +1,44 @@
# libtommath
This is the git repository for [LibTomMath](http://www.libtom.net/LibTomMath/), a free open source portable number theoretic multiple-precision integer (MPI) library written entirely in C.
## Build Status
### Travis CI
master: [![Build Status](https://api.travis-ci.org/libtom/libtommath.png?branch=master)](https://travis-ci.org/libtom/libtommath)
develop: [![Build Status](https://api.travis-ci.org/libtom/libtommath.png?branch=develop)](https://travis-ci.org/libtom/libtommath)
### AppVeyor
master: [![Build status](https://ci.appveyor.com/api/projects/status/b80lpolw3i8m6hsh/branch/master?svg=true)](https://ci.appveyor.com/project/libtom/libtommath/branch/master)
develop: [![Build status](https://ci.appveyor.com/api/projects/status/b80lpolw3i8m6hsh/branch/develop?svg=true)](https://ci.appveyor.com/project/libtom/libtommath/branch/develop)
### ABI Laboratory
API/ABI changes: [check here](https://abi-laboratory.pro/tracker/timeline/libtommath/)
## Summary
The `develop` branch contains the in-development version. Stable releases are tagged.
Documentation is built from the LaTeX file `bn.tex`. There is also limited documentation in `tommath.h`.
There is also a document, `tommath.pdf`, which describes the goals of the project and many of the algorithms used.
The project can be build by using `make`. Along with the usual `make`, `make clean` and `make install`,
there are several other build targets, see the makefile for details.
There are also makefiles for certain specific platforms.
## Testing
Tests are located in `demo/` and can be built in two flavors.
* `make test` creates a stand-alone test binary that executes several test routines.
* `make mtest_opponent` creates a test binary that is intended to be run against `mtest`.
`mtest` can be built with `make mtest` and test execution is done like `./mtest/mtest | ./mtest_opponent`.
`mtest` is creating test vectors using an alternative MPI library and `test` is consuming these vectors to verify correct behavior of ltm
## Building and Installing
Building is straightforward for GNU Linux only, the section "Building LibTomMath" in the documentation in `doc/bn.pdf` has the details.
+31
View File
@@ -0,0 +1,31 @@
#include "tommath_private.h"
#ifdef MP_2EXPT_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* computes a = 2**b
*
* Simple algorithm which zeroes the int, grows it then just sets one bit
* as required.
*/
mp_err mp_2expt(mp_int *a, int b)
{
mp_err err;
/* zero a as per default */
mp_zero(a);
/* grow a to accomodate the single bit */
if ((err = mp_grow(a, (b / MP_DIGIT_BIT) + 1)) != MP_OKAY) {
return err;
}
/* set the used count of where the bit will go */
a->used = (b / MP_DIGIT_BIT) + 1;
/* put the single bit in its place */
a->dp[b / MP_DIGIT_BIT] = (mp_digit)1 << (mp_digit)(b % MP_DIGIT_BIT);
return MP_OKAY;
}
#endif
+24
View File
@@ -0,0 +1,24 @@
#include "tommath_private.h"
#ifdef MP_ABS_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* b = |a|
*
* Simple function copies the input and fixes the sign to positive
*/
mp_err mp_abs(const mp_int *a, mp_int *b)
{
mp_err err;
/* copy a to b */
if ((err = mp_copy(a, b)) != MP_OKAY) {
return err;
}
/* force the sign of b to positive */
b->sign = MP_ZPOS;
return MP_OKAY;
}
#endif
+29
View File
@@ -0,0 +1,29 @@
#include "tommath_private.h"
#ifdef MP_ADD_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* high level addition (handles signs) */
mp_err mp_add(const mp_int *a, const mp_int *b, mp_int *c)
{
/* handle two cases, not four */
if (a->sign == b->sign) {
/* both positive or both negative */
/* add their magnitudes, copy the sign */
c->sign = a->sign;
return s_mp_add(a, b, c);
}
/* one positive, the other negative */
/* subtract the one with the greater magnitude from */
/* the one of the lesser magnitude. The result gets */
/* the sign of the one with the greater magnitude. */
if (mp_cmp_mag(a, b) == MP_LT) {
MP_EXCH(const mp_int *, a, b);
}
c->sign = a->sign;
return s_mp_sub(a, b, c);
}
#endif
+86
View File
@@ -0,0 +1,86 @@
#include "tommath_private.h"
#ifdef MP_ADD_D_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* single digit addition */
mp_err mp_add_d(const mp_int *a, mp_digit b, mp_int *c)
{
mp_err err;
int oldused;
/* fast path for a == c */
if (a == c) {
if (!mp_isneg(c) &&
!mp_iszero(c) &&
((c->dp[0] + b) < MP_DIGIT_MAX)) {
c->dp[0] += b;
return MP_OKAY;
}
if (mp_isneg(c) &&
(c->dp[0] > b)) {
c->dp[0] -= b;
return MP_OKAY;
}
}
/* grow c as required */
if ((err = mp_grow(c, a->used + 1)) != MP_OKAY) {
return err;
}
/* if a is negative and |a| >= b, call c = |a| - b */
if (mp_isneg(a) && ((a->used > 1) || (a->dp[0] >= b))) {
mp_int a_ = *a;
/* temporarily fix sign of a */
a_.sign = MP_ZPOS;
/* c = |a| - b */
err = mp_sub_d(&a_, b, c);
/* fix sign */
c->sign = MP_NEG;
/* clamp */
mp_clamp(c);
return err;
}
/* old number of used digits in c */
oldused = c->used;
/* if a is positive */
if (!mp_isneg(a)) {
/* add digits, mu is carry */
int i;
mp_digit mu = b;
for (i = 0; i < a->used; i++) {
c->dp[i] = a->dp[i] + mu;
mu = c->dp[i] >> MP_DIGIT_BIT;
c->dp[i] &= MP_MASK;
}
/* set final carry */
c->dp[i] = mu;
/* setup size */
c->used = a->used + 1;
} else {
/* a was negative and |a| < b */
c->used = 1;
/* the result is a single digit */
c->dp[0] = (a->used == 1) ? b - a->dp[0] : b;
}
/* sign always positive */
c->sign = MP_ZPOS;
/* now zero to oldused */
s_mp_zero_digs(c->dp + c->used, oldused - c->used);
mp_clamp(c);
return MP_OKAY;
}
#endif
+15
View File
@@ -0,0 +1,15 @@
#include "tommath_private.h"
#ifdef MP_ADDMOD_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* d = a + b (mod c) */
mp_err mp_addmod(const mp_int *a, const mp_int *b, const mp_int *c, mp_int *d)
{
mp_err err;
if ((err = mp_add(a, b, d)) != MP_OKAY) {
return err;
}
return mp_mod(d, c, d);
}
#endif
+54
View File
@@ -0,0 +1,54 @@
#include "tommath_private.h"
#ifdef MP_AND_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* two complement and */
mp_err mp_and(const mp_int *a, const mp_int *b, mp_int *c)
{
int used = MP_MAX(a->used, b->used) + 1, i;
mp_err err;
mp_digit ac = 1, bc = 1, cc = 1;
bool neg = (mp_isneg(a) && mp_isneg(b));
if ((err = mp_grow(c, used)) != MP_OKAY) {
return err;
}
for (i = 0; i < used; i++) {
mp_digit x, y;
/* convert to two complement if negative */
if (mp_isneg(a)) {
ac += (i >= a->used) ? MP_MASK : (~a->dp[i] & MP_MASK);
x = ac & MP_MASK;
ac >>= MP_DIGIT_BIT;
} else {
x = (i >= a->used) ? 0uL : a->dp[i];
}
/* convert to two complement if negative */
if (mp_isneg(b)) {
bc += (i >= b->used) ? MP_MASK : (~b->dp[i] & MP_MASK);
y = bc & MP_MASK;
bc >>= MP_DIGIT_BIT;
} else {
y = (i >= b->used) ? 0uL : b->dp[i];
}
c->dp[i] = x & y;
/* convert to to sign-magnitude if negative */
if (neg) {
cc += ~c->dp[i] & MP_MASK;
c->dp[i] = cc & MP_MASK;
cc >>= MP_DIGIT_BIT;
}
}
c->used = used;
c->sign = (neg ? MP_NEG : MP_ZPOS);
mp_clamp(c);
return MP_OKAY;
}
#endif
+27
View File
@@ -0,0 +1,27 @@
#include "tommath_private.h"
#ifdef MP_CLAMP_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* trim unused digits
*
* This is used to ensure that leading zero digits are
* trimed and the leading "used" digit will be non-zero
* Typically very fast. Also fixes the sign if there
* are no more leading digits
*/
void mp_clamp(mp_int *a)
{
/* decrease used while the most significant digit is
* zero.
*/
while ((a->used > 0) && (a->dp[a->used - 1] == 0u)) {
--(a->used);
}
/* reset the sign flag if zero */
if (mp_iszero(a)) {
a->sign = MP_ZPOS;
}
}
#endif
+20
View File
@@ -0,0 +1,20 @@
#include "tommath_private.h"
#ifdef MP_CLEAR_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* clear one (frees) */
void mp_clear(mp_int *a)
{
/* only do anything if a hasn't been freed previously */
if (a->dp != NULL) {
/* free ram */
MP_FREE_DIGS(a->dp, a->alloc);
/* reset members to make debugging easier */
a->dp = NULL;
a->alloc = a->used = 0;
a->sign = MP_ZPOS;
}
}
#endif
+18
View File
@@ -0,0 +1,18 @@
#include "tommath_private.h"
#ifdef MP_CLEAR_MULTI_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
#include <stdarg.h>
void mp_clear_multi(mp_int *mp, ...)
{
va_list args;
va_start(args, mp);
while (mp != NULL) {
mp_clear(mp);
mp = va_arg(args, mp_int *);
}
va_end(args);
}
#endif
+21
View File
@@ -0,0 +1,21 @@
#include "tommath_private.h"
#ifdef MP_CMP_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* compare two ints (signed)*/
mp_ord mp_cmp(const mp_int *a, const mp_int *b)
{
/* compare based on sign */
if (a->sign != b->sign) {
return mp_isneg(a) ? MP_LT : MP_GT;
}
/* if negative compare opposite direction */
if (mp_isneg(a)) {
MP_EXCH(const mp_int *, a, b);
}
return mp_cmp_mag(a, b);
}
#endif
+26
View File
@@ -0,0 +1,26 @@
#include "tommath_private.h"
#ifdef MP_CMP_D_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* compare a digit */
mp_ord mp_cmp_d(const mp_int *a, mp_digit b)
{
/* compare based on sign */
if (mp_isneg(a)) {
return MP_LT;
}
/* compare based on magnitude */
if (a->used > 1) {
return MP_GT;
}
/* compare the only digit of a to b */
if (a->dp[0] != b) {
return a->dp[0] > b ? MP_GT : MP_LT;
}
return MP_EQ;
}
#endif
+25
View File
@@ -0,0 +1,25 @@
#include "tommath_private.h"
#ifdef MP_CMP_MAG_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* compare maginitude of two ints (unsigned) */
mp_ord mp_cmp_mag(const mp_int *a, const mp_int *b)
{
int n;
/* compare based on # of non-zero digits */
if (a->used != b->used) {
return a->used > b->used ? MP_GT : MP_LT;
}
/* compare based on digits */
for (n = a->used; n --> 0;) {
if (a->dp[n] != b->dp[n]) {
return a->dp[n] > b->dp[n] ? MP_GT : MP_LT;
}
}
return MP_EQ;
}
#endif
+38
View File
@@ -0,0 +1,38 @@
#include "tommath_private.h"
#ifdef MP_CNT_LSB_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
static const char lnz[16] = {
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
};
/* Counts the number of lsbs which are zero before the first zero bit */
int mp_cnt_lsb(const mp_int *a)
{
int x;
mp_digit q;
/* easy out */
if (mp_iszero(a)) {
return 0;
}
/* scan lower digits until non-zero */
for (x = 0; (x < a->used) && (a->dp[x] == 0u); x++) {}
q = a->dp[x];
x *= MP_DIGIT_BIT;
/* now scan this digit until a 1 is found */
if ((q & 1u) == 0u) {
mp_digit p;
do {
p = q & 15u;
x += lnz[p];
q >>= 4;
} while (p == 0u);
}
return x;
}
#endif
+13
View File
@@ -0,0 +1,13 @@
#include "tommath_private.h"
#ifdef MP_COMPLEMENT_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* b = ~a */
mp_err mp_complement(const mp_int *a, mp_int *b)
{
mp_int a_ = *a;
a_.sign = ((a_.sign == MP_ZPOS) && !mp_iszero(a)) ? MP_NEG : MP_ZPOS;
return mp_sub_d(&a_, 1uL, b);
}
#endif
+29
View File
@@ -0,0 +1,29 @@
#include "tommath_private.h"
#ifdef MP_COPY_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* copy, b = a */
mp_err mp_copy(const mp_int *a, mp_int *b)
{
mp_err err;
/* if dst == src do nothing */
if (a == b) {
return MP_OKAY;
}
/* grow dest */
if ((err = mp_grow(b, a->used)) != MP_OKAY) {
return err;
}
/* copy everything over and zero high digits */
s_mp_copy_digs(b->dp, a->dp, a->used);
s_mp_zero_digs(b->dp + a->used, b->used - a->used);
b->used = a->used;
b->sign = a->sign;
return MP_OKAY;
}
#endif
+28
View File
@@ -0,0 +1,28 @@
#include "tommath_private.h"
#ifdef MP_COUNT_BITS_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* returns the number of bits in an int */
int mp_count_bits(const mp_int *a)
{
int r;
mp_digit q;
/* shortcut */
if (mp_iszero(a)) {
return 0;
}
/* get number of digits and add that */
r = (a->used - 1) * MP_DIGIT_BIT;
/* take the last digit and count the bits in it */
q = a->dp[a->used - 1];
while (q > 0u) {
++r;
q >>= 1u;
}
return r;
}
#endif
+14
View File
@@ -0,0 +1,14 @@
#include "tommath_private.h"
#ifdef MP_CUTOFFS_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
#ifndef MP_FIXED_CUTOFFS
#include "tommath_cutoffs.h"
int MP_MUL_KARATSUBA_CUTOFF = MP_DEFAULT_MUL_KARATSUBA_CUTOFF,
MP_SQR_KARATSUBA_CUTOFF = MP_DEFAULT_SQR_KARATSUBA_CUTOFF,
MP_MUL_TOOM_CUTOFF = MP_DEFAULT_MUL_TOOM_CUTOFF,
MP_SQR_TOOM_CUTOFF = MP_DEFAULT_SQR_TOOM_CUTOFF;
#endif
#endif
+42
View File
@@ -0,0 +1,42 @@
#include "tommath_private.h"
#ifdef MP_DIV_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
mp_err mp_div(const mp_int *a, const mp_int *b, mp_int *c, mp_int *d)
{
mp_err err;
/* is divisor zero ? */
if (mp_iszero(b)) {
return MP_VAL;
}
/* if a < b then q = 0, r = a */
if (mp_cmp_mag(a, b) == MP_LT) {
if (d != NULL) {
if ((err = mp_copy(a, d)) != MP_OKAY) {
return err;
}
}
if (c != NULL) {
mp_zero(c);
}
return MP_OKAY;
}
if (MP_HAS(S_MP_DIV_RECURSIVE)
&& (b->used > (2 * MP_MUL_KARATSUBA_CUTOFF))
&& (b->used <= ((a->used/3)*2))) {
err = s_mp_div_recursive(a, b, c, d);
} else if (MP_HAS(S_MP_DIV_SCHOOL)) {
err = s_mp_div_school(a, b, c, d);
} else if (MP_HAS(S_MP_DIV_SMALL)) {
err = s_mp_div_small(a, b, c, d);
} else {
err = MP_VAL;
}
return err;
}
#endif
+40
View File
@@ -0,0 +1,40 @@
#include "tommath_private.h"
#ifdef MP_DIV_2_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* b = a/2 */
mp_err mp_div_2(const mp_int *a, mp_int *b)
{
mp_err err;
int x, oldused;
mp_digit r;
if ((err = mp_grow(b, a->used)) != MP_OKAY) {
return err;
}
oldused = b->used;
b->used = a->used;
/* carry */
r = 0;
for (x = b->used; x --> 0;) {
/* get the carry for the next iteration */
mp_digit rr = a->dp[x] & 1u;
/* shift the current digit, add in carry and store */
b->dp[x] = (a->dp[x] >> 1) | (r << (MP_DIGIT_BIT - 1));
/* forward carry to next iteration */
r = rr;
}
/* zero excess digits */
s_mp_zero_digs(b->dp + b->used, oldused - b->used);
b->sign = a->sign;
mp_clamp(b);
return MP_OKAY;
}
#endif
+61
View File
@@ -0,0 +1,61 @@
#include "tommath_private.h"
#ifdef MP_DIV_2D_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* shift right by a certain bit count (store quotient in c, optional remainder in d) */
mp_err mp_div_2d(const mp_int *a, int b, mp_int *c, mp_int *d)
{
mp_err err;
if (b < 0) {
return MP_VAL;
}
if ((err = mp_copy(a, c)) != MP_OKAY) {
return err;
}
/* 'a' should not be used after here - it might be the same as d */
/* get the remainder */
if (d != NULL) {
if ((err = mp_mod_2d(a, b, d)) != MP_OKAY) {
return err;
}
}
/* shift by as many digits in the bit count */
if (b >= MP_DIGIT_BIT) {
mp_rshd(c, b / MP_DIGIT_BIT);
}
/* shift any bit count < MP_DIGIT_BIT */
b %= MP_DIGIT_BIT;
if (b != 0u) {
int x;
mp_digit r, mask, shift;
/* mask */
mask = ((mp_digit)1 << b) - 1uL;
/* shift for lsb */
shift = (mp_digit)(MP_DIGIT_BIT - b);
/* carry */
r = 0;
for (x = c->used; x --> 0;) {
/* get the lower bits of this word in a temp */
mp_digit rr = c->dp[x] & mask;
/* shift the current word and mix in the carry bits from the previous word */
c->dp[x] = (c->dp[x] >> b) | (r << shift);
/* set the carry to the carry bits of the current word found above */
r = rr;
}
}
mp_clamp(c);
return MP_OKAY;
}
#endif
+84
View File
@@ -0,0 +1,84 @@
#include "tommath_private.h"
#ifdef MP_DIV_D_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* single digit division (based on routine from MPI) */
mp_err mp_div_d(const mp_int *a, mp_digit b, mp_int *c, mp_digit *d)
{
mp_int q;
mp_word w;
mp_err err;
int ix;
/* cannot divide by zero */
if (b == 0u) {
return MP_VAL;
}
/* quick outs */
if ((b == 1u) || mp_iszero(a)) {
if (d != NULL) {
*d = 0;
}
if (c != NULL) {
return mp_copy(a, c);
}
return MP_OKAY;
}
/* power of two ? */
if (MP_HAS(MP_DIV_2) && (b == 2u)) {
if (d != NULL) {
*d = mp_isodd(a) ? 1u : 0u;
}
return (c == NULL) ? MP_OKAY : mp_div_2(a, c);
}
if (MP_HAS(MP_DIV_2D) && MP_IS_2EXPT(b)) {
ix = 1;
while ((ix < MP_DIGIT_BIT) && (b != (((mp_digit)1)<<ix))) {
ix++;
}
if (d != NULL) {
*d = a->dp[0] & (((mp_digit)1<<(mp_digit)ix) - 1uL);
}
return (c == NULL) ? MP_OKAY : mp_div_2d(a, ix, c, NULL);
}
/* three? */
if (MP_HAS(S_MP_DIV_3) && (b == 3u)) {
return s_mp_div_3(a, c, d);
}
/* no easy answer [c'est la vie]. Just division */
if ((err = mp_init_size(&q, a->used)) != MP_OKAY) {
return err;
}
q.used = a->used;
q.sign = a->sign;
w = 0;
for (ix = a->used; ix --> 0;) {
mp_digit t = 0;
w = (w << (mp_word)MP_DIGIT_BIT) | (mp_word)a->dp[ix];
if (w >= b) {
t = (mp_digit)(w / b);
w -= (mp_word)t * (mp_word)b;
}
q.dp[ix] = t;
}
if (d != NULL) {
*d = (mp_digit)w;
}
if (c != NULL) {
mp_clamp(&q);
mp_exch(&q, c);
}
mp_clear(&q);
return MP_OKAY;
}
#endif
+27
View File
@@ -0,0 +1,27 @@
#include "tommath_private.h"
#ifdef MP_DR_IS_MODULUS_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* determines if a number is a valid DR modulus */
bool mp_dr_is_modulus(const mp_int *a)
{
int ix;
/* must be at least two digits */
if (a->used < 2) {
return false;
}
/* must be of the form b**k - a [a <= b] so all
* but the first digit must be equal to -1 (mod b).
*/
for (ix = 1; ix < a->used; ix++) {
if (a->dp[ix] != MP_MASK) {
return false;
}
}
return true;
}
#endif
+68
View File
@@ -0,0 +1,68 @@
#include "tommath_private.h"
#ifdef MP_DR_REDUCE_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* reduce "x" in place modulo "n" using the Diminished Radix algorithm.
*
* Based on algorithm from the paper
*
* "Generating Efficient Primes for Discrete Log Cryptosystems"
* Chae Hoon Lim, Pil Joong Lee,
* POSTECH Information Research Laboratories
*
* The modulus must be of a special format [see manual]
*
* Has been modified to use algorithm 7.10 from the LTM book instead
*
* Input x must be in the range 0 <= x <= (n-1)**2
*/
mp_err mp_dr_reduce(mp_int *x, const mp_int *n, mp_digit k)
{
mp_err err;
/* m = digits in modulus */
int m = n->used;
/* ensure that "x" has at least 2m digits */
if ((err = mp_grow(x, m + m)) != MP_OKAY) {
return err;
}
/* top of loop, this is where the code resumes if
* another reduction pass is required.
*/
for (;;) {
int i;
mp_digit mu = 0;
/* compute (x mod B**m) + k * [x/B**m] inline and inplace */
for (i = 0; i < m; i++) {
mp_word r = ((mp_word)x->dp[i + m] * (mp_word)k) + x->dp[i] + mu;
x->dp[i] = (mp_digit)(r & MP_MASK);
mu = (mp_digit)(r >> ((mp_word)MP_DIGIT_BIT));
}
/* set final carry */
x->dp[i] = mu;
/* zero words above m */
s_mp_zero_digs(x->dp + m + 1, (x->used - m) - 1);
/* clamp, sub and return */
mp_clamp(x);
/* if x >= n then subtract and reduce again
* Each successive "recursion" makes the input smaller and smaller.
*/
if (mp_cmp_mag(x, n) == MP_LT) {
break;
}
if ((err = s_mp_sub(x, n, x)) != MP_OKAY) {
return err;
}
}
return MP_OKAY;
}
#endif
+15
View File
@@ -0,0 +1,15 @@
#include "tommath_private.h"
#ifdef MP_DR_SETUP_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* determines the setup value */
void mp_dr_setup(const mp_int *a, mp_digit *d)
{
/* the casts are required if MP_DIGIT_BIT is one less than
* the number of bits in a mp_digit [e.g. MP_DIGIT_BIT==31]
*/
*d = (mp_digit)(((mp_word)1 << (mp_word)MP_DIGIT_BIT) - (mp_word)a->dp[0]);
}
#endif
+29
View File
@@ -0,0 +1,29 @@
#include "tommath_private.h"
#ifdef MP_ERROR_TO_STRING_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* return a char * string for a given code */
const char *mp_error_to_string(mp_err code)
{
switch (code) {
case MP_OKAY:
return "Successful";
case MP_ERR:
return "Unknown error";
case MP_MEM:
return "Out of heap";
case MP_VAL:
return "Value out of range";
case MP_ITER:
return "Max. iterations reached";
case MP_BUF:
return "Buffer overflow";
case MP_OVF:
return "Integer overflow";
default:
return "Invalid error code";
}
}
#endif
+13
View File
@@ -0,0 +1,13 @@
#include "tommath_private.h"
#ifdef MP_EXCH_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* swap the elements of two integers, for cases where you can't simply swap the
* mp_int pointers around
*/
void mp_exch(mp_int *a, mp_int *b)
{
MP_EXCH(mp_int, *a, *b);
}
#endif
+43
View File
@@ -0,0 +1,43 @@
#include "tommath_private.h"
#ifdef MP_EXPT_N_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* calculate c = a**b using a square-multiply algorithm */
mp_err mp_expt_n(const mp_int *a, int b, mp_int *c)
{
mp_err err;
mp_int g;
if ((err = mp_init_copy(&g, a)) != MP_OKAY) {
return err;
}
/* set initial result */
mp_set(c, 1uL);
while (b > 0) {
/* if the bit is set multiply */
if ((b & 1) != 0) {
if ((err = mp_mul(c, &g, c)) != MP_OKAY) {
goto LBL_ERR;
}
}
/* square */
if (b > 1) {
if ((err = mp_sqr(&g, &g)) != MP_OKAY) {
goto LBL_ERR;
}
}
/* shift to next bit */
b >>= 1;
}
LBL_ERR:
mp_clear(&g);
return err;
}
#endif
+78
View File
@@ -0,0 +1,78 @@
#include "tommath_private.h"
#ifdef MP_EXPTMOD_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* this is a shell function that calls either the normal or Montgomery
* exptmod functions. Originally the call to the montgomery code was
* embedded in the normal function but that wasted alot of stack space
* for nothing (since 99% of the time the Montgomery code would be called)
*/
mp_err mp_exptmod(const mp_int *G, const mp_int *X, const mp_int *P, mp_int *Y)
{
int dr;
/* modulus P must be positive */
if (mp_isneg(P)) {
return MP_VAL;
}
/* if exponent X is negative we have to recurse */
if (mp_isneg(X)) {
mp_int tmpG, tmpX;
mp_err err;
if (!MP_HAS(MP_INVMOD)) {
return MP_VAL;
}
if ((err = mp_init_multi(&tmpG, &tmpX, NULL)) != MP_OKAY) {
return err;
}
/* first compute 1/G mod P */
if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) {
goto LBL_ERR;
}
/* now get |X| */
if ((err = mp_abs(X, &tmpX)) != MP_OKAY) {
goto LBL_ERR;
}
/* and now compute (1/G)**|X| instead of G**X [X < 0] */
err = mp_exptmod(&tmpG, &tmpX, P, Y);
LBL_ERR:
mp_clear_multi(&tmpG, &tmpX, NULL);
return err;
}
/* modified diminished radix reduction */
if (MP_HAS(MP_REDUCE_IS_2K_L) && MP_HAS(MP_REDUCE_2K_L) && MP_HAS(S_MP_EXPTMOD) &&
mp_reduce_is_2k_l(P)) {
return s_mp_exptmod(G, X, P, Y, 1);
}
/* is it a DR modulus? default to no */
dr = (MP_HAS(MP_DR_IS_MODULUS) && mp_dr_is_modulus(P)) ? 1 : 0;
/* if not, is it a unrestricted DR modulus? */
if (MP_HAS(MP_REDUCE_IS_2K) && (dr == 0)) {
dr = (mp_reduce_is_2k(P)) ? 2 : 0;
}
/* if the modulus is odd or dr != 0 use the montgomery method */
if (MP_HAS(S_MP_EXPTMOD_FAST) && (mp_isodd(P) || (dr != 0))) {
return s_mp_exptmod_fast(G, X, P, Y, dr);
}
/* otherwise use the generic Barrett reduction technique */
if (MP_HAS(S_MP_EXPTMOD)) {
return s_mp_exptmod(G, X, P, Y, 0);
}
/* no exptmod for evens */
return MP_VAL;
}
#endif
+72
View File
@@ -0,0 +1,72 @@
#include "tommath_private.h"
#ifdef MP_EXTEUCLID_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* Extended euclidean algorithm of (a, b) produces
a*u1 + b*u2 = u3
*/
mp_err mp_exteuclid(const mp_int *a, const mp_int *b, mp_int *U1, mp_int *U2, mp_int *U3)
{
mp_int u1, u2, u3, v1, v2, v3, t1, t2, t3, q, tmp;
mp_err err;
if ((err = mp_init_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL)) != MP_OKAY) {
return err;
}
/* initialize, (u1,u2,u3) = (1,0,a) */
mp_set(&u1, 1uL);
if ((err = mp_copy(a, &u3)) != MP_OKAY) goto LBL_ERR;
/* initialize, (v1,v2,v3) = (0,1,b) */
mp_set(&v2, 1uL);
if ((err = mp_copy(b, &v3)) != MP_OKAY) goto LBL_ERR;
/* loop while v3 != 0 */
while (!mp_iszero(&v3)) {
/* q = u3/v3 */
if ((err = mp_div(&u3, &v3, &q, NULL)) != MP_OKAY) goto LBL_ERR;
/* (t1,t2,t3) = (u1,u2,u3) - (v1,v2,v3)q */
if ((err = mp_mul(&v1, &q, &tmp)) != MP_OKAY) goto LBL_ERR;
if ((err = mp_sub(&u1, &tmp, &t1)) != MP_OKAY) goto LBL_ERR;
if ((err = mp_mul(&v2, &q, &tmp)) != MP_OKAY) goto LBL_ERR;
if ((err = mp_sub(&u2, &tmp, &t2)) != MP_OKAY) goto LBL_ERR;
if ((err = mp_mul(&v3, &q, &tmp)) != MP_OKAY) goto LBL_ERR;
if ((err = mp_sub(&u3, &tmp, &t3)) != MP_OKAY) goto LBL_ERR;
/* (u1,u2,u3) = (v1,v2,v3) */
if ((err = mp_copy(&v1, &u1)) != MP_OKAY) goto LBL_ERR;
if ((err = mp_copy(&v2, &u2)) != MP_OKAY) goto LBL_ERR;
if ((err = mp_copy(&v3, &u3)) != MP_OKAY) goto LBL_ERR;
/* (v1,v2,v3) = (t1,t2,t3) */
if ((err = mp_copy(&t1, &v1)) != MP_OKAY) goto LBL_ERR;
if ((err = mp_copy(&t2, &v2)) != MP_OKAY) goto LBL_ERR;
if ((err = mp_copy(&t3, &v3)) != MP_OKAY) goto LBL_ERR;
}
/* make sure U3 >= 0 */
if (mp_isneg(&u3)) {
if ((err = mp_neg(&u1, &u1)) != MP_OKAY) goto LBL_ERR;
if ((err = mp_neg(&u2, &u2)) != MP_OKAY) goto LBL_ERR;
if ((err = mp_neg(&u3, &u3)) != MP_OKAY) goto LBL_ERR;
}
/* copy result out */
if (U1 != NULL) {
mp_exch(U1, &u1);
}
if (U2 != NULL) {
mp_exch(U2, &u2);
}
if (U3 != NULL) {
mp_exch(U3, &u3);
}
LBL_ERR:
mp_clear_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL);
return err;
}
#endif
+66
View File
@@ -0,0 +1,66 @@
#include "tommath_private.h"
#ifdef MP_FREAD_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
#ifndef MP_NO_FILE
/* read a bigint from a file stream in ASCII */
mp_err mp_fread(mp_int *a, int radix, FILE *stream)
{
mp_err err;
mp_sign sign = MP_ZPOS;
int ch;
/* make sure the radix is ok */
if ((radix < 2) || (radix > 64)) {
return MP_VAL;
}
/* if first digit is - then set negative */
ch = fgetc(stream);
if (ch == (int)'-') {
sign = MP_NEG;
ch = fgetc(stream);
}
/* no digits, return error */
if (ch == EOF) {
return MP_ERR;
}
/* clear a */
mp_zero(a);
do {
uint8_t y;
unsigned pos;
ch = (radix <= 36) ? MP_TOUPPER(ch) : ch;
pos = (unsigned)(ch - (int)'+');
if (MP_RADIX_MAP_REVERSE_SIZE <= pos) {
break;
}
y = s_mp_radix_map_reverse[pos];
if (y >= radix) {
break;
}
/* shift up and add */
if ((err = mp_mul_d(a, (mp_digit)radix, a)) != MP_OKAY) {
return err;
}
if ((err = mp_add_d(a, y, a)) != MP_OKAY) {
return err;
}
} while ((ch = fgetc(stream)) != EOF);
if (!mp_iszero(a)) {
a->sign = sign;
}
return MP_OKAY;
}
#endif
#endif
+21
View File
@@ -0,0 +1,21 @@
#include "tommath_private.h"
#ifdef MP_FROM_SBIN_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* read signed bin, big endian, first byte is 0==positive or 1==negative */
mp_err mp_from_sbin(mp_int *a, const uint8_t *buf, size_t size)
{
mp_err err;
/* read magnitude */
if ((err = mp_from_ubin(a, buf + 1, size - 1u)) != MP_OKAY) {
return err;
}
/* first byte is 0 for positive, non-zero for negative */
a->sign = (buf[0] != (uint8_t)0) ? MP_NEG : MP_ZPOS;
return MP_OKAY;
}
#endif
+30
View File
@@ -0,0 +1,30 @@
#include "tommath_private.h"
#ifdef MP_FROM_UBIN_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* reads a uint8_t array, assumes the msb is stored first [big endian] */
mp_err mp_from_ubin(mp_int *a, const uint8_t *buf, size_t size)
{
mp_err err;
/* make sure there are at least two digits */
if ((err = mp_grow(a, 2)) != MP_OKAY) {
return err;
}
/* zero the int */
mp_zero(a);
/* read the bytes in */
while (size-- > 0u) {
if ((err = mp_mul_2d(a, 8, a)) != MP_OKAY) {
return err;
}
a->dp[0] |= *buf++;
a->used += 1;
}
mp_clamp(a);
return MP_OKAY;
}
#endif
+33
View File
@@ -0,0 +1,33 @@
#include "tommath_private.h"
#ifdef MP_FWRITE_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
#ifndef MP_NO_FILE
mp_err mp_fwrite(const mp_int *a, int radix, FILE *stream)
{
char *buf;
mp_err err;
size_t size, written;
if ((err = mp_radix_size_overestimate(a, radix, &size)) != MP_OKAY) {
return err;
}
buf = (char *) MP_MALLOC(size);
if (buf == NULL) {
return MP_MEM;
}
if ((err = mp_to_radix(a, buf, size, &written, radix)) == MP_OKAY) {
if (fwrite(buf, written, 1uL, stream) != 1uL) {
err = MP_ERR;
}
}
MP_FREE_BUF(buf, size);
return err;
}
#endif
#endif
+92
View File
@@ -0,0 +1,92 @@
#include "tommath_private.h"
#ifdef MP_GCD_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* Greatest Common Divisor using the binary method */
mp_err mp_gcd(const mp_int *a, const mp_int *b, mp_int *c)
{
mp_int u, v;
int k, u_lsb, v_lsb;
mp_err err;
/* either zero than gcd is the largest */
if (mp_iszero(a)) {
return mp_abs(b, c);
}
if (mp_iszero(b)) {
return mp_abs(a, c);
}
/* get copies of a and b we can modify */
if ((err = mp_init_copy(&u, a)) != MP_OKAY) {
return err;
}
if ((err = mp_init_copy(&v, b)) != MP_OKAY) {
goto LBL_U;
}
/* must be positive for the remainder of the algorithm */
u.sign = v.sign = MP_ZPOS;
/* B1. Find the common power of two for u and v */
u_lsb = mp_cnt_lsb(&u);
v_lsb = mp_cnt_lsb(&v);
k = MP_MIN(u_lsb, v_lsb);
if (k > 0) {
/* divide the power of two out */
if ((err = mp_div_2d(&u, k, &u, NULL)) != MP_OKAY) {
goto LBL_V;
}
if ((err = mp_div_2d(&v, k, &v, NULL)) != MP_OKAY) {
goto LBL_V;
}
}
/* divide any remaining factors of two out */
if (u_lsb != k) {
if ((err = mp_div_2d(&u, u_lsb - k, &u, NULL)) != MP_OKAY) {
goto LBL_V;
}
}
if (v_lsb != k) {
if ((err = mp_div_2d(&v, v_lsb - k, &v, NULL)) != MP_OKAY) {
goto LBL_V;
}
}
while (!mp_iszero(&v)) {
/* make sure v is the largest */
if (mp_cmp_mag(&u, &v) == MP_GT) {
/* swap u and v to make sure v is >= u */
mp_exch(&u, &v);
}
/* subtract smallest from largest */
if ((err = s_mp_sub(&v, &u, &v)) != MP_OKAY) {
goto LBL_V;
}
/* Divide out all factors of two */
if ((err = mp_div_2d(&v, mp_cnt_lsb(&v), &v, NULL)) != MP_OKAY) {
goto LBL_V;
}
}
/* multiply by 2**k which we divided out at the beginning */
if ((err = mp_mul_2d(&u, k, c)) != MP_OKAY) {
goto LBL_V;
}
c->sign = MP_ZPOS;
err = MP_OKAY;
LBL_V:
mp_clear(&u);
LBL_U:
mp_clear(&v);
return err;
}
#endif
+18
View File
@@ -0,0 +1,18 @@
#include "tommath_private.h"
#ifdef MP_GET_DOUBLE_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
double mp_get_double(const mp_int *a)
{
int i;
double d = 0.0, fac = 1.0;
for (i = 0; i < MP_DIGIT_BIT; ++i) {
fac *= 2.0;
}
for (i = a->used; i --> 0;) {
d = (d * fac) + (double)a->dp[i];
}
return mp_isneg(a) ? -d : d;
}
#endif
+7
View File
@@ -0,0 +1,7 @@
#include "tommath_private.h"
#ifdef MP_GET_I32_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
MP_GET_SIGNED(mp_get_i32, mp_get_mag_u32, int32_t, uint32_t)
#endif
+7
View File
@@ -0,0 +1,7 @@
#include "tommath_private.h"
#ifdef MP_GET_I64_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
MP_GET_SIGNED(mp_get_i64, mp_get_mag_u64, int64_t, uint64_t)
#endif
+7
View File
@@ -0,0 +1,7 @@
#include "tommath_private.h"
#ifdef MP_GET_L_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
MP_GET_SIGNED(mp_get_l, mp_get_mag_ul, long, unsigned long)
#endif
+7
View File
@@ -0,0 +1,7 @@
#include "tommath_private.h"
#ifdef MP_GET_MAG_U32_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
MP_GET_MAG(mp_get_mag_u32, uint32_t)
#endif
+7
View File
@@ -0,0 +1,7 @@
#include "tommath_private.h"
#ifdef MP_GET_MAG_U64_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
MP_GET_MAG(mp_get_mag_u64, uint64_t)
#endif
+7
View File
@@ -0,0 +1,7 @@
#include "tommath_private.h"
#ifdef MP_GET_MAG_UL_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
MP_GET_MAG(mp_get_mag_ul, unsigned long)
#endif
+40
View File
@@ -0,0 +1,40 @@
#include "tommath_private.h"
#ifdef MP_GROW_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* grow as required */
mp_err mp_grow(mp_int *a, int size)
{
/* if the alloc size is smaller alloc more ram */
if (a->alloc < size) {
mp_digit *dp;
if (size > MP_MAX_DIGIT_COUNT) {
return MP_OVF;
}
/* reallocate the array a->dp
*
* We store the return in a temporary variable
* in case the operation failed we don't want
* to overwrite the dp member of a.
*/
dp = (mp_digit *) MP_REALLOC(a->dp,
(size_t)a->alloc * sizeof(mp_digit),
(size_t)size * sizeof(mp_digit));
if (dp == NULL) {
/* reallocation failed but "a" is still valid [can be freed] */
return MP_MEM;
}
/* reallocation succeeded so set a->dp */
a->dp = dp;
/* zero excess digits */
s_mp_zero_digs(a->dp + a->alloc, size - a->alloc);
a->alloc = size;
}
return MP_OKAY;
}
#endif
+23
View File
@@ -0,0 +1,23 @@
#include "tommath_private.h"
#ifdef MP_INIT_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* init a new mp_int */
mp_err mp_init(mp_int *a)
{
/* allocate memory required and clear it */
a->dp = (mp_digit *) MP_CALLOC((size_t)MP_DEFAULT_DIGIT_COUNT, sizeof(mp_digit));
if (a->dp == NULL) {
return MP_MEM;
}
/* set the used to zero, allocated digits to the default precision
* and sign to positive */
a->used = 0;
a->alloc = MP_DEFAULT_DIGIT_COUNT;
a->sign = MP_ZPOS;
return MP_OKAY;
}
#endif
+21
View File
@@ -0,0 +1,21 @@
#include "tommath_private.h"
#ifdef MP_INIT_COPY_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* creates "a" then copies b into it */
mp_err mp_init_copy(mp_int *a, const mp_int *b)
{
mp_err err;
if ((err = mp_init_size(a, b->used)) != MP_OKAY) {
return err;
}
if ((err = mp_copy(b, a)) != MP_OKAY) {
mp_clear(a);
}
return err;
}
#endif
+7
View File
@@ -0,0 +1,7 @@
#include "tommath_private.h"
#ifdef MP_INIT_I32_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
MP_INIT_INT(mp_init_i32, mp_set_i32, int32_t)
#endif
+7
View File
@@ -0,0 +1,7 @@
#include "tommath_private.h"
#ifdef MP_INIT_I64_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
MP_INIT_INT(mp_init_i64, mp_set_i64, int64_t)
#endif
+7
View File
@@ -0,0 +1,7 @@
#include "tommath_private.h"
#ifdef MP_INIT_L_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
MP_INIT_INT(mp_init_l, mp_set_l, long)
#endif
+41
View File
@@ -0,0 +1,41 @@
#include "tommath_private.h"
#ifdef MP_INIT_MULTI_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
#include <stdarg.h>
mp_err mp_init_multi(mp_int *mp, ...)
{
mp_err err = MP_OKAY;
int n = 0; /* Number of ok inits */
mp_int *cur_arg = mp;
va_list args;
va_start(args, mp); /* init args to next argument from caller */
while (cur_arg != NULL) {
err = mp_init(cur_arg);
if (err != MP_OKAY) {
/* Oops - error! Back-track and mp_clear what we already
succeeded in init-ing, then return error.
*/
va_list clean_args;
/* now start cleaning up */
cur_arg = mp;
va_start(clean_args, mp);
while (n-- != 0) {
mp_clear(cur_arg);
cur_arg = va_arg(clean_args, mp_int *);
}
va_end(clean_args);
break;
}
n++;
cur_arg = va_arg(args, mp_int *);
}
va_end(args);
return err;
}
#endif
+16
View File
@@ -0,0 +1,16 @@
#include "tommath_private.h"
#ifdef MP_INIT_SET_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* initialize and set a digit */
mp_err mp_init_set(mp_int *a, mp_digit b)
{
mp_err err;
if ((err = mp_init(a)) != MP_OKAY) {
return err;
}
mp_set(a, b);
return err;
}
#endif

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