mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-17 19:32:23 -07:00
Merge remote-tracking branch 'offical/master'
This commit is contained in:
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 = ---
|
||||
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
@@ -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*.
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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^ = {}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 }
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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])
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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 ---
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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});
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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, ¶ms);
|
||||
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;
|
||||
}
|
||||
@@ -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
@@ -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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -67,7 +67,7 @@ main :: proc() {
|
||||
```
|
||||
|
||||
Writing to memory:
|
||||
```c
|
||||
```odin
|
||||
package main
|
||||
|
||||
import "vendor:cgltf"
|
||||
|
||||
Vendored
+15
-1
@@ -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}}
|
||||
|
||||
Vendored
+27
@@ -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.
|
||||
|
||||
Vendored
+353
@@ -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.
|
||||
Vendored
+23
@@ -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.
|
||||
Vendored
+21
@@ -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.
|
||||
Vendored
+17
@@ -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.
|
||||
Vendored
+30
@@ -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.
|
||||
|
||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
+2110
File diff suppressed because it is too large
Load Diff
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
+107
@@ -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 ---
|
||||
}
|
||||
Vendored
+1
-1
@@ -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 ---
|
||||
|
||||
Vendored
+4
-4
@@ -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 ---
|
||||
|
||||
Vendored
+9
-9
@@ -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
@@ -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
Reference in New Issue
Block a user