Merge branch 'master' into windows-llvm-13.0.0

This commit is contained in:
gingerBill
2023-04-03 21:12:10 +01:00
102 changed files with 7075 additions and 6085 deletions

View File

@@ -163,6 +163,13 @@ jobs:
cd tests\internal
call build.bat
timeout-minutes: 10
- name: Odin documentation tests
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
cd tests\documentation
call build.bat
timeout-minutes: 10
- name: core:math/big tests
shell: cmd
run: |

View File

@@ -94,7 +94,15 @@ cap :: proc(array: Array_Type) -> int ---
size_of :: proc($T: typeid) -> int ---
align_of :: proc($T: typeid) -> int ---
offset_of :: proc($T: typeid) -> uintptr ---
// e.g. offset_of(t.f), where t is an instance of the type T
offset_of_selector :: proc(selector: $T) -> uintptr ---
// e.g. offset_of(T, f), where T can be the type instead of a variable
offset_of_member :: proc($T: typeid, member: $M) -> uintptr ---
offset_of :: proc{offset_of_selector, offset_of_member}
// e.g. offset_of(T, "f"), where T can be the type instead of a variable
offset_of_by_string :: proc($T: typeid, member: string) -> uintptr ---
type_of :: proc(x: expr) -> type ---
type_info_of :: proc($T: typeid) -> ^runtime.Type_Info ---
typeid_of :: proc($T: typeid) -> typeid ---

View File

@@ -11,6 +11,8 @@ package util
*/
import "core:mem"
// Keep vet happy
_ :: mem
// @note(bp): this can replace the other two
cast_slice :: #force_inline proc "contextless" ($D: typeid/[]$DE, src: $S/[]$SE) -> D {

15
core/dynlib/lib_js.odin Normal file
View File

@@ -0,0 +1,15 @@
//+build js
//+private
package dynlib
_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
return
}
_unload_library :: proc(library: Library) -> bool {
return
}
_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
return
}

View File

@@ -87,7 +87,8 @@ Error :: enum {
destroy_value :: proc(value: Value) {
destroy_value :: proc(value: Value, allocator := context.allocator) {
context.allocator = allocator
#partial switch v in value {
case Object:
for key, elem in v {
@@ -103,5 +104,4 @@ destroy_value :: proc(value: Value) {
case String:
delete(v)
}
}
}

View File

@@ -68,7 +68,7 @@ A period with no following number specifies a precision of 0.
Examples:
%f default width, default precision
%8f width 8, default precision
%.3f default width, precision 2
%.2f default width, precision 2
%8.3f width 8, precision 3
%8.f width 8, precision 0

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,48 @@
package image
import "core:os"
import "core:mem"
import "core:bytes"
Loader_Proc :: #type proc(data: []byte, options: Options, allocator: mem.Allocator) -> (img: ^Image, err: Error)
Destroy_Proc :: #type proc(img: ^Image)
@(private)
_internal_loaders: [Which_File_Type]Loader_Proc
_internal_destroyers: [Which_File_Type]Destroy_Proc
register :: proc(kind: Which_File_Type, loader: Loader_Proc, destroyer: Destroy_Proc) {
assert(loader != nil)
assert(destroyer != nil)
assert(_internal_loaders[kind] == nil)
_internal_loaders[kind] = loader
assert(_internal_destroyers[kind] == nil)
_internal_destroyers[kind] = destroyer
}
load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
loader := _internal_loaders[which(data)]
if loader == nil {
return nil, .Unsupported_Format
}
return loader(data, options, allocator)
}
destroy :: proc(img: ^Image, allocator := context.allocator) {
if img == nil {
return
}
context.allocator = allocator
destroyer := _internal_destroyers[img.which]
if destroyer != nil {
destroyer(img)
} else {
assert(img.metadata == nil)
bytes.buffer_destroy(&img.pixels)
free(img)
}
}
Which_File_Type :: enum {
Unknown,
@@ -28,11 +70,6 @@ Which_File_Type :: enum {
XBM, // X BitMap
}
which :: proc{
which_bytes,
which_file,
}
which_bytes :: proc(data: []byte) -> Which_File_Type {
test_tga :: proc(s: string) -> bool {
get8 :: #force_inline proc(s: ^string) -> u8 {
@@ -164,16 +201,3 @@ which_bytes :: proc(data: []byte) -> Which_File_Type {
}
return .Unknown
}
which_file :: proc(path: string) -> Which_File_Type {
f, err := os.open(path)
if err != 0 {
return .Unknown
}
header: [128]byte
os.read(f, header[:])
file_type := which_bytes(header[:])
os.close(f)
return file_type
}

View File

@@ -0,0 +1,10 @@
//+build js
package image
load :: proc{
load_from_bytes,
}
which :: proc{
which_bytes,
}

View File

@@ -1,61 +0,0 @@
package image
import "core:mem"
import "core:os"
import "core:bytes"
Loader_Proc :: #type proc(data: []byte, options: Options, allocator: mem.Allocator) -> (img: ^Image, err: Error)
Destroy_Proc :: #type proc(img: ^Image)
@(private)
_internal_loaders: [Which_File_Type]Loader_Proc
_internal_destroyers: [Which_File_Type]Destroy_Proc
register :: proc(kind: Which_File_Type, loader: Loader_Proc, destroyer: Destroy_Proc) {
assert(loader != nil)
assert(destroyer != nil)
assert(_internal_loaders[kind] == nil)
_internal_loaders[kind] = loader
assert(_internal_destroyers[kind] == nil)
_internal_destroyers[kind] = destroyer
}
load :: proc{
load_from_bytes,
load_from_file,
}
load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
loader := _internal_loaders[which(data)]
if loader == nil {
return nil, .Unsupported_Format
}
return loader(data, options, allocator)
}
load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
data, ok := os.read_entire_file(filename, allocator)
defer delete(data, allocator)
if ok {
return load_from_bytes(data, options, allocator)
} else {
return nil, .Unable_To_Read_File
}
}
destroy :: proc(img: ^Image, allocator := context.allocator) {
if img == nil {
return
}
context.allocator = allocator
destroyer := _internal_destroyers[img.which]
if destroyer != nil {
destroyer(img)
} else {
assert(img.metadata == nil)
bytes.buffer_destroy(&img.pixels)
free(img)
}
}

View File

@@ -0,0 +1,38 @@
//+build !js
package image
import "core:os"
load :: proc{
load_from_bytes,
load_from_file,
}
load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
data, ok := os.read_entire_file(filename, allocator)
defer delete(data, allocator)
if ok {
return load_from_bytes(data, options, allocator)
} else {
return nil, .Unable_To_Read_File
}
}
which :: proc{
which_bytes,
which_file,
}
which_file :: proc(path: string) -> Which_File_Type {
f, err := os.open(path)
if err != 0 {
return .Unknown
}
header: [128]byte
os.read(f, header[:])
file_type := which_bytes(header[:])
os.close(f)
return file_type
}

View File

@@ -4,7 +4,6 @@ import "core:bytes"
import "core:fmt"
import "core:image"
import "core:mem"
import "core:os"
import "core:strconv"
import "core:strings"
import "core:unicode"
@@ -27,23 +26,6 @@ PFM :: Formats{.Pf, .PF}
ASCII :: Formats{.P1, .P2, .P3}
BINARY :: Formats{.P4, .P5, .P6} + PAM + PFM
load :: proc {
load_from_file,
load_from_bytes,
}
load_from_file :: proc(filename: string, allocator := context.allocator) -> (img: ^Image, err: Error) {
context.allocator = allocator
data, ok := os.read_entire_file(filename); defer delete(data)
if !ok {
err = .Unable_To_Read_File
return
}
return load_from_bytes(data)
}
load_from_bytes :: proc(data: []byte, allocator := context.allocator) -> (img: ^Image, err: Error) {
context.allocator = allocator
@@ -67,24 +49,6 @@ load_from_bytes :: proc(data: []byte, allocator := context.allocator) -> (img: ^
return img, nil
}
save :: proc {
save_to_file,
save_to_buffer,
}
save_to_file :: proc(filename: string, img: ^Image, custom_info: Info = {}, allocator := context.allocator) -> (err: Error) {
context.allocator = allocator
data: []byte; defer delete(data)
data = save_to_buffer(img, custom_info) or_return
if ok := os.write_entire_file(filename, data); !ok {
return .Unable_To_Write_File
}
return Format_Error.None
}
save_to_buffer :: proc(img: ^Image, custom_info: Info = {}, allocator := context.allocator) -> (buffer: []byte, err: Error) {
context.allocator = allocator

View File

@@ -0,0 +1,10 @@
//+build js
package netpbm
load :: proc {
load_from_bytes,
}
save :: proc {
save_to_buffer,
}

View File

@@ -0,0 +1,41 @@
//+build !js
package netpbm
import "core:os"
load :: proc {
load_from_file,
load_from_bytes,
}
load_from_file :: proc(filename: string, allocator := context.allocator) -> (img: ^Image, err: Error) {
context.allocator = allocator
data, ok := os.read_entire_file(filename); defer delete(data)
if !ok {
err = .Unable_To_Read_File
return
}
return load_from_bytes(data)
}
save :: proc {
save_to_file,
save_to_buffer,
}
save_to_file :: proc(filename: string, img: ^Image, custom_info: Info = {}, allocator := context.allocator) -> (err: Error) {
context.allocator = allocator
data: []byte; defer delete(data)
data = save_to_buffer(img, custom_info) or_return
if ok := os.write_entire_file(filename, data); !ok {
return .Unable_To_Write_File
}
return Format_Error.None
}

View File

@@ -17,7 +17,6 @@ import "core:compress"
import "core:compress/zlib"
import "core:image"
import "core:os"
import "core:hash"
import "core:bytes"
import "core:io"
@@ -336,19 +335,6 @@ load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context
return img, err
}
load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
context.allocator = allocator
data, ok := os.read_entire_file(filename)
defer delete(data)
if ok {
return load_from_bytes(data, options)
} else {
return nil, .Unable_To_Read_File
}
}
load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
context.allocator = allocator
options := options
@@ -1641,8 +1627,6 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IH
return nil
}
load :: proc{load_from_file, load_from_bytes, load_from_context}
@(init, private)
_register :: proc() {

View File

@@ -0,0 +1,4 @@
//+build js
package png
load :: proc{load_from_bytes, load_from_context}

View File

@@ -0,0 +1,19 @@
//+build !js
package png
import "core:os"
load :: proc{load_from_file, load_from_bytes, load_from_context}
load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
context.allocator = allocator
data, ok := os.read_entire_file(filename)
defer delete(data)
if ok {
return load_from_bytes(data, options)
} else {
return nil, .Unable_To_Read_File
}
}

View File

@@ -15,7 +15,6 @@ package qoi
import "core:image"
import "core:compress"
import "core:bytes"
import "core:os"
Error :: image.Error
Image :: image.Image
@@ -24,7 +23,7 @@ Options :: image.Options
RGB_Pixel :: image.RGB_Pixel
RGBA_Pixel :: image.RGBA_Pixel
save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
save_to_buffer :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
context.allocator = allocator
if img == nil {
@@ -166,20 +165,6 @@ save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}
return nil
}
save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
context.allocator = allocator
out := &bytes.Buffer{}
defer bytes.buffer_destroy(out)
save_to_memory(out, img, options) or_return
write_ok := os.write_entire_file(output, out.buf[:])
return nil if write_ok else .Unable_To_Write_File
}
save :: proc{save_to_memory, save_to_file}
load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
ctx := &compress.Context_Memory_Input{
input_data = data,
@@ -189,19 +174,6 @@ load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context
return img, err
}
load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
context.allocator = allocator
data, ok := os.read_entire_file(filename)
defer delete(data)
if ok {
return load_from_bytes(data, options)
} else {
return nil, .Unable_To_Read_File
}
}
@(optimization_mode="speed")
load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
context.allocator = allocator
@@ -359,8 +331,6 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
return
}
load :: proc{load_from_file, load_from_bytes, load_from_context}
/*
Cleanup of image-specific data.
*/

View File

@@ -0,0 +1,6 @@
//+build js
package qoi
save :: proc{save_to_buffer}
load :: proc{load_from_bytes, load_from_context}

View File

@@ -0,0 +1,37 @@
//+build !js
package qoi
import "core:os"
import "core:bytes"
save :: proc{save_to_buffer, save_to_file}
save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
context.allocator = allocator
out := &bytes.Buffer{}
defer bytes.buffer_destroy(out)
save_to_buffer(out, img, options) or_return
write_ok := os.write_entire_file(output, out.buf[:])
return nil if write_ok else .Unable_To_Write_File
}
load :: proc{load_from_file, load_from_bytes, load_from_context}
load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
context.allocator = allocator
data, ok := os.read_entire_file(filename)
defer delete(data)
if ok {
return load_from_bytes(data, options)
} else {
return nil, .Unable_To_Read_File
}
}

View File

@@ -14,7 +14,6 @@ package tga
import "core:mem"
import "core:image"
import "core:bytes"
import "core:os"
import "core:compress"
import "core:strings"
@@ -28,7 +27,7 @@ GA_Pixel :: image.GA_Pixel
RGB_Pixel :: image.RGB_Pixel
RGBA_Pixel :: image.RGBA_Pixel
save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
save_to_buffer :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
context.allocator = allocator
if img == nil {
@@ -92,20 +91,6 @@ save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}
return nil
}
save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
context.allocator = allocator
out := &bytes.Buffer{}
defer bytes.buffer_destroy(out)
save_to_memory(out, img, options) or_return
write_ok := os.write_entire_file(output, out.buf[:])
return nil if write_ok else .Unable_To_Write_File
}
save :: proc{save_to_memory, save_to_file}
load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
context.allocator = allocator
options := options
@@ -398,20 +383,6 @@ load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context
return img, err
}
load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
context.allocator = allocator
data, ok := os.read_entire_file(filename)
defer delete(data)
if ok {
return load_from_bytes(data, options)
} else {
return nil, .Unable_To_Read_File
}
}
load :: proc{load_from_file, load_from_bytes, load_from_context}
destroy :: proc(img: ^Image) {
if img == nil || img.width == 0 || img.height == 0 {

View File

@@ -0,0 +1,5 @@
//+build js
package tga
save :: proc{save_to_buffer}
load :: proc{load_from_bytes, load_from_context}

View File

@@ -0,0 +1,34 @@
//+build !js
package tga
import "core:os"
import "core:bytes"
save :: proc{save_to_buffer, save_to_file}
save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
context.allocator = allocator
out := &bytes.Buffer{}
defer bytes.buffer_destroy(out)
save_to_buffer(out, img, options) or_return
write_ok := os.write_entire_file(output, out.buf[:])
return nil if write_ok else .Unable_To_Write_File
}
load :: proc{load_from_file, load_from_bytes, load_from_context}
load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
context.allocator = allocator
data, ok := os.read_entire_file(filename)
defer delete(data)
if ok {
return load_from_bytes(data, options)
} else {
return nil, .Unable_To_Read_File
}
}

View File

@@ -429,11 +429,11 @@ reflect :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T))
b := N * (2 * dot(N, I))
return I - b
}
refract :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) {
dv := dot(N, I)
k := 1 - eta*eta - (1 - dv*dv)
refract :: proc(I, Normal: $V/[$N]$E, eta: E) -> (out: V) where IS_ARRAY(V), IS_FLOAT(ELEM_TYPE(V)) {
dv := dot(Normal, I)
k := 1 - eta*eta * (1 - dv*dv)
a := I * eta
b := N * eta*dv*math.sqrt(k)
b := Normal * (eta*dv+math.sqrt(k))
return (a - b) * E(int(k >= 0))
}

View File

@@ -1425,7 +1425,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
return es
case "force_inline", "force_no_inline":
expr := parse_inlining_operand(p, true, tok)
expr := parse_inlining_operand(p, true, tag)
es := ast.new(ast.Expr_Stmt, expr.pos, expr.end)
es.expr = expr
return es

View File

@@ -211,7 +211,7 @@ _getwd :: proc(allocator: runtime.Allocator) -> (string, Error) {
#no_bounds_check res := unix.sys_getcwd(&buf[0], uint(len(buf)))
if res >= 0 {
return strings.string_from_nul_terminated_ptr(&buf[0], len(buf)), nil
return strings.string_from_null_terminated_ptr(&buf[0], len(buf)), nil
}
if res != -ERANGE {
return "", _get_platform_error(res)

View File

@@ -1,4 +1,277 @@
//+build js
package os
// +build js
#panic("package os does not support a js target")
import "core:intrinsics"
import "core:runtime"
import "core:unicode/utf16"
is_path_separator :: proc(c: byte) -> bool {
return c == '/' || c == '\\'
}
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) {
unimplemented("core:os procedure not supported on JS target")
}
close :: proc(fd: Handle) -> Errno {
unimplemented("core:os procedure not supported on JS target")
}
flush :: proc(fd: Handle) -> (err: Errno) {
unimplemented("core:os procedure not supported on JS target")
}
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
unimplemented("core:os procedure not supported on JS target")
}
@(private="file")
read_console :: proc(handle: Handle, b: []byte) -> (n: int, err: Errno) {
unimplemented("core:os procedure not supported on JS target")
}
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
unimplemented("core:os procedure not supported on JS target")
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
unimplemented("core:os procedure not supported on JS target")
}
file_size :: proc(fd: Handle) -> (i64, Errno) {
unimplemented("core:os procedure not supported on JS target")
}
@(private)
MAX_RW :: 1<<30
@(private)
pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
unimplemented("core:os procedure not supported on JS target")
}
@(private)
pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
unimplemented("core:os procedure not supported on JS target")
}
read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) {
unimplemented("core:os procedure not supported on JS target")
}
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) {
unimplemented("core:os procedure not supported on JS target")
}
// NOTE(bill): Uses startup to initialize it
//stdin := get_std_handle(uint(win32.STD_INPUT_HANDLE))
//stdout := get_std_handle(uint(win32.STD_OUTPUT_HANDLE))
//stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE))
get_std_handle :: proc "contextless" (h: uint) -> Handle {
context = runtime.default_context()
unimplemented("core:os procedure not supported on JS target")
}
exists :: proc(path: string) -> bool {
unimplemented("core:os procedure not supported on JS target")
}
is_file :: proc(path: string) -> bool {
unimplemented("core:os procedure not supported on JS target")
}
is_dir :: proc(path: string) -> bool {
unimplemented("core:os procedure not supported on JS target")
}
// NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName
//@private cwd_lock := win32.SRWLOCK{} // zero is initialized
get_current_directory :: proc(allocator := context.allocator) -> string {
unimplemented("core:os procedure not supported on JS target")
}
set_current_directory :: proc(path: string) -> (err: Errno) {
unimplemented("core:os procedure not supported on JS target")
}
change_directory :: proc(path: string) -> (err: Errno) {
unimplemented("core:os procedure not supported on JS target")
}
make_directory :: proc(path: string, mode: u32 = 0) -> (err: Errno) {
unimplemented("core:os procedure not supported on JS target")
}
remove_directory :: proc(path: string) -> (err: Errno) {
unimplemented("core:os procedure not supported on JS target")
}
@(private)
is_abs :: proc(path: string) -> bool {
unimplemented("core:os procedure not supported on JS target")
}
@(private)
fix_long_path :: proc(path: string) -> string {
unimplemented("core:os procedure not supported on JS target")
}
link :: proc(old_name, new_name: string) -> (err: Errno) {
unimplemented("core:os procedure not supported on JS target")
}
unlink :: proc(path: string) -> (err: Errno) {
unimplemented("core:os procedure not supported on JS target")
}
rename :: proc(old_path, new_path: string) -> (err: Errno) {
unimplemented("core:os procedure not supported on JS target")
}
ftruncate :: proc(fd: Handle, length: i64) -> (err: Errno) {
unimplemented("core:os procedure not supported on JS target")
}
truncate :: proc(path: string, length: i64) -> (err: Errno) {
unimplemented("core:os procedure not supported on JS target")
}
remove :: proc(name: string) -> Errno {
unimplemented("core:os procedure not supported on JS target")
}
pipe :: proc() -> (r, w: Handle, err: Errno) {
unimplemented("core:os procedure not supported on JS target")
}
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
unimplemented("core:os procedure not supported on JS target")
}
Handle :: distinct uintptr
File_Time :: distinct u64
Errno :: distinct int
INVALID_HANDLE :: ~Handle(0)
O_RDONLY :: 0x00000
O_WRONLY :: 0x00001
O_RDWR :: 0x00002
O_CREATE :: 0x00040
O_EXCL :: 0x00080
O_NOCTTY :: 0x00100
O_TRUNC :: 0x00200
O_NONBLOCK :: 0x00800
O_APPEND :: 0x00400
O_SYNC :: 0x01000
O_ASYNC :: 0x02000
O_CLOEXEC :: 0x80000
ERROR_NONE: Errno : 0
ERROR_FILE_NOT_FOUND: Errno : 2
ERROR_PATH_NOT_FOUND: Errno : 3
ERROR_ACCESS_DENIED: Errno : 5
ERROR_INVALID_HANDLE: Errno : 6
ERROR_NOT_ENOUGH_MEMORY: Errno : 8
ERROR_NO_MORE_FILES: Errno : 18
ERROR_HANDLE_EOF: Errno : 38
ERROR_NETNAME_DELETED: Errno : 64
ERROR_FILE_EXISTS: Errno : 80
ERROR_INVALID_PARAMETER: Errno : 87
ERROR_BROKEN_PIPE: Errno : 109
ERROR_BUFFER_OVERFLOW: Errno : 111
ERROR_INSUFFICIENT_BUFFER: Errno : 122
ERROR_MOD_NOT_FOUND: Errno : 126
ERROR_PROC_NOT_FOUND: Errno : 127
ERROR_DIR_NOT_EMPTY: Errno : 145
ERROR_ALREADY_EXISTS: Errno : 183
ERROR_ENVVAR_NOT_FOUND: Errno : 203
ERROR_MORE_DATA: Errno : 234
ERROR_OPERATION_ABORTED: Errno : 995
ERROR_IO_PENDING: Errno : 997
ERROR_NOT_FOUND: Errno : 1168
ERROR_PRIVILEGE_NOT_HELD: Errno : 1314
WSAEACCES: Errno : 10013
WSAECONNRESET: Errno : 10054
// Windows reserves errors >= 1<<29 for application use
ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0
ERROR_FILE_IS_NOT_DIR: Errno : 1<<29 + 1
ERROR_NEGATIVE_OFFSET: Errno : 1<<29 + 2
// "Argv" arguments converted to Odin strings
args := _alloc_command_line_arguments()
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
unimplemented("core:os procedure not supported on JS target")
}
last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
unimplemented("core:os procedure not supported on JS target")
}
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
unimplemented("core:os procedure not supported on JS target")
}
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
unimplemented("core:os procedure not supported on JS target")
}
heap_free :: proc(ptr: rawptr) {
unimplemented("core:os procedure not supported on JS target")
}
get_page_size :: proc() -> int {
unimplemented("core:os procedure not supported on JS target")
}
@(private)
_processor_core_count :: proc() -> int {
unimplemented("core:os procedure not supported on JS target")
}
exit :: proc "contextless" (code: int) -> ! {
context = runtime.default_context()
unimplemented("core:os procedure not supported on JS target")
}
current_thread_id :: proc "contextless" () -> int {
context = runtime.default_context()
unimplemented("core:os procedure not supported on JS target")
}
_alloc_command_line_arguments :: proc() -> []string {
return nil
}

View File

@@ -913,7 +913,7 @@ get_current_directory :: proc() -> string {
#no_bounds_check res := unix.sys_getcwd(&buf[0], uint(len(buf)))
if res >= 0 {
return strings.string_from_nul_terminated_ptr(&buf[0], len(buf))
return strings.string_from_null_terminated_ptr(&buf[0], len(buf))
}
if _get_errno(res) != ERANGE {
delete(buf)

View File

@@ -449,7 +449,14 @@ struct_field_value_by_name :: proc(a: any, field: string, allow_using := false)
return nil
}
@(require_results)
struct_field_value :: proc(a: any, field: Struct_Field) -> any {
if a == nil { return nil }
return any {
rawptr(uintptr(a.data) + field.offset),
field.type.id,
}
}
@(require_results)
struct_field_names :: proc(T: typeid) -> []string {

View File

@@ -621,7 +621,9 @@ __init_context :: proc "contextless" (c: ^Context) {
c.allocator.data = nil
c.temp_allocator.procedure = default_temp_allocator_proc
c.temp_allocator.data = &global_default_temp_allocator_data
when !NO_DEFAULT_TEMP_ALLOCATOR {
c.temp_allocator.data = &global_default_temp_allocator_data
}
when !ODIN_DISABLE_ASSERT {
c.assertion_failure_proc = default_assertion_failure_proc

View File

@@ -15,11 +15,15 @@ container_of :: #force_inline proc "contextless" (ptr: $P/^$Field_Type, $T: type
}
@thread_local global_default_temp_allocator_data: Default_Temp_Allocator
when !NO_DEFAULT_TEMP_ALLOCATOR {
@thread_local global_default_temp_allocator_data: Default_Temp_Allocator
}
@builtin
@(builtin, disabled=NO_DEFAULT_TEMP_ALLOCATOR)
init_global_temporary_allocator :: proc(size: int, backup_allocator := context.allocator) {
default_temp_allocator_init(&global_default_temp_allocator_data, size, backup_allocator)
when !NO_DEFAULT_TEMP_ALLOCATOR {
default_temp_allocator_init(&global_default_temp_allocator_data, size, backup_allocator)
}
}

View File

@@ -1,9 +1,9 @@
package runtime
DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, 4 * Megabyte)
NO_DEFAULT_TEMP_ALLOCATOR: bool : ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR
when ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR {
when NO_DEFAULT_TEMP_ALLOCATOR {
Default_Temp_Allocator :: struct {}
default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator := context.allocator) {}
@@ -54,6 +54,11 @@ when ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR
default_temp_allocator_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
arena_temp_end(temp, loc)
}
@(fini, private)
_destroy_temp_allocator_fini :: proc() {
default_temp_allocator_destroy(&global_default_temp_allocator_data)
}
}
@(deferred_out=default_temp_allocator_temp_end)
@@ -72,8 +77,3 @@ default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator
data = allocator,
}
}
@(fini, private)
_destroy_temp_allocator_fini :: proc() {
default_temp_allocator_destroy(&global_default_temp_allocator_data)
}

View File

@@ -3,9 +3,22 @@ package strings
import "core:unicode/utf8"
/*
Ascii_Set is designed to store ASCII characters efficiently as a bit-array
Each bit in the array corresponds to a specific ASCII character, where the value of the bit (0 or 1)
indicates if the character is present in the set or not.
*/
Ascii_Set :: distinct [8]u32
/*
Creates an Ascii_Set with unique characters from the input string.
// create an ascii set of all unique characters in the string
Inputs:
- chars: A string containing characters to include in the Ascii_Set.
Returns:
- as: An Ascii_Set with unique characters from the input string.
- ok: false if any character in the input string is not a valid ASCII character.
*/
ascii_set_make :: proc(chars: string) -> (as: Ascii_Set, ok: bool) #no_bounds_check {
for i in 0..<len(chars) {
c := chars[i]
@@ -17,8 +30,16 @@ ascii_set_make :: proc(chars: string) -> (as: Ascii_Set, ok: bool) #no_bounds_ch
ok = true
return
}
/*
Determines if a given char is contained within an Ascii_Set.
// returns true when the `c` byte is contained in the `as` ascii set
Inputs:
- as: The Ascii_Set to search.
- c: The char to check for in the Ascii_Set.
Returns:
A boolean indicating if the byte is contained in the Ascii_Set (true) or not (false).
*/
ascii_set_contains :: proc(as: Ascii_Set, c: byte) -> bool #no_bounds_check {
return as[c>>5] & (1<<(c&31)) != 0
}
}

View File

