Merge branch 'odin-lang:master' into master

This commit is contained in:
ftphikari
2022-09-27 11:06:05 +03:00
committed by GitHub
73 changed files with 1847 additions and 519 deletions

View File

@@ -50,6 +50,7 @@ jobs:
run: |
mkdir dist
cp odin dist
cp libLLVM*.so dist
cp -r shared dist
cp -r core dist
cp -r vendor dist

2
.gitignore vendored
View File

@@ -271,6 +271,7 @@ odin
odin.dSYM
*.bin
demo.bin
libLLVM*.so
# shared collection
shared/
@@ -283,3 +284,4 @@ shared/
*.sublime-workspace
examples/bug/
build.sh
!core/debug/

View File

@@ -99,7 +99,8 @@ config_linux() {
LDFLAGS="$LDFLAGS -ldl"
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs --libfiles) -Wl,-rpath=\$ORIGIN"
cp $($LLVM_CONFIG --libfiles) ./
}
build_odin() {

View File

@@ -49,8 +49,8 @@ foreign libc {
// 7.3.8 Power and absolute-value functions
cabs :: proc(z: complex_double) -> complex_double ---
cabsf :: proc(z: complex_float) -> complex_float ---
cpow :: proc(z: complex_double) -> complex_double ---
cpowf :: proc(z: complex_float) -> complex_float ---
cpow :: proc(x, y: complex_double) -> complex_double ---
cpowf :: proc(x, y: complex_float) -> complex_float ---
csqrt :: proc(z: complex_double) -> complex_double ---
csqrtf :: proc(z: complex_float) -> complex_float ---

View File

@@ -88,7 +88,6 @@ foreign libc {
srand :: proc(seed: uint) ---
// 7.22.3 Memory management functions
aligned_alloc :: proc(aligment, size: size_t) -> rawptr ---
calloc :: proc(nmemb, size: size_t) -> rawptr ---
free :: proc(ptr: rawptr) ---
malloc :: proc(size: size_t) -> rawptr ---
@@ -125,3 +124,30 @@ foreign libc {
mbstowcs :: proc(pwcs: ^wchar_t, s: cstring, n: size_t) -> size_t ---
wcstombs :: proc(s: [^]char, pwcs: ^wchar_t, n: size_t) -> size_t ---
}
aligned_alloc :: #force_inline proc "c" (alignment, size: size_t) -> rawptr {
when ODIN_OS == .Windows {
foreign libc {
_aligned_malloc :: proc(size, alignment: size_t) -> rawptr ---
}
return _aligned_malloc(size=size, alignment=alignment)
} else {
foreign libc {
aligned_alloc :: proc(alignment, size: size_t) -> rawptr ---
}
return aligned_alloc(alignment=alignment, size=size)
}
}
aligned_free :: #force_inline proc "c" (ptr: rawptr) {
when ODIN_OS == .Windows {
foreign libc {
_aligned_free :: proc(ptr: rawptr) ---
}
_aligned_free(ptr)
} else {
free(ptr)
}
}

221
core/debug/pe/pe.odin Normal file
View File

@@ -0,0 +1,221 @@
package debug_pe
PE_SIGNATURE_OFFSET_INDEX_POS :: 0x3c
PE_SIGNATURE :: u32le(0x0000_4550) // "PE\x00\x00"
PE_SIGNATURE_STRING :: "PE\x00\x00"
OPTIONAL_HEADER_MAGIC :: enum u16le {
PE32 = 0x010b,
PE32_PLUS = 0x020b,
}
Optional_Header_Base :: struct #packed {
magic: OPTIONAL_HEADER_MAGIC,
major_linker_version: u8,
minor_linker_version: u8,
size_of_code: u32le,
size_of_initialized_data: u32le,
size_of_uninitialized_data: u32le,
address_of_entry_point: u32le,
base_of_code: u32le,
}
File_Header :: struct #packed {
machine: IMAGE_FILE_MACHINE,
number_of_sections: u16le,
time_date_stamp: u32le,
pointer_to_symbol_table: u32le,
number_of_symbols: u32le,
size_of_optional_header: u16le,
characteristics: IMAGE_FILE_CHARACTERISTICS,
}
Data_Directory :: struct #packed {
virtual_address: u32le,
size: u32le,
}
Optional_Header32 :: struct #packed {
using base: Optional_Header_Base,
base_of_data: u32le,
image_base: u32le,
section_alignment: u32le,
file_alignment: u32le,
major_operating_system_version: u16le,
minor_operating_system_version: u16le,
major_image_version: u16le,
minor_image_version: u16le,
major_subsystem_version: u16le,
minor_subsystem_version: u16le,
win32_version_value: u32le,
size_of_image: u32le,
size_of_headers: u32le,
check_sum: u32le,
subsystem: IMAGE_SUBSYSTEM,
dll_characteristics: IMAGE_DLLCHARACTERISTICS,
size_of_stack_reserve: u32le,
size_of_stack_commit: u32le,
size_of_heap_reserve: u32le,
size_of_heap_commit: u32le,
loader_flags: u32le,
number_of_rva_and_sizes: u32le,
data_directory: [16]Data_Directory,
}
Optional_Header64 :: struct #packed {
using base: Optional_Header_Base,
image_base: u64le,
section_alignment: u32le,
file_alignment: u32le,
major_operating_system_version: u16le,
minor_operating_system_version: u16le,
major_image_version: u16le,
minor_image_version: u16le,
major_subsystem_version: u16le,
minor_subsystem_version: u16le,
win32_version_value: u32le,
size_of_image: u32le,
size_of_headers: u32le,
check_sum: u32le,
subsystem: IMAGE_SUBSYSTEM,
dll_characteristics: IMAGE_DLLCHARACTERISTICS,
size_of_stack_reserve: u64le,
size_of_stack_commit: u64le,
size_of_heap_reserve: u64le,
size_of_heap_commit: u64le,
loader_flags: u32le,
number_of_rva_and_sizes: u32le,
data_directory: [16]Data_Directory,
}
// .debug section
Debug_Directory_Entry :: struct {
characteristics: u32le,
time_date_stamp: u32le,
major_version: u16le,
minor_version: u16le,
type: IMAGE_DEBUG_TYPE,
size_of_data: u32le,
address_of_raw_data: u32le,
pointer_to_raw_data: u32le,
}
IMAGE_FILE_MACHINE :: enum u16le {
UNKNOWN = 0x0,
AM33 = 0x1d3,
AMD64 = 0x8664,
ARM = 0x1c0,
ARMNT = 0x1c4,
ARM64 = 0xaa64,
EBC = 0xebc,
I386 = 0x14c,
IA64 = 0x200,
LOONGARCH32 = 0x6232,
LOONGARCH64 = 0x6264,
M32R = 0x9041,
MIPS16 = 0x266,
MIPSFPU = 0x366,
MIPSFPU16 = 0x466,
POWERPC = 0x1f0,
POWERPCFP = 0x1f1,
R4000 = 0x166,
SH3 = 0x1a2,
SH3DSP = 0x1a3,
SH4 = 0x1a6,
SH5 = 0x1a8,
THUMB = 0x1c2,
WCEMIPSV2 = 0x169,
}
// IMAGE_DIRECTORY_ENTRY constants
IMAGE_DIRECTORY_ENTRY :: enum u8 {
EXPORT = 0,
IMPORT = 1,
RESOURCE = 2,
EXCEPTION = 3,
SECURITY = 4,
BASERELOC = 5,
DEBUG = 6,
ARCHITECTURE = 7, // reserved
GLOBALPTR = 8,
TLS = 9,
LOAD_CONFIG = 10,
BOUND_IMPORT = 11,
IAT = 12,
DELAY_IMPORT = 13,
COM_DESCRIPTOR = 14, // DLR Runtime headers
_RESERVED = 15,
}
#assert(len(IMAGE_DIRECTORY_ENTRY) == 16)
IMAGE_FILE_CHARACTERISTICS :: distinct bit_set[IMAGE_FILE_CHARACTERISTIC; u16le]
IMAGE_FILE_CHARACTERISTIC :: enum u16le {
RELOCS_STRIPPED = 0,
EXECUTABLE_IMAGE = 1,
LINE_NUMS_STRIPPED = 2,
LOCAL_SYMS_STRIPPED = 3,
AGGRESIVE_WS_TRIM = 4,
LARGE_ADDRESS_AWARE = 5,
BYTES_REVERSED_LO = 7,
MACHINE_32BIT = 8, // IMAGE_FILE_32BIT_MACHINE originally
DEBUG_STRIPPED = 9,
REMOVABLE_RUN_FROM_SWAP = 10,
NET_RUN_FROM_SWAP = 11,
SYSTEM = 12,
DLL = 13,
UP_SYSTEM_ONLY = 14,
BYTES_REVERSED_HI = 15,
}
IMAGE_SUBSYSTEM :: enum u16le {
UNKNOWN = 0,
NATIVE = 1,
WINDOWS_GUI = 2,
WINDOWS_CUI = 3,
OS2_CUI = 5,
POSIX_CUI = 7,
NATIVE_WINDOWS = 8,
WINDOWS_CE_GUI = 9,
EFI_APPLICATION = 10,
EFI_BOOT_SERVICE_DRIVER = 11,
EFI_RUNTIME_DRIVER = 12,
EFI_ROM = 13,
XBOX = 14,
WINDOWS_BOOT_APPLICATION = 16,
}
IMAGE_DLLCHARACTERISTICS :: distinct bit_set[IMAGE_DLLCHARACTERISTIC; u16le]
IMAGE_DLLCHARACTERISTIC :: enum u16le {
HIGH_ENTROPY_VA = 5,
DYNAMIC_BASE = 6,
FORCE_INTEGRITY = 7,
NX_COMPAT = 8,
NO_ISOLATION = 9,
NO_SEH = 10,
NO_BIND = 11,
APPCONTAINER = 12,
WDM_DRIVER = 13,
GUARD_CF = 14,
TERMINAL_SERVER_AWARE = 15,
}
IMAGE_DEBUG_TYPE :: enum u32le {
UNKNOWN = 0, // An unknown value that is ignored by all tools.
COFF = 1, // The COFF debug information (line numbers, symbol table, and string table). This type of debug information is also pointed to by fields in the file headers.
CODEVIEW = 2, // The Visual C++ debug information.
FPO = 3, // The frame pointer omission (FPO) information. This information tells the debugger how to interpret nonstandard stack frames, which use the EBP register for a purpose other than as a frame pointer.
MISC = 4, // The location of DBG file.
EXCEPTION = 5, // A copy of .pdata section.
FIXUP = 6, // Reserved.
OMAP_TO_SRC = 7, // The mapping from an RVA in image to an RVA in source image.
OMAP_FROM_SRC = 8, // The mapping from an RVA in source image to an RVA in image.
BORLAND = 9, // Reserved for Borland.
RESERVED10 = 10, // Reserved.
CLSID = 11, // Reserved.
REPRO = 16, // PE determinism or reproducibility.
EX_DLLCHARACTERISTICS = 20, // Extended DLL characteristics bits.
}

131
core/debug/pe/section.odin Normal file
View File

@@ -0,0 +1,131 @@
package debug_pe
import "core:runtime"
import "core:io"
Section_Header32 :: struct {
name: [8]u8,
virtual_size: u32le,
virtual_address: u32le,
size_of_raw_data: u32le,
pointer_to_raw_data: u32le,
pointer_to_relocations: u32le,
pointer_to_line_numbers: u32le,
number_of_relocations: u16le,
number_of_line_numbers: u16le,
characteristics: IMAGE_SCN_CHARACTERISTICS,
}
Reloc :: struct {
virtual_address: u32le,
symbol_table_index: u32le,
type: IMAGE_REL,
}
IMAGE_SCN_CHARACTERISTICS :: enum u32le {
TYPE_NO_PAD = 0x00000008, // The section should not be padded to the next boundary. This flag is obsolete and is replaced by IMAGE_SCN_ALIGN_1BYTES. This is valid only for object files. = 0x00000010, // Reserved for future use.
CNT_CODE = 0x00000020, // The section contains executable code.
CNT_INITIALIZED_DATA = 0x00000040, // The section contains initialized data.
CNT_UNINITIALIZED_DATA = 0x00000080, // The section contains uninitialized data.
LNK_OTHER = 0x00000100, // Reserved for future use.
LNK_INFO = 0x00000200, // The section contains comments or other information. The .drectve section has this type. This is valid for object files only. = 0x00000400, // Reserved for future use.
LNK_REMOVE = 0x00000800, // The section will not become part of the image. This is valid only for object files.
LNK_COMDAT = 0x00001000, // The section contains COMDAT data. For more information, see COMDAT Sections (Object Only). This is valid only for object files.
GPREL = 0x00008000, // The section contains data referenced through the global pointer (GP).
MEM_PURGEABLE = 0x00020000, // Reserved for future use.
MEM_16BIT = 0x00020000, // Reserved for future use.
MEM_LOCKED = 0x00040000, // Reserved for future use.
MEM_PRELOAD = 0x00080000, // Reserved for future use.
ALIGN_1BYTES = 0x00100000, // Align data on a 1-byte boundary. Valid only for object files.
ALIGN_2BYTES = 0x00200000, // Align data on a 2-byte boundary. Valid only for object files.
ALIGN_4BYTES = 0x00300000, // Align data on a 4-byte boundary. Valid only for object files.
ALIGN_8BYTES = 0x00400000, // Align data on an 8-byte boundary. Valid only for object files.
ALIGN_16BYTES = 0x00500000, // Align data on a 16-byte boundary. Valid only for object files.
ALIGN_32BYTES = 0x00600000, // Align data on a 32-byte boundary. Valid only for object files.
ALIGN_64BYTES = 0x00700000, // Align data on a 64-byte boundary. Valid only for object files.
ALIGN_128BYTES = 0x00800000, // Align data on a 128-byte boundary. Valid only for object files.
ALIGN_256BYTES = 0x00900000, // Align data on a 256-byte boundary. Valid only for object files.
ALIGN_512BYTES = 0x00A00000, // Align data on a 512-byte boundary. Valid only for object files.
ALIGN_1024BYTES = 0x00B00000, // Align data on a 1024-byte boundary. Valid only for object files.
ALIGN_2048BYTES = 0x00C00000, // Align data on a 2048-byte boundary. Valid only for object files.
ALIGN_4096BYTES = 0x00D00000, // Align data on a 4096-byte boundary. Valid only for object files.
ALIGN_8192BYTES = 0x00E00000, // Align data on an 8192-byte boundary. Valid only for object files.
LNK_NRELOC_OVFL = 0x01000000, // The section contains extended relocations.
MEM_DISCARDABLE = 0x02000000, // The section can be discarded as needed.
MEM_NOT_CACHED = 0x04000000, // The section cannot be cached.
MEM_NOT_PAGED = 0x08000000, // The section is not pageable.
MEM_SHARED = 0x10000000, // The section can be shared in memory.
MEM_EXECUTE = 0x20000000, // The section can be executed as code.
MEM_READ = 0x40000000, // The section can be read.
MEM_WRITE = 0x80000000, // The section can be written to.
}
IMAGE_REL :: enum u16le {
I386_ABSOLUTE = 0x0000,
I386_DIR16 = 0x0001,
I386_REL16 = 0x0002,
I386_DIR32 = 0x0006,
I386_DIR32NB = 0x0007,
I386_SEG12 = 0x0009,
I386_SECTION = 0x000A,
I386_SECREL = 0x000B,
I386_TOKEN = 0x000C,
I386_SECREL7 = 0x000D,
I386_REL32 = 0x0014,
AMD64_ABSOLUTE = 0x0000,
AMD64_ADDR64 = 0x0001,
AMD64_ADDR32 = 0x0002,
AMD64_ADDR32NB = 0x0003,
AMD64_REL32 = 0x0004,
AMD64_REL32_1 = 0x0005,
AMD64_REL32_2 = 0x0006,
AMD64_REL32_3 = 0x0007,
AMD64_REL32_4 = 0x0008,
AMD64_REL32_5 = 0x0009,
AMD64_SECTION = 0x000A,
AMD64_SECREL = 0x000B,
AMD64_SECREL7 = 0x000C,
AMD64_TOKEN = 0x000D,
AMD64_SREL32 = 0x000E,
AMD64_PAIR = 0x000F,
AMD64_SSPAN32 = 0x0010,
ARM_ABSOLUTE = 0x0000,
ARM_ADDR32 = 0x0001,
ARM_ADDR32NB = 0x0002,
ARM_BRANCH24 = 0x0003,
ARM_BRANCH11 = 0x0004,
ARM_SECTION = 0x000E,
ARM_SECREL = 0x000F,
ARM_MOV32 = 0x0010,
THUMB_MOV32 = 0x0011,
THUMB_BRANCH20 = 0x0012,
THUMB_BRANCH24 = 0x0014,
THUMB_BLX23 = 0x0015,
ARM_PAIR = 0x0016,
ARM64_ABSOLUTE = 0x0000,
ARM64_ADDR32 = 0x0001,
ARM64_ADDR32NB = 0x0002,
ARM64_BRANCH26 = 0x0003,
ARM64_PAGEBASE_REL21 = 0x0004,
ARM64_REL21 = 0x0005,
ARM64_PAGEOFFSET_12A = 0x0006,
ARM64_PAGEOFFSET_12L = 0x0007,
ARM64_SECREL = 0x0008,
ARM64_SECREL_LOW12A = 0x0009,
ARM64_SECREL_HIGH12A = 0x000A,
ARM64_SECREL_LOW12L = 0x000B,
ARM64_TOKEN = 0x000C,
ARM64_SECTION = 0x000D,
ARM64_ADDR64 = 0x000E,
ARM64_BRANCH19 = 0x000F,
ARM64_BRANCH14 = 0x0010,
ARM64_REL32 = 0x0011,
}
PE_CODE_VIEW_SIGNATURE_RSDS :: u32le(0x5344_5352)

