Merge remote-tracking branch 'offical/master'

# Conflicts:
#	vendor/raylib/raygui.odin
#	vendor/raylib/raylib.odin
This commit is contained in:
ed
2025-01-06 10:51:25 -05:00
105 changed files with 7185 additions and 3321 deletions
+230 -148
View File
@@ -1,148 +1,230 @@
package encoding_base32
// @note(zh): Encoding utility for Base32
// A secondary param can be used to supply a custom alphabet to
// @link(encode) and a matching decoding table to @link(decode).
// If none is supplied it just uses the standard Base32 alphabet.
// Incase your specific version does not use padding, you may
// truncate it from the encoded output.
ENC_TABLE := [32]byte {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', '2', '3', '4', '5', '6', '7',
}
PADDING :: '='
DEC_TABLE := [?]u8 {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
}
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string {
out_length := (len(data) + 4) / 5 * 8
out := make([]byte, out_length)
_encode(out, data)
return string(out)
}
@private
_encode :: proc(out, data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) {
out := out
data := data
for len(data) > 0 {
carry: byte
switch len(data) {
case:
out[7] = ENC_TABLE[data[4] & 0x1f]
carry = data[4] >> 5
fallthrough
case 4:
out[6] = ENC_TABLE[carry | (data[3] << 3) & 0x1f]
out[5] = ENC_TABLE[(data[3] >> 2) & 0x1f]
carry = data[3] >> 7
fallthrough
case 3:
out[4] = ENC_TABLE[carry | (data[2] << 1) & 0x1f]
carry = (data[2] >> 4) & 0x1f
fallthrough
case 2:
out[3] = ENC_TABLE[carry | (data[1] << 4) & 0x1f]
out[2] = ENC_TABLE[(data[1] >> 1) & 0x1f]
carry = (data[1] >> 6) & 0x1f
fallthrough
case 1:
out[1] = ENC_TABLE[carry | (data[0] << 2) & 0x1f]
out[0] = ENC_TABLE[data[0] >> 3]
}
if len(data) < 5 {
out[7] = byte(PADDING)
if len(data) < 4 {
out[6] = byte(PADDING)
out[5] = byte(PADDING)
if len(data) < 3 {
out[4] = byte(PADDING)
if len(data) < 2 {
out[3] = byte(PADDING)
out[2] = byte(PADDING)
}
}
}
break
}
data = data[5:]
out = out[8:]
}
}
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check{
if len(data) == 0 {
return nil
}
outi := 0
data := data
out := make([]byte, len(data) / 8 * 5, allocator)
end := false
for len(data) > 0 && !end {
dbuf : [8]byte
dlen := 8
for j := 0; j < 8; {
if len(data) == 0 {
dlen, end = j, true
break
}
input := data[0]
data = data[1:]
if input == byte(PADDING) && j >= 2 && len(data) < 8 {
assert(!(len(data) + j < 8 - 1), "Corrupted input")
for k := 0; k < 8-1-j; k +=1 {
assert(len(data) < k || data[k] == byte(PADDING), "Corrupted input")
}
dlen, end = j, true
assert(dlen != 1 && dlen != 3 && dlen != 6, "Corrupted input")
break
}
dbuf[j] = DEC_TABLE[input]
assert(dbuf[j] != 0xff, "Corrupted input")
j += 1
}
switch dlen {
case 8:
out[outi + 4] = dbuf[6] << 5 | dbuf[7]
fallthrough
case 7:
out[outi + 3] = dbuf[4] << 7 | dbuf[5] << 2 | dbuf[6] >> 3
fallthrough
case 5:
out[outi + 2] = dbuf[3] << 4 | dbuf[4] >> 1
fallthrough
case 4:
out[outi + 1] = dbuf[1] << 6 | dbuf[2] << 1 | dbuf[3] >> 4
fallthrough
case 2:
out[outi + 0] = dbuf[0] << 3 | dbuf[1] >> 2
}
outi += 5
}
return out
}
// Base32 encoding/decoding implementation as specified in RFC 4648.
// [[ More; https://www.rfc-editor.org/rfc/rfc4648.html ]]
package encoding_base32
// @note(zh): Encoding utility for Base32
// A secondary param can be used to supply a custom alphabet to
// @link(encode) and a matching decoding table to @link(decode).
// If none is supplied it just uses the standard Base32 alphabet.
// In case your specific version does not use padding, you may
// truncate it from the encoded output.
// Error represents errors that can occur during base32 decoding operations.
// As per RFC 4648:
// - Section 3.3: Invalid character handling
// - Section 3.2: Padding requirements
// - Section 6: Base32 encoding specifics (including block size requirements)
Error :: enum {
None,
Invalid_Character, // Input contains characters outside the specified alphabet
Invalid_Length, // Input length is not valid for base32 (must be a multiple of 8 with proper padding)
Malformed_Input, // Input has improper structure (wrong padding position or incomplete groups)
}
Validate_Proc :: #type proc(c: byte) -> bool
@private
_validate_default :: proc(c: byte) -> bool {
return (c >= 'A' && c <= 'Z') || (c >= '2' && c <= '7')
}
@(rodata)
ENC_TABLE := [32]byte {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', '2', '3', '4', '5', '6', '7',
}
PADDING :: '='
@(rodata)
DEC_TABLE := [256]u8 {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
}
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string {
out_length := (len(data) + 4) / 5 * 8
out := make([]byte, out_length, allocator)
_encode(out, data, ENC_TBL)
return string(out[:])
}
@private
_encode :: proc(out, data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) {
out := out
data := data
for len(data) > 0 {
carry: byte
switch len(data) {
case:
out[7] = ENC_TBL[data[4] & 0x1f]
carry = data[4] >> 5
fallthrough
case 4:
out[6] = ENC_TBL[carry | (data[3] << 3) & 0x1f]
out[5] = ENC_TBL[(data[3] >> 2) & 0x1f]
carry = data[3] >> 7
fallthrough
case 3:
out[4] = ENC_TBL[carry | (data[2] << 1) & 0x1f]
carry = (data[2] >> 4) & 0x1f
fallthrough
case 2:
out[3] = ENC_TBL[carry | (data[1] << 4) & 0x1f]
out[2] = ENC_TBL[(data[1] >> 1) & 0x1f]
carry = (data[1] >> 6) & 0x1f
fallthrough
case 1:
out[1] = ENC_TBL[carry | (data[0] << 2) & 0x1f]
out[0] = ENC_TBL[data[0] >> 3]
}
if len(data) < 5 {
out[7] = byte(PADDING)
if len(data) < 4 {
out[6] = byte(PADDING)
out[5] = byte(PADDING)
if len(data) < 3 {
out[4] = byte(PADDING)
if len(data) < 2 {
out[3] = byte(PADDING)
out[2] = byte(PADDING)
}
}
}
break
}
data = data[5:]
out = out[8:]
}
}
@(optimization_mode="favor_size")
decode :: proc(
data: string,
DEC_TBL := DEC_TABLE,
validate: Validate_Proc = _validate_default,
allocator := context.allocator) -> (out: []byte, err: Error) {
if len(data) == 0 {
return nil, .None
}
// Check minimum length requirement first
if len(data) < 2 {
return nil, .Invalid_Length
}
// Validate characters using provided validation function
for i := 0; i < len(data); i += 1 {
c := data[i]
if c == byte(PADDING) {
break
}
if !validate(c) {
return nil, .Invalid_Character
}
}
// Validate padding and length
data_len := len(data)
padding_count := 0
for i := data_len - 1; i >= 0; i -= 1 {
if data[i] != byte(PADDING) {
break
}
padding_count += 1
}
// Check for proper padding and length combinations
if padding_count > 0 {
// Verify no padding in the middle
for i := 0; i < data_len - padding_count; i += 1 {
if data[i] == byte(PADDING) {
return nil, .Malformed_Input
}
}
content_len := data_len - padding_count
mod8 := content_len % 8
required_padding: int
switch mod8 {
case 2: required_padding = 6 // 2 chars need 6 padding chars
case 4: required_padding = 4 // 4 chars need 4 padding chars
case 5: required_padding = 3 // 5 chars need 3 padding chars
case 7: required_padding = 1 // 7 chars need 1 padding char
case: required_padding = 0
}
if required_padding > 0 {
if padding_count != required_padding {
return nil, .Malformed_Input
}
} else if mod8 != 0 {
return nil, .Malformed_Input
}
} else {
// No padding - must be multiple of 8
if data_len % 8 != 0 {
return nil, .Malformed_Input
}
}
// Calculate decoded length: 5 bytes for every 8 input chars
input_chars := data_len - padding_count
out_len := input_chars * 5 / 8
out = make([]byte, out_len, allocator)
defer if err != .None {
delete(out)
}
// Process input in 8-byte blocks
outi := 0
for i := 0; i < input_chars; i += 8 {
buf: [8]byte
block_size := min(8, input_chars - i)
// Decode block
for j := 0; j < block_size; j += 1 {
buf[j] = DEC_TBL[data[i + j]]
}
// Convert to output bytes based on block size
bytes_to_write := block_size * 5 / 8
switch block_size {
case 8:
out[outi + 4] = (buf[6] << 5) | buf[7]
fallthrough
case 7:
out[outi + 3] = (buf[4] << 7) | (buf[5] << 2) | (buf[6] >> 3)
fallthrough
case 5:
out[outi + 2] = (buf[3] << 4) | (buf[4] >> 1)
fallthrough
case 4:
out[outi + 1] = (buf[1] << 6) | (buf[2] << 1) | (buf[3] >> 4)
fallthrough
case 2:
out[outi] = (buf[0] << 3) | (buf[1] >> 2)
}
outi += bytes_to_write
}
return
}
+227
View File
@@ -0,0 +1,227 @@
package encoding_base32
import "core:testing"
import "core:bytes"
@(test)
test_base32_decode_valid :: proc(t: ^testing.T) {
// RFC 4648 Section 10 - Test vectors
cases := [?]struct {
input, expected: string,
}{
{"", ""},
{"MY======", "f"},
{"MZXQ====", "fo"},
{"MZXW6===", "foo"},
{"MZXW6YQ=", "foob"},
{"MZXW6YTB", "fooba"},
{"MZXW6YTBOI======", "foobar"},
}
for c in cases {
output, err := decode(c.input)
if output != nil {
defer delete(output)
}
testing.expect_value(t, err, Error.None)
expected := transmute([]u8)c.expected
if output != nil {
testing.expect(t, bytes.equal(output, expected))
} else {
testing.expect(t, len(c.expected) == 0)
}
}
}
@(test)
test_base32_encode :: proc(t: ^testing.T) {
// RFC 4648 Section 10 - Test vectors
cases := [?]struct {
input, expected: string,
}{
{"", ""},
{"f", "MY======"},
{"fo", "MZXQ===="},
{"foo", "MZXW6==="},
{"foob", "MZXW6YQ="},
{"fooba", "MZXW6YTB"},
{"foobar", "MZXW6YTBOI======"},
}
for c in cases {
output := encode(transmute([]byte)c.input)
defer delete(output)
testing.expect(t, output == c.expected)
}
}
@(test)
test_base32_decode_invalid :: proc(t: ^testing.T) {
// Section 3.3 - Non-alphabet characters
{
// Characters outside alphabet
input := "MZ1W6YTB" // '1' not in alphabet (A-Z, 2-7)
output, err := decode(input)
if output != nil {
defer delete(output)
}
testing.expect_value(t, err, Error.Invalid_Character)
}
{
// Lowercase not allowed
input := "mzxq===="
output, err := decode(input)
if output != nil {
defer delete(output)
}
testing.expect_value(t, err, Error.Invalid_Character)
}
// Section 3.2 - Padding requirements
{
// Padding must only be at end
input := "MZ=Q===="
output, err := decode(input)
if output != nil {
defer delete(output)
}
testing.expect_value(t, err, Error.Malformed_Input)
}
{
// Missing padding
input := "MZXQ" // Should be MZXQ====
output, err := decode(input)
if output != nil {
defer delete(output)
}
testing.expect_value(t, err, Error.Malformed_Input)
}
{
// Incorrect padding length
input := "MZXQ=" // Needs 4 padding chars
output, err := decode(input)
if output != nil {
defer delete(output)
}
testing.expect_value(t, err, Error.Malformed_Input)
}
{
// Too much padding
input := "MY=========" // Extra padding chars
output, err := decode(input)
if output != nil {
defer delete(output)
}
testing.expect_value(t, err, Error.Malformed_Input)
}
// Section 6 - Base32 block size requirements
{
// Single character (invalid block)
input := "M"
output, err := decode(input)
if output != nil {
defer delete(output)
}
testing.expect_value(t, err, Error.Invalid_Length)
}
}
@(test)
test_base32_roundtrip :: proc(t: ^testing.T) {
cases := [?]string{
"",
"f",
"fo",
"foo",
"foob",
"fooba",
"foobar",
}
for input in cases {
encoded := encode(transmute([]byte)input)
defer delete(encoded)
decoded, err := decode(encoded)
if decoded != nil {
defer delete(decoded)
}
testing.expect_value(t, err, Error.None)
testing.expect(t, bytes.equal(decoded, transmute([]byte)input))
}
}
@(test)
test_base32_custom_alphabet :: proc(t: ^testing.T) {
custom_enc_table := [32]byte{
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
}
custom_dec_table: [256]u8
for i := 0; i < len(custom_enc_table); i += 1 {
custom_dec_table[custom_enc_table[i]] = u8(i)
}
/*
custom_dec_table := [256]u8{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00-0x0f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10-0x1f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x20-0x2f
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // 0x30-0x3f ('0'-'9')
0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40-0x4f ('A'-'O')
25, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x50-0x5f ('P'-'V')
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60-0x6f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x70-0x7f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80-0x8f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90-0x9f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xa0-0xaf
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xb0-0xbf
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xc0-0xcf
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xd0-0xdf
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xe0-0xef
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xf0-0xff
}
*/
custom_validate :: proc(c: byte) -> bool {
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'V') || c == byte(PADDING)
}
cases := [?]struct {
input: string,
enc_expected: string,
}{
{"f", "CO======"},
{"fo", "CPNG===="},
{"foo", "CPNMU==="},
}
for c in cases {
// Test encoding
encoded := encode(transmute([]byte)c.input, custom_enc_table)
defer delete(encoded)
testing.expect(t, encoded == c.enc_expected)
// Test decoding
decoded, err := decode(encoded, custom_dec_table, custom_validate)
defer if decoded != nil {
delete(decoded)
}
testing.expect_value(t, err, Error.None)
testing.expect(t, bytes.equal(decoded, transmute([]byte)c.input))
}
// Test invalid character detection
{
input := "WXY=====" // Contains chars not in our alphabet
output, err := decode(input, custom_dec_table, custom_validate)
if output != nil {
delete(output)
}
testing.expect_value(t, err, Error.Invalid_Character)
}
}
+3 -3
View File
@@ -433,13 +433,13 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
}
field_used_bytes := (reflect.size_of_typeid(ti.id)+7)/8
field_used := intrinsics.alloca(field_used_bytes, 1)
field_used := intrinsics.alloca(field_used_bytes + 1, 1) // + 1 to not overflow on size_of 0 types.
intrinsics.mem_zero(field_used, field_used_bytes)
use_field_idx := -1
for field, field_idx in fields {
tag_value := string(reflect.struct_tag_get(field.tag, "json"))
tag_value := reflect.struct_tag_get(field.tag, "json")
json_name, _ := json_name_from_tag_value(tag_value)
if key == json_name {
use_field_idx = field_idx
@@ -470,7 +470,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
}
}
if field.name == key {
if field.name == key || (field.tag != "" && reflect.struct_tag_get(field.tag, "json") == key) {
offset = field.offset
type = field.type
found = true
+1 -1
View File
@@ -146,7 +146,7 @@ which_bytes :: proc(data: []byte) -> Which_File_Type {
case s[6:10] == "JFIF", s[6:10] == "Exif":
return .JPEG
case s[:3] == "\xff\xd8\xff":
switch s[4] {
switch s[3] {
case 0xdb, 0xee, 0xe1, 0xe0:
return .JPEG
}
+1 -129
View File
@@ -396,132 +396,4 @@ exif :: proc(c: image.PNG_Chunk) -> (res: Exif, ok: bool) {
General helper functions
*/
compute_buffer_size :: image.compute_buffer_size
/*
PNG save helpers
*/
when false {
make_chunk :: proc(c: any, t: Chunk_Type) -> (res: Chunk) {
data: []u8
if v, ok := c.([]u8); ok {
data = v
} else {
data = mem.any_to_bytes(c)
}
res.header.length = u32be(len(data))
res.header.type = t
res.data = data
// CRC the type
crc := hash.crc32(mem.any_to_bytes(res.header.type))
// Extend the CRC with the data
res.crc = u32be(hash.crc32(data, crc))
return
}
write_chunk :: proc(fd: os.Handle, chunk: Chunk) {
c := chunk
// Write length + type
os.write_ptr(fd, &c.header, 8)
// Write data
os.write_ptr(fd, mem.raw_data(c.data), int(c.header.length))
// Write CRC32
os.write_ptr(fd, &c.crc, 4)
}
write_image_as_png :: proc(filename: string, image: Image) -> (err: Error) {
profiler.timed_proc()
using image
using os
flags: int = O_WRONLY|O_CREATE|O_TRUNC
if len(image.pixels) == 0 || len(image.pixels) < image.width * image.height * int(image.channels) {
return .Invalid_Image_Dimensions
}
mode: int = 0
when ODIN_OS == .Linux || ODIN_OS == .Darwin {
// NOTE(justasd): 644 (owner read, write; group read; others read)
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
}
fd, fderr := open(filename, flags, mode)
if fderr != nil {
return .Cannot_Open_File
}
defer close(fd)
magic := Signature
write_ptr(fd, &magic, 8)
ihdr := IHDR{
width = u32be(width),
height = u32be(height),
bit_depth = depth,
compression_method = 0,
filter_method = 0,
interlace_method = .None,
}
switch channels {
case 1: ihdr.color_type = Color_Type{}
case 2: ihdr.color_type = Color_Type{.Alpha}
case 3: ihdr.color_type = Color_Type{.Color}
case 4: ihdr.color_type = Color_Type{.Color, .Alpha}
case:// Unhandled
return .Unknown_Color_Type
}
h := make_chunk(ihdr, .IHDR)
write_chunk(fd, h)
bytes_needed := width * height * int(channels) + height
filter_bytes := mem.make_dynamic_array_len_cap([dynamic]u8, bytes_needed, bytes_needed, context.allocator)
defer delete(filter_bytes)
i := 0; j := 0
// Add a filter byte 0 per pixel row
for y := 0; y < height; y += 1 {
filter_bytes[j] = 0; j += 1
for x := 0; x < width; x += 1 {
for z := 0; z < channels; z += 1 {
filter_bytes[j+z] = image.pixels[i+z]
}
i += channels; j += channels
}
}
assert(j == bytes_needed)
a: []u8 = filter_bytes[:]
out_buf: ^[dynamic]u8
defer free(out_buf)
ctx := zlib.ZLIB_Context{
in_buf = &a,
out_buf = out_buf,
}
err = zlib.write_zlib_stream_from_memory(&ctx)
b: []u8
if err == nil {
b = ctx.out_buf[:]
} else {
return err
}
idat := make_chunk(b, .IDAT)
write_chunk(fd, idat)
iend := make_chunk([]u8{}, .IEND)
write_chunk(fd, iend)
return nil
}
}
compute_buffer_size :: image.compute_buffer_size
+7 -3
View File
@@ -132,9 +132,13 @@ write_encoded_rune :: proc(w: Writer, r: rune, write_quote := true, n_written: ^
buf: [2]byte
s := strconv.append_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil)
switch len(s) {
case 0: write_string(w, "00", &n) or_return
case 1: write_byte(w, '0', &n) or_return
case 2: write_string(w, s, &n) or_return
case 0:
write_string(w, "00", &n) or_return
case 1:
write_byte(w, '0', &n) or_return
fallthrough
case 2:
write_string(w, s, &n) or_return
}
} else {
write_rune(w, r, &n) or_return
+66 -18
View File
@@ -417,6 +417,13 @@ adjugate :: proc{
matrix4x4_adjugate,
}
cofactor :: proc{
matrix1x1_cofactor,
matrix2x2_cofactor,
matrix3x3_cofactor,
matrix4x4_cofactor,
}
inverse_transpose :: proc{
matrix1x1_inverse_transpose,
matrix2x2_inverse_transpose,
@@ -479,9 +486,9 @@ matrix3x3_determinant :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (det: T) #
}
@(require_results)
matrix4x4_determinant :: proc "contextless" (m: $M/matrix[4, 4]$T) -> (det: T) #no_bounds_check {
a := adjugate(m)
c := cofactor(m)
for i in 0..<4 {
det += m[0, i] * a[0, i]
det += m[0, i] * c[0, i]
}
return
}
@@ -497,6 +504,47 @@ matrix1x1_adjugate :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) #no_bo
@(require_results)
matrix2x2_adjugate :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) #no_bounds_check {
y[0, 0] = +x[1, 1]
y[0, 1] = -x[0, 1]
y[1, 0] = -x[1, 0]
y[1, 1] = +x[0, 0]
return
}
@(require_results)
matrix3x3_adjugate :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
y[1, 0] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
y[2, 0] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
y[0, 1] = -(m[0, 1] * m[2, 2] - m[2, 1] * m[0, 2])
y[1, 1] = +(m[0, 0] * m[2, 2] - m[2, 0] * m[0, 2])
y[2, 1] = -(m[0, 0] * m[2, 1] - m[2, 0] * m[0, 1])
y[0, 2] = +(m[0, 1] * m[1, 2] - m[1, 1] * m[0, 2])
y[1, 2] = -(m[0, 0] * m[1, 2] - m[1, 0] * m[0, 2])
y[2, 2] = +(m[0, 0] * m[1, 1] - m[1, 0] * m[0, 1])
return
}
@(require_results)
matrix4x4_adjugate :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
for i in 0..<4 {
for j in 0..<4 {
sign: T = 1 if (i + j) % 2 == 0 else -1
y[i, j] = sign * matrix_minor(x, j, i)
}
}
return
}
@(require_results)
matrix1x1_cofactor :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) #no_bounds_check {
y = x
return
}
@(require_results)
matrix2x2_cofactor :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) #no_bounds_check {
y[0, 0] = +x[1, 1]
y[0, 1] = -x[1, 0]
y[1, 0] = -x[0, 1]
@@ -505,7 +553,7 @@ matrix2x2_adjugate :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) #no_bo
}
@(require_results)
matrix3x3_adjugate :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
matrix3x3_cofactor :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
y[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
y[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
@@ -520,7 +568,7 @@ matrix3x3_adjugate :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) #no_bo
@(require_results)
matrix4x4_adjugate :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
matrix4x4_cofactor :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
for i in 0..<4 {
for j in 0..<4 {
sign: T = 1 if (i + j) % 2 == 0 else -1
@@ -556,19 +604,19 @@ matrix2x2_inverse_transpose :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y:
@(require_results)
matrix3x3_inverse_transpose :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
a := adjugate(x)
c := cofactor(x)
d := determinant(x)
when intrinsics.type_is_integer(T) {
for i in 0..<3 {
for j in 0..<3 {
y[i, j] = a[i, j] / d
y[i, j] = c[i, j] / d
}
}
} else {
id := 1/d
for i in 0..<3 {
for j in 0..<3 {
y[i, j] = a[i, j] * id
y[i, j] = c[i, j] * id
}
}
}
@@ -577,22 +625,22 @@ matrix3x3_inverse_transpose :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y:
@(require_results)
matrix4x4_inverse_transpose :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
a := adjugate(x)
c := cofactor(x)
d: T
for i in 0..<4 {
d += x[0, i] * a[0, i]
d += x[0, i] * c[0, i]
}
when intrinsics.type_is_integer(T) {
for i in 0..<4 {
for j in 0..<4 {
y[i, j] = a[i, j] / d
y[i, j] = c[i, j] / d
}
}
} else {
id := 1/d
for i in 0..<4 {
for j in 0..<4 {
y[i, j] = a[i, j] * id
y[i, j] = c[i, j] * id
}
}
}
@@ -625,19 +673,19 @@ matrix2x2_inverse :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) #no_bou
@(require_results)
matrix3x3_inverse :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
a := adjugate(x)
c := cofactor(x)
d := determinant(x)
when intrinsics.type_is_integer(T) {
for i in 0..<3 {
for j in 0..<3 {
y[i, j] = a[j, i] / d
y[i, j] = c[j, i] / d
}
}
} else {
id := 1/d
for i in 0..<3 {
for j in 0..<3 {
y[i, j] = a[j, i] * id
y[i, j] = c[j, i] * id
}
}
}
@@ -646,22 +694,22 @@ matrix3x3_inverse :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bou
@(require_results)
matrix4x4_inverse :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
a := adjugate(x)
c := cofactor(x)
d: T
for i in 0..<4 {
d += x[0, i] * a[0, i]
d += x[0, i] * c[0, i]
}
when intrinsics.type_is_integer(T) {
for i in 0..<4 {
for j in 0..<4 {
y[i, j] = a[j, i] / d
y[i, j] = c[j, i] / d
}
}
} else {
id := 1/d
for i in 0..<4 {
for j in 0..<4 {
y[i, j] = a[j, i] * id
y[i, j] = c[j, i] * id
}
}
}
+82 -18
View File
@@ -473,6 +473,22 @@ floor :: proc{
@(require_results) floor_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {floor(x.x), floor(x.y), floor(x.z)} }
@(require_results) floor_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {floor(x.x), floor(x.y), floor(x.z), floor(x.w)} }
trunc :: proc{
trunc_f32,
trunc_f64,
trunc_vec2,
trunc_vec3,
trunc_vec4,
trunc_dvec2,
trunc_dvec3,
trunc_dvec4,
}
@(require_results) trunc_vec2 :: proc "c" (x: vec2) -> vec2 { return {trunc(x.x), trunc(x.y)} }
@(require_results) trunc_vec3 :: proc "c" (x: vec3) -> vec3 { return {trunc(x.x), trunc(x.y), trunc(x.z)} }
@(require_results) trunc_vec4 :: proc "c" (x: vec4) -> vec4 { return {trunc(x.x), trunc(x.y), trunc(x.z), trunc(x.w)} }
@(require_results) trunc_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {trunc(x.x), trunc(x.y)} }
@(require_results) trunc_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {trunc(x.x), trunc(x.y), trunc(x.z)} }
@(require_results) trunc_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {trunc(x.x), trunc(x.y), trunc(x.z), trunc(x.w)} }
round :: proc{
@@ -1866,6 +1882,13 @@ adjugate :: proc{
adjugate_matrix4x4,
}
cofactor :: proc{
cofactor_matrix1x1,
cofactor_matrix2x2,
cofactor_matrix3x3,
cofactor_matrix4x4,
}
inverse_transpose :: proc{
inverse_transpose_matrix1x1,
inverse_transpose_matrix2x2,
@@ -1928,9 +1951,9 @@ determinant_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (det: T) {
}
@(require_results)
determinant_matrix4x4 :: proc "contextless" (m: $M/matrix[4, 4]$T) -> (det: T) {
a := adjugate(m)
c := cofactor(m)
#no_bounds_check for i in 0..<4 {
det += m[0, i] * a[0, i]
det += m[0, i] * c[0, i]
}
return
}
@@ -1946,6 +1969,47 @@ adjugate_matrix1x1 :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
@(require_results)
adjugate_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
y[0, 0] = +x[1, 1]
y[0, 1] = -x[0, 1]
y[1, 0] = -x[1, 0]
y[1, 1] = +x[0, 0]
return
}
@(require_results)
adjugate_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
y[1, 0] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
y[2, 0] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
y[0, 1] = -(m[0, 1] * m[2, 2] - m[2, 1] * m[0, 2])
y[1, 1] = +(m[0, 0] * m[2, 2] - m[2, 0] * m[0, 2])
y[2, 1] = -(m[0, 0] * m[2, 1] - m[2, 0] * m[0, 1])
y[0, 2] = +(m[0, 1] * m[1, 2] - m[1, 1] * m[0, 2])
y[1, 2] = -(m[0, 0] * m[1, 2] - m[1, 0] * m[0, 2])
y[2, 2] = +(m[0, 0] * m[1, 1] - m[1, 0] * m[0, 1])
return
}
@(require_results)
adjugate_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
for i in 0..<4 {
for j in 0..<4 {
sign: T = 1 if (i + j) % 2 == 0 else -1
y[i, j] = sign * matrix_minor(x, j, i)
}
}
return
}
@(require_results)
cofactor_matrix1x1 :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
y = x
return
}
@(require_results)
cofactor_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
y[0, 0] = +x[1, 1]
y[0, 1] = -x[1, 0]
y[1, 0] = -x[0, 1]
@@ -1954,7 +2018,7 @@ adjugate_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
}
@(require_results)
adjugate_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
cofactor_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
y[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
y[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
@@ -1969,7 +2033,7 @@ adjugate_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
@(require_results)
adjugate_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
cofactor_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
for i in 0..<4 {
for j in 0..<4 {
sign: T = 1 if (i + j) % 2 == 0 else -1
@@ -2005,19 +2069,19 @@ inverse_transpose_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y:
@(require_results)
inverse_transpose_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
a := adjugate(x)
c := cofactor(x)
d := determinant(x)
when intrinsics.type_is_integer(T) {
for i in 0..<3 {
for j in 0..<3 {
y[i, j] = a[i, j] / d
y[i, j] = c[i, j] / d
}
}
} else {
id := 1/d
for i in 0..<3 {
for j in 0..<3 {
y[i, j] = a[i, j] * id
y[i, j] = c[i, j] * id
}
}
}
@@ -2026,22 +2090,22 @@ inverse_transpose_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y:
@(require_results)
inverse_transpose_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
a := adjugate(x)
c := cofactor(x)
d: T
for i in 0..<4 {
d += x[0, i] * a[0, i]
d += x[0, i] * c[0, i]
}
when intrinsics.type_is_integer(T) {
for i in 0..<4 {
for j in 0..<4 {
y[i, j] = a[i, j] / d
y[i, j] = c[i, j] / d
}
}
} else {
id := 1/d
for i in 0..<4 {
for j in 0..<4 {
y[i, j] = a[i, j] * id
y[i, j] = c[i, j] * id
}
}
}
@@ -2074,19 +2138,19 @@ inverse_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
@(require_results)
inverse_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
a := adjugate(x)
c := cofactor(x)
d := determinant(x)
when intrinsics.type_is_integer(T) {
for i in 0..<3 {
for j in 0..<3 {
y[i, j] = a[j, i] / d
y[i, j] = c[j, i] / d
}
}
} else {
id := 1/d
for i in 0..<3 {
for j in 0..<3 {
y[i, j] = a[j, i] * id
y[i, j] = c[j, i] * id
}
}
}
@@ -2095,22 +2159,22 @@ inverse_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bou
@(require_results)
inverse_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
a := adjugate(x)
c := cofactor(x)
d: T
for i in 0..<4 {
d += x[0, i] * a[0, i]
d += x[0, i] * c[0, i]
}
when intrinsics.type_is_integer(T) {
for i in 0..<4 {
for j in 0..<4 {
y[i, j] = a[j, i] / d
y[i, j] = c[j, i] / d
}
}
} else {
id := 1/d
for i in 0..<4 {
for j in 0..<4 {
y[i, j] = a[j, i] * id
y[i, j] = c[j, i] * id
}
}
}
@@ -23,6 +23,7 @@ import "core:math"
@(require_results) exp2_f32 :: proc "c" (x: f32) -> f32 { return math.pow(f32(2), x) }
@(require_results) sign_f32 :: proc "c" (x: f32) -> f32 { return math.sign(x) }
@(require_results) floor_f32 :: proc "c" (x: f32) -> f32 { return math.floor(x) }
@(require_results) trunc_f32 :: proc "c" (x: f32) -> f32 { return math.trunc(x) }
@(require_results) round_f32 :: proc "c" (x: f32) -> f32 { return math.round(x) }
@(require_results) ceil_f32 :: proc "c" (x: f32) -> f32 { return math.ceil(x) }
@(require_results) mod_f32 :: proc "c" (x, y: f32) -> f32 { return math.mod(x, y) }
@@ -55,6 +56,7 @@ fract_f32 :: proc "c" (x: f32) -> f32 {
@(require_results) exp2_f64 :: proc "c" (x: f64) -> f64 { return math.pow(f64(2), x) }
@(require_results) sign_f64 :: proc "c" (x: f64) -> f64 { return math.sign(x) }
@(require_results) floor_f64 :: proc "c" (x: f64) -> f64 { return math.floor(x) }
@(require_results) trunc_f64 :: proc "c" (x: f64) -> f64 { return math.trunc(x) }
@(require_results) round_f64 :: proc "c" (x: f64) -> f64 { return math.round(x) }
@(require_results) ceil_f64 :: proc "c" (x: f64) -> f64 { return math.ceil(x) }
@(require_results) mod_f64 :: proc "c" (x, y: f64) -> f64 { return math.mod(x, y) }
+66 -18
View File
@@ -1514,6 +1514,13 @@ adjugate :: proc{
adjugate_matrix4x4,
}
cofactor :: proc{
cofactor_matrix1x1,
cofactor_matrix2x2,
cofactor_matrix3x3,
cofactor_matrix4x4,
}
inverse_transpose :: proc{
inverse_transpose_matrix1x1,
inverse_transpose_matrix2x2,
@@ -1568,9 +1575,9 @@ determinant_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (det: T) {
}
@(require_results)
determinant_matrix4x4 :: proc "contextless" (m: $M/matrix[4, 4]$T) -> (det: T) {
a := adjugate(m)
c := cofactor(m)
#no_bounds_check for i in 0..<4 {
det += m[0, i] * a[0, i]
det += m[0, i] * c[0, i]
}
return
}
@@ -1586,6 +1593,47 @@ adjugate_matrix1x1 :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
@(require_results)
adjugate_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
y[0, 0] = +x[1, 1]
y[0, 1] = -x[0, 1]
y[1, 0] = -x[1, 0]
y[1, 1] = +x[0, 0]
return
}
@(require_results)
adjugate_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
y[1, 0] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
y[2, 0] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
y[0, 1] = -(m[0, 1] * m[2, 2] - m[2, 1] * m[0, 2])
y[1, 1] = +(m[0, 0] * m[2, 2] - m[2, 0] * m[0, 2])
y[2, 1] = -(m[0, 0] * m[2, 1] - m[2, 0] * m[0, 1])
y[0, 2] = +(m[0, 1] * m[1, 2] - m[1, 1] * m[0, 2])
y[1, 2] = -(m[0, 0] * m[1, 2] - m[1, 0] * m[0, 2])
y[2, 2] = +(m[0, 0] * m[1, 1] - m[1, 0] * m[0, 1])
return
}
@(require_results)
adjugate_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
for i in 0..<4 {
for j in 0..<4 {
sign: T = 1 if (i + j) % 2 == 0 else -1
y[i, j] = sign * matrix_minor(x, j, i)
}
}
return
}
@(require_results)
cofactor_matrix1x1 :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
y = x
return
}
@(require_results)
cofactor_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
y[0, 0] = +x[1, 1]
y[0, 1] = -x[1, 0]
y[1, 0] = -x[0, 1]
@@ -1594,7 +1642,7 @@ adjugate_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
}
@(require_results)
adjugate_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
cofactor_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
y[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
y[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
@@ -1609,7 +1657,7 @@ adjugate_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
@(require_results)
adjugate_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
cofactor_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
for i in 0..<4 {
for j in 0..<4 {
sign: T = 1 if (i + j) % 2 == 0 else -1
@@ -1645,19 +1693,19 @@ inverse_transpose_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y:
@(require_results)
inverse_transpose_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
a := adjugate(x)
c := cofactor(x)
d := determinant(x)
when intrinsics.type_is_integer(T) {
for i in 0..<3 {
for j in 0..<3 {
y[i, j] = a[i, j] / d
y[i, j] = c[i, j] / d
}
}
} else {
id := 1/d
for i in 0..<3 {
for j in 0..<3 {
y[i, j] = a[i, j] * id
y[i, j] = c[i, j] * id
}
}
}
@@ -1666,22 +1714,22 @@ inverse_transpose_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y:
@(require_results)
inverse_transpose_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
a := adjugate(x)
c := cofactor(x)
d: T
for i in 0..<4 {
d += x[0, i] * a[0, i]
d += x[0, i] * c[0, i]
}
when intrinsics.type_is_integer(T) {
for i in 0..<4 {
for j in 0..<4 {
y[i, j] = a[i, j] / d
y[i, j] = c[i, j] / d
}
}
} else {
id := 1/d
for i in 0..<4 {
for j in 0..<4 {
y[i, j] = a[i, j] * id
y[i, j] = c[i, j] * id
}
}
}
@@ -1714,19 +1762,19 @@ inverse_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
@(require_results)
inverse_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
a := adjugate(x)
c := cofactor(x)
d := determinant(x)
when intrinsics.type_is_integer(T) {
for i in 0..<3 {
for j in 0..<3 {
y[i, j] = a[j, i] / d
y[i, j] = c[j, i] / d
}
}
} else {
id := 1/d
for i in 0..<3 {
for j in 0..<3 {
y[i, j] = a[j, i] * id
y[i, j] = c[j, i] * id
}
}
}
@@ -1735,22 +1783,22 @@ inverse_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bou
@(require_results)
inverse_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
a := adjugate(x)
c := cofactor(x)
d: T
for i in 0..<4 {
d += x[0, i] * a[0, i]
d += x[0, i] * c[0, i]
}
when intrinsics.type_is_integer(T) {
for i in 0..<4 {
for j in 0..<4 {
y[i, j] = a[j, i] / d
y[i, j] = c[j, i] / d
}
}
} else {
id := 1/d
for i in 0..<4 {
for j in 0..<4 {
y[i, j] = a[j, i] * id
y[i, j] = c[j, i] * id
}
}
}
-24
View File
@@ -29,30 +29,6 @@ Reset the seed used by the context.random_generator.
Inputs:
- seed: The seed value
Example:
import "core:math/rand"
import "core:fmt"
set_global_seed_example :: proc() {
rand.set_global_seed(1)
fmt.println(rand.uint64())
}
Possible Output:
10
*/
@(deprecated="Prefer `rand.reset`")
set_global_seed :: proc(seed: u64) {
runtime.random_generator_reset_u64(context.random_generator, seed)
}
/*
Reset the seed used by the context.random_generator.
Inputs:
- seed: The seed value
Example:
import "core:math/rand"
import "core:fmt"
-23
View File
@@ -140,14 +140,6 @@ arena_init :: proc(a: ^Arena, data: []byte) {
a.temp_count = 0
}
@(deprecated="prefer 'mem.arena_init'")
init_arena :: proc(a: ^Arena, data: []byte) {
a.data = data
a.offset = 0
a.peak_used = 0
a.temp_count = 0
}
/*
Allocate memory from an arena.
@@ -786,14 +778,6 @@ stack_init :: proc(s: ^Stack, data: []byte) {
s.peak_used = 0
}
@(deprecated="prefer 'mem.stack_init'")
init_stack :: proc(s: ^Stack, data: []byte) {
s.data = data
s.prev_offset = 0
s.curr_offset = 0
s.peak_used = 0
}
/*
Allocate memory from stack.
@@ -1162,13 +1146,6 @@ small_stack_init :: proc(s: ^Small_Stack, data: []byte) {
s.peak_used = 0
}
@(deprecated="prefer 'small_stack_init'")
init_small_stack :: proc(s: ^Small_Stack, data: []byte) {
s.data = data
s.offset = 0
s.peak_used = 0
}
/*
Small stack allocator.
+1 -8
View File
@@ -685,11 +685,4 @@ calc_padding_with_header :: proc "contextless" (ptr: uintptr, align: uintptr, he
}
}
return int(padding)
}
@(require_results, deprecated="prefer 'slice.clone'")
clone_slice :: proc(slice: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> (new_slice: T) {
new_slice, _ = make(T, len(slice), allocator, loc)
runtime.copy(new_slice, slice)
return new_slice
}
}
+2 -1
View File
@@ -204,8 +204,9 @@ arena_free_all :: proc(arena: ^Arena, loc := #caller_location) {
}
// Zero the first block's memory
if arena.curr_block != nil {
mem.zero(arena.curr_block.base, int(arena.curr_block.used))
curr_block_used := int(arena.curr_block.used)
arena.curr_block.used = 0
mem.zero(arena.curr_block.base, curr_block_used)
}
arena.total_used = 0
case .Static, .Buffer:
+1 -1
View File
@@ -115,7 +115,7 @@ open :: proc(name: string, flags := File_Flags{.Read}, perm := 0o777) -> (^File,
@(require_results)
new_file :: proc(handle: uintptr, name: string) -> ^File {
file, err := _new_file(handle, name)
file, err := _new_file(handle, name, file_allocator())
if err != nil {
panic(error_string(err))
}
+27 -54
View File
@@ -39,37 +39,23 @@ _stderr := File{
@init
_standard_stream_init :: proc() {
@static stdin_impl := File_Impl {
name = "/proc/self/fd/0",
fd = 0,
new_std :: proc(impl: ^File_Impl, fd: linux.Fd, name: string) -> ^File {
impl.file.impl = impl
impl.fd = linux.Fd(fd)
impl.allocator = runtime.nil_allocator()
impl.name = name
impl.file.stream = {
data = impl,
procedure = _file_stream_proc,
}
impl.file.fstat = _fstat
return &impl.file
}
@static stdout_impl := File_Impl {
name = "/proc/self/fd/1",
fd = 1,
}
@static stderr_impl := File_Impl {
name = "/proc/self/fd/2",
fd = 2,
}
stdin_impl.allocator = file_allocator()
stdout_impl.allocator = file_allocator()
stderr_impl.allocator = file_allocator()
_stdin.impl = &stdin_impl
_stdout.impl = &stdout_impl
_stderr.impl = &stderr_impl
// cannot define these initially because cyclic reference
_stdin.stream.data = &stdin_impl
_stdout.stream.data = &stdout_impl
_stderr.stream.data = &stderr_impl
stdin = &_stdin
stdout = &_stdout
stderr = &_stderr
@(static) files: [3]File_Impl
stdin = new_std(&files[0], 0, "/proc/self/fd/0")
stdout = new_std(&files[1], 1, "/proc/self/fd/1")
stderr = new_std(&files[2], 2, "/proc/self/fd/2")
}
_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
@@ -80,6 +66,9 @@ _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Err
// terminal would be incredibly rare. This has no effect on files while
// allowing us to open serial devices.
sys_flags: linux.Open_Flags = {.NOCTTY, .CLOEXEC}
when size_of(rawptr) == 4 {
sys_flags += {.LARGEFILE}
}
switch flags & (O_RDONLY|O_WRONLY|O_RDWR) {
case O_RDONLY:
case O_WRONLY: sys_flags += {.WRONLY}
@@ -97,18 +86,18 @@ _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Err
return nil, _get_platform_error(errno)
}
return _new_file(uintptr(fd), name)
return _new_file(uintptr(fd), name, file_allocator())
}
_new_file :: proc(fd: uintptr, _: string = "") -> (f: ^File, err: Error) {
impl := new(File_Impl, file_allocator()) or_return
_new_file :: proc(fd: uintptr, _: string, allocator: runtime.Allocator) -> (f: ^File, err: Error) {
impl := new(File_Impl, allocator) or_return
defer if err != nil {
free(impl, file_allocator())
free(impl, allocator)
}
impl.file.impl = impl
impl.fd = linux.Fd(fd)
impl.allocator = file_allocator()
impl.name = _get_full_path(impl.fd, file_allocator()) or_return
impl.allocator = allocator
impl.name = _get_full_path(impl.fd, impl.allocator) or_return
impl.file.stream = {
data = impl,
procedure = _file_stream_proc,
@@ -272,28 +261,12 @@ _truncate :: proc(f: ^File, size: i64) -> Error {
}
_remove :: proc(name: string) -> Error {
is_dir_fd :: proc(fd: linux.Fd) -> bool {
s: linux.Stat
if linux.fstat(fd, &s) != .NONE {
return false
}
return linux.S_ISDIR(s.mode)
}
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
fd, errno := linux.open(name_cstr, {.NOFOLLOW})
#partial switch (errno) {
case .ELOOP:
/* symlink */
case .NONE:
defer linux.close(fd)
if is_dir_fd(fd) {
return _get_platform_error(linux.rmdir(name_cstr))
}
case:
return _get_platform_error(errno)
if fd, errno := linux.open(name_cstr, _OPENDIR_FLAGS + {.NOFOLLOW}); errno == .NONE {
linux.close(fd)
return _get_platform_error(linux.rmdir(name_cstr))
}
return _get_platform_error(linux.unlink(name_cstr))
+29 -20
View File
@@ -21,23 +21,29 @@ File_Impl :: struct {
name: string,
cname: cstring,
fd: posix.FD,
allocator: runtime.Allocator,
}
@(init)
init_std_files :: proc() {
// NOTE: is this (paths) also the case on non darwin?
new_std :: proc(impl: ^File_Impl, fd: posix.FD, name: cstring) -> ^File {
impl.file.impl = impl
impl.fd = fd
impl.allocator = runtime.nil_allocator()
impl.cname = name
impl.name = string(name)
impl.file.stream = {
data = impl,
procedure = _file_stream_proc,
}
impl.file.fstat = _fstat
return &impl.file
}
stdin = __new_file(posix.STDIN_FILENO)
(^File_Impl)(stdin.impl).name = "/dev/stdin"
(^File_Impl)(stdin.impl).cname = "/dev/stdin"
stdout = __new_file(posix.STDIN_FILENO)
(^File_Impl)(stdout.impl).name = "/dev/stdout"
(^File_Impl)(stdout.impl).cname = "/dev/stdout"
stderr = __new_file(posix.STDIN_FILENO)
(^File_Impl)(stderr.impl).name = "/dev/stderr"
(^File_Impl)(stderr.impl).cname = "/dev/stderr"
@(static) files: [3]File_Impl
stdin = new_std(&files[0], posix.STDIN_FILENO, "/dev/stdin")
stdout = new_std(&files[1], posix.STDOUT_FILENO, "/dev/stdout")
stderr = new_std(&files[2], posix.STDERR_FILENO, "/dev/stderr")
}
_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
@@ -72,10 +78,10 @@ _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Err
return
}
return _new_file(uintptr(fd), name)
return _new_file(uintptr(fd), name, file_allocator())
}
_new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
_new_file :: proc(handle: uintptr, name: string, allocator: runtime.Allocator) -> (f: ^File, err: Error) {
if name == "" {
err = .Invalid_Path
return
@@ -84,10 +90,10 @@ _new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
return
}
crname := _posix_absolute_path(posix.FD(handle), name, file_allocator()) or_return
crname := _posix_absolute_path(posix.FD(handle), name, allocator) or_return
rname := string(crname)
f = __new_file(posix.FD(handle))
f = __new_file(posix.FD(handle), allocator)
impl := (^File_Impl)(f.impl)
impl.name = rname
impl.cname = crname
@@ -95,10 +101,11 @@ _new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
return f, nil
}
__new_file :: proc(handle: posix.FD) -> ^File {
impl := new(File_Impl, file_allocator())
__new_file :: proc(handle: posix.FD, allocator: runtime.Allocator) -> ^File {
impl := new(File_Impl, allocator)
impl.file.impl = impl
impl.fd = posix.FD(handle)
impl.allocator = allocator
impl.file.stream = {
data = impl,
procedure = _file_stream_proc,
@@ -114,8 +121,10 @@ _close :: proc(f: ^File_Impl) -> (err: Error) {
err = _get_platform_error()
}
delete(f.cname, file_allocator())
free(f, file_allocator())
allocator := f.allocator
delete(f.cname, allocator)
free(f, allocator)
return
}
+37 -16
View File
@@ -44,17 +44,38 @@ File_Impl :: struct {
@(init)
init_std_files :: proc() {
stdin = new_file(uintptr(win32.GetStdHandle(win32.STD_INPUT_HANDLE)), "<stdin>")
stdout = new_file(uintptr(win32.GetStdHandle(win32.STD_OUTPUT_HANDLE)), "<stdout>")
stderr = new_file(uintptr(win32.GetStdHandle(win32.STD_ERROR_HANDLE)), "<stderr>")
}
@(fini)
fini_std_files :: proc() {
_destroy((^File_Impl)(stdin.impl))
_destroy((^File_Impl)(stdout.impl))
_destroy((^File_Impl)(stderr.impl))
}
new_std :: proc(impl: ^File_Impl, code: u32, name: string) -> ^File {
impl.file.impl = impl
impl.allocator = runtime.nil_allocator()
impl.fd = win32.GetStdHandle(code)
impl.name = name
impl.wname = nil
handle := _handle(&impl.file)
kind := File_Impl_Kind.File
if m: u32; win32.GetConsoleMode(handle, &m) {
kind = .Console
}
if win32.GetFileType(handle) == win32.FILE_TYPE_PIPE {
kind = .Pipe
}
impl.kind = kind
impl.file.stream = {
data = impl,
procedure = _file_stream_proc,
}
impl.file.fstat = _fstat
return &impl.file
}
@(static) files: [3]File_Impl
stdin = new_std(&files[0], win32.STD_INPUT_HANDLE, "<stdin>")
stdout = new_std(&files[1], win32.STD_OUTPUT_HANDLE, "<stdout>")
stderr = new_std(&files[2], win32.STD_ERROR_HANDLE, "<stderr>")
}
_handle :: proc(f: ^File) -> win32.HANDLE {
return win32.HANDLE(_fd(f))
@@ -132,21 +153,21 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: int) -> (handle: u
_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
flags := flags if flags != nil else {.Read}
handle := _open_internal(name, flags, perm) or_return
return _new_file(handle, name)
return _new_file(handle, name, file_allocator())
}
_new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
_new_file :: proc(handle: uintptr, name: string, allocator: runtime.Allocator) -> (f: ^File, err: Error) {
if handle == INVALID_HANDLE {
return
}
impl := new(File_Impl, file_allocator()) or_return
impl := new(File_Impl, allocator) or_return
defer if err != nil {
free(impl, file_allocator())
free(impl, allocator)
}
impl.file.impl = impl
impl.allocator = file_allocator()
impl.allocator = allocator
impl.fd = rawptr(handle)
impl.name = clone_string(name, impl.allocator) or_return
impl.wname = win32_utf8_to_wstring(name, impl.allocator) or_return
@@ -180,7 +201,7 @@ _open_buffered :: proc(name: string, buffer_size: uint, flags := File_Flags{.Rea
}
_new_file_buffered :: proc(handle: uintptr, name: string, buffer_size: uint) -> (f: ^File, err: Error) {
f, err = _new_file(handle, name)
f, err = _new_file(handle, name, file_allocator())
if f != nil && err == nil {
impl := (^File_Impl)(f.impl)
impl.r_buf = make([]byte, buffer_size, file_allocator())
+1 -1
View File
@@ -415,7 +415,7 @@ _region_resize :: proc(alloc: ^Allocation_Header, new_size: int, alloc_is_free_l
back_idx := -1
idx: u16
infinite: for {
for i := 0; i < len(region_iter.hdr.free_list); i += 1 {
for i := 0; i < int(region_iter.hdr.free_list_len); i += 1 {
idx = region_iter.hdr.free_list[i]
if _get_block_count(region_iter.memory[idx]) >= new_block_count {
break infinite
-2
View File
@@ -77,8 +77,6 @@ _mkdir_all :: proc(path: string, perm: int) -> Error {
}
_remove_all :: proc(path: string) -> Error {
DT_DIR :: 4
remove_all_dir :: proc(dfd: linux.Fd) -> Error {
n := 64
buf := make([]u8, n)
+2 -2
View File
@@ -10,8 +10,8 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
return nil, nil,_get_platform_error(errno)
}
r = _new_file(uintptr(fds[0])) or_return
w = _new_file(uintptr(fds[1])) or_return
r = _new_file(uintptr(fds[0]), "", file_allocator()) or_return
w = _new_file(uintptr(fds[1]), "", file_allocator()) or_return
return
}
+2 -2
View File
@@ -21,7 +21,7 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
return
}
r = __new_file(fds[0])
r = __new_file(fds[0], file_allocator())
ri := (^File_Impl)(r.impl)
rname := strings.builder_make(file_allocator())
@@ -31,7 +31,7 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
ri.name = strings.to_string(rname)
ri.cname = strings.to_cstring(&rname)
w = __new_file(fds[1])
w = __new_file(fds[1], file_allocator())
wi := (^File_Impl)(w.impl)
wname := strings.builder_make(file_allocator())
+10 -1
View File
@@ -290,12 +290,21 @@ process_open :: proc(pid: int, flags := Process_Open_Flags {}) -> (Process, Erro
return _process_open(pid, flags)
}
/*
OS-specific process attributes.
*/
Process_Attributes :: struct {
sys_attr: _Sys_Process_Attributes,
}
/*
The description of how a process should be created.
*/
Process_Desc :: struct {
// OS-specific attributes.
sys_attr: _Sys_Process_Attributes,
sys_attr: Process_Attributes,
// The working directory of the process. If the string has length 0, the
// working directory is assumed to be the current working directory of the
// current process.
+11 -36
View File
@@ -384,14 +384,6 @@ _Sys_Process_Attributes :: struct {}
@(private="package")
_process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
has_executable_permissions :: proc(fd: linux.Fd) -> bool {
backing: [48]u8
b := strings.builder_from_bytes(backing[:])
strings.write_string(&b, "/proc/self/fd/")
strings.write_int(&b, int(fd))
return linux.access(strings.to_cstring(&b), linux.X_OK) == .NONE
}
TEMP_ALLOCATOR_GUARD()
if len(desc.command) == 0 {
@@ -411,7 +403,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
}
// search PATH if just a plain name is provided
exe_fd: linux.Fd
exe_path: cstring
executable_name := desc.command[0]
if strings.index_byte(executable_name, '/') < 0 {
path_env := get_env("PATH", temp_allocator())
@@ -426,16 +418,11 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
strings.write_byte(&exe_builder, '/')
strings.write_string(&exe_builder, executable_name)
exe_path := strings.to_cstring(&exe_builder)
if exe_fd, errno = linux.openat(dir_fd, exe_path, {.PATH, .CLOEXEC}); errno != .NONE {
continue
exe_path = strings.to_cstring(&exe_builder)
if linux.access(exe_path, linux.X_OK) == .NONE {
found = true
break
}
if !has_executable_permissions(exe_fd) {
linux.close(exe_fd)
continue
}
found = true
break
}
if !found {
// check in cwd to match windows behavior
@@ -443,29 +430,18 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
strings.write_string(&exe_builder, "./")
strings.write_string(&exe_builder, executable_name)
exe_path := strings.to_cstring(&exe_builder)
if exe_fd, errno = linux.openat(dir_fd, exe_path, {.PATH, .CLOEXEC}); errno != .NONE {
exe_path = strings.to_cstring(&exe_builder)
if linux.access(exe_path, linux.X_OK) != .NONE {
return process, .Not_Exist
}
if !has_executable_permissions(exe_fd) {
linux.close(exe_fd)
return process, .Permission_Denied
}
}
} else {
exe_path := temp_cstring(executable_name) or_return
if exe_fd, errno = linux.openat(dir_fd, exe_path, {.PATH, .CLOEXEC}); errno != .NONE {
return process, _get_platform_error(errno)
}
if !has_executable_permissions(exe_fd) {
linux.close(exe_fd)
return process, .Permission_Denied
exe_path = temp_cstring(executable_name) or_return
if linux.access(exe_path, linux.X_OK) != .NONE {
return process, .Not_Exist
}
}
// At this point, we have an executable.
defer linux.close(exe_fd)
// args and environment need to be a list of cstrings
// that are terminated by a nil pointer.
cargs := make([]cstring, len(desc.command) + 1, temp_allocator()) or_return
@@ -492,7 +468,6 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
}
defer linux.close(child_pipe_fds[READ])
// TODO: This is the traditional textbook implementation with fork.
// A more efficient implementation with vfork:
//
@@ -573,7 +548,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno)
}
errno = linux.execveat(exe_fd, "", &cargs[0], env, {.AT_EMPTY_PATH})
errno = linux.execveat(dir_fd, exe_path, &cargs[0], env)
assert(errno != nil)
write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno)
}
+20 -3
View File
@@ -72,7 +72,11 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator: runt
ok := win32.GetFileAttributesExW(wname, win32.GetFileExInfoStandard, &fa)
if ok && fa.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
// Not a symlink
return _file_info_from_win32_file_attribute_data(&fa, name, allocator)
fi = _file_info_from_win32_file_attribute_data(&fa, name, allocator) or_return
if fi.type == .Undetermined {
fi.type = _file_type_from_create_file(wname, create_file_attributes)
}
return
}
err := 0 if ok else win32.GetLastError()
@@ -86,7 +90,11 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator: runt
}
win32.FindClose(sh)
return _file_info_from_win32_find_data(&fd, name, allocator)
fi = _file_info_from_win32_find_data(&fd, name, allocator) or_return
if fi.type == .Undetermined {
fi.type = _file_type_from_create_file(wname, create_file_attributes)
}
return
}
h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil)
@@ -194,6 +202,15 @@ file_type :: proc(h: win32.HANDLE) -> File_Type {
return .Undetermined
}
_file_type_from_create_file :: proc(wname: win32.wstring, create_file_attributes: u32) -> File_Type {
h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil)
if h == win32.INVALID_HANDLE_VALUE {
return .Undetermined
}
defer win32.CloseHandle(h)
return file_type(h)
}
_file_type_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (type: File_Type, mode: int) {
if file_attributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
mode |= 0o444
@@ -266,7 +283,7 @@ _file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HA
fi.name = basename(path)
fi.inode = u128(u64(d.nFileIndexHigh)<<32 + u64(d.nFileIndexLow))
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
type, mode := _file_type_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
type, mode := _file_type_mode_from_file_attributes(d.dwFileAttributes, h, 0)
fi.type = type
fi.mode |= mode
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
+2
View File
@@ -18,6 +18,8 @@ Example:
defer spall.context_destroy(&spall_ctx)
buffer_backing := make([]u8, spall.BUFFER_DEFAULT_SIZE)
defer delete(buffer_backing)
spall_buffer = spall.buffer_create(buffer_backing, u32(sync.current_thread_id()))
defer spall.buffer_destroy(&spall_ctx, &spall_buffer)
+33 -14
View File
@@ -48,22 +48,41 @@ to_type :: proc(buf: []u8, $T: typeid) -> (T, bool) #optional_ok {
}
/*
Turn a slice of one type, into a slice of another type.
Turn a slice of one type, into a slice of another type.
Only converts the type and length of the slice itself.
The length is rounded down to the nearest whole number of items.
Only converts the type and length of the slice itself.
The length is rounded down to the nearest whole number of items.
Example:
import "core:fmt"
import "core:slice"
i64s_as_i32s :: proc() {
large_items := []i64{1, 2, 3, 4}
small_items := slice.reinterpret([]i32, large_items)
assert(len(small_items) == 8)
fmt.println(large_items, "->", small_items)
}
bytes_as_i64s :: proc() {
small_items := [12]byte{}
small_items[0] = 1
small_items[8] = 2
large_items := slice.reinterpret([]i64, small_items[:])
assert(len(large_items) == 1) // only enough bytes to make 1 x i64; two would need at least 8 bytes.
fmt.println(small_items, "->", large_items)
}
reinterpret_example :: proc() {
i64s_as_i32s()
bytes_as_i64s()
}
Output:
[1, 2, 3, 4] -> [1, 0, 2, 0, 3, 0, 4, 0]
[1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0] -> [1]
```
large_items := []i64{1, 2, 3, 4}
small_items := slice.reinterpret([]i32, large_items)
assert(len(small_items) == 8)
```
```
small_items := []byte{1, 0, 0, 0, 0, 0, 0, 0,
2, 0, 0, 0}
large_items := slice.reinterpret([]i64, small_items)
assert(len(large_items) == 1) // only enough bytes to make 1 x i64; two would need at least 8 bytes.
```
*/
@(require_results)
reinterpret :: proc "contextless" ($T: typeid/[]$U, s: []$V) -> []U {
+1 -1
View File
@@ -67,7 +67,7 @@ atomic_mutex_unlock :: proc "contextless" (m: ^Atomic_Mutex) {
switch atomic_exchange_explicit(&m.state, .Unlocked, .Release) {
case .Unlocked:
unreachable()
// Kind of okay - unlocking while already unlocked.
case .Locked:
// Okay
case .Waiting:
+58 -36
View File
@@ -152,43 +152,65 @@ Errno :: enum i32 {
RDONLY flag is not present, because it has the value of 0, i.e. it is the
default, unless WRONLY or RDWR is specified.
*/
Open_Flags_Bits :: enum {
WRONLY = 0,
RDWR = 1,
CREAT = 6,
EXCL = 7,
NOCTTY = 8,
TRUNC = 9,
APPEND = 10,
NONBLOCK = 11,
DSYNC = 12,
ASYNC = 13,
DIRECT = 14,
LARGEFILE = 15,
DIRECTORY = 16,
NOFOLLOW = 17,
NOATIME = 18,
CLOEXEC = 19,
PATH = 21,
when ODIN_ARCH != .arm64 && ODIN_ARCH != .arm32 {
Open_Flags_Bits :: enum {
WRONLY = 0,
RDWR = 1,
CREAT = 6,
EXCL = 7,
NOCTTY = 8,
TRUNC = 9,
APPEND = 10,
NONBLOCK = 11,
DSYNC = 12,
ASYNC = 13,
DIRECT = 14,
LARGEFILE = 15,
DIRECTORY = 16,
NOFOLLOW = 17,
NOATIME = 18,
CLOEXEC = 19,
PATH = 21,
}
// https://github.com/torvalds/linux/blob/7367539ad4b0f8f9b396baf02110962333719a48/include/uapi/asm-generic/fcntl.h#L19
#assert(1 << uint(Open_Flags_Bits.WRONLY) == 0o0000000_1)
#assert(1 << uint(Open_Flags_Bits.RDWR) == 0o0000000_2)
#assert(1 << uint(Open_Flags_Bits.CREAT) == 0o00000_100)
#assert(1 << uint(Open_Flags_Bits.EXCL) == 0o00000_200)
#assert(1 << uint(Open_Flags_Bits.NOCTTY) == 0o00000_400)
#assert(1 << uint(Open_Flags_Bits.TRUNC) == 0o0000_1000)
#assert(1 << uint(Open_Flags_Bits.APPEND) == 0o0000_2000)
#assert(1 << uint(Open_Flags_Bits.NONBLOCK) == 0o0000_4000)
#assert(1 << uint(Open_Flags_Bits.DSYNC) == 0o000_10000)
#assert(1 << uint(Open_Flags_Bits.ASYNC) == 0o000_20000)
#assert(1 << uint(Open_Flags_Bits.DIRECT) == 0o000_40000)
#assert(1 << uint(Open_Flags_Bits.LARGEFILE) == 0o00_100000)
#assert(1 << uint(Open_Flags_Bits.DIRECTORY) == 0o00_200000)
#assert(1 << uint(Open_Flags_Bits.NOFOLLOW) == 0o00_400000)
#assert(1 << uint(Open_Flags_Bits.NOATIME) == 0o0_1000000)
#assert(1 << uint(Open_Flags_Bits.CLOEXEC) == 0o0_2000000)
#assert(1 << uint(Open_Flags_Bits.PATH) == 0o_10000000)
} else {
Open_Flags_Bits :: enum {
WRONLY = 0,
RDWR = 1,
CREAT = 6,
EXCL = 7,
NOCTTY = 8,
TRUNC = 9,
APPEND = 10,
NONBLOCK = 11,
DSYNC = 12,
ASYNC = 13,
DIRECTORY = 14,
NOFOLLOW = 15,
DIRECT = 16,
LARGEFILE = 17,
NOATIME = 18,
CLOEXEC = 19,
PATH = 21,
}
}
// https://github.com/torvalds/linux/blob/7367539ad4b0f8f9b396baf02110962333719a48/include/uapi/asm-generic/fcntl.h#L19
#assert(1 << uint(Open_Flags_Bits.WRONLY) == 0o0000000_1)
#assert(1 << uint(Open_Flags_Bits.RDWR) == 0o0000000_2)
#assert(1 << uint(Open_Flags_Bits.CREAT) == 0o00000_100)
#assert(1 << uint(Open_Flags_Bits.EXCL) == 0o00000_200)
#assert(1 << uint(Open_Flags_Bits.NOCTTY) == 0o00000_400)
#assert(1 << uint(Open_Flags_Bits.TRUNC) == 0o0000_1000)
#assert(1 << uint(Open_Flags_Bits.APPEND) == 0o0000_2000)
#assert(1 << uint(Open_Flags_Bits.NONBLOCK) == 0o0000_4000)
#assert(1 << uint(Open_Flags_Bits.DSYNC) == 0o000_10000)
#assert(1 << uint(Open_Flags_Bits.ASYNC) == 0o000_20000)
#assert(1 << uint(Open_Flags_Bits.DIRECT) == 0o000_40000)
#assert(1 << uint(Open_Flags_Bits.LARGEFILE) == 0o00_100000)
#assert(1 << uint(Open_Flags_Bits.DIRECTORY) == 0o00_200000)
#assert(1 << uint(Open_Flags_Bits.NOFOLLOW) == 0o00_400000)
#assert(1 << uint(Open_Flags_Bits.NOATIME) == 0o0_1000000)
#assert(1 << uint(Open_Flags_Bits.CLOEXEC) == 0o0_2000000)
#assert(1 << uint(Open_Flags_Bits.PATH) == 0o_10000000)
/*
Bits for FD_Flags bitset
+2 -2
View File
@@ -138,8 +138,8 @@ errno_unwrap :: proc {errno_unwrap2, errno_unwrap3}
when size_of(int) == 4 {
// xxx64 system calls take some parameters as pairs of ulongs rather than a single pointer
@(private)
compat64_arg_pair :: #force_inline proc "contextless" (a: i64) -> (hi: uint, lo: uint) {
no_sign := uint(a)
compat64_arg_pair :: #force_inline proc "contextless" (a: i64) -> (lo: uint, hi: uint) {
no_sign := u64(a)
hi = uint(no_sign >> 32)
lo = uint(no_sign & 0xffff_ffff)
return
+25 -15
View File
@@ -151,7 +151,8 @@ lseek :: proc "contextless" (fd: Fd, off: i64, whence: Seek_Whence) -> (i64, Err
return errno_unwrap(ret, i64)
} else {
result: i64 = ---
ret := syscall(SYS__llseek, fd, compat64_arg_pair(off), &result, whence)
lo, hi := compat64_arg_pair(off)
ret := syscall(SYS__llseek, fd, hi, lo, &result, whence)
return result, Errno(-ret)
}
}
@@ -251,7 +252,11 @@ ioctl :: proc "contextless" (fd: Fd, request: u32, arg: uintptr) -> (uintptr) {
Available since Linux 2.2.
*/
pread :: proc "contextless" (fd: Fd, buf: []u8, offset: i64) -> (int, Errno) {
ret := syscall(SYS_pread64, fd, raw_data(buf), len(buf), compat64_arg_pair(offset))
when ODIN_ARCH == .arm32 {
ret := syscall(SYS_pread64, fd, raw_data(buf), len(buf), 0, compat64_arg_pair(offset))
} else {
ret := syscall(SYS_pread64, fd, raw_data(buf), len(buf), compat64_arg_pair(offset))
}
return errno_unwrap(ret, int)
}
@@ -261,7 +266,11 @@ pread :: proc "contextless" (fd: Fd, buf: []u8, offset: i64) -> (int, Errno) {
Available since Linux 2.2.
*/
pwrite :: proc "contextless" (fd: Fd, buf: []u8, offset: i64) -> (int, Errno) {
ret := syscall(SYS_pwrite64, fd, raw_data(buf), len(buf), compat64_arg_pair(offset))
when ODIN_ARCH == .arm32 {
ret := syscall(SYS_pwrite64, fd, raw_data(buf), len(buf), 0, compat64_arg_pair(offset))
} else {
ret := syscall(SYS_pwrite64, fd, raw_data(buf), len(buf), compat64_arg_pair(offset))
}
return errno_unwrap(ret, int)
}
@@ -1127,7 +1136,10 @@ fdatasync :: proc "contextless" (fd: Fd) -> (Errno) {
On 32-bit architectures available since Linux 2.4.
*/
truncate :: proc "contextless" (name: cstring, length: i64) -> (Errno) {
when size_of(int) == 4 {
when ODIN_ARCH == .arm32 {
ret := syscall(SYS_truncate64, cast(rawptr) name, 0, compat64_arg_pair(length))
return Errno(-ret)
} else when size_of(int) == 4 {
ret := syscall(SYS_truncate64, cast(rawptr) name, compat64_arg_pair(length))
return Errno(-ret)
} else {
@@ -1141,7 +1153,10 @@ truncate :: proc "contextless" (name: cstring, length: i64) -> (Errno) {
On 32-bit architectures available since 2.4.
*/
ftruncate :: proc "contextless" (fd: Fd, length: i64) -> (Errno) {
when size_of(int) == 4 {
when ODIN_ARCH == .arm32 {
ret := syscall(SYS_ftruncate64, fd, 0, compat64_arg_pair(length))
return Errno(-ret)
} else when size_of(int) == 4 {
ret := syscall(SYS_ftruncate64, fd, compat64_arg_pair(length))
return Errno(-ret)
} else {
@@ -1952,10 +1967,10 @@ sigaltstack :: proc "contextless" (stack: ^Sig_Stack, old_stack: ^Sig_Stack) ->
*/
mknod :: proc "contextless" (name: cstring, mode: Mode, dev: Dev) -> (Errno) {
when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
ret := syscall(SYS_mknodat, AT_FDCWD, cast(rawptr) name, transmute(u32) mode, dev)
ret := syscall(SYS_mknodat, AT_FDCWD, cast(rawptr) name, transmute(u32) mode, cast(uint) dev)
return Errno(-ret)
} else {
ret := syscall(SYS_mknod, cast(rawptr) name, transmute(u32) mode, dev)
ret := syscall(SYS_mknod, cast(rawptr) name, transmute(u32) mode, cast(uint) dev)
return Errno(-ret)
}
}
@@ -2586,7 +2601,7 @@ mkdirat :: proc "contextless" (dirfd: Fd, name: cstring, mode: Mode) -> (Errno)
Available since Linux 2.6.16.
*/
mknodat :: proc "contextless" (dirfd: Fd, name: cstring, mode: Mode, dev: Dev) -> (Errno) {
ret := syscall(SYS_mknodat, dirfd, cast(rawptr) name, transmute(u32) mode, dev)
ret := syscall(SYS_mknodat, dirfd, cast(rawptr) name, transmute(u32) mode, cast(uint) dev)
return Errno(-ret)
}
@@ -2684,13 +2699,8 @@ faccessat :: proc "contextless" (dirfd: Fd, name: cstring, mode: Mode = F_OK) ->
Available since Linux 2.6.16.
*/
ppoll :: proc "contextless" (fds: []Poll_Fd, timeout: ^Time_Spec, sigmask: ^Sig_Set) -> (i32, Errno) {
when size_of(int) == 8 {
ret := syscall(SYS_ppoll, raw_data(fds), len(fds), timeout, sigmask, size_of(Sig_Set))
return errno_unwrap(ret, i32)
} else {
ret := syscall(SYS_ppoll_time64, raw_data(fds), len(fds), timeout, sigmask, size_of(Sig_Set))
return errno_unwrap(ret, i32)
}
ret := syscall(SYS_ppoll, raw_data(fds), len(fds), timeout, sigmask, size_of(Sig_Set))
return errno_unwrap(ret, i32)
}
// TODO(flysand): unshare
+166 -66
View File
@@ -3,7 +3,7 @@ package linux
/*
Type for storage device handle.
*/
Dev :: distinct int
Dev :: distinct u64
/*
Type for 32-bit User IDs.
@@ -153,6 +153,7 @@ when ODIN_ARCH == .amd64 {
uid: Uid,
gid: Gid,
rdev: Dev,
_: [4]u8,
size: i64,
blksize: uint,
blocks: u64,
@@ -516,79 +517,79 @@ Pid_FD_Flags :: bit_set[Pid_FD_Flags_Bits; i32]
Sig_Set :: [_SIGSET_NWORDS]uint
@private SI_MAX_SIZE :: 128
@private SI_ARCH_PREAMBLE :: 4 * size_of(i32)
@private SI_ARCH_PREAMBLE :: 4 * size_of(i32) when size_of(rawptr) == 8 else 3 * size_of(i32)
@private SI_PAD_SIZE :: SI_MAX_SIZE - SI_ARCH_PREAMBLE
Sig_Handler_Fn :: #type proc "c" (sig: Signal)
Sig_Restore_Fn :: #type proc "c" () -> !
Sig_Info :: struct #packed {
signo: Signal,
errno: Errno,
code: i32,
_pad0: i32,
using _union: struct #raw_union {
_pad1: [SI_PAD_SIZE]u8,
using _kill: struct {
pid: Pid, /* sender's pid */
uid: Uid, /* sender's uid */
},
using _timer: struct {
timerid: i32, /* timer id */
overrun: i32, /* overrun count */
value: Sig_Val, /* timer value */
},
/* POSIX.1b signals */
using _rt: struct {
_pid0: Pid, /* sender's pid */
_uid0: Uid, /* sender's uid */
},
/* SIGCHLD */
using _sigchld: struct {
_pid1: Pid, /* which child */
_uid1: Uid, /* sender's uid */
status: i32, /* exit code */
utime: uint,
stime: uint, //clock_t
},
/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
using _sigfault: struct {
addr: rawptr, /* faulting insn/memory ref. */
using _: struct #raw_union {
trapno: i32, /* Trap number that caused signal */
addr_lsb: i16, /* LSB of the reported address */
using _addr_bnd: struct {
_pad2: u64,
lower: rawptr, /* lower bound during fault */
upper: rawptr, /* upper bound during fault */
},
using _addr_pkey: struct {
_pad3: u64,
pkey: u32, /* protection key on PTE that faulted */
},
using _perf: struct {
perf_data: u64,
perf_type: u32,
perf_flags: u32,
when size_of(rawptr) == 8 {
Sig_Info :: struct #packed {
signo: Signal,
errno: Errno,
code: i32,
_pad0: i32,
using _union: struct #raw_union {
_pad1: [SI_PAD_SIZE]u8,
using _kill: struct {
pid: Pid, /* sender's pid */
uid: Uid, /* sender's uid */
},
using _timer: struct {
timerid: i32, /* timer id */
overrun: i32, /* overrun count */
value: Sig_Val, /* timer value */
},
/* POSIX.1b signals */
using _rt: struct {
_pid0: Pid, /* sender's pid */
_uid0: Uid, /* sender's uid */
},
/* SIGCHLD */
using _sigchld: struct {
_pid1: Pid, /* which child */
_uid1: Uid, /* sender's uid */
status: i32, /* exit code */
utime: uint,
stime: uint, //clock_t
},
/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
using _sigfault: struct {
addr: rawptr, /* faulting insn/memory ref. */
using _: struct #raw_union {
trapno: i32, /* Trap number that caused signal */
addr_lsb: i16, /* LSB of the reported address */
using _addr_bnd: struct {
_pad2: u64,
lower: rawptr, /* lower bound during fault */
upper: rawptr, /* upper bound during fault */
},
using _addr_pkey: struct {
_pad3: u64,
pkey: u32, /* protection key on PTE that faulted */
},
using _perf: struct {
perf_data: u64,
perf_type: u32,
perf_flags: u32,
},
},
},
/* SIGPOLL */
using _sigpoll: struct {
band: int, /* POLL_IN, POLL_OUT, POLL_MSG */
fd: Fd,
},
/* SIGSYS */
using _sigsys: struct {
call_addr: rawptr, /* calling user insn */
syscall: i32, /* triggering system call number */
arch: u32, /* AUDIT_ARCH_* of syscall */
},
},
/* SIGPOLL */
using _sigpoll: struct {
band: int, /* POLL_IN, POLL_OUT, POLL_MSG */
fd: Fd,
},
/* SIGSYS */
using _sigsys: struct {
call_addr: rawptr, /* calling user insn */
syscall: i32, /* triggering system call number */
arch: u32, /* AUDIT_ARCH_* of syscall */
},
},
}
}
#assert(size_of(Sig_Info) == 128)
when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
#assert(size_of(Sig_Info) == 128)
#assert(offset_of(Sig_Info, signo) == 0x00)
#assert(offset_of(Sig_Info, errno) == 0x04)
#assert(offset_of(Sig_Info, code) == 0x08)
@@ -615,7 +616,96 @@ when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
#assert(offset_of(Sig_Info, syscall) == 0x18)
#assert(offset_of(Sig_Info, arch) == 0x1C)
} else {
// TODO
Sig_Info :: struct {
signo: Signal,
errno: Errno,
code: i32,
using _union: struct #raw_union {
_pad1: [SI_PAD_SIZE]u8,
using _kill: struct {
pid: Pid, /* sender's pid */
uid: Uid, /* sender's uid */
},
using _timer: struct {
timerid: i32, /* timer id */
overrun: i32, /* overrun count */
value: Sig_Val, /* timer value */
},
/* POSIX.1b signals */
using _rt: struct {
_pid0: Pid, /* sender's pid */
_uid0: Uid, /* sender's uid */
},
/* SIGCHLD */
using _sigchld: struct {
_pid1: Pid, /* which child */
_uid1: Uid, /* sender's uid */
status: i32, /* exit code */
utime: uint,
stime: uint, //clock_t
},
/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
using _sigfault: struct {
addr: rawptr, /* faulting insn/memory ref. */
using _: struct #raw_union {
trapno: i32, /* Trap number that caused signal */
addr_lsb: i16, /* LSB of the reported address */
using _addr_bnd: struct {
_pad2: u32,
lower: rawptr, /* lower bound during fault */
upper: rawptr, /* upper bound during fault */
},
using _addr_pkey: struct {
_pad3: u32,
pkey: u32, /* protection key on PTE that faulted */
},
using _perf: struct {
perf_data: u32,
perf_type: u32,
perf_flags: u32,
},
},
},
/* SIGPOLL */
using _sigpoll: struct {
band: int, /* POLL_IN, POLL_OUT, POLL_MSG */
fd: Fd,
},
/* SIGSYS */
using _sigsys: struct {
call_addr: rawptr, /* calling user insn */
syscall: i32, /* triggering system call number */
arch: u32, /* AUDIT_ARCH_* of syscall */
},
},
}
#assert(size_of(Sig_Info) == 128)
#assert(offset_of(Sig_Info, signo) == 0x00)
#assert(offset_of(Sig_Info, errno) == 0x04)
#assert(offset_of(Sig_Info, code) == 0x08)
#assert(offset_of(Sig_Info, pid) == 0x0c)
#assert(offset_of(Sig_Info, uid) == 0x10)
#assert(offset_of(Sig_Info, timerid) == 0x0c)
#assert(offset_of(Sig_Info, overrun) == 0x10)
#assert(offset_of(Sig_Info, value) == 0x14)
#assert(offset_of(Sig_Info, status) == 0x14)
#assert(offset_of(Sig_Info, utime) == 0x18)
#assert(offset_of(Sig_Info, stime) == 0x1c)
#assert(offset_of(Sig_Info, addr) == 0x0c)
#assert(offset_of(Sig_Info, addr_lsb) == 0x10)
#assert(offset_of(Sig_Info, trapno) == 0x10)
#assert(offset_of(Sig_Info, lower) == 0x14)
#assert(offset_of(Sig_Info, upper) == 0x18)
#assert(offset_of(Sig_Info, pkey) == 0x14)
#assert(offset_of(Sig_Info, perf_data) == 0x10)
#assert(offset_of(Sig_Info, perf_type) == 0x14)
#assert(offset_of(Sig_Info, perf_flags) == 0x18)
#assert(offset_of(Sig_Info, band) == 0x0c)
#assert(offset_of(Sig_Info, fd) == 0x10)
#assert(offset_of(Sig_Info, call_addr) == 0x0c)
#assert(offset_of(Sig_Info, syscall) == 0x10)
#assert(offset_of(Sig_Info, arch) == 0x14)
}
SIGEV_MAX_SIZE :: 64
@@ -684,6 +774,14 @@ Address_Family :: distinct Protocol_Family
*/
Socket_Msg :: bit_set[Socket_Msg_Bits; i32]
/*
Struct representing a generic socket address.
*/
Sock_Addr :: struct #packed {
sa_family: Address_Family,
sa_data: [14]u8,
}
/*
Struct representing IPv4 socket address.
*/
@@ -691,6 +789,7 @@ Sock_Addr_In :: struct #packed {
sin_family: Address_Family,
sin_port: u16be,
sin_addr: [4]u8,
sin_zero: [size_of(Sock_Addr) - size_of(Address_Family) - size_of(u16be) - size_of([4]u8)]u8,
}
/*
@@ -720,6 +819,7 @@ Sock_Addr_Any :: struct #raw_union {
family: Address_Family,
port: u16be,
},
using generic: Sock_Addr,
using ipv4: Sock_Addr_In,
using ipv6: Sock_Addr_In6,
using uds: Sock_Addr_Un,
+21 -1
View File
@@ -20,6 +20,15 @@ COMMON_LVB_GRID_RVERTICAL :: WORD(0x1000)
COMMON_LVB_REVERSE_VIDEO :: WORD(0x4000)
COMMON_LVB_UNDERSCORE :: WORD(0x8000)
COMMON_LVB_SBCSDBCS :: WORD(0x0300)
EV_BREAK :: DWORD(0x0040)
EV_CTS :: DWORD(0x0008)
EV_DSR :: DWORD(0x0010)
EV_ERR :: DWORD(0x0080)
EV_RING :: DWORD(0x0100)
EV_RLSD :: DWORD(0x0020)
EV_RXCHAR :: DWORD(0x0001)
EV_RXFLAG :: DWORD(0x0002)
EV_TXEMPTY :: DWORD(0x0004)
@(default_calling_convention="system")
foreign kernel32 {
@@ -109,7 +118,9 @@ foreign kernel32 {
ClearCommError :: proc(hFile: HANDLE, lpErrors: ^Com_Error, lpStat: ^COMSTAT) -> BOOL ---
GetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL ---
SetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL ---
GetCommPorts :: proc(lpPortNumbers: PULONG, uPortNumbersCount: ULONG, puPortNumbersFound: PULONG) -> ULONG ---
SetCommMask :: proc(handle: HANDLE, dwEvtMap: DWORD) -> BOOL ---
GetCommMask :: proc(handle: HANDLE, lpEvtMask: LPDWORD) -> BOOL ---
WaitCommEvent :: proc(handle: HANDLE, lpEvtMask: LPDWORD, lpOverlapped: LPOVERLAPPED) -> BOOL ---
GetCommandLineW :: proc() -> LPCWSTR ---
GetTempPathW :: proc(nBufferLength: DWORD, lpBuffer: LPCWSTR) -> DWORD ---
GetCurrentProcess :: proc() -> HANDLE ---
@@ -239,6 +250,10 @@ foreign kernel32 {
hThread: HANDLE,
lpContext: LPCONTEXT,
) -> BOOL ---
SetThreadContext :: proc(
hThread: HANDLE,
lpContext: LPCONTEXT,
) -> BOOL ---
CreateProcessW :: proc(
lpApplicationName: LPCWSTR,
lpCommandLine: LPWSTR,
@@ -1068,6 +1083,11 @@ foreign one_core {
PageProtection: ULONG,
PreferredNode: ULONG,
) -> PVOID ---
GetCommPorts :: proc(
lpPortNumbers: PULONG,
uPortNumbersCount: ULONG,
puPortNumbersFound: PULONG,
) -> ULONG ---
}
+26 -2
View File
@@ -143,6 +143,7 @@ LPWSAPROTOCOL_INFO :: ^WSAPROTOCOL_INFO
LPSTR :: ^CHAR
LPWSTR :: ^WCHAR
OLECHAR :: WCHAR
BSTR :: ^OLECHAR
LPOLESTR :: ^OLECHAR
LPCOLESTR :: LPCSTR
LPFILETIME :: ^FILETIME
@@ -2694,11 +2695,23 @@ EXCEPTION_MAXIMUM_PARAMETERS :: 15
EXCEPTION_DATATYPE_MISALIGNMENT :: 0x80000002
EXCEPTION_BREAKPOINT :: 0x80000003
EXCEPTION_SINGLE_STEP :: 0x80000004
EXCEPTION_ACCESS_VIOLATION :: 0xC0000005
EXCEPTION_IN_PAGE_ERROR :: 0xC0000006
EXCEPTION_ILLEGAL_INSTRUCTION :: 0xC000001D
EXCEPTION_NONCONTINUABLE_EXCEPTION :: 0xC0000025
EXCEPTION_INVALID_DISPOSITION :: 0xC0000026
EXCEPTION_ARRAY_BOUNDS_EXCEEDED :: 0xC000008C
EXCEPTION_FLT_DENORMAL_OPERAND :: 0xC000008D
EXCEPTION_FLT_DIVIDE_BY_ZERO :: 0xC000008E
EXCEPTION_FLT_INEXACT_RESULT :: 0xC000008F
EXCEPTION_FLT_INVALID_OPERATION :: 0xC0000090
EXCEPTION_FLT_OVERFLOW :: 0xC0000091
EXCEPTION_FLT_STACK_CHECK :: 0xC0000092
EXCEPTION_FLT_UNDERFLOW :: 0xC0000093
EXCEPTION_INT_DIVIDE_BY_ZERO :: 0xC0000094
EXCEPTION_INT_OVERFLOW :: 0xC0000095
EXCEPTION_PRIV_INSTRUCTION :: 0xC0000096
EXCEPTION_STACK_OVERFLOW :: 0xC00000FD
STATUS_PRIVILEGED_INSTRUCTION :: 0xC0000096
@@ -3415,8 +3428,6 @@ TIME_ZONE_INFORMATION :: struct {
DaylightBias: LONG,
}
@(private="file")
IMAGE_DOS_HEADER :: struct {
e_magic: WORD,
e_cblp: WORD,
@@ -3534,6 +3545,19 @@ IMAGE_EXPORT_DIRECTORY :: struct {
AddressOfNameOrdinals: DWORD, // RVA from base of image
}
IMAGE_DEBUG_DIRECTORY :: struct {
Characteristics: DWORD,
TimeDateStamp: DWORD,
MajorVersion: WORD,
MinorVersion: WORD,
Type: DWORD,
SizeOfData: DWORD,
AddressOfRawData: DWORD,
PointerToRawData: DWORD,
}
IMAGE_DEBUG_TYPE_CODEVIEW :: 2
SICHINTF :: DWORD
SHCONTF :: DWORD
SFGAOF :: ULONG
+2 -1
View File
@@ -51,6 +51,7 @@ foreign user32 {
IsWindowVisible :: proc(hwnd: HWND) -> BOOL ---
IsWindowEnabled :: proc(hwnd: HWND) -> BOOL ---
IsIconic :: proc(hwnd: HWND) -> BOOL ---
IsZoomed :: proc(hwnd: HWND) -> BOOL ---
BringWindowToTop :: proc(hWnd: HWND) -> BOOL ---
GetTopWindow :: proc(hWnd: HWND) -> HWND ---
SetForegroundWindow :: proc(hWnd: HWND) -> BOOL ---
@@ -548,7 +549,7 @@ RI_KEY_TERMSRV_SHADOW :: 0x10
MOUSE_MOVE_RELATIVE :: 0x00
MOUSE_MOVE_ABSOLUTE :: 0x01
MOUSE_VIRTUAL_DESKTOP :: 0x02
MOUSE_ATTRIUBTTES_CHANGED :: 0x04
MOUSE_ATTRIBUTES_CHANGED :: 0x04
MOUSE_MOVE_NOCOALESCE :: 0x08
RI_MOUSE_BUTTON_1_DOWN :: 0x0001
+210
View File
@@ -0,0 +1,210 @@
// +build windows
package sys_windows
foreign import "system:xinput.lib"
// Device types available in XINPUT_CAPABILITIES
// Correspond to XINPUT_DEVTYPE_...
XINPUT_DEVTYPE :: enum BYTE {
GAMEPAD = 0x01,
}
// Device subtypes available in XINPUT_CAPABILITIES
// Correspond to XINPUT_DEVSUBTYPE_...
XINPUT_DEVSUBTYPE :: enum BYTE {
UNKNOWN = 0x00,
GAMEPAD = 0x01,
WHEEL = 0x02,
ARCADE_STICK = 0x03,
FLIGHT_STICK = 0x04,
DANCE_PAD = 0x05,
GUITAR = 0x06,
GUITAR_ALTERNATE = 0x07,
DRUM_KIT = 0x08,
GUITAR_BASS = 0x0B,
ARCADE_PAD = 0x13,
}
// Flags for XINPUT_CAPABILITIES
// Correspond to log2(XINPUT_CAPS_...)
XINPUT_CAP :: enum WORD {
FFB_SUPPORTED = 0,
WIRELESS = 1,
VOICE_SUPPORTED = 2,
PMD_SUPPORTED = 3,
NO_NAVIGATION = 4,
}
XINPUT_CAPS :: distinct bit_set[XINPUT_CAP;WORD]
// Constants for gamepad buttons
// Correspond to log2(XINPUT_GAMEPAD_...)
XINPUT_GAMEPAD_BUTTON_BIT :: enum WORD {
DPAD_UP = 0,
DPAD_DOWN = 1,
DPAD_LEFT = 2,
DPAD_RIGHT = 3,
START = 4,
BACK = 5,
LEFT_THUMB = 6,
RIGHT_THUMB = 7,
LEFT_SHOULDER = 8,
RIGHT_SHOULDER = 9,
A = 12,
B = 13,
X = 14,
Y = 15,
}
XINPUT_GAMEPAD_BUTTON :: distinct bit_set[XINPUT_GAMEPAD_BUTTON_BIT;WORD]
// Gamepad thresholds
XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE: SHORT : 7849
XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE: SHORT : 8689
XINPUT_GAMEPAD_TRIGGER_THRESHOLD: SHORT : 30
// Flags to pass to XInputGetCapabilities
// Corresponds to log2(XINPUT_FLAG_...)
XINPUT_FLAG_BIT :: enum WORD {
GAMEPAD = 0,
}
XINPUT_FLAG :: distinct bit_set[XINPUT_FLAG_BIT;DWORD]
// Devices that support batteries
// Corresponds to BATTERY_DEVTYPE_...
BATTERY_DEVTYPE :: enum BYTE {
GAMEPAD = 0x00,
HEADSET = 0x01,
}
// Flags for battery status level
// Correspond to BATTERY_TYPE_...
BATTERY_TYPE :: enum BYTE {
DISCONNECTED = 0x00, // This device is not connected
WIRED = 0x01, // Wired device, no battery
ALKALINE = 0x02, // Alkaline battery source
NIMH = 0x03, // Nickel Metal Hydride battery source
UNKNOWN = 0xFF, // Cannot determine the battery type
}
// These are only valid for wireless, connected devices, with known battery types
// The amount of use time remaining depends on the type of device.
// Correspond to BATTERY_LEVEL_...
BATTERY_LEVEL :: enum BYTE {
EMPTY = 0x00,
LOW = 0x01,
MEDIUM = 0x02,
FULL = 0x03,
}
// User index definitions
// Index of the gamer associated with the device
XUSER :: enum DWORD {
One = 0,
Two = 1,
Three = 2,
Four = 3,
Any = 0x000000FF, // Can be only used with XInputGetKeystroke
}
XUSER_MAX_COUNT :: 4
// Codes returned for the gamepad keystroke
// Corresponds to VK_PAD_...
VK_PAD :: enum WORD {
A = 0x5800,
B = 0x5801,
X = 0x5802,
Y = 0x5803,
RSHOULDER = 0x5804,
LSHOULDER = 0x5805,
LTRIGGER = 0x5806,
RTRIGGER = 0x5807,
DPAD_UP = 0x5810,
DPAD_DOWN = 0x5811,
DPAD_LEFT = 0x5812,
DPAD_RIGHT = 0x5813,
START = 0x5814,
BACK = 0x5815,
LTHUMB_PRESS = 0x5816,
RTHUMB_PRESS = 0x5817,
LTHUMB_UP = 0x5820,
LTHUMB_DOWN = 0x5821,
LTHUMB_RIGHT = 0x5822,
LTHUMB_LEFT = 0x5823,
LTHUMB_UPLEFT = 0x5824,
LTHUMB_UPRIGHT = 0x5825,
LTHUMB_DOWNRIGHT = 0x5826,
LTHUMB_DOWNLEFT = 0x5827,
RTHUMB_UP = 0x5830,
RTHUMB_DOWN = 0x5831,
RTHUMB_RIGHT = 0x5832,
RTHUMB_LEFT = 0x5833,
RTHUMB_UPLEFT = 0x5834,
RTHUMB_UPRIGHT = 0x5835,
RTHUMB_DOWNRIGHT = 0x5836,
RTHUMB_DOWNLEFT = 0x5837,
}
// Flags used in XINPUT_KEYSTROKE
// Correspond to log2(XINPUT_KEYSTROKE_...)
XINPUT_KEYSTROKE_BIT :: enum WORD {
KEYDOWN = 0,
KEYUP = 1,
REPEAT = 2,
}
XINPUT_KEYSTROKES :: distinct bit_set[XINPUT_KEYSTROKE_BIT;WORD]
// Structures used by XInput APIs
XINPUT_GAMEPAD :: struct {
wButtons: XINPUT_GAMEPAD_BUTTON,
bLeftTrigger: BYTE,
bRightTrigger: BYTE,
sThumbLX: SHORT,
sThumbLY: SHORT,
sThumbRX: SHORT,
sThumbRY: SHORT,
}
XINPUT_STATE :: struct {
dwPacketNumber: DWORD,
Gamepad: XINPUT_GAMEPAD,
}
XINPUT_VIBRATION :: struct {
wLeftMotorSpeed: WORD,
wRightMotorSpeed: WORD,
}
XINPUT_CAPABILITIES :: struct {
Type: XINPUT_DEVTYPE,
SubType: XINPUT_DEVSUBTYPE,
Flags: XINPUT_CAPS,
Gamepad: XINPUT_GAMEPAD,
Vibration: XINPUT_VIBRATION,
}
XINPUT_BATTERY_INFORMATION :: struct {
BatteryType: BATTERY_TYPE,
BatteryLevel: BATTERY_LEVEL,
}
XINPUT_KEYSTROKE :: struct {
VirtualKey: VK_PAD,
Unicode: WCHAR,
Flags: XINPUT_KEYSTROKES,
UserIndex: XUSER,
HidCode: BYTE,
}
// XInput APIs
@(default_calling_convention = "system")
foreign xinput {
XInputGetState :: proc(user: XUSER, pState: ^XINPUT_STATE) -> System_Error ---
XInputSetState :: proc(user: XUSER, pVibration: ^XINPUT_VIBRATION) -> System_Error ---
XInputGetCapabilities :: proc(user: XUSER, dwFlags: XINPUT_FLAG, pCapabilities: ^XINPUT_CAPABILITIES) -> System_Error ---
XInputEnable :: proc(enable: BOOL) ---
XInputGetAudioDeviceIds :: proc(user: XUSER, pRenderDeviceId: LPWSTR, pRenderCount: ^UINT, pCaptureDeviceId: LPWSTR, pCaptureCount: ^UINT) -> System_Error ---
XInputGetBatteryInformation :: proc(user: XUSER, devType: BATTERY_DEVTYPE, pBatteryInformation: ^XINPUT_BATTERY_INFORMATION) -> System_Error ---
XInputGetKeystroke :: proc(user: XUSER, dwReserved: DWORD, pKeystroke: ^XINPUT_KEYSTROKE) -> System_Error ---
XInputGetDSoundAudioDeviceGuids :: proc(user: XUSER, pDSoundRenderGuid: ^GUID, pDSoundCaptureGuid: ^GUID) -> System_Error ---
}
+153 -141
View File
@@ -11,146 +11,147 @@ TZ_Abbrev :: struct {
dst: string,
}
tz_abbrevs := map[string]TZ_Abbrev {
"Egypt Standard Time" = {"EET", "EEST"}, // Africa/Cairo
"Morocco Standard Time" = {"+00", "+01"}, // Africa/Casablanca
"South Africa Standard Time" = {"SAST", "SAST"}, // Africa/Johannesburg
"South Sudan Standard Time" = {"CAT", "CAT"}, // Africa/Juba
"Sudan Standard Time" = {"CAT", "CAT"}, // Africa/Khartoum
"W. Central Africa Standard Time" = {"WAT", "WAT"}, // Africa/Lagos
"E. Africa Standard Time" = {"EAT", "EAT"}, // Africa/Nairobi
"Sao Tome Standard Time" = {"GMT", "GMT"}, // Africa/Sao_Tome
"Libya Standard Time" = {"EET", "EET"}, // Africa/Tripoli
"Namibia Standard Time" = {"CAT", "CAT"}, // Africa/Windhoek
"Aleutian Standard Time" = {"HST", "HDT"}, // America/Adak
"Alaskan Standard Time" = {"AKST", "AKDT"}, // America/Anchorage
"Tocantins Standard Time" = {"-03", "-03"}, // America/Araguaina
"Paraguay Standard Time" = {"-04", "-03"}, // America/Asuncion
"Bahia Standard Time" = {"-03", "-03"}, // America/Bahia
"SA Pacific Standard Time" = {"-05", "-05"}, // America/Bogota
"Argentina Standard Time" = {"-03", "-03"}, // America/Buenos_Aires
"Eastern Standard Time (Mexico)" = {"EST", "EST"}, // America/Cancun
"Venezuela Standard Time" = {"-04", "-04"}, // America/Caracas
"SA Eastern Standard Time" = {"-03", "-03"}, // America/Cayenne
"Central Standard Time" = {"CST", "CDT"}, // America/Chicago
"Central Brazilian Standard Time" = {"-04", "-04"}, // America/Cuiaba
"Mountain Standard Time" = {"MST", "MDT"}, // America/Denver
"Greenland Standard Time" = {"-03", "-02"}, // America/Godthab
"Turks And Caicos Standard Time" = {"EST", "EDT"}, // America/Grand_Turk
"Central America Standard Time" = {"CST", "CST"}, // America/Guatemala
"Atlantic Standard Time" = {"AST", "ADT"}, // America/Halifax
"Cuba Standard Time" = {"CST", "CDT"}, // America/Havana
"US Eastern Standard Time" = {"EST", "EDT"}, // America/Indianapolis
"SA Western Standard Time" = {"-04", "-04"}, // America/La_Paz
"Pacific Standard Time" = {"PST", "PDT"}, // America/Los_Angeles
"Mountain Standard Time (Mexico)" = {"MST", "MST"}, // America/Mazatlan
"Central Standard Time (Mexico)" = {"CST", "CST"}, // America/Mexico_City
"Saint Pierre Standard Time" = {"-03", "-02"}, // America/Miquelon
"Montevideo Standard Time" = {"-03", "-03"}, // America/Montevideo
"Eastern Standard Time" = {"EST", "EDT"}, // America/New_York
"US Mountain Standard Time" = {"MST", "MST"}, // America/Phoenix
"Haiti Standard Time" = {"EST", "EDT"}, // America/Port-au-Prince
"Magallanes Standard Time" = {"-03", "-03"}, // America/Punta_Arenas
"Canada Central Standard Time" = {"CST", "CST"}, // America/Regina
"Pacific SA Standard Time" = {"-04", "-03"}, // America/Santiago
"E. South America Standard Time" = {"-03", "-03"}, // America/Sao_Paulo
"Newfoundland Standard Time" = {"NST", "NDT"}, // America/St_Johns
"Pacific Standard Time (Mexico)" = {"PST", "PDT"}, // America/Tijuana
"Yukon Standard Time" = {"MST", "MST"}, // America/Whitehorse
"Central Asia Standard Time" = {"+06", "+06"}, // Asia/Almaty
"Jordan Standard Time" = {"+03", "+03"}, // Asia/Amman
"Arabic Standard Time" = {"+03", "+03"}, // Asia/Baghdad
"Azerbaijan Standard Time" = {"+04", "+04"}, // Asia/Baku
"SE Asia Standard Time" = {"+07", "+07"}, // Asia/Bangkok
"Altai Standard Time" = {"+07", "+07"}, // Asia/Barnaul
"Middle East Standard Time" = {"EET", "EEST"}, // Asia/Beirut
"India Standard Time" = {"IST", "IST"}, // Asia/Calcutta
"Transbaikal Standard Time" = {"+09", "+09"}, // Asia/Chita
"Sri Lanka Standard Time" = {"+0530", "+0530"}, // Asia/Colombo
"Syria Standard Time" = {"+03", "+03"}, // Asia/Damascus
"Bangladesh Standard Time" = {"+06", "+06"}, // Asia/Dhaka
"Arabian Standard Time" = {"+04", "+04"}, // Asia/Dubai
"West Bank Standard Time" = {"EET", "EEST"}, // Asia/Hebron
"W. Mongolia Standard Time" = {"+07", "+07"}, // Asia/Hovd
"North Asia East Standard Time" = {"+08", "+08"}, // Asia/Irkutsk
"Israel Standard Time" = {"IST", "IDT"}, // Asia/Jerusalem
"Afghanistan Standard Time" = {"+0430", "+0430"}, // Asia/Kabul
"Russia Time Zone 11" = {"+12", "+12"}, // Asia/Kamchatka
"Pakistan Standard Time" = {"PKT", "PKT"}, // Asia/Karachi
"Nepal Standard Time" = {"+0545", "+0545"}, // Asia/Katmandu
"North Asia Standard Time" = {"+07", "+07"}, // Asia/Krasnoyarsk
"Magadan Standard Time" = {"+11", "+11"}, // Asia/Magadan
"N. Central Asia Standard Time" = {"+07", "+07"}, // Asia/Novosibirsk
"Omsk Standard Time" = {"+06", "+06"}, // Asia/Omsk
"North Korea Standard Time" = {"KST", "KST"}, // Asia/Pyongyang
"Qyzylorda Standard Time" = {"+05", "+05"}, // Asia/Qyzylorda
"Myanmar Standard Time" = {"+0630", "+0630"}, // Asia/Rangoon
"Arab Standard Time" = {"+03", "+03"}, // Asia/Riyadh
"Sakhalin Standard Time" = {"+11", "+11"}, // Asia/Sakhalin
"Korea Standard Time" = {"KST", "KST"}, // Asia/Seoul
"China Standard Time" = {"CST", "CST"}, // Asia/Shanghai
"Singapore Standard Time" = {"+08", "+08"}, // Asia/Singapore
"Russia Time Zone 10" = {"+11", "+11"}, // Asia/Srednekolymsk
"Taipei Standard Time" = {"CST", "CST"}, // Asia/Taipei
"West Asia Standard Time" = {"+05", "+05"}, // Asia/Tashkent
"Georgian Standard Time" = {"+04", "+04"}, // Asia/Tbilisi
"Iran Standard Time" = {"+0330", "+0330"}, // Asia/Tehran
"Tokyo Standard Time" = {"JST", "JST"}, // Asia/Tokyo
"Tomsk Standard Time" = {"+07", "+07"}, // Asia/Tomsk
"Ulaanbaatar Standard Time" = {"+08", "+08"}, // Asia/Ulaanbaatar
"Vladivostok Standard Time" = {"+10", "+10"}, // Asia/Vladivostok
"Yakutsk Standard Time" = {"+09", "+09"}, // Asia/Yakutsk
"Ekaterinburg Standard Time" = {"+05", "+05"}, // Asia/Yekaterinburg
"Caucasus Standard Time" = {"+04", "+04"}, // Asia/Yerevan
"Azores Standard Time" = {"-01", "+00"}, // Atlantic/Azores
"Cape Verde Standard Time" = {"-01", "-01"}, // Atlantic/Cape_Verde
"Greenwich Standard Time" = {"GMT", "GMT"}, // Atlantic/Reykjavik
"Cen. Australia Standard Time" = {"ACST", "ACDT"}, // Australia/Adelaide
"E. Australia Standard Time" = {"AEST", "AEST"}, // Australia/Brisbane
"AUS Central Standard Time" = {"ACST", "ACST"}, // Australia/Darwin
"Aus Central W. Standard Time" = {"+0845", "+0845"}, // Australia/Eucla
"Tasmania Standard Time" = {"AEST", "AEDT"}, // Australia/Hobart
"Lord Howe Standard Time" = {"+1030", "+11"}, // Australia/Lord_Howe
"W. Australia Standard Time" = {"AWST", "AWST"}, // Australia/Perth
"AUS Eastern Standard Time" = {"AEST", "AEDT"}, // Australia/Sydney
"UTC-11" = {"-11", "-11"}, // Etc/GMT+11
"Dateline Standard Time" = {"-12", "-12"}, // Etc/GMT+12
"UTC-02" = {"-02", "-02"}, // Etc/GMT+2
"UTC-08" = {"-08", "-08"}, // Etc/GMT+8
"UTC-09" = {"-09", "-09"}, // Etc/GMT+9
"UTC+12" = {"+12", "+12"}, // Etc/GMT-12
"UTC+13" = {"+13", "+13"}, // Etc/GMT-13
"UTC" = {"UTC", "UTC"}, // Etc/UTC
"Astrakhan Standard Time" = {"+04", "+04"}, // Europe/Astrakhan
"W. Europe Standard Time" = {"CET", "CEST"}, // Europe/Berlin
"GTB Standard Time" = {"EET", "EEST"}, // Europe/Bucharest
"Central Europe Standard Time" = {"CET", "CEST"}, // Europe/Budapest
"E. Europe Standard Time" = {"EET", "EEST"}, // Europe/Chisinau
"Turkey Standard Time" = {"+03", "+03"}, // Europe/Istanbul
"Kaliningrad Standard Time" = {"EET", "EET"}, // Europe/Kaliningrad
"FLE Standard Time" = {"EET", "EEST"}, // Europe/Kiev
"GMT Standard Time" = {"GMT", "BST"}, // Europe/London
"Belarus Standard Time" = {"+03", "+03"}, // Europe/Minsk
"Russian Standard Time" = {"MSK", "MSK"}, // Europe/Moscow
"Romance Standard Time" = {"CET", "CEST"}, // Europe/Paris
"Russia Time Zone 3" = {"+04", "+04"}, // Europe/Samara
"Saratov Standard Time" = {"+04", "+04"}, // Europe/Saratov
"Volgograd Standard Time" = {"MSK", "MSK"}, // Europe/Volgograd
"Central European Standard Time" = {"CET", "CEST"}, // Europe/Warsaw
"Mauritius Standard Time" = {"+04", "+04"}, // Indian/Mauritius
"Samoa Standard Time" = {"+13", "+13"}, // Pacific/Apia
"New Zealand Standard Time" = {"NZST", "NZDT"}, // Pacific/Auckland
"Bougainville Standard Time" = {"+11", "+11"}, // Pacific/Bougainville
"Chatham Islands Standard Time" = {"+1245", "+1345"}, // Pacific/Chatham
"Easter Island Standard Time" = {"-06", "-05"}, // Pacific/Easter
"Fiji Standard Time" = {"+12", "+12"}, // Pacific/Fiji
"Central Pacific Standard Time" = {"+11", "+11"}, // Pacific/Guadalcanal
"Hawaiian Standard Time" = {"HST", "HST"}, // Pacific/Honolulu
"Line Islands Standard Time" = {"+14", "+14"}, // Pacific/Kiritimati
"Marquesas Standard Time" = {"-0930", "-0930"}, // Pacific/Marquesas
"Norfolk Standard Time" = {"+11", "+12"}, // Pacific/Norfolk
"West Pacific Standard Time" = {"+10", "+10"}, // Pacific/Port_Moresby
"Tonga Standard Time" = {"+13", "+13"}, // Pacific/Tongatapu
@(rodata)
tz_abbrevs := [?]struct{key: string, value: TZ_Abbrev}{
{"Egypt Standard Time", {"EET", "EEST"}}, // Africa/Cairo
{"Morocco Standard Time", {"+00", "+01"}}, // Africa/Casablanca
{"South Africa Standard Time", {"SAST", "SAST"}}, // Africa/Johannesburg
{"South Sudan Standard Time", {"CAT", "CAT"}}, // Africa/Juba
{"Sudan Standard Time", {"CAT", "CAT"}}, // Africa/Khartoum
{"W. Central Africa Standard Time", {"WAT", "WAT"}}, // Africa/Lagos
{"E. Africa Standard Time", {"EAT", "EAT"}}, // Africa/Nairobi
{"Sao Tome Standard Time", {"GMT", "GMT"}}, // Africa/Sao_Tome
{"Libya Standard Time", {"EET", "EET"}}, // Africa/Tripoli
{"Namibia Standard Time", {"CAT", "CAT"}}, // Africa/Windhoek
{"Aleutian Standard Time", {"HST", "HDT"}}, // America/Adak
{"Alaskan Standard Time", {"AKST", "AKDT"}}, // America/Anchorage
{"Tocantins Standard Time", {"-03", "-03"}}, // America/Araguaina
{"Paraguay Standard Time", {"-04", "-03"}}, // America/Asuncion
{"Bahia Standard Time", {"-03", "-03"}}, // America/Bahia
{"SA Pacific Standard Time", {"-05", "-05"}}, // America/Bogota
{"Argentina Standard Time", {"-03", "-03"}}, // America/Buenos_Aires
{"Eastern Standard Time (Mexico)", {"EST", "EST"}}, // America/Cancun
{"Venezuela Standard Time", {"-04", "-04"}}, // America/Caracas
{"SA Eastern Standard Time", {"-03", "-03"}}, // America/Cayenne
{"Central Standard Time", {"CST", "CDT"}}, // America/Chicago
{"Central Brazilian Standard Time", {"-04", "-04"}}, // America/Cuiaba
{"Mountain Standard Time", {"MST", "MDT"}}, // America/Denver
{"Greenland Standard Time", {"-03", "-02"}}, // America/Godthab
{"Turks And Caicos Standard Time", {"EST", "EDT"}}, // America/Grand_Turk
{"Central America Standard Time", {"CST", "CST"}}, // America/Guatemala
{"Atlantic Standard Time", {"AST", "ADT"}}, // America/Halifax
{"Cuba Standard Time", {"CST", "CDT"}}, // America/Havana
{"US Eastern Standard Time", {"EST", "EDT"}}, // America/Indianapolis
{"SA Western Standard Time", {"-04", "-04"}}, // America/La_Paz
{"Pacific Standard Time", {"PST", "PDT"}}, // America/Los_Angeles
{"Mountain Standard Time (Mexico)", {"MST", "MST"}}, // America/Mazatlan
{"Central Standard Time (Mexico)", {"CST", "CST"}}, // America/Mexico_City
{"Saint Pierre Standard Time", {"-03", "-02"}}, // America/Miquelon
{"Montevideo Standard Time", {"-03", "-03"}}, // America/Montevideo
{"Eastern Standard Time", {"EST", "EDT"}}, // America/New_York
{"US Mountain Standard Time", {"MST", "MST"}}, // America/Phoenix
{"Haiti Standard Time", {"EST", "EDT"}}, // America/Port-au-Prince
{"Magallanes Standard Time", {"-03", "-03"}}, // America/Punta_Arenas
{"Canada Central Standard Time", {"CST", "CST"}}, // America/Regina
{"Pacific SA Standard Time", {"-04", "-03"}}, // America/Santiago
{"E. South America Standard Time", {"-03", "-03"}}, // America/Sao_Paulo
{"Newfoundland Standard Time", {"NST", "NDT"}}, // America/St_Johns
{"Pacific Standard Time (Mexico)", {"PST", "PDT"}}, // America/Tijuana
{"Yukon Standard Time", {"MST", "MST"}}, // America/Whitehorse
{"Central Asia Standard Time", {"+06", "+06"}}, // Asia/Almaty
{"Jordan Standard Time", {"+03", "+03"}}, // Asia/Amman
{"Arabic Standard Time", {"+03", "+03"}}, // Asia/Baghdad
{"Azerbaijan Standard Time", {"+04", "+04"}}, // Asia/Baku
{"SE Asia Standard Time", {"+07", "+07"}}, // Asia/Bangkok
{"Altai Standard Time", {"+07", "+07"}}, // Asia/Barnaul
{"Middle East Standard Time", {"EET", "EEST"}}, // Asia/Beirut
{"India Standard Time", {"IST", "IST"}}, // Asia/Calcutta
{"Transbaikal Standard Time", {"+09", "+09"}}, // Asia/Chita
{"Sri Lanka Standard Time", {"+0530", "+0530"}}, // Asia/Colombo
{"Syria Standard Time", {"+03", "+03"}}, // Asia/Damascus
{"Bangladesh Standard Time", {"+06", "+06"}}, // Asia/Dhaka
{"Arabian Standard Time", {"+04", "+04"}}, // Asia/Dubai
{"West Bank Standard Time", {"EET", "EEST"}}, // Asia/Hebron
{"W. Mongolia Standard Time", {"+07", "+07"}}, // Asia/Hovd
{"North Asia East Standard Time", {"+08", "+08"}}, // Asia/Irkutsk
{"Israel Standard Time", {"IST", "IDT"}}, // Asia/Jerusalem
{"Afghanistan Standard Time", {"+0430", "+0430"}}, // Asia/Kabul
{"Russia Time Zone 11", {"+12", "+12"}}, // Asia/Kamchatka
{"Pakistan Standard Time", {"PKT", "PKT"}}, // Asia/Karachi
{"Nepal Standard Time", {"+0545", "+0545"}}, // Asia/Katmandu
{"North Asia Standard Time", {"+07", "+07"}}, // Asia/Krasnoyarsk
{"Magadan Standard Time", {"+11", "+11"}}, // Asia/Magadan
{"N. Central Asia Standard Time", {"+07", "+07"}}, // Asia/Novosibirsk
{"Omsk Standard Time", {"+06", "+06"}}, // Asia/Omsk
{"North Korea Standard Time", {"KST", "KST"}}, // Asia/Pyongyang
{"Qyzylorda Standard Time", {"+05", "+05"}}, // Asia/Qyzylorda
{"Myanmar Standard Time", {"+0630", "+0630"}}, // Asia/Rangoon
{"Arab Standard Time", {"+03", "+03"}}, // Asia/Riyadh
{"Sakhalin Standard Time", {"+11", "+11"}}, // Asia/Sakhalin
{"Korea Standard Time", {"KST", "KST"}}, // Asia/Seoul
{"China Standard Time", {"CST", "CST"}}, // Asia/Shanghai
{"Singapore Standard Time", {"+08", "+08"}}, // Asia/Singapore
{"Russia Time Zone 10", {"+11", "+11"}}, // Asia/Srednekolymsk
{"Taipei Standard Time", {"CST", "CST"}}, // Asia/Taipei
{"West Asia Standard Time", {"+05", "+05"}}, // Asia/Tashkent
{"Georgian Standard Time", {"+04", "+04"}}, // Asia/Tbilisi
{"Iran Standard Time", {"+0330", "+0330"}}, // Asia/Tehran
{"Tokyo Standard Time", {"JST", "JST"}}, // Asia/Tokyo
{"Tomsk Standard Time", {"+07", "+07"}}, // Asia/Tomsk
{"Ulaanbaatar Standard Time", {"+08", "+08"}}, // Asia/Ulaanbaatar
{"Vladivostok Standard Time", {"+10", "+10"}}, // Asia/Vladivostok
{"Yakutsk Standard Time", {"+09", "+09"}}, // Asia/Yakutsk
{"Ekaterinburg Standard Time", {"+05", "+05"}}, // Asia/Yekaterinburg
{"Caucasus Standard Time", {"+04", "+04"}}, // Asia/Yerevan
{"Azores Standard Time", {"-01", "+00"}}, // Atlantic/Azores
{"Cape Verde Standard Time", {"-01", "-01"}}, // Atlantic/Cape_Verde
{"Greenwich Standard Time", {"GMT", "GMT"}}, // Atlantic/Reykjavik
{"Cen. Australia Standard Time", {"ACST", "ACDT"}}, // Australia/Adelaide
{"E. Australia Standard Time", {"AEST", "AEST"}}, // Australia/Brisbane
{"AUS Central Standard Time", {"ACST", "ACST"}}, // Australia/Darwin
{"Aus Central W. Standard Time", {"+0845", "+0845"}}, // Australia/Eucla
{"Tasmania Standard Time", {"AEST", "AEDT"}}, // Australia/Hobart
{"Lord Howe Standard Time", {"+1030", "+11"}}, // Australia/Lord_Howe
{"W. Australia Standard Time", {"AWST", "AWST"}}, // Australia/Perth
{"AUS Eastern Standard Time", {"AEST", "AEDT"}}, // Australia/Sydney
{"UTC-11", {"-11", "-11"}}, // Etc/GMT+11
{"Dateline Standard Time", {"-12", "-12"}}, // Etc/GMT+12
{"UTC-02", {"-02", "-02"}}, // Etc/GMT+2
{"UTC-08", {"-08", "-08"}}, // Etc/GMT+8
{"UTC-09", {"-09", "-09"}}, // Etc/GMT+9
{"UTC+12", {"+12", "+12"}}, // Etc/GMT-12
{"UTC+13", {"+13", "+13"}}, // Etc/GMT-13
{"UTC", {"UTC", "UTC"}}, // Etc/UTC
{"Astrakhan Standard Time", {"+04", "+04"}}, // Europe/Astrakhan
{"W. Europe Standard Time", {"CET", "CEST"}}, // Europe/Berlin
{"GTB Standard Time", {"EET", "EEST"}}, // Europe/Bucharest
{"Central Europe Standard Time", {"CET", "CEST"}}, // Europe/Budapest
{"E. Europe Standard Time", {"EET", "EEST"}}, // Europe/Chisinau
{"Turkey Standard Time", {"+03", "+03"}}, // Europe/Istanbul
{"Kaliningrad Standard Time", {"EET", "EET"}}, // Europe/Kaliningrad
{"FLE Standard Time", {"EET", "EEST"}}, // Europe/Kiev
{"GMT Standard Time", {"GMT", "BST"}}, // Europe/London
{"Belarus Standard Time", {"+03", "+03"}}, // Europe/Minsk
{"Russian Standard Time", {"MSK", "MSK"}}, // Europe/Moscow
{"Romance Standard Time", {"CET", "CEST"}}, // Europe/Paris
{"Russia Time Zone 3", {"+04", "+04"}}, // Europe/Samara
{"Saratov Standard Time", {"+04", "+04"}}, // Europe/Saratov
{"Volgograd Standard Time", {"MSK", "MSK"}}, // Europe/Volgograd
{"Central European Standard Time", {"CET", "CEST"}}, // Europe/Warsaw
{"Mauritius Standard Time", {"+04", "+04"}}, // Indian/Mauritius
{"Samoa Standard Time", {"+13", "+13"}}, // Pacific/Apia
{"New Zealand Standard Time", {"NZST", "NZDT"}}, // Pacific/Auckland
{"Bougainville Standard Time", {"+11", "+11"}}, // Pacific/Bougainville
{"Chatham Islands Standard Time", {"+1245", "+1345"}}, // Pacific/Chatham
{"Easter Island Standard Time", {"-06", "-05"}}, // Pacific/Easter
{"Fiji Standard Time", {"+12", "+12"}}, // Pacific/Fiji
{"Central Pacific Standard Time", {"+11", "+11"}}, // Pacific/Guadalcanal
{"Hawaiian Standard Time", {"HST", "HST"}}, // Pacific/Honolulu
{"Line Islands Standard Time", {"+14", "+14"}}, // Pacific/Kiritimati
{"Marquesas Standard Time", {"-0930", "-0930"}}, // Pacific/Marquesas
{"Norfolk Standard Time", {"+11", "+12"}}, // Pacific/Norfolk
{"West Pacific Standard Time", {"+10", "+10"}}, // Pacific/Port_Moresby
{"Tonga Standard Time", {"+13", "+13"}}, // Pacific/Tongatapu
}
iana_to_windows_tz :: proc(iana_name: string, allocator := context.allocator) -> (name: string, success: bool) {
@@ -269,7 +270,18 @@ _region_load :: proc(reg_str: string, allocator := context.allocator) -> (out_re
defer delete(wintz_name, allocator)
defer delete(iana_name, allocator)
abbrevs := tz_abbrevs[wintz_name] or_return
abbrevs: TZ_Abbrev
abbrevs_ok: bool
for pair in tz_abbrevs {
if pair.key == wintz_name {
abbrevs = pair.value
abbrevs_ok = true
break
}
}
if !abbrevs_ok {
return
}
if abbrevs.std == "UTC" && abbrevs.dst == abbrevs.std {
return nil, true
}