mirror of
https://github.com/odin-lang/Odin.git
synced 2026-01-13 08:15:25 +00:00
Merge branch 'master' into windows-llvm-11.1.0
This commit is contained in:
11
.github/workflows/ci.yml
vendored
11
.github/workflows/ci.yml
vendored
@@ -39,7 +39,9 @@ jobs:
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin issues tests
|
||||
run: tests/issues/run.sh
|
||||
run: |
|
||||
cd tests/issues
|
||||
./run.sh
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for Linux i386
|
||||
run: ./odin check examples/all -vet -strict-style -target:linux_i386
|
||||
@@ -91,7 +93,9 @@ jobs:
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin issues tests
|
||||
run: tests/issues/run.sh
|
||||
run: |
|
||||
cd tests/issues
|
||||
./run.sh
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for Darwin arm64
|
||||
run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
|
||||
@@ -163,7 +167,8 @@ jobs:
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
call tests\issues\run.bat
|
||||
cd tests\issues
|
||||
call run.bat
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for Windows 32bits
|
||||
shell: cmd
|
||||
|
||||
4
Makefile
4
Makefile
@@ -1,7 +1,7 @@
|
||||
all: debug demo
|
||||
all: debug
|
||||
|
||||
demo:
|
||||
./odin run examples/demo/demo.odin
|
||||
./odin run examples/demo/demo.odin -file
|
||||
|
||||
report:
|
||||
./odin report
|
||||
|
||||
148
core/compress/shoco/model.odin
Normal file
148
core/compress/shoco/model.odin
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
This file was generated, so don't edit this by hand.
|
||||
Transliterated from https://github.com/Ed-von-Schleck/shoco/blob/master/shoco_model.h,
|
||||
which is an English word model.
|
||||
*/
|
||||
|
||||
// package shoco is an implementation of the shoco short string compressor
|
||||
package shoco
|
||||
|
||||
DEFAULT_MODEL :: Shoco_Model {
|
||||
min_char = 39,
|
||||
max_char = 122,
|
||||
characters_by_id = {
|
||||
'e', 'a', 'i', 'o', 't', 'h', 'n', 'r', 's', 'l', 'u', 'c', 'w', 'm', 'd', 'b', 'p', 'f', 'g', 'v', 'y', 'k', '-', 'H', 'M', 'T', '\'', 'B', 'x', 'I', 'W', 'L',
|
||||
},
|
||||
ids_by_character = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 27, -1, -1, -1, -1, -1, 23, 29, -1, -1, 31, 24, -1, -1, -1, -1, -1, -1, 25, -1, -1, 30, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 15, 11, 14, 0, 17, 18, 5, 2, -1, 21, 9, 13, 6, 3, 16, -1, 7, 8, 4, 10, 19, 12, 28, 20, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
},
|
||||
successors_by_bigram = {
|
||||
7, 4, 12, -1, 6, -1, 1, 0, 3, 5, -1, 9, -1, 8, 2, -1, 15, 14, -1, 10, 11, -1, -1, -1, -1, -1, -1, -1, 13, -1, -1, -1,
|
||||
1, -1, 6, -1, 1, -1, 0, 3, 2, 4, 15, 11, -1, 9, 5, 10, 13, -1, 12, 8, 7, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
9, 11, -1, 4, 2, -1, 0, 8, 1, 5, -1, 6, -1, 3, 7, 15, -1, 12, 10, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, 14, 7, 5, -1, 1, 2, 8, 9, 0, 15, 6, 4, 11, -1, 12, 3, -1, 10, -1, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
2, 4, 3, 1, 5, 0, -1, 6, 10, 9, 7, 12, 11, -1, -1, -1, -1, 13, -1, -1, 8, -1, 15, -1, -1, -1, 14, -1, -1, -1, -1, -1,
|
||||
0, 1, 2, 3, 4, -1, -1, 5, 9, 10, 6, -1, -1, 8, 15, 11, -1, 14, -1, -1, 7, -1, 13, -1, -1, -1, 12, -1, -1, -1, -1, -1,
|
||||
2, 8, 7, 4, 3, -1, 9, -1, 6, 11, -1, 5, -1, -1, 0, -1, -1, 14, 1, 15, 10, 12, -1, -1, -1, -1, 13, -1, -1, -1, -1, -1,
|
||||
0, 3, 1, 2, 6, -1, 9, 8, 4, 12, 13, 10, -1, 11, 7, -1, -1, 15, 14, -1, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
0, 6, 3, 4, 1, 2, -1, -1, 5, 10, 7, 9, 11, 12, -1, -1, 8, 14, -1, -1, 15, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
0, 6, 2, 5, 9, -1, -1, -1, 10, 1, 8, -1, 12, 14, 4, -1, 15, 7, -1, 13, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
8, 10, 9, 15, 1, -1, 4, 0, 3, 2, -1, 6, -1, 12, 11, 13, 7, 14, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
1, 3, 6, 0, 4, 2, -1, 7, 13, 8, 9, 11, -1, -1, 15, -1, -1, -1, -1, -1, 10, 5, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
3, 0, 1, 4, -1, 2, 5, 6, 7, 8, -1, 14, -1, -1, 9, 15, -1, 12, -1, -1, -1, 10, 11, -1, -1, -1, 13, -1, -1, -1, -1, -1,
|
||||
0, 1, 3, 2, 15, -1, 12, -1, 7, 14, 4, -1, -1, 9, -1, 8, 5, 10, -1, -1, 6, -1, 13, -1, -1, -1, 11, -1, -1, -1, -1, -1,
|
||||
0, 3, 1, 2, -1, -1, 12, 6, 4, 9, 7, -1, -1, 14, 8, -1, -1, 15, 11, 13, 5, -1, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
0, 5, 7, 2, 10, 13, -1, 6, 8, 1, 3, -1, -1, 14, 15, 11, -1, -1, -1, 12, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
0, 2, 6, 3, 7, 10, -1, 1, 9, 4, 8, -1, -1, 15, -1, 12, 5, -1, -1, -1, 11, -1, 13, -1, -1, -1, 14, -1, -1, -1, -1, -1,
|
||||
1, 3, 4, 0, 7, -1, 12, 2, 11, 8, 6, 13, -1, -1, -1, -1, -1, 5, -1, -1, 10, 15, 9, -1, -1, -1, 14, -1, -1, -1, -1, -1,
|
||||
1, 3, 5, 2, 13, 0, 9, 4, 7, 6, 8, -1, -1, 15, -1, 11, -1, -1, 10, -1, 14, -1, 12, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
0, 2, 1, 3, -1, -1, -1, 6, -1, -1, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
1, 11, 4, 0, 3, -1, 13, 12, 2, 7, -1, -1, 15, 10, 5, 8, 14, -1, -1, -1, -1, -1, 9, -1, -1, -1, 6, -1, -1, -1, -1, -1,
|
||||
0, 9, 2, 14, 15, 4, 1, 13, 3, 5, -1, -1, 10, -1, -1, -1, -1, 6, 12, -1, 7, -1, 8, -1, -1, -1, 11, -1, -1, -1, -1, -1,
|
||||
-1, 2, 14, -1, 1, 5, 8, 7, 4, 12, -1, 6, 9, 11, 13, 3, 10, 15, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
0, 1, 3, 2, -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
4, 3, 1, 5, -1, -1, -1, 0, -1, -1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
2, 8, 4, 1, -1, 0, -1, 6, -1, -1, 5, -1, 7, -1, -1, -1, -1, -1, -1, -1, 10, -1, -1, 9, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
12, 5, -1, -1, 1, -1, -1, 7, 0, 3, -1, 2, -1, 4, 6, -1, -1, -1, -1, 8, -1, -1, 15, -1, 13, 9, -1, -1, -1, -1, -1, 11,
|
||||
1, 3, 2, 4, -1, -1, -1, 5, -1, 7, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1, -1, -1, -1, -1, -1, -1, 8, -1, -1,
|
||||
5, 3, 4, 12, 1, 6, -1, -1, -1, -1, 8, 2, -1, -1, -1, -1, 0, 9, -1, -1, 11, -1, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, 0, -1, 1, 12, 3, -1, -1, -1, -1, 5, -1, -1, -1, 2, -1, -1, -1, -1, -1, -1, -1, -1, 4, -1, -1, 6, -1, 10,
|
||||
2, 3, 1, 4, -1, 0, -1, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 7, -1, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1,
|
||||
5, 1, 3, 0, -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, 9, -1, -1, 6, -1, 7,
|
||||
},
|
||||
successors_reversed = {
|
||||
's', 't', 'c', 'l', 'm', 'a', 'd', 'r', 'v', 'T', 'A', 'L', 'e', 'M', 'Y', '-',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'-', 't', 'a', 'b', 's', 'h', 'c', 'r', 'n', 'w', 'p', 'm', 'l', 'd', 'i', 'f',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'u', 'e', 'i', 'a', 'o', 'r', 'y', 'l', 'I', 'E', 'R', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'e', 'a', 'o', 'i', 'u', 'A', 'y', 'E', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
't', 'n', 'f', 's', '\'', 'm', 'I', 'N', 'A', 'E', 'L', 'Z', 'r', 'V', 'R', 'C',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'o', 'a', 'y', 'i', 'u', 'e', 'I', 'L', 'D', '\'', 'E', 'Y', '\x00', '\x00', '\x00', '\x00',
|
||||
'r', 'i', 'y', 'a', 'e', 'o', 'u', 'Y', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'h', 'o', 'e', 'E', 'i', 'u', 'r', 'w', 'a', 'H', 'y', 'R', 'Z', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'h', 'i', 'e', 'a', 'o', 'r', 'I', 'y', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'n', 't', 's', 'r', 'l', 'd', 'i', 'y', 'v', 'm', 'b', 'c', 'g', 'p', 'k', 'u',
|
||||
'e', 'l', 'o', 'u', 'y', 'a', 'r', 'i', 's', 'j', 't', 'b', 'v', 'h', 'm', 'd',
|
||||
'o', 'e', 'h', 'a', 't', 'k', 'i', 'r', 'l', 'u', 'y', 'c', 'q', 's', '-', 'd',
|
||||
'e', 'i', 'o', 'a', 's', 'y', 'r', 'u', 'd', 'l', '-', 'g', 'n', 'v', 'm', 'f',
|
||||
'r', 'n', 'd', 's', 'a', 'l', 't', 'e', 'm', 'c', 'v', 'y', 'i', 'x', 'f', 'p',
|
||||
'o', 'e', 'r', 'a', 'i', 'f', 'u', 't', 'l', '-', 'y', 's', 'n', 'c', '\'', 'k',
|
||||
'h', 'e', 'o', 'a', 'r', 'i', 'l', 's', 'u', 'n', 'g', 'b', '-', 't', 'y', 'm',
|
||||
'e', 'a', 'i', 'o', 't', 'r', 'u', 'y', 'm', 's', 'l', 'b', '\'', '-', 'f', 'd',
|
||||
'n', 's', 't', 'm', 'o', 'l', 'c', 'd', 'r', 'e', 'g', 'a', 'f', 'v', 'z', 'b',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'e', 'n', 'i', 's', 'h', 'l', 'f', 'y', '-', 'a', 'w', '\'', 'g', 'r', 'o', 't',
|
||||
'e', 'l', 'i', 'y', 'd', 'o', 'a', 'f', 'u', 't', 's', 'k', 'w', 'v', 'm', 'p',
|
||||
'e', 'a', 'o', 'i', 'u', 'p', 'y', 's', 'b', 'm', 'f', '\'', 'n', '-', 'l', 't',
|
||||
'd', 'g', 'e', 't', 'o', 'c', 's', 'i', 'a', 'n', 'y', 'l', 'k', '\'', 'f', 'v',
|
||||
'u', 'n', 'r', 'f', 'm', 't', 'w', 'o', 's', 'l', 'v', 'd', 'p', 'k', 'i', 'c',
|
||||
'e', 'r', 'a', 'o', 'l', 'p', 'i', 't', 'u', 's', 'h', 'y', 'b', '-', '\'', 'm',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'e', 'i', 'o', 'a', 's', 'y', 't', 'd', 'r', 'n', 'c', 'm', 'l', 'u', 'g', 'f',
|
||||
'e', 't', 'h', 'i', 'o', 's', 'a', 'u', 'p', 'c', 'l', 'w', 'm', 'k', 'f', 'y',
|
||||
'h', 'o', 'e', 'i', 'a', 't', 'r', 'u', 'y', 'l', 's', 'w', 'c', 'f', '\'', '-',
|
||||
'r', 't', 'l', 's', 'n', 'g', 'c', 'p', 'e', 'i', 'a', 'd', 'm', 'b', 'f', 'o',
|
||||
'e', 'i', 'a', 'o', 'y', 'u', 'r', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'a', 'i', 'h', 'e', 'o', 'n', 'r', 's', 'l', 'd', 'k', '-', 'f', '\'', 'c', 'b',
|
||||
'p', 't', 'c', 'a', 'i', 'e', 'h', 'q', 'u', 'f', '-', 'y', 'o', '\x00', '\x00', '\x00',
|
||||
'o', 'e', 's', 't', 'i', 'd', '\'', 'l', 'b', '-', 'm', 'a', 'r', 'n', 'p', 'w',
|
||||
},
|
||||
|
||||
character_count = 32,
|
||||
successor_count = 16,
|
||||
|
||||
max_successor_n = 7,
|
||||
packs = {
|
||||
{ 0x80000000, 1, 2, { 26, 24, 24, 24, 24, 24, 24, 24 }, { 15, 3, 0, 0, 0, 0, 0, 0 }, 0xc0, 0x80 },
|
||||
{ 0xc0000000, 2, 4, { 25, 22, 19, 16, 16, 16, 16, 16 }, { 15, 7, 7, 7, 0, 0, 0, 0 }, 0xe0, 0xc0 },
|
||||
{ 0xe0000000, 4, 8, { 23, 19, 15, 11, 8, 5, 2, 0 }, { 31, 15, 15, 15, 7, 7, 7, 3 }, 0xf0, 0xe0 },
|
||||
},
|
||||
}
|
||||
318
core/compress/shoco/shoco.odin
Normal file
318
core/compress/shoco/shoco.odin
Normal file
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
|
||||
An implementation of [shoco](https://github.com/Ed-von-Schleck/shoco) by Christian Schramm.
|
||||
*/
|
||||
|
||||
// package shoco is an implementation of the shoco short string compressor
|
||||
package shoco
|
||||
|
||||
import "core:intrinsics"
|
||||
import "core:compress"
|
||||
|
||||
Shoco_Pack :: struct {
|
||||
word: u32,
|
||||
bytes_packed: i8,
|
||||
bytes_unpacked: i8,
|
||||
offsets: [8]u16,
|
||||
masks: [8]i16,
|
||||
header_mask: u8,
|
||||
header: u8,
|
||||
}
|
||||
|
||||
Shoco_Model :: struct {
|
||||
min_char: u8,
|
||||
max_char: u8,
|
||||
characters_by_id: []u8,
|
||||
ids_by_character: [256]i16,
|
||||
successors_by_bigram: []i8,
|
||||
successors_reversed: []u8,
|
||||
|
||||
character_count: u8,
|
||||
successor_count: u8,
|
||||
max_successor_n: i8,
|
||||
packs: []Shoco_Pack,
|
||||
}
|
||||
|
||||
compress_bound :: proc(uncompressed_size: int) -> (worst_case_compressed_size: int) {
|
||||
// Worst case compression happens when input is non-ASCII (128-255)
|
||||
// Encoded as 0x00 + the byte in question.
|
||||
return uncompressed_size * 2
|
||||
}
|
||||
|
||||
decompress_bound :: proc(compressed_size: int, model := DEFAULT_MODEL) -> (maximum_decompressed_size: int) {
|
||||
// Best case compression is 2:1
|
||||
most: f64
|
||||
for pack in model.packs {
|
||||
val := f64(compressed_size) / f64(pack.bytes_packed) * f64(pack.bytes_unpacked)
|
||||
most = max(most, val)
|
||||
}
|
||||
return int(most)
|
||||
}
|
||||
|
||||
find_best_encoding :: proc(indices: []i16, n_consecutive: i8, model := DEFAULT_MODEL) -> (res: int) {
|
||||
for p := len(model.packs); p > 0; p -= 1 {
|
||||
pack := model.packs[p - 1]
|
||||
if n_consecutive >= pack.bytes_unpacked {
|
||||
have_index := true
|
||||
for i := 0; i < int(pack.bytes_unpacked); i += 1 {
|
||||
if indices[i] > pack.masks[i] {
|
||||
have_index = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if have_index {
|
||||
return p - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
validate_model :: proc(model: Shoco_Model) -> (int, compress.Error) {
|
||||
if len(model.characters_by_id) != int(model.character_count) {
|
||||
return 0, .Unknown_Compression_Method
|
||||
}
|
||||
|
||||
if len(model.successors_by_bigram) != int(model.character_count) * int(model.character_count) {
|
||||
return 0, .Unknown_Compression_Method
|
||||
}
|
||||
|
||||
if len(model.successors_reversed) != int(model.successor_count) * int(model.max_char - model.min_char) {
|
||||
return 0, .Unknown_Compression_Method
|
||||
}
|
||||
|
||||
// Model seems legit.
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Decompresses into provided buffer.
|
||||
decompress_slice_to_output_buffer :: proc(input: []u8, output: []u8, model := DEFAULT_MODEL) -> (size: int, err: compress.Error) {
|
||||
inp, inp_end := 0, len(input)
|
||||
out, out_end := 0, len(output)
|
||||
|
||||
validate_model(model) or_return
|
||||
|
||||
for inp < inp_end {
|
||||
val := transmute(i8)input[inp]
|
||||
mark := int(-1)
|
||||
|
||||
for val < 0 {
|
||||
val <<= 1
|
||||
mark += 1
|
||||
}
|
||||
|
||||
if mark > len(model.packs) {
|
||||
return out, .Unknown_Compression_Method
|
||||
}
|
||||
|
||||
if mark < 0 {
|
||||
if out >= out_end {
|
||||
return out, .Output_Too_Short
|
||||
}
|
||||
|
||||
// Ignore the sentinel value for non-ASCII chars
|
||||
if input[inp] == 0x00 {
|
||||
inp += 1
|
||||
if inp >= inp_end {
|
||||
return out, .Stream_Too_Short
|
||||
}
|
||||
}
|
||||
output[out] = input[inp]
|
||||
inp, out = inp + 1, out + 1
|
||||
|
||||
} else {
|
||||
pack := model.packs[mark]
|
||||
|
||||
if out + int(pack.bytes_unpacked) > out_end {
|
||||
return out, .Output_Too_Short
|
||||
} else if inp + int(pack.bytes_packed) > inp_end {
|
||||
return out, .Stream_Too_Short
|
||||
}
|
||||
|
||||
code := intrinsics.unaligned_load((^u32)(&input[inp]))
|
||||
when ODIN_ENDIAN == .Little {
|
||||
code = intrinsics.byte_swap(code)
|
||||
}
|
||||
|
||||
// Unpack the leading char
|
||||
offset := pack.offsets[0]
|
||||
mask := pack.masks[0]
|
||||
|
||||
last_chr := model.characters_by_id[(code >> offset) & u32(mask)]
|
||||
output[out] = last_chr
|
||||
|
||||
// Unpack the successor chars
|
||||
for i := 1; i < int(pack.bytes_unpacked); i += 1 {
|
||||
offset = pack.offsets[i]
|
||||
mask = pack.masks[i]
|
||||
|
||||
index_major := u32(last_chr - model.min_char) * u32(model.successor_count)
|
||||
index_minor := (code >> offset) & u32(mask)
|
||||
|
||||
last_chr = model.successors_reversed[index_major + index_minor]
|
||||
|
||||
output[out + i] = last_chr
|
||||
}
|
||||
|
||||
out += int(pack.bytes_unpacked)
|
||||
inp += int(pack.bytes_packed)
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
decompress_slice_to_string :: proc(input: []u8, model := DEFAULT_MODEL, allocator := context.allocator) -> (res: string, err: compress.Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
if len(input) == 0 {
|
||||
return "", .Stream_Too_Short
|
||||
}
|
||||
|
||||
max_output_size := decompress_bound(len(input), model)
|
||||
|
||||
buf: [dynamic]u8
|
||||
if !resize(&buf, max_output_size) {
|
||||
return "", .Out_Of_Memory
|
||||
}
|
||||
|
||||
length, result := decompress_slice_to_output_buffer(input, buf[:])
|
||||
resize(&buf, length)
|
||||
return string(buf[:]), result
|
||||
}
|
||||
decompress :: proc{decompress_slice_to_output_buffer, decompress_slice_to_string}
|
||||
|
||||
compress_string_to_buffer :: proc(input: string, output: []u8, model := DEFAULT_MODEL, allocator := context.allocator) -> (size: int, err: compress.Error) {
|
||||
inp, inp_end := 0, len(input)
|
||||
out, out_end := 0, len(output)
|
||||
output := output
|
||||
|
||||
validate_model(model) or_return
|
||||
|
||||
indices := make([]i16, model.max_successor_n + 1)
|
||||
defer delete(indices)
|
||||
|
||||
last_resort := false
|
||||
|
||||
encode: for inp < inp_end {
|
||||
if last_resort {
|
||||
last_resort = false
|
||||
|
||||
if input[inp] & 0x80 == 0x80 {
|
||||
// Non-ASCII case
|
||||
if out + 2 > out_end {
|
||||
return out, .Output_Too_Short
|
||||
}
|
||||
|
||||
// Put in a sentinel byte
|
||||
output[out] = 0x00
|
||||
out += 1
|
||||
} else {
|
||||
// An ASCII byte
|
||||
if out + 1 > out_end {
|
||||
return out, .Output_Too_Short
|
||||
}
|
||||
}
|
||||
output[out] = input[inp]
|
||||
out, inp = out + 1, inp + 1
|
||||
} else {
|
||||
// Find the longest string of known successors
|
||||
indices[0] = model.ids_by_character[input[inp]]
|
||||
last_chr_index := indices[0]
|
||||
|
||||
if last_chr_index < 0 {
|
||||
last_resort = true
|
||||
continue encode
|
||||
}
|
||||
|
||||
rest := inp_end - inp
|
||||
n_consecutive: i8 = 1
|
||||
for ; n_consecutive <= model.max_successor_n; n_consecutive += 1 {
|
||||
if inp_end > 0 && int(n_consecutive) == rest {
|
||||
break
|
||||
}
|
||||
|
||||
current_index := model.ids_by_character[input[inp + int(n_consecutive)]]
|
||||
if current_index < 0 { // '\0' is always -1
|
||||
break
|
||||
}
|
||||
|
||||
successor_index := model.successors_by_bigram[last_chr_index * i16(model.character_count) + current_index]
|
||||
if successor_index < 0 {
|
||||
break
|
||||
}
|
||||
|
||||
indices[n_consecutive] = i16(successor_index)
|
||||
last_chr_index = current_index
|
||||
}
|
||||
|
||||
if n_consecutive < 2 {
|
||||
last_resort = true
|
||||
continue encode
|
||||
}
|
||||
|
||||
pack_n := find_best_encoding(indices, n_consecutive)
|
||||
if pack_n >= 0 {
|
||||
if out + int(model.packs[pack_n].bytes_packed) > out_end {
|
||||
return out, .Output_Too_Short
|
||||
}
|
||||
|
||||
pack := model.packs[pack_n]
|
||||
code := pack.word
|
||||
|
||||
for i := 0; i < int(pack.bytes_unpacked); i += 1 {
|
||||
code |= u32(indices[i]) << pack.offsets[i]
|
||||
}
|
||||
|
||||
// In the little-endian world, we need to swap what's in the register to match the memory representation.
|
||||
when ODIN_ENDIAN == .Little {
|
||||
code = intrinsics.byte_swap(code)
|
||||
}
|
||||
out_ptr := raw_data(output[out:])
|
||||
|
||||
switch pack.bytes_packed {
|
||||
case 4:
|
||||
intrinsics.unaligned_store(transmute(^u32)out_ptr, code)
|
||||
case 2:
|
||||
intrinsics.unaligned_store(transmute(^u16)out_ptr, u16(code))
|
||||
case 1:
|
||||
intrinsics.unaligned_store(transmute(^u8)out_ptr, u8(code))
|
||||
case:
|
||||
return out, .Unknown_Compression_Method
|
||||
}
|
||||
|
||||
out += int(pack.bytes_packed)
|
||||
inp += int(pack.bytes_unpacked)
|
||||
} else {
|
||||
last_resort = true
|
||||
continue encode
|
||||
}
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
compress_string :: proc(input: string, model := DEFAULT_MODEL, allocator := context.allocator) -> (output: []u8, err: compress.Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
if len(input) == 0 {
|
||||
return {}, .Stream_Too_Short
|
||||
}
|
||||
|
||||
max_output_size := compress_bound(len(input))
|
||||
|
||||
buf: [dynamic]u8
|
||||
if !resize(&buf, max_output_size) {
|
||||
return {}, .Out_Of_Memory
|
||||
}
|
||||
|
||||
length, result := compress_string_to_buffer(input, buf[:])
|
||||
resize(&buf, length)
|
||||
return buf[:length], result
|
||||
}
|
||||
compress :: proc{compress_string_to_buffer, compress_string}
|
||||
@@ -60,19 +60,27 @@ clear :: proc(c: ^$C/Cache($Key, $Value), call_on_remove: bool) {
|
||||
set :: proc(c: ^$C/Cache($Key, $Value), key: Key, value: Value) -> runtime.Allocator_Error {
|
||||
if e, ok := c.entries[key]; ok {
|
||||
e.value = value
|
||||
_pop_node(c, e)
|
||||
_push_front_node(c, e)
|
||||
return nil
|
||||
}
|
||||
|
||||
e := new(Node(Key, Value), c.node_allocator) or_return
|
||||
e.key = key
|
||||
e.value = value
|
||||
|
||||
_push_front_node(c, e)
|
||||
if c.count > c.capacity {
|
||||
_remove_node(c, c.tail)
|
||||
e : ^Node(Key, Value) = nil
|
||||
assert(c.count <= c.capacity)
|
||||
if c.count == c.capacity {
|
||||
e = c.tail
|
||||
_remove_node(c, e)
|
||||
}
|
||||
else {
|
||||
c.count += 1
|
||||
e = new(Node(Key, Value), c.node_allocator) or_return
|
||||
}
|
||||
|
||||
e.key = key
|
||||
e.value = value
|
||||
_push_front_node(c, e)
|
||||
c.entries[key] = e
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -122,6 +130,8 @@ remove :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> bool {
|
||||
return false
|
||||
}
|
||||
_remove_node(c, e)
|
||||
free(node, c.node_allocator)
|
||||
c.count -= 1
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -143,14 +153,9 @@ _remove_node :: proc(c: ^$C/Cache($Key, $Value), node: ^Node(Key, Value)) {
|
||||
node.prev = nil
|
||||
node.next = nil
|
||||
|
||||
c.count -= 1
|
||||
|
||||
delete_key(&c.entries, node.key)
|
||||
|
||||
_call_on_remove(c, node)
|
||||
|
||||
free(node, c.node_allocator)
|
||||
|
||||
}
|
||||
|
||||
@(private)
|
||||
@@ -171,8 +176,6 @@ _push_front_node :: proc(c: ^$C/Cache($Key, $Value), e: ^Node(Key, Value)) {
|
||||
c.tail = e
|
||||
}
|
||||
e.prev = nil
|
||||
|
||||
c.count += 1
|
||||
}
|
||||
|
||||
@(private)
|
||||
@@ -180,6 +183,12 @@ _pop_node :: proc(c: ^$C/Cache($Key, $Value), e: ^Node(Key, Value)) {
|
||||
if e == nil {
|
||||
return
|
||||
}
|
||||
if c.head == e {
|
||||
c.head = e.next
|
||||
}
|
||||
if c.tail == e {
|
||||
c.tail = e.prev
|
||||
}
|
||||
if e.prev != nil {
|
||||
e.prev.next = e.next
|
||||
}
|
||||
|
||||
@@ -354,6 +354,12 @@ unquote_string :: proc(token: Token, spec: Specification, allocator := context.a
|
||||
|
||||
b := bytes_make(len(s) + 2*utf8.UTF_MAX, 1, allocator) or_return
|
||||
w := copy(b, s[0:i])
|
||||
|
||||
if len(b) == 0 && allocator.data == nil {
|
||||
// `unmarshal_count_array` calls us with a nil allocator
|
||||
return string(b[:w]), nil
|
||||
}
|
||||
|
||||
loop: for i < len(s) {
|
||||
c := s[i]
|
||||
switch {
|
||||
|
||||
@@ -320,6 +320,20 @@ QOI_Info :: struct {
|
||||
header: QOI_Header,
|
||||
}
|
||||
|
||||
TGA_Header :: struct #packed {
|
||||
id_length: u8,
|
||||
color_map_type: u8,
|
||||
data_type_code: u8,
|
||||
color_map_origin: u16le,
|
||||
color_map_length: u16le,
|
||||
color_map_depth: u8,
|
||||
origin: [2]u16le,
|
||||
dimensions: [2]u16le,
|
||||
bits_per_pixel: u8,
|
||||
image_descriptor: u8,
|
||||
}
|
||||
#assert(size_of(TGA_Header) == 18)
|
||||
|
||||
// Function to help with image buffer calculations
|
||||
compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes := int(0)) -> (size: int) {
|
||||
size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height
|
||||
|
||||
103
core/image/tga/tga.odin
Normal file
103
core/image/tga/tga.odin
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
*/
|
||||
|
||||
|
||||
// package tga implements a TGA image writer for 8-bit RGB and RGBA images.
|
||||
package tga
|
||||
|
||||
import "core:mem"
|
||||
import "core:image"
|
||||
import "core:compress"
|
||||
import "core:bytes"
|
||||
import "core:os"
|
||||
|
||||
Error :: image.Error
|
||||
General :: compress.General_Error
|
||||
Image :: image.Image
|
||||
Options :: image.Options
|
||||
|
||||
RGB_Pixel :: image.RGB_Pixel
|
||||
RGBA_Pixel :: image.RGBA_Pixel
|
||||
|
||||
save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
if img == nil {
|
||||
return .Invalid_Input_Image
|
||||
}
|
||||
|
||||
if output == nil {
|
||||
return .Invalid_Output
|
||||
}
|
||||
|
||||
pixels := img.width * img.height
|
||||
if pixels == 0 || pixels > image.MAX_DIMENSIONS || img.width > 65535 || img.height > 65535 {
|
||||
return .Invalid_Input_Image
|
||||
}
|
||||
|
||||
// Our TGA writer supports only 8-bit images with 3 or 4 channels.
|
||||
if img.depth != 8 || img.channels < 3 || img.channels > 4 {
|
||||
return .Invalid_Input_Image
|
||||
}
|
||||
|
||||
if img.channels * pixels != len(img.pixels.buf) {
|
||||
return .Invalid_Input_Image
|
||||
}
|
||||
|
||||
written := 0
|
||||
|
||||
// Calculate and allocate necessary space.
|
||||
necessary := pixels * img.channels + size_of(image.TGA_Header)
|
||||
|
||||
if !resize(&output.buf, necessary) {
|
||||
return General.Resize_Failed
|
||||
}
|
||||
|
||||
header := image.TGA_Header{
|
||||
data_type_code = 0x02, // Color, uncompressed.
|
||||
dimensions = {u16le(img.width), u16le(img.height)},
|
||||
bits_per_pixel = u8(img.depth * img.channels),
|
||||
image_descriptor = 1 << 5, // Origin is top left.
|
||||
}
|
||||
header_bytes := transmute([size_of(image.TGA_Header)]u8)header
|
||||
|
||||
copy(output.buf[written:], header_bytes[:])
|
||||
written += size_of(image.TGA_Header)
|
||||
|
||||
/*
|
||||
Encode loop starts here.
|
||||
*/
|
||||
if img.channels == 3 {
|
||||
pix := mem.slice_data_cast([]RGB_Pixel, img.pixels.buf[:])
|
||||
out := mem.slice_data_cast([]RGB_Pixel, output.buf[written:])
|
||||
for p, i in pix {
|
||||
out[i] = p.bgr
|
||||
}
|
||||
} else if img.channels == 4 {
|
||||
pix := mem.slice_data_cast([]RGBA_Pixel, img.pixels.buf[:])
|
||||
out := mem.slice_data_cast([]RGBA_Pixel, output.buf[written:])
|
||||
for p, i in pix {
|
||||
out[i] = p.bgra
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
defer bytes.buffer_destroy(out)
|
||||
|
||||
save_to_memory(out, img, options) or_return
|
||||
write_ok := os.write_entire_file(output, out.buf[:])
|
||||
|
||||
return nil if write_ok else General.Cannot_Open_File
|
||||
}
|
||||
|
||||
save :: proc{save_to_memory, save_to_file}
|
||||
@@ -479,21 +479,21 @@ angle_from_quaternion_f16 :: proc(q: Quaternionf16) -> f16 {
|
||||
return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2
|
||||
}
|
||||
|
||||
return math.cos(q.x) * 2
|
||||
return math.acos(q.w) * 2
|
||||
}
|
||||
angle_from_quaternion_f32 :: proc(q: Quaternionf32) -> f32 {
|
||||
if abs(q.w) > math.SQRT_THREE*0.5 {
|
||||
return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2
|
||||
}
|
||||
|
||||
return math.cos(q.x) * 2
|
||||
return math.acos(q.w) * 2
|
||||
}
|
||||
angle_from_quaternion_f64 :: proc(q: Quaternionf64) -> f64 {
|
||||
if abs(q.w) > math.SQRT_THREE*0.5 {
|
||||
return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2
|
||||
}
|
||||
|
||||
return math.cos(q.x) * 2
|
||||
return math.acos(q.w) * 2
|
||||
}
|
||||
angle_from_quaternion :: proc{
|
||||
angle_from_quaternion_f16,
|
||||
|
||||
@@ -6,24 +6,7 @@ import "core:runtime"
|
||||
nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return nil, .Out_Of_Memory
|
||||
case .Free:
|
||||
return nil, .None
|
||||
case .Free_All:
|
||||
return nil, .Mode_Not_Implemented
|
||||
case .Resize:
|
||||
if size == 0 {
|
||||
return nil, .None
|
||||
}
|
||||
return nil, .Out_Of_Memory
|
||||
case .Query_Features:
|
||||
return nil, .Mode_Not_Implemented
|
||||
case .Query_Info:
|
||||
return nil, .Mode_Not_Implemented
|
||||
}
|
||||
return nil, .None
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
nil_allocator :: proc() -> Allocator {
|
||||
|
||||
@@ -172,7 +172,7 @@ slice_data_cast :: proc "contextless" ($T: typeid/[]$A, slice: $S/[]$B) -> T {
|
||||
|
||||
slice_to_components :: proc "contextless" (slice: $E/[]$T) -> (data: ^T, len: int) {
|
||||
s := transmute(Raw_Slice)slice
|
||||
return s.data, s.len
|
||||
return (^T)(s.data), s.len
|
||||
}
|
||||
|
||||
buffer_from_slice :: proc "contextless" (backing: $T/[]$E) -> [dynamic]E {
|
||||
|
||||
@@ -120,7 +120,7 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int)
|
||||
do_commit_if_necessary :: proc(block: ^Memory_Block, size: uint) -> (err: Allocator_Error) {
|
||||
if block.committed - block.used < size {
|
||||
pmblock := (^Platform_Memory_Block)(block)
|
||||
base_offset := uint(uintptr(block) - uintptr(pmblock))
|
||||
base_offset := uint(uintptr(pmblock.block.base) - uintptr(pmblock))
|
||||
platform_total_commit := base_offset + block.used + size
|
||||
|
||||
assert(pmblock.committed <= pmblock.reserved)
|
||||
|
||||
@@ -4,6 +4,8 @@ package filepath
|
||||
|
||||
import "core:strings"
|
||||
|
||||
SEPARATOR_CHARS :: `/\`
|
||||
|
||||
// is_separator checks whether the byte is a valid separator character
|
||||
is_separator :: proc(c: byte) -> bool {
|
||||
switch c {
|
||||
@@ -69,6 +71,16 @@ volume_name_len :: proc(path: string) -> int {
|
||||
return 0
|
||||
}
|
||||
|
||||
/*
|
||||
Gets the file name and extension from a path.
|
||||
|
||||
i.e:
|
||||
'path/to/name.tar.gz' -> 'name.tar.gz'
|
||||
'path/to/name.txt' -> 'name.txt'
|
||||
'path/to/name' -> 'name'
|
||||
|
||||
Returns "." if the path is an empty string.
|
||||
*/
|
||||
base :: proc(path: string) -> string {
|
||||
if path == "" {
|
||||
return "."
|
||||
@@ -94,6 +106,118 @@ base :: proc(path: string) -> string {
|
||||
return path
|
||||
}
|
||||
|
||||
/*
|
||||
Gets the name of a file from a path.
|
||||
|
||||
The stem of a file is such that stem(path) + ext(path) = base(path).
|
||||
|
||||
Only the last dot is considered when splitting the file extension.
|
||||
See `short_stem`.
|
||||
|
||||
i.e:
|
||||
'name.tar.gz' -> 'name.tar'
|
||||
'name.txt' -> 'name'
|
||||
|
||||
Returns an empty string if there is no stem. e.g: '.gitignore'.
|
||||
Returns an empty string if there's a trailing path separator.
|
||||
*/
|
||||
stem :: proc(path: string) -> string {
|
||||
if len(path) > 0 && is_separator(path[len(path) - 1]) {
|
||||
// NOTE(tetra): Trailing separator
|
||||
return ""
|
||||
}
|
||||
|
||||
// NOTE(tetra): Get the basename
|
||||
path := path
|
||||
if i := strings.last_index_any(path, SEPARATOR_CHARS); i != -1 {
|
||||
path = path[i+1:]
|
||||
}
|
||||
|
||||
if i := strings.last_index_byte(path, '.'); i != -1 {
|
||||
return path[:i]
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
/*
|
||||
Gets the name of a file from a path.
|
||||
|
||||
The short stem is such that short_stem(path) + long_ext(path) = base(path).
|
||||
|
||||
The first dot is used to split off the file extension, unlike `stem` which uses the last dot.
|
||||
|
||||
i.e:
|
||||
'name.tar.gz' -> 'name'
|
||||
'name.txt' -> 'name'
|
||||
|
||||
Returns an empty string if there is no stem. e.g: '.gitignore'.
|
||||
Returns an empty string if there's a trailing path separator.
|
||||
*/
|
||||
short_stem :: proc(path: string) -> string {
|
||||
s := stem(path)
|
||||
if i := strings.index_byte(s, '.'); i != -1 {
|
||||
return s[:i]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
/*
|
||||
Gets the file extension from a path, including the dot.
|
||||
|
||||
The file extension is such that stem(path) + ext(path) = base(path).
|
||||
|
||||
Only the last dot is considered when splitting the file extension.
|
||||
See `long_ext`.
|
||||
|
||||
i.e:
|
||||
'name.tar.gz' -> '.gz'
|
||||
'name.txt' -> '.txt'
|
||||
|
||||
Returns an empty string if there is no dot.
|
||||
Returns an empty string if there is a trailing path separator.
|
||||
*/
|
||||
ext :: proc(path: string) -> string {
|
||||
for i := len(path)-1; i >= 0 && !is_separator(path[i]); i -= 1 {
|
||||
if path[i] == '.' {
|
||||
return path[i:]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
/*
|
||||
Gets the file extension from a path, including the dot.
|
||||
|
||||
The long file extension is such that short_stem(path) + long_ext(path) = base(path).
|
||||
|
||||
The first dot is used to split off the file extension, unlike `ext` which uses the last dot.
|
||||
|
||||
i.e:
|
||||
'name.tar.gz' -> '.tar.gz'
|
||||
'name.txt' -> '.txt'
|
||||
|
||||
Returns an empty string if there is no dot.
|
||||
Returns an empty string if there is a trailing path separator.
|
||||
*/
|
||||
long_ext :: proc(path: string) -> string {
|
||||
if len(path) > 0 && is_separator(path[len(path) - 1]) {
|
||||
// NOTE(tetra): Trailing separator
|
||||
return ""
|
||||
}
|
||||
|
||||
// NOTE(tetra): Get the basename
|
||||
path := path
|
||||
if i := strings.last_index_any(path, SEPARATOR_CHARS); i != -1 {
|
||||
path = path[i+1:]
|
||||
}
|
||||
|
||||
if i := strings.index_byte(path, '.'); i != -1 {
|
||||
return path[i:]
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
clean :: proc(path: string, allocator := context.allocator) -> string {
|
||||
context.allocator = allocator
|
||||
@@ -189,15 +313,6 @@ to_slash :: proc(path: string, allocator := context.allocator) -> (new_path: str
|
||||
return strings.replace_all(path, SEPARATOR_STRING, "/", allocator)
|
||||
}
|
||||
|
||||
ext :: proc(path: string) -> string {
|
||||
for i := len(path)-1; i >= 0 && !is_separator(path[i]); i -= 1 {
|
||||
if path[i] == '.' {
|
||||
return path[i:]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
Relative_Error :: enum {
|
||||
None,
|
||||
|
||||
@@ -10,6 +10,53 @@ _ :: builtin
|
||||
_ :: bits
|
||||
_ :: mem
|
||||
|
||||
/*
|
||||
Turn a pointer and a length into a slice.
|
||||
*/
|
||||
from_ptr :: proc "contextless" (ptr: ^$T, count: int) -> []T {
|
||||
return ([^]T)(ptr)[:count]
|
||||
}
|
||||
|
||||
/*
|
||||
Turn a pointer and a length into a byte slice.
|
||||
*/
|
||||
bytes_from_ptr :: proc "contextless" (ptr: rawptr, byte_count: int) -> []byte {
|
||||
return ([^]byte)(ptr)[:byte_count]
|
||||
}
|
||||
|
||||
/*
|
||||
Turn a slice into a byte slice.
|
||||
|
||||
See `slice.reinterpret` to go the other way.
|
||||
*/
|
||||
to_bytes :: proc "contextless" (s: []$T) -> []byte {
|
||||
return ([^]byte)(raw_data(s))[:len(s) * size_of(T)]
|
||||
}
|
||||
|
||||
/*
|
||||
Turn a slice of one type, into a slice of another type.
|
||||
|
||||
Only converts the type and length of the slice itself.
|
||||
The length is rounded down to the nearest whole number of items.
|
||||
|
||||
```
|
||||
large_items := []i64{1, 2, 3, 4}
|
||||
small_items := slice.reinterpret([]i32, large_items)
|
||||
assert(len(small_items) == 8)
|
||||
```
|
||||
```
|
||||
small_items := []byte{1, 0, 0, 0, 0, 0, 0, 0,
|
||||
2, 0, 0, 0}
|
||||
large_items := slice.reinterpret([]i64, small_items)
|
||||
assert(len(large_items) == 1) // only enough bytes to make 1 x i64; two would need at least 8 bytes.
|
||||
```
|
||||
*/
|
||||
reinterpret :: proc "contextless" ($T: typeid/[]$U, s: []$V) -> []U {
|
||||
bytes := to_bytes(s)
|
||||
n := len(bytes) / size_of(U)
|
||||
return ([^]U)(raw_data(bytes))[:n]
|
||||
}
|
||||
|
||||
|
||||
swap :: proc(array: $T/[]$E, a, b: int) {
|
||||
when size_of(E) > 8 {
|
||||
|
||||
@@ -15,7 +15,7 @@ clone :: proc(s: string, allocator := context.allocator, loc := #caller_location
|
||||
}
|
||||
|
||||
// returns a clone of the string `s` allocated using the `allocator` as a cstring
|
||||
// a nul byte is appended to the clone, to make the cstring safe
|
||||
// a nul byte is appended to the clone, to make the cstring safe
|
||||
clone_to_cstring :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> cstring {
|
||||
c := make([]byte, len(s)+1, allocator, loc)
|
||||
copy(c, s)
|
||||
@@ -37,7 +37,7 @@ string_from_nul_terminated_ptr :: proc(ptr: ^byte, len: int) -> string {
|
||||
return s
|
||||
}
|
||||
|
||||
// returns the raw ^byte start of the string `str`
|
||||
// returns the raw ^byte start of the string `str`
|
||||
ptr_from_string :: proc(str: string) -> ^byte {
|
||||
d := transmute(mem.Raw_String)str
|
||||
return d.data
|
||||
@@ -969,7 +969,7 @@ count :: proc(s, substr: string) -> int {
|
||||
repeats the string `s` multiple `count` times and returns the allocated string
|
||||
panics when `count` is below 0
|
||||
|
||||
strings.repeat("abc", 2) -> "abcabc"
|
||||
strings.repeat("abc", 2) -> "abcabc"
|
||||
*/
|
||||
repeat :: proc(s: string, count: int, allocator := context.allocator) -> string {
|
||||
if count < 0 {
|
||||
@@ -1378,7 +1378,7 @@ split_multi :: proc(s: string, substrs: []string, allocator := context.allocator
|
||||
|
||||
// skip when no results
|
||||
if substrings_found < 1 {
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
buf = make([]string, substrings_found + 1, allocator)
|
||||
@@ -1809,3 +1809,65 @@ fields_iterator :: proc(s: ^string) -> (field: string, ok: bool) {
|
||||
s^ = s[len(s):]
|
||||
return
|
||||
}
|
||||
|
||||
// `levenshtein_distance` returns the Levenshtein edit distance between 2 strings.
|
||||
// This is a single-row-version of the Wagner–Fischer algorithm, based on C code by Martin Ettl.
|
||||
// Note: allocator isn't used if the length of string b in runes is smaller than 64.
|
||||
levenshtein_distance :: proc(a, b: string, allocator := context.allocator) -> int {
|
||||
LEVENSHTEIN_DEFAULT_COSTS: []int : {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
|
||||
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
|
||||
30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
|
||||
40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
|
||||
50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
|
||||
60, 61, 62, 63,
|
||||
}
|
||||
|
||||
m, n := utf8.rune_count_in_string(a), utf8.rune_count_in_string(b)
|
||||
|
||||
if m == 0 {
|
||||
return n
|
||||
}
|
||||
if n == 0 {
|
||||
return m
|
||||
}
|
||||
|
||||
costs: []int
|
||||
|
||||
if n + 1 > len(LEVENSHTEIN_DEFAULT_COSTS) {
|
||||
costs = make([]int, n + 1, allocator)
|
||||
for k in 0..=n {
|
||||
costs[k] = k
|
||||
}
|
||||
} else {
|
||||
costs = LEVENSHTEIN_DEFAULT_COSTS
|
||||
}
|
||||
|
||||
defer if n + 1 > len(LEVENSHTEIN_DEFAULT_COSTS) {
|
||||
delete(costs, allocator)
|
||||
}
|
||||
|
||||
i: int
|
||||
for c1 in a {
|
||||
costs[0] = i + 1
|
||||
corner := i
|
||||
j: int
|
||||
for c2 in b {
|
||||
upper := costs[j + 1]
|
||||
if c1 == c2 {
|
||||
costs[j + 1] = corner
|
||||
} else {
|
||||
t := upper if upper < corner else corner
|
||||
costs[j + 1] = (costs[j] if costs[j] < t else t) + 1
|
||||
}
|
||||
|
||||
corner = upper
|
||||
j += 1
|
||||
}
|
||||
|
||||
i += 1
|
||||
}
|
||||
|
||||
return costs[n]
|
||||
}
|
||||
|
||||
@@ -62,6 +62,13 @@ foreign kernel32 {
|
||||
GetCurrentProcessId :: proc() -> DWORD ---
|
||||
GetCurrentThread :: proc() -> HANDLE ---
|
||||
GetCurrentThreadId :: proc() -> DWORD ---
|
||||
GetProcessTimes :: proc(
|
||||
hProcess: HANDLE,
|
||||
lpCreationTime: LPFILETIME,
|
||||
lpExitTime: LPFILETIME,
|
||||
lpKernelTime: LPFILETIME,
|
||||
lpUserTime: LPFILETIME,
|
||||
) -> BOOL ---
|
||||
GetStdHandle :: proc(which: DWORD) -> HANDLE ---
|
||||
ExitProcess :: proc(uExitCode: c_uint) -> ! ---
|
||||
DeviceIoControl :: proc(
|
||||
|
||||
@@ -60,6 +60,12 @@ foreign user32 {
|
||||
DestroyWindow :: proc(hWnd: HWND) -> BOOL ---
|
||||
|
||||
ShowWindow :: proc(hWnd: HWND, nCmdShow: c_int) -> BOOL ---
|
||||
BringWindowToTop :: proc(hWnd: HWND) -> BOOL ---
|
||||
GetTopWindow :: proc(hWnd: HWND) -> HWND ---
|
||||
SetForegroundWindow :: proc(hWnd: HWND) -> BOOL ---
|
||||
GetForegroundWindow :: proc() -> HWND ---
|
||||
SetActiveWindow :: proc(hWnd: HWND) -> HWND ---
|
||||
GetActiveWindow :: proc() -> HWND ---
|
||||
|
||||
GetMessageA :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) -> BOOL ---
|
||||
GetMessageW :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) -> BOOL ---
|
||||
|
||||
@@ -6,4 +6,5 @@ foreign import winmm "system:Winmm.lib"
|
||||
@(default_calling_convention="stdcall")
|
||||
foreign winmm {
|
||||
timeBeginPeriod :: proc(uPeriod: UINT) -> MMRESULT ---
|
||||
timeEndPeriod :: proc(uPeriod: UINT) -> MMRESULT ---
|
||||
}
|
||||
|
||||
@@ -44,10 +44,10 @@ Pool :: struct {
|
||||
}
|
||||
|
||||
// Once initialized, the pool's memory address is not allowed to change until
|
||||
// it is destroyed. If thread_count < 1, thread count 1 will be used.
|
||||
// it is destroyed.
|
||||
//
|
||||
// The thread pool requires an allocator which it either owns, or which is thread safe.
|
||||
pool_init :: proc(pool: ^Pool, thread_count: int, allocator: mem.Allocator) {
|
||||
pool_init :: proc(pool: ^Pool, allocator: mem.Allocator, thread_count: int) {
|
||||
context.allocator = allocator
|
||||
pool.allocator = allocator
|
||||
pool.tasks = make([dynamic]Task)
|
||||
@@ -113,9 +113,8 @@ pool_join :: proc(pool: ^Pool) {
|
||||
// the thread pool. You can even add tasks from inside other tasks.
|
||||
//
|
||||
// Each task also needs an allocator which it either owns, or which is thread
|
||||
// safe. By default, allocations in the task are disabled by use of the
|
||||
// nil_allocator.
|
||||
pool_add_task :: proc(pool: ^Pool, procedure: Task_Proc, data: rawptr, user_index: int = 0, allocator := context.allocator) {
|
||||
// safe.
|
||||
pool_add_task :: proc(pool: ^Pool, allocator: mem.Allocator, procedure: Task_Proc, data: rawptr, user_index: int = 0) {
|
||||
sync.guard(&pool.mutex)
|
||||
|
||||
append(&pool.tasks, Task{
|
||||
|
||||
@@ -10,6 +10,7 @@ import c "core:c"
|
||||
import libc "core:c/libc"
|
||||
|
||||
import compress "core:compress"
|
||||
import shoco "core:compress/shoco"
|
||||
import gzip "core:compress/gzip"
|
||||
import zlib "core:compress/zlib"
|
||||
|
||||
@@ -61,6 +62,7 @@ import hash "core:hash"
|
||||
|
||||
import image "core:image"
|
||||
import png "core:image/png"
|
||||
import qoi "core:image/qoi"
|
||||
|
||||
import io "core:io"
|
||||
import log "core:log"
|
||||
@@ -114,6 +116,7 @@ _ :: bytes
|
||||
_ :: c
|
||||
_ :: libc
|
||||
_ :: compress
|
||||
_ :: shoco
|
||||
_ :: gzip
|
||||
_ :: zlib
|
||||
_ :: bit_array
|
||||
@@ -159,6 +162,7 @@ _ :: fmt
|
||||
_ :: hash
|
||||
_ :: image
|
||||
_ :: png
|
||||
_ :: qoi
|
||||
_ :: io
|
||||
_ :: log
|
||||
_ :: math
|
||||
|
||||
@@ -1166,7 +1166,8 @@ threading_example :: proc() {
|
||||
|
||||
|
||||
for i in 0..<30 {
|
||||
thread.pool_add_task(pool=&pool, procedure=task_proc, data=nil, user_index=i)
|
||||
// be mindful of the allocator used for tasks. The allocator needs to be thread safe, or be owned by the task for exclusive use
|
||||
thread.pool_add_task(pool=&pool, procedure=task_proc, data=nil, user_index=i, allocator=context.allocator)
|
||||
}
|
||||
|
||||
thread.pool_start(&pool)
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
|
||||
// #if defined(GB_SYSTEM_WINDOWS)
|
||||
// #define DEFAULT_TO_THREADED_CHECKER
|
||||
// #endif
|
||||
@@ -198,6 +197,22 @@ enum RelocMode : u8 {
|
||||
RelocMode_DynamicNoPIC,
|
||||
};
|
||||
|
||||
enum BuildPath : u8 {
|
||||
BuildPath_Main_Package, // Input Path to the package directory (or file) we're building.
|
||||
BuildPath_RC, // Input Path for .rc file, can be set with `-resource:`.
|
||||
BuildPath_RES, // Output Path for .res file, generated from previous.
|
||||
BuildPath_Win_SDK_Root, // windows_sdk_root
|
||||
BuildPath_Win_SDK_UM_Lib, // windows_sdk_um_library_path
|
||||
BuildPath_Win_SDK_UCRT_Lib, // windows_sdk_ucrt_library_path
|
||||
BuildPath_VS_EXE, // vs_exe_path
|
||||
BuildPath_VS_LIB, // vs_library_path
|
||||
|
||||
BuildPath_Output, // Output Path for .exe, .dll, .so, etc. Can be overridden with `-out:`.
|
||||
BuildPath_PDB, // Output Path for .pdb file, can be overridden with `-pdb-name:`.
|
||||
|
||||
BuildPathCOUNT,
|
||||
};
|
||||
|
||||
// This stores the information for the specify architecture of this build
|
||||
struct BuildContext {
|
||||
// Constants
|
||||
@@ -226,9 +241,13 @@ struct BuildContext {
|
||||
|
||||
bool show_help;
|
||||
|
||||
Array<Path> build_paths; // Contains `Path` objects to output filename, pdb, resource and intermediate files.
|
||||
// BuildPath enum contains the indices of paths we know *before* the work starts.
|
||||
|
||||
String out_filepath;
|
||||
String resource_filepath;
|
||||
String pdb_filepath;
|
||||
|
||||
bool has_resource;
|
||||
String link_flags;
|
||||
String extra_linker_flags;
|
||||
@@ -300,8 +319,6 @@ struct BuildContext {
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
gb_global BuildContext build_context = {0};
|
||||
|
||||
bool global_warnings_as_errors(void) {
|
||||
@@ -605,28 +622,6 @@ bool allow_check_foreign_filepath(void) {
|
||||
// is_abs_path
|
||||
// has_subdir
|
||||
|
||||
enum TargetFileValidity : u8 {
|
||||
TargetFileValidity_Invalid,
|
||||
|
||||
TargetFileValidity_Writable_File,
|
||||
TargetFileValidity_No_Write_Permission,
|
||||
TargetFileValidity_Directory,
|
||||
|
||||
TargetTargetFileValidity_COUNT,
|
||||
};
|
||||
|
||||
TargetFileValidity set_output_filename(void) {
|
||||
// Assembles the output filename from build_context information.
|
||||
// Returns `true` if it doesn't exist or is a file.
|
||||
// Returns `false` if a directory or write-protected file.
|
||||
|
||||
|
||||
|
||||
|
||||
return TargetFileValidity_Writable_File;
|
||||
}
|
||||
|
||||
|
||||
String const WIN32_SEPARATOR_STRING = {cast(u8 *)"\\", 1};
|
||||
String const NIX_SEPARATOR_STRING = {cast(u8 *)"/", 1};
|
||||
|
||||
@@ -973,7 +968,6 @@ char *token_pos_to_string(TokenPos const &pos) {
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
void init_build_context(TargetMetrics *cross_target) {
|
||||
BuildContext *bc = &build_context;
|
||||
|
||||
@@ -1152,8 +1146,194 @@ void init_build_context(TargetMetrics *cross_target) {
|
||||
|
||||
bc->optimization_level = gb_clamp(bc->optimization_level, 0, 3);
|
||||
|
||||
|
||||
|
||||
#undef LINK_FLAG_X64
|
||||
#undef LINK_FLAG_386
|
||||
}
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
// NOTE(IC): In order to find Visual C++ paths without relying on environment variables.
|
||||
// NOTE(Jeroen): No longer needed in `main.cpp -> linker_stage`. We now resolve those paths in `init_build_paths`.
|
||||
#include "microsoft_craziness.h"
|
||||
#endif
|
||||
|
||||
// NOTE(Jeroen): Set/create the output and other paths and report an error as appropriate.
|
||||
// We've previously called `parse_build_flags`, so `out_filepath` should be set.
|
||||
bool init_build_paths(String init_filename) {
|
||||
gbAllocator ha = heap_allocator();
|
||||
BuildContext *bc = &build_context;
|
||||
|
||||
// NOTE(Jeroen): We're pre-allocating BuildPathCOUNT slots so that certain paths are always at the same enumerated index.
|
||||
array_init(&bc->build_paths, permanent_allocator(), BuildPathCOUNT);
|
||||
|
||||
// [BuildPathMainPackage] Turn given init path into a `Path`, which includes normalizing it into a full path.
|
||||
bc->build_paths[BuildPath_Main_Package] = path_from_string(ha, init_filename);
|
||||
|
||||
bool produces_output_file = false;
|
||||
if (bc->command_kind == Command_doc && bc->cmd_doc_flags & CmdDocFlag_DocFormat) {
|
||||
produces_output_file = true;
|
||||
} else if (bc->command_kind & Command__does_build) {
|
||||
produces_output_file = true;
|
||||
}
|
||||
|
||||
if (!produces_output_file) {
|
||||
// Command doesn't produce output files. We're done.
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
if (bc->resource_filepath.len > 0) {
|
||||
bc->build_paths[BuildPath_RC] = path_from_string(ha, bc->resource_filepath);
|
||||
bc->build_paths[BuildPath_RES] = path_from_string(ha, bc->resource_filepath);
|
||||
bc->build_paths[BuildPath_RC].ext = copy_string(ha, STR_LIT("rc"));
|
||||
bc->build_paths[BuildPath_RES].ext = copy_string(ha, STR_LIT("res"));
|
||||
}
|
||||
|
||||
if (bc->pdb_filepath.len > 0) {
|
||||
bc->build_paths[BuildPath_PDB] = path_from_string(ha, bc->pdb_filepath);
|
||||
}
|
||||
|
||||
if ((bc->command_kind & Command__does_build) && (!bc->ignore_microsoft_magic)) {
|
||||
// NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest.
|
||||
Find_Result_Utf8 find_result = find_visual_studio_and_windows_sdk_utf8();
|
||||
|
||||
if (find_result.windows_sdk_version == 0) {
|
||||
gb_printf_err("Windows SDK not found.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
GB_ASSERT(find_result.windows_sdk_um_library_path.len > 0);
|
||||
GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0);
|
||||
|
||||
if (find_result.windows_sdk_root.len > 0) {
|
||||
bc->build_paths[BuildPath_Win_SDK_Root] = path_from_string(ha, find_result.windows_sdk_root);
|
||||
}
|
||||
|
||||
if (find_result.windows_sdk_um_library_path.len > 0) {
|
||||
bc->build_paths[BuildPath_Win_SDK_UM_Lib] = path_from_string(ha, find_result.windows_sdk_um_library_path);
|
||||
}
|
||||
|
||||
if (find_result.windows_sdk_ucrt_library_path.len > 0) {
|
||||
bc->build_paths[BuildPath_Win_SDK_UCRT_Lib] = path_from_string(ha, find_result.windows_sdk_ucrt_library_path);
|
||||
}
|
||||
|
||||
if (find_result.vs_exe_path.len > 0) {
|
||||
bc->build_paths[BuildPath_VS_EXE] = path_from_string(ha, find_result.vs_exe_path);
|
||||
}
|
||||
|
||||
if (find_result.vs_library_path.len > 0) {
|
||||
bc->build_paths[BuildPath_VS_LIB] = path_from_string(ha, find_result.vs_library_path);
|
||||
}
|
||||
|
||||
gb_free(ha, find_result.windows_sdk_root.text);
|
||||
gb_free(ha, find_result.windows_sdk_um_library_path.text);
|
||||
gb_free(ha, find_result.windows_sdk_ucrt_library_path.text);
|
||||
gb_free(ha, find_result.vs_exe_path.text);
|
||||
gb_free(ha, find_result.vs_library_path.text);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
// All the build targets and OSes.
|
||||
String output_extension;
|
||||
|
||||
if (bc->command_kind == Command_doc && bc->cmd_doc_flags & CmdDocFlag_DocFormat) {
|
||||
output_extension = STR_LIT("odin-doc");
|
||||
} else if (is_arch_wasm()) {
|
||||
output_extension = STR_LIT("wasm");
|
||||
} else if (build_context.build_mode == BuildMode_Executable) {
|
||||
// By default use a .bin executable extension.
|
||||
output_extension = STR_LIT("bin");
|
||||
|
||||
if (build_context.metrics.os == TargetOs_windows) {
|
||||
output_extension = STR_LIT("exe");
|
||||
} else if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
|
||||
output_extension = make_string(nullptr, 0);
|
||||
}
|
||||
} else if (build_context.build_mode == BuildMode_DynamicLibrary) {
|
||||
// By default use a .so shared library extension.
|
||||
output_extension = STR_LIT("so");
|
||||
|
||||
if (build_context.metrics.os == TargetOs_windows) {
|
||||
output_extension = STR_LIT("dll");
|
||||
} else if (build_context.metrics.os == TargetOs_darwin) {
|
||||
output_extension = STR_LIT("dylib");
|
||||
}
|
||||
} else if (build_context.build_mode == BuildMode_Object) {
|
||||
// By default use a .o object extension.
|
||||
output_extension = STR_LIT("o");
|
||||
|
||||
if (build_context.metrics.os == TargetOs_windows) {
|
||||
output_extension = STR_LIT("obj");
|
||||
}
|
||||
} else if (build_context.build_mode == BuildMode_Assembly) {
|
||||
// By default use a .S asm extension.
|
||||
output_extension = STR_LIT("S");
|
||||
} else if (build_context.build_mode == BuildMode_LLVM_IR) {
|
||||
output_extension = STR_LIT("ll");
|
||||
} else {
|
||||
GB_PANIC("Unhandled build mode/target combination.\n");
|
||||
}
|
||||
|
||||
if (bc->out_filepath.len > 0) {
|
||||
bc->build_paths[BuildPath_Output] = path_from_string(ha, bc->out_filepath);
|
||||
if (build_context.metrics.os == TargetOs_windows) {
|
||||
String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
|
||||
defer (gb_free(ha, output_file.text));
|
||||
if (path_is_directory(bc->build_paths[BuildPath_Output])) {
|
||||
gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file));
|
||||
return false;
|
||||
} else if (bc->build_paths[BuildPath_Output].ext.len == 0) {
|
||||
gb_printf_err("Output path %.*s must have an appropriate extension.\n", LIT(output_file));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Path output_path;
|
||||
|
||||
if (str_eq(init_filename, str_lit("."))) {
|
||||
// We must name the output file after the current directory.
|
||||
debugf("Output name will be created from current base name %.*s.\n", LIT(bc->build_paths[BuildPath_Main_Package].basename));
|
||||
String last_element = last_path_element(bc->build_paths[BuildPath_Main_Package].basename);
|
||||
|
||||
if (last_element.len == 0) {
|
||||
gb_printf_err("The output name is created from the last path element. `%.*s` has none. Use `-out:output_name.ext` to set it.\n", LIT(bc->build_paths[BuildPath_Main_Package].basename));
|
||||
return false;
|
||||
}
|
||||
output_path.basename = copy_string(ha, bc->build_paths[BuildPath_Main_Package].basename);
|
||||
output_path.name = copy_string(ha, last_element);
|
||||
|
||||
} else {
|
||||
// Init filename was not 'current path'.
|
||||
// Contruct the output name from the path elements as usual.
|
||||
String output_name = remove_directory_from_path(init_filename);
|
||||
output_name = remove_extension_from_path(output_name);
|
||||
output_name = copy_string(ha, string_trim_whitespace(output_name));
|
||||
output_path = path_from_string(ha, output_name);
|
||||
|
||||
// Replace extension.
|
||||
if (output_path.ext.len > 0) {
|
||||
gb_free(ha, output_path.ext.text);
|
||||
}
|
||||
}
|
||||
output_path.ext = copy_string(ha, output_extension);
|
||||
|
||||
bc->build_paths[BuildPath_Output] = output_path;
|
||||
}
|
||||
|
||||
// Do we have an extension? We might not if the output filename was supplied.
|
||||
if (bc->build_paths[BuildPath_Output].ext.len == 0) {
|
||||
if (build_context.metrics.os == TargetOs_windows || build_context.build_mode != BuildMode_Executable) {
|
||||
bc->build_paths[BuildPath_Output].ext = copy_string(ha, output_extension);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if output path is a directory.
|
||||
if (path_is_directory(bc->build_paths[BuildPath_Output])) {
|
||||
String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
|
||||
defer (gb_free(ha, output_file.text));
|
||||
gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
257
src/common.cpp
257
src/common.cpp
@@ -675,262 +675,7 @@ wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc) {
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
bool path_is_directory(String path) {
|
||||
gbAllocator a = heap_allocator();
|
||||
String16 wstr = string_to_string16(a, path);
|
||||
defer (gb_free(a, wstr.text));
|
||||
|
||||
i32 attribs = GetFileAttributesW(wstr.text);
|
||||
if (attribs < 0) return false;
|
||||
|
||||
return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
}
|
||||
|
||||
#else
|
||||
bool path_is_directory(String path) {
|
||||
gbAllocator a = heap_allocator();
|
||||
char *copy = cast(char *)copy_string(a, path).text;
|
||||
defer (gb_free(a, copy));
|
||||
|
||||
struct stat s;
|
||||
if (stat(copy, &s) == 0) {
|
||||
return (s.st_mode & S_IFDIR) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
String path_to_full_path(gbAllocator a, String path) {
|
||||
gbAllocator ha = heap_allocator();
|
||||
char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len);
|
||||
defer (gb_free(ha, path_c));
|
||||
|
||||
char *fullpath = gb_path_get_full_name(a, path_c);
|
||||
String res = string_trim_whitespace(make_string_c(fullpath));
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
for (isize i = 0; i < res.len; i++) {
|
||||
if (res.text[i] == '\\') {
|
||||
res.text[i] = '/';
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct FileInfo {
|
||||
String name;
|
||||
String fullpath;
|
||||
i64 size;
|
||||
bool is_dir;
|
||||
};
|
||||
|
||||
enum ReadDirectoryError {
|
||||
ReadDirectory_None,
|
||||
|
||||
ReadDirectory_InvalidPath,
|
||||
ReadDirectory_NotExists,
|
||||
ReadDirectory_Permission,
|
||||
ReadDirectory_NotDir,
|
||||
ReadDirectory_Empty,
|
||||
ReadDirectory_Unknown,
|
||||
|
||||
ReadDirectory_COUNT,
|
||||
};
|
||||
|
||||
i64 get_file_size(String path) {
|
||||
char *c_str = alloc_cstring(heap_allocator(), path);
|
||||
defer (gb_free(heap_allocator(), c_str));
|
||||
|
||||
gbFile f = {};
|
||||
gbFileError err = gb_file_open(&f, c_str);
|
||||
defer (gb_file_close(&f));
|
||||
if (err != gbFileError_None) {
|
||||
return -1;
|
||||
}
|
||||
return gb_file_size(&f);
|
||||
}
|
||||
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
|
||||
GB_ASSERT(fi != nullptr);
|
||||
|
||||
gbAllocator a = heap_allocator();
|
||||
|
||||
while (path.len > 0) {
|
||||
Rune end = path[path.len-1];
|
||||
if (end == '/') {
|
||||
path.len -= 1;
|
||||
} else if (end == '\\') {
|
||||
path.len -= 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (path.len == 0) {
|
||||
return ReadDirectory_InvalidPath;
|
||||
}
|
||||
{
|
||||
char *c_str = alloc_cstring(a, path);
|
||||
defer (gb_free(a, c_str));
|
||||
|
||||
gbFile f = {};
|
||||
gbFileError file_err = gb_file_open(&f, c_str);
|
||||
defer (gb_file_close(&f));
|
||||
|
||||
switch (file_err) {
|
||||
case gbFileError_Invalid: return ReadDirectory_InvalidPath;
|
||||
case gbFileError_NotExists: return ReadDirectory_NotExists;
|
||||
// case gbFileError_Permission: return ReadDirectory_Permission;
|
||||
}
|
||||
}
|
||||
|
||||
if (!path_is_directory(path)) {
|
||||
return ReadDirectory_NotDir;
|
||||
}
|
||||
|
||||
|
||||
char *new_path = gb_alloc_array(a, char, path.len+3);
|
||||
defer (gb_free(a, new_path));
|
||||
|
||||
gb_memmove(new_path, path.text, path.len);
|
||||
gb_memmove(new_path+path.len, "/*", 2);
|
||||
new_path[path.len+2] = 0;
|
||||
|
||||
String np = make_string(cast(u8 *)new_path, path.len+2);
|
||||
String16 wstr = string_to_string16(a, np);
|
||||
defer (gb_free(a, wstr.text));
|
||||
|
||||
WIN32_FIND_DATAW file_data = {};
|
||||
HANDLE find_file = FindFirstFileW(wstr.text, &file_data);
|
||||
if (find_file == INVALID_HANDLE_VALUE) {
|
||||
return ReadDirectory_Unknown;
|
||||
}
|
||||
defer (FindClose(find_file));
|
||||
|
||||
array_init(fi, a, 0, 100);
|
||||
|
||||
do {
|
||||
wchar_t *filename_w = file_data.cFileName;
|
||||
i64 size = cast(i64)file_data.nFileSizeLow;
|
||||
size |= (cast(i64)file_data.nFileSizeHigh) << 32;
|
||||
String name = string16_to_string(a, make_string16_c(filename_w));
|
||||
if (name == "." || name == "..") {
|
||||
gb_free(a, name.text);
|
||||
continue;
|
||||
}
|
||||
|
||||
String filepath = {};
|
||||
filepath.len = path.len+1+name.len;
|
||||
filepath.text = gb_alloc_array(a, u8, filepath.len+1);
|
||||
defer (gb_free(a, filepath.text));
|
||||
gb_memmove(filepath.text, path.text, path.len);
|
||||
gb_memmove(filepath.text+path.len, "/", 1);
|
||||
gb_memmove(filepath.text+path.len+1, name.text, name.len);
|
||||
|
||||
FileInfo info = {};
|
||||
info.name = name;
|
||||
info.fullpath = path_to_full_path(a, filepath);
|
||||
info.size = size;
|
||||
info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
array_add(fi, info);
|
||||
} while (FindNextFileW(find_file, &file_data));
|
||||
|
||||
if (fi->count == 0) {
|
||||
return ReadDirectory_Empty;
|
||||
}
|
||||
|
||||
return ReadDirectory_None;
|
||||
}
|
||||
#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
|
||||
GB_ASSERT(fi != nullptr);
|
||||
|
||||
gbAllocator a = heap_allocator();
|
||||
|
||||
char *c_path = alloc_cstring(a, path);
|
||||
defer (gb_free(a, c_path));
|
||||
|
||||
DIR *dir = opendir(c_path);
|
||||
if (!dir) {
|
||||
switch (errno) {
|
||||
case ENOENT:
|
||||
return ReadDirectory_NotExists;
|
||||
case EACCES:
|
||||
return ReadDirectory_Permission;
|
||||
case ENOTDIR:
|
||||
return ReadDirectory_NotDir;
|
||||
default:
|
||||
// ENOMEM: out of memory
|
||||
// EMFILE: per-process limit on open fds reached
|
||||
// ENFILE: system-wide limit on total open files reached
|
||||
return ReadDirectory_Unknown;
|
||||
}
|
||||
GB_PANIC("unreachable");
|
||||
}
|
||||
|
||||
array_init(fi, a, 0, 100);
|
||||
|
||||
for (;;) {
|
||||
struct dirent *entry = readdir(dir);
|
||||
if (entry == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
String name = make_string_c(entry->d_name);
|
||||
if (name == "." || name == "..") {
|
||||
continue;
|
||||
}
|
||||
|
||||
String filepath = {};
|
||||
filepath.len = path.len+1+name.len;
|
||||
filepath.text = gb_alloc_array(a, u8, filepath.len+1);
|
||||
defer (gb_free(a, filepath.text));
|
||||
gb_memmove(filepath.text, path.text, path.len);
|
||||
gb_memmove(filepath.text+path.len, "/", 1);
|
||||
gb_memmove(filepath.text+path.len+1, name.text, name.len);
|
||||
filepath.text[filepath.len] = 0;
|
||||
|
||||
|
||||
struct stat dir_stat = {};
|
||||
|
||||
if (stat((char *)filepath.text, &dir_stat)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (S_ISDIR(dir_stat.st_mode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
i64 size = dir_stat.st_size;
|
||||
|
||||
FileInfo info = {};
|
||||
info.name = name;
|
||||
info.fullpath = path_to_full_path(a, filepath);
|
||||
info.size = size;
|
||||
array_add(fi, info);
|
||||
}
|
||||
|
||||
if (fi->count == 0) {
|
||||
return ReadDirectory_Empty;
|
||||
}
|
||||
|
||||
return ReadDirectory_None;
|
||||
}
|
||||
#else
|
||||
#error Implement read_directory
|
||||
#endif
|
||||
|
||||
|
||||
#include "path.cpp"
|
||||
|
||||
struct LoadedFile {
|
||||
void *handle;
|
||||
|
||||
50
src/gb/gb.h
50
src/gb/gb.h
@@ -6273,20 +6273,44 @@ char *gb_path_get_full_name(gbAllocator a, char const *path) {
|
||||
#else
|
||||
char *p, *result, *fullpath = NULL;
|
||||
isize len;
|
||||
p = realpath(path, NULL);
|
||||
fullpath = p;
|
||||
if (p == NULL) {
|
||||
// NOTE(bill): File does not exist
|
||||
fullpath = cast(char *)path;
|
||||
fullpath = realpath(path, NULL);
|
||||
|
||||
if (fullpath == NULL) {
|
||||
// NOTE(Jeroen): Path doesn't exist.
|
||||
if (gb_strlen(path) > 0 && path[0] == '/') {
|
||||
// But it is an absolute path, so return as-is.
|
||||
|
||||
fullpath = (char *)path;
|
||||
len = gb_strlen(fullpath) + 1;
|
||||
result = gb_alloc_array(a, char, len + 1);
|
||||
|
||||
gb_memmove(result, fullpath, len);
|
||||
result[len] = 0;
|
||||
|
||||
} else {
|
||||
// Appears to be a relative path, so construct an absolute one relative to <cwd>.
|
||||
char cwd[4096];
|
||||
getcwd(&cwd[0], 4096);
|
||||
|
||||
isize path_len = gb_strlen(path);
|
||||
isize cwd_len = gb_strlen(cwd);
|
||||
len = cwd_len + 1 + path_len + 1;
|
||||
result = gb_alloc_array(a, char, len);
|
||||
|
||||
gb_memmove(result, (void *)cwd, cwd_len);
|
||||
result[cwd_len] = '/';
|
||||
|
||||
gb_memmove(result + cwd_len + 1, (void *)path, gb_strlen(path));
|
||||
result[len] = 0;
|
||||
|
||||
}
|
||||
} else {
|
||||
len = gb_strlen(fullpath) + 1;
|
||||
result = gb_alloc_array(a, char, len + 1);
|
||||
gb_memmove(result, fullpath, len);
|
||||
result[len] = 0;
|
||||
free(fullpath);
|
||||
}
|
||||
|
||||
len = gb_strlen(fullpath);
|
||||
|
||||
result = gb_alloc_array(a, char, len + 1);
|
||||
gb_memmove(result, fullpath, len);
|
||||
result[len] = 0;
|
||||
free(p);
|
||||
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -967,7 +967,12 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime)
|
||||
}
|
||||
|
||||
String lb_filepath_ll_for_module(lbModule *m) {
|
||||
String path = m->gen->output_base;
|
||||
String path = concatenate3_strings(permanent_allocator(),
|
||||
build_context.build_paths[BuildPath_Output].basename,
|
||||
STR_LIT("/"),
|
||||
build_context.build_paths[BuildPath_Output].name
|
||||
);
|
||||
|
||||
if (m->pkg) {
|
||||
path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
|
||||
} else if (USE_SEPARATE_MODULES) {
|
||||
@@ -978,7 +983,12 @@ String lb_filepath_ll_for_module(lbModule *m) {
|
||||
return path;
|
||||
}
|
||||
String lb_filepath_obj_for_module(lbModule *m) {
|
||||
String path = m->gen->output_base;
|
||||
String path = concatenate3_strings(permanent_allocator(),
|
||||
build_context.build_paths[BuildPath_Output].basename,
|
||||
STR_LIT("/"),
|
||||
build_context.build_paths[BuildPath_Output].name
|
||||
);
|
||||
|
||||
if (m->pkg) {
|
||||
path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
|
||||
}
|
||||
|
||||
@@ -87,7 +87,6 @@ bool lb_init_generator(lbGenerator *gen, Checker *c) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
String init_fullpath = c->parser->init_fullpath;
|
||||
|
||||
if (build_context.out_filepath.len == 0) {
|
||||
|
||||
152
src/main.cpp
152
src/main.cpp
@@ -46,7 +46,6 @@ gb_global Timings global_timings = {0};
|
||||
#include "checker.cpp"
|
||||
#include "docs.cpp"
|
||||
|
||||
|
||||
#include "llvm_backend.cpp"
|
||||
|
||||
#if defined(GB_SYSTEM_OSX)
|
||||
@@ -57,16 +56,8 @@ gb_global Timings global_timings = {0};
|
||||
#endif
|
||||
|
||||
#include "query_data.cpp"
|
||||
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
// NOTE(IC): In order to find Visual C++ paths without relying on environment variables.
|
||||
#include "microsoft_craziness.h"
|
||||
#endif
|
||||
|
||||
#include "bug_report.cpp"
|
||||
|
||||
|
||||
// NOTE(bill): 'name' is used in debugging and profiling modes
|
||||
i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
|
||||
isize const cmd_cap = 64<<20; // 64 MiB should be more than enough
|
||||
@@ -130,34 +121,35 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
i32 linker_stage(lbGenerator *gen) {
|
||||
i32 result = 0;
|
||||
Timings *timings = &global_timings;
|
||||
|
||||
String output_base = gen->output_base;
|
||||
String output_filename = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]);
|
||||
debugf("Linking %.*s\n", LIT(output_filename));
|
||||
|
||||
// TOOD(Jeroen): Make a `build_paths[BuildPath_Object] to avoid `%.*s.o`.
|
||||
|
||||
if (is_arch_wasm()) {
|
||||
timings_start_section(timings, str_lit("wasm-ld"));
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
result = system_exec_command_line_app("wasm-ld",
|
||||
"\"%.*s\\bin\\wasm-ld\" \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s",
|
||||
"\"%.*s\\bin\\wasm-ld\" \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
|
||||
LIT(build_context.ODIN_ROOT),
|
||||
LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
|
||||
LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
|
||||
#else
|
||||
result = system_exec_command_line_app("wasm-ld",
|
||||
"wasm-ld \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s",
|
||||
LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
|
||||
"wasm-ld \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
|
||||
LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
|
||||
#ifdef GB_SYSTEM_UNIX
|
||||
#if defined(GB_SYSTEM_UNIX)
|
||||
result = system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
|
||||
LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
|
||||
LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
|
||||
#else
|
||||
gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
|
||||
LIT(target_os_names[build_context.metrics.os]),
|
||||
@@ -181,28 +173,11 @@ i32 linker_stage(lbGenerator *gen) {
|
||||
gbString lib_str = gb_string_make(heap_allocator(), "");
|
||||
defer (gb_string_free(lib_str));
|
||||
|
||||
char const *output_ext = "exe";
|
||||
gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
|
||||
defer (gb_string_free(link_settings));
|
||||
|
||||
|
||||
// NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest.
|
||||
Find_Result_Utf8 find_result = find_visual_studio_and_windows_sdk_utf8();
|
||||
|
||||
if (find_result.windows_sdk_version == 0) {
|
||||
gb_printf_err("Windows SDK not found.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (build_context.ignore_microsoft_magic) {
|
||||
find_result = {};
|
||||
}
|
||||
|
||||
// Add library search paths.
|
||||
if (find_result.vs_library_path.len > 0) {
|
||||
GB_ASSERT(find_result.windows_sdk_um_library_path.len > 0);
|
||||
GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0);
|
||||
|
||||
if (build_context.build_paths[BuildPath_VS_LIB].basename.len > 0) {
|
||||
String path = {};
|
||||
auto add_path = [&](String path) {
|
||||
if (path[path.len-1] == '\\') {
|
||||
@@ -210,9 +185,9 @@ i32 linker_stage(lbGenerator *gen) {
|
||||
}
|
||||
link_settings = gb_string_append_fmt(link_settings, " /LIBPATH:\"%.*s\"", LIT(path));
|
||||
};
|
||||
add_path(find_result.windows_sdk_um_library_path);
|
||||
add_path(find_result.windows_sdk_ucrt_library_path);
|
||||
add_path(find_result.vs_library_path);
|
||||
add_path(build_context.build_paths[BuildPath_Win_SDK_UM_Lib].basename);
|
||||
add_path(build_context.build_paths[BuildPath_Win_SDK_UCRT_Lib].basename);
|
||||
add_path(build_context.build_paths[BuildPath_VS_LIB].basename);
|
||||
}
|
||||
|
||||
|
||||
@@ -252,14 +227,14 @@ i32 linker_stage(lbGenerator *gen) {
|
||||
|
||||
|
||||
if (build_context.build_mode == BuildMode_DynamicLibrary) {
|
||||
output_ext = "dll";
|
||||
link_settings = gb_string_append_fmt(link_settings, " /DLL");
|
||||
} else {
|
||||
link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup");
|
||||
}
|
||||
|
||||
if (build_context.pdb_filepath != "") {
|
||||
link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(build_context.pdb_filepath));
|
||||
String pdb_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_PDB]);
|
||||
link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(pdb_path));
|
||||
}
|
||||
|
||||
if (build_context.no_crt) {
|
||||
@@ -300,13 +275,21 @@ i32 linker_stage(lbGenerator *gen) {
|
||||
object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
|
||||
}
|
||||
|
||||
String vs_exe_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_VS_EXE]);
|
||||
defer (gb_free(heap_allocator(), vs_exe_path.text));
|
||||
|
||||
char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
|
||||
if (!build_context.use_lld) { // msvc
|
||||
if (build_context.has_resource) {
|
||||
String rc_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]);
|
||||
String res_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RES]);
|
||||
defer (gb_free(heap_allocator(), rc_path.text));
|
||||
defer (gb_free(heap_allocator(), res_path.text));
|
||||
|
||||
result = system_exec_command_line_app("msvc-link",
|
||||
"\"rc.exe\" /nologo /fo \"%.*s.res\" \"%.*s.rc\"",
|
||||
LIT(output_base),
|
||||
LIT(build_context.resource_filepath)
|
||||
"\"rc.exe\" /nologo /fo \"%.*s\" \"%.*s\"",
|
||||
LIT(res_path),
|
||||
LIT(rc_path)
|
||||
);
|
||||
|
||||
if (result) {
|
||||
@@ -314,13 +297,13 @@ i32 linker_stage(lbGenerator *gen) {
|
||||
}
|
||||
|
||||
result = system_exec_command_line_app("msvc-link",
|
||||
"\"%.*slink.exe\" %s \"%.*s.res\" -OUT:\"%.*s.%s\" %s "
|
||||
"\"%.*slink.exe\" %s \"%.*s\" -OUT:\"%.*s\" %s "
|
||||
"/nologo /incremental:no /opt:ref /subsystem:%s "
|
||||
" %.*s "
|
||||
" %.*s "
|
||||
" %s "
|
||||
"",
|
||||
LIT(find_result.vs_exe_path), object_files, LIT(output_base), LIT(output_base), output_ext,
|
||||
LIT(vs_exe_path), object_files, LIT(res_path), LIT(output_filename),
|
||||
link_settings,
|
||||
subsystem_str,
|
||||
LIT(build_context.link_flags),
|
||||
@@ -329,13 +312,13 @@ i32 linker_stage(lbGenerator *gen) {
|
||||
);
|
||||
} else {
|
||||
result = system_exec_command_line_app("msvc-link",
|
||||
"\"%.*slink.exe\" %s -OUT:\"%.*s.%s\" %s "
|
||||
"\"%.*slink.exe\" %s -OUT:\"%.*s\" %s "
|
||||
"/nologo /incremental:no /opt:ref /subsystem:%s "
|
||||
" %.*s "
|
||||
" %.*s "
|
||||
" %s "
|
||||
"",
|
||||
LIT(find_result.vs_exe_path), object_files, LIT(output_base), output_ext,
|
||||
LIT(vs_exe_path), object_files, LIT(output_filename),
|
||||
link_settings,
|
||||
subsystem_str,
|
||||
LIT(build_context.link_flags),
|
||||
@@ -350,13 +333,13 @@ i32 linker_stage(lbGenerator *gen) {
|
||||
|
||||
} else { // lld
|
||||
result = system_exec_command_line_app("msvc-lld-link",
|
||||
"\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s.%s\" %s "
|
||||
"\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s "
|
||||
"/nologo /incremental:no /opt:ref /subsystem:%s "
|
||||
" %.*s "
|
||||
" %.*s "
|
||||
" %s "
|
||||
"",
|
||||
LIT(build_context.ODIN_ROOT), object_files, LIT(output_base),output_ext,
|
||||
LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename),
|
||||
link_settings,
|
||||
subsystem_str,
|
||||
LIT(build_context.link_flags),
|
||||
@@ -415,7 +398,7 @@ i32 linker_stage(lbGenerator *gen) {
|
||||
} else if (string_ends_with(lib, str_lit(".so"))) {
|
||||
// dynamic lib, relative path to executable
|
||||
// NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
|
||||
// at runtimeto the executable
|
||||
// at runtime to the executable
|
||||
lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, LIT(lib));
|
||||
} else {
|
||||
// dynamic or static system lib, just link regularly searching system library paths
|
||||
@@ -431,9 +414,6 @@ i32 linker_stage(lbGenerator *gen) {
|
||||
object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
|
||||
}
|
||||
|
||||
// Unlike the Win32 linker code, the output_ext includes the dot, because
|
||||
// typically executable files on *NIX systems don't have extensions.
|
||||
String output_ext = {};
|
||||
gbString link_settings = gb_string_make_reserve(heap_allocator(), 32);
|
||||
|
||||
if (build_context.no_crt) {
|
||||
@@ -461,26 +441,12 @@ i32 linker_stage(lbGenerator *gen) {
|
||||
// correctly this way since all the other dependencies provided implicitly
|
||||
// by the compiler frontend are still needed and most of the command
|
||||
// line arguments prepared previously are incompatible with ld.
|
||||
//
|
||||
// Shared libraries are .dylib on MacOS and .so on Linux.
|
||||
if (build_context.metrics.os == TargetOs_darwin) {
|
||||
output_ext = STR_LIT(".dylib");
|
||||
} else {
|
||||
output_ext = STR_LIT(".so");
|
||||
}
|
||||
link_settings = gb_string_appendc(link_settings, "-Wl,-init,'_odin_entry_point' ");
|
||||
link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
|
||||
} else if (build_context.metrics.os != TargetOs_openbsd) {
|
||||
// OpenBSD defaults to PIE executable. do not pass -no-pie for it.
|
||||
link_settings = gb_string_appendc(link_settings, "-no-pie ");
|
||||
}
|
||||
if (build_context.out_filepath.len > 0) {
|
||||
//NOTE(thebirk): We have a custom -out arguments, so we should use the extension from that
|
||||
isize pos = string_extension_position(build_context.out_filepath);
|
||||
if (pos > 0) {
|
||||
output_ext = substring(build_context.out_filepath, pos, build_context.out_filepath.len);
|
||||
}
|
||||
}
|
||||
|
||||
gbString platform_lib_str = gb_string_make(heap_allocator(), "");
|
||||
defer (gb_string_free(platform_lib_str));
|
||||
@@ -507,7 +473,7 @@ i32 linker_stage(lbGenerator *gen) {
|
||||
defer (gb_string_free(link_command_line));
|
||||
|
||||
link_command_line = gb_string_appendc(link_command_line, object_files);
|
||||
link_command_line = gb_string_append_fmt(link_command_line, " -o \"%.*s%.*s\" ", LIT(output_base), LIT(output_ext));
|
||||
link_command_line = gb_string_append_fmt(link_command_line, " -o \"%.*s\" ", LIT(output_filename));
|
||||
link_command_line = gb_string_append_fmt(link_command_line, " %s ", platform_lib_str);
|
||||
link_command_line = gb_string_append_fmt(link_command_line, " %s ", lib_str);
|
||||
link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.link_flags));
|
||||
@@ -524,9 +490,7 @@ i32 linker_stage(lbGenerator *gen) {
|
||||
if (build_context.ODIN_DEBUG) {
|
||||
// NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
|
||||
// to the symbols in the object file
|
||||
result = system_exec_command_line_app("dsymutil",
|
||||
"dsymutil %.*s%.*s", LIT(output_base), LIT(output_ext)
|
||||
);
|
||||
result = system_exec_command_line_app("dsymutil", "dsymutil %.*s", LIT(output_filename));
|
||||
|
||||
if (result) {
|
||||
return result;
|
||||
@@ -1526,6 +1490,10 @@ bool parse_build_flags(Array<String> args) {
|
||||
gb_printf_err("Invalid -resource path %.*s, missing .rc\n", LIT(path));
|
||||
bad_flags = true;
|
||||
break;
|
||||
} else if (!gb_file_exists((const char *)path.text)) {
|
||||
gb_printf_err("Invalid -resource path %.*s, file does not exist.\n", LIT(path));
|
||||
bad_flags = true;
|
||||
break;
|
||||
}
|
||||
build_context.resource_filepath = substring(path, 0, string_extension_position(path));
|
||||
build_context.has_resource = true;
|
||||
@@ -1540,6 +1508,11 @@ bool parse_build_flags(Array<String> args) {
|
||||
String path = value.value_string;
|
||||
path = string_trim_whitespace(path);
|
||||
if (is_build_flag_path_valid(path)) {
|
||||
if (path_is_directory(path)) {
|
||||
gb_printf_err("Invalid -pdb-name path. %.*s, is a directory.\n", LIT(path));
|
||||
bad_flags = true;
|
||||
break;
|
||||
}
|
||||
// #if defined(GB_SYSTEM_WINDOWS)
|
||||
// String ext = path_extension(path);
|
||||
// if (ext != ".pdb") {
|
||||
@@ -2666,6 +2639,8 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
init_filename = copy_string(permanent_allocator(), init_filename);
|
||||
|
||||
if (init_filename == "-help" ||
|
||||
init_filename == "--help") {
|
||||
build_context.show_help = true;
|
||||
@@ -2688,6 +2663,12 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
gb_printf_err("Did you mean `%.*s %.*s %.*s -file`?\n", LIT(args[0]), LIT(command), LIT(init_filename));
|
||||
gb_printf_err("The `-file` flag tells it to treat a file as a self-contained package.\n");
|
||||
return 1;
|
||||
} else {
|
||||
String const ext = str_lit(".odin");
|
||||
if (!string_ends_with(init_filename, ext)) {
|
||||
gb_printf_err("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2709,13 +2690,24 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared")));
|
||||
}
|
||||
|
||||
|
||||
init_build_context(selected_target_metrics ? selected_target_metrics->metrics : nullptr);
|
||||
// if (build_context.word_size == 4 && build_context.metrics.os != TargetOs_js) {
|
||||
// print_usage_line(0, "%.*s 32-bit is not yet supported for this platform", LIT(args[0]));
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
// Set and check build paths...
|
||||
if (!init_build_paths(init_filename)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (build_context.show_debug_messages) {
|
||||
for_array(i, build_context.build_paths) {
|
||||
String build_path = path_to_string(heap_allocator(), build_context.build_paths[i]);
|
||||
debugf("build_paths[%ld]: %.*s\n", i, LIT(build_path));
|
||||
}
|
||||
}
|
||||
|
||||
init_global_thread_pool();
|
||||
defer (thread_pool_destroy(&global_thread_pool));
|
||||
|
||||
@@ -2732,6 +2724,8 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
}
|
||||
defer (destroy_parser(parser));
|
||||
|
||||
// TODO(jeroen): Remove the `init_filename` param.
|
||||
// Let's put that on `build_context.build_paths[0]` instead.
|
||||
if (parse_packages(parser, init_filename) != ParseFile_None) {
|
||||
return 1;
|
||||
}
|
||||
@@ -2810,16 +2804,14 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
}
|
||||
|
||||
if (run_output) {
|
||||
String exe_name = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]);
|
||||
defer (gb_free(heap_allocator(), exe_name.text));
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
return system_exec_command_line_app("odin run", "%.*s.exe %.*s", LIT(gen->output_base), LIT(run_args_string));
|
||||
return system_exec_command_line_app("odin run", "%.*s %.*s", LIT(exe_name), LIT(run_args_string));
|
||||
#else
|
||||
//NOTE(thebirk): This whole thing is a little leaky
|
||||
String output_ext = {};
|
||||
String complete_path = concatenate_strings(permanent_allocator(), gen->output_base, output_ext);
|
||||
complete_path = path_to_full_path(permanent_allocator(), complete_path);
|
||||
return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(complete_path), LIT(run_args_string));
|
||||
return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(exe_name), LIT(run_args_string));
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -5751,7 +5751,7 @@ ParseFileError parse_packages(Parser *p, String init_filename) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
{ // Add these packages serially and then process them parallel
|
||||
mutex_lock(&p->wait_mutex);
|
||||
|
||||
394
src/path.cpp
Normal file
394
src/path.cpp
Normal file
@@ -0,0 +1,394 @@
|
||||
/*
|
||||
Path handling utilities.
|
||||
*/
|
||||
String remove_extension_from_path(String const &s) {
|
||||
for (isize i = s.len-1; i >= 0; i--) {
|
||||
if (s[i] == '.') {
|
||||
return substring(s, 0, i);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
String remove_directory_from_path(String const &s) {
|
||||
isize len = 0;
|
||||
for (isize i = s.len-1; i >= 0; i--) {
|
||||
if (s[i] == '/' ||
|
||||
s[i] == '\\') {
|
||||
break;
|
||||
}
|
||||
len += 1;
|
||||
}
|
||||
return substring(s, s.len-len, s.len);
|
||||
}
|
||||
|
||||
bool path_is_directory(String path);
|
||||
|
||||
String directory_from_path(String const &s) {
|
||||
if (path_is_directory(s)) {
|
||||
return s;
|
||||
}
|
||||
|
||||
isize i = s.len-1;
|
||||
for (; i >= 0; i--) {
|
||||
if (s[i] == '/' ||
|
||||
s[i] == '\\') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i >= 0) {
|
||||
return substring(s, 0, i);
|
||||
}
|
||||
return substring(s, 0, 0);
|
||||
}
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
bool path_is_directory(String path) {
|
||||
gbAllocator a = heap_allocator();
|
||||
String16 wstr = string_to_string16(a, path);
|
||||
defer (gb_free(a, wstr.text));
|
||||
|
||||
i32 attribs = GetFileAttributesW(wstr.text);
|
||||
if (attribs < 0) return false;
|
||||
|
||||
return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
}
|
||||
|
||||
#else
|
||||
bool path_is_directory(String path) {
|
||||
gbAllocator a = heap_allocator();
|
||||
char *copy = cast(char *)copy_string(a, path).text;
|
||||
defer (gb_free(a, copy));
|
||||
|
||||
struct stat s;
|
||||
if (stat(copy, &s) == 0) {
|
||||
return (s.st_mode & S_IFDIR) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
String path_to_full_path(gbAllocator a, String path) {
|
||||
gbAllocator ha = heap_allocator();
|
||||
char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len);
|
||||
defer (gb_free(ha, path_c));
|
||||
|
||||
char *fullpath = gb_path_get_full_name(a, path_c);
|
||||
String res = string_trim_whitespace(make_string_c(fullpath));
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
for (isize i = 0; i < res.len; i++) {
|
||||
if (res.text[i] == '\\') {
|
||||
res.text[i] = '/';
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return copy_string(a, res);
|
||||
}
|
||||
|
||||
struct Path {
|
||||
String basename;
|
||||
String name;
|
||||
String ext;
|
||||
};
|
||||
|
||||
// NOTE(Jeroen): Naively turns a Path into a string.
|
||||
String path_to_string(gbAllocator a, Path path) {
|
||||
if (path.basename.len + path.name.len + path.ext.len == 0) {
|
||||
return make_string(nullptr, 0);
|
||||
}
|
||||
|
||||
isize len = path.basename.len + 1 + path.name.len + 1;
|
||||
if (path.ext.len > 0) {
|
||||
len += path.ext.len + 1;
|
||||
}
|
||||
|
||||
u8 *str = gb_alloc_array(a, u8, len);
|
||||
|
||||
isize i = 0;
|
||||
gb_memmove(str+i, path.basename.text, path.basename.len); i += path.basename.len;
|
||||
gb_memmove(str+i, "/", 1); i += 1;
|
||||
gb_memmove(str+i, path.name.text, path.name.len); i += path.name.len;
|
||||
if (path.ext.len > 0) {
|
||||
gb_memmove(str+i, ".", 1); i += 1;
|
||||
gb_memmove(str+i, path.ext.text, path.ext.len); i += path.ext.len;
|
||||
}
|
||||
str[i] = 0;
|
||||
|
||||
String res = make_string(str, i);
|
||||
res = string_trim_whitespace(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
// NOTE(Jeroen): Naively turns a Path into a string, then normalizes it using `path_to_full_path`.
|
||||
String path_to_full_path(gbAllocator a, Path path) {
|
||||
String temp = path_to_string(heap_allocator(), path);
|
||||
defer (gb_free(heap_allocator(), temp.text));
|
||||
|
||||
return path_to_full_path(a, temp);
|
||||
}
|
||||
|
||||
// NOTE(Jeroen): Takes a path like "odin" or "W:\Odin", turns it into a full path,
|
||||
// and then breaks it into its components to make a Path.
|
||||
Path path_from_string(gbAllocator a, String const &path) {
|
||||
Path res = {};
|
||||
|
||||
if (path.len == 0) return res;
|
||||
|
||||
String fullpath = path_to_full_path(a, path);
|
||||
defer (gb_free(heap_allocator(), fullpath.text));
|
||||
|
||||
res.basename = directory_from_path(fullpath);
|
||||
res.basename = copy_string(a, res.basename);
|
||||
|
||||
if (path_is_directory(fullpath)) {
|
||||
// It's a directory. We don't need to tinker with the name and extension.
|
||||
// It could have a superfluous trailing `/`. Remove it if so.
|
||||
if (res.basename.len > 0 && res.basename.text[res.basename.len - 1] == '/') {
|
||||
res.basename.len--;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
isize name_start = (res.basename.len > 0) ? res.basename.len + 1 : res.basename.len;
|
||||
res.name = substring(fullpath, name_start, fullpath.len);
|
||||
res.name = remove_extension_from_path(res.name);
|
||||
res.name = copy_string(a, res.name);
|
||||
|
||||
res.ext = path_extension(fullpath, false); // false says not to include the dot.
|
||||
res.ext = copy_string(a, res.ext);
|
||||
return res;
|
||||
}
|
||||
|
||||
// NOTE(Jeroen): Takes a path String and returns the last path element.
|
||||
String last_path_element(String const &path) {
|
||||
isize count = 0;
|
||||
u8 * start = (u8 *)(&path.text[path.len - 1]);
|
||||
for (isize length = path.len; length > 0 && path.text[length - 1] != '/'; length--) {
|
||||
count++;
|
||||
start--;
|
||||
}
|
||||
if (count > 0) {
|
||||
start++; // Advance past the `/` and return the substring.
|
||||
String res = make_string(start, count);
|
||||
return res;
|
||||
}
|
||||
// Must be a root path like `/` or `C:/`, return empty String.
|
||||
return STR_LIT("");
|
||||
}
|
||||
|
||||
bool path_is_directory(Path path) {
|
||||
String path_string = path_to_full_path(heap_allocator(), path);
|
||||
defer (gb_free(heap_allocator(), path_string.text));
|
||||
|
||||
return path_is_directory(path_string);
|
||||
}
|
||||
|
||||
struct FileInfo {
|
||||
String name;
|
||||
String fullpath;
|
||||
i64 size;
|
||||
bool is_dir;
|
||||
};
|
||||
|
||||
enum ReadDirectoryError {
|
||||
ReadDirectory_None,
|
||||
|
||||
ReadDirectory_InvalidPath,
|
||||
ReadDirectory_NotExists,
|
||||
ReadDirectory_Permission,
|
||||
ReadDirectory_NotDir,
|
||||
ReadDirectory_Empty,
|
||||
ReadDirectory_Unknown,
|
||||
|
||||
ReadDirectory_COUNT,
|
||||
};
|
||||
|
||||
i64 get_file_size(String path) {
|
||||
char *c_str = alloc_cstring(heap_allocator(), path);
|
||||
defer (gb_free(heap_allocator(), c_str));
|
||||
|
||||
gbFile f = {};
|
||||
gbFileError err = gb_file_open(&f, c_str);
|
||||
defer (gb_file_close(&f));
|
||||
if (err != gbFileError_None) {
|
||||
return -1;
|
||||
}
|
||||
return gb_file_size(&f);
|
||||
}
|
||||
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
|
||||
GB_ASSERT(fi != nullptr);
|
||||
|
||||
gbAllocator a = heap_allocator();
|
||||
|
||||
while (path.len > 0) {
|
||||
Rune end = path[path.len-1];
|
||||
if (end == '/') {
|
||||
path.len -= 1;
|
||||
} else if (end == '\\') {
|
||||
path.len -= 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (path.len == 0) {
|
||||
return ReadDirectory_InvalidPath;
|
||||
}
|
||||
{
|
||||
char *c_str = alloc_cstring(a, path);
|
||||
defer (gb_free(a, c_str));
|
||||
|
||||
gbFile f = {};
|
||||
gbFileError file_err = gb_file_open(&f, c_str);
|
||||
defer (gb_file_close(&f));
|
||||
|
||||
switch (file_err) {
|
||||
case gbFileError_Invalid: return ReadDirectory_InvalidPath;
|
||||
case gbFileError_NotExists: return ReadDirectory_NotExists;
|
||||
// case gbFileError_Permission: return ReadDirectory_Permission;
|
||||
}
|
||||
}
|
||||
|
||||
if (!path_is_directory(path)) {
|
||||
return ReadDirectory_NotDir;
|
||||
}
|
||||
|
||||
|
||||
char *new_path = gb_alloc_array(a, char, path.len+3);
|
||||
defer (gb_free(a, new_path));
|
||||
|
||||
gb_memmove(new_path, path.text, path.len);
|
||||
gb_memmove(new_path+path.len, "/*", 2);
|
||||
new_path[path.len+2] = 0;
|
||||
|
||||
String np = make_string(cast(u8 *)new_path, path.len+2);
|
||||
String16 wstr = string_to_string16(a, np);
|
||||
defer (gb_free(a, wstr.text));
|
||||
|
||||
WIN32_FIND_DATAW file_data = {};
|
||||
HANDLE find_file = FindFirstFileW(wstr.text, &file_data);
|
||||
if (find_file == INVALID_HANDLE_VALUE) {
|
||||
return ReadDirectory_Unknown;
|
||||
}
|
||||
defer (FindClose(find_file));
|
||||
|
||||
array_init(fi, a, 0, 100);
|
||||
|
||||
do {
|
||||
wchar_t *filename_w = file_data.cFileName;
|
||||
i64 size = cast(i64)file_data.nFileSizeLow;
|
||||
size |= (cast(i64)file_data.nFileSizeHigh) << 32;
|
||||
String name = string16_to_string(a, make_string16_c(filename_w));
|
||||
if (name == "." || name == "..") {
|
||||
gb_free(a, name.text);
|
||||
continue;
|
||||
}
|
||||
|
||||
String filepath = {};
|
||||
filepath.len = path.len+1+name.len;
|
||||
filepath.text = gb_alloc_array(a, u8, filepath.len+1);
|
||||
defer (gb_free(a, filepath.text));
|
||||
gb_memmove(filepath.text, path.text, path.len);
|
||||
gb_memmove(filepath.text+path.len, "/", 1);
|
||||
gb_memmove(filepath.text+path.len+1, name.text, name.len);
|
||||
|
||||
FileInfo info = {};
|
||||
info.name = name;
|
||||
info.fullpath = path_to_full_path(a, filepath);
|
||||
info.size = size;
|
||||
info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
array_add(fi, info);
|
||||
} while (FindNextFileW(find_file, &file_data));
|
||||
|
||||
if (fi->count == 0) {
|
||||
return ReadDirectory_Empty;
|
||||
}
|
||||
|
||||
return ReadDirectory_None;
|
||||
}
|
||||
#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
|
||||
GB_ASSERT(fi != nullptr);
|
||||
|
||||
gbAllocator a = heap_allocator();
|
||||
|
||||
char *c_path = alloc_cstring(a, path);
|
||||
defer (gb_free(a, c_path));
|
||||
|
||||
DIR *dir = opendir(c_path);
|
||||
if (!dir) {
|
||||
switch (errno) {
|
||||
case ENOENT:
|
||||
return ReadDirectory_NotExists;
|
||||
case EACCES:
|
||||
return ReadDirectory_Permission;
|
||||
case ENOTDIR:
|
||||
return ReadDirectory_NotDir;
|
||||
default:
|
||||
// ENOMEM: out of memory
|
||||
// EMFILE: per-process limit on open fds reached
|
||||
// ENFILE: system-wide limit on total open files reached
|
||||
return ReadDirectory_Unknown;
|
||||
}
|
||||
GB_PANIC("unreachable");
|
||||
}
|
||||
|
||||
array_init(fi, a, 0, 100);
|
||||
|
||||
for (;;) {
|
||||
struct dirent *entry = readdir(dir);
|
||||
if (entry == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
String name = make_string_c(entry->d_name);
|
||||
if (name == "." || name == "..") {
|
||||
continue;
|
||||
}
|
||||
|
||||
String filepath = {};
|
||||
filepath.len = path.len+1+name.len;
|
||||
filepath.text = gb_alloc_array(a, u8, filepath.len+1);
|
||||
defer (gb_free(a, filepath.text));
|
||||
gb_memmove(filepath.text, path.text, path.len);
|
||||
gb_memmove(filepath.text+path.len, "/", 1);
|
||||
gb_memmove(filepath.text+path.len+1, name.text, name.len);
|
||||
filepath.text[filepath.len] = 0;
|
||||
|
||||
|
||||
struct stat dir_stat = {};
|
||||
|
||||
if (stat((char *)filepath.text, &dir_stat)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (S_ISDIR(dir_stat.st_mode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
i64 size = dir_stat.st_size;
|
||||
|
||||
FileInfo info = {};
|
||||
info.name = name;
|
||||
info.fullpath = path_to_full_path(a, filepath);
|
||||
info.size = size;
|
||||
array_add(fi, info);
|
||||
}
|
||||
|
||||
if (fi->count == 0) {
|
||||
return ReadDirectory_Empty;
|
||||
}
|
||||
|
||||
return ReadDirectory_None;
|
||||
}
|
||||
#else
|
||||
#error Implement read_directory
|
||||
#endif
|
||||
|
||||
@@ -245,15 +245,14 @@ gb_inline isize string_extension_position(String const &str) {
|
||||
return dot_pos;
|
||||
}
|
||||
|
||||
String path_extension(String const &str) {
|
||||
String path_extension(String const &str, bool include_dot = true) {
|
||||
isize pos = string_extension_position(str);
|
||||
if (pos < 0) {
|
||||
return make_string(nullptr, 0);
|
||||
}
|
||||
return substring(str, pos, str.len);
|
||||
return substring(str, include_dot ? pos : pos + 1, str.len);
|
||||
}
|
||||
|
||||
|
||||
String string_trim_whitespace(String str) {
|
||||
while (str.len > 0 && rune_is_whitespace(str[str.len-1])) {
|
||||
str.len--;
|
||||
@@ -299,38 +298,6 @@ String filename_from_path(String s) {
|
||||
return make_string(nullptr, 0);
|
||||
}
|
||||
|
||||
String remove_extension_from_path(String const &s) {
|
||||
for (isize i = s.len-1; i >= 0; i--) {
|
||||
if (s[i] == '.') {
|
||||
return substring(s, 0, i);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
String remove_directory_from_path(String const &s) {
|
||||
isize len = 0;
|
||||
for (isize i = s.len-1; i >= 0; i--) {
|
||||
if (s[i] == '/' ||
|
||||
s[i] == '\\') {
|
||||
break;
|
||||
}
|
||||
len += 1;
|
||||
}
|
||||
return substring(s, s.len-len, s.len);
|
||||
}
|
||||
|
||||
String directory_from_path(String const &s) {
|
||||
isize i = s.len-1;
|
||||
for (; i >= 0; i--) {
|
||||
if (s[i] == '/' ||
|
||||
s[i] == '\\') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return substring(s, 0, i);
|
||||
}
|
||||
|
||||
String concatenate_strings(gbAllocator a, String const &x, String const &y) {
|
||||
isize len = x.len+y.len;
|
||||
u8 *data = gb_alloc_array(a, u8, len+1);
|
||||
|
||||
@@ -8,39 +8,39 @@ download_test_assets:
|
||||
$(PYTHON) download_assets.py
|
||||
|
||||
image_test:
|
||||
$(ODIN) run image/test_core_image.odin -file
|
||||
$(ODIN) run image/test_core_image.odin -file -out:test_core_image
|
||||
|
||||
compress_test:
|
||||
$(ODIN) run compress/test_core_compress.odin -file
|
||||
$(ODIN) run compress/test_core_compress.odin -file -out:test_core_compress
|
||||
|
||||
strings_test:
|
||||
$(ODIN) run strings/test_core_strings.odin -file
|
||||
$(ODIN) run strings/test_core_strings.odin -file -out:test_core_strings
|
||||
|
||||
hash_test:
|
||||
$(ODIN) run hash -out=test_hash -o:speed -no-bounds-check
|
||||
$(ODIN) run hash -o:speed -no-bounds-check -out:test_hash
|
||||
|
||||
crypto_test:
|
||||
$(ODIN) run crypto -out=test_crypto_hash -o:speed -no-bounds-check
|
||||
$(ODIN) run crypto -o:speed -no-bounds-check -out:test_crypto_hash
|
||||
|
||||
noise_test:
|
||||
$(ODIN) run math/noise -out=test_noise
|
||||
$(ODIN) run math/noise -out:test_noise
|
||||
|
||||
encoding_test:
|
||||
$(ODIN) run encoding/hxa -out=test_hxa -collection:tests=..
|
||||
$(ODIN) run encoding/json -out=test_json
|
||||
$(ODIN) run encoding/varint -out=test_varint
|
||||
$(ODIN) run encoding/hxa -collection:tests=.. -out:test_hxa
|
||||
$(ODIN) run encoding/json -out:test_json
|
||||
$(ODIN) run encoding/varint -out:test_varint
|
||||
|
||||
math_test:
|
||||
$(ODIN) run math/test_core_math.odin -out=test_core_math -file -collection:tests=..
|
||||
$(ODIN) run math/test_core_math.odin -file -collection:tests=.. -out:test_core_math
|
||||
|
||||
linalg_glsl_math_test:
|
||||
$(ODIN) run math/linalg/glsl/test_linalg_glsl_math.odin -file -out=test_linalg_glsl_math -collection:tests=..
|
||||
$(ODIN) run math/linalg/glsl/test_linalg_glsl_math.odin -file -collection:tests=.. -out:test_linalg_glsl_math
|
||||
|
||||
filepath_test:
|
||||
$(ODIN) run path/filepath/test_core_filepath.odin -file -out=test_core_filepath -collection:tests=..
|
||||
$(ODIN) run path/filepath/test_core_filepath.odin -file -collection:tests=.. -out:test_core_filepath
|
||||
|
||||
reflect_test:
|
||||
$(ODIN) run reflect/test_core_reflect.odin -file -out=test_core_reflect -collection:tests=..
|
||||
$(ODIN) run reflect/test_core_reflect.odin -file -collection:tests=.. -out:test_core_reflect
|
||||
|
||||
os_exit_test:
|
||||
$(ODIN) run os/test_core_os_exit.odin -file -out=test_core_os_exit && exit 1 || exit 0
|
||||
$(ODIN) run os/test_core_os_exit.odin -file -out:test_core_os_exit && exit 1 || exit 0
|
||||
26
tests/core/assets/Shoco/LICENSE
Normal file
26
tests/core/assets/Shoco/LICENSE
Normal file
@@ -0,0 +1,26 @@
|
||||
Copyright (c) 2016-2021 Ginger Bill. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
BIN
tests/core/assets/Shoco/LICENSE.shoco
Normal file
BIN
tests/core/assets/Shoco/LICENSE.shoco
Normal file
Binary file not shown.
95
tests/core/assets/Shoco/README.md
Normal file
95
tests/core/assets/Shoco/README.md
Normal file
@@ -0,0 +1,95 @@
|
||||
<p align="center">
|
||||
<img src="misc/logo-slim.png" alt="Odin logo" style="width:65%">
|
||||
<br/>
|
||||
The Data-Oriented Language for Sane Software Development.
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="https://github.com/odin-lang/odin/releases/latest">
|
||||
<img src="https://img.shields.io/github/release/odin-lang/odin.svg">
|
||||
</a>
|
||||
<a href="https://github.com/odin-lang/odin/releases/latest">
|
||||
<img src="https://img.shields.io/badge/platforms-Windows%20|%20Linux%20|%20macOS-green.svg">
|
||||
</a>
|
||||
<br>
|
||||
<a href="https://discord.gg/odinlang">
|
||||
<img src="https://img.shields.io/discord/568138951836172421?logo=discord">
|
||||
</a>
|
||||
<a href="https://github.com/odin-lang/odin/actions">
|
||||
<img src="https://github.com/odin-lang/odin/workflows/CI/badge.svg?branch=master&event=push">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
# The Odin Programming Language
|
||||
|
||||
|
||||
Odin is a general-purpose programming language with distinct typing, built for high performance, modern systems, and built-in data-oriented data types. The Odin Programming Language, the C alternative for the joy of programming.
|
||||
|
||||
Website: [https://odin-lang.org/](https://odin-lang.org/)
|
||||
|
||||
```odin
|
||||
package main
|
||||
|
||||
import "core:fmt"
|
||||
|
||||
main :: proc() {
|
||||
program := "+ + * 😃 - /"
|
||||
accumulator := 0
|
||||
|
||||
for token in program {
|
||||
switch token {
|
||||
case '+': accumulator += 1
|
||||
case '-': accumulator -= 1
|
||||
case '*': accumulator *= 2
|
||||
case '/': accumulator /= 2
|
||||
case '😃': accumulator *= accumulator
|
||||
case: // Ignore everything else
|
||||
}
|
||||
}
|
||||
|
||||
fmt.printf("The program \"%s\" calculates the value %d\n",
|
||||
program, accumulator)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
#### [Getting Started](https://odin-lang.org/docs/install)
|
||||
|
||||
Instructions for downloading and installing the Odin compiler and libraries.
|
||||
|
||||
#### [Nightly Builds](https://odin-lang.org/docs/nightly/)
|
||||
|
||||
Get the latest nightly builds of Odin.
|
||||
|
||||
### Learning Odin
|
||||
|
||||
#### [Overview of Odin](https://odin-lang.org/docs/overview)
|
||||
|
||||
An overview of the Odin programming language.
|
||||
|
||||
#### [Frequently Asked Questions (FAQ)](https://odin-lang.org/docs/faq)
|
||||
|
||||
Answers to common questions about Odin.
|
||||
|
||||
#### [Packages](https://pkg.odin-lang.org/)
|
||||
|
||||
Documentation for all the official packages part of the [core](https://pkg.odin-lang.org/core/) and [vendor](https://pkg.odin-lang.org/vendor/) library collections.
|
||||
|
||||
#### [The Odin Wiki](https://github.com/odin-lang/Odin/wiki)
|
||||
|
||||
A wiki maintained by the Odin community.
|
||||
|
||||
#### [Odin Discord](https://discord.gg/sVBPHEv)
|
||||
|
||||
Get live support and talk with other odiners on the Odin Discord.
|
||||
|
||||
### Articles
|
||||
|
||||
#### [The Odin Blog](https://odin-lang.org/news/)
|
||||
|
||||
The official blog of the Odin programming language, featuring announcements, news, and in-depth articles by the Odin team and guests.
|
||||
|
||||
## Warnings
|
||||
|
||||
* The Odin compiler is still in development.
|
||||
BIN
tests/core/assets/Shoco/README.md.shoco
Normal file
BIN
tests/core/assets/Shoco/README.md.shoco
Normal file
Binary file not shown.
@@ -5,61 +5,61 @@ python3 download_assets.py
|
||||
echo ---
|
||||
echo Running core:image tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% run image %COMMON%
|
||||
%PATH_TO_ODIN% run image %COMMON% -out:test_core_image.exe
|
||||
|
||||
echo ---
|
||||
echo Running core:compress tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% run compress %COMMON%
|
||||
%PATH_TO_ODIN% run compress %COMMON% -out:test_core_compress.exe
|
||||
|
||||
echo ---
|
||||
echo Running core:strings tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% run strings %COMMON%
|
||||
%PATH_TO_ODIN% run strings %COMMON% -out:test_core_strings.exe
|
||||
|
||||
echo ---
|
||||
echo Running core:hash tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% run hash %COMMON% -o:size
|
||||
%PATH_TO_ODIN% run hash %COMMON% -o:size -out:test_core_hash.exe
|
||||
|
||||
echo ---
|
||||
echo Running core:odin tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% run odin %COMMON% -o:size
|
||||
%PATH_TO_ODIN% run odin %COMMON% -o:size -out:test_core_odin.exe
|
||||
|
||||
echo ---
|
||||
echo Running core:crypto hash tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% run crypto %COMMON%
|
||||
%PATH_TO_ODIN% run crypto %COMMON% -out:test_crypto_hash.exe
|
||||
|
||||
echo ---
|
||||
echo Running core:encoding tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% run encoding/hxa %COMMON%
|
||||
%PATH_TO_ODIN% run encoding/json %COMMON%
|
||||
%PATH_TO_ODIN% run encoding/varint %COMMON%
|
||||
%PATH_TO_ODIN% run encoding/hxa %COMMON% -out:test_hxa.exe
|
||||
%PATH_TO_ODIN% run encoding/json %COMMON% -out:test_json.exe
|
||||
%PATH_TO_ODIN% run encoding/varint %COMMON% -out:test_varint.exe
|
||||
|
||||
echo ---
|
||||
echo Running core:math/noise tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% run math/noise %COMMON%
|
||||
%PATH_TO_ODIN% run math/noise %COMMON% -out:test_noise.exe
|
||||
|
||||
echo ---
|
||||
echo Running core:math tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% run math %COMMON%
|
||||
%PATH_TO_ODIN% run math %COMMON% -out:test_core_math.exe
|
||||
|
||||
echo ---
|
||||
echo Running core:math/linalg/glsl tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% run math/linalg/glsl %COMMON%
|
||||
%PATH_TO_ODIN% run math/linalg/glsl %COMMON% -out:test_linalg_glsl.exe
|
||||
|
||||
echo ---
|
||||
echo Running core:path/filepath tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% run path/filepath %COMMON%
|
||||
%PATH_TO_ODIN% run path/filepath %COMMON% -out:test_core_filepath.exe
|
||||
|
||||
echo ---
|
||||
echo Running core:reflect tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% run reflect %COMMON%
|
||||
%PATH_TO_ODIN% run reflect %COMMON% -out:test_core_reflect.exe
|
||||
@@ -7,13 +7,14 @@ package test_core_compress
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
|
||||
A test suite for ZLIB, GZIP.
|
||||
A test suite for ZLIB, GZIP and Shoco.
|
||||
*/
|
||||
|
||||
import "core:testing"
|
||||
|
||||
import "core:compress/zlib"
|
||||
import "core:compress/gzip"
|
||||
import "core:compress/shoco"
|
||||
|
||||
import "core:bytes"
|
||||
import "core:fmt"
|
||||
@@ -48,6 +49,7 @@ main :: proc() {
|
||||
t := testing.T{w=w}
|
||||
zlib_test(&t)
|
||||
gzip_test(&t)
|
||||
shoco_test(&t)
|
||||
|
||||
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
|
||||
if TEST_fail > 0 {
|
||||
@@ -134,3 +136,56 @@ gzip_test :: proc(t: ^testing.T) {
|
||||
expect(t, false, error)
|
||||
}
|
||||
}
|
||||
|
||||
@test
|
||||
shoco_test :: proc(t: ^testing.T) {
|
||||
|
||||
Shoco_Tests :: []struct{
|
||||
compressed: []u8,
|
||||
raw: []u8,
|
||||
short_pack: int,
|
||||
short_sentinel: int,
|
||||
}{
|
||||
{ #load("../assets/Shoco/README.md.shoco"), #load("../assets/Shoco/README.md"), 10, 1006 },
|
||||
{ #load("../assets/Shoco/LICENSE.shoco"), #load("../assets/Shoco/LICENSE"), 25, 68 },
|
||||
}
|
||||
|
||||
for v in Shoco_Tests {
|
||||
expected_raw := len(v.raw)
|
||||
expected_compressed := len(v.compressed)
|
||||
|
||||
biggest_unpacked := shoco.decompress_bound(expected_compressed)
|
||||
biggest_packed := shoco.compress_bound(expected_raw)
|
||||
|
||||
buffer := make([]u8, max(biggest_packed, biggest_unpacked))
|
||||
defer delete(buffer)
|
||||
|
||||
size, err := shoco.decompress(v.compressed, buffer[:])
|
||||
msg := fmt.tprintf("Expected `decompress` to return `nil`, got %v", err)
|
||||
expect(t, err == nil, msg)
|
||||
|
||||
msg = fmt.tprintf("Decompressed %v bytes into %v. Expected to decompress into %v bytes.", len(v.compressed), size, expected_raw)
|
||||
expect(t, size == expected_raw, msg)
|
||||
expect(t, string(buffer[:size]) == string(v.raw), "Decompressed contents don't match.")
|
||||
|
||||
size, err = shoco.compress(string(v.raw), buffer[:])
|
||||
expect(t, err == nil, "Expected `compress` to return `nil`.")
|
||||
|
||||
msg = fmt.tprintf("Compressed %v bytes into %v. Expected to compress into %v bytes.", expected_raw, size, expected_compressed)
|
||||
expect(t, size == expected_compressed, msg)
|
||||
|
||||
size, err = shoco.decompress(v.compressed, buffer[:expected_raw - 10])
|
||||
msg = fmt.tprintf("Decompressing into too small a buffer returned %v, expected `.Output_Too_Short`", err)
|
||||
expect(t, err == .Output_Too_Short, msg)
|
||||
|
||||
size, err = shoco.compress(string(v.raw), buffer[:expected_compressed - 10])
|
||||
msg = fmt.tprintf("Compressing into too small a buffer returned %v, expected `.Output_Too_Short`", err)
|
||||
expect(t, err == .Output_Too_Short, msg)
|
||||
|
||||
size, err = shoco.decompress(v.compressed[:v.short_pack], buffer[:])
|
||||
expect(t, err == .Stream_Too_Short, "Expected `decompress` to return `Stream_Too_Short` because there was no more data after selecting a pack.")
|
||||
|
||||
size, err = shoco.decompress(v.compressed[:v.short_sentinel], buffer[:])
|
||||
expect(t, err == .Stream_Too_Short, "Expected `decompress` to return `Stream_Too_Short` because there was no more data after non-ASCII sentinel.")
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ main :: proc() {
|
||||
|
||||
parse_json(&t)
|
||||
marshal_json(&t)
|
||||
unmarshal_json(&t)
|
||||
|
||||
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
|
||||
if TEST_fail > 0 {
|
||||
@@ -70,7 +71,8 @@ parse_json :: proc(t: ^testing.T) {
|
||||
|
||||
_, err := json.parse(transmute([]u8)json_data)
|
||||
|
||||
expect(t, err == .None, "expected json error to be none")
|
||||
msg := fmt.tprintf("Expected `json.parse` to return nil, got %v", err)
|
||||
expect(t, err == nil, msg)
|
||||
}
|
||||
|
||||
@test
|
||||
@@ -87,6 +89,259 @@ marshal_json :: proc(t: ^testing.T) {
|
||||
}
|
||||
|
||||
_, err := json.marshal(my_struct)
|
||||
|
||||
expect(t, err == nil, "expected json error to be none")
|
||||
msg := fmt.tprintf("Expected `json.marshal` to return nil, got %v", err)
|
||||
expect(t, err == nil, msg)
|
||||
}
|
||||
|
||||
PRODUCTS := `
|
||||
{
|
||||
"cash": "0",
|
||||
"products": [
|
||||
{
|
||||
"name": "Cog\nCola",
|
||||
"cost": "3",
|
||||
"owned": "1",
|
||||
|
||||
"profit": "4",
|
||||
"seconds": 3,
|
||||
"multiplier": 1,
|
||||
"auto_click": false
|
||||
},
|
||||
{
|
||||
"name": "gingerBeer",
|
||||
"cost": "9",
|
||||
"owned": "0",
|
||||
|
||||
"profit": "16",
|
||||
"seconds": 5,
|
||||
"multiplier": 1,
|
||||
"auto_click": false
|
||||
},
|
||||
{
|
||||
"name": "Coffee",
|
||||
"cost": "27",
|
||||
"owned": "0",
|
||||
|
||||
"profit": "64",
|
||||
"seconds": 7,
|
||||
"multiplier": 1,
|
||||
"auto_click": false
|
||||
},
|
||||
{
|
||||
"name": "Haggis",
|
||||
"cost": "81",
|
||||
"owned": "0",
|
||||
|
||||
"profit": "256",
|
||||
"seconds": 11,
|
||||
"multiplier": 1,
|
||||
"auto_click": false
|
||||
},
|
||||
{
|
||||
"name": "Lasagna",
|
||||
"cost": "243",
|
||||
"owned": "0",
|
||||
|
||||
"profit": "1024",
|
||||
"seconds": 13,
|
||||
"multiplier": 1,
|
||||
"auto_click": false
|
||||
},
|
||||
{
|
||||
"name": "Asparagus",
|
||||
"cost": "729",
|
||||
"owned": "0",
|
||||
|
||||
"profit": "4096",
|
||||
"seconds": 17,
|
||||
"multiplier": 1,
|
||||
"auto_click": false
|
||||
},
|
||||
{
|
||||
"name": "Yorkshire Pudding",
|
||||
"cost": "2187",
|
||||
"owned": "0",
|
||||
|
||||
"profit": "16384",
|
||||
"seconds": 19,
|
||||
"multiplier": 1,
|
||||
"auto_click": false
|
||||
},
|
||||
{
|
||||
"name": "Salmon Wrap",
|
||||
"cost": "6561",
|
||||
"owned": "0",
|
||||
|
||||
"profit": "65536",
|
||||
"seconds": 23,
|
||||
"multiplier": 1,
|
||||
"auto_click": false
|
||||
},
|
||||
{
|
||||
"name": "Poke Bowl",
|
||||
"cost": "19683",
|
||||
"owned": "0",
|
||||
|
||||
"profit": "262144",
|
||||
"seconds": 29,
|
||||
"multiplier": 1,
|
||||
"auto_click": false
|
||||
},
|
||||
{
|
||||
"name": "Chili Con Carne",
|
||||
"cost": "59049",
|
||||
"owned": "0",
|
||||
|
||||
"profit": "1048576",
|
||||
"seconds": 59,
|
||||
"multiplier": 1,
|
||||
"auto_click": false
|
||||
},
|
||||
],
|
||||
}
|
||||
`
|
||||
|
||||
original_data := Game_Marshal{
|
||||
cash = "0",
|
||||
products = {
|
||||
{
|
||||
name = "Cog\nCola",
|
||||
cost = "3",
|
||||
owned = "1",
|
||||
profit = "4",
|
||||
seconds = 3,
|
||||
multiplier = 1,
|
||||
auto_click = false,
|
||||
},
|
||||
{
|
||||
name = "gingerBeer",
|
||||
cost = "9",
|
||||
owned = "0",
|
||||
profit = "16",
|
||||
seconds = 5,
|
||||
multiplier = 1,
|
||||
auto_click = false,
|
||||
},
|
||||
{
|
||||
name = "Coffee",
|
||||
cost = "27",
|
||||
owned = "0",
|
||||
profit = "64",
|
||||
seconds = 7,
|
||||
multiplier = 1,
|
||||
auto_click = false,
|
||||
},
|
||||
{
|
||||
name = "Haggis",
|
||||
cost = "81",
|
||||
owned = "0",
|
||||
profit = "256",
|
||||
seconds = 11,
|
||||
multiplier = 1,
|
||||
auto_click = false,
|
||||
},
|
||||
{
|
||||
name = "Lasagna",
|
||||
cost = "243",
|
||||
owned = "0",
|
||||
profit = "1024",
|
||||
seconds = 13,
|
||||
multiplier = 1,
|
||||
auto_click = false,
|
||||
},
|
||||
{
|
||||
name = "Asparagus",
|
||||
cost = "729",
|
||||
owned = "0",
|
||||
profit = "4096",
|
||||
seconds = 17,
|
||||
multiplier = 1,
|
||||
auto_click = false,
|
||||
},
|
||||
{
|
||||
name = "Yorkshire Pudding",
|
||||
cost = "2187",
|
||||
owned = "0",
|
||||
profit = "16384",
|
||||
seconds = 19,
|
||||
multiplier = 1,
|
||||
auto_click = false,
|
||||
},
|
||||
{
|
||||
name = "Salmon Wrap",
|
||||
cost = "6561",
|
||||
owned = "0",
|
||||
profit = "65536",
|
||||
seconds = 23,
|
||||
multiplier = 1,
|
||||
auto_click = false,
|
||||
},
|
||||
{
|
||||
name = "Poke Bowl",
|
||||
cost = "19683",
|
||||
owned = "0",
|
||||
profit = "262144",
|
||||
seconds = 29,
|
||||
multiplier = 1,
|
||||
auto_click = false,
|
||||
},
|
||||
{
|
||||
name = "Chili Con Carne",
|
||||
cost = "59049",
|
||||
owned = "0",
|
||||
profit = "1048576",
|
||||
seconds = 59,
|
||||
multiplier = 1,
|
||||
auto_click = false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Product_Marshal :: struct {
|
||||
name: cstring,
|
||||
owned: string,
|
||||
|
||||
cost: string,
|
||||
|
||||
profit: string,
|
||||
seconds: int,
|
||||
multiplier: int,
|
||||
|
||||
auto_click: bool,
|
||||
}
|
||||
|
||||
Game_Marshal :: struct {
|
||||
cash: string,
|
||||
products: []Product_Marshal,
|
||||
}
|
||||
|
||||
cleanup :: proc(g: Game_Marshal) {
|
||||
for p in g.products {
|
||||
delete(p.name)
|
||||
delete(p.owned)
|
||||
delete(p.cost)
|
||||
delete(p.profit)
|
||||
}
|
||||
delete(g.products)
|
||||
delete(g.cash)
|
||||
}
|
||||
|
||||
@test
|
||||
unmarshal_json :: proc(t: ^testing.T) {
|
||||
g: Game_Marshal
|
||||
err := json.unmarshal(transmute([]u8)PRODUCTS, &g, json.DEFAULT_SPECIFICATION)
|
||||
defer cleanup(g)
|
||||
|
||||
msg := fmt.tprintf("Expected `json.unmarshal` to return nil, got %v", err)
|
||||
expect(t, err == nil, msg)
|
||||
|
||||
msg = fmt.tprintf("Expected %v products to have been unmarshaled, got %v", len(original_data.products), len(g.products))
|
||||
expect(t, len(g.products) == len(original_data.products), msg)
|
||||
|
||||
msg = fmt.tprintf("Expected cash to have been unmarshaled as %v, got %v", original_data.cash, g.cash)
|
||||
expect(t, original_data.cash == g.cash, msg)
|
||||
|
||||
for p, i in g.products {
|
||||
expect(t, p == original_data.products[i], "Producted unmarshaled improperly")
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -4,7 +4,7 @@ set PATH_TO_ODIN==..\..\..\..\odin
|
||||
set TEST_ARGS=-fast-tests
|
||||
set TEST_ARGS=-no-random
|
||||
set TEST_ARGS=
|
||||
set OUT_NAME=math_big_test_library
|
||||
set OUT_NAME=math_big_test_library.dll
|
||||
set COMMON=-build-mode:shared -show-timings -no-bounds-check -define:MATH_BIG_EXE=false -vet -strict-style
|
||||
echo ---
|
||||
echo Running core:math/big tests
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
@echo off
|
||||
|
||||
if not exist "tests\issues\build\" mkdir tests\issues\build
|
||||
if not exist "build\" mkdir build
|
||||
|
||||
set COMMON=-collection:tests=tests -out:tests\issues\build\test_issue
|
||||
set COMMON=-collection:tests=.. -out:build\test_issue.exe
|
||||
|
||||
@echo on
|
||||
|
||||
.\odin build tests\issues\test_issue_829.odin %COMMON% -file
|
||||
tests\issues\build\test_issue
|
||||
..\..\odin build test_issue_829.odin %COMMON% -file
|
||||
build\test_issue
|
||||
|
||||
.\odin build tests\issues\test_issue_1592.odin %COMMON% -file
|
||||
tests\issues\build\test_issue
|
||||
..\..\odin build test_issue_1592.odin %COMMON% -file
|
||||
build\test_issue
|
||||
|
||||
@echo off
|
||||
|
||||
rmdir /S /Q tests\issues\build
|
||||
rmdir /S /Q build
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
mkdir -p tests/issues/build
|
||||
|
||||
COMMON="-collection:tests=tests -out:tests/issues/build/test_issue"
|
||||
mkdir -p build
|
||||
ODIN=../../odin
|
||||
COMMON="-collection:tests=.. -out:build/test_issue"
|
||||
|
||||
set -x
|
||||
|
||||
./odin build tests/issues/test_issue_829.odin $COMMON -file
|
||||
tests/issues/build/test_issue
|
||||
$ODIN build test_issue_829.odin $COMMON -file
|
||||
./build/test_issue
|
||||
|
||||
./odin build tests/issues/test_issue_1592.odin $COMMON -file
|
||||
tests/issues/build/test_issue
|
||||
$ODIN build test_issue_1592.odin $COMMON -file
|
||||
./build/test_issue
|
||||
|
||||
set +x
|
||||
|
||||
rm -rf tests/issues/build
|
||||
rm -rf build
|
||||
|
||||
2
tests/vendor/Makefile
vendored
2
tests/vendor/Makefile
vendored
@@ -10,4 +10,4 @@ endif
|
||||
all: botan_test
|
||||
|
||||
botan_test:
|
||||
$(ODIN) run botan -out=botan_hash -o:speed -no-bounds-check $(ODINFLAGS)
|
||||
$(ODIN) run botan -o:speed -no-bounds-check $(ODINFLAGS) -out=vendor_botan
|
||||
|
||||
4
tests/vendor/build.bat
vendored
4
tests/vendor/build.bat
vendored
@@ -5,9 +5,9 @@ set PATH_TO_ODIN==..\..\odin
|
||||
echo ---
|
||||
echo Running vendor:botan tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% run botan %COMMON%
|
||||
%PATH_TO_ODIN% run botan %COMMON% -out:vendor_botan.exe
|
||||
|
||||
echo ---
|
||||
echo Running vendor:glfw tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% run glfw %COMMON%
|
||||
%PATH_TO_ODIN% run glfw %COMMON% -out:vendor_glfw.exe
|
||||
@@ -626,6 +626,9 @@ with open("../core.odin", 'w', encoding='utf-8') as f:
|
||||
f.write(BASE)
|
||||
f.write("""
|
||||
API_VERSION_1_0 :: (1<<22) | (0<<12) | (0)
|
||||
API_VERSION_1_1 :: (1<<22) | (1<<12) | (0)
|
||||
API_VERSION_1_2 :: (1<<22) | (2<<12) | (0)
|
||||
API_VERSION_1_3 :: (1<<22) | (3<<12) | (0)
|
||||
|
||||
MAKE_VERSION :: proc(major, minor, patch: u32) -> u32 {
|
||||
return (major<<22) | (minor<<12) | (patch)
|
||||
|
||||
3
vendor/vulkan/core.odin
vendored
3
vendor/vulkan/core.odin
vendored
@@ -3,6 +3,9 @@
|
||||
//
|
||||
package vulkan
|
||||
API_VERSION_1_0 :: (1<<22) | (0<<12) | (0)
|
||||
API_VERSION_1_1 :: (1<<22) | (1<<12) | (0)
|
||||
API_VERSION_1_2 :: (1<<22) | (2<<12) | (0)
|
||||
API_VERSION_1_3 :: (1<<22) | (3<<12) | (0)
|
||||
|
||||
MAKE_VERSION :: proc(major, minor, patch: u32) -> u32 {
|
||||
return (major<<22) | (minor<<12) | (patch)
|
||||
|
||||
Reference in New Issue
Block a user