108
core/debug/pe/symbol.odin Normal file
View File

@@ -0,0 +1,108 @@
package debug_pe
COFF_SYMBOL_SIZE :: 18
COFF_Symbol :: struct {
name: [8]u8,
value: u32le,
section_number: i16le,
type: IMAGE_SYM_TYPE,
storage_class: IMAGE_SYM_CLASS,
number_of_aux_symbols: u8,
}
// COFF_Symbol_Aux_Format5 describes the expected form of an aux symbol
// attached to a section definition symbol. The PE format defines a
// number of different aux symbol formats: format 1 for function
// definitions, format 2 for .be and .ef symbols, and so on. Format 5
// holds extra info associated with a section definition, including
// number of relocations + line numbers, as well as COMDAT info. See
// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-format-5-section-definitions
// for more on what's going on here.
COFF_Symbol_Aux_Format5 :: struct {
size: u32le,
num_relocs: u16le,
num_line_numbers: u16le,
checksum: u32le,
sec_num: u16le,
selection: IMAGE_COMDAT_SELECT,
_: [3]u8, // padding
}
IMAGE_COMDAT_SELECT :: enum u8 {
NODUPLICATES = 1,
ANY = 2,
SAME_SIZE = 3,
EXACT_MATCH = 4,
ASSOCIATIVE = 5,
LARGEST = 6,
}
// The symbol record is not yet assigned a section. A value of zero indicates
// that a reference to an external symbol is defined elsewhere. A value of
// non-zero is a common symbol with a size that is specified by the value.
IMAGE_SYM_UNDEFINED :: 0
// The symbol has an absolute (non-relocatable) value and is not an address.
IMAGE_SYM_ABSOLUTE :: -1
// The symbol provides general type or debugging information but does not
// correspond to a section. Microsoft tools use this setting along
// with .file records (storage class FILE).
IMAGE_SYM_DEBUG :: -2
IMAGE_SYM_TYPE :: enum u16le {
NULL = 0,
VOID = 1,
CHAR = 2,
SHORT = 3,
INT = 4,
LONG = 5,
FLOAT = 6,
DOUBLE = 7,
STRUCT = 8,
UNION = 9,
ENUM = 10,
MOE = 11,
BYTE = 12,
WORD = 13,
UINT = 14,
DWORD = 15,
PCODE = 32768,
DTYPE_NULL = 0,
DTYPE_POINTER = 0x10,
DTYPE_FUNCTION = 0x20,
DTYPE_ARRAY = 0x30,
}
IMAGE_SYM_CLASS :: enum u8 {
NULL = 0,
AUTOMATIC = 1,
EXTERNAL = 2,
STATIC = 3,
REGISTER = 4,
EXTERNAL_DEF = 5,
LABEL = 6,
UNDEFINED_LABEL = 7,
MEMBER_OF_STRUCT = 8,
ARGUMENT = 9,
STRUCT_TAG = 10,
MEMBER_OF_UNION = 11,
UNION_TAG = 12,
TYPE_DEFINITION = 13,
UNDEFINED_STATIC = 14,
ENUM_TAG = 15,
MEMBER_OF_ENUM = 16,
REGISTER_PARAM = 17,
BIT_FIELD = 18,
FAR_EXTERNAL = 68, // Not in PECOFF v8 spec
BLOCK = 100,
FUNCTION = 101,
END_OF_STRUCT = 102,
FILE = 103,
SECTION = 104,
WEAK_EXTERNAL = 105,
CLR_TOKEN = 107,
END_OF_FUNCTION = 255,
}

View File

@@ -405,7 +405,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
raw_map.entries.allocator = p.allocator
}
header := runtime.__get_map_header_runtime(raw_map, t)
header := runtime.__get_map_header_table_runtime(t)
elem_backing := bytes_make(t.value.size, t.value.align, p.allocator) or_return
defer delete(elem_backing, p.allocator)
@@ -422,19 +422,17 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
delete(key, p.allocator)
return err
}
hash := runtime.Map_Hash {
hash = runtime.default_hasher_string(&key, 0),
key_ptr = &key,
}
key_hash := runtime.default_hasher_string(&key, 0)
key_ptr := rawptr(&key)
key_cstr: cstring
if reflect.is_cstring(t.key) {
key_cstr = cstring(raw_data(key))
hash.key_ptr = &key_cstr
key_ptr = &key_cstr
}
set_ptr := runtime.__dynamic_map_set(header, hash, map_backing_value.data)
set_ptr := runtime.__dynamic_map_set(raw_map, header, key_hash, key_ptr, map_backing_value.data)
if set_ptr == nil {
delete(key, p.allocator)
}

View File

@@ -81,7 +81,7 @@ max_single :: proc(a: $T) -> (out: ELEM_TYPE(T)) where IS_NUMERIC(ELEM_TYPE(T))
} else when N == 2 {
out = builtin.max(a[0], a[1])
} else when N == 3 {
out = builtin.max(a[0], a[1], a[3])
out = builtin.max(a[0], a[1], a[2])
}else {
out = builtin.max(a[0], a[1])
for i in 2..<N {

View File

@@ -120,7 +120,7 @@ read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator)
data = make([]byte, int(length), allocator)
if data == nil {
return nil, false
return nil, false
}
bytes_read, read_err := read_full(fd, data)

View File

@@ -394,7 +394,7 @@ Raw_Dynamic_Array :: struct {
}
Raw_Map :: struct {
hashes: []int,
hashes: []Map_Index,
entries: Raw_Dynamic_Array,
}

View File

@@ -289,14 +289,15 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) {
entries := (^Raw_Dynamic_Array)(&raw_map.entries)
entries.len = 0
for _, i in raw_map.hashes {
raw_map.hashes[i] = -1
raw_map.hashes[i] = MAP_SENTINEL
}
}
@builtin
reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int, loc := #caller_location) {
if m != nil {
__dynamic_map_reserve(__get_map_header(m), capacity, loc)
h := __get_map_header_table(T)
__dynamic_map_reserve(m, h, uint(capacity), loc)
}
}
@@ -325,9 +326,8 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value:
if m != nil {
key := key
h := __get_map_header(m)
hash := __get_map_hash(&key)
fr := __dynamic_map_find(h, hash)
if fr.entry_index >= 0 {
fr := __map_find(h, &key)
if fr.entry_index != MAP_SENTINEL {
entry := __dynamic_map_get_entry(h, fr.entry_index)
deleted_key = (^K)(uintptr(entry)+h.key_offset)^
deleted_value = (^V)(uintptr(entry)+h.value_offset)^
@@ -335,7 +335,6 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value:
__dynamic_map_erase(h, fr)
}
}
return
}
@@ -673,11 +672,10 @@ shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #call
@builtin
map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (ptr: ^V) {
key, value := key, value
h := __get_map_header(m)
hash := __get_map_hash(&key)
data := uintptr(__dynamic_map_set(h, hash, &value, loc))
return (^V)(data + h.value_offset)
h := __get_map_header_table(T)
e := __dynamic_map_set(m, h, __get_map_key_hash(&key), &key, &value, loc)
return (^V)(uintptr(e) + h.value_offset)
}

View File

@@ -59,6 +59,8 @@ __dynamic_array_shrink :: proc(array_: rawptr, elem_size, elem_align: int, new_c
return
}
new_cap := new_cap
new_cap = max(new_cap, 0)
old_size := array.cap * elem_size
new_size := new_cap * elem_size
allocator := array.allocator

View File

