diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1a6fa65bb..ce23c2415 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,9 +56,9 @@ jobs: runs-on: macos-latest steps: - uses: actions/checkout@v1 - - name: Download LLVM and setup PATH + - name: Download LLVM, botan and setup PATH run: | - brew install llvm@11 + brew install llvm@11 botan echo "/usr/local/opt/llvm@11/bin" >> $GITHUB_PATH TMP_PATH=$(xcrun --show-sdk-path)/user/include echo "CPATH=$TMP_PATH" >> $GITHUB_ENV @@ -87,6 +87,11 @@ jobs: cd tests/core make timeout-minutes: 10 + - name: Vendor library tests + run: | + cd tests/vendor + make + timeout-minutes: 10 - name: Odin internals tests run: | cd tests/internal @@ -99,13 +104,13 @@ jobs: run: ./odin check examples/all -vet -strict-style -target:linux_arm64 timeout-minutes: 10 build_windows: - runs-on: windows-2019 + runs-on: windows-2022 steps: - uses: actions/checkout@v1 - name: build Odin shell: cmd run: | - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat ./build.bat 1 - name: Odin version run: ./odin version @@ -116,65 +121,65 @@ jobs: - name: Odin check shell: cmd run: | - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat odin check examples/demo -vet timeout-minutes: 10 - name: Odin run shell: cmd run: | - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat odin run examples/demo timeout-minutes: 10 - name: Odin run -debug shell: cmd run: | - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat odin run examples/demo -debug timeout-minutes: 10 - name: Odin check examples/all shell: cmd run: | - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat odin check examples/all -strict-style timeout-minutes: 10 - name: Core library tests shell: cmd run: | - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat cd tests\core call build.bat timeout-minutes: 10 - name: Vendor library tests shell: cmd run: | - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat cd tests\vendor call build.bat timeout-minutes: 10 - name: Odin internals tests shell: cmd run: | - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat cd tests\internal call build.bat timeout-minutes: 10 - name: Odin documentation tests shell: cmd run: | - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat cd tests\documentation call build.bat timeout-minutes: 10 - name: core:math/big tests shell: cmd run: | - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat cd tests\core\math\big call build.bat timeout-minutes: 10 - name: Odin check examples/all for Windows 32bits shell: cmd run: | - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat odin check examples/all -strict-style -target:windows_i386 timeout-minutes: 10 diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 7fe7513e0..67d0396c1 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -7,18 +7,18 @@ on: jobs: build_windows: - runs-on: windows-2019 + runs-on: windows-2022 steps: - uses: actions/checkout@v1 - name: build Odin shell: cmd run: | - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat ./build.bat 1 1 - name: Odin run shell: cmd run: | - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat odin run examples/demo - name: Copy artifacts run: | diff --git a/build_odin.sh b/build_odin.sh index 9f4e7101a..93d6a979c 100755 --- a/build_odin.sh +++ b/build_odin.sh @@ -4,14 +4,12 @@ set -eu : ${CXX=clang++} : ${CPPFLAGS=} : ${CXXFLAGS=} -: ${INCLUDE_DIRECTORIES=} : ${LDFLAGS=} : ${ODIN_VERSION=dev-$(date +"%Y-%m")} : ${GIT_SHA=} CPPFLAGS="$CPPFLAGS -DODIN_VERSION_RAW=\"$ODIN_VERSION\"" CXXFLAGS="$CXXFLAGS -std=c++14" -INCLUDE_DIRECTORIES="$INCLUDE_DIRECTORIES -Isrc/" LDFLAGS="$LDFLAGS -pthread -lm -lstdc++" if [ -d ".git" ]; then @@ -148,7 +146,7 @@ build_odin() { esac set -x - $CXX src/main.cpp src/libtommath.cpp $DISABLED_WARNINGS $CPPFLAGS $CXXFLAGS $INCLUDE_DIRECTORIES $EXTRAFLAGS $LDFLAGS -o odin + $CXX src/main.cpp src/libtommath.cpp $DISABLED_WARNINGS $CPPFLAGS $CXXFLAGS $EXTRAFLAGS $LDFLAGS -o odin set +x } diff --git a/core/bytes/buffer.odin b/core/bytes/buffer.odin index bba834f7e..b60a8e877 100644 --- a/core/bytes/buffer.odin +++ b/core/bytes/buffer.odin @@ -38,6 +38,11 @@ buffer_init_string :: proc(b: ^Buffer, s: string) { } buffer_init_allocator :: proc(b: ^Buffer, len, cap: int, allocator := context.allocator) { + if b.buf == nil { + b.buf = make([dynamic]byte, len, cap, allocator) + return + } + b.buf.allocator = allocator reserve(&b.buf, cap) resize(&b.buf, len) diff --git a/core/encoding/hex/hex.odin b/core/encoding/hex/hex.odin new file mode 100644 index 000000000..ef0bab1d0 --- /dev/null +++ b/core/encoding/hex/hex.odin @@ -0,0 +1,73 @@ +package hex + +import "core:strings" + +encode :: proc(src: []byte, allocator := context.allocator) -> []byte #no_bounds_check { + dst := make([]byte, len(src) * 2, allocator) + for i, j := 0, 0; i < len(src); i += 1 { + v := src[i] + dst[j] = HEXTABLE[v>>4] + dst[j+1] = HEXTABLE[v&0x0f] + j += 2 + } + + return dst +} + + +decode :: proc(src: []byte, allocator := context.allocator) -> (dst: []byte, ok: bool) #no_bounds_check { + if len(src) % 2 == 1 { + return + } + + dst = make([]byte, len(src) / 2, allocator) + for i, j := 0, 1; j < len(src); j += 2 { + p := src[j-1] + q := src[j] + + a := hex_digit(p) or_return + b := hex_digit(q) or_return + + dst[i] = (a << 4) | b + i += 1 + } + + return dst, true +} + +// Decodes the given sequence into one byte. +// Should be called with one byte worth of the source, eg: 0x23 -> '#'. +decode_sequence :: proc(str: string) -> (res: byte, ok: bool) { + str := str + if strings.has_prefix(str, "0x") || strings.has_prefix(str, "0X") { + str = str[2:] + } + + if len(str) != 2 { + return 0, false + } + + upper := hex_digit(str[0]) or_return + lower := hex_digit(str[1]) or_return + + return upper << 4 | lower, true +} + +@(private) +HEXTABLE := [16]byte { + '0', '1', '2', '3', + '4', '5', '6', '7', + '8', '9', 'a', 'b', + 'c', 'd', 'e', 'f', +} + +@(private) +hex_digit :: proc(char: byte) -> (u8, bool) { + switch char { + case '0' ..= '9': return char - '0', true + case 'a' ..= 'f': return char - 'a' + 10, true + case 'A' ..= 'F': return char - 'A' + 10, true + case: return 0, false + } +} + diff --git a/core/net/url.odin b/core/net/url.odin index a5e529928..ed39f7859 100644 --- a/core/net/url.odin +++ b/core/net/url.odin @@ -19,7 +19,7 @@ package net import "core:strings" import "core:strconv" import "core:unicode/utf8" -import "core:mem" +import "core:encoding/hex" split_url :: proc(url: string, allocator := context.allocator) -> (scheme, host, path: string, queries: map[string]string) { s := url @@ -36,9 +36,11 @@ split_url :: proc(url: string, allocator := context.allocator) -> (scheme, host, s = s[:i] if query_str != "" { queries_parts := strings.split(query_str, "&") + defer delete(queries_parts) queries = make(map[string]string, len(queries_parts), allocator) for q in queries_parts { parts := strings.split(q, "=") + defer delete(parts) switch len(parts) { case 1: queries[parts[0]] = "" // NOTE(tetra): Query not set to anything, was but present. case 2: queries[parts[0]] = parts[1] // NOTE(tetra): Query set to something. @@ -76,13 +78,19 @@ join_url :: proc(scheme, host, path: string, queries: map[string]string, allocat } - if len(queries) > 0 do write_string(&b, "?") + query_length := len(queries) + if query_length > 0 do write_string(&b, "?") + i := 0 for query_name, query_value in queries { write_string(&b, query_name) if query_value != "" { write_string(&b, "=") write_string(&b, query_value) } + if i < query_length - 1 { + write_string(&b, "&") + } + i += 1 } return to_string(b) @@ -119,12 +127,10 @@ percent_decode :: proc(encoded_string: string, allocator := context.allocator) - builder_grow(&b, len(encoded_string)) defer if !ok do builder_destroy(&b) - stack_buf: [4]u8 - pending := mem.buffer_from_slice(stack_buf[:]) s := encoded_string for len(s) > 0 { - i := index_rune(s, '%') + i := index_byte(s, '%') if i == -1 { write_string(&b, s) // no '%'s; the string is already decoded break @@ -137,47 +143,15 @@ percent_decode :: proc(encoded_string: string, allocator := context.allocator) - s = s[1:] if s[0] == '%' { - write_rune(&b, '%') + write_byte(&b, '%') s = s[1:] continue } if len(s) < 2 do return // percent without encoded value - n: int - n, _ = strconv.parse_int(s[:2], 16) - switch n { - case 0x20: write_rune(&b, ' ') - case 0x21: write_rune(&b, '!') - case 0x23: write_rune(&b, '#') - case 0x24: write_rune(&b, '$') - case 0x25: write_rune(&b, '%') - case 0x26: write_rune(&b, '&') - case 0x27: write_rune(&b, '\'') - case 0x28: write_rune(&b, '(') - case 0x29: write_rune(&b, ')') - case 0x2A: write_rune(&b, '*') - case 0x2B: write_rune(&b, '+') - case 0x2C: write_rune(&b, ',') - case 0x2F: write_rune(&b, '/') - case 0x3A: write_rune(&b, ':') - case 0x3B: write_rune(&b, ';') - case 0x3D: write_rune(&b, '=') - case 0x3F: write_rune(&b, '?') - case 0x40: write_rune(&b, '@') - case 0x5B: write_rune(&b, '[') - case 0x5D: write_rune(&b, ']') - case: - // utf-8 bytes - // TODO(tetra): Audit this - 4 bytes??? - append(&pending, s[0]) - append(&pending, s[1]) - if len(pending) == 4 { - r, _ := utf8.decode_rune(pending[:]) - write_rune(&b, r) - clear(&pending) - } - } + val := hex.decode_sequence(s[:2]) or_return + write_byte(&b, val) s = s[2:] } diff --git a/core/os/file_windows.odin b/core/os/file_windows.odin index a5f329d45..9d62014af 100644 --- a/core/os/file_windows.odin +++ b/core/os/file_windows.odin @@ -220,6 +220,7 @@ file_size :: proc(fd: Handle) -> (i64, Errno) { @(private) MAX_RW :: 1<<30 +ERROR_EOF :: 38 @(private) pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { @@ -228,11 +229,6 @@ pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { buf = buf[:MAX_RW] } - curr_offset, e := seek(fd, offset, 1) - if e != 0 { - return 0, e - } - defer seek(fd, curr_offset, 0) o := win32.OVERLAPPED{ OffsetHigh = u32(offset>>32), @@ -243,6 +239,7 @@ pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { h := win32.HANDLE(fd) done: win32.DWORD + e: Errno if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) { e = Errno(win32.GetLastError()) done = 0 @@ -256,11 +253,6 @@ pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { buf = buf[:MAX_RW] } - curr_offset, e := seek(fd, offset, 1) - if e != 0 { - return 0, e - } - defer seek(fd, curr_offset, 0) o := win32.OVERLAPPED{ OffsetHigh = u32(offset>>32), @@ -269,6 +261,7 @@ pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { h := win32.HANDLE(fd) done: win32.DWORD + e: Errno if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) { e = Errno(win32.GetLastError()) done = 0 @@ -276,6 +269,16 @@ pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { return int(done), e } +/* +read_at returns n: 0, err: 0 on EOF +on Windows, read_at changes the position of the file cursor, on *nix, it does not. + + bytes: [8]u8{} + read_at(fd, bytes, 0) + read(fd, bytes) + +will read from the location twice on *nix, and from two different locations on Windows +*/ read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) { if offset < 0 { return 0, ERROR_NEGATIVE_OFFSET @@ -284,6 +287,10 @@ read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) { b, offset := data, offset for len(b) > 0 { m, e := pread(fd, b, offset) + if e == ERROR_EOF { + err = 0 + break + } if e != 0 { err = e break @@ -294,6 +301,16 @@ read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) { } return } + +/* +on Windows, write_at changes the position of the file cursor, on *nix, it does not. + + bytes: [8]u8{} + write_at(fd, bytes, 0) + write(fd, bytes) + +will write to the location twice on *nix, and to two different locations on Windows +*/ write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) { if offset < 0 { return 0, ERROR_NEGATIVE_OFFSET diff --git a/core/os/os.odin b/core/os/os.odin index 971abd911..b71ea261e 100644 --- a/core/os/os.odin +++ b/core/os/os.odin @@ -194,7 +194,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, ptr := uintptr(aligned_mem) aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a) diff := int(aligned_ptr - ptr) - if (size + diff) > space { + if (size + diff) > space || allocated_mem == nil { return nil, .Out_Of_Memory } diff --git a/core/os/os2/env_linux.odin b/core/os/os2/env_linux.odin index 9a2c52739..e7165b583 100644 --- a/core/os/os2/env_linux.odin +++ b/core/os/os2/env_linux.odin @@ -3,7 +3,7 @@ package os2 import "core:runtime" -_get_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) { +_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) { //TODO return } diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index 1a6f69d31..890bbfc43 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -9,19 +9,20 @@ import "core:sys/unix" INVALID_HANDLE :: -1 -_O_RDONLY :: 0o0 -_O_WRONLY :: 0o1 -_O_RDWR :: 0o2 -_O_CREAT :: 0o100 -_O_EXCL :: 0o200 -_O_TRUNC :: 0o1000 -_O_APPEND :: 0o2000 -_O_NONBLOCK :: 0o4000 -_O_LARGEFILE :: 0o100000 -_O_DIRECTORY :: 0o200000 -_O_NOFOLLOW :: 0o400000 -_O_SYNC :: 0o4010000 -_O_CLOEXEC :: 0o2000000 +_O_RDONLY :: 0o00000000 +_O_WRONLY :: 0o00000001 +_O_RDWR :: 0o00000002 +_O_CREAT :: 0o00000100 +_O_EXCL :: 0o00000200 +_O_NOCTTY :: 0o00000400 +_O_TRUNC :: 0o00001000 +_O_APPEND :: 0o00002000 +_O_NONBLOCK :: 0o00004000 +_O_LARGEFILE :: 0o00100000 +_O_DIRECTORY :: 0o00200000 +_O_NOFOLLOW :: 0o00400000 +_O_SYNC :: 0o04010000 +_O_CLOEXEC :: 0o02000000 _O_PATH :: 0o10000000 _AT_FDCWD :: -100 @@ -40,9 +41,12 @@ _file_allocator :: proc() -> runtime.Allocator { _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (^File, Error) { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - name_cstr := _name_to_cstring(name) + name_cstr := strings.clone_to_cstring(name, context.temp_allocator) - flags_i: int + // Just default to using O_NOCTTY because needing to open a controlling + // terminal would be incredibly rare. This has no effect on files while + // allowing us to open serial devices. + flags_i: int = _O_NOCTTY switch flags & O_RDONLY|O_WRONLY|O_RDWR { case O_RDONLY: flags_i = _O_RDONLY case O_WRONLY: flags_i = _O_WRONLY @@ -56,7 +60,7 @@ _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (^File, Error flags_i |= (_O_TRUNC * int(.Trunc in flags)) flags_i |= (_O_CLOEXEC * int(.Close_On_Exec in flags)) - fd := unix.sys_open(name_cstr, flags_i, int(perm)) + fd := unix.sys_open(name_cstr, flags_i, uint(perm)) if fd < 0 { return nil, _get_platform_error(fd) } @@ -196,10 +200,7 @@ _truncate :: proc(f: ^File, size: i64) -> Error { } _remove :: proc(name: string) -> Error { - name_cstr, allocated := _name_to_cstring(name) - defer if allocated { - delete(name_cstr) - } + name_cstr := strings.clone_to_cstring(name, context.temp_allocator) fd := unix.sys_open(name_cstr, int(File_Flags.Read)) if fd < 0 { @@ -214,40 +215,22 @@ _remove :: proc(name: string) -> Error { } _rename :: proc(old_name, new_name: string) -> Error { - old_name_cstr, old_allocated := _name_to_cstring(old_name) - new_name_cstr, new_allocated := _name_to_cstring(new_name) - defer if old_allocated { - delete(old_name_cstr) - } - defer if new_allocated { - delete(new_name_cstr) - } + old_name_cstr := strings.clone_to_cstring(old_name, context.temp_allocator) + new_name_cstr := strings.clone_to_cstring(new_name, context.temp_allocator) return _ok_or_error(unix.sys_rename(old_name_cstr, new_name_cstr)) } _link :: proc(old_name, new_name: string) -> Error { - old_name_cstr, old_allocated := _name_to_cstring(old_name) - new_name_cstr, new_allocated := _name_to_cstring(new_name) - defer if old_allocated { - delete(old_name_cstr) - } - defer if new_allocated { - delete(new_name_cstr) - } + old_name_cstr := strings.clone_to_cstring(old_name, context.temp_allocator) + new_name_cstr := strings.clone_to_cstring(new_name, context.temp_allocator) return _ok_or_error(unix.sys_link(old_name_cstr, new_name_cstr)) } _symlink :: proc(old_name, new_name: string) -> Error { - old_name_cstr, old_allocated := _name_to_cstring(old_name) - new_name_cstr, new_allocated := _name_to_cstring(new_name) - defer if old_allocated { - delete(old_name_cstr) - } - defer if new_allocated { - delete(new_name_cstr) - } + old_name_cstr := strings.clone_to_cstring(old_name, context.temp_allocator) + new_name_cstr := strings.clone_to_cstring(new_name, context.temp_allocator) return _ok_or_error(unix.sys_symlink(old_name_cstr, new_name_cstr)) } @@ -271,26 +254,17 @@ _read_link_cstr :: proc(name_cstr: cstring, allocator: runtime.Allocator) -> (st } _read_link :: proc(name: string, allocator: runtime.Allocator) -> (string, Error) { - name_cstr, allocated := _name_to_cstring(name) - defer if allocated { - delete(name_cstr) - } + name_cstr := strings.clone_to_cstring(name, context.temp_allocator) return _read_link_cstr(name_cstr, allocator) } _unlink :: proc(name: string) -> Error { - name_cstr, allocated := _name_to_cstring(name) - defer if allocated { - delete(name_cstr) - } + name_cstr := strings.clone_to_cstring(name, context.temp_allocator) return _ok_or_error(unix.sys_unlink(name_cstr)) } _chdir :: proc(name: string) -> Error { - name_cstr, allocated := _name_to_cstring(name) - defer if allocated { - delete(name_cstr) - } + name_cstr := strings.clone_to_cstring(name, context.temp_allocator) return _ok_or_error(unix.sys_chdir(name_cstr)) } @@ -299,32 +273,23 @@ _fchdir :: proc(f: ^File) -> Error { } _chmod :: proc(name: string, mode: File_Mode) -> Error { - name_cstr, allocated := _name_to_cstring(name) - defer if allocated { - delete(name_cstr) - } - return _ok_or_error(unix.sys_chmod(name_cstr, int(mode))) + name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + return _ok_or_error(unix.sys_chmod(name_cstr, uint(mode))) } _fchmod :: proc(f: ^File, mode: File_Mode) -> Error { - return _ok_or_error(unix.sys_fchmod(f.impl.fd, int(mode))) + return _ok_or_error(unix.sys_fchmod(f.impl.fd, uint(mode))) } // NOTE: will throw error without super user priviledges _chown :: proc(name: string, uid, gid: int) -> Error { - name_cstr, allocated := _name_to_cstring(name) - defer if allocated { - delete(name_cstr) - } + name_cstr := strings.clone_to_cstring(name, context.temp_allocator) return _ok_or_error(unix.sys_chown(name_cstr, uid, gid)) } // NOTE: will throw error without super user priviledges _lchown :: proc(name: string, uid, gid: int) -> Error { - name_cstr, allocated := _name_to_cstring(name) - defer if allocated { - delete(name_cstr) - } + name_cstr := strings.clone_to_cstring(name, context.temp_allocator) return _ok_or_error(unix.sys_lchown(name_cstr, uid, gid)) } @@ -334,10 +299,7 @@ _fchown :: proc(f: ^File, uid, gid: int) -> Error { } _chtimes :: proc(name: string, atime, mtime: time.Time) -> Error { - name_cstr, allocated := _name_to_cstring(name) - defer if allocated { - delete(name_cstr) - } + name_cstr := strings.clone_to_cstring(name, context.temp_allocator) times := [2]Unix_File_Time { { atime._nsec, 0 }, { mtime._nsec, 0 }, @@ -354,18 +316,12 @@ _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error { } _exists :: proc(name: string) -> bool { - name_cstr, allocated := _name_to_cstring(name) - defer if allocated { - delete(name_cstr) - } + name_cstr := strings.clone_to_cstring(name, context.temp_allocator) return unix.sys_access(name_cstr, F_OK) == 0 } _is_file :: proc(name: string) -> bool { - name_cstr, allocated := _name_to_cstring(name) - defer if allocated { - delete(name_cstr) - } + name_cstr := strings.clone_to_cstring(name, context.temp_allocator) s: _Stat res := unix.sys_stat(name_cstr, &s) if res < 0 { @@ -384,10 +340,7 @@ _is_file_fd :: proc(fd: int) -> bool { } _is_dir :: proc(name: string) -> bool { - name_cstr, allocated := _name_to_cstring(name) - defer if allocated { - delete(name_cstr) - } + name_cstr := strings.clone_to_cstring(name, context.temp_allocator) s: _Stat res := unix.sys_stat(name_cstr, &s) if res < 0 { diff --git a/core/os/os2/heap_linux.odin b/core/os/os2/heap_linux.odin index b631268a1..136c3e6cb 100644 --- a/core/os/os2/heap_linux.odin +++ b/core/os/os2/heap_linux.odin @@ -166,7 +166,7 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, ptr := uintptr(aligned_mem) aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a) diff := int(aligned_ptr - ptr) - if (size + diff) > space { + if (size + diff) > space || allocated_mem == nil { return nil, .Out_Of_Memory } diff --git a/core/os/os2/heap_windows.odin b/core/os/os2/heap_windows.odin index 0f154cd8c..eba403c1d 100644 --- a/core/os/os2/heap_windows.odin +++ b/core/os/os2/heap_windows.odin @@ -52,7 +52,7 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, ptr := uintptr(aligned_mem) aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a) diff := int(aligned_ptr - ptr) - if (size + diff) > space { + if (size + diff) > space || allocated_mem == nil { return nil, .Out_Of_Memory } diff --git a/core/os/os2/path_linux.odin b/core/os/os2/path_linux.odin index 3120fcda3..2a0ef29d8 100644 --- a/core/os/os2/path_linux.odin +++ b/core/os/os2/path_linux.odin @@ -31,11 +31,8 @@ _mkdir :: proc(path: string, perm: File_Mode) -> Error { return .Invalid_Argument } - path_cstr, allocated := _name_to_cstring(path) - defer if allocated { - delete(path_cstr) - } - return _ok_or_error(unix.sys_mkdir(path_cstr, int(perm & 0o777))) + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + return _ok_or_error(unix.sys_mkdir(path_cstr, uint(perm & 0o777))) } _mkdir_all :: proc(path: string, perm: File_Mode) -> Error { @@ -49,7 +46,7 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error { new_dfd := unix.sys_openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS) switch new_dfd { case -ENOENT: - if res := unix.sys_mkdirat(dfd, cstring(&path[0]), perm); res < 0 { + if res := unix.sys_mkdirat(dfd, cstring(&path[0]), uint(perm)); res < 0 { return _get_platform_error(res) } has_created^ = true @@ -83,9 +80,6 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error { } else { path_bytes = make([]u8, len(path) + 1, context.temp_allocator) } - defer if allocated { - delete(path_bytes) - } // NULL terminate the byte slice to make it a valid cstring copy(path_bytes, path) @@ -182,10 +176,7 @@ _remove_all :: proc(path: string) -> Error { return nil } - path_cstr, allocated := _name_to_cstring(path) - defer if allocated { - delete(path_cstr) - } + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) fd := unix.sys_open(path_cstr, _OPENDIR_FLAGS) switch fd { @@ -222,10 +213,7 @@ _getwd :: proc(allocator: runtime.Allocator) -> (string, Error) { } _setwd :: proc(dir: string) -> Error { - dir_cstr, allocated := _name_to_cstring(dir) - defer if allocated { - delete(dir_cstr) - } + dir_cstr := strings.clone_to_cstring(dir, context.temp_allocator) return _ok_or_error(unix.sys_chdir(dir_cstr)) } diff --git a/core/os/os2/stat_linux.odin b/core/os/os2/stat_linux.odin index 2f99f6d2e..530e0e7d0 100644 --- a/core/os/os2/stat_linux.odin +++ b/core/os/os2/stat_linux.odin @@ -3,6 +3,7 @@ package os2 import "core:time" import "core:runtime" +import "core:strings" import "core:sys/unix" import "core:path/filepath" @@ -112,10 +113,7 @@ _fstat_internal :: proc(fd: int, allocator: runtime.Allocator) -> (File_Info, Er // NOTE: _stat and _lstat are using _fstat to avoid a race condition when populating fullpath _stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { - name_cstr, allocated := _name_to_cstring(name) - defer if allocated { - delete(name_cstr) - } + name_cstr := strings.clone_to_cstring(name, context.temp_allocator) fd := unix.sys_open(name_cstr, _O_RDONLY) if fd < 0 { @@ -126,10 +124,8 @@ _stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) } _lstat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { - name_cstr, allocated := _name_to_cstring(name) - defer if allocated { - delete(name_cstr) - } + name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + fd := unix.sys_open(name_cstr, _O_RDONLY | _O_PATH | _O_NOFOLLOW) if fd < 0 { return {}, _get_platform_error(fd) @@ -143,10 +139,7 @@ _same_file :: proc(fi1, fi2: File_Info) -> bool { } _stat_internal :: proc(name: string) -> (s: _Stat, res: int) { - name_cstr, allocated := _name_to_cstring(name) - defer if allocated { - delete(name_cstr) - } + name_cstr := strings.clone_to_cstring(name, context.temp_allocator) res = unix.sys_stat(name_cstr, &s) return } diff --git a/core/os/os2/user.odin b/core/os/os2/user.odin index 00cccd2a7..0e9f126aa 100644 --- a/core/os/os2/user.odin +++ b/core/os/os2/user.odin @@ -8,12 +8,12 @@ user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error case .Windows: dir = get_env("LocalAppData", allocator) if dir != "" { - dir = strings.clone_safe(dir, allocator) or_return + dir = strings.clone(dir, allocator) or_return } case .Darwin: dir = get_env("HOME", allocator) if dir != "" { - dir = strings.concatenate_safe({dir, "/Library/Caches"}, allocator) or_return + dir = strings.concatenate({dir, "/Library/Caches"}, allocator) or_return } case: // All other UNIX systems dir = get_env("XDG_CACHE_HOME", allocator) @@ -22,7 +22,7 @@ user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error if dir == "" { return } - dir = strings.concatenate_safe({dir, "/.cache"}, allocator) or_return + dir = strings.concatenate({dir, "/.cache"}, allocator) or_return } } if dir == "" { @@ -36,12 +36,12 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro case .Windows: dir = get_env("AppData", allocator) if dir != "" { - dir = strings.clone_safe(dir, allocator) or_return + dir = strings.clone(dir, allocator) or_return } case .Darwin: dir = get_env("HOME", allocator) if dir != "" { - dir = strings.concatenate_safe({dir, "/Library/Application Support"}, allocator) or_return + dir = strings.concatenate({dir, "/Library/Application Support"}, allocator) or_return } case: // All other UNIX systems dir = get_env("XDG_CACHE_HOME", allocator) @@ -50,7 +50,7 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro if dir == "" { return } - dir = strings.concatenate_safe({dir, "/.config"}, allocator) or_return + dir = strings.concatenate({dir, "/.config"}, allocator) or_return } } if dir == "" { diff --git a/core/prof/spall/spall.odin b/core/prof/spall/spall.odin index 6a78c466e..19a05d70a 100644 --- a/core/prof/spall/spall.odin +++ b/core/prof/spall/spall.odin @@ -67,16 +67,15 @@ Buffer :: struct { BUFFER_DEFAULT_SIZE :: 0x10_0000 -context_create :: proc(filename: string) -> (ctx: Context, ok: bool) #optional_ok { +context_create_with_scale :: proc(filename: string, precise_time: bool, timestamp_scale: f64) -> (ctx: Context, ok: bool) #optional_ok { fd, err := os.open(filename, os.O_WRONLY | os.O_APPEND | os.O_CREATE | os.O_TRUNC, 0o600) if err != os.ERROR_NONE { return } - ctx.fd = fd - freq, freq_ok := time.tsc_frequency() - ctx.precise_time = freq_ok - ctx.timestamp_scale = ((1 / f64(freq)) * 1_000_000) if freq_ok else 1 + ctx.fd = fd + ctx.precise_time = precise_time + ctx.timestamp_scale = timestamp_scale temp := [size_of(Manual_Header)]u8{} _build_header(temp[:], ctx.timestamp_scale) @@ -85,6 +84,14 @@ context_create :: proc(filename: string) -> (ctx: Context, ok: bool) #optional_o return } +context_create_with_sleep :: proc(filename: string, sleep := 2 * time.Second) -> (ctx: Context, ok: bool) #optional_ok { + freq, freq_ok := time.tsc_frequency(sleep) + timestamp_scale: f64 = ((1 / f64(freq)) * 1_000_000) if freq_ok else 1 + return context_create_with_scale(filename, freq_ok, timestamp_scale) +} + +context_create :: proc{context_create_with_scale, context_create_with_sleep} + context_destroy :: proc(ctx: ^Context) { if ctx == nil { return diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin index 4b152e7cc..bd919247e 100644 --- a/core/runtime/core_builtin.odin +++ b/core/runtime/core_builtin.odin @@ -401,7 +401,7 @@ append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> int { return 0 } prev_len := len(array) - resize(array, len(array)+1) + resize(array, len(array)+1, loc) return len(array)-prev_len } diff --git a/core/runtime/os_specific_windows.odin b/core/runtime/os_specific_windows.odin index 6e7474257..732715793 100644 --- a/core/runtime/os_specific_windows.odin +++ b/core/runtime/os_specific_windows.odin @@ -112,7 +112,7 @@ _windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, ol ptr := uintptr(aligned_mem) aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a) diff := int(aligned_ptr - ptr) - if (size + diff) > space { + if (size + diff) > space || allocated_mem == nil { return nil, .Out_Of_Memory } diff --git a/core/sys/info/cpu_intel.odin b/core/sys/info/cpu_intel.odin index 8cd723203..5a11863d4 100644 --- a/core/sys/info/cpu_intel.odin +++ b/core/sys/info/cpu_intel.odin @@ -38,7 +38,7 @@ cpu_name: Maybe(string) @(init, private) init_cpu_features :: proc "c" () { is_set :: #force_inline proc "c" (hwc: u32, value: u32) -> bool { - return hwc&value != 0 + return hwc&(1 << value) != 0 } try_set :: #force_inline proc "c" (set: ^CPU_Features, feature: CPU_Feature, hwc: u32, value: u32) { if is_set(hwc, value) { @@ -74,8 +74,15 @@ init_cpu_features :: proc "c" () { return } + // In certain rare cases (reason unknown), XGETBV generates an + // illegal instruction, even if OSXSAVE is set per CPUID. + // + // When Chrome ran into this problem, the problem went away + // after they started checking both OSXSAVE and XSAVE. + // + // See: crbug.com/375968 os_supports_avx := false - if .os_xsave in set { + if .os_xsave in set && is_set(26, ecx1) { eax, _ := xgetbv(0) os_supports_avx = is_set(1, eax) && is_set(2, eax) } diff --git a/core/time/perf.odin b/core/time/perf.odin index 69f7eceaa..87192093a 100644 --- a/core/time/perf.odin +++ b/core/time/perf.odin @@ -70,7 +70,7 @@ has_invariant_tsc :: proc "contextless" () -> bool { return false } -tsc_frequency :: proc "contextless" () -> (u64, bool) { +tsc_frequency :: proc "contextless" (fallback_sleep := 2 * Second) -> (u64, bool) { if !has_invariant_tsc() { return 0, false } @@ -81,7 +81,7 @@ tsc_frequency :: proc "contextless" () -> (u64, bool) { tsc_begin := intrinsics.read_cycle_counter() tick_begin := tick_now() - sleep(2 * Second) + sleep(fallback_sleep) tsc_end := intrinsics.read_cycle_counter() tick_end := tick_now() diff --git a/core/time/time.odin b/core/time/time.odin index 74c80c8f7..6c424a62e 100644 --- a/core/time/time.odin +++ b/core/time/time.odin @@ -170,6 +170,12 @@ day :: proc "contextless" (t: Time) -> (day: int) { return } +weekday :: proc "contextless" (t: Time) -> (weekday: Weekday) { + abs := _time_abs(t) + sec := (abs + u64(Weekday.Monday) * SECONDS_PER_DAY) % SECONDS_PER_WEEK + return Weekday(int(sec) / SECONDS_PER_DAY) +} + clock :: proc { clock_from_time, clock_from_duration, clock_from_stopwatch } clock_from_time :: proc "contextless" (t: Time) -> (hour, min, sec: int) { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 6eb517251..0db12aba0 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7887,8 +7887,11 @@ gb_internal ExprKind check_or_return_expr(CheckerContext *c, Operand *o, Ast *no rhs.type = right_type; rhs.mode = Addressing_Value; - // TODO(bill): better error message - if (!check_is_assignable_to(c, &rhs, end_type)) { + if (is_type_boolean(right_type) && is_type_boolean(end_type)) { + // NOTE(bill): allow implicit conversion between boolean types + // within 'or_return' to improve the experience using third-party code + } else if (!check_is_assignable_to(c, &rhs, end_type)) { + // TODO(bill): better error message gbString a = type_to_string(right_type); gbString b = type_to_string(end_type); gbString ret_type = type_to_string(result_type); diff --git a/src/error.cpp b/src/error.cpp index 6314c43bb..defc2593f 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -8,8 +8,9 @@ struct ErrorCollector { BlockingMutex string_mutex; RecursiveMutex block_mutex; - Array error_buffer; - Array errors; + RecursiveMutex error_buffer_mutex; + Array error_buffer; + Array errors; }; gb_global ErrorCollector global_error_collector; @@ -119,6 +120,7 @@ gb_internal void begin_error_block(void) { } gb_internal void end_error_block(void) { + mutex_lock(&global_error_collector.error_buffer_mutex); isize n = global_error_collector.error_buffer.count; if (n > 0) { u8 *text = global_error_collector.error_buffer.data; @@ -150,11 +152,16 @@ gb_internal void end_error_block(void) { text = gb_alloc_array(permanent_allocator(), u8, n+1); gb_memmove(text, global_error_collector.error_buffer.data, n); text[n] = 0; + + + mutex_lock(&global_error_collector.error_out_mutex); String s = {text, n}; array_add(&global_error_collector.errors, s); + mutex_unlock(&global_error_collector.error_out_mutex); + global_error_collector.error_buffer.count = 0; } - + mutex_unlock(&global_error_collector.error_buffer_mutex); global_error_collector.in_block.store(false); mutex_unlock(&global_error_collector.block_mutex); } @@ -172,11 +179,15 @@ gb_internal ERROR_OUT_PROC(default_error_out_va) { isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va); isize n = len-1; if (global_error_collector.in_block) { + mutex_lock(&global_error_collector.error_buffer_mutex); + isize cap = global_error_collector.error_buffer.count + n; array_reserve(&global_error_collector.error_buffer, cap); u8 *data = global_error_collector.error_buffer.data + global_error_collector.error_buffer.count; gb_memmove(data, buf, n); global_error_collector.error_buffer.count += n; + + mutex_unlock(&global_error_collector.error_buffer_mutex); } else { mutex_lock(&global_error_collector.error_out_mutex); { diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index c1d3471f3..412698368 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -2083,10 +2083,12 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { Type *elem = base_array_type(dst); lbValue e = lb_emit_conv(p, value, elem); lbAddr v = lb_add_local_generated(p, t, false); - for (i64 i = 0; i < dst->Matrix.row_count; i++) { - isize j = cast(isize)i; - lbValue ptr = lb_emit_matrix_epi(p, v.addr, j, j); - lb_emit_store(p, ptr, e); + lbValue zero = lb_const_value(p->module, elem, exact_value_i64(0), true); + for (i64 j = 0; j < dst->Matrix.column_count; j++) { + for (i64 i = 0; i < dst->Matrix.row_count; i++) { + lbValue ptr = lb_emit_matrix_epi(p, v.addr, i, j); + lb_emit_store(p, ptr, i == j ? e : zero); + } } diff --git a/src/parser.cpp b/src/parser.cpp index f33a44f31..698ba99ab 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2462,7 +2462,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { } is_raw_union = true; } else if (tag.string == "no_copy") { - if (is_packed) { + if (no_copy) { syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string)); } no_copy = true; diff --git a/src/ptr_map.cpp b/src/ptr_map.cpp index fbde98693..23278014f 100644 --- a/src/ptr_map.cpp +++ b/src/ptr_map.cpp @@ -47,7 +47,7 @@ gb_internal gb_inline u32 ptr_map_hash_key(uintptr key) { key = key ^ (key << 28); res = cast(u32)key; #elif defined(GB_ARCH_32_BIT) - u32 state = ((u32)key) * 747796405u + 2891336453u; + u32 state = (cast(u32)key) * 747796405u + 2891336453u; u32 word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; res = (word >> 22u) ^ word; #endif diff --git a/tests/core/encoding/hex/test_core_hex.odin b/tests/core/encoding/hex/test_core_hex.odin new file mode 100644 index 000000000..a5daa206e --- /dev/null +++ b/tests/core/encoding/hex/test_core_hex.odin @@ -0,0 +1,93 @@ +package test_core_hex + +import "core:encoding/hex" +import "core:testing" +import "core:fmt" +import "core:os" +import "core:bytes" + +TEST_count := 0 +TEST_fail := 0 + +when ODIN_TEST { + expect :: testing.expect + log :: testing.log +} else { + expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) { + TEST_count += 1 + if !condition { + TEST_fail += 1 + fmt.printf("[%v] %v\n", loc, message) + return + } + } + log :: proc(t: ^testing.T, v: any, loc := #caller_location) { + fmt.printf("[%v] ", loc) + fmt.printf("log: %v\n", v) + } +} + +main :: proc() { + t := testing.T{} + + hex_encode(&t) + hex_decode(&t) + hex_decode_sequence(&t) + + fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) + if TEST_fail > 0 { + os.exit(1) + } +} + +CASES :: [][2]string{ + {"11", "3131"}, + {"g", "67"}, + {"Hello", "48656c6c6f"}, +} + +@(test) +hex_encode :: proc(t: ^testing.T) { + for test in CASES { + encoded := string(hex.encode(transmute([]byte)test[0])) + expect( + t, + encoded == test[1], + fmt.tprintf("encode: %q -> %q (should be: %q)", test[0], encoded, test[1]), + ) + } +} + +@(test) +hex_decode :: proc(t: ^testing.T) { + for test in CASES { + decoded, ok := hex.decode(transmute([]byte)test[1]) + expect(t, ok, fmt.tprintf("decode: %q not ok", test[1])) + expect( + t, + string(decoded) == test[0], + fmt.tprintf("decode: %q -> %q (should be: %q)", test[1], string(decoded), test[0]), + ) + } +} + +@(test) +hex_decode_sequence :: proc(t: ^testing.T) { + b, ok := hex.decode_sequence("0x23") + expect(t, ok, "decode_sequence: 0x23 not ok") + expect(t, b == '#', fmt.tprintf("decode_sequence: 0x23 -> %c (should be: %c)", b, '#')) + + b, ok = hex.decode_sequence("0X3F") + expect(t, ok, "decode_sequence: 0X3F not ok") + expect(t, b == '?', fmt.tprintf("decode_sequence: 0X3F -> %c (should be: %c)", b, '?')) + + b, ok = hex.decode_sequence("2a") + expect(t, ok, "decode_sequence: 2a not ok") + expect(t, b == '*', fmt.tprintf("decode_sequence: 2a -> %c (should be: %c)", b, '*')) + + _, ok = hex.decode_sequence("1") + expect(t, !ok, "decode_sequence: 1 should be too short") + + _, ok = hex.decode_sequence("123") + expect(t, !ok, "decode_sequence: 123 should be too long") +} diff --git a/tests/core/net/test_core_net.odin b/tests/core/net/test_core_net.odin index 00c29db95..14e1cbb17 100644 --- a/tests/core/net/test_core_net.odin +++ b/tests/core/net/test_core_net.odin @@ -67,6 +67,9 @@ main :: proc() { tcp_tests(t) } + split_url_test(t) + join_url_test(t) + fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) print_tracking_allocator_report() @@ -508,4 +511,75 @@ client_sends_server_data :: proc(t: ^testing.T) { okay = received == CONTENT msg = fmt.tprintf("Expected client to send \"{}\", got \"{}\"", CONTENT, received) expect(t, okay, msg) -} \ No newline at end of file +} + +URL_Test :: struct { + scheme, host, path: string, + queries: map[string]string, + url: string, +} + +@test +split_url_test :: proc(t: ^testing.T) { + test_cases := []URL_Test{ + { "http", "example.com", "/", {}, "http://example.com" }, + { "https", "odin-lang.org", "/", {}, "https://odin-lang.org" }, + { "https", "odin-lang.org", "/docs/", {}, "https://odin-lang.org/docs/" }, + { "https", "odin-lang.org", "/docs/overview", {}, "https://odin-lang.org/docs/overview" }, + { "http", "example.com", "/", {"a" = "b"}, "http://example.com?a=b" }, + { "http", "example.com", "/", {"a" = ""}, "http://example.com?a" }, + { "http", "example.com", "/", {"a" = "b", "c" = "d"}, "http://example.com?a=b&c=d" }, + { "http", "example.com", "/", {"a" = "", "c" = "d"}, "http://example.com?a&c=d" }, + { "http", "example.com", "/example", {"a" = "", "b" = ""}, "http://example.com/example?a&b" }, + } + + for test in test_cases { + scheme, host, path, queries := net.split_url(test.url) + defer { + delete(queries) + delete(test.queries) + } + + msg := fmt.tprintf("Expected `net.split_url` to return %s, got %s", test.scheme, scheme) + expect(t, scheme == test.scheme, msg) + msg = fmt.tprintf("Expected `net.split_url` to return %s, got %s", test.host, host) + expect(t, host == test.host, msg) + msg = fmt.tprintf("Expected `net.split_url` to return %s, got %s", test.path, path) + expect(t, path == test.path, msg) + msg = fmt.tprintf("Expected `net.split_url` to return %d queries, got %d queries", len(test.queries), len(queries)) + expect(t, len(queries) == len(test.queries), msg) + for k, v in queries { + expected := test.queries[k] + msg = fmt.tprintf("Expected `net.split_url` to return %s, got %s", expected, v) + expect(t, v == expected, msg) + } + } +} + + +@test +join_url_test :: proc(t: ^testing.T) { + test_cases := []URL_Test{ + { "http", "example.com", "", {}, "http://example.com" }, + { "https", "odin-lang.org", "", {}, "https://odin-lang.org" }, + { "https", "odin-lang.org", "docs/", {}, "https://odin-lang.org/docs/" }, + { "https", "odin-lang.org", "/docs/overview", {}, "https://odin-lang.org/docs/overview" }, + { "http", "example.com", "", {"a" = "b"}, "http://example.com?a=b" }, + { "http", "example.com", "", {"a" = ""}, "http://example.com?a" }, + { "http", "example.com", "", {"a" = "b", "c" = "d"}, "http://example.com?a=b&c=d" }, + { "http", "example.com", "", {"a" = "", "c" = "d"}, "http://example.com?a&c=d" }, + { "http", "example.com", "example", {"a" = "", "b" = ""}, "http://example.com/example?a&b" }, + } + + for test in test_cases { + url := net.join_url(test.scheme, test.host, test.path, test.queries) + defer { + delete(url) + delete(test.queries) + } + + okay := url == test.url + msg := fmt.tprintf("Expected `net.join_url` to return %s, got %s", test.url, url) + expect(t, okay, msg) + } +} diff --git a/tests/issues/run.bat b/tests/issues/run.bat index ea5feddaa..87492bc29 100644 --- a/tests/issues/run.bat +++ b/tests/issues/run.bat @@ -9,6 +9,7 @@ set COMMON=-collection:tests=..\.. ..\..\..\odin test ..\test_issue_829.odin %COMMON% -file || exit /b ..\..\..\odin test ..\test_issue_1592.odin %COMMON% -file || exit /b +..\..\..\odin test ..\test_issue_2056.odin %COMMON% -file || exit /b ..\..\..\odin test ..\test_issue_2087.odin %COMMON% -file || exit /b ..\..\..\odin build ..\test_issue_2113.odin %COMMON% -file -debug || exit /b diff --git a/tests/issues/run.sh b/tests/issues/run.sh index 440c953d9..f894f2dae 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -10,6 +10,7 @@ set -x $ODIN test ../test_issue_829.odin $COMMON -file $ODIN test ../test_issue_1592.odin $COMMON -file +$ODIN test ../test_issue_2056.odin $COMMON -file $ODIN test ../test_issue_2087.odin $COMMON -file $ODIN build ../test_issue_2113.odin $COMMON -file -debug diff --git a/tests/issues/test_issue_2056.odin b/tests/issues/test_issue_2056.odin new file mode 100644 index 000000000..4869b557e --- /dev/null +++ b/tests/issues/test_issue_2056.odin @@ -0,0 +1,22 @@ +// Tests issue #2056 https://github.com/odin-lang/Odin/issues/2056 +package test_issues + +import "core:fmt" +import "core:testing" + +@test +test_scalar_matrix_conversion :: proc(t: ^testing.T) { + l := f32(1.0) + m := (matrix[4,4]f32)(l) + + for i in 0..<4 { + for j in 0..<4 { + if i == j { + testing.expect(t, m[i,j] == 1, fmt.tprintf("expected 1 at m[%d,%d], found %f\n", i, j, m[i,j])) + } else { + testing.expect(t, m[i,j] == 0, fmt.tprintf("expected 0 at m[%d,%d], found %f\n", i, j, m[i,j])) + } + } + } +} + diff --git a/tests/vendor/botan-3.dll b/tests/vendor/botan-3.dll new file mode 100644 index 000000000..d8f04720b Binary files /dev/null and b/tests/vendor/botan-3.dll differ diff --git a/tests/vendor/botan.dll b/tests/vendor/botan.dll deleted file mode 100644 index 423231d86..000000000 Binary files a/tests/vendor/botan.dll and /dev/null differ diff --git a/tests/vendor/botan/test_vendor_botan.odin b/tests/vendor/botan/test_vendor_botan.odin index 0a93723c8..51043d813 100644 --- a/tests/vendor/botan/test_vendor_botan.odin +++ b/tests/vendor/botan/test_vendor_botan.odin @@ -70,8 +70,8 @@ main :: proc() { test_sha3_256(&t) test_sha3_384(&t) test_sha3_512(&t) - test_shake_128(&t) - test_shake_256(&t) + // test_shake_128(&t) + // test_shake_256(&t) test_keccak_512(&t) test_whirlpool(&t) test_gost(&t) @@ -79,9 +79,9 @@ main :: proc() { test_streebog_512(&t) test_blake2b(&t) test_ripemd_160(&t) - test_tiger_128(&t) - test_tiger_160(&t) - test_tiger_192(&t) + // test_tiger_128(&t) + // test_tiger_160(&t) + // test_tiger_192(&t) test_sm3(&t) test_skein512_256(&t) test_skein512_512(&t) diff --git a/vendor/README.md b/vendor/README.md index e7d8d4047..03f4ef97c 100644 --- a/vendor/README.md +++ b/vendor/README.md @@ -143,7 +143,7 @@ Includes full bindings as well as wrappers to match the `core:crypto` API. See also LICENSE in the `commonmark` directory itself. Includes full bindings and Windows `.lib` and `.dll`. -## CommonMark +## zlib [zlib](https://github.com/madler/zlib) data compression library @@ -158,4 +158,4 @@ Includes full bindings. Used in: [bgfx](https://github.com/bkaradzic/bgfx), [Filament](https://github.com/google/filament), [gltfpack](https://github.com/zeux/meshoptimizer/tree/master/gltf), [raylib](https://github.com/raysan5/raylib), [Unigine](https://developer.unigine.com/en/docs/2.14.1/third_party?rlang=cpp#cgltf), and more! -Se also LICENCE in `cgltf` directory itself. \ No newline at end of file +See also LICENCE in `cgltf` directory itself. diff --git a/vendor/botan/bindings/botan-3.lib b/vendor/botan/bindings/botan-3.lib new file mode 100644 index 000000000..a3b94e3f6 Binary files /dev/null and b/vendor/botan/bindings/botan-3.lib differ diff --git a/vendor/botan/bindings/botan.lib b/vendor/botan/bindings/botan.lib deleted file mode 100644 index 5731855cb..000000000 Binary files a/vendor/botan/bindings/botan.lib and /dev/null differ diff --git a/vendor/botan/bindings/botan.odin b/vendor/botan/bindings/botan.odin index 2217eda5a..7c8b0997a 100644 --- a/vendor/botan/bindings/botan.odin +++ b/vendor/botan/bindings/botan.odin @@ -62,7 +62,7 @@ CRL_SIGN :: x509_cert_key_constraints(512) ENCIPHER_ONLY :: x509_cert_key_constraints(256) DECIPHER_ONLY :: x509_cert_key_constraints(128) -HASH_SHA1 :: "SHA1" +HASH_SHA1 :: "SHA-1" HASH_SHA_224 :: "SHA-224" HASH_SHA_256 :: "SHA-256" HASH_SHA_384 :: "SHA-384" @@ -141,10 +141,12 @@ fpe_struct :: struct{} fpe_t :: ^fpe_struct when ODIN_OS == .Windows { - foreign import botan_lib "botan.lib" + foreign import botan_lib "botan-3.lib" +} else when ODIN_OS == .Darwin { + foreign import botan_lib "system:botan-3" } else { foreign import botan_lib "system:botan-2" -} +} @(default_calling_convention="c") @(link_prefix="botan_") diff --git a/vendor/lua/5.4/lua.odin b/vendor/lua/5.4/lua.odin index 06199dee7..8aa274201 100644 --- a/vendor/lua/5.4/lua.odin +++ b/vendor/lua/5.4/lua.odin @@ -44,7 +44,7 @@ REGISTRYINDEX :: -MAXSTACK - 1000 ** space (and to reserve some numbers for pseudo-indices). ** (It must fit into max(size_t)/32.) */ -MAXSTACK :: 1000000 when size_of(rawptr) == 4 else 15000 +MAXSTACK :: 1000000 when size_of(rawptr) >= 4 else 15000 /*