Merge remote-tracking branch 'offical/master'

This commit is contained in:
2025-03-06 05:27:42 -05:00
158 changed files with 12539 additions and 4060 deletions
+3
View File
@@ -285,6 +285,9 @@ simd_reduce_xor :: proc(a: #simd[N]T) -> T where type_is_integer(T) || t
simd_reduce_any :: proc(a: #simd[N]T) -> T where type_is_boolean(T) ---
simd_reduce_all :: proc(a: #simd[N]T) -> T where type_is_boolean(T) ---
simd_extract_lsbs :: proc(a: #simd[N]T) -> bit_set[0..<N] where type_is_integer(T) || type_is_boolean(T) ---
simd_extract_msbs :: proc(a: #simd[N]T) -> bit_set[0..<N] where type_is_integer(T) || type_is_boolean(T) ---
simd_gather :: proc(ptr: #simd[N]rawptr, val: #simd[N]T, mask: #simd[N]U) -> #simd[N]T where type_is_integer(U) || type_is_boolean(U) ---
simd_scatter :: proc(ptr: #simd[N]rawptr, val: #simd[N]T, mask: #simd[N]U) where type_is_integer(U) || type_is_boolean(U) ---
+13 -47
View File
@@ -239,47 +239,6 @@ Type_Info :: struct {
},
}
// NOTE(bill): This must match the compiler's
Typeid_Kind :: enum u8 {
Invalid,
Integer,
Rune,
Float,
Complex,
Quaternion,
String,
Boolean,
Any,
Type_Id,
Pointer,
Multi_Pointer,
Procedure,
Array,
Enumerated_Array,
Dynamic_Array,
Slice,
Tuple,
Struct,
Union,
Enum,
Map,
Bit_Set,
Simd_Vector,
Matrix,
Soa_Pointer,
Bit_Field,
}
#assert(len(Typeid_Kind) < 32)
Typeid_Bit_Field :: bit_field uintptr {
index: uintptr | 8*size_of(uintptr) - 8,
kind: Typeid_Kind | 5, // Typeid_Kind
named: bool | 1,
special: bool | 1, // signed, cstring, etc
reserved: bool | 1,
}
#assert(size_of(Typeid_Bit_Field) == size_of(uintptr))
// NOTE(bill): only the ones that are needed (not all types)
// This will be set by the compiler
type_table: []^Type_Info
@@ -483,10 +442,14 @@ Raw_Any :: struct {
data: rawptr,
id: typeid,
}
when !ODIN_NO_RTTI {
#assert(size_of(Raw_Any) == size_of(any))
}
Raw_Cstring :: struct {
data: [^]byte,
}
#assert(size_of(Raw_Cstring) == size_of(cstring))
Raw_Soa_Pointer :: struct {
data: rawptr,
@@ -686,13 +649,16 @@ type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
type_info_base_without_enum :: type_info_core
__type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check {
MASK :: 1<<(8*size_of(typeid) - 8) - 1
data := transmute(uintptr)id
n := int(data & MASK)
if n < 0 || n >= len(type_table) {
n = 0
n := u64(len(type_table))
i := transmute(u64)id % n
for _ in 0..<n {
ptr := type_table[i]
if ptr != nil && ptr.id == id {
return ptr
}
i = i+1 if i+1 < n else 0
}
return type_table[n]
return type_table[0]
}
when !ODIN_NO_RTTI {
+1
View File
@@ -119,6 +119,7 @@ default_random_generator_proc :: proc(data: rawptr, mode: Random_Generator_Mode,
}
}
@(require_results)
default_random_generator :: proc "contextless" (state: ^Default_Random_State = nil) -> Random_Generator {
return {
procedure = default_random_generator_proc,
+5
View File
@@ -145,6 +145,11 @@ initialize_symbols :: proc(
}
}
// No field for it in the struct.
if handle == nil {
handle = load_library(library_path) or_return
}
// Buffer to concatenate the prefix + symbol name.
prefixed_symbol_buf: [2048]u8 = ---
+1 -1
View File
@@ -36,7 +36,7 @@ parse_or_exit :: proc(
args = program_args[1:]
}
error := parse(model, args, style)
error := parse(model, args, style, true, true, allocator, loc)
if error != nil {
stderr := os.stream_from_handle(os.stderr)
+9 -1
View File
@@ -613,6 +613,10 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true, newline :
i += 1
width_index, _, index_ok := _arg_number(fmt, &i, len(args))
if !index_ok {
width_index, index_ok = error_check_arg(fi, false, unused_args^)
}
if index_ok {
unused_args^ -= {width_index}
@@ -638,6 +642,10 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true, newline :
i += 1
precision_index, _, index_ok := _arg_number(fmt, &i, len(args))
if !index_ok {
precision_index, index_ok = error_check_arg(fi, false, unused_args^)
}
if index_ok {
unused_args^ -= {precision_index}
fi.prec, _, fi.prec_set = int_from_arg(args, precision_index)
@@ -1289,7 +1297,7 @@ fmt_rune :: proc(fi: ^Info, r: rune, verb: rune) {
case 'q', 'w':
fi.n += io.write_quoted_rune(fi.writer, r)
case:
fmt_int(fi, u64(r), false, 32, verb)
fmt_int(fi, u64(u32(r)), false, 32, verb)
}
}
// Formats an integer value according to the specified formatting verb.
-1
View File
@@ -19,7 +19,6 @@ default_random_generator :: runtime.default_random_generator
@(require_results)
create :: proc(seed: u64) -> (state: Default_Random_State) {
seed := seed
runtime.default_random_generator(&state)
runtime.default_random_generator_proc(&state, .Reset, ([^]byte)(&seed)[:size_of(seed)])
return
}
+2 -2
View File
@@ -16,8 +16,8 @@ a multipointer can be indexed, but does not have a definite length. A slice is
a pointer that points to multiple objects equipped with the length, specifying
the amount of objects a slice points to.
When object's values are read through a pointer, that operation is called a
*load* operation. When memory is read through a pointer, that operation is
When an object's values are read through a pointer, that operation is called a
*load* operation. When memory is written to through a pointer, that operation is
called a *store* operation. Both of these operations can be called a *memory
access operation*.
+2
View File
@@ -341,7 +341,9 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
new_end := start + size
if start < old_end && old_end == block.used && new_end <= block.reserved {
// grow data in-place, adjusting next allocation
prev_used := block.used
_ = alloc_from_memory_block(block, new_end - old_end, 1, default_commit_size=arena.default_commit_size) or_return
arena.total_used += block.used - prev_used
data = block.base[start:new_end]
return
}
+21 -9
View File
@@ -7,10 +7,11 @@ package net
*/
/*
Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
Copyright 2024 Feoramund <rune@swevencraft.org>.
Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
Copyright 2024 Feoramund <rune@swevencraft.org>.
Copyright 2025 Christiano Haesbaert <haesbaert@haesbaert.org>.
Made available under Odin's BSD-3 license.
List of contributors:
@@ -18,12 +19,14 @@ package net
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
Jeroen van Rijn: Cross platform unification, code style, documentation
Feoramund: FreeBSD platform code
Haesbaert: Security fixes
*/
import "core:mem"
import "core:strings"
import "core:time"
import "core:os"
import "core:math/rand"
/*
Default configuration for DNS resolution.
*/
@@ -227,7 +230,7 @@ get_dns_records_from_nameservers :: proc(hostname: string, type: DNS_Record_Type
}
hdr := DNS_Header{
id = 0,
id = u16be(rand.uint32()),
is_response = false,
opcode = 0,
is_authoritative = false,
@@ -272,17 +275,23 @@ get_dns_records_from_nameservers :: proc(hostname: string, type: DNS_Record_Type
return nil, .Connection_Error
}
recv_sz, _ := recv_udp(conn, dns_response_buf[:]) or_continue
recv_sz, src := recv_udp(conn, dns_response_buf[:]) or_continue
if recv_sz == 0 {
continue
}
if src != name_server {
continue
}
dns_response = dns_response_buf[:recv_sz]
rsp, _ok := parse_response(dns_response, type)
rsp, xid, _ok := parse_response(dns_response, type)
if !_ok {
return nil, .Server_Error
}
if id != xid {
continue
}
if len(rsp) == 0 {
continue
@@ -803,7 +812,7 @@ parse_record :: proc(packet: []u8, cur_off: ^int, filter: DNS_Record_Type = nil)
- Data[]
*/
parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator := context.allocator) -> (records: []DNS_Record, ok: bool) {
parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator := context.allocator) -> (records: []DNS_Record, xid: u16be, ok: bool) {
context.allocator = allocator
HEADER_SIZE_BYTES :: 12
@@ -816,11 +825,13 @@ parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator
dns_hdr_chunks := mem.slice_data_cast([]u16be, response[:HEADER_SIZE_BYTES])
hdr := unpack_dns_header(dns_hdr_chunks[0], dns_hdr_chunks[1])
if !hdr.is_response {
delete(_records)
return
}
question_count := int(dns_hdr_chunks[2])
if question_count != 1 {
delete(_records)
return
}
answer_count := int(dns_hdr_chunks[3])
@@ -872,6 +883,7 @@ parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator
append(&_records, rec)
}
}
xid = hdr.id
return _records[:], true
return _records[:], xid, true
}
+2 -1
View File
@@ -80,8 +80,9 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
sockaddr := _endpoint_to_sockaddr(endpoint)
res := win.connect(win.SOCKET(socket), &sockaddr, size_of(sockaddr))
if res < 0 {
err = Dial_Error(win.WSAGetLastError())
close(socket)
return {}, Dial_Error(win.WSAGetLastError())
return {}, err
}
if options.no_delay {
+118 -6
View File
@@ -20,7 +20,7 @@ read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files
TEMP_ALLOCATOR_GUARD()
it := read_directory_iterator_create(f) or_return
it := read_directory_iterator_create(f)
defer _read_directory_iterator_destroy(&it)
dfi := make([dynamic]File_Info, 0, size, temp_allocator())
@@ -34,9 +34,14 @@ read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files
if n > 0 && index == n {
break
}
_ = read_directory_iterator_error(&it) or_break
append(&dfi, file_info_clone(fi, allocator) or_return)
}
_ = read_directory_iterator_error(&it) or_return
return slice.clone(dfi[:], allocator)
}
@@ -61,22 +66,129 @@ read_all_directory_by_path :: proc(path: string, allocator: runtime.Allocator) -
Read_Directory_Iterator :: struct {
f: ^File,
f: ^File,
err: struct {
err: Error,
path: [dynamic]byte,
},
index: int,
impl: Read_Directory_Iterator_Impl,
}
/*
Creates a directory iterator with the given directory.
@(require_results)
read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) {
return _read_directory_iterator_create(f)
For an example on how to use the iterator, see `read_directory_iterator`.
*/
read_directory_iterator_create :: proc(f: ^File) -> (it: Read_Directory_Iterator) {
read_directory_iterator_init(&it, f)
return
}
/*
Initialize a directory iterator with the given directory.
This procedure may be called on an existing iterator to reuse it for another directory.
For an example on how to use the iterator, see `read_directory_iterator`.
*/
read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
it.err.err = nil
it.err.path.allocator = file_allocator()
clear(&it.err.path)
it.f = f
it.index = 0
_read_directory_iterator_init(it, f)
}
/*
Destroys a directory iterator.
*/
read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
if it == nil {
return
}
delete(it.err.path)
_read_directory_iterator_destroy(it)
}
// NOTE(bill): `File_Info` does not need to deleted on each iteration. Any copies must be manually copied with `file_info_clone`
/*
Retrieve the last error that happened during iteration.
*/
@(require_results)
read_directory_iterator_error :: proc(it: ^Read_Directory_Iterator) -> (path: string, err: Error) {
return string(it.err.path[:]), it.err.err
}
@(private)
read_directory_iterator_set_error :: proc(it: ^Read_Directory_Iterator, path: string, err: Error) {
if err == nil {
return
}
resize(&it.err.path, len(path))
copy(it.err.path[:], path)
it.err.err = err
}
/*
Returns the next file info entry for the iterator's directory.
The given `File_Info` is reused in subsequent calls so a copy (`file_info_clone`) has to be made to
extend its lifetime.
Example:
package main
import "core:fmt"
import os "core:os/os2"
main :: proc() {
f, oerr := os.open("core")
ensure(oerr == nil)
defer os.close(f)
it := os.read_directory_iterator_create(f)
defer os.read_directory_iterator_destroy(&it)
for info in os.read_directory_iterator(&it) {
// Optionally break on the first error:
// Supports not doing this, and keeping it going with remaining items.
// _ = os.read_directory_iterator_error(&it) or_break
// Handle error as we go:
// Again, no need to do this as it will keep going with remaining items.
if path, err := os.read_directory_iterator_error(&it); err != nil {
fmt.eprintfln("failed reading %s: %s", path, err)
continue
}
// Or, do not handle errors during iteration, and just check the error at the end.
fmt.printfln("%#v", info)
}
// Handle error if one happened during iteration at the end:
if path, err := os.read_directory_iterator_error(&it); err != nil {
fmt.eprintfln("read directory failed at %s: %s", path, err)
}
}
*/
@(require_results)
read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
if it.f == nil {
return
}
if it.index == 0 && it.err.err != nil {
return
}
return _read_directory_iterator(it)
}
+32 -14
View File
@@ -8,12 +8,11 @@ Read_Directory_Iterator_Impl :: struct {
dirent_backing: []u8,
dirent_buflen: int,
dirent_off: int,
index: int,
}
@(require_results)
_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
scan_entries :: proc(dfd: linux.Fd, entries: []u8, offset: ^int) -> (fd: linux.Fd, file_name: string) {
scan_entries :: proc(it: ^Read_Directory_Iterator, dfd: linux.Fd, entries: []u8, offset: ^int) -> (fd: linux.Fd, file_name: string) {
for d in linux.dirent_iterate_buf(entries, offset) {
file_name = linux.dirent_name(d)
if file_name == "." || file_name == ".." {
@@ -24,18 +23,21 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
entry_fd, errno := linux.openat(dfd, file_name_cstr, {.NOFOLLOW, .PATH})
if errno == .NONE {
return entry_fd, file_name
} else {
read_directory_iterator_set_error(it, file_name, _get_platform_error(errno))
}
}
return -1, ""
}
index = it.impl.index
it.impl.index += 1
index = it.index
it.index += 1
dfd := linux.Fd(_fd(it.f))
entries := it.impl.dirent_backing[:it.impl.dirent_buflen]
entry_fd, file_name := scan_entries(dfd, entries, &it.impl.dirent_off)
entry_fd, file_name := scan_entries(it, dfd, entries, &it.impl.dirent_off)
for entry_fd == -1 {
if len(it.impl.dirent_backing) == 0 {
@@ -58,44 +60,60 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
it.impl.dirent_buflen = buflen
entries = it.impl.dirent_backing[:buflen]
break loop
case: // error
case:
read_directory_iterator_set_error(it, name(it.f), _get_platform_error(errno))
return
}
}
entry_fd, file_name = scan_entries(dfd, entries, &it.impl.dirent_off)
entry_fd, file_name = scan_entries(it, dfd, entries, &it.impl.dirent_off)
}
defer linux.close(entry_fd)
// PERF: reuse the fullpath string like on posix and wasi.
file_info_delete(it.impl.prev_fi, file_allocator())
fi, _ = _fstat_internal(entry_fd, file_allocator())
err: Error
fi, err = _fstat_internal(entry_fd, file_allocator())
it.impl.prev_fi = fi
if err != nil {
path, _ := _get_full_path(entry_fd, temp_allocator())
read_directory_iterator_set_error(it, path, err)
}
ok = true
return
}
@(require_results)
_read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) {
_read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
// NOTE: Allow calling `init` to target a new directory with the same iterator.
it.impl.dirent_buflen = 0
it.impl.dirent_off = 0
if f == nil || f.impl == nil {
return {}, .Invalid_File
read_directory_iterator_set_error(it, "", .Invalid_File)
return
}
stat: linux.Stat
errno := linux.fstat(linux.Fd(fd(f)), &stat)
if errno != .NONE {
return {}, _get_platform_error(errno)
read_directory_iterator_set_error(it, name(f), _get_platform_error(errno))
return
}
if (stat.mode & linux.S_IFMT) != linux.S_IFDIR {
return {}, .Invalid_Dir
read_directory_iterator_set_error(it, name(f), .Invalid_Dir)
return
}
return {f = f}, nil
}
_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
if it == nil {
return
}
delete(it.impl.dirent_backing, file_allocator())
file_info_delete(it.impl.prev_fi, file_allocator())
}
+37 -28
View File
@@ -6,7 +6,6 @@ import "core:sys/posix"
Read_Directory_Iterator_Impl :: struct {
dir: posix.DIR,
idx: int,
fullpath: [dynamic]byte,
}
@@ -14,14 +13,16 @@ Read_Directory_Iterator_Impl :: struct {
_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
fimpl := (^File_Impl)(it.f.impl)
index = it.impl.idx
it.impl.idx += 1
index = it.index
it.index += 1
for {
posix.set_errno(nil)
entry := posix.readdir(it.impl.dir)
if entry == nil {
// NOTE(laytan): would be good to have an `error` field on the `Read_Directory_Iterator`
// There isn't a way to now know if it failed or if we are at the end.
if errno := posix.errno(); errno != nil {
read_directory_iterator_set_error(it, name(it.f), _get_platform_error(errno))
}
return
}
@@ -31,54 +32,62 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
}
sname := string(cname)
stat: posix.stat_t
if posix.fstatat(posix.dirfd(it.impl.dir), cname, &stat, { .SYMLINK_NOFOLLOW }) != .OK {
// NOTE(laytan): would be good to have an `error` field on the `Read_Directory_Iterator`
// There isn't a way to now know if it failed or if we are at the end.
return
}
n := len(fimpl.name)+1
if err := non_zero_resize(&it.impl.fullpath, n+len(sname)); err != nil {
// Can't really tell caller we had an error, sad.
read_directory_iterator_set_error(it, sname, err)
ok = true
return
}
copy(it.impl.fullpath[n:], sname)
stat: posix.stat_t
if posix.fstatat(posix.dirfd(it.impl.dir), cname, &stat, { .SYMLINK_NOFOLLOW }) != .OK {
read_directory_iterator_set_error(it, string(it.impl.fullpath[:]), _get_platform_error())
ok = true
return
}
fi = internal_stat(stat, string(it.impl.fullpath[:]))
ok = true
return
}
}
@(require_results)
_read_directory_iterator_create :: proc(f: ^File) -> (iter: Read_Directory_Iterator, err: Error) {
_read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
if f == nil || f.impl == nil {
err = .Invalid_File
read_directory_iterator_set_error(it, "", .Invalid_File)
return
}
impl := (^File_Impl)(f.impl)
iter.f = f
iter.impl.idx = 0
// NOTE: Allow calling `init` to target a new directory with the same iterator.
it.impl.fullpath.allocator = file_allocator()
clear(&it.impl.fullpath)
if err := reserve(&it.impl.fullpath, len(impl.name)+128); err != nil {
read_directory_iterator_set_error(it, name(f), err)
return
}
iter.impl.fullpath = make([dynamic]byte, 0, len(impl.name)+128, file_allocator()) or_return
append(&iter.impl.fullpath, impl.name)
append(&iter.impl.fullpath, "/")
defer if err != nil { delete(iter.impl.fullpath) }
append(&it.impl.fullpath, impl.name)
append(&it.impl.fullpath, "/")
// `fdopendir` consumes the file descriptor so we need to `dup` it.
dupfd := posix.dup(impl.fd)
if dupfd == -1 {
err = _get_platform_error()
read_directory_iterator_set_error(it, name(f), _get_platform_error())
return
}
defer if err != nil { posix.close(dupfd) }
defer if it.err.err != nil { posix.close(dupfd) }
iter.impl.dir = posix.fdopendir(dupfd)
if iter.impl.dir == nil {
err = _get_platform_error()
// NOTE: Allow calling `init` to target a new directory with the same iterator.
if it.impl.dir != nil {
posix.closedir(it.impl.dir)
}
it.impl.dir = posix.fdopendir(dupfd)
if it.impl.dir == nil {
read_directory_iterator_set_error(it, name(f), _get_platform_error())
return
}
@@ -86,7 +95,7 @@ _read_directory_iterator_create :: proc(f: ^File) -> (iter: Read_Directory_Itera
}
_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
if it == nil || it.impl.dir == nil {
if it.impl.dir == nil {
return
}
+230
View File
@@ -0,0 +1,230 @@
package os2
import "core:container/queue"
/*
A recursive directory walker.
Note that none of the fields should be accessed directly.
*/
Walker :: struct {
todo: queue.Queue(string),
skip_dir: bool,
err: struct {
path: [dynamic]byte,
err: Error,
},
iter: Read_Directory_Iterator,
}
walker_init_path :: proc(w: ^Walker, path: string) {
cloned_path, err := clone_string(path, file_allocator())
if err != nil {
walker_set_error(w, path, err)
return
}
walker_clear(w)
if _, err = queue.push(&w.todo, cloned_path); err != nil {
walker_set_error(w, cloned_path, err)
return
}
}
walker_init_file :: proc(w: ^Walker, f: ^File) {
handle, err := clone(f)
if err != nil {
path, _ := clone_string(name(f), file_allocator())
walker_set_error(w, path, err)
return
}
walker_clear(w)
read_directory_iterator_init(&w.iter, handle)
}
/*
Initializes a walker, either using a path or a file pointer to a directory the walker will start at.
You are allowed to repeatedly call this to reuse it for later walks.
For an example on how to use the walker, see `walker_walk`.
*/
walker_init :: proc {
walker_init_path,
walker_init_file,
}
@(require_results)
walker_create_path :: proc(path: string) -> (w: Walker) {
walker_init_path(&w, path)
return
}
@(require_results)
walker_create_file :: proc(f: ^File) -> (w: Walker) {
walker_init_file(&w, f)
return
}
/*
Creates a walker, either using a path or a file pointer to a directory the walker will start at.
For an example on how to use the walker, see `walker_walk`.
*/
walker_create :: proc {
walker_create_path,
walker_create_file,
}
/*
Returns the last error that occurred during the walker's operations.
Can be called while iterating, or only at the end to check if anything failed.
*/
@(require_results)
walker_error :: proc(w: ^Walker) -> (path: string, err: Error) {
return string(w.err.path[:]), w.err.err
}
@(private)
walker_set_error :: proc(w: ^Walker, path: string, err: Error) {
if err == nil {
return
}
resize(&w.err.path, len(path))
copy(w.err.path[:], path)
w.err.err = err
}
@(private)
walker_clear :: proc(w: ^Walker) {
w.iter.f = nil
w.skip_dir = false
w.err.path.allocator = file_allocator()
clear(&w.err.path)
w.todo.data.allocator = file_allocator()
for path in queue.pop_front_safe(&w.todo) {
delete(path, file_allocator())
}
}
walker_destroy :: proc(w: ^Walker) {
walker_clear(w)
queue.destroy(&w.todo)
delete(w.err.path)
read_directory_iterator_destroy(&w.iter)
}
// Marks the current directory to be skipped (not entered into).
walker_skip_dir :: proc(w: ^Walker) {
w.skip_dir = true
}
/*
Returns the next file info in the iterator, files are iterated in breadth-first order.
If an error occurred opening a directory, you may get zero'd info struct and
`walker_error` will return the error.
Example:
package main
import "core:fmt"
import "core:strings"
import os "core:os/os2"
main :: proc() {
w := os.walker_create("core")
defer os.walker_destroy(&w)
for info in os.walker_walk(&w) {
// Optionally break on the first error:
// _ = walker_error(&w) or_break
// Or, handle error as we go:
if path, err := os.walker_error(&w); err != nil {
fmt.eprintfln("failed walking %s: %s", path, err)
continue
}
// Or, do not handle errors during iteration, and just check the error at the end.
// Skip a directory:
if strings.has_suffix(info.fullpath, ".git") {
os.walker_skip_dir(&w)
continue
}
fmt.printfln("%#v", info)
}
// Handle error if one happened during iteration at the end:
if path, err := os.walker_error(&w); err != nil {
fmt.eprintfln("failed walking %s: %v", path, err)
}
}
*/
@(require_results)
walker_walk :: proc(w: ^Walker) -> (fi: File_Info, ok: bool) {
if w.skip_dir {
w.skip_dir = false
if skip, sok := queue.pop_back_safe(&w.todo); sok {
delete(skip, file_allocator())
}
}
if w.iter.f == nil {
if queue.len(w.todo) == 0 {
return
}
next := queue.pop_front(&w.todo)
handle, err := open(next)
if err != nil {
walker_set_error(w, next, err)
return {}, true
}
read_directory_iterator_init(&w.iter, handle)
delete(next, file_allocator())
}
info, _, iter_ok := read_directory_iterator(&w.iter)
if path, err := read_directory_iterator_error(&w.iter); err != nil {
walker_set_error(w, path, err)
}
if !iter_ok {
close(w.iter.f)
w.iter.f = nil
return walker_walk(w)
}
if info.type == .Directory {
path, err := clone_string(info.fullpath, file_allocator())
if err != nil {
walker_set_error(w, "", err)
return
}
_, err = queue.push_back(&w.todo, path)
if err != nil {
walker_set_error(w, path, err)
return
}
}
return info, iter_ok
}
+37 -23
View File
@@ -1,6 +1,8 @@
#+private
package os2
import "base:runtime"
import "core:slice"
import "base:intrinsics"
import "core:sys/wasm/wasi"
@@ -8,7 +10,6 @@ Read_Directory_Iterator_Impl :: struct {
fullpath: [dynamic]byte,
buf: []byte,
off: int,
idx: int,
}
@(require_results)
@@ -17,8 +18,8 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
buf := it.impl.buf[it.impl.off:]
index = it.impl.idx
it.impl.idx += 1
index = it.index
it.index += 1
for {
if len(buf) < size_of(wasi.dirent_t) {
@@ -28,10 +29,7 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
entry := intrinsics.unaligned_load((^wasi.dirent_t)(raw_data(buf)))
buf = buf[size_of(wasi.dirent_t):]
if len(buf) < int(entry.d_namlen) {
// shouldn't be possible.
return
}
assert(len(buf) < int(entry.d_namlen))
name := string(buf[:entry.d_namlen])
buf = buf[entry.d_namlen:]
@@ -43,7 +41,8 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
n := len(fimpl.name)+1
if alloc_err := non_zero_resize(&it.impl.fullpath, n+len(name)); alloc_err != nil {
// Can't really tell caller we had an error, sad.
read_directory_iterator_set_error(it, name, alloc_err)
ok = true
return
}
copy(it.impl.fullpath[n:], name)
@@ -55,6 +54,7 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
ino = entry.d_ino,
filetype = entry.d_type,
}
read_directory_iterator_set_error(it, string(it.impl.fullpath[:]), _get_platform_error(err))
}
fi = internal_stat(stat, string(it.impl.fullpath[:]))
@@ -63,27 +63,35 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
}
}
@(require_results)
_read_directory_iterator_create :: proc(f: ^File) -> (iter: Read_Directory_Iterator, err: Error) {
_read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
// NOTE: Allow calling `init` to target a new directory with the same iterator.
it.impl.off = 0
if f == nil || f.impl == nil {
err = .Invalid_File
read_directory_iterator_set_error(it, "", .Invalid_File)
return
}
impl := (^File_Impl)(f.impl)
iter.f = f
buf: [dynamic]byte
// NOTE: Allow calling `init` to target a new directory with the same iterator.
if it.impl.buf != nil {
buf = slice.into_dynamic(it.impl.buf)
}
buf.allocator = file_allocator()
defer if err != nil { delete(buf) }
// NOTE: this is very grug.
defer if it.err.err != nil { delete(buf) }
for {
non_zero_resize(&buf, 512 if len(buf) == 0 else len(buf)*2) or_return
if err := non_zero_resize(&buf, 512 if len(buf) == 0 else len(buf)*2); err != nil {
read_directory_iterator_set_error(it, name(f), err)
return
}
n, _err := wasi.fd_readdir(__fd(f), buf[:], 0)
if _err != nil {
err = _get_platform_error(_err)
n, err := wasi.fd_readdir(__fd(f), buf[:], 0)
if err != nil {
read_directory_iterator_set_error(it, name(f), _get_platform_error(err))
return
}
@@ -94,11 +102,18 @@ _read_directory_iterator_create :: proc(f: ^File) -> (iter: Read_Directory_Itera
assert(n == len(buf))
}
iter.impl.buf = buf[:]
it.impl.buf = buf[:]
iter.impl.fullpath = make([dynamic]byte, 0, len(impl.name)+128, file_allocator()) or_return
append(&iter.impl.fullpath, impl.name)
append(&iter.impl.fullpath, "/")
// NOTE: Allow calling `init` to target a new directory with the same iterator.
it.impl.fullpath.allocator = file_allocator()
clear(&it.impl.fullpath)
if err := reserve(&it.impl.fullpath, len(impl.name)+128); err != nil {
read_directory_iterator_set_error(it, name(f), err)
return
}
append(&it.impl.fullpath, impl.name)
append(&it.impl.fullpath, "/")
return
}
@@ -106,5 +121,4 @@ _read_directory_iterator_create :: proc(f: ^File) -> (iter: Read_Directory_Itera
_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
delete(it.impl.buf, file_allocator())
delete(it.impl.fullpath)
it^ = {}
}
+29 -16
View File
@@ -44,16 +44,11 @@ Read_Directory_Iterator_Impl :: struct {
path: string,
prev_fi: File_Info,
no_more_files: bool,
index: int,
}
@(require_results)
_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
if it.f == nil {
return
}
TEMP_ALLOCATOR_GUARD()
for !it.impl.no_more_files {
@@ -63,19 +58,21 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
fi, err = find_data_to_file_info(it.impl.path, &it.impl.find_data, file_allocator())
if err != nil {
read_directory_iterator_set_error(it, it.impl.path, err)
return
}
if fi.name != "" {
it.impl.prev_fi = fi
ok = true
index = it.impl.index
it.impl.index += 1
index = it.index
it.index += 1
}
if !win32.FindNextFileW(it.impl.find_handle, &it.impl.find_data) {
e := _get_platform_error()
if pe, _ := is_platform_error(e); pe == i32(win32.ERROR_NO_MORE_FILES) {
it.impl.no_more_files = true
if pe, _ := is_platform_error(e); pe != i32(win32.ERROR_NO_MORE_FILES) {
read_directory_iterator_set_error(it, it.impl.path, e)
}
it.impl.no_more_files = true
}
@@ -86,16 +83,27 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
return
}
@(require_results)
_read_directory_iterator_create :: proc(f: ^File) -> (it: Read_Directory_Iterator, err: Error) {
if f == nil {
_read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
it.impl.no_more_files = false
if f == nil || f.impl == nil {
read_directory_iterator_set_error(it, "", .Invalid_File)
return
}
it.f = f
impl := (^File_Impl)(f.impl)
// NOTE: Allow calling `init` to target a new directory with the same iterator - reset idx.
if it.impl.find_handle != nil {
win32.FindClose(it.impl.find_handle)
}
if it.impl.path != "" {
delete(it.impl.path, file_allocator())
}
if !is_directory(impl.name) {
err = .Invalid_Dir
read_directory_iterator_set_error(it, impl.name, .Invalid_Dir)
return
}
@@ -118,14 +126,19 @@ _read_directory_iterator_create :: proc(f: ^File) -> (it: Read_Directory_Iterato
it.impl.find_handle = win32.FindFirstFileW(raw_data(wpath_search), &it.impl.find_data)
if it.impl.find_handle == win32.INVALID_HANDLE_VALUE {
err = _get_platform_error()
read_directory_iterator_set_error(it, impl.name, _get_platform_error())
return
}
defer if err != nil {
defer if it.err.err != nil {
win32.FindClose(it.impl.find_handle)
}
it.impl.path = _cleanpath_from_buf(wpath, file_allocator()) or_return
err: Error
it.impl.path, err = _cleanpath_from_buf(wpath, file_allocator())
if err != nil {
read_directory_iterator_set_error(it, impl.name, err)
}
return
}
+3 -3
View File
@@ -22,8 +22,8 @@ lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string,
}
// set_env sets the value of the environment variable named by the key
// Returns true on success, false on failure
set_env :: proc(key, value: string) -> bool {
// Returns Error on failure
set_env :: proc(key, value: string) -> Error {
return _set_env(key, value)
}
@@ -41,7 +41,7 @@ clear_env :: proc() {
// environ returns a copy of strings representing the environment, in the form "key=value"
// NOTE: the slice of strings and the strings with be allocated using the supplied allocator
@(require_results)
environ :: proc(allocator: runtime.Allocator) -> []string {
environ :: proc(allocator: runtime.Allocator) -> ([]string, Error) {
return _environ(allocator)
}
+40 -40
View File
@@ -20,19 +20,18 @@ NOT_FOUND :: -1
// the environment is a 0 delimited list of <key>=<value> strings
_env: [dynamic]string
_env_mutex: sync.Mutex
_env_mutex: sync.Recursive_Mutex
// We need to be able to figure out if the environment variable
// is contained in the original environment or not. This also
// serves as a flag to determine if we have built _env.
_org_env_begin: uintptr
_org_env_end: uintptr
_org_env_begin: uintptr // atomic
_org_env_end: uintptr // guarded by _env_mutex
// Returns value + index location into _env
// or -1 if not found
_lookup :: proc(key: string) -> (value: string, idx: int) {
sync.mutex_lock(&_env_mutex)
defer sync.mutex_unlock(&_env_mutex)
sync.guard(&_env_mutex)
for entry, i in _env {
if k, v := _kv_from_entry(entry); k == key {
@@ -43,7 +42,7 @@ _lookup :: proc(key: string) -> (value: string, idx: int) {
}
_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
if _org_env_begin == 0 {
if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
_build_env()
}
@@ -54,19 +53,18 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
return
}
_set_env :: proc(key, v_new: string) -> bool {
if _org_env_begin == 0 {
_set_env :: proc(key, v_new: string) -> Error {
if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
_build_env()
}
sync.guard(&_env_mutex)
// all key values are stored as "key=value\x00"
kv_size := len(key) + len(v_new) + 2
if v_curr, idx := _lookup(key); idx != NOT_FOUND {
if v_curr == v_new {
return true
return nil
}
sync.mutex_lock(&_env_mutex)
defer sync.mutex_unlock(&_env_mutex)
unordered_remove(&_env, idx)
@@ -78,7 +76,7 @@ _set_env :: proc(key, v_new: string) -> bool {
if len(v_new) > len(v_curr) {
k_addr = ([^]u8)(runtime.heap_resize(k_addr, kv_size))
if k_addr == nil {
return false
return .Out_Of_Memory
}
v_addr = &k_addr[len(key) + 1]
}
@@ -86,13 +84,13 @@ _set_env :: proc(key, v_new: string) -> bool {
v_addr[len(v_new)] = 0
append(&_env, string(k_addr[:kv_size]))
return true
return nil
}
}
k_addr := ([^]u8)(runtime.heap_alloc(kv_size))
if k_addr == nil {
return false
return .Out_Of_Memory
}
intrinsics.mem_copy_non_overlapping(k_addr, raw_data(key), len(key))
k_addr[len(key)] = '='
@@ -101,16 +99,15 @@ _set_env :: proc(key, v_new: string) -> bool {
intrinsics.mem_copy_non_overlapping(&val_slice[0], raw_data(v_new), len(v_new))
val_slice[len(v_new)] = 0
sync.mutex_lock(&_env_mutex)
append(&_env, string(k_addr[:kv_size - 1]))
sync.mutex_unlock(&_env_mutex)
return true
return nil
}
_unset_env :: proc(key: string) -> bool {
if _org_env_begin == 0 {
if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
_build_env()
}
sync.guard(&_env_mutex)
v: string
i: int
@@ -118,15 +115,13 @@ _unset_env :: proc(key: string) -> bool {
return false
}
sync.mutex_lock(&_env_mutex)
unordered_remove(&_env, i)
sync.mutex_unlock(&_env_mutex)
if _is_in_org_env(v) {
return true
}
// if we got this far, the envrionment variable
// if we got this far, the environment variable
// existed AND was allocated by us.
k_addr, _ := _kv_addr_from_val(v, key)
runtime.heap_free(k_addr)
@@ -134,8 +129,7 @@ _unset_env :: proc(key: string) -> bool {
}
_clear_env :: proc() {
sync.mutex_lock(&_env_mutex)
defer sync.mutex_unlock(&_env_mutex)
sync.guard(&_env_mutex)
for kv in _env {
if !_is_in_org_env(kv) {
@@ -145,28 +139,36 @@ _clear_env :: proc() {
clear(&_env)
// nothing resides in the original environment either
_org_env_begin = ~uintptr(0)
intrinsics.atomic_store_explicit(&_org_env_begin, ~uintptr(0), .Release)
_org_env_end = ~uintptr(0)
}
_environ :: proc(allocator: runtime.Allocator) -> []string {
if _org_env_begin == 0 {
_environ :: proc(allocator: runtime.Allocator) -> (environ: []string, err: Error) {
if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
_build_env()
}
env := make([]string, len(_env), allocator)
sync.guard(&_env_mutex)
sync.mutex_lock(&_env_mutex)
defer sync.mutex_unlock(&_env_mutex)
for entry, i in _env {
env[i], _ = clone_string(entry, allocator)
env := make([dynamic]string, 0, len(_env), allocator) or_return
defer if err != nil {
for e in env {
delete(e, allocator)
}
delete(env)
}
return env
for entry in _env {
s := clone_string(entry, allocator) or_return
append(&env, s)
}
environ = env[:]
return
}
// The entire environment is stored as 0 terminated strings,
// so there is no need to clone/free individual variables
export_cstring_environment :: proc(allocator: runtime.Allocator) -> []cstring {
if _org_env_begin == 0 {
if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
// The environment has not been modified, so we can just
// send the original environment
org_env := _get_original_env()
@@ -174,12 +176,11 @@ export_cstring_environment :: proc(allocator: runtime.Allocator) -> []cstring {
for ; org_env[n] != nil; n += 1 {}
return slice.clone(org_env[:n + 1], allocator)
}
sync.guard(&_env_mutex)
// NOTE: already terminated by nil pointer via + 1
env := make([]cstring, len(_env) + 1, allocator)
sync.mutex_lock(&_env_mutex)
defer sync.mutex_unlock(&_env_mutex)
for entry, i in _env {
env[i] = cstring(raw_data(entry))
}
@@ -187,15 +188,14 @@ export_cstring_environment :: proc(allocator: runtime.Allocator) -> []cstring {
}
_build_env :: proc() {
sync.mutex_lock(&_env_mutex)
defer sync.mutex_unlock(&_env_mutex)
if _org_env_begin != 0 {
sync.guard(&_env_mutex)
if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) != 0 {
return
}
_env = make(type_of(_env), runtime.heap_allocator())
cstring_env := _get_original_env()
_org_env_begin = uintptr(rawptr(cstring_env[0]))
intrinsics.atomic_store_explicit(&_org_env_begin, uintptr(rawptr(cstring_env[0])), .Release)
for i := 0; cstring_env[i] != nil; i += 1 {
bytes := ([^]u8)(cstring_env[i])
n := len(cstring_env[i])
@@ -227,5 +227,5 @@ _kv_addr_from_val :: #force_inline proc(val: string, key: string) -> ([^]u8, [^]
_is_in_org_env :: #force_inline proc(env_data: string) -> bool {
addr := uintptr(raw_data(env_data))
return addr >= _org_env_begin && addr < _org_env_end
return addr >= intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) && addr < _org_env_end
}
+16 -14
View File
@@ -26,13 +26,15 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
return
}
_set_env :: proc(key, value: string) -> (ok: bool) {
_set_env :: proc(key, value: string) -> (err: Error) {
TEMP_ALLOCATOR_GUARD()
ckey := strings.clone_to_cstring(key, temp_allocator())
cval := strings.clone_to_cstring(key, temp_allocator())
ckey := strings.clone_to_cstring(key, temp_allocator()) or_return
cval := strings.clone_to_cstring(key, temp_allocator()) or_return
ok = posix.setenv(ckey, cval, true) == .OK
if posix.setenv(ckey, cval, true) != nil {
err = _get_platform_error_from_errno()
}
return
}
@@ -54,23 +56,23 @@ _clear_env :: proc() {
}
}
_environ :: proc(allocator: runtime.Allocator) -> (environ: []string) {
n := 0
_environ :: proc(allocator: runtime.Allocator) -> (environ: []string, err: Error) {
n := 0
for entry := posix.environ[0]; entry != nil; n, entry = n+1, posix.environ[n] {}
err: runtime.Allocator_Error
if environ, err = make([]string, n, allocator); err != nil {
// NOTE(laytan): is the environment empty or did allocation fail, how does the user know?
return
r := make([dynamic]string, 0, n, allocator) or_return
defer if err != nil {
for e in r {
delete(e, allocator)
}
delete(r)
}
for i, entry := 0, posix.environ[0]; entry != nil; i, entry = i+1, posix.environ[i] {
if environ[i], err = strings.clone(string(entry), allocator); err != nil {
// NOTE(laytan): is the entire environment returned or did allocation fail, how does the user know?
return
}
append(&r, strings.clone(string(entry), allocator) or_return)
}
environ = r[:]
return
}
+19 -46
View File
@@ -80,46 +80,30 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
}
@(require_results)
_set_env :: proc(key, value: string) -> bool {
if err := build_env(); err != nil {
return false
}
_set_env :: proc(key, value: string) -> (err: Error) {
build_env() or_return
sync.guard(&g_env_mutex)
key_ptr, value_ptr, just_inserted, err := map_entry(&g_env, key)
if err != nil {
return false
defer if err != nil {
delete_key(&g_env, key)
}
alloc_err: runtime.Allocator_Error
key_ptr, value_ptr, just_inserted := map_entry(&g_env, key) or_return
if just_inserted {
key_ptr^, alloc_err = clone_string(key, file_allocator())
if alloc_err != nil {
delete_key(&g_env, key)
return false
}
value_ptr^, alloc_err = clone_string(value, file_allocator())
if alloc_err != nil {
delete_key(&g_env, key)
key_ptr^ = clone_string(key, file_allocator()) or_return
defer if err != nil {
delete(key_ptr^, file_allocator())
return false
}
return true
value_ptr^ = clone_string(value, file_allocator()) or_return
return
}
delete_string_if_not_original(value_ptr^)
value_ptr^, alloc_err = clone_string(value, file_allocator())
if alloc_err != nil {
delete_key(&g_env, key)
return false
}
return true
value_ptr^ = clone_string(value, file_allocator()) or_return
return
}
@(require_results)
@@ -153,34 +137,23 @@ _clear_env :: proc() {
}
@(require_results)
_environ :: proc(allocator: runtime.Allocator) -> []string {
if err := build_env(); err != nil {
return nil
}
_environ :: proc(allocator: runtime.Allocator) -> (environ: []string, err: Error) {
build_env() or_return
sync.shared_guard(&g_env_mutex)
envs, alloc_err := make([]string, len(g_env), allocator)
if alloc_err != nil {
return nil
}
defer if alloc_err != nil {
envs := make([dynamic]string, 0, len(g_env), allocator) or_return
defer if err != nil {
for env in envs {
delete(env, allocator)
}
delete(envs, allocator)
delete(envs)
}
i: int
for k, v in g_env {
defer i += 1
envs[i], alloc_err = concatenate({k, "=", v}, allocator)
if alloc_err != nil {
return nil
}
append(&envs, concatenate({k, "=", v}, allocator) or_return)
}
return envs
environ = envs[:]
return
}
+21 -10
View File
@@ -36,12 +36,15 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
return
}
_set_env :: proc(key, value: string) -> bool {
_set_env :: proc(key, value: string) -> Error {
TEMP_ALLOCATOR_GUARD()
k, _ := win32_utf8_to_wstring(key, temp_allocator())
v, _ := win32_utf8_to_wstring(value, temp_allocator())
k := win32_utf8_to_wstring(key, temp_allocator()) or_return
v := win32_utf8_to_wstring(value, temp_allocator()) or_return
return bool(win32.SetEnvironmentVariableW(k, v))
if !win32.SetEnvironmentVariableW(k, v) {
return _get_platform_error()
}
return nil
}
_unset_env :: proc(key: string) -> bool {
@@ -52,7 +55,7 @@ _unset_env :: proc(key: string) -> bool {
_clear_env :: proc() {
TEMP_ALLOCATOR_GUARD()
envs := environ(temp_allocator())
envs, _ := environ(temp_allocator())
for env in envs {
for j in 1..<len(env) {
if env[j] == '=' {
@@ -63,10 +66,10 @@ _clear_env :: proc() {
}
}
_environ :: proc(allocator: runtime.Allocator) -> []string {
_environ :: proc(allocator: runtime.Allocator) -> (environ: []string, err: Error) {
envs := win32.GetEnvironmentStringsW()
if envs == nil {
return nil
return
}
defer win32.FreeEnvironmentStringsW(envs)
@@ -82,7 +85,13 @@ _environ :: proc(allocator: runtime.Allocator) -> []string {
}
}
r := make([dynamic]string, 0, n, allocator)
r := make([dynamic]string, 0, n, allocator) or_return
defer if err != nil {
for e in r {
delete(e, allocator)
}
delete(r)
}
for from, i, p := 0, 0, envs; true; i += 1 {
c := ([^]u16)(p)[i]
if c == 0 {
@@ -90,12 +99,14 @@ _environ :: proc(allocator: runtime.Allocator) -> []string {
break
}
w := ([^]u16)(p)[from:i]
append(&r, win32_utf16_to_utf8(w, allocator) or_else "")
s := win32_utf16_to_utf8(w, allocator) or_return
append(&r, s)
from = i + 1
}
}
return r[:]
environ = r[:]
return
}
+5
View File
@@ -122,6 +122,11 @@ new_file :: proc(handle: uintptr, name: string) -> ^File {
return file
}
@(require_results)
clone :: proc(f: ^File) -> (^File, Error) {
return _clone(f)
}
@(require_results)
fd :: proc(f: ^File) -> uintptr {
return _fd(f)
+17
View File
@@ -113,6 +113,23 @@ _new_file :: proc(fd: uintptr, _: string, allocator: runtime.Allocator) -> (f: ^
return &impl.file, nil
}
_clone :: proc(f: ^File) -> (clone: ^File, err: Error) {
if f == nil || f.impl == nil {
return
}
fd := (^File_Impl)(f.impl).fd
clonefd, errno := linux.dup(fd)
if errno != nil {
err = _get_platform_error(errno)
return
}
defer if err != nil { linux.close(clonefd) }
return _new_file(uintptr(clonefd), "", file_allocator())
}
@(require_results)
_open_buffered :: proc(name: string, buffer_size: uint, flags := File_Flags{.Read}, perm := 0o777) -> (f: ^File, err: Error) {
+23
View File
@@ -114,6 +114,29 @@ __new_file :: proc(handle: posix.FD, allocator: runtime.Allocator) -> ^File {
return &impl.file
}
_clone :: proc(f: ^File) -> (clone: ^File, err: Error) {
if f == nil || f.impl == nil {
err = .Invalid_Pointer
return
}
impl := (^File_Impl)(f.impl)
fd := posix.dup(impl.fd)
if fd <= 0 {
err = _get_platform_error()
return
}
defer if err != nil { posix.close(fd) }
clone = __new_file(fd, file_allocator())
clone_impl := (^File_Impl)(clone.impl)
clone_impl.cname = clone_to_cstring(impl.name, file_allocator()) or_return
clone_impl.name = string(clone_impl.cname)
return
}
_close :: proc(f: ^File_Impl) -> (err: Error) {
if f == nil { return nil }
+26
View File
@@ -223,6 +223,32 @@ _new_file :: proc(handle: uintptr, name: string, allocator: runtime.Allocator) -
return &impl.file, nil
}
_clone :: proc(f: ^File) -> (clone: ^File, err: Error) {
if f == nil || f.impl == nil {
return
}
dir_fd, relative, ok := match_preopen(name(f))
if !ok {
return nil, .Invalid_Path
}
fd, fderr := wasi.path_open(dir_fd, {.SYMLINK_FOLLOW}, relative, {}, {}, {}, {})
if fderr != nil {
err = _get_platform_error(fderr)
return
}
defer if err != nil { wasi.fd_close(fd) }
fderr = wasi.fd_renumber((^File_Impl)(f.impl).fd, fd)
if fderr != nil {
err = _get_platform_error(fderr)
return
}
return _new_file(uintptr(fd), name(f), file_allocator())
}
_close :: proc(f: ^File_Impl) -> (err: Error) {
if errno := wasi.fd_close(f.fd); errno != nil {
err = _get_platform_error(errno)
+23
View File
@@ -210,6 +210,29 @@ _new_file_buffered :: proc(handle: uintptr, name: string, buffer_size: uint) ->
return
}
_clone :: proc(f: ^File) -> (clone: ^File, err: Error) {
if f == nil || f.impl == nil {
return
}
clonefd: win32.HANDLE
process := win32.GetCurrentProcess()
if !win32.DuplicateHandle(
process,
win32.HANDLE(_fd(f)),
process,
&clonefd,
0,
false,
win32.DUPLICATE_SAME_ACCESS,
) {
err = _get_platform_error()
return
}
defer if err != nil { win32.CloseHandle(clonefd) }
return _new_file(uintptr(clonefd), name(f), file_allocator())
}
_fd :: proc(f: ^File) -> uintptr {
if f == nil || f.impl == nil {
+9 -38
View File
@@ -3,6 +3,7 @@ package os2
import "base:intrinsics"
import "base:runtime"
import "core:math/rand"
// Splits pattern by the last wildcard "*", if it exists, and returns the prefix and suffix
@@ -84,45 +85,15 @@ concatenate :: proc(strings: []string, allocator: runtime.Allocator) -> (res: st
return string(buf), nil
}
@(private="file")
random_string_seed: [2]u64
@(init, private="file")
init_random_string_seed :: proc() {
seed := u64(intrinsics.read_cycle_counter())
s := &random_string_seed
s[0] = 0
s[1] = (seed << 1) | 1
_ = next_random(s)
s[1] += seed
_ = next_random(s)
}
@(require_results)
next_random :: proc(r: ^[2]u64) -> u64 {
old_state := r[0]
r[0] = old_state * 6364136223846793005 + (r[1]|1)
xor_shifted := (((old_state >> 59) + 5) ~ old_state) * 12605985483714917081
rot := (old_state >> 59)
return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 63))
}
@(require_results)
random_string :: proc(buf: []byte) -> string {
@(static, rodata) digits := "0123456789"
u := next_random(&random_string_seed)
b :: 10
i := len(buf)
for u >= b {
i -= 1
buf[i] = digits[u % b]
u /= b
for i := 0; i < len(buf); i += 16 {
n := rand.uint64()
end := min(i + 16, len(buf))
for j := i; j < end; j += 1 {
buf[j] = '0' + u8(n) % 10
n >>= 4
}
}
i -= 1
buf[i] = digits[u % b]
return string(buf[i:])
return string(buf)
}
+1 -1
View File
@@ -46,7 +46,7 @@ _get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err
strings.write_string(&buf, "/")
strings.write_string(&buf, sarg)
cpath := strings.to_cstring(&buf)
cpath := strings.to_cstring(&buf) or_return
if posix.access(cpath, {.X_OK}) == .OK {
return real(cpath, allocator)
}
+5 -1
View File
@@ -60,16 +60,20 @@ _remove_all :: proc(path: string) -> (err: Error) {
dir := open(path) or_return
defer close(dir)
iter := read_directory_iterator_create(dir) or_return
iter := read_directory_iterator_create(dir)
defer read_directory_iterator_destroy(&iter)
for fi in read_directory_iterator(&iter) {
_ = read_directory_iterator_error(&iter) or_break
if fi.type == .Directory {
_remove_all(fi.fullpath) or_return
} else {
remove(fi.fullpath) or_return
}
}
_ = read_directory_iterator_error(&iter) or_return
}
return remove(path)
+2 -2
View File
@@ -29,7 +29,7 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
strings.write_string(&rname, "/dev/fd/")
strings.write_int(&rname, int(fds[0]))
ri.name = strings.to_string(rname)
ri.cname = strings.to_cstring(&rname)
ri.cname = strings.to_cstring(&rname) or_return
w = __new_file(fds[1], file_allocator())
wi := (^File_Impl)(w.impl)
@@ -39,7 +39,7 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
strings.write_string(&wname, "/dev/fd/")
strings.write_int(&wname, int(fds[1]))
wi.name = strings.to_string(wname)
wi.cname = strings.to_cstring(&wname)
wi.cname = strings.to_cstring(&wname) or_return
return
}
+8 -8
View File
@@ -111,7 +111,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
strings.write_string(&path_builder, "/proc/")
strings.write_int(&path_builder, pid)
proc_fd, errno := linux.open(strings.to_cstring(&path_builder), _OPENDIR_FLAGS)
proc_fd, errno := linux.open(strings.to_cstring(&path_builder) or_return, _OPENDIR_FLAGS)
if errno != .NONE {
err = _get_platform_error(errno)
return
@@ -169,7 +169,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
strings.write_int(&path_builder, pid)
strings.write_string(&path_builder, "/cmdline")
cmdline_bytes, cmdline_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder), temp_allocator())
cmdline_bytes, cmdline_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder) or_return, temp_allocator())
if cmdline_err != nil || len(cmdline_bytes) == 0 {
err = cmdline_err
break cmdline_if
@@ -190,7 +190,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
strings.write_int(&path_builder, pid)
strings.write_string(&path_builder, "/cwd")
cwd, cwd_err = _read_link_cstr(strings.to_cstring(&path_builder), temp_allocator()) // allowed to fail
cwd, cwd_err = _read_link_cstr(strings.to_cstring(&path_builder) or_return, temp_allocator()) // allowed to fail
if cwd_err == nil && .Working_Dir in selection {
info.working_dir = strings.clone(cwd, allocator) or_return
info.fields += {.Working_Dir}
@@ -258,7 +258,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
strings.write_int(&path_builder, pid)
strings.write_string(&path_builder, "/stat")
proc_stat_bytes, stat_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder), temp_allocator())
proc_stat_bytes, stat_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder) or_return, temp_allocator())
if stat_err != nil {
err = stat_err
break stat_if
@@ -330,7 +330,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
strings.write_int(&path_builder, pid)
strings.write_string(&path_builder, "/environ")
if env_bytes, env_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder), temp_allocator()); env_err == nil {
if env_bytes, env_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder) or_return, temp_allocator()); env_err == nil {
env := string(env_bytes)
env_list := make([dynamic]string, allocator) or_return
@@ -418,7 +418,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
strings.write_byte(&exe_builder, '/')
strings.write_string(&exe_builder, executable_name)
exe_path = strings.to_cstring(&exe_builder)
exe_path = strings.to_cstring(&exe_builder) or_return
if linux.access(exe_path, linux.X_OK) == .NONE {
found = true
break
@@ -430,7 +430,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
strings.write_string(&exe_builder, "./")
strings.write_string(&exe_builder, executable_name)
exe_path = strings.to_cstring(&exe_builder)
exe_path = strings.to_cstring(&exe_builder) or_return
if linux.access(exe_path, linux.X_OK) != .NONE {
return process, .Not_Exist
}
@@ -594,7 +594,7 @@ _process_state_update_times :: proc(state: ^Process_State) -> (err: Error) {
strings.write_string(&path_builder, "/stat")
stat_buf: []u8
stat_buf, err = _read_entire_pseudo_file(strings.to_cstring(&path_builder), temp_allocator())
stat_buf, err = _read_entire_pseudo_file(strings.to_cstring(&path_builder) or_return, temp_allocator())
if err != nil {
return
}
+4 -4
View File
@@ -71,7 +71,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
strings.write_byte(&exe_builder, '/')
strings.write_string(&exe_builder, exe_name)
if exe_fd := posix.open(strings.to_cstring(&exe_builder), {.CLOEXEC, .EXEC}); exe_fd == -1 {
if exe_fd := posix.open(strings.to_cstring(&exe_builder) or_return, {.CLOEXEC, .EXEC}); exe_fd == -1 {
continue
} else {
posix.close(exe_fd)
@@ -91,7 +91,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
// "hello/./world" is fine right?
if exe_fd := posix.open(strings.to_cstring(&exe_builder), {.CLOEXEC, .EXEC}); exe_fd == -1 {
if exe_fd := posix.open(strings.to_cstring(&exe_builder) or_return, {.CLOEXEC, .EXEC}); exe_fd == -1 {
err = .Not_Exist
return
} else {
@@ -102,7 +102,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
strings.builder_reset(&exe_builder)
strings.write_string(&exe_builder, exe_name)
if exe_fd := posix.open(strings.to_cstring(&exe_builder), {.CLOEXEC, .EXEC}); exe_fd == -1 {
if exe_fd := posix.open(strings.to_cstring(&exe_builder) or_return, {.CLOEXEC, .EXEC}); exe_fd == -1 {
err = .Not_Exist
return
} else {
@@ -181,7 +181,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
if posix.chdir(cwd) != .OK { abort(pipe[WRITE]) }
}
res := posix.execve(strings.to_cstring(&exe_builder), raw_data(cmd), env)
res := posix.execve(strings.to_cstring(&exe_builder) or_return, raw_data(cmd), env)
assert(res == -1)
abort(pipe[WRITE])
+1 -1
View File
@@ -427,7 +427,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
command_line_w := win32_utf8_to_wstring(command_line, temp_allocator()) or_return
environment := desc.env
if desc.env == nil {
environment = environ(temp_allocator())
environment = environ(temp_allocator()) or_return
}
environment_block := _build_environment_block(environment, temp_allocator())
environment_block_w := win32_utf8_to_utf16(environment_block, temp_allocator()) or_return
+1 -1
View File
@@ -24,7 +24,7 @@ File_Info :: struct {
@(require_results)
file_info_clone :: proc(fi: File_Info, allocator: runtime.Allocator) -> (cloned: File_Info, err: runtime.Allocator_Error) {
cloned = fi
cloned.fullpath = strings.clone(fi.fullpath) or_return
cloned.fullpath = strings.clone(fi.fullpath, allocator) or_return
cloned.name = filepath.base(cloned.fullpath)
return
}
+2 -2
View File
@@ -20,7 +20,7 @@ create_temp_file :: proc(dir, pattern: string) -> (f: ^File, err: Error) {
prefix, suffix := _prefix_and_suffix(pattern) or_return
prefix = temp_join_path(dir, prefix) or_return
rand_buf: [32]byte
rand_buf: [10]byte
name_buf := make([]byte, len(prefix)+len(rand_buf)+len(suffix), temp_allocator())
attempts := 0
@@ -52,7 +52,7 @@ make_directory_temp :: proc(dir, pattern: string, allocator: runtime.Allocator)
prefix, suffix := _prefix_and_suffix(pattern) or_return
prefix = temp_join_path(dir, prefix) or_return
rand_buf: [32]byte
rand_buf: [10]byte
name_buf := make([]byte, len(prefix)+len(rand_buf)+len(suffix), temp_allocator())
attempts := 0
+1 -1
View File
@@ -49,7 +49,7 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro
dir = concatenate({dir, "/.config"}, allocator) or_return
}
case: // All other UNIX systems
dir = get_env("XDG_CACHE_HOME", allocator)
dir = get_env("XDG_CONFIG_HOME", allocator)
if dir == "" {
dir = get_env("HOME", temp_allocator())
if dir == "" {
+2285 -38
View File
File diff suppressed because it is too large Load Diff
+1 -2
View File
@@ -898,8 +898,7 @@ bitset_to_enum_slice_with_buffer :: proc(buf: []$E, bs: $T) -> (slice: []E) wher
// sl := slice.bitset_to_enum_slice(bs)
@(require_results)
bitset_to_enum_slice_with_make :: proc(bs: $T, $E: typeid, allocator := context.allocator) -> (slice: []E) where intrinsics.type_is_enum(E), intrinsics.type_bit_set_elem_type(T) == E {
ones := intrinsics.count_ones(transmute(E)bs)
buf := make([]E, int(ones), allocator)
buf := make([]E, card(bs), allocator)
return bitset_to_enum_slice(buf, bs)
}
+24 -1
View File
@@ -288,18 +288,41 @@ to_string :: proc(b: Builder) -> (res: string) {
/*
Appends a trailing null byte after the end of the current Builder byte buffer and then casts it to a cstring
NOTE: This procedure will not check if the backing buffer has enough space to include the extra null byte.
Inputs:
- b: A pointer to builder
Returns:
- res: A cstring of the Builder's buffer
*/
to_cstring :: proc(b: ^Builder) -> (res: cstring) {
unsafe_to_cstring :: proc(b: ^Builder) -> (res: cstring) {
append(&b.buf, 0)
pop(&b.buf)
return cstring(raw_data(b.buf))
}
/*
Appends a trailing null byte after the end of the current Builder byte buffer and then casts it to a cstring
Inputs:
- b: A pointer to builder
Returns:
- res: A cstring of the Builder's buffer upon success
- err: An optional allocator error if one occured, `nil` otherwise
*/
to_cstring :: proc(b: ^Builder) -> (res: cstring, err: mem.Allocator_Error) {
n := append(&b.buf, 0) or_return
if n != 1 {
return nil, .Out_Of_Memory
}
pop(&b.buf)
#no_bounds_check {
assert(b.buf[len(b.buf)] == 0)
}
return cstring(raw_data(b.buf)), nil
}
/*
Returns the length of the Builder's buffer, in bytes
Inputs:
+1 -1
View File
@@ -25,7 +25,7 @@ Returns:
clone :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
c := make([]byte, len(s), allocator, loc) or_return
copy(c, s)
return string(c[:len(s)]), nil
return string(c), nil
}
/*
Clones a string safely (returns early with an allocation error on failure)
+2 -2
View File
@@ -14,6 +14,6 @@ SavePanel_savePanel :: proc "c" () -> ^SavePanel {
}
@(objc_type=SavePanel, objc_name="URL")
SavePanel_URL :: proc "c" (self: ^SavePanel) -> ^Array {
return msgSend(^Array, self, "URL")
SavePanel_URL :: proc "c" (self: ^SavePanel) -> ^URL {
return msgSend(^URL, self, "URL")
}
+47 -30
View File
@@ -37,12 +37,7 @@ log_info :: proc "contextless" (msg: cstring, loc := #caller_location) {
}
abort :: proc "contextless" (msg: cstring, loc := #caller_location) {
abort_ext(
cstring(raw_data(loc.procedure)),
cstring(raw_data(loc.file_path)),
loc.line,
msg,
)
abort_ext(cstring(raw_data(loc.procedure)), cstring(raw_data(loc.file_path)), loc.line, msg)
}
////////////////////////////////////////////////////////////////////////////////
@@ -55,7 +50,12 @@ list_entry :: proc "contextless" (elt: ^list_elt, $T: typeid, $member: string) -
}
// Get the next entry in a list.
list_next_entry :: proc "contextless" (list: ^list, elt: ^list_elt, $T: typeid, $member: string) -> ^T {
list_next_entry :: proc "contextless" (
list: ^list,
elt: ^list_elt,
$T: typeid,
$member: string,
) -> ^T {
if elt.next != list.last {
return list_entry(elt.next, T, member)
}
@@ -64,7 +64,12 @@ list_next_entry :: proc "contextless" (list: ^list, elt: ^list_elt, $T: typeid,
}
// Get the previous entry in a list.
list_prev_entry :: proc "contextless" (list: ^list, elt: ^list_elt, $T: typeid, $member: string) -> ^T {
list_prev_entry :: proc "contextless" (
list: ^list,
elt: ^list_elt,
$T: typeid,
$member: string,
) -> ^T {
if elt.prev != list.last {
return list_entry(elt.prev, T, member)
}
@@ -94,9 +99,23 @@ list_last_entry :: proc "contextless" (list: ^list, $T: typeid, $member: string)
// _elt: ^list_elt
// for elt in oc.list_for(list, &_elt, int, "elt") {
// }
list_for :: proc "contextless" (list: ^list, elt: ^^list_elt, $T: typeid, $member: string) -> (^T, bool) {
list_for :: proc "contextless" (
list: ^list,
elt: ^^list_elt,
$T: typeid,
$member: string,
) -> (
^T,
bool,
) {
if elt == nil {
assert_fail(#file, #procedure, #line, "elt != nil", "misuse of `list_for`, expected `elt` to not be nil")
assert_fail(
#file,
#procedure,
#line,
"elt != nil",
"misuse of `list_for`, expected `elt` to not be nil",
)
}
if elt^ == nil {
@@ -112,7 +131,15 @@ list_for :: proc "contextless" (list: ^list, elt: ^^list_elt, $T: typeid, $membe
list_iter :: list_for
list_for_reverse :: proc "contextless" (list: ^list, elt: ^^list_elt, $T: typeid, $member: string) -> (^T, bool) {
list_for_reverse :: proc "contextless" (
list: ^list,
elt: ^^list_elt,
$T: typeid,
$member: string,
) -> (
^T,
bool,
) {
if elt^ == nil {
elt^ = list.last
entry := list_checked_entry(elt^, T, member)
@@ -226,27 +253,17 @@ str32_list_for :: proc "contextless" (list: ^str32_list, elt: ^^list_elt) -> (^s
return list_for(&list.list, elt, str32_elt, "listElt")
}
@(deferred_none=ui_box_end)
ui_container :: proc "contextless" (name: string, flags: ui_flags = {}) -> ^ui_box {
return ui_box_begin_str8(name, flags)
@(deferred_none = ui_box_end)
ui_container :: proc "contextless" (name: string) -> ^ui_box {
return ui_box_begin_str8(name)
}
@(deferred_none=ui_end_frame)
ui_frame :: proc "contextless" (frame_size: [2]f32, style: ui_style, mask: ui_style_mask) {
ui_begin_frame(frame_size, style, mask)
@(deferred_none = ui_menu_end)
ui_menu :: proc "contextless" (key, name: string) {
ui_menu_begin_str8(key, name)
}
@(deferred_none=ui_panel_end)
ui_panel :: proc "contextless" (name: cstring, flags: ui_flags) {
ui_panel_begin(name, flags)
}
@(deferred_none=ui_menu_end)
ui_menu :: proc "contextless" (name: cstring) {
ui_menu_begin(name)
}
@(deferred_none=ui_menu_bar_end)
ui_menu_bar :: proc "contextless" (name: cstring) {
ui_menu_bar_begin(name)
@(deferred_none = ui_menu_bar_end)
ui_menu_bar :: proc "contextless" (key: string) {
ui_menu_bar_begin_str8(key)
}
+13 -3
View File
@@ -8,15 +8,25 @@ create_odin_logger :: proc(lowest := runtime.Logger_Level.Debug, ident := "") ->
return runtime.Logger{odin_logger_proc, nil, lowest, {}}
}
log_typed :: proc "contextless" (level: log_level, msg: cstring, loc := #caller_location) {
log_ext(
level,
cstring(raw_data(loc.procedure)),
cstring(raw_data(loc.file_path)),
loc.line,
msg,
)
}
odin_logger_proc :: proc(logger_data: rawptr, level: runtime.Logger_Level, text: string, options: runtime.Logger_Options, location := #caller_location) {
cbuf := make([]byte, len(text)+1, context.temp_allocator)
copy(cbuf, text)
ctext := cstring(raw_data(cbuf))
switch level {
case .Debug, .Info: log_info(ctext, location)
case .Warning: log_warning(ctext, location)
case .Debug, .Info: log_typed(.INFO, ctext, location)
case .Warning: log_typed(.WARNING, ctext, location)
case: fallthrough
case .Error, .Fatal: log_error(ctext, location)
case .Error, .Fatal: log_typed(.ERROR, ctext, location)
}
}
+529 -565
View File
File diff suppressed because it is too large Load Diff
+7
View File
@@ -38,6 +38,13 @@ DWMNCRENDERINGPOLICY :: enum {
DWMNCRP_LAST,
}
DWM_WINDOW_CORNER_PREFERENCE :: enum c_int {
DEFAULT,
DONOTROUND,
ROUND,
ROUNDSMALL,
}
@(default_calling_convention="system")
foreign dwmapi {
DwmFlush :: proc() -> HRESULT ---
+3 -3
View File
@@ -2185,7 +2185,7 @@ or_break_and_or_continue_operators :: proc() {
// The concept of 'or_break' and 'or_continue' is very similar to that of 'or_return'.
// The difference is that unlike 'or_return', the value does not get returned from
// the current procedure but rather discarded if it is 'false' or not 'nil', and then
// the specified branch (i.e. break or_continue).
// the specified branch (i.e. break or continue).
// The or branch expression can be labelled if a specific statement needs to be used.
Error :: enum {
@@ -2337,7 +2337,7 @@ matrix_type :: proc() {
// There is no support for booleans, quaternions, or any compound type.
// Indexing a matrix can be used with the matrix indexing syntax
// This mirrors othe type usages: type on the left, usage on the right
// This mirrors other type usages: type on the left, usage on the right
elem := m[1, 2] // row 1, column 2
assert(elem == -6)
@@ -2599,6 +2599,7 @@ main :: proc() {
using_statement()
implicit_context_system()
parametric_polymorphism()
threading_example()
array_programming()
map_type()
implicit_selector_expression()
@@ -2614,7 +2615,6 @@ main :: proc() {
ranged_fields_for_array_compound_literals()
deprecated_attribute()
range_statements_with_multiple_return_values()
threading_example()
soa_struct_layout()
constant_literal_expressions()
union_maybe()
+2 -2
View File
@@ -21,7 +21,7 @@ with tempfile.NamedTemporaryFile(suffix=".odin", delete=True) as temp_file:
temp_file.write(b"package main\n")
for arch, target, triple, cpus, features in archs:
cmd = ["odin", "build", temp_file.name, "-file", "-build-mode:llvm", "-out:temp", "-target-features:\"help\"", f"-target:\"{target}\""]
cmd = ["odin", "build", temp_file.name, "-file", "-use-single-module", "-build-mode:asm", "-out:temp", "-target-features:\"help\"", f"-target:\"{target}\""]
process = subprocess.Popen(cmd, stderr=subprocess.PIPE, text=True)
state = SEEKING_CPUS
@@ -59,7 +59,7 @@ with tempfile.NamedTemporaryFile(suffix=".odin", delete=True) as temp_file:
print(f"odin build returned with non-zero exit code {process.returncode}")
sys.exit(1)
os.remove("temp.ll")
os.remove("temp.S")
def print_default_features(triple, microarch):
cmd = ["./featuregen", triple, microarch]
BIN
View File
Binary file not shown.
+33
View File
@@ -888,6 +888,39 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan
return true;
}
case BuiltinProc_simd_extract_lsbs:
case BuiltinProc_simd_extract_msbs:
{
Operand x = {};
check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
if (!is_type_simd_vector(x.type)) {
gbString xs = type_to_string(x.type);
error(x.expr, "'%.*s' expected a simd vector type, got '%s'", LIT(builtin_name), xs);
gb_string_free(xs);
return false;
}
Type *elem = base_array_type(x.type);
if (!is_type_integer_like(elem)) {
gbString xs = type_to_string(x.type);
error(x.expr, "'%.*s' expected a #simd type with integer or boolean elements, got '%s'", LIT(builtin_name), xs);
gb_string_free(xs);
return false;
}
i64 num_elems = get_array_type_count(x.type);
Type *result_type = alloc_type_bit_set();
result_type->BitSet.elem = t_int;
result_type->BitSet.lower = 0;
result_type->BitSet.upper = num_elems - 1;
operand->mode = Addressing_Value;
operand->type = result_type;
return true;
}
case BuiltinProc_simd_shuffle:
{
+8 -2
View File
@@ -1742,8 +1742,8 @@ gb_internal void add_deps_from_child_to_parent(DeclInfo *decl) {
rw_mutex_shared_lock(&decl->type_info_deps_mutex);
rw_mutex_lock(&decl->parent->type_info_deps_mutex);
for (Type *t : decl->type_info_deps) {
ptr_set_add(&decl->parent->type_info_deps, t);
for (auto const &tt : decl->type_info_deps) {
type_set_add(&decl->parent->type_info_deps, tt);
}
rw_mutex_unlock(&decl->parent->type_info_deps_mutex);
@@ -1784,6 +1784,10 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
ctx->curr_proc_sig = type;
ctx->curr_proc_calling_convention = type->Proc.calling_convention;
if (decl->parent && decl->entity && decl->parent->entity) {
decl->entity->parent_proc_decl = decl->parent;
}
if (ctx->pkg->name != "runtime") {
switch (type->Proc.calling_convention) {
case ProcCC_None:
@@ -1873,6 +1877,8 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
check_open_scope(ctx, body);
{
ctx->scope->decl_info = decl;
for (auto const &entry : using_entities) {
Entity *uvar = entry.uvar;
Entity *prev = scope_insert(ctx->scope, uvar);
+8 -4
View File
@@ -345,7 +345,7 @@ gb_internal void check_scope_decls(CheckerContext *c, Slice<Ast *> const &nodes,
check_collect_entities(c, nodes);
for (auto const &entry : s->elements) {
Entity *e = entry.value;
Entity *e = entry.value;\
switch (e->kind) {
case Entity_Constant:
case Entity_TypeName:
@@ -2599,9 +2599,8 @@ gb_internal ExactValue exact_bit_set_all_set_mask(Type *type) {
continue;
}
BigInt shift_amount = f->Constant.value.value_integer;
big_int_sub_eq(&shift_amount, &b_lower_base);
BigInt shift_amount = {};
big_int_sub(&shift_amount, &f->Constant.value.value_integer, &b_lower_base);
BigInt value = {};
big_int_shl(&value, &one, &shift_amount);
@@ -5509,6 +5508,11 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
case Addressing_SwizzleVariable:
operand->mode = Addressing_SwizzleVariable;
break;
case Addressing_Value:
if (is_type_pointer(original_type)) {
operand->mode = Addressing_SwizzleVariable;
}
break;
}
if (array_type->kind == Type_SimdVector) {
+4 -4
View File
@@ -1446,8 +1446,8 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
Ast *nil_seen = nullptr;
PtrSet<Type *> seen = {};
defer (ptr_set_destroy(&seen));
TypeSet seen = {};
defer (type_set_destroy(&seen));
for (Ast *stmt : bs->stmts) {
if (stmt->kind != Ast_CaseClause) {
@@ -1515,7 +1515,7 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
GB_PANIC("Unknown type to type switch statement");
}
if (type_ptr_set_update(&seen, y.type)) {
if (type_set_update(&seen, y.type)) {
TokenPos pos = cc->token.pos;
gbString expr_str = expr_to_string(y.expr);
error(y.expr,
@@ -1569,7 +1569,7 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
auto unhandled = array_make<Type *>(temporary_allocator(), 0, variants.count);
for (Type *t : variants) {
if (!type_ptr_set_exists(&seen, t)) {
if (!type_set_exists(&seen, t)) {
array_add(&unhandled, t);
}
}
+14 -6
View File
@@ -2859,15 +2859,23 @@ gb_internal void check_matrix_type(CheckerContext *ctx, Type **type, Ast *node)
}
if (generic_row == nullptr && row_count < MATRIX_ELEMENT_COUNT_MIN) {
gbString s = expr_to_string(row.expr);
error(row.expr, "Invalid matrix row count, expected %d+ rows, got %s", MATRIX_ELEMENT_COUNT_MIN, s);
gb_string_free(s);
if (row.expr == nullptr) {
error(node, "Invalid matrix row count, got nothing");
} else {
gbString s = expr_to_string(row.expr);
error(row.expr, "Invalid matrix row count, expected %d+ rows, got %s", MATRIX_ELEMENT_COUNT_MIN, s);
gb_string_free(s);
}
}
if (generic_column == nullptr && column_count < MATRIX_ELEMENT_COUNT_MIN) {
gbString s = expr_to_string(column.expr);
error(column.expr, "Invalid matrix column count, expected %d+ rows, got %s", MATRIX_ELEMENT_COUNT_MIN, s);
gb_string_free(s);
if (column.expr == nullptr) {
error(node, "Invalid matrix column count, got nothing");
} else {
gbString s = expr_to_string(column.expr);
error(column.expr, "Invalid matrix column count, expected %d+ rows, got %s", MATRIX_ELEMENT_COUNT_MIN, s);
gb_string_free(s);
}
}
if ((generic_row == nullptr && generic_column == nullptr) && row_count*column_count > MATRIX_ELEMENT_COUNT_MAX) {
+110 -51
View File
@@ -3,7 +3,10 @@
#include "entity.cpp"
#include "types.cpp"
String get_final_microarchitecture();
gb_internal u64 type_hash_canonical_type(Type *type);
gb_internal String get_final_microarchitecture();
gb_internal void check_expr(CheckerContext *c, Operand *operand, Ast *expression);
gb_internal void check_expr_or_type(CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint=nullptr);
@@ -170,7 +173,7 @@ gb_internal void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) {
d->parent = parent;
d->scope = scope;
ptr_set_init(&d->deps, 0);
ptr_set_init(&d->type_info_deps, 0);
type_set_init(&d->type_info_deps, 0);
d->labels.allocator = heap_allocator();
d->variadic_reuses.allocator = heap_allocator();
d->variadic_reuse_max_bytes = 0;
@@ -355,6 +358,10 @@ gb_internal void check_open_scope(CheckerContext *c, Ast *node) {
scope->flags |= ScopeFlag_Type;
break;
}
if (c->decl && c->decl->proc_lit) {
// Number the scopes within a procedure body depth-first
scope->index = c->decl->scope_index++;
}
c->scope = scope;
c->state_flags |= StateFlag_bounds_check;
}
@@ -825,11 +832,17 @@ gb_internal void add_dependency(CheckerInfo *info, DeclInfo *d, Entity *e) {
rw_mutex_unlock(&d->deps_mutex);
}
gb_internal void add_type_info_dependency(CheckerInfo *info, DeclInfo *d, Type *type) {
if (d == nullptr) {
if (d == nullptr || type == nullptr) {
return;
}
if (type->kind == Type_Named) {
Entity *e = type->Named.type_name;
if (e->TypeName.is_type_alias) {
type = type->Named.base;
}
}
rw_mutex_lock(&d->type_info_deps_mutex);
ptr_set_add(&d->type_info_deps, type);
type_set_add(&d->type_info_deps, type);
rw_mutex_unlock(&d->type_info_deps_mutex);
}
@@ -1358,8 +1371,11 @@ gb_internal void init_checker_info(CheckerInfo *i) {
string_map_init(&i->foreigns);
// map_init(&i->gen_procs);
map_init(&i->gen_types);
array_init(&i->type_info_types, a);
map_init(&i->type_info_map);
type_set_init(&i->min_dep_type_info_set);
map_init(&i->min_dep_type_info_index_map);
// map_init(&i->type_info_map);
string_map_init(&i->files);
string_map_init(&i->packages);
array_init(&i->variable_init_order, a);
@@ -1392,8 +1408,10 @@ gb_internal void destroy_checker_info(CheckerInfo *i) {
string_map_destroy(&i->foreigns);
// map_destroy(&i->gen_procs);
map_destroy(&i->gen_types);
array_free(&i->type_info_types);
map_destroy(&i->type_info_map);
type_set_destroy(&i->min_dep_type_info_set);
map_destroy(&i->min_dep_type_info_index_map);
string_map_destroy(&i->files);
string_map_destroy(&i->packages);
array_free(&i->variable_init_order);
@@ -1627,6 +1645,23 @@ gb_internal void check_remove_expr_info(CheckerContext *c, Ast *e) {
}
}
gb_internal isize type_info_index(CheckerInfo *info, TypeInfoPair pair, bool error_on_failure) {
mutex_lock(&info->minimum_dependency_type_info_mutex);
isize entry_index = -1;
u64 hash = pair.hash;
isize *found_entry_index = map_get(&info->min_dep_type_info_index_map, hash);
if (found_entry_index) {
entry_index = *found_entry_index;
}
mutex_unlock(&info->minimum_dependency_type_info_mutex);
if (error_on_failure && entry_index < 0) {
compiler_error("Type_Info for '%s' could not be found", type_to_string(pair.type));
}
return entry_index;
}
gb_internal isize type_info_index(CheckerInfo *info, Type *type, bool error_on_failure) {
type = default_type(type);
@@ -1634,34 +1669,12 @@ gb_internal isize type_info_index(CheckerInfo *info, Type *type, bool error_on_f
type = t_bool;
}
mutex_lock(&info->type_info_mutex);
isize entry_index = -1;
isize *found_entry_index = map_get(&info->type_info_map, type);
if (found_entry_index) {
entry_index = *found_entry_index;
}
if (entry_index < 0) {
// NOTE(bill): Do manual linear search
for (auto const &e : info->type_info_map) {
if (are_types_identical_unique_tuples(e.key, type)) {
entry_index = e.value;
// NOTE(bill): Add it to the search map
map_set(&info->type_info_map, type, entry_index);
break;
}
}
}
mutex_unlock(&info->type_info_mutex);
if (error_on_failure && entry_index < 0) {
compiler_error("Type_Info for '%s' could not be found", type_to_string(type));
}
return entry_index;
u64 hash = type_hash_canonical_type(type);
return type_info_index(info, {type, hash}, error_on_failure);
}
gb_internal void add_untyped(CheckerContext *c, Ast *expr, AddressingMode mode, Type *type, ExactValue const &value) {
if (expr == nullptr) {
return;
@@ -2013,8 +2026,12 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
}
add_type_info_dependency(c->info, c->decl, t);
#if 0
MUTEX_GUARD_BLOCK(&c->info->type_info_mutex) {
if (type_set_update(&c->info->type_info_set, t)) {
// return;
}
auto found = map_get(&c->info->type_info_map, t);
if (found != nullptr) {
// Types have already been added
@@ -2037,7 +2054,8 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
// Unique entry
// NOTE(bill): map entries grow linearly and in order
ti_index = c->info->type_info_types.count;
array_add(&c->info->type_info_types, t);
TypeInfoPair tt = {t, type_hash_canonical_type(t)};
array_add(&c->info->type_info_types, tt);
}
map_set(&c->checker->info.type_info_map, t, ti_index);
@@ -2232,6 +2250,7 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
GB_PANIC("Unhandled type: %*.s %d", LIT(type_strings[bt->kind]), bt->kind);
break;
}
#endif
}
@@ -2289,19 +2308,7 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) {
return;
}
auto *set = &c->info.minimum_dependency_type_info_set;
isize ti_index = type_info_index(&c->info, t, false);
if (ti_index < 0) {
add_type_info_type(&c->builtin_ctx, t); // Missing the type information
ti_index = type_info_index(&c->info, t, false);
}
GB_ASSERT(ti_index >= 0);
// IMPORTANT NOTE(bill): this must be copied as `map_set` takes a const ref
// and effectively assigns the `+1` of the value
isize const count = set->count;
if (map_set_if_not_previously_exists(set, ti_index+1, count)) {
// Type already exists;
if (type_set_update(&c->info.min_dep_type_info_set, t)) {
return;
}
@@ -2501,8 +2508,8 @@ gb_internal void add_dependency_to_set(Checker *c, Entity *entity) {
if (decl == nullptr) {
return;
}
for (Type *t : decl->type_info_deps) {
add_min_dep_type_info(c, t);
for (TypeInfoPair const tt : decl->type_info_deps) {
add_min_dep_type_info(c, tt.type);
}
for (Entity *e : decl->deps) {
@@ -2702,7 +2709,6 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
isize min_dep_set_cap = next_pow2_isize(entity_count*4); // empirically determined factor
ptr_set_init(&c->info.minimum_dependency_set, min_dep_set_cap);
map_init(&c->info.minimum_dependency_type_info_set);
#define FORCE_ADD_RUNTIME_ENTITIES(condition, ...) do { \
if (condition) { \
@@ -3894,6 +3900,7 @@ gb_internal DECL_ATTRIBUTE_PROC(type_decl_attribute) {
#include "check_expr.cpp"
#include "check_builtin.cpp"
#include "check_type.cpp"
#include "name_canonicalization.cpp"
#include "check_decl.cpp"
#include "check_stmt.cpp"
@@ -6724,6 +6731,58 @@ gb_internal void check_parsed_files(Checker *c) {
add_type_and_value(&c->builtin_ctx, u.expr, u.info->mode, u.info->type, u.info->value);
}
TIME_SECTION("initialize and check for collisions in type info array");
{
Array<TypeInfoPair> type_info_types; // sorted after filled
array_init(&type_info_types, heap_allocator());
defer (array_free(&type_info_types));
for (auto const &tt : c->info.min_dep_type_info_set) {
array_add(&type_info_types, tt);
}
array_sort(type_info_types, type_info_pair_cmp);
array_init(&c->info.type_info_types_hash_map, heap_allocator(), type_info_types.count*2 + 1);
map_reserve(&c->info.min_dep_type_info_index_map, type_info_types.count);
isize hash_map_len = c->info.type_info_types_hash_map.count;
for (auto const &tt : type_info_types) {
isize index = tt.hash % hash_map_len;
// NOTE(bill): no need for a sanity check since there
// will always be enough space for the entries
for (;;) {
if (index == 0 || c->info.type_info_types_hash_map[index].hash != 0) {
index = (index+1) % hash_map_len;
continue;
}
break;
}
c->info.type_info_types_hash_map[index] = tt;
bool exists = map_set_if_not_previously_exists(&c->info.min_dep_type_info_index_map, tt.hash, index);
if (exists) {
for (auto const &entry : c->info.min_dep_type_info_index_map) {
if (entry.key != tt.hash) {
continue;
}
auto const &other = type_info_types[entry.value];
if (are_types_identical_unique_tuples(tt.type, other.type)) {
continue;
}
gbString t = temp_canonical_string(tt.type);
gbString o = temp_canonical_string(other.type);
GB_PANIC("%s (%s) %llu vs %s (%s) %llu",
type_to_string(tt.type, false), t, cast(unsigned long long)tt.hash,
type_to_string(other.type, false), o, cast(unsigned long long)other.hash);
}
}
}
GB_ASSERT(c->info.min_dep_type_info_index_map.count <= type_info_types.count);
}
TIME_SECTION("sort init and fini procedures");
check_sort_init_and_fini_procedures(c);
+18 -7
View File
@@ -167,6 +167,7 @@ typedef DECL_ATTRIBUTE_PROC(DeclAttributeProc);
gb_internal void check_decl_attributes(CheckerContext *c, Array<Ast *> const &attributes, DeclAttributeProc *proc, AttributeContext *ac);
#include "name_canonicalization.hpp"
enum ProcCheckedState : u8 {
ProcCheckedState_Unchecked,
@@ -221,13 +222,15 @@ struct DeclInfo {
RwMutex deps_mutex;
PtrSet<Entity *> deps;
RwMutex type_info_deps_mutex;
PtrSet<Type *> type_info_deps;
RwMutex type_info_deps_mutex;
TypeSet type_info_deps;
BlockingMutex type_and_value_mutex;
Array<BlockLabel> labels;
i32 scope_index;
Array<VariadicReuseData> variadic_reuses;
i64 variadic_reuse_max_bytes;
i64 variadic_reuse_max_align;
@@ -272,10 +275,14 @@ struct Scope {
std::atomic<Scope *> next;
std::atomic<Scope *> head_child;
i32 index; // within a procedure
RwMutex mutex;
StringMap<Entity *> elements;
PtrSet<Scope *> imported;
DeclInfo *decl_info;
i32 flags; // ScopeFlag
union {
AstPackage *pkg;
@@ -421,8 +428,10 @@ struct CheckerInfo {
Scope * init_scope;
Entity * entry_point;
PtrSet<Entity *> minimum_dependency_set;
PtrMap</*type info index*/isize, /*min dep index*/isize> minimum_dependency_type_info_set;
BlockingMutex minimum_dependency_type_info_mutex;
PtrMap</*type info hash*/u64, /*min dep index*/isize> min_dep_type_info_index_map;
TypeSet min_dep_type_info_set;
Array<TypeInfoPair> type_info_types_hash_map; // 2 * type_info_types.count
Array<Entity *> testing_procedures;
@@ -450,9 +459,10 @@ struct CheckerInfo {
BlockingMutex gen_types_mutex;
PtrMap<Type *, GenTypesData *> gen_types;
BlockingMutex type_info_mutex; // NOT recursive
Array<Type *> type_info_types;
PtrMap<Type *, isize> type_info_map;
// BlockingMutex type_info_mutex; // NOT recursive
// Array<TypeInfoPair> type_info_types;
// PtrMap<Type *, isize> type_info_map;
// TypeSet type_info_set;
BlockingMutex foreign_mutex; // NOT recursive
StringMap<Entity *> foreigns;
@@ -571,6 +581,7 @@ gb_internal DeclInfo * decl_info_of_entity (Entity * e);
gb_internal AstFile * ast_file_of_filename (CheckerInfo *i, String filename);
// IMPORTANT: Only to use once checking is done
gb_internal isize type_info_index (CheckerInfo *i, Type *type, bool error_on_failure);
gb_internal isize type_info_index (CheckerInfo *info, TypeInfoPair pair, bool error_on_failure);
// Will return nullptr if not found
gb_internal Entity *entity_of_node(Ast *expr);
+6
View File
@@ -181,6 +181,9 @@ BuiltinProc__simd_begin,
BuiltinProc_simd_reduce_any,
BuiltinProc_simd_reduce_all,
BuiltinProc_simd_extract_lsbs,
BuiltinProc_simd_extract_msbs,
BuiltinProc_simd_shuffle,
BuiltinProc_simd_select,
@@ -523,6 +526,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("simd_reduce_any"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_all"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_extract_lsbs"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_extract_msbs"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_shuffle"), 2, true, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_select"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+2 -2
View File
@@ -134,9 +134,9 @@ gb_internal u32 fnv32a(void const *data, isize len) {
return h;
}
gb_internal u64 fnv64a(void const *data, isize len) {
gb_internal u64 fnv64a(void const *data, isize len, u64 seed=0xcbf29ce484222325ull) {
u8 const *bytes = cast(u8 const *)data;
u64 h = 0xcbf29ce484222325ull;
u64 h = seed;
for (; len >= 8; len -= 8, bytes += 8) {
h = (h ^ bytes[0]) * 0x100000001b3ull;
+18 -47
View File
@@ -16,6 +16,8 @@ gb_global char const* OdinDocWriterState_strings[] {
"writing ",
};
gb_global std::atomic<bool> g_in_doc_writer;
struct OdinDocWriter {
CheckerInfo *info;
OdinDocWriterState state;
@@ -26,11 +28,10 @@ struct OdinDocWriter {
StringMap<OdinDocString> string_cache;
OrderedInsertPtrMap<AstFile *, OdinDocFileIndex> file_cache;
OrderedInsertPtrMap<AstPackage *, OdinDocPkgIndex> pkg_cache;
OrderedInsertPtrMap<Entity *, OdinDocEntityIndex> entity_cache;
OrderedInsertPtrMap<Type *, OdinDocTypeIndex> type_cache;
OrderedInsertPtrMap<Type *, Type *> stable_type_cache;
OrderedInsertPtrMap<AstFile *, OdinDocFileIndex> file_cache;
OrderedInsertPtrMap<AstPackage *, OdinDocPkgIndex> pkg_cache;
OrderedInsertPtrMap<Entity *, OdinDocEntityIndex> entity_cache;
OrderedInsertPtrMap<u64/*type hash*/, OdinDocTypeIndex> type_cache;
OdinDocWriterItemTracker<OdinDocFile> files;
OdinDocWriterItemTracker<OdinDocPkg> pkgs;
@@ -61,7 +62,6 @@ gb_internal void odin_doc_writer_prepare(OdinDocWriter *w) {
map_init(&w->pkg_cache, 1<<10);
map_init(&w->entity_cache, 1<<18);
map_init(&w->type_cache, 1<<18);
map_init(&w->stable_type_cache, 1<<18);
odin_doc_writer_item_tracker_init(&w->files, 1);
odin_doc_writer_item_tracker_init(&w->pkgs, 1);
@@ -81,7 +81,6 @@ gb_internal void odin_doc_writer_destroy(OdinDocWriter *w) {
map_destroy(&w->pkg_cache);
map_destroy(&w->entity_cache);
map_destroy(&w->type_cache);
map_destroy(&w->stable_type_cache);
}
@@ -492,55 +491,18 @@ gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
}
}
// Type **mapped_type = map_get(&w->stable_type_cache, type); // may map to itself
// if (mapped_type && *mapped_type) {
// type = *mapped_type;
// }
OdinDocTypeIndex *found = map_get(&w->type_cache, type);
u64 type_hash = type_hash_canonical_type(type);
OdinDocTypeIndex *found = map_get(&w->type_cache, type_hash);
if (found) {
return *found;
}
for (auto const &entry : w->type_cache) {
// NOTE(bill): THIS IS SLOW
Type *x = type;
Type *y = entry.key;
if (x == y) {
goto do_set;
}
if (!x | !y) {
continue;
}
if (y->kind == Type_Named) {
Entity *e = y->Named.type_name;
if (e->TypeName.is_type_alias) {
y = y->Named.base;
}
}
if (x->kind != y->kind) {
continue;
}
if (!are_types_identical_internal(x, y, true)) {
continue;
}
do_set:
OdinDocTypeIndex index = entry.value;
map_set(&w->type_cache, type, index);
map_set(&w->stable_type_cache, type, entry.key);
return index;
}
OdinDocType *dst = nullptr;
OdinDocType doc_type = {};
OdinDocTypeIndex type_index = 0;
type_index = odin_doc_write_item(w, &w->types, &doc_type, &dst);
map_set(&w->type_cache, type, type_index);
map_set(&w->stable_type_cache, type, type);
map_set(&w->type_cache, type_hash, type_index);
switch (type->kind) {
case Type_Basic:
@@ -1177,6 +1139,8 @@ gb_internal void odin_doc_write_to_file(OdinDocWriter *w, char const *filename)
}
gb_internal void odin_doc_write(CheckerInfo *info, char const *filename) {
g_in_doc_writer.store(true);
OdinDocWriter w_ = {};
OdinDocWriter *w = &w_;
defer (odin_doc_writer_destroy(w));
@@ -1192,4 +1156,11 @@ gb_internal void odin_doc_write(CheckerInfo *info, char const *filename) {
odin_doc_writer_end_writing(w);
odin_doc_write_to_file(w, filename);
g_in_doc_writer.store(false);
}
gb_internal bool is_in_doc_writer(void) {
return g_in_doc_writer.load();
}
+1
View File
@@ -257,6 +257,7 @@ struct Entity {
bool has_instrumentation : 1;
bool is_memcpy_like : 1;
bool uses_branch_location : 1;
bool is_anonymous : 1;
} Procedure;
struct {
Array<Entity *> entities;
+1 -1
View File
@@ -5856,7 +5856,7 @@ gb_inline isize gb_fprintf_va(struct gbFile *f, char const *fmt, va_list va) {
gb_inline char *gb_bprintf_va(char const *fmt, va_list va) {
gb_local_persist char buffer[4096];
gb_thread_local gb_local_persist char buffer[4096];
gb_snprintf_va(buffer, gb_size_of(buffer), fmt, va);
return buffer;
}
+72 -78
View File
@@ -24,7 +24,7 @@
#include "llvm_backend_stmt.cpp"
#include "llvm_backend_proc.cpp"
String get_default_microarchitecture() {
gb_internal String get_default_microarchitecture() {
String default_march = str_lit("generic");
if (build_context.metrics.arch == TargetArch_amd64) {
// NOTE(bill): x86-64-v2 is more than enough for everyone
@@ -47,7 +47,7 @@ String get_default_microarchitecture() {
return default_march;
}
String get_final_microarchitecture() {
gb_internal String get_final_microarchitecture() {
BuildContext *bc = &build_context;
String microarch = bc->microarch;
@@ -169,7 +169,7 @@ gb_internal void lb_correct_entity_linkage(lbGenerator *gen) {
other_global = LLVMGetNamedGlobal(ec.other_module->mod, ec.cname);
if (other_global) {
LLVMSetLinkage(other_global, LLVMWeakAnyLinkage);
if (!ec.e->Variable.is_export) {
if (!ec.e->Variable.is_export && !ec.e->Variable.is_foreign) {
LLVMSetVisibility(other_global, LLVMHiddenVisibility);
}
}
@@ -177,7 +177,7 @@ gb_internal void lb_correct_entity_linkage(lbGenerator *gen) {
other_global = LLVMGetNamedFunction(ec.other_module->mod, ec.cname);
if (other_global) {
LLVMSetLinkage(other_global, LLVMWeakAnyLinkage);
if (!ec.e->Procedure.is_export) {
if (!ec.e->Procedure.is_export && !ec.e->Procedure.is_foreign) {
LLVMSetVisibility(other_global, LLVMHiddenVisibility);
}
}
@@ -233,6 +233,16 @@ gb_internal lbContextData *lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx
}
gb_internal String lb_internal_gen_name_from_type(char const *prefix, Type *type) {
gbString str = gb_string_make(permanent_allocator(), prefix);
u64 hash = type_hash_canonical_type(type);
str = gb_string_appendc(str, "-");
str = gb_string_append_fmt(str, "%llu", cast(unsigned long long)hash);
String proc_name = make_string(cast(u8 const *)str, gb_string_length(str));
return proc_name;
}
gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type) {
type = base_type(type);
GB_ASSERT(is_type_comparable(type));
@@ -240,7 +250,8 @@ gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type) {
Type *pt = alloc_type_pointer(type);
LLVMTypeRef ptr_type = lb_type(m, pt);
lbProcedure **found = map_get(&m->equal_procs, type);
String proc_name = lb_internal_gen_name_from_type("__$equal", type);
lbProcedure **found = string_map_get(&m->gen_procs, proc_name);
lbProcedure *compare_proc = nullptr;
if (found) {
compare_proc = *found;
@@ -248,17 +259,12 @@ gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type) {
return {compare_proc->value, compare_proc->type};
}
static std::atomic<u32> proc_index;
char buf[32] = {};
isize n = gb_snprintf(buf, 32, "__$equal%u", 1+proc_index.fetch_add(1));
char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
String proc_name = make_string_c(str);
lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_equal_proc);
map_set(&m->equal_procs, type, p);
string_map_set(&m->gen_procs, proc_name, p);
lb_begin_procedure_body(p);
LLVMSetLinkage(p->value, LLVMInternalLinkage);
// lb_add_attribute_to_proc(m, p->value, "readonly");
lb_add_attribute_to_proc(m, p->value, "nounwind");
@@ -410,24 +416,19 @@ gb_internal lbValue lb_hasher_proc_for_type(lbModule *m, Type *type) {
Type *pt = alloc_type_pointer(type);
lbProcedure **found = map_get(&m->hasher_procs, type);
String proc_name = lb_internal_gen_name_from_type("__$hasher", type);
lbProcedure **found = string_map_get(&m->gen_procs, proc_name);
if (found) {
GB_ASSERT(*found != nullptr);
return {(*found)->value, (*found)->type};
}
static std::atomic<u32> proc_index;
char buf[32] = {};
isize n = gb_snprintf(buf, 32, "__$hasher%u", 1+proc_index.fetch_add(1));
char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
String proc_name = make_string_c(str);
lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_hasher_proc);
map_set(&m->hasher_procs, type, p);
string_map_set(&m->gen_procs, proc_name, p);
lb_begin_procedure_body(p);
defer (lb_end_procedure_body(p));
LLVMSetLinkage(p->value, LLVMInternalLinkage);
// lb_add_attribute_to_proc(m, p->value, "readonly");
lb_add_attribute_to_proc(m, p->value, "nounwind");
@@ -577,21 +578,15 @@ gb_internal lbValue lb_map_get_proc_for_type(lbModule *m, Type *type) {
type = base_type(type);
GB_ASSERT(type->kind == Type_Map);
lbProcedure **found = map_get(&m->map_get_procs, type);
String proc_name = lb_internal_gen_name_from_type("__$map_get", type);
lbProcedure **found = string_map_get(&m->gen_procs, proc_name);
if (found) {
GB_ASSERT(*found != nullptr);
return {(*found)->value, (*found)->type};
}
static std::atomic<u32> proc_index;
char buf[32] = {};
isize n = gb_snprintf(buf, 32, "__$map_get-%u", 1+proc_index.fetch_add(1));
char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
String proc_name = make_string_c(str);
lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_map_get_proc);
map_set(&m->map_get_procs, type, p);
string_map_set(&m->gen_procs, proc_name, p);
lb_begin_procedure_body(p);
defer (lb_end_procedure_body(p));
@@ -758,21 +753,15 @@ gb_internal lbValue lb_map_set_proc_for_type(lbModule *m, Type *type) {
type = base_type(type);
GB_ASSERT(type->kind == Type_Map);
lbProcedure **found = map_get(&m->map_set_procs, type);
String proc_name = lb_internal_gen_name_from_type("__$map_set", type);
lbProcedure **found = string_map_get(&m->gen_procs, proc_name);
if (found) {
GB_ASSERT(*found != nullptr);
return {(*found)->value, (*found)->type};
}
static std::atomic<u32> proc_index;
char buf[32] = {};
isize n = gb_snprintf(buf, 32, "__$map_set-%u", 1+proc_index.fetch_add(1));
char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
String proc_name = make_string_c(str);
lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_map_set_proc);
map_set(&m->map_set_procs, type, p);
string_map_set(&m->gen_procs, proc_name, p);
lb_begin_procedure_body(p);
defer (lb_end_procedure_body(p));
@@ -917,7 +906,7 @@ gb_internal lbValue lb_gen_map_cell_info_ptr(lbModule *m, Type *type) {
LLVMValueRef llvm_res = llvm_const_named_struct(m, t_map_cell_info, const_values, gb_count_of(const_values));
lbValue res = {llvm_res, t_map_cell_info};
lbAddr addr = lb_add_global_generated(m, t_map_cell_info, res, nullptr);
lbAddr addr = lb_add_global_generated_with_name(m, t_map_cell_info, res, lb_internal_gen_name_from_type("ggv$map_cell_info", type));
lb_make_global_private_const(addr);
map_set(&m->map_cell_info_map, type, addr);
@@ -948,7 +937,7 @@ gb_internal lbValue lb_gen_map_info_ptr(lbModule *m, Type *map_type) {
LLVMValueRef llvm_res = llvm_const_named_struct(m, t_map_info, const_values, gb_count_of(const_values));
lbValue res = {llvm_res, t_map_info};
lbAddr addr = lb_add_global_generated(m, t_map_info, res, nullptr);
lbAddr addr = lb_add_global_generated_with_name(m, t_map_info, res, lb_internal_gen_name_from_type("ggv$map_info", map_type));
lb_make_global_private_const(addr);
map_set(&m->map_info_map, map_type, addr);
@@ -1284,7 +1273,10 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
if (is_type_any(t)) {
// NOTE(bill): Edge case for 'any' type
Type *var_type = default_type(var.init.type);
lbAddr g = lb_add_global_generated(main_module, var_type, var.init);
gbString var_name = gb_string_make(permanent_allocator(), "__$global_any::");
gbString e_str = string_canonical_entity_name(temporary_allocator(), e);
var_name = gb_string_append_length(var_name, e_str, gb_strlen(e_str));
lbAddr g = lb_add_global_generated_with_name(main_module, var_type, var.init, make_string_c(var_name));
lb_addr_store(p, g, var.init);
lbValue gp = lb_addr_get_ptr(p, g);
@@ -1563,21 +1555,13 @@ gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) {
}
}
for (auto const &entry : m->equal_procs) {
for (auto const &entry : m->gen_procs) {
lbProcedure *p = entry.value;
lb_llvm_function_pass_per_function_internal(m, p);
}
for (auto const &entry : m->hasher_procs) {
lbProcedure *p = entry.value;
lb_llvm_function_pass_per_function_internal(m, p);
}
for (auto const &entry : m->map_get_procs) {
lbProcedure *p = entry.value;
lb_llvm_function_pass_per_function_internal(m, p, lbFunctionPassManager_none);
}
for (auto const &entry : m->map_set_procs) {
lbProcedure *p = entry.value;
lb_llvm_function_pass_per_function_internal(m, p, lbFunctionPassManager_none);
if (string_starts_with(p->name, str_lit("__$map"))) {
lb_llvm_function_pass_per_function_internal(m, p, lbFunctionPassManager_none);
} else {
lb_llvm_function_pass_per_function_internal(m, p);
}
}
return 0;
@@ -2575,17 +2559,16 @@ gb_internal String lb_filepath_ll_for_module(lbModule *m) {
build_context.build_paths[BuildPath_Output].name
);
if (m->file) {
char buf[32] = {};
isize n = gb_snprintf(buf, gb_size_of(buf), "-%u", m->file->id);
String suffix = make_string((u8 *)buf, n-1);
path = concatenate_strings(permanent_allocator(), path, suffix);
} else if (m->pkg) {
path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
} else if (USE_SEPARATE_MODULES) {
path = concatenate_strings(permanent_allocator(), path, STR_LIT("-builtin"));
GB_ASSERT(m->module_name != nullptr);
String s = make_string_c(m->module_name);
String prefix = str_lit("odin_package-");
if (string_starts_with(s, prefix)) {
s.text += prefix.len;
s.len -= prefix.len;
}
path = concatenate_strings(permanent_allocator(), path, STR_LIT(".ll"));
path = concatenate_strings(permanent_allocator(), path, s);
path = concatenate_strings(permanent_allocator(), s, STR_LIT(".ll"));
return path;
}
@@ -2608,17 +2591,21 @@ gb_internal String lb_filepath_obj_for_module(lbModule *m) {
path = gb_string_appendc(path, "/");
path = gb_string_append_length(path, name.text, name.len);
if (m->file) {
char buf[32] = {};
isize n = gb_snprintf(buf, gb_size_of(buf), "-%u", m->file->id);
String suffix = make_string((u8 *)buf, n-1);
path = gb_string_append_length(path, suffix.text, suffix.len);
} else if (m->pkg) {
path = gb_string_appendc(path, "-");
path = gb_string_append_length(path, m->pkg->name.text, m->pkg->name.len);
{
GB_ASSERT(m->module_name != nullptr);
String s = make_string_c(m->module_name);
String prefix = str_lit("odin_package");
if (string_starts_with(s, prefix)) {
s.text += prefix.len;
s.len -= prefix.len;
}
path = gb_string_append_length(path, s.text, s.len);
}
if (use_temporary_directory) {
// NOTE(bill): this must be suffixed to ensure it is not conflicting with anything else in the temporary directory
path = gb_string_append_fmt(path, "-%p", m);
}
@@ -2825,7 +2812,7 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star
Type *t_Internal_Test = find_type_in_pkg(m->info, str_lit("testing"), str_lit("Internal_Test"));
Type *array_type = alloc_type_array(t_Internal_Test, m->info->testing_procedures.count);
Type *slice_type = alloc_type_slice(t_Internal_Test);
lbAddr all_tests_array_addr = lb_add_global_generated(p->module, array_type, {});
lbAddr all_tests_array_addr = lb_add_global_generated_with_name(p->module, array_type, {}, str_lit("__$all_tests_array"));
lbValue all_tests_array = lb_addr_get_ptr(p, all_tests_array_addr);
LLVMValueRef indices[2] = {};
@@ -3154,7 +3141,10 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
lbModule *m = default_module;
{ // Add type info data
isize max_type_info_count = info->minimum_dependency_type_info_set.count+1;
// GB_ASSERT_MSG(info->minimum_dependency_type_info_index_map.count == info->type_info_types.count, "%tu vs %tu", info->minimum_dependency_type_info_index_map.count, info->type_info_types.count);
// isize max_type_info_count = info->minimum_dependency_type_info_index_map.count+1;
isize max_type_info_count = info->type_info_types_hash_map.count;
Type *t = alloc_type_array(t_type_info_ptr, max_type_info_count);
// IMPORTANT NOTE(bill): As LLVM does not have a union type, an array of unions cannot be initialized
@@ -3166,7 +3156,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
LLVMValueRef g = LLVMAddGlobal(m->mod, internal_llvm_type, LB_TYPE_INFO_DATA_NAME);
LLVMSetInitializer(g, LLVMConstNull(internal_llvm_type));
LLVMSetLinkage(g, USE_SEPARATE_MODULES ? LLVMExternalLinkage : LLVMInternalLinkage);
LLVMSetUnnamedAddress(g, LLVMGlobalUnnamedAddr);
// LLVMSetUnnamedAddress(g, LLVMGlobalUnnamedAddr);
LLVMSetGlobalConstant(g, true);
lbValue value = {};
@@ -3182,7 +3172,11 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
isize count = 0;
isize offsets_extra = 0;
for (Type *t : m->info->type_info_types) {
for (auto const &tt : m->info->type_info_types_hash_map) {
Type *t = tt.type;
if (t == nullptr) {
continue;
}
isize index = lb_type_info_index(m->info, t, false);
if (index < 0) {
continue;
+18 -18
View File
@@ -160,15 +160,17 @@ struct lbModule {
AstFile *file; // possibly associated
char const *module_name;
PtrMap<Type *, LLVMTypeRef> types; // mutex: types_mutex
PtrMap<u64/*type hash*/, LLVMTypeRef> types; // mutex: types_mutex
PtrMap<void *, lbStructFieldRemapping> struct_field_remapping; // Key: LLVMTypeRef or Type *, mutex: types_mutex
PtrMap<Type *, LLVMTypeRef> func_raw_types; // mutex: func_raw_types_mutex
RecursiveMutex types_mutex;
RecursiveMutex func_raw_types_mutex;
PtrMap<u64/*type hash*/, LLVMTypeRef> func_raw_types; // mutex: func_raw_types_mutex
RecursiveMutex types_mutex;
RecursiveMutex func_raw_types_mutex;
i32 internal_type_level;
RwMutex values_mutex;
std::atomic<u32> global_array_index;
PtrMap<Entity *, lbValue> values;
PtrMap<Entity *, lbAddr> soa_values;
StringMap<lbValue> members;
@@ -178,14 +180,9 @@ struct lbModule {
StringMap<LLVMValueRef> const_strings;
PtrMap<Type *, struct lbFunctionType *> function_type_map;
PtrMap<u64/*type hash*/, struct lbFunctionType *> function_type_map;
PtrMap<Type *, lbProcedure *> equal_procs;
PtrMap<Type *, lbProcedure *> hasher_procs;
PtrMap<Type *, lbProcedure *> map_get_procs;
PtrMap<Type *, lbProcedure *> map_set_procs;
std::atomic<u32> nested_type_name_guid;
StringMap<lbProcedure *> gen_procs; // key is the canonicalized name
Array<lbProcedure *> procedures_to_generate;
Array<Entity *> global_procedures_to_create;
@@ -204,8 +201,8 @@ struct lbModule {
StringMap<lbObjcRef> objc_classes;
StringMap<lbObjcRef> objc_selectors;
PtrMap<Type *, lbAddr> map_cell_info_map; // address of runtime.Map_Info
PtrMap<Type *, lbAddr> map_info_map; // address of runtime.Map_Cell_Info
PtrMap<u64/*type hash*/, lbAddr> map_cell_info_map; // address of runtime.Map_Info
PtrMap<u64/*type hash*/, lbAddr> map_info_map; // address of runtime.Map_Cell_Info
PtrMap<Ast *, lbAddr> exact_value_compound_literal_addr_map; // Key: Ast_CompoundLit
@@ -231,9 +228,6 @@ struct lbGenerator : LinkerData {
RecursiveMutex anonymous_proc_lits_mutex;
PtrMap<Ast *, lbProcedure *> anonymous_proc_lits;
std::atomic<u32> global_array_index;
std::atomic<u32> global_generated_index;
isize used_module_count;
lbProcedure *startup_runtime;
@@ -364,6 +358,8 @@ struct lbProcedure {
bool in_multi_assignment;
Array<LLVMValueRef> raw_input_parameters;
u32 global_generated_index;
bool uses_branch_location;
TokenPos branch_location_pos;
TokenPos curr_token_pos;
@@ -399,7 +395,7 @@ struct lbProcedure {
gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c);
gb_internal String lb_mangle_name(Entity *e);
gb_internal String lb_get_entity_name(lbModule *m, Entity *e, String name = {});
gb_internal String lb_get_entity_name(lbModule *m, Entity *e);
gb_internal LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char const *name, u64 value=0);
gb_internal LLVMAttributeRef lb_create_enum_attribute_with_type(LLVMContextRef ctx, char const *name, LLVMTypeRef type);
@@ -473,7 +469,8 @@ gb_internal lbContextData *lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx
gb_internal lbContextData *lb_push_context_onto_stack_from_implicit_parameter(lbProcedure *p);
gb_internal lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value={}, Entity **entity_=nullptr);
gb_internal lbAddr lb_add_global_generated_from_procedure(lbProcedure *p, Type *type, lbValue value={});
gb_internal lbAddr lb_add_global_generated_with_name(lbModule *m, Type *type, lbValue value, String name, Entity **entity_=nullptr);
gb_internal lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e=nullptr, bool zero_init=true, bool force_no_init=false);
gb_internal void lb_add_foreign_library_path(lbModule *m, Entity *e);
@@ -610,6 +607,9 @@ gb_internal LLVMTypeRef llvm_array_type(LLVMTypeRef ElementType, uint64_t Elemen
}
gb_internal String lb_internal_gen_name_from_type(char const *prefix, Type *type);
gb_internal void lb_set_metadata_custom_u64(lbModule *m, LLVMValueRef v_ref, String name, u64 value);
gb_internal u64 lb_get_metadata_custom_u64(lbModule *m, LLVMValueRef v_ref, String name);
+37 -11
View File
@@ -330,31 +330,57 @@ gb_internal lbValue lb_emit_source_code_location_const(lbProcedure *p, Ast *node
return lb_emit_source_code_location_const(p, proc_name, pos);
}
gb_internal String lb_source_code_location_gen_name(String const &procedure, TokenPos const &pos) {
gbString s = gb_string_make(permanent_allocator(), "scl$[");
s = gb_string_append_length(s, procedure.text, procedure.len);
if (pos.offset != 0) {
s = gb_string_append_fmt(s, "%d", pos.offset);
} else {
s = gb_string_append_fmt(s, "%d_%d", pos.line, pos.column);
}
s = gb_string_appendc(s, "]");
return make_string(cast(u8 const *)s, gb_string_length(s));
}
gb_internal String lb_source_code_location_gen_name(lbProcedure *p, Ast *node) {
String proc_name = {};
if (p->entity) {
proc_name = p->entity->token.string;
}
TokenPos pos = {};
if (node) {
pos = ast_token(node).pos;
}
return lb_source_code_location_gen_name(proc_name, pos);
}
gb_internal lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, String const &procedure, TokenPos const &pos) {
lbValue loc = lb_emit_source_code_location_const(p, procedure, pos);
lbAddr addr = lb_add_global_generated(p->module, loc.type, loc, nullptr);
lbAddr addr = lb_add_global_generated_with_name(p->module, loc.type, loc, lb_source_code_location_gen_name(procedure, pos));
lb_make_global_private_const(addr);
return addr.addr;
}
gb_internal lbValue lb_const_source_code_location_as_global_ptr(lbModule *m, String const &procedure, TokenPos const &pos) {
lbValue loc = lb_const_source_code_location_const(m, procedure, pos);
lbAddr addr = lb_add_global_generated(m, loc.type, loc, nullptr);
lbAddr addr = lb_add_global_generated_with_name(m, loc.type, loc, lb_source_code_location_gen_name(procedure, pos));
lb_make_global_private_const(addr);
return addr.addr;
}
gb_internal lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, Ast *node) {
lbValue loc = lb_emit_source_code_location_const(p, node);
lbAddr addr = lb_add_global_generated(p->module, loc.type, loc, nullptr);
lbAddr addr = lb_add_global_generated_with_name(p->module, loc.type, loc, lb_source_code_location_gen_name(p, node));
lb_make_global_private_const(addr);
return addr.addr;
}
gb_internal lbValue lb_emit_source_code_location_as_global(lbProcedure *p, String const &procedure, TokenPos const &pos) {
return lb_emit_load(p, lb_emit_source_code_location_as_global_ptr(p, procedure, pos));
}
@@ -562,12 +588,12 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
return lb_addr_load(p, slice);
}
} else {
isize max_len = 7+8+1;
char *str = gb_alloc_array(permanent_allocator(), char, max_len);
u32 id = m->gen->global_array_index.fetch_add(1);
isize len = gb_snprintf(str, max_len, "csba$%x", id);
u32 id = m->global_array_index.fetch_add(1);
gbString str = gb_string_make(temporary_allocator(), "csba$");
str = gb_string_appendc(str, m->module_name);
str = gb_string_append_fmt(str, "$%x", id);
String name = make_string(cast(u8 *)str, len-1);
String name = make_string(cast(u8 const *)str, gb_string_length(str));
Entity *e = alloc_entity_constant(nullptr, make_token_ident(name), t, value);
array_data = LLVMAddGlobal(m->mod, lb_type(m, t), str);
+13 -16
View File
@@ -843,7 +843,7 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
8*cast(unsigned)type_align_of(type),
lb_debug_type(m, type->EnumeratedArray.elem),
subscripts, gb_count_of(subscripts));
gbString name = type_to_string(type, temporary_allocator());
gbString name = temp_canonical_string(type);
return LLVMDIBuilderCreateTypedef(m->debug_builder, array_type, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type)));
}
@@ -852,16 +852,16 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
Type *bt = base_type(type->Map.debug_metadata_type);
GB_ASSERT(bt->kind == Type_Struct);
return lb_debug_struct(m, type, bt, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
return lb_debug_struct(m, type, bt, type_to_canonical_string(temporary_allocator(), type), nullptr, nullptr, 0);
}
case Type_Struct: return lb_debug_struct( m, type, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
case Type_Slice: return lb_debug_slice( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
case Type_DynamicArray: return lb_debug_dynamic_array(m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
case Type_Union: return lb_debug_union( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
case Type_BitSet: return lb_debug_bitset( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
case Type_Enum: return lb_debug_enum( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
case Type_BitField: return lb_debug_bitfield( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
case Type_Struct: return lb_debug_struct( m, type, type, type_to_canonical_string(temporary_allocator(), type), nullptr, nullptr, 0);
case Type_Slice: return lb_debug_slice( m, type, type_to_canonical_string(temporary_allocator(), type), nullptr, nullptr, 0);
case Type_DynamicArray: return lb_debug_dynamic_array(m, type, type_to_canonical_string(temporary_allocator(), type), nullptr, nullptr, 0);
case Type_Union: return lb_debug_union( m, type, type_to_canonical_string(temporary_allocator(), type), nullptr, nullptr, 0);
case Type_BitSet: return lb_debug_bitset( m, type, type_to_canonical_string(temporary_allocator(), type), nullptr, nullptr, 0);
case Type_Enum: return lb_debug_enum( m, type, type_to_canonical_string(temporary_allocator(), type), nullptr, nullptr, 0);
case Type_BitField: return lb_debug_bitfield( m, type, type_to_canonical_string(temporary_allocator(), type), nullptr, nullptr, 0);
case Type_Tuple:
if (type->Tuple.variables.count == 1) {
@@ -904,7 +904,7 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
{
LLVMMetadataRef proc_underlying_type = lb_debug_type_internal_proc(m, type);
LLVMMetadataRef pointer_type = LLVMDIBuilderCreatePointerType(m->debug_builder, proc_underlying_type, ptr_bits, ptr_bits, 0, nullptr, 0);
gbString name = type_to_string(type, temporary_allocator());
gbString name = temp_canonical_string(type);
return LLVMDIBuilderCreateTypedef(m->debug_builder, pointer_type, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type)));
}
break;
@@ -987,10 +987,7 @@ gb_internal LLVMMetadataRef lb_debug_type(lbModule *m, Type *type) {
line = cast(unsigned)e->token.pos.line;
}
String name = type->Named.name;
if (type->Named.type_name && type->Named.type_name->pkg && type->Named.type_name->pkg->name.len != 0) {
name = concatenate3_strings(temporary_allocator(), type->Named.type_name->pkg->name, str_lit("."), type->Named.name);
}
String name = type_to_canonical_string(temporary_allocator(), type);
Type *bt = base_type(type->Named.base);
@@ -1187,8 +1184,8 @@ gb_internal void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx
gb_internal String debug_info_mangle_constant_name(Entity *e, gbAllocator const &allocator, bool *did_allocate_) {
String name = e->token.string;
if (e->pkg && e->pkg->name.len > 0) {
// NOTE(bill): C++ NONSENSE FOR DEBUG SHITE!
name = concatenate3_strings(allocator, e->pkg->name, str_lit("::"), name);
gbString s = string_canonical_entity_name(allocator, e);
name = make_string(cast(u8 const *)s, gb_string_length(s));
if (did_allocate_) *did_allocate_ = true;
}
return name;
+16 -7
View File
@@ -1554,7 +1554,7 @@ gb_internal lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
lbValue cmp = lb_emit_comp_against_nil(p, be->op.kind, right);
Type *type = default_type(tv.type);
return lb_emit_conv(p, cmp, type);
} else if (lb_is_empty_string_constant(be->right)) {
} else if (lb_is_empty_string_constant(be->right) && !is_type_union(be->left->tav.type)) {
// `x == ""` or `x != ""`
lbValue s = lb_build_expr(p, be->left);
s = lb_emit_conv(p, s, t_string);
@@ -1562,7 +1562,7 @@ gb_internal lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
lbValue cmp = lb_emit_comp(p, be->op.kind, len, lb_const_int(p->module, t_int, 0));
Type *type = default_type(tv.type);
return lb_emit_conv(p, cmp, type);
} else if (lb_is_empty_string_constant(be->left)) {
} else if (lb_is_empty_string_constant(be->left) && !is_type_union(be->right->tav.type)) {
// `"" == x` or `"" != x`
lbValue s = lb_build_expr(p, be->right);
s = lb_emit_conv(p, s, t_string);
@@ -2312,9 +2312,9 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
lbValue array_const_value = {};
array_const_value.type = t;
array_const_value.value = LLVMConstArray(lb_type(m, elem), values, cast(unsigned)index_count);
v = lb_add_global_generated(m, t, array_const_value);
v = lb_add_global_generated_from_procedure(p, t, array_const_value);
} else {
v = lb_add_global_generated(m, t);
v = lb_add_global_generated_from_procedure(p, t);
}
lb_make_global_private_const(v);
@@ -3004,7 +3004,16 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
LLVMTypeRef mask_int_type = LLVMIntTypeInContext(p->module->ctx, cast(unsigned)(8*type_size_of(a)));
LLVMValueRef mask_int = LLVMBuildBitCast(p->builder, mask, mask_int_type, "");
res.value = LLVMBuildICmp(p->builder, LLVMIntNE, mask_int, LLVMConstNull(LLVMTypeOf(mask_int)), "");
switch (op_kind) {
case Token_CmpEq:
res.value = LLVMBuildICmp(p->builder, LLVMIntEQ, mask_int, LLVMConstInt(mask_int_type, U64_MAX, true), "");
break;
case Token_NotEq:
res.value = LLVMBuildICmp(p->builder, LLVMIntNE, mask_int, LLVMConstNull(mask_int_type), "");
break;
}
return res;
} else {
@@ -3255,7 +3264,7 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
Type *type = v.type;
lbAddr addr = {};
if (p->is_startup) {
addr = lb_add_global_generated(p->module, type, v);
addr = lb_add_global_generated_from_procedure(p, type, v);
} else {
addr = lb_add_local_generated(p, type, false);
}
@@ -3842,7 +3851,7 @@ gb_internal lbAddr lb_build_addr_from_entity(lbProcedure *p, Entity *e, Ast *exp
Type *t = default_type(type_of_expr(expr));
lbValue v = lb_const_value(p->module, t, e->Constant.value);
if (LLVMIsConstant(v.value)) {
lbAddr g = lb_add_global_generated(p->module, t, v);
lbAddr g = lb_add_global_generated_from_procedure(p, t, v);
return g;
}
GB_ASSERT(LLVMIsALoadInst(v.value));
+120 -186
View File
@@ -18,17 +18,36 @@ gb_global isize lb_global_type_info_member_tags_index = 0;
gb_internal void lb_init_module(lbModule *m, Checker *c) {
m->info = &c->info;
gbString module_name = gb_string_make(heap_allocator(), "odin_package");
if (m->file) {
module_name = gb_string_append_fmt(module_name, "-%u", m->file->id+1);
String name = build_context.build_paths[BuildPath_Output].name;
gbString module_name = gb_string_make(heap_allocator(), "");
module_name = gb_string_append_length(module_name, name.text, name.len);
if (!USE_SEPARATE_MODULES) {
// ignore suffixes
} else if (m->file) {
if (gb_string_length(module_name)) {
module_name = gb_string_appendc(module_name, "-");
}
if (m->pkg) {
module_name = gb_string_append_length(module_name, m->pkg->name.text, m->pkg->name.len);
module_name = gb_string_appendc(module_name, "-");
}
String filename = filename_from_path(m->file->filename);
module_name = gb_string_append_length(module_name, filename.text, filename.len);
} else if (m->pkg) {
module_name = gb_string_appendc(module_name, "-");
if (gb_string_length(module_name)) {
module_name = gb_string_appendc(module_name, "-");
}
module_name = gb_string_append_length(module_name, m->pkg->name.text, m->pkg->name.len);
} else if (USE_SEPARATE_MODULES) {
module_name = gb_string_appendc(module_name, "-builtin");
} else {
if (gb_string_length(module_name)) {
module_name = gb_string_appendc(module_name, "-");
}
module_name = gb_string_appendc(module_name, "builtin");
}
m->module_name = module_name ? module_name : "odin_package";
m->module_name = module_name;
m->ctx = LLVMContextCreate();
m->mod = LLVMModuleCreateWithNameInContext(m->module_name, m->ctx);
// m->debug_builder = nullptr;
@@ -67,10 +86,7 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) {
string_map_init(&m->procedures);
string_map_init(&m->const_strings);
map_init(&m->function_type_map);
map_init(&m->equal_procs);
map_init(&m->hasher_procs);
map_init(&m->map_get_procs);
map_init(&m->map_set_procs);
string_map_init(&m->gen_procs);
if (USE_SEPARATE_MODULES) {
array_init(&m->procedures_to_generate, a, 0, 1<<10);
map_init(&m->procedure_values, 1<<11);
@@ -218,7 +234,7 @@ gb_internal void lb_loop_end(lbProcedure *p, lbLoopData const &data) {
gb_internal void lb_make_global_private_const(LLVMValueRef global_data) {
LLVMSetLinkage(global_data, LLVMLinkerPrivateLinkage);
LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr);
// LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr);
LLVMSetGlobalConstant(global_data, true);
}
gb_internal void lb_make_global_private_const(lbAddr const &addr) {
@@ -1010,7 +1026,7 @@ gb_internal void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) {
LLVMConstInt(LLVMInt64TypeInContext(p->module->ctx), lb_sizeof(LLVMTypeOf(value.value)), false));
return;
} else if (LLVMIsConstant(value.value)) {
lbAddr addr = lb_add_global_generated(p->module, value.type, value, nullptr);
lbAddr addr = lb_add_global_generated_from_procedure(p, value.type, value);
lb_make_global_private_const(addr);
LLVMValueRef dst_ptr = ptr.value;
@@ -1443,148 +1459,30 @@ gb_internal void lb_clone_struct_type(LLVMTypeRef dst, LLVMTypeRef src) {
LLVMStructSetBody(dst, fields, field_count, LLVMIsPackedStruct(src));
}
gb_internal String lb_mangle_name(Entity *e) {
String name = e->token.string;
AstPackage *pkg = e->pkg;
GB_ASSERT_MSG(pkg != nullptr, "Missing package for '%.*s'", LIT(name));
String pkgn = pkg->name;
GB_ASSERT(!rune_is_digit(pkgn[0]));
if (pkgn == "llvm") {
pkgn = str_lit("llvm$");
}
isize max_len = pkgn.len + 1 + name.len + 1;
bool require_suffix_id = is_type_polymorphic(e->type, true);
if ((e->scope->flags & (ScopeFlag_File | ScopeFlag_Pkg)) == 0) {
require_suffix_id = true;
} else if (is_blank_ident(e->token)) {
require_suffix_id = true;
}if (e->flags & EntityFlag_NotExported) {
require_suffix_id = true;
}
if (require_suffix_id) {
max_len += 21;
}
char *new_name = gb_alloc_array(permanent_allocator(), char, max_len);
isize new_name_len = gb_snprintf(
new_name, max_len,
"%.*s" ABI_PKG_NAME_SEPARATOR "%.*s", LIT(pkgn), LIT(name)
);
if (require_suffix_id) {
char *str = new_name + new_name_len-1;
isize len = max_len-new_name_len;
isize extra = gb_snprintf(str, len, "-%llu", cast(unsigned long long)e->id);
new_name_len += extra-1;
}
String mangled_name = make_string((u8 const *)new_name, new_name_len-1);
return mangled_name;
}
gb_internal String lb_set_nested_type_name_ir_mangled_name(Entity *e, lbProcedure *p, lbModule *module) {
// NOTE(bill, 2020-03-08): A polymorphic procedure may take a nested type declaration
// and as a result, the declaration does not have time to determine what it should be
GB_ASSERT(e != nullptr && e->kind == Entity_TypeName);
if (e->TypeName.ir_mangled_name.len != 0) {
return e->TypeName.ir_mangled_name;
}
GB_ASSERT((e->scope->flags & ScopeFlag_File) == 0);
if (p == nullptr) {
Entity *proc = nullptr;
if (e->parent_proc_decl != nullptr) {
proc = e->parent_proc_decl->entity;
} else {
Scope *scope = e->scope;
while (scope != nullptr && (scope->flags & ScopeFlag_Proc) == 0) {
scope = scope->parent;
}
GB_ASSERT(scope != nullptr);
GB_ASSERT(scope->flags & ScopeFlag_Proc);
proc = scope->procedure_entity;
}
if (proc != nullptr) {
GB_ASSERT(proc->kind == Entity_Procedure);
if (proc->code_gen_procedure != nullptr) {
p = proc->code_gen_procedure;
}
}
}
// NOTE(bill): Generate a new name
// parent_proc.name-guid
String ts_name = e->token.string;
if (p != nullptr) {
isize name_len = p->name.len + 1 + ts_name.len + 1 + 10 + 1;
char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
u32 guid = 1+p->module->nested_type_name_guid.fetch_add(1);
name_len = gb_snprintf(name_text, name_len, "%.*s" ABI_PKG_NAME_SEPARATOR "%.*s-%u", LIT(p->name), LIT(ts_name), guid);
String name = make_string(cast(u8 *)name_text, name_len-1);
e->TypeName.ir_mangled_name = name;
return name;
} else {
// NOTE(bill): a nested type be required before its parameter procedure exists. Just give it a temp name for now
isize name_len = 9 + 1 + ts_name.len + 1 + 10 + 1;
char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
static std::atomic<u32> guid;
name_len = gb_snprintf(name_text, name_len, "_internal" ABI_PKG_NAME_SEPARATOR "%.*s-%u", LIT(ts_name), 1+guid.fetch_add(1));
String name = make_string(cast(u8 *)name_text, name_len-1);
e->TypeName.ir_mangled_name = name;
return name;
}
}
gb_internal String lb_get_entity_name(lbModule *m, Entity *e, String default_name) {
gb_internal String lb_get_entity_name(lbModule *m, Entity *e) {
GB_ASSERT(m != nullptr);
if (e != nullptr && e->kind == Entity_TypeName && e->TypeName.ir_mangled_name.len != 0) {
return e->TypeName.ir_mangled_name;
}
GB_ASSERT(e != nullptr);
if (e->kind == Entity_TypeName && e->TypeName.ir_mangled_name.len != 0) {
return e->TypeName.ir_mangled_name;
} else if (e->kind == Entity_Procedure && e->Procedure.link_name.len != 0) {
return e->Procedure.link_name;
}
if (e->pkg == nullptr) {
return e->token.string;
}
if (e->kind == Entity_TypeName && (e->scope->flags & ScopeFlag_File) == 0) {
return lb_set_nested_type_name_ir_mangled_name(e, nullptr, m);
}
gbString w = string_canonical_entity_name(heap_allocator(), e);
defer (gb_string_free(w));
String name = {};
bool no_name_mangle = false;
if (e->kind == Entity_Variable) {
bool is_foreign = e->Variable.is_foreign;
bool is_export = e->Variable.is_export;
no_name_mangle = e->Variable.link_name.len > 0 || is_foreign || is_export;
if (e->Variable.link_name.len > 0) {
return e->Variable.link_name;
}
} else if (e->kind == Entity_Procedure && e->Procedure.link_name.len > 0) {
return e->Procedure.link_name;
} else if (e->kind == Entity_Procedure && e->Procedure.is_export) {
no_name_mangle = true;
}
if (!no_name_mangle) {
name = lb_mangle_name(e);
}
if (name.len == 0) {
name = e->token.string;
}
String name = copy_string(permanent_allocator(), make_string(cast(u8 const *)w, gb_string_length(w)));
if (e->kind == Entity_TypeName) {
e->TypeName.ir_mangled_name = name;
} else if (e->kind == Entity_Procedure) {
e->Procedure.link_name = name;
} else if (e->kind == Entity_Variable) {
e->Variable.link_name = name;
}
return name;
@@ -1902,15 +1800,24 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
return type;
}
type = LLVMStructCreateNamed(ctx, name);
LLVMTypeRef fields[2] = {
lb_type(m, t_rawptr),
lb_type(m, t_typeid),
};
LLVMStructSetBody(type, fields, 2, false);
if (build_context.ptr_size == 4) {
LLVMTypeRef fields[3] = {
lb_type(m, t_rawptr),
lb_type_padding_filler(m, build_context.ptr_size, build_context.ptr_size), // padding
lb_type(m, t_typeid),
};
LLVMStructSetBody(type, fields, 3, false);
} else {
LLVMTypeRef fields[2] = {
lb_type(m, t_rawptr),
lb_type(m, t_typeid),
};
LLVMStructSetBody(type, fields, 2, false);
}
return type;
}
case Basic_typeid: return LLVMIntTypeInContext(m->ctx, 8*cast(unsigned)build_context.ptr_size);
case Basic_typeid: return LLVMIntTypeInContext(m->ctx, 64);
// Endian Specific Types
case Basic_i16le: return LLVMInt16TypeInContext(ctx);
@@ -2088,26 +1995,26 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
case Type_Struct:
{
type_set_offsets(type);
i64 full_type_size = type_size_of(type);
i64 full_type_align = type_align_of(type);
GB_ASSERT(full_type_size % full_type_align == 0);
if (type->Struct.is_raw_union) {
lbStructFieldRemapping field_remapping = {};
slice_init(&field_remapping, permanent_allocator(), 1);
LLVMTypeRef fields[1] = {};
fields[0] = lb_type_padding_filler(m, full_type_size, full_type_align);
field_remapping[0] = 0;
LLVMTypeRef struct_type = LLVMStructTypeInContext(ctx, fields, gb_count_of(fields), false);
map_set(&m->struct_field_remapping, cast(void *)struct_type, field_remapping);
map_set(&m->struct_field_remapping, cast(void *)type, field_remapping);
return struct_type;
}
lbStructFieldRemapping field_remapping = {};
slice_init(&field_remapping, permanent_allocator(), type->Struct.fields.count);
@@ -2120,7 +2027,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
LLVMTypeRef padding_type = lb_type_padding_filler(m, 0, type_align_of(type));
array_add(&fields, padding_type);
}
i64 prev_offset = 0;
bool requires_packing = type->Struct.is_packed;
for (i32 field_index : struct_fields_index_by_increasing_offset(temporary_allocator(), type)) {
@@ -2151,7 +2058,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
prev_offset = offset + type_size_of(field->type);
}
i64 end_padding = full_type_size-prev_offset;
if (end_padding > 0) {
array_add(&fields, lb_type_padding_filler(m, end_padding, 1));
@@ -2160,14 +2067,14 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
for_array(i, fields) {
GB_ASSERT(fields[i] != nullptr);
}
LLVMTypeRef struct_type = LLVMStructTypeInContext(ctx, fields.data, cast(unsigned)fields.count, requires_packing);
map_set(&m->struct_field_remapping, cast(void *)struct_type, field_remapping);
map_set(&m->struct_field_remapping, cast(void *)type, field_remapping);
map_set(&m->struct_field_remapping, cast(void *)type, field_remapping);
#if 0
GB_ASSERT_MSG(lb_sizeof(struct_type) == full_type_size,
"(%lld) %s vs (%lld) %s",
cast(long long)lb_sizeof(struct_type), LLVMPrintTypeToString(struct_type),
GB_ASSERT_MSG(lb_sizeof(struct_type) == full_type_size,
"(%lld) %s vs (%lld) %s",
cast(long long)lb_sizeof(struct_type), LLVMPrintTypeToString(struct_type),
cast(long long)full_type_size, type_to_string(type));
#endif
return struct_type;
@@ -2196,8 +2103,22 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
LLVMTypeRef variant = lb_type(m, type->Union.variants[0]);
array_add(&fields, variant);
} else {
LLVMTypeRef block_type = lb_type_padding_filler(m, block_size, align);
LLVMTypeRef tag_type = lb_type(m, union_tag_type(type));
LLVMTypeRef block_type = nullptr;
bool all_pointers = align == build_context.ptr_size;
for (isize i = 0; all_pointers && i < type->Union.variants.count; i++) {
Type *t = type->Union.variants[i];
if (!is_type_internally_pointer_like(t)) {
all_pointers = false;
}
}
if (all_pointers) {
block_type = lb_type(m, t_rawptr);
} else {
block_type = lb_type_padding_filler(m, block_size, align);
}
LLVMTypeRef tag_type = lb_type(m, union_tag_type(type));
array_add(&fields, block_type);
array_add(&fields, tag_type);
i64 used_size = lb_sizeof(block_type) + lb_sizeof(tag_type);
@@ -2633,12 +2554,10 @@ gb_internal LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String co
false);
isize max_len = 7+8+1;
char *name = gb_alloc_array(permanent_allocator(), char, max_len);
u32 id = m->gen->global_array_index.fetch_add(1);
isize len = gb_snprintf(name, max_len, "csbs$%x", id);
len -= 1;
u32 id = m->global_array_index.fetch_add(1);
gbString name = gb_string_make(temporary_allocator(), "csbs$");
name = gb_string_appendc(name, m->module_name);
name = gb_string_append_fmt(name, "$%x", id);
LLVMTypeRef type = LLVMTypeOf(data);
LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name);
@@ -2676,14 +2595,11 @@ gb_internal lbValue lb_find_or_add_entity_string_byte_slice_with_type(lbModule *
false);
char *name = nullptr;
{
isize max_len = 7+8+1;
name = gb_alloc_array(permanent_allocator(), char, max_len);
u32 id = m->gen->global_array_index.fetch_add(1);
isize len = gb_snprintf(name, max_len, "csbs$%x", id);
len -= 1;
}
u32 id = m->global_array_index.fetch_add(1);
gbString name = gb_string_make(temporary_allocator(), "csba$");
name = gb_string_appendc(name, m->module_name);
name = gb_string_append_fmt(name, "$%x", id);
LLVMTypeRef type = LLVMTypeOf(data);
LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name);
LLVMSetInitializer(global_data, data);
@@ -2869,6 +2785,8 @@ gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &pr
pl->decl->code_gen_module = m;
e->decl_info = pl->decl;
pl->decl->entity = e;
e->parent_proc_decl = pl->decl->parent;
e->Procedure.is_anonymous = true;
e->flags |= EntityFlag_ProcBodyChecked;
lbProcedure *p = lb_create_procedure(m, e);
@@ -2889,17 +2807,14 @@ gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &pr
}
gb_internal lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value, Entity **entity_) {
gb_internal lbAddr lb_add_global_generated_with_name(lbModule *m, Type *type, lbValue value, String name, Entity **entity_) {
GB_ASSERT(name.len != 0);
GB_ASSERT(type != nullptr);
type = default_type(type);
isize max_len = 7+8+1;
u8 *str = cast(u8 *)gb_alloc_array(permanent_allocator(), u8, max_len);
u32 id = m->gen->global_generated_index.fetch_add(1);
isize len = gb_snprintf(cast(char *)str, max_len, "ggv$%x", id);
String name = make_string(str, len-1);
u8 *str = cast(u8 *)gb_alloc_array(temporary_allocator(), u8, name.len);
memcpy(str, name.text, name.len);
str[name.len] = 0;
Scope *scope = nullptr;
Entity *e = alloc_entity_variable(scope, make_token_ident(name), type);
@@ -2921,6 +2836,25 @@ gb_internal lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue valu
return lb_addr(g);
}
gb_internal lbAddr lb_add_global_generated_from_procedure(lbProcedure *p, Type *type, lbValue value) {
GB_ASSERT(type != nullptr);
type = default_type(type);
u32 index = ++p->global_generated_index;
gbString s = gb_string_make(temporary_allocator(), "ggv$");
// s = gb_string_appendc(s, p->module->module_name);
// s = gb_string_appendc(s, "$");
s = gb_string_append_length(s, p->name.text, p->name.len);
s = gb_string_append_fmt(s, "$%u", index);
String name = make_string(cast(u8 const *)s, gb_string_length(s));
return lb_add_global_generated_with_name(p->module, type, value, name);
}
gb_internal lbValue lb_find_runtime_value(lbModule *m, String const &name) {
AstPackage *p = m->info->runtime_package;
Entity *e = scope_lookup_current(p->scope, name);
@@ -3028,7 +2962,7 @@ gb_internal lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 c
g.type = alloc_type_pointer(t);
LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, t)));
LLVMSetLinkage(g.value, LLVMPrivateLinkage);
LLVMSetUnnamedAddress(g.value, LLVMGlobalUnnamedAddr);
// LLVMSetUnnamedAddress(g.value, LLVMGlobalUnnamedAddr);
string_map_set(&m->members, s, g);
return g;
}
+35 -6
View File
@@ -1119,7 +1119,7 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c
if (LLVMIsConstant(x.value)) {
// NOTE(bill): if the value is already constant, then just it as a global variable
// and pass it by pointer
lbAddr addr = lb_add_global_generated(p->module, original_type, x);
lbAddr addr = lb_add_global_generated_from_procedure(p, original_type, x);
lb_make_global_private_const(addr);
ptr = addr.addr;
} else {
@@ -1564,6 +1564,34 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn
return res;
}
case BuiltinProc_simd_extract_lsbs:
case BuiltinProc_simd_extract_msbs:
{
Type *vt = arg0.type;
GB_ASSERT(vt->kind == Type_SimdVector);
i64 elem_bits = 8*type_size_of(elem);
i64 num_elems = get_array_type_count(vt);
LLVMValueRef broadcast_value = arg0.value;
if (builtin_id == BuiltinProc_simd_extract_msbs) {
LLVMTypeRef word_type = lb_type(m, elem);
LLVMValueRef shift_value = llvm_splat_int(num_elems, word_type, elem_bits - 1);
broadcast_value = LLVMBuildAShr(p->builder, broadcast_value, shift_value, "");
}
LLVMTypeRef bitvec_type = LLVMVectorType(LLVMInt1TypeInContext(m->ctx), (unsigned)num_elems);
LLVMValueRef bitvec_value = LLVMBuildTrunc(p->builder, broadcast_value, bitvec_type, "");
LLVMTypeRef mask_type = LLVMIntTypeInContext(m->ctx, (unsigned)num_elems);
LLVMValueRef mask_value = LLVMBuildBitCast(p->builder, bitvec_value, mask_type, "");
LLVMTypeRef result_type = lb_type(m, res.type);
res.value = LLVMBuildZExtOrBitCast(p->builder, mask_value, result_type, "");
return res;
}
case BuiltinProc_simd_shuffle:
{
@@ -1846,7 +1874,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
LLVMValueRef backing_array = llvm_const_array(lb_type(m, t_load_directory_file), elements, count);
Type *array_type = alloc_type_array(t_load_directory_file, count);
lbAddr backing_array_addr = lb_add_global_generated(m, array_type, {backing_array, array_type}, nullptr);
lbAddr backing_array_addr = lb_add_global_generated_from_procedure(p, array_type, {backing_array, array_type});
lb_make_global_private_const(backing_array_addr);
LLVMValueRef backing_array_ptr = backing_array_addr.addr.value;
@@ -1854,7 +1882,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
LLVMValueRef const_slice = llvm_const_slice_internal(m, backing_array_ptr, LLVMConstInt(lb_type(m, t_int), count, false));
lbAddr addr = lb_add_global_generated(p->module, tv.type, {const_slice, t_load_directory_file_slice}, nullptr);
lbAddr addr = lb_add_global_generated_from_procedure(p, tv.type, {const_slice, t_load_directory_file_slice});
lb_make_global_private_const(addr);
return lb_addr_load(p, addr);
@@ -2548,8 +2576,8 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
}
case BuiltinProc_ptr_sub:
{
Type *elem0 = type_deref(type_of_expr(ce->args[0]));
Type *elem1 = type_deref(type_of_expr(ce->args[1]));
Type *elem0 = type_deref(type_of_expr(ce->args[0]), true);
Type *elem1 = type_deref(type_of_expr(ce->args[1]), true);
GB_ASSERT(are_types_identical(elem0, elem1));
Type *elem = elem0;
@@ -3280,13 +3308,14 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
{
isize max_len = 7+8+1;
name = gb_alloc_array(permanent_allocator(), char, max_len);
u32 id = m->gen->global_array_index.fetch_add(1);
u32 id = m->global_array_index.fetch_add(1);
isize len = gb_snprintf(name, max_len, "csbs$%x", id);
len -= 1;
}
LLVMTypeRef type = LLVMTypeOf(array);
LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name);
LLVMSetInitializer(global_data, array);
LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr);
LLVMSetLinkage(global_data, LLVMInternalLinkage);
+36 -56
View File
@@ -5,8 +5,6 @@ gb_internal void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd)
auto *min_dep_set = &p->module->info->minimum_dependency_set;
static i32 global_guid = 0;
for (Ast *ident : vd->names) {
GB_ASSERT(ident->kind == Ast_Ident);
Entity *e = entity_of_node(ident);
@@ -32,7 +30,8 @@ gb_internal void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd)
continue;
}
lb_set_nested_type_name_ir_mangled_name(e, p, p->module);
String name = lb_get_entity_name(p->module, e);
gb_unused(name);
}
for_array(i, vd->names) {
@@ -1115,62 +1114,43 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
}
Type *elem = et->BitSet.elem;
if (is_type_enum(elem)) {
i64 enum_count = 0;
lbValue values = lb_enum_values_slice(p, elem, &enum_count);
lbValue values_data = lb_slice_elem(p, values);
lbValue max_count = lb_const_int(m, t_int, enum_count);
Type *mask = bit_set_to_int(et);
lbAddr offset_ = lb_add_local_generated(p, t_int, false);
lb_addr_store(p, offset_, lb_const_int(m, t_int, 0));
lbValue all_mask = lb_const_value(p->module, mask, exact_bit_set_all_set_mask(et));
lbValue initial_mask = lb_emit_arith(p, Token_And, the_set, all_mask, mask);
loop = lb_create_block(p, "for.bit_set.enum.loop");
lb_emit_jump(p, loop);
lb_start_block(p, loop);
lbBlock *body_check = lb_create_block(p, "for.bit_set.enum.body-check");
lbBlock *body = lb_create_block(p, "for.bit_set.enum.body");
done = lb_create_block(p, "for.bit_set.enum.done");
lbValue offset = lb_addr_load(p, offset_);
lbValue cond = lb_emit_comp(p, Token_Lt, offset, max_count);
lb_emit_if(p, cond, body_check, done);
lb_start_block(p, body_check);
lbValue val_ptr = lb_emit_ptr_offset(p, values_data, offset);
lb_emit_increment(p, offset_.addr);
val = lb_emit_load(p, val_ptr);
val = lb_emit_conv(p, val, elem);
lbValue check = lb_build_binary_in(p, val, the_set, Token_in);
lb_emit_if(p, check, body, loop);
lb_start_block(p, body);
} else {
lbAddr offset_ = lb_add_local_generated(p, t_int, false);
lb_addr_store(p, offset_, lb_const_int(m, t_int, et->BitSet.lower));
lbValue max_count = lb_const_int(m, t_int, et->BitSet.upper);
loop = lb_create_block(p, "for.bit_set.range.loop");
lb_emit_jump(p, loop);
lb_start_block(p, loop);
lbBlock *body_check = lb_create_block(p, "for.bit_set.range.body-check");
lbBlock *body = lb_create_block(p, "for.bit_set.range.body");
done = lb_create_block(p, "for.bit_set.range.done");
lbValue offset = lb_addr_load(p, offset_);
lbValue cond = lb_emit_comp(p, Token_LtEq, offset, max_count);
lb_emit_if(p, cond, body_check, done);
lb_start_block(p, body_check);
val = lb_emit_conv(p, offset, elem);
lb_emit_increment(p, offset_.addr);
lbValue check = lb_build_binary_in(p, val, the_set, Token_in);
lb_emit_if(p, check, body, loop);
lb_start_block(p, body);
if (rs->reverse) {
initial_mask = lb_emit_reverse_bits(p, initial_mask, mask);
}
lbAddr remaining = lb_add_local_generated(p, mask, false);
lb_addr_store(p, remaining, initial_mask);
loop = lb_create_block(p, "for.bit_set.loop");
lbBlock *body = lb_create_block(p, "for.bit_set.body");
done = lb_create_block(p, "for.bit_set.done");
lb_emit_jump(p, loop);
lb_start_block(p, loop);
lbValue remaining_val = lb_addr_load(p, remaining);
lbValue cond = lb_emit_comp(p, Token_NotEq, remaining_val, lb_zero(m, mask));
lb_emit_if(p, cond, body, done);
lb_start_block(p, body);
val = lb_emit_count_trailing_zeros(p, remaining_val, mask);
val = lb_emit_conv(p, val, elem);
if (rs->reverse) {
val = lb_emit_arith(p, Token_Sub, lb_const_int(m, elem, et->BitSet.lower + 8*type_size_of(mask) - 1), val, elem);
} else {
val = lb_emit_arith(p, Token_Add, val, lb_const_int(m, elem, et->BitSet.lower), elem);
}
lbValue reduce_val = lb_emit_arith(p, Token_Sub, remaining_val, lb_const_int(m, mask, 1), mask);
remaining_val = lb_emit_arith(p, Token_And, remaining_val, reduce_val, mask);
lb_addr_store(p, remaining, remaining_val);
break;
}
default:
+25 -48
View File
@@ -1,24 +1,24 @@
gb_internal isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=true) {
auto *set = &info->minimum_dependency_type_info_set;
isize index = type_info_index(info, type, err_on_not_found);
gb_internal isize lb_type_info_index(CheckerInfo *info, TypeInfoPair pair, bool err_on_not_found=true) {
isize index = type_info_index(info, pair, err_on_not_found);
if (index >= 0) {
auto *found = map_get(set, index+1);
if (found) {
GB_ASSERT(*found >= 0);
return *found + 1;
}
return index;
}
if (err_on_not_found) {
gb_printf_err("NOT FOUND lb_type_info_index:\n\t%s\n\t@ index %td\n\tmax count: %u\nFound:\n", type_to_string(type), index, set->count);
for (auto const &entry : *set) {
gb_printf_err("NOT FOUND lb_type_info_index:\n\t%s\n\t@ index %td\n\tmax count: %u\nFound:\n", type_to_string(pair.type), index, info->min_dep_type_info_index_map.count);
for (auto const &entry : info->min_dep_type_info_index_map) {
isize type_info_index = entry.key;
gb_printf_err("\t%s\n", type_to_string(info->type_info_types[type_info_index]));
gb_printf_err("\t%s\n", type_to_string(info->type_info_types_hash_map[type_info_index].type));
}
GB_PANIC("NOT FOUND");
}
return -1;
}
gb_internal isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=true) {
return lb_type_info_index(info, {type, type_hash_canonical_type(type)}, err_on_not_found);
}
gb_internal u64 lb_typeid_kind(lbModule *m, Type *type, u64 id=0) {
GB_ASSERT(!build_context.no_rtti);
@@ -73,37 +73,8 @@ gb_internal lbValue lb_typeid(lbModule *m, Type *type) {
type = default_type(type);
u64 id = cast(u64)lb_type_info_index(m->info, type);
GB_ASSERT(id >= 0);
u64 kind = lb_typeid_kind(m, type, id);
u64 named = is_type_named(type) && type->kind != Type_Basic;
u64 special = 0;
u64 reserved = 0;
if (is_type_cstring(type)) {
special = 1;
} else if (is_type_integer(type) && !is_type_unsigned(type)) {
special = 1;
}
u64 data = 0;
if (build_context.ptr_size == 4) {
GB_ASSERT(id <= (1u<<24u));
data |= (id &~ (1u<<24)) << 0u; // index
data |= (kind &~ (1u<<5)) << 24u; // kind
data |= (named &~ (1u<<1)) << 29u; // named
data |= (special &~ (1u<<1)) << 30u; // special
data |= (reserved &~ (1u<<1)) << 31u; // reserved
} else {
GB_ASSERT(build_context.ptr_size == 8);
GB_ASSERT(id <= (1ull<<56u));
data |= (id &~ (1ull<<56)) << 0ul; // index
data |= (kind &~ (1ull<<5)) << 56ull; // kind
data |= (named &~ (1ull<<1)) << 61ull; // named
data |= (special &~ (1ull<<1)) << 62ull; // special
data |= (reserved &~ (1ull<<1)) << 63ull; // reserved
}
u64 data = type_hash_canonical_type(type);
GB_ASSERT(data != 0);
lbValue res = {};
res.value = LLVMConstInt(lb_type(m, t_typeid), data, false);
@@ -279,13 +250,14 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
LLVMTypeRef *modified_types = lb_setup_modified_types_for_type_info(m, global_type_info_data_entity_count);
defer (gb_free(heap_allocator(), modified_types));
for_array(type_info_type_index, info->type_info_types) {
Type *t = info->type_info_types[type_info_type_index];
for_array(type_info_type_index, info->type_info_types_hash_map) {
auto const &tt = info->type_info_types_hash_map[type_info_type_index];
Type *t = tt.type;
if (t == nullptr || t == t_invalid) {
continue;
}
isize entry_index = lb_type_info_index(info, t, false);
isize entry_index = lb_type_info_index(info, tt, false);
if (entry_index <= 0) {
continue;
}
@@ -342,8 +314,8 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
return giant_const_values[index];
};
for_array(type_info_type_index, info->type_info_types) {
Type *t = info->type_info_types[type_info_type_index];
for_array(type_info_type_index, info->type_info_types_hash_map) {
Type *t = info->type_info_types_hash_map[type_info_type_index].type;
if (t == nullptr || t == t_invalid) {
continue;
}
@@ -1071,7 +1043,12 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
LLVMSetInitializer(giant_const_values[entry_index], LLVMConstNamedStruct(stype, small_const_values, variant_index+1));
}
for (isize i = 0; i < global_type_info_data_entity_count; i++) {
giant_const_values[i] = LLVMConstPointerCast(giant_const_values[i], lb_type(m, t_type_info_ptr));
auto *ptr = &giant_const_values[i];
if (*ptr != nullptr) {
*ptr = LLVMConstPointerCast(*ptr, lb_type(m, t_type_info_ptr));
} else {
*ptr = LLVMConstNull(lb_type(m, t_type_info_ptr));
}
}
+20 -2
View File
@@ -971,6 +971,13 @@ gb_internal i32 lb_convert_struct_index(lbModule *m, Type *t, i32 index) {
if (t->kind == Type_Struct) {
auto field_remapping = lb_get_struct_remapping(m, t);
return field_remapping[index];
} else if (is_type_any(t) && build_context.ptr_size == 4) {
GB_ASSERT(t->kind == Type_Basic);
GB_ASSERT(t->Basic.kind == Basic_any);
switch (index) {
case 0: return 0; // data
case 1: return 2; // id
}
} else if (build_context.ptr_size != build_context.int_size) {
switch (t->kind) {
case Type_Basic:
@@ -2105,7 +2112,13 @@ gb_internal lbAddr lb_handle_objc_find_or_register_selector(lbProcedure *p, Stri
}
if (!entity) {
lbAddr default_addr = lb_add_global_generated(default_module, t_objc_SEL, {}, &entity);
gbString global_name = gb_string_make(temporary_allocator(), "__$objc_SEL$");
global_name = gb_string_append_length(global_name, name.text, name.len);
lbAddr default_addr = lb_add_global_generated_with_name(
default_module, t_objc_SEL, {},
make_string(cast(u8 const *)global_name, gb_string_length(global_name)),
&entity);
string_map_set(&default_module->objc_selectors, name, lbObjcRef{entity, default_addr});
}
@@ -2162,7 +2175,12 @@ gb_internal lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String
}
if (!entity) {
lbAddr default_addr = lb_add_global_generated(default_module, t_objc_Class, {}, &entity);
gbString global_name = gb_string_make(temporary_allocator(), "__$objc_Class$");
global_name = gb_string_append_length(global_name, name.text, name.len);
lbAddr default_addr = lb_add_global_generated_with_name(default_module, t_objc_Class, {},
make_string(cast(u8 const *)global_name, gb_string_length(global_name)),
&entity);
string_map_set(&default_module->objc_classes, name, lbObjcRef{entity, default_addr});
}
+5
View File
@@ -3593,10 +3593,15 @@ int main(int arg_count, char const **arg_ptr) {
}
if (build_context.generate_docs) {
MAIN_TIME_SECTION("generate documentation");
if (global_error_collector.count != 0) {
return 1;
}
generate_documentation(checker);
if (build_context.show_timings) {
show_timings(checker, &global_timings);
}
return 0;
}
+752
View File
@@ -0,0 +1,752 @@
gb_internal GB_COMPARE_PROC(type_info_pair_cmp) {
TypeInfoPair *x = cast(TypeInfoPair *)a;
TypeInfoPair *y = cast(TypeInfoPair *)b;
if (x->hash == y->hash) {
return 0;
}
return x->hash < y->hash ? -1 : +1;
}
gb_internal gbAllocator type_set_allocator(void) {
return heap_allocator();
}
gb_internal TypeSetIterator begin(TypeSet &set) noexcept {
usize index = 0;
while (index < set.capacity) {
TypeInfoPair key = set.keys[index];
if (key.hash != 0 && key.hash != TYPE_SET_TOMBSTONE) {
break;
}
index++;
}
return TypeSetIterator{&set, index};
}
gb_internal TypeSetIterator end(TypeSet &set) noexcept {
return TypeSetIterator{&set, set.capacity};
}
gb_internal void type_set_init(TypeSet *s, isize capacity) {
GB_ASSERT(s->keys == nullptr);
if (capacity != 0) {
capacity = next_pow2_isize(gb_max(16, capacity));
s->keys = gb_alloc_array(type_set_allocator(), TypeInfoPair, capacity);
// This memory will be zeroed, no need to explicitly zero it
}
s->count = 0;
s->capacity = capacity;
}
gb_internal void type_set_destroy(TypeSet *s) {
gb_free(type_set_allocator(), s->keys);
s->keys = nullptr;
s->count = 0;
s->capacity = 0;
}
gb_internal isize type_set__find(TypeSet *s, TypeInfoPair pair) {
GB_ASSERT(pair.type != nullptr);
GB_ASSERT(pair.hash != 0);
if (s->count != 0) {
usize hash = pair.hash;
usize mask = s->capacity-1;
usize hash_index = cast(usize)hash & mask;
for (usize i = 0; i < s->capacity; i++) {
Type *key = s->keys[hash_index].type;
if (are_types_identical_unique_tuples(key, pair.type)) {
return hash_index;
} else if (key == 0) {
return -1;
}
hash_index = (hash_index+1)&mask;
}
}
return -1;
}
gb_internal isize type_set__find(TypeSet *s, Type *ptr) {
GB_ASSERT(ptr != 0);
if (s->count != 0) {
usize hash = cast(usize)type_hash_canonical_type(ptr);
usize mask = s->capacity-1;
usize hash_index = cast(usize)hash & mask;
for (usize i = 0; i < s->capacity; i++) {
Type *key = s->keys[hash_index].type;
if (are_types_identical_unique_tuples(key, ptr)) {
return hash_index;
} else if (key == 0) {
return -1;
}
hash_index = (hash_index+1)&mask;
}
}
return -1;
}
gb_internal bool type_set__full(TypeSet *s) {
return 0.75f * s->capacity <= s->count;
}
gb_internal gb_inline void type_set_grow(TypeSet *old_set) {
if (old_set->capacity == 0) {
type_set_init(old_set);
return;
}
TypeSet new_set = {};
type_set_init(&new_set, gb_max(old_set->capacity<<1, 16));
for (TypeInfoPair const &set : *old_set) {
bool was_new = type_set_update(&new_set, set);
GB_ASSERT(!was_new);
}
GB_ASSERT(old_set->count == new_set.count);
type_set_destroy(old_set);
*old_set = new_set;
}
gb_internal gb_inline bool type_set_exists(TypeSet *s, Type *ptr) {
return type_set__find(s, ptr) >= 0;
}
gb_internal gb_inline bool type_set_exists(TypeSet *s, TypeInfoPair pair) {
return type_set__find(s, pair) >= 0;
}
gb_internal gb_inline TypeInfoPair *type_set_retrieve(TypeSet *s, Type *type) {
isize index = type_set__find(s, type);
if (index >= 0) {
return &s->keys[index];
}
return nullptr;
}
gb_internal bool type_set_update(TypeSet *s, TypeInfoPair pair) { // returns true if it previously existsed
if (type_set_exists(s, pair)) {
return true;
}
if (s->keys == nullptr) {
type_set_init(s);
} else if (type_set__full(s)) {
type_set_grow(s);
}
GB_ASSERT(s->count < s->capacity);
GB_ASSERT(s->capacity >= 0);
usize mask = s->capacity-1;
usize hash = cast(usize)pair.hash;
usize hash_index = (cast(usize)hash) & mask;
GB_ASSERT(hash_index < s->capacity);
for (usize i = 0; i < s->capacity; i++) {
TypeInfoPair *key = &s->keys[hash_index];
GB_ASSERT(!are_types_identical_unique_tuples(key->type, pair.type));
if (key->hash == TYPE_SET_TOMBSTONE || key->hash == 0) {
*key = pair;
s->count++;
return false;
}
hash_index = (hash_index+1)&mask;
}
GB_PANIC("ptr set out of memory");
return false;
}
gb_internal bool type_set_update(TypeSet *s, Type *ptr) { // returns true if it previously existsed
TypeInfoPair pair = {ptr, type_hash_canonical_type(ptr)};
return type_set_update(s, pair);
}
gb_internal Type *type_set_add(TypeSet *s, Type *ptr) {
type_set_update(s, ptr);
return ptr;
}
gb_internal Type *type_set_add(TypeSet *s, TypeInfoPair pair) {
type_set_update(s, pair);
return pair.type;
}
gb_internal void type_set_remove(TypeSet *s, Type *ptr) {
isize index = type_set__find(s, ptr);
if (index >= 0) {
GB_ASSERT(s->count > 0);
s->keys[index].type = nullptr;
s->keys[index].hash = TYPE_SET_TOMBSTONE;
s->count--;
}
}
gb_internal gb_inline void type_set_clear(TypeSet *s) {
s->count = 0;
gb_zero_size(s->keys, s->capacity*gb_size_of(*s->keys));
}
#define TYPE_WRITER_PROC(name) bool name(TypeWriter *w, void const *ptr, isize len)
typedef TYPE_WRITER_PROC(TypeWriterProc);
struct TypeWriter {
TypeWriterProc *proc;
void *user_data;
};
bool type_writer_append(TypeWriter *w, void const *ptr, isize len) {
return w->proc(w, ptr, len);
}
bool type_writer_appendb(TypeWriter *w, char b) {
return w->proc(w, &b, 1);
}
bool type_writer_appendc(TypeWriter *w, char const *str) {
isize len = gb_strlen(str);
return w->proc(w, str, len);
}
bool type_writer_append_fmt(TypeWriter *w, char const *fmt, ...) {
va_list va;
char *str;
va_start(va, fmt);
str = gb_bprintf_va(fmt, va);
va_end(va);
return type_writer_appendc(w, str);
}
TYPE_WRITER_PROC(type_writer_string_writer_proc) {
gbString *s = cast(gbString *)&w->user_data;
*s = gb_string_append_length(*s, ptr, len);
return true;
}
void type_writer_make_string(TypeWriter *w, gbAllocator allocator) {
w->user_data = gb_string_make(allocator, "");
w->proc = type_writer_string_writer_proc;
}
void type_writer_destroy_string(TypeWriter *w) {
gb_string_free(cast(gbString)w->user_data);
}
TYPE_WRITER_PROC(type_writer_hasher_writer_proc) {
u64 *seed = cast(u64 *)w->user_data;
*seed = fnv64a(ptr, len, *seed);
return true;
}
void type_writer_make_hasher(TypeWriter *w, u64 *hash) {
w->user_data = hash;
w->proc = type_writer_hasher_writer_proc;
}
gb_internal void write_canonical_params(TypeWriter *w, Type *params) {
type_writer_appendc(w, "(");
defer (type_writer_appendc(w, ")"));
if (params == nullptr) {
return;
}
GB_ASSERT(params->kind == Type_Tuple);
for_array(i, params->Tuple.variables) {
Entity *v = params->Tuple.variables[i];
if (i > 0) {
type_writer_appendc(w, CANONICAL_PARAM_SEPARATOR);
}
type_writer_append(w, v->token.string.text, v->token.string.len);
type_writer_appendc(w, CANONICAL_TYPE_SEPARATOR);
switch (v->kind) {
case Entity_Variable:
if (v->flags&EntityFlag_CVarArg) {
type_writer_appendc(w, CANONICAL_PARAM_C_VARARG);
}
if (v->flags&EntityFlag_Ellipsis) {
Type *slice = base_type(v->type);
type_writer_appendc(w, CANONICAL_PARAM_VARARG);
GB_ASSERT(v->type->kind == Type_Slice);
write_type_to_canonical_string(w, slice->Slice.elem);
} else {
write_type_to_canonical_string(w, v->type);
}
break;
case Entity_TypeName:
type_writer_appendc(w, CANONICAL_PARAM_TYPEID);
write_type_to_canonical_string(w, v->type);
break;
case Entity_Constant:
{
type_writer_appendc(w, CANONICAL_PARAM_CONST);
gbString s = exact_value_to_string(v->Constant.value, 1<<16);
type_writer_append(w, s, gb_string_length(s));
gb_string_free(s);
}
break;
default:
GB_PANIC("TODO(bill): handle non type/const parapoly parameter values");
break;
}
}
return;
}
gb_internal u64 type_hash_canonical_type(Type *type) {
if (type == nullptr) {
return 0;
}
u64 hash = fnv64a(nullptr, 0);
TypeWriter w = {};
type_writer_make_hasher(&w, &hash);
write_type_to_canonical_string(&w, type);
return hash ? hash : 1;
}
gb_internal String type_to_canonical_string(gbAllocator allocator, Type *type) {
TypeWriter w = {};
type_writer_make_string(&w, allocator);
write_type_to_canonical_string(&w, type);
gbString s = cast(gbString)w.user_data;
return make_string(cast(u8 const *)s, gb_string_length(s));
}
gb_internal gbString temp_canonical_string(Type *type) {
TypeWriter w = {};
type_writer_make_string(&w, temporary_allocator());
write_type_to_canonical_string(&w, type);
return cast(gbString)w.user_data;
}
gb_internal gbString string_canonical_entity_name(gbAllocator allocator, Entity *e) {
TypeWriter w = {};
type_writer_make_string(&w, allocator);
write_canonical_entity_name(&w, e);
return cast(gbString)w.user_data;
}
gb_internal void write_canonical_parent_prefix(TypeWriter *w, Entity *e) {
GB_ASSERT(e != nullptr);
if (e->kind == Entity_Procedure || e->kind == Entity_TypeName) {
if (e->kind == Entity_Procedure && (e->Procedure.is_export || e->Procedure.is_foreign)) {
// no prefix
return;
}
if (e->parent_proc_decl) {
Entity *p = e->parent_proc_decl->entity;
write_canonical_parent_prefix(w, p);
type_writer_append(w, p->token.string.text, p->token.string.len);
if (is_type_polymorphic(p->type)) {
type_writer_appendc(w, CANONICAL_TYPE_SEPARATOR);
write_type_to_canonical_string(w, p->type);
}
type_writer_appendc(w, CANONICAL_NAME_SEPARATOR);
} else if (e->pkg && (scope_lookup_current(e->pkg->scope, e->token.string) == e)) {
type_writer_append(w, e->pkg->name.text, e->pkg->name.len);
if (e->pkg->name == "llvm") {
type_writer_appendc(w, "$");
}
type_writer_appendc(w, CANONICAL_NAME_SEPARATOR);
} else {
String file_name = filename_without_directory(e->file->fullpath);
type_writer_append(w, e->pkg->name.text, e->pkg->name.len);
if (e->pkg->name == "llvm") {
type_writer_appendc(w, "$");
}
type_writer_append_fmt(w, CANONICAL_NAME_SEPARATOR "%.*s" CANONICAL_NAME_SEPARATOR, LIT(file_name));
}
} else {
GB_PANIC("TODO(bill): handle entity kind: %d", e->kind);
}
if (e->kind == Entity_Procedure && e->Procedure.is_anonymous) {
String file_name = filename_without_directory(e->file->fullpath);
type_writer_append_fmt(w, CANONICAL_ANON_PREFIX "_%.*s:%d", LIT(file_name), e->token.pos.offset);
} else {
type_writer_append(w, e->token.string.text, e->token.string.len);
}
if (is_type_polymorphic(e->type)) {
type_writer_appendc(w, CANONICAL_TYPE_SEPARATOR);
write_type_to_canonical_string(w, e->type);
}
type_writer_appendc(w, CANONICAL_NAME_SEPARATOR);
return;
}
gb_internal void write_canonical_entity_name(TypeWriter *w, Entity *e) {
GB_ASSERT(e != nullptr);
if (e->token.string == "_") {
GB_PANIC("_ string");
}
if (e->token.string.len == 0) {
GB_PANIC("empty string");
}
if (e->kind == Entity_Variable) {
bool is_foreign = e->Variable.is_foreign;
bool is_export = e->Variable.is_export;
if (e->Variable.link_name.len > 0) {
type_writer_append(w, e->Variable.link_name.text, e->Variable.link_name.len);
return;
} else if (is_foreign || is_export) {
type_writer_append(w, e->token.string.text, e->token.string.len);
return;
}
} else if (e->kind == Entity_Procedure && e->Procedure.link_name.len > 0) {
type_writer_append(w, e->Procedure.link_name.text, e->Procedure.link_name.len);
return;
} else if (e->kind == Entity_Procedure && e->Procedure.is_export) {
type_writer_append(w, e->token.string.text, e->token.string.len);
return;
}
bool write_scope_index_suffix = false;
if (e->scope->flags & (ScopeFlag_Builtin)) {
goto write_base_name;
} else if ((e->scope->flags & (ScopeFlag_File | ScopeFlag_Pkg)) == 0 ||
e->flags & EntityFlag_NotExported) {
Scope *s = e->scope;
while ((s->flags & (ScopeFlag_Proc|ScopeFlag_File)) == 0 && s->decl_info == nullptr) {
if (s->parent == nullptr) {
break;
}
s = s->parent;
}
if (s->decl_info != nullptr && s->decl_info->entity) {
Entity *parent = s->decl_info->entity;
write_canonical_parent_prefix(w, parent);
if (e->scope->index > 0) {
write_scope_index_suffix = true;
}
goto write_base_name;
} else if ((s->flags & ScopeFlag_File) && s->file != nullptr) {
String file_name = filename_without_directory(s->file->fullpath);
type_writer_append(w, e->pkg->name.text, e->pkg->name.len);
if (e->pkg->name == "llvm") {
type_writer_appendc(w, "$");
}
type_writer_appendc(w, gb_bprintf(CANONICAL_NAME_SEPARATOR "[%.*s]" CANONICAL_NAME_SEPARATOR, LIT(file_name)));
goto write_base_name;
} else if (s->flags & (ScopeFlag_Builtin)) {
goto write_base_name;
}
gb_printf_err("%s WEIRD ENTITY TYPE %s %u %p\n", token_pos_to_string(e->token.pos), type_to_string(e->type), s->flags, s->decl_info);
auto const print_scope_flags = [](Scope *s) {
if (s->flags & ScopeFlag_Pkg) gb_printf_err("Pkg ");
if (s->flags & ScopeFlag_Builtin) gb_printf_err("Builtin ");
if (s->flags & ScopeFlag_Global) gb_printf_err("Global ");
if (s->flags & ScopeFlag_File) gb_printf_err("File ");
if (s->flags & ScopeFlag_Init) gb_printf_err("Init ");
if (s->flags & ScopeFlag_Proc) gb_printf_err("Proc ");
if (s->flags & ScopeFlag_Type) gb_printf_err("Type ");
if (s->flags & ScopeFlag_HasBeenImported) gb_printf_err("HasBeenImported ");
if (s->flags & ScopeFlag_ContextDefined) gb_printf_err("ContextDefined ");
gb_printf_err("\n");
};
print_scope_flags(s);
GB_PANIC("weird entity %.*s", LIT(e->token.string));
}
if (e->pkg != nullptr) {
type_writer_append(w, e->pkg->name.text, e->pkg->name.len);
type_writer_appendc(w, CANONICAL_NAME_SEPARATOR);
}
write_base_name:
switch (e->kind) {
case Entity_TypeName:
{
Type *params = nullptr;
Entity *parent = type_get_polymorphic_parent(e->type, &params);
if (parent && (parent->token.string == e->token.string)) {
type_writer_append(w, parent->token.string.text, parent->token.string.len);
write_canonical_params(w, params);
} else {
type_writer_append(w, e->token.string.text, e->token.string.len);
}
}
break;
case Entity_Constant:
// For debug symbols only
/*fallthrough*/
case Entity_Procedure:
case Entity_Variable:
type_writer_append(w, e->token.string.text, e->token.string.len);
if (is_type_polymorphic(e->type)) {
type_writer_appendc(w, CANONICAL_TYPE_SEPARATOR);
write_type_to_canonical_string(w, e->type);
}
break;
default:
GB_PANIC("TODO(bill): entity kind %d", e->kind);
break;
}
if (write_scope_index_suffix) {
GB_ASSERT(e != nullptr && e->scope != nullptr);
type_writer_append_fmt(w, CANONICAL_NAME_SEPARATOR "$%d", e->scope->index);
}
return;
}
gb_internal bool is_in_doc_writer(void);
// NOTE(bill): This exists so that we deterministically hash a type by serializing it to a canonical string
gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) {
if (type == nullptr) {
type_writer_appendc(w, CANONICAL_NONE_TYPE); // none/void type
return;
}
type = default_type(type);
GB_ASSERT(!is_type_untyped(type));
switch (type->kind) {
case Type_Basic:
type_writer_append(w, type->Basic.name.text, type->Basic.name.len);
return;
case Type_Pointer:
type_writer_appendb(w, '^');
write_type_to_canonical_string(w, type->Pointer.elem);
return;
case Type_MultiPointer:
type_writer_appendc(w, "[^]");
write_type_to_canonical_string(w, type->Pointer.elem);
return;
case Type_SoaPointer:
type_writer_appendc(w, "#soa^");
write_type_to_canonical_string(w, type->Pointer.elem);
return;
case Type_EnumeratedArray:
if (type->EnumeratedArray.is_sparse) {
type_writer_appendc(w, "#sparse");
}
type_writer_appendb(w, '[');
write_type_to_canonical_string(w, type->EnumeratedArray.index);
type_writer_appendb(w, ']');
write_type_to_canonical_string(w, type->EnumeratedArray.elem);
return;
case Type_Array:
type_writer_append_fmt(w, "[%lld]", cast(long long)type->Array.count);
write_type_to_canonical_string(w, type->Array.elem);
return;
case Type_Slice:
type_writer_appendc(w, "[]");
write_type_to_canonical_string(w, type->Array.elem);
return;
case Type_DynamicArray:
type_writer_appendc(w, "[dynamic]");
write_type_to_canonical_string(w, type->DynamicArray.elem);
return;
case Type_SimdVector:
type_writer_append_fmt(w, "#simd[%lld]", cast(long long)type->SimdVector.count);
write_type_to_canonical_string(w, type->SimdVector.elem);
return;
case Type_Matrix:
if (type->Matrix.is_row_major) {
type_writer_appendc(w, "#row_major ");
}
type_writer_append_fmt(w, "matrix[%lld, %lld]", cast(long long)type->Matrix.row_count, cast(long long)type->Matrix.column_count);
write_type_to_canonical_string(w, type->Matrix.elem);
return;
case Type_Map:
type_writer_appendc(w, "map[");
write_type_to_canonical_string(w, type->Map.key);
type_writer_appendc(w, "]");
write_type_to_canonical_string(w, type->Map.value);
return;
case Type_Enum:
type_writer_appendc(w, "enum");
if (type->Enum.base_type != nullptr) {
type_writer_appendb(w, ' ');
write_type_to_canonical_string(w, type->Enum.base_type);
type_writer_appendb(w, ' ');
}
type_writer_appendb(w, '{');
for_array(i, type->Enum.fields) {
Entity *f = type->Enum.fields[i];
GB_ASSERT(f->kind == Entity_Constant);
if (i > 0) {
type_writer_appendc(w, CANONICAL_FIELD_SEPARATOR);
}
type_writer_append(w, f->token.string.text, f->token.string.len);
type_writer_appendc(w, "=");
gbString s = exact_value_to_string(f->Constant.value, 1<<16);
type_writer_append(w, s, gb_string_length(s));
gb_string_free(s);
}
type_writer_appendb(w, '}');
return;
case Type_BitSet:
type_writer_appendc(w, "bit_set[");
if (type->BitSet.elem == nullptr) {
type_writer_appendc(w, CANONICAL_NONE_TYPE);
} else if (is_type_enum(type->BitSet.elem)) {
write_type_to_canonical_string(w, type->BitSet.elem);
} else {
type_writer_append_fmt(w, "%lld", type->BitSet.lower);
type_writer_append_fmt(w, CANONICAL_RANGE_OPERATOR);
type_writer_append_fmt(w, "%lld", type->BitSet.upper);
}
if (type->BitSet.underlying != nullptr) {
type_writer_appendc(w, ";");
write_type_to_canonical_string(w, type->BitSet.underlying);
}
type_writer_appendc(w, "]");
return;
case Type_Union:
type_writer_appendc(w, "union");
switch (type->Union.kind) {
case UnionType_no_nil: type_writer_appendc(w, "#no_nil"); break;
case UnionType_shared_nil: type_writer_appendc(w, "#shared_nil"); break;
}
if (type->Union.custom_align != 0) {
type_writer_append_fmt(w, "#align(%lld)", cast(long long)type->Union.custom_align);
}
type_writer_appendc(w, "{");
for_array(i, type->Union.variants) {
Type *t = type->Union.variants[i];
if (i > 0) type_writer_appendc(w, CANONICAL_FIELD_SEPARATOR);
write_type_to_canonical_string(w, t);
}
type_writer_appendc(w, "}");
return;
case Type_Struct:
if (type->Struct.soa_kind != StructSoa_None) {
switch (type->Struct.soa_kind) {
case StructSoa_Fixed: type_writer_append_fmt(w, "#soa[%lld]", cast(long long)type->Struct.soa_count); break;
case StructSoa_Slice: type_writer_appendc(w, "#soa[]"); break;
case StructSoa_Dynamic: type_writer_appendc(w, "#soa[dynamic]"); break;
default: GB_PANIC("Unknown StructSoaKind"); break;
}
return write_type_to_canonical_string(w, type->Struct.soa_elem);
}
type_writer_appendc(w, "struct");
if (type->Struct.is_packed) type_writer_appendc(w, "#packed");
if (type->Struct.is_raw_union) type_writer_appendc(w, "#raw_union");
if (type->Struct.is_no_copy) type_writer_appendc(w, "#no_copy");
if (type->Struct.custom_min_field_align != 0) type_writer_append_fmt(w, "#min_field_align(%lld)", cast(long long)type->Struct.custom_min_field_align);
if (type->Struct.custom_max_field_align != 0) type_writer_append_fmt(w, "#max_field_align(%lld)", cast(long long)type->Struct.custom_max_field_align);
if (type->Struct.custom_align != 0) type_writer_append_fmt(w, "#align(%lld)", cast(long long)type->Struct.custom_align);
type_writer_appendb(w, '{');
for_array(i, type->Struct.fields) {
Entity *f = type->Struct.fields[i];
GB_ASSERT(f->kind == Entity_Variable);
if (i > 0) {
type_writer_appendc(w, CANONICAL_FIELD_SEPARATOR);
}
type_writer_append(w, f->token.string.text, f->token.string.len);
type_writer_appendc(w, CANONICAL_TYPE_SEPARATOR);
write_type_to_canonical_string(w, f->type);
String tag = {};
if (type->Struct.tags != nullptr) {
tag = type->Struct.tags[i];
}
if (tag.len != 0) {
String s = quote_to_ascii(heap_allocator(), tag);
type_writer_append(w, s.text, s.len);
gb_free(heap_allocator(), s.text);
}
}
type_writer_appendb(w, '}');
return;
case Type_BitField:
type_writer_appendc(w, "bit_field");
write_type_to_canonical_string(w, type->BitField.backing_type);
type_writer_appendc(w, " {");
for (isize i = 0; i < type->BitField.fields.count; i++) {
Entity *f = type->BitField.fields[i];
if (i > 0) {
type_writer_appendc(w, CANONICAL_FIELD_SEPARATOR);
}
type_writer_append(w, f->token.string.text, f->token.string.len);
type_writer_appendc(w, CANONICAL_TYPE_SEPARATOR);
write_type_to_canonical_string(w, f->type);
type_writer_appendc(w, CANONICAL_BIT_FIELD_SEPARATOR);
type_writer_append_fmt(w, "%u", type->BitField.bit_sizes[i]);
}
type_writer_appendc(w, " }");
return;
case Type_Proc:
type_writer_appendc(w, "proc");
if (default_calling_convention() != type->Proc.calling_convention) {
type_writer_appendc(w, "\"");
type_writer_appendc(w, proc_calling_convention_strings[type->Proc.calling_convention]);
type_writer_appendc(w, "\"");
}
write_canonical_params(w, type->Proc.params);
if (type->Proc.result_count > 0) {
type_writer_appendc(w, "->");
write_canonical_params(w, type->Proc.results);
}
return;
case Type_Generic:
if (is_in_doc_writer()) {
type_writer_appendc(w, "$");
type_writer_append(w, type->Generic.name.text, type->Generic.name.len);
type_writer_append_fmt(w, "%lld", cast(long long)type->Generic.id);
} else {
GB_PANIC("Type_Generic should never be hit");
}
return;
case Type_Named:
if (type->Named.type_name != nullptr) {
write_canonical_entity_name(w, type->Named.type_name);
return;
} else {
type_writer_append(w, type->Named.name.text, type->Named.name.len);
}
return;
case Type_Tuple:
type_writer_appendc(w, "params");
write_canonical_params(w, type);
return;
default:
GB_PANIC("unknown type kind %d %.*s", type->kind, LIT(type_strings[type->kind]));
break;
}
return;
}
+128
View File
@@ -0,0 +1,128 @@
/*
General Rules for canonical name mangling
* No spaces between any values
* normal declarations - pkg::name
* builtin names - just their normal name e.g. `i32` or `string`
* nested (zero level) - pkg::parent1::parent2::name
* nested (more scopes) - pkg::parent1::parent2::name[4]
* [4] indicates the 4th scope within a procedure numbered in depth-first order
* file private - pkg::[file_name]::name
* Example: `pkg::[file.odin]::Type`
* polymorphic procedure/type - pkg::foo:TYPE
* naming convention for parameters
* type
* $typeid_based_name
* $$constant_parameter
* Example: `foo::to_thing:proc(u64)->([]u8)`
* nested decl in polymorphic procedure - pkg::foo:TYPE::name
* anonymous procedures - pkg::foo::$anon[file.odin:123]
* 123 is the file offset in bytes
*/
#define CANONICAL_TYPE_SEPARATOR ":"
#define CANONICAL_NAME_SEPARATOR "::"
// #define CANONICAL_NAME_SEPARATOR "·"
#define CANONICAL_BIT_FIELD_SEPARATOR "|"
#define CANONICAL_PARAM_SEPARATOR ","
#define CANONICAL_PARAM_TYPEID "$"
#define CANONICAL_PARAM_CONST "$$"
#define CANONICAL_PARAM_C_VARARG "#c_vararg"
#define CANONICAL_PARAM_VARARG ".."
#define CANONICAL_FIELD_SEPARATOR ","
#define CANONICAL_ANON_PREFIX "$anon"
#define CANONICAL_NONE_TYPE "<>"
#define CANONICAL_RANGE_OPERATOR "..="
struct TypeWriter;
gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type);
gb_internal void write_canonical_entity_name(TypeWriter *w, Entity *e);
gb_internal u64 type_hash_canonical_type(Type *type);
gb_internal String type_to_canonical_string(gbAllocator allocator, Type *type);
gb_internal gbString temp_canonical_string(Type *type);
gb_internal GB_COMPARE_PROC(type_info_pair_cmp);
struct TypeInfoPair {
Type *type;
u64 hash; // see: type_hash_canonical_type
};
struct TypeSet {
TypeInfoPair *keys;
usize count;
usize capacity;
};
static constexpr u64 TYPE_SET_TOMBSTONE = ~(u64)(0ull);
struct TypeSetIterator {
TypeSet *set;
usize index;
TypeSetIterator &operator++() noexcept {
for (;;) {
++index;
if (set->capacity == index) {
return *this;
}
TypeInfoPair key = set->keys[index];
if (key.hash != 0 && key.hash != TYPE_SET_TOMBSTONE) {
return *this;
}
}
}
bool operator==(TypeSetIterator const &other) const noexcept {
return this->set == other.set && this->index == other.index;
}
operator TypeInfoPair *() const {
return &set->keys[index];
}
};
gb_internal void type_set_init (TypeSet *s, isize capacity = 16);
gb_internal void type_set_destroy(TypeSet *s);
gb_internal Type *type_set_add (TypeSet *s, Type *ptr);
gb_internal Type *type_set_add (TypeSet *s, TypeInfoPair pair);
gb_internal bool type_set_update (TypeSet *s, Type *ptr); // returns true if it previously existed
gb_internal bool type_set_update (TypeSet *s, TypeInfoPair pair); // returns true if it previously existed
gb_internal bool type_set_exists (TypeSet *s, Type *ptr);
gb_internal void type_set_remove (TypeSet *s, Type *ptr);
gb_internal void type_set_clear (TypeSet *s);
gb_internal TypeInfoPair *type_set_retrieve(TypeSet *s, Type *ptr);
gb_internal TypeSetIterator begin(TypeSet &set) noexcept;
gb_internal TypeSetIterator end(TypeSet &set) noexcept;
template <typename V>
gb_internal gb_inline V *map_get(PtrMap<u64, V> *h, Type *key) {
return map_get(h, type_hash_canonical_type(key));
}
template <typename V>
gb_internal gb_inline void map_set(PtrMap<u64, V> *h, Type *key, V const &value) {
map_set(h, type_hash_canonical_type(key), value);
}
template <typename V>
gb_internal gb_inline V &map_must_get(PtrMap<u64, V> *h, Type *key) {
V *ptr = map_get(h, type_hash_canonical_type(key));
GB_ASSERT(ptr != nullptr);
return *ptr;
}
+123 -113
View File
@@ -3016,9 +3016,10 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
syntax_error(token, "Expected a type or range, got nothing");
}
if (allow_token(f, Token_Semicolon)) {
if (f->curr_token.kind == Token_Semicolon && f->curr_token.string == ";") {
expect_token(f, Token_Semicolon);
underlying = parse_type(f);
} else if (allow_token(f, Token_Comma)) {
} else if (allow_token(f, Token_Comma) || allow_token(f, Token_Semicolon)) {
String p = token_to_string(f->prev_token);
syntax_error(token_end_of_line(f, f->prev_token), "Expected a semicolon, got a %.*s", LIT(p));
@@ -4342,30 +4343,132 @@ gb_internal Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_fl
}
if (f->curr_token.kind == Token_Colon) {
Array<Ast *> names = convert_to_ident_list(f, list, true, allow_poly_names); // Copy for semantic reasons
if (f->curr_token.kind != Token_Colon) {
// NOTE(bill): proc(Type, Type, Type)
for (AstAndFlags const &item : list) {
Ast *type = item.node;
Token token = blank_token;
if (allowed_flags&FieldFlag_Results) {
// NOTE(bill): Make this nothing and not `_`
token.string = str_lit("");
}
auto names = array_make<Ast *>(ast_allocator(f), 1);
token.pos = ast_token(type).pos;
names[0] = ast_ident(f, token);
u32 flags = check_field_prefixes(f, list.count, allowed_flags, item.flags);
Token tag = {};
Ast *param = ast_field(f, names, item.node, nullptr, flags, tag, docs, f->line_comment);
array_add(&params, param);
}
if (name_count_) *name_count_ = total_name_count;
return ast_field_list(f, start_token, params);
}
// NOTE(bill): proc(ident, ident, ident: Type)
if (f->prev_token.kind == Token_Comma) {
syntax_error(f->prev_token, "Trailing comma before a colon is not allowed");
}
Array<Ast *> names = convert_to_ident_list(f, list, true, allow_poly_names); // Copy for semantic reasons
if (names.count == 0) {
syntax_error(f->curr_token, "Empty field declaration");
}
bool any_polymorphic_names = check_procedure_name_list(names);
u32 set_flags = 0;
if (list.count > 0) {
set_flags = list[0].flags;
}
set_flags = check_field_prefixes(f, names.count, allowed_flags, set_flags);
total_name_count += names.count;
Ast *type = nullptr;
Ast *default_value = nullptr;
Token tag = {};
expect_token_after(f, Token_Colon, "field list");
if (f->curr_token.kind != Token_Eq) {
type = parse_var_type(f, allow_ellipsis, allow_typeid_token);
Ast *tt = unparen_expr(type);
if (tt == nullptr) {
syntax_error(f->prev_token, "Invalid type expression in field list");
} else if (is_signature && !any_polymorphic_names && tt->kind == Ast_TypeidType && tt->TypeidType.specialization != nullptr) {
syntax_error(type, "Specialization of typeid is not allowed without polymorphic names");
}
}
if (allow_token(f, Token_Eq)) {
default_value = parse_expr(f, false);
if (!allow_default_parameters) {
syntax_error(f->curr_token, "Default parameters are only allowed for procedures");
default_value = nullptr;
}
}
if (default_value != nullptr && names.count > 1) {
syntax_error(f->curr_token, "Default parameters can only be applied to single values");
}
if (allowed_flags == FieldFlag_Struct && default_value != nullptr) {
syntax_error(default_value, "Default parameters are not allowed for structs");
default_value = nullptr;
}
if (type != nullptr && type->kind == Ast_Ellipsis) {
if (seen_ellipsis) syntax_error(type, "Extra variadic parameter after ellipsis");
seen_ellipsis = true;
if (names.count != 1) {
syntax_error(type, "Variadic parameters can only have one field name");
}
} else if (seen_ellipsis && default_value == nullptr) {
syntax_error(f->curr_token, "Extra parameter after ellipsis without a default value");
}
if (type != nullptr && default_value == nullptr) {
if (f->curr_token.kind == Token_String) {
tag = expect_token(f, Token_String);
if ((allowed_flags & FieldFlag_Tags) == 0) {
syntax_error(tag, "Field tags are only allowed within structures");
}
}
}
bool more_fields = allow_field_separator(f);
Ast *param = ast_field(f, names, type, default_value, set_flags, tag, docs, f->line_comment);
array_add(&params, param);
if (!more_fields) {
if (name_count_) *name_count_ = total_name_count;
return ast_field_list(f, start_token, params);
}
while (f->curr_token.kind != follow &&
f->curr_token.kind != Token_EOF &&
f->curr_token.kind != Token_Semicolon) {
CommentGroup *docs = f->lead_comment;
if (!is_signature) parse_enforce_tabs(f);
u32 set_flags = parse_field_prefixes(f);
Token tag = {};
Array<Ast *> names = parse_ident_list(f, allow_poly_names);
if (names.count == 0) {
syntax_error(f->curr_token, "Empty field declaration");
break;
}
bool any_polymorphic_names = check_procedure_name_list(names);
u32 set_flags = 0;
if (list.count > 0) {
set_flags = list[0].flags;
}
set_flags = check_field_prefixes(f, names.count, allowed_flags, set_flags);
total_name_count += names.count;
Ast *type = nullptr;
Ast *default_value = nullptr;
Token tag = {};
expect_token_after(f, Token_Colon, "field list");
if (f->curr_token.kind != Token_Eq) {
type = parse_var_type(f, allow_ellipsis, allow_typeid_token);
Ast *tt = unparen_expr(type);
if (tt == nullptr) {
syntax_error(f->prev_token, "Invalid type expression in field list");
} else if (is_signature && !any_polymorphic_names && tt->kind == Ast_TypeidType && tt->TypeidType.specialization != nullptr) {
if (is_signature && !any_polymorphic_names &&
tt != nullptr &&
tt->kind == Ast_TypeidType && tt->TypeidType.specialization != nullptr) {
syntax_error(type, "Specialization of typeid is not allowed without polymorphic names");
}
}
@@ -4382,11 +4485,6 @@ gb_internal Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_fl
syntax_error(f->curr_token, "Default parameters can only be applied to single values");
}
if (allowed_flags == FieldFlag_Struct && default_value != nullptr) {
syntax_error(default_value, "Default parameters are not allowed for structs");
default_value = nullptr;
}
if (type != nullptr && type->kind == Ast_Ellipsis) {
if (seen_ellipsis) syntax_error(type, "Extra variadic parameter after ellipsis");
seen_ellipsis = true;
@@ -4406,105 +4504,14 @@ gb_internal Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_fl
}
}
bool more_fields = allow_field_separator(f);
bool ok = allow_field_separator(f);
Ast *param = ast_field(f, names, type, default_value, set_flags, tag, docs, f->line_comment);
array_add(&params, param);
if (!more_fields) {
if (name_count_) *name_count_ = total_name_count;
return ast_field_list(f, start_token, params);
if (!ok) {
break;
}
while (f->curr_token.kind != follow &&
f->curr_token.kind != Token_EOF &&
f->curr_token.kind != Token_Semicolon) {
CommentGroup *docs = f->lead_comment;
if (!is_signature) parse_enforce_tabs(f);
u32 set_flags = parse_field_prefixes(f);
Token tag = {};
Array<Ast *> names = parse_ident_list(f, allow_poly_names);
if (names.count == 0) {
syntax_error(f->curr_token, "Empty field declaration");
break;
}
bool any_polymorphic_names = check_procedure_name_list(names);
set_flags = check_field_prefixes(f, names.count, allowed_flags, set_flags);
total_name_count += names.count;
Ast *type = nullptr;
Ast *default_value = nullptr;
expect_token_after(f, Token_Colon, "field list");
if (f->curr_token.kind != Token_Eq) {
type = parse_var_type(f, allow_ellipsis, allow_typeid_token);
Ast *tt = unparen_expr(type);
if (is_signature && !any_polymorphic_names &&
tt != nullptr &&
tt->kind == Ast_TypeidType && tt->TypeidType.specialization != nullptr) {
syntax_error(type, "Specialization of typeid is not allowed without polymorphic names");
}
}
if (allow_token(f, Token_Eq)) {
default_value = parse_expr(f, false);
if (!allow_default_parameters) {
syntax_error(f->curr_token, "Default parameters are only allowed for procedures");
default_value = nullptr;
}
}
if (default_value != nullptr && names.count > 1) {
syntax_error(f->curr_token, "Default parameters can only be applied to single values");
}
if (type != nullptr && type->kind == Ast_Ellipsis) {
if (seen_ellipsis) syntax_error(type, "Extra variadic parameter after ellipsis");
seen_ellipsis = true;
if (names.count != 1) {
syntax_error(type, "Variadic parameters can only have one field name");
}
} else if (seen_ellipsis && default_value == nullptr) {
syntax_error(f->curr_token, "Extra parameter after ellipsis without a default value");
}
if (type != nullptr && default_value == nullptr) {
if (f->curr_token.kind == Token_String) {
tag = expect_token(f, Token_String);
if ((allowed_flags & FieldFlag_Tags) == 0) {
syntax_error(tag, "Field tags are only allowed within structures");
}
}
}
bool ok = allow_field_separator(f);
Ast *param = ast_field(f, names, type, default_value, set_flags, tag, docs, f->line_comment);
array_add(&params, param);
if (!ok) {
break;
}
}
if (name_count_) *name_count_ = total_name_count;
return ast_field_list(f, start_token, params);
}
for (AstAndFlags const &item : list) {
Ast *type = item.node;
Token token = blank_token;
if (allowed_flags&FieldFlag_Results) {
// NOTE(bill): Make this nothing and not `_`
token.string = str_lit("");
}
auto names = array_make<Ast *>(ast_allocator(f), 1);
token.pos = ast_token(type).pos;
names[0] = ast_ident(f, token);
u32 flags = check_field_prefixes(f, list.count, allowed_flags, item.flags);
Token tag = {};
Ast *param = ast_field(f, names, item.node, nullptr, flags, tag, docs, f->line_comment);
array_add(&params, param);
}
if (name_count_) *name_count_ = total_name_count;
@@ -4572,6 +4579,9 @@ gb_internal Ast *parse_do_body(AstFile *f, Token const &token, char const *msg)
gb_internal bool parse_control_statement_semicolon_separator(AstFile *f) {
Token tok = peek_token(f);
if (tok.kind != Token_OpenBrace) {
if (f->curr_token.kind == Token_Semicolon && f->curr_token.string != ";") {
syntax_error(token_end_of_line(f, f->prev_token), "Expected ';', got newline");
}
return allow_token(f, Token_Semicolon);
}
if (f->curr_token.string == ";") {
+5 -5
View File
@@ -42,7 +42,7 @@ gb_internal void ptr_set_destroy(PtrSet<T> *s) {
template <typename T>
gb_internal isize ptr_set__find(PtrSet<T> *s, T ptr) {
GB_ASSERT(ptr != nullptr);
GB_ASSERT(ptr != 0);
if (s->count != 0) {
#if 0
for (usize i = 0; i < s->capacity; i++) {
@@ -58,7 +58,7 @@ gb_internal isize ptr_set__find(PtrSet<T> *s, T ptr) {
T key = s->keys[hash_index];
if (key == ptr) {
return hash_index;
} else if (key == nullptr) {
} else if (key == 0) {
return -1;
}
hash_index = (hash_index+1)&mask;
@@ -122,7 +122,7 @@ gb_internal bool ptr_set_update(PtrSet<T> *s, T ptr) { // returns true if it pre
for (usize i = 0; i < s->capacity; i++) {
T *key = &s->keys[hash_index];
GB_ASSERT(*key != ptr);
if (*key == (T)PtrSet<T>::TOMBSTONE || *key == nullptr) {
if (*key == (T)PtrSet<T>::TOMBSTONE || *key == 0) {
*key = ptr;
s->count++;
return false;
@@ -169,7 +169,7 @@ struct PtrSetIterator {
return *this;
}
T key = set->keys[index];
if (key != nullptr && key != (T)PtrSet<T>::TOMBSTONE) {
if (key != 0 && key != (T)PtrSet<T>::TOMBSTONE) {
return *this;
}
}
@@ -191,7 +191,7 @@ gb_internal PtrSetIterator<T> begin(PtrSet<T> &set) noexcept {
usize index = 0;
while (index < set.capacity) {
T key = set.keys[index];
if (key != nullptr && key != (T)PtrSet<T>::TOMBSTONE) {
if (key != 0 && key != (T)PtrSet<T>::TOMBSTONE) {
break;
}
index++;
+92 -77
View File
@@ -1,5 +1,5 @@
struct Scope;
struct Ast;
struct Scope;
struct Entity;
enum BasicKind {
@@ -161,10 +161,10 @@ struct TypeStruct {
struct TypeUnion {
Slice<Type *> variants;
Ast * node;
Scope * scope;
i64 variant_block_size;
i64 custom_align;
Type * polymorphic_params; // Type_Tuple
@@ -503,9 +503,9 @@ gb_global Type basic_types[] = {
{Type_Basic, {Basic_rawptr, BasicFlag_Pointer, -1, STR_LIT("rawptr")}},
{Type_Basic, {Basic_string, BasicFlag_String, -1, STR_LIT("string")}},
{Type_Basic, {Basic_cstring, BasicFlag_String, -1, STR_LIT("cstring")}},
{Type_Basic, {Basic_any, 0, -1, STR_LIT("any")}},
{Type_Basic, {Basic_any, 0, 16, STR_LIT("any")}},
{Type_Basic, {Basic_typeid, 0, -1, STR_LIT("typeid")}},
{Type_Basic, {Basic_typeid, 0, 8, STR_LIT("typeid")}},
// Endian
{Type_Basic, {Basic_i16le, BasicFlag_Integer | BasicFlag_EndianLittle, 2, STR_LIT("i16le")}},
@@ -856,40 +856,6 @@ gb_internal void type_path_pop(TypePath *tp) {
#define FAILURE_SIZE 0
#define FAILURE_ALIGNMENT 0
gb_internal bool type_ptr_set_exists(PtrSet<Type *> *s, Type *t);
gb_internal bool type_ptr_set_update(PtrSet<Type *> *s, Type *t) {
if (t == nullptr) {
return true;
}
if (type_ptr_set_exists(s, t)) {
return true;
}
ptr_set_add(s, t);
return false;
}
gb_internal bool type_ptr_set_exists(PtrSet<Type *> *s, Type *t) {
if (t == nullptr) {
return true;
}
if (ptr_set_exists(s, t)) {
return true;
}
// TODO(bill, 2019-10-05): This is very slow and it's probably a lot
// faster to cache types correctly
for (Type *f : *s) {
if (are_types_identical(t, f)) {
ptr_set_add(s, t);
return true;
}
}
return false;
}
gb_internal Type *base_type(Type *t) {
for (;;) {
if (t == nullptr) {
@@ -1438,7 +1404,7 @@ gb_internal bool is_type_matrix(Type *t) {
gb_internal i64 matrix_align_of(Type *t, struct TypePath *tp) {
t = base_type(t);
GB_ASSERT(t->kind == Type_Matrix);
Type *elem = t->Matrix.elem;
i64 row_count = gb_max(t->Matrix.row_count, 1);
i64 column_count = gb_max(t->Matrix.column_count, 1);
@@ -1450,15 +1416,15 @@ gb_internal i64 matrix_align_of(Type *t, struct TypePath *tp) {
i64 elem_align = type_align_of_internal(elem, tp);
if (pop) type_path_pop(tp);
i64 elem_size = type_size_of(elem);
// NOTE(bill, 2021-10-25): The alignment strategy here is to have zero padding
// It would be better for performance to pad each column so that each column
// could be maximally aligned but as a compromise, having no padding will be
// beneficial to third libraries that assume no padding
i64 total_expected_size = row_count*column_count*elem_size;
// i64 min_alignment = prev_pow2(elem_align * row_count);
i64 min_alignment = prev_pow2(total_expected_size);
@@ -1466,7 +1432,7 @@ gb_internal i64 matrix_align_of(Type *t, struct TypePath *tp) {
min_alignment >>= 1;
}
min_alignment = gb_max(min_alignment, elem_align);
i64 align = gb_min(min_alignment, build_context.max_simd_align);
return align;
}
@@ -1480,7 +1446,7 @@ gb_internal i64 matrix_type_stride_in_bytes(Type *t, struct TypePath *tp) {
} else if (t->Matrix.row_count == 0) {
return 0;
}
i64 elem_size;
if (tp != nullptr) {
elem_size = type_size_of_internal(t->Matrix.elem, tp);
@@ -1489,7 +1455,7 @@ gb_internal i64 matrix_type_stride_in_bytes(Type *t, struct TypePath *tp) {
}
i64 stride_in_bytes = 0;
// NOTE(bill, 2021-10-25): The alignment strategy here is to have zero padding
// It would be better for performance to pad each column/row so that each column/row
// could be maximally aligned but as a compromise, having no padding will be
@@ -1545,7 +1511,7 @@ gb_internal i64 matrix_row_major_index_to_offset(Type *t, i64 index) {
gb_internal i64 matrix_column_major_index_to_offset(Type *t, i64 index) {
t = base_type(t);
GB_ASSERT(t->kind == Type_Matrix);
i64 row_index = index%t->Matrix.row_count;
i64 column_index = index/t->Matrix.row_count;
return matrix_indices_to_offset(t, row_index, column_index);
@@ -1566,7 +1532,7 @@ gb_internal bool is_type_valid_for_matrix_elems(Type *t) {
return true;
} else if (is_type_complex(t)) {
return true;
}
}
if (t->kind == Type_Generic) {
return true;
}
@@ -2119,6 +2085,26 @@ gb_internal bool is_type_sliceable(Type *t) {
return false;
}
gb_internal Entity *type_get_polymorphic_parent(Type *t, Type **params_) {
t = base_type(t);
if (t == nullptr) {
return nullptr;
}
Type *parent = nullptr;
if (t->kind == Type_Struct) {
parent = t->Struct.polymorphic_parent;
if (params_) *params_ = t->Struct.polymorphic_params;
} else if (t->kind == Type_Union) {
parent = t->Union.polymorphic_parent;
if (params_) *params_ = t->Union.polymorphic_params;
}
if (parent != nullptr) {
GB_ASSERT(parent->kind == Type_Named);
return parent->Named.type_name;
}
return nullptr;
}
gb_internal bool is_type_polymorphic_record(Type *t) {
t = base_type(t);
@@ -2485,7 +2471,7 @@ gb_internal bool is_type_simple_compare(Type *t) {
case Type_Proc:
case Type_BitSet:
return true;
case Type_Matrix:
return is_type_simple_compare(t->Matrix.elem);
@@ -2732,7 +2718,7 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple
case Type_Array:
return (x->Array.count == y->Array.count) && are_types_identical(x->Array.elem, y->Array.elem);
case Type_Matrix:
return x->Matrix.row_count == y->Matrix.row_count &&
x->Matrix.column_count == y->Matrix.column_count &&
@@ -2757,7 +2743,37 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple
case Type_Enum:
return x == y; // NOTE(bill): All enums are unique
if (x == y) {
return true;
}
if (x->Enum.fields.count != y->Enum.fields.count) {
return false;
}
if (!are_types_identical(x->Enum.base_type, y->Enum.base_type)) {
return false;
}
if (x->Enum.min_value_index != y->Enum.min_value_index) {
return false;
}
if (x->Enum.max_value_index != y->Enum.max_value_index) {
return false;
}
for (isize i = 0; i < x->Enum.fields.count; i++) {
Entity *a = x->Enum.fields[i];
Entity *b = y->Enum.fields[i];
if (a->token.string != b->token.string) {
return false;
}
GB_ASSERT(a->kind == b->kind);
GB_ASSERT(a->kind == Entity_Constant);
bool same = compare_exact_values(Token_CmpEq, a->Constant.value, b->Constant.value);
if (!same) {
return false;
}
}
return true;
case Type_Union:
if (x->Union.variants.count == y->Union.variants.count &&
@@ -2815,7 +2831,9 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple
return false;
}
}
return are_types_identical(x->Struct.polymorphic_params, y->Struct.polymorphic_params);
// TODO(bill): Which is the correct logic here?
// return are_types_identical(x->Struct.polymorphic_params, y->Struct.polymorphic_params);
return true;
}
break;
@@ -3592,7 +3610,7 @@ gb_internal bool are_struct_fields_reordered(Type *type) {
return false;
}
GB_ASSERT(type->Struct.offsets != nullptr);
i64 prev_offset = 0;
for_array(i, type->Struct.fields) {
i64 offset = type->Struct.offsets[i];
@@ -3613,9 +3631,9 @@ gb_internal Slice<i32> struct_fields_index_by_increasing_offset(gbAllocator allo
return {};
}
GB_ASSERT(type->Struct.offsets != nullptr);
auto indices = slice_make<i32>(allocator, type->Struct.fields.count);
i64 prev_offset = 0;
bool is_ordered = true;
for_array(i, indices) {
@@ -3630,14 +3648,14 @@ gb_internal Slice<i32> struct_fields_index_by_increasing_offset(gbAllocator allo
isize n = indices.count;
for (isize i = 1; i < n; i++) {
isize j = i;
while (j > 0 && type->Struct.offsets[indices[j-1]] > type->Struct.offsets[indices[j]]) {
gb_swap(i32, indices[j-1], indices[j]);
j -= 1;
}
}
}
}
return indices;
}
@@ -3685,8 +3703,8 @@ gb_internal i64 type_size_of(Type *t) {
switch (t->Basic.kind) {
case Basic_string: size = 2*build_context.int_size; break;
case Basic_cstring: size = build_context.ptr_size; break;
case Basic_any: size = 2*build_context.ptr_size; break;
case Basic_typeid: size = build_context.ptr_size; break;
case Basic_any: size = 16; break;
case Basic_typeid: size = 8; break;
case Basic_int: case Basic_uint:
size = build_context.int_size;
@@ -3748,8 +3766,8 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) {
switch (t->Basic.kind) {
case Basic_string: return build_context.int_size;
case Basic_cstring: return build_context.ptr_size;
case Basic_any: return build_context.ptr_size;
case Basic_typeid: return build_context.ptr_size;
case Basic_any: return 8;
case Basic_typeid: return 8;
case Basic_int: case Basic_uint:
return build_context.int_size;
@@ -3887,8 +3905,8 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) {
// IMPORTANT TODO(bill): Figure out the alignment of vector types
return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_simd_align*2);
}
case Type_Matrix:
case Type_Matrix:
return matrix_align_of(t, path);
case Type_SoaPointer:
@@ -3999,8 +4017,8 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) {
switch (kind) {
case Basic_string: return 2*build_context.int_size;
case Basic_cstring: return build_context.ptr_size;
case Basic_any: return 2*build_context.ptr_size;
case Basic_typeid: return build_context.ptr_size;
case Basic_any: return 16;
case Basic_typeid: return 8;
case Basic_int: case Basic_uint:
return build_context.int_size;
@@ -4175,7 +4193,7 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) {
Type *elem = t->SimdVector.elem;
return count * type_size_of_internal(elem, path);
}
case Type_Matrix: {
i64 stride_in_bytes = matrix_type_stride_in_bytes(t, path);
if (t->Matrix.is_row_major) {
@@ -4236,7 +4254,7 @@ gb_internal i64 type_offset_of(Type *t, i64 index, Type **field_type_) {
return 0; // data
case 1:
if (field_type_) *field_type_ = t_typeid;
return build_context.ptr_size; // id
return 8; // id
}
}
break;
@@ -4307,8 +4325,8 @@ gb_internal i64 type_offset_of_from_selection(Type *type, Selection sel) {
}
} else if (t->Basic.kind == Basic_any) {
switch (index) {
case 0: t = t_type_info_ptr; break;
case 1: t = t_rawptr; break;
case 0: t = t_rawptr; break;
case 1: t = t_typeid; break;
}
}
break;
@@ -4580,7 +4598,7 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
break;
case Type_Array:
str = gb_string_appendc(str, gb_bprintf("[%d]", cast(int)type->Array.count));
str = gb_string_appendc(str, gb_bprintf("[%lld]", cast(long long)type->Array.count));
str = write_type_to_string(str, type->Array.elem);
break;
@@ -4753,10 +4771,10 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
}
break;
case ProcCC_CDecl:
str = gb_string_appendc(str, " \"cdecl\" ");
str = gb_string_appendc(str, " \"c\" ");
break;
case ProcCC_StdCall:
str = gb_string_appendc(str, " \"stdcall\" ");
str = gb_string_appendc(str, " \"std\" ");
break;
case ProcCC_FastCall:
str = gb_string_appendc(str, " \"fastcall\" ");
@@ -4814,7 +4832,7 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
str = gb_string_append_fmt(str, "#simd[%d]", cast(int)type->SimdVector.count);
str = write_type_to_string(str, type->SimdVector.elem);
break;
case Type_Matrix:
if (type->Matrix.is_row_major) {
str = gb_string_appendc(str, "#row_major ");
@@ -4855,6 +4873,3 @@ gb_internal gbString type_to_string(Type *type, bool shorthand) {
gb_internal gbString type_to_string_shorthand(Type *type) {
return type_to_string(type, true);
}
+74
View File
@@ -5,6 +5,7 @@ import "core:log"
import "core:path/filepath"
import "core:slice"
import "core:testing"
import "core:strings"
@(test)
test_read_dir :: proc(t: ^testing.T) {
@@ -30,3 +31,76 @@ test_read_dir :: proc(t: ^testing.T) {
testing.expect_value(t, fis[1].name, "sub")
testing.expect_value(t, fis[1].type, os.File_Type.Directory)
}
@(test)
test_walker :: proc(t: ^testing.T) {
path := filepath.join({#directory, "../dir"})
defer delete(path)
w := os.walker_create(path)
defer os.walker_destroy(&w)
test_walker_internal(t, &w)
}
@(test)
test_walker_file :: proc(t: ^testing.T) {
path := filepath.join({#directory, "../dir"})
defer delete(path)
f, err := os.open(path)
testing.expect_value(t, err, nil)
defer os.close(f)
w := os.walker_create(f)
defer os.walker_destroy(&w)
test_walker_internal(t, &w)
}
test_walker_internal :: proc(t: ^testing.T, w: ^os.Walker) {
Seen :: struct {
type: os.File_Type,
path: string,
}
expected := [?]Seen{
{.Regular, filepath.join({"dir", "b.txt"})},
{.Directory, filepath.join({"dir", "sub"})},
{.Regular, filepath.join({"dir", "sub", ".gitkeep"})},
}
seen: [dynamic]Seen
defer delete(seen)
for info in os.walker_walk(w) {
errpath, err := os.walker_error(w)
testing.expectf(t, err == nil, "walker error for %q: %v", errpath, err)
append(&seen, Seen{
info.type,
strings.clone(info.fullpath),
})
}
if _, err := os.walker_error(w); err == .Unsupported {
log.warn("os2 directory functionality is unsupported, skipping test")
return
}
testing.expect_value(t, len(seen), len(expected))
for expectation in expected {
found: bool
for entry in seen {
if strings.has_suffix(entry.path, expectation.path) {
found = true
testing.expect_value(t, entry.type, expectation.type)
delete(entry.path)
}
}
testing.expectf(t, found, "%q not found in %v", expectation, seen)
delete(expectation.path)
}
}
+31
View File
@@ -0,0 +1,31 @@
package tests_core_os_os2
import os "core:os/os2"
import "core:testing"
import "core:path/filepath"
@(test)
test_clone :: proc(t: ^testing.T) {
f, err := os.open(filepath.join({#directory, "file.odin"}, context.temp_allocator))
testing.expect_value(t, err, nil)
testing.expect(t, f != nil)
clone: ^os.File
clone, err = os.clone(f)
testing.expect_value(t, err, nil)
testing.expect(t, clone != nil)
testing.expect_value(t, os.name(clone), os.name(f))
testing.expect(t, os.fd(clone) != os.fd(f))
os.close(f)
buf: [128]byte
n: int
n, err = os.read(clone, buf[:])
testing.expect_value(t, err, nil)
testing.expect(t, n > 13)
testing.expect_value(t, string(buf[:13]), "package tests")
os.close(clone)
}
+43
View File
@@ -1,5 +1,6 @@
package test_core_strings
import "core:mem"
import "core:strings"
import "core:testing"
import "base:runtime"
@@ -175,3 +176,45 @@ test_substring :: proc(t: ^testing.T) {
testing.expectf(t, sub == tc.sub, "expected %v[%v:%v] to return sub: %v, got: %v", tc.s, tc.start, tc.end, tc.sub, sub)
}
}
@test
test_builder_to_cstring_with_nil_allocator :: proc(t: ^testing.T) {
b := strings.builder_make_none(mem.nil_allocator())
cstr, err := strings.to_cstring(&b)
testing.expect_value(t, cstr, nil)
testing.expect_value(t, err, mem.Allocator_Error.Out_Of_Memory)
}
@test
test_builder_to_cstring :: proc(t: ^testing.T) {
buf: [8]byte
a: mem.Arena
mem.arena_init(&a, buf[:])
b := strings.builder_make_none(mem.arena_allocator(&a))
{
cstr, err := strings.to_cstring(&b)
testing.expectf(t, cstr != nil, "expected cstr to not be nil, got %v", cstr)
testing.expect_value(t, err, nil)
}
n := strings.write_byte(&b, 'a')
testing.expect(t, n == 1)
{
cstr, err := strings.to_cstring(&b)
testing.expectf(t, cstr != nil, "expected cstr to not be nil, got %v", cstr)
testing.expect_value(t, err, nil)
}
n = strings.write_string(&b, "aaaaaaa")
testing.expect(t, n == 7)
{
cstr, err := strings.to_cstring(&b)
testing.expect(t, cstr == nil)
testing.expect(t, err == .Out_Of_Memory)
}
}
+1 -1
View File
@@ -67,7 +67,7 @@ main :: proc() {
```
Writing to memory:
```c
```odin
package main
import "vendor:cgltf"
+15 -1
View File
@@ -19,6 +19,8 @@ BOOL :: dxgi.BOOL
UINT :: dxgi.UINT
INT :: dxgi.INT
LPCWSTR :: [^]u16
RECT :: dxgi.RECT
SIZE :: dxgi.SIZE
@@ -3281,7 +3283,19 @@ IVideoContext_VTable :: struct {
VideoProcessorGetStreamRotation: proc "system" (this: ^IVideoContext, pVideoProcessor: ^IVideoProcessor, StreamIndex: u32, pEnable: ^BOOL, pRotation: ^VIDEO_PROCESSOR_ROTATION),
}
IUserDefinedAnnotation_UUID_STRING :: "B2DAAD8B-03D4-4DBF-95EB-32AB4B63D0AB"
IUserDefinedAnnotation_UUID := &IID{0xB2DAAD8B, 0x03D4, 0x4DBF, {0x95, 0xEB, 0x32, 0xAB, 0x4B, 0x63, 0xD0, 0xAB}}
IUserDefinedAnnotation :: struct #raw_union {
#subtype iunknown: IUnknown,
using id3d11userdefinedannotation_vtable: ^IUserDefinedAnnotation_VTable,
}
IUserDefinedAnnotation_VTable :: struct {
using iunknown_vtable : IUnknown_VTable,
BeginEvent: proc "system" (this: ^IUserDefinedAnnotation, Name: LPCWSTR) -> i32,
EndEvent: proc "system" (this: ^IUserDefinedAnnotation) -> i32,
GetStatus: proc "system" (this: ^IUserDefinedAnnotation) -> i32,
SetMarker: proc "system" (this: ^IUserDefinedAnnotation, Name: LPCWSTR),
}
IVideoDevice_UUID_STRING :: "10EC4D5B-975A-4689-B9E4-D0AAC30FE333"
IVideoDevice_UUID := &IID{0x10EC4D5B, 0x975A, 0x4689, {0xB9, 0xE4, 0xD0, 0xAA, 0xC3, 0x0F, 0xE3, 0x33}}
+27
View File
@@ -0,0 +1,27 @@
Copyright (c) 2016, Alliance for Open Media. 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.
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.
+353
View File
@@ -0,0 +1,353 @@
Copyright 2019 Joe Drago. 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.
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.
------------------------------------------------------------------------------
Files: src/obu.c
Copyright © 2018-2019, VideoLAN and dav1d authors
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.
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 OWNER 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.
------------------------------------------------------------------------------
Files: apps/shared/iccjpeg.*
In plain English:
1. We don't promise that this software works. (But if you find any bugs,
please let us know!)
2. You can use this software for whatever you want. You don't have to pay us.
3. You may not pretend that you wrote this software. If you use it in a
program, you must acknowledge somewhere in your documentation that
you've used the IJG code.
In legalese:
The authors make NO WARRANTY or representation, either express or implied,
with respect to this software, its quality, accuracy, merchantability, or
fitness for a particular purpose. This software is provided "AS IS", and you,
its user, assume the entire risk as to its quality and accuracy.
This software is copyright (C) 1991-2013, Thomas G. Lane, Guido Vollbeding.
All Rights Reserved except as specified below.
Permission is hereby granted to use, copy, modify, and distribute this
software (or portions thereof) for any purpose, without fee, subject to these
conditions:
(1) If any part of the source code for this software is distributed, then this
README file must be included, with this copyright and no-warranty notice
unaltered; and any additions, deletions, or changes to the original files
must be clearly indicated in accompanying documentation.
(2) If only executable code is distributed, then the accompanying
documentation must state that "this software is based in part on the work of
the Independent JPEG Group".
(3) Permission for use of this software is granted only if the user accepts
full responsibility for any undesirable consequences; the authors accept
NO LIABILITY for damages of any kind.
These conditions apply to any software derived from or based on the IJG code,
not just to the unmodified library. If you use our work, you ought to
acknowledge us.
Permission is NOT granted for the use of any IJG author's name or company name
in advertising or publicity relating to this software or products derived from
it. This software may be referred to only as "the Independent JPEG Group's
software".
We specifically permit and encourage the use of this software as the basis of
commercial products, provided that all warranty or liability claims are
assumed by the product vendor.
The Unix configuration script "configure" was produced with GNU Autoconf.
It is copyright by the Free Software Foundation but is freely distributable.
The same holds for its supporting scripts (config.guess, config.sub,
ltmain.sh). Another support script, install-sh, is copyright by X Consortium
but is also freely distributable.
The IJG distribution formerly included code to read and write GIF files.
To avoid entanglement with the Unisys LZW patent, GIF reading support has
been removed altogether, and the GIF writer has been simplified to produce
"uncompressed GIFs". This technique does not use the LZW algorithm; the
resulting GIF files are larger than usual, but are readable by all standard
GIF decoders.
We are required to state that
"The Graphics Interchange Format(c) is the Copyright property of
CompuServe Incorporated. GIF(sm) is a Service Mark property of
CompuServe Incorporated."
------------------------------------------------------------------------------
Files: contrib/gdk-pixbuf/*
Copyright 2020 Emmanuel Gil Peyrot. 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.
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.
------------------------------------------------------------------------------
Files: android_jni/gradlew*
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+23
View File
@@ -0,0 +1,23 @@
Copyright © 2018-2019, VideoLAN and dav1d authors
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.
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 OWNER 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.
+21
View File
@@ -0,0 +1,21 @@
Copyright (c) 1988-1997 Sam Leffler
Copyright (c) 1991-1997 Silicon Graphics, Inc.
Permission to use, copy, modify, distribute, and sell this software and
its documentation for any purpose is hereby granted without fee, provided
that (i) the above copyright notices and this permission notice appear in
all copies of the software and related documentation, and (ii) the names of
Sam Leffler and Silicon Graphics may not be used in any advertising or
publicity relating to the software without the specific, prior written
permission of Sam Leffler and Silicon Graphics.
THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
+17
View File
@@ -0,0 +1,17 @@
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
+30
View File
@@ -0,0 +1,30 @@
Copyright (c) 2010, Google Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* 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.
* Neither the name of Google 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.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
+107
View File
@@ -0,0 +1,107 @@
package sdl3_image
import "core:c"
import SDL "vendor:sdl3"
when ODIN_OS == .Windows {
foreign import lib "SDL3_image.lib"
} else {
foreign import lib "system:SDL3_image"
}
MAJOR_VERSION :: 3
MINOR_VERSION :: 2
PATCHLEVEL :: 0
Animation :: struct {
w, h: c.int,
count: c.int,
frames: [^]^SDL.Surface,
delays: [^]c.int,
}
@(default_calling_convention="c", link_prefix="IMG_")
foreign lib {
Version :: proc() -> c.int ---
/* Load an image from an SDL data source.
The 'type' may be one of: "BMP", "GIF", "PNG", etc.
If the image format supports a transparent pixel, SDL will set the
colorkey for the surface. You can enable RLE acceleration on the
surface afterwards by calling:
SDL_SetColorKey(image, SDL_RLEACCEL, image->format->colorkey);
*/
LoadTyped_IO :: proc(src: ^SDL.IOStream, closeio: bool, type: cstring) -> ^SDL.Surface ---
/* Convenience functions */
Load :: proc(file: cstring) -> ^SDL.Surface ---
Load_IO :: proc(src: ^SDL.IOStream, closeio: bool) -> ^SDL.Surface ---
/* Load an image directly into a render texture. */
LoadTexture :: proc(renderer: ^SDL.Renderer, file: cstring) -> ^SDL.Texture ---
LoadTexture_IO :: proc(renderer: ^SDL.Renderer, src: ^SDL.IOStream, closeio: bool) -> ^SDL.Texture ---
LoadTextureTyped_IO :: proc(renderer: ^SDL.Renderer, src: ^SDL.IOStream, closeio: bool, type: cstring) -> ^SDL.Texture ---
/* Functions to detect a file type, given a seekable source */
isAVIF :: proc(src: ^SDL.IOStream) -> bool ---
isICO :: proc(src: ^SDL.IOStream) -> bool ---
isCUR :: proc(src: ^SDL.IOStream) -> bool ---
isBMP :: proc(src: ^SDL.IOStream) -> bool ---
isGIF :: proc(src: ^SDL.IOStream) -> bool ---
isJPG :: proc(src: ^SDL.IOStream) -> bool ---
isJXL :: proc(src: ^SDL.IOStream) -> bool ---
isLBM :: proc(src: ^SDL.IOStream) -> bool ---
isPCX :: proc(src: ^SDL.IOStream) -> bool ---
isPNG :: proc(src: ^SDL.IOStream) -> bool ---
isPNM :: proc(src: ^SDL.IOStream) -> bool ---
isSVG :: proc(src: ^SDL.IOStream) -> bool ---
isQOI :: proc(src: ^SDL.IOStream) -> bool ---
isTIF :: proc(src: ^SDL.IOStream) -> bool ---
isXCF :: proc(src: ^SDL.IOStream) -> bool ---
isXPM :: proc(src: ^SDL.IOStream) -> bool ---
isXV :: proc(src: ^SDL.IOStream) -> bool ---
isWEBP :: proc(src: ^SDL.IOStream) -> bool ---
/* Individual loading functions */
LoadAVIF_IO :: proc(src: ^SDL.IOStream) -> ^SDL.Surface ---
LoadICO_IO :: proc(src: ^SDL.IOStream) -> ^SDL.Surface ---
LoadCUR_IO :: proc(src: ^SDL.IOStream) -> ^SDL.Surface ---
LoadBMP_IO :: proc(src: ^SDL.IOStream) -> ^SDL.Surface ---
LoadGIF_IO :: proc(src: ^SDL.IOStream) -> ^SDL.Surface ---
LoadJPG_IO :: proc(src: ^SDL.IOStream) -> ^SDL.Surface ---
LoadJXL_IO :: proc(src: ^SDL.IOStream) -> ^SDL.Surface ---
LoadLBM_IO :: proc(src: ^SDL.IOStream) -> ^SDL.Surface ---
LoadPCX_IO :: proc(src: ^SDL.IOStream) -> ^SDL.Surface ---
LoadPNG_IO :: proc(src: ^SDL.IOStream) -> ^SDL.Surface ---
LoadPNM_IO :: proc(src: ^SDL.IOStream) -> ^SDL.Surface ---
LoadSVG_IO :: proc(src: ^SDL.IOStream) -> ^SDL.Surface ---
LoadQOI_IO :: proc(src: ^SDL.IOStream) -> ^SDL.Surface ---
LoadTGA_IO :: proc(src: ^SDL.IOStream) -> ^SDL.Surface ---
LoadTIF_IO :: proc(src: ^SDL.IOStream) -> ^SDL.Surface ---
LoadXCF_IO :: proc(src: ^SDL.IOStream) -> ^SDL.Surface ---
LoadXPM_IO :: proc(src: ^SDL.IOStream) -> ^SDL.Surface ---
LoadXV_IO :: proc(src: ^SDL.IOStream) -> ^SDL.Surface ---
LoadWEBP_IO :: proc(src: ^SDL.IOStream) -> ^SDL.Surface ---
LoadSizedSVG_IO :: proc(src: ^SDL.IOStream, width, height: c.int) -> ^SDL.Surface ---
ReadXPMFromArray :: proc(xpm: [^]cstring) -> ^SDL.Surface ---
ReadXPMFromArrayToRGB888 :: proc(xpm: [^]cstring) -> ^SDL.Surface ---
/* Individual saving functions */
SaveAVIF :: proc(surface: ^SDL.Surface, file: cstring, quality: c.int) -> c.int ---
SaveAVIF_IO :: proc(surface: ^SDL.Surface, dst: ^SDL.IOStream, closeio: bool, quality: c.int) -> c.int ---
SavePNG :: proc(surface: ^SDL.Surface, file: cstring) -> c.int ---
SavePNG_IO :: proc(surface: ^SDL.Surface, dst: ^SDL.IOStream, closeio: bool) -> c.int ---
SaveJPG :: proc(surface: ^SDL.Surface, file: cstring, quality: c.int) -> c.int ---
SaveJPG_IO :: proc(surface: ^SDL.Surface, dst: ^SDL.IOStream, closeio: bool, quality: c.int) -> c.int ---
LoadAnimation :: proc(file: cstring) -> ^Animation ---
LoadAnimation_IO :: proc(src: ^SDL.IOStream, closeio: bool) -> ^Animation ---
LoadAnimationTyped_IO :: proc(src: ^SDL.IOStream, closeio: bool, type: cstring) -> ^Animation ---
FreeAnimation :: proc(anim: ^Animation) ---
/* Individual loading functions */
LoadGIFAnimation_IO :: proc(src: ^SDL.IOStream) -> ^Animation ---
LoadWEBPAnimation_IO :: proc(src: ^SDL.IOStream) -> ^Animation ---
}
+1 -1
View File
@@ -52,7 +52,7 @@ foreign lib {
TextInputActive :: proc(window: ^Window) -> bool ---
StopTextInput :: proc(window: ^Window) -> bool ---
ClearComposition :: proc(window: ^Window) -> bool ---
SetTextInputArea :: proc(window: ^Window, #by_ptr rect: Rect, cursor: c.int) -> bool ---
SetTextInputArea :: proc(window: ^Window, rect: Maybe(^Rect), cursor: c.int) -> bool ---
GetTextInputArea :: proc(window: ^Window, rect: ^Rect, cursor: ^c.int) -> bool ---
HasScreenKeyboardSupport :: proc() -> bool ---
ScreenKeyboardShown :: proc(window: ^Window) -> bool ---
+4 -4
View File
@@ -183,10 +183,10 @@ foreign lib {
RenderCoordinatesFromWindow :: proc(renderer: ^Renderer, window_x, window_y: f32, x, y: ^f32) -> bool ---
RenderCoordinatesToWindow :: proc(renderer: ^Renderer, x, y: f32, window_x, window_y: ^f32) -> bool ---
ConvertEventToRenderCoordinates :: proc(renderer: ^Renderer, event: ^Event) -> bool ---
SetRenderViewport :: proc(renderer: ^Renderer, #by_ptr rect: Rect) -> bool ---
SetRenderViewport :: proc(renderer: ^Renderer, rect: Maybe(^Rect)) -> bool ---
GetRenderViewport :: proc(renderer: ^Renderer, rect: ^Rect) -> bool ---
GetRenderSafeArea :: proc(renderer: ^Renderer, rect: ^Rect) -> bool ---
SetRenderClipRect :: proc(renderer: ^Renderer, #by_ptr rect: Rect) -> bool ---
SetRenderClipRect :: proc(renderer: ^Renderer, rect: Maybe(^Rect)) -> bool ---
GetRenderClipRect :: proc(renderer: ^Renderer, rect: ^Rect) -> bool ---
SetRenderScale :: proc(renderer: ^Renderer, scaleX, scaleY: f32) -> bool ---
GetRenderScale :: proc(renderer: ^Renderer, scaleX, scaleY: ^f32) -> bool ---
@@ -203,9 +203,9 @@ foreign lib {
RenderPoints :: proc(renderer: ^Renderer, points: [^]FPoint, count: c.int) -> bool ---
RenderLine :: proc(renderer: ^Renderer, x1, y1, x2, y2: f32) -> bool ---
RenderLines :: proc(renderer: ^Renderer, points: [^]FPoint, count: c.int) -> bool ---
RenderRect :: proc(renderer: ^Renderer, #by_ptr rect: FRect) -> bool ---
RenderRect :: proc(renderer: ^Renderer, rect: Maybe(^FRect)) -> bool ---
RenderRects :: proc(renderer: ^Renderer, rects: [^]FRect, count: c.int) -> bool ---
RenderFillRect :: proc(renderer: ^Renderer, #by_ptr rect: FRect) -> bool ---
RenderFillRect :: proc(renderer: ^Renderer, rect: Maybe(^FRect)) -> bool ---
RenderFillRects :: proc(renderer: ^Renderer, rects: [^]FRect, count: c.int) -> bool ---
RenderTexture :: proc(renderer: ^Renderer, texture: ^Texture, srcrect, dstrect: Maybe(^FRect)) -> bool ---
RenderTextureRotated :: proc(renderer: ^Renderer, texture: ^Texture, srcrect, dstrect: Maybe(^FRect), angle: f64, #by_ptr center: FPoint, flip: FlipMode) -> bool ---
+9 -9
View File
@@ -78,7 +78,7 @@ foreign lib {
GetSurfaceAlphaMod :: proc(surface: ^Surface, alpha: ^Uint8) -> bool ---
SetSurfaceBlendMode :: proc(surface: ^Surface, blendMode: BlendMode) -> bool ---
GetSurfaceBlendMode :: proc(surface: ^Surface, blendMode: ^BlendMode) -> bool ---
SetSurfaceClipRect :: proc(surface: ^Surface, #by_ptr rect: Rect) -> bool ---
SetSurfaceClipRect :: proc(surface: ^Surface, rect: Maybe(^Rect)) -> bool ---
GetSurfaceClipRect :: proc(surface: ^Surface, rect: ^Rect) -> bool ---
FlipSurface :: proc(surface: ^Surface, flip: FlipMode) -> bool ---
DuplicateSurface :: proc(surface: ^Surface) -> ^Surface ---
@@ -90,15 +90,15 @@ foreign lib {
PremultiplyAlpha :: proc(width, height: c.int, src_format: PixelFormat, src: rawptr, src_pitch: c.int, dst_format: PixelFormat, dst: rawptr, dst_pitch: c.int, linear: bool) -> bool ---
PremultiplySurfaceAlpha :: proc(surface: ^Surface, linear: bool) -> bool ---
ClearSurface :: proc(surface: ^Surface, r, g, b, a: f32) -> bool ---
FillSurfaceRect :: proc(dst: ^Surface, #by_ptr rect: Rect, color: Uint32) -> bool ---
FillSurfaceRect :: proc(dst: ^Surface, rect: Maybe(^Rect), color: Uint32) -> bool ---
FillSurfaceRects :: proc(dst: ^Surface, rects: [^]Rect, count: c.int, color: Uint32) -> bool ---
BlitSurface :: proc(src: ^Surface, #by_ptr srcrect: Rect, dst: ^Surface, #by_ptr dstrect: Rect) -> bool ---
BlitSurfaceUnchecked :: proc(src: ^Surface, #by_ptr srcrect: Rect, dst: ^Surface, #by_ptr dstrect: Rect) -> bool ---
BlitSurfaceScaled :: proc(src: ^Surface, #by_ptr srcrect: Rect, dst: ^Surface, #by_ptr dstrect: Rect, scaleMode: ScaleMode) -> bool ---
BlitSurfaceUncheckedScaled :: proc(src: ^Surface, #by_ptr srcrect: Rect, dst: ^Surface, #by_ptr dstrect: Rect, scaleMode: ScaleMode) -> bool ---
BlitSurfaceTiled :: proc(src: ^Surface, #by_ptr srcrect: Rect, dst: ^Surface, #by_ptr dstrect: Rect) -> bool ---
BlitSurfaceTiledWithScale :: proc(src: ^Surface, #by_ptr srcrect: Rect, scale: f32, scaleMode: ScaleMode, dst: ^Surface, #by_ptr dstrect: Rect) -> bool ---
BlitSurface9Grid :: proc(src: ^Surface, #by_ptr srcrect: Rect, left_width, right_width, top_height, bottom_height: c.int, scale: f32, scaleMode: ScaleMode, dst: ^Surface, #by_ptr dstrect: Rect) -> bool ---
BlitSurface :: proc(src: ^Surface, srcrect: Maybe(^Rect), dst: ^Surface, dstrect: Maybe(^Rect)) -> bool ---
BlitSurfaceUnchecked :: proc(src: ^Surface, srcrect: Maybe(^Rect), dst: ^Surface, dstrect: Maybe(^Rect)) -> bool ---
BlitSurfaceScaled :: proc(src: ^Surface, srcrect: Maybe(^Rect), dst: ^Surface, dstrect: Maybe(^Rect), scaleMode: ScaleMode) -> bool ---
BlitSurfaceUncheckedScaled :: proc(src: ^Surface, srcrect: Maybe(^Rect), dst: ^Surface, dstrect: Maybe(^Rect), scaleMode: ScaleMode) -> bool ---
BlitSurfaceTiled :: proc(src: ^Surface, srcrect: Maybe(^Rect), dst: ^Surface, dstrect: Maybe(^Rect)) -> bool ---
BlitSurfaceTiledWithScale :: proc(src: ^Surface, srcrect: Maybe(^Rect), scale: f32, scaleMode: ScaleMode, dst: ^Surface, dstrect: Maybe(^Rect)) -> bool ---
BlitSurface9Grid :: proc(src: ^Surface, srcrect: Maybe(^Rect), left_width, right_width, top_height, bottom_height: c.int, scale: f32, scaleMode: ScaleMode, dst: ^Surface, dstrect: Maybe(^Rect)) -> bool ---
MapSurfaceRGB :: proc(surface: ^Surface, r, g, b: Uint8) -> Uint32 ---
MapSurfaceRGBA :: proc(surface: ^Surface, r, g, b, a: Uint8) -> Uint32 ---
ReadSurfacePixel :: proc(surface: ^Surface, x, y: c.int, r, g, b, a: ^Uint8) -> bool ---
+128 -44
View File
@@ -18,6 +18,7 @@ file_and_urls = [
("vulkan_wayland.h", 'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/main/include/vulkan/vulkan_wayland.h', False),
("vulkan_xlib.h", 'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/main/include/vulkan/vulkan_xlib.h', False),
("vulkan_xcb.h", 'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/main/include/vulkan/vulkan_xcb.h', False),
("vulkan_beta.h", 'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/main/include/vulkan/vulkan_beta.h', False),
# Vulkan Video
("vulkan_video_codec_av1std.h", 'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/main/include/vk_video/vulkan_video_codec_av1std.h', False),
("vulkan_video_codec_av1std_decode.h", 'https://raw.githubusercontent.com/KhronosGroup/Vulkan-Headers/main/include/vk_video/vulkan_video_codec_av1std_decode.h', False),
@@ -338,7 +339,6 @@ def parse_enums(f):
generated_flags.add(flags_name)
f.write("{} :: distinct bit_set[{}; Flags]\n".format(flags_name, enum_name))
if is_flag_bit:
f.write("{} :: enum Flags {{\n".format(name.replace("FlagBits", "Flag")))
else:
@@ -537,80 +537,164 @@ def parse_fake_enums(f):
f.write("}\n\n")
class BitfieldError(ValueError):
pass
def bitfield_type_to_size(type_):
if type_ == 'u8':
return 8
if type_ == 'u16':
return 16
if type_ == 'u32':
return 32
if type_ == 'u64':
return 64
if 'Flags' in type_:
return 32
else:
raise BitfieldError(f"Invalid type for bitfield: {type_}")
def bitfield_size_to_type(size):
if size == 8:
return 'u8'
if size == 16:
return 'u16'
if size == 32:
return 'u32'
if size == 64:
return 'u64'
else:
raise BitfieldError(f"Invalid size for bitfield: {size}")
class Bitfield:
class Field:
def __init__(self, name, type_, bitsize):
self.name = name
self.type = type_
self.bitsize = bitsize
def __init__(self, type_):
self.bitsize = bitfield_type_to_size(type_)
self.type = bitfield_size_to_type(self.bitsize)
self.fields_bitsize = 0
self.fields = []
def add_field(self, name, type_, bitsize):
self.fields.append(Bitfield.Field(name, type_, bitsize))
self.fields_bitsize += bitsize
def write(self, f, name=None, indent=0, justify=True):
max_name = 1 if not justify else max([len(f.name) for f in self.fields], default=0)
max_type = 1 if not justify else max([len(f.type) for f in self.fields], default=0)
is_bit_set = all([f.bitsize == 1 or f.name == "reserved" for f in self.fields])
if is_bit_set and name is None:
raise BitfieldError(f"bit_set can not be anonymous")
if is_bit_set:
if not name.endswith("Flags"):
raise BitfieldError(f"bit_set name should end with 'Flags': {name}")
enum_name = re.sub('Flags$', 'Flag', name)
f.write("{}{} :: distinct bit_set[{}; {}]\n".format('\t' * indent, name, enum_name, self.type))
f.write("{}{} :: enum {} {{\n".format('\t' * indent, enum_name, self.type))
for field in self.fields:
if field.name != "reserved":
f.write("{}{},\n".format('\t' * (indent + 1), field.name))
f.write(('\t' * indent) + "}\n")
else:
f.write("{}{} bit_field {} {{\n".format('\t' * indent, name + ' ::' if name else 'using _:', self.type))
for field in self.fields:
type_ = field.type.replace("Flags", "Flag")
f.write("{}{} {} | {},\n".format(
'\t' * (indent + 1),
(field.name + ":").ljust(max_name + 1),
type_.ljust(max_type),
field.bitsize))
f.write(('\t' * indent) + "}" + ("," if name is None else "") + "\n")
def parse_structs(f):
data = re.findall(r"typedef (struct|union) Vk(\w+?) {(.+?)} \w+?;", src, re.S)
data += re.findall(r"typedef (struct|union) Std(\w+?) {(.+?)} \w+?;", src, re.S)
for _type, name, fields in data:
for _type, struct_name, fields in data:
fields = re.findall(r"\s+(.+?)[\s:]+([_a-zA-Z0-9[\]]+);", fields)
f.write("{} :: struct ".format(name))
if _type == "union":
f.write("#raw_union ")
f.write("{\n")
prev_name = ""
ffields = []
bitfield = None
for type_, fname in fields:
# If the field name only has a number in it, then it is a C bit field.
# We will collect all the bit fields and then create either a bit_field or a bit_set.
if is_int(fname):
comment = None
bit_field = type_.split(' ')
bf_field = type_.split(' ')
# Get rid of empty spaces
bit_field = list(filter(bool, bit_field))
bf_field = list(filter(bool, bf_field))
# [type, fieldname]
assert len(bit_field) == 2, "Failed to parse the bit field!"
assert len(bf_field) == 2, "Failed to parse the bit field!"
field_type = do_type(bf_field[0])
bitsize = int(fname)
# Close the set because the field size is greater than the bitfield type
if bitfield and (bitfield.fields_bitsize + bitsize) > bitfield_type_to_size(field_type):
ffields.append(tuple([None, bitfield]))
bitfield = None
bit_field_type = ""
# Right now there are only two ways that C bit fields exist in
# the header files.
# Raise an error if the field type size is greater than the bitfield type size
if bitfield is not None and bitfield_type_to_size(bitfield.type) < bitfield_type_to_size(field_type):
raise BitfieldError(f"field will not fit in the bitfield: {bitfield.type} < {field_type}")
# First one uses the 8 MOST significant bits for one field, and
# 24 bits for the other field.
# In the bindings these two fields are merged into one u32.
if int(fname) == 24:
prev_name = bit_field[1]
continue
elif prev_name:
bit_field_type = do_type("uint32_t")
bit_field_name = prev_name + "And" + bit_field[1].capitalize()
comment = " // Most significant byte is {}".format(bit_field[1])
ffields.append(tuple([bit_field_name, bit_field_type, comment]))
prev_name = ""
continue
# The second way has many fields that are each 1 bit
elif int(fname) == 1:
bit_field_type = do_type(bit_field[0], prev_name, fname)
ffields.append(tuple(["bitfield", bit_field_type, comment]))
break
# Create a new bitfield if we don't have one
if not bitfield:
bitfield = Bitfield(field_type)
# Add the field to the bitfield
bitfield.add_field(bf_field[1], field_type, bitsize)
continue
# Close the bitfield because this is not a field
elif bitfield:
ffields.append(tuple([None, bitfield]))
bitfield = None
if '[' in fname:
fname, type_ = parse_array(fname, type_)
comment = None
n = fix_arg(fname)
if "Flag_Bits" in type_:
comment = " // only single bit set"
# comment = " // only single bit set"
raise BitfieldError("only single bit set")
t = do_type(type_, prev_name, fname)
if n == "matrix":
n = "mat"
ffields.append(tuple([n, t, comment]))
ffields.append(tuple([n, t]))
prev_name = fname
max_len = max([len(n) for n, _, _ in ffields], default=0)
# Close the bitfield because we have no more fields
if bitfield:
ffields.append(tuple([None, bitfield]))
for n, t, comment in ffields:
k = max_len-len(n)+len(t)
f.write("\t{}: {},{}\n".format(n, t.rjust(k), comment or ""))
# Write the struct as a bitfield if it only has bit fields
if len(ffields) == 1 and ffields[0][0] is None:
ffields[0][1].write(f, struct_name, 0, True)
f.write("\n")
f.write("}\n\n")
# Write as a normal struct (or union) if it has other fields
# and inject anonymous bitfields into the struct if there are any
else:
has_anon_bitfield = any(name is None for name, _ in ffields)
max_len = max([0 if n is None else len(n) for n, _ in ffields], default=0)
f.write("{} :: struct ".format(struct_name))
if _type == "union":
f.write("#raw_union ")
f.write("{\n")
for name, type_ in ffields:
if name is None:
# Inject an anonymous bitfield into the struct
type_.write(f, None, indent=1, justify=True)
else:
f.write("\t{} {},\n".format((name + ":").ljust(max_len + 1), type_))
f.write("}\n\n")
f.write("// Opaque structs\n")
f.write(OPAQUE_STRUCTS)

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