mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-08 04:21:38 -07:00
Compare commits
116 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9e6e769141 | |||
| c0f746a251 | |||
| ccbdf086ff | |||
| d260ca6738 | |||
| b0e64ca7e8 | |||
| 7f3d4cb504 | |||
| 97be36d18a | |||
| be76da2c90 | |||
| b1a8357f50 | |||
| 0dc900ba34 | |||
| 700624119b | |||
| af32aba7fc | |||
| 541c79c01a | |||
| 358226468d | |||
| d1e5f34f76 | |||
| 416dd93bf7 | |||
| 7c80577160 | |||
| a5d6fda433 | |||
| 4bc3796f9b | |||
| 5e12f5a746 | |||
| 4080ba4026 | |||
| e17593be94 | |||
| 44aa69748c | |||
| 9cd5ea59dd | |||
| 116e98b378 | |||
| ae25787f48 | |||
| 08dc829b70 | |||
| 3ac674cf02 | |||
| 6cd06ab95f | |||
| 99080d41f3 | |||
| 92f3567ee6 | |||
| 481fc8a5b6 | |||
| 5dfff51a40 | |||
| 2938ec028f | |||
| 981b9fb7a8 | |||
| 10f4d8df32 | |||
| e15858e2be | |||
| 6d8302825c | |||
| fae8bf96dd | |||
| bc59dc6389 | |||
| bd8e2f82be | |||
| 69027b6840 | |||
| 7a9b7af078 | |||
| 31c7afce1b | |||
| bab1873416 | |||
| 5e2950e9fb | |||
| 74c019f271 | |||
| a745bb8f42 | |||
| da9870c77d | |||
| 698eeaf7c3 | |||
| bd954d9990 | |||
| 4ded42a33b | |||
| 1877965ac3 | |||
| cec2309504 | |||
| ed5a4afc8c | |||
| f29b51efdd | |||
| 3930a32b0c | |||
| 76707e1d2f | |||
| ff2e5c3efe | |||
| 3600b2e209 | |||
| eb36a0f3b1 | |||
| b397254696 | |||
| 9a37d3b6e5 | |||
| 51c4a19234 | |||
| a1a1668dcf | |||
| e308098f18 | |||
| 63b572a0ab | |||
| e90e7d4af9 | |||
| 460e14e586 | |||
| ebcabb8a27 | |||
| 257b749e9d | |||
| d9e6ade030 | |||
| 690374d4de | |||
| adb25d9d19 | |||
| 2949e4b0c7 | |||
| 6de0181c75 | |||
| 8a6b743d2a | |||
| ed8a6f872d | |||
| 0a61d4bf2b | |||
| 332461c0d2 | |||
| d8abe7fc4d | |||
| ec9667ef5a | |||
| 9f7154a039 | |||
| 4a932616fc | |||
| 73fe36f19c | |||
| 4167ae95ae | |||
| 13c3c5be95 | |||
| 3afec0bcbe | |||
| 8b1bfc80fb | |||
| 3662275119 | |||
| 141573c18c | |||
| e692efbe09 | |||
| f6c1a5bf6e | |||
| 6afc28f827 | |||
| df6681ad4e | |||
| 114efbc57c | |||
| 2c71494ad1 | |||
| 35230b1a11 | |||
| 7acbf8b7b9 | |||
| f7413ca974 | |||
| 9b3a0251ca | |||
| 3b9ca8535f | |||
| a98eee145d | |||
| c6b9b3b9a4 | |||
| a4be1a5e4c | |||
| ee908c00de | |||
| 46264032aa | |||
| 4b831dbddd | |||
| a01d6dcea7 | |||
| 01a15f78e6 | |||
| 5f71c41582 | |||
| e8f2c5a48a | |||
| 1c76577918 | |||
| 4285b58aaa | |||
| 9cc366de97 | |||
| 212d294b84 |
@@ -1,5 +1,5 @@
|
|||||||
GIT_SHA=$(shell git rev-parse --short HEAD)
|
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++
|
LDFLAGS=-pthread -ldl -lm -lstdc++
|
||||||
CFLAGS=-std=c++14 -DGIT_SHA=\"$(GIT_SHA)\"
|
CFLAGS=-std=c++14 -DGIT_SHA=\"$(GIT_SHA)\"
|
||||||
CFLAGS:=$(CFLAGS) -DODIN_VERSION_RAW=\"dev-$(shell date +"%Y-%m")\"
|
CFLAGS:=$(CFLAGS) -DODIN_VERSION_RAW=\"dev-$(shell date +"%Y-%m")\"
|
||||||
@@ -8,31 +8,31 @@ CC=clang
|
|||||||
OS=$(shell uname)
|
OS=$(shell uname)
|
||||||
|
|
||||||
ifeq ($(OS), Darwin)
|
ifeq ($(OS), Darwin)
|
||||||
LLVM_CONFIG=llvm-config
|
LLVM_CONFIG=llvm-config
|
||||||
ifneq ($(shell llvm-config --version | grep '^11\.'),)
|
ifneq ($(shell llvm-config --version | grep '^11\.'),)
|
||||||
LLVM_CONFIG=llvm-config
|
LLVM_CONFIG=llvm-config
|
||||||
else
|
else
|
||||||
$(error "Requirement: llvm-config must be version 11")
|
$(error "Requirement: llvm-config must be version 11")
|
||||||
endif
|
endif
|
||||||
|
|
||||||
LDFLAGS:=$(LDFLAGS) -liconv
|
LDFLAGS:=$(LDFLAGS) -liconv
|
||||||
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
|
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
|
||||||
LDFLAGS:=$(LDFLAGS) -lLLVM-C
|
LDFLAGS:=$(LDFLAGS) -lLLVM-C
|
||||||
endif
|
endif
|
||||||
ifeq ($(OS), Linux)
|
ifeq ($(OS), Linux)
|
||||||
LLVM_CONFIG=llvm-config-11
|
LLVM_CONFIG=llvm-config-11
|
||||||
ifneq ($(shell which llvm-config-11 2>/dev/null),)
|
ifneq ($(shell which llvm-config-11 2>/dev/null),)
|
||||||
LLVM_CONFIG=llvm-config-11
|
LLVM_CONFIG=llvm-config-11
|
||||||
else
|
else
|
||||||
ifneq ($(shell llvm-config --version | grep '^11\.'),)
|
ifneq ($(shell llvm-config --version | grep '^11\.'),)
|
||||||
LLVM_CONFIG=llvm-config
|
LLVM_CONFIG=llvm-config
|
||||||
else
|
else
|
||||||
$(error "Requirement: llvm-config must be version 11")
|
$(error "Requirement: llvm-config must be version 11")
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
|
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
|
||||||
LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
|
LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
all: debug demo
|
all: debug demo
|
||||||
@@ -41,13 +41,16 @@ demo:
|
|||||||
./odin run examples/demo/demo.odin
|
./odin run examples/demo/demo.odin
|
||||||
|
|
||||||
debug:
|
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:
|
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:
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ if %release_mode% EQU 0 ( rem Debug
|
|||||||
|
|
||||||
set compiler_warnings= ^
|
set compiler_warnings= ^
|
||||||
-W4 -WX ^
|
-W4 -WX ^
|
||||||
-wd4100 -wd4101 -wd4127 -wd4189 ^
|
-wd4100 -wd4101 -wd4127 -wd4146 -wd4189 ^
|
||||||
-wd4201 -wd4204 ^
|
-wd4201 -wd4204 ^
|
||||||
-wd4456 -wd4457 -wd4480 ^
|
-wd4456 -wd4457 -wd4480 ^
|
||||||
-wd4512
|
-wd4512
|
||||||
@@ -70,7 +70,7 @@ set linker_settings=%libs% %linker_flags%
|
|||||||
del *.pdb > NUL 2> NUL
|
del *.pdb > NUL 2> NUL
|
||||||
del *.ilk > 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 %errorlevel% neq 0 goto end_of_build
|
||||||
if %release_mode% EQU 0 odin run examples/demo/demo.odin
|
if %release_mode% EQU 0 odin run examples/demo/demo.odin
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ ring_prev :: proc(r: ^$R/Ring) -> ^R {
|
|||||||
|
|
||||||
|
|
||||||
ring_move :: proc(r: ^$R/Ring, n: int) -> ^R {
|
ring_move :: proc(r: ^$R/Ring, n: int) -> ^R {
|
||||||
|
r := r;
|
||||||
if r.next == nil {
|
if r.next == nil {
|
||||||
return ring_init(r);
|
return ring_init(r);
|
||||||
}
|
}
|
||||||
@@ -64,7 +65,7 @@ ring_len :: proc(r: ^$R/Ring) -> int {
|
|||||||
n := 0;
|
n := 0;
|
||||||
if r != nil {
|
if r != nil {
|
||||||
n = 1;
|
n = 1;
|
||||||
for p := ring_next(&p); p != r; p = p.next {
|
for p := ring_next(r); p != r; p = p.next {
|
||||||
n += 1;
|
n += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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 '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 '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 '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);
|
||||||
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':
|
case 's', 'v':
|
||||||
str, ok := enum_value_to_string(v);
|
str, ok := enum_value_to_string(v);
|
||||||
if !ok {
|
if !ok {
|
||||||
str = "!%(BAD ENUM VALUE)";
|
str = "%!(BAD ENUM VALUE)";
|
||||||
}
|
}
|
||||||
io.write_string(fi.writer, str);
|
io.write_string(fi.writer, str);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ scalar_dot :: proc(a, b: $T) -> T where IS_FLOAT(T), !IS_ARRAY(T) {
|
|||||||
return a * b;
|
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 {
|
for i in 0..<N {
|
||||||
c += a[i] * b[i];
|
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};
|
dot :: proc{scalar_dot, vector_dot, quaternion64_dot, quaternion128_dot, quaternion256_dot};
|
||||||
|
|
||||||
inner_product :: 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 i in 0..<M {
|
||||||
for j in 0..<N {
|
for j in 0..<N {
|
||||||
out[i][j] = a[i]*b[j];
|
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;
|
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 {
|
for i in 0..<N {
|
||||||
m[i][i] = E(1);
|
m[i][i] = E(1);
|
||||||
}
|
}
|
||||||
@@ -170,7 +170,7 @@ trace :: proc(m: $T/[$N][N]$E) -> (tr: E) {
|
|||||||
return;
|
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 j in 0..<M {
|
||||||
for i in 0..<N {
|
for i in 0..<N {
|
||||||
m[j][i] = a[i][j];
|
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)
|
matrix_mul :: proc(a, b: $M/[$N][N]$E) -> (c: M)
|
||||||
where !IS_ARRAY(E),
|
where !IS_ARRAY(E), IS_NUMERIC(E) #no_bounds_check {
|
||||||
IS_NUMERIC(E) {
|
|
||||||
for i in 0..<N {
|
for i in 0..<N {
|
||||||
for k in 0..<N {
|
for k in 0..<N {
|
||||||
for j 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)
|
matrix_comp_mul :: proc(a, b: $M/[$J][$I]$E) -> (c: M)
|
||||||
where !IS_ARRAY(E),
|
where !IS_ARRAY(E), IS_NUMERIC(E) #no_bounds_check {
|
||||||
IS_NUMERIC(E) {
|
|
||||||
for j in 0..<J {
|
for j in 0..<J {
|
||||||
for i in 0..<I {
|
for i in 0..<I {
|
||||||
c[j][i] = a[j][i] * b[j][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)
|
matrix_mul_differ :: proc(a: $A/[$J][$I]$E, b: $B/[$K][J]E) -> (c: [K][I]E)
|
||||||
where !IS_ARRAY(E),
|
where !IS_ARRAY(E), IS_NUMERIC(E), I != K #no_bounds_check {
|
||||||
IS_NUMERIC(E),
|
|
||||||
I != K {
|
|
||||||
for k in 0..<K {
|
for k in 0..<K {
|
||||||
for j in 0..<J {
|
for j in 0..<J {
|
||||||
for i in 0..<I {
|
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)
|
matrix_mul_vector :: proc(a: $A/[$I][$J]$E, b: $B/[I]E) -> (c: B)
|
||||||
where !IS_ARRAY(E),
|
where !IS_ARRAY(E), IS_NUMERIC(E) #no_bounds_check {
|
||||||
IS_NUMERIC(E) {
|
|
||||||
for i in 0..<I {
|
for i in 0..<I {
|
||||||
for j in 0..<J {
|
for j in 0..<J {
|
||||||
c[j] += a[i][j] * b[i];
|
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 {
|
for i in 0..<N {
|
||||||
w[i] = Elem_Type(v[i]);
|
w[i] = Elem_Type(v[i]);
|
||||||
}
|
}
|
||||||
return;
|
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 i in 0..<M {
|
||||||
for j in 0..<N {
|
for j in 0..<N {
|
||||||
w[i][j] = Elem_Type(v[i][j]);
|
w[i][j] = Elem_Type(v[i][j]);
|
||||||
|
|||||||
@@ -57,11 +57,8 @@ link_error_delete :: proc(lerr: Maybe(Link_Error)) {
|
|||||||
|
|
||||||
|
|
||||||
is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) {
|
is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) {
|
||||||
v: Platform_Error;
|
v := or_else(ferr.(Platform_Error), {});
|
||||||
if v, ok = ferr.(Platform_Error); ok {
|
return v.err, v.err != 0;
|
||||||
err = v.err;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -13,11 +13,7 @@ error_to_io_error :: proc(ferr: Error) -> io.Error {
|
|||||||
if ferr == nil {
|
if ferr == nil {
|
||||||
return .None;
|
return .None;
|
||||||
}
|
}
|
||||||
err, ok := ferr.(io.Error);
|
return or_else(ferr.(io.Error), .Unknown);
|
||||||
if !ok {
|
|
||||||
err = .Unknown;
|
|
||||||
}
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -427,7 +427,7 @@ typeid_base :: proc "contextless" (id: typeid) -> typeid {
|
|||||||
return ti.id;
|
return ti.id;
|
||||||
}
|
}
|
||||||
typeid_core :: proc "contextless" (id: typeid) -> typeid {
|
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;
|
return ti.id;
|
||||||
}
|
}
|
||||||
typeid_base_without_enum :: typeid_core;
|
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_string(message);
|
||||||
}
|
}
|
||||||
print_byte('\n');
|
print_byte('\n');
|
||||||
// debug_trap();
|
// intrinsics.debug_trap();
|
||||||
trap();
|
intrinsics.trap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
// The delete_key built-in procedure deletes the element with the specified key (m[key]) from the map.
|
||||||
// If m is nil, or there is no such element, this procedure is a no-op
|
// If m is nil, or there is no such element, this procedure is a no-op
|
||||||
@builtin
|
@builtin
|
||||||
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 {
|
if m != nil {
|
||||||
key := key;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -716,7 +716,7 @@ floattidf :: proc(a: i128) -> f64 {
|
|||||||
a <<= u128(DBL_MANT_DIG - sd);
|
a <<= u128(DBL_MANT_DIG - sd);
|
||||||
}
|
}
|
||||||
fb: [2]u32;
|
fb: [2]u32;
|
||||||
fb[1] = (u32(s) & 0x80000000) | // sign
|
fb[0] = (u32(s) & 0x80000000) | // sign
|
||||||
((e + 1023) << 20) | // exponent
|
((e + 1023) << 20) | // exponent
|
||||||
u32((u64(a) >> 32) & 0x000FFFFF); // mantissa-high
|
u32((u64(a) >> 32) & 0x000FFFFF); // mantissa-high
|
||||||
fb[1] = u32(a); // mantissa-low
|
fb[1] = u32(a); // mantissa-low
|
||||||
@@ -757,7 +757,7 @@ floattidf_unsigned :: proc(a: u128) -> f64 {
|
|||||||
a <<= u128(DBL_MANT_DIG - sd);
|
a <<= u128(DBL_MANT_DIG - sd);
|
||||||
}
|
}
|
||||||
fb: [2]u32;
|
fb: [2]u32;
|
||||||
fb[1] = (0) | // sign
|
fb[0] = (0) | // sign
|
||||||
((e + 1023) << 20) | // exponent
|
((e + 1023) << 20) | // exponent
|
||||||
u32((u64(a) >> 32) & 0x000FFFFF); // mantissa-high
|
u32((u64(a) >> 32) & 0x000FFFFF); // mantissa-high
|
||||||
fb[1] = u32(a); // mantissa-low
|
fb[1] = u32(a); // mantissa-low
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
package slice
|
package slice
|
||||||
|
|
||||||
import "intrinsics"
|
import "intrinsics"
|
||||||
|
import "builtin"
|
||||||
import "core:math/bits"
|
import "core:math/bits"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
_ :: intrinsics;
|
_ :: intrinsics;
|
||||||
|
_ :: builtin;
|
||||||
_ :: bits;
|
_ :: bits;
|
||||||
_ :: mem;
|
_ :: 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
|
dot_product :: proc(a, b: $S/[]$T) -> T
|
||||||
where intrinsics.type_is_numeric(T) {
|
where intrinsics.type_is_numeric(T) {
|
||||||
if len(a) != len(b) {
|
if len(a) != len(b) {
|
||||||
|
|||||||
+229
-8
@@ -5,6 +5,33 @@ _ :: intrinsics;
|
|||||||
|
|
||||||
ORD :: intrinsics.type_is_ordered;
|
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
|
// sort sorts a slice
|
||||||
// This sort is not guaranteed to be stable
|
// 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) {
|
sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) {
|
||||||
when size_of(E) != 0 {
|
when size_of(E) != 0 {
|
||||||
if n := len(data); n > 1 {
|
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;
|
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) {
|
reverse_sort :: proc(data: $T/[]$E) where ORD(E) {
|
||||||
sort_by(data, proc(i, j: E) -> bool {
|
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?
|
// 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) {
|
sort_by_key :: proc(data: $T/[]$E, key: proc(E) -> $K) where ORD(K) {
|
||||||
context._internal = rawptr(key);
|
context._internal = rawptr(key);
|
||||||
@@ -250,7 +312,7 @@ _heap_sort :: proc(data: $T/[]$E, a, b: int) where ORD(E) {
|
|||||||
|
|
||||||
|
|
||||||
@(private)
|
@(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) {
|
median3 :: proc(data: T, m1, m0, m2: int, less: proc(i, j: E) -> bool) {
|
||||||
if less(data[m1], data[m0]) {
|
if less(data[m1], data[m0]) {
|
||||||
swap(data, m1, 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 b-a > 12 { // only use shell sort for lengths <= 12
|
||||||
if max_depth == 0 {
|
if max_depth == 0 {
|
||||||
_heap_sort_proc(data, a, b, less);
|
_heap_sort_less(data, a, b, less);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
max_depth -= 1;
|
max_depth -= 1;
|
||||||
mlo, mhi := do_pivot(data, a, b, less);
|
mlo, mhi := do_pivot(data, a, b, less);
|
||||||
if mlo-a < b-mhi {
|
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;
|
a = mhi;
|
||||||
} else {
|
} else {
|
||||||
_quick_sort_proc(data, mhi, b, max_depth, less);
|
_quick_sort_less(data, mhi, b, max_depth, less);
|
||||||
b = mlo;
|
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);
|
swap(data, i, i-6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_insertion_sort_proc(data, a, b, less);
|
_insertion_sort_less(data, a, b, less);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(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 i in a+1..<b {
|
||||||
for j := i; j > a && less(data[j], data[j-1]); j -= 1 {
|
for j := i; j > a && less(data[j], data[j-1]); j -= 1 {
|
||||||
swap(data, j, 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)
|
@(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) {
|
sift_down :: proc(data: T, lo, hi, first: int, less: proc(i, j: E) -> bool) {
|
||||||
root := lo;
|
root := lo;
|
||||||
for {
|
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
@@ -1,6 +1,7 @@
|
|||||||
package sort
|
package sort
|
||||||
|
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
import _slice "core:slice"
|
||||||
import "intrinsics"
|
import "intrinsics"
|
||||||
|
|
||||||
_ :: intrinsics;
|
_ :: intrinsics;
|
||||||
@@ -29,9 +30,11 @@ sort :: proc(it: Interface) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@(deprecated="use slice.sort")
|
||||||
slice :: proc(array: $T/[]$E) where ORD(E) {
|
slice :: proc(array: $T/[]$E) where ORD(E) {
|
||||||
s := array;
|
_slice.sort(array);
|
||||||
sort(slice_interface(&s));
|
// s := array;
|
||||||
|
// sort(slice_interface(&s));
|
||||||
}
|
}
|
||||||
|
|
||||||
slice_interface :: proc(s: ^$T/[]$E) -> Interface where ORD(E) {
|
slice_interface :: proc(s: ^$T/[]$E) -> Interface where ORD(E) {
|
||||||
@@ -76,7 +79,10 @@ reverse_sort :: proc(it: Interface) {
|
|||||||
sort(reverse_interface(&it));
|
sort(reverse_interface(&it));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(deprecated="use slice.reverse")
|
||||||
reverse_slice :: proc(array: $T/[]$E) where ORD(E) {
|
reverse_slice :: proc(array: $T/[]$E) where ORD(E) {
|
||||||
|
_slice.reverse(array);
|
||||||
|
/*
|
||||||
s := array;
|
s := array;
|
||||||
sort(Interface{
|
sort(Interface{
|
||||||
collection = rawptr(&s),
|
collection = rawptr(&s),
|
||||||
@@ -93,6 +99,7 @@ reverse_slice :: proc(array: $T/[]$E) where ORD(E) {
|
|||||||
s[i], s[j] = s[j], s[i];
|
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;
|
y := transmute(mem.Raw_String)b;
|
||||||
return mem.compare_byte_ptrs(x.data, y.data, min(x.len, y.len));
|
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;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -165,6 +165,40 @@ concatenate :: proc(a: []string, allocator := context.allocator) -> string {
|
|||||||
return string(b);
|
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
|
@private
|
||||||
_split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocator) -> []string {
|
_split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocator) -> []string {
|
||||||
s, n := s_, n_;
|
s, n := s_, n_;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// +build windows
|
||||||
package win32
|
package win32
|
||||||
|
|
||||||
import "core:strings";
|
import "core:strings";
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// +build windows
|
||||||
package sys_windows
|
package sys_windows
|
||||||
|
|
||||||
foreign import advapi32 "system:Advapi32.lib"
|
foreign import advapi32 "system:Advapi32.lib"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// +build windows
|
||||||
package sys_windows
|
package sys_windows
|
||||||
|
|
||||||
foreign import bcrypt "system:Bcrypt.lib"
|
foreign import bcrypt "system:Bcrypt.lib"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// +build windows
|
||||||
package sys_windows
|
package sys_windows
|
||||||
|
|
||||||
foreign import kernel32 "system:Kernel32.lib"
|
foreign import kernel32 "system:Kernel32.lib"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// +build windows
|
||||||
package sys_windows
|
package sys_windows
|
||||||
|
|
||||||
foreign import netapi32 "system:Netapi32.lib"
|
foreign import netapi32 "system:Netapi32.lib"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// +build windows
|
||||||
package sys_windows
|
package sys_windows
|
||||||
|
|
||||||
foreign import ntdll_lib "system:ntdll.lib"
|
foreign import ntdll_lib "system:ntdll.lib"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// +build windows
|
||||||
package sys_windows
|
package sys_windows
|
||||||
|
|
||||||
foreign import shell32 "system:Shell32.lib"
|
foreign import shell32 "system:Shell32.lib"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// +build windows
|
||||||
package sys_windows
|
package sys_windows
|
||||||
|
|
||||||
foreign import Synchronization "system:Synchronization.lib"
|
foreign import Synchronization "system:Synchronization.lib"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// +build windows
|
||||||
package sys_windows
|
package sys_windows
|
||||||
|
|
||||||
import "core:c"
|
import "core:c"
|
||||||
@@ -396,6 +397,8 @@ when size_of(uintptr) == 4 {
|
|||||||
szDescription: [WSADESCRIPTION_LEN + 1]u8,
|
szDescription: [WSADESCRIPTION_LEN + 1]u8,
|
||||||
szSystemStatus: [WSASYS_STATUS_LEN + 1]u8,
|
szSystemStatus: [WSASYS_STATUS_LEN + 1]u8,
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
#panic("unknown word size");
|
||||||
}
|
}
|
||||||
|
|
||||||
WSABUF :: struct {
|
WSABUF :: struct {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// +build windows
|
||||||
package sys_windows
|
package sys_windows
|
||||||
|
|
||||||
foreign import userenv "system:Userenv.lib"
|
foreign import userenv "system:Userenv.lib"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// +build windows
|
||||||
package sys_windows
|
package sys_windows
|
||||||
|
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// +build windows
|
||||||
package sys_windows
|
package sys_windows
|
||||||
|
|
||||||
foreign import ws2_32 "system:Ws2_32.lib"
|
foreign import ws2_32 "system:Ws2_32.lib"
|
||||||
|
|||||||
@@ -68,12 +68,7 @@ Thread_Os_Specific :: struct {
|
|||||||
thread_create :: proc(procedure: Thread_Proc) -> ^Thread {
|
thread_create :: proc(procedure: Thread_Proc) -> ^Thread {
|
||||||
__windows_thread_entry_proc :: proc "stdcall" (t_: rawptr) -> win32.DWORD {
|
__windows_thread_entry_proc :: proc "stdcall" (t_: rawptr) -> win32.DWORD {
|
||||||
t := (^Thread)(t_);
|
t := (^Thread)(t_);
|
||||||
context = runtime.default_context();
|
context = or_else(t.init_context.?, runtime.default_context());
|
||||||
c := context;
|
|
||||||
if ic, ok := t.init_context.?; ok {
|
|
||||||
c = ic;
|
|
||||||
}
|
|
||||||
context = c;
|
|
||||||
|
|
||||||
t.procedure(t);
|
t.procedure(t);
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ Thread_Os_Specific :: struct #align 16 {
|
|||||||
_create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
|
_create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
|
||||||
__linux_thread_entry_proc :: proc "c" (t: rawptr) -> rawptr {
|
__linux_thread_entry_proc :: proc "c" (t: rawptr) -> rawptr {
|
||||||
context = runtime.default_context();
|
context = runtime.default_context();
|
||||||
|
|
||||||
t := (^Thread)(t);
|
t := (^Thread)(t);
|
||||||
sync.condition_wait_for(&t.start_gate);
|
sync.condition_wait_for(&t.start_gate);
|
||||||
sync.condition_destroy(&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_gate = {};
|
||||||
t.start_mutex = {};
|
t.start_mutex = {};
|
||||||
|
|
||||||
c := context;
|
context = or_else(t.init_context.?, runtime.default_context());
|
||||||
if ic, ok := t.init_context.?; ok {
|
|
||||||
c = ic;
|
|
||||||
}
|
|
||||||
context = c;
|
|
||||||
|
|
||||||
t.procedure(t);
|
t.procedure(t);
|
||||||
|
|
||||||
|
|||||||
@@ -23,12 +23,7 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
|
|||||||
|
|
||||||
__windows_thread_entry_proc :: proc "stdcall" (t_: rawptr) -> win32.DWORD {
|
__windows_thread_entry_proc :: proc "stdcall" (t_: rawptr) -> win32.DWORD {
|
||||||
t := (^Thread)(t_);
|
t := (^Thread)(t_);
|
||||||
context = runtime.default_context();
|
context = or_else(t.init_context.?, runtime.default_context());
|
||||||
c := context;
|
|
||||||
if ic, ok := t.init_context.?; ok {
|
|
||||||
c = ic;
|
|
||||||
}
|
|
||||||
context = c;
|
|
||||||
|
|
||||||
t.procedure(t);
|
t.procedure(t);
|
||||||
|
|
||||||
|
|||||||
@@ -1999,6 +1999,40 @@ relative_data_types :: proc() {
|
|||||||
fmt.println(rel_slice[1]);
|
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() {
|
main :: proc() {
|
||||||
when true {
|
when true {
|
||||||
the_basics();
|
the_basics();
|
||||||
@@ -2031,5 +2065,6 @@ main :: proc() {
|
|||||||
union_maybe();
|
union_maybe();
|
||||||
explicit_context_definition();
|
explicit_context_definition();
|
||||||
relative_data_types();
|
relative_data_types();
|
||||||
|
or_else_procedure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,19 @@ template <typename T>
|
|||||||
Slice<T> slice_from_array(Array<T> const &a) {
|
Slice<T> slice_from_array(Array<T> const &a) {
|
||||||
return {a.data, a.count};
|
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>
|
template <typename T>
|
||||||
Slice<T> slice_clone(gbAllocator const &allocator, Slice<T> const &a) {
|
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));
|
T *data = cast(T *)gb_alloc_copy_align(allocator, a.data, a.count*gb_size_of(T), gb_align_of(T));
|
||||||
|
|||||||
+174
-1097
File diff suppressed because it is too large
Load Diff
+24
-10
@@ -3,6 +3,11 @@
|
|||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// #if defined(GB_SYSTEM_WINDOWS)
|
||||||
|
#define DEFAULT_TO_THREADED_CHECKER
|
||||||
|
// #endif
|
||||||
|
|
||||||
enum TargetOsKind {
|
enum TargetOsKind {
|
||||||
TargetOs_Invalid,
|
TargetOs_Invalid,
|
||||||
|
|
||||||
@@ -203,11 +208,16 @@ struct BuildContext {
|
|||||||
bool warnings_as_errors;
|
bool warnings_as_errors;
|
||||||
bool show_error_line;
|
bool show_error_line;
|
||||||
|
|
||||||
|
bool ignore_lazy;
|
||||||
|
|
||||||
bool use_subsystem_windows;
|
bool use_subsystem_windows;
|
||||||
bool ignore_microsoft_magic;
|
bool ignore_microsoft_magic;
|
||||||
bool linker_map_file;
|
bool linker_map_file;
|
||||||
|
|
||||||
bool use_separate_modules;
|
bool use_separate_modules;
|
||||||
|
bool threaded_checker;
|
||||||
|
|
||||||
|
bool show_debug_messages;
|
||||||
|
|
||||||
u32 cmd_doc_flags;
|
u32 cmd_doc_flags;
|
||||||
Array<String> extra_packages;
|
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
|
len += 1; // NOTE(bill): It needs an extra 1 for some reason
|
||||||
|
|
||||||
gb_mutex_lock(&string_buffer_mutex);
|
mutex_lock(&string_buffer_mutex);
|
||||||
defer (gb_mutex_unlock(&string_buffer_mutex));
|
defer (mutex_unlock(&string_buffer_mutex));
|
||||||
|
|
||||||
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||||
defer (gb_temp_arena_memory_end(tmp));
|
defer (gb_temp_arena_memory_end(tmp));
|
||||||
@@ -571,8 +581,8 @@ String internal_odin_root_dir(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gb_mutex_lock(&string_buffer_mutex);
|
mutex_lock(&string_buffer_mutex);
|
||||||
defer (gb_mutex_unlock(&string_buffer_mutex));
|
defer (mutex_unlock(&string_buffer_mutex));
|
||||||
|
|
||||||
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||||
defer (gb_temp_arena_memory_end(tmp));
|
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);
|
array_resize(&path_buf, 2*path_buf.count + 300);
|
||||||
}
|
}
|
||||||
|
|
||||||
gb_mutex_lock(&string_buffer_mutex);
|
mutex_lock(&string_buffer_mutex);
|
||||||
defer (gb_mutex_unlock(&string_buffer_mutex));
|
defer (mutex_unlock(&string_buffer_mutex));
|
||||||
|
|
||||||
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||||
defer (gb_temp_arena_memory_end(tmp));
|
defer (gb_temp_arena_memory_end(tmp));
|
||||||
@@ -678,8 +688,8 @@ String internal_odin_root_dir(void) {
|
|||||||
#if defined(GB_SYSTEM_WINDOWS)
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
String path_to_fullpath(gbAllocator a, String s) {
|
String path_to_fullpath(gbAllocator a, String s) {
|
||||||
String result = {};
|
String result = {};
|
||||||
gb_mutex_lock(&string_buffer_mutex);
|
mutex_lock(&string_buffer_mutex);
|
||||||
defer (gb_mutex_unlock(&string_buffer_mutex));
|
defer (mutex_unlock(&string_buffer_mutex));
|
||||||
|
|
||||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||||
defer (gb_temp_arena_memory_end(tmp));
|
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)
|
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
|
||||||
String path_to_fullpath(gbAllocator a, String s) {
|
String path_to_fullpath(gbAllocator a, String s) {
|
||||||
char *p;
|
char *p;
|
||||||
gb_mutex_lock(&string_buffer_mutex);
|
mutex_lock(&string_buffer_mutex);
|
||||||
p = realpath(cast(char *)s.text, 0);
|
p = realpath(cast(char *)s.text, 0);
|
||||||
gb_mutex_unlock(&string_buffer_mutex);
|
mutex_unlock(&string_buffer_mutex);
|
||||||
if(p == nullptr) return String{};
|
if(p == nullptr) return String{};
|
||||||
return make_string_c(p);
|
return make_string_c(p);
|
||||||
}
|
}
|
||||||
@@ -820,6 +830,10 @@ void init_build_context(TargetMetrics *cross_target) {
|
|||||||
bc->max_align = metrics->max_align;
|
bc->max_align = metrics->max_align;
|
||||||
bc->link_flags = str_lit(" ");
|
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
|
// 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
|
// across OSs. It doesn't make sense to allocate extra data on the heap
|
||||||
|
|||||||
+147
-24
@@ -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) {
|
bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
|
||||||
ast_node(ce, CallExpr, call);
|
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
|
// NOTE(bill): The first arg may be a Type, this will be checked case by case
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case BuiltinProc_or_else:
|
||||||
|
// NOTE(bill): The arguments may be multi-expr
|
||||||
|
break;
|
||||||
|
|
||||||
case BuiltinProc_DIRECTIVE: {
|
case BuiltinProc_DIRECTIVE: {
|
||||||
ast_node(bd, BasicDirective, ce->proc);
|
ast_node(bd, BasicDirective, ce->proc);
|
||||||
String name = bd->name.string;
|
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;
|
String original_string = o.value.value_string;
|
||||||
|
|
||||||
|
|
||||||
gbMutex *ignore_mutex = nullptr;
|
BlockingMutex *ignore_mutex = nullptr;
|
||||||
String path = {};
|
String path = {};
|
||||||
bool ok = determine_path_from_string(ignore_mutex, call, base_dir, original_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],
|
error(ce->args[0],
|
||||||
"'%s' has no field named '%.*s'", type_str, LIT(arg->token.string));
|
"'%s' has no field named '%.*s'", type_str, LIT(arg->token.string));
|
||||||
gb_string_free(type_str);
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
if (sel.indirect) {
|
if (sel.indirect) {
|
||||||
@@ -672,7 +745,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (op.value.value_integer.neg) {
|
if (big_int_is_neg(&op.value.value_integer)) {
|
||||||
error(op.expr, "Negative 'swizzle' index");
|
error(op.expr, "Negative 'swizzle' index");
|
||||||
return false;
|
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;
|
convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
|
||||||
if (x.mode == Addressing_Constant &&
|
if (x.mode == Addressing_Constant &&
|
||||||
y.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;
|
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;
|
y.type = t_untyped_float;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -809,16 +884,20 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
|||||||
y.mode == Addressing_Constant &&
|
y.mode == Addressing_Constant &&
|
||||||
z.mode == Addressing_Constant &&
|
z.mode == Addressing_Constant &&
|
||||||
w.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;
|
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;
|
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;
|
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;
|
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?
|
// 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);
|
gb_memmove_array(tuple->Tuple.variables.data, type->Struct.fields.data, variable_count);
|
||||||
} else if (is_type_array(type)) {
|
} 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);
|
array_init(&tuple->Tuple.variables, a, variable_count);
|
||||||
for (isize i = 0; i < variable_count; i++) {
|
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);
|
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) {
|
if (operand->mode == Addressing_Constant) {
|
||||||
switch (operand->value.kind) {
|
switch (operand->value.kind) {
|
||||||
case ExactValue_Integer:
|
case ExactValue_Integer:
|
||||||
operand->value.value_integer.neg = false;
|
mp_abs(&operand->value.value_integer, &operand->value.value_integer);
|
||||||
break;
|
break;
|
||||||
case ExactValue_Float:
|
case ExactValue_Float:
|
||||||
operand->value.value_float = gb_abs(operand->value.value_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 = {};
|
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) {
|
for_array(i, ce->args) {
|
||||||
String name = {};
|
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_struct = alloc_ast_node(nullptr, Ast_Invalid);
|
||||||
Ast *dummy_node_soa = 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);
|
auto fields = array_make<Entity *>(permanent_allocator(), 0, types.count);
|
||||||
for_array(i, types) {
|
for_array(i, types) {
|
||||||
@@ -1713,6 +1792,46 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
|||||||
break;
|
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: {
|
case BuiltinProc_simd_vector: {
|
||||||
Operand x = {};
|
Operand x = {};
|
||||||
@@ -1724,7 +1843,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
|||||||
operand->type = t_invalid;
|
operand->type = t_invalid;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (x.value.value_integer.neg) {
|
if (big_int_is_neg(&x.value.value_integer)) {
|
||||||
error(call, "Negative vector element length");
|
error(call, "Negative vector element length");
|
||||||
operand->mode = Addressing_Type;
|
operand->mode = Addressing_Type;
|
||||||
operand->type = t_invalid;
|
operand->type = t_invalid;
|
||||||
@@ -1764,7 +1883,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
|||||||
operand->type = t_invalid;
|
operand->type = t_invalid;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (x.value.value_integer.neg) {
|
if (big_int_is_neg(&x.value.value_integer)) {
|
||||||
error(call, "Negative array element length");
|
error(call, "Negative array element length");
|
||||||
operand->mode = Addressing_Type;
|
operand->mode = Addressing_Type;
|
||||||
operand->type = t_invalid;
|
operand->type = t_invalid;
|
||||||
@@ -1797,14 +1916,14 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
|||||||
if (is_type_array(elem)) {
|
if (is_type_array(elem)) {
|
||||||
Type *old_array = base_type(elem);
|
Type *old_array = base_type(elem);
|
||||||
soa_struct = alloc_type_struct();
|
soa_struct = alloc_type_struct();
|
||||||
soa_struct->Struct.fields = array_make<Entity *>(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(), 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.node = operand->expr;
|
||||||
soa_struct->Struct.soa_kind = StructSoa_Fixed;
|
soa_struct->Struct.soa_kind = StructSoa_Fixed;
|
||||||
soa_struct->Struct.soa_elem = elem;
|
soa_struct->Struct.soa_elem = elem;
|
||||||
soa_struct->Struct.soa_count = count;
|
soa_struct->Struct.soa_count = count;
|
||||||
|
|
||||||
scope = create_scope(c->scope);
|
scope = create_scope(c->info, c->scope);
|
||||||
soa_struct->Struct.scope = scope;
|
soa_struct->Struct.scope = scope;
|
||||||
|
|
||||||
String params_xyzw[4] = {
|
String params_xyzw[4] = {
|
||||||
@@ -1814,14 +1933,14 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
|||||||
str_lit("w")
|
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);
|
Type *array_type = alloc_type_array(old_array->Array.elem, count);
|
||||||
Token token = {};
|
Token token = {};
|
||||||
token.string = params_xyzw[i];
|
token.string = params_xyzw[i];
|
||||||
|
|
||||||
Entity *new_field = alloc_entity_field(scope, token, array_type, false, cast(i32)i);
|
Entity *new_field = alloc_entity_field(scope, token, array_type, false, cast(i32)i);
|
||||||
soa_struct->Struct.fields[i] = new_field;
|
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);
|
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_elem = elem;
|
||||||
soa_struct->Struct.soa_count = count;
|
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;
|
soa_struct->Struct.scope = scope;
|
||||||
|
|
||||||
for_array(i, old_struct->Struct.fields) {
|
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);
|
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);
|
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;
|
soa_struct->Struct.fields[i] = new_field;
|
||||||
add_entity(c->checker, scope, nullptr, new_field);
|
add_entity(c, scope, nullptr, new_field);
|
||||||
} else {
|
} else {
|
||||||
soa_struct->Struct.fields[i] = old_field;
|
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 token = {};
|
||||||
token.string = str_lit("Base_Type");
|
token.string = str_lit("Base_Type");
|
||||||
Entity *base_type_entity = alloc_entity_type_name(scope, token, elem, EntityState_Resolved);
|
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);
|
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) {
|
if (bt->kind == Type_Proc) {
|
||||||
count = bt->Proc.param_count;
|
count = bt->Proc.param_count;
|
||||||
if (index < 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) {
|
if (bt->kind == Type_Proc) {
|
||||||
count = bt->Proc.result_count;
|
count = bt->Proc.result_count;
|
||||||
if (index < 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],
|
error(ce->args[0],
|
||||||
"'%s' has no field named '%.*s'", type_str, LIT(field_name));
|
"'%s' has no field named '%.*s'", type_str, LIT(field_name));
|
||||||
gb_string_free(type_str);
|
gb_string_free(type_str);
|
||||||
|
|
||||||
|
if (bt->kind == Type_Struct) {
|
||||||
|
check_did_you_mean_type(field_name, bt->Struct.fields);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (sel.indirect) {
|
if (sel.indirect) {
|
||||||
|
|||||||
+68
-66
@@ -313,7 +313,7 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
|
|||||||
if (is_blank_ident(name)) {
|
if (is_blank_ident(name)) {
|
||||||
continue;
|
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->Procedure.is_export = ac.is_export;
|
||||||
e->deprecated_message = ac.deprecated_message;
|
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);
|
ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
|
||||||
if (ac.has_disabled_proc) {
|
if (ac.has_disabled_proc) {
|
||||||
if (ac.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);
|
GB_ASSERT(pl->body->kind == Ast_BlockStmt);
|
||||||
if (!pt->is_polymorphic) {
|
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) {
|
} else if (!is_foreign) {
|
||||||
if (e->Procedure.is_export) {
|
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) {
|
if (ac.deferred_procedure.entity != nullptr) {
|
||||||
e->Procedure.deferred_procedure = ac.deferred_procedure;
|
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) {
|
if (is_foreign) {
|
||||||
@@ -821,6 +822,8 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
|||||||
|
|
||||||
init_entity_foreign_library(ctx, e);
|
init_entity_foreign_library(ctx, e);
|
||||||
|
|
||||||
|
mutex_lock(&ctx->info->foreign_mutex);
|
||||||
|
|
||||||
auto *fp = &ctx->info->foreigns;
|
auto *fp = &ctx->info->foreigns;
|
||||||
StringHashKey key = string_hash_string(name);
|
StringHashKey key = string_hash_string(name);
|
||||||
Entity **found = string_map_get(fp, key);
|
Entity **found = string_map_get(fp, key);
|
||||||
@@ -847,12 +850,16 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
|||||||
} else {
|
} else {
|
||||||
string_map_set(fp, key, e);
|
string_map_set(fp, key, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&ctx->info->foreign_mutex);
|
||||||
} else {
|
} else {
|
||||||
String name = e->token.string;
|
String name = e->token.string;
|
||||||
if (e->Procedure.link_name.len > 0) {
|
if (e->Procedure.link_name.len > 0) {
|
||||||
name = e->Procedure.link_name;
|
name = e->Procedure.link_name;
|
||||||
}
|
}
|
||||||
if (e->Procedure.link_name.len > 0 || is_export) {
|
if (e->Procedure.link_name.len > 0 || is_export) {
|
||||||
|
mutex_lock(&ctx->info->foreign_mutex);
|
||||||
|
|
||||||
auto *fp = &ctx->info->foreigns;
|
auto *fp = &ctx->info->foreigns;
|
||||||
StringHashKey key = string_hash_string(name);
|
StringHashKey key = string_hash_string(name);
|
||||||
Entity **found = string_map_get(fp, key);
|
Entity **found = string_map_get(fp, key);
|
||||||
@@ -869,6 +876,8 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
|||||||
} else {
|
} else {
|
||||||
string_map_set(fp, key, e);
|
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) {
|
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) {
|
if (e->state == EntityState_Resolved) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (e->flags & EntityFlag_Lazy) {
|
||||||
|
mutex_lock(&ctx->info->lazy_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
String name = e->token.string;
|
String name = e->token.string;
|
||||||
|
|
||||||
if (e->type != nullptr || e->state != EntityState_Unresolved) {
|
if (e->type != nullptr || e->state != EntityState_Unresolved) {
|
||||||
error(e->token, "Illegal declaration cycle of `%.*s`", LIT(name));
|
error(e->token, "Illegal declaration cycle of `%.*s`", LIT(name));
|
||||||
return;
|
} else {
|
||||||
}
|
GB_ASSERT(e->state == EntityState_Unresolved);
|
||||||
|
|
||||||
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);
|
|
||||||
if (d == nullptr) {
|
if (d == nullptr) {
|
||||||
// TODO(bill): Err here?
|
d = decl_info_of_entity(e);
|
||||||
e->type = t_invalid;
|
if (d == nullptr) {
|
||||||
e->state = EntityState_Resolved;
|
// TODO(bill): Err here?
|
||||||
set_base_type(named_type, t_invalid);
|
e->type = t_invalid;
|
||||||
return;
|
e->state = EntityState_Resolved;
|
||||||
// GB_PANIC("'%.*s' should been declared!", LIT(name));
|
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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
end:;
|
||||||
CheckerContext c = *ctx;
|
// NOTE(bill): Add it to the list of checked entities
|
||||||
c.scope = d->scope;
|
if (e->flags & EntityFlag_Lazy) {
|
||||||
c.decl = d;
|
array_add(&ctx->info->entities, e);
|
||||||
c.type_level = 0;
|
mutex_unlock(&ctx->info->lazy_mutex);
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
#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 *uvar = using_entities[i].uvar;
|
||||||
Entity *prev = scope_insert(ctx->scope, uvar);
|
Entity *prev = scope_insert(ctx->scope, uvar);
|
||||||
if (prev != nullptr) {
|
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;
|
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)) {
|
if (ps->flags & (ScopeFlag_File & ScopeFlag_Pkg & ScopeFlag_Global)) {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
mutex_lock(&ctx->info->deps_mutex);
|
||||||
|
|
||||||
// NOTE(bill): Add the dependencies from the procedure literal (lambda)
|
// NOTE(bill): Add the dependencies from the procedure literal (lambda)
|
||||||
// But only at the procedure level
|
// But only at the procedure level
|
||||||
for_array(i, decl->deps.entries) {
|
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;
|
Type *t = decl->type_info_deps.entries[i].ptr;
|
||||||
ptr_set_add(&decl->parent->type_info_deps, t);
|
ptr_set_add(&decl->parent->type_info_deps, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&ctx->info->deps_mutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+259
-159
@@ -48,8 +48,8 @@ struct CallArgumentData {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct PolyProcData {
|
struct PolyProcData {
|
||||||
Entity * gen_entity;
|
Entity * gen_entity;
|
||||||
ProcInfo proc_info;
|
ProcInfo *proc_info;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ValidIndexAndScore {
|
struct ValidIndexAndScore {
|
||||||
@@ -73,6 +73,7 @@ typedef CALL_ARGUMENT_CHECKER(CallArgumentCheckerType);
|
|||||||
void check_expr (CheckerContext *c, Operand *operand, Ast *expression);
|
void check_expr (CheckerContext *c, Operand *operand, Ast *expression);
|
||||||
void check_multi_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_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);
|
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);
|
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);
|
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);
|
void convert_to_typed (CheckerContext *c, Operand *operand, Type *target_type);
|
||||||
gbString expr_to_string (Ast *expression);
|
gbString expr_to_string (Ast *expression);
|
||||||
void check_proc_body (CheckerContext *c, Token token, DeclInfo *decl, Type *type, Ast *body);
|
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_is_terminating (Ast *node, String const &label);
|
||||||
bool check_has_break (Ast *stmt, String const &label, bool implicit);
|
bool check_has_break (Ast *stmt, String const &label, bool implicit);
|
||||||
void check_stmt (CheckerContext *c, Ast *node, u32 flags);
|
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);
|
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) {
|
Entity *entity_from_expr(Ast *expr) {
|
||||||
expr = unparen_expr(expr);
|
expr = unparen_expr(expr);
|
||||||
switch (expr->kind) {
|
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) {
|
if (base_entity == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
@@ -220,11 +224,14 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
|
|||||||
if (!is_type_proc(base_entity->type)) {
|
if (!is_type_proc(base_entity->type)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String name = base_entity->token.string;
|
String name = base_entity->token.string;
|
||||||
|
|
||||||
Type *src = base_type(base_entity->type);
|
Type *src = base_type(base_entity->type);
|
||||||
Type *dst = nullptr;
|
Type *dst = nullptr;
|
||||||
if (type != nullptr) dst = base_type(type);
|
if (type != nullptr) {
|
||||||
|
dst = base_type(type);
|
||||||
|
}
|
||||||
|
|
||||||
if (param_operands == nullptr) {
|
if (param_operands == nullptr) {
|
||||||
GB_ASSERT(dst != nullptr);
|
GB_ASSERT(dst != nullptr);
|
||||||
@@ -233,6 +240,8 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
|
|||||||
GB_ASSERT(dst == nullptr);
|
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) {
|
if (!src->Proc.is_polymorphic || src->Proc.is_poly_specialized) {
|
||||||
return false;
|
return false;
|
||||||
@@ -276,10 +285,9 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CheckerContext nctx = *c;
|
CheckerContext nctx = *c;
|
||||||
|
|
||||||
Scope *scope = create_scope(base_entity->scope);
|
Scope *scope = create_scope(c->info, base_entity->scope);
|
||||||
scope->flags |= ScopeFlag_Proc;
|
scope->flags |= ScopeFlag_Proc;
|
||||||
nctx.scope = scope;
|
nctx.scope = scope;
|
||||||
nctx.allow_polymorphic_types = true;
|
nctx.allow_polymorphic_types = true;
|
||||||
@@ -291,9 +299,10 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
auto *pt = &src->Proc;
|
auto *pt = &src->Proc;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// NOTE(bill): This is slightly memory leaking if the type already exists
|
// NOTE(bill): This is slightly memory leaking if the type already exists
|
||||||
// Maybe it's better to check with the previous types first?
|
// 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);
|
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;
|
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) {
|
if (found_gen_procs) {
|
||||||
auto procs = *found_gen_procs;
|
auto procs = *found_gen_procs;
|
||||||
for_array(i, procs) {
|
for_array(i, procs) {
|
||||||
@@ -400,22 +409,22 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcInfo proc_info = {};
|
ProcInfo *proc_info = gb_alloc_item(permanent_allocator(), ProcInfo);
|
||||||
proc_info.file = file;
|
proc_info->file = file;
|
||||||
proc_info.token = token;
|
proc_info->token = token;
|
||||||
proc_info.decl = d;
|
proc_info->decl = d;
|
||||||
proc_info.type = final_proc_type;
|
proc_info->type = final_proc_type;
|
||||||
proc_info.body = pl->body;
|
proc_info->body = pl->body;
|
||||||
proc_info.tags = tags;
|
proc_info->tags = tags;
|
||||||
proc_info.generated_from_polymorphic = true;
|
proc_info->generated_from_polymorphic = true;
|
||||||
proc_info.poly_def_node = poly_def_node;
|
proc_info->poly_def_node = poly_def_node;
|
||||||
|
|
||||||
if (found_gen_procs) {
|
if (found_gen_procs) {
|
||||||
array_add(found_gen_procs, entity);
|
array_add(found_gen_procs, entity);
|
||||||
} else {
|
} else {
|
||||||
auto array = array_make<Entity *>(heap_allocator());
|
auto array = array_make<Entity *>(heap_allocator());
|
||||||
array_add(&array, entity);
|
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);
|
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
|
// NOTE(bill): Check the newly generated procedure body
|
||||||
check_procedure_later(nctx.checker, proc_info);
|
check_procedure_later(&nctx, proc_info);
|
||||||
|
|
||||||
return true;
|
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_from_i64(&bi127, 127);
|
||||||
|
|
||||||
big_int_shl_eq(&umax, &bi128);
|
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_shl_eq(&imin, &bi127);
|
||||||
big_int_neg(&imin, &imin);
|
big_int_neg(&imin, &imin);
|
||||||
|
|
||||||
big_int_shl_eq(&imax, &bi127);
|
big_int_shl_eq(&imax, &bi127);
|
||||||
big_int_sub_eq(&imax, &BIG_INT_ONE);
|
mp_decr(&imax);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (type->Basic.kind) {
|
switch (type->Basic.kind) {
|
||||||
@@ -1542,7 +1551,7 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ
|
|||||||
{
|
{
|
||||||
// return 0ull <= i && i <= umax;
|
// return 0ull <= i && i <= umax;
|
||||||
int b = big_int_cmp(&i, &umax);
|
int b = big_int_cmp(&i, &umax);
|
||||||
return !i.neg && (b <= 0);
|
return !i.sign && (b <= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
case Basic_UntypedInteger:
|
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) {
|
void check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) {
|
||||||
GB_ASSERT(o->mode == Addressing_Constant);
|
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 a = expr_to_string(o->expr);
|
||||||
gbString b = type_to_string(type);
|
gbString b = type_to_string(type);
|
||||||
gbString c = type_to_string(o->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)) {
|
if (!is_type_integer(o->type) && is_type_integer(type)) {
|
||||||
error(o->expr, "'%s' truncated to '%s'", a, b);
|
error(o->expr, "'%s' truncated to '%s'", a, b);
|
||||||
} else {
|
} 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);
|
check_assignment_error_suggestion(ctx, o, type);
|
||||||
}
|
}
|
||||||
} else {
|
} 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) {
|
void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) {
|
||||||
switch (op.kind) {
|
switch (op.kind) {
|
||||||
case Token_And: { // Pointer address
|
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 (check_is_not_addressable(c, o)) {
|
||||||
if (ast_node_expect(node, Ast_UnaryExpr)) {
|
if (ast_node_expect(node, Ast_UnaryExpr)) {
|
||||||
ast_node(ue, UnaryExpr, node);
|
ast_node(ue, UnaryExpr, node);
|
||||||
@@ -2085,8 +2095,8 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) {
|
|||||||
} else {
|
} else {
|
||||||
x->mode = Addressing_Value;
|
x->mode = Addressing_Value;
|
||||||
|
|
||||||
update_expr_type(c, x->expr, default_type(x->type), true);
|
update_untyped_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, y->expr, default_type(y->type), true);
|
||||||
|
|
||||||
i64 size = 0;
|
i64 size = 0;
|
||||||
if (!is_type_untyped(x->type)) size = gb_max(size, type_size_of(x->type));
|
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 = {};
|
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) {
|
if (big_int_cmp(&y_val.value_integer, &max_shift) > 0) {
|
||||||
gbString err_str = expr_to_string(y->expr);
|
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;
|
TokenPos pos = ast_token(x->expr).pos;
|
||||||
if (x_is_untyped) {
|
if (x_is_untyped) {
|
||||||
ExprInfo *info = check_get_expr_info(&c->checker->info, x->expr);
|
if (x->expr != nullptr) {
|
||||||
if (info != nullptr) {
|
x->expr->tav.is_lhs = true;
|
||||||
info->is_lhs = true;
|
|
||||||
}
|
}
|
||||||
x->mode = Addressing_Value;
|
x->mode = Addressing_Value;
|
||||||
if (type_hint && is_type_integer(type_hint)) {
|
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);
|
gbString err_str = expr_to_string(y->expr);
|
||||||
error(node, "Shift amount cannot be negative: '%s'", err_str);
|
error(node, "Shift amount cannot be negative: '%s'", err_str);
|
||||||
gb_string_free(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)) {
|
if (is_const_expr && !is_type_constant_type(type)) {
|
||||||
final_type = default_type(x->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) {
|
if (build_context.vet_extra) {
|
||||||
@@ -2506,6 +2515,25 @@ bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) {
|
|||||||
return false;
|
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 srcz = type_size_of(o->type);
|
||||||
i64 dstz = type_size_of(t);
|
i64 dstz = type_size_of(t);
|
||||||
if (srcz != dstz) {
|
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_in:
|
||||||
case Token_not_in:
|
case Token_not_in:
|
||||||
|
{
|
||||||
// IMPORTANT NOTE(bill): This uses right-left evaluation in type checking only no in
|
// IMPORTANT NOTE(bill): This uses right-left evaluation in type checking only no in
|
||||||
|
|
||||||
check_expr(c, y, be->right);
|
check_expr(c, y, be->right);
|
||||||
|
Type *rhs_type = type_deref(y->type);
|
||||||
|
|
||||||
if (is_type_bit_set(y->type)) {
|
if (is_type_bit_set(rhs_type)) {
|
||||||
Type *elem = base_type(y->type)->BitSet.elem;
|
Type *elem = base_type(rhs_type)->BitSet.elem;
|
||||||
check_expr_with_type_hint(c, x, be->left, elem);
|
check_expr_with_type_hint(c, x, be->left, elem);
|
||||||
} else if (is_type_map(y->type)) {
|
} else if (is_type_map(rhs_type)) {
|
||||||
Type *key = base_type(y->type)->Map.key;
|
Type *key = base_type(rhs_type)->Map.key;
|
||||||
check_expr_with_type_hint(c, x, be->left, key);
|
check_expr_with_type_hint(c, x, be->left, key);
|
||||||
} else {
|
} else {
|
||||||
check_expr(c, x, be->left);
|
check_expr(c, x, be->left);
|
||||||
@@ -2617,8 +2646,8 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_type_map(y->type)) {
|
if (is_type_map(rhs_type)) {
|
||||||
Type *yt = base_type(y->type);
|
Type *yt = base_type(rhs_type);
|
||||||
if (op.kind == Token_in) {
|
if (op.kind == Token_in) {
|
||||||
check_assignment(c, x, yt->Map.key, str_lit("map 'in'"));
|
check_assignment(c, x, yt->Map.key, str_lit("map 'in'"));
|
||||||
} else {
|
} 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");
|
add_package_dependency(c, "runtime", "__dynamic_map_get");
|
||||||
} else if (is_type_bit_set(y->type)) {
|
} else if (is_type_bit_set(rhs_type)) {
|
||||||
Type *yt = base_type(y->type);
|
Type *yt = base_type(rhs_type);
|
||||||
|
|
||||||
if (op.kind == Token_in) {
|
if (op.kind == Token_in) {
|
||||||
check_assignment(c, x, yt->BitSet.elem, str_lit("bit_set '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;
|
x->expr = node;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (is_ise_expr(be->left)) {
|
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);
|
GB_ASSERT(e != nullptr);
|
||||||
ExprInfo *found = check_get_expr_info(&c->checker->info, e);
|
ExprInfo *old = check_get_expr_info(c, e);
|
||||||
if (found == nullptr) {
|
if (old == nullptr) {
|
||||||
if (type != nullptr && type != t_invalid) {
|
if (type != nullptr && type != t_invalid) {
|
||||||
if (e->tav.type == nullptr || e->tav.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;
|
return;
|
||||||
}
|
}
|
||||||
ExprInfo old = *found;
|
|
||||||
|
|
||||||
switch (e->kind) {
|
switch (e->kind) {
|
||||||
case_ast_node(ue, UnaryExpr, e);
|
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.
|
// 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
|
// They don't need to be updated as they will be updated later and
|
||||||
// checked at the end of general checking stage.
|
// checked at the end of general checking stage.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
update_expr_type(c, ue->expr, type, final);
|
update_untyped_expr_type(c, ue->expr, type, final);
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
case_ast_node(be, BinaryExpr, e);
|
case_ast_node(be, BinaryExpr, e);
|
||||||
if (old.value.kind != ExactValue_Invalid) {
|
if (old->value.kind != ExactValue_Invalid) {
|
||||||
// See above note in UnaryExpr case
|
// See above note in UnaryExpr case
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (token_is_comparison(be->op.kind)) {
|
if (token_is_comparison(be->op.kind)) {
|
||||||
// NOTE(bill): Do nothing as the types are fine
|
// NOTE(bill): Do nothing as the types are fine
|
||||||
} else if (token_is_shift(be->op.kind)) {
|
} 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 {
|
} else {
|
||||||
update_expr_type(c, be->left, type, final);
|
update_untyped_expr_type(c, be->left, type, final);
|
||||||
update_expr_type(c, be->right, type, final);
|
update_untyped_expr_type(c, be->right, type, final);
|
||||||
}
|
}
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
case_ast_node(te, TernaryIfExpr, e);
|
case_ast_node(te, TernaryIfExpr, e);
|
||||||
if (old.value.kind != ExactValue_Invalid) {
|
if (old->value.kind != ExactValue_Invalid) {
|
||||||
// See above note in UnaryExpr case
|
// See above note in UnaryExpr case
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
update_expr_type(c, te->x, type, final);
|
update_untyped_expr_type(c, te->x, type, final);
|
||||||
update_expr_type(c, te->y, type, final);
|
update_untyped_expr_type(c, te->y, type, final);
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
case_ast_node(te, TernaryWhenExpr, e);
|
case_ast_node(te, TernaryWhenExpr, e);
|
||||||
if (old.value.kind != ExactValue_Invalid) {
|
if (old->value.kind != ExactValue_Invalid) {
|
||||||
// See above note in UnaryExpr case
|
// See above note in UnaryExpr case
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
update_expr_type(c, te->x, type, final);
|
update_untyped_expr_type(c, te->x, type, final);
|
||||||
update_expr_type(c, te->y, type, final);
|
update_untyped_expr_type(c, te->y, type, final);
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
case_ast_node(pe, ParenExpr, e);
|
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;
|
case_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!final && is_type_untyped(type)) {
|
if (!final && is_type_untyped(type)) {
|
||||||
old.type = base_type(type);
|
old->type = base_type(type);
|
||||||
check_set_expr_info(&c->checker->info, e, old);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to remove it and then give it a new one
|
// 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 expr_str = expr_to_string(e);
|
||||||
gbString type_str = type_to_string(type);
|
gbString type_str = type_to_string(type);
|
||||||
error(e, "Shifted operand %s must be an integer, got %s", expr_str, type_str);
|
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;
|
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) {
|
void update_untyped_expr_value(CheckerContext *c, Ast *e, ExactValue value) {
|
||||||
ExprInfo *found = check_get_expr_info(&c->checker->info, e);
|
GB_ASSERT(e != nullptr);
|
||||||
|
ExprInfo *found = check_get_expr_info(c, e);
|
||||||
if (found) {
|
if (found) {
|
||||||
found->value = value;
|
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(from_type_str);
|
||||||
gb_string_free(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 (is_type_numeric(operand->type) && is_type_numeric(target_type)) {
|
||||||
if (x_kind < y_kind) {
|
if (x_kind < y_kind) {
|
||||||
operand->type = target_type;
|
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) {
|
} else if (x_kind != y_kind) {
|
||||||
operand->mode = Addressing_Invalid;
|
operand->mode = Addressing_Invalid;
|
||||||
@@ -3053,7 +3082,7 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
|
|||||||
if (operand->mode == Addressing_Invalid) {
|
if (operand->mode == Addressing_Invalid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
update_expr_value(c, operand->expr, operand->value);
|
update_untyped_expr_value(c, operand->expr, operand->value);
|
||||||
} else {
|
} else {
|
||||||
switch (operand->type->Basic.kind) {
|
switch (operand->type->Basic.kind) {
|
||||||
case Basic_UntypedBool:
|
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 > 0 && count > 2) error_line(", ");
|
||||||
if (i == count-1) {
|
if (i == count-1) {
|
||||||
if (count == 2) error_line(" ");
|
if (count == 2) error_line(" ");
|
||||||
error_line("or ");
|
if (count > 1) {
|
||||||
|
error_line("or ");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
gbString str = type_to_string(v);
|
gbString str = type_to_string(v);
|
||||||
error_line("'%s'", str);
|
error_line("'%s'", str);
|
||||||
@@ -3228,7 +3259,7 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
update_expr_type(c, operand->expr, target_type, true);
|
update_untyped_expr_type(c, operand->expr, target_type, true);
|
||||||
operand->type = target_type;
|
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 &&
|
if (operand.mode == Addressing_Constant &&
|
||||||
(c->state_flags & StateFlag_no_bounds_check) == 0) {
|
(c->state_flags & StateFlag_no_bounds_check) == 0) {
|
||||||
BigInt i = exact_value_to_integer(operand.value).value_integer;
|
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);
|
gbString expr_str = expr_to_string(operand.expr);
|
||||||
error(operand.expr, "Index '%s' cannot be a negative value", expr_str);
|
error(operand.expr, "Index '%s' cannot be a negative value", expr_str);
|
||||||
gb_string_free(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
|
} else { // NOTE(bill): Do array bound checking
|
||||||
i64 v = -1;
|
i64 v = -1;
|
||||||
if (i.len <= 1) {
|
if (i.used <= 1) {
|
||||||
v = big_int_to_i64(&i);
|
v = big_int_to_i64(&i);
|
||||||
}
|
}
|
||||||
if (value) *value = v;
|
if (value) *value = v;
|
||||||
@@ -3573,6 +3604,40 @@ ExactValue get_constant_field(CheckerContext *c, Operand const *operand, Selecti
|
|||||||
if (success_) *success_ = true;
|
if (success_) *success_ = true;
|
||||||
return empty_exact_value;
|
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) {
|
Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *type_hint) {
|
||||||
ast_node(se, SelectorExpr, node);
|
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));
|
error(op_expr, "'%.*s' is not declared by '%.*s'", LIT(entity_name), LIT(import_name));
|
||||||
operand->mode = Addressing_Invalid;
|
operand->mode = Addressing_Invalid;
|
||||||
operand->expr = node;
|
operand->expr = node;
|
||||||
|
|
||||||
|
check_did_you_mean_scope(entity_name, import_scope);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3785,7 +3852,9 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
|
|||||||
|
|
||||||
Type *swizzle_array_type = nullptr;
|
Type *swizzle_array_type = nullptr;
|
||||||
Type *bth = base_type(type_hint);
|
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;
|
swizzle_array_type = type_hint;
|
||||||
} else {
|
} else {
|
||||||
swizzle_array_type = alloc_type_array(array_type->Array.elem, index_count);
|
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 type_str = type_to_string(operand->type);
|
||||||
gbString sel_str = expr_to_string(selector);
|
gbString sel_str = expr_to_string(selector);
|
||||||
error(op_expr, "'%s' of type '%s' has no field '%s'", op_str, type_str, sel_str);
|
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(sel_str);
|
||||||
gb_string_free(type_str);
|
gb_string_free(type_str);
|
||||||
gb_string_free(op_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.mode = Addressing_Value;
|
||||||
val1.type = t_untyped_bool;
|
val1.type = t_untyped_bool;
|
||||||
|
|
||||||
|
check_promote_optional_ok(c, &o, nullptr, &val1.type);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (expr->kind == Ast_TypeAssertion &&
|
if (expr->kind == Ast_TypeAssertion &&
|
||||||
(o.mode == Addressing_OptionalOk || o.mode == Addressing_OptionalOkPtr)) {
|
(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.mode = Addressing_Value;
|
||||||
val1.type = t_untyped_bool;
|
val1.type = t_untyped_bool;
|
||||||
|
|
||||||
|
check_promote_optional_ok(c, &o, nullptr, &val1.type);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (expr->kind == Ast_TypeAssertion &&
|
if (expr->kind == Ast_TypeAssertion &&
|
||||||
(o.mode == Addressing_OptionalOk || o.mode == Addressing_OptionalOkPtr)) {
|
(o.mode == Addressing_OptionalOk || o.mode == Addressing_OptionalOkPtr)) {
|
||||||
@@ -4357,9 +4399,8 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// NOTE(bill): Generate the procedure type for this generic instance
|
// NOTE(bill): Generate the procedure type for this generic instance
|
||||||
PolyProcData poly_proc_data = {};
|
|
||||||
|
|
||||||
if (pt->is_polymorphic && !pt->is_poly_specialized) {
|
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)) {
|
if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &operands, call, &poly_proc_data)) {
|
||||||
gen_entity = poly_proc_data.gen_entity;
|
gen_entity = poly_proc_data.gen_entity;
|
||||||
GB_ASSERT(is_type_proc(gen_entity->type));
|
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_info_type(c, o.type);
|
||||||
add_type_and_value(c->info, o.expr, Addressing_Value, e->type, exact_value_typeid(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)) {
|
} 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_info_type(c, o.type);
|
||||||
add_type_and_value(c->info, o.expr, Addressing_Value, t, exact_value_typeid(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)) {
|
} 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;
|
Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
|
||||||
add_entity_use(c, ident, entity_to_use);
|
add_entity_use(c, ident, entity_to_use);
|
||||||
if (entity_to_use != nullptr) {
|
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;
|
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;
|
Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
|
||||||
add_entity_use(c, ident, entity_to_use);
|
add_entity_use(c, ident, entity_to_use);
|
||||||
if (entity_to_use != nullptr) {
|
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) {
|
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;
|
Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
|
||||||
add_entity_use(c, ident, entity_to_use);
|
add_entity_use(c, ident, entity_to_use);
|
||||||
if (entity_to_use != nullptr) {
|
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) {
|
if (data.gen_entity != nullptr) {
|
||||||
@@ -5495,7 +5536,7 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (ordered_operands.count >= 0) {
|
while (ordered_operands.count > 0) {
|
||||||
if (ordered_operands[ordered_operands.count-1].expr != nullptr) {
|
if (ordered_operands[ordered_operands.count-1].expr != nullptr) {
|
||||||
break;
|
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;
|
i64 score = 0;
|
||||||
for (isize i = 0; i < param_count; i++) {
|
for (isize i = 0; i < oo_count; i++) {
|
||||||
Entity *e = tuple->variables[i];
|
Entity *e = tuple->variables[i];
|
||||||
Operand *o = &ordered_operands[i];
|
Operand *o = &ordered_operands[i];
|
||||||
if (o->mode == Addressing_Invalid) {
|
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->builtin_id = BuiltinProc_DIRECTIVE;
|
||||||
operand->expr = proc;
|
operand->expr = proc;
|
||||||
operand->type = t_invalid;
|
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 {
|
} else {
|
||||||
GB_PANIC("Unhandled #%.*s", LIT(name));
|
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;;
|
operand->type = t_invalid;;
|
||||||
return Expr_Expr;
|
return Expr_Expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto err = check_polymorphic_record_type(c, operand, call);
|
auto err = check_polymorphic_record_type(c, operand, call);
|
||||||
if (err == 0) {
|
if (err == 0) {
|
||||||
Ast *ident = operand->expr;
|
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);
|
GB_ASSERT(ot->kind == Type_Named);
|
||||||
Entity *e = ot->Named.type_name;
|
Entity *e = ot->Named.type_name;
|
||||||
add_entity_use(c, ident, e);
|
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 {
|
} else {
|
||||||
operand->mode = Addressing_Invalid;
|
operand->mode = Addressing_Invalid;
|
||||||
operand->type = t_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;
|
arg = arg->FieldValue.value;
|
||||||
// NOTE(bill): Carry on the cast regardless
|
// 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 (operand->mode != Addressing_Invalid) {
|
||||||
if (is_type_polymorphic(t)) {
|
if (is_type_polymorphic(t)) {
|
||||||
error(call, "A polymorphic type cannot be used in a type conversion");
|
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->type = t;
|
||||||
|
operand->expr = call;
|
||||||
|
if (operand->mode != Addressing_Invalid) {
|
||||||
|
update_untyped_expr_type(c, arg, t, false);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6124,8 +6169,8 @@ bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValu
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
add_type_and_value(&c->checker->info, ie->left, x->mode, x->type, x->value);
|
add_type_and_value(c->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->right, y->mode, y->type, y->value);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -6215,9 +6260,14 @@ ExprKind check_implicit_selector_expr(CheckerContext *c, Operand *o, Ast *node,
|
|||||||
String name = ise->selector->Ident.token.string;
|
String name = ise->selector->Ident.token.string;
|
||||||
|
|
||||||
if (is_type_enum(th)) {
|
if (is_type_enum(th)) {
|
||||||
|
Type *bt = base_type(th);
|
||||||
|
GB_ASSERT(bt->kind == Type_Enum);
|
||||||
|
|
||||||
gbString typ = type_to_string(th);
|
gbString typ = type_to_string(th);
|
||||||
error(node, "Undeclared name %.*s for type '%s'", LIT(name), typ);
|
defer (gb_string_free(typ));
|
||||||
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 {
|
} else {
|
||||||
gbString typ = type_to_string(th);
|
gbString typ = type_to_string(th);
|
||||||
gbString str = expr_to_string(node);
|
gbString str = expr_to_string(node);
|
||||||
@@ -6231,6 +6281,44 @@ ExprKind check_implicit_selector_expr(CheckerContext *c, Operand *o, Ast *node,
|
|||||||
return Expr_Expr;
|
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) {
|
ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
|
||||||
u32 prev_state_flags = c->state_flags;
|
u32 prev_state_flags = c->state_flags;
|
||||||
defer (c->state_flags = prev_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;
|
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);
|
check_close_scope(&ctx);
|
||||||
|
|
||||||
@@ -6450,20 +6538,13 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
|
|||||||
|
|
||||||
o->type = type;
|
o->type = type;
|
||||||
o->mode = Addressing_Value;
|
o->mode = Addressing_Value;
|
||||||
|
if (type_hint != nullptr && is_type_untyped(type)) {
|
||||||
// if (cond.mode == Addressing_Constant && is_type_boolean(cond.type) &&
|
if (check_cast_internal(c, &x, type_hint) &&
|
||||||
// x.mode == Addressing_Constant &&
|
check_cast_internal(c, &y, type_hint)) {
|
||||||
// y.mode == Addressing_Constant) {
|
update_untyped_expr_type(c, node, type_hint, !is_type_untyped(type_hint));
|
||||||
|
o->type = type_hint;
|
||||||
// o->mode = Addressing_Constant;
|
}
|
||||||
|
}
|
||||||
// if (cond.value.value_bool) {
|
|
||||||
// o->value = x.value;
|
|
||||||
// } else {
|
|
||||||
// o->value = y.value;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
case_ast_node(te, TernaryWhenExpr, node);
|
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_end;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
case_ast_node(se, SelectorExpr, node);
|
case_ast_node(se, SelectorExpr, node);
|
||||||
check_selector(c, o, node, type_hint);
|
check_selector(c, o, node, type_hint);
|
||||||
node->viral_state_flags |= se->expr->viral_state_flags;
|
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->mode = Addressing_Constant;
|
||||||
o->type = t;
|
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;
|
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");
|
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;
|
scope->flags |= ScopeFlag_Proc;
|
||||||
|
|
||||||
Type *params = alloc_type_tuple();
|
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);
|
gb_string_free(xs);
|
||||||
}
|
}
|
||||||
if (o->type != nullptr && is_type_untyped(o->type)) {
|
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;
|
return kind;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8192,6 +8271,21 @@ void check_multi_expr(CheckerContext *c, Operand *o, Ast *e) {
|
|||||||
o->mode = Addressing_Invalid;
|
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) {
|
void check_not_tuple(CheckerContext *c, Operand *o) {
|
||||||
if (o->mode == Addressing_Value) {
|
if (o->mode == Addressing_Value) {
|
||||||
// NOTE(bill): Tuples are not first class thus never named
|
// 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);
|
case_ast_node(ta, TypeAssertion, node);
|
||||||
str = write_expr_to_string(str, ta->expr, shorthand);
|
str = write_expr_to_string(str, ta->expr, shorthand);
|
||||||
str = gb_string_appendc(str, ".(");
|
if (ta->type != nullptr &&
|
||||||
str = write_expr_to_string(str, ta->type, shorthand);
|
ta->type->kind == Ast_UnaryExpr &&
|
||||||
str = gb_string_append_rune(str, ')');
|
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_end;
|
||||||
|
|
||||||
case_ast_node(tc, TypeCast, node);
|
case_ast_node(tc, TypeCast, node);
|
||||||
|
|||||||
+12
-11
@@ -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);
|
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;
|
e->parent_proc_decl = ctx->curr_proc_decl;
|
||||||
|
|
||||||
if (ok) {
|
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);
|
Entity *found = scope_insert(ctx->scope, f);
|
||||||
if (found != nullptr) {
|
if (found != nullptr) {
|
||||||
gbString expr_str = expr_to_string(expr);
|
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);
|
gb_string_free(expr_str);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -611,7 +611,7 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
|
|||||||
if (found != nullptr) {
|
if (found != nullptr) {
|
||||||
gbString expr_str = expr_to_string(expr);
|
gbString expr_str = expr_to_string(expr);
|
||||||
error(us->token,
|
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\n"
|
||||||
"\tat %s",
|
"\tat %s",
|
||||||
expr_str, LIT(found->token.string),
|
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++) {
|
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) {
|
if (y.mode != Addressing_Constant) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
update_untyped_expr_type(ctx, z.expr, x.type, !is_type_untyped(x.type));
|
||||||
add_constant_switch_case(ctx, &seen, y);
|
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) {
|
if (!is_reference) {
|
||||||
tag_var->flags |= EntityFlag_Value;
|
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_entity_use(ctx, lhs, tag_var);
|
||||||
add_implicit_entity(ctx, stmt, 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);
|
GB_ASSERT(ctx->curr_proc_sig != nullptr);
|
||||||
|
|
||||||
if (ctx->in_defer) {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1706,7 +1706,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
|||||||
Operand *o = &operands[i];
|
Operand *o = &operands[i];
|
||||||
check_assignment(ctx, o, e->type, str_lit("return statement"));
|
check_assignment(ctx, o, e->type, str_lit("return statement"));
|
||||||
if (is_type_untyped(o->type)) {
|
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);
|
error(operand.expr, "Cannot iterate over '%s' of type '%s'", s, t);
|
||||||
|
|
||||||
if (rs->vals.count == 1) {
|
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]);
|
gbString v = expr_to_string(rs->vals[0]);
|
||||||
defer (gb_string_free(v));
|
defer (gb_string_free(v));
|
||||||
error_line("\tSuggestion: place parentheses around the expression\n");
|
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];
|
Entity *e = entities[i];
|
||||||
DeclInfo *d = decl_info_of_entity(e);
|
DeclInfo *d = decl_info_of_entity(e);
|
||||||
GB_ASSERT(d == nullptr);
|
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);
|
d = make_decl_info(ctx->scope, ctx->decl);
|
||||||
add_entity_and_decl_info(ctx, e->identifier, e, d);
|
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) {
|
if (vd->is_using != 0) {
|
||||||
|
|||||||
+39
-31
@@ -23,7 +23,7 @@ void populate_using_array_index(CheckerContext *ctx, Ast *node, AstField *field,
|
|||||||
tok.pos = ast_token(field->type).pos;
|
tok.pos = ast_token(field->type).pos;
|
||||||
}
|
}
|
||||||
Entity *f = alloc_entity_array_elem(nullptr, tok, t->Array.elem, idx);
|
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));
|
error(e->token, "'%.*s' is already declared", LIT(name));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
add_entity(ctx->checker, ctx->scope, nullptr, f);
|
add_entity(ctx, ctx->scope, nullptr, f);
|
||||||
if (f->flags & EntityFlag_Using) {
|
if (f->flags & EntityFlag_Using) {
|
||||||
populate_using_entity_scope(ctx, node, field, f->type);
|
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;
|
Token name_token = name->Ident.token;
|
||||||
|
|
||||||
Entity *field = alloc_entity_field(ctx->scope, name_token, type, is_using, field_src_index);
|
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(fields, field);
|
||||||
array_add(tags, p->tag.string);
|
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 (is_type_untyped(type) || is_type_integer(type)) {
|
||||||
if (o.value.kind == ExactValue_Integer) {
|
if (o.value.kind == ExactValue_Integer) {
|
||||||
BigInt v = o.value.value_integer;
|
BigInt v = o.value.value_integer;
|
||||||
if (v.len > 1) {
|
if (v.used > 1) {
|
||||||
gbAllocator a = heap_allocator();
|
gbAllocator a = heap_allocator();
|
||||||
String str = big_int_to_string(a, &v);
|
String str = big_int_to_string(a, &v);
|
||||||
error(node, "#align too large, %.*s", LIT(str));
|
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) {
|
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) {
|
if (found_gen_types != nullptr) {
|
||||||
for_array(i, *found_gen_types) {
|
for_array(i, *found_gen_types) {
|
||||||
Entity *e = (*found_gen_types)[i];
|
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;
|
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) {
|
if (found_gen_types) {
|
||||||
array_add(found_gen_types, e);
|
array_add(found_gen_types, e);
|
||||||
} else {
|
} else {
|
||||||
auto array = array_make<Entity *>(heap_allocator());
|
auto array = array_make<Entity *>(heap_allocator());
|
||||||
array_add(&array, e);
|
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,
|
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;
|
e->state = EntityState_Resolved;
|
||||||
add_entity(ctx->checker, scope, name, e);
|
add_entity(ctx, scope, name, e);
|
||||||
array_add(&entities, 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) {
|
if (scope_lookup_current(ctx->scope, name) != nullptr) {
|
||||||
error(ident, "'%.*s' is already declared in this enumeration", LIT(name));
|
error(ident, "'%.*s' is already declared in this enumeration", LIT(name));
|
||||||
} else {
|
} else {
|
||||||
add_entity(ctx->checker, ctx->scope, nullptr, e);
|
add_entity(ctx, ctx->scope, nullptr, e);
|
||||||
array_add(&fields, e);
|
array_add(&fields, e);
|
||||||
// TODO(bill): Should I add a use for the enum value?
|
// TODO(bill): Should I add a use for the enum value?
|
||||||
add_entity_use(ctx, field, e);
|
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.kind = ParameterValue_Constant;
|
||||||
param_value.value = o.value;
|
param_value.value = o.value;
|
||||||
} else {
|
} 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
|
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) {
|
if (is_using) {
|
||||||
add_entity_use(ctx, name, param);
|
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->flags |= EntityFlag_Result;
|
||||||
param->Variable.param_value = param_value;
|
param->Variable.param_value = param_value;
|
||||||
array_add(&variables, param);
|
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
|
// NOTE(bill): Removes `declared but not used` when using -vet
|
||||||
add_entity_use(ctx, name, param);
|
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 (is_type_untyped(type) || is_type_integer(type)) {
|
||||||
if (o->value.kind == ExactValue_Integer) {
|
if (o->value.kind == ExactValue_Integer) {
|
||||||
BigInt count = o->value.value_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();
|
gbAllocator a = heap_allocator();
|
||||||
String str = big_int_to_string(a, &count);
|
String str = big_int_to_string(a, &count);
|
||||||
error(e, "Invalid negative array count, %.*s", LIT(str));
|
error(e, "Invalid negative array count, %.*s", LIT(str));
|
||||||
gb_free(a, str.text);
|
gb_free(a, str.text);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
switch (count.len) {
|
switch (count.used) {
|
||||||
case 0: return 0;
|
case 0: return 0;
|
||||||
case 1: return count.d.word;
|
case 1: return big_int_to_u64(&count);
|
||||||
}
|
}
|
||||||
gbAllocator a = heap_allocator();
|
gbAllocator a = heap_allocator();
|
||||||
String str = big_int_to_string(a, &count);
|
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);
|
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);
|
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));
|
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);
|
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 *hashes_type = alloc_type_slice(t_int);
|
||||||
Type *entries_type = alloc_type_dynamic_array(type->Map.entry_type);
|
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.soa_count = 0;
|
||||||
soa_struct->Struct.is_polymorphic = true;
|
soa_struct->Struct.is_polymorphic = true;
|
||||||
|
|
||||||
scope = create_scope(ctx->scope);
|
scope = create_scope(ctx->info, ctx->scope);
|
||||||
soa_struct->Struct.scope = scope;
|
soa_struct->Struct.scope = scope;
|
||||||
} else if (is_type_array(elem)) {
|
} else if (is_type_array(elem)) {
|
||||||
Type *old_array = base_type(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 = alloc_type_struct();
|
||||||
soa_struct->Struct.fields = array_make<Entity *>(heap_allocator(), field_count+extra_field_count);
|
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_elem = elem;
|
||||||
soa_struct->Struct.soa_count = count;
|
soa_struct->Struct.soa_count = count;
|
||||||
|
|
||||||
scope = create_scope(ctx->scope);
|
scope = create_scope(ctx->info, ctx->scope, 8);
|
||||||
soa_struct->Struct.scope = scope;
|
soa_struct->Struct.scope = scope;
|
||||||
|
|
||||||
String params_xyzw[4] = {
|
String params_xyzw[4] = {
|
||||||
@@ -2230,7 +2238,7 @@ Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *el
|
|||||||
str_lit("w")
|
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;
|
Type *field_type = nullptr;
|
||||||
if (soa_kind == StructSoa_Fixed) {
|
if (soa_kind == StructSoa_Fixed) {
|
||||||
GB_ASSERT(count >= 0);
|
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);
|
Entity *new_field = alloc_entity_field(scope, token, field_type, false, cast(i32)i);
|
||||||
soa_struct->Struct.fields[i] = new_field;
|
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);
|
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_elem = elem;
|
||||||
soa_struct->Struct.soa_count = count;
|
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;
|
soa_struct->Struct.scope = scope;
|
||||||
|
|
||||||
for_array(i, old_struct->Struct.fields) {
|
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);
|
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;
|
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);
|
add_entity_use(ctx, nullptr, new_field);
|
||||||
} else {
|
} else {
|
||||||
soa_struct->Struct.fields[i] = old_field;
|
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) {
|
if (soa_kind != StructSoa_Fixed) {
|
||||||
Entity *len_field = alloc_entity_field(scope, empty_token, t_int, false, cast(i32)field_count+0);
|
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;
|
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);
|
add_entity_use(ctx, nullptr, len_field);
|
||||||
|
|
||||||
if (soa_kind == StructSoa_Dynamic) {
|
if (soa_kind == StructSoa_Dynamic) {
|
||||||
Entity *cap_field = alloc_entity_field(scope, empty_token, t_int, false, cast(i32)field_count+1);
|
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;
|
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);
|
add_entity_use(ctx, nullptr, cap_field);
|
||||||
|
|
||||||
Token token = {};
|
Token token = {};
|
||||||
@@ -2304,7 +2312,7 @@ Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *el
|
|||||||
init_mem_allocator(ctx->checker);
|
init_mem_allocator(ctx->checker);
|
||||||
Entity *allocator_field = alloc_entity_field(scope, token, t_allocator, false, cast(i32)field_count+2);
|
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;
|
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);
|
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 token = {};
|
||||||
token.string = str_lit("Base_Type");
|
token.string = str_lit("Base_Type");
|
||||||
Entity *base_type_entity = alloc_entity_type_name(scope, token, elem, EntityState_Resolved);
|
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);
|
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;
|
t->Generic.entity = e;
|
||||||
e->TypeName.is_type_alias = true;
|
e->TypeName.is_type_alias = true;
|
||||||
e->state = EntityState_Resolved;
|
e->state = EntityState_Resolved;
|
||||||
add_entity(ctx->checker, ps, ident, e);
|
add_entity(ctx, ps, ident, e);
|
||||||
add_entity(ctx->checker, s, ident, e);
|
add_entity(ctx, s, ident, e);
|
||||||
} else {
|
} else {
|
||||||
error(ident, "Invalid use of a polymorphic parameter '$%.*s'", LIT(token.string));
|
error(ident, "Invalid use of a polymorphic parameter '$%.*s'", LIT(token.string));
|
||||||
*type = t_invalid;
|
*type = t_invalid;
|
||||||
@@ -2796,7 +2804,7 @@ Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (is_type_typed(type)) {
|
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 {
|
} else {
|
||||||
gbString name = type_to_string(type);
|
gbString name = type_to_string(type);
|
||||||
error(e, "Invalid type definition of %s", name);
|
error(e, "Invalid type definition of %s", name);
|
||||||
|
|||||||
+1036
-511
File diff suppressed because it is too large
Load Diff
+86
-42
@@ -9,23 +9,23 @@ struct Checker;
|
|||||||
struct CheckerInfo;
|
struct CheckerInfo;
|
||||||
struct CheckerContext;
|
struct CheckerContext;
|
||||||
|
|
||||||
enum AddressingMode;
|
enum AddressingMode : u8;
|
||||||
struct TypeAndValue;
|
struct TypeAndValue;
|
||||||
|
|
||||||
// ExprInfo stores information used for "untyped" expressions
|
// ExprInfo stores information used for "untyped" expressions
|
||||||
struct ExprInfo {
|
struct ExprInfo {
|
||||||
AddressingMode mode;
|
AddressingMode mode;
|
||||||
|
bool is_lhs; // Debug info
|
||||||
Type * type;
|
Type * type;
|
||||||
ExactValue value;
|
ExactValue value;
|
||||||
bool is_lhs; // Debug info
|
|
||||||
};
|
};
|
||||||
|
|
||||||
gb_inline ExprInfo make_expr_info(AddressingMode mode, Type *type, ExactValue value, bool is_lhs) {
|
gb_inline ExprInfo *make_expr_info(AddressingMode mode, Type *type, ExactValue const &value, bool is_lhs) {
|
||||||
ExprInfo ei = {};
|
ExprInfo *ei = gb_alloc_item(permanent_allocator(), ExprInfo);
|
||||||
ei.mode = mode;
|
ei->mode = mode;
|
||||||
ei.type = type;
|
ei->type = type;
|
||||||
ei.value = value;
|
ei->value = value;
|
||||||
ei.is_lhs = is_lhs;
|
ei->is_lhs = is_lhs;
|
||||||
return ei;
|
return ei;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,6 +113,7 @@ struct AttributeContext {
|
|||||||
isize init_expr_list_count;
|
isize init_expr_list_count;
|
||||||
String thread_local_model;
|
String thread_local_model;
|
||||||
String deprecated_message;
|
String deprecated_message;
|
||||||
|
String warning_message;
|
||||||
DeferredProcedure deferred_procedure;
|
DeferredProcedure deferred_procedure;
|
||||||
u32 optimization_mode; // ProcedureOptimizationMode
|
u32 optimization_mode; // ProcedureOptimizationMode
|
||||||
};
|
};
|
||||||
@@ -144,6 +145,7 @@ struct DeclInfo {
|
|||||||
Type * gen_proc_type; // Precalculated
|
Type * gen_proc_type; // Precalculated
|
||||||
bool is_using;
|
bool is_using;
|
||||||
bool where_clauses_evaluated;
|
bool where_clauses_evaluated;
|
||||||
|
bool proc_checked;
|
||||||
|
|
||||||
CommentGroup *comment;
|
CommentGroup *comment;
|
||||||
CommentGroup *docs;
|
CommentGroup *docs;
|
||||||
@@ -186,14 +188,10 @@ enum { DEFAULT_SCOPE_CAPACITY = 29 };
|
|||||||
struct Scope {
|
struct Scope {
|
||||||
Ast * node;
|
Ast * node;
|
||||||
Scope * parent;
|
Scope * parent;
|
||||||
Scope * prev;
|
std::atomic<Scope *> next;
|
||||||
Scope * next;
|
std::atomic<Scope *> head_child;
|
||||||
Scope * first_child;
|
|
||||||
Scope * last_child;
|
|
||||||
StringMap<Entity *> elements;
|
|
||||||
|
|
||||||
Array<Ast *> delayed_directives;
|
StringMap<Entity *> elements;
|
||||||
Array<Ast *> delayed_imports;
|
|
||||||
PtrSet<Scope *> imported;
|
PtrSet<Scope *> imported;
|
||||||
|
|
||||||
i32 flags; // ScopeFlag
|
i32 flags; // ScopeFlag
|
||||||
@@ -258,26 +256,21 @@ struct AtomOpMapEntry {
|
|||||||
|
|
||||||
struct CheckerContext;
|
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
|
// CheckerInfo stores all the symbol information for a type-checked program
|
||||||
struct CheckerInfo {
|
struct CheckerInfo {
|
||||||
Checker *checker;
|
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<AstFile *> files; // Key (full path)
|
||||||
StringMap<AstPackage *> packages; // Key (full path)
|
StringMap<AstPackage *> packages; // Key (full path)
|
||||||
StringMap<Entity *> foreigns;
|
Array<DeclInfo *> variable_init_order;
|
||||||
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 *
|
|
||||||
|
|
||||||
|
|
||||||
AstPackage * builtin_package;
|
AstPackage * builtin_package;
|
||||||
AstPackage * runtime_package;
|
AstPackage * runtime_package;
|
||||||
@@ -287,15 +280,57 @@ struct CheckerInfo {
|
|||||||
PtrSet<Entity *> minimum_dependency_set;
|
PtrSet<Entity *> minimum_dependency_set;
|
||||||
PtrSet<isize> minimum_dependency_type_info_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;
|
Array<Entity *> testing_procedures;
|
||||||
|
|
||||||
bool allow_identifier_uses;
|
Array<Entity *> definitions;
|
||||||
Array<Ast *> identifier_uses; // only used by 'odin query'
|
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 {
|
struct CheckerContext {
|
||||||
@@ -322,6 +357,8 @@ struct CheckerContext {
|
|||||||
CheckerPolyPath *poly_path;
|
CheckerPolyPath *poly_path;
|
||||||
isize poly_level; // TODO(bill): Actually handle correctly
|
isize poly_level; // TODO(bill): Actually handle correctly
|
||||||
|
|
||||||
|
UntypedExprInfoMap *untyped;
|
||||||
|
|
||||||
#define MAX_INLINE_FOR_DEPTH 1024ll
|
#define MAX_INLINE_FOR_DEPTH 1024ll
|
||||||
i64 inline_for_depth;
|
i64 inline_for_depth;
|
||||||
|
|
||||||
@@ -335,17 +372,24 @@ struct CheckerContext {
|
|||||||
Scope * polymorphic_scope;
|
Scope * polymorphic_scope;
|
||||||
|
|
||||||
Ast *assignment_lhs_hint;
|
Ast *assignment_lhs_hint;
|
||||||
|
|
||||||
|
ProcBodyQueue *procs_to_check_queue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct Checker {
|
struct Checker {
|
||||||
Parser * parser;
|
Parser * parser;
|
||||||
CheckerInfo info;
|
CheckerInfo info;
|
||||||
|
|
||||||
Array<ProcInfo> procs_to_check;
|
CheckerContext builtin_ctx;
|
||||||
Array<Entity *> procs_with_deferred_to_check;
|
|
||||||
|
|
||||||
CheckerContext *curr_ctx;
|
MPMCQueue<Entity *> procs_with_deferred_to_check;
|
||||||
CheckerContext init_ctx;
|
|
||||||
|
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);
|
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);
|
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_entity_use (CheckerContext *c, Ast *identifier, Entity *entity);
|
||||||
void add_implicit_entity (CheckerContext *c, Ast *node, Entity *e);
|
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);
|
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_core_context(Checker *c);
|
||||||
void init_mem_allocator(Checker *c);
|
void init_mem_allocator(Checker *c);
|
||||||
|
|
||||||
|
void add_untyped_expressions(CheckerInfo *cinfo, UntypedExprInfoMap *untyped);
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ enum BuiltinProcId {
|
|||||||
BuiltinProc_soa_zip,
|
BuiltinProc_soa_zip,
|
||||||
BuiltinProc_soa_unzip,
|
BuiltinProc_soa_unzip,
|
||||||
|
|
||||||
|
BuiltinProc_or_else,
|
||||||
|
|
||||||
BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures
|
BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures
|
||||||
|
|
||||||
// "Intrinsics"
|
// "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_zip"), 1, true, Expr_Expr, BuiltinProcPkg_builtin},
|
||||||
{STR_LIT("soa_unzip"), 1, false, 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
|
{STR_LIT(""), 0, true, Expr_Expr, BuiltinProcPkg_builtin}, // DIRECTIVE
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+202
-6
@@ -27,6 +27,65 @@
|
|||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <string.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) {
|
gb_inline void zero_size(void *ptr, isize len) {
|
||||||
memset(ptr, 0, 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))
|
#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>
|
template <typename U, typename V>
|
||||||
gb_inline U bit_cast(V &v) { return reinterpret_cast<U &>(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 "unicode.cpp"
|
||||||
#include "array.cpp"
|
#include "array.cpp"
|
||||||
#include "string.cpp"
|
#include "string.cpp"
|
||||||
|
#include "queue.cpp"
|
||||||
|
|
||||||
#define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++)
|
#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) {
|
bool add_overflow_u64(u64 x, u64 y, u64 *result) {
|
||||||
*result = x + y;
|
*result = x + y;
|
||||||
return *result < x || *result < y;
|
return *result < x || *result < y;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sub_overflow_u64(u64 x, u64 y, u64 *result) {
|
bool sub_overflow_u64(u64 x, u64 y, u64 *result) {
|
||||||
*result = x - y;
|
*result = x - y;
|
||||||
return *result > x;
|
return *result > x;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mul_overflow_u64(u64 x, u64 y, u64 *lo, u64 *hi) {
|
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);
|
*lo = _umul128(x, y, hi);
|
||||||
#else
|
#else
|
||||||
// URL(bill): https://stackoverflow.com/questions/25095741/how-can-i-multiply-64-bit-operands-and-get-128-bit-result-portably#25096197
|
// 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) {
|
void arena_init(Arena *arena, gbAllocator backing, isize block_size=ARENA_DEFAULT_BLOCK_SIZE) {
|
||||||
arena->backing = backing;
|
arena->backing = backing;
|
||||||
arena->block_size = block_size;
|
arena->block_size = block_size;
|
||||||
|
arena->use_mutex = true;
|
||||||
array_init(&arena->blocks, backing, 0, 2);
|
array_init(&arena->blocks, backing, 0, 2);
|
||||||
gb_mutex_init(&arena->mutex);
|
gb_mutex_init(&arena->mutex);
|
||||||
}
|
}
|
||||||
@@ -515,6 +581,7 @@ struct Temp_Allocator {
|
|||||||
isize curr_offset;
|
isize curr_offset;
|
||||||
gbAllocator backup_allocator;
|
gbAllocator backup_allocator;
|
||||||
Array<void *> leaked_allocations;
|
Array<void *> leaked_allocations;
|
||||||
|
gbMutex mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
gb_global Temp_Allocator temporary_allocator_data = {};
|
gb_global Temp_Allocator temporary_allocator_data = {};
|
||||||
@@ -525,6 +592,7 @@ void temp_allocator_init(Temp_Allocator *s, isize size) {
|
|||||||
s->len = size;
|
s->len = size;
|
||||||
s->curr_offset = 0;
|
s->curr_offset = 0;
|
||||||
s->leaked_allocations.allocator = s->backup_allocator;
|
s->leaked_allocations.allocator = s->backup_allocator;
|
||||||
|
gb_mutex_init(&s->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *temp_allocator_alloc(Temp_Allocator *s, isize size, isize alignment) {
|
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;
|
Temp_Allocator *s = cast(Temp_Allocator *)allocator_data;
|
||||||
GB_ASSERT_NOT_NULL(s);
|
GB_ASSERT_NOT_NULL(s);
|
||||||
|
|
||||||
|
gb_mutex_lock(&s->mutex);
|
||||||
|
defer (gb_mutex_unlock(&s->mutex));
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case gbAllocation_Alloc:
|
case gbAllocation_Alloc:
|
||||||
return temp_allocator_alloc(s, size, alignment);
|
return temp_allocator_alloc(s, size, alignment);
|
||||||
@@ -592,7 +663,8 @@ GB_ALLOCATOR_PROC(temp_allocator_proc) {
|
|||||||
|
|
||||||
|
|
||||||
gbAllocator temporary_allocator() {
|
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++;
|
n++;
|
||||||
return 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) {
|
i32 bit_set_count(u32 x) {
|
||||||
x -= ((x >> 1) & 0x55555555);
|
x -= ((x >> 1) & 0x55555555);
|
||||||
@@ -1174,3 +1276,97 @@ ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
|
|||||||
#else
|
#else
|
||||||
#error Implement read_directory
|
#error Implement read_directory
|
||||||
#endif
|
#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
@@ -187,7 +187,7 @@ T *odin_doc_get_item(OdinDocWriter *w, OdinDocWriterItemTracker<T> *t, u32 index
|
|||||||
if (w->state != OdinDocWriterState_Writing) {
|
if (w->state != OdinDocWriterState_Writing) {
|
||||||
return nullptr;
|
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);
|
uintptr data = cast(uintptr)w->data + cast(uintptr)(t->offset + gb_size_of(T)*index);
|
||||||
return cast(T *)data;
|
return cast(T *)data;
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -66,6 +66,8 @@ enum EntityFlag : u64 {
|
|||||||
EntityFlag_Disabled = 1ull<<24,
|
EntityFlag_Disabled = 1ull<<24,
|
||||||
EntityFlag_Cold = 1ull<<25, // procedure is rarely called
|
EntityFlag_Cold = 1ull<<25, // procedure is rarely called
|
||||||
|
|
||||||
|
EntityFlag_Lazy = 1ull<<26, // Lazily type checked
|
||||||
|
|
||||||
EntityFlag_Test = 1ull<<30,
|
EntityFlag_Test = 1ull<<30,
|
||||||
|
|
||||||
EntityFlag_Overridden = 1ull<<63,
|
EntityFlag_Overridden = 1ull<<63,
|
||||||
@@ -132,8 +134,9 @@ struct Entity {
|
|||||||
lbModule * code_gen_module;
|
lbModule * code_gen_module;
|
||||||
lbProcedure *code_gen_procedure;
|
lbProcedure *code_gen_procedure;
|
||||||
|
|
||||||
isize order_in_src;
|
u64 order_in_src;
|
||||||
String deprecated_message;
|
String deprecated_message;
|
||||||
|
String warning_message;
|
||||||
|
|
||||||
// IMPORTANT NOTE(bill): This must be a discriminated union because of patching
|
// IMPORTANT NOTE(bill): This must be a discriminated union because of patching
|
||||||
// later entity kinds
|
// later entity kinds
|
||||||
@@ -225,7 +228,7 @@ bool is_entity_exported(Entity *e, bool allow_builtin = false) {
|
|||||||
if (e->flags & EntityFlag_NotExported) {
|
if (e->flags & EntityFlag_NotExported) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (e->file != nullptr && e->file->is_private) {
|
if (e->file != nullptr && (e->file->flags & AstFile_IsPrivate) != 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+20
-21
@@ -27,18 +27,18 @@ Quaternion256 quaternion256_inverse(Quaternion256 x) {
|
|||||||
|
|
||||||
|
|
||||||
enum ExactValueKind {
|
enum ExactValueKind {
|
||||||
ExactValue_Invalid,
|
ExactValue_Invalid = 0,
|
||||||
|
|
||||||
ExactValue_Bool,
|
ExactValue_Bool = 1,
|
||||||
ExactValue_String,
|
ExactValue_String = 2,
|
||||||
ExactValue_Integer,
|
ExactValue_Integer = 3,
|
||||||
ExactValue_Float,
|
ExactValue_Float = 4,
|
||||||
ExactValue_Complex,
|
ExactValue_Complex = 5,
|
||||||
ExactValue_Quaternion,
|
ExactValue_Quaternion = 6,
|
||||||
ExactValue_Pointer,
|
ExactValue_Pointer = 7,
|
||||||
ExactValue_Compound, // TODO(bill): Is this good enough?
|
ExactValue_Compound = 8, // TODO(bill): Is this good enough?
|
||||||
ExactValue_Procedure, // TODO(bill): Is this good enough?
|
ExactValue_Procedure = 9, // TODO(bill): Is this good enough?
|
||||||
ExactValue_Typeid,
|
ExactValue_Typeid = 10,
|
||||||
|
|
||||||
ExactValue_Count,
|
ExactValue_Count,
|
||||||
};
|
};
|
||||||
@@ -75,8 +75,8 @@ HashKey hash_exact_value(ExactValue v) {
|
|||||||
}
|
}
|
||||||
case ExactValue_Integer:
|
case ExactValue_Integer:
|
||||||
{
|
{
|
||||||
HashKey key = hashing_proc(big_int_ptr(&v.value_integer), v.value_integer.len * gb_size_of(u64));
|
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.neg;
|
u8 last = (u8)v.value_integer.sign;
|
||||||
key.key = (key.key ^ last) * 0x100000001b3ll;
|
key.key = (key.key ^ last) * 0x100000001b3ll;
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
@@ -297,13 +297,13 @@ ExactValue exact_value_float_from_string(String string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ExactValue exact_value_from_basic_literal(Token token) {
|
ExactValue exact_value_from_basic_literal(TokenKind kind, String const &string) {
|
||||||
switch (token.kind) {
|
switch (kind) {
|
||||||
case Token_String: return exact_value_string(token.string);
|
case Token_String: return exact_value_string(string);
|
||||||
case Token_Integer: return exact_value_integer_from_string(token.string);
|
case Token_Integer: return exact_value_integer_from_string(string);
|
||||||
case Token_Float: return exact_value_float_from_string(token.string);
|
case Token_Float: return exact_value_float_from_string(string);
|
||||||
case Token_Imag: {
|
case Token_Imag: {
|
||||||
String str = token.string;
|
String str = string;
|
||||||
Rune last_rune = cast(Rune)str[str.len-1];
|
Rune last_rune = cast(Rune)str[str.len-1];
|
||||||
str.len--; // Ignore the 'i|j|k'
|
str.len--; // Ignore the 'i|j|k'
|
||||||
f64 imag = float_from_string(str);
|
f64 imag = float_from_string(str);
|
||||||
@@ -317,7 +317,7 @@ ExactValue exact_value_from_basic_literal(Token token) {
|
|||||||
}
|
}
|
||||||
case Token_Rune: {
|
case Token_Rune: {
|
||||||
Rune r = GB_RUNE_INVALID;
|
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);
|
return exact_value_i64(r);
|
||||||
}
|
}
|
||||||
default:
|
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;
|
case Token_Shr: big_int_shr(&c, a, b); break;
|
||||||
default: goto error;
|
default: goto error;
|
||||||
}
|
}
|
||||||
big_int_normalize(&c);
|
|
||||||
ExactValue res = {ExactValue_Integer};
|
ExactValue res = {ExactValue_Integer};
|
||||||
res.value_integer = c;
|
res.value_integer = c;
|
||||||
return res;
|
return res;
|
||||||
|
|||||||
+4
-2
@@ -6162,7 +6162,7 @@ void gb_shuffle(void *base, isize count, isize size) {
|
|||||||
|
|
||||||
a = cast(u8 *)base + (count-1) * size;
|
a = cast(u8 *)base + (count-1) * size;
|
||||||
for (i = count; i > 1; i--) {
|
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);
|
gb_memswap(a, cast(u8 *)base + j*size, size);
|
||||||
a -= 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) {
|
if (gb_file_open(&file, filepath) == gbFileError_None) {
|
||||||
isize file_size = cast(isize)gb_file_size(&file);
|
isize file_size = cast(isize)gb_file_size(&file);
|
||||||
if (file_size > 0) {
|
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;
|
result.size = file_size;
|
||||||
gb_file_read_at(&file, result.data, result.size, 0);
|
gb_file_read_at(&file, result.data, result.size, 0);
|
||||||
if (zero_terminate) {
|
if (zero_terminate) {
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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"
|
||||||
@@ -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/>
|
||||||
@@ -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: [](https://travis-ci.org/libtom/libtommath)
|
||||||
|
|
||||||
|
develop: [](https://travis-ci.org/libtom/libtommath)
|
||||||
|
|
||||||
|
### AppVeyor
|
||||||
|
|
||||||
|
master: [](https://ci.appveyor.com/project/libtom/libtommath/branch/master)
|
||||||
|
|
||||||
|
develop: [](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.
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
Reference in New Issue
Block a user