@@ -11,38 +11,34 @@ Map_Hash :: struct {
key_ptr: rawptr, // address of Map_Entry_Header.key
}
__get_map_hash :: proc "contextless" (k: ^$K) -> (map_hash: Map_Hash) {
__get_map_key_hash :: #force_inline proc "contextless" (k: ^$K) -> uintptr {
hasher := intrinsics.type_hasher_proc(K)
map_hash.key_ptr = k
map_hash.hash = hasher(k, 0)
return
return hasher(k, 0)
}
__get_map_hash_from_entry :: proc "contextless" (h: Map_Header, entry: ^Map_Entry_Header) -> (hash: Map_Hash) {
hash.hash = entry.hash
hash.key_ptr = rawptr(uintptr(entry) + h.key_offset)
return
__get_map_entry_key_ptr :: #force_inline proc "contextless" (h: Map_Header_Table, entry: ^Map_Entry_Header) -> rawptr {
return rawptr(uintptr(entry) + h.key_offset)
}
Map_Index :: distinct uint
MAP_SENTINEL :: ~Map_Index(0)
Map_Find_Result :: struct {
hash_index: int,
entry_prev: int,
entry_index: int,
hash_index: Map_Index,
entry_prev: Map_Index,
entry_index: Map_Index,
}
Map_Entry_Header :: struct {
hash: uintptr,
next: int,
next: Map_Index,
/*
key: Key_Value,
value: Value_Type,
*/
}
Map_Header :: struct {
m: ^Raw_Map,
Map_Header_Table :: struct {
equal: Equal_Proc,
entry_size: int,
@@ -55,6 +51,102 @@ Map_Header :: struct {
value_size: int,
}
Map_Header :: struct {
m: ^Raw_Map,
using table: Map_Header_Table,
}
// USED INTERNALLY BY THE COMPILER
__dynamic_map_get :: proc "contextless" (m: rawptr, table: Map_Header_Table, key_hash: uintptr, key_ptr: rawptr) -> rawptr {
if m != nil {
h := Map_Header{(^Raw_Map)(m), table}
index := __dynamic_map_find(h, key_hash, key_ptr).entry_index
if index != MAP_SENTINEL {
data := uintptr(__dynamic_map_get_entry(h, index))
return rawptr(data + h.value_offset)
}
}
return nil
}
// USED INTERNALLY BY THE COMPILER
__dynamic_map_set :: proc "odin" (m: rawptr, table: Map_Header_Table, key_hash: uintptr, key_ptr: rawptr, value: rawptr, loc := #caller_location) -> ^Map_Entry_Header #no_bounds_check {
add_entry :: proc "odin" (h: Map_Header, key_hash: uintptr, key_ptr: rawptr, loc := #caller_location) -> Map_Index {
prev := Map_Index(h.m.entries.len)
c := Map_Index(__dynamic_array_append_nothing(&h.m.entries, h.entry_size, h.entry_align, loc))
if c != prev {
end := __dynamic_map_get_entry(h, c-1)
end.hash = key_hash
mem_copy(rawptr(uintptr(end) + h.key_offset), key_ptr, h.key_size)
end.next = MAP_SENTINEL
}
return prev
}
h := Map_Header{(^Raw_Map)(m), table}
index := MAP_SENTINEL
if len(h.m.hashes) == 0 {
__dynamic_map_reserve(m, table, INITIAL_MAP_CAP, loc)
__dynamic_map_grow(h, loc)
}
fr := __dynamic_map_find(h, key_hash, key_ptr)
if fr.entry_index != MAP_SENTINEL {
index = fr.entry_index
} else {
index = add_entry(h, key_hash, key_ptr, loc)
if fr.entry_prev != MAP_SENTINEL {
entry := __dynamic_map_get_entry(h, fr.entry_prev)
entry.next = index
} else if fr.hash_index != MAP_SENTINEL {
h.m.hashes[fr.hash_index] = index
} else {
return nil
}
}
e := __dynamic_map_get_entry(h, index)
e.hash = key_hash
key := rawptr(uintptr(e) + h.key_offset)
val := rawptr(uintptr(e) + h.value_offset)
mem_copy(key, key_ptr, h.key_size)
mem_copy(val, value, h.value_size)
if __dynamic_map_full(h) {
__dynamic_map_grow(h, loc)
}
return __dynamic_map_get_entry(h, index)
}
// USED INTERNALLY BY THE COMPILER
__dynamic_map_reserve :: proc "odin" (m: rawptr, table: Map_Header_Table, cap: uint, loc := #caller_location) {
h := Map_Header{(^Raw_Map)(m), table}
c := context
if h.m.entries.allocator.procedure != nil {
c.allocator = h.m.entries.allocator
}
context = c
cap := cap
cap = ceil_to_pow2(cap)
__dynamic_array_reserve(&h.m.entries, h.entry_size, h.entry_align, int(cap), loc)
if h.m.entries.len*2 < len(h.m.hashes) {
return
}
if __slice_resize(&h.m.hashes, int(cap*2), h.m.entries.allocator, loc) {
__dynamic_map_reset_entries(h, loc)
}
}
INITIAL_HASH_SEED :: 0xcbf29ce484222325
_fnv64a :: proc "contextless" (data: []byte, seed: u64 = INITIAL_HASH_SEED) -> u64 {
@@ -138,11 +230,22 @@ default_hasher_cstring :: proc "contextless" (data: rawptr, seed: uintptr) -> ui
}
__get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header {
header := Map_Header{m = (^Raw_Map)(m)}
__get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> (header: Map_Header) {
header.m = (^Raw_Map)(m)
header.table = #force_inline __get_map_header_table(T)
return
}
__get_map_header_runtime :: proc "contextless" (m: ^Raw_Map, ti: Type_Info_Map) -> (header: Map_Header) {
header.m = m
header.table = #force_inline __get_map_header_table_runtime(ti)
return
}
__get_map_header_table :: proc "contextless" ($T: typeid/map[$K]$V) -> (header: Map_Header_Table) {
Entry :: struct {
hash: uintptr,
next: int,
next: Map_Index,
key: K,
value: V,
}
@@ -158,18 +261,16 @@ __get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header {
header.value_offset = offset_of(Entry, value)
header.value_size = size_of(V)
return header
return
}
__get_map_header_runtime :: proc "contextless" (m: ^Raw_Map, ti: Type_Info_Map) -> Map_Header {
header := Map_Header{m = m}
__get_map_header_table_runtime :: proc "contextless" (ti: Type_Info_Map) -> (header: Map_Header) {
header.equal = ti.key_equal
entries := ti.generated_struct.variant.(Type_Info_Struct).types[1]
entry := entries.variant.(Type_Info_Dynamic_Array).elem
e := entry.variant.(Type_Info_Struct)
header.entry_size = entry.size
header.entry_align = entry.align
@@ -179,11 +280,12 @@ __get_map_header_runtime :: proc "contextless" (m: ^Raw_Map, ti: Type_Info_Map)
header.value_offset = e.offsets[3]
header.value_size = e.types[3].size
return header
return
}
__slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, loc := #caller_location) -> bool {
__slice_resize :: proc "odin" (array_: ^$T/[]$E, new_count: int, allocator: Allocator, loc := #caller_location) -> bool {
array := (^Raw_Slice)(array_)
if new_count < array.len {
@@ -205,136 +307,82 @@ __slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, l
return false
}
__dynamic_map_reset_entries :: proc(using header: Map_Header, loc := #caller_location) {
for i in 0..<len(m.hashes) {
m.hashes[i] = -1
__dynamic_map_reset_entries :: proc "contextless" (h: Map_Header, loc := #caller_location) {
for i in 0..<len(h.m.hashes) {
h.m.hashes[i] = MAP_SENTINEL
}
for i in 0..<m.entries.len {
entry_header := __dynamic_map_get_entry(header, i)
entry_hash := __get_map_hash_from_entry(header, entry_header)
entry_header.next = -1
fr := __dynamic_map_find(header, entry_hash)
if fr.entry_prev < 0 {
m.hashes[fr.hash_index] = i
} else {
e := __dynamic_map_get_entry(header, fr.entry_prev)
for i in 0..<Map_Index(h.m.entries.len) {
entry_header := __dynamic_map_get_entry(h, i)
entry_header.next = MAP_SENTINEL
fr := __dynamic_map_find_from_entry(h, entry_header)
if fr.entry_prev != MAP_SENTINEL {
e := __dynamic_map_get_entry(h, fr.entry_prev)
e.next = i
}
}
}
__dynamic_map_reserve :: proc(using header: Map_Header, cap: int, loc := #caller_location) {
c := context
if m.entries.allocator.procedure != nil {
c.allocator = m.entries.allocator
}
context = c
__dynamic_array_reserve(&m.entries, entry_size, entry_align, cap, loc)
if m.entries.len*2 < len(m.hashes) {
return
}
if __slice_resize(&m.hashes, cap*2, m.entries.allocator, loc) {
__dynamic_map_reset_entries(header, loc)
}
}
__dynamic_map_shrink :: proc(using header: Map_Header, cap: int, loc := #caller_location) -> (did_shrink: bool) {
c := context
if m.entries.allocator.procedure != nil {
c.allocator = m.entries.allocator
}
context = c
return __dynamic_array_shrink(&m.entries, entry_size, entry_align, cap, loc)
}
__dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #caller_location) {
#force_inline __dynamic_map_reserve(header, new_count, loc)
}
__dynamic_map_get :: proc(h: Map_Header, hash: Map_Hash) -> rawptr {
index := __dynamic_map_find(h, hash).entry_index
if index >= 0 {
data := uintptr(__dynamic_map_get_entry(h, index))
return rawptr(data + h.value_offset)
}
return nil
}
__dynamic_map_set :: proc(h: Map_Header, hash: Map_Hash, value: rawptr, loc := #caller_location) -> ^Map_Entry_Header #no_bounds_check {
index: int
// assert(value != nil)
if len(h.m.hashes) == 0 {
__dynamic_map_reserve(h, INITIAL_MAP_CAP, loc)
__dynamic_map_grow(h, loc)
}
fr := __dynamic_map_find(h, hash)
if fr.entry_index >= 0 {
index = fr.entry_index
} else {
index = __dynamic_map_add_entry(h, hash, loc)
if fr.entry_prev >= 0 {
entry := __dynamic_map_get_entry(h, fr.entry_prev)
entry.next = index
} else if fr.hash_index >= 0 {
h.m.hashes[fr.hash_index] = index
} else {
return nil
h.m.hashes[fr.hash_index] = i
}
}
}
e := __dynamic_map_get_entry(h, index)
e.hash = hash.hash
key := rawptr(uintptr(e) + h.key_offset)
mem_copy(key, hash.key_ptr, h.key_size)
val := rawptr(uintptr(e) + h.value_offset)
mem_copy(val, value, h.value_size)
if __dynamic_map_full(h) {
__dynamic_map_grow(h, loc)
// index = __dynamic_map_find(h, hash).entry_index
// assert(index >= 0)
__dynamic_map_shrink :: proc "odin" (h: Map_Header, cap: int, loc := #caller_location) -> (did_shrink: bool) {
c := context
if h.m.entries.allocator.procedure != nil {
c.allocator = h.m.entries.allocator
}
return __dynamic_map_get_entry(h, index)
context = c
return __dynamic_array_shrink(&h.m.entries, h.entry_size, h.entry_align, cap, loc)
}
__dynamic_map_grow :: proc(using h: Map_Header, loc := #caller_location) {
// TODO(bill): Determine an efficient growing rate
new_count := max(4*m.entries.cap + 7, INITIAL_MAP_CAP)
__dynamic_map_rehash(h, new_count, loc)
@(private="file")
ceil_to_pow2 :: proc "contextless" (n: uint) -> uint {
if n <= 2 {
return n
}
n := n
n -= 1
n |= n >> 1
n |= n >> 2
n |= n >> 4
n |= n >> 8
n |= n >> 16
when size_of(int) == 8 {
n |= n >> 32
}
n += 1
return n
}
__dynamic_map_full :: #force_inline proc "contextless" (using h: Map_Header) -> bool {
return int(0.75 * f64(len(m.hashes))) <= m.entries.len
__dynamic_map_grow :: proc "odin" (h: Map_Header, loc := #caller_location) {
new_count := max(uint(h.m.entries.cap) * 2, INITIAL_MAP_CAP)
// Rehash through Reserve
__dynamic_map_reserve(h.m, h.table, new_count, loc)
}
__dynamic_map_hash_equal :: proc "contextless" (h: Map_Header, a, b: Map_Hash) -> bool {
return a.hash == b.hash && h.equal(a.key_ptr, b.key_ptr)
__dynamic_map_full :: #force_inline proc "contextless" (h: Map_Header) -> bool {
return int(0.75 * f64(len(h.m.hashes))) <= h.m.entries.len
}
__dynamic_map_find :: proc(using h: Map_Header, hash: Map_Hash) -> Map_Find_Result #no_bounds_check {
fr := Map_Find_Result{-1, -1, -1}
if n := uintptr(len(m.hashes)); n > 0 {
fr.hash_index = int(hash.hash % n)
fr.entry_index = m.hashes[fr.hash_index]
for fr.entry_index >= 0 {
__dynamic_map_find_from_entry :: proc "contextless" (h: Map_Header, e: ^Map_Entry_Header) -> Map_Find_Result #no_bounds_check {
key_ptr := __get_map_entry_key_ptr(h, e)
return __dynamic_map_find(h, e.hash, key_ptr)
}
__dynamic_map_find :: proc "contextless" (h: Map_Header, key_hash: uintptr, key_ptr: rawptr) -> Map_Find_Result #no_bounds_check {
fr := Map_Find_Result{MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL}
if n := uintptr(len(h.m.hashes)); n != 0 {
fr.hash_index = Map_Index(key_hash & (n-1))
fr.entry_index = h.m.hashes[fr.hash_index]
for fr.entry_index != MAP_SENTINEL {
entry := __dynamic_map_get_entry(h, fr.entry_index)
entry_hash := __get_map_hash_from_entry(h, entry)
if __dynamic_map_hash_equal(h, entry_hash, hash) {
entry_key_ptr := __get_map_entry_key_ptr(h, entry)
if entry.hash == key_hash && h.equal(entry_key_ptr, key_ptr) {
return fr
}
// assert(entry.next < m.entries.len)
fr.entry_prev = fr.entry_index
fr.entry_index = entry.next
@@ -343,58 +391,38 @@ __dynamic_map_find :: proc(using h: Map_Header, hash: Map_Hash) -> Map_Find_Resu
return fr
}
__dynamic_map_add_entry :: proc(using h: Map_Header, hash: Map_Hash, loc := #caller_location) -> int {
prev := m.entries.len
c := __dynamic_array_append_nothing(&m.entries, entry_size, entry_align, loc)
if c != prev {
end := __dynamic_map_get_entry(h, c-1)
end.hash = hash.hash
mem_copy(rawptr(uintptr(end) + key_offset), hash.key_ptr, key_size)
end.next = -1
}
return prev
// Utility procedure used by other runtime procedures
__map_find :: proc "contextless" (h: Map_Header, key_ptr: ^$K) -> Map_Find_Result #no_bounds_check {
hash := __get_map_key_hash(key_ptr)
return #force_inline __dynamic_map_find(h, hash, key_ptr)
}
__dynamic_map_delete_key :: proc(using h: Map_Header, hash: Map_Hash) {
fr := __dynamic_map_find(h, hash)
if fr.entry_index >= 0 {
__dynamic_map_erase(h, fr)
}
__dynamic_map_get_entry :: #force_inline proc "contextless" (h: Map_Header, index: Map_Index) -> ^Map_Entry_Header {
return (^Map_Entry_Header)(uintptr(h.m.entries.data) + uintptr(index*Map_Index(h.entry_size)))
}
__dynamic_map_get_entry :: proc(using h: Map_Header, index: int) -> ^Map_Entry_Header {
// assert(0 <= index && index < m.entries.len)
return (^Map_Entry_Header)(uintptr(m.entries.data) + uintptr(index*entry_size))
}
__dynamic_map_copy_entry :: proc(h: Map_Header, new, old: ^Map_Entry_Header) {
mem_copy(new, old, h.entry_size)
}
__dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds_check {
if fr.entry_prev < 0 {
m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next
} else {
__dynamic_map_erase :: proc "contextless" (h: Map_Header, fr: Map_Find_Result) #no_bounds_check {
if fr.entry_prev != MAP_SENTINEL {
prev := __dynamic_map_get_entry(h, fr.entry_prev)
curr := __dynamic_map_get_entry(h, fr.entry_index)
prev.next = curr.next
}
if fr.entry_index == m.entries.len-1 {
// NOTE(bill): No need to do anything else, just pop
} else {
h.m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next
}
last_index := Map_Index(h.m.entries.len-1)
if fr.entry_index != last_index {
old := __dynamic_map_get_entry(h, fr.entry_index)
end := __dynamic_map_get_entry(h, m.entries.len-1)
__dynamic_map_copy_entry(h, old, end)
end := __dynamic_map_get_entry(h, last_index)
mem_copy(old, end, h.entry_size)
old_hash := __get_map_hash_from_entry(h, old)
if last := __dynamic_map_find(h, old_hash); last.entry_prev >= 0 {
last_entry := __dynamic_map_get_entry(h, last.entry_prev)
last_entry.next = fr.entry_index
last := __dynamic_map_find_from_entry(h, old)
if last.entry_prev != MAP_SENTINEL {
e := __dynamic_map_get_entry(h, last.entry_prev)
e.next = fr.entry_index
} else {
m.hashes[last.hash_index] = fr.entry_index
h.m.hashes[last.hash_index] = fr.entry_index
}
}
m.entries.len -= 1
h.m.entries.len -= 1
}

View File

@@ -4,10 +4,10 @@ import "core:builtin"
import "core:mem"
ptr_add :: proc(p: $P/^$T, x: int) -> ^T {
return (^T)(uintptr(p) + size_of(T)*x)
return ([^]T)(p)[x:]
}
ptr_sub :: proc(p: $P/^$T, x: int) -> ^T {
return #force_inline ptr_add(p, -x)
return ([^]T)(p)[-x:]
}
ptr_swap_non_overlapping :: proc(x, y: rawptr, len: int) {
@@ -84,12 +84,14 @@ ptr_rotate :: proc(left: int, mid: ^$T, right: int) {
}
}
} else {
ptr_swap_non_overlapping(ptr_sub(mid, left), mid, left)
mid = ptr_add(mid, left)
for {
ptr_swap_non_overlapping(ptr_sub(mid, left), mid, left)
mid = ptr_add(mid, left)
right -= left
if right < left {
break
right -= left
if right < left {
break
}
}
}
}

View File

@@ -509,3 +509,10 @@ dot_product :: proc(a, b: $S/[]$T) -> (r: T, ok: bool)
}
return r, true
}
// Convert a pointer to an enumerated array to a slice of the element type
enumerated_array :: proc(ptr: ^$T) -> []intrinsics.type_elem_type(T)
where intrinsics.type_is_enumerated_array(T) {
return ([^]intrinsics.type_elem_type(T))(ptr)[:len(T)]
}

View File

@@ -820,3 +820,147 @@ foreign kernel32 {
HandlerRoutine :: proc "stdcall" (dwCtrlType: DWORD) -> BOOL
PHANDLER_ROUTINE :: HandlerRoutine
DCB_Config :: struct {
fParity: bool,
fOutxCtsFlow: bool,
fOutxDsrFlow: bool,
fDtrControl: DTR_Control,
fDsrSensitivity: bool,
fTXContinueOnXoff: bool,
fOutX: bool,
fInX: bool,
fErrorChar: bool,
fNull: bool,
fRtsControl: RTS_Control,
fAbortOnError: bool,
BaudRate: DWORD,
ByteSize: BYTE,
Parity: Parity,
StopBits: Stop_Bits,
XonChar: byte,
XoffChar: byte,
ErrorChar: byte,
EvtChar: byte,
}
DTR_Control :: enum byte {
Disable = 0,
Enable = 1,
Handshake = 2,
}
RTS_Control :: enum byte {
Disable = 0,
Enable = 1,
Handshake = 2,
Toggle = 3,
}
Parity :: enum byte {
None = 0,
Odd = 1,
Even = 2,
Mark = 3,
Space = 4,
}
Stop_Bits :: enum byte {
One = 0,
One_And_A_Half = 1,
Two = 2,
}
// A helper procedure to set the values of a DCB structure.
init_dcb_with_config :: proc "contextless" (dcb: ^DCB, config: DCB_Config) {
out: u32
// NOTE(tetra, 2022-09-21): On both Clang 14 on Windows, and MSVC, the bits in the bitfield
// appear to be defined from LSB to MSB order.
// i.e: `fBinary` (the first bitfield in the C source) is the LSB in the `settings` u32.
out |= u32(1) << 0 // fBinary must always be true on Windows.
out |= u32(config.fParity) << 1
out |= u32(config.fOutxCtsFlow) << 2
out |= u32(config.fOutxDsrFlow) << 3
out |= u32(config.fDtrControl) << 4
out |= u32(config.fDsrSensitivity) << 6
out |= u32(config.fTXContinueOnXoff) << 7
out |= u32(config.fOutX) << 8
out |= u32(config.fInX) << 9
out |= u32(config.fErrorChar) << 10
out |= u32(config.fNull) << 11
out |= u32(config.fRtsControl) << 12
out |= u32(config.fAbortOnError) << 14
dcb.settings = out
dcb.BaudRate = config.BaudRate
dcb.ByteSize = config.ByteSize
dcb.Parity = config.Parity
dcb.StopBits = config.StopBits
dcb.XonChar = config.XonChar
dcb.XoffChar = config.XoffChar
dcb.ErrorChar = config.ErrorChar
dcb.EvtChar = config.EvtChar
dcb.DCBlength = size_of(DCB)
}
get_dcb_config :: proc "contextless" (dcb: DCB) -> (config: DCB_Config) {
config.fParity = bool((dcb.settings >> 1) & 0x01)
config.fOutxCtsFlow = bool((dcb.settings >> 2) & 0x01)
config.fOutxDsrFlow = bool((dcb.settings >> 3) & 0x01)
config.fDtrControl = DTR_Control((dcb.settings >> 4) & 0x02)
config.fDsrSensitivity = bool((dcb.settings >> 6) & 0x01)
config.fTXContinueOnXoff = bool((dcb.settings >> 7) & 0x01)
config.fOutX = bool((dcb.settings >> 8) & 0x01)
config.fInX = bool((dcb.settings >> 9) & 0x01)
config.fErrorChar = bool((dcb.settings >> 10) & 0x01)
config.fNull = bool((dcb.settings >> 11) & 0x01)
config.fRtsControl = RTS_Control((dcb.settings >> 12) & 0x02)
config.fAbortOnError = bool((dcb.settings >> 14) & 0x01)
config.BaudRate = dcb.BaudRate
config.ByteSize = dcb.ByteSize
config.Parity = dcb.Parity
config.StopBits = dcb.StopBits
config.XonChar = dcb.XonChar
config.XoffChar = dcb.XoffChar
config.ErrorChar = dcb.ErrorChar
config.EvtChar = dcb.EvtChar
return
}
// NOTE(tetra): See get_dcb_config() and init_dcb_with_config() for help with initializing this.
DCB :: struct {
DCBlength: DWORD, // NOTE(tetra): Must be set to size_of(DCB).
BaudRate: DWORD,
settings: u32, // NOTE(tetra): These are bitfields in the C struct.
wReserved: WORD,
XOnLim: WORD,
XOffLim: WORD,
ByteSize: BYTE,
Parity: Parity,
StopBits: Stop_Bits,
XonChar: byte,
XoffChar: byte,
ErrorChar: byte,
EofChar: byte,
EvtChar: byte,
wReserved1: WORD,
}
@(default_calling_convention="stdcall")
foreign kernel32 {
GetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL ---
SetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL ---
}

View File

@@ -3,22 +3,22 @@ package all
// Imports every package
// This is useful for knowing what exists and producing documentation with `odin doc`
import bufio "core:bufio"
import bytes "core:bytes"
import bufio "core:bufio"
import bytes "core:bytes"
import c "core:c"
import libc "core:c/libc"
import c "core:c"
import libc "core:c/libc"
import compress "core:compress"
import shoco "core:compress/shoco"
import gzip "core:compress/gzip"
import zlib "core:compress/zlib"
import compress "core:compress"
import shoco "core:compress/shoco"
import gzip "core:compress/gzip"
import zlib "core:compress/zlib"
import bit_array "core:container/bit_array"
import priority_queue "core:container/priority_queue"
import queue "core:container/queue"
import small_array "core:container/small_array"
import lru "core:container/lru"
import bit_array "core:container/bit_array"
import priority_queue "core:container/priority_queue"
import queue "core:container/queue"
import small_array "core:container/small_array"
import lru "core:container/lru"
import crypto "core:crypto"
import blake "core:crypto/blake"
@@ -27,7 +27,7 @@ import blake2s "core:crypto/blake2s"
import chacha20 "core:crypto/chacha20"
import chacha20poly1305 "core:crypto/chacha20poly1305"
import gost "core:crypto/gost"
import groestl "core:crypto/groestl"
import groestl "core:crypto/groestl"
import haval "core:crypto/haval"
import jh "core:crypto/jh"
import keccak "core:crypto/keccak"
@@ -48,73 +48,74 @@ import crypto_util "core:crypto/util"
import whirlpool "core:crypto/whirlpool"
import x25519 "core:crypto/x25519"
import dynlib "core:dynlib"
import dynlib "core:dynlib"
import base32 "core:encoding/base32"
import base64 "core:encoding/base64"
import csv "core:encoding/csv"
import hxa "core:encoding/hxa"
import json "core:encoding/json"
import varint "core:encoding/varint"
import xml "core:encoding/xml"
import base32 "core:encoding/base32"
import base64 "core:encoding/base64"
import csv "core:encoding/csv"
import hxa "core:encoding/hxa"
import json "core:encoding/json"
import varint "core:encoding/varint"
import xml "core:encoding/xml"
import fmt "core:fmt"
import hash "core:hash"
import fmt "core:fmt"
import hash "core:hash"
import image "core:image"
import netpbm "core:image/netpbm"
import png "core:image/png"
import qoi "core:image/qoi"
import tga "core:image/tga"
import image "core:image"
import netpbm "core:image/netpbm"
import png "core:image/png"
import qoi "core:image/qoi"
import tga "core:image/tga"
import io "core:io"
import log "core:log"
import io "core:io"
import log "core:log"
import math "core:math"
import big "core:math/big"
import bits "core:math/bits"
import fixed "core:math/fixed"
import linalg "core:math/linalg"
import glm "core:math/linalg/glsl"
import hlm "core:math/linalg/hlsl"
import rand "core:math/rand"
import math "core:math"
import big "core:math/big"
import bits "core:math/bits"
import fixed "core:math/fixed"
import linalg "core:math/linalg"
import glm "core:math/linalg/glsl"
import hlm "core:math/linalg/hlsl"
import noise "core:math/noise"
import rand "core:math/rand"
import mem "core:mem"
import mem "core:mem"
// import virtual "core:mem/virtual"
import ast "core:odin/ast"
import doc_format "core:odin/doc-format"
import odin_format "core:odin/format"
import odin_parser "core:odin/parser"
import odin_printer "core:odin/printer"
import odin_tokenizer "core:odin/tokenizer"
import ast "core:odin/ast"
import doc_format "core:odin/doc-format"
import odin_format "core:odin/format"
import odin_parser "core:odin/parser"
import odin_printer "core:odin/printer"
import odin_tokenizer "core:odin/tokenizer"
import os "core:os"
import os "core:os"
import slashpath "core:path/slashpath"
import filepath "core:path/filepath"
import slashpath "core:path/slashpath"
import filepath "core:path/filepath"
import reflect "core:reflect"
import runtime "core:runtime"
import simd "core:simd"
import slice "core:slice"
import slice_heap "core:slice/heap"
import sort "core:sort"
import strconv "core:strconv"
import strings "core:strings"
import sync "core:sync"
import testing "core:testing"
import scanner "core:text/scanner"
import i18n "core:text/i18n"
import thread "core:thread"
import time "core:time"
import reflect "core:reflect"
import runtime "core:runtime"
import simd "core:simd"
import slice "core:slice"
import slice_heap "core:slice/heap"
import sort "core:sort"
import strconv "core:strconv"
import strings "core:strings"
import sync "core:sync"
import testing "core:testing"
import scanner "core:text/scanner"
import i18n "core:text/i18n"
import thread "core:thread"
import time "core:time"
import sysinfo "core:sys/info"
import sysinfo "core:sys/info"
import unicode "core:unicode"
import utf8 "core:unicode/utf8"
import utf8string "core:unicode/utf8/utf8string"
import utf16 "core:unicode/utf16"
import unicode "core:unicode"
import utf8 "core:unicode/utf8"
import utf8string "core:unicode/utf8/utf8string"
import utf16 "core:unicode/utf16"
main :: proc(){}
@@ -183,6 +184,7 @@ _ :: fixed
_ :: linalg
_ :: glm
_ :: hlm
_ :: noise
_ :: rand
_ :: mem
_ :: ast

View File

@@ -1,27 +1,27 @@
package all
import botan "vendor:botan"
import ENet "vendor:ENet"
import ggpo "vendor:ggpo"
import gl "vendor:OpenGL"
import glfw "vendor:glfw"
import microui "vendor:microui"
import miniaudio "vendor:miniaudio"
import PM "vendor:portmidi"
import rl "vendor:raylib"
import exr "vendor:OpenEXRCore"
import botan "vendor:botan"
import ENet "vendor:ENet"
import ggpo "vendor:ggpo"
import gl "vendor:OpenGL"
import glfw "vendor:glfw"
import microui "vendor:microui"
import miniaudio "vendor:miniaudio"
import PM "vendor:portmidi"
import rl "vendor:raylib"
import exr "vendor:OpenEXRCore"
import SDL "vendor:sdl2"
import SDLNet "vendor:sdl2/net"
import IMG "vendor:sdl2/image"
import MIX "vendor:sdl2/mixer"
import TTF "vendor:sdl2/ttf"
import SDL "vendor:sdl2"
import SDLNet "vendor:sdl2/net"
import IMG "vendor:sdl2/image"
import MIX "vendor:sdl2/mixer"
import TTF "vendor:sdl2/ttf"
import vk "vendor:vulkan"
import vk "vendor:vulkan"
import NS "vendor:darwin/Foundation"
import MTL "vendor:darwin/Metal"
import CA "vendor:darwin/QuartzCore"
import NS "vendor:darwin/Foundation"
import MTL "vendor:darwin/Metal"
import CA "vendor:darwin/QuartzCore"
_ :: botan
_ :: ENet
@@ -33,12 +33,15 @@ _ :: miniaudio
_ :: PM
_ :: rl
_ :: exr
_ :: SDL
_ :: SDLNet
_ :: IMG
_ :: MIX
_ :: TTF
_ :: vk
_ :: NS
_ :: MTL
_ :: CA
_ :: CA

View File

@@ -0,0 +1,5 @@
//+build windows, linux
package all
import cm "vendor:commonmark"
_ :: cm

View File

@@ -0,0 +1,5 @@
//+build windows, linux
package all
import zlib "vendor:zlib"
_ :: zlib

View File

@@ -986,6 +986,15 @@ String get_fullpath_relative(gbAllocator a, String base_dir, String path) {
gb_memmove(str+i, path.text, path.len); i += path.len;
str[i] = 0;
// IMPORTANT NOTE(bill): Remove trailing path separators
// this is required to make sure there is a conventional
// notation for the path
for (/**/; i > 0; i--) {
u8 c = str[i-1];
if (c != '/' && c != '\\') {
break;
}
}
String res = make_string(str, i);
res = string_trim_whitespace(res);
@@ -1302,13 +1311,16 @@ void enable_target_feature(TokenPos pos, String const &target_feature_list) {
defer (mutex_unlock(&bc->target_features_mutex));
auto items = split_by_comma(target_feature_list);
array_free(&items);
for_array(i, items) {
String const &item = items.data[i];
if (!check_target_feature_is_valid(pos, item)) {
error(pos, "Target feature '%.*s' is not valid", LIT(item));
continue;
}
string_set_add(&bc->target_features_set, item);
}
array_free(&items);
}
@@ -1331,7 +1343,7 @@ char const *target_features_set_to_cstring(gbAllocator allocator, bool with_quot
if (with_quotes) features[len++] = '"';
String feature = build_context.target_features_set.entries[i].value;
gb_memmove(features, feature.text, feature.len);
gb_memmove(features + len, feature.text, feature.len);
len += feature.len;
if (with_quotes) features[len++] = '"';
}

View File

@@ -1614,6 +1614,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case BuiltinProc_type_info_of:
case BuiltinProc_typeid_of:
case BuiltinProc_len:
case BuiltinProc_cap:
case BuiltinProc_min:
case BuiltinProc_max:
case BuiltinProc_type_is_subtype_of:
@@ -1696,16 +1697,14 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
return check_builtin_procedure_directive(c, operand, call, type_hint);
case BuiltinProc_len:
check_expr_or_type(c, operand, ce->args[0]);
if (operand->mode == Addressing_Invalid) {
return false;
}
/* fallthrough */
case BuiltinProc_cap:
{
// len :: proc(Type) -> int
// cap :: proc(Type) -> int
check_expr_or_type(c, operand, ce->args[0]);
if (operand->mode == Addressing_Invalid) {
return false;
}
Type *op_type = type_deref(operand->type);
Type *type = t_int;
@@ -1749,11 +1748,17 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
mode = Addressing_Value;
} else if (is_type_map(op_type)) {
mode = Addressing_Value;
} else if (operand->mode == Addressing_Type && is_type_enum(op_type) && id == BuiltinProc_len) {
} else if (operand->mode == Addressing_Type && is_type_enum(op_type)) {
Type *bt = base_type(op_type);
mode = Addressing_Constant;
value = exact_value_i64(bt->Enum.fields.count);
type = t_untyped_integer;
mode = Addressing_Constant;
type = t_untyped_integer;
if (id == BuiltinProc_len) {
value = exact_value_i64(bt->Enum.fields.count);
} else {
GB_ASSERT(id == BuiltinProc_cap);
value = exact_value_sub(*bt->Enum.max_value, *bt->Enum.min_value);
value = exact_value_increment_one(value);
}
} else if (is_type_struct(op_type)) {
Type *bt = base_type(op_type);
if (bt->Struct.soa_kind == StructSoa_Fixed) {
@@ -1899,6 +1904,21 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
gb_string_free(t);
return false;
}
Type *bt = base_type(type);
if (bt->kind == Type_Struct && bt->Struct.scope != nullptr) {
if (is_type_polymorphic(bt)) {
gbString t = type_to_string(type);
error(field_arg, "Cannot use '%.*s' on an unspecialized polymorphic struct type, got '%s'", LIT(builtin_name), t);
gb_string_free(t);
return false;
} else if (bt->Struct.fields.count == 0 && bt->Struct.node == nullptr) {
gbString t = type_to_string(type);
error(field_arg, "Cannot use '%.*s' on incomplete struct declaration, got '%s'", LIT(builtin_name), t);
gb_string_free(t);
return false;
}
}
Selection sel = lookup_field(type, field_name, false);
if (sel.entity == nullptr) {
@@ -3665,8 +3685,92 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
gb_string_free(xts);
}
Type *type = default_type(x.type);
operand->mode = Addressing_Value;
operand->type = default_type(x.type);
operand->type = type;
if (id == BuiltinProc_reverse_bits) {
// make runtime only for the time being
} else if (x.mode == Addressing_Constant && x.value.kind == ExactValue_Integer) {
convert_to_typed(c, &x, type);
if (x.mode == Addressing_Invalid) {
return false;
}
ExactValue res = {};
i64 sz = type_size_of(x.type);
u64 bit_size = sz*8;
u64 rop64[4] = {}; // 2 u64 is the maximum we will ever need, so doubling it will ne fine
u8 *rop = cast(u8 *)rop64;
size_t max_count = 0;
size_t written = 0;
size_t size = 1;
size_t nails = 0;
mp_endian endian = MP_LITTLE_ENDIAN;
max_count = mp_pack_count(&x.value.value_integer, nails, size);
GB_ASSERT(sz >= cast(i64)max_count);
mp_err err = mp_pack(rop, max_count, &written, MP_LSB_FIRST, size, endian, nails, &x.value.value_integer);
GB_ASSERT(err == MP_OKAY);
if (id == BuiltinProc_reverse_bits) {
// TODO(bill): Should this even be allowed at compile time?
} else {
u64 v = 0;
switch (id) {
case BuiltinProc_count_ones:
case BuiltinProc_count_zeros:
switch (sz) {
case 1: v = bit_set_count(cast(u32)rop[0]); break;
case 2: v = bit_set_count(cast(u32)*(u16 *)rop); break;
case 4: v = bit_set_count(*(u32 *)rop); break;
case 8: v = bit_set_count(rop64[0]); break;
case 16:
v += bit_set_count(rop64[0]);
v += bit_set_count(rop64[1]);
break;
default: GB_PANIC("Unhandled sized");
}
if (id == BuiltinProc_count_zeros) {
// flip the result
v = bit_size - v;
}
break;
case BuiltinProc_count_trailing_zeros:
for (u64 i = 0; i < bit_size; i++) {
u8 b = cast(u8)(i & 7);
u8 j = cast(u8)(i >> 3);
if (rop[j] & (1 << b)) {
break;
}
v += 1;
}
break;
case BuiltinProc_count_leading_zeros:
for (u64 i = bit_size-1; i < bit_size; i--) {
u8 b = cast(u8)(i & 7);
u8 j = cast(u8)(i >> 3);
if (rop[j] & (1 << b)) {
break;
}
v += 1;
}
break;
}
res = exact_value_u64(v);
}
if (res.kind != ExactValue_Invalid) {
operand->mode = Addressing_Constant;
operand->value = res;
}
}
}
break;

View File

@@ -1060,6 +1060,8 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co
type_extra = gb_string_append_fmt(type_extra, " (package %.*s)", LIT(type_pkg->name));
}
}
ERROR_BLOCK();
error(operand->expr,
"Cannot assign value '%s' of type '%s%s' to '%s%s' in %.*s",
expr_str,
@@ -1143,6 +1145,12 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source,
return true;
}
return is_polymorphic_type_assignable(c, poly->Pointer.elem, source->Pointer.elem, true, modify_type);
} else if (source->kind == Type_MultiPointer) {
isize level = check_is_assignable_to_using_subtype(source->MultiPointer.elem, poly->Pointer.elem);
if (level > 0) {
return true;
}
return is_polymorphic_type_assignable(c, poly->Pointer.elem, source->MultiPointer.elem, true, modify_type);
}
return false;
@@ -1153,6 +1161,12 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source,
return true;
}
return is_polymorphic_type_assignable(c, poly->MultiPointer.elem, source->MultiPointer.elem, true, modify_type);
} else if (source->kind == Type_Pointer) {
isize level = check_is_assignable_to_using_subtype(source->Pointer.elem, poly->MultiPointer.elem);
if (level > 0) {
return true;
}
return is_polymorphic_type_assignable(c, poly->MultiPointer.elem, source->Pointer.elem, true, modify_type);
}
return false;
case Type_Array:
@@ -1348,7 +1362,13 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source,
if (source->kind == Type_Map) {
bool key = is_polymorphic_type_assignable(c, poly->Map.key, source->Map.key, true, modify_type);
bool value = is_polymorphic_type_assignable(c, poly->Map.value, source->Map.value, true, modify_type);
return key || value;
if (key || value) {
poly->Map.entry_type = nullptr;
poly->Map.internal_type = nullptr;
poly->Map.lookup_result_type = nullptr;
init_map_internal_types(poly);
return true;
}
}
return false;
@@ -1965,10 +1985,18 @@ void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type
if (are_types_identical(s, d)) {
error_line("\tSuggestion: the array expression may be sliced with %s[:]\n", a);
}
} else if (are_types_identical(src, dst)) {
} else if (is_type_dynamic_array(src) && is_type_slice(dst)) {
Type *s = src->DynamicArray.elem;
Type *d = dst->Slice.elem;
if (are_types_identical(s, d)) {
error_line("\tSuggestion: the dynamic array expression may be sliced with %s[:]\n", a);
}
}else if (are_types_identical(src, dst) && !are_types_identical(o->type, type)) {
error_line("\tSuggestion: the expression may be directly casted to type %s\n", b);
} else if (are_types_identical(src, t_string) && is_type_u8_slice(dst)) {
error_line("\tSuggestion: a string may be transmuted to %s\n", b);
error_line("\t This is an UNSAFE operation as string data is assumed to be immutable, \n");
error_line("\t whereas slices in general are assumed to be mutable.\n");
} else if (is_type_u8_slice(src) && are_types_identical(dst, t_string)) {
error_line("\tSuggestion: the expression may be casted to %s\n", b);
}
@@ -2028,7 +2056,9 @@ bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) {
gbString a = expr_to_string(o->expr);
gbString b = type_to_string(type);
gbString c = type_to_string(o->type);
gbString s = exact_value_to_string(o->value);
defer(
gb_string_free(s);
gb_string_free(c);
gb_string_free(b);
gb_string_free(a);
@@ -2037,13 +2067,15 @@ bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) {
if (is_type_numeric(o->type) && is_type_numeric(type)) {
if (!is_type_integer(o->type) && is_type_integer(type)) {
error(o->expr, "'%s' truncated to '%s'", a, b);
error(o->expr, "'%s' truncated to '%s', got %s", a, b, s);
} else {
error(o->expr, "Cannot convert numeric value '%s' to '%s' from '%s", a, b, c);
ERROR_BLOCK();
error(o->expr, "Cannot convert numeric value '%s' to '%s' from '%s', got %s", a, b, c, s);
check_assignment_error_suggestion(ctx, o, type);
}
} else {
error(o->expr, "Cannot convert '%s' to '%s' from '%s", a, b, c);
ERROR_BLOCK();
error(o->expr, "Cannot convert '%s' to '%s' from '%s', got %s", a, b, c, s);
check_assignment_error_suggestion(ctx, o, type);
}
return false;
@@ -3904,7 +3936,9 @@ bool check_index_value(CheckerContext *c, Type *main_type, bool open_range, Ast
}
} else if (!is_type_integer(operand.type) && !is_type_enum(operand.type)) {
gbString expr_str = expr_to_string(operand.expr);
error(operand.expr, "Index '%s' must be an integer", expr_str);
gbString type_str = type_to_string(operand.type);
error(operand.expr, "Index '%s' must be an integer, got %s", expr_str, type_str);
gb_string_free(type_str);
gb_string_free(expr_str);
if (value) *value = 0;
return false;
@@ -3914,8 +3948,9 @@ bool check_index_value(CheckerContext *c, Type *main_type, bool open_range, Ast
(c->state_flags & StateFlag_no_bounds_check) == 0) {
BigInt i = exact_value_to_integer(operand.value).value_integer;
if (i.sign && !is_type_enum(index_type) && !is_type_multi_pointer(main_type)) {
String idx_str = big_int_to_string(temporary_allocator(), &i);
gbString expr_str = expr_to_string(operand.expr);
error(operand.expr, "Index '%s' cannot be a negative value", expr_str);
error(operand.expr, "Index '%s' cannot be a negative value, got %.*s", expr_str, LIT(idx_str));
gb_string_free(expr_str);
if (value) *value = 0;
return false;
@@ -3946,7 +3981,7 @@ bool check_index_value(CheckerContext *c, Type *main_type, bool open_range, Ast
if (out_of_bounds) {
gbString expr_str = expr_to_string(operand.expr);
if (lo_str.len > 0) {
error(operand.expr, "Index '%s' is out of bounds range %.*s .. %.*s", expr_str, LIT(lo_str), LIT(hi_str));
error(operand.expr, "Index '%s' is out of bounds range %.*s ..= %.*s", expr_str, LIT(lo_str), LIT(hi_str));
} else {
gbString index_type_str = type_to_string(index_type);
error(operand.expr, "Index '%s' is out of bounds range of enum type %s", expr_str, index_type_str);
@@ -3976,8 +4011,9 @@ bool check_index_value(CheckerContext *c, Type *main_type, bool open_range, Ast
}
if (out_of_bounds) {
String idx_str = big_int_to_string(temporary_allocator(), &i);
gbString expr_str = expr_to_string(operand.expr);
error(operand.expr, "Index '%s' is out of bounds range 0..<%lld", expr_str, max_count);
error(operand.expr, "Index '%s' is out of bounds range 0..<%lld, got %.*s", expr_str, max_count, LIT(idx_str));
gb_string_free(expr_str);
return false;
}
@@ -4022,6 +4058,7 @@ ExactValue get_constant_field_single(CheckerContext *c, ExactValue value, i32 in
if (cl->elems[0]->kind == Ast_FieldValue) {
if (is_type_struct(node->tav.type)) {
bool found = false;
for_array(i, cl->elems) {
Ast *elem = cl->elems[i];
if (elem->kind != Ast_FieldValue) {
@@ -4033,9 +4070,14 @@ ExactValue get_constant_field_single(CheckerContext *c, ExactValue value, i32 in
defer (array_free(&sub_sel.index));
if (sub_sel.index[0] == index) {
value = fv->value->tav.value;
found = true;
break;
}
}
if (!found) {
// Use the zero value if it is not found
value = {};
}
} else if (is_type_array(node->tav.type) || is_type_enumerated_array(node->tav.type)) {
for_array(i, cl->elems) {
Ast *elem = cl->elems[i];
@@ -4677,7 +4719,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
switch (entity->kind) {
case Entity_Constant:
operand->value = entity->Constant.value;
operand->value = entity->Constant.value;
operand->mode = Addressing_Constant;
if (operand->value.kind == ExactValue_Procedure) {
Entity *proc = strip_entity_wrapping(operand->value.value_procedure);
@@ -9064,7 +9106,20 @@ ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_h
o->type = t->RelativeSlice.slice_type;
if (o->mode != Addressing_Variable) {
gbString str = expr_to_string(node);
error(node, "Cannot relative slice '%s', value is not addressable", str);
error(node, "Cannot relative slice '%s', as value is not addressable", str);
gb_string_free(str);
o->mode = Addressing_Invalid;
o->expr = node;
return kind;
}
break;
case Type_EnumeratedArray:
{
gbString str = expr_to_string(o->expr);
gbString type_str = type_to_string(o->type);
error(o->expr, "Cannot slice '%s' of type '%s', as enumerated arrays cannot be sliced", str, type_str);
gb_string_free(type_str);
gb_string_free(str);
o->mode = Addressing_Invalid;
o->expr = node;

View File

@@ -2211,7 +2211,6 @@ void init_map_internal_types(Type *type) {
GB_ASSERT(type->kind == Type_Map);
init_map_entry_type(type);
if (type->Map.internal_type != nullptr) return;
if (type->Map.generated_struct_type != nullptr) return;
Type *key = type->Map.key;
Type *value = type->Map.value;
@@ -2239,7 +2238,6 @@ void init_map_internal_types(Type *type) {
generated_struct_type->Struct.fields = fields;
type_set_offsets(generated_struct_type);
type->Map.generated_struct_type = generated_struct_type;
type->Map.internal_type = generated_struct_type;
type->Map.lookup_result_type = make_optional_ok_type(value);
}

View File

@@ -1922,7 +1922,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
init_map_internal_types(bt);
add_type_info_type_internal(c, bt->Map.key);
add_type_info_type_internal(c, bt->Map.value);
add_type_info_type_internal(c, bt->Map.generated_struct_type);
add_type_info_type_internal(c, bt->Map.internal_type);
break;
case Type_Tuple:
@@ -2144,7 +2144,7 @@ void add_min_dep_type_info(Checker *c, Type *t) {
init_map_internal_types(bt);
add_min_dep_type_info(c, bt->Map.key);
add_min_dep_type_info(c, bt->Map.value);
add_min_dep_type_info(c, bt->Map.generated_struct_type);
add_min_dep_type_info(c, bt->Map.internal_type);
break;
case Type_Tuple:
@@ -2831,23 +2831,12 @@ void init_core_source_code_location(Checker *c) {
}
void init_core_map_type(Checker *c) {
if (t_map_hash == nullptr) {
Entity *e = find_core_entity(c, str_lit("Map_Hash"));
if (e->state == EntityState_Unresolved) {
check_entity_decl(&c->builtin_ctx, e, nullptr, nullptr);
}
t_map_hash = e->type;
GB_ASSERT(t_map_hash != nullptr);
}
if (t_map_header == nullptr) {
Entity *e = find_core_entity(c, str_lit("Map_Header"));
if (e->state == EntityState_Unresolved) {
check_entity_decl(&c->builtin_ctx, e, nullptr, nullptr);
}
t_map_header = e->type;
GB_ASSERT(t_map_header != nullptr);
if (t_map_hash != nullptr) {
return;
}
t_map_hash = find_core_type(c, str_lit("Map_Hash"));
t_map_header = find_core_type(c, str_lit("Map_Header"));
t_map_header_table = find_core_type(c, str_lit("Map_Header_Table"));
}
void init_preload(Checker *c) {

View File

@@ -1,8 +1,5 @@
#include <math.h>
// TODO(bill): Big numbers
// IMPORTANT TODO(bill): This needs to be completely fixed!!!!!!!!
gb_global BlockingMutex hash_exact_value_mutex;
struct Ast;

View File

@@ -500,52 +500,51 @@ lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, A
return value;
}
lbValue lb_gen_map_header(lbProcedure *p, lbValue map_val_ptr, Type *map_type) {
GB_ASSERT_MSG(is_type_pointer(map_val_ptr.type), "%s", type_to_string(map_val_ptr.type));
lbAddr h = lb_add_local_generated(p, t_map_header, false); // all the values will be initialzed later
lbValue lb_gen_map_header_table_internal(lbProcedure *p, Type *map_type) {
lbModule *m = p->module;
map_type = base_type(map_type);
GB_ASSERT(map_type->kind == Type_Map);
Type *key_type = map_type->Map.key;
Type *val_type = map_type->Map.value;
gb_unused(val_type);
lbAddr *found = map_get(&m->map_header_table_map, map_type);
if (found) {
return lb_addr_load(p, *found);
}
GB_ASSERT(map_type->Map.entry_type->kind == Type_Struct);
map_type->Map.entry_type->cached_size = -1;
map_type->Map.entry_type->Struct.are_offsets_set = false;
i64 entry_size = type_size_of (map_type->Map.entry_type);
i64 entry_align = type_align_of (map_type->Map.entry_type);
i64 key_offset = type_offset_of(map_type->Map.entry_type, 2);
i64 key_size = type_size_of (map_type->Map.key);
i64 value_offset = type_offset_of(map_type->Map.entry_type, 3);
i64 value_size = type_size_of (map_type->Map.value);
Type *map_header_base = base_type(t_map_header);
GB_ASSERT(map_header_base->Struct.fields.count == 8);
Type *raw_map_ptr_type = map_header_base->Struct.fields[0]->type;
LLVMValueRef const_values[8] = {};
const_values[0] = LLVMConstNull(lb_type(p->module, raw_map_ptr_type));
const_values[1] = lb_get_equal_proc_for_type(p->module, key_type) .value;
const_values[2] = lb_const_int(p->module, t_int, entry_size) .value;
const_values[3] = lb_const_int(p->module, t_int, entry_align) .value;
const_values[4] = lb_const_int(p->module, t_uintptr, key_offset) .value;
const_values[5] = lb_const_int(p->module, t_int, key_size) .value;
const_values[6] = lb_const_int(p->module, t_uintptr, value_offset).value;
const_values[7] = lb_const_int(p->module, t_int, value_size) .value;
LLVMValueRef const_value = llvm_const_named_struct(p->module, t_map_header, const_values, gb_count_of(const_values));
LLVMBuildStore(p->builder, const_value, h.addr.value);
// NOTE(bill): Removes unnecessary allocation if split gep
lbValue gep0 = lb_emit_struct_ep(p, h.addr, 0);
lbValue m = lb_emit_conv(p, map_val_ptr, type_deref(gep0.type));
lb_emit_store(p, gep0, m);
return lb_addr_load(p, h);
Type *key_type = map_type->Map.key;
Type *val_type = map_type->Map.value;
gb_unused(val_type);
Type *st = base_type(t_map_header_table);
GB_ASSERT(st->Struct.fields.count == 7);
LLVMValueRef const_values[7] = {};
const_values[0] = lb_get_equal_proc_for_type(m, key_type) .value;
const_values[1] = lb_const_int(m, t_int, entry_size) .value;
const_values[2] = lb_const_int(m, t_int, entry_align) .value;
const_values[3] = lb_const_int(m, t_uintptr, key_offset) .value;
const_values[4] = lb_const_int(m, t_int, key_size) .value;
const_values[5] = lb_const_int(m, t_uintptr, value_offset).value;
const_values[6] = lb_const_int(m, t_int, value_size) .value;
LLVMValueRef llvm_res = llvm_const_named_struct(m, t_map_header_table, const_values, gb_count_of(const_values));
lbValue res = {llvm_res, t_map_header_table};
lbAddr addr = lb_add_global_generated(m, t_map_header_table, res, nullptr);
lb_make_global_private_const(addr);
map_set(&m->map_header_table_map, map_type, addr);
return lb_addr_load(p, addr);
}
lbValue lb_const_hash(lbModule *m, lbValue key, Type *key_type) {
@@ -595,14 +594,12 @@ lbValue lb_const_hash(lbModule *m, lbValue key, Type *key_type) {
return hashed_key;
}
lbValue lb_gen_map_hash(lbProcedure *p, lbValue key, Type *key_type) {
lbAddr v = lb_add_local_generated(p, t_map_hash, true);
lbValue vp = lb_addr_get_ptr(p, v);
key = lb_emit_conv(p, key, key_type);
lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue key, Type *key_type, lbValue *key_ptr_) {
lbValue key_ptr = lb_address_from_load_or_generate_local(p, key);
key_ptr = lb_emit_conv(p, key_ptr, t_rawptr);
if (key_ptr_) *key_ptr_ = key_ptr;
lbValue hashed_key = lb_const_hash(p->module, key, key_type);
if (hashed_key.value == nullptr) {
lbValue hasher = lb_get_hasher_proc_for_type(p->module, key_type);
@@ -613,32 +610,62 @@ lbValue lb_gen_map_hash(lbProcedure *p, lbValue key, Type *key_type) {
hashed_key = lb_emit_call(p, hasher, args);
}
lb_emit_store(p, lb_emit_struct_ep(p, vp, 0), hashed_key);
lb_emit_store(p, lb_emit_struct_ep(p, vp, 1), key_ptr);
return lb_addr_load(p, v);
return hashed_key;
}
void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbAddr addr, Type *map_type,
lbValue map_key, lbValue map_value, Ast *node) {
lbValue lb_internal_dynamic_map_get_ptr(lbProcedure *p, lbValue const &map_ptr, lbValue const &key) {
Type *map_type = base_type(type_deref(map_ptr.type));
lbValue key_ptr = {};
auto args = array_make<lbValue>(permanent_allocator(), 4);
args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
args[1] = lb_gen_map_header_table_internal(p, map_type);
args[2] = lb_gen_map_key_hash(p, key, map_type->Map.key, &key_ptr);
args[3] = key_ptr;
lbValue ptr = lb_emit_runtime_call(p, "__dynamic_map_get", args);
return lb_emit_conv(p, ptr, alloc_type_pointer(map_type->Map.value));
}
void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbValue const &map_ptr, Type *map_type,
lbValue const &map_key, lbValue const &map_value, Ast *node) {
map_type = base_type(map_type);
GB_ASSERT(map_type->kind == Type_Map);
lbValue h = lb_gen_map_header(p, addr.addr, map_type);
lbValue key = lb_gen_map_hash(p, map_key, map_type->Map.key);
lbValue key_ptr = {};
lbValue key_hash = lb_gen_map_key_hash(p, map_key, map_type->Map.key, &key_ptr);
lbValue v = lb_emit_conv(p, map_value, map_type->Map.value);
lbAddr value_addr = lb_add_local_generated(p, v.type, false);
lb_addr_store(p, value_addr, v);
auto args = array_make<lbValue>(permanent_allocator(), 4);
args[0] = h;
args[1] = key;
args[2] = lb_emit_conv(p, value_addr.addr, t_rawptr);
args[3] = lb_emit_source_code_location(p, node);
auto args = array_make<lbValue>(permanent_allocator(), 6);
args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
args[1] = lb_gen_map_header_table_internal(p, map_type);
args[2] = key_hash;
args[3] = key_ptr;
args[4] = lb_emit_conv(p, value_addr.addr, t_rawptr);
args[5] = lb_emit_source_code_location(p, node);
lb_emit_runtime_call(p, "__dynamic_map_set", args);
}
void lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const capacity, TokenPos const &pos) {
GB_ASSERT(!build_context.no_dynamic_literals);
String proc_name = {};
if (p->entity) {
proc_name = p->entity->token.string;
}
auto args = array_make<lbValue>(permanent_allocator(), 4);
args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
args[1] = lb_gen_map_header_table_internal(p, type_deref(map_ptr.type));
args[2] = lb_const_int(p->module, t_int, capacity);
args[3] = lb_emit_source_code_location(p, proc_name, pos);
lb_emit_runtime_call(p, "__dynamic_map_reserve", args);
}
struct lbGlobalVariable {
lbValue var;
@@ -780,6 +807,9 @@ lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *start
var->init = init;
} else if (lb_is_const_or_global(init)) {
if (!var->is_initialized) {
if (is_type_proc(init.type)) {
init.value = LLVMConstPointerCast(init.value, lb_type(p->module, init.type));
}
LLVMSetInitializer(var->var.value, init.value);
var->is_initialized = true;
continue;

View File

@@ -159,6 +159,8 @@ struct lbModule {
StringMap<lbAddr> objc_classes;
StringMap<lbAddr> objc_selectors;
PtrMap<Type *, lbAddr> map_header_table_map;
};
struct lbGenerator {
@@ -443,9 +445,11 @@ String lb_get_const_string(lbModule *m, lbValue value);
lbValue lb_generate_local_array(lbProcedure *p, Type *elem_type, i64 count, bool zero_init=true);
lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 count, String prefix, i64 id);
lbValue lb_gen_map_header(lbProcedure *p, lbValue map_val_ptr, Type *map_type);
lbValue lb_gen_map_hash(lbProcedure *p, lbValue key, Type *key_type);
void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbAddr addr, Type *map_type, lbValue map_key, lbValue map_value, Ast *node);
lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue key, Type *key_type, lbValue *key_ptr_);
lbValue lb_internal_dynamic_map_get_ptr(lbProcedure *p, lbValue const &map_ptr, lbValue const &key);
void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbValue const &map_ptr, Type *map_type, lbValue const &map_key, lbValue const &map_value, Ast *node);
void lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const capacity, TokenPos const &pos);
lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e);
lbValue lb_find_value_from_entity(lbModule *m, Entity *e);

View File

@@ -1423,15 +1423,9 @@ lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
switch (rt->kind) {
case Type_Map:
{
lbValue addr = lb_address_from_load_or_generate_local(p, right);
lbValue h = lb_gen_map_header(p, addr, rt);
lbValue key = lb_gen_map_hash(p, left, rt->Map.key);
auto args = array_make<lbValue>(permanent_allocator(), 2);
args[0] = h;
args[1] = key;
lbValue ptr = lb_emit_runtime_call(p, "__dynamic_map_get", args);
lbValue map_ptr = lb_address_from_load_or_generate_local(p, right);
lbValue key = left;
lbValue ptr = lb_internal_dynamic_map_get_ptr(p, map_ptr, key);
if (be->op.kind == Token_in) {
return lb_emit_conv(p, lb_emit_comp_against_nil(p, Token_NotEq, ptr), t_bool);
} else {
@@ -3676,16 +3670,14 @@ lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) {
if (is_type_map(t)) {
lbAddr map_addr = lb_build_addr(p, ie->expr);
lbValue map_val = lb_addr_load(p, map_addr);
if (deref) {
map_val = lb_emit_load(p, map_val);
}
lbValue key = lb_build_expr(p, ie->index);
key = lb_emit_conv(p, key, t->Map.key);
Type *result_type = type_of_expr(expr);
lbValue map_ptr = lb_address_from_load_or_generate_local(p, map_val);
lbValue map_ptr = lb_addr_get_ptr(p, map_addr);
if (is_type_pointer(type_deref(map_ptr.type))) {
map_ptr = lb_emit_load(p, map_ptr);
}
return lb_addr_map(map_ptr, key, t, result_type);
}
@@ -3725,8 +3717,11 @@ lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) {
ExactValue idx = exact_value_sub(index_tv.value, *t->EnumeratedArray.min_value);
index = lb_const_value(p->module, index_type, idx);
} else {
index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int);
index = lb_emit_arith(p, Token_Sub, index, lb_const_value(p->module, index_type, *t->EnumeratedArray.min_value), index_type);
index = lb_emit_arith(p, Token_Sub,
lb_build_expr(p, ie->index),
lb_const_value(p->module, index_type, *t->EnumeratedArray.min_value),
index_type);
index = lb_emit_conv(p, index, t_int);
}
} else {
index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int);
@@ -4136,20 +4131,16 @@ lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
break;
}
GB_ASSERT(!build_context.no_dynamic_literals);
{
auto args = array_make<lbValue>(permanent_allocator(), 3);
args[0] = lb_gen_map_header(p, v.addr, type);
args[1] = lb_const_int(p->module, t_int, 2*cl->elems.count);
args[2] = lb_emit_source_code_location(p, proc_name, pos);
lb_emit_runtime_call(p, "__dynamic_map_reserve", args);
}
lb_dynamic_map_reserve(p, v.addr, 2*cl->elems.count, pos);
for_array(field_index, cl->elems) {
Ast *elem = cl->elems[field_index];
ast_node(fv, FieldValue, elem);
lbValue key = lb_build_expr(p, fv->field);
lbValue value = lb_build_expr(p, fv->value);
lb_insert_dynamic_map_key_and_value(p, v, type, key, value, elem);
lb_insert_dynamic_map_key_and_value(p, v.addr, type, key, value, elem);
}
break;
}

View File

@@ -74,6 +74,9 @@ void lb_init_module(lbModule *m, Checker *c) {
string_map_init(&m->objc_classes, a);
string_map_init(&m->objc_selectors, a);
map_init(&m->map_header_table_map, a, 0);
}
bool lb_init_generator(lbGenerator *gen, Checker *c) {
@@ -213,6 +216,17 @@ void lb_loop_end(lbProcedure *p, lbLoopData const &data) {
}
void lb_make_global_private_const(LLVMValueRef global_data) {
LLVMSetLinkage(global_data, LLVMPrivateLinkage);
LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr);
LLVMSetGlobalConstant(global_data, true);
}
void lb_make_global_private_const(lbAddr const &addr) {
lb_make_global_private_const(addr.addr.value);
}
// This emits a GEP at 0, index
lbValue lb_emit_epi(lbProcedure *p, lbValue const &value, isize index) {
GB_ASSERT(is_type_pointer(value.type));
@@ -390,19 +404,8 @@ lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) {
}
switch (addr.kind) {
case lbAddr_Map: {
Type *map_type = base_type(addr.map.type);
lbValue h = lb_gen_map_header(p, addr.addr, map_type);
lbValue key = lb_gen_map_hash(p, addr.map.key, map_type->Map.key);
auto args = array_make<lbValue>(permanent_allocator(), 2);
args[0] = h;
args[1] = key;
lbValue ptr = lb_emit_runtime_call(p, "__dynamic_map_get", args);
return lb_emit_conv(p, ptr, alloc_type_pointer(map_type->Map.value));
}
case lbAddr_Map:
return lb_internal_dynamic_map_get_ptr(p, addr.addr, addr.map.key);
case lbAddr_RelativePointer: {
Type *rel_ptr = base_type(lb_addr_type(addr));
@@ -711,7 +714,7 @@ void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
return;
} else if (addr.kind == lbAddr_Map) {
lb_insert_dynamic_map_key_and_value(p, addr, addr.map.type, addr.map.key, value, p->curr_stmt);
lb_insert_dynamic_map_key_and_value(p, addr.addr, addr.map.type, addr.map.key, value, p->curr_stmt);
return;
} else if (addr.kind == lbAddr_Context) {
lbAddr old_addr = lb_find_or_generate_context_ptr(p);
@@ -926,19 +929,15 @@ void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) {
return;
} else if (LLVMIsConstant(value.value)) {
lbAddr addr = lb_add_global_generated(p->module, value.type, value, nullptr);
LLVMValueRef global_data = addr.addr.value;
// make it truly private data
LLVMSetLinkage(global_data, LLVMPrivateLinkage);
LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr);
LLVMSetGlobalConstant(global_data, true);
lb_make_global_private_const(addr);
LLVMValueRef dst_ptr = ptr.value;
LLVMValueRef src_ptr = global_data;
LLVMValueRef src_ptr = addr.addr.value;
src_ptr = LLVMBuildPointerCast(p->builder, src_ptr, LLVMTypeOf(dst_ptr), "");
LLVMBuildMemMove(p->builder,
dst_ptr, lb_try_get_alignment(dst_ptr, 1),
src_ptr, lb_try_get_alignment(global_data, 1),
src_ptr, lb_try_get_alignment(src_ptr, 1),
LLVMConstInt(LLVMInt64TypeInContext(p->module->ctx), lb_sizeof(LLVMTypeOf(value.value)), false));
return;
}
@@ -1059,16 +1058,11 @@ lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
} else if (addr.kind == lbAddr_Map) {
Type *map_type = base_type(addr.map.type);
Type *map_type = base_type(type_deref(addr.addr.type));
GB_ASSERT(map_type->kind == Type_Map);
lbAddr v = lb_add_local_generated(p, map_type->Map.lookup_result_type, true);
lbValue h = lb_gen_map_header(p, addr.addr, map_type);
lbValue key = lb_gen_map_hash(p, addr.map.key, map_type->Map.key);
auto args = array_make<lbValue>(permanent_allocator(), 2);
args[0] = h;
args[1] = key;
lbValue ptr = lb_emit_runtime_call(p, "__dynamic_map_get", args);
lbValue ptr = lb_internal_dynamic_map_get_ptr(p, addr.addr, addr.map.key);
lbValue ok = lb_emit_conv(p, lb_emit_comp_against_nil(p, Token_NotEq, ptr), t_bool);
lb_emit_store(p, lb_emit_struct_ep(p, v.addr, 1), ok);
@@ -1513,6 +1507,7 @@ LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *type) {
LLVMTypeRef ret = nullptr;
LLVMTypeRef *params = gb_alloc_array(permanent_allocator(), LLVMTypeRef, param_count);
bool *params_by_ptr = gb_alloc_array(permanent_allocator(), bool, param_count);
if (type->Proc.result_count != 0) {
Type *single_ret = reduce_tuple_to_single_type(type->Proc.results);
ret = lb_type(m, single_ret);
@@ -1538,9 +1533,12 @@ LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *type) {
}
Type *e_type = reduce_tuple_to_single_type(e->type);
bool param_is_by_ptr = false;
LLVMTypeRef param_type = nullptr;
if (e->flags & EntityFlag_ByPtr) {
param_type = lb_type(m, alloc_type_pointer(e_type));
// it will become a pointer afterwards by making it indirect
param_type = lb_type(m, e_type);
param_is_by_ptr = true;
} else if (is_type_boolean(e_type) &&
type_size_of(e_type) <= 1) {
param_type = LLVMInt1TypeInContext(m->ctx);
@@ -1552,6 +1550,7 @@ LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *type) {
}
}
params_by_ptr[param_index] = param_is_by_ptr;
params[param_index++] = param_type;
}
}
@@ -1577,6 +1576,12 @@ LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *type) {
LLVMPrintTypeToString(ft->ret.type),
LLVMGetTypeContext(ft->ret.type), ft->ctx, LLVMGetGlobalContext());
}
for_array(j, ft->args) {
if (params_by_ptr[j]) {
// NOTE(bill): The parameter needs to be passed "indirectly", override it
ft->args[j].kind = lbArg_Indirect;
}
}
map_set(&m->function_type_map, type, ft);
LLVMTypeRef new_abi_fn_type = lb_function_type_to_llvm_raw(ft, type->Proc.c_vararg);
@@ -2473,10 +2478,8 @@ LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String const &str) {
LLVMTypeRef type = LLVMTypeOf(data);
LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name);
LLVMSetInitializer(global_data, data);
LLVMSetLinkage(global_data, LLVMPrivateLinkage);
LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr);
lb_make_global_private_const(global_data);
LLVMSetAlignment(global_data, 1);
LLVMSetGlobalConstant(global_data, true);
LLVMValueRef ptr = LLVMConstInBoundsGEP2(type, global_data, indices, 2);
string_map_set(&m->const_strings, key, ptr);
@@ -2519,10 +2522,8 @@ lbValue lb_find_or_add_entity_string_byte_slice(lbModule *m, String const &str)
LLVMTypeRef type = LLVMTypeOf(data);
LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name);
LLVMSetInitializer(global_data, data);
LLVMSetLinkage(global_data, LLVMPrivateLinkage);
LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr);
lb_make_global_private_const(global_data);
LLVMSetAlignment(global_data, 1);
LLVMSetGlobalConstant(global_data, true);
LLVMValueRef ptr = nullptr;
if (str.len != 0) {
@@ -2558,10 +2559,8 @@ lbValue lb_find_or_add_entity_string_byte_slice_with_type(lbModule *m, String co
LLVMTypeRef type = LLVMTypeOf(data);
LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name);
LLVMSetInitializer(global_data, data);
LLVMSetLinkage(global_data, LLVMPrivateLinkage);
LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr);
lb_make_global_private_const(global_data);
LLVMSetAlignment(global_data, 1);
LLVMSetGlobalConstant(global_data, true);
i64 data_len = str.len;
LLVMValueRef ptr = nullptr;
@@ -2669,6 +2668,7 @@ lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) {
return {};
}
lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value, Entity **entity_) {
GB_ASSERT(type != nullptr);
type = default_type(type);

View File

@@ -121,8 +121,8 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
p->branch_blocks.allocator = a;
p->context_stack.allocator = a;
p->scope_stack.allocator = a;
map_init(&p->selector_values, a, 0);
map_init(&p->selector_addr, a, 0);
map_init(&p->selector_values, a, 0);
map_init(&p->selector_addr, a, 0);
if (p->is_foreign) {
lb_add_foreign_library_path(p->module, entity->Procedure.foreign_library);
@@ -379,7 +379,6 @@ lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type
lb_add_proc_attribute_at_index(p, offset+parameter_index, "nonnull");
lb_add_proc_attribute_at_index(p, offset+parameter_index, "nocapture");
}
return p;
}
@@ -577,20 +576,13 @@ void lb_begin_procedure_body(lbProcedure *p) {
if (e->token.string != "") {
GB_ASSERT(!is_blank_ident(e->token));
lbAddr res = {};
if (return_ptr_value.value != nullptr) {
lbValue ptr = return_ptr_value;
if (results->variables.count != 1) {
ptr = lb_emit_struct_ep(p, ptr, cast(i32)i);
}
res = lb_addr(ptr);
lb_add_entity(p->module, e, ptr);
lb_add_debug_local_variable(p, ptr.value, e->type, e->token);
} else {
res = lb_add_local(p, e->type, e);
}
// NOTE(bill): Don't even bother trying to optimize this with the return ptr value
// This will violate the defer rules if you do:
// foo :: proc() -> (x, y: T) {
// defer x = ... // defer is executed after the `defer`
// return // the values returned should be zeroed
// }
lbAddr res = lb_add_local(p, e->type, e);
if (e->Variable.param_value.kind != ParameterValue_Invalid) {
lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, e->token.pos);
lb_addr_store(p, res, c);
@@ -893,7 +885,7 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args,
GB_ASSERT(param_count-1 <= args.count);
param_count -= 1;
} else {
GB_ASSERT_MSG(param_count == args.count, "%td == %td", param_count, args.count);
GB_ASSERT_MSG(param_count == args.count, "%td == %td (%s)", param_count, args.count, LLVMPrintValueToString(value.value));
}
lbValue result = {};

View File

@@ -1273,6 +1273,7 @@ void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss) {
lbValue parent = lb_build_expr(p, as->rhs[0]);
bool is_parent_ptr = is_type_pointer(parent.type);
Type *parent_base_type = type_deref(parent.type);
TypeSwitchKind switch_kind = check_valid_type_switch_type(parent.type);
GB_ASSERT(switch_kind != TypeSwitch_Invalid);
@@ -1288,8 +1289,11 @@ void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss) {
lbValue union_data = {};
if (switch_kind == TypeSwitch_Union) {
union_data = lb_emit_conv(p, parent_ptr, t_rawptr);
if (is_type_union_maybe_pointer(type_deref(parent_ptr.type))) {
Type *union_type = type_deref(parent_ptr.type);
if (is_type_union_maybe_pointer(union_type)) {
tag = lb_emit_conv(p, lb_emit_comp_against_nil(p, Token_NotEq, union_data), t_int);
} else if (union_tag_size(union_type) == 0) {
tag = {}; // there is no tag for a zero sized union
} else {
lbValue tag_ptr = lb_emit_union_tag_ptr(p, parent_ptr);
tag = lb_emit_load(p, tag_ptr);
@@ -1318,8 +1322,15 @@ void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss) {
}
}
GB_ASSERT(tag.value != nullptr);
LLVMValueRef switch_instr = LLVMBuildSwitch(p->builder, tag.value, else_block->block, cast(unsigned)num_cases);
LLVMValueRef switch_instr = nullptr;
if (type_size_of(parent_base_type) == 0) {
GB_ASSERT(tag.value == nullptr);
switch_instr = LLVMBuildSwitch(p->builder, lb_const_bool(p->module, t_llvm_bool, false).value, else_block->block, cast(unsigned)num_cases);
} else {
GB_ASSERT(tag.value != nullptr);
switch_instr = LLVMBuildSwitch(p->builder, tag.value, else_block->block, cast(unsigned)num_cases);
}
for_array(i, body->stmts) {
Ast *clause = body->stmts[i];

View File

@@ -612,6 +612,8 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
LLVMValueRef value_init = llvm_const_array(lb_type(m, t_type_info_enum_value), value_values, cast(unsigned)fields.count);
LLVMSetInitializer(name_array.value, name_init);
LLVMSetInitializer(value_array.value, value_init);
LLVMSetGlobalConstant(name_array.value, true);
LLVMSetGlobalConstant(value_array.value, true);
lbValue v_count = lb_const_int(m, t_int, fields.count);
@@ -787,7 +789,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_map_ptr);
init_map_internal_types(t);
lbValue gst = lb_type_info(m, t->Map.generated_struct_type);
lbValue gst = lb_type_info(m, t->Map.internal_type);
LLVMValueRef vals[5] = {
lb_type_info(m, t->Map.key).value,

View File

@@ -1130,7 +1130,7 @@ lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) {
case Type_Map:
{
init_map_internal_types(t);
Type *gst = t->Map.generated_struct_type;
Type *gst = t->Map.internal_type;
switch (index) {
case 0: result_type = get_struct_field_type(gst, 0); break;
case 1: result_type = get_struct_field_type(gst, 1); break;

View File

@@ -41,7 +41,6 @@ gbString get_file_line_as_string(TokenPos const &pos, i32 *offset_) {
while (line_end < end) {
if (*line_end == '\n') {
line_end -= 1;
break;
}
line_end += 1;

View File

@@ -227,7 +227,6 @@ struct TypeProc {
Type *key; \
Type *value; \
Type *entry_type; \
Type *generated_struct_type; \
Type *internal_type; \
Type *lookup_result_type; \
}) \
@@ -688,6 +687,7 @@ gb_global Type *t_source_code_location_ptr = nullptr;
gb_global Type *t_map_hash = nullptr;
gb_global Type *t_map_header = nullptr;
gb_global Type *t_map_header_table = nullptr;
gb_global Type *t_equal_proc = nullptr;
@@ -2107,6 +2107,9 @@ bool is_type_polymorphic(Type *t, bool or_specialized=false) {
case Type_Pointer:
return is_type_polymorphic(t->Pointer.elem, or_specialized);
case Type_MultiPointer:
return is_type_polymorphic(t->MultiPointer.elem, or_specialized);
case Type_SoaPointer:
return is_type_polymorphic(t->SoaPointer.elem, or_specialized);
@@ -2130,6 +2133,15 @@ bool is_type_polymorphic(Type *t, bool or_specialized=false) {
case Type_Slice:
return is_type_polymorphic(t->Slice.elem, or_specialized);
case Type_Matrix:
if (t->Matrix.generic_row_count != nullptr) {
return true;
}
if (t->Matrix.generic_column_count != nullptr) {
return true;
}
return is_type_polymorphic(t->Matrix.elem, or_specialized);
case Type_Tuple:
for_array(i, t->Tuple.variables) {
if (is_type_polymorphic(t->Tuple.variables[i]->type, or_specialized)) {
@@ -2196,6 +2208,34 @@ bool is_type_polymorphic(Type *t, bool or_specialized=false) {
}
break;
case Type_BitSet:
if (is_type_polymorphic(t->BitSet.elem, or_specialized)) {
return true;
}
if (t->BitSet.underlying != nullptr &&
is_type_polymorphic(t->BitSet.underlying, or_specialized)) {
return true;
}
break;
case Type_RelativeSlice:
if (is_type_polymorphic(t->RelativeSlice.slice_type, or_specialized)) {
return true;
}
if (t->RelativeSlice.base_integer != nullptr &&
is_type_polymorphic(t->RelativeSlice.base_integer, or_specialized)) {
return true;
}
break;
case Type_RelativePointer:
if (is_type_polymorphic(t->RelativePointer.pointer_type, or_specialized)) {
return true;
}
if (t->RelativePointer.base_integer != nullptr &&
is_type_polymorphic(t->RelativePointer.base_integer, or_specialized)) {
return true;
}
break;
}
return false;

View File

@@ -2,7 +2,7 @@ ODIN=../../odin
PYTHON=$(shell which python3)
all: download_test_assets image_test compress_test strings_test hash_test crypto_test noise_test encoding_test \
math_test linalg_glsl_math_test filepath_test reflect_test os_exit_test i18n_test
math_test linalg_glsl_math_test filepath_test reflect_test os_exit_test i18n_test c_libc_test
download_test_assets:
$(PYTHON) download_assets.py
@@ -47,4 +47,7 @@ os_exit_test:
$(ODIN) run os/test_core_os_exit.odin -file -out:test_core_os_exit && exit 1 || exit 0
i18n_test:
$(ODIN) run text/i18n -out:test_core_i18n
$(ODIN) run text/i18n -out:test_core_i18n
c_libc_test:
$(ODIN) run c/libc -out:test_core_libc

View File

@@ -0,0 +1,37 @@
package test_core_libc
import "core:fmt"
import "core:os"
import "core:strings"
import "core:testing"
TEST_count := 0
TEST_fail := 0
when ODIN_TEST {
expect :: testing.expect
log :: testing.log
} else {
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
TEST_count += 1
if !condition {
TEST_fail += 1
fmt.printf("[%v] %v\n", loc, message)
return
}
}
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
fmt.printf("[%v] ", loc)
fmt.printf("log: %v\n", v)
}
}
main :: proc() {
t := testing.T{}
test_libc_complex(&t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
os.exit(1)
}
}

View File

@@ -0,0 +1,91 @@
package test_core_libc
import "core:testing"
import "core:fmt"
import "core:c/libc"
reldiff :: proc(lhs, rhs: $T) -> f64 {
if lhs == rhs {
return 0.
}
amean := f64((abs(lhs)+abs(rhs)) / 2.)
adiff := f64(abs(lhs - rhs))
out := adiff / amean
return out
}
isclose :: proc(lhs, rhs: $T, rtol:f64 = 1e-12, atol:f64 = 1e-12) -> bool {
adiff := f64(abs(lhs - rhs))
if adiff < atol {
return true
}
rdiff := reldiff(lhs, rhs)
if rdiff < rtol {
return true
}
fmt.printf("not close -- lhs:%v rhs:%v -- adiff:%e rdiff:%e\n",lhs, rhs, adiff, rdiff)
return false
}
// declaring here so they can be used as function pointers
libc_pow :: proc(x, y: libc.complex_double) -> libc.complex_double {
return libc.pow(x,y)
}
libc_powf :: proc(x, y: libc.complex_float) -> libc.complex_float {
return libc.pow(x,y)
}
@test
test_libc_complex :: proc(t: ^testing.T) {
test_libc_pow_binding(t, libc.complex_double, f64, libc_pow, 1e-12, 1e-12)
// f32 needs more atol for comparing values close to zero
test_libc_pow_binding(t, libc.complex_float, f32, libc_powf, 1e-12, 1e-5)
}
@test
test_libc_pow_binding :: proc(t: ^testing.T, $LIBC_COMPLEX:typeid, $F:typeid, pow: proc(LIBC_COMPLEX, LIBC_COMPLEX) -> LIBC_COMPLEX,
rtol: f64, atol: f64) {
// Tests that c/libc/pow(f) functions have two arguments and that the function works as expected for simple inputs
{
// tests 2^n
expected_real : F = 1./16.
expected_imag : F = 0.
complex_base := LIBC_COMPLEX(complex(F(2.), F(0.)))
for n in -4..=4 {
complex_power := LIBC_COMPLEX(complex(F(n), F(0.)))
result := pow(complex_base, complex_power)
expect(t, isclose(expected_real, F(real(result)), rtol, atol), fmt.tprintf("ftype:%T, n:%v reldiff(%v, re(%v)) is greater than specified rtol:%e", F{}, n, expected_real, result, rtol))
expect(t, isclose(expected_imag, F(imag(result)), rtol, atol), fmt.tprintf("ftype:%T, n:%v reldiff(%v, im(%v)) is greater than specified rtol:%e", F{}, n, expected_imag, result, rtol))
expected_real *= 2
}
}
{
// tests (2i)^n
value : F = 1/16.
expected_real, expected_imag : F
complex_base := LIBC_COMPLEX(complex(F(0.), F(2.)))
for n in -4..=4 {
complex_power := LIBC_COMPLEX(complex(F(n), F(0.)))
result := pow(complex_base, complex_power)
switch n%%4 {
case 0:
expected_real = value
expected_imag = 0.
case 1:
expected_real = 0.
expected_imag = value
case 2:
expected_real = -value
expected_imag = 0.
case 3:
expected_real = 0.
expected_imag = -value
}
expect(t, isclose(expected_real, F(real(result)), rtol, atol), fmt.tprintf("ftype:%T, n:%v reldiff(%v, re(%v)) is greater than specified rtol:%e", F{}, n, expected_real, result, rtol))
expect(t, isclose(expected_imag, F(imag(result)), rtol, atol), fmt.tprintf("ftype:%T, n:%v reldiff(%v, im(%v)) is greater than specified rtol:%e", F{}, n, expected_imag, result, rtol))
value *= 2
}
}
}

View File

@@ -1,4 +1,4 @@
package odin_gl
package vendor_gl
GL_DEBUG :: #config(GL_DEBUG, ODIN_DEBUG)

View File

@@ -1,4 +1,4 @@
package odin_gl
package vendor_gl
GL_Enum :: enum u64 {
FALSE = 0,

View File

@@ -1,4 +1,4 @@
package odin_gl
package vendor_gl
// Helper for loading shaders into a program

View File

@@ -1,4 +1,4 @@
package odin_gl
package vendor_gl
loaded_up_to: [2]int
loaded_up_to_major := 0

View File

@@ -1,4 +1,4 @@
package odin_gl
package vendor_gl
#assert(size_of(bool) == size_of(u8))

9
vendor/README.md vendored
View File

@@ -141,4 +141,11 @@ Includes full bindings as well as wrappers to match the `core:crypto` API.
[CMark](https://github.com/commonmark/cmark) CommonMark parsing library.
See also LICENSE in the `commonmark` directory itself.
Includes full bindings and Windows `.lib` and `.dll`.
Includes full bindings and Windows `.lib` and `.dll`.
## CommonMark
[zlib](https://github.com/madler/zlib) data compression library
See also LICENSE in the `zlib` directory itself.
Includes full bindings.

View File

@@ -1,4 +1,4 @@
package botan_bindings
package vendor_botan
/*
Copyright 2021 zhibog

View File

@@ -1,4 +1,4 @@
package botan_blake2b
package vendor_botan_blake2b
/*
Copyright 2021 zhibog

View File

@@ -1,4 +1,4 @@
package gost
package vendor_gost
/*
Copyright 2021 zhibog

View File

@@ -1,4 +1,4 @@
package keccak
package vendor_keccak
/*
Copyright 2021 zhibog

View File

@@ -1,4 +1,4 @@
package md4
package vendor_md4
/*
Copyright 2021 zhibog

View File

@@ -1,4 +1,4 @@
package md5
package vendor_md5
/*
Copyright 2021 zhibog

View File

@@ -1,4 +1,4 @@
package ripemd
package vendor_ripemd
/*
Copyright 2021 zhibog

View File

@@ -1,4 +1,4 @@
package sha1
package vendor_sha1
/*
Copyright 2021 zhibog

View File

@@ -1,4 +1,4 @@
package sha2
package vendor_sha2
/*
Copyright 2021 zhibog

View File

@@ -1,4 +1,4 @@
package sha3
package vendor_sha3
/*
Copyright 2021 zhibog

View File

@@ -1,4 +1,4 @@
package shake
package vendor_shake
/*
Copyright 2021 zhibog

View File

@@ -1,4 +1,4 @@
package siphash
package vendor_siphash
/*
Copyright 2022 zhibog

View File

@@ -1,4 +1,4 @@
package skein512
package vendor_skein512
/*
Copyright 2021 zhibog

View File

@@ -1,4 +1,4 @@
package sm3
package vendor_sm3
/*
Copyright 2021 zhibog

View File

@@ -1,4 +1,4 @@
package streebog
package vendor_streebog
/*
Copyright 2021 zhibog

View File

@@ -1,4 +1,4 @@
package tiger
package vendor_tiger
/*
Copyright 2021 zhibog

View File

@@ -1,4 +1,4 @@
package whirlpool
package vendor_whirlpool
/*
Copyright 2021 zhibog

View File

@@ -4,7 +4,7 @@
Original authors: John MacFarlane, Vicent Marti, Kārlis Gaņģis, Nick Wellnhofer.
See LICENSE for license details.
*/
package commonmark
package vendor_commonmark
import "core:c"
import "core:c/libc"

View File

@@ -5,7 +5,7 @@
Original authors: John MacFarlane, Vicent Marti, Kārlis Gaņģis, Nick Wellnhofer.
See LICENSE for license details.
*/
package commonmark
package vendor_commonmark
/*
Parsing - Simple interface:

View File

@@ -1,4 +1,4 @@
package ggpo
package vendor_ggpo
foreign import lib "GGPO.lib"

View File

@@ -6,7 +6,7 @@ import c "core:c/libc"
when ODIN_OS == .Windows { foreign import stbi "../lib/stb_image.lib" }
when ODIN_OS == .Linux { foreign import stbi "../lib/stb_image.a" }
when ODIN_OS == .Darwin { foreign import stbi "../lib/stb_image.a" }
when ODIN_OS == .Darwin { foreign import stbi "../lib/darwin/stb_image.a" }
#assert(size_of(b32) == size_of(c.int))

View File

@@ -4,7 +4,7 @@ import c "core:c/libc"
when ODIN_OS == .Windows { foreign import lib "../lib/stb_image_resize.lib" }
when ODIN_OS == .Linux { foreign import lib "../lib/stb_image_resize.a" }
when ODIN_OS == .Darwin { foreign import lib "../lib/stb_image_resize.a" }
when ODIN_OS == .Darwin { foreign import lib "../lib/darwin/stb_image_resize.a" }
//////////////////////////////////////////////////////////////////////////////
//

View File

@@ -4,7 +4,7 @@ import c "core:c/libc"
when ODIN_OS == .Windows { foreign import stbiw "../lib/stb_image_write.lib" }
when ODIN_OS == .Linux { foreign import stbiw "../lib/stb_image_write.a" }
when ODIN_OS == .Darwin { foreign import stbiw "../lib/stb_image_write.a" }
when ODIN_OS == .Darwin { foreign import stbiw "../lib/darwin/stb_image_write.a" }
write_func :: proc "c" (ctx: rawptr, data: rawptr, size: c.int)

20
vendor/zlib/LICENSE vendored Normal file
View File

@@ -0,0 +1,20 @@
(C) 1995-2022 Jean-loup Gailly and Mark Adler
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.
Jean-loup Gailly Mark Adler
jloup@gzip.org madler@alumni.caltech.edu

BIN
vendor/zlib/libz.lib vendored Normal file

Binary file not shown.

262
vendor/zlib/zlib.odin vendored Normal file
View File

@@ -0,0 +1,262 @@
package vendor_zlib
import "core:c"
when ODIN_OS == .Windows { foreign import zlib "libz.lib" }
when ODIN_OS == .Linux { foreign import zlib "system:z" }
VERSION :: "1.2.12"
VERNUM :: 0x12c0
VER_MAJOR :: 1
VER_MINOR :: 2
VER_REVISION :: 12
VER_SUBREVISION :: 0
voidp :: rawptr
voidpf :: rawptr
voidpc :: rawptr
Byte :: c.uchar
Bytef :: c.uchar
uInt :: c.uint
uIntf :: c.uint
uLong :: c.ulong
uLongf :: c.ulong
size_t :: c.size_t
off_t :: c.long
off64_t :: i64
crc_t :: u32
alloc_func :: proc "c" (opaque: voidp, items: uInt, size: uInt) -> voidpf
free_func :: proc "c" (opaque: voidp, address: voidpf)
in_func :: proc "c" (rawptr, [^][^]c.uchar) -> c.uint
out_func :: proc "c" (rawptr, [^]c.uchar, c.uint) -> c.int
gzFile_s :: struct {
have: c.uint,
next: [^]c.uchar,
pos: off64_t,
}
gzFile :: ^gzFile_s
z_stream_s :: struct {
next_in: ^Bytef,
avail_in: uInt,
total_in: uLong,
next_out: ^Bytef,
avail_out: uInt,
total_out: uLong,
msg: [^]c.char,
state: rawptr,
zalloc: alloc_func,
zfree: free_func,
opaque: voidpf,
data_type: c.int,
adler: uLong,
reserved: uLong,
}
z_stream :: z_stream_s
z_streamp :: ^z_stream
gz_header_s :: struct {
text: c.int,
time: uLong,
xflags: c.int,
os: c.int,
extra: [^]Bytef,
extra_len: uInt,
extra_max: uInt,
name: [^]Bytef,
name_max: uInt,
comment: [^]Bytef,
comm_max: uInt,
hcrc: c.int,
done: c.int,
}
gz_header :: gz_header_s
gz_headerp :: ^gz_header
// Allowed flush values; see deflate() and inflate() below for details
NO_FLUSH :: 0
PARTIAL_FLUSH :: 1
SYNC_FLUSH :: 2
FULL_FLUSH :: 3
FINISH :: 4
BLOCK :: 5
TREES :: 6
// Return codes for the compression/decompression functions. Negative values are
// errors, positive values are used for special but normal events.
OK :: 0
STREAM_END :: 1
NEED_DICT :: 2
ERRNO :: -1
STREAM_ERROR :: -2
DATA_ERROR :: -3
MEM_ERROR :: -4
BUF_ERROR :: -5
VERSION_ERROR :: -6
// compression levels
NO_COMPRESSION :: 0
BEST_SPEED :: 1
BEST_COMPRESSION :: 9
DEFAULT_COMPRESSION :: -1
// compression strategy; see deflateInit2() below for details
FILTERED :: 1
HUFFMAN_ONLY :: 2
RLE :: 3
FIXED :: 4
DEFAULT_STRATEGY :: 0
// Possible values of the data_type field for deflate()
BINARY :: 0
TEXT :: 1
ASCII :: TEXT // for compatibility with 1.2.2 and earlier
UNKNOWN :: 2
// The deflate compression method (the only one supported in this version)
DEFLATED :: 8
NULL :: 0 // for initializing zalloc, zfree, opaque
version :: Version // for compatibility with versions < 1.0.2
@(default_calling_convention="c")
foreign zlib {
// becase zlib.zlibVersion would be silly to write
@(link_prefix="zlib")
Version :: proc() -> cstring ---
deflate :: proc(strm: z_streamp, flush: c.int) -> c.int ---
deflateEnd :: proc(strm: z_streamp) -> c.int ---
inflate :: proc(strm: z_streamp, flush: c.int) -> c.int ---
inflateEnd :: proc(strm: z_streamp) -> c.int ---
deflateSetDictionary :: proc(strm: z_streamp, dictionary: [^]Bytef, dictLength: uInt) -> c.int ---
deflateGetDictionary :: proc(strm: z_streamp, dictionary: [^]Bytef, dictLength: ^uInt) -> c.int ---
deflateCopy :: proc(dest, source: z_streamp) -> c.int ---
deflateReset :: proc(strm: z_streamp) -> c.int ---
deflateParams :: proc(strm: z_streamp, level, strategy: c.int) -> c.int ---
deflateTune :: proc(strm: z_streamp, good_length, max_lazy, nice_length, max_chain: c.int) -> c.int ---
deflateBound :: proc(strm: z_streamp, sourceLen: uLong) -> uLong ---
deflatePending :: proc(strm: z_streamp, pending: [^]c.uint, bits: [^]c.int) -> c.int ---
deflatePrime :: proc(strm: z_streamp, bits, value: c.int) -> c.int ---
deflateSetHeader :: proc(strm: z_streamp, head: gz_headerp) -> c.int ---
inflateSetDictionary :: proc(strm: z_streamp, dictionary: [^]Bytef, dictLength: uInt) -> c.int ---
inflateGetDictionary :: proc(strm: z_streamp, dictionary: [^]Bytef, dictLength: ^uInt) -> c.int ---
inflateSync :: proc(strm: z_streamp) -> c.int ---
inflateCopy :: proc(dest, source: z_streamp) -> c.int ---
inflateReset :: proc(strm: z_streamp) -> c.int ---
inflateReset2 :: proc(strm: z_streamp, windowBits: c.int) -> c.int ---
inflatePrime :: proc(strm: z_streamp, bits, value: c.int) -> c.int ---
inflateMark :: proc(strm: z_streamp) -> c.long ---
inflateGetHeader :: proc(strm: z_streamp, head: gz_headerp) -> c.int ---
inflateBack :: proc(strm: z_streamp, _in: in_func, in_desc: rawptr, out: out_func, out_desc: rawptr) -> c.int ---
inflateBackEnd :: proc(strm: z_streamp) -> c.int ---
zlibCompileFlags :: proc() -> uLong ---
compress :: proc(dest: [^]Bytef, destLen: ^uLongf, source: [^]Bytef, sourceLen: uLong) -> c.int ---
compress2 :: proc(dest: [^]Bytef, destLen: ^uLongf, source: [^]Bytef, sourceLen: uLong, level: c.int) -> c.int ---
compressBound :: proc(sourceLen: uLong) -> uLong ---
uncompress :: proc(dest: [^]Bytef, destLen: ^uLongf, source: [^]Bytef, sourceLen: uLong) -> c.int ---
uncompress2 :: proc(dest: [^]Bytef, destLen: ^uLongf, source: [^]Bytef, sourceLen: ^uLong) -> c.int ---
gzdopen :: proc(fd: c.int, mode: cstring) -> gzFile ---
gzbuffer :: proc(file: gzFile, size: c.uint) -> c.int ---
gzsetparams :: proc(file: gzFile, level, strategy: c.int) -> c.int ---
gzread :: proc(file: gzFile, buf: voidp, len: c.uint) -> c.int ---
gzfread :: proc(buf: voidp, size, nitems: size_t, file: gzFile) -> size_t ---
gzwrite :: proc(file: gzFile, buf: voidpc, len: c.uint) -> c.int ---
gzfwrite :: proc(buf: voidpc, size, nitems: size_t, file: gzFile) -> size_t ---
gzprintf :: proc(file: gzFile, format: cstring, #c_vararg args: ..any) -> c.int ---
gzputs :: proc(file: gzFile, s: cstring) -> c.int ---
gzgets :: proc(file: gzFile, buf: [^]c.char, len: c.int) -> [^]c.char ---
gzputc :: proc(file: gzFile, ch: c.int) -> c.int ---
gzgetc_ :: proc(file: gzFile) -> c.int --- // backwards compat, not the same as gzget
gzungetc :: proc(ch: c.int, file: gzFile) -> c.int ---
gzflush :: proc(file: gzFile, flush: c.int) -> c.int ---
gzrewind :: proc(file: gzFile) -> c.int ---
gzeof :: proc(file: gzFile) -> c.int ---
gzdirect :: proc(file: gzFile) -> c.int ---
gzclose :: proc(file: gzFile) -> c.int ---
gzclose_r :: proc(file: gzFile) -> c.int ---
gzclose_w :: proc(file: gzFile) -> c.int ---
gzerror :: proc(file: gzFile, errnum: ^c.int) -> cstring ---
gzclearerr :: proc(file: gzFile) ---
adler32 :: proc(adler: uLong, buf: [^]Bytef, len: uInt) -> uLong ---
adler32_z :: proc(adler: uLong, buf: [^]Bytef, len: size_t) -> uLong ---
crc32 :: proc(crc: uLong, buf: [^]Bytef, len: uInt) -> uLong ---
crc32_z :: proc(crc: uLong, buf: [^]Bytef, len: size_t) -> uLong ---
crc32_combine_op :: proc(crc1, crc2, op: uLong) -> uLong ---
gzopen64 :: proc(cstring, cstring) -> gzFile ---
gzseek64 :: proc(gzFile, off64_t, c.int) -> off64_t ---
gztell64 :: proc(gzFile) -> off64_t ---
gzoffset64 :: proc(gzFile) -> off64_t ---
adler32_combine64 :: proc(uLong, uLong, off64_t) -> uLong ---
crc32_combine64 :: proc(uLong, uLong, off64_t) -> uLong ---
crc32_combine_gen64 :: proc(off64_t) -> uLong ---
adler32_combine :: proc(uLong, uLong, off_t) -> uLong ---
crc32_combine :: proc(uLong, uLong, off_t) -> uLong ---
crc32_combine_gen :: proc(off_t) -> uLong ---
zError :: proc(c.int) -> cstring ---
inflateSyncPoint :: proc(z_streamp) -> c.int ---
get_crc_table :: proc() -> [^]crc_t ---
inflateUndermine :: proc(z_streamp, c.int) -> c.int ---
inflateValidate :: proc(z_streamp, c.int) -> c.int ---
inflateCodesUsed :: proc(z_streamp) -> c.ulong ---
inflateResetKeep :: proc(z_streamp) -> c.int ---
deflateResetKeep :: proc(z_streamp) -> c.int ---
}
// Make these private since we create wrappers below passing in version and size
// of the stream structure like zlib.h does
@(private)
@(default_calling_convention="c")
foreign zlib {
deflateInit_ :: proc(strm: z_streamp, level: c.int, version: cstring, stream_size: c.int) -> c.int ---
inflateInit_ :: proc(strm: z_streamp, level: c.int, version: cstring, stream_size: c.int) -> c.int ---
deflateInit2_ :: proc(strm: z_streamp, level, method, windowBits, memLevel, strategy: c.int, version: cstring, stream_size: c.int) -> c.int ---
inflateInit2_ :: proc(strm: z_streamp, windowBits: c.int, version: cstring, stream_size: c.int) -> c.int ---
inflateBackInit_ :: proc(strm: z_streamp, windowBits: c.int, window: [^]c.uchar, version: cstring, stream_size: c.int) -> c.int ---
// see below for explanation
@(link_name="gzgetc")
gzgetc_unique :: proc(file: gzFile) -> c.int ---
}
deflateInit :: #force_inline proc "c" (strm: z_streamp, level: c.int) -> c.int {
return deflateInit_(strm, level, VERSION, c.int(size_of(z_stream)))
}
inflateInit :: #force_inline proc "c" (strm: z_streamp, level: c.int) -> c.int {
return inflateInit_(strm, level, VERSION, c.int(size_of(z_stream)))
}
deflateInit2 :: #force_inline proc "c" (strm: z_streamp, level, method, windowBits, memLevel, strategy: c.int) -> c.int {
return deflateInit2_(strm, level, method, windowBits, memLevel, strategy, VERSION, c.int(size_of(z_stream)))
}
inflateInit2 :: #force_inline proc "c" (strm: z_streamp, windowBits: c.int) -> c.int {
return inflateInit2_(strm, windowBits, VERSION, c.int(size_of(z_stream)))
}
inflateBackInit :: #force_inline proc "c" (strm: z_streamp, windowBits: c.int, window: [^]c.uchar) -> c.int {
return inflateBackInit_(strm, windowBits, window, VERSION, c.int(size_of(z_stream)))
}
// zlib.h redefines gzgetc with a macro and uses (gzgetc)(g) to invoke it from
// inside the same macro (preventing macro expansion), in Odin we give that a
// unique name using link_prefix then implement the body of the macro in our own
// procedure calling the unique named gzgetc instead.
gzgetc :: #force_inline proc(file: gzFile) -> c.int {
if file.have != 0 {
file.have -= 1
file.pos += 1
ch := c.int(file.next[0])
file.next = &file.next[1]
return ch
}
return gzgetc_unique(file)
}