@@ -4,68 +4,133 @@ import "core:runtime"
import "core:unicode/utf8"
import "core:strconv"
import "core:io"
Builder_Flush_Proc :: #type proc(b: ^Builder) -> (do_reset: bool)
/*
dynamic byte buffer / string builder with helper procedures
the dynamic array is wrapped inside the struct to be more opaque
you can use `fmt.sbprint*` procedures with a `^strings.Builder` directly
Type definition for a procedure that flushes a Builder
Inputs:
- b: A pointer to the Builder
Returns:
A boolean indicating whether the Builder should be reset
*/
Builder_Flush_Proc :: #type proc(b: ^Builder) -> (do_reset: bool)
/*
A dynamic byte buffer / string builder with helper procedures
The dynamic array is wrapped inside the struct to be more opaque
You can use `fmt.sbprint*` procedures with a `^strings.Builder` directly
*/
Builder :: struct {
buf: [dynamic]byte,
}
/*
Produces a Builder with a default length of 0 and cap of 16
// return a builder, default length 0 / cap 16 are done through make
*Allocates Using Provided Allocator*
Inputs:
- allocator: (default is context.allocator)
Returns:
A new Builder
*/
builder_make_none :: proc(allocator := context.allocator) -> Builder {
return Builder{buf=make([dynamic]byte, allocator)}
}
/*
Produces a Builder with a specified length and cap of max(16,len) byte buffer
// return a builder, with a set length `len` and cap 16 byte buffer
*Allocates Using Provided Allocator*
Inputs:
- len: The desired length of the Builder's buffer
- allocator: (default is context.allocator)
Returns:
A new Builder
*/
builder_make_len :: proc(len: int, allocator := context.allocator) -> Builder {
return Builder{buf=make([dynamic]byte, len, allocator)}
}
/*
Produces a Builder with a specified length and cap
// return a builder, with a set length `len` byte buffer and a custom `cap`
*Allocates Using Provided Allocator*
Inputs:
- len: The desired length of the Builder's buffer
- cap: The desired capacity of the Builder's buffer, cap is max(cap, len)
- allocator: (default is context.allocator)
Returns:
A new Builder
*/
builder_make_len_cap :: proc(len, cap: int, allocator := context.allocator) -> Builder {
return Builder{buf=make([dynamic]byte, len, cap, allocator)}
}
// overload simple `builder_make_*` with or without len / cap parameters
builder_make :: proc{
builder_make_none,
builder_make_len,
builder_make_len_cap,
}
/*
Initializes a Builder with a length of 0 and cap of 16
It replaces the existing `buf`
// initialize a builder, default length 0 / cap 16 are done through make
// replaces the existing `buf`
*Allocates Using Provided Allocator*
Inputs:
- b: A pointer to the Builder
- allocator: (default is context.allocator)
Returns:
initialized ^Builder
*/
builder_init_none :: proc(b: ^Builder, allocator := context.allocator) -> ^Builder {
b.buf = make([dynamic]byte, allocator)
return b
}
/*
Initializes a Builder with a specified length and cap, which is max(len,16)
It replaces the existing `buf`
// initialize a builder, with a set length `len` and cap 16 byte buffer
// replaces the existing `buf`
*Allocates Using Provided Allocator*
Inputs:
- b: A pointer to the Builder
- len: The desired length of the Builder's buffer
- allocator: (default is context.allocator)
Returns:
Initialized ^Builder
*/
builder_init_len :: proc(b: ^Builder, len: int, allocator := context.allocator) -> ^Builder {
b.buf = make([dynamic]byte, len, allocator)
return b
}
/*
Initializes a Builder with a specified length and cap
It replaces the existing `buf`
// initialize a builder, with a set length `len` byte buffer and a custom `cap`
// replaces the existing `buf`
Inputs:
- b: A pointer to the Builder
- len: The desired length of the Builder's buffer
- cap: The desired capacity of the Builder's buffer, actual max(len,cap)
- allocator: (default is context.allocator)
Returns:
A pointer to the initialized Builder
*/
builder_init_len_cap :: proc(b: ^Builder, len, cap: int, allocator := context.allocator) -> ^Builder {
b.buf = make([dynamic]byte, len, cap, allocator)
return b
}
// overload simple `builder_init_*` with or without len / ap parameters
// Overload simple `builder_init_*` with or without len / ap parameters
builder_init :: proc{
builder_init_none,
builder_init_len,
builder_init_len_cap,
}
@(private)
_builder_stream_vtable_obj := io.Stream_VTable{
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
@@ -90,50 +155,95 @@ _builder_stream_vtable_obj := io.Stream_VTable{
},
impl_destroy = proc(s: io.Stream) -> io.Error {
b := (^Builder)(s.stream_data)
delete(b.buf)
builder_destroy(b)
return .None
},
}
// NOTE(dweiler): Work around a miscompilation bug on Linux still.
@(private)
_builder_stream_vtable := &_builder_stream_vtable_obj
/*
Returns an io.Stream from a Builder
// return an `io.Stream` from a builder
Inputs:
- b: A pointer to the Builder
Returns:
An io.Stream
*/
to_stream :: proc(b: ^Builder) -> io.Stream {
return io.Stream{stream_vtable=_builder_stream_vtable, stream_data=b}
}
/*
Returns an io.Writer from a Builder
// return an `io.Writer` from a builder
Inputs:
- b: A pointer to the Builder
Returns:
An io.Writer
*/
to_writer :: proc(b: ^Builder) -> io.Writer {
return io.to_writer(to_stream(b))
}
/*
Deletes the Builder byte buffer content
// delete and clear the builder byte buffer content
Inputs:
- b: A pointer to the Builder
*/
builder_destroy :: proc(b: ^Builder) {
delete(b.buf)
clear(&b.buf)
b.buf = nil
}
/*
Reserves the Builder byte buffer to a specific capacity, when it's higher than before
// reserve the builfer byte buffer to a specific cap, when it's higher than before
Inputs:
- b: A pointer to the Builder
- cap: The desired capacity for the Builder's buffer
*/
builder_grow :: proc(b: ^Builder, cap: int) {
reserve(&b.buf, cap)
}
/*
Clears the Builder byte buffer content (sets len to zero)
// clear the builder byte buffer content
Inputs:
- b: A pointer to the Builder
*/
builder_reset :: proc(b: ^Builder) {
clear(&b.buf)
}
/*
create an empty builder with the same slice length as its cap
uses the `mem.nil_allocator` to avoid allocation and keep a fixed length
used in `fmt.bprint*`
bytes: [8]byte // <-- gets filled
builder := strings.builder_from_bytes(bytes[:])
strings.write_byte(&builder, 'a') -> "a"
strings.write_byte(&builder, 'b') -> "ab"
Creates a Builder from a slice of bytes with the same slice length as its capacity. Used in fmt.bprint*
*Uses Nil Allocator - Does NOT allocate*
Inputs:
- backing: A slice of bytes to be used as the backing buffer
Returns:
A new Builder
Example:
import "core:fmt"
import "core:strings"
builder_from_bytes_example :: proc() {
bytes: [8]byte // <-- gets filled
builder := strings.builder_from_bytes(bytes[:])
strings.write_byte(&builder, 'a')
fmt.println(strings.to_string(builder)) // -> "a"
strings.write_byte(&builder, 'b')
fmt.println(strings.to_string(builder)) // -> "ab"
}
Output:
a
ab
*/
builder_from_bytes :: proc(backing: []byte) -> Builder {
s := transmute(runtime.Raw_Slice)backing
@@ -147,36 +257,84 @@ builder_from_bytes :: proc(backing: []byte) -> Builder {
buf = transmute([dynamic]byte)d,
}
}
// Alias to `builder_from_bytes`
builder_from_slice :: builder_from_bytes
/*
Casts the Builder byte buffer to a string and returns it
// cast the builder byte buffer to a string and return it
Inputs:
- b: A Builder
Returns:
The contents of the Builder's buffer, as a string
*/
to_string :: proc(b: Builder) -> string {
return string(b.buf[:])
}
/*
Returns the length of the Builder's buffer, in bytes
// return the length of the builder byte buffer
Inputs:
- b: A Builder
Returns:
The length of the Builder's buffer
*/
builder_len :: proc(b: Builder) -> int {
return len(b.buf)
}
/*
Returns the capacity of the Builder's buffer, in bytes
// return the cap of the builder byte buffer
Inputs:
- b: A Builder
Returns:
The capacity of the Builder's buffer
*/
builder_cap :: proc(b: Builder) -> int {
return cap(b.buf)
}
/*
The free space left in the Builder's buffer, in bytes
// returns the space left in the builder byte buffer to use up
Inputs:
- b: A Builder
Returns:
The available space left in the Builder's buffer
*/
builder_space :: proc(b: Builder) -> int {
return cap(b.buf) - len(b.buf)
}
/*
appends a byte to the builder, returns the append diff
Appends a byte to the Builder and returns the number of bytes appended
Inputs:
- b: A pointer to the Builder
- x: The byte to be appended
Returns:
The number of bytes appended
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
Example:
import "core:fmt"
import "core:strings"
write_byte_example :: proc() {
builder := strings.builder_make()
strings.write_byte(&builder, 'a') // 1
strings.write_byte(&builder, 'b') // 1
fmt.println(strings.to_string(builder)) // -> ab
}
Output:
ab
builder := strings.builder_make()
strings.write_byte(&builder, 'a') // 1
strings.write_byte(&builder, 'b') // 1
strings.write_byte(&builder, 'c') // 1
fmt.println(strings.to_string(builder)) // -> abc
*/
write_byte :: proc(b: ^Builder, x: byte) -> (n: int) {
n0 := len(b.buf)
@@ -184,14 +342,29 @@ write_byte :: proc(b: ^Builder, x: byte) -> (n: int) {
n1 := len(b.buf)
return n1-n0
}
/*
appends a slice of bytes to the builder, returns the append diff
Appends a slice of bytes to the Builder and returns the number of bytes appended
builder := strings.builder_make()
bytes := [?]byte { 'a', 'b', 'c' }
strings.write_bytes(&builder, bytes[:]) // 3
fmt.println(strings.to_string(builder)) // -> abc
Inputs:
- b: A pointer to the Builder
- x: The slice of bytes to be appended
Example:
import "core:fmt"
import "core:strings"
write_bytes_example :: proc() {
builder := strings.builder_make()
bytes := [?]byte { 'a', 'b', 'c' }
strings.write_bytes(&builder, bytes[:]) // 3
fmt.println(strings.to_string(builder)) // -> abc
}
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
Returns:
The number of bytes appended
*/
write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
n0 := len(b.buf)
@@ -199,42 +372,99 @@ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
n1 := len(b.buf)
return n1-n0
}
/*
appends a single rune into the builder, returns written rune size and an `io.Error`
Appends a single rune to the Builder and returns the number of bytes written and an `io.Error`
Inputs:
- b: A pointer to the Builder
- r: The rune to be appended
Returns:
The number of bytes written and an io.Error (if any)
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
Example:
import "core:fmt"
import "core:strings"
write_rune_example :: proc() {
builder := strings.builder_make()
strings.write_rune(&builder, 'ä') // 2 None
strings.write_rune(&builder, 'b') // 1 None
fmt.println(strings.to_string(builder)) // -> äb
}
Output:
äb
builder := strings.builder_make()
strings.write_rune(&builder, 'ä') // 2 None
strings.write_rune(&builder, 'b') // 1 None
strings.write_rune(&builder, 'c') // 1 None
fmt.println(strings.to_string(builder)) // -> äbc
*/
write_rune :: proc(b: ^Builder, r: rune) -> (int, io.Error) {
return io.write_rune(to_writer(b), r)
}
/*
appends a quoted rune into the builder, returns written size
Appends a quoted rune to the Builder and returns the number of bytes written
Inputs:
- b: A pointer to the Builder
- r: The rune to be appended
Returns:
The number of bytes written
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
Example:
import "core:fmt"
import "core:strings"
write_quoted_rune_example :: proc() {
builder := strings.builder_make()
strings.write_string(&builder, "abc") // 3
strings.write_quoted_rune(&builder, 'ä') // 4
strings.write_string(&builder, "abc") // 3
fmt.println(strings.to_string(builder)) // -> abc'ä'abc
}
Output:
abc'ä'abc
builder := strings.builder_make()
strings.write_string(&builder, "abc") // 3
strings.write_quoted_rune(&builder, 'ä') // 4
strings.write_string(&builder, "abc") // 3
fmt.println(strings.to_string(builder)) // -> abc'ä'abc
*/
write_quoted_rune :: proc(b: ^Builder, r: rune) -> (n: int) {
return io.write_quoted_rune(to_writer(b), r)
}
/*
appends a string to the builder, return the written byte size
builder := strings.builder_make()
strings.write_string(&builder, "a") // 1
strings.write_string(&builder, "bc") // 2
strings.write_string(&builder, "xyz") // 3
fmt.println(strings.to_string(builder)) // -> abcxyz
Appends a string to the Builder and returns the number of bytes written
Inputs:
- b: A pointer to the Builder
- s: The string to be appended
Returns:
The number of bytes written
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
Example:
import "core:fmt"
import "core:strings"
write_string_example :: proc() {
builder := strings.builder_make()
strings.write_string(&builder, "a") // 1
strings.write_string(&builder, "bc") // 2
fmt.println(strings.to_string(builder)) // -> abc
}
Output:
abc
*/
write_string :: proc(b: ^Builder, s: string) -> (n: int) {
n0 := len(b.buf)
@@ -242,10 +472,15 @@ write_string :: proc(b: ^Builder, s: string) -> (n: int) {
n1 := len(b.buf)
return n1-n0
}
/*
Pops and returns the last byte in the Builder or 0 when the Builder is empty
Inputs:
- b: A pointer to the Builder
// pops and returns the last byte in the builder
// returns 0 when the builder is empty
Returns:
The last byte in the Builder or 0 if empty
*/
pop_byte :: proc(b: ^Builder) -> (r: byte) {
if len(b.buf) == 0 {
return 0
@@ -256,9 +491,15 @@ pop_byte :: proc(b: ^Builder) -> (r: byte) {
d.len = max(d.len-1, 0)
return
}
/*
Pops the last rune in the Builder and returns the popped rune and its rune width or (0, 0) if empty
// pops the last rune in the builder and returns the popped rune and its rune width
// returns 0, 0 when the builder is empty
Inputs:
- b: A pointer to the Builder
Returns:
The popped rune and its rune width or (0, 0) if empty
*/
pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) {
if len(b.buf) == 0 {
return 0, 0
@@ -269,41 +510,116 @@ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) {
d.len = max(d.len-width, 0)
return
}
@(private)
DIGITS_LOWER := "0123456789abcdefx"
/*
append a quoted string into the builder, return the written byte size
Inputs:
- b: A pointer to the Builder
- str: The string to be quoted and appended
- quote: The optional quote character (default is double quotes)
Returns:
The number of bytes written
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
Example:
import "core:fmt"
import "core:strings"
write_quoted_string_example :: proc() {
builder := strings.builder_make()
strings.write_quoted_string(&builder, "a") // 3
strings.write_quoted_string(&builder, "bc", '\'') // 4
strings.write_quoted_string(&builder, "xyz") // 5
fmt.println(strings.to_string(builder))
}
Output:
"a"'bc'"xyz"
builder := strings.builder_make()
strings.write_quoted_string(&builder, "a") // 3
strings.write_quoted_string(&builder, "bc", '\'') // 4
strings.write_quoted_string(&builder, "xyz") // 5
fmt.println(strings.to_string(builder)) // -> "a"'bc'xyz"
*/
write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) {
n, _ = io.write_quoted_string(to_writer(b), str, quote)
return
}
/*
Appends a rune to the Builder and returns the number of bytes written
Inputs:
- b: A pointer to the Builder
- r: The rune to be appended
- write_quote: Optional boolean flag to wrap in single-quotes (') (default is true)
// appends a rune to the builder, optional `write_quote` boolean tag, returns the written rune size
Returns:
The number of bytes written
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
Example:
import "core:fmt"
import "core:strings"
write_encoded_rune_example :: proc() {
builder := strings.builder_make()
strings.write_encoded_rune(&builder, 'a', false) // 1
strings.write_encoded_rune(&builder, '\"', true) // 3
strings.write_encoded_rune(&builder, 'x', false) // 1
fmt.println(strings.to_string(builder))
}
Output:
a'"'x
*/
write_encoded_rune :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) {
n, _ = io.write_encoded_rune(to_writer(b), r, write_quote)
return
}
/*
Appends an escaped rune to the Builder and returns the number of bytes written
// appends a rune to the builder, fully written out in case of escaped runes e.g. '\a' will be written as such
// when `r` and `quote` match and `quote` is `\\` - they will be written as two slashes
// `html_safe` flag in case the runes '<', '>', '&' should be encoded as digits e.g. `\u0026`
Inputs:
- b: A pointer to the Builder
- r: The rune to be appended
- quote: The quote character
- html_safe: Optional boolean flag to encode '<', '>', '&' as digits (default is false)
**Usage**
- '\a' will be written as such
- `r` and `quote` match and `quote` is `\\` - they will be written as two slashes
- `html_safe` flag in case the runes '<', '>', '&' should be encoded as digits e.g. `\u0026`
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
Returns:
The number of bytes written
*/
write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) {
n, _ = io.write_escaped_rune(to_writer(b), r, quote, html_safe)
return
}
/*
Writes a f64 value to the Builder and returns the number of characters written
// writes a f64 value into the builder, returns the written amount of characters
Inputs:
- b: A pointer to the Builder
- f: The f64 value to be appended
- fmt: The format byte
- prec: The precision
- bit_size: The bit size
- always_signed: Optional boolean flag to always include the sign (default is false)
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
Returns:
The number of characters written
*/
write_float :: proc(b: ^Builder, f: f64, fmt: byte, prec, bit_size: int, always_signed := false) -> (n: int) {
buf: [384]byte
s := strconv.append_float(buf[:], f, fmt, prec, bit_size)
@@ -314,8 +630,20 @@ write_float :: proc(b: ^Builder, f: f64, fmt: byte, prec, bit_size: int, always_
}
return write_string(b, s)
}
/*
Writes a f16 value to the Builder and returns the number of characters written
// writes a f16 value into the builder, returns the written amount of characters
Inputs:
- b: A pointer to the Builder
- f: The f16 value to be appended
- fmt: The format byte
- always_signed: Optional boolean flag to always include the sign
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
Returns:
The number of characters written
*/
write_f16 :: proc(b: ^Builder, f: f16, fmt: byte, always_signed := false) -> (n: int) {
buf: [384]byte
s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
@@ -324,8 +652,38 @@ write_f16 :: proc(b: ^Builder, f: f16, fmt: byte, always_signed := false) -> (n:
}
return write_string(b, s)
}
/*
Writes a f32 value to the Builder and returns the number of characters written
// writes a f32 value into the builder, returns the written amount of characters
Inputs:
- b: A pointer to the Builder
- f: The f32 value to be appended
- fmt: The format byte
- always_signed: Optional boolean flag to always include the sign
Returns:
The number of characters written
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
Example:
import "core:fmt"
import "core:strings"
write_f32_example :: proc() {
builder := strings.builder_make()
strings.write_f32(&builder, 3.14159, 'f') // 6
strings.write_string(&builder, " - ") // 3
strings.write_f32(&builder, -0.123, 'e') // 8
fmt.println(strings.to_string(builder)) // -> 3.14159012 - -1.23000003e-01
}
Output:
3.14159012 - -1.23000003e-01
*/
write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n: int) {
buf: [384]byte
s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
@@ -334,8 +692,20 @@ write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n:
}
return write_string(b, s)
}
/*
Writes a f32 value to the Builder and returns the number of characters written
// writes a f64 value into the builder, returns the written amount of characters
Inputs:
- b: A pointer to the Builder
- f: The f32 value to be appended
- fmt: The format byte
- always_signed: Optional boolean flag to always include the sign
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
Returns:
The number of characters written
*/
write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n: int) {
buf: [384]byte
s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
@@ -344,30 +714,71 @@ write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n:
}
return write_string(b, s)
}
/*
Writes a u64 value to the Builder and returns the number of characters written
Inputs:
- b: A pointer to the Builder
- i: The u64 value to be appended
- base: The optional base for the numeric representation
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
// writes a u64 value `i` in `base` = 10 into the builder, returns the written amount of characters
Returns:
The number of characters written
*/
write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) {
buf: [32]byte
s := strconv.append_bits(buf[:], i, base, false, 64, strconv.digits, nil)
return write_string(b, s)
}
/*
Writes a i64 value to the Builder and returns the number of characters written
// writes a i64 value `i` in `base` = 10 into the builder, returns the written amount of characters
Inputs:
- b: A pointer to the Builder
- i: The i64 value to be appended
- base: The optional base for the numeric representation
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
Returns:
The number of characters written
*/
write_i64 :: proc(b: ^Builder, i: i64, base: int = 10) -> (n: int) {
buf: [32]byte
s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil)
return write_string(b, s)
}
/*
Writes a uint value to the Builder and returns the number of characters written
// writes a uint value `i` in `base` = 10 into the builder, returns the written amount of characters
Inputs:
- b: A pointer to the Builder
- i: The uint value to be appended
- base: The optional base for the numeric representation
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
Returns:
The number of characters written
*/
write_uint :: proc(b: ^Builder, i: uint, base: int = 10) -> (n: int) {
return write_u64(b, u64(i), base)
}
/*
Writes a int value to the Builder and returns the number of characters written
// writes a int value `i` in `base` = 10 into the builder, returns the written amount of characters
Inputs:
- b: A pointer to the Builder
- i: The int value to be appended
- base: The optional base for the numeric representation
NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
Returns:
The number of characters written
*/
write_int :: proc(b: ^Builder, i: int, base: int = 10) -> (n: int) {
return write_i64(b, i64(i), base)
}

View File

@@ -4,6 +4,21 @@ import "core:io"
import "core:unicode"
import "core:unicode/utf8"
/*
Converts invalid UTF-8 sequences in the input string `s` to the `replacement` string.
*Allocates Using Provided Allocator*
Inputs:
- s: Input string that may contain invalid UTF-8 sequences.
- replacement: String to replace invalid UTF-8 sequences with.
- allocator: (default: context.allocator).
WARNING: Allocation does not occur when len(s) == 0
Returns:
A valid UTF-8 string with invalid sequences replaced by `replacement`.
*/
to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> string {
if len(s) == 0 {
return ""
@@ -33,7 +48,7 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) ->
invalid := false
for i := 0; i < len(s); /**/ {
for i := 0; i < len(s); /**/{
c := s[i]
if c < utf8.RUNE_SELF {
i += 1
@@ -57,13 +72,31 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) ->
}
return to_string(b)
}
/*
returns the input string `s` with all runes set to lowered case
always allocates using the `allocator`
Converts the input string `s` to all lowercase characters.
*Allocates Using Provided Allocator*
Inputs:
- s: Input string to be converted.
- allocator: (default: context.allocator).
Returns:
A new string with all characters converted to lowercase.
Example:
import "core:fmt"
import "core:strings"
to_lower_example :: proc() {
fmt.println(strings.to_lower("TeST"))
}
Output:
test
strings.to_lower("test") -> test
strings.to_lower("Test") -> test
*/
to_lower :: proc(s: string, allocator := context.allocator) -> string {
b: Builder
@@ -73,13 +106,31 @@ to_lower :: proc(s: string, allocator := context.allocator) -> string {
}
return to_string(b)
}
/*
returns the input string `s` with all runes set to upper case
always allocates using the `allocator`
Converts the input string `s` to all uppercase characters.
*Allocates Using Provided Allocator*
Inputs:
- s: Input string to be converted.
- allocator: (default: context.allocator).
Returns:
A new string with all characters converted to uppercase.
Example:
import "core:fmt"
import "core:strings"
to_upper_example :: proc() {
fmt.println(strings.to_upper("Test"))
}
Output:
TEST
strings.to_upper("test") -> TEST
strings.to_upper("Test") -> TEST
*/
to_upper :: proc(s: string, allocator := context.allocator) -> string {
b: Builder
@@ -89,21 +140,38 @@ to_upper :: proc(s: string, allocator := context.allocator) -> string {
}
return to_string(b)
}
/*
Checks if the rune `r` is a delimiter (' ', '-', or '_').
// returns true when the `c` rune is a space, '-' or '_'
// useful when treating strings like words in a text editor or html paths
is_delimiter :: proc(c: rune) -> bool {
return c == '-' || c == '_' || is_space(c)
Inputs:
- r: Rune to check for delimiter status.
Returns:
True if `r` is a delimiter, false otherwise.
*/
is_delimiter :: proc(r: rune) -> bool {
return r == '-' || r == '_' || is_space(r)
}
/*
Checks if the rune `r` is a non-alphanumeric or space character.
// returns true when the `r` rune is a non alpha or `unicode.is_space` rune
Inputs:
- r: Rune to check for separator status.
Returns:
True if `r` is a non-alpha or `unicode.is_space` rune.
*/
is_separator :: proc(r: rune) -> bool {
if r <= 0x7f {
switch r {
case '0'..='9': return false
case 'a'..='z': return false
case 'A'..='Z': return false
case '_': return false
case '0' ..= '9':
return false
case 'a' ..= 'z':
return false
case 'A' ..= 'Z':
return false
case '_':
return false
}
return true
}
@@ -115,12 +183,46 @@ is_separator :: proc(r: rune) -> bool {
return unicode.is_space(r)
}
/*
iterator that loops through the string and calls the callback with the `prev`, `curr` and `next` rune
on empty string `s` the callback gets called once with empty runes
Iterates over a string, calling a callback for each rune with the previous, current, and next runes as arguments.
Inputs:
- w: An io.Writer to be used by the callback for writing output.
- s: The input string to be iterated over.
- callback: A procedure to be called for each rune in the string, with arguments (w: io.Writer, prev, curr, next: rune).
The callback can utilize the provided io.Writer to write output during the iteration.
Example:
import "core:fmt"
import "core:strings"
import "core:io"
string_case_iterator_example :: proc() {
my_callback :: proc(w: io.Writer, prev, curr, next: rune) {
fmt.println("my_callback", curr) // <-- Custom logic here
}
s := "hello"
b: strings.Builder
strings.builder_init_len(&b, len(s))
w := strings.to_writer(&b)
strings.string_case_iterator(w, s, my_callback)
}
Output:
my_callback h
my_callback e
my_callback l
my_callback l
my_callback o
*/
string_case_iterator :: proc(w: io.Writer, s: string, callback: proc(w: io.Writer, prev, curr, next: rune)) {
string_case_iterator :: proc(
w: io.Writer,
s: string,
callback: proc(w: io.Writer, prev, curr, next: rune),
) {
prev, curr: rune
for next in s {
if curr == 0 {
@@ -139,10 +241,20 @@ string_case_iterator :: proc(w: io.Writer, s: string, callback: proc(w: io.Write
callback(w, prev, curr, 0)
}
}
// Alias to `to_camel_case`
to_lower_camel_case :: to_camel_case
/*
Converts the input string `s` to "lowerCamelCase".
// converts the `s` string to "lowerCamelCase"
*Allocates Using Provided Allocator*
Inputs:
- s: Input string to be converted.
- allocator: (default: context.allocator).
Returns:
A "lowerCamelCase" formatted string.
*/
to_camel_case :: proc(s: string, allocator := context.allocator) -> string {
s := s
s = trim_space(s)
@@ -164,10 +276,20 @@ to_camel_case :: proc(s: string, allocator := context.allocator) -> string {
return to_string(b)
}
// Alias to `to_pascal_case`
to_upper_camel_case :: to_pascal_case
/*
Converts the input string `s` to "UpperCamelCase" (PascalCase).
// converts the `s` string to "PascalCase"
*Allocates Using Provided Allocator*
Inputs:
- s: Input string to be converted.
- allocator: (default: context.allocator).
Returns:
A "PascalCase" formatted string.
*/
to_pascal_case :: proc(s: string, allocator := context.allocator) -> string {
s := s
s = trim_space(s)
@@ -189,17 +311,44 @@ to_pascal_case :: proc(s: string, allocator := context.allocator) -> string {
return to_string(b)
}
/*
Returns a string converted to a delimiter-separated case with configurable casing
/*
returns the `s` string to words seperated by the given `delimiter` rune
all runes will be upper or lowercased based on the `all_uppercase` bool
*Allocates Using Provided Allocator*
Inputs:
- s: The input string to be converted
- delimiter: The rune to be used as the delimiter between words
- all_upper_case: A boolean indicating if the output should be all uppercased (true) or lowercased (false)
- allocator: (default: context.allocator).
Returns:
The converted string
Example:
import "core:fmt"
import "core:strings"
to_delimiter_case_example :: proc() {
fmt.println(strings.to_delimiter_case("Hello World", '_', false))
fmt.println(strings.to_delimiter_case("Hello World", ' ', true))
fmt.println(strings.to_delimiter_case("aBC", '_', false))
}
Output:
hello_world
HELLO WORLD
a_bc
strings.to_delimiter_case("Hello World", '_', false) -> hello_world
strings.to_delimiter_case("Hello World", ' ', true) -> HELLO WORLD
strings.to_delimiter_case("Hello World", ' ', true) -> HELLO WORLD
strings.to_delimiter_case("aBC", '_', false) -> a_b_c
*/
to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allocator := context.allocator) -> string {
to_delimiter_case :: proc(
s: string,
delimiter: rune,
all_upper_case: bool,
allocator := context.allocator,
) -> string {
s := s
s = trim_space(s)
b: Builder
@@ -237,73 +386,171 @@ to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allo
return to_string(b)
}
/*
Converts a string to "snake_case" with all runes lowercased
*Allocates Using Provided Allocator*
Inputs:
- s: The input string to be converted
- allocator: (default: context.allocator).
Returns:
The converted string
Example:
import "core:fmt"
import "core:strings"
to_snake_case_example :: proc() {
fmt.println(strings.to_snake_case("HelloWorld"))
fmt.println(strings.to_snake_case("Hello World"))
}
Output:
hello_world
hello_world
/*
converts the `s` string to "snake_case" with all runes lowercased
strings.to_snake_case("HelloWorld") -> hello_world
strings.to_snake_case("Hello World") -> hello_world
*/
to_snake_case :: proc(s: string, allocator := context.allocator) -> string {
return to_delimiter_case(s, '_', false, allocator)
}
// Alias for `to_upper_snake_case`
to_screaming_snake_case :: to_upper_snake_case
/*
Converts a string to "SNAKE_CASE" with all runes uppercased
// converts the `s` string to "SNAKE_CASE" with all runes uppercased
*Allocates Using Provided Allocator*
Inputs:
- s: The input string to be converted
- allocator: (default: context.allocator).
Returns:
The converted string
Example:
import "core:fmt"
import "core:strings"
to_upper_snake_case_example :: proc() {
fmt.println(strings.to_upper_snake_case("HelloWorld"))
}
Output:
HELLO_WORLD
*/
to_upper_snake_case :: proc(s: string, allocator := context.allocator) -> string {
return to_delimiter_case(s, '_', true, allocator)
}
/*
Converts a string to "kebab-case" with all runes lowercased
// converts the `s` string to "kebab-case" with all runes lowercased
*Allocates Using Provided Allocator*
Inputs:
- s: The input string to be converted
- allocator: (default: context.allocator).
Returns:
The converted string
Example:
import "core:fmt"
import "core:strings"
to_kebab_case_example :: proc() {
fmt.println(strings.to_kebab_case("HelloWorld"))
}
Output:
hello-world
*/
to_kebab_case :: proc(s: string, allocator := context.allocator) -> string {
return to_delimiter_case(s, '-', false, allocator)
}
/*
Converts a string to "KEBAB-CASE" with all runes uppercased
// converts the `s` string to "KEBAB-CASE" with all runes uppercased
*Allocates Using Provided Allocator*
Inputs:
- s: The input string to be converted
- allocator: (default: context.allocator).
Returns:
The converted string
Example:
import "core:fmt"
import "core:strings"
to_upper_kebab_case_example :: proc() {
fmt.println(strings.to_upper_kebab_case("HelloWorld"))
}
Output:
HELLO-WORLD
*/
to_upper_kebab_case :: proc(s: string, allocator := context.allocator) -> string {
return to_delimiter_case(s, '-', true, allocator)
}
/*
Converts a string to "Ada_Case"
// converts the `s` string to "Ada_case"
*Allocates Using Provided Allocator*
Inputs:
- s: The input string to be converted
- allocator: (default: context.allocator).
Returns:
The converted string
Example:
import "core:fmt"
import "core:strings"
to_ada_case_example :: proc() {
fmt.println(strings.to_ada_case("HelloWorld"))
}
Output:
Hello_World
*/
to_ada_case :: proc(s: string, allocator := context.allocator) -> string {
delimiter :: '_'
s := s
s = trim_space(s)
b: Builder
builder_init(&b, 0, len(s), allocator)
w := to_writer(&b)
prev, curr: rune
for next in s {
if is_delimiter(curr) {
if !is_delimiter(prev) {
io.write_rune(w, delimiter)
string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) {
if !is_delimiter(curr) {
if is_delimiter(prev) || prev == 0 || (unicode.is_lower(prev) && unicode.is_upper(curr)) {
if prev != 0 {
io.write_rune(w, '_')
}
io.write_rune(w, unicode.to_upper(curr))
} else {
io.write_rune(w, unicode.to_lower(curr))
}
} else if unicode.is_upper(curr) {
if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) {
io.write_rune(w, delimiter)
}
io.write_rune(w, unicode.to_upper(curr))
} else if curr != 0 {
io.write_rune(w, unicode.to_lower(curr))
}
prev = curr
curr = next
}
if len(s) > 0 {
if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 {
io.write_rune(w, delimiter)
io.write_rune(w, unicode.to_upper(curr))
} else {
io.write_rune(w, unicode.to_lower(curr))
}
}
})
return to_string(b)
}

View File

@@ -2,49 +2,99 @@ package strings
import "core:runtime"
// custom string entry struct
// Custom string entry struct
Intern_Entry :: struct {
len: int,
str: [1]byte, // string is allocated inline with the entry to keep allocations simple
}
/*
Intern is a more memory efficient string map
// "intern" is a more memory efficient string map
// `allocator` is used to allocate the actual `Intern_Entry` strings
Uses Specified Allocator for `Intern_Entry` strings
Fields:
- allocator: The allocator used for the Intern_Entry strings
- entries: A map of strings to interned string entries
*/
Intern :: struct {
allocator: runtime.Allocator,
entries: map[string]^Intern_Entry,
}
/*
Initializes the entries map and sets the allocator for the string entries
// initialize the entries map and set the allocator for the string entries
*Allocates Using Provided Allocators*
Inputs:
- m: A pointer to the Intern struct to be initialized
- allocator: The allocator for the Intern_Entry strings (Default: context.allocator)
- map_allocator: The allocator for the map of entries (Default: context.allocator)
*/
intern_init :: proc(m: ^Intern, allocator := context.allocator, map_allocator := context.allocator) {
m.allocator = allocator
m.entries = make(map[string]^Intern_Entry, 16, map_allocator)
}
/*
Frees the map and all its content allocated using the `.allocator`.
// free the map and all its content allocated using the `.allocator`
Inputs:
- m: A pointer to the Intern struct to be destroyed
*/
intern_destroy :: proc(m: ^Intern) {
for _, value in m.entries {
free(value, m.allocator)
}
delete(m.entries)
}
/*
Returns an interned copy of the given text, adding it to the map if not already present.
// returns the `text` string from the intern map - gets set if it didnt exist yet
// the returned string lives as long as the map entry lives
*Allocate using the Intern's Allocator (First time string is seen only)*
Inputs:
- m: A pointer to the Intern struct
- text: The string to be interned
NOTE: The returned string lives as long as the map entry lives.
Returns:
The interned string and an allocator error if any
*/
intern_get :: proc(m: ^Intern, text: string) -> (str: string, err: runtime.Allocator_Error) {
entry := _intern_get_entry(m, text) or_return
#no_bounds_check return string(entry.str[:entry.len]), nil
}
/*
Returns an interned copy of the given text as a cstring, adding it to the map if not already present.
// returns the `text` cstring from the intern map - gets set if it didnt exist yet
// the returned cstring lives as long as the map entry lives
*Allocate using the Intern's Allocator (First time string is seen only)*
Inputs:
- m: A pointer to the Intern struct
- text: The string to be interned
NOTE: The returned cstring lives as long as the map entry lives
Returns:
The interned cstring and an allocator error if any
*/
intern_get_cstring :: proc(m: ^Intern, text: string) -> (str: cstring, err: runtime.Allocator_Error) {
entry := _intern_get_entry(m, text) or_return
return cstring(&entry.str[0]), nil
}
/*
Internal function to lookup whether the text string exists in the map, returns the entry
Sets and allocates the entry if it wasn't set yet
// looks up wether the `text` string exists in the map, returns the entry
// sets & allocates the entry if it wasnt set yet
*Allocate using the Intern's Allocator (First time string is seen only)*
Inputs:
- m: A pointer to the Intern struct
- text: The string to be looked up or interned
Returns:
The new or existing interned entry and an allocator error if any
*/
_intern_get_entry :: proc(m: ^Intern, text: string) -> (new_entry: ^Intern_Entry, err: runtime.Allocator_Error) #no_bounds_check {
if prev, ok := m.entries[text]; ok {
return prev, nil

View File

@@ -4,59 +4,109 @@ import "core:io"
import "core:unicode/utf8"
/*
io stream data for a string reader that can read based on bytes or runes
implements the vtable when using the io.Reader variants
"read" calls advance the current reading offset `i`
io stream data for a string reader that can read based on bytes or runes
implements the vtable when using the `io.Reader` variants
"read" calls advance the current reading offset `i`
*/
Reader :: struct {
s: string, // read-only buffer
i: i64, // current reading index
prev_rune: int, // previous reading index of rune or < 0
}
/*
Initializes a string Reader with the provided string
// init the reader to the string `s`
Inputs:
- r: A pointer to a Reader struct
- s: The input string to be read
*/
reader_init :: proc(r: ^Reader, s: string) {
r.s = s
r.i = 0
r.prev_rune = -1
}
/*
Converts a Reader into an `io.Stream`
// returns a stream from the reader data
Inputs:
- r: A pointer to a Reader struct
Returns:
An io.Stream for the given Reader
*/
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
s.stream_data = r
s.stream_vtable = &_reader_vtable
return
}
/*
Initializes a string Reader and returns an `io.Reader` for the given string
// init a reader to the string `s` and return an io.Reader
Inputs:
- r: A pointer to a Reader struct
- s: The input string to be read
Returns:
An io.Reader for the given string
*/
to_reader :: proc(r: ^Reader, s: string) -> io.Reader {
reader_init(r, s)
rr, _ := io.to_reader(reader_to_stream(r))
return rr
}
/*
Initializes a string Reader and returns an `io.Reader_At` for the given string
// init a reader to the string `s` and return an io.Reader_At
Inputs:
- r: A pointer to a Reader struct
- s: The input string to be read
Returns:
An `io.Reader_At` for the given string
*/
to_reader_at :: proc(r: ^Reader, s: string) -> io.Reader_At {
reader_init(r, s)
rr, _ := io.to_reader_at(reader_to_stream(r))
return rr
}
/*
Returns the remaining length of the Reader
// remaining length of the reader
Inputs:
- r: A pointer to a Reader struct
Returns:
The remaining length of the Reader
*/
reader_length :: proc(r: ^Reader) -> int {
if r.i >= i64(len(r.s)) {
return 0
}
return int(i64(len(r.s)) - r.i)
}
/*
Returns the length of the string stored in the Reader
// returns the string length stored by the reader
Inputs:
- r: A pointer to a Reader struct
Returns:
The length of the string stored in the Reader
*/
reader_size :: proc(r: ^Reader) -> i64 {
return i64(len(r.s))
}
/*
Reads len(p) bytes from the Reader's string and copies into the provided slice.
// reads len(p) bytes into the slice from the string in the reader
// returns `n` amount of read bytes and an io.Error
Inputs:
- r: A pointer to a Reader struct
- p: A byte slice to copy data into
Returns:
- n: The number of bytes read
- err: An `io.Error` if an error occurs while reading, including `.EOF`, otherwise `nil` denotes success.
*/
reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
if r.i >= i64(len(r.s)) {
return 0, .EOF
@@ -66,9 +116,18 @@ reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
r.i += i64(n)
return
}
/*
Reads len(p) bytes from the Reader's string and copies into the provided slice, at the specified offset from the current index.
// reads len(p) bytes into the slice from the string in the reader at an offset
// returns `n` amount of read bytes and an io.Error
Inputs:
- r: A pointer to a Reader struct
- p: A byte slice to copy data into
- off: The offset from which to read
Returns:
- n: The number of bytes read
- err: An `io.Error` if an error occurs while reading, including `.EOF`, otherwise `nil` denotes success.
*/
reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) {
if off < 0 {
return 0, .Invalid_Offset
@@ -82,8 +141,16 @@ reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Erro
}
return
}
/*
Reads and returns a single byte from the Reader's string
// reads and returns a single byte - error when out of bounds
Inputs:
- r: A pointer to a Reader struct
Returns:
- The byte read from the Reader
- err: An `io.Error` if an error occurs while reading, including `.EOF`, otherwise `nil` denotes success.
*/
reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
r.prev_rune = -1
if r.i >= i64(len(r.s)) {
@@ -93,8 +160,15 @@ reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
r.i += 1
return b, nil
}
/*
Decrements the Reader's index (i) by 1
// decreases the reader offset - error when below 0
Inputs:
- r: A pointer to a Reader struct
Returns:
An `io.Error` if `r.i <= 0` (`.Invalid_Unread`), otherwise `nil` denotes success.
*/
reader_unread_byte :: proc(r: ^Reader) -> io.Error {
if r.i <= 0 {
return .Invalid_Unread
@@ -103,9 +177,18 @@ reader_unread_byte :: proc(r: ^Reader) -> io.Error {
r.i -= 1
return nil
}
/*
Reads and returns a single rune and its `size` from the Reader's string
// reads and returns a single rune and the rune size - error when out bounds
reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) {
Inputs:
- r: A pointer to a Reader struct
Returns:
- rr: The rune read from the Reader
- size: The size of the rune in bytes
- err: An `io.Error` if an error occurs while reading
*/
reader_read_rune :: proc(r: ^Reader) -> (rr: rune, size: int, err: io.Error) {
if r.i >= i64(len(r.s)) {
r.prev_rune = -1
return 0, 0, .EOF
@@ -115,13 +198,21 @@ reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) {
r.i += 1
return rune(c), 1, nil
}
ch, size = utf8.decode_rune_in_string(r.s[r.i:])
rr, size = utf8.decode_rune_in_string(r.s[r.i:])
r.i += i64(size)
return
}
/*
Decrements the Reader's index (i) by the size of the last read rune
// decreases the reader offset by the last rune
// can only be used once and after a valid read_rune call
Inputs:
- r: A pointer to a Reader struct
WARNING: May only be used once and after a valid `read_rune` call
Returns:
An `io.Error` if an error occurs while unreading (`.Invalid_Unread`), else `nil` denotes success.
*/
reader_unread_rune :: proc(r: ^Reader) -> io.Error {
if r.i <= 0 {
return .Invalid_Unread
@@ -133,8 +224,18 @@ reader_unread_rune :: proc(r: ^Reader) -> io.Error {
r.prev_rune = -1
return nil
}
/*
Seeks the Reader's index to a new position
// seeks the reader offset to a wanted offset
Inputs:
- r: A pointer to a Reader struct
- offset: The new offset position
- whence: The reference point for the new position (`.Start`, `.Current`, or `.End`)
Returns:
- The absolute offset after seeking
- err: An `io.Error` if an error occurs while seeking (`.Invalid_Whence`, `.Invalid_Offset`)
*/
reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
r.prev_rune = -1
abs: i64
@@ -155,8 +256,19 @@ reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.E
r.i = abs
return abs, nil
}
/*
Writes the remaining content of the Reader's string into the provided `io.Writer`
// writes the string content left to read into the io.Writer `w`
Inputs:
- r: A pointer to a Reader struct
- w: The io.Writer to write the remaining content into
WARNING: Panics if writer writes more bytes than remainig length of string.
Returns:
- n: The number of bytes written
- err: An io.Error if an error occurs while writing (`.Short_Write`)
*/
reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
r.prev_rune = -1
if r.i >= i64(len(r.s)) {
@@ -175,7 +287,12 @@ reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
}
return
}
/*
VTable containing implementations for various `io.Stream` methods
This VTable is used by the Reader struct to provide its functionality
as an `io.Stream`.
*/
@(private)
_reader_vtable := io.Stream_VTable{
impl_size = proc(s: io.Stream) -> i64 {

File diff suppressed because it is too large Load Diff

View File

@@ -393,6 +393,8 @@ foreign kernel32 {
GetConsoleScreenBufferInfo :: proc(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: PCONSOLE_SCREEN_BUFFER_INFO) -> BOOL ---
SetConsoleScreenBufferSize :: proc(hConsoleOutput: HANDLE, dwSize: COORD) -> BOOL ---
SetConsoleWindowInfo :: proc(hConsoleOutput: HANDLE, bAbsolute : BOOL, lpConsoleWindow: ^SMALL_RECT) -> BOOL ---
GetConsoleCursorInfo :: proc(hConsoleOutput: HANDLE, lpConsoleCursorInfo: PCONSOLE_CURSOR_INFO) -> BOOL ---
SetConsoleCursorInfo :: proc(hConsoleOutput: HANDLE, lpConsoleCursorInfo: PCONSOLE_CURSOR_INFO) -> BOOL ---
GetDiskFreeSpaceExW :: proc(
lpDirectoryName: LPCWSTR,

View File

@@ -23,6 +23,8 @@ foreign shell32 {
SHFileOperationW :: proc(lpFileOp: LPSHFILEOPSTRUCTW) -> c_int ---
SHGetFolderPathW :: proc(hwnd: HWND, csidl: c_int, hToken: HANDLE, dwFlags: DWORD, pszPath: LPWSTR) -> HRESULT ---
SHAppBarMessage :: proc(dwMessage: DWORD, pData: PAPPBARDATA) -> UINT_PTR ---
Shell_NotifyIconW :: proc(dwMessage: DWORD, lpData: ^NOTIFYICONDATAW) -> BOOL ---
}
APPBARDATA :: struct {

View File

@@ -145,8 +145,6 @@ PCONDITION_VARIABLE :: ^CONDITION_VARIABLE
PLARGE_INTEGER :: ^LARGE_INTEGER
PSRWLOCK :: ^SRWLOCK
MMRESULT :: UINT
CREATE_WAITABLE_TIMER_MANUAL_RESET :: 0x00000001
CREATE_WAITABLE_TIMER_HIGH_RESOLUTION :: 0x00000002
@@ -261,26 +259,6 @@ GET_FILEEX_INFO_LEVELS :: distinct i32
GetFileExInfoStandard: GET_FILEEX_INFO_LEVELS : 0
GetFileExMaxInfoLevel: GET_FILEEX_INFO_LEVELS : 1
// String resource number bases (internal use)
MMSYSERR_BASE :: 0
WAVERR_BASE :: 32
MIDIERR_BASE :: 64
TIMERR_BASE :: 96
JOYERR_BASE :: 160
MCIERR_BASE :: 256
MIXERR_BASE :: 1024
MCI_STRING_OFFSET :: 512
MCI_VD_OFFSET :: 1024
MCI_CD_OFFSET :: 1088
MCI_WAVE_OFFSET :: 1152
MCI_SEQ_OFFSET :: 1216
// timer error return values
TIMERR_NOERROR :: 0 // no error
TIMERR_NOCANDO :: TIMERR_BASE + 1 // request not completed
TIMERR_STRUCT :: TIMERR_BASE + 33 // time struct size
DIAGNOSTIC_REASON_VERSION :: 0
@@ -724,6 +702,14 @@ CWPRETSTRUCT :: struct {
hwnd: HWND,
}
MSLLHOOKSTRUCT :: struct {
pt: POINT,
mouseData: DWORD,
flags: DWORD,
time: DWORD,
dwExtraInfo: ULONG_PTR,
}
KBDLLHOOKSTRUCT :: struct {
vkCode: DWORD,
scanCode: DWORD,
@@ -732,6 +718,59 @@ KBDLLHOOKSTRUCT :: struct {
dwExtraInfo: ULONG_PTR,
}
MOUSEINPUT :: struct {
dx: LONG,
dy: LONG,
mouseData: DWORD,
dwFlags: DWORD,
time: DWORD,
dwExtraInfo: ULONG_PTR,
}
KEYBDINPUT :: struct {
wVk: WORD,
wScan: WORD,
dwFlags: DWORD,
time: DWORD,
dwExtraInfo: ULONG_PTR,
}
HARDWAREINPUT :: struct {
uMsg: DWORD,
wParamL: WORD,
wParamH: WORD,
}
INPUT_TYPE :: enum DWORD {
MOUSE = 0,
KEYBOARD = 1,
HARDWARE = 2,
}
INPUT :: struct {
type: INPUT_TYPE,
using _: struct #raw_union {
mi: MOUSEINPUT,
ki: KEYBDINPUT,
hi: HARDWAREINPUT,
},
}
MOUSEEVENTF_MOVE :: 0x0001
MOUSEEVENTF_LEFTDOWN :: 0x0002
MOUSEEVENTF_LEFTUP :: 0x0004
MOUSEEVENTF_RIGHTDOWN :: 0x0008
MOUSEEVENTF_RIGHTUP :: 0x0010
MOUSEEVENTF_MIDDLEDOWN :: 0x0020
MOUSEEVENTF_MIDDLEUP :: 0x0040
MOUSEEVENTF_XDOWN :: 0x0080
MOUSEEVENTF_XUP :: 0x0100
MOUSEEVENTF_WHEEL :: 0x0800
MOUSEEVENTF_HWHEEL :: 0x1000
MOUSEEVENTF_MOVE_NOCOALESCE :: 0x2000
MOUSEEVENTF_VIRTUALDESK :: 0x4000
MOUSEEVENTF_ABSOLUTE :: 0x8000
WNDCLASSA :: struct {
style: UINT,
lpfnWndProc: WNDPROC,
@@ -799,6 +838,104 @@ MSG :: struct {
LPMSG :: ^MSG
NOTIFYICONDATAW :: struct {
cbSize: DWORD,
hWnd: HWND,
uID: UINT,
uFlags: UINT,
uCallbackMessage: UINT,
hIcon: HICON,
szTip: [128]WCHAR,
dwState: DWORD,
dwStateMask: DWORD,
szInfo: [256]WCHAR,
using _: struct #raw_union {
uTimeout: UINT,
uVersion: UINT,
},
szInfoTitle: [64]WCHAR,
dwInfoFlags: DWORD,
guidItem: GUID,
hBalloonIcon: HICON,
}
NIF_MESSAGE :: 0x00000001
NIF_ICON :: 0x00000002
NIF_TIP :: 0x00000004
NIF_STATE :: 0x00000008
NIF_INFO :: 0x00000010
NIF_GUID :: 0x00000020
NIF_REALTIME :: 0x00000040
NIF_SHOWTIP :: 0x00000080
NIM_ADD :: 0x00000000
NIM_MODIFY :: 0x00000001
NIM_DELETE :: 0x00000002
NIM_SETFOCUS :: 0x00000003
NIM_SETVERSION :: 0x00000004
// Menu flags for Add/Check/EnableMenuItem()
MF_INSERT :: 0x00000000
MF_CHANGE :: 0x00000080
MF_APPEND :: 0x00000100
MF_DELETE :: 0x00000200
MF_REMOVE :: 0x00001000
MF_BYCOMMAND :: 0x00000000
MF_BYPOSITION :: 0x00000400
MF_SEPARATOR :: 0x00000800
MF_ENABLED :: 0x00000000
MF_GRAYED :: 0x00000001
MF_DISABLED :: 0x00000002
MF_UNCHECKED :: 0x00000000
MF_CHECKED :: 0x00000008
MF_USECHECKBITMAPS :: 0x00000200
MF_STRING :: 0x00000000
MF_BITMAP :: 0x00000004
MF_OWNERDRAW :: 0x00000100
MF_POPUP :: 0x00000010
MF_MENUBARBREAK :: 0x00000020
MF_MENUBREAK :: 0x00000040
MF_UNHILITE :: 0x00000000
MF_HILITE :: 0x00000080
MF_DEFAULT :: 0x00001000
MF_SYSMENU :: 0x00002000
MF_HELP :: 0x00004000
MF_RIGHTJUSTIFY :: 0x00004000
MF_MOUSESELECT :: 0x00008000
MF_END :: 0x00000080 // Obsolete -- only used by old RES files
// Flags for TrackPopupMenu
TPM_LEFTBUTTON :: 0x0000
TPM_RIGHTBUTTON :: 0x0002
TPM_LEFTALIGN :: 0x0000
TPM_CENTERALIGN :: 0x0004
TPM_RIGHTALIGN :: 0x0008
TPM_TOPALIGN :: 0x0000
TPM_VCENTERALIGN :: 0x0010
TPM_BOTTOMALIGN :: 0x0020
TPM_HORIZONTAL :: 0x0000 /* Horz alignment matters more */
TPM_VERTICAL :: 0x0040 /* Vert alignment matters more */
TPM_NONOTIFY :: 0x0080 /* Don't send any notification msgs */
TPM_RETURNCMD :: 0x0100
TPM_RECURSE :: 0x0001
TPM_HORPOSANIMATION :: 0x0400
TPM_HORNEGANIMATION :: 0x0800
TPM_VERPOSANIMATION :: 0x1000
TPM_VERNEGANIMATION :: 0x2000
TPM_NOANIMATION :: 0x4000
TPM_LAYOUTRTL :: 0x8000
TPM_WORKAREA :: 0x10000
// WM_NCHITTEST and MOUSEHOOKSTRUCT Mouse Position Codes
HTERROR :: -2
HTTRANSPARENT :: -1
@@ -3782,8 +3919,14 @@ CONSOLE_SCREEN_BUFFER_INFO :: struct {
dwMaximumWindowSize: COORD,
}
CONSOLE_CURSOR_INFO :: struct {
dwSize: DWORD,
bVisible: BOOL,
}
PCONSOLE_SCREEN_BUFFER_INFO :: ^CONSOLE_SCREEN_BUFFER_INFO
PCONSOLE_CURSOR_INFO :: ^CONSOLE_CURSOR_INFO
//
// Networking
@@ -4018,4 +4161,4 @@ DNS_SRV_DATAA :: struct {
SOCKADDR :: struct {
sa_family: ADDRESS_FAMILY,
sa_data: [14]CHAR,
}
}

View File

@@ -109,6 +109,12 @@ foreign user32 {
GetDlgCtrlID :: proc(hWnd: HWND) -> c_int ---
GetDlgItem :: proc(hDlg: HWND, nIDDlgItem: c_int) -> HWND ---
CreatePopupMenu :: proc() -> HMENU ---
DestroyMenu :: proc(hMenu: HMENU) -> BOOL ---
AppendMenuW :: proc(hMenu: HMENU, uFlags: UINT, uIDNewItem: UINT_PTR, lpNewItem: LPCWSTR) -> BOOL ---
TrackPopupMenu :: proc(hMenu: HMENU, uFlags: UINT, x: int, y: int, nReserved: int, hWnd: HWND, prcRect: ^RECT) -> i32 ---
RegisterWindowMessageW :: proc(lpString: LPCWSTR) -> UINT ---
GetUpdateRect :: proc(hWnd: HWND, lpRect: LPRECT, bErase: BOOL) -> BOOL ---
ValidateRect :: proc(hWnd: HWND, lpRect: ^RECT) -> BOOL ---
InvalidateRect :: proc(hWnd: HWND, lpRect: ^RECT, bErase: BOOL) -> BOOL ---
@@ -206,6 +212,8 @@ foreign user32 {
GetRegisteredRawInputDevices :: proc(pRawInputDevices: PRAWINPUTDEVICE, puiNumDevices: PUINT, cbSize: UINT) -> UINT ---
RegisterRawInputDevices :: proc(pRawInputDevices: PCRAWINPUTDEVICE, uiNumDevices: UINT, cbSize: UINT) -> BOOL ---
SendInput :: proc(cInputs: UINT, pInputs: [^]INPUT, cbSize: ^c_int) -> UINT ---
SetLayeredWindowAttributes :: proc(hWnd: HWND, crKey: COLORREF, bAlpha: BYTE, dwFlags: DWORD) -> BOOL ---
FillRect :: proc(hDC: HDC, lprc: ^RECT, hbr: HBRUSH) -> int ---
@@ -469,4 +477,4 @@ WINDOWINFO :: struct {
atomWindowType: ATOM,
wCreatorVersion: WORD,
}
PWINDOWINFO :: ^WINDOWINFO
PWINDOWINFO :: ^WINDOWINFO

View File

@@ -3,9 +3,170 @@ package sys_windows
foreign import winmm "system:Winmm.lib"
MMRESULT :: UINT
@(default_calling_convention="stdcall")
foreign winmm {
timeGetDevCaps :: proc(ptc: LPTIMECAPS, cbtc: UINT) -> MMRESULT ---
timeBeginPeriod :: proc(uPeriod: UINT) -> MMRESULT ---
timeEndPeriod :: proc(uPeriod: UINT) -> MMRESULT ---
timeGetTime :: proc() -> DWORD ---
}
LPTIMECAPS :: ^TIMECAPS
TIMECAPS :: struct {
wPeriodMin: UINT,
wPeriodMax: UINT,
}
// String resource number bases (internal use)
MMSYSERR_BASE :: 0
WAVERR_BASE :: 32
MIDIERR_BASE :: 64
TIMERR_BASE :: 96
JOYERR_BASE :: 160
MCIERR_BASE :: 256
MIXERR_BASE :: 1024
MCI_STRING_OFFSET :: 512
MCI_VD_OFFSET :: 1024
MCI_CD_OFFSET :: 1088
MCI_WAVE_OFFSET :: 1152
MCI_SEQ_OFFSET :: 1216
/* general error return values */
MMSYSERR_NOERROR :: 0 /* no error */
MMSYSERR_ERROR :: MMSYSERR_BASE + 1 /* unspecified error */
MMSYSERR_BADDEVICEID :: MMSYSERR_BASE + 2 /* device ID out of range */
MMSYSERR_NOTENABLED :: MMSYSERR_BASE + 3 /* driver failed enable */
MMSYSERR_ALLOCATED :: MMSYSERR_BASE + 4 /* device already allocated */
MMSYSERR_INVALHANDLE :: MMSYSERR_BASE + 5 /* device handle is invalid */
MMSYSERR_NODRIVER :: MMSYSERR_BASE + 6 /* no device driver present */
MMSYSERR_NOMEM :: MMSYSERR_BASE + 7 /* memory allocation error */
MMSYSERR_NOTSUPPORTED :: MMSYSERR_BASE + 8 /* function isn't supported */
MMSYSERR_BADERRNUM :: MMSYSERR_BASE + 9 /* error value out of range */
MMSYSERR_INVALFLAG :: MMSYSERR_BASE + 10 /* invalid flag passed */
MMSYSERR_INVALPARAM :: MMSYSERR_BASE + 11 /* invalid parameter passed */
MMSYSERR_HANDLEBUSY :: MMSYSERR_BASE + 12 /* handle being used simultaneously on another thread (eg callback) */
MMSYSERR_INVALIDALIAS :: MMSYSERR_BASE + 13 /* specified alias not found */
MMSYSERR_BADDB :: MMSYSERR_BASE + 14 /* bad registry database */
MMSYSERR_KEYNOTFOUND :: MMSYSERR_BASE + 15 /* registry key not found */
MMSYSERR_READERROR :: MMSYSERR_BASE + 16 /* registry read error */
MMSYSERR_WRITEERROR :: MMSYSERR_BASE + 17 /* registry write error */
MMSYSERR_DELETEERROR :: MMSYSERR_BASE + 18 /* registry delete error */
MMSYSERR_VALNOTFOUND :: MMSYSERR_BASE + 19 /* registry value not found */
MMSYSERR_NODRIVERCB :: MMSYSERR_BASE + 20 /* driver does not call DriverCallback */
MMSYSERR_MOREDATA :: MMSYSERR_BASE + 21 /* more data to be returned */
MMSYSERR_LASTERROR :: MMSYSERR_BASE + 21 /* last error in range */
/* waveform audio error return values */
WAVERR_BADFORMAT :: WAVERR_BASE + 0 /* unsupported wave format */
WAVERR_STILLPLAYING :: WAVERR_BASE + 1 /* still something playing */
WAVERR_UNPREPARED :: WAVERR_BASE + 2 /* header not prepared */
WAVERR_SYNC :: WAVERR_BASE + 3 /* device is synchronous */
WAVERR_LASTERROR :: WAVERR_BASE + 3 /* last error in range */
/* MIDI error return values */
MIDIERR_UNPREPARED :: MIDIERR_BASE + 0 /* header not prepared */
MIDIERR_STILLPLAYING :: MIDIERR_BASE + 1 /* still something playing */
MIDIERR_NOMAP :: MIDIERR_BASE + 2 /* no configured instruments */
MIDIERR_NOTREADY :: MIDIERR_BASE + 3 /* hardware is still busy */
MIDIERR_NODEVICE :: MIDIERR_BASE + 4 /* port no longer connected */
MIDIERR_INVALIDSETUP :: MIDIERR_BASE + 5 /* invalid MIF */
MIDIERR_BADOPENMODE :: MIDIERR_BASE + 6 /* operation unsupported w/ open mode */
MIDIERR_DONT_CONTINUE :: MIDIERR_BASE + 7 /* thru device 'eating' a message */
MIDIERR_LASTERROR :: MIDIERR_BASE + 7 /* last error in range */
/* timer error return values */
TIMERR_NOERROR :: 0 /* no error */
TIMERR_NOCANDO :: TIMERR_BASE + 1 /* request not completed */
TIMERR_STRUCT :: TIMERR_BASE + 33 /* time struct size */
/* joystick error return values */
JOYERR_NOERROR :: 0 /* no error */
JOYERR_PARMS :: JOYERR_BASE + 5 /* bad parameters */
JOYERR_NOCANDO :: JOYERR_BASE + 6 /* request not completed */
JOYERR_UNPLUGGED :: JOYERR_BASE + 7 /* joystick is unplugged */
/* MCI error return values */
MCIERR_INVALID_DEVICE_ID :: MCIERR_BASE + 1
MCIERR_UNRECOGNIZED_KEYWORD :: MCIERR_BASE + 3
MCIERR_UNRECOGNIZED_COMMAND :: MCIERR_BASE + 5
MCIERR_HARDWARE :: MCIERR_BASE + 6
MCIERR_INVALID_DEVICE_NAME :: MCIERR_BASE + 7
MCIERR_OUT_OF_MEMORY :: MCIERR_BASE + 8
MCIERR_DEVICE_OPEN :: MCIERR_BASE + 9
MCIERR_CANNOT_LOAD_DRIVER :: MCIERR_BASE + 10
MCIERR_MISSING_COMMAND_STRING :: MCIERR_BASE + 11
MCIERR_PARAM_OVERFLOW :: MCIERR_BASE + 12
MCIERR_MISSING_STRING_ARGUMENT :: MCIERR_BASE + 13
MCIERR_BAD_INTEGER :: MCIERR_BASE + 14
MCIERR_PARSER_INTERNAL :: MCIERR_BASE + 15
MCIERR_DRIVER_INTERNAL :: MCIERR_BASE + 16
MCIERR_MISSING_PARAMETER :: MCIERR_BASE + 17
MCIERR_UNSUPPORTED_FUNCTION :: MCIERR_BASE + 18
MCIERR_FILE_NOT_FOUND :: MCIERR_BASE + 19
MCIERR_DEVICE_NOT_READY :: MCIERR_BASE + 20
MCIERR_INTERNAL :: MCIERR_BASE + 21
MCIERR_DRIVER :: MCIERR_BASE + 22
MCIERR_CANNOT_USE_ALL :: MCIERR_BASE + 23
MCIERR_MULTIPLE :: MCIERR_BASE + 24
MCIERR_EXTENSION_NOT_FOUND :: MCIERR_BASE + 25
MCIERR_OUTOFRANGE :: MCIERR_BASE + 26
MCIERR_FLAGS_NOT_COMPATIBLE :: MCIERR_BASE + 28
MCIERR_FILE_NOT_SAVED :: MCIERR_BASE + 30
MCIERR_DEVICE_TYPE_REQUIRED :: MCIERR_BASE + 31
MCIERR_DEVICE_LOCKED :: MCIERR_BASE + 32
MCIERR_DUPLICATE_ALIAS :: MCIERR_BASE + 33
MCIERR_BAD_CONSTANT :: MCIERR_BASE + 34
MCIERR_MUST_USE_SHAREABLE :: MCIERR_BASE + 35
MCIERR_MISSING_DEVICE_NAME :: MCIERR_BASE + 36
MCIERR_BAD_TIME_FORMAT :: MCIERR_BASE + 37
MCIERR_NO_CLOSING_QUOTE :: MCIERR_BASE + 38
MCIERR_DUPLICATE_FLAGS :: MCIERR_BASE + 39
MCIERR_INVALID_FILE :: MCIERR_BASE + 40
MCIERR_NULL_PARAMETER_BLOCK :: MCIERR_BASE + 41
MCIERR_UNNAMED_RESOURCE :: MCIERR_BASE + 42
MCIERR_NEW_REQUIRES_ALIAS :: MCIERR_BASE + 43
MCIERR_NOTIFY_ON_AUTO_OPEN :: MCIERR_BASE + 44
MCIERR_NO_ELEMENT_ALLOWED :: MCIERR_BASE + 45
MCIERR_NONAPPLICABLE_FUNCTION :: MCIERR_BASE + 46
MCIERR_ILLEGAL_FOR_AUTO_OPEN :: MCIERR_BASE + 47
MCIERR_FILENAME_REQUIRED :: MCIERR_BASE + 48
MCIERR_EXTRA_CHARACTERS :: MCIERR_BASE + 49
MCIERR_DEVICE_NOT_INSTALLED :: MCIERR_BASE + 50
MCIERR_GET_CD :: MCIERR_BASE + 51
MCIERR_SET_CD :: MCIERR_BASE + 52
MCIERR_SET_DRIVE :: MCIERR_BASE + 53
MCIERR_DEVICE_LENGTH :: MCIERR_BASE + 54
MCIERR_DEVICE_ORD_LENGTH :: MCIERR_BASE + 55
MCIERR_NO_INTEGER :: MCIERR_BASE + 56
MCIERR_WAVE_OUTPUTSINUSE :: MCIERR_BASE + 64
MCIERR_WAVE_SETOUTPUTINUSE :: MCIERR_BASE + 65
MCIERR_WAVE_INPUTSINUSE :: MCIERR_BASE + 66
MCIERR_WAVE_SETINPUTINUSE :: MCIERR_BASE + 67
MCIERR_WAVE_OUTPUTUNSPECIFIED :: MCIERR_BASE + 68
MCIERR_WAVE_INPUTUNSPECIFIED :: MCIERR_BASE + 69
MCIERR_WAVE_OUTPUTSUNSUITABLE :: MCIERR_BASE + 70
MCIERR_WAVE_SETOUTPUTUNSUITABLE :: MCIERR_BASE + 71
MCIERR_WAVE_INPUTSUNSUITABLE :: MCIERR_BASE + 72
MCIERR_WAVE_SETINPUTUNSUITABLE :: MCIERR_BASE + 73
MCIERR_SEQ_DIV_INCOMPATIBLE :: MCIERR_BASE + 80
MCIERR_SEQ_PORT_INUSE :: MCIERR_BASE + 81
MCIERR_SEQ_PORT_NONEXISTENT :: MCIERR_BASE + 82
MCIERR_SEQ_PORT_MAPNODEVICE :: MCIERR_BASE + 83
MCIERR_SEQ_PORT_MISCERROR :: MCIERR_BASE + 84
MCIERR_SEQ_TIMER :: MCIERR_BASE + 85
MCIERR_SEQ_PORTUNSPECIFIED :: MCIERR_BASE + 86
MCIERR_SEQ_NOMIDIPRESENT :: MCIERR_BASE + 87
MCIERR_NO_WINDOW :: MCIERR_BASE + 90
MCIERR_CREATEWINDOW :: MCIERR_BASE + 91
MCIERR_FILE_READ :: MCIERR_BASE + 92
MCIERR_FILE_WRITE :: MCIERR_BASE + 93
MCIERR_NO_IDENTITY :: MCIERR_BASE + 94
/* MMRESULT error return values specific to the mixer API */
MIXERR_INVALLINE :: (MIXERR_BASE + 0)
MIXERR_INVALCONTROL :: (MIXERR_BASE + 1)
MIXERR_INVALVALUE :: (MIXERR_BASE + 2)
MIXERR_LASTERROR :: (MIXERR_BASE + 2)

100
core/text/table/doc.odin Normal file
View File

@@ -0,0 +1,100 @@
/*
package table implements ascii/markdown/html/custom rendering of tables.
---
Custom rendering example:
```odin
tbl := init(&Table{})
padding(tbl, 0, 1)
row(tbl, "A_LONG_ENUM", "= 54,", "// A comment about A_LONG_ENUM")
row(tbl, "AN_EVEN_LONGER_ENUM", "= 1,", "// A comment about AN_EVEN_LONGER_ENUM")
build(tbl)
for row in 0..<tbl.nr_rows {
for col in 0..<tbl.nr_cols {
write_table_cell(stdio_writer(), tbl, row, col)
}
io.write_byte(stdio_writer(), '\n')
}
```
This outputs:
```
A_LONG_ENUM = 54, // A comment about A_LONG_ENUM
AN_EVEN_LONGER_ENUM = 1, // A comment about AN_EVEN_LONGER_ENUM
```
---
ASCII rendering example:
```odin
tbl := init(&Table{})
defer destroy(tbl)
caption(tbl, "This is a table caption and it is very long")
padding(tbl, 1, 1) // Left/right padding of cells
header(tbl, "AAAAAAAAA", "B")
header(tbl, "C") // Appends to previous header row. Same as if done header("AAAAAAAAA", "B", "C") from start.
// Create a row with two values. Since there are three columns the third
// value will become the empty string.
//
// NOTE: header() is not allowed anymore after this.
row(tbl, 123, "foo")
// Use `format()` if you need custom formatting. This will allocate into
// the arena specified at init.
row(tbl,
format(tbl, "%09d", 5),
format(tbl, "%.6f", 6.28318530717958647692528676655900576))
// A row with zero values is allowed as long as a previous row or header
// exist. The value and alignment of each cell can then be set
// individually.
row(tbl)
set_cell_value_and_alignment(tbl, last_row(tbl), 0, "a", .Center)
set_cell_value(tbl, last_row(tbl), 1, "bbb")
set_cell_value(tbl, last_row(tbl), 2, "c")
// Headers are regular cells, too. Use header_row() as row index to modify
// header cells.
set_cell_alignment(tbl, header_row(tbl), 1, .Center) // Sets alignment of 'B' column to Center.
set_cell_alignment(tbl, header_row(tbl), 2, .Right) // Sets alignment of 'C' column to Right.
build(tbl)
write_ascii_table(stdio_writer(), tbl)
write_markdown_table(stdio_writer(), tbl)
```
This outputs:
```
+-----------------------------------------------+
| This is a table caption and it is very long |
+------------------+-----------------+----------+
| AAAAAAAAA | B | C |
+------------------+-----------------+----------+
| 123 | foo | |
| 000000005 | 6.283185 | |
| a | bbb | c |
+------------------+-----------------+----------+
```
and
```
| AAAAAAAAA | B | C |
|:-----------------|:---------------:|---------:|
| 123 | foo | |
| 000000005 | 6.283185 | |
| a | bbb | c |
```
respectively.
*/
package text_table

384
core/text/table/table.odin Normal file
View File

@@ -0,0 +1,384 @@
/*
Copyright 2023 oskarnp <oskarnp@proton.me>
Made available under Odin's BSD-3 license.
List of contributors:
oskarnp: Initial implementation.
*/
package text_table
import "core:io"
import "core:os"
import "core:fmt"
import "core:mem"
import "core:mem/virtual"
import "core:runtime"
import "core:strings"
Cell :: struct {
text: string,
alignment: Cell_Alignment,
}
Cell_Alignment :: enum {
Left,
Center,
Right,
}
Table :: struct {
lpad, rpad: int, // Cell padding (left/right)
cells: [dynamic]Cell,
caption: string,
nr_rows, nr_cols: int,
has_header_row: bool,
table_allocator: runtime.Allocator, // Used for allocating cells/colw
format_allocator: runtime.Allocator, // Used for allocating Cell.text when applicable
dirty: bool, // True if build() needs to be called before rendering
// The following are computed on build()
colw: [dynamic]int, // Width of each column (including padding, excluding borders)
tblw: int, // Width of entire table (including padding, excluding borders)
}
init :: proc{init_with_allocator, init_with_virtual_arena, init_with_mem_arena}
init_with_allocator :: proc(tbl: ^Table, format_allocator := context.temp_allocator, table_allocator := context.allocator) -> ^Table {
tbl.table_allocator = table_allocator
tbl.cells = make([dynamic]Cell, tbl.table_allocator)
tbl.colw = make([dynamic]int, tbl.table_allocator)
tbl.format_allocator = format_allocator
return tbl
}
init_with_virtual_arena :: proc(tbl: ^Table, format_arena: ^virtual.Arena, table_allocator := context.allocator) -> ^Table {
return init_with_allocator(tbl, virtual.arena_allocator(format_arena), table_allocator)
}
init_with_mem_arena :: proc(tbl: ^Table, format_arena: ^mem.Arena, table_allocator := context.allocator) -> ^Table {
return init_with_allocator(tbl, mem.arena_allocator(format_arena), table_allocator)
}
destroy :: proc(tbl: ^Table) {
free_all(tbl.format_allocator)
delete(tbl.cells)
delete(tbl.colw)
}
caption :: proc(tbl: ^Table, value: string) {
tbl.caption = value
tbl.dirty = true
}
padding :: proc(tbl: ^Table, lpad, rpad: int) {
tbl.lpad = lpad
tbl.rpad = rpad
tbl.dirty = true
}
get_cell :: proc(tbl: ^Table, row, col: int, loc := #caller_location) -> ^Cell {
assert(col >= 0 && col < tbl.nr_cols, "cell column out of range", loc)
assert(row >= 0 && row < tbl.nr_rows, "cell row out of range", loc)
resize(&tbl.cells, tbl.nr_cols * tbl.nr_rows)
return &tbl.cells[row*tbl.nr_cols + col]
}
set_cell_value_and_alignment :: proc(tbl: ^Table, row, col: int, value: string, alignment: Cell_Alignment) {
cell := get_cell(tbl, row, col)
cell.text = format(tbl, "%v", value)
cell.alignment = alignment
tbl.dirty = true
}
set_cell_value :: proc(tbl: ^Table, row, col: int, value: any, loc := #caller_location) {
cell := get_cell(tbl, row, col, loc)
switch val in value {
case nil:
cell.text = ""
case string:
cell.text = string(val)
case cstring:
cell.text = string(val)
case:
cell.text = format(tbl, "%v", val)
if cell.text == "" {
fmt.eprintf("{} text/table: format() resulted in empty string (arena out of memory?)\n", loc)
}
}
tbl.dirty = true
}
set_cell_alignment :: proc(tbl: ^Table, row, col: int, alignment: Cell_Alignment, loc := #caller_location) {
cell := get_cell(tbl, row, col, loc)
cell.alignment = alignment
tbl.dirty = true
}
format :: proc(tbl: ^Table, _fmt: string, args: ..any, loc := #caller_location) -> string {
context.allocator = tbl.format_allocator
return fmt.aprintf(fmt = _fmt, args = args)
}
header :: proc(tbl: ^Table, values: ..any, loc := #caller_location) {
if (tbl.has_header_row && tbl.nr_rows != 1) || (!tbl.has_header_row && tbl.nr_rows != 0) {
panic("Cannot add headers after rows have been added", loc)
}
if tbl.nr_rows == 0 {
tbl.nr_rows += 1
tbl.has_header_row = true
}
col := tbl.nr_cols
tbl.nr_cols += len(values)
for val in values {
set_cell_value(tbl, header_row(tbl), col, val, loc)
col += 1
}
tbl.dirty = true
}
row :: proc(tbl: ^Table, values: ..any, loc := #caller_location) {
if tbl.nr_cols == 0 {
if len(values) == 0 {
panic("Cannot create row without values unless knowing amount of columns in advance")
} else {
tbl.nr_cols = len(values)
}
}
tbl.nr_rows += 1
for col in 0..<tbl.nr_cols {
val := values[col] if col < len(values) else nil
set_cell_value(tbl, last_row(tbl), col, val)
}
tbl.dirty = true
}
last_row :: proc(tbl: ^Table) -> int {
return tbl.nr_rows - 1
}
header_row :: proc(tbl: ^Table) -> int {
return 0 if tbl.has_header_row else -1
}
first_row :: proc(tbl: ^Table) -> int {
return header_row(tbl)+1 if tbl.has_header_row else 0
}
build :: proc(tbl: ^Table) {
tbl.dirty = false
resize(&tbl.colw, tbl.nr_cols)
mem.zero_slice(tbl.colw[:])
for row in 0..<tbl.nr_rows {
for col in 0..<tbl.nr_cols {
cell := get_cell(tbl, row, col)
if w := len(cell.text) + tbl.lpad + tbl.rpad; w > tbl.colw[col] {
tbl.colw[col] = w
}
}
}
colw_sum := 0
for v in tbl.colw {
colw_sum += v
}
tbl.tblw = max(colw_sum, len(tbl.caption) + tbl.lpad + tbl.rpad)
// Resize columns to match total width of table
remain := tbl.tblw-colw_sum
for col := 0; remain > 0; col = (col + 1) % tbl.nr_cols {
tbl.colw[col] += 1
remain -= 1
}
return
}
write_html_table :: proc(w: io.Writer, tbl: ^Table) {
if tbl.dirty {
build(tbl)
}
io.write_string(w, "<table>\n")
if tbl.caption != "" {
io.write_string(w, "<caption>")
io.write_string(w, tbl.caption)
io.write_string(w, "</caption>\n")
}
align_attribute :: proc(cell: ^Cell) -> string {
switch cell.alignment {
case .Left: return ` align="left"`
case .Center: return ` align="center"`
case .Right: return ` align="right"`
}
unreachable()
}
if tbl.has_header_row {
io.write_string(w, "<thead>\n")
io.write_string(w, " <tr>\n")
for col in 0..<tbl.nr_cols {
cell := get_cell(tbl, header_row(tbl), col)
io.write_string(w, " <th")
io.write_string(w, align_attribute(cell))
io.write_string(w, ">")
io.write_string(w, cell.text)
io.write_string(w, "</th>\n")
}
io.write_string(w, " </tr>\n")
io.write_string(w, "</thead>\n")
}
io.write_string(w, "<tbody>\n")
for row in 0..<tbl.nr_rows {
if tbl.has_header_row && row == header_row(tbl) {
continue
}
io.write_string(w, " <tr>\n")
for col in 0..<tbl.nr_cols {
cell := get_cell(tbl, row, col)
io.write_string(w, " <td")
io.write_string(w, align_attribute(cell))
io.write_string(w, ">")
io.write_string(w, cell.text)
io.write_string(w, "</td>\n")
}
io.write_string(w, " </tr>\n")
}
io.write_string(w, " </tbody>\n")
io.write_string(w, "</table>\n")
}
write_ascii_table :: proc(w: io.Writer, tbl: ^Table) {
if tbl.dirty {
build(tbl)
}
write_caption_separator :: proc(w: io.Writer, tbl: ^Table) {
io.write_byte(w, '+')
write_byte_repeat(w, tbl.tblw + tbl.nr_cols - 1, '-')
io.write_byte(w, '+')
io.write_byte(w, '\n')
}
write_table_separator :: proc(w: io.Writer, tbl: ^Table) {
for col in 0..<tbl.nr_cols {
if col == 0 {
io.write_byte(w, '+')
}
write_byte_repeat(w, tbl.colw[col], '-')
io.write_byte(w, '+')
}
io.write_byte(w, '\n')
}
if tbl.caption != "" {
write_caption_separator(w, tbl)
io.write_byte(w, '|')
write_text_align(w, tbl.tblw - tbl.lpad - tbl.rpad + tbl.nr_cols - 1,
tbl.lpad, tbl.rpad, tbl.caption, .Center)
io.write_byte(w, '|')
io.write_byte(w, '\n')
}
write_table_separator(w, tbl)
for row in 0..<tbl.nr_rows {
for col in 0..<tbl.nr_cols {
if col == 0 {
io.write_byte(w, '|')
}
write_table_cell(w, tbl, row, col)
io.write_byte(w, '|')
}
io.write_byte(w, '\n')
if tbl.has_header_row && row == header_row(tbl) {
write_table_separator(w, tbl)
}
}
write_table_separator(w, tbl)
}
// Renders table according to GitHub Flavored Markdown (GFM) specification
write_markdown_table :: proc(w: io.Writer, tbl: ^Table) {
// NOTE(oskar): Captions or colspans are not supported by GFM as far as I can tell.
if tbl.dirty {
build(tbl)
}
for row in 0..<tbl.nr_rows {
for col in 0..<tbl.nr_cols {
cell := get_cell(tbl, row, col)
if col == 0 {
io.write_byte(w, '|')
}
write_text_align(w, tbl.colw[col] - tbl.lpad - tbl.rpad, tbl.lpad, tbl.rpad, cell.text,
.Center if tbl.has_header_row && row == header_row(tbl) else .Left)
io.write_string(w, "|")
}
io.write_byte(w, '\n')
if tbl.has_header_row && row == header_row(tbl) {
for col in 0..<tbl.nr_cols {
cell := get_cell(tbl, row, col)
if col == 0 {
io.write_byte(w, '|')
}
switch cell.alignment {
case .Left:
io.write_byte(w, ':')
write_byte_repeat(w, max(1, tbl.colw[col]-1), '-')
case .Center:
io.write_byte(w, ':')
write_byte_repeat(w, max(1, tbl.colw[col]-2), '-')
io.write_byte(w, ':')
case .Right:
write_byte_repeat(w, max(1, tbl.colw[col]-1), '-')
io.write_byte(w, ':')
}
io.write_byte(w, '|')
}
io.write_byte(w, '\n')
}
}
}
write_byte_repeat :: proc(w: io.Writer, n: int, b: byte) {
for _ in 0..<n {
io.write_byte(w, b)
}
}
write_table_cell :: proc(w: io.Writer, tbl: ^Table, row, col: int) {
if tbl.dirty {
build(tbl)
}
cell := get_cell(tbl, row, col)
write_text_align(w, tbl.colw[col]-tbl.lpad-tbl.rpad, tbl.lpad, tbl.rpad, cell.text, cell.alignment)
}
write_text_align :: proc(w: io.Writer, colw, lpad, rpad: int, text: string, alignment: Cell_Alignment) {
write_byte_repeat(w, lpad, ' ')
switch alignment {
case .Left:
io.write_string(w, text)
write_byte_repeat(w, colw - len(text), ' ')
case .Center:
pad := colw - len(text)
odd := pad & 1 != 0
write_byte_repeat(w, pad/2, ' ')
io.write_string(w, text)
write_byte_repeat(w, pad/2 + 1 if odd else pad/2, ' ')
case .Right:
write_byte_repeat(w, colw - len(text), ' ')
io.write_string(w, text)
}
write_byte_repeat(w, rpad, ' ')
}

View File

@@ -0,0 +1,13 @@
package text_table
import "core:io"
import "core:os"
import "core:strings"
stdio_writer :: proc() -> io.Writer {
return io.to_writer(os.stream_from_handle(os.stdout))
}
strings_builder_writer :: proc(b: ^strings.Builder) -> io.Writer {
return strings.to_writer(b)
}

View File

@@ -0,0 +1,55 @@
//+build js
package thread
import "core:intrinsics"
import "core:sync"
import "core:mem"
Thread_State :: enum u8 {
Started,
Joined,
Done,
}
Thread_Os_Specific :: struct {
flags: bit_set[Thread_State; u8],
}
_thread_priority_map := [Thread_Priority]i32{
.Normal = 0,
.Low = -2,
.High = +2,
}
_create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
unimplemented("core:thread procedure not supported on js target")
}
_start :: proc(t: ^Thread) {
unimplemented("core:thread procedure not supported on js target")
}
_is_done :: proc(t: ^Thread) -> bool {
unimplemented("core:thread procedure not supported on js target")
}
_join :: proc(t: ^Thread) {
unimplemented("core:thread procedure not supported on js target")
}
_join_multiple :: proc(threads: ..^Thread) {
unimplemented("core:thread procedure not supported on js target")
}
_destroy :: proc(thread: ^Thread) {
unimplemented("core:thread procedure not supported on js target")
}
_terminate :: proc(using thread : ^Thread, exit_code: int) {
unimplemented("core:thread procedure not supported on js target")
}
_yield :: proc() {
unimplemented("core:thread procedure not supported on js target")
}

View File

@@ -1,337 +0,0 @@
import "core:fmt.odin";
import "core:os.odin";
import "core:mem.odin";
// import "http_test.odin" as ht;
// import "game.odin" as game;
// import "punity.odin" as pn;
main :: proc() {
struct_padding();
bounds_checking();
type_introspection();
any_type();
crazy_introspection();
namespaces_and_files();
miscellany();
/*
ht.run();
game.run();
{
init :: proc(c: ^pn.Core) {}
step :: proc(c: ^pn.Core) {}
pn.run(init, step);
}
*/
}
struct_padding :: proc() {
{
A :: struct {
a: u8,
b: u32,
c: u16,
}
B :: struct {
a: [7]u8,
b: [3]u16,
c: u8,
d: u16,
}
fmt.println("size_of(A):", size_of(A));
fmt.println("size_of(B):", size_of(B));
// n.b. http://cbloomrants.blogspot.co.uk/2012/07/07-23-12-structs-are-not-what-you-want.html
}
{
A :: struct #ordered {
a: u8,
b: u32,
c: u16,
}
B :: struct #ordered {
a: [7]u8,
b: [3]u16,
c: u8,
d: u16,
}
fmt.println("size_of(A):", size_of(A));
fmt.println("size_of(B):", size_of(B));
// C-style structure layout
}
{
A :: struct #packed {
a: u8,
b: u32,
c: u16,
}
B :: struct #packed {
a: [7]u8,
b: [3]u16,
c: u8,
d: u16,
}
fmt.println("size_of(A):", size_of(A));
fmt.println("size_of(B):", size_of(B));
// Useful for explicit layout
}
// Member sorting by priority
// Alignment desc.
// Size desc.
// source order asc.
/*
A :: struct {
a: u8
b: u32
c: u16
}
B :: struct {
a: [7]u8
b: [3]u16
c: u8
d: u16
}
Equivalent too
A :: struct #ordered {
b: u32
c: u16
a: u8
}
B :: struct #ordered {
b: [3]u16
d: u16
a: [7]u8
c: u8
}
*/
}
bounds_checking :: proc() {
x: [4]int;
// x[-1] = 0; // Compile Time
// x[4] = 0; // Compile Time
{
a, b := -1, 4;
// x[a] = 0; // Runtime Time
// x[b] = 0; // Runtime Time
}
// Works for arrays, strings, slices, and related procedures & operations
{
base: [10]int;
s := base[2..6];
a, b := -1, 6;
#no_bounds_check {
s[a] = 0;
// #bounds_check s[b] = 0;
}
#no_bounds_check
if s[a] == 0 {
// Do whatever
}
// Bounds checking can be toggled explicit
// on a per statement basis.
// _any statement_
}
}
type_introspection :: proc() {
{
info: ^Type_Info;
x: int;
info = type_info_of(int); // by type
info = type_info_of(x); // by value
// See: runtime.odin
match i in info.variant {
case Type_Info_Integer:
fmt.println("integer!");
case Type_Info_Float:
fmt.println("float!");
case:
fmt.println("potato!");
}
// Unsafe cast
integer_info := cast(^Type_Info_Integer)cast(rawptr)info;
}
{
Vector2 :: struct { x, y: f32 }
Vector3 :: struct { x, y, z: f32 }
v1: Vector2;
v2: Vector3;
v3: Vector3;
t1 := type_info_of(v1);
t2 := type_info_of(v2);
t3 := type_info_of(v3);
fmt.println();
fmt.print("Type of v1 is:\n\t", t1);
fmt.println();
fmt.print("Type of v2 is:\n\t", t2);
fmt.println("\n");
fmt.println("t1 == t2:", t1 == t2);
fmt.println("t2 == t3:", t2 == t3);
}
}
any_type :: proc() {
a: any;
x: int = 123;
y: f64 = 6.28;
z: string = "Yo-Yo Ma";
// All types can be implicit cast to `any`
a = x;
a = y;
a = z;
a = a; // This the "identity" type, it doesn't get converted
a = 123; // Literals are copied onto the stack first
// any has two members
// data - rawptr to the data
// type_info - pointer to the type info
fmt.println(x, y, z);
// See: fmt.odin
// For variadic any procedures in action
}
crazy_introspection :: proc() {
{
Fruit :: enum {
APPLE,
BANANA,
GRAPE,
MELON,
PEACH,
TOMATO,
}
s: string;
// s = enum_to_string(Fruit.PEACH);
fmt.println(s);
f := Fruit.GRAPE;
// s = enum_to_string(f);
fmt.println(s);
fmt.println(f);
// See: runtime.odin
}
{
// NOTE(bill): This is not safe code and I would not recommend this at all
// I'd recommend you use `match type` to get the subtype rather than
// casting pointers
Fruit :: enum {
APPLE,
BANANA,
GRAPE,
MELON,
PEACH,
TOMATO,
}
fruit_ti := type_info_of(Fruit);
name := fruit_ti.variant.(Type_Info_Named).name;
info, _ := type_info_base(fruit_ti).variant.(Type_Info_Enum);
fmt.printf("%s :: enum %T {\n", name, info.base);
for _, i in info.values {
fmt.printf("\t%s\t= %v,\n", info.names[i], info.values[i]);
}
fmt.printf("}\n");
// NOTE(bill): look at that type-safe printf!
}
{
Vector3 :: struct {x, y, z: f32}
a := Vector3{x = 1, y = 4, z = 9};
fmt.println(a);
b := Vector3{x = 9, y = 3, z = 1};
fmt.println(b);
// NOTE(bill): See fmt.odin
}
// n.b. This pretty much "solves" serialization (to strings)
}
// #import "test.odin"
namespaces_and_files :: proc() {
// test.thing()
// test.format.println()
// test.println()
/*
// Non-exporting import
#import "file.odin"
#import "file.odin" as file
#import "file.odin" as .
#import "file.odin" as _
// Exporting import
#include "file.odin"
*/
// Talk about scope rules and diagram
}
miscellany :: proc() {
/*
win32 `__imp__` prefix
#dll_import
#dll_export
Change exported name/symbol for linking
#link_name
Custom calling conventions
#stdcall
#fastcall
Runtime stuff
#shared_global_scope
*/
// assert(false)
// #assert(false)
// panic("Panic message goes here")
}

View File

@@ -1,879 +0,0 @@
// Demo 002
export "core:fmt.odin";
export "core:math.odin";
export "core:mem.odin";
// export "game.odin"
#thread_local tls_int: int;
main :: proc() {
// Forenotes
// Semicolons are now optional
// Rule for when a semicolon is expected after a statement
// - If the next token is not on the same line
// - if the next token is a closing brace }
// - Otherwise, a semicolon is needed
//
// Expections:
// for, if, match
// if x := thing(); x < 123 {}
// for i := 0; i < 123; i++ {}
// Q: Should I use the new rule or go back to the old one without optional semicolons?
// #thread_local - see runtime.odin and above at `tls_int`
// #foreign_system_library - see win32.odin
// struct_compound_literals();
// enumerations();
// variadic_procedures();
// new_builtins();
// match_statement();
// namespacing();
// subtyping();
// tagged_unions();
}
struct_compound_literals :: proc() {
Thing :: struct {
id: int,
x: f32,
name: string,
};
{
t1: Thing;
t1.id = 1;
t3 := Thing{};
t4 := Thing{1, 2, "Fred"};
// t5 := Thing{1, 2};
t6 := Thing{
name = "Tom",
x = 23,
};
}
}
enumerations :: proc() {
{
Fruit :: enum {
APPLE, // 0
BANANA, // 1
PEAR, // 2
};
f := Fruit.APPLE;
// g12: int = Fruit.BANANA
g: int = cast(int)Fruit.BANANA;
// However, you can use enums are index values as _any_ integer allowed
}
{
Fruit1 :: enum int {
APPLE,
BANANA,
PEAR,
}
Fruit2 :: enum u8 {
APPLE,
BANANA,
PEAR,
}
Fruit3 :: enum u8 {
APPLE = 1,
BANANA, // 2
PEAR = 5,
TOMATO, // 6
}
}
// Q: remove the need for `type` if it's a record (struct/enum/raw_union/union)?
}
variadic_procedures :: proc() {
print_ints :: proc(args: ..int) {
for arg, i in args {
if i > 0 do print(", ");
print(arg);
}
}
print_ints(); // nl()
print_ints(1); nl();
print_ints(1, 2, 3); nl();
print_prefix_f32s :: proc(prefix: string, args: ..f32) {
print(prefix);
print(": ");
for arg, i in args {
if i > 0 do print(", ");
print(arg);
}
}
print_prefix_f32s("a"); nl();
print_prefix_f32s("b", 1); nl();
print_prefix_f32s("c", 1, 2, 3); nl();
// Internally, the variadic procedures get allocated to an array on the stack,
// and this array is passed a slice
// This is first step for a `print` procedure but I do not have an `any` type
// yet as this requires a few other things first - i.e. introspection
// NOTE(bill): I haven't yet added the feature of expanding a slice or array into
// a variadic a parameter but it's pretty trivial to add
}
new_builtins :: proc() {
{
a := new(int);
b := make([]int, 12);
c := make([]int, 12, 16);
defer free(a);
defer free(b);
defer free(c);
// NOTE(bill): These use the current context's allocator not the default allocator
// see runtime.odin
// Q: Should this be `free` rather than `free` and should I overload it for slices too?
push_allocator default_allocator() {
a := new(int);
defer free(a);
// Do whatever
}
}
{
a: int = 123;
b: type_of(a) = 321;
// NOTE(bill): This matches the current naming scheme
// size_of
// align_of
// offset_of
//
// size_of_val
// align_of_val
// offset_of_val
// type_of_val
}
{
// Compile time assert
COND :: true;
#assert(COND);
// #assert(!COND)
// Runtime assert
x := true;
assert(x);
// assert(!x);
}
{
x: ^u32 = nil;
y := x+100;
z := y-x;
w := slice_ptr(x, 12);
t := slice_ptr(x, 12, 16);
// NOTE(bill): These are here because I've removed:
// pointer arithmetic
// pointer indexing
// pointer slicing
// Reason
a: [16]int;
a[1] = 1;
b := &a;
// Auto pointer deref
// consistent with record members
assert(b[1] == 1);
// Q: Should I add them back in at the cost of inconsitency?
}
{
a, b := -1, 2;
print(min(a, b)); nl();
print(max(a, b)); nl();
print(abs(a)); nl();
// These work at compile time too
A :: -1;
B :: 2;
C :: min(A, B);
D :: max(A, B);
E :: abs(A);
print(C); nl();
print(D); nl();
print(E); nl();
}
}
match_statement :: proc() {
// NOTE(bill): `match` statements are similar to `switch` statements
// in other languages but there are few differences
{
match x := 5; x {
case 1: // cases must be constant expression
print("1!\n");
// break by default
case 2:
s := "2!\n"; // Each case has its own scope
print(s);
break; // explicit break
case 3, 4: // multiple cases
print("3 or 4!\n");
case 5:
print("5!\n");
fallthrough; // explicit fallthrough
case:
print("default!\n");
}
match x := 1.5; x {
case 1.5:
print("1.5!\n");
// break by default
case TAU:
print("τ!\n");
case:
print("default!\n");
}
match x := "Hello"; x {
case "Hello":
print("greeting\n");
// break by default
case "Goodbye":
print("farewell\n");
case:
print("???\n");
}
a := 53;
match {
case a == 1:
print("one\n");
case a == 2:
print("a couple\n");
case a < 7, a == 7:
print("a few\n");
case a < 12: // intentional bug
print("several\n");
case a >= 12 && a < 100:
print("dozens\n");
case a >= 100 && a < 1000:
print("hundreds\n");
case:
print("a fuck ton\n");
}
// Identical to this
b := 53;
if b == 1 {
print("one\n");
} else if b == 2 {
print("a couple\n");
} else if b < 7 || b == 7 {
print("a few\n");
} else if b < 12 { // intentional bug
print("several\n");
} else if b >= 12 && b < 100 {
print("dozens\n");
} else if b >= 100 && b < 1000 {
print("hundreds\n");
} else {
print("a fuck ton\n");
}
// However, match statements allow for `break` and `fallthrough` unlike
// an if statement
}
}
Vector3 :: struct {x, y, z: f32}
print_floats :: proc(args: ..f32) {
for arg, i in args {
if i > 0 do print(", ");
print(arg);
}
println();
}
namespacing :: proc() {
{
Thing :: #type struct {
x: f32,
name: string,
};
a: Thing;
a.x = 3;
{
Thing :: #type struct {
y: int,
test: bool,
};
b: Thing; // Uses this scope's Thing
b.test = true;
}
}
/*
{
Entity :: struct {
Guid :: int
Nested :: struct {
MyInt :: int
i: int
}
CONSTANT :: 123
guid: Guid
name: string
pos: Vector3
vel: Vector3
nested: Nested
}
guid: Entity.Guid = Entity.CONSTANT
i: Entity.Nested.MyInt
{
using Entity
guid: Guid = CONSTANT
using Nested
i: MyInt
}
{
using Entity.Nested
guid: Entity.Guid = Entity.CONSTANT
i: MyInt
}
{
e: Entity
using e
guid = 27832
name = "Bob"
print(e.guid as int); nl()
print(e.name); nl()
}
{
using e: Entity
guid = 78456
name = "Thing"
print(e.guid as int); nl()
print(e.name); nl()
}
}
{
Entity :: struct {
Guid :: int
Nested :: struct {
MyInt :: int
i: int
}
CONSTANT :: 123
guid: Guid
name: string
using pos: Vector3
vel: Vector3
using nested: ^Nested
}
e := Entity{nested = new(Entity.Nested)}
e.x = 123
e.i = Entity.CONSTANT
}
*/
{
Entity :: struct {
position: Vector3
}
print_pos_1 :: proc(entity: ^Entity) {
print("print_pos_1: ");
print_floats(entity.position.x, entity.position.y, entity.position.z);
}
print_pos_2 :: proc(entity: ^Entity) {
using entity;
print("print_pos_2: ");
print_floats(position.x, position.y, position.z);
}
print_pos_3 :: proc(using entity: ^Entity) {
print("print_pos_3: ");
print_floats(position.x, position.y, position.z);
}
print_pos_4 :: proc(using entity: ^Entity) {
using position;
print("print_pos_4: ");
print_floats(x, y, z);
}
e := Entity{position = Vector3{1, 2, 3}};
print_pos_1(&e);
print_pos_2(&e);
print_pos_3(&e);
print_pos_4(&e);
// This is similar to C++'s `this` pointer that is implicit and only available in methods
}
}
subtyping :: proc() {
{
// C way for subtyping/subclassing
Entity :: struct {
position: Vector3,
}
Frog :: struct {
entity: Entity,
jump_height: f32,
}
f: Frog;
f.entity.position = Vector3{1, 2, 3};
using f.entity;
position = Vector3{1, 2, 3};
}
{
// C++ way for subtyping/subclassing
Entity :: struct {
position: Vector3
}
Frog :: struct {
using entity: Entity,
jump_height: f32,
}
f: Frog;
f.position = Vector3{1, 2, 3};
print_pos :: proc(using entity: Entity) {
print("print_pos: ");
print_floats(position.x, position.y, position.z);
}
print_pos(f.entity);
// print_pos(f);
// Subtype Polymorphism
}
{
// More than C++ way for subtyping/subclassing
Entity :: struct {
position: Vector3,
}
Frog :: struct {
jump_height: f32,
using entity: ^Entity, // Doesn't have to be first member!
}
f: Frog;
f.entity = new(Entity);
f.position = Vector3{1, 2, 3};
print_pos :: proc(using entity: ^Entity) {
print("print_pos: ");
print_floats(position.x, position.y, position.z);
}
print_pos(f.entity);
// print_pos(^f);
// print_pos(f);
}
{
// More efficient subtyping
Entity :: struct {
position: Vector3,
}
Frog :: struct {
jump_height: f32,
using entity: ^Entity,
}
MAX_ENTITES :: 64;
entities: [MAX_ENTITES]Entity;
entity_count := 0;
next_entity :: proc(entities: []Entity, entity_count: ^int) -> ^Entity {
e := &entities[entity_count^];
entity_count^ += 1;
return e;
}
f: Frog;
f.entity = next_entity(entities[..], &entity_count);
f.position = Vector3{3, 4, 6};
using f.position;
print_floats(x, y, z);
}
/*{
// Down casting
Entity :: struct {
position: Vector3,
}
Frog :: struct {
jump_height: f32,
using entity: Entity,
}
f: Frog;
f.jump_height = 564;
e := ^f.entity;
frog := down_cast(^Frog)e;
print("down_cast: ");
print(frog.jump_height); nl();
// NOTE(bill): `down_cast` is unsafe and there are not check are compile time or run time
// Q: Should I completely remove `down_cast` as I added it in about 30 minutes
}*/
{
// Multiple "inheritance"/subclassing
Entity :: struct {
position: Vector3,
}
Climber :: struct {
speed: f32,
}
Frog :: struct {
using entity: Entity,
using climber: Climber,
}
}
}
tagged_unions :: proc() {
{
Entity_Kind :: enum {
INVALID,
FROG,
GIRAFFE,
HELICOPTER,
}
Entity :: struct {
kind: Entity_Kind
using data: struct #raw_union {
frog: struct {
jump_height: f32,
colour: u32,
},
giraffe: struct {
neck_length: f32,
spot_count: int,
},
helicopter: struct {
blade_count: int,
weight: f32,
pilot_name: string,
},
}
}
e: Entity;
e.kind = Entity_Kind.FROG;
e.frog.jump_height = 12;
f: type_of(e.frog);
// But this is very unsafe and extremely cumbersome to write
// In C++, I use macros to alleviate this but it's not a solution
}
{
Frog :: struct {
jump_height: f32,
colour: u32,
}
Giraffe :: struct {
neck_length: f32,
spot_count: int,
}
Helicopter :: struct {
blade_count: int,
weight: f32,
pilot_name: string,
}
Entity :: union {Frog, Giraffe, Helicopter};
f1: Frog = Frog{12, 0xff9900};
f2: Entity = Frog{12, 0xff9900}; // Implicit cast
f3 := cast(Entity)Frog{12, 0xff9900}; // Explicit cast
// f3.Frog.jump_height = 12 // There are "members" of a union
e, f, g, h: Entity;
f = Frog{12, 0xff9900};
g = Giraffe{2.1, 23};
h = Helicopter{4, 1000, "Frank"};
// Requires a pointer to the union
// `x` will be a pointer to type of the case
match x in &f {
case Frog:
print("Frog!\n");
print(x.jump_height); nl();
// x.jump_height = 3;
print(x.jump_height); nl();
case Giraffe:
print("Giraffe!\n");
case Helicopter:
print("ROFLCOPTER!\n");
case:
print("invalid entity\n");
}
// Q: Allow for a non pointer version with takes a copy instead?
// Or it takes the pointer the data and not a copy
// fp := cast(^Frog)^f; // Unsafe
// print(fp.jump_height); nl();
// Internals of a tagged union
/*
struct {
data: [size_of_biggest_tag]u8,
tag_index: int,
}
*/
// This is to allow for pointer casting if needed
// Advantage over subtyping version
MAX_ENTITES :: 64;
entities: [MAX_ENTITES]Entity;
entities[0] = Frog{};
entities[1] = Helicopter{};
// etc.
}
{
// Transliteration of code from this actual compiler
// Some stuff is missing
Type :: struct {};
Scope :: struct {};
Token :: struct {};
AstNode :: struct {};
ExactValue :: struct {};
Entity_Kind :: enum {
Invalid,
Constant,
Variable,
Using_Variable,
TypeName,
Procedure,
Builtin,
Count,
}
Guid :: i64;
Entity :: struct {
kind: Entity_Kind,
guid: Guid,
scope: ^Scope,
token: Token,
type_: ^Type,
using data: struct #raw_union {
Constant: struct {
value: ExactValue,
},
Variable: struct {
visited: bool, // Cycle detection
used: bool, // Variable is used
is_field: bool, // Is struct field
anonymous: bool, // Variable is an anonymous
},
Using_Variable: struct {
},
TypeName: struct {
},
Procedure: struct {
used: bool,
},
Builtin: struct {
id: int,
},
},
}
// Plus all the constructing procedures that go along with them!!!!
// It's a nightmare
}
{
Type :: struct {};
Scope :: struct {};
Token :: struct {};
AstNode :: struct {};
ExactValue :: struct {};
Guid :: i64;
Entity_Base :: struct {
}
Constant :: struct {
value: ExactValue,
}
Variable :: struct {
visited: bool, // Cycle detection
used: bool, // Variable is used
is_field: bool, // Is struct field
anonymous: bool, // Variable is an anonymous
}
Using_Variable :: struct {
}
TypeName :: struct {
}
Procedure :: struct {
used: bool,
}
Builtin :: struct {
id: int,
}
Entity :: struct {
guid: Guid,
scope: ^Scope,
token: Token,
type_: ^Type,
variant: union {Constant, Variable, Using_Variable, TypeName, Procedure, Builtin},
}
e := Entity{
variant = Variable{
used = true,
anonymous = false,
},
};
// Q: Allow a "base" type to be added to a union?
// Or even `using` on union to get the same properties?
}
{
// `Raw` unions still have uses, especially for mathematic types
Vector2 :: struct #raw_union {
using xy_: struct { x, y: f32 },
e: [2]f32,
v: [vector 2]f32,
}
Vector3 :: struct #raw_union {
using xyz_: struct { x, y, z: f32 },
xy: Vector2,
e: [3]f32,
v: [vector 3]f32,
}
v2: Vector2;
v2.x = 1;
v2.e[0] = 1;
v2.v[0] = 1;
v3: Vector3;
v3.x = 1;
v3.e[0] = 1;
v3.v[0] = 1;
v3.xy.x = 1;
}
}
nl :: proc() { println(); }

View File

@@ -1,66 +0,0 @@
import "core:fmt.odin";
import "core:utf8.odin";
import "core:hash.odin";
import "core:mem.odin";
main :: proc() {
{ // New Standard Library stuff
s := "Hello";
fmt.println(s,
utf8.valid_string(s),
hash.murmur64(cast([]u8)s));
// utf8.odin
// hash.odin
// - crc, fnv, fnva, murmur
// mem.odin
// - Custom allocators
// - Helpers
}
{
arena: mem.Arena;
mem.init_arena_from_context(&arena, mem.megabytes(16)); // Uses default allocator
defer mem.destroy_arena(&arena);
push_allocator mem.arena_allocator(&arena) {
x := new(int);
x^ = 1337;
fmt.println(x^);
}
/*
push_allocator x {
..
}
is equivalent to:
{
prev_allocator := __context.allocator
__context.allocator = x
defer __context.allocator = prev_allocator
..
}
*/
// You can also "push" a context
c := context; // Create copy of the allocator
c.allocator = mem.arena_allocator(&arena);
push_context c {
x := new(int);
x^ = 365;
fmt.println(x^);
}
}
// Backend improvements
// - Minimal dependency building (only build what is needed)
// - Numerous bugs fixed
// - Mild parsing recovery after bad syntax error
}

View File

@@ -1,283 +0,0 @@
import "core:fmt.odin";
import "core:utf8.odin";
// import "core:atomic.odin";
// import "core:hash.odin";
// import "core:math.odin";
// import "core:mem.odin";
// import "core:opengl.odin";
// import "core:os.odin";
// import "core:sync.odin";
// import win32 "core:sys/windows.odin";
main :: proc() {
// syntax();
procedure_overloading();
}
syntax :: proc() {
// Cyclic type checking
// Uncomment to see the error
// A :: struct {b: B};
// B :: struct {a: A};
x: int;
y := cast(f32)x;
z := transmute(u32)y;
// down_cast, union_cast are similar too
// Basic directives
fmt.printf("Basic directives = %s(%d): %s\n", #file, #line, #procedure);
// NOTE: new and improved `printf`
// TODO: It does need accurate float printing
// record fields use the same syntax a procedure signatures
Thing1 :: struct {
x: f32,
y: int,
z: ^[]int,
};
Thing2 :: struct {x: f32, y: int, z: ^[]int};
// Slice interals are now just a `ptr+len+cap`
slice: []int; #assert(size_of(slice) == 3*size_of(int));
// Helper type - Help the reader understand what it is quicker
My_Int :: #type int;
My_Proc :: #type proc(int) -> f32;
// All declarations with : are either variable or constant
// To make these declarations syntactically consistent
v_variable := 123;
c_constant :: 123;
c_type1 :: int;
c_type2 :: []int;
c_proc :: proc() { /* code here */ };
/*
x += 1;
x -= 1;
// ++ and -- have been removed
// x++;
// x--;
// Question: Should they be added again?
// They were removed as they are redundant and statements, not expressions
// like in C/C++
*/
// You can now build files as a `.dll`
// `odin build_dll demo.odin`
// New vector syntax
u, v: [vector 3]f32;
v[0] = 123;
v.x = 123; // valid for all vectors with count 1 to 4
// Next part
prefixes();
}
Prefix_Type :: struct {x: int, y: f32, z: rawptr};
#thread_local my_tls: Prefix_Type;
prefixes :: proc() {
using var: Prefix_Type;
var.x = 123;
x = 123;
foo :: proc(using pt: Prefix_Type) {
}
// Same as C99's `restrict`
bar :: proc(#no_alias a, b: ^int) {
// Assumes a never equals b so it can perform optimizations with that fact
}
when_statements();
}
when_statements :: proc() {
X :: 123 + 12;
Y :: X/5;
COND :: Y > 0;
when COND {
fmt.println("Y > 0");
} else {
fmt.println("Y <= 0");
}
when false {
this_code_does_not_exist(123, 321);
but_its_syntax_is_valid();
x :: ^^^^int;
}
foreign_procedures();
}
when ODIN_OS == "windows" {
foreign_system_library win32_user "user32.lib";
}
// NOTE: This is done on purpose for two reasons:
// * Makes it clear where the platform specific stuff is
// * Removes the need to solve the travelling salesman problem when importing files :P
foreign_procedures :: proc() {
foreign win32_user {
ShowWindow :: proc(hwnd: rawptr, cmd_show: i32) -> i32 ---;
show_window :: proc(hwnd: rawptr, cmd_show: i32) -> i32 #link_name "ShowWindow" ---;
}
// NOTE: If that library doesn't get used, it doesn't get linked with
// NOTE: There is not link checking yet to see if that procedure does come from that library
// See sys/windows.odin for more examples
special_expressions();
}
special_expressions :: proc() {
/*
// Block expression
x := {
a: f32 = 123;
b := a-123;
c := b/a;
give c;
}; // semicolon is required as it's an expression
y := if x < 50 {
give x;
} else {
// TODO: Type cohesion is not yet finished
give 123;
}; // semicolon is required as it's an expression
*/
// This is allows for inline blocks of code and will be a useful feature to have when
// macros will be implemented into the language
loops();
}
loops :: proc() {
// The C-style for loop
for i := 0; i < 123; i += 1 {
break;
}
for i := 0; i < 123; {
break;
}
for false {
break;
}
for {
break;
}
for i in 0..123 { // 123 exclusive
}
for i in 0..123-1 { // 122 inclusive
}
for val, idx in 12..16 {
fmt.println(val, idx);
}
primes := [?]int{2, 3, 5, 7, 11, 13, 17, 19};
for p in primes {
fmt.println(p);
}
// Pointers to arrays, slices, or strings are allowed
for _ in &primes {
// ignore the value and just iterate across it
}
name := "你好,世界";
fmt.println(name);
for r in name {
#assert(type_of(r) == rune);
fmt.printf("%r\n", r);
}
when false {
for i, size := 0; i < name.count; i += size {
r: rune;
r, size = utf8.decode_rune(name[i..]);
fmt.printf("%r\n", r);
}
}
procedure_overloading();
}
procedure_overloading :: proc() {
THINGF :: 14451.1;
THINGI :: 14451;
foo :: proc() {
fmt.printf("Zero args\n");
}
foo :: proc(i: int) {
fmt.printf("int arg, i=%d\n", i);
}
foo :: proc(f: f64) {
i := cast(int)f;
fmt.printf("f64 arg, f=%d\n", i);
}
foo();
foo(THINGF);
// foo(THINGI); // 14451 is just a number so it could go to either procedures
foo(cast(int)THINGI);
foo :: proc(x: ^i32) -> (int, int) {
fmt.println("^int");
return 123, cast(int)(x^);
}
foo :: proc(x: rawptr) {
fmt.println("rawptr");
}
a: i32 = 123;
b: f32;
c: rawptr;
fmt.println(foo(&a));
foo(&b);
foo(c);
// foo(nil); // nil could go to numerous types thus the ambiguity
f: proc();
f = foo; // The correct `foo` to chosen
f();
// See math.odin and atomic.odin for more examples
}

View File

@@ -1,310 +0,0 @@
// import "core:atomic.odin";
import "core:hash.odin";
import "core:mem.odin";
import "core:opengl.odin";
import "core:strconv.odin";
import "core:sync.odin";
import win32 "core:sys/windows.odin";
import "core:fmt.odin";
import "core:os.odin";
import "core:math.odin";
main :: proc() {
when true {
/*
Added:
* Unexported entities and fields using an underscore prefix
- See `sync.odin` and explain
Removed:
* Maybe/option types
* Remove `type` keyword and other "reserved" keywords
* ..< and .. removed and replace with .. (half-closed range)
Changed:
* `#assert` and `assert` return the value of the condition for semantic reasons
* thread_local -> #thread_local
* #include -> #load
* Files only get checked if they are actually used
* match x in y {} // For type match statements
* Version numbering now starts from 0.1.0 and uses the convention:
- major.minor.patch
* Core library additions to Windows specific stuff
*/
{
Fruit :: enum {
APPLE,
BANANA,
COCONUT,
}
fmt.println(Fruit.names);
}
{
A :: struct {x, y: f32};
B :: struct #align 16 {x, y: f32};
fmt.println("align_of(A) =", align_of(A));
fmt.println("align_of(B) =", align_of(B));
}
{
// Removal of ..< and ..
for i in 0..16 {
}
// Is similar to
for i := 0; i < 16; i += 1 {
}
}
{
thing: for i in 0..10 {
for j in i+1..10 {
if j == 2 {
fmt.println(i, j);
continue thing;
}
if j == 3 {
break thing;
}
}
}
// Works with, `for`, `for in`, `match`, `match in`
// NOTE(bill): This solves most of the problems I need `goto` for
}
{
t := type_info_of(int);
match i in t.variant {
case Type_Info_Integer, Type_Info_Float:
fmt.println("It's a number");
}
x: any = 123;
foo: match i in x {
case int, f32:
fmt.println("It's an int or f32");
break foo;
}
}
{
cond := true;
x: int;
if cond {
x = 3;
} else {
x = 4;
}
// Ternary operator
y := cond ? 3 : 4;
FOO :: true ? 123 : 432; // Constant ternary expression
fmt.println("Ternary values:", y, FOO);
}
{
// Slices now store a capacity
buf: [256]u8;
s: []u8;
s = buf[..0]; // == buf[0..0];
fmt.println("count =", len(s));
fmt.println("capacity =", cap(s));
append(&s, 1, 2, 3);
fmt.println(s);
s = buf[1..2..3];
fmt.println("count =", len(s));
fmt.println("capacity =", cap(s));
fmt.println(s);
clear(&s); // Sets count to zero
}
{
Foo :: struct {
x, y, z: f32,
ok: bool,
flags: u32,
}
foo_array: [256]Foo;
foo_as_bytes: []u8 = mem.slice_to_bytes(foo_array[..]);
// Useful for things like
// os.write(handle, foo_as_bytes);
foo_slice := mem.slice_ptr(cast(^Foo)&foo_as_bytes[0], len(foo_as_bytes)/size_of(Foo), cap(foo_as_bytes)/size_of(Foo));
// Question: Should there be a bytes_to_slice procedure or is it clearer to do this even if it is error prone?
// And if so what would the syntax be?
// slice_transmute([]Foo, foo_as_bytes);
}
{
Vec3 :: [vector 3]f32;
x := Vec3{1, 2, 3};
y := Vec3{4, 5, 6};
fmt.println(x < y);
fmt.println(x + y);
fmt.println(x - y);
fmt.println(x * y);
fmt.println(x / y);
for i in x {
fmt.println(i);
}
#assert(size_of([vector 7]bool) >= size_of([7]bool));
#assert(size_of([vector 7]i32) >= size_of([7]i32));
// align_of([vector 7]i32) != align_of([7]i32) // this may be the case
}
{
// fmt.* changes
// bprint* returns `string`
data: [256]u8;
str := fmt.bprintf(data[..], "Hellope %d %s %c", 123, "others", '!');
fmt.println(str);
}
{
x: [dynamic]f64;
reserve(&x, 16);
defer free(x); // `free` is overloaded for numerous types
// Number literals can have underscores in them for readability
append(&x, 2_000_000.500_000, 123, 5, 7); // variadic append
for p, i in x {
if i > 0 { fmt.print(", "); }
fmt.print(p);
}
fmt.println();
}
{
// Dynamic array "literals"
x := [dynamic]f64{2_000_000.500_000, 3, 5, 7};
defer free(x);
fmt.println(x); // fmt.print* supports printing of dynamic types
clear(&x);
fmt.println(x);
}
{
m: map[f32]int;
reserve(&m, 16);
defer free(m);
m[1.0] = 1278;
m[2.0] = 7643;
m[3.0] = 564;
_, ok := m[3.0];
c := m[3.0];
assert(ok && c == 564);
fmt.print("map[");
i := 0;
for val, key in m {
if i > 0 {
fmt.print(", ");
}
fmt.printf("%v=%v", key, val);
i += 1;
}
fmt.println("]");
}
{
m := map[string]u32{
"a" = 56,
"b" = 13453,
"c" = 7654,
};
defer free(m);
c := m["c"];
_, ok := m["c"];
assert(ok && c == 7654);
fmt.println(m);
delete(&m, "c"); // deletes entry with key "c"
_, found := m["c"];
assert(!found);
fmt.println(m);
clear(&m);
fmt.println(m);
// NOTE: Fixed size maps are planned but we have not yet implemented
// them as we have had no need for them as of yet
}
{
Vector3 :: struct{x, y, z: f32};
Quaternion :: struct{x, y, z, w: f32};
// Variants
Frog :: struct {
ribbit_volume: f32,
jump_height: f32,
}
Door :: struct {
openness: f32,
}
Map :: struct {
width, height: f32,
place_positions: []Vector3,
place_names: []string,
}
Entity :: struct {
// Common Fields
id: u64,
name: string,
using position: Vector3,
orientation: Quaternion,
flags: u32,
variant: union { Frog, Door, Map },
}
entity: Entity;
entity.id = 1337;
// implicit conversion from variant to base type
entity.variant = Frog{
ribbit_volume = 0.5,
jump_height = 2.1,
/*other data */
};
entity.name = "Frank";
entity.position = Vector3{1, 4, 9};
match e in entity.variant {
case Frog:
fmt.println("Ribbit");
case Door:
fmt.println("Creak");
case Map:
fmt.println("Rustle");
case:
fmt.println("Just a normal entity");
}
if frog, ok := entity.variant.(Frog); ok {
fmt.printf("The frog jumps %f feet high at %v\n", frog.jump_height, entity.position);
}
// Panics if not the correct type
frog: Frog;
frog = entity.variant.(Frog);
frog, _ = entity.variant.(Frog); // ignore error and force cast
}
}
}

View File

@@ -1,570 +0,0 @@
import "core:fmt.odin"
import "core:strconv.odin"
import "core:mem.odin"
import "core:bits.odin"
import "core:hash.odin"
import "core:math.odin"
import "core:os.odin"
import "core:raw.odin"
import "core:sort.odin"
import "core:strings.odin"
import "core:types.odin"
import "core:utf16.odin"
import "core:utf8.odin"
when ODIN_OS == "windows" {
import "core:atomics.odin"
import "core:opengl.odin"
import "core:thread.odin"
import win32 "core:sys/windows.odin"
}
general_stuff :: proc() {
{ // `do` for inline statmes rather than block
foo :: proc() do fmt.println("Foo!");
if false do foo();
for false do foo();
when false do foo();
if false do foo();
else do foo();
}
{ // Removal of `++` and `--` (again)
x: int;
x += 1;
x -= 1;
}
{ // Casting syntaxes
i := i32(137);
ptr := &i;
fp1 := (^f32)(ptr);
// ^f32(ptr) == ^(f32(ptr))
fp2 := cast(^f32)ptr;
f1 := (^f32)(ptr)^;
f2 := (cast(^f32)ptr)^;
// Questions: Should there be two ways to do it?
}
/*
* Remove *_val_of built-in procedures
* size_of, align_of, offset_of
* type_of, type_info_of
*/
{ // `expand_to_tuple` built-in procedure
Foo :: struct {
x: int,
b: bool,
}
f := Foo{137, true};
x, b := expand_to_tuple(f);
fmt.println(f);
fmt.println(x, b);
fmt.println(expand_to_tuple(f));
}
{
// .. half-closed range
// .. open range
for in 0..2 {} // 0, 1
for in 0..2 {} // 0, 1, 2
}
}
default_struct_values :: proc() {
{
Vector3 :: struct {
x: f32,
y: f32,
z: f32,
}
v: Vector3;
fmt.println(v);
}
{
// Default values must be constants
Vector3 :: struct {
x: f32 = 1,
y: f32 = 4,
z: f32 = 9,
}
v: Vector3;
fmt.println(v);
v = Vector3{};
fmt.println(v);
// Uses the same semantics as a default values in a procedure
v = Vector3{137};
fmt.println(v);
v = Vector3{z = 137};
fmt.println(v);
}
{
Vector3 :: struct {
x := 1.0,
y := 4.0,
z := 9.0,
}
stack_default: Vector3;
stack_literal := Vector3{};
heap_one := new(Vector3); defer free(heap_one);
heap_two := new_clone(Vector3{}); defer free(heap_two);
fmt.println("stack_default - ", stack_default);
fmt.println("stack_literal - ", stack_literal);
fmt.println("heap_one - ", heap_one^);
fmt.println("heap_two - ", heap_two^);
N :: 4;
stack_array: [N]Vector3;
heap_array := new([N]Vector3); defer free(heap_array);
heap_slice := make([]Vector3, N); defer free(heap_slice);
fmt.println("stack_array[1] - ", stack_array[1]);
fmt.println("heap_array[1] - ", heap_array[1]);
fmt.println("heap_slice[1] - ", heap_slice[1]);
}
}
union_type :: proc() {
{
val: union{int, bool};
val = 137;
if i, ok := val.(int); ok {
fmt.println(i);
}
val = true;
fmt.println(val);
val = nil;
switch v in val {
case int: fmt.println("int", v);
case bool: fmt.println("bool", v);
case: fmt.println("nil");
}
}
{
// There is a duality between `any` and `union`
// An `any` has a pointer to the data and allows for any type (open)
// A `union` has as binary blob to store the data and allows only certain types (closed)
// The following code is with `any` but has the same syntax
val: any;
val = 137;
if i, ok := val.(int); ok {
fmt.println(i);
}
val = true;
fmt.println(val);
val = nil;
switch v in val {
case int: fmt.println("int", v);
case bool: fmt.println("bool", v);
case: fmt.println("nil");
}
}
Vector3 :: struct {x, y, z: f32};
Quaternion :: struct {x, y, z: f32, w: f32 = 1};
// More realistic examples
{
// NOTE(bill): For the above basic examples, you may not have any
// particular use for it. However, my main use for them is not for these
// simple cases. My main use is for hierarchical types. Many prefer
// subtyping, embedding the base data into the derived types. Below is
// an example of this for a basic game Entity.
Entity :: struct {
id: u64,
name: string,
position: Vector3,
orientation: Quaternion,
derived: any,
}
Frog :: struct {
using entity: Entity,
jump_height: f32,
}
Monster :: struct {
using entity: Entity,
is_robot: bool,
is_zombie: bool,
}
// See `parametric_polymorphism` procedure for details
new_entity :: proc(T: type) -> ^Entity {
t := new(T);
t.derived = t^;
return t;
}
entity := new_entity(Monster);
switch e in entity.derived {
case Frog:
fmt.println("Ribbit");
case Monster:
if e.is_robot do fmt.println("Robotic");
if e.is_zombie do fmt.println("Grrrr!");
}
}
{
// NOTE(bill): A union can be used to achieve something similar. Instead
// of embedding the base data into the derived types, the derived data
// in embedded into the base type. Below is the same example of the
// basic game Entity but using an union.
Entity :: struct {
id: u64,
name: string,
position: Vector3,
orientation: Quaternion,
derived: union {Frog, Monster},
}
Frog :: struct {
using entity: ^Entity,
jump_height: f32,
}
Monster :: struct {
using entity: ^Entity,
is_robot: bool,
is_zombie: bool,
}
// See `parametric_polymorphism` procedure for details
new_entity :: proc(T: type) -> ^Entity {
t := new(Entity);
t.derived = T{entity = t};
return t;
}
entity := new_entity(Monster);
switch e in entity.derived {
case Frog:
fmt.println("Ribbit");
case Monster:
if e.is_robot do fmt.println("Robotic");
if e.is_zombie do fmt.println("Grrrr!");
}
// NOTE(bill): As you can see, the usage code has not changed, only its
// memory layout. Both approaches have their own advantages but they can
// be used together to achieve different results. The subtyping approach
// can allow for a greater control of the memory layout and memory
// allocation, e.g. storing the derivatives together. However, this is
// also its disadvantage. You must either preallocate arrays for each
// derivative separation (which can be easily missed) or preallocate a
// bunch of "raw" memory; determining the maximum size of the derived
// types would require the aid of metaprogramming. Unions solve this
// particular problem as the data is stored with the base data.
// Therefore, it is possible to preallocate, e.g. [100]Entity.
// It should be noted that the union approach can have the same memory
// layout as the any and with the same type restrictions by using a
// pointer type for the derivatives.
/*
Entity :: struct {
..
derived: union{^Frog, ^Monster};
}
Frog :: struct {
using entity: Entity;
..
}
Monster :: struct {
using entity: Entity;
..
}
new_entity :: proc(T: type) -> ^Entity {
t := new(T);
t.derived = t;
return t;
}
*/
}
}
parametric_polymorphism :: proc() {
print_value :: proc(value: $T) {
fmt.printf("print_value: %T %v\n", value, value);
}
v1: int = 1;
v2: f32 = 2.1;
v3: f64 = 3.14;
v4: string = "message";
print_value(v1);
print_value(v2);
print_value(v3);
print_value(v4);
fmt.println();
add :: proc(p, q: $T) -> T {
x: T = p + q;
return x;
}
a := add(3, 4);
fmt.printf("a: %T = %v\n", a, a);
b := add(3.2, 4.3);
fmt.printf("b: %T = %v\n", b, b);
// This is how `new` is implemented
alloc_type :: proc(T: type) -> ^T {
t := cast(^T)alloc(size_of(T), align_of(T));
t^ = T{}; // Use default initialization value
return t;
}
copy_slice :: proc(dst, src: []$T) -> int {
n := min(len(dst), len(src));
if n > 0 {
mem.copy(&dst[0], &src[0], n*size_of(T));
}
return n;
}
double_params :: proc(a: $A, b: $B) -> A {
return a + A(b);
}
fmt.println(double_params(12, 1.345));
{ // Polymorphic Types and Type Specialization
Table_Slot :: struct(Key, Value: type) {
occupied: bool,
hash: u32,
key: Key,
value: Value,
}
TABLE_SIZE_MIN :: 32;
Table :: struct(Key, Value: type) {
count: int,
allocator: Allocator,
slots: []Table_Slot(Key, Value),
}
// Only allow types that are specializations of a (polymorphic) slice
make_slice :: proc(T: type/[]$E, len: int) -> T {
return make(T, len);
}
// Only allow types that are specializations of `Table`
allocate :: proc(table: ^$T/Table, capacity: int) {
c := context;
if table.allocator.procedure != nil do c.allocator = table.allocator;
push_context c {
table.slots = make_slice(type_of(table.slots), max(capacity, TABLE_SIZE_MIN));
}
}
expand :: proc(table: ^$T/Table) {
c := context;
if table.allocator.procedure != nil do c.allocator = table.allocator;
push_context c {
old_slots := table.slots;
cap := max(2*cap(table.slots), TABLE_SIZE_MIN);
allocate(table, cap);
for s in old_slots do if s.occupied {
put(table, s.key, s.value);
}
free(old_slots);
}
}
// Polymorphic determination of a polymorphic struct
// put :: proc(table: ^$T/Table, key: T.Key, value: T.Value) {
put :: proc(table: ^Table($Key, $Value), key: Key, value: Value) {
hash := get_hash(key); // Ad-hoc method which would fail in a different scope
index := find_index(table, key, hash);
if index < 0 {
if f64(table.count) >= 0.75*f64(cap(table.slots)) {
expand(table);
}
assert(table.count <= cap(table.slots));
hash := get_hash(key);
index = int(hash % u32(cap(table.slots)));
for table.slots[index].occupied {
if index += 1; index >= cap(table.slots) {
index = 0;
}
}
table.count += 1;
}
slot := &table.slots[index];
slot.occupied = true;
slot.hash = hash;
slot.key = key;
slot.value = value;
}
// find :: proc(table: ^$T/Table, key: T.Key) -> (T.Value, bool) {
find :: proc(table: ^Table($Key, $Value), key: Key) -> (Value, bool) {
hash := get_hash(key);
index := find_index(table, key, hash);
if index < 0 {
return Value{}, false;
}
return table.slots[index].value, true;
}
find_index :: proc(table: ^Table($Key, $Value), key: Key, hash: u32) -> int {
if cap(table.slots) <= 0 do return -1;
index := int(hash % u32(cap(table.slots)));
for table.slots[index].occupied {
if table.slots[index].hash == hash {
if table.slots[index].key == key {
return index;
}
}
if index += 1; index >= cap(table.slots) {
index = 0;
}
}
return -1;
}
get_hash :: proc(s: string) -> u32 { // fnv32a
h: u32 = 0x811c9dc5;
for i in 0..len(s) {
h = (h ~ u32(s[i])) * 0x01000193;
}
return h;
}
table: Table(string, int);
for i in 0..36 do put(&table, "Hellope", i);
for i in 0..42 do put(&table, "World!", i);
found, _ := find(&table, "Hellope");
fmt.printf("`found` is %v\n", found);
found, _ = find(&table, "World!");
fmt.printf("`found` is %v\n", found);
// I would not personally design a hash table like this in production
// but this is a nice basic example
// A better approach would either use a `u64` or equivalent for the key
// and let the user specify the hashing function or make the user store
// the hashing procedure with the table
}
}
prefix_table := [?]string{
"White",
"Red",
"Green",
"Blue",
"Octarine",
"Black",
};
threading_example :: proc() {
when ODIN_OS == "windows" {
unordered_remove :: proc(array: ^[]$T, index: int, loc := #caller_location) {
__bounds_check_error_loc(loc, index, len(array));
array[index] = array[len(array)-1];
pop(array);
}
ordered_remove :: proc(array: ^[]$T, index: int, loc := #caller_location) {
__bounds_check_error_loc(loc, index, len(array));
copy(array[index..], array[index+1..]);
pop(array);
}
worker_proc :: proc(t: ^thread.Thread) -> int {
for iteration in 1..5 {
fmt.printf("Thread %d is on iteration %d\n", t.user_index, iteration);
fmt.printf("`%s`: iteration %d\n", prefix_table[t.user_index], iteration);
// win32.sleep(1);
}
return 0;
}
threads := make([]^thread.Thread, 0, len(prefix_table));
defer free(threads);
for i in 0..len(prefix_table) {
if t := thread.create(worker_proc); t != nil {
t.init_context = context;
t.use_init_context = true;
t.user_index = len(threads);
append(&threads, t);
thread.start(t);
}
}
for len(threads) > 0 {
for i := 0; i < len(threads); /**/ {
if t := threads[i]; thread.is_done(t) {
fmt.printf("Thread %d is done\n", t.user_index);
thread.destroy(t);
ordered_remove(&threads, i);
} else {
i += 1;
}
}
}
}
}
main :: proc() {
when false {
fmt.println("\n# general_stuff"); general_stuff();
fmt.println("\n# default_struct_values"); default_struct_values();
fmt.println("\n# union_type"); union_type();
fmt.println("\n# parametric_polymorphism"); parametric_polymorphism();
fmt.println("\n# threading_example"); threading_example();
}
}

View File

@@ -1,778 +0,0 @@
import "core:fmt.odin"
import "core:strconv.odin"
import "core:mem.odin"
import "core:bits.odin"
import "core:hash.odin"
import "core:math.odin"
import "core:math/rand.odin"
import "core:os.odin"
import "core:raw.odin"
import "core:sort.odin"
import "core:strings.odin"
import "core:types.odin"
import "core:utf16.odin"
import "core:utf8.odin"
// File scope `when` statements
when ODIN_OS == "windows" {
import "core:atomics.odin"
import "core:thread.odin"
import win32 "core:sys/windows.odin"
}
@(link_name="general_stuff")
general_stuff :: proc() {
fmt.println("# general_stuff");
{ // `do` for inline statements rather than block
foo :: proc() do fmt.println("Foo!");
if false do foo();
for false do foo();
when false do foo();
if false do foo();
else do foo();
}
{ // Removal of `++` and `--` (again)
x: int;
x += 1;
x -= 1;
}
{ // Casting syntaxes
i := i32(137);
ptr := &i;
_ = (^f32)(ptr);
// ^f32(ptr) == ^(f32(ptr))
_ = cast(^f32)ptr;
_ = (^f32)(ptr)^;
_ = (cast(^f32)ptr)^;
// Questions: Should there be two ways to do it?
}
/*
* Remove *_val_of built-in procedures
* size_of, align_of, offset_of
* type_of, type_info_of
*/
{ // `expand_to_tuple` built-in procedure
Foo :: struct {
x: int,
b: bool,
}
f := Foo{137, true};
x, b := expand_to_tuple(f);
fmt.println(f);
fmt.println(x, b);
fmt.println(expand_to_tuple(f));
}
{
// .. half-closed range
// .. open range
for in 0..2 {} // 0, 1
for in 0..2 {} // 0, 1, 2
}
{ // Multiple sized booleans
x0: bool; // default
x1: b8 = true;
x2: b16 = false;
x3: b32 = true;
x4: b64 = false;
fmt.printf("x1: %T = %v;\n", x1, x1);
fmt.printf("x2: %T = %v;\n", x2, x2);
fmt.printf("x3: %T = %v;\n", x3, x3);
fmt.printf("x4: %T = %v;\n", x4, x4);
// Having specific sized booleans is very useful when dealing with foreign code
// and to enforce specific alignment for a boolean, especially within a struct
}
{ // `distinct` types
// Originally, all type declarations would create a distinct type unless #type_alias was present.
// Now the behaviour has been reversed. All type declarations create a type alias unless `distinct` is present.
// If the type expression is `struct`, `union`, `enum`, or `proc`, the types will always been distinct.
Int32 :: i32;
#assert(Int32 == i32);
My_Int32 :: distinct i32;
#assert(My_Int32 != i32);
My_Struct :: struct{x: int};
#assert(My_Struct != struct{x: int});
}
}
default_struct_values :: proc() {
fmt.println("# default_struct_values");
{
Vector3 :: struct {
x: f32,
y: f32,
z: f32,
}
v: Vector3;
fmt.println(v);
}
{
// Default values must be constants
Vector3 :: struct {
x: f32 = 1,
y: f32 = 4,
z: f32 = 9,
}
v: Vector3;
fmt.println(v);
v = Vector3{};
fmt.println(v);
// Uses the same semantics as a default values in a procedure
v = Vector3{137};
fmt.println(v);
v = Vector3{z = 137};
fmt.println(v);
}
{
Vector3 :: struct {
x := 1.0,
y := 4.0,
z := 9.0,
}
stack_default: Vector3;
stack_literal := Vector3{};
heap_one := new(Vector3); defer free(heap_one);
heap_two := new_clone(Vector3{}); defer free(heap_two);
fmt.println("stack_default - ", stack_default);
fmt.println("stack_literal - ", stack_literal);
fmt.println("heap_one - ", heap_one^);
fmt.println("heap_two - ", heap_two^);
N :: 4;
stack_array: [N]Vector3;
heap_array := new([N]Vector3); defer free(heap_array);
heap_slice := make([]Vector3, N); defer free(heap_slice);
fmt.println("stack_array[1] - ", stack_array[1]);
fmt.println("heap_array[1] - ", heap_array[1]);
fmt.println("heap_slice[1] - ", heap_slice[1]);
}
}
union_type :: proc() {
fmt.println("\n# union_type");
{
val: union{int, bool};
val = 137;
if i, ok := val.(int); ok {
fmt.println(i);
}
val = true;
fmt.println(val);
val = nil;
switch v in val {
case int: fmt.println("int", v);
case bool: fmt.println("bool", v);
case: fmt.println("nil");
}
}
{
// There is a duality between `any` and `union`
// An `any` has a pointer to the data and allows for any type (open)
// A `union` has as binary blob to store the data and allows only certain types (closed)
// The following code is with `any` but has the same syntax
val: any;
val = 137;
if i, ok := val.(int); ok {
fmt.println(i);
}
val = true;
fmt.println(val);
val = nil;
switch v in val {
case int: fmt.println("int", v);
case bool: fmt.println("bool", v);
case: fmt.println("nil");
}
}
Vector3 :: struct {x, y, z: f32};
Quaternion :: struct {x, y, z: f32, w: f32 = 1};
// More realistic examples
{
// NOTE(bill): For the above basic examples, you may not have any
// particular use for it. However, my main use for them is not for these
// simple cases. My main use is for hierarchical types. Many prefer
// subtyping, embedding the base data into the derived types. Below is
// an example of this for a basic game Entity.
Entity :: struct {
id: u64,
name: string,
position: Vector3,
orientation: Quaternion,
derived: any,
}
Frog :: struct {
using entity: Entity,
jump_height: f32,
}
Monster :: struct {
using entity: Entity,
is_robot: bool,
is_zombie: bool,
}
// See `parametric_polymorphism` procedure for details
new_entity :: proc(T: type) -> ^Entity {
t := new(T);
t.derived = t^;
return t;
}
entity := new_entity(Monster);
switch e in entity.derived {
case Frog:
fmt.println("Ribbit");
case Monster:
if e.is_robot do fmt.println("Robotic");
if e.is_zombie do fmt.println("Grrrr!");
}
}
{
// NOTE(bill): A union can be used to achieve something similar. Instead
// of embedding the base data into the derived types, the derived data
// in embedded into the base type. Below is the same example of the
// basic game Entity but using an union.
Entity :: struct {
id: u64,
name: string,
position: Vector3,
orientation: Quaternion,
derived: union {Frog, Monster},
}
Frog :: struct {
using entity: ^Entity,
jump_height: f32,
}
Monster :: struct {
using entity: ^Entity,
is_robot: bool,
is_zombie: bool,
}
// See `parametric_polymorphism` procedure for details
new_entity :: proc(T: type) -> ^Entity {
t := new(Entity);
t.derived = T{entity = t};
return t;
}
entity := new_entity(Monster);
switch e in entity.derived {
case Frog:
fmt.println("Ribbit");
case Monster:
if e.is_robot do fmt.println("Robotic");
if e.is_zombie do fmt.println("Grrrr!");
}
// NOTE(bill): As you can see, the usage code has not changed, only its
// memory layout. Both approaches have their own advantages but they can
// be used together to achieve different results. The subtyping approach
// can allow for a greater control of the memory layout and memory
// allocation, e.g. storing the derivatives together. However, this is
// also its disadvantage. You must either preallocate arrays for each
// derivative separation (which can be easily missed) or preallocate a
// bunch of "raw" memory; determining the maximum size of the derived
// types would require the aid of metaprogramming. Unions solve this
// particular problem as the data is stored with the base data.
// Therefore, it is possible to preallocate, e.g. [100]Entity.
// It should be noted that the union approach can have the same memory
// layout as the any and with the same type restrictions by using a
// pointer type for the derivatives.
/*
Entity :: struct {
..
derived: union{^Frog, ^Monster},
}
Frog :: struct {
using entity: Entity,
..
}
Monster :: struct {
using entity: Entity,
..
}
new_entity :: proc(T: type) -> ^Entity {
t := new(T);
t.derived = t;
return t;
}
*/
}
}
parametric_polymorphism :: proc() {
fmt.println("# parametric_polymorphism");
print_value :: proc(value: $T) {
fmt.printf("print_value: %T %v\n", value, value);
}
v1: int = 1;
v2: f32 = 2.1;
v3: f64 = 3.14;
v4: string = "message";
print_value(v1);
print_value(v2);
print_value(v3);
print_value(v4);
fmt.println();
add :: proc(p, q: $T) -> T {
x: T = p + q;
return x;
}
a := add(3, 4);
fmt.printf("a: %T = %v\n", a, a);
b := add(3.2, 4.3);
fmt.printf("b: %T = %v\n", b, b);
// This is how `new` is implemented
alloc_type :: proc(T: type) -> ^T {
t := cast(^T)alloc(size_of(T), align_of(T));
t^ = T{}; // Use default initialization value
return t;
}
copy_slice :: proc(dst, src: []$T) -> int {
return mem.copy(&dst[0], &src[0], n*size_of(T));
}
double_params :: proc(a: $A, b: $B) -> A {
return a + A(b);
}
fmt.println(double_params(12, 1.345));
{ // Polymorphic Types and Type Specialization
Table_Slot :: struct(Key, Value: type) {
occupied: bool,
hash: u32,
key: Key,
value: Value,
}
TABLE_SIZE_MIN :: 32;
Table :: struct(Key, Value: type) {
count: int,
allocator: Allocator,
slots: []Table_Slot(Key, Value),
}
// Only allow types that are specializations of a (polymorphic) slice
make_slice :: proc(T: type/[]$E, len: int) -> T {
return make(T, len);
}
// Only allow types that are specializations of `Table`
allocate :: proc(table: ^$T/Table, capacity: int) {
c := context;
if table.allocator.procedure != nil do c.allocator = table.allocator;
context <- c {
table.slots = make_slice(type_of(table.slots), max(capacity, TABLE_SIZE_MIN));
}
}
expand :: proc(table: ^$T/Table) {
c := context;
if table.allocator.procedure != nil do c.allocator = table.allocator;
context <- c {
old_slots := table.slots;
cap := max(2*len(table.slots), TABLE_SIZE_MIN);
allocate(table, cap);
for s in old_slots do if s.occupied {
put(table, s.key, s.value);
}
free(old_slots);
}
}
// Polymorphic determination of a polymorphic struct
// put :: proc(table: ^$T/Table, key: T.Key, value: T.Value) {
put :: proc(table: ^Table($Key, $Value), key: Key, value: Value) {
hash := get_hash(key); // Ad-hoc method which would fail in a different scope
index := find_index(table, key, hash);
if index < 0 {
if f64(table.count) >= 0.75*f64(len(table.slots)) {
expand(table);
}
assert(table.count <= len(table.slots));
hash := get_hash(key);
index = int(hash % u32(len(table.slots)));
for table.slots[index].occupied {
if index += 1; index >= len(table.slots) {
index = 0;
}
}
table.count += 1;
}
slot := &table.slots[index];
slot.occupied = true;
slot.hash = hash;
slot.key = key;
slot.value = value;
}
// find :: proc(table: ^$T/Table, key: T.Key) -> (T.Value, bool) {
find :: proc(table: ^Table($Key, $Value), key: Key) -> (Value, bool) {
hash := get_hash(key);
index := find_index(table, key, hash);
if index < 0 {
return Value{}, false;
}
return table.slots[index].value, true;
}
find_index :: proc(table: ^Table($Key, $Value), key: Key, hash: u32) -> int {
if len(table.slots) <= 0 do return -1;
index := int(hash % u32(len(table.slots)));
for table.slots[index].occupied {
if table.slots[index].hash == hash {
if table.slots[index].key == key {
return index;
}
}
if index += 1; index >= len(table.slots) {
index = 0;
}
}
return -1;
}
get_hash :: proc(s: string) -> u32 { // fnv32a
h: u32 = 0x811c9dc5;
for i in 0..len(s) {
h = (h ~ u32(s[i])) * 0x01000193;
}
return h;
}
table: Table(string, int);
for i in 0..36 do put(&table, "Hellope", i);
for i in 0..42 do put(&table, "World!", i);
found, _ := find(&table, "Hellope");
fmt.printf("`found` is %v\n", found);
found, _ = find(&table, "World!");
fmt.printf("`found` is %v\n", found);
// I would not personally design a hash table like this in production
// but this is a nice basic example
// A better approach would either use a `u64` or equivalent for the key
// and let the user specify the hashing function or make the user store
// the hashing procedure with the table
}
}
prefix_table := [?]string{
"White",
"Red",
"Green",
"Blue",
"Octarine",
"Black",
};
threading_example :: proc() {
when ODIN_OS == "windows" {
fmt.println("# threading_example");
unordered_remove :: proc(array: ^[dynamic]$T, index: int, loc := #caller_location) {
__bounds_check_error_loc(loc, index, len(array));
array[index] = array[len(array)-1];
pop(array);
}
ordered_remove :: proc(array: ^[dynamic]$T, index: int, loc := #caller_location) {
__bounds_check_error_loc(loc, index, len(array));
copy(array[index..], array[index+1..]);
pop(array);
}
worker_proc :: proc(t: ^thread.Thread) -> int {
for iteration in 1..5 {
fmt.printf("Thread %d is on iteration %d\n", t.user_index, iteration);
fmt.printf("`%s`: iteration %d\n", prefix_table[t.user_index], iteration);
// win32.sleep(1);
}
return 0;
}
threads := make([dynamic]^thread.Thread, 0, len(prefix_table));
defer free(threads);
for in prefix_table {
if t := thread.create(worker_proc); t != nil {
t.init_context = context;
t.use_init_context = true;
t.user_index = len(threads);
append(&threads, t);
thread.start(t);
}
}
for len(threads) > 0 {
for i := 0; i < len(threads); /**/ {
if t := threads[i]; thread.is_done(t) {
fmt.printf("Thread %d is done\n", t.user_index);
thread.destroy(t);
ordered_remove(&threads, i);
} else {
i += 1;
}
}
}
}
}
array_programming :: proc() {
fmt.println("# array_programming");
{
a := [3]f32{1, 2, 3};
b := [3]f32{5, 6, 7};
c := a * b;
d := a + b;
e := 1 + (c - d) / 2;
fmt.printf("%.1f\n", e); // [0.5, 3.0, 6.5]
}
{
a := [3]f32{1, 2, 3};
b := swizzle(a, 2, 1, 0);
assert(b == [3]f32{3, 2, 1});
c := swizzle(a, 0, 0);
assert(c == [2]f32{1, 1});
assert(c == 1);
}
{
Vector3 :: distinct [3]f32;
a := Vector3{1, 2, 3};
b := Vector3{5, 6, 7};
c := (a * b)/2 + 1;
d := c.x + c.y + c.z;
fmt.printf("%.1f\n", d); // 22.0
cross :: proc(a, b: Vector3) -> Vector3 {
i := swizzle(a, 1, 2, 0) * swizzle(b, 2, 0, 1);
j := swizzle(a, 2, 0, 1) * swizzle(b, 1, 2, 0);
return i - j;
}
blah :: proc(a: Vector3) -> f32 {
return a.x + a.y + a.z;
}
x := cross(a, b);
fmt.println(x);
fmt.println(blah(x));
}
}
using println in import "core:fmt.odin"
using_in :: proc() {
fmt.println("# using in");
using print in fmt;
println("Hellope1");
print("Hellope2\n");
Foo :: struct {
x, y: int,
b: bool,
}
f: Foo;
f.x, f.y = 123, 321;
println(f);
using x, y in f;
x, y = 456, 654;
println(f);
}
named_proc_return_parameters :: proc() {
fmt.println("# named proc return parameters");
foo0 :: proc() -> int {
return 123;
}
foo1 :: proc() -> (a: int) {
a = 123;
return;
}
foo2 :: proc() -> (a, b: int) {
// Named return values act like variables within the scope
a = 321;
b = 567;
return b, a;
}
fmt.println("foo0 =", foo0()); // 123
fmt.println("foo1 =", foo1()); // 123
fmt.println("foo2 =", foo2()); // 567 321
}
enum_export :: proc() {
fmt.println("# enum #export");
Foo :: enum #export {A, B, C};
f0 := A;
f1 := B;
f2 := C;
fmt.println(f0, f1, f2);
}
explicit_procedure_overloading :: proc() {
fmt.println("# explicit procedure overloading");
add_ints :: proc(a, b: int) -> int {
x := a + b;
fmt.println("add_ints", x);
return x;
}
add_floats :: proc(a, b: f32) -> f32 {
x := a + b;
fmt.println("add_floats", x);
return x;
}
add_numbers :: proc(a: int, b: f32, c: u8) -> int {
x := int(a) + int(b) + int(c);
fmt.println("add_numbers", x);
return x;
}
add :: proc[add_ints, add_floats, add_numbers];
add(int(1), int(2));
add(f32(1), f32(2));
add(int(1), f32(2), u8(3));
add(1, 2); // untyped ints coerce to int tighter than f32
add(1.0, 2.0); // untyped floats coerce to f32 tighter than int
add(1, 2, 3); // three parameters
// Ambiguous answers
// add(1.0, 2);
// add(1, 2.0);
}
complete_switch :: proc() {
fmt.println("# complete_switch");
{ // enum
Foo :: enum #export {
A,
B,
C,
D,
}
b := Foo.B;
f := Foo.A;
#complete switch f {
case A: fmt.println("A");
case B: fmt.println("B");
case C: fmt.println("C");
case D: fmt.println("D");
case: fmt.println("?");
}
}
{ // union
Foo :: union {int, bool};
f: Foo = 123;
#complete switch in f {
case int: fmt.println("int");
case bool: fmt.println("bool");
case:
}
}
}
main :: proc() {
when true {
general_stuff();
default_struct_values();
union_type();
parametric_polymorphism();
threading_example();
array_programming();
using_in();
named_proc_return_parameters();
enum_export();
explicit_procedure_overloading();
complete_switch();
}
}

View File

@@ -1,412 +0,0 @@
#include "win32.odin"
assume :: proc(cond: bool) #foreign "llvm.assume"
__debug_trap :: proc() #foreign "llvm.debugtrap"
__trap :: proc() #foreign "llvm.trap"
read_cycle_counter :: proc() -> u64 #foreign "llvm.readcyclecounter"
bit_reverse16 :: proc(b: u16) -> u16 #foreign "llvm.bitreverse.i16"
bit_reverse32 :: proc(b: u32) -> u32 #foreign "llvm.bitreverse.i32"
bit_reverse64 :: proc(b: u64) -> u64 #foreign "llvm.bitreverse.i64"
byte_swap16 :: proc(b: u16) -> u16 #foreign "llvm.bswap.i16"
byte_swap32 :: proc(b: u32) -> u32 #foreign "llvm.bswap.i32"
byte_swap64 :: proc(b: u64) -> u64 #foreign "llvm.bswap.i64"
fmuladd_f32 :: proc(a, b, c: f32) -> f32 #foreign "llvm.fmuladd.f32"
fmuladd_f64 :: proc(a, b, c: f64) -> f64 #foreign "llvm.fmuladd.f64"
// TODO(bill): make custom heap procedures
heap_alloc :: proc(len: int) -> rawptr #foreign "malloc"
heap_dealloc :: proc(ptr: rawptr) #foreign "free"
memory_zero :: proc(data: rawptr, len: int) {
d := slice_ptr(data as ^byte, len)
for i := 0; i < len; i++ {
d[i] = 0
}
}
memory_compare :: proc(dst, src: rawptr, len: int) -> int {
s1, s2: ^byte = dst, src
for i := 0; i < len; i++ {
a := ptr_offset(s1, i)^
b := ptr_offset(s2, i)^
if a != b {
return (a - b) as int
}
}
return 0
}
memory_copy :: proc(dst, src: rawptr, n: int) #inline {
if dst == src {
return
}
v128b :: type {4}u32
#assert(align_of(v128b) == 16)
d, s: ^byte = dst, src
for ; s as uint % 16 != 0 && n != 0; n-- {
d^ = s^
d, s = ptr_offset(d, 1), ptr_offset(s, 1)
}
if d as uint % 16 == 0 {
for ; n >= 16; d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16 {
(d as ^v128b)^ = (s as ^v128b)^
}
if n&8 != 0 {
(d as ^u64)^ = (s as ^u64)^
d, s = ptr_offset(d, 8), ptr_offset(s, 8)
}
if n&4 != 0 {
(d as ^u32)^ = (s as ^u32)^;
d, s = ptr_offset(d, 4), ptr_offset(s, 4)
}
if n&2 != 0 {
(d as ^u16)^ = (s as ^u16)^
d, s = ptr_offset(d, 2), ptr_offset(s, 2)
}
if n&1 != 0 {
d^ = s^
d, s = ptr_offset(d, 1), ptr_offset(s, 1)
}
return;
}
// IMPORTANT NOTE(bill): Little endian only
LS :: proc(a, b: u32) -> u32 #inline { return a << b }
RS :: proc(a, b: u32) -> u32 #inline { return a >> b }
/* NOTE(bill): Big endian version
LS :: proc(a, b: u32) -> u32 #inline { return a >> b; }
RS :: proc(a, b: u32) -> u32 #inline { return a << b; }
*/
w, x: u32
if d as uint % 4 == 1 {
w = (s as ^u32)^
d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1)
d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1)
d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1)
n -= 3
for n > 16 {
d32 := d as ^u32
s32 := ptr_offset(s, 1) as ^u32
x = s32^; d32^ = LS(w, 24) | RS(x, 8)
d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
w = s32^; d32^ = LS(x, 24) | RS(w, 8)
d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
x = s32^; d32^ = LS(w, 24) | RS(x, 8)
d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
w = s32^; d32^ = LS(x, 24) | RS(w, 8)
d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16
}
} else if d as uint % 4 == 2 {
w = (s as ^u32)^
d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1)
d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1)
n -= 2
for n > 17 {
d32 := d as ^u32
s32 := ptr_offset(s, 2) as ^u32
x = s32^; d32^ = LS(w, 16) | RS(x, 16)
d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
w = s32^; d32^ = LS(x, 16) | RS(w, 16)
d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
x = s32^; d32^ = LS(w, 16) | RS(x, 16)
d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
w = s32^; d32^ = LS(x, 16) | RS(w, 16)
d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16
}
} else if d as uint % 4 == 3 {
w = (s as ^u32)^
d^ = s^
n -= 1
for n > 18 {
d32 := d as ^u32
s32 := ptr_offset(s, 3) as ^u32
x = s32^; d32^ = LS(w, 8) | RS(x, 24)
d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
w = s32^; d32^ = LS(x, 8) | RS(w, 24)
d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
x = s32^; d32^ = LS(w, 8) | RS(x, 24)
d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
w = s32^; d32^ = LS(x, 8) | RS(w, 24)
d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16
}
}
if n&16 != 0 {
(d as ^v128b)^ = (s as ^v128b)^
d, s = ptr_offset(d, 16), ptr_offset(s, 16)
}
if n&8 != 0 {
(d as ^u64)^ = (s as ^u64)^
d, s = ptr_offset(d, 8), ptr_offset(s, 8)
}
if n&4 != 0 {
(d as ^u32)^ = (s as ^u32)^;
d, s = ptr_offset(d, 4), ptr_offset(s, 4)
}
if n&2 != 0 {
(d as ^u16)^ = (s as ^u16)^
d, s = ptr_offset(d, 2), ptr_offset(s, 2)
}
if n&1 != 0 {
d^ = s^
}
}
memory_move :: proc(dst, src: rawptr, n: int) #inline {
d, s: ^byte = dst, src
if d == s {
return
}
if d >= ptr_offset(s, n) || ptr_offset(d, n) <= s {
memory_copy(d, s, n)
return
}
// TODO(bill): Vectorize the shit out of this
if d < s {
if s as int % size_of(int) == d as int % size_of(int) {
for d as int % size_of(int) != 0 {
if n == 0 {
return
}
n--
d^ = s^
d, s = ptr_offset(d, 1), ptr_offset(s, 1)
}
di, si := d as ^int, s as ^int
for n >= size_of(int) {
di^ = si^
di, si = ptr_offset(di, 1), ptr_offset(si, 1)
n -= size_of(int)
}
}
for ; n > 0; n-- {
d^ = s^
d, s = ptr_offset(d, 1), ptr_offset(s, 1)
}
} else {
if s as int % size_of(int) == d as int % size_of(int) {
for ptr_offset(d, n) as int % size_of(int) != 0 {
if n == 0 {
return
}
n--
d^ = s^
d, s = ptr_offset(d, 1), ptr_offset(s, 1)
}
for n >= size_of(int) {
n -= size_of(int)
di := ptr_offset(d, n) as ^int
si := ptr_offset(s, n) as ^int
di^ = si^
}
for ; n > 0; n-- {
d^ = s^
d, s = ptr_offset(d, 1), ptr_offset(s, 1)
}
}
for n > 0 {
n--
dn := ptr_offset(d, n)
sn := ptr_offset(s, n)
dn^ = sn^
}
}
}
__string_eq :: proc(a, b: string) -> bool {
if len(a) != len(b) {
return false
}
if ^a[0] == ^b[0] {
return true
}
return memory_compare(^a[0], ^b[0], len(a)) == 0
}
__string_cmp :: proc(a, b : string) -> int {
min_len := len(a)
if len(b) < min_len {
min_len = len(b)
}
for i := 0; i < min_len; i++ {
x := a[i]
y := b[i]
if x < y {
return -1
} else if x > y {
return +1
}
}
if len(a) < len(b) {
return -1
} else if len(a) > len(b) {
return +1
}
return 0
}
__string_ne :: proc(a, b : string) -> bool #inline { return !__string_eq(a, b) }
__string_lt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) < 0 }
__string_gt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) > 0 }
__string_le :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) <= 0 }
__string_ge :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) >= 0 }
Allocation_Mode :: type enum {
ALLOC,
DEALLOC,
DEALLOC_ALL,
RESIZE,
}
Allocator_Proc :: type proc(allocator_data: rawptr, mode: Allocation_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64) -> rawptr
Allocator :: type struct {
procedure: Allocator_Proc;
data: rawptr
}
Context :: type struct {
thread_ptr: rawptr
user_data: rawptr
user_index: int
allocator: Allocator
}
#thread_local context: Context
DEFAULT_ALIGNMENT :: 2*size_of(int)
__check_context :: proc() {
if context.allocator.procedure == null {
context.allocator = __default_allocator()
}
if context.thread_ptr == null {
// TODO(bill):
// context.thread_ptr = current_thread_pointer()
}
}
alloc :: proc(size: int) -> rawptr #inline { return alloc_align(size, DEFAULT_ALIGNMENT) }
alloc_align :: proc(size, alignment: int) -> rawptr #inline {
__check_context()
a := context.allocator
return a.procedure(a.data, Allocation_Mode.ALLOC, size, alignment, null, 0, 0)
}
dealloc :: proc(ptr: rawptr) #inline {
__check_context()
a := context.allocator
_ = a.procedure(a.data, Allocation_Mode.DEALLOC, 0, 0, ptr, 0, 0)
}
dealloc_all :: proc(ptr: rawptr) #inline {
__check_context()
a := context.allocator
_ = a.procedure(a.data, Allocation_Mode.DEALLOC_ALL, 0, 0, ptr, 0, 0)
}
resize :: proc(ptr: rawptr, old_size, new_size: int) -> rawptr #inline { return resize_align(ptr, old_size, new_size, DEFAULT_ALIGNMENT) }
resize_align :: proc(ptr: rawptr, old_size, new_size, alignment: int) -> rawptr #inline {
__check_context()
a := context.allocator
return a.procedure(a.data, Allocation_Mode.RESIZE, new_size, alignment, ptr, old_size, 0)
}
default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment: int) -> rawptr {
if old_memory == null {
return alloc_align(new_size, alignment)
}
if new_size == 0 {
dealloc(old_memory)
return null
}
if new_size == old_size {
return old_memory
}
new_memory := alloc_align(new_size, alignment)
if new_memory == null {
return null
}
memory_copy(new_memory, old_memory, min(old_size, new_size));
dealloc(old_memory)
return new_memory
}
__default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocation_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
using Allocation_Mode
match mode {
case ALLOC:
return heap_alloc(size)
case RESIZE:
return default_resize_align(old_memory, old_size, size, alignment)
case DEALLOC:
heap_dealloc(old_memory)
case DEALLOC_ALL:
// NOTE(bill): Does nothing
}
return null
}
__default_allocator :: proc() -> Allocator {
return Allocator{
__default_allocator_proc,
null,
}
}
__assert :: proc(msg: string) {
file_write(file_get_standard(File_Standard.ERROR), msg as []byte)
// TODO(bill): Which is better?
// __trap()
__debug_trap()
}

View File

@@ -1,430 +0,0 @@
import (
"fmt.odin";
"atomics.odin";
"bits.odin";
"decimal.odin";
"hash.odin";
"math.odin";
"mem.odin";
"opengl.odin";
"os.odin";
"raw.odin";
"strconv.odin";
"strings.odin";
"sync.odin";
"sort.odin";
"types.odin";
"utf8.odin";
"utf16.odin";
/*
*/
)
general_stuff :: proc() {
// Complex numbers
a := 3 + 4i;
b: complex64 = 3 + 4i;
c: complex128 = 3 + 4i;
d := complex(2, 3);
e := a / conj(a);
fmt.println("(3+4i)/(3-4i) =", e);
fmt.println(real(e), "+", imag(e), "i");
// C-style variadic procedures
foreign __llvm_core {
// The variadic part allows for extra type checking too which C does not provide
c_printf :: proc(fmt: ^u8, #c_vararg args: ..any) -> i32 #link_name "printf" ---;
}
str := "%d\n\x00";
// c_printf(&str[0], i32(789456123));
Foo :: struct {
x: int;
y: f32;
z: string;
}
foo := Foo{123, 0.513, "A string"};
x, y, z := expand_to_tuple(foo);
fmt.println(x, y, z);
#assert(type_of(x) == int);
#assert(type_of(y) == f32);
#assert(type_of(z) == string);
// By default, all variables are zeroed
// This can be overridden with the "uninitialized value"
// This is similar to `nil` but applied to everything
undef_int: int = ---;
// Context system is now implemented using Implicit Parameter Passing (IPP)
// The previous implementation was Thread Local Storage (TLS)
// IPP has the advantage that it works on systems without TLS and that you can
// link the context to the stack frame and thus look at previous contexts
//
// It does mean that a pointer is implicitly passed procedures with the default
// Odin calling convention (#cc_odin)
// This can be overridden with something like #cc_contextless or #cc_c if performance
// is worried about
}
foreign_blocks :: proc() {
// See sys/windows.odin
}
default_arguments :: proc() {
hello :: proc(a: int = 9, b: int = 9) do fmt.printf("a is %d; b is %d\n", a, b);
fmt.println("\nTesting default arguments:");
hello(1, 2);
hello(1);
hello();
}
named_arguments :: proc() {
Colour :: enum {
Red,
Orange,
Yellow,
Green,
Blue,
Octarine,
};
using Colour;
make_character :: proc(name, catch_phrase: string, favourite_colour, least_favourite_colour: Colour) {
fmt.println();
fmt.printf("My name is %v and I like %v. %v\n", name, favourite_colour, catch_phrase);
}
make_character("Frank", "¡Ay, caramba!", Blue, Green);
// As the procedures have more and more parameters, it is very easy
// to get many of the arguments in the wrong order especialy if the
// types are the same
make_character("¡Ay, caramba!", "Frank", Green, Blue);
// Named arguments help to disambiguate this problem
make_character(catch_phrase = "¡Ay, caramba!", name = "Frank",
least_favourite_colour = Green, favourite_colour = Blue);
// The named arguments can be specifed in any order.
make_character(favourite_colour = Octarine, catch_phrase = "U wot m8!",
least_favourite_colour = Green, name = "Dennis");
// NOTE: You cannot mix named arguments with normal values
/*
make_character("Dennis",
favourite_colour = Octarine, catch_phrase = "U wot m8!",
least_favourite_colour = Green);
*/
// Named arguments can also aid with default arguments
numerous_things :: proc(s: string, a := 1, b := 2, c := 3.14,
d := "The Best String!", e := false, f := 10.3/3.1, g := false) {
g_str := g ? "true" : "false";
fmt.printf("How many?! %s: %v\n", s, g_str);
}
numerous_things("First");
numerous_things(s = "Second", g = true);
// Default values can be placed anywhere, not just at the end like in other languages
weird :: proc(pre: string, mid: int = 0, post: string) {
fmt.println(pre, mid, post);
}
weird("How many things", 42, "huh?");
weird(pre = "Prefix", post = "Pat");
}
default_return_values :: proc() {
foo :: proc(x: int) -> (first: string = "Hellope", second := "world!") {
match x {
case 0: return;
case 1: return "Goodbye";
case 2: return "Goodbye", "cruel world..";
case 3: return second = "cruel world..", first = "Goodbye";
}
return second = "my old friend.";
}
fmt.printf("%s %s\n", foo(0));
fmt.printf("%s %s\n", foo(1));
fmt.printf("%s %s\n", foo(2));
fmt.printf("%s %s\n", foo(3));
fmt.printf("%s %s\n", foo(4));
fmt.println();
// A more "real" example
Error :: enum {
None,
WhyTheNumberThree,
TenIsTooBig,
};
Entity :: struct {
name: string;
id: u32;
}
some_thing :: proc(input: int) -> (result: ^Entity = nil, err := Error.None) {
match {
case input == 3: return err = Error.WhyTheNumberThree;
case input >= 10: return err = Error.TenIsTooBig;
}
e := new(Entity);
e.id = u32(input);
return result = e;
}
}
call_location :: proc() {
amazing :: proc(n: int, using loc := #caller_location) {
fmt.printf("%s(%d:%d) just asked to do something amazing.\n",
fully_pathed_filename, line, column);
fmt.printf("Normal -> %d\n", n);
fmt.printf("Amazing -> %d\n", n+1);
fmt.println();
}
loc := #location(main);
fmt.println("`main` is located at", loc);
fmt.println("This line is located at", #location());
fmt.println();
amazing(3);
amazing(4, #location(call_location));
// See _preload.odin for the implementations of `assert` and `panic`
}
explicit_parametric_polymorphic_procedures :: proc() {
// This is how `new` is actually implemented, see _preload.odin
alloc_type :: proc(T: type) -> ^T do return cast(^T)alloc(size_of(T), align_of(T));
int_ptr := alloc_type(int);
defer free(int_ptr);
int_ptr^ = 137;
fmt.println(int_ptr, int_ptr^);
// Named arguments work too!
another_ptr := alloc_type(T = f32);
defer free(another_ptr);
add :: proc(T: type, args: ..T) -> T {
res: T;
for arg in args do res += arg;
return res;
}
fmt.println("add =", add(int, 1, 2, 3, 4, 5, 6));
swap :: proc(T: type, a, b: ^T) {
tmp := a^;
a^ = b^;
b^ = tmp;
}
a, b: int = 3, 4;
fmt.println("Pre-swap:", a, b);
swap(int, &a, &b);
fmt.println("Post-swap:", a, b);
a, b = b, a; // Or use this syntax for this silly example case
Vector2 :: struct {x, y: f32;};
{
// A more complicated example using subtyping
// Something like this could be used in a game
Entity :: struct {
using position: Vector2;
flags: u64;
id: u64;
derived: any;
}
Rock :: struct {
using entity: Entity;
heavy: bool;
}
Door :: struct {
using entity: Entity;
open: bool;
}
Monster :: struct {
using entity: Entity;
is_robot: bool;
is_zombie: bool;
}
new_entity :: proc(T: type, x, y: f32) -> ^T {
result := new(T);
result.derived = result^;
result.x = x;
result.y = y;
return result;
}
entities: [dynamic]^Entity;
rock := new_entity(Rock, 3, 5);
// Named arguments work too!
door := new_entity(T = Door, x = 3, y = 6);
// And named arguments can be any order
monster := new_entity(
y = 1,
x = 2,
T = Monster,
);
append(&entities, rock, door, monster);
fmt.println("Subtyping");
for entity in entities {
match e in entity.derived {
case Rock: fmt.println("Rock", e.x, e.y);
case Door: fmt.println("Door", e.x, e.y);
case Monster: fmt.println("Monster", e.x, e.y);
}
}
}
{
Entity :: struct {
using position: Vector2;
flags: u64;
id: u64;
variant: union { Rock, Door, Monster };
}
Rock :: struct {
using entity: ^Entity;
heavy: bool;
}
Door :: struct {
using entity: ^Entity;
open: bool;
}
Monster :: struct {
using entity: ^Entity;
is_robot: bool;
is_zombie: bool;
}
new_entity :: proc(T: type, x, y: f32) -> ^T {
result := new(Entity);
result.variant = T{entity = result};
result.x = x;
result.y = y;
return cast(^T)&result.variant;
}
entities: [dynamic]^Entity;
rock := new_entity(Rock, 3, 5);
// Named arguments work too!
door := new_entity(T = Door, x = 3, y = 6);
// And named arguments can be any order
monster := new_entity(
y = 1,
x = 2,
T = Monster,
);
append(&entities, rock, door, monster);
fmt.println("Union");
for entity in entities {
match e in entity.variant {
case Rock: fmt.println("Rock", e.x, e.y);
case Door: fmt.println("Door", e.x, e.y);
case Monster: fmt.println("Monster", e.x, e.y);
}
}
}
}
implicit_polymorphic_assignment :: proc() {
yep :: proc(p: proc(x: int)) {
p(123);
}
frank :: proc(x: $T) do fmt.println("frank ->", x);
tim :: proc(x, y: $T) do fmt.println("tim ->", x, y);
yep(frank);
// yep(tim);
}
main :: proc() {
/*
foo :: proc(x: i64, y: f32) do fmt.println("#1", x, y);
foo :: proc(x: type, y: f32) do fmt.println("#2", type_info(x), y);
foo :: proc(x: type) do fmt.println("#3", type_info(x));
f :: foo;
f(y = 3785.1546, x = 123);
f(x = int, y = 897.513);
f(x = f32);
general_stuff();
foreign_blocks();
default_arguments();
named_arguments();
default_return_values();
call_location();
explicit_parametric_polymorphic_procedures();
implicit_polymorphic_assignment();
// Command line argument(s)!
// -opt=0,1,2,3
*/
/*
program := "+ + * - /";
accumulator := 0;
for token in program {
match token {
case '+': accumulator += 1;
case '-': accumulator -= 1;
case '*': accumulator *= 2;
case '/': accumulator /= 2;
case: // Ignore everything else
}
}
fmt.printf("The program \"%s\" calculates the value %d\n",
program, accumulator);
*/
}

View File

@@ -4447,6 +4447,14 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) {
ac->foreign_import_priority_index = exact_value_to_i64(ev);
}
return true;
} else if (name == "extra_linker_flags") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind != ExactValue_String) {
error(elem, "Expected a string value for '%.*s'", LIT(name));
} else {
ac->extra_linker_flags = ev.value_string;
}
return true;
}
return false;
}
@@ -4506,6 +4514,10 @@ gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
if (ac.foreign_import_priority_index != 0) {
e->LibraryName.priority_index = ac.foreign_import_priority_index;
}
String extra_linker_flags = string_trim_whitespace(ac.extra_linker_flags);
if (extra_linker_flags.len != 0) {
e->LibraryName.extra_linker_flags = extra_linker_flags;
}
if (has_asm_extension(fullpath)) {
if (build_context.metrics.arch != TargetArch_amd64 ||

View File

@@ -121,6 +121,7 @@ struct AttributeContext {
bool set_cold : 1;
u32 optimization_mode; // ProcedureOptimizationMode
i64 foreign_import_priority_index;
String extra_linker_flags;
String objc_class;
String objc_name;

View File

@@ -915,18 +915,20 @@ gb_internal void odin_doc_update_entities(OdinDocWriter *w) {
auto entities = array_make<Entity *>(heap_allocator(), 0, w->entity_cache.count);
defer (array_free(&entities));
for (auto const &entry : w->entity_cache) {
array_add(&entities, entry.key);
for (u32 i = 0; i < w->entity_cache.count; i++) {
Entity *e = w->entity_cache.entries[i].key;
array_add(&entities, e);
}
for (Entity *e : entities) {
GB_ASSERT(e != nullptr);
OdinDocTypeIndex type_index = odin_doc_type(w, e->type);
gb_unused(type_index);
}
}
for (auto const &entry : w->entity_cache) {
Entity *e = entry.key;
OdinDocEntityIndex entity_index = entry.value;
for (u32 i = 0; i < w->entity_cache.count; i++) {
Entity *e = w->entity_cache.entries[i].key;
OdinDocEntityIndex entity_index = w->entity_cache.entries[i].value;
OdinDocTypeIndex type_index = odin_doc_type(w, e->type);
OdinDocEntityIndex foreign_library = 0;

View File

@@ -259,6 +259,7 @@ struct Entity {
Slice<String> paths;
String name;
i64 priority_index;
String extra_linker_flags;
} LibraryName;
i32 Nil;
struct {

View File

@@ -563,7 +563,7 @@ namespace lbAbiAmd64SysV {
gb_internal void fixup(LLVMTypeRef t, Array<RegClass> *cls);
gb_internal lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention);
gb_internal Array<RegClass> classify(LLVMTypeRef t);
gb_internal LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const &reg_classes);
gb_internal LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const &reg_classes, LLVMTypeRef type);
gb_internal LB_ABI_INFO(abi_info) {
lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
@@ -662,7 +662,7 @@ namespace lbAbiAmd64SysV {
// the ABI rules for SysV
reg_type = type;
} else {
reg_type = llreg(c, cls);
reg_type = llreg(c, cls, type);
}
return lb_arg_type_direct(type, reg_type, nullptr, nullptr);
}
@@ -785,67 +785,92 @@ namespace lbAbiAmd64SysV {
}
gb_internal LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const &reg_classes) {
gb_internal LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const &reg_classes, LLVMTypeRef type) {
auto types = array_make<LLVMTypeRef>(heap_allocator(), 0, reg_classes.count);
for (isize i = 0; i < reg_classes.count; /**/) {
RegClass reg_class = reg_classes[i];
switch (reg_class) {
case RegClass_Int:
array_add(&types, LLVMIntTypeInContext(c, 64));
break;
case RegClass_SSEFv:
case RegClass_SSEDv:
case RegClass_SSEInt8:
case RegClass_SSEInt16:
case RegClass_SSEInt32:
case RegClass_SSEInt64:
{
unsigned elems_per_word = 0;
LLVMTypeRef elem_type = nullptr;
switch (reg_class) {
case RegClass_SSEFv:
elems_per_word = 2;
elem_type = LLVMFloatTypeInContext(c);
break;
case RegClass_SSEDv:
elems_per_word = 1;
elem_type = LLVMDoubleTypeInContext(c);
break;
case RegClass_SSEInt8:
elems_per_word = 64/8;
elem_type = LLVMIntTypeInContext(c, 8);
break;
case RegClass_SSEInt16:
elems_per_word = 64/16;
elem_type = LLVMIntTypeInContext(c, 16);
break;
case RegClass_SSEInt32:
elems_per_word = 64/32;
elem_type = LLVMIntTypeInContext(c, 32);
break;
case RegClass_SSEInt64:
elems_per_word = 64/64;
elem_type = LLVMIntTypeInContext(c, 64);
break;
}
unsigned vec_len = llvec_len(reg_classes, i+1);
LLVMTypeRef vec_type = LLVMVectorType(elem_type, vec_len * elems_per_word);
array_add(&types, vec_type);
i += vec_len;
continue;
}
bool all_ints = true;
for (RegClass reg_class : reg_classes) {
if (reg_class != RegClass_Int) {
all_ints = false;
break;
case RegClass_SSEFs:
array_add(&types, LLVMFloatTypeInContext(c));
break;
case RegClass_SSEDs:
array_add(&types, LLVMDoubleTypeInContext(c));
break;
default:
GB_PANIC("Unhandled RegClass");
}
i += 1;
}
if (all_ints) {
i64 sz = lb_sizeof(type);
for_array(i, reg_classes) {
GB_ASSERT(sz != 0);
// TODO(bill): is this even correct? BECAUSE LLVM DOES NOT DOCUMENT ANY OF THIS!!!
if (sz >= 8) {
array_add(&types, LLVMIntTypeInContext(c, 64));
sz -= 8;
} else {
array_add(&types, LLVMIntTypeInContext(c, cast(unsigned)(sz*8)));
sz = 0;
}
}
} else {
for (isize i = 0; i < reg_classes.count; /**/) {
RegClass reg_class = reg_classes[i];
switch (reg_class) {
case RegClass_Int:
// TODO(bill): is this even correct? BECAUSE LLVM DOES NOT DOCUMENT ANY OF THIS!!!
array_add(&types, LLVMIntTypeInContext(c, 64));
break;
case RegClass_SSEFv:
case RegClass_SSEDv:
case RegClass_SSEInt8:
case RegClass_SSEInt16:
case RegClass_SSEInt32:
case RegClass_SSEInt64:
{
unsigned elems_per_word = 0;
LLVMTypeRef elem_type = nullptr;
switch (reg_class) {
case RegClass_SSEFv:
elems_per_word = 2;
elem_type = LLVMFloatTypeInContext(c);
break;
case RegClass_SSEDv:
elems_per_word = 1;
elem_type = LLVMDoubleTypeInContext(c);
break;
case RegClass_SSEInt8:
elems_per_word = 64/8;
elem_type = LLVMIntTypeInContext(c, 8);
break;
case RegClass_SSEInt16:
elems_per_word = 64/16;
elem_type = LLVMIntTypeInContext(c, 16);
break;
case RegClass_SSEInt32:
elems_per_word = 64/32;
elem_type = LLVMIntTypeInContext(c, 32);
break;
case RegClass_SSEInt64:
elems_per_word = 64/64;
elem_type = LLVMIntTypeInContext(c, 64);
break;
}
unsigned vec_len = llvec_len(reg_classes, i+1);
LLVMTypeRef vec_type = LLVMVectorType(elem_type, vec_len * elems_per_word);
array_add(&types, vec_type);
i += vec_len;
continue;
}
break;
case RegClass_SSEFs:
array_add(&types, LLVMFloatTypeInContext(c));
break;
case RegClass_SSEDs:
array_add(&types, LLVMDoubleTypeInContext(c));
break;
default:
GB_PANIC("Unhandled RegClass");
}
i += 1;
}
}
if (types.count == 1) {

View File

@@ -278,6 +278,13 @@ gb_internal i32 linker_stage(lbGenerator *gen) {
}
}
for (Entity *e : gen->foreign_libraries) {
GB_ASSERT(e->kind == Entity_LibraryName);
if (e->LibraryName.extra_linker_flags.len != 0) {
lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(e->LibraryName.extra_linker_flags));
}
}
if (build_context.build_mode == BuildMode_DynamicLibrary) {
link_settings = gb_string_append_fmt(link_settings, " /DLL");
} else {
@@ -449,6 +456,12 @@ gb_internal i32 linker_stage(lbGenerator *gen) {
}
}
for (Entity *e : gen->foreign_libraries) {
GB_ASSERT(e->kind == Entity_LibraryName);
if (e->LibraryName.extra_linker_flags.len != 0) {
lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(e->LibraryName.extra_linker_flags));
}
}
gbString object_files = gb_string_make(heap_allocator(), "");
defer (gb_string_free(object_files));
@@ -581,13 +594,13 @@ gb_internal Array<String> setup_args(int argc, char const **argv) {
gb_internal void print_usage_line(i32 indent, char const *fmt, ...) {
while (indent --> 0) {
gb_printf_err("\t");
gb_printf("\t");
}
va_list va;
va_start(va, fmt);
gb_printf_err_va(fmt, va);
gb_printf_va(fmt, va);
va_end(va);
gb_printf_err("\n");
gb_printf("\n");
}
gb_internal void usage(String argv0) {

View File

@@ -1411,7 +1411,7 @@ gb_internal Token expect_operator(AstFile *f) {
LIT(p));
}
if (f->curr_token.kind == Token_Ellipsis) {
syntax_warning(f->curr_token, "'..' for ranges has now be deprecated, prefer '..='");
syntax_warning(f->curr_token, "'..' for ranges has now been deprecated, prefer '..='");
f->tokens[f->curr_token_index].flags |= TokenFlag_Replace;
}
@@ -1434,7 +1434,7 @@ gb_internal Token expect_closing_brace_of_field_list(AstFile *f) {
return token;
}
bool ok = true;
if (!f->allow_newline) {
if (f->allow_newline) {
ok = !skip_possible_newline(f);
}
if (ok && allow_token(f, Token_Semicolon)) {
@@ -3191,6 +3191,15 @@ gb_internal Ast *parse_foreign_block(AstFile *f, Token token) {
return decl;
}
gb_internal void print_comment_group(CommentGroup *group) {
if (group) {
for (Token const &token : group->list) {
gb_printf_err("%.*s\n", LIT(token.string));
}
gb_printf_err("\n");
}
}
gb_internal Ast *parse_value_decl(AstFile *f, Array<Ast *> names, CommentGroup *docs) {
bool is_mutable = true;
@@ -3232,6 +3241,8 @@ gb_internal Ast *parse_value_decl(AstFile *f, Array<Ast *> names, CommentGroup *
values.allocator = heap_allocator();
}
CommentGroup *end_comment = f->lead_comment;
if (f->expr_level >= 0) {
if (f->curr_token.kind == Token_CloseBrace &&
f->curr_token.pos.line == f->prev_token.pos.line) {
@@ -3252,7 +3263,7 @@ gb_internal Ast *parse_value_decl(AstFile *f, Array<Ast *> names, CommentGroup *
}
}
return ast_value_decl(f, names, type, values, is_mutable, docs, f->line_comment);
return ast_value_decl(f, names, type, values, is_mutable, docs, end_comment);
}
gb_internal Ast *parse_simple_stmt(AstFile *f, u32 flags) {
@@ -3682,9 +3693,11 @@ gb_internal bool allow_field_separator(AstFile *f) {
if (allow_token(f, Token_Comma)) {
return true;
}
if (ALLOW_NEWLINE && token.kind == Token_Semicolon && !token_is_newline(token)) {
String p = token_to_string(token);
syntax_error(token_end_of_line(f, f->prev_token), "Expected a comma, got a %.*s", LIT(p));
if (ALLOW_NEWLINE && token.kind == Token_Semicolon) {
if (!token_is_newline(token)) {
String p = token_to_string(token);
syntax_error(token_end_of_line(f, f->prev_token), "Expected a comma, got a %.*s", LIT(p));
}
advance_token(f);
return true;
}

View File

@@ -6,87 +6,82 @@ python3 download_assets.py
echo ---
echo Running core:image tests
echo ---
%PATH_TO_ODIN% run image %COMMON% -out:test_core_image.exe
%PATH_TO_ODIN% run image %COMMON% -out:test_core_image.exe || exit /b
echo ---
echo Running core:compress tests
echo ---
%PATH_TO_ODIN% run compress %COMMON% -out:test_core_compress.exe
%PATH_TO_ODIN% run compress %COMMON% -out:test_core_compress.exe || exit /b
echo ---
echo Running core:strings tests
echo ---
%PATH_TO_ODIN% run strings %COMMON% -out:test_core_strings.exe
%PATH_TO_ODIN% run strings %COMMON% -out:test_core_strings.exe || exit /b
echo ---
echo Running core:hash tests
echo ---
%PATH_TO_ODIN% run hash %COMMON% -o:size -out:test_core_hash.exe
%PATH_TO_ODIN% run hash %COMMON% -o:size -out:test_core_hash.exe || exit /b
echo ---
echo Running core:odin tests
echo ---
%PATH_TO_ODIN% run odin %COMMON% -o:size -out:test_core_odin.exe
%PATH_TO_ODIN% run odin %COMMON% -o:size -out:test_core_odin.exe || exit /b
echo ---
echo Running core:crypto hash tests
echo ---
%PATH_TO_ODIN% run crypto %COMMON% -out:test_crypto_hash.exe
%PATH_TO_ODIN% run crypto %COMMON% -out:test_crypto_hash.exe || exit /b
echo ---
echo Running core:encoding tests
echo ---
%PATH_TO_ODIN% run encoding/hxa %COMMON% %COLLECTION% -out:test_hxa.exe
%PATH_TO_ODIN% run encoding/json %COMMON% -out:test_json.exe
%PATH_TO_ODIN% run encoding/varint %COMMON% -out:test_varint.exe
%PATH_TO_ODIN% run encoding/xml %COMMON% -out:test_xml.exe
%PATH_TO_ODIN% run encoding/hxa %COMMON% %COLLECTION% -out:test_hxa.exe || exit /b
%PATH_TO_ODIN% run encoding/json %COMMON% -out:test_json.exe || exit /b
%PATH_TO_ODIN% run encoding/varint %COMMON% -out:test_varint.exe || exit /b
%PATH_TO_ODIN% run encoding/xml %COMMON% -out:test_xml.exe || exit /b
echo ---
echo Running core:math/noise tests
echo ---
%PATH_TO_ODIN% run math/noise %COMMON% -out:test_noise.exe
%PATH_TO_ODIN% run math/noise %COMMON% -out:test_noise.exe || exit /b
echo ---
echo Running core:math tests
echo ---
%PATH_TO_ODIN% run math %COMMON% %COLLECTION% -out:test_core_math.exe
%PATH_TO_ODIN% run math %COMMON% %COLLECTION% -out:test_core_math.exe || exit /b
echo ---
echo Running core:math/linalg/glsl tests
echo ---
%PATH_TO_ODIN% run math/linalg/glsl %COMMON% %COLLECTION% -out:test_linalg_glsl.exe
%PATH_TO_ODIN% run math/linalg/glsl %COMMON% %COLLECTION% -out:test_linalg_glsl.exe || exit /b
echo ---
echo Running core:path/filepath tests
echo ---
%PATH_TO_ODIN% run path/filepath %COMMON% %COLLECTION% -out:test_core_filepath.exe
%PATH_TO_ODIN% run path/filepath %COMMON% %COLLECTION% -out:test_core_filepath.exe || exit /b
echo ---
echo Running core:reflect tests
echo ---
%PATH_TO_ODIN% run reflect %COMMON% %COLLECTION% -out:test_core_reflect.exe
%PATH_TO_ODIN% run reflect %COMMON% %COLLECTION% -out:test_core_reflect.exe || exit /b
echo ---
echo Running core:text/i18n tests
echo ---
%PATH_TO_ODIN% run text\i18n %COMMON% -out:test_core_i18n.exe
%PATH_TO_ODIN% run text\i18n %COMMON% -out:test_core_i18n.exe || exit /b
echo ---
echo Running core:net
echo ---
%PATH_TO_ODIN% run net %COMMON% -out:test_core_net.exe
echo ---
echo Running core:text/lua tests
echo ---
%PATH_TO_ODIN% run text\lua %COMMON% -out:test_core_lua_strlib.exe
%PATH_TO_ODIN% run net %COMMON% -out:test_core_net.exe || exit /b
echo ---
echo Running core:slice tests
echo ---
%PATH_TO_ODIN% run slice %COMMON% -out:test_core_slice.exe
%PATH_TO_ODIN% run slice %COMMON% -out:test_core_slice.exe || exit /b
echo ---
echo Running core:container tests
echo ---
%PATH_TO_ODIN% run container %COMMON% %COLLECTION% -out:test_core_container.exe
%PATH_TO_ODIN% run container %COMMON% %COLLECTION% -out:test_core_container.exe || exit /b

View File

@@ -151,6 +151,13 @@ shoco_test :: proc(t: ^testing.T) {
}
for v in Shoco_Tests {
when ODIN_OS == .Windows {
v := v
// Compressed source files are not encoded with carriage returns but git replaces raw files lf with crlf on commit (on windows only)
// So replace crlf with lf on windows
v.raw, _ = bytes.replace_all(v.raw, { 0xD, 0xA }, { 0xA })
}
expected_raw := len(v.raw)
expected_compressed := len(v.compressed)

View File

@@ -4,6 +4,7 @@ import "core:strings"
import "core:testing"
import "core:fmt"
import "core:os"
import "core:runtime"
TEST_count := 0
TEST_fail := 0
@@ -33,6 +34,7 @@ main :: proc() {
test_index_any_small_string_found(&t)
test_index_any_larger_string_found(&t)
test_cut(&t)
test_case_conversion(&t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
@@ -89,4 +91,48 @@ test_cut :: proc(t: ^testing.T) {
test.input, test.offset, test.length, test.output, res)
expect(t, res == test.output, msg)
}
}
Case_Kind :: enum {
Lower_Space_Case,
Upper_Space_Case,
Lower_Snake_Case,
Upper_Snake_Case,
Lower_Kebab_Case,
Upper_Kebab_Case,
Camel_Case,
Pascal_Case,
Ada_Case,
}
test_cases := [Case_Kind]struct{s: string, p: proc(r: string, allocator: runtime.Allocator) -> string}{
.Lower_Space_Case = {"hellope world", to_lower_space_case},
.Upper_Space_Case = {"HELLOPE WORLD", to_upper_space_case},
.Lower_Snake_Case = {"hellope_world", strings.to_snake_case},
.Upper_Snake_Case = {"HELLOPE_WORLD", strings.to_upper_snake_case},
.Lower_Kebab_Case = {"hellope-world", strings.to_kebab_case},
.Upper_Kebab_Case = {"HELLOPE-WORLD", strings.to_upper_kebab_case},
.Camel_Case = {"hellopeWorld", strings.to_camel_case},
.Pascal_Case = {"HellopeWorld", strings.to_pascal_case},
.Ada_Case = {"Hellope_World", strings.to_ada_case},
}
to_lower_space_case :: proc(r: string, allocator: runtime.Allocator) -> string {
return strings.to_delimiter_case(r, ' ', false, allocator)
}
to_upper_space_case :: proc(r: string, allocator: runtime.Allocator) -> string {
return strings.to_delimiter_case(r, ' ', true, allocator)
}
@test
test_case_conversion :: proc(t: ^testing.T) {
for entry in test_cases {
for test_case, case_kind in test_cases {
result := entry.p(test_case.s, context.allocator)
defer delete(result)
msg := fmt.tprintf("ERROR: Input `{}` to converter {} does not match `{}`, got `{}`.\n", test_case.s, case_kind, entry.s, result)
expect(t, result == entry.s, msg)
}
}
}

View File

@@ -0,0 +1,13 @@
@echo off
set PATH_TO_ODIN==..\..\odin
echo ---
echo Building Documentation File
echo ---
%PATH_TO_ODIN% doc ..\..\examples\all -all-packages -doc-format || exit /b
echo ---
echo Running Documentation Tester
echo ---
%PATH_TO_ODIN% run documentation_tester.odin -file -vet -strict-style -- %PATH_TO_ODIN% || exit /b

View File

@@ -0,0 +1,421 @@
package documentation_tester
import "core:os"
import "core:io"
import "core:fmt"
import "core:strings"
import "core:odin/ast"
import "core:odin/parser"
import "core:c/libc"
import doc "core:odin/doc-format"
Example_Test :: struct {
entity_name: string,
package_name: string,
example_code: []string,
expected_output: []string,
}
g_header: ^doc.Header
g_bad_doc: bool
g_examples_to_verify: [dynamic]Example_Test
g_path_to_odin: string
array :: proc(a: $A/doc.Array($T)) -> []T {
return doc.from_array(g_header, a)
}
str :: proc(s: $A/doc.String) -> string {
return doc.from_string(g_header, s)
}
common_prefix :: proc(strs: []string) -> string {
if len(strs) == 0 {
return ""
}
n := max(int)
for str in strs {
n = min(n, len(str))
}
prefix := strs[0][:n]
for str in strs[1:] {
for len(prefix) != 0 && str[:len(prefix)] != prefix {
prefix = prefix[:len(prefix)-1]
}
if len(prefix) == 0 {
break
}
}
return prefix
}
errorf :: proc(format: string, args: ..any) -> ! {
fmt.eprintf("%s ", os.args[0])
fmt.eprintf(format, ..args)
fmt.eprintln()
os.exit(1)
}
main :: proc() {
if len(os.args) != 2 {
errorf("expected path to odin executable")
}
g_path_to_odin = os.args[1]
data, ok := os.read_entire_file("all.odin-doc")
if !ok {
errorf("unable to read file: all.odin-doc")
}
err: doc.Reader_Error
g_header, err = doc.read_from_bytes(data)
switch err {
case .None:
case .Header_Too_Small:
errorf("file is too small for the file format")
case .Invalid_Magic:
errorf("invalid magic for the file format")
case .Data_Too_Small:
errorf("data is too small for the file format")
case .Invalid_Version:
errorf("invalid file format version")
}
pkgs := array(g_header.pkgs)
entities := array(g_header.entities)
path_prefix: string
{
fullpaths: [dynamic]string
defer delete(fullpaths)
for pkg in pkgs[1:] {
append(&fullpaths, str(pkg.fullpath))
}
path_prefix = common_prefix(fullpaths[:])
}
for pkg in pkgs[1:] {
entries_array := array(pkg.entries)
fullpath := str(pkg.fullpath)
path := strings.trim_prefix(fullpath, path_prefix)
if ! strings.has_prefix(path, "core/") {
continue
}
trimmed_path := strings.trim_prefix(path, "core/")
if strings.has_prefix(trimmed_path, "sys") {
continue
}
if strings.contains(trimmed_path, "/_") {
continue
}
for entry in entries_array {
entity := entities[entry.entity]
find_and_add_examples(
docs = str(entity.docs),
package_name = str(pkg.name),
entity_name = str(entity.name),
)
}
}
write_test_suite(g_examples_to_verify[:])
if g_bad_doc {
errorf("We created bad documentation!")
}
if ! run_test_suite() {
errorf("Test suite failed!")
}
fmt.println("Examples verified")
}
// NOTE: this is a pretty close copy paste from the website pkg documentation on parsing the docs
find_and_add_examples :: proc(docs: string, package_name: string, entity_name: string) {
if docs == "" {
return
}
Block_Kind :: enum {
Other,
Example,
Output,
}
Block :: struct {
kind: Block_Kind,
lines: []string,
}
lines := strings.split_lines(docs)
curr_block_kind := Block_Kind.Other
start := 0
example_block: Block // when set the kind should be Example
output_block: Block // when set the kind should be Output
// rely on zii that the kinds have not been set
assert(example_block.kind != .Example)
assert(output_block.kind != .Output)
insert_block :: proc(block: Block, example: ^Block, output: ^Block, name: string) {
switch block.kind {
case .Other:
case .Example:
if example.kind == .Example {
fmt.eprintf("The documentation for %q has multiple examples which is not allowed\n", name)
g_bad_doc = true
}
example^ = block
case .Output: output^ = block
if example.kind == .Output {
fmt.eprintf("The documentation for %q has multiple output which is not allowed\n", name)
g_bad_doc = true
}
output^ = block
}
}
for line, i in lines {
text := strings.trim_space(line)
next_block_kind := curr_block_kind
switch curr_block_kind {
case .Other:
switch {
case strings.has_prefix(line, "Example:"): next_block_kind = .Example
case strings.has_prefix(line, "Output:"): next_block_kind = .Output
}
case .Example:
switch {
case strings.has_prefix(line, "Output:"): next_block_kind = .Output
case ! (text == "" || strings.has_prefix(line, "\t")): next_block_kind = .Other
}
case .Output:
switch {
case strings.has_prefix(line, "Example:"): next_block_kind = .Example
case ! (text == "" || strings.has_prefix(line, "\t")): next_block_kind = .Other
}
}
if i-start > 0 && (curr_block_kind != next_block_kind) {
insert_block(Block{curr_block_kind, lines[start:i]}, &example_block, &output_block, entity_name)
curr_block_kind, start = next_block_kind, i
}
}
if start < len(lines) {
insert_block(Block{curr_block_kind, lines[start:]}, &example_block, &output_block, entity_name)
}
if output_block.kind == .Output && example_block.kind != .Example {
fmt.eprintf("The documentation for %q has an output block but no example\n", entity_name)
g_bad_doc = true
}
// Write example and output block if they're both present
if example_block.kind == .Example && output_block.kind == .Output {
{
// Example block starts with
// `Example:` and a number of white spaces,
lines := &example_block.lines
for len(lines) > 0 && (strings.trim_space(lines[0]) == "" || strings.has_prefix(lines[0], "Example:")) {
lines^ = lines[1:]
}
}
{
// Output block starts with
// `Output:` and a number of white spaces,
lines := &output_block.lines
for len(lines) > 0 && (strings.trim_space(lines[0]) == "" || strings.has_prefix(lines[0], "Output:")) {
lines^ = lines[1:]
}
// Additionally we need to strip all empty lines at the end of output to not include those in the expected output
for len(lines) > 0 && (strings.trim_space(lines[len(lines) - 1]) == "") {
lines^ = lines[:len(lines) - 1]
}
}
// Remove first layer of tabs which are always present
for line in &example_block.lines {
line = strings.trim_prefix(line, "\t")
}
for line in &output_block.lines {
line = strings.trim_prefix(line, "\t")
}
append(&g_examples_to_verify, Example_Test {
entity_name = entity_name,
package_name = package_name,
example_code = example_block.lines,
expected_output = output_block.lines,
})
}
}
write_test_suite :: proc(example_tests: []Example_Test) {
TEST_SUITE_DIRECTORY :: "verify"
os.remove_directory(TEST_SUITE_DIRECTORY)
os.make_directory(TEST_SUITE_DIRECTORY)
example_build := strings.builder_make()
test_runner := strings.builder_make()
strings.write_string(&test_runner,
`//+private
package documentation_verification
import "core:os"
import "core:mem"
import "core:io"
import "core:fmt"
import "core:thread"
import "core:sync"
import "core:intrinsics"
@(private="file")
_read_pipe: os.Handle
@(private="file")
_write_pipe: os.Handle
@(private="file")
_pipe_reader_semaphore: sync.Sema
@(private="file")
_out_data: string
@(private="file")
_out_buffer: [mem.Megabyte]byte
@(private="file")
_bad_test_found: bool
@(private="file")
_spawn_pipe_reader :: proc() {
thread.create_and_start(proc(^thread.Thread) {
stream := os.stream_from_handle(_read_pipe)
reader := io.to_reader(stream)
sync.post(&_pipe_reader_semaphore) // notify thread is ready
for {
n_read := 0
read_to_null_byte := 0
finished_reading := false
for ! finished_reading {
just_read, err := io.read(reader, _out_buffer[n_read:], &n_read); if err != .None {
panic("We got an IO error!")
}
for b in _out_buffer[n_read - just_read: n_read] {
if b == 0 {
finished_reading = true
break
}
read_to_null_byte += 1
}
}
intrinsics.volatile_store(&_out_data, transmute(string)_out_buffer[:read_to_null_byte])
sync.post(&_pipe_reader_semaphore) // notify we read the null byte
}
})
sync.wait(&_pipe_reader_semaphore) // wait for thread to be ready
}
@(private="file")
_check :: proc(test_name: string, expected: string) {
null_byte: [1]byte
os.write(_write_pipe, null_byte[:])
os.flush(_write_pipe)
sync.wait(&_pipe_reader_semaphore)
output := intrinsics.volatile_load(&_out_data) // wait for thread to read null byte
if expected != output {
fmt.eprintf("Test %q got unexpected output:\n%q\n", test_name, output)
fmt.eprintf("Expected:\n%q\n", expected)
_bad_test_found = true
}
}
main :: proc() {
_read_pipe, _write_pipe, _ = os.pipe()
os.stdout = _write_pipe
_spawn_pipe_reader()
`)
for test in example_tests {
strings.builder_reset(&example_build)
strings.write_string(&example_build, "package documentation_verification\n\n")
for line in test.example_code {
strings.write_string(&example_build, line)
strings.write_byte(&example_build, '\n')
}
code_string := strings.to_string(example_build)
example_ast := ast.File { src = code_string }
odin_parser := parser.default_parser()
if ! parser.parse_file(&odin_parser, &example_ast) {
g_bad_doc = true
continue
}
if odin_parser.error_count > 0 {
fmt.eprintf("Errors on the following code generated for %q:\n%v\n", test.entity_name, code_string)
g_bad_doc = true
continue
}
enforced_name := fmt.tprintf("%v_example", test.entity_name)
index_of_proc_name: int
code_test_name: string
for d in example_ast.decls {
value_decl, is_value := d.derived.(^ast.Value_Decl); if ! is_value {
continue
}
if len(value_decl.values) != 1 {
continue
}
proc_lit, is_proc_lit := value_decl.values[0].derived_expr.(^ast.Proc_Lit); if ! is_proc_lit {
continue
}
if len(proc_lit.type.params.list) > 0 {
continue
}
this_procedure_name := code_string[value_decl.names[0].pos.offset:value_decl.names[0].end.offset]
if this_procedure_name != enforced_name {
continue
}
index_of_proc_name = value_decl.names[0].pos.offset
code_test_name = this_procedure_name
break
}
if code_test_name == "" {
fmt.eprintf("We could not any find procedure literals with no arguments with the identifier %q for the example for %q\n", enforced_name, test.entity_name)
g_bad_doc = true
continue
}
fmt.sbprintf(&test_runner, "\t%v_%v()\n", test.package_name, code_test_name)
fmt.sbprintf(&test_runner, "\t_check(%q, `", code_test_name)
for line in test.expected_output {
strings.write_string(&test_runner, line)
strings.write_string(&test_runner, "\n")
}
strings.write_string(&test_runner, "`)\n")
save_path := fmt.tprintf("verify/test_%v_%v.odin", test.package_name, code_test_name)
test_file_handle, err := os.open(save_path, os.O_WRONLY | os.O_CREATE); if err != 0 {
fmt.eprintf("We could not open the file to the path %q for writing\n", save_path)
g_bad_doc = true
continue
}
defer os.close(test_file_handle)
stream := os.stream_from_handle(test_file_handle)
writer, ok := io.to_writer(stream); if ! ok {
fmt.eprintf("We could not make the writer for the path %q\n", save_path)
g_bad_doc = true
continue
}
fmt.wprintf(writer, "%v%v_%v", code_string[:index_of_proc_name], test.package_name, code_string[index_of_proc_name:])
}
strings.write_string(&test_runner,
`
if _bad_test_found {
fmt.eprintln("One or more tests failed")
os.exit(1)
}
}`)
os.write_entire_file("verify/main.odin", transmute([]byte)strings.to_string(test_runner))
}
run_test_suite :: proc() -> bool {
return libc.system(fmt.caprintf("%v run verify", g_path_to_odin)) == 0
}

View File

@@ -1,4 +1,4 @@
@echo off
set PATH_TO_ODIN==..\..\odin
%PATH_TO_ODIN% run test_map.odin -file -vet -strict-style -o:minimal
%PATH_TO_ODIN% run test_map.odin -file -vet -strict-style -o:minimal || exit /b
rem -define:SEED=42

View File

@@ -5,19 +5,14 @@ pushd build
set COMMON=-collection:tests=..\..
set ERROR_DID_OCCUR=0
@echo on
..\..\..\odin test ..\test_issue_829.odin %COMMON% -file
..\..\..\odin test ..\test_issue_1592.odin %COMMON% -file
..\..\..\odin test ..\test_issue_2087.odin %COMMON% -file
..\..\..\odin build ..\test_issue_2113.odin %COMMON% -file -debug
..\..\..\odin test ..\test_issue_829.odin %COMMON% -file || exit /b
..\..\..\odin test ..\test_issue_1592.odin %COMMON% -file || exit /b
..\..\..\odin test ..\test_issue_2087.odin %COMMON% -file || exit /b
..\..\..\odin build ..\test_issue_2113.odin %COMMON% -file -debug || exit /b
@echo off
if %ERRORLEVEL% NEQ 0 set ERROR_DID_OCCUR=1
popd
rmdir /S /Q build
if %ERROR_DID_OCCUR% NEQ 0 EXIT /B 1

View File

@@ -5,9 +5,9 @@ set PATH_TO_ODIN==..\..\odin
echo ---
echo Running vendor:botan tests
echo ---
%PATH_TO_ODIN% run botan %COMMON% -out:vendor_botan.exe
%PATH_TO_ODIN% run botan %COMMON% -out:vendor_botan.exe || exit /b
echo ---
echo Running vendor:glfw tests
echo ---
%PATH_TO_ODIN% run glfw %COMMON% -out:vendor_glfw.exe
%PATH_TO_ODIN% run glfw %COMMON% -out:vendor_glfw.exe || exit /b

84
vendor/ENet/enet.odin vendored
View File

@@ -343,58 +343,58 @@ foreign ENet {
deinitialize :: proc() ---
linked_version :: proc() -> Version ---
time_get :: proc() -> u32 ---
time_set :: proc(u32) ---
time_set :: proc(newTimeBase: u32) ---
socket_create :: proc(SocketType) -> Socket ---
socket_bind :: proc(Socket, ^Address) -> i32 ---
socket_get_address :: proc(Socket, ^Address) -> i32 ---
socket_listen :: proc(Socket, i32) -> i32 ---
socket_accept :: proc(Socket, ^Address) -> Socket ---
socket_connect :: proc(Socket, ^Address) -> i32 ---
socket_send :: proc(Socket, ^Address, ^Buffer, uint) -> i32 ---
socket_receive :: proc(Socket, ^Address, ^Buffer, uint) -> i32 ---
socket_wait :: proc(Socket, ^u32, u32) -> i32 ---
socket_set_option :: proc(Socket, SocketOption, i32) -> i32 ---
socket_get_option :: proc(Socket, SocketOption, ^i32) -> i32 ---
socket_shutdown :: proc(Socket, SocketShutdown) -> i32 ---
socket_destroy :: proc(Socket) ---
socketset_select :: proc(Socket, ^SocketSet, ^SocketSet, u32) -> i32 ---
socket_bind :: proc(socket: Socket, address: ^Address) -> i32 ---
socket_get_address :: proc(socket: Socket, address: ^Address) -> i32 ---
socket_listen :: proc(socket: Socket, backlog: i32) -> i32 ---
socket_accept :: proc(socket: Socket, address: ^Address) -> Socket ---
socket_connect :: proc(socket: Socket, address: ^Address) -> i32 ---
socket_send :: proc(socket: Socket, address: ^Address, buffers: [^]Buffer, bufferCount: uint) -> i32 ---
socket_receive :: proc(socket: Socket, address: ^Address, buffers: [^]Buffer, bufferCount: uint) -> i32 ---
socket_wait :: proc(socket: Socket, condition: ^u32, timeout: u32) -> i32 ---
socket_set_option :: proc(socket: Socket, option: SocketOption, value: i32) -> i32 ---
socket_get_option :: proc(socket: Socket, option: SocketOption, value: ^i32) -> i32 ---
socket_shutdown :: proc(socket: Socket, how: SocketShutdown) -> i32 ---
socket_destroy :: proc(socket: Socket) ---
socketset_select :: proc(socket: Socket, readSet: ^SocketSet, writeSet: ^SocketSet, timeout: u32) -> i32 ---
address_set_host_ip :: proc(address: ^Address, hostName: cstring) -> i32 ---
address_set_host :: proc(address: ^Address, hostName: cstring) -> i32 ---
address_get_host_ip :: proc(address: ^Address, hostName: [^]u8, nameLength: uint) -> i32 ---
address_get_host :: proc(address: ^Address, hostName: [^]u8, nameLength: uint) -> i32 ---
packet_create :: proc(rawptr, uint, u32) -> ^Packet ---
packet_destroy :: proc(^Packet) ---
packet_resize :: proc(^Packet, uint) -> i32 ---
crc32 :: proc(^Buffer, uint) -> u32 ---
packet_create :: proc(data: rawptr, dataLength: uint, flags: PacketFlag) -> ^Packet ---
packet_destroy :: proc(packet: ^Packet) ---
packet_resize :: proc(packet: ^Packet, dataLength: uint) -> i32 ---
crc32 :: proc(buffers: [^]Buffer, bufferCount: uint) -> u32 ---
host_create :: proc(^Address, uint, uint, u32, u32) -> ^Host ---
host_destroy :: proc(^Host) ---
host_connect :: proc(^Host, ^Address, uint, u32) -> ^Peer ---
host_check_events :: proc(^Host, ^Event) -> i32 ---
host_service :: proc(^Host, ^Event, u32) -> i32 ---
host_flush :: proc(^Host) ---
host_broadcast :: proc(^Host, u8, ^Packet) ---
host_compress :: proc(^Host, ^Compressor) ---
host_compress_with_range_coder :: proc(^Host) -> i32 ---
host_channel_limit :: proc(^Host, uint) ---
host_bandwidth_limit :: proc(^Host, u32, u32) ---
host_create :: proc(address: ^Address, peerCount: uint, channelLimit: uint, incomingBandwidth: u32, outgoingBandwidth: u32) -> ^Host ---
host_destroy :: proc(host: ^Host) ---
host_connect :: proc(host: ^Host, address: ^Address, channelCount: uint, data: u32) -> ^Peer ---
host_check_events :: proc(host: ^Host, event: ^Event) -> i32 ---
host_service :: proc(host: ^Host, event: ^Event, timeout: u32) -> i32 ---
host_flush :: proc(host: ^Host) ---
host_broadcast :: proc(host: ^Host, channelID: u8, packet: ^Packet) ---
host_compress :: proc(host: ^Host, compressor: ^Compressor) ---
host_compress_with_range_coder :: proc(host: ^Host) -> i32 ---
host_channel_limit :: proc(host: ^Host, channelLimit: uint) ---
host_bandwidth_limit :: proc(host: ^Host, incomingBandwidth: u32, outgoingBandwidth: u32) ---
peer_send :: proc(^Peer, u8, ^Packet) -> i32 ---
peer_receive :: proc(^Peer, ^u8) -> ^Packet ---
peer_ping :: proc(^Peer) ---
peer_ping_interval :: proc(^Peer, u32) ---
peer_timeout :: proc(^Peer, u32, u32, u32) ---
peer_reset :: proc(^Peer) ---
peer_disconnect :: proc(^Peer, u32) ---
peer_disconnect_now :: proc(^Peer, u32) ---
peer_disconnect_later :: proc(^Peer, u32) ---
peer_throttle_configure :: proc(^Peer, u32, u32, u32) ---
peer_send :: proc(peer: ^Peer, channelID: u8, packet: ^Packet) -> i32 ---
peer_receive :: proc(peer: ^Peer, channelID: ^u8) -> ^Packet ---
peer_ping :: proc(peer: ^Peer) ---
peer_ping_interval :: proc(peer: ^Peer, pingInterval: u32) ---
peer_timeout :: proc(peer: ^Peer, timoutLimit: u32, timeoutMinimum: u32, timeoutMaximum: u32) ---
peer_reset :: proc(peer: ^Peer) ---
peer_disconnect :: proc(peer: ^Peer, data: u32) ---
peer_disconnect_now :: proc(peer: ^Peer, data: u32) ---
peer_disconnect_later :: proc(peer: ^Peer, data: u32) ---
peer_throttle_configure :: proc(peer: ^Peer, interval: u32, acceleration: u32, deceleration: u32) ---
range_coder_create :: proc() -> rawptr ---
range_coder_destroy :: proc(rawptr) ---
range_coder_compress :: proc(rawptr, [^]Buffer, uint, uint, [^]u8, uint) -> uint ---
range_coder_decompress :: proc(rawptr, [^]u8, uint, [^]u8, uint) -> uint ---
range_coder_destroy :: proc(ctx: rawptr) ---
range_coder_compress :: proc(ctx: rawptr, inBuffers: [^]Buffer, inBufferCount: uint, inLimit: uint, outData: [^]u8, outLimit: uint) -> uint ---
range_coder_decompress :: proc(ctx: rawptr, inData: [^]u8, inLimit: uint, outData: [^]u8, outLimit: uint) -> uint ---
}

View File

@@ -1,4 +1,4 @@
Copyright (c) 2013-2021 Ramon Santamaria (@raysan5)
Copyright (c) 2013-2023 Ramon Santamaria (@raysan5)
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.

View File

@@ -12,15 +12,17 @@ Ready to learn? Jump to [code examples!](https://www.raylib.com/examples.html)
<br>
[![GitHub contributors](https://img.shields.io/github/contributors/raysan5/raylib)](https://github.com/raysan5/raylib/graphs/contributors)
[![GitHub All Releases](https://img.shields.io/github/downloads/raysan5/raylib/total)](https://github.com/raysan5/raylib/releases)
[![GitHub commits since tagged version](https://img.shields.io/github/commits-since/raysan5/raylib/4.0.0)](https://github.com/raysan5/raylib/commits/master)
[![GitHub Releases Downloads](https://img.shields.io/github/downloads/raysan5/raylib/total)](https://github.com/raysan5/raylib/releases)
[![GitHub Stars](https://img.shields.io/github/stars/raysan5/raylib?style=flat&label=stars)](https://github.com/raysan5/raylib/stargazers)
[![GitHub commits since tagged version](https://img.shields.io/github/commits-since/raysan5/raylib/4.2.0)](https://github.com/raysan5/raylib/commits/master)
[![GitHub Sponsors](https://img.shields.io/github/sponsors/raysan5?label=sponsors)](https://github.com/sponsors/raysan5)
[![Packaging Status](https://repology.org/badge/tiny-repos/raylib.svg)](https://repology.org/project/raylib/versions)
[![License](https://img.shields.io/badge/license-zlib%2Flibpng-blue.svg)](LICENSE)
[![Chat on Discord](https://img.shields.io/discord/426912293134270465.svg?logo=discord)](https://discord.gg/raylib)
[![GitHub stars](https://img.shields.io/github/stars/raysan5/raylib?style=social)](https://github.com/raysan5/raylib/stargazers)
[![Twitter Follow](https://img.shields.io/twitter/follow/raysan5?style=social)](https://twitter.com/raysan5)
[![Subreddit subscribers](https://img.shields.io/reddit/subreddit-subscribers/raylib?style=social)](https://www.reddit.com/r/raylib/)
[![Discord Members](https://img.shields.io/discord/426912293134270465.svg?label=Discord&logo=discord)](https://discord.gg/raylib)
[![Subreddit Subscribers](https://img.shields.io/reddit/subreddit-subscribers/raylib?label=reddit%20r%2Fraylib&logo=reddit)](https://www.reddit.com/r/raylib/)
[![Youtube Subscribers](https://img.shields.io/youtube/channel/subscribers/UC8WIBkhYb5sBNqXO1mZ7WSQ?style=flat&label=Youtube&logo=youtube)](https://www.youtube.com/c/raylib)
[![Twitch Status](https://img.shields.io/twitch/status/raysan5?style=flat&label=Twitch&logo=twitch)](https://www.twitch.tv/raysan5)
[![Windows](https://github.com/raysan5/raylib/workflows/Windows/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AWindows)
[![Linux](https://github.com/raysan5/raylib/workflows/Linux/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3ALinux)
@@ -36,10 +38,10 @@ features
--------
- **NO external dependencies**, all required libraries are [bundled into raylib](https://github.com/raysan5/raylib/tree/master/src/external)
- Multiple platforms supported: **Windows, Linux, MacOS, RPI, Android, HTML5... and more!**
- Written in plain C code (C99) in PascalCase/camelCase notation
- Written in plain C code (C99) using PascalCase/camelCase notation
- Hardware accelerated with OpenGL (**1.1, 2.1, 3.3, 4.3 or ES 2.0**)
- **Unique OpenGL abstraction layer** (usable as standalone module): [rlgl](https://github.com/raysan5/raylib/blob/master/src/rlgl.h)
- Multiple **Fonts** formats supported (TTF, XNA fonts, AngelCode fonts)
- Multiple **Fonts** formats supported (TTF, Image fonts, AngelCode fonts)
- Multiple texture formats supported, including **compressed formats** (DXT, ETC, ASTC)
- **Full 3D support**, including 3D Shapes, Models, Billboards, Heightmaps and more!
- Flexible Materials system, supporting classic maps and **PBR maps**
@@ -49,7 +51,7 @@ features
- Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD)
- **VR stereo rendering** support with configurable HMD device parameters
- Huge examples collection with [+120 code examples](https://github.com/raysan5/raylib/tree/master/examples)!
- Bindings to [+50 programming languages](https://github.com/raysan5/raylib/blob/master/BINDINGS.md)!
- Bindings to [+60 programming languages](https://github.com/raysan5/raylib/blob/master/BINDINGS.md)!
- **Free and open source**.
basic example
@@ -61,25 +63,57 @@ package example
import rl "vendor:raylib"
main :: proc() {
rl.InitWindow(800, 450, "raylib [core] example - basic window")
rl.InitWindow(800, 450, "raylib [core] example - basic window")
for !rl.WindowShouldClose() {
rl.BeginDrawing()
rl.ClearBackground(rl.RAYWHITE)
rl.DrawText("Congrats! You created your first window!", 190, 200, 20, rl.LIGHTGRAY)
rl.EndDrawing()
}
for !rl.WindowShouldClose() {
rl.BeginDrawing()
rl.ClearBackground(rl.RAYWHITE)
rl.DrawText("Congrats! You created your first window!", 190, 200, 20, rl.LIGHTGRAY)
rl.EndDrawing()
}
rl.CloseWindow()
rl.CloseWindow()
}
```
build and installation
----------------------
raylib binary releases for Windows, Linux, macOS, Android and HTML5 are available at the [Github Releases page](https://github.com/raysan5/raylib/releases).
raylib is also available via multiple [package managers](https://github.com/raysan5/raylib/issues/613) on multiple OS distributions.
#### Installing and building raylib on multiple platforms
[raylib Wiki](https://github.com/raysan5/raylib/wiki#development-platforms) contains detailed instructions on building and usage on multiple platforms.
- [Working on Windows](https://github.com/raysan5/raylib/wiki/Working-on-Windows)
- [Working on macOS](https://github.com/raysan5/raylib/wiki/Working-on-macOS)
- [Working on GNU Linux](https://github.com/raysan5/raylib/wiki/Working-on-GNU-Linux)
- [Working on Chrome OS](https://github.com/raysan5/raylib/wiki/Working-on-Chrome-OS)
- [Working on FreeBSD](https://github.com/raysan5/raylib/wiki/Working-on-FreeBSD)
- [Working on Raspberry Pi](https://github.com/raysan5/raylib/wiki/Working-on-Raspberry-Pi)
- [Working for Android](https://github.com/raysan5/raylib/wiki/Working-for-Android)
- [Working for Web (HTML5)](https://github.com/raysan5/raylib/wiki/Working-for-Web-(HTML5))
- [Working anywhere with CMake](https://github.com/raysan5/raylib/wiki/Working-with-CMake)
*Note that the Wiki is open for edit, if you find some issues while building raylib for your target platform, feel free to edit the Wiki or open an issue related to it.*
#### Setup raylib with multiple IDEs
raylib has been developed on Windows platform using [Notepad++](https://notepad-plus-plus.org/) and [MinGW GCC](https://www.mingw-w64.org/) compiler but it can be used with other IDEs on multiple platforms.
[Projects directory](https://github.com/raysan5/raylib/tree/master/projects) contains several ready-to-use **project templates** to build raylib and code examples with multiple IDEs.
*Note that there are lots of IDEs supported, some of the provided templates could require some review, so please, if you find some issue with a template or you think they could be improved, feel free to send a PR or open a related issue.*
learning and docs
------------------
raylib is designed to be learned using [the examples](https://github.com/raysan5/raylib/tree/master/examples) as the main reference. There is no standard API documentation but there is a [**cheatsheet**](https://www.raylib.com/cheatsheet/cheatsheet.html) containing all the functions available on the library and a short description of each one of them, input parameters and result value names should be intuitive enough to understand how each function works.
raylib is designed to be learned using [the examples](https://github.com/raysan5/raylib/tree/master/examples) as the main reference. There is no standard API documentation but there is a [**cheatsheet**](https://www.raylib.com/cheatsheet/cheatsheet.html) containing all the functions available on the library a short description of each one of them, input parameters and result value names should be intuitive enough to understand how each function works.
Some additional documentation about raylib design can be found in raylib GitHub Wiki. Here the more relevant links:
Some additional documentation about raylib design can be found in raylib GitHub Wiki. Here are the relevant links:
- [raylib cheatsheet](https://www.raylib.com/cheatsheet/cheatsheet.html)
- [raylib architecture](https://github.com/raysan5/raylib/wiki/raylib-architecture)
@@ -106,4 +140,4 @@ license
raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, BSD-like license that allows static linking with closed source software. Check [LICENSE](LICENSE) for further details.
raylib uses internally some libraries for window/graphics/inputs management and also to support different fileformats loading, all those libraries are embedded with and are available in [src/external](https://github.com/raysan5/raylib/tree/master/src/external) directory. Check [raylib dependencies LICENSES](https://github.com/raysan5/raylib/wiki/raylib-dependencies) on raylib Wiki for details.
raylib uses internally some libraries for window/graphics/inputs management and also to support different file formats loading, all those libraries are embedded with and are available in [src/external](https://github.com/raysan5/raylib/tree/master/src/external) directory. Check [raylib dependencies LICENSES](https://github.com/raysan5/raylib/wiki/raylib-dependencies) on raylib Wiki for details.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
vendor/raylib/linux/libraylib.so.4.5.0 vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
vendor/raylib/linux/libraylib.so.450 vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
vendor/raylib/macos/libraylib.450.dylib vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,116 @@
/**********************************************************************************************
*
* rlgl v4.5 - A multi-OpenGL abstraction layer with an immediate-mode style API
*
* An abstraction layer for multiple OpenGL versions (1.1, 2.1, 3.3 Core, 4.3 Core, ES 2.0)
* that provides a pseudo-OpenGL 1.1 immediate-mode style API (rlVertex, rlTranslate, rlRotate...)
*
* When choosing an OpenGL backend different than OpenGL 1.1, some internal buffer are
* initialized on rlglInit() to accumulate vertex data.
*
* When an internal state change is required all the stored vertex data is renderer in batch,
* additionally, rlDrawRenderBatchActive() could be called to force flushing of the batch.
*
* Some additional resources are also loaded for convenience, here the complete list:
* - Default batch (RLGL.defaultBatch): RenderBatch system to accumulate vertex data
* - Default texture (RLGL.defaultTextureId): 1x1 white pixel R8G8B8A8
* - Default shader (RLGL.State.defaultShaderId, RLGL.State.defaultShaderLocs)
*
* Internal buffer (and additional resources) must be manually unloaded calling rlglClose().
*
*
* CONFIGURATION:
*
* #define GRAPHICS_API_OPENGL_11
* #define GRAPHICS_API_OPENGL_21
* #define GRAPHICS_API_OPENGL_33
* #define GRAPHICS_API_OPENGL_43
* #define GRAPHICS_API_OPENGL_ES2
* Use selected OpenGL graphics backend, should be supported by platform
* Those preprocessor defines are only used on rlgl module, if OpenGL version is
* required by any other module, use rlGetVersion() to check it
*
* #define RLGL_IMPLEMENTATION
* Generates the implementation of the library into the included file.
* If not defined, the library is in header only mode and can be included in other headers
* or source files without problems. But only ONE file should hold the implementation.
*
* #define RLGL_RENDER_TEXTURES_HINT
* Enable framebuffer objects (fbo) support (enabled by default)
* Some GPUs could not support them despite the OpenGL version
*
* #define RLGL_SHOW_GL_DETAILS_INFO
* Show OpenGL extensions and capabilities detailed logs on init
*
* #define RLGL_ENABLE_OPENGL_DEBUG_CONTEXT
* Enable debug context (only available on OpenGL 4.3)
*
* rlgl capabilities could be customized just defining some internal
* values before library inclusion (default values listed):
*
* #define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 8192 // Default internal render batch elements limits
* #define RL_DEFAULT_BATCH_BUFFERS 1 // Default number of batch buffers (multi-buffering)
* #define RL_DEFAULT_BATCH_DRAWCALLS 256 // Default number of batch draw calls (by state changes: mode, texture)
* #define RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS 4 // Maximum number of textures units that can be activated on batch drawing (SetShaderValueTexture())
*
* #define RL_MAX_MATRIX_STACK_SIZE 32 // Maximum size of internal Matrix stack
* #define RL_MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported
* #define RL_CULL_DISTANCE_NEAR 0.01 // Default projection matrix near cull distance
* #define RL_CULL_DISTANCE_FAR 1000.0 // Default projection matrix far cull distance
*
* When loading a shader, the following vertex attribute and uniform
* location names are tried to be set automatically:
*
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: 0
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: 1
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: 2
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: 3
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: 4
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION "matProjection" // projection matrix
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView))
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color)
* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0)
* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 "texture1" // texture1 (texture slot active 1)
* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2)
*
* DEPENDENCIES:
*
* - OpenGL libraries (depending on platform and OpenGL version selected)
* - GLAD OpenGL extensions loading library (only for OpenGL 3.3 Core, 4.3 Core)
*
*
* LICENSE: zlib/libpng
*
* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5)
*
* 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.
*
**********************************************************************************************/
package raylib
import "core:c"
when ODIN_OS == .Windows {
foreign import lib {
"raylib.lib",
"windows/raylib.lib",
"system:Winmm.lib",
"system:Gdi32.lib",
"system:User32.lib",
@@ -13,37 +119,51 @@ when ODIN_OS == .Windows {
} else when ODIN_OS == .Linux {
foreign import lib "linux/libraylib.a"
} else when ODIN_OS == .Darwin {
foreign import lib "macos/libraylib.a"
when ODIN_ARCH == .arm64 {
foreign import lib {
"macos-arm64/libraylib.a",
"system:Cocoa.framework",
"system:OpenGL.framework",
"system:IOKit.framework",
}
} else {
foreign import lib {
"macos/libraylib.a",
"system:Cocoa.framework",
"system:OpenGL.framework",
"system:IOKit.framework",
}
}
} else {
foreign import lib "system:raylib"
}
GRAPHICS_API_OPENGL_11 :: false
GRAPHICS_API_OPENGL_21 :: true
GRAPHICS_API_OPENGL_33 :: GRAPHICS_API_OPENGL_21
GRAPHICS_API_OPENGL_ES2 :: false
GRAPHICS_API_OPENGL_43 :: false
RL_GRAPHICS_API_OPENGL_11 :: false
RL_GRAPHICS_API_OPENGL_21 :: true
RL_GRAPHICS_API_OPENGL_33 :: RL_GRAPHICS_API_OPENGL_21 // default currently
RL_GRAPHICS_API_OPENGL_ES2 :: false
RL_GRAPHICS_API_OPENGL_43 :: false
when !GRAPHICS_API_OPENGL_ES2 {
when !RL_GRAPHICS_API_OPENGL_ES2 {
// This is the maximum amount of elements (quads) per batch
// NOTE: Be careful with text, every letter maps to a quad
DEFAULT_BATCH_BUFFER_ELEMENTS :: 8192
RL_DEFAULT_BATCH_BUFFER_ELEMENTS :: 8192
} else {
// We reduce memory sizes for embedded systems (RPI and HTML5)
// NOTE: On HTML5 (emscripten) this is allocated on heap,
// by default it's only 16MB!...just take care...
DEFAULT_BATCH_BUFFER_ELEMENTS :: 2048
RL_DEFAULT_BATCH_BUFFER_ELEMENTS :: 2048
}
DEFAULT_BATCH_BUFFERS :: 1 // Default number of batch buffers (multi-buffering)
DEFAULT_BATCH_DRAWCALLS :: 256 // Default number of batch draw calls (by state changes: mode, texture)
MAX_BATCH_ACTIVE_TEXTURES :: 4 // Maximum number of additional textures that can be activated on batch drawing (SetShaderValueTexture())
RL_DEFAULT_BATCH_BUFFERS :: 1 // Default number of batch buffers (multi-buffering)
RL_DEFAULT_BATCH_DRAWCALLS :: 256 // Default number of batch draw calls (by state changes: mode, texture)
RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS :: 4 // Maximum number of additional textures that can be activated on batch drawing (SetShaderValueTexture())
// Internal Matrix stack
MAX_MATRIX_STACK_SIZE :: 32 // Maximum size of Matrix stack
RL_MAX_MATRIX_STACK_SIZE :: 32 // Maximum size of Matrix stack
// Shader limits
MAX_SHADER_LOCATIONS :: 32 // Maximum number of shader locations supported
RL_MAX_SHADER_LOCATIONS :: 32 // Maximum number of shader locations supported
// Projection matrix culling
RL_CULL_DISTANCE_NEAR :: 0.01 // Default near cull distance
@@ -98,87 +218,129 @@ RL_FRAGMENT_SHADER :: 0x8B30 // GL_FRAGMENT_SHADER
RL_VERTEX_SHADER :: 0x8B31 // GL_VERTEX_SHADER
RL_COMPUTE_SHADER :: 0x91B9 // GL_COMPUTE_SHADER
// GL blending factors
RL_ZERO :: 0 // GL_ZERO
RL_ONE :: 1 // GL_ONE
RL_SRC_COLOR :: 0x0300 // GL_SRC_COLOR
RL_ONE_MINUS_SRC_COLOR :: 0x0301 // GL_ONE_MINUS_SRC_COLOR
RL_SRC_ALPHA :: 0x0302 // GL_SRC_ALPHA
RL_ONE_MINUS_SRC_ALPHA :: 0x0303 // GL_ONE_MINUS_SRC_ALPHA
RL_DST_ALPHA :: 0x0304 // GL_DST_ALPHA
RL_ONE_MINUS_DST_ALPHA :: 0x0305 // GL_ONE_MINUS_DST_ALPHA
RL_DST_COLOR :: 0x0306 // GL_DST_COLOR
RL_ONE_MINUS_DST_COLOR :: 0x0307 // GL_ONE_MINUS_DST_COLOR
RL_SRC_ALPHA_SATURATE :: 0x0308 // GL_SRC_ALPHA_SATURATE
RL_CONSTANT_COLOR :: 0x8001 // GL_CONSTANT_COLOR
RL_ONE_MINUS_CONSTANT_COLOR :: 0x8002 // GL_ONE_MINUS_CONSTANT_COLOR
RL_CONSTANT_ALPHA :: 0x8003 // GL_CONSTANT_ALPHA
RL_ONE_MINUS_CONSTANT_ALPHA :: 0x8004 // GL_ONE_MINUS_CONSTANT_ALPHA
// GL blending functions/equations
RL_FUNC_ADD :: 0x8006 // GL_FUNC_ADD
RL_MIN :: 0x8007 // GL_MIN
RL_MAX :: 0x8008 // GL_MAX
RL_FUNC_SUBTRACT :: 0x800A // GL_FUNC_SUBTRACT
RL_FUNC_REVERSE_SUBTRACT :: 0x800B // GL_FUNC_REVERSE_SUBTRACT
RL_BLEND_EQUATION :: 0x8009 // GL_BLEND_EQUATION
RL_BLEND_EQUATION_RGB :: 0x8009 // GL_BLEND_EQUATION_RGB // (Same as BLEND_EQUATION)
RL_BLEND_EQUATION_ALPHA :: 0x883D // GL_BLEND_EQUATION_ALPHA
RL_BLEND_DST_RGB :: 0x80C8 // GL_BLEND_DST_RGB
RL_BLEND_SRC_RGB :: 0x80C9 // GL_BLEND_SRC_RGB
RL_BLEND_DST_ALPHA :: 0x80CA // GL_BLEND_DST_ALPHA
RL_BLEND_SRC_ALPHA :: 0x80CB // GL_BLEND_SRC_ALPHA
RL_BLEND_COLOR :: 0x8005 // GL_BLEND_COLOR
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
GlVersion :: enum c.int { OPENGL_11 = 1, OPENGL_21, OPENGL_33, OPENGL_ES_20 }
FramebufferAttachType :: enum c. int {
COLOR_CHANNEL0 = 0,
COLOR_CHANNEL1,
COLOR_CHANNEL2,
COLOR_CHANNEL3,
COLOR_CHANNEL4,
COLOR_CHANNEL5,
COLOR_CHANNEL6,
COLOR_CHANNEL7,
DEPTH = 100,
STENCIL = 200,
}
FramebufferAttachTextureType :: enum c.int {
CUBEMAP_POSITIVE_X = 0,
CUBEMAP_NEGATIVE_X,
CUBEMAP_POSITIVE_Y,
CUBEMAP_NEGATIVE_Y,
CUBEMAP_POSITIVE_Z,
CUBEMAP_NEGATIVE_Z,
TEXTURE2D = 100,
RENDERBUFFER = 200,
}
VertexBufferIndexType :: c.ushort when GRAPHICS_API_OPENGL_ES2 else c.uint
VertexBufferIndexType :: c.ushort when RL_GRAPHICS_API_OPENGL_ES2 else c.uint
// Dynamic vertex buffers (position + texcoords + colors + indices arrays)
VertexBuffer :: struct {
elementsCount: c.int, // Number of elements in the buffer (QUADS)
elementCount: c.int, // Number of elements in the buffer (QUADS)
vCounter: c.int, // Vertex position counter to process (and draw) from full buffer
tcCounter: c.int, // Vertex texcoord counter to process (and draw) from full buffer
cCounter: c.int, // Vertex color counter to process (and draw) from full buffer
vertices: [^]f32, // Vertex position (XYZ - 3 components per vertex) (shader-location = 0)
texcoords: [^]f32, // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1)
colors: [^]u8, // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3)
indices: [^]VertexBufferIndexType, // Vertex indices (in case vertex data comes indexed) (6 indices per quad)
vaoId: u32, // OpenGL Vertex Array Object id
vboId: [4]u32, // OpenGL Vertex Buffer Objects id (4 types of vertex data)
}
vertices: [^]f32, // Vertex position (XYZ - 3 components per vertex) (shader-location = 0)
texcoords: [^]f32, // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1)
colors: [^]u8, // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3)
indices: [^]VertexBufferIndexType, // Vertex indices (in case vertex data comes indexed) (6 indices per quad)
vaoId: c.uint, // OpenGL Vertex Array Object id
vboId: [4]c.uint, // OpenGL Vertex Buffer Objects id (4 types of vertex data)
}
// Draw call type
// NOTE: Only texture changes register a new draw, other state-change-related elements are not
// used at this moment (vaoId, shaderId, matrices), raylib just forces a batch draw call if any
// of those state-change happens (this is done in core module)
DrawCall :: struct {
mode: c.int, // Drawing mode: LINES, TRIANGLES, QUADS
vertexCount: c.int, // Number of vertex of the draw
vertexAlignment: c.int, // Number of vertex required for index alignment (LINES, TRIANGLES)
//vaoId: u32, // Vertex array id to be used on the draw -> Using RLGL.currentBatch->vertexBuffer.vaoId
//shaderId: u32, // Shader id to be used on the draw -> Using RLGL.currentShader.id
textureId: u32, // Texture id to be used on the draw -> Use to create new draw call if changes
//projection: Matrix, // Projection matrix for this draw -> Using RLGL.projection by default
//modelview: Matrix, // Modelview matrix for this draw -> Using RLGL.modelview by default
}
mode: c.int, // Drawing mode: LINES, TRIANGLES, QUADS
vertexCount: c.int, // Number of vertex of the draw
vertexAlignment: c.int, // Number of vertex required for index alignment (LINES, TRIANGLES)
textureId: c.uint, // Texture id to be used on the draw -> Use to create new draw call if changes
}
// RenderBatch type
RenderBatch :: struct {
buffersCount: c.int, // Number of vertex buffers (multi-buffering support)
bufferCount: c.int, // Number of vertex buffers (multi-buffering support)
currentBuffer: c.int, // Current buffer tracking in case of multi-buffering
vertexBuffer: [^]VertexBuffer, // Dynamic buffer(s) for vertex data
draws: [^]DrawCall, // Draw calls array, depends on textureId
drawsCounter: c.int, // Draw calls counter
currentDepth: f32, // Current depth value for next draw
draws: [^]DrawCall, // Draw calls array, depends on textureId
drawCounter: c.int, // Draw calls counter
currentDepth: f32, // Current depth value for next draw
}
// OpenGL version
GlVersion :: enum c.int {
OPENGL_11 = 1, // OpenGL 1.1
OPENGL_21, // OpenGL 2.1 (GLSL 120)
OPENGL_33, // OpenGL 3.3 (GLSL 330)
OPENGL_43, // OpenGL 4.3 (using GLSL 330)
OPENGL_ES_20, // OpenGL ES 2.0 (GLSL 100)
}
// Shader attribute data types
ShaderAttributeDataType :: enum c.int {
FLOAT = 0,
VEC2,
VEC3,
VEC4,
FLOAT = 0, // Shader attribute type: float
VEC2, // Shader attribute type: vec2 (2 float)
VEC3, // Shader attribute type: vec3 (3 float)
VEC4, // Shader attribute type: vec4 (4 float)
}
// Framebuffer attachment type
// NOTE: By default up to 8 color channels defined, but it can be more
FramebufferAttachType :: enum c.int {
COLOR_CHANNEL0 = 0, // Framebuffer attachment type: color 0
COLOR_CHANNEL1, // Framebuffer attachment type: color 1
COLOR_CHANNEL2, // Framebuffer attachment type: color 2
COLOR_CHANNEL3, // Framebuffer attachment type: color 3
COLOR_CHANNEL4, // Framebuffer attachment type: color 4
COLOR_CHANNEL5, // Framebuffer attachment type: color 5
COLOR_CHANNEL6, // Framebuffer attachment type: color 6
COLOR_CHANNEL7, // Framebuffer attachment type: color 7
DEPTH = 100, // Framebuffer attachment type: depth
STENCIL = 200, // Framebuffer attachment type: stencil
}
// Framebuffer texture attachment type
FramebufferAttachTextureType :: enum c.int {
CUBEMAP_POSITIVE_X = 0, // Framebuffer texture attachment type: cubemap, +X side
CUBEMAP_NEGATIVE_X, // Framebuffer texture attachment type: cubemap, -X side
CUBEMAP_POSITIVE_Y, // Framebuffer texture attachment type: cubemap, +Y side
CUBEMAP_NEGATIVE_Y, // Framebuffer texture attachment type: cubemap, -Y side
CUBEMAP_POSITIVE_Z, // Framebuffer texture attachment type: cubemap, +Z side
CUBEMAP_NEGATIVE_Z, // Framebuffer texture attachment type: cubemap, -Z side
TEXTURE2D = 100, // Framebuffer texture attachment type: texture2d
RENDERBUFFER = 200, // Framebuffer texture attachment type: renderbuffer
}
CullMode :: enum c.int {
FRONT = 0,
BACK,
}
@(default_calling_convention="c")
@@ -219,38 +381,38 @@ foreign lib {
//------------------------------------------------------------------------------------
// Vertex buffers state
rlEnableVertexArray :: proc(vaoId: u32) -> bool --- // Enable vertex array (VAO, if supported)
rlDisableVertexArray :: proc() --- // Disable vertex array (VAO, if supported)
rlEnableVertexBuffer :: proc(id: u32) --- // Enable vertex buffer (VBO)
rlDisableVertexBuffer :: proc() --- // Disable vertex buffer (VBO)
rlEnableVertexBufferElement :: proc(id: u32) --- // Enable vertex buffer element (VBO element)
rlDisableVertexBufferElement :: proc() --- // Disable vertex buffer element (VBO element)
rlEnableVertexAttribute :: proc(index: u32) --- // Enable vertex attribute index
rlDisableVertexAttribute :: proc(index: u32) --- // Disable vertex attribute index
when GRAPHICS_API_OPENGL_11 {
rlEnableVertexArray :: proc(vaoId: c.uint) -> bool --- // Enable vertex array (VAO, if supported)
rlDisableVertexArray :: proc() --- // Disable vertex array (VAO, if supported)
rlEnableVertexBuffer :: proc(id: c.uint) --- // Enable vertex buffer (VBO)
rlDisableVertexBuffer :: proc() --- // Disable vertex buffer (VBO)
rlEnableVertexBufferElement :: proc(id: c.uint) --- // Enable vertex buffer element (VBO element)
rlDisableVertexBufferElement :: proc() --- // Disable vertex buffer element (VBO element)
rlEnableVertexAttribute :: proc(index: c.uint) --- // Enable vertex attribute index
rlDisableVertexAttribute :: proc(index: c.uint) --- // Disable vertex attribute index
when RL_GRAPHICS_API_OPENGL_11 {
rlEnableStatePointer :: proc(vertexAttribType: c.int, buffer: rawptr) ---
rlDisableStatePointer :: proc(vertexAttribType: c.int) ---
}
// Textures state
rlActiveTextureSlot :: proc(slot: c.int) --- // Select and active a texture slot
rlEnableTexture :: proc(id: u32) --- // Enable texture
rlDisableTexture :: proc() --- // Disable texture
rlEnableTextureCubemap :: proc(id: u32) --- // Enable texture cubemap
rlDisableTextureCubemap :: proc() --- // Disable texture cubemap
rlTextureParameters :: proc(id: u32, param: c.int, value: c.int) --- // Set texture parameters (filter, wrap)
rlActiveTextureSlot :: proc(slot: c.int) --- // Select and active a texture slot
rlEnableTexture :: proc(id: c.uint) --- // Enable texture
rlDisableTexture :: proc() --- // Disable texture
rlEnableTextureCubemap :: proc(id: c.uint) --- // Enable texture cubemap
rlDisableTextureCubemap :: proc() --- // Disable texture cubemap
rlTextureParameters :: proc(id: c.uint, param: c.int, value: c.int) --- // Set texture parameters (filter, wrap)
rlCubemapParameters :: proc(id: i32, param: c.int, value: c.int) --- // Set cubemap parameters (filter, wrap)
// Shader state
rlEnableShader :: proc(id: u32) --- // Enable shader program
rlEnableShader :: proc(id: c.uint) --- // Enable shader program
rlDisableShader :: proc() --- // Disable shader program
// Framebuffer state
rlEnableFramebuffer :: proc(id: u32) --- // Enable render texture (fbo)
rlEnableFramebuffer :: proc(id: c.uint) --- // Enable render texture (fbo)
rlDisableFramebuffer :: proc() --- // Disable render texture (fbo), return to default framebuffer
rlActiveDrawBuffers :: proc(count: c.int) --- // Activate multiple draw color buffers
// General render state
rlEnableColorBlend :: proc() --- // Enable color blending
rlDisableColorBlend :: proc() --- // Disable color blending
rlEnableDepthTest :: proc() --- // Enable depth test
rlDisableDepthTest :: proc() --- // Disable depth test
@@ -258,6 +420,7 @@ foreign lib {
rlDisableDepthMask :: proc() --- // Disable depth write
rlEnableBackfaceCulling :: proc() --- // Enable backface culling
rlDisableBackfaceCulling :: proc() --- // Disable backface culling
rlSetCullFace :: proc(mode: CullMode) --- // Set face culling mode
rlEnableScissorTest :: proc() --- // Enable scissor test
rlDisableScissorTest :: proc() --- // Disable scissor test
rlScissor :: proc(x, y, width, height: c.int) --- // Scissor test
@@ -271,102 +434,107 @@ foreign lib {
rlDisableStereoRender :: proc() --- // Disable stereo rendering
rlIsStereoRenderEnabled :: proc() -> bool --- // Check if stereo render is enabled
rlClearColor :: proc(r, g, b, a: u8) --- // Clear color buffer with color
rlClearScreenBuffers :: proc() --- // Clear used screen buffers (color and depth)
rlCheckErrors :: proc() --- // Check and log OpenGL error codes
rlSetBlendMode :: proc(mode: c.int) --- // Set blending mode
rlSetBlendFactors :: proc(glSrcFactor, glDstFactor, glEquation: c.int) --- // Set blending mode factor and equation (using OpenGL factors)
rlClearColor :: proc(r, g, b, a: u8) --- // Clear color buffer with color
rlClearScreenBuffers :: proc() --- // Clear used screen buffers (color and depth)
rlCheckErrors :: proc() --- // Check and log OpenGL error codes
rlSetBlendMode :: proc(mode: c.int) --- // Set blending mode
rlSetBlendFactors :: proc(glSrcFactor, glDstFactor, glEquation: c.int) --- // Set blending mode factor and equation (using OpenGL factors)
rlSetBlendFactorsSeparate :: proc(glSrcRGB, glDstRGB, glSrcAlpha, glDstAlpha, glEqRGB, glEqAlpha: c.int) --- // Set blending mode factors and equations separately (using OpenGL factors)
//------------------------------------------------------------------------------------
// Functions Declaration - rlgl functionality
//------------------------------------------------------------------------------------
// rlgl initialization functions
rlglInit :: proc(width, height: c.int) --- // Initialize rlgl (buffers, shaders, textures, states)
rlglClose :: proc() --- // De-inititialize rlgl (buffers, shaders, textures)
rlLoadExtensions :: proc(loader: rawptr) --- // Load OpenGL extensions (loader function pointer required)
rlGetVersion :: proc() -> GlVersion --- // Returns current OpenGL version
rlGetFramebufferWidth :: proc() -> c.int --- // Get default framebuffer width
rlGetFramebufferHeight :: proc() -> c.int --- // Get default framebuffer height
rlglInit :: proc(width, height: c.int) --- // Initialize rlgl (buffers, shaders, textures, states)
rlglClose :: proc() --- // De-initialize rlgl (buffers, shaders, textures)
rlLoadExtensions :: proc(loader: rawptr) --- // Load OpenGL extensions (loader function required)
rlGetVersion :: proc() -> GlVersion --- // Get current OpenGL version
rlSetFramebufferWidth :: proc(width: c.int) --- // Set current framebuffer width
rlGetFramebufferWidth :: proc() -> c.int --- // Get default framebuffer width
rlSetFramebufferHeight :: proc(height: c.int) --- // Set current framebuffer height
rlGetFramebufferHeight :: proc() -> c.int --- // Get default framebuffer height
rlGetTextureIdDefault :: proc() -> u32 --- // Get default texture id
rlGetShaderIdDefault :: proc() -> u32 --- // Get default shader id
rlGetShaderLocsDefault :: proc() -> [^]i32 --- // Get default shader locations
rlGetTextureIdDefault :: proc() -> c.uint --- // Get default texture id
rlGetShaderIdDefault :: proc() -> c.uint --- // Get default shader id
rlGetShaderLocsDefault :: proc() -> [^]c.int --- // Get default shader locations
// Render batch management
// NOTE: rlgl provides a default render batch to behave like OpenGL 1.1 immediate mode
// but this render batch API is exposed in case of custom batches are required
rlLoadRenderBatch :: proc(numBuffers, bufferElements: c.int) -> RenderBatch --- // Load a render batch system
rlUnloadRenderBatch :: proc(batch: RenderBatch) --- // Unload render batch system
rlDrawRenderBatch :: proc(batch: ^RenderBatch) --- // Draw render batch data (Update->Draw->Reset)
rlSetRenderBatchActive :: proc(batch: ^RenderBatch) --- // Set the active render batch for rlgl (NULL for default internal)
rlDrawRenderBatchActive :: proc() --- // Update and draw internal render batch
rlCheckRenderBatchLimit :: proc(vCount: c.int) -> bool --- // Check internal buffer overflow for a given number of vertex
rlSetTexture :: proc(id: u32) --- // Set current texture for render batch and check buffers limits
rlLoadRenderBatch :: proc(numBuffers, bufferElements: c.int) -> RenderBatch --- // Load a render batch system
rlUnloadRenderBatch :: proc(batch: RenderBatch) --- // Unload render batch system
rlDrawRenderBatch :: proc(batch: ^RenderBatch) --- // Draw render batch data (Update->Draw->Reset)
rlSetRenderBatchActive :: proc(batch: ^RenderBatch) --- // Set the active render batch for rlgl (NULL for default internal)
rlDrawRenderBatchActive :: proc() --- // Update and draw internal render batch
rlCheckRenderBatchLimit :: proc(vCount: c.int) -> c.int --- // Check internal buffer overflow for a given number of vertex
rlSetTexture :: proc(id: c.uint) --- // Set current texture for render batch and check buffers limits
//------------------------------------------------------------------------------------------------------------------------
// Vertex buffers management
rlLoadVertexArray :: proc() -> u32 --- // Load vertex array (vao) if supported
rlLoadVertexBuffer :: proc(buffer: rawptr, size: c.int, is_dynamic: bool) -> u32 --- // Load a vertex buffer attribute
rlLoadVertexBufferElement :: proc(buffer: rawptr, size: c.int, is_dynamic: bool) -> u32 --- // Load a new attributes element buffer
rlUpdateVertexBuffer :: proc(bufferId: c.int, data: rawptr, dataSize: c.int, offset: c.int) -> u32 --- // Update GPU buffer with new data
rlUnloadVertexArray :: proc(vaoId: u32) ---
rlUnloadVertexBuffer :: proc(vboId: u32) ---
rlSetVertexAttribute :: proc(index: u32, compSize: c.int, type: c.int, normalized: bool, stride: c.int, pointer: uintptr) ---
rlSetVertexAttributeDivisor :: proc(index: u32, divisor: c.int) ---
rlSetVertexAttributeDefault :: proc(locIndex: c.int, value: rawptr, attribType: c.int, count: c.int) --- // Set vertex attribute default value
rlLoadVertexArray :: proc() -> c.uint --- // Load vertex array (vao) if supported
rlLoadVertexBuffer :: proc(buffer: rawptr, size: c.int, is_dynamic: bool) -> c.uint --- // Load a vertex buffer attribute
rlLoadVertexBufferElement :: proc(buffer: rawptr, size: c.int, is_dynamic: bool) -> c.uint --- // Load a new attributes element buffer
rlUpdateVertexBuffer :: proc(bufferId: c.uint, data: rawptr, dataSize: c.int, offset: c.int) --- // Update GPU buffer with new data
rlUpdateVertexBufferElements :: proc(id: c.uint, data: rawptr, dataSize: c.int, offset: c.int) --- // Update vertex buffer elements with new data
rlUnloadVertexArray :: proc(vaoId: c.uint) ---
rlUnloadVertexBuffer :: proc(vboId: c.uint) ---
rlSetVertexAttribute :: proc(index: c.uint, compSize: c.int, type: c.int, normalized: bool, stride: c.int, pointer: rawptr) ---
rlSetVertexAttributeDivisor :: proc(index: c.uint, divisor: c.int) ---
rlSetVertexAttributeDefault :: proc(locIndex: c.int, value: rawptr, attribType: c.int, count: c.int) --- // Set vertex attribute default value
rlDrawVertexArray :: proc(offset: c.int, count: c.int) ---
rlDrawVertexArrayElements :: proc(offset: c.int, count: c.int, buffer: rawptr) ---
rlDrawVertexArrayInstanced :: proc(offset: c.int, count: c.int, instances: c.int) ---
rlDrawVertexArrayElementsInstanced :: proc(offset: c.int, count: c.int, buffer: rawptr, instances: c.int) ---
// Textures management
rlLoadTexture :: proc(data: rawptr, width, height: c.int, format: c.int, mipmapCount: c.int) -> u32 --- // Load texture in GPU
rlLoadTextureDepth :: proc(width, height: c.int, useRenderBuffer: bool) -> u32 --- // Load depth texture/renderbuffer (to be attached to fbo)
rlLoadTextureCubemap :: proc(data: rawptr, size: c.int, format: c.int) -> u32 --- // Load texture cubemap
rlUpdateTexture :: proc(id: u32, offsetX, offsetY, width, height: c.int, format: c.int, data: rawptr) --- // Update GPU texture with new data
rlGetGlTextureFormats :: proc(format: c.int, glInternalFormat: ^u32, glFormat: ^u32, glType: ^u32) --- // Get OpenGL internal formats
rlGetPixelFormatName :: proc(format: PixelFormat) -> cstring --- // Get name string for pixel format
rlUnloadTexture :: proc(id: u32) --- // Unload texture from GPU memory
rlGenerateMipmaps :: proc(texture: ^Texture2D) --- // Generate mipmap data for selected texture
rlReadTexturePixels :: proc(texture: Texture2D) -> rawptr --- // Read texture pixel data
rlReadScreenPixels :: proc(width, height: c.int) -> [^]u8 --- // Read screen pixel data (color buffer)
rlLoadTexture :: proc(data: rawptr, width, height: c.int, format: c.int, mipmapCount: c.int) -> c.uint --- // Load texture in GPU
rlLoadTextureDepth :: proc(width, height: c.int, useRenderBuffer: bool) -> c.uint --- // Load depth texture/renderbuffer (to be attached to fbo)
rlLoadTextureCubemap :: proc(data: rawptr, size: c.int, format: c.int) -> c.uint --- // Load texture cubemap
rlUpdateTexture :: proc(id: c.uint, offsetX, offsetY: c.int, width, height: c.int, format: c.int, data: rawptr) --- // Update GPU texture with new data
rlGetGlTextureFormats :: proc(format: c.int, glInternalFormat, glFormat, glType: ^c.uint) --- // Get OpenGL internal formats
rlGetPixelFormatName :: proc(format: c.uint) -> cstring --- // Get name string for pixel format
rlUnloadTexture :: proc(id: c.uint) --- // Unload texture from GPU memory
rlGenTextureMipmaps :: proc(id: c.uint, width, height: c.int, format: c.int, mipmaps: ^c.int) --- // Generate mipmap data for selected texture
rlReadTexturePixels :: proc(id: c.uint, width, height: c.int, format: c.int) -> rawptr --- // Read texture pixel data
rlReadScreenPixels :: proc(width, height: c.int) -> [^]byte --- // Read screen pixel data (color buffer)
// Framebuffer management (fbo)
rlLoadFramebuffer :: proc(width, height: c.int) -> u32 --- // Load an empty framebuffer
rlFramebufferAttach :: proc(fboId: u32, texId: u32, attachType: c.int, texType: c.int, mipLevel: c.int) --- // Attach texture/renderbuffer to a framebuffer
rlFramebufferComplete :: proc(id: u32) -> bool --- // Verify framebuffer is complete
rlUnloadFramebuffer :: proc(id: u32) --- // Delete framebuffer from GPU
rlLoadFramebuffer :: proc(width, height: c.int) -> c.uint --- // Load an empty framebuffer
rlFramebufferAttach :: proc(fboId, texId: c.uint, attachType: c.int, texType: c.int, mipLevel: c.int) --- // Attach texture/renderbuffer to a framebuffer
rlFramebufferComplete :: proc(id: c.uint) -> bool --- // Verify framebuffer is complete
rlUnloadFramebuffer :: proc(id: c.uint) --- // Delete framebuffer from GPU
// Shaders management
rlLoadShaderCode :: proc(vsCode, fsCode: cstring) -> u32 --- // Load shader from code strings
rlCompileShader :: proc(shaderCode: cstring, type: c.int) -> u32 --- // Compile custom shader and return shader id (type: GL_VERTEX_SHADER, GL_FRAGMENT_SHADER)
rlLoadShaderProgram :: proc(vShaderId, fShaderId: u32) -> u32 --- // Load custom shader program
rlUnloadShaderProgram :: proc(id: u32) --- // Unload shader program
rlGetLocationUniform :: proc(shaderId: u32, uniformName: cstring) -> c.int --- // Get shader location uniform
rlGetLocationAttrib :: proc(shaderId: u32, attribName: cstring) -> c.int --- // Get shader location attribute
rlLoadShaderCode :: proc(vsCode, fsCode: cstring) -> c.uint --- // Load shader from code strings
rlCompileShader :: proc(shaderCode: cstring, type: c.int) -> c.uint --- // Compile custom shader and return shader id (type: RL_VERTEX_SHADER, RL_FRAGMENT_SHADER, RL_COMPUTE_SHADER)
rlLoadShaderProgram :: proc(vShaderId, fShaderId: c.uint) -> c.uint --- // Load custom shader program
rlUnloadShaderProgram :: proc(id: c.uint) --- // Unload shader program
rlGetLocationUniform :: proc(shaderId: c.uint, uniformName: cstring) -> c.int --- // Get shader location uniform
rlGetLocationAttrib :: proc(shaderId: c.uint, attribName: cstring) -> c.int --- // Get shader location attribute
rlSetUniform :: proc(locIndex: c.int, value: rawptr, uniformType: c.int, count: c.int) --- // Set shader value uniform
rlSetUniformMatrix :: proc(locIndex: c.int, mat: Matrix) --- // Set shader value matrix
rlSetUniformSampler :: proc(locIndex: c.int, textureId: u32) --- // Set shader value sampler
rlSetShader :: proc(shader: Shader) --- // Set shader currently active
rlSetUniformSampler :: proc(locIndex: c.int, textureId: c.uint) --- // Set shader value sampler
rlSetShader :: proc(id: c.uint, locs: [^]c.int) --- // Set shader currently active (id and locations)
// Compute shader management
rlLoadComputeShaderProgram :: proc(shaderId: u32) -> u32 --- // Load compute shader program
rlComputeShaderDispatch :: proc(groupX, groupY, groupZ: u32) --- // Dispatch compute shader (equivalent to *draw* for graphics pilepine)
rlLoadComputeShaderProgram :: proc(shaderId: c.uint) -> c.uint --- // Load compute shader program
rlComputeShaderDispatch :: proc(groupX, groupY, groupZ: c.uint) --- // Dispatch compute shader (equivalent to *draw* for graphics pipeline)
// Shader buffer storage object management (ssbo)
rlLoadShaderBuffer :: proc(size: u64, data: rawptr, usageHint: c.int) -> u32 --- // Load shader storage buffer object (SSBO)
rlUnloadShaderBuffer :: proc(ssboId: u32) --- // Unload shader storage buffer object (SSBO)
rlUpdateShaderBufferElements :: proc(id: u32, data: rawptr, dataSize: u64, offset: u64) --- // Update SSBO buffer data
rlGetShaderBufferSize :: proc(id: u32) -> u64 --- // Get SSBO buffer size
rlReadShaderBufferElements :: proc(id: u32, dest: rawptr, count: u64, offset: u64) --- // Bind SSBO buffer
rlBindShaderBuffer :: proc(id: u32, index: u32) --- // Copy SSBO buffer data
rlLoadShaderBuffer :: proc(size: c.uint, data: rawptr, usageHint: c.int) -> c.uint --- // Load shader storage buffer object (SSBO)
rlUnloadShaderBuffer :: proc(ssboId: c.uint) --- // Unload shader storage buffer object (SSBO)
rlUpdateShaderBuffer :: proc(id: c.uint, data: rawptr, dataSize: c.uint, offset: c.uint) --- // Update SSBO buffer data
rlBindShaderBuffer :: proc(id: c.uint, index: c.uint) --- // Bind SSBO buffer
rlReadShaderBuffer :: proc(id: c.uint, dest: rawptr, count: c.uint, offset: c.uint) --- // Read SSBO buffer data (GPU->CPU)
rlCopyShaderBuffer :: proc(destId, srcId: c.uint, destOffset, srcOffset: c.uint, count: c.uint) --- // Copy SSBO data between buffers
rlGetShaderBufferSize :: proc(id: c.uint) -> c.uint --- // Get SSBO buffer size
// Buffer management
rlCopyBuffersElements :: proc(destId, srcId: u32, destOffset, srcOffset: u64, count: u64) --- // Copy SSBO buffer data
rlBindImageTexture :: proc(id: u32, index: u32, format: u32, readonly: b32) --- // Bind image texture
rlBindImageTexture :: proc(id: c.uint, index: c.uint, format: c.int, readonly: bool) --- // Bind image texture
// Matrix state management
rlGetMatrixModelview :: proc() -> Matrix --- // Get internal modelview matrix

BIN
vendor/raylib/windows/raylib.dll vendored Normal file

Binary file not shown.

BIN
vendor/raylib/windows/raylib.lib vendored Normal file

Binary file not shown.

BIN
vendor/raylib/windows/raylibdll.lib vendored Normal file

Binary file not shown.

36
vendor/wasm/js/dom_all_targets.odin vendored Normal file
View File

@@ -0,0 +1,36 @@
//+build !js
package wasm_js_interface
import "core:runtime"
get_element_value_string :: proc "contextless" (id: string, buf: []byte) -> string {
context = runtime.default_context()
panic("vendor:wasm/js not supported on non JS targets")
}
get_element_min_max :: proc "contextless" (id: string) -> (min, max: f64) {
context = runtime.default_context()
panic("vendor:wasm/js not supported on non JS targets")
}
Rect :: struct {
x, y, width, height: f64,
}
get_bounding_client_rect :: proc "contextless" (id: string) -> (rect: Rect) {
context = runtime.default_context()
panic("vendor:wasm/js not supported on non JS targets")
}
window_get_rect :: proc "contextless" () -> (rect: Rect) {
context = runtime.default_context()
panic("vendor:wasm/js not supported on non JS targets")
}
window_get_scroll :: proc "contextless" () -> (x, y: f64) {
context = runtime.default_context()
panic("vendor:wasm/js not supported on non JS targets")
}

288
vendor/wasm/js/events_all_targets.odin vendored Normal file
View File

@@ -0,0 +1,288 @@
//+build !js
package wasm_js_interface
Event_Kind :: enum u32 {
Invalid,
Load,
Unload,
Error,
Resize,
Visibility_Change,
Fullscreen_Change,
Fullscreen_Error,
Click,
Double_Click,
Mouse_Move,
Mouse_Over,
Mouse_Out,
Mouse_Up,
Mouse_Down,
Key_Up,
Key_Down,
Key_Press,
Scroll,
Wheel,
Focus,
Submit,
Blur,
Change,
Select,
Animation_Start,
Animation_End,
Animation_Iteration,
Animation_Cancel,
Copy,
Cut,
Paste,
// Drag,
// Drag_Start,
// Drag_End,
// Drag_Enter,
// Drag_Leave,
// Drag_Over,
// Drop,
Pointer_Cancel,
Pointer_Down,
Pointer_Enter,
Pointer_Leave,
Pointer_Move,
Pointer_Over,
Pointer_Up,
Got_Pointer_Capture,
Lost_Pointer_Capture,
Pointer_Lock_Change,
Pointer_Lock_Error,
Selection_Change,
Selection_Start,
Touch_Cancel,
Touch_End,
Touch_Move,
Touch_Start,
Transition_Start,
Transition_End,
Transition_Run,
Transition_Cancel,
Context_Menu,
Custom,
}
event_kind_string := [Event_Kind]string{
.Invalid = "",
.Load = "load",
.Unload = "unload",
.Error = "error",
.Resize = "resize",
.Visibility_Change = "visibilitychange",
.Fullscreen_Change = "fullscreenchange",
.Fullscreen_Error = "fullscreenerror",
.Click = "click",
.Double_Click = "dblclick",
.Mouse_Move = "mousemove",
.Mouse_Over = "mouseover",
.Mouse_Out = "mouseout",
.Mouse_Up = "mouseup",
.Mouse_Down = "mousedown",
.Key_Up = "keyup",
.Key_Down = "keydown",
.Key_Press = "keypress",
.Scroll = "scroll",
.Wheel = "wheel",
.Focus = "focus",
.Submit = "submit",
.Blur = "blur",
.Change = "change",
.Select = "select",
.Animation_Start = "animationstart",
.Animation_End = "animationend",
.Animation_Iteration = "animationiteration",
.Animation_Cancel = "animationcancel",
.Copy = "copy",
.Cut = "cut",
.Paste = "paste",
// .Drag, = "drag",
// .Drag_Start, = "dragstart",
// .Drag_End, = "dragend",
// .Drag_Enter, = "dragenter",
// .Drag_Leave, = "dragleave",
// .Drag_Over, = "dragover",
// .Drop, = "drop",
.Pointer_Cancel = "pointercancel",
.Pointer_Down = "pointerdown",
.Pointer_Enter = "pointerenter",
.Pointer_Leave = "pointerleave",
.Pointer_Move = "pointermove",
.Pointer_Over = "pointerover",
.Pointer_Up = "pointerup",
.Got_Pointer_Capture = "gotpointercapture",
.Lost_Pointer_Capture = "lostpointercapture",
.Pointer_Lock_Change = "pointerlockchange",
.Pointer_Lock_Error = "pointerlockerror",
.Selection_Change = "selectionchange",
.Selection_Start = "selectionstart",
.Transition_Start = "transitionstart",
.Transition_End = "transitionend",
.Transition_Run = "transitionrun",
.Transition_Cancel = "transitioncancel",
.Touch_Cancel = "touchcancel",
.Touch_End = "touchend",
.Touch_Move = "touchmove",
.Touch_Start = "touchstart",
.Context_Menu = "contextmenu",
.Custom = "?custom?",
}
Delta_Mode :: enum u32 {
Pixel = 0,
Line = 1,
Page = 2,
}
Key_Location :: enum u8 {
Standard = 0,
Left = 1,
Right = 2,
Numpad = 3,
}
KEYBOARD_MAX_KEY_SIZE :: 16
KEYBOARD_MAX_CODE_SIZE :: 16
Event_Target_Kind :: enum u32 {
Element = 0,
Document = 1,
Window = 2,
}
Event_Phase :: enum u8 {
None = 0,
Capturing_Phase = 1,
At_Target = 2,
Bubbling_Phase = 3,
}
Event_Option :: enum u8 {
Bubbles = 0,
Cancelable = 1,
Composed = 2,
}
Event_Options :: distinct bit_set[Event_Option; u8]
Event :: struct {
kind: Event_Kind,
target_kind: Event_Target_Kind,
current_target_kind: Event_Target_Kind,
id: string,
timestamp: f64,
phase: Event_Phase,
options: Event_Options,
is_composing: bool,
is_trusted: bool,
using data: struct #raw_union #align 8 {
scroll: struct {
delta: [2]f64,
},
visibility_change: struct {
is_visible: bool,
},
wheel: struct {
delta: [3]f64,
delta_mode: Delta_Mode,
},
key: struct {
key: string,
code: string,
location: Key_Location,
ctrl: bool,
shift: bool,
alt: bool,
meta: bool,
repeat: bool,
_key_buf: [KEYBOARD_MAX_KEY_SIZE]byte,
_code_buf: [KEYBOARD_MAX_KEY_SIZE]byte,
},
mouse: struct {
screen: [2]i64,
client: [2]i64,
offset: [2]i64,
page: [2]i64,
movement: [2]i64,
ctrl: bool,
shift: bool,
alt: bool,
meta: bool,
button: i16,
buttons: bit_set[0..<16; u16],
},
},
user_data: rawptr,
callback: proc(e: Event),
}
add_event_listener :: proc(id: string, kind: Event_Kind, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool {
panic("vendor:wasm/js not supported on non JS targets")
}
remove_event_listener :: proc(id: string, kind: Event_Kind, user_data: rawptr, callback: proc(e: Event)) -> bool {
panic("vendor:wasm/js not supported on non JS targets")
}
add_window_event_listener :: proc(kind: Event_Kind, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool {
panic("vendor:wasm/js not supported on non JS targets")
}
remove_window_event_listener :: proc(kind: Event_Kind, user_data: rawptr, callback: proc(e: Event)) -> bool {
panic("vendor:wasm/js not supported on non JS targets")
}
remove_event_listener_from_event :: proc(e: Event) -> bool {
panic("vendor:wasm/js not supported on non JS targets")
}
add_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool {
panic("vendor:wasm/js not supported on non JS targets")
}
remove_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc(e: Event)) -> bool {
panic("vendor:wasm/js not supported on non JS targets")
}

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