mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-29 19:00:06 +00:00
Merge branch 'odin-lang:master' into master
This commit is contained in:
+230
-148
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -439,7 +439,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1882,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,
|
||||
@@ -1944,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
|
||||
}
|
||||
@@ -1962,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]
|
||||
@@ -1970,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])
|
||||
@@ -1985,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
|
||||
@@ -2021,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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2042,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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2090,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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2111,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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
+58
-36
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
@@ -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 ---
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ---
|
||||
|
||||
+153
-141
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user