Merge branch 'odin-lang:master' into json-better-enum-support

This commit is contained in:
blob1807
2024-03-01 14:24:51 +10:00
committed by GitHub
60 changed files with 4247 additions and 2320 deletions
+678
View File
@@ -0,0 +1,678 @@
/*
package avl implements an AVL tree.
The implementation is non-intrusive, and non-recursive.
*/
package container_avl
import "base:intrinsics"
import "base:runtime"
import "core:slice"
_ :: intrinsics
_ :: runtime
// Originally based on the CC0 implementation by Eric Biggers
// See: https://github.com/ebiggers/avl_tree/
// Direction specifies the traversal direction for a tree iterator.
Direction :: enum i8 {
// Backward is the in-order backwards direction.
Backward = -1,
// Forward is the in-order forwards direction.
Forward = 1,
}
// Ordering specifies order when inserting/finding values into the tree.
Ordering :: slice.Ordering
// Tree is an AVL tree.
Tree :: struct($Value: typeid) {
// user_data is a parameter that will be passed to the on_remove
// callback.
user_data: rawptr,
// on_remove is an optional callback that can be called immediately
// after a node is removed from the tree.
on_remove: proc(value: Value, user_data: rawptr),
_root: ^Node(Value),
_node_allocator: runtime.Allocator,
_cmp_fn: proc(a, b: Value) -> Ordering,
_size: int,
}
// Node is an AVL tree node.
//
// WARNING: It is unsafe to mutate value if the node is part of a tree
// if doing so will alter the Node's sort position relative to other
// elements in the tree.
Node :: struct($Value: typeid) {
value: Value,
_parent: ^Node(Value),
_left: ^Node(Value),
_right: ^Node(Value),
_balance: i8,
}
// Iterator is a tree iterator.
//
// WARNING: It is unsafe to modify the tree while iterating, except via
// the iterator_remove method.
Iterator :: struct($Value: typeid) {
_tree: ^Tree(Value),
_cur: ^Node(Value),
_next: ^Node(Value),
_direction: Direction,
_called_next: bool,
}
// init initializes a tree.
init :: proc {
init_ordered,
init_cmp,
}
// init_cmp initializes a tree.
init_cmp :: proc(
t: ^$T/Tree($Value),
cmp_fn: proc(a, b: Value) -> Ordering,
node_allocator := context.allocator,
) {
t._root = nil
t._node_allocator = node_allocator
t._cmp_fn = cmp_fn
t._size = 0
}
// init_ordered initializes a tree containing ordered items, with
// a comparison function that results in an ascending order sort.
init_ordered :: proc(
t: ^$T/Tree($Value),
node_allocator := context.allocator,
) where intrinsics.type_is_ordered_numeric(Value) {
init_cmp(t, slice.cmp_proc(Value), node_allocator)
}
// destroy de-initializes a tree.
destroy :: proc(t: ^$T/Tree($Value), call_on_remove: bool = true) {
iter := iterator(t, Direction.Forward)
for _ in iterator_next(&iter) {
iterator_remove(&iter, call_on_remove)
}
}
// len returns the number of elements in the tree.
len :: proc "contextless" (t: ^$T/Tree($Value)) -> int {
return t._size
}
// first returns the first node in the tree (in-order) or nil iff
// the tree is empty.
first :: proc "contextless" (t: ^$T/Tree($Value)) -> ^Node(Value) {
return tree_first_or_last_in_order(t, Direction.Backward)
}
// last returns the last element in the tree (in-order) or nil iff
// the tree is empty.
last :: proc "contextless" (t: ^$T/Tree($Value)) -> ^Node(Value) {
return tree_first_or_last_in_order(t, Direction.Forward)
}
// find finds the value in the tree, and returns the corresponding
// node or nil iff the value is not present.
find :: proc(t: ^$T/Tree($Value), value: Value) -> ^Node(Value) {
cur := t._root
descend_loop: for cur != nil {
switch t._cmp_fn(value, cur.value) {
case .Less:
cur = cur._left
case .Greater:
cur = cur._right
case .Equal:
break descend_loop
}
}
return cur
}
// find_or_insert attempts to insert the value into the tree, and returns
// the node, a boolean indicating if the value was inserted, and the
// node allocator error if relevant. If the value is already
// present, the existing node is returned un-altered.
find_or_insert :: proc(
t: ^$T/Tree($Value),
value: Value,
) -> (
n: ^Node(Value),
inserted: bool,
err: runtime.Allocator_Error,
) {
n_ptr := &t._root
for n_ptr^ != nil {
n = n_ptr^
switch t._cmp_fn(value, n.value) {
case .Less:
n_ptr = &n._left
case .Greater:
n_ptr = &n._right
case .Equal:
return
}
}
parent := n
n = new(Node(Value), t._node_allocator) or_return
n.value = value
n._parent = parent
n_ptr^ = n
tree_rebalance_after_insert(t, n)
t._size += 1
inserted = true
return
}
// remove removes a node or value from the tree, and returns true iff the
// removal was successful. While the node's value will be left intact,
// the node itself will be freed via the tree's node allocator.
remove :: proc {
remove_value,
remove_node,
}
// remove_value removes a value from the tree, and returns true iff the
// removal was successful. While the node's value will be left intact,
// the node itself will be freed via the tree's node allocator.
remove_value :: proc(t: ^$T/Tree($Value), value: Value, call_on_remove: bool = true) -> bool {
n := find(t, value)
if n == nil {
return false
}
return remove_node(t, n, call_on_remove)
}
// remove_node removes a node from the tree, and returns true iff the
// removal was successful. While the node's value will be left intact,
// the node itself will be freed via the tree's node allocator.
remove_node :: proc(t: ^$T/Tree($Value), node: ^Node(Value), call_on_remove: bool = true) -> bool {
if node._parent == node || (node._parent == nil && t._root != node) {
return false
}
defer {
if call_on_remove && t.on_remove != nil {
t.on_remove(node.value, t.user_data)
}
free(node, t._node_allocator)
}
parent: ^Node(Value)
left_deleted: bool
t._size -= 1
if node._left != nil && node._right != nil {
parent, left_deleted = tree_swap_with_successor(t, node)
} else {
child := node._left
if child == nil {
child = node._right
}
parent = node._parent
if parent != nil {
if node == parent._left {
parent._left = child
left_deleted = true
} else {
parent._right = child
left_deleted = false
}
if child != nil {
child._parent = parent
}
} else {
if child != nil {
child._parent = parent
}
t._root = child
node_reset(node)
return true
}
}
for {
if left_deleted {
parent = tree_handle_subtree_shrink(t, parent, +1, &left_deleted)
} else {
parent = tree_handle_subtree_shrink(t, parent, -1, &left_deleted)
}
if parent == nil {
break
}
}
node_reset(node)
return true
}
// iterator returns a tree iterator in the specified direction.
iterator :: proc "contextless" (t: ^$T/Tree($Value), direction: Direction) -> Iterator(Value) {
it: Iterator(Value)
it._tree = transmute(^Tree(Value))t
it._direction = direction
iterator_first(&it)
return it
}
// iterator_from_pos returns a tree iterator in the specified direction,
// spanning the range [pos, last] (inclusive).
iterator_from_pos :: proc "contextless" (
t: ^$T/Tree($Value),
pos: ^Node(Value),
direction: Direction,
) -> Iterator(Value) {
it: Iterator(Value)
it._tree = transmute(^Tree(Value))t
it._direction = direction
it._next = nil
it._called_next = false
if it._cur = pos; pos != nil {
it._next = node_next_or_prev_in_order(it._cur, it._direction)
}
return it
}
// iterator_get returns the node currently pointed to by the iterator,
// or nil iff the node has been removed, the tree is empty, or the end
// of the tree has been reached.
iterator_get :: proc "contextless" (it: ^$I/Iterator($Value)) -> ^Node(Value) {
return it._cur
}
// iterator_remove removes the node currently pointed to by the iterator,
// and returns true iff the removal was successful. Semantics are the
// same as the Tree remove.
iterator_remove :: proc(it: ^$I/Iterator($Value), call_on_remove: bool = true) -> bool {
if it._cur == nil {
return false
}
ok := remove_node(it._tree, it._cur, call_on_remove)
if ok {
it._cur = nil
}
return ok
}
// iterator_next advances the iterator and returns the (node, true) or
// or (nil, false) iff the end of the tree has been reached.
//
// Note: The first call to iterator_next will return the first node instead
// of advancing the iterator.
iterator_next :: proc "contextless" (it: ^$I/Iterator($Value)) -> (^Node(Value), bool) {
// This check is needed so that the first element gets returned from
// a brand-new iterator, and so that the somewhat contrived case where
// iterator_remove is called before the first call to iterator_next
// returns the correct value.
if !it._called_next {
it._called_next = true
// There can be the contrived case where iterator_remove is
// called before ever calling iterator_next, which needs to be
// handled as an actual call to next.
//
// If this happens it._cur will be nil, so only return the
// first value, if it._cur is valid.
if it._cur != nil {
return it._cur, true
}
}
if it._next == nil {
return nil, false
}
it._cur = it._next
it._next = node_next_or_prev_in_order(it._cur, it._direction)
return it._cur, true
}
@(private)
tree_first_or_last_in_order :: proc "contextless" (
t: ^$T/Tree($Value),
direction: Direction,
) -> ^Node(Value) {
first, sign := t._root, i8(direction)
if first != nil {
for {
tmp := node_get_child(first, +sign)
if tmp == nil {
break
}
first = tmp
}
}
return first
}
@(private)
tree_replace_child :: proc "contextless" (
t: ^$T/Tree($Value),
parent, old_child, new_child: ^Node(Value),
) {
if parent != nil {
if old_child == parent._left {
parent._left = new_child
} else {
parent._right = new_child
}
} else {
t._root = new_child
}
}
@(private)
tree_rotate :: proc "contextless" (t: ^$T/Tree($Value), a: ^Node(Value), sign: i8) {
b := node_get_child(a, -sign)
e := node_get_child(b, +sign)
p := a._parent
node_set_child(a, -sign, e)
a._parent = b
node_set_child(b, +sign, a)
b._parent = p
if e != nil {
e._parent = a
}
tree_replace_child(t, p, a, b)
}
@(private)
tree_double_rotate :: proc "contextless" (
t: ^$T/Tree($Value),
b, a: ^Node(Value),
sign: i8,
) -> ^Node(Value) {
e := node_get_child(b, +sign)
f := node_get_child(e, -sign)
g := node_get_child(e, +sign)
p := a._parent
e_bal := e._balance
node_set_child(a, -sign, g)
a_bal := -e_bal
if sign * e_bal >= 0 {
a_bal = 0
}
node_set_parent_balance(a, e, a_bal)
node_set_child(b, +sign, f)
b_bal := -e_bal
if sign * e_bal <= 0 {
b_bal = 0
}
node_set_parent_balance(b, e, b_bal)
node_set_child(e, +sign, a)
node_set_child(e, -sign, b)
node_set_parent_balance(e, p, 0)
if g != nil {
g._parent = a
}
if f != nil {
f._parent = b
}
tree_replace_child(t, p, a, e)
return e
}
@(private)
tree_handle_subtree_growth :: proc "contextless" (
t: ^$T/Tree($Value),
node, parent: ^Node(Value),
sign: i8,
) -> bool {
old_balance_factor := parent._balance
if old_balance_factor == 0 {
node_adjust_balance_factor(parent, sign)
return false
}
new_balance_factor := old_balance_factor + sign
if new_balance_factor == 0 {
node_adjust_balance_factor(parent, sign)
return true
}
if sign * node._balance > 0 {
tree_rotate(t, parent, -sign)
node_adjust_balance_factor(parent, -sign)
node_adjust_balance_factor(node, -sign)
} else {
tree_double_rotate(t, node, parent, -sign)
}
return true
}
@(private)
tree_rebalance_after_insert :: proc "contextless" (t: ^$T/Tree($Value), inserted: ^Node(Value)) {
node, parent := inserted, inserted._parent
switch {
case parent == nil:
return
case node == parent._left:
node_adjust_balance_factor(parent, -1)
case:
node_adjust_balance_factor(parent, +1)
}
if parent._balance == 0 {
return
}
for done := false; !done; {
node = parent
if parent = node._parent; parent == nil {
return
}
if node == parent._left {
done = tree_handle_subtree_growth(t, node, parent, -1)
} else {
done = tree_handle_subtree_growth(t, node, parent, +1)
}
}
}
@(private)
tree_swap_with_successor :: proc "contextless" (
t: ^$T/Tree($Value),
x: ^Node(Value),
) -> (
^Node(Value),
bool,
) {
ret: ^Node(Value)
left_deleted: bool
y := x._right
if y._left == nil {
ret = y
} else {
q: ^Node(Value)
for {
q = y
if y = y._left; y._left == nil {
break
}
}
if q._left = y._right; q._left != nil {
q._left._parent = q
}
y._right = x._right
x._right._parent = y
ret = q
left_deleted = true
}
y._left = x._left
x._left._parent = y
y._parent = x._parent
y._balance = x._balance
tree_replace_child(t, x._parent, x, y)
return ret, left_deleted
}
@(private)
tree_handle_subtree_shrink :: proc "contextless" (
t: ^$T/Tree($Value),
parent: ^Node(Value),
sign: i8,
left_deleted: ^bool,
) -> ^Node(Value) {
old_balance_factor := parent._balance
if old_balance_factor == 0 {
node_adjust_balance_factor(parent, sign)
return nil
}
node: ^Node(Value)
new_balance_factor := old_balance_factor + sign
if new_balance_factor == 0 {
node_adjust_balance_factor(parent, sign)
node = parent
} else {
node = node_get_child(parent, sign)
if sign * node._balance >= 0 {
tree_rotate(t, parent, -sign)
if node._balance == 0 {
node_adjust_balance_factor(node, -sign)
return nil
}
node_adjust_balance_factor(parent, -sign)
node_adjust_balance_factor(node, -sign)
} else {
node = tree_double_rotate(t, node, parent, -sign)
}
}
parent := parent
if parent = node._parent; parent != nil {
left_deleted^ = node == parent._left
}
return parent
}
@(private)
node_reset :: proc "contextless" (n: ^Node($Value)) {
// Mostly pointless as n will be deleted after this is called, but
// attempt to be able to catch cases of n not being in the tree.
n._parent = n
n._left = nil
n._right = nil
n._balance = 0
}
@(private)
node_set_parent_balance :: #force_inline proc "contextless" (
n, parent: ^Node($Value),
balance: i8,
) {
n._parent = parent
n._balance = balance
}
@(private)
node_get_child :: #force_inline proc "contextless" (n: ^Node($Value), sign: i8) -> ^Node(Value) {
if sign < 0 {
return n._left
}
return n._right
}
@(private)
node_next_or_prev_in_order :: proc "contextless" (
n: ^Node($Value),
direction: Direction,
) -> ^Node(Value) {
next, tmp: ^Node(Value)
sign := i8(direction)
if next = node_get_child(n, +sign); next != nil {
for {
tmp = node_get_child(next, -sign)
if tmp == nil {
break
}
next = tmp
}
} else {
tmp, next = n, n._parent
for next != nil && tmp == node_get_child(next, +sign) {
tmp, next = next, next._parent
}
}
return next
}
@(private)
node_set_child :: #force_inline proc "contextless" (
n: ^Node($Value),
sign: i8,
child: ^Node(Value),
) {
if sign < 0 {
n._left = child
} else {
n._right = child
}
}
@(private)
node_adjust_balance_factor :: #force_inline proc "contextless" (n: ^Node($Value), amount: i8) {
n._balance += amount
}
@(private)
iterator_first :: proc "contextless" (it: ^Iterator($Value)) {
// This is private because behavior when the user manually calls
// iterator_first followed by iterator_next is unintuitive, since
// the first call to iterator_next MUST return the first node
// instead of advancing so that `for node in iterator_next(&next)`
// works as expected.
switch it._direction {
case .Forward:
it._cur = tree_first_or_last_in_order(it._tree, .Backward)
case .Backward:
it._cur = tree_first_or_last_in_order(it._tree, .Forward)
}
it._next = nil
it._called_next = false
if it._cur != nil {
it._next = node_next_or_prev_in_order(it._cur, it._direction)
}
}
+3
View File
@@ -231,6 +231,9 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
case runtime.Type_Info_Matrix:
return .Unsupported_Type
case runtime.Type_Info_Bit_Field:
return .Unsupported_Type
case runtime.Type_Info_Array:
opt_write_start(w, opt, '[') or_return
for i in 0..<info.count {
+218 -51
View File
@@ -147,16 +147,30 @@ aprintln :: proc(args: ..any, sep := " ", allocator := context.allocator) -> str
// *Allocates Using Context's Allocator*
//
// Inputs:
// - fmt: A format string with placeholders for the provided arguments.
// - args: A variadic list of arguments to be formatted.
// - newline: Whether the string should end with a newline. (See `aprintfln`.)
//
// Returns: A formatted string. The returned string must be freed accordingly.
//
aprintf :: proc(fmt: string, args: ..any, allocator := context.allocator, newline := false) -> string {
str: strings.Builder
strings.builder_init(&str, allocator)
sbprintf(&str, fmt, ..args, newline=newline)
return strings.to_string(str)
}
// Creates a formatted string using a format string and arguments, followed by a newline.
//
// *Allocates Using Context's Allocator*
//
// Inputs:
// - fmt: A format string with placeholders for the provided arguments.
// - args: A variadic list of arguments to be formatted.
//
// Returns: A formatted string. The returned string must be freed accordingly.
//
aprintf :: proc(fmt: string, args: ..any, allocator := context.allocator) -> string {
str: strings.Builder
strings.builder_init(&str, allocator)
sbprintf(&str, fmt, ..args)
return strings.to_string(str)
aprintfln :: proc(fmt: string, args: ..any, allocator := context.allocator) -> string {
return aprintf(fmt, ..args, allocator=allocator, newline=true)
}
// Creates a formatted string
//
@@ -195,16 +209,30 @@ tprintln :: proc(args: ..any, sep := " ") -> string {
// *Allocates Using Context's Temporary Allocator*
//
// Inputs:
// - fmt: A format string with placeholders for the provided arguments.
// - args: A variadic list of arguments to be formatted.
// - newline: Whether the string should end with a newline. (See `tprintfln`.)
//
// Returns: A formatted string.
//
tprintf :: proc(fmt: string, args: ..any, newline := false) -> string {
str: strings.Builder
strings.builder_init(&str, context.temp_allocator)
sbprintf(&str, fmt, ..args, newline=newline)
return strings.to_string(str)
}
// Creates a formatted string using a format string and arguments, followed by a newline.
//
// *Allocates Using Context's Temporary Allocator*
//
// Inputs:
// - fmt: A format string with placeholders for the provided arguments.
// - args: A variadic list of arguments to be formatted.
//
// Returns: A formatted string.
//
tprintf :: proc(fmt: string, args: ..any) -> string {
str: strings.Builder
strings.builder_init(&str, context.temp_allocator)
sbprintf(&str, fmt, ..args)
return strings.to_string(str)
tprintfln :: proc(fmt: string, args: ..any) -> string {
return tprintf(fmt, ..args, newline=true)
}
// Creates a formatted string using a supplied buffer as the backing array. Writes into the buffer.
//
@@ -238,12 +266,25 @@ bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string {
// - buf: The backing buffer
// - fmt: A format string with placeholders for the provided arguments
// - args: A variadic list of arguments to be formatted
// - newline: Whether the string should end with a newline. (See `bprintfln`.)
//
// Returns: A formatted string
//
bprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string {
bprintf :: proc(buf: []byte, fmt: string, args: ..any, newline := false) -> string {
sb := strings.builder_from_bytes(buf)
return sbprintf(&sb, fmt, ..args)
return sbprintf(&sb, fmt, ..args, newline=newline)
}
// Creates a formatted string using a supplied buffer as the backing array, followed by a newline. Writes into the buffer.
//
// Inputs:
// - buf: The backing buffer
// - fmt: A format string with placeholders for the provided arguments
// - args: A variadic list of arguments to be formatted
//
// Returns: A formatted string
//
bprintfln :: proc(buf: []byte, fmt: string, args: ..any) -> string {
return bprintf(buf, fmt, ..args, newline=true)
}
// Runtime assertion with a formatted message
//
@@ -294,17 +335,31 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
// Inputs:
// - format: A format string with placeholders for the provided arguments
// - args: A variadic list of arguments to be formatted
// - newline: Whether the string should end with a newline. (See `caprintfln`.)
//
// Returns: A formatted C string
//
caprintf :: proc(format: string, args: ..any) -> cstring {
caprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
str: strings.Builder
strings.builder_init(&str)
sbprintf(&str, format, ..args)
sbprintf(&str, format, ..args, newline=newline)
strings.write_byte(&str, 0)
s := strings.to_string(str)
return cstring(raw_data(s))
}
// Creates a formatted C string, followed by a newline.
//
// *Allocates Using Context's Allocator*
//
// Inputs:
// - format: A format string with placeholders for the provided arguments
// - args: A variadic list of arguments to be formatted
//
// Returns: A formatted C string
//
caprintfln :: proc(format: string, args: ..any) -> cstring {
return caprintf(format, ..args, newline=true)
}
// Creates a formatted C string
//
// *Allocates Using Context's Temporary Allocator*
@@ -312,16 +367,30 @@ caprintf :: proc(format: string, args: ..any) -> cstring {
// Inputs:
// - format: A format string with placeholders for the provided arguments
// - args: A variadic list of arguments to be formatted
// - newline: Whether the string should end with a newline. (See `ctprintfln`.)
//
// Returns: A formatted C string
//
ctprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
str: strings.Builder
strings.builder_init(&str, context.temp_allocator)
sbprintf(&str, format, ..args, newline=newline)
strings.write_byte(&str, 0)
s := strings.to_string(str)
return cstring(raw_data(s))
}
// Creates a formatted C string, followed by a newline.
//
// *Allocates Using Context's Temporary Allocator*
//
// Inputs:
// - format: A format string with placeholders for the provided arguments
// - args: A variadic list of arguments to be formatted
//
// Returns: A formatted C string
//
ctprintf :: proc(format: string, args: ..any) -> cstring {
str: strings.Builder
strings.builder_init(&str, context.temp_allocator)
sbprintf(&str, format, ..args)
strings.write_byte(&str, 0)
s := strings.to_string(str)
return cstring(raw_data(s))
ctprintfln :: proc(format: string, args: ..any) -> cstring {
return ctprintf(format, ..args, newline=true)
}
// Formats using the default print settings and writes to the given strings.Builder
//
@@ -355,13 +424,25 @@ sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
// - buf: A pointer to a strings.Builder buffer
// - fmt: The format string
// - args: A variadic list of arguments to be formatted
// - newline: Whether a trailing newline should be written. (See `sbprintfln`.)
//
// Returns: The resulting formatted string
//
sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any) -> string {
wprintf(strings.to_writer(buf), fmt, ..args, flush=true)
sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any, newline := false) -> string {
wprintf(strings.to_writer(buf), fmt, ..args, flush=true, newline=newline)
return strings.to_string(buf^)
}
// Formats and writes to a strings.Builder buffer according to the specified format string, followed by a newline.
//
// Inputs:
// - buf: A pointer to a strings.Builder to store the formatted string
// - args: A variadic list of arguments to be formatted
//
// Returns: A formatted string
//
sbprintfln :: proc(buf: ^strings.Builder, format: string, args: ..any) -> string {
return sbprintf(buf, format, ..args, newline=true)
}
// Formats and writes to an io.Writer using the default print settings
//
// Inputs:
@@ -435,10 +516,11 @@ wprintln :: proc(w: io.Writer, args: ..any, sep := " ", flush := true) -> int {
// - w: An io.Writer to write to
// - fmt: The format string
// - args: A variadic list of arguments to be formatted
// - newline: Whether a trailing newline should be written. (See `wprintfln`.)
//
// Returns: The number of bytes written
//
wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true) -> int {
wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true, newline := false) -> int {
fi: Info
arg_index: int = 0
end := len(fmt)
@@ -708,12 +790,27 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true) -> int {
}
io.write_string(fi.writer, ")", &fi.n)
}
if newline {
io.write_byte(w, '\n', &fi.n)
}
if flush {
io.flush(w)
}
return fi.n
}
// Formats and writes to an io.Writer according to the specified format string, followed by a newline.
//
// Inputs:
// - w: The io.Writer to write to.
// - args: A variadic list of arguments to be formatted.
//
// Returns: The number of bytes written.
//
wprintfln :: proc(w: io.Writer, format: string, args: ..any, flush := true) -> int {
return wprintf(w, format, ..args, flush=flush, newline=true)
}
// Writes a ^runtime.Type_Info value to an io.Writer
//
// Inputs:
@@ -1408,34 +1505,9 @@ fmt_soa_pointer :: proc(fi: ^Info, p: runtime.Raw_Soa_Pointer, verb: rune) {
//
// Returns: The string representation of the enum value and a boolean indicating success.
//
@(require_results)
enum_value_to_string :: proc(val: any) -> (string, bool) {
v := val
v.id = runtime.typeid_base(v.id)
type_info := type_info_of(v.id)
#partial switch e in type_info.variant {
case: return "", false
case runtime.Type_Info_Enum:
Enum_Value :: runtime.Type_Info_Enum_Value
ev_, ok := reflect.as_i64(val)
ev := Enum_Value(ev_)
if ok {
if len(e.values) == 0 {
return "", true
} else {
for val, idx in e.values {
if val == ev {
return e.names[idx], true
}
}
}
return "", false
}
}
return "", false
return reflect.enum_name_from_value_any(val)
}
// Returns the enum value of a string representation.
//
@@ -2198,6 +2270,8 @@ fmt_named :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Named)
#partial switch b in info.base.variant {
case runtime.Type_Info_Struct:
fmt_struct(fi, v, verb, b, info.name)
case runtime.Type_Info_Bit_Field:
fmt_bit_field(fi, v, verb, b, info.name)
case runtime.Type_Info_Bit_Set:
fmt_bit_set(fi, v, verb = verb)
case:
@@ -2308,6 +2382,96 @@ fmt_matrix :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Matrix
fmt_write_indent(fi)
}
}
fmt_bit_field :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Bit_Field, type_name: string) {
read_bits :: proc(ptr: [^]byte, offset, size: uintptr) -> (res: u64) {
for i in 0..<size {
j := i+offset
B := ptr[j/8]
k := j&7
if B & (u8(1)<<k) != 0 {
res |= u64(1)<<u64(i)
}
}
return
}
handle_bit_field_tag :: proc(data: rawptr, info: reflect.Type_Info_Bit_Field, idx: int, verb: ^rune) -> (do_continue: bool) {
tag := info.tags[idx]
if vt, ok := reflect.struct_tag_lookup(reflect.Struct_Tag(tag), "fmt"); ok {
value := strings.trim_space(string(vt))
switch value {
case "": return false
case "-": return true
}
r, w := utf8.decode_rune_in_string(value)
value = value[w:]
if value == "" || value[0] == ',' {
verb^ = r
}
}
return false
}
io.write_string(fi.writer, type_name if len(type_name) != 0 else "bit_field", &fi.n)
io.write_string(fi.writer, "{", &fi.n)
hash := fi.hash; defer fi.hash = hash
indent := fi.indent; defer fi.indent -= 1
do_trailing_comma := hash
fi.indent += 1
if hash {
io.write_byte(fi.writer, '\n', &fi.n)
}
defer {
if hash {
for _ in 0..<indent { io.write_byte(fi.writer, '\t', &fi.n) }
}
io.write_byte(fi.writer, '}', &fi.n)
}
field_count := -1
for name, i in info.names {
field_verb := verb
if handle_bit_field_tag(v.data, info, i, &field_verb) {
continue
}
field_count += 1
if !do_trailing_comma && field_count > 0 {
io.write_string(fi.writer, ", ")
}
if hash {
fmt_write_indent(fi)
}
io.write_string(fi.writer, name, &fi.n)
io.write_string(fi.writer, " = ", &fi.n)
bit_offset := info.bit_offsets[i]
bit_size := info.bit_sizes[i]
value := read_bits(([^]byte)(v.data), bit_offset, bit_size)
type := info.types[i]
if !reflect.is_unsigned(runtime.type_info_core(type)) {
// Sign Extension
m := u64(1<<(bit_size-1))
value = (value ~ m) - m
}
fmt_value(fi, any{&value, type.id}, field_verb)
if do_trailing_comma { io.write_string(fi.writer, ",\n", &fi.n) }
}
}
// Formats a value based on its type and formatting verb
//
// Inputs:
@@ -2636,6 +2800,9 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case runtime.Type_Info_Matrix:
fmt_matrix(fi, v, verb, info)
case runtime.Type_Info_Bit_Field:
fmt_bit_field(fi, v, verb, info, "")
}
}
// Formats a complex number based on the given formatting verb
+16 -8
View File
@@ -30,7 +30,7 @@ fprintln :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true) -> int {
return wprintln(w, ..args, sep=sep, flush=flush)
}
// fprintf formats according to the specified format string and writes to fd
fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true) -> int {
fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, newline := false) -> int {
buf: [1024]byte
b: bufio.Writer
defer bufio.writer_flush(&b)
@@ -38,7 +38,11 @@ fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true) -> int {
bufio.writer_init_with_buf(&b, os.stream_from_handle(fd), buf[:])
w := bufio.writer_to_writer(&b)
return wprintf(w, fmt, ..args, flush=flush)
return wprintf(w, fmt, ..args, flush=flush, newline=newline)
}
// fprintfln formats according to the specified format string and writes to fd, followed by a newline.
fprintfln :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true) -> int {
return fprintf(fd, fmt, ..args, flush=flush, newline=true)
}
fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info, flush := true) -> (n: int, err: io.Error) {
buf: [1024]byte
@@ -62,15 +66,19 @@ fprint_typeid :: proc(fd: os.Handle, id: typeid, flush := true) -> (n: int, err:
}
// print formats using the default print settings and writes to os.stdout
print :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stdout, ..args, sep=sep, flush=flush) }
print :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stdout, ..args, sep=sep, flush=flush) }
// println formats using the default print settings and writes to os.stdout
println :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stdout, ..args, sep=sep, flush=flush) }
println :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stdout, ..args, sep=sep, flush=flush) }
// printf formats according to the specified format string and writes to os.stdout
printf :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush) }
printf :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush) }
// printfln formats according to the specified format string and writes to os.stdout, followed by a newline.
printfln :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush, newline=true) }
// eprint formats using the default print settings and writes to os.stderr
eprint :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stderr, ..args, sep=sep, flush=flush) }
eprint :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stderr, ..args, sep=sep, flush=flush) }
// eprintln formats using the default print settings and writes to os.stderr
eprintln :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stderr, ..args, sep=sep, flush=flush) }
eprintln :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stderr, ..args, sep=sep, flush=flush) }
// eprintf formats according to the specified format string and writes to os.stderr
eprintf :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush) }
eprintf :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush) }
// eprintfln formats according to the specified format string and writes to os.stderr, followed by a newline.
eprintfln :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush, newline=true) }
+4 -2
View File
@@ -34,7 +34,7 @@ Create_Socket_Error :: enum c.int {
Dial_Error :: enum c.int {
None = 0,
Port_Required = -1,
Port_Required = -1, // Attempted to dial an endpointing without a port being set.
Address_In_Use = c.int(os.EADDRINUSE),
In_Progress = c.int(os.EINPROGRESS),
@@ -54,7 +54,9 @@ Dial_Error :: enum c.int {
}
Bind_Error :: enum c.int {
None = 0,
None = 0,
Privileged_Port_Without_Root = -1, // Attempted to bind to a port less than 1024 without root access.
Address_In_Use = c.int(os.EADDRINUSE), // Another application is currently bound to this endpoint.
Given_Nonlocal_Address = c.int(os.EADDRNOTAVAIL), // The address is not a local address on this machine.
Broadcast_Disabled = c.int(os.EACCES), // To bind a UDP socket to the broadcast address, the appropriate socket option must be set.
+8 -1
View File
@@ -92,13 +92,20 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
return
}
// On Darwin, any port below 1024 is 'privileged' - which means that you need root access in order to use it.
MAX_PRIVILEGED_PORT :: 1023
@(private)
_bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
sockaddr := _endpoint_to_sockaddr(ep)
s := any_socket_to_socket(skt)
res := os.bind(os.Socket(s), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
if res != os.ERROR_NONE {
err = Bind_Error(res)
if res == os.EACCES && ep.port <= MAX_PRIVILEGED_PORT {
err = .Privileged_Port_Without_Root
} else {
err = Bind_Error(res)
}
}
return
}
+7 -2
View File
@@ -10,8 +10,8 @@ Array :: struct($T: typeid) {
String :: distinct Array(byte)
Version_Type_Major :: 0
Version_Type_Minor :: 2
Version_Type_Patch :: 4
Version_Type_Minor :: 3
Version_Type_Patch :: 0
Version_Type :: struct {
major, minor, patch: u8,
@@ -110,6 +110,8 @@ Entity_Flag :: enum u32le {
Param_No_Alias = 7, // #no_alias
Param_Any_Int = 8, // #any_int
Bit_Field_Field = 19,
Type_Alias = 20,
Builtin_Pkg_Builtin = 30,
@@ -137,6 +139,7 @@ Entity :: struct {
// May be used by (Struct fields and procedure fields):
// .Variable
// .Constant
// This is equal to the negative of the "bit size" it this is a `bit_field`s field
field_group_index: i32le,
// May used by:
@@ -187,6 +190,7 @@ Type_Kind :: enum u32le {
Multi_Pointer = 22,
Matrix = 23,
Soa_Pointer = 24,
Bit_Field = 25,
}
Type_Elems_Cap :: 4
@@ -247,6 +251,7 @@ Type :: struct {
// .Multi_Pointer - 1 type: 0=element
// .Matrix - 1 type: 0=element
// .Soa_Pointer - 1 type: 0=element
// .Bit_Field - 1 type: 0=backing type
types: Array(Type_Index),
// Used by:
+2
View File
@@ -137,6 +137,7 @@ Token_Kind :: enum u32 {
Union, // union
Enum, // enum
Bit_Set, // bit_set
Bit_Field, // bit_field
Map, // map
Dynamic, // dynamic
Auto_Cast, // auto_cast
@@ -270,6 +271,7 @@ tokens := [Token_Kind.COUNT]string {
"union",
"enum",
"bit_set",
"bit_field",
"map",
"dynamic",
"auto_cast",
+2 -5
View File
@@ -1,7 +1,6 @@
package os
import "base:runtime"
import "core:mem"
import "core:strconv"
import "core:unicode/utf8"
@@ -160,13 +159,11 @@ write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (succ
}
write_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) {
s := transmute([]byte)mem.Raw_Slice{data, len}
return write(fd, s)
return write(fd, ([^]byte)(data)[:len])
}
read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) {
s := transmute([]byte)mem.Raw_Slice{data, len}
return read(fd, s)
return read(fd, ([^]byte)(data)[:len])
}
heap_allocator_proc :: runtime.heap_allocator_proc
+2 -5
View File
@@ -1,6 +1,5 @@
package os2
import "core:mem"
import "base:runtime"
import "core:strconv"
import "core:unicode/utf8"
@@ -64,13 +63,11 @@ write_encoded_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) {
write_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) {
s := transmute([]byte)mem.Raw_Slice{data, len}
return write(f, s)
return write(f, ([^]byte)(data)[:len])
}
read_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) {
s := transmute([]byte)mem.Raw_Slice{data, len}
return read(f, s)
return read(f, ([^]byte)(data)[:len])
}
+47
View File
@@ -35,6 +35,7 @@ Type_Info_Relative_Pointer :: runtime.Type_Info_Relative_Pointer
Type_Info_Relative_Multi_Pointer :: runtime.Type_Info_Relative_Multi_Pointer
Type_Info_Matrix :: runtime.Type_Info_Matrix
Type_Info_Soa_Pointer :: runtime.Type_Info_Soa_Pointer
Type_Info_Bit_Field :: runtime.Type_Info_Bit_Field
Type_Info_Enum_Value :: runtime.Type_Info_Enum_Value
@@ -70,6 +71,7 @@ Type_Kind :: enum {
Relative_Multi_Pointer,
Matrix,
Soa_Pointer,
Bit_Field,
}
@@ -106,6 +108,7 @@ type_kind :: proc(T: typeid) -> Type_Kind {
case Type_Info_Relative_Multi_Pointer: return .Relative_Multi_Pointer
case Type_Info_Matrix: return .Matrix
case Type_Info_Soa_Pointer: return .Soa_Pointer
case Type_Info_Bit_Field: return .Bit_Field
}
}
@@ -627,6 +630,43 @@ enum_from_name_any :: proc(Enum_Type: typeid, name: string) -> (value: Type_Info
return
}
@(require_results)
enum_name_from_value :: proc(value: $Enum_Type) -> (name: string, ok: bool) where intrinsics.type_is_enum(Enum_Type) {
ti := type_info_base(type_info_of(Enum_Type))
e := ti.variant.(runtime.Type_Info_Enum) or_return
if len(e.values) == 0 {
return
}
ev := Type_Info_Enum_Value(value)
for val, idx in e.values {
if val == ev {
return e.names[idx], true
}
}
return
}
@(require_results)
enum_name_from_value_any :: proc(value: any) -> (name: string, ok: bool) {
if value.id == nil {
return
}
ti := type_info_base(type_info_of(value.id))
e := ti.variant.(runtime.Type_Info_Enum) or_return
if len(e.values) == 0 {
return
}
ev := Type_Info_Enum_Value(as_i64(value) or_return)
for val, idx in e.values {
if val == ev {
return e.names[idx], true
}
}
return
}
@(require_results)
enum_field_names :: proc(Enum_Type: typeid) -> []string {
@@ -1567,6 +1607,13 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
}
}
return true
case Type_Info_Bit_Field:
x, y := a, b
x.id = v.backing_type.id
y.id = v.backing_type.id
return equal(x, y, including_indirect_array_recursion, recursion_level+0)
}
runtime.print_typeid(a.id)
+31
View File
@@ -174,6 +174,23 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
if x.row_count != y.row_count { return false }
if x.column_count != y.column_count { return false }
return are_types_identical(x.elem, y.elem)
case Type_Info_Bit_Field:
y := b.variant.(Type_Info_Bit_Field) or_return
if !are_types_identical(x.backing_type, y.backing_type) { return false }
if len(x.names) != len(y.names) { return false }
for _, i in x.names {
if x.names[i] != y.names[i] {
return false
}
if !are_types_identical(x.types[i], y.types[i]) {
return false
}
if x.bit_sizes[i] != y.bit_sizes[i] {
return false
}
}
return true
}
return false
@@ -639,6 +656,20 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) -
}
io.write_byte(w, ']', &n) or_return
case Type_Info_Bit_Field:
io.write_string(w, "bit_field ", &n) or_return
write_type(w, info.backing_type, &n) or_return
io.write_string(w, " {", &n) or_return
for name, i in info.names {
if i > 0 { io.write_string(w, ", ", &n) or_return }
io.write_string(w, name, &n) or_return
io.write_string(w, ": ", &n) or_return
write_type(w, info.types[i], &n) or_return
io.write_string(w, " | ", &n) or_return
io.write_u64(w, u64(info.bit_sizes[i]), 10, &n) or_return
}
io.write_string(w, "}", &n) or_return
case Type_Info_Simd_Vector:
io.write_string(w, "#simd[", &n) or_return
io.write_i64(w, i64(info.count), 10, &n) or_return
+489
View File
@@ -0,0 +1,489 @@
package sync_chan
import "base:builtin"
import "base:intrinsics"
import "base:runtime"
import "core:mem"
import "core:sync"
import "core:math/rand"
Direction :: enum {
Send = -1,
Both = 0,
Recv = +1,
}
Chan :: struct($T: typeid, $D: Direction = Direction.Both) {
#subtype impl: ^Raw_Chan `fmt:"-"`,
}
Raw_Chan :: struct {
// Shared
allocator: runtime.Allocator,
allocation_size: int,
msg_size: u16,
closed: b16, // atomic
mutex: sync.Mutex,
r_cond: sync.Cond,
w_cond: sync.Cond,
r_waiting: int, // atomic
w_waiting: int, // atomic
// Buffered
queue: ^Raw_Queue,
// Unbuffered
r_mutex: sync.Mutex,
w_mutex: sync.Mutex,
unbuffered_data: rawptr,
}
create :: proc{
create_unbuffered,
create_buffered,
}
@(require_results)
create_unbuffered :: proc($C: typeid/Chan($T), allocator: runtime.Allocator) -> (c: C, err: runtime.Allocator_Error)
where size_of(T) <= int(max(u16)) {
c.impl, err = create_raw_unbuffered(size_of(T), align_of(T), allocator)
return
}
@(require_results)
create_buffered :: proc($C: typeid/Chan($T), #any_int cap: int, allocator: runtime.Allocator) -> (c: C, err: runtime.Allocator_Error)
where size_of(T) <= int(max(u16)) {
c.impl, err = create_raw_buffered(size_of(T), align_of(T), cap, allocator)
return
}
create_raw :: proc{
create_raw_unbuffered,
create_raw_buffered,
}
@(require_results)
create_raw_unbuffered :: proc(#any_int msg_size, msg_alignment: int, allocator: runtime.Allocator) -> (c: ^Raw_Chan, err: runtime.Allocator_Error) {
assert(msg_size <= int(max(u16)))
align := max(align_of(Raw_Chan), msg_alignment)
size := mem.align_forward_int(size_of(Raw_Chan), align)
offset := size
size += msg_size
size = mem.align_forward_int(size, align)
ptr := mem.alloc(size, align, allocator) or_return
c = (^Raw_Chan)(ptr)
c.allocation_size = size
c.unbuffered_data = ([^]byte)(ptr)[offset:]
c.msg_size = u16(msg_size)
return
}
@(require_results)
create_raw_buffered :: proc(#any_int msg_size, msg_alignment: int, #any_int cap: int, allocator: runtime.Allocator) -> (c: ^Raw_Chan, err: runtime.Allocator_Error) {
assert(msg_size <= int(max(u16)))
if cap <= 0 {
return create_raw_unbuffered(msg_size, msg_alignment, allocator)
}
align := max(align_of(Raw_Chan), msg_alignment, align_of(Raw_Queue))
size := mem.align_forward_int(size_of(Raw_Chan), align)
q_offset := size
size = mem.align_forward_int(q_offset + size_of(Raw_Queue), msg_alignment)
offset := size
size += msg_size * cap
size = mem.align_forward_int(size, align)
ptr := mem.alloc(size, align, allocator) or_return
c = (^Raw_Chan)(ptr)
c.allocation_size = size
bptr := ([^]byte)(ptr)
c.queue = (^Raw_Queue)(bptr[q_offset:])
c.msg_size = u16(msg_size)
raw_queue_init(c.queue, ([^]byte)(bptr[offset:]), cap, msg_size)
return
}
destroy :: proc(c: ^Raw_Chan) -> (err: runtime.Allocator_Error) {
if c != nil {
allocator := c.allocator
err = mem.free_with_size(c, c.allocation_size, allocator)
}
return
}
@(require_results)
as_send :: #force_inline proc "contextless" (c: $C/Chan($T, $D)) -> (s: Chan(T, .Send)) where C.D <= .Both {
return transmute(type_of(s))c
}
@(require_results)
as_recv :: #force_inline proc "contextless" (c: $C/Chan($T, $D)) -> (r: Chan(T, .Recv)) where C.D >= .Both {
return transmute(type_of(r))c
}
send :: proc "contextless" (c: $C/Chan($T, $D), data: T) -> (ok: bool) where C.D <= .Both {
data := data
ok = send_raw(c, &data)
return
}
@(require_results)
try_send :: proc "contextless" (c: $C/Chan($T, $D), data: T) -> (ok: bool) where C.D <= .Both {
data := data
ok = try_send_raw(c, &data)
return
}
@(require_results)
recv :: proc "contextless" (c: $C/Chan($T)) -> (data: T, ok: bool) where C.D >= .Both {
ok = recv_raw(c, &data)
return
}
@(require_results)
try_recv :: proc "contextless" (c: $C/Chan($T)) -> (data: T, ok: bool) where C.D >= .Both {
ok = try_recv_raw(c, &data)
return
}
@(require_results)
send_raw :: proc "contextless" (c: ^Raw_Chan, msg_in: rawptr) -> (ok: bool) {
if c == nil {
return
}
if c.queue != nil { // buffered
sync.guard(&c.mutex)
for c.queue.len == c.queue.cap {
sync.atomic_add(&c.w_waiting, 1)
sync.wait(&c.w_cond, &c.mutex)
sync.atomic_sub(&c.w_waiting, 1)
}
ok = raw_queue_push(c.queue, msg_in)
if sync.atomic_load(&c.r_waiting) > 0 {
sync.signal(&c.r_cond)
}
} else if c.unbuffered_data != nil { // unbuffered
sync.guard(&c.w_mutex)
sync.guard(&c.mutex)
if sync.atomic_load(&c.closed) {
return false
}
mem.copy(c.unbuffered_data, msg_in, int(c.msg_size))
sync.atomic_add(&c.w_waiting, 1)
if sync.atomic_load(&c.r_waiting) > 0 {
sync.signal(&c.r_cond)
}
sync.wait(&c.w_cond, &c.mutex)
ok = true
}
return
}
@(require_results)
recv_raw :: proc "contextless" (c: ^Raw_Chan, msg_out: rawptr) -> (ok: bool) {
if c == nil {
return
}
if c.queue != nil { // buffered
sync.guard(&c.mutex)
for c.queue.len == 0 {
if sync.atomic_load(&c.closed) {
return
}
sync.atomic_add(&c.r_waiting, 1)
sync.wait(&c.r_cond, &c.mutex)
sync.atomic_sub(&c.r_waiting, 1)
}
msg := raw_queue_pop(c.queue)
if msg != nil {
mem.copy(msg_out, msg, int(c.msg_size))
}
if sync.atomic_load(&c.w_waiting) > 0 {
sync.signal(&c.w_cond)
}
ok = true
} else if c.unbuffered_data != nil { // unbuffered
sync.guard(&c.r_mutex)
sync.guard(&c.mutex)
for !sync.atomic_load(&c.closed) &&
sync.atomic_load(&c.w_waiting) == 0 {
sync.atomic_add(&c.r_waiting, 1)
sync.wait(&c.r_cond, &c.mutex)
sync.atomic_sub(&c.r_waiting, 1)
}
if sync.atomic_load(&c.closed) {
return
}
mem.copy(msg_out, c.unbuffered_data, int(c.msg_size))
sync.atomic_sub(&c.w_waiting, 1)
sync.signal(&c.w_cond)
ok = true
}
return
}
@(require_results)
try_send_raw :: proc "contextless" (c: ^Raw_Chan, msg_in: rawptr) -> (ok: bool) {
if c == nil {
return false
}
if c.queue != nil { // buffered
sync.guard(&c.mutex)
if c.queue.len == c.queue.cap {
return false
}
ok = raw_queue_push(c.queue, msg_in)
if sync.atomic_load(&c.r_waiting) > 0 {
sync.signal(&c.r_cond)
}
} else if c.unbuffered_data != nil { // unbuffered
sync.guard(&c.w_mutex)
sync.guard(&c.mutex)
if sync.atomic_load(&c.closed) {
return false
}
mem.copy(c.unbuffered_data, msg_in, int(c.msg_size))
sync.atomic_add(&c.w_waiting, 1)
if sync.atomic_load(&c.r_waiting) > 0 {
sync.signal(&c.r_cond)
}
sync.wait(&c.w_cond, &c.mutex)
ok = true
}
return
}
@(require_results)
try_recv_raw :: proc "contextless" (c: ^Raw_Chan, msg_out: rawptr) -> bool {
if c == nil {
return false
}
if c.queue != nil { // buffered
sync.guard(&c.mutex)
if c.queue.len == 0 {
return false
}
msg := raw_queue_pop(c.queue)
if msg != nil {
mem.copy(msg_out, msg, int(c.msg_size))
}
if sync.atomic_load(&c.w_waiting) > 0 {
sync.signal(&c.w_cond)
}
return true
} else if c.unbuffered_data != nil { // unbuffered
sync.guard(&c.r_mutex)
sync.guard(&c.mutex)
if sync.atomic_load(&c.closed) ||
sync.atomic_load(&c.w_waiting) == 0 {
return false
}
mem.copy(msg_out, c.unbuffered_data, int(c.msg_size))
sync.atomic_sub(&c.w_waiting, 1)
sync.signal(&c.w_cond)
return true
}
return false
}
@(require_results)
is_buffered :: proc "contextless" (c: ^Raw_Chan) -> bool {
return c != nil && c.queue != nil
}
@(require_results)
is_unbuffered :: proc "contextless" (c: ^Raw_Chan) -> bool {
return c != nil && c.unbuffered_data != nil
}
@(require_results)
len :: proc "contextless" (c: ^Raw_Chan) -> int {
if c != nil && c.queue != nil {
sync.guard(&c.mutex)
return c.queue.len
}
return 0
}
@(require_results)
cap :: proc "contextless" (c: ^Raw_Chan) -> int {
if c != nil && c.queue != nil {
sync.guard(&c.mutex)
return c.queue.cap
}
return 0
}
close :: proc "contextless" (c: ^Raw_Chan) -> bool {
if c == nil {
return false
}
sync.guard(&c.mutex)
if sync.atomic_load(&c.closed) {
return false
}
sync.atomic_store(&c.closed, true)
sync.broadcast(&c.r_cond)
sync.broadcast(&c.w_cond)
return true
}
@(require_results)
is_closed :: proc "contextless" (c: ^Raw_Chan) -> bool {
if c == nil {
return true
}
sync.guard(&c.mutex)
return bool(sync.atomic_load(&c.closed))
}
Raw_Queue :: struct {
data: [^]byte,
len: int,
cap: int,
next: int,
size: int, // element size
}
raw_queue_init :: proc "contextless" (q: ^Raw_Queue, data: rawptr, cap: int, size: int) {
q.data = ([^]byte)(data)
q.len = 0
q.cap = cap
q.next = 0
q.size = size
}
@(require_results)
raw_queue_push :: proc "contextless" (q: ^Raw_Queue, data: rawptr) -> bool {
if q.len == q.cap {
return false
}
pos := q.next + q.len
if pos >= q.cap {
pos -= q.cap
}
val_ptr := q.data[pos*q.size:]
mem.copy(val_ptr, data, q.size)
q.len += 1
return true
}
@(require_results)
raw_queue_pop :: proc "contextless" (q: ^Raw_Queue) -> (data: rawptr) {
if q.len > 0 {
data = q.data[q.next*q.size:]
q.next += 1
q.len -= 1
if q.next >= q.cap {
q.next -= q.cap
}
}
return
}
@(require_results)
can_recv :: proc "contextless" (c: ^Raw_Chan) -> bool {
if is_buffered(c) {
return len(c) > 0
}
sync.guard(&c.mutex)
return sync.atomic_load(&c.w_waiting) > 0
}
@(require_results)
can_send :: proc "contextless" (c: ^Raw_Chan) -> bool {
if is_buffered(c) {
sync.guard(&c.mutex)
return len(c) < cap(c)
}
sync.guard(&c.mutex)
return sync.atomic_load(&c.r_waiting) > 0
}
@(require_results)
select_raw :: proc "odin" (recvs: []^Raw_Chan, sends: []^Raw_Chan, send_msgs: []rawptr, recv_out: rawptr) -> (select_idx: int, ok: bool) #no_bounds_check {
Select_Op :: struct {
idx: int, // local to the slice that was given
is_recv: bool,
}
candidate_count := builtin.len(recvs)+builtin.len(sends)
candidates := ([^]Select_Op)(intrinsics.alloca(candidate_count*size_of(Select_Op), align_of(Select_Op)))
count := 0
for c, i in recvs {
if can_recv(c) {
candidates[count] = {
is_recv = true,
idx = i,
}
count += 1
}
}
for c, i in sends {
if can_send(c) {
candidates[count] = {
is_recv = false,
idx = i,
}
count += 1
}
}
if count == 0 {
return
}
r: ^rand.Rand = nil
select_idx = rand.int_max(count, r) if count > 0 else 0
sel := candidates[select_idx]
if sel.is_recv {
ok = recv_raw(recvs[sel.idx], recv_out)
} else {
ok = send_raw(sends[sel.idx], send_msgs[sel.idx])
}
return
}
+24
View File
@@ -417,4 +417,28 @@ unpark :: proc "contextless" (p: ^Parker) {
if atomic_exchange_explicit(&p.state, NOTIFIED, .Release) == PARKED {
futex_signal(&p.state)
}
}
// A One_Shot_Event is an associated token which is initially not present:
// * The `one_shot_event_wait` blocks the current thread until the event
// is made available
// * The `one_shot_event_signal` procedure automatically makes the token
// available if its was not already.
One_Shot_Event :: struct #no_copy {
state: Futex,
}
// Blocks the current thread until the event is made available with `one_shot_event_signal`.
one_shot_event_wait :: proc "contextless" (e: ^One_Shot_Event) {
for atomic_load_explicit(&e.state, .Acquire) == 0 {
futex_wait(&e.state, 1)
}
}
// Releases any threads that are currently blocked by this event with `one_shot_event_wait`.
one_shot_event_signal :: proc "contextless" (e: ^One_Shot_Event) {
atomic_store_explicit(&e.state, 1, .Release)
futex_broadcast(&e.state)
}
+8
View File
@@ -369,6 +369,10 @@ datetime_to_time :: proc "contextless" (year, month, day, hour, minute, second:
mod = year % divisor
return
}
_is_leap_year :: proc "contextless" (year: int) -> bool {
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
}
ok = true
@@ -395,6 +399,10 @@ datetime_to_time :: proc "contextless" (year, month, day, hour, minute, second:
days += int(days_before[_m]) + _d
if _is_leap_year(year) && _m >= 2 {
days += 1
}
s += i64(days) * SECONDS_PER_DAY
s += i64(hour) * SECONDS_PER_HOUR
s += i64(minute) * SECONDS_PER_MINUTE