mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-14 09:44:26 +00:00
Merge branch 'master' into bill/odin-windows-amd64-abi-improvements
This commit is contained in:
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -10,3 +10,7 @@ vendor/sdl3/SDL3.dll filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/sdl3/SDL3.lib filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/sdl3/mixer/*.dll filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/sdl3/mixer/*.lib filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.dll filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.dll.lib filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.lib filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.pdb filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -140,8 +140,9 @@ jobs:
|
||||
- name: Optimized Core library tests
|
||||
run: ./odin test tests/core/speed.odin -o:speed -file -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -sanitize:address
|
||||
- name: Wycheproof tests
|
||||
|
||||
run: ./odin test tests/core/crypto/wycheproof -vet -vet-tabs -strict-style -vet-style -vet-cast -warnings-as-errors -disallow-do -o:speed
|
||||
- name: Noise Protocol Framework tests
|
||||
run: ./odin test tests/core/crypto/noise -vet -vet-tabs -strict-style -vet-style -vet-cast -warnings-as-errors -disallow-do -o:speed
|
||||
- name: Vendor library tests
|
||||
run: ./odin test tests/vendor -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -sanitize:address
|
||||
if: matrix.os != 'macos-15-intel' && matrix.os != 'macos-latest'
|
||||
@@ -244,6 +245,11 @@ jobs:
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
|
||||
odin test tests/core/crypto/wycheproof -vet -vet-tabs -strict-style -vet-style -vet-cast -warnings-as-errors -disallow-do -o:speed -microarch:native
|
||||
- name: Noise Protocol Framework tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
|
||||
odin test tests/core/crypto/noise -vet -vet-tabs -strict-style -vet-style -vet-cast -warnings-as-errors -disallow-do -o:speed -microarch:native
|
||||
- name: Vendor library tests
|
||||
shell: cmd
|
||||
run: |
|
||||
|
||||
@@ -23,6 +23,12 @@ HAS_HARDWARE_SIMD :: false when (ODIN_ARCH == .amd64 || ODIN_ARCH == .i386) && !
|
||||
false when (ODIN_ARCH == .riscv64) && !intrinsics.has_target_feature("v") else
|
||||
true
|
||||
|
||||
// Size of a native SIMD register for the current compilation target
|
||||
NATIVE_SIMD_BIT_WIDTH ::
|
||||
512 when (ODIN_ARCH == .amd64) && intrinsics.has_target_feature("avx512f") else
|
||||
256 when (ODIN_ARCH == .amd64) && (intrinsics.has_target_feature("avx2") || intrinsics.has_target_feature("avx")) else
|
||||
// Fallback for no hardware SIMD, but also SSE, NEON, SVE, RVV and WASM SIMD128.
|
||||
128
|
||||
|
||||
@(private)
|
||||
byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte #no_bounds_check {
|
||||
|
||||
@@ -375,7 +375,7 @@ array_pop_safe :: proc(x: ^$X/Array($T, $SHIFT)) -> (val: T, ok: bool) {
|
||||
|
||||
Example:
|
||||
|
||||
import "core:encoding/xar"
|
||||
import "core:container/xar"
|
||||
|
||||
unordered_remove_example :: proc() {
|
||||
x: xar.Array(int, 4)
|
||||
|
||||
@@ -16,7 +16,8 @@ HAS_RAND_BYTES :: runtime.HAS_RAND_BYTES
|
||||
//
|
||||
// The execution time of this routine is constant regardless of the contents
|
||||
// of the slices being compared, as long as the length of the slices is equal.
|
||||
// If the length of the two slices is dif and only if (⟺)erent, it will early-return 0.
|
||||
// If and only if (⟺) the length of the two slices is diferent, it will
|
||||
// early-return 0.
|
||||
compare_constant_time :: proc "contextless" (a, b: []byte) -> int {
|
||||
// If the length of the slices is different, early return.
|
||||
//
|
||||
|
||||
@@ -222,6 +222,40 @@ private_key_generate_public :: proc(priv_key: ^Private_Key) {
|
||||
priv_key._pub_key._curve = priv_key._curve
|
||||
}
|
||||
|
||||
// private_key_set sets priv_key to src.
|
||||
private_key_set :: proc(priv_key, src: ^Private_Key) {
|
||||
if src == nil || src._curve == .Invalid {
|
||||
private_key_clear(priv_key)
|
||||
return
|
||||
}
|
||||
|
||||
priv_key._curve = src._curve
|
||||
|
||||
reflect.set_union_variant_typeid(
|
||||
priv_key._impl,
|
||||
_PRIV_IMPL_IDS[priv_key._curve],
|
||||
)
|
||||
|
||||
#partial switch priv_key._curve {
|
||||
case .SECP256R1:
|
||||
secec.sc_set(&priv_key._impl.(secec.Scalar_p256r1), &src._impl.(secec.Scalar_p256r1))
|
||||
case .SECP384R1:
|
||||
secec.sc_set(&priv_key._impl.(secec.Scalar_p384r1), &src._impl.(secec.Scalar_p384r1))
|
||||
case .X25519:
|
||||
priv_buf := &(priv_key._impl.(X25519_Buf))
|
||||
src_buf := &(src._impl.(X25519_Buf))
|
||||
copy(priv_buf[:], src_buf[:])
|
||||
case .X448:
|
||||
priv_buf := &(priv_key._impl.(X448_Buf))
|
||||
src_buf := &(src._impl.(X448_Buf))
|
||||
copy(priv_buf[:], src_buf[:])
|
||||
case:
|
||||
panic("crypto/ecdh: invalid curve")
|
||||
}
|
||||
|
||||
public_key_set(&priv_key._pub_key, &src._pub_key)
|
||||
}
|
||||
|
||||
// private_key_bytes sets dst to byte-encoding of priv_key.
|
||||
private_key_bytes :: proc(priv_key: ^Private_Key, dst: []byte) {
|
||||
ensure(priv_key._curve != .Invalid, "crypto/ecdh: uninitialized private key")
|
||||
@@ -325,6 +359,16 @@ public_key_set_bytes :: proc(pub_key: ^Public_Key, curve: Curve, b: []byte) -> b
|
||||
return true
|
||||
}
|
||||
|
||||
// public_key_set sets pub_key to src.
|
||||
public_key_set :: proc(pub_key, src: ^Public_Key) {
|
||||
if src == nil || src._curve == .Invalid {
|
||||
public_key_clear(pub_key)
|
||||
return
|
||||
}
|
||||
|
||||
pub_key^ = src^
|
||||
}
|
||||
|
||||
// public_key_set_priv sets pub_key to the public component of priv_key.
|
||||
public_key_set_priv :: proc(pub_key: ^Public_Key, priv_key: ^Private_Key) {
|
||||
ensure(priv_key._curve != .Invalid, "crypto/ecdh: uninitialized private key")
|
||||
|
||||
@@ -194,6 +194,32 @@ private_key_bytes :: proc(priv_key: ^Private_Key, dst: []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
// private_key_set sets priv_key to src.
|
||||
private_key_set :: proc(priv_key, src: ^Private_Key) {
|
||||
if src == nil || src._curve == .Invalid {
|
||||
private_key_clear(priv_key)
|
||||
return
|
||||
}
|
||||
|
||||
priv_key._curve = src._curve
|
||||
|
||||
reflect.set_union_variant_typeid(
|
||||
priv_key._impl,
|
||||
_PRIV_IMPL_IDS[priv_key._curve],
|
||||
)
|
||||
|
||||
#partial switch priv_key._curve {
|
||||
case .SECP256R1:
|
||||
secec.sc_set(&priv_key._impl.(secec.Scalar_p256r1), &src._impl.(secec.Scalar_p256r1))
|
||||
case .SECP384R1:
|
||||
secec.sc_set(&priv_key._impl.(secec.Scalar_p384r1), &src._impl.(secec.Scalar_p384r1))
|
||||
case:
|
||||
panic("crypto/ecdh: invalid curve")
|
||||
}
|
||||
|
||||
public_key_set(&priv_key._pub_key, &src._pub_key)
|
||||
}
|
||||
|
||||
// private_key_equal returns true if and only if (⟺) the private keys are equal,
|
||||
// in constant time.
|
||||
private_key_equal :: proc(p, q: ^Private_Key) -> bool {
|
||||
@@ -262,6 +288,16 @@ public_key_set_bytes :: proc(pub_key: ^Public_Key, curve: Curve, b: []byte) -> b
|
||||
return true
|
||||
}
|
||||
|
||||
// public_key_set sets pub_key to src.
|
||||
public_key_set :: proc(pub_key, src: ^Public_Key) {
|
||||
if src == nil || src._curve == .Invalid {
|
||||
public_key_clear(pub_key)
|
||||
return
|
||||
}
|
||||
|
||||
pub_key^ = src^
|
||||
}
|
||||
|
||||
// public_key_set_priv sets pub_key to the public component of priv_key.
|
||||
public_key_set_priv :: proc(pub_key: ^Public_Key, priv_key: ^Private_Key) {
|
||||
ensure(priv_key._curve != .Invalid, "crypto/ecdsa: uninitialized private key")
|
||||
|
||||
@@ -97,6 +97,21 @@ private_key_set_bytes :: proc(priv_key: ^Private_Key, b: []byte) -> bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// private_key_set sets priv_key to src.
|
||||
private_key_set :: proc(priv_key, src: ^Private_Key) {
|
||||
if src == nil || !src._is_initialized {
|
||||
private_key_clear(priv_key)
|
||||
return
|
||||
}
|
||||
|
||||
copy(priv_key._b[:], src._b[:])
|
||||
grp.sc_set(&priv_key._s, &src._s)
|
||||
copy(priv_key._hdigest2[:], src._hdigest2[:])
|
||||
public_key_set(&priv_key._pub_key, &src._pub_key)
|
||||
|
||||
priv_key._is_initialized = true
|
||||
}
|
||||
|
||||
// private_key_bytes sets dst to byte-encoding of priv_key.
|
||||
private_key_bytes :: proc(priv_key: ^Private_Key, dst: []byte) {
|
||||
ensure(priv_key._is_initialized, "crypto/ed25519: uninitialized private key")
|
||||
@@ -186,6 +201,16 @@ public_key_set_bytes :: proc "contextless" (pub_key: ^Public_Key, b: []byte) ->
|
||||
return true
|
||||
}
|
||||
|
||||
// public_key_set sets pub_key to src.
|
||||
public_key_set :: proc(pub_key, src: ^Public_Key) {
|
||||
if src == nil || !src._is_initialized {
|
||||
public_key_clear(pub_key)
|
||||
return
|
||||
}
|
||||
|
||||
pub_key^ = src^
|
||||
}
|
||||
|
||||
// public_key_set_priv sets pub_key to the public component of priv_key.
|
||||
public_key_set_priv :: proc(pub_key: ^Public_Key, priv_key: ^Private_Key) {
|
||||
ensure(priv_key._is_initialized, "crypto/ed25519: uninitialized private key")
|
||||
@@ -212,6 +237,11 @@ public_key_equal :: proc(pub_key, other: ^Public_Key) -> bool {
|
||||
return crypto.compare_constant_time(pub_key._b[:], other._b[:]) == 1
|
||||
}
|
||||
|
||||
// public_key_clear clears pub_key to the uninitialized state.
|
||||
public_key_clear :: proc "contextless" (pub_key: ^Public_Key) {
|
||||
crypto.zero_explicit(pub_key, size_of(Public_Key))
|
||||
}
|
||||
|
||||
// verify returns true if and only if (⟺) sig is a valid signature by pub_key over msg.
|
||||
//
|
||||
// The optional `allow_small_order_A` parameter will make this
|
||||
|
||||
590
core/crypto/noise/api.odin
Normal file
590
core/crypto/noise/api.odin
Normal file
@@ -0,0 +1,590 @@
|
||||
package noise
|
||||
|
||||
import "base:runtime"
|
||||
import "core:crypto/aead"
|
||||
import "core:crypto/ecdh"
|
||||
import "core:crypto/hash"
|
||||
import "core:strings"
|
||||
|
||||
// MAX_PACKET_SIZE is the maximum Noise message size, including TAG_SIZE
|
||||
// if relevant (`seal_message`, `open_message`).
|
||||
MAX_PACKET_SIZE :: 65535
|
||||
|
||||
// PSK_SIZE is the size of an optional handshake pre-shared symmetric key.
|
||||
PSK_SIZE :: 32
|
||||
// TAG_SIZE is the size of the AEAD authentication tag.
|
||||
TAG_SIZE :: 16
|
||||
// MAX_STEP_MSG_SIZE is the maximum per-handshake step message size,
|
||||
// excluding the optional payload.
|
||||
//
|
||||
// `e` is DH_LEN, `s` is either DH_LEN or DH_LEN + TAG_SIZE, and there
|
||||
// is a maximum of one per each message, and a possible mandatory tag.
|
||||
MAX_STEP_MSG_SIZE :: (MAX_DH_SIZE*2)+TAG_SIZE+TAG_SIZE
|
||||
|
||||
// Status is the status of Noise protocol operation.
|
||||
Status :: enum {
|
||||
Ok,
|
||||
|
||||
// States
|
||||
Handshake_Pending,
|
||||
Handshake_Complete,
|
||||
Handshake_Split,
|
||||
Handshake_Failed,
|
||||
|
||||
// Errors
|
||||
Invalid_Protocol_String,
|
||||
Invalid_Pre_Shared_Key,
|
||||
Invalid_DH_Key,
|
||||
No_Self_Identity,
|
||||
No_Peer_Identity,
|
||||
Unexpected_Peer_Identity,
|
||||
Unexpected_Pre_Shared_Key,
|
||||
|
||||
DH_Failure,
|
||||
Invalid_Handshake_Message,
|
||||
|
||||
Decryption_Failure,
|
||||
IV_Exhausted,
|
||||
Invalid_Cipher_State,
|
||||
Invalid_Destination_Buffer,
|
||||
Invalid_Payload_Message,
|
||||
Max_Packet_Size,
|
||||
|
||||
Out_Of_Memory,
|
||||
}
|
||||
|
||||
// Handshake_State is the per-handshake state.
|
||||
Handshake_State :: struct {
|
||||
s: ecdh.Private_Key,
|
||||
e: ecdh.Private_Key,
|
||||
rs: ecdh.Public_Key,
|
||||
re: ecdh.Public_Key,
|
||||
psk: [PSK_SIZE]byte,
|
||||
|
||||
symmetric_state: Symmetric_State,
|
||||
message_pattern: ^Message_Pattern,
|
||||
current_message: int,
|
||||
|
||||
status: Status,
|
||||
|
||||
initiator: bool,
|
||||
pre_set_e: bool,
|
||||
}
|
||||
|
||||
// Cipher_States are the keyed AEAD instances and associated state,
|
||||
// derived from a successful handshake.
|
||||
Cipher_States :: struct {
|
||||
c1_i_to_r: Cipher_State,
|
||||
c2_r_to_i: Cipher_State,
|
||||
|
||||
initiator: bool,
|
||||
}
|
||||
|
||||
// handshake_init initializes a Handshake_State with the provided parameters.
|
||||
// The relevant values are copied into the Handshake_State instance, and
|
||||
// can be discarded/sanitized right after handshake_init returns (eg: psk).
|
||||
//
|
||||
// Note: While this implementation supports setting `e`, this is primarily
|
||||
// intended for testing, or cases where the runtime cryptographic entropy
|
||||
// source is unavailable. Use of this functionality is STRONGLY
|
||||
// discouraged.
|
||||
@(require_results)
|
||||
handshake_init :: proc(
|
||||
self: ^Handshake_State,
|
||||
initiator: bool,
|
||||
prologue: []byte,
|
||||
s: ^ecdh.Private_Key, // Our static key
|
||||
rs: ^ecdh.Public_Key, // Peer static key
|
||||
protocol_name: string,
|
||||
psk: []byte = nil,
|
||||
_e: ^ecdh.Private_Key = nil, // Our ephemeral key (for testing/RNG-less systems)
|
||||
) -> Status {
|
||||
return handshakestate_initialize(
|
||||
self,
|
||||
initiator,
|
||||
prologue,
|
||||
s,
|
||||
_e,
|
||||
rs,
|
||||
nil,
|
||||
protocol_name,
|
||||
psk,
|
||||
)
|
||||
}
|
||||
|
||||
// handshake_initiator_step takes an input_message received from the responder
|
||||
// if any and an optional payload to be sent to the responder, and performs
|
||||
// one step of the Noise handshake process, returning the message to be sent
|
||||
// to the responder if any, the payload received from the responder if any,
|
||||
// and the status of the handshake.
|
||||
//
|
||||
// The output message MUST be sent to the responder even if the status code
|
||||
// returned is .Handshake_Complete.
|
||||
//
|
||||
// If the dst parameter is provided, the message and payload will be written
|
||||
// to dst, otherwise new buffers will be allocated.
|
||||
@(require_results)
|
||||
handshake_initiator_step :: proc(
|
||||
self: ^Handshake_State,
|
||||
input_message: []byte,
|
||||
payload: []byte = nil,
|
||||
dst: []byte = nil,
|
||||
allocator := context.allocator,
|
||||
) -> ([]byte, []byte, Status) {
|
||||
output_message: []byte
|
||||
payload_buffer: []byte
|
||||
status: Status
|
||||
|
||||
dst := dst
|
||||
if input_message == nil {
|
||||
output_message, status = handshakestate_write_message(self, payload, dst, allocator)
|
||||
} else {
|
||||
payload_buffer, status = handshakestate_read_message(self, input_message, dst, allocator)
|
||||
if status == .Handshake_Pending {
|
||||
if dst != nil {
|
||||
dst = dst[len(payload_buffer):]
|
||||
}
|
||||
output_message, status = handshakestate_write_message(self, payload, dst, allocator)
|
||||
}
|
||||
}
|
||||
|
||||
return output_message, payload_buffer, status
|
||||
}
|
||||
|
||||
// handshake_responder_step takes a input_message received from the initiator,
|
||||
// and and an optional payload to be sent to the initiator, and performs
|
||||
// one step of the Noise handshake process, returning the message to be sent
|
||||
// to the initiator if any, the payload received from the initiator if any,
|
||||
// and the status of the handshake.
|
||||
//
|
||||
// The output message MUST be sent to the initiator even if the status code
|
||||
// returned is .Handshake_Complete.
|
||||
//
|
||||
// If the dst parameter is provided, the message and payload will be written
|
||||
// to dst, otherwise new buffers will be allocated.
|
||||
@(require_results)
|
||||
handshake_responder_step :: proc(
|
||||
self: ^Handshake_State,
|
||||
input_message: []byte,
|
||||
payload: []byte = nil,
|
||||
dst: []byte = nil,
|
||||
allocator := context.allocator,
|
||||
) -> ([]byte, []byte, Status) {
|
||||
output_message: []byte
|
||||
|
||||
if input_message == nil {
|
||||
return nil, nil, .Invalid_Handshake_Message
|
||||
}
|
||||
|
||||
dst := dst
|
||||
payload_buffer, status := handshakestate_read_message(self, input_message, dst, allocator)
|
||||
if status == .Handshake_Pending {
|
||||
if dst != nil {
|
||||
dst = dst[len(payload_buffer):]
|
||||
}
|
||||
output_message, status = handshakestate_write_message(self, payload, dst, allocator)
|
||||
}
|
||||
|
||||
return output_message, payload_buffer, status
|
||||
}
|
||||
|
||||
// handshake_write_message calls the Noise HandshakeState's WriteMessage
|
||||
// function directly. In most cases you are better off using
|
||||
// handshake_initiator_step or handshake_responder_step.
|
||||
//
|
||||
// If the dst parameter is provided, the message and payload will be written
|
||||
// to dst, otherwise new buffers will be allocated.
|
||||
@(require_results)
|
||||
handshake_write_message :: proc(
|
||||
self: ^Handshake_State,
|
||||
payload: []byte,
|
||||
dst: []byte = nil,
|
||||
allocator := context.allocator,
|
||||
) -> ([]byte, Status) {
|
||||
return handshakestate_write_message(self, payload, dst, allocator)
|
||||
}
|
||||
|
||||
// handshake_read_message calls the Noise HandshakeState's ReadMessage
|
||||
// function directly. In most cases you are better off using
|
||||
// handshake_initiator_step or handshake_responder_step.
|
||||
//
|
||||
// If the dst parameter is provided, the message and payload will be written
|
||||
// to dst, otherwise new buffers will be allocated.
|
||||
@(require_results)
|
||||
handshake_read_message :: proc(
|
||||
self: ^Handshake_State,
|
||||
message: []byte,
|
||||
dst: []byte = nil,
|
||||
allocator := context.allocator,
|
||||
) -> ([]byte, Status) {
|
||||
return handshakestate_read_message(self, message, dst, allocator)
|
||||
}
|
||||
|
||||
// handshake_split initializes a Cipher_States instance from a completed
|
||||
// handshake. This can be called once and only once per Handshake_State
|
||||
// instance.
|
||||
@(require_results)
|
||||
handshake_split :: proc(self: ^Handshake_State, cipher_states: ^Cipher_States) -> Status {
|
||||
if self.status != .Handshake_Complete {
|
||||
return self.status
|
||||
}
|
||||
|
||||
symmetricstate_split(&self.symmetric_state, cipher_states)
|
||||
if self.message_pattern.is_one_way {
|
||||
cipherstate_reset(&cipher_states.c2_r_to_i)
|
||||
cipher_states.c2_r_to_i.is_invalid = true
|
||||
}
|
||||
cipher_states.initiator = self.initiator
|
||||
self.status = .Handshake_Split
|
||||
|
||||
return .Ok
|
||||
}
|
||||
|
||||
// handshake_peer_identity returns the peer's static DH key used by
|
||||
// a completed handshake.
|
||||
//
|
||||
// This returns a pointer to the Handshake_State's copy of the peer's
|
||||
// public key, that will get wiped by handshake_reset. If the key is
|
||||
// needed after a call to handshake_reset, it must be copied.
|
||||
@(require_results)
|
||||
handshake_peer_identity :: proc(self: ^Handshake_State) -> (^ecdh.Public_Key, Status) {
|
||||
#partial switch self.status {
|
||||
case .Handshake_Complete, .Handshake_Split:
|
||||
case:
|
||||
return nil, self.status
|
||||
}
|
||||
|
||||
if ecdh.curve(&self.rs) == .Invalid {
|
||||
return nil, .No_Peer_Identity
|
||||
}
|
||||
|
||||
return &self.rs, .Ok
|
||||
}
|
||||
|
||||
// handshake_hash returns the handshake transcript hash of a completed
|
||||
// handshake, for the purposes of channel binding. See 11.2 of the
|
||||
// specification for details on usage.
|
||||
//
|
||||
// This returns a slice to an internal buffer that will get wiped by
|
||||
// handshake_reset. If the hash is needed after a call to handshake_reset,
|
||||
// the slice must be copied.
|
||||
@(require_results)
|
||||
handshake_hash :: proc(self: ^Handshake_State) -> ([]byte, Status) {
|
||||
#partial switch self.status {
|
||||
case .Handshake_Complete, .Handshake_Split:
|
||||
case:
|
||||
return nil, self.status
|
||||
}
|
||||
|
||||
return symmetricstate_get_handshake_hash(&self.symmetric_state), .Ok
|
||||
}
|
||||
|
||||
// handshake_reset sanitizes the Handshake_State. It is both safe and
|
||||
// recommended to call this as soon as practical (after any calls to
|
||||
// handshake_peer_identity, handshake_hash, and handshake_split are
|
||||
// complete).
|
||||
handshake_reset :: proc(self: ^Handshake_State) {
|
||||
handshakestate_reset(self)
|
||||
}
|
||||
|
||||
// seal_message encrypts the provided data, authenticates the aad and
|
||||
// ciphertext, and returns the resulting ciphertext. The ciphertext
|
||||
// will ALWAYS be `len(plaintext) + TAG_SIZE` bytes in length.
|
||||
//
|
||||
// If the dst parameter is provided, the ciphertext will be written
|
||||
// to dst, otherwise a new buffer will be allocated.
|
||||
@(require_results)
|
||||
seal_message :: proc(self: ^Cipher_States, aad, plaintext: []byte, dst: []byte = nil, allocator := context.allocator) -> ([]byte, Status) {
|
||||
data_len := len(plaintext)
|
||||
|
||||
dst := dst
|
||||
did_alloc: bool
|
||||
switch {
|
||||
case dst == nil:
|
||||
err: runtime.Allocator_Error
|
||||
dst, err = make([]byte, data_len + TAG_SIZE, allocator)
|
||||
if err != nil {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
did_alloc = true
|
||||
case:
|
||||
if len(dst) != data_len + TAG_SIZE {
|
||||
return nil, .Invalid_Destination_Buffer
|
||||
}
|
||||
}
|
||||
|
||||
status: Status
|
||||
switch self.initiator {
|
||||
case true:
|
||||
dst, status = cipherstate_encrypt_with_ad(&self.c1_i_to_r, aad, plaintext, dst)
|
||||
case false:
|
||||
dst, status = cipherstate_encrypt_with_ad(&self.c2_r_to_i, aad, plaintext, dst)
|
||||
}
|
||||
if status != .Ok && did_alloc {
|
||||
delete(dst, allocator)
|
||||
dst = nil
|
||||
}
|
||||
|
||||
return dst, status
|
||||
}
|
||||
|
||||
// open_message authenticates the aad and ciphertext, decrypts the
|
||||
// ciphertext and returns the resulting plaintext. The plaintext will
|
||||
// ALWAYS be `len(ciphertext) - TAG_SIZE` bytes in length.
|
||||
//
|
||||
// If the dst parameter is provided, the plaintext will be written to
|
||||
// dst, otherwise a new buffer will be allocated.
|
||||
@(require_results)
|
||||
open_message :: proc(self: ^Cipher_States, aad, ciphertext: []byte, dst: []byte = nil, allocator := context.allocator) -> ([]byte, Status) {
|
||||
if len(ciphertext) < TAG_SIZE {
|
||||
return nil, .Invalid_Payload_Message
|
||||
}
|
||||
|
||||
data_len := len(ciphertext) - TAG_SIZE
|
||||
|
||||
dst := dst
|
||||
did_alloc: bool
|
||||
switch {
|
||||
case dst == nil:
|
||||
if data_len > 0 {
|
||||
err: runtime.Allocator_Error
|
||||
dst, err = make([]byte, data_len, allocator)
|
||||
if err != nil {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
did_alloc = true
|
||||
}
|
||||
case:
|
||||
if len(dst) != data_len {
|
||||
return nil, .Invalid_Destination_Buffer
|
||||
}
|
||||
}
|
||||
|
||||
status: Status
|
||||
switch self.initiator {
|
||||
case true:
|
||||
dst, status = cipherstate_decrypt_with_ad(&self.c2_r_to_i, aad, ciphertext, dst)
|
||||
case false:
|
||||
dst, status = cipherstate_decrypt_with_ad(&self.c1_i_to_r, aad, ciphertext, dst)
|
||||
}
|
||||
if status != .Ok && did_alloc {
|
||||
delete(dst, allocator)
|
||||
dst = nil
|
||||
}
|
||||
|
||||
return dst, status
|
||||
}
|
||||
|
||||
// cipherstates_rekey updates the selected AEAD key, using a one way function.
|
||||
// See 11.3 of the specification for examples of usage.
|
||||
//
|
||||
// Note: If one side updates the seal_key, the other side must update
|
||||
// the non-seal_key and vice versa.
|
||||
@(require_results)
|
||||
cipherstates_rekey :: proc(self: ^Cipher_States, seal_key: bool) -> Status {
|
||||
cs := cipherstates_cs(self, seal_key)
|
||||
if cs.is_invalid {
|
||||
return .Invalid_Cipher_State
|
||||
}
|
||||
if !cipherstate_has_key(cs) {
|
||||
return .Handshake_Pending
|
||||
}
|
||||
|
||||
cipherstate_rekey(cs)
|
||||
|
||||
return .Ok
|
||||
}
|
||||
|
||||
// cipherstates_set_n sets the interal counter used to generate the AEAD
|
||||
// IV to an explicit value. This can be used to deal with out-of-order
|
||||
// transport messages. See 11.4 of the specification.
|
||||
//
|
||||
// WARNING: Reusing n across different aad/messages with the same Cipher_States
|
||||
// will result in catastrophic loss of security.
|
||||
@(require_results)
|
||||
cipherstates_set_n :: proc(self: ^Cipher_States, seal_key: bool, n: u64) -> Status {
|
||||
cs := cipherstates_cs(self, seal_key)
|
||||
if cs.is_invalid {
|
||||
return .Invalid_Cipher_State
|
||||
}
|
||||
if !cipherstate_has_key(cs) {
|
||||
return .Handshake_Pending
|
||||
}
|
||||
|
||||
cs.n = n
|
||||
|
||||
return .Ok
|
||||
}
|
||||
|
||||
// cipherstates_n returns the interal counter used to generate the AEAD
|
||||
// IV. This can be used to deal with out-of-order transport messages.
|
||||
// See 11.4 of the specification.
|
||||
//
|
||||
// WARNING: Reusing n across different aad/messages with the same Cipher_States
|
||||
// will result in catastrophic loss of security.
|
||||
@(require_results)
|
||||
cipherstates_n :: proc(self: ^Cipher_States, seal_key: bool, n: u64) -> (u64, Status) {
|
||||
cs := cipherstates_cs(self, seal_key)
|
||||
if cs.is_invalid {
|
||||
return 0, .Invalid_Cipher_State
|
||||
}
|
||||
if !cipherstate_has_key(cs) {
|
||||
return 0, .Handshake_Pending
|
||||
}
|
||||
|
||||
return cs.n, .Ok
|
||||
}
|
||||
|
||||
// cipherstates_reset sanitizes the Cipher_States.
|
||||
cipherstates_reset :: proc(self: ^Cipher_States) {
|
||||
self.initiator = false
|
||||
cipherstate_reset(&self.c1_i_to_r)
|
||||
cipherstate_reset(&self.c2_r_to_i)
|
||||
}
|
||||
|
||||
@(private = "file")
|
||||
cipherstates_cs :: proc(self: ^Cipher_States, seal_key: bool) -> ^Cipher_State {
|
||||
switch self.initiator {
|
||||
case true:
|
||||
switch seal_key {
|
||||
case true:
|
||||
return &self.c1_i_to_r
|
||||
case false:
|
||||
return &self.c2_r_to_i
|
||||
}
|
||||
case false:
|
||||
switch seal_key {
|
||||
case true:
|
||||
return &self.c2_r_to_i
|
||||
case false:
|
||||
return &self.c1_i_to_r
|
||||
}
|
||||
}
|
||||
unreachable()
|
||||
}
|
||||
|
||||
// split_protocol_string splits a protocol string into individual components.
|
||||
@(require_results)
|
||||
split_protocol_string :: proc(protocol_name: string) -> (Handshake_Pattern, ecdh.Curve, aead.Algorithm, hash.Algorithm, Status) {
|
||||
str := protocol_name
|
||||
|
||||
if len(str) > 255 {
|
||||
return .Invalid, .Invalid, .Invalid, .Invalid, .Invalid_Protocol_String
|
||||
}
|
||||
|
||||
s, ok := strings.split_by_byte_iterator(&str, '_')
|
||||
if !ok || s != "Noise" {
|
||||
return .Invalid, .Invalid, .Invalid, .Invalid, .Invalid_Protocol_String
|
||||
}
|
||||
|
||||
if s, ok = strings.split_by_byte_iterator(&str, '_'); !ok {
|
||||
return .Invalid, .Invalid, .Invalid, .Invalid, .Invalid_Protocol_String
|
||||
}
|
||||
|
||||
pattern: Handshake_Pattern
|
||||
switch s {
|
||||
case "N" : pattern = .N
|
||||
case "K" : pattern = .K
|
||||
case "X" : pattern = .X
|
||||
case "XX": pattern = .XX
|
||||
case "NK": pattern = .NK
|
||||
case "NN": pattern = .NN
|
||||
case "KN": pattern = .KN
|
||||
case "KK": pattern = .KK
|
||||
case "NX": pattern = .NX
|
||||
case "KX": pattern = .KX
|
||||
case "XN": pattern = .XN
|
||||
case "IN": pattern = .IN
|
||||
case "XK": pattern = .XK
|
||||
case "IK": pattern = .IK
|
||||
case "IX": pattern = .IX
|
||||
case "NK1": pattern = .NK1
|
||||
case "NX1": pattern = .NX1
|
||||
case "X1N": pattern = .X1N
|
||||
case "X1K": pattern = .X1K
|
||||
case "XK1": pattern = .XK1
|
||||
case "X1K1": pattern = .X1K1
|
||||
case "X1X": pattern = .X1X
|
||||
case "XX1": pattern = .XX1
|
||||
case "X1X1": pattern = .X1X1
|
||||
case "K1N": pattern = .K1N
|
||||
case "K1K": pattern = .K1K
|
||||
case "KK1": pattern = .KK1
|
||||
case "K1K1": pattern = .K1K1
|
||||
case "K1X": pattern = .K1X
|
||||
case "KX1": pattern = .KX1
|
||||
case "K1X1": pattern = .K1X1
|
||||
case "I1N": pattern = .I1N
|
||||
case "I1K": pattern = .I1K
|
||||
case "IK1": pattern = .IK1
|
||||
case "I1K1": pattern = .I1K1
|
||||
case "I1X": pattern = .I1X
|
||||
case "IX1": pattern = .IX1
|
||||
case "I1X1": pattern = .I1X1
|
||||
case "Npsk0": pattern = .Npsk0
|
||||
case "Kpsk0": pattern = .Kpsk0
|
||||
case "Xpsk1": pattern = .Xpsk1
|
||||
case "NNpsk0": pattern = .NNpsk0
|
||||
case "NNpsk2": pattern = .NNpsk2
|
||||
case "NKpsk0": pattern = .NKpsk0
|
||||
case "NKpsk2": pattern = .NKpsk2
|
||||
case "NXpsk2": pattern = .NXpsk2
|
||||
case "XNpsk3": pattern = .XNpsk3
|
||||
case "XKpsk3": pattern = .XKpsk3
|
||||
case "XXpsk3": pattern = .XXpsk3
|
||||
case "KNpsk0": pattern = .KNpsk0
|
||||
case "KNpsk2": pattern = .KNpsk2
|
||||
case "KKpsk0": pattern = .KKpsk0
|
||||
case "KKpsk2": pattern = .KKpsk2
|
||||
case "KXpsk2": pattern = .KXpsk2
|
||||
case "INpsk1": pattern = .INpsk1
|
||||
case "INpsk2": pattern = .INpsk2
|
||||
case "IKpsk1": pattern = .IKpsk1
|
||||
case "IKpsk2": pattern = .IKpsk2
|
||||
case "IXpsk2": pattern = .IXpsk2
|
||||
case: pattern = .Invalid
|
||||
}
|
||||
|
||||
if s, ok = strings.split_by_byte_iterator(&str, '_'); !ok {
|
||||
return .Invalid, .Invalid, .Invalid, .Invalid, .Invalid_Protocol_String
|
||||
}
|
||||
dh: ecdh.Curve
|
||||
switch s {
|
||||
case "25519": dh = .X25519
|
||||
case "448": dh = .X448
|
||||
case: dh = .Invalid
|
||||
}
|
||||
|
||||
if s, ok = strings.split_by_byte_iterator(&str, '_'); !ok {
|
||||
return .Invalid, .Invalid, .Invalid, .Invalid, .Invalid_Protocol_String
|
||||
}
|
||||
cipher: aead.Algorithm
|
||||
switch s {
|
||||
case "AESGCM": cipher = .AES_GCM_256
|
||||
case "ChaChaPoly": cipher = .CHACHA20POLY1305
|
||||
case: cipher = .Invalid
|
||||
}
|
||||
|
||||
if s, ok = strings.split_by_byte_iterator(&str, '_'); !ok {
|
||||
return .Invalid, .Invalid, .Invalid, .Invalid, .Invalid_Protocol_String
|
||||
}
|
||||
hash: hash.Algorithm
|
||||
switch s {
|
||||
case "SHA512": hash = .SHA512
|
||||
case "SHA256": hash = .SHA256
|
||||
case "BLAKE2s": hash = .BLAKE2S
|
||||
case "BLAKE2b": hash = .BLAKE2B
|
||||
case: hash = .Invalid
|
||||
}
|
||||
|
||||
status: Status
|
||||
|
||||
if len(str) != 0 {
|
||||
status = .Invalid_Protocol_String
|
||||
}
|
||||
if pattern == .Invalid || dh == .Invalid || cipher == .Invalid || hash == .Invalid {
|
||||
status = .Invalid_Protocol_String
|
||||
}
|
||||
|
||||
return pattern, dh, cipher, hash, status
|
||||
}
|
||||
35
core/crypto/noise/doc.odin
Normal file
35
core/crypto/noise/doc.odin
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
An implementation of the Noise Protocol Framework (Revision 34).
|
||||
|
||||
The `fallback` modifier and multi-PSK patterns are not supported
|
||||
for the sake of simplicity.
|
||||
|
||||
See:
|
||||
- [[ https://noiseprotocol.org/ ]]
|
||||
*/
|
||||
package noise
|
||||
|
||||
// In general, to complete a noise handshake you must:
|
||||
//
|
||||
// - If you are initiating the connection, call `handshake_initiator_step`
|
||||
// passing `nil` as the `input_message` parameter.
|
||||
//
|
||||
// - Send the resulting `[]byte` to the responder (generally a server) via
|
||||
// the method of your choice. This MUST be done even if the status code
|
||||
// returned is `.Handshake_Complete`.
|
||||
//
|
||||
// - If the status code returned by `handshake_initiator_step` was
|
||||
// `.Handshake_Complete`, the handshake completed successfully,
|
||||
// and it is now possible to validate the peer identity, obtain the
|
||||
// handshake transcript hash, and most usefully call `handshake_split`
|
||||
// to populate the `Cipher_States` struct that will be used to
|
||||
// encrypt/decrypt data.
|
||||
//
|
||||
// Otherwise, read the response from the responder and feed the response
|
||||
// data as the `input_message` to the next `handshake_initiator_step`
|
||||
// until it returns `.Handshake_Complete`.
|
||||
//
|
||||
// - If you are the responder, the method is much the same, except you
|
||||
// must pass a valid `input_message` received from an initiator to the
|
||||
// first call to `handshake_responder_step`. Repeat until the returned
|
||||
// status is `.Handshake_Complete`.
|
||||
1098
core/crypto/noise/patterns.odin
Normal file
1098
core/crypto/noise/patterns.odin
Normal file
File diff suppressed because it is too large
Load Diff
982
core/crypto/noise/protocol.odin
Normal file
982
core/crypto/noise/protocol.odin
Normal file
@@ -0,0 +1,982 @@
|
||||
#+private
|
||||
package noise
|
||||
|
||||
import "base:runtime"
|
||||
import "core:crypto"
|
||||
import "core:crypto/aead"
|
||||
import "core:crypto/ecdh"
|
||||
import "core:crypto/hash"
|
||||
import "core:crypto/hkdf"
|
||||
import "core:encoding/endian"
|
||||
import "core:slice"
|
||||
|
||||
AEAD_KEY_SIZE :: 32
|
||||
|
||||
MIN_DH_SIZE :: 32
|
||||
MAX_DH_SIZE :: 56
|
||||
MAX_HASH_SIZE :: 64
|
||||
|
||||
Protocol :: struct {
|
||||
handshake_pattern: Handshake_Pattern,
|
||||
dh: ecdh.Curve,
|
||||
cipher: aead.Algorithm,
|
||||
hash: hash.Algorithm,
|
||||
}
|
||||
|
||||
Symmetric_State :: struct {
|
||||
protocol: Protocol,
|
||||
cipher_state: Cipher_State,
|
||||
|
||||
_ck: [MAX_HASH_SIZE]byte,
|
||||
_h: [MAX_HASH_SIZE]byte,
|
||||
}
|
||||
|
||||
Cipher_State :: struct {
|
||||
ctx: aead.Context,
|
||||
n: u64,
|
||||
n_exhausted: bool,
|
||||
is_invalid: bool,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
dh_len :: proc(protocol: ^Protocol) -> int {
|
||||
return ecdh.PUBLIC_KEY_SIZES[protocol.dh]
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
hash_len :: proc(protocol: ^Protocol) -> int {
|
||||
return hash.DIGEST_SIZES[protocol.hash]
|
||||
}
|
||||
|
||||
// Generates a new Diffie-Hellman key pair. A DH key pair consists of
|
||||
// public_key and private_key elements. public_key represents an encoding
|
||||
// of a DH public key into a byte sequence of length DHLEN. The public_key
|
||||
// encoding details are specific to each set of DH functions.
|
||||
generate_keypair :: proc(protocol: ^Protocol, private_key: ^ecdh.Private_Key) {
|
||||
#partial switch protocol.dh {
|
||||
case .X25519, .X448:
|
||||
case: panic("crypto/noise: unsupported DH curve in protocol")
|
||||
}
|
||||
|
||||
ecdh.private_key_generate(private_key, protocol.dh)
|
||||
}
|
||||
|
||||
// Performs a Diffie-Hellman calculation between the private key in key_pair
|
||||
// and the public_key and returns an output sequence of bytes of length DHLEN.
|
||||
// For security, the Gap-DH problem based on this function must be unsolvable
|
||||
// by any practical cryptanalytic adversary [2].
|
||||
//
|
||||
// The public_key either encodes some value which is a generator in a
|
||||
// large prime-order group (which value may have multiple equivalent
|
||||
// encodings), or is an invalid value. Implementations must handle invalid
|
||||
// public keys either by returning some output which is purely a function
|
||||
// of the public key and does not depend on the private key, or by signaling
|
||||
// an error to the caller.
|
||||
//
|
||||
// The DH function may define more specific rules for handling invalid values.
|
||||
@(require_results)
|
||||
_dh :: proc(our_private_key: ^ecdh.Private_Key, their_public_key: ^ecdh.Public_Key, dst: []byte) -> Status {
|
||||
if ok := ecdh.ecdh(our_private_key, their_public_key, dst); !ok {
|
||||
return .DH_Failure
|
||||
}
|
||||
return .Ok
|
||||
}
|
||||
|
||||
// Encrypts plaintext using the cipher key k of 32 bytes and an 8-byte
|
||||
// unsigned integer nonce n which must be unique for the key k.
|
||||
// Returns the ciphertext. Encryption must be done with an "AEAD"
|
||||
// encryption mode with the associated data(AD) (using the terminology
|
||||
// from [1]) and returns a ciphertext that is the same size as the plaintext
|
||||
// plus 16 bytes for authentication data. The entire ciphertext must be
|
||||
// indistinguishable from random if the key is secret (note that this is
|
||||
// an additional requirement that isn't necessarily met by all AEAD schemes).
|
||||
_encrypt :: proc(ctx: ^aead.Context, n: u64, ad, plaintext, dst: []byte) {
|
||||
pt_len := len(plaintext)
|
||||
ensure(len(dst) == pt_len + TAG_SIZE, "crypto/noise: invalid AEAD encrypt destination")
|
||||
|
||||
iv: [12]byte
|
||||
#partial switch aead.algorithm(ctx) {
|
||||
case .AES_GCM_256: endian.unchecked_put_u64be(iv[4:], n)
|
||||
case .CHACHA20POLY1305: endian.unchecked_put_u64le(iv[4:], n)
|
||||
}
|
||||
|
||||
ciphertext, tag := dst[:pt_len], dst[pt_len:]
|
||||
aead.seal_ctx(ctx, ciphertext, tag, iv[:], ad, plaintext)
|
||||
}
|
||||
|
||||
// Decrypts ciphertext using a cipher key k of 32 bytes, an 8-byte unsigned
|
||||
// integer nonce n, and associated data ad. Returns the plaintext, unless
|
||||
// authentication fails, in which case an error is signaled to the caller.
|
||||
@(require_results)
|
||||
_decrypt :: proc(ctx: ^aead.Context, n: u64, ad, ciphertext, dst: []byte) -> Status {
|
||||
if len(ciphertext) < TAG_SIZE {
|
||||
return .Decryption_Failure
|
||||
}
|
||||
|
||||
iv: [12]byte
|
||||
#partial switch aead.algorithm(ctx) {
|
||||
case .AES_GCM_256: endian.unchecked_put_u64be(iv[4:], n)
|
||||
case .CHACHA20POLY1305: endian.unchecked_put_u64le(iv[4:], n)
|
||||
}
|
||||
|
||||
ct_len := len(ciphertext) - TAG_SIZE
|
||||
ct, tag := ciphertext[:ct_len], ciphertext[ct_len:]
|
||||
if ok := aead.open_ctx(ctx, dst, iv[:], ad, ct, tag); !ok {
|
||||
return .Decryption_Failure
|
||||
}
|
||||
|
||||
return .Ok
|
||||
}
|
||||
|
||||
// Hashes some arbitrary-length data with a collision-resistant cryptographic
|
||||
// hash function and returns an output of HASHLEN bytes.
|
||||
_hash :: proc(dst: []byte, protocol: ^Protocol, data: ..[]byte) {
|
||||
ctx: hash.Context
|
||||
hash.init(&ctx, protocol.hash)
|
||||
|
||||
for datum in data {
|
||||
hash.update(&ctx, datum)
|
||||
}
|
||||
|
||||
hash.final(&ctx, dst)
|
||||
}
|
||||
|
||||
// Takes a chaining_key byte sequence of length HASHLEN, and an
|
||||
// input_key_material byte sequence with length either zero bytes,
|
||||
// 32 bytes, or DHLEN bytes. Returns a pair or triple of byte sequences
|
||||
// each of length HASHLEN, depending on whether num_outputs is two or three:
|
||||
// - Sets temp_key = HMAC-HASH(chaining_key, input_key_material).
|
||||
// - Sets output1 = HMAC-HASH(temp_key, byte(0x01)).
|
||||
// - Sets output2 = HMAC-HASH(temp_key, output1 || byte(0x02)).
|
||||
// - If num_outputs == 2 then returns the pair (output1, output2).
|
||||
// - Sets output3 = HMAC-HASH(temp_key, output2 || byte(0x03)).
|
||||
// - Returns the triple (output1, output2, output3).
|
||||
//
|
||||
// Note that temp_key, output1, output2, and output3 are all HASHLEN
|
||||
// bytes in length. Also note that the HKDF() function is simply HKDF
|
||||
// from [4] with the chaining_key as HKDF salt, and zero-length HKDF info.
|
||||
@(require_results)
|
||||
_hkdf :: proc(dst, chaining_key, input_key_material: []byte, protocol: ^Protocol) -> ([]byte, []byte, []byte) {
|
||||
assert(len(input_key_material) == 0 || len(input_key_material) == 32 || len(input_key_material) == dh_len(protocol))
|
||||
|
||||
hkdf.extract_and_expand(protocol.hash, chaining_key, input_key_material, nil, dst)
|
||||
|
||||
h_len := hash_len(protocol)
|
||||
assert(len(dst) == h_len * 2 || len(dst) == h_len * 3)
|
||||
|
||||
r1, r2 := dst[:h_len], dst[h_len:h_len*2]
|
||||
if len(dst) == h_len * 2 {
|
||||
return r1, r2, nil
|
||||
}
|
||||
return r1, r2, dst[h_len*2:]
|
||||
}
|
||||
|
||||
// Sets k = key. Sets n = 0.
|
||||
cipherstate_initialize_key :: proc(self: ^Cipher_State, key: []byte, protocol: ^Protocol) {
|
||||
k_len := len(key)
|
||||
switch {
|
||||
case k_len == 0:
|
||||
// k = empty
|
||||
aead.reset(&self.ctx)
|
||||
self.n = 0
|
||||
case k_len < AEAD_KEY_SIZE:
|
||||
panic("crypto/noise: invalid AEAD key size")
|
||||
case:
|
||||
aead.init(&self.ctx, protocol.cipher, key[:AEAD_KEY_SIZE])
|
||||
self.n = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if k is non-empty, false otherwise.
|
||||
@(require_results)
|
||||
cipherstate_has_key :: proc(self: ^Cipher_State) -> bool {
|
||||
return aead.algorithm(&self.ctx) != .Invalid
|
||||
}
|
||||
|
||||
// If k is non-empty returns ENCRYPT(k, n++, ad, plaintext). Otherwise
|
||||
// returns plaintext.
|
||||
@(require_results)
|
||||
cipherstate_encrypt_with_ad :: proc(self: ^Cipher_State, ad, plaintext, dst: []byte) -> ([]byte, Status) {
|
||||
if self.is_invalid {
|
||||
return nil, .Invalid_Cipher_State
|
||||
}
|
||||
if self.n_exhausted {
|
||||
return nil, .IV_Exhausted
|
||||
}
|
||||
|
||||
pt_len := len(plaintext)
|
||||
if pt_len > MAX_PACKET_SIZE - 16 {
|
||||
return nil, .Max_Packet_Size
|
||||
}
|
||||
|
||||
if cipherstate_has_key(self) {
|
||||
if len(dst) != pt_len + TAG_SIZE {
|
||||
return nil, .Invalid_Destination_Buffer
|
||||
}
|
||||
_encrypt(&self.ctx, self.n, ad, plaintext, dst)
|
||||
self.n += 1
|
||||
if self.n == 0 {
|
||||
self.n_exhausted = true
|
||||
}
|
||||
} else {
|
||||
if len(dst) != pt_len {
|
||||
return nil, .Invalid_Destination_Buffer
|
||||
}
|
||||
if raw_data(dst) != raw_data(plaintext) {
|
||||
copy(dst, plaintext)
|
||||
}
|
||||
}
|
||||
|
||||
return dst, .Ok
|
||||
}
|
||||
|
||||
// If k is non-empty returns DECRYPT(k, n++, ad, ciphertext). Otherwise
|
||||
// returns ciphertext. If an authentication failure occurs in DECRYPT()
|
||||
// then n is not incremented and an error is signaled to the caller.
|
||||
@(require_results)
|
||||
cipherstate_decrypt_with_ad :: proc(self: ^Cipher_State, ad, ciphertext, dst: []byte) -> ([]byte, Status) {
|
||||
if self.is_invalid {
|
||||
return nil, .Invalid_Cipher_State
|
||||
}
|
||||
if self.n_exhausted {
|
||||
return nil, .IV_Exhausted
|
||||
}
|
||||
|
||||
if cipherstate_has_key(self) {
|
||||
if status := _decrypt(&self.ctx, self.n, ad, ciphertext, dst); status != .Ok {
|
||||
return nil, status
|
||||
}
|
||||
self.n += 1
|
||||
if self.n == 0 {
|
||||
self.n_exhausted = true
|
||||
}
|
||||
} else {
|
||||
if len(dst) != len(ciphertext) {
|
||||
return nil, .Invalid_Destination_Buffer
|
||||
}
|
||||
if raw_data(dst) != raw_data(ciphertext) {
|
||||
copy(dst, ciphertext)
|
||||
}
|
||||
}
|
||||
|
||||
return dst, .Ok
|
||||
}
|
||||
|
||||
// Sets k = REKEY(k).
|
||||
cipherstate_rekey :: proc(self: ^Cipher_State) {
|
||||
if cipherstate_has_key(self) {
|
||||
algorithm := aead.algorithm(&self.ctx)
|
||||
|
||||
// The "sensible" way to implement this is to inlike REKEY(k),
|
||||
// so we do.
|
||||
//
|
||||
// Returns a new 32-byte cipher key as a pseudorandom function
|
||||
// of k. If this function is not specifically defined for some
|
||||
// set of cipher functions, then it defaults to returning the
|
||||
// first 32 bytes from `ENCRYPT(k, maxnonce, zerolen, zeros)`,
|
||||
// where maxnonce equals (2^64)-1, zerolen is a zero-length
|
||||
// byte sequence, and zeros is a sequence of 32 bytes filled
|
||||
// with zeros.
|
||||
|
||||
zeroes: [AEAD_KEY_SIZE + TAG_SIZE]byte
|
||||
defer crypto.zero_explicit(&zeroes, size_of(zeroes))
|
||||
|
||||
// 1 2 3 4 5 6 7 8
|
||||
n: u64 = 0xFF_FF_FF_FF_FF_FF_FF_FF
|
||||
_encrypt(&self.ctx, n, nil, zeroes[:AEAD_KEY_SIZE], zeroes[:])
|
||||
aead.init(&self.ctx, algorithm, zeroes[:AEAD_KEY_SIZE])
|
||||
}
|
||||
}
|
||||
|
||||
cipherstate_reset :: proc(self: ^Cipher_State) {
|
||||
aead.reset(&self.ctx)
|
||||
crypto.zero_explicit(self, size_of(Cipher_State))
|
||||
}
|
||||
|
||||
// Takes an arbitrary-length protocol_name byte sequence (see Section 8).
|
||||
// Executes the following steps:
|
||||
// - If protocol_name is less than or equal to HASHLEN bytes in length,
|
||||
// sets h equal to protocol_name with zero bytes appended to make
|
||||
// HASHLEN bytes.
|
||||
// - Otherwise sets h = HASH(protocol_name).
|
||||
// - Sets ck = h.
|
||||
// - Calls InitializeKey(empty).
|
||||
@(require_results)
|
||||
symmetricstate_initialize :: proc(ss: ^Symmetric_State, protocol_name: string) -> Status {
|
||||
if status := protocol_from_string(&ss.protocol, protocol_name); status != .Ok {
|
||||
return status
|
||||
}
|
||||
|
||||
cipherstate_initialize_key(&ss.cipher_state, nil, &ss.protocol)
|
||||
|
||||
h_len := hash_len(&ss.protocol)
|
||||
h := ss._h[:h_len]
|
||||
if len(protocol_name) <= h_len {
|
||||
copy(h, protocol_name)
|
||||
} else {
|
||||
_hash(h, &ss.protocol, transmute([]byte)protocol_name)
|
||||
}
|
||||
|
||||
copy(ss._ck[:h_len], h)
|
||||
|
||||
return .Ok
|
||||
}
|
||||
|
||||
// Sets h = HASH(h || data).
|
||||
symmetricstate_mix_hash :: proc(self: ^Symmetric_State, data: ..[]byte) {
|
||||
h := self._h[:hash_len(&self.protocol)]
|
||||
if len(data) == 1 {
|
||||
_hash(h, &self.protocol, h, data[0])
|
||||
} else if len(data) == 2 {
|
||||
_hash(h, &self.protocol, h, data[0], data[1])
|
||||
} else if len(data) == 3 {
|
||||
_hash(h, &self.protocol, h, data[0], data[1], data[2])
|
||||
} else {
|
||||
panic("crypto/noise: invalid MixHash inputs")
|
||||
}
|
||||
}
|
||||
|
||||
// Executes the following steps:
|
||||
// - Sets ck, temp_k = HKDF(ck, input_key_material, 2).
|
||||
// - If HASHLEN is 64, then truncates temp_k to 32 bytes.
|
||||
// - Calls InitializeKey(temp_k).
|
||||
symmetricstate_mix_key :: proc(self: ^Symmetric_State, input_key_material: []byte) {
|
||||
h_len := hash_len(&self.protocol)
|
||||
|
||||
dst_len := h_len * 2
|
||||
dst: [2*MAX_HASH_SIZE]byte = ---
|
||||
defer crypto.zero_explicit(&dst, dst_len)
|
||||
|
||||
ck, temp_k, _ := _hkdf(dst[:dst_len], self._ck[:h_len], input_key_material, &self.protocol)
|
||||
copy(self._ck[:], ck)
|
||||
cipherstate_initialize_key(&self.cipher_state, temp_k, &self.protocol)
|
||||
}
|
||||
|
||||
// This function is used for handling pre-shared symmetric keys, as described
|
||||
// in Section 9. It executes the following steps:
|
||||
// - Sets ck, temp_h, temp_k = HKDF(ck, input_key_material, 3).
|
||||
// - Calls MixHash(temp_h).
|
||||
// - If HASHLEN is 64, then truncates temp_k to 32 bytes.
|
||||
// - Calls InitializeKey(temp_k).
|
||||
symmetricstate_mix_key_and_hash :: proc(self: ^Symmetric_State, input_key_material: []byte) {
|
||||
h_len := hash_len(&self.protocol)
|
||||
|
||||
dst_len := h_len * 3
|
||||
dst: [3*MAX_HASH_SIZE]byte = ---
|
||||
defer crypto.zero_explicit(&dst, dst_len)
|
||||
|
||||
ck, temp_h, temp_k := _hkdf(dst[:dst_len], self._ck[:h_len], input_key_material, &self.protocol)
|
||||
copy(self._ck[:], ck)
|
||||
symmetricstate_mix_hash(self, temp_h)
|
||||
cipherstate_initialize_key(&self.cipher_state, temp_k, &self.protocol)
|
||||
}
|
||||
|
||||
// Returns h. This function should only be called at the end of a handshake,
|
||||
// i.e. after the Split() function has been called.
|
||||
//
|
||||
// This function is used for channel binding, as described in Section 11.2
|
||||
@(require_results)
|
||||
symmetricstate_get_handshake_hash :: proc(self: ^Symmetric_State) -> []byte {
|
||||
return self._h[:hash_len(&self.protocol)]
|
||||
}
|
||||
|
||||
// Sets ciphertext = EncryptWithAd(h, plaintext), calls MixHash(ciphertext),
|
||||
// and returns ciphertext.
|
||||
//
|
||||
// Note that if k is empty, the EncryptWithAd() call will set ciphertext
|
||||
// equal to plaintext.
|
||||
@(require_results)
|
||||
symmetricstate_encrypt_and_hash :: proc(self: ^Symmetric_State, plaintext, dst: []byte) -> ([]byte, Status) {
|
||||
ciphertext, status := cipherstate_encrypt_with_ad(&self.cipher_state, self._h[:hash_len(&self.protocol)], plaintext, dst)
|
||||
if status != .Ok {
|
||||
return nil, status
|
||||
}
|
||||
symmetricstate_mix_hash(self, ciphertext)
|
||||
return ciphertext, status
|
||||
}
|
||||
|
||||
// Sets plaintext = DecryptWithAd(h, ciphertext), calls MixHash(ciphertext),
|
||||
// and returns plaintext.
|
||||
//
|
||||
// Note that if k is empty, the DecryptWithAd() call will set plaintext
|
||||
// equal to ciphertext.
|
||||
@(require_results)
|
||||
symmetricstate_decrypt_and_hash :: proc(self: ^Symmetric_State, ciphertext, dst: []byte) -> ([]byte, Status) {
|
||||
h_len := hash_len(&self.protocol)
|
||||
|
||||
h: [MAX_HASH_SIZE]byte = ---
|
||||
copy(h[:], self._h[:h_len])
|
||||
defer crypto.zero_explicit(&h, size_of(h))
|
||||
|
||||
// We reverse the order to save having to copy the ciphertext, in
|
||||
// the case that ciphertext and dst alias.
|
||||
symmetricstate_mix_hash(self, ciphertext)
|
||||
return cipherstate_decrypt_with_ad(&self.cipher_state, h[:h_len], ciphertext, dst)
|
||||
}
|
||||
|
||||
// Returns a pair of CipherState objects for encrypting transport messages.
|
||||
// Executes the following steps, where zerolen is a zero-length byte sequence:
|
||||
// - Sets temp_k1, temp_k2 = HKDF(ck, zerolen, 2).
|
||||
// - If HASHLEN is 64, then truncates temp_k1 and temp_k2 to 32 bytes.
|
||||
// - Creates two new CipherState objects c1 and c2.
|
||||
// - Calls c1.InitializeKey(temp_k1) and c2.InitializeKey(temp_k2).
|
||||
// - Returns the pair (c1, c2).
|
||||
symmetricstate_split :: proc(self: ^Symmetric_State, cipher_states: ^Cipher_States) {
|
||||
h_len := hash_len(&self.protocol)
|
||||
|
||||
dst_len := h_len * 2
|
||||
dst: [2*MAX_HASH_SIZE]byte = ---
|
||||
defer crypto.zero_explicit(&dst, dst_len)
|
||||
|
||||
temp_k1, temp_k2, _ := _hkdf(dst[:dst_len], self._ck[:h_len], nil, &self.protocol)
|
||||
cipherstate_initialize_key(&cipher_states.c1_i_to_r, temp_k1, &self.protocol)
|
||||
cipherstate_initialize_key(&cipher_states.c2_r_to_i, temp_k2, &self.protocol)
|
||||
}
|
||||
|
||||
symmetricstate_reset :: proc(self: ^Symmetric_State) {
|
||||
cipherstate_reset(&self.cipher_state)
|
||||
|
||||
crypto.zero_explicit(self, size_of(Symmetric_State))
|
||||
}
|
||||
|
||||
// Takes a valid handshake_pattern (see Section 7) and an initiator boolean
|
||||
// specifying this party's role as either initiator or responder.
|
||||
// Takes a prologue byte sequence which may be zero-length, or which may
|
||||
// contain context information that both parties want to confirm is identical
|
||||
// (see Section 6).
|
||||
//
|
||||
// Takes a set of DH key pairs (s, e) and public keys (rs, re) for
|
||||
// initializing local variables, any of which may be empty. Public keys
|
||||
// are only passed in if the handshake_pattern uses pre-messages
|
||||
// (see Section 7). The ephemeral values (e, re) are typically left empty,
|
||||
// since they are created and exchanged during the handshake; but there
|
||||
// are exceptions (see Section 10).
|
||||
//
|
||||
// Performs the following steps:
|
||||
// - Derives a protocol_name byte sequence by combining the names for
|
||||
// the handshake pattern and crypto functions, as specified in Section 8.
|
||||
// - Calls InitializeSymmetric(protocol_name).
|
||||
// - Calls MixHash(prologue).
|
||||
// - Sets the initiator, s, e, rs, and re variables to the corresponding
|
||||
// arguments.
|
||||
// - Calls MixHash() once for each public key listed in the pre-messages
|
||||
// from handshake_pattern, with the specified public key as input
|
||||
// (see Section 7 for an explanation of pre-messages).
|
||||
// - If both initiator and responder have pre-messages, the initiator's
|
||||
// public keys are hashed first.
|
||||
// - If multiple public keys are listed in either party's pre-message,
|
||||
// the public keys are hashed in the order that they are listed.
|
||||
// - Sets message_pattern to the message patterns from handshake_pattern.
|
||||
@(require_results)
|
||||
handshakestate_initialize :: proc(
|
||||
handshake_state: ^Handshake_State,
|
||||
initiator: bool,
|
||||
prologue: []byte,
|
||||
s: ^ecdh.Private_Key,
|
||||
e: ^ecdh.Private_Key, // Only set for testing.
|
||||
rs: ^ecdh.Public_Key,
|
||||
re: ^ecdh.Public_Key, // Only set for testing.
|
||||
protocol_name: string,
|
||||
psk: []byte = nil,
|
||||
) -> Status {
|
||||
crypto.zero_explicit(handshake_state, size_of(Handshake_State))
|
||||
|
||||
symmetric_state := &handshake_state.symmetric_state
|
||||
status: Status
|
||||
do_init: {
|
||||
if status = symmetricstate_initialize(symmetric_state, protocol_name); status != .Ok {
|
||||
break do_init
|
||||
}
|
||||
|
||||
curve := symmetric_state.protocol.dh
|
||||
if s != nil && ecdh.curve(s) != curve {
|
||||
status = .Invalid_DH_Key
|
||||
break do_init
|
||||
}
|
||||
if e != nil && ecdh.curve(e) != curve {
|
||||
status = .Invalid_DH_Key
|
||||
break do_init
|
||||
}
|
||||
if rs != nil && ecdh.curve(rs) != curve {
|
||||
status = .Invalid_DH_Key
|
||||
break do_init
|
||||
}
|
||||
if re != nil && ecdh.curve(re) != curve {
|
||||
status = .Invalid_DH_Key
|
||||
break do_init
|
||||
}
|
||||
|
||||
// Check if we will require s later down the line.
|
||||
s_pre, s_hs: bool
|
||||
if initiator {
|
||||
s_pre, s_hs = pattern_requires_initiator_s(symmetric_state.protocol.handshake_pattern)
|
||||
} else {
|
||||
s_pre, s_hs = pattern_requires_responder_s(symmetric_state.protocol.handshake_pattern)
|
||||
}
|
||||
if (s_pre || s_hs) && s == nil {
|
||||
status = .No_Self_Identity
|
||||
break do_init
|
||||
}
|
||||
|
||||
message_pattern := HANDSHAKE_PATTERNS[symmetric_state.protocol.handshake_pattern]
|
||||
if message_pattern.pre_messages != nil {
|
||||
if initiator {
|
||||
if slice.contains(message_pattern.pre_messages, Pre_Token.res_s) {
|
||||
if rs == nil {
|
||||
status = .No_Peer_Identity
|
||||
break do_init
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if slice.contains(message_pattern.pre_messages, Pre_Token.ini_s) {
|
||||
if rs == nil {
|
||||
status = .No_Peer_Identity
|
||||
break do_init
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if rs != nil {
|
||||
status = .Unexpected_Peer_Identity
|
||||
break do_init
|
||||
}
|
||||
}
|
||||
|
||||
symmetricstate_mix_hash(symmetric_state, prologue)
|
||||
|
||||
// In all supported patterns, `ini_s` will always precede `res_s`.
|
||||
if message_pattern.pre_messages != nil {
|
||||
tmp: [MAX_DH_SIZE]byte = ---
|
||||
d_len := dh_len(&symmetric_state.protocol)
|
||||
dst := tmp[:d_len]
|
||||
|
||||
if initiator {
|
||||
if slice.contains(message_pattern.pre_messages, Pre_Token.ini_s) {
|
||||
ecdh.public_key_bytes(&s._pub_key, dst)
|
||||
symmetricstate_mix_hash(symmetric_state, dst)
|
||||
}
|
||||
if slice.contains(message_pattern.pre_messages, Pre_Token.res_s) {
|
||||
ecdh.public_key_bytes(rs, dst)
|
||||
symmetricstate_mix_hash(symmetric_state, dst)
|
||||
}
|
||||
} else {
|
||||
if slice.contains(message_pattern.pre_messages, Pre_Token.ini_s) {
|
||||
ecdh.public_key_bytes(rs, dst)
|
||||
symmetricstate_mix_hash(symmetric_state, dst)
|
||||
}
|
||||
if slice.contains(message_pattern.pre_messages, Pre_Token.res_s) {
|
||||
ecdh.public_key_bytes(&s._pub_key, dst)
|
||||
symmetricstate_mix_hash(symmetric_state, dst)
|
||||
}
|
||||
}
|
||||
}
|
||||
if message_pattern.is_psk {
|
||||
if len(psk) != PSK_SIZE {
|
||||
status = .Invalid_Pre_Shared_Key
|
||||
break do_init
|
||||
}
|
||||
} else if len(psk) != 0 {
|
||||
status = .Unexpected_Pre_Shared_Key
|
||||
break do_init
|
||||
}
|
||||
}
|
||||
if status != .Ok {
|
||||
symmetricstate_reset(symmetric_state)
|
||||
return status
|
||||
}
|
||||
|
||||
if s != nil {
|
||||
ecdh.private_key_set(&handshake_state.s, s)
|
||||
}
|
||||
if e != nil {
|
||||
ecdh.private_key_set(&handshake_state.e, e)
|
||||
handshake_state.pre_set_e = true
|
||||
}
|
||||
if rs != nil {
|
||||
ecdh.public_key_set(&handshake_state.rs, rs)
|
||||
}
|
||||
if re != nil {
|
||||
ecdh.public_key_set(&handshake_state.re, re)
|
||||
}
|
||||
copy(handshake_state.psk[:], psk)
|
||||
handshake_state.message_pattern = HANDSHAKE_PATTERNS[symmetric_state.protocol.handshake_pattern]
|
||||
handshake_state.current_message = 0
|
||||
handshake_state.status = .Handshake_Pending
|
||||
handshake_state.initiator = initiator
|
||||
|
||||
return .Ok
|
||||
}
|
||||
|
||||
handshakestate_reset :: proc(self: ^Handshake_State) {
|
||||
symmetricstate_reset(&self.symmetric_state)
|
||||
ecdh.private_key_clear(&self.s)
|
||||
ecdh.private_key_clear(&self.e)
|
||||
|
||||
crypto.zero_explicit(self, size_of(Handshake_State))
|
||||
}
|
||||
|
||||
// Takes a payload byte sequence which may be zero-length, and a
|
||||
// message_buffer to write the output into.
|
||||
// Performs the following steps, aborting if any EncryptAndHash() call
|
||||
// returns an error:
|
||||
// - Fetches and deletes the next message pattern from message_pattern,
|
||||
// then sequentially processes each token from the message pattern:
|
||||
// - For "e": Sets e (which must be empty) to GENERATE_KEYPAIR().
|
||||
// Appends e.public_key to the buffer. Calls MixHash(e.public_key).
|
||||
// - For "s": Appends EncryptAndHash(s.public_key) to the buffer.
|
||||
// - For "ee": Calls MixKey(DH(e, re)).
|
||||
// - For "es": Calls MixKey(DH(e, rs)) if initiator, MixKey(DH(s, re))
|
||||
// if responder.
|
||||
// - For "se": Calls MixKey(DH(s, re)) if initiator, MixKey(DH(e, rs))
|
||||
// if responder.
|
||||
// - For "ss": Calls MixKey(DH(s, rs)).
|
||||
// - Appends EncryptAndHash(payload) to the buffer.
|
||||
// – (SKIPPED) If there are no more message patterns returns two new
|
||||
// CipherState objects by calling Split().
|
||||
//
|
||||
// Calling Split() is left to a separate function, although it is technically
|
||||
// part of the specification.
|
||||
@(require_results)
|
||||
handshakestate_write_message :: proc(self: ^Handshake_State, payload, dst: []byte, allocator := context.allocator) -> ([]byte, Status) {
|
||||
ensure(self.status == .Handshake_Pending, "crypto/noise: invalid state for WriteMessage")
|
||||
|
||||
protocol := &self.symmetric_state.protocol
|
||||
d_len := dh_len(protocol)
|
||||
|
||||
pattern_buf: [dynamic; MAX_STEP_MSG_SIZE]byte
|
||||
dh_buf: [MAX_DH_SIZE]byte = ---
|
||||
defer crypto.zero_explicit(&dh_buf, size_of(dh_buf))
|
||||
|
||||
pattern := self.message_pattern.messages[self.current_message]
|
||||
for token in pattern {
|
||||
switch token {
|
||||
case .e:
|
||||
switch self.pre_set_e {
|
||||
case true:
|
||||
// Note: "which must be empty", but we allow pre-generated `e`
|
||||
// for testing/rng-less systems.
|
||||
self.pre_set_e = false
|
||||
case false:
|
||||
if ecdh.curve(&self.e) != .Invalid {
|
||||
panic("crypto/noise: e was not empty when processing token 'e' during WriteMessage")
|
||||
}
|
||||
generate_keypair(protocol, &self.e)
|
||||
}
|
||||
e_public := dh_buf[:d_len]
|
||||
ecdh.public_key_bytes(&self.e._pub_key, e_public)
|
||||
n := append(&pattern_buf, ..e_public)
|
||||
ensure(n == d_len, "crypto/noise: truncated append `e`")
|
||||
|
||||
symmetricstate_mix_hash(&self.symmetric_state, e_public)
|
||||
if self.message_pattern.is_psk {
|
||||
symmetricstate_mix_key(&self.symmetric_state, e_public)
|
||||
}
|
||||
|
||||
case .s:
|
||||
s_public := dh_buf[:d_len]
|
||||
ecdh.public_key_bytes(&self.s._pub_key, s_public)
|
||||
|
||||
tmp: [MAX_DH_SIZE+TAG_SIZE]byte = ---
|
||||
dh_buf := tmp[:d_len+TAG_SIZE]
|
||||
if !cipherstate_has_key(&self.symmetric_state.cipher_state) {
|
||||
dh_buf = tmp[:d_len]
|
||||
}
|
||||
ct, status := symmetricstate_encrypt_and_hash(&self.symmetric_state, s_public, dh_buf)
|
||||
if status != .Ok {
|
||||
self.status = .Handshake_Failed
|
||||
return nil, status
|
||||
}
|
||||
n := append(&pattern_buf, ..ct)
|
||||
ensure(n == len(ct), "crypto/noise: truncated append `s`")
|
||||
|
||||
case .ee:
|
||||
dh := dh_buf[:d_len]
|
||||
if status := _dh(&self.e, &self.re, dh); status != .Ok {
|
||||
self.status = .Handshake_Failed
|
||||
return nil, status
|
||||
}
|
||||
symmetricstate_mix_key(&self.symmetric_state, dh)
|
||||
|
||||
case .es:
|
||||
dh := dh_buf[:d_len]
|
||||
if self.initiator {
|
||||
if status := _dh(&self.e, &self.rs, dh); status != .Ok {
|
||||
self.status = .Handshake_Failed
|
||||
return nil, status
|
||||
}
|
||||
symmetricstate_mix_key(&self.symmetric_state, dh)
|
||||
} else {
|
||||
if status := _dh(&self.s, &self.re, dh); status != .Ok {
|
||||
self.status = .Handshake_Failed
|
||||
return nil, status
|
||||
}
|
||||
symmetricstate_mix_key(&self.symmetric_state, dh)
|
||||
}
|
||||
|
||||
case .se:
|
||||
dh := dh_buf[:d_len]
|
||||
if self.initiator {
|
||||
if status := _dh(&self.s, &self.re, dh); status != .Ok {
|
||||
self.status = .Handshake_Failed
|
||||
return nil, status
|
||||
}
|
||||
symmetricstate_mix_key(&self.symmetric_state, dh)
|
||||
} else {
|
||||
if status := _dh(&self.e, &self.rs, dh); status != .Ok {
|
||||
self.status = .Handshake_Failed
|
||||
return nil, status
|
||||
}
|
||||
symmetricstate_mix_key(&self.symmetric_state, dh)
|
||||
}
|
||||
|
||||
case .ss:
|
||||
dh := dh_buf[:d_len]
|
||||
if status := _dh(&self.s, &self.rs, dh); status != .Ok {
|
||||
self.status = .Handshake_Failed
|
||||
return nil, status
|
||||
}
|
||||
symmetricstate_mix_key(&self.symmetric_state, dh)
|
||||
|
||||
case .psk:
|
||||
symmetricstate_mix_key_and_hash(&self.symmetric_state, self.psk[:])
|
||||
}
|
||||
}
|
||||
self.current_message += 1 // Advance after the current message is successful.
|
||||
|
||||
pattern_len := len(pattern_buf)
|
||||
payload_len := len(payload)
|
||||
msg_len := pattern_len + payload_len
|
||||
if cipherstate_has_key(&self.symmetric_state.cipher_state) {
|
||||
msg_len += TAG_SIZE
|
||||
}
|
||||
|
||||
msg: []byte
|
||||
if msg_len != 0 {
|
||||
did_alloc: bool
|
||||
if dst != nil {
|
||||
if len(dst) < msg_len {
|
||||
self.status = .Handshake_Failed
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
msg = dst[:msg_len]
|
||||
} else {
|
||||
err: runtime.Allocator_Error
|
||||
msg, err = make([]byte, msg_len, allocator)
|
||||
if err != nil {
|
||||
self.status = .Handshake_Failed
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
did_alloc = true
|
||||
}
|
||||
|
||||
copy(msg, pattern_buf[:])
|
||||
ciphertext := msg[pattern_len:]
|
||||
if _, status := symmetricstate_encrypt_and_hash(&self.symmetric_state, payload, ciphertext); status != .Ok {
|
||||
if did_alloc {
|
||||
delete(msg)
|
||||
}
|
||||
self.status = .Handshake_Failed
|
||||
return nil, status
|
||||
}
|
||||
}
|
||||
|
||||
if self.current_message == len(self.message_pattern.messages) {
|
||||
self.current_message = -1
|
||||
self.status = .Handshake_Complete
|
||||
}
|
||||
|
||||
return msg, self.status
|
||||
}
|
||||
|
||||
// Takes a byte sequence containing a Noise handshake message, and a
|
||||
// payload_buffer to write the message's plaintext payload into.
|
||||
// Performs the following steps, aborting if any DecryptAndHash()
|
||||
// call returns an error:
|
||||
// - Fetches and deletes the next message pattern from message_pattern,
|
||||
// then sequentially processes each token from the message pattern:
|
||||
// - For "e": Sets re (which must be empty) to the next DHLEN bytes
|
||||
// from the message. Calls MixHash(re.public_key).
|
||||
// - For "s": Sets temp to the next DHLEN + 16 bytes of the message
|
||||
// if HasKey() == True, or to the next DHLEN bytes otherwise.
|
||||
// Sets rs (which must be empty) to DecryptAndHash(temp).
|
||||
// - For "ee": Calls MixKey(DH(e, re)).
|
||||
// - For "es": Calls MixKey(DH(e, rs)) if initiator, MixKey(DH(s, re))
|
||||
// if responder.
|
||||
// - For "se": Calls MixKey(DH(s, re)) if initiator, MixKey(DH(e, rs))
|
||||
// if responder.
|
||||
// -For "ss": Calls MixKey(DH(s, rs)).
|
||||
// - Calls DecryptAndHash() on the remaining bytes of the message and stores
|
||||
// the output into payload_buffer.
|
||||
// – (SKIPPED) If there are no more message patterns returns two new
|
||||
// CipherState objects by calling Split().
|
||||
//
|
||||
// Calling Split() is left to a separate function, although it is technically
|
||||
// part of the specification.
|
||||
@(require_results)
|
||||
handshakestate_read_message :: proc(self: ^Handshake_State, message, dst: []byte, allocator := context.allocator) -> ([]byte, Status) {
|
||||
ensure(self.status == .Handshake_Pending, "crypto/noise: invalid state for ReadMessage")
|
||||
|
||||
protocol := &self.symmetric_state.protocol
|
||||
d_len := dh_len(&self.symmetric_state.protocol)
|
||||
|
||||
dh_buf: [MAX_DH_SIZE]byte = ---
|
||||
defer crypto.zero_explicit(&dh_buf, size_of(dh_buf))
|
||||
|
||||
msg := message
|
||||
|
||||
pattern := self.message_pattern.messages[self.current_message]
|
||||
for token in pattern {
|
||||
switch token {
|
||||
case .e:
|
||||
if len(msg) < d_len {
|
||||
return nil, .Invalid_Handshake_Message
|
||||
}
|
||||
re := msg[:d_len]
|
||||
|
||||
if ecdh.curve(&self.re) != .Invalid {
|
||||
panic("crypto/noise: re was not empty when processing token 'e' during ReadMessage")
|
||||
}
|
||||
|
||||
ecdh.public_key_set_bytes(&self.re, protocol.dh, re)
|
||||
symmetricstate_mix_hash(&self.symmetric_state, re)
|
||||
if self.message_pattern.is_psk {
|
||||
symmetricstate_mix_key(&self.symmetric_state, re)
|
||||
}
|
||||
msg = msg[d_len:]
|
||||
|
||||
case .s:
|
||||
rs_len := d_len
|
||||
if cipherstate_has_key(&self.symmetric_state.cipher_state) {
|
||||
rs_len += TAG_SIZE
|
||||
}
|
||||
if len(msg) < rs_len {
|
||||
self.status = .Handshake_Failed
|
||||
return nil, .Invalid_Handshake_Message
|
||||
}
|
||||
|
||||
rs := dh_buf[:d_len]
|
||||
if _, status := symmetricstate_decrypt_and_hash(&self.symmetric_state, msg[:rs_len], rs); status != .Ok {
|
||||
self.status = .Handshake_Failed
|
||||
return nil, status
|
||||
}
|
||||
|
||||
if ecdh.curve(&self.rs) != .Invalid {
|
||||
panic("crypto/noise: rs was not empty when processing token 's' during ReadMessage")
|
||||
}
|
||||
|
||||
ecdh.public_key_set_bytes(&self.rs, protocol.dh, rs)
|
||||
msg = msg[rs_len:]
|
||||
|
||||
case .ee:
|
||||
dh := dh_buf[:d_len]
|
||||
if status := _dh(&self.e, &self.re, dh); status != .Ok {
|
||||
self.status = .Handshake_Failed
|
||||
return nil, status
|
||||
}
|
||||
symmetricstate_mix_key(&self.symmetric_state, dh)
|
||||
|
||||
case .es:
|
||||
dh := dh_buf[:d_len]
|
||||
if self.initiator {
|
||||
if status := _dh(&self.e, &self.rs, dh); status != .Ok {
|
||||
self.status = .Handshake_Failed
|
||||
return nil, status
|
||||
}
|
||||
symmetricstate_mix_key(&self.symmetric_state, dh)
|
||||
} else {
|
||||
if status := _dh(&self.s, &self.re, dh); status != .Ok {
|
||||
self.status = .Handshake_Failed
|
||||
return nil, status
|
||||
}
|
||||
symmetricstate_mix_key(&self.symmetric_state, dh)
|
||||
}
|
||||
|
||||
case .se:
|
||||
dh := dh_buf[:d_len]
|
||||
if self.initiator {
|
||||
if status := _dh(&self.s, &self.re, dh); status != .Ok {
|
||||
self.status = .Handshake_Failed
|
||||
return nil, status
|
||||
}
|
||||
symmetricstate_mix_key(&self.symmetric_state, dh)
|
||||
} else {
|
||||
if status := _dh(&self.e, &self.rs, dh); status != .Ok {
|
||||
self.status = .Handshake_Failed
|
||||
return nil, status
|
||||
}
|
||||
symmetricstate_mix_key(&self.symmetric_state, dh)
|
||||
}
|
||||
|
||||
case .ss:
|
||||
dh := dh_buf[:d_len]
|
||||
if status := _dh(&self.s, &self.rs, dh); status != .Ok {
|
||||
self.status = .Handshake_Failed
|
||||
return nil, status
|
||||
}
|
||||
symmetricstate_mix_key(&self.symmetric_state, dh)
|
||||
|
||||
case .psk:
|
||||
symmetricstate_mix_key_and_hash(&self.symmetric_state, self.psk[:])
|
||||
}
|
||||
}
|
||||
self.current_message += 1 // Advance after the current message is successful.
|
||||
|
||||
payload: []byte
|
||||
payload_len := len(msg)
|
||||
if cipherstate_has_key(&self.symmetric_state.cipher_state) {
|
||||
if payload_len < TAG_SIZE {
|
||||
self.status = .Handshake_Failed
|
||||
return nil, self.status
|
||||
}
|
||||
payload_len -= TAG_SIZE
|
||||
}
|
||||
|
||||
did_alloc: bool
|
||||
if dst != nil {
|
||||
if len(dst) < payload_len {
|
||||
self.status = .Handshake_Failed
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
payload = dst[:payload_len]
|
||||
} else if payload_len > 0 {
|
||||
err: runtime.Allocator_Error
|
||||
payload, err = make([]byte, payload_len, allocator)
|
||||
if err != nil {
|
||||
self.status = .Handshake_Failed
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
did_alloc = true
|
||||
}
|
||||
|
||||
if _, status := symmetricstate_decrypt_and_hash(&self.symmetric_state, msg, payload); status != .Ok {
|
||||
if did_alloc {
|
||||
delete(payload)
|
||||
}
|
||||
self.status = .Handshake_Failed
|
||||
return nil, self.status
|
||||
}
|
||||
|
||||
if self.current_message == len(self.message_pattern.messages) {
|
||||
self.current_message = -1
|
||||
self.status = .Handshake_Complete
|
||||
}
|
||||
|
||||
return payload, self.status
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
protocol_from_string :: proc(self: ^Protocol, protocol_name: string) -> Status {
|
||||
self^ = Protocol{}
|
||||
|
||||
pattern, dh, cipher, hash, status := split_protocol_string(protocol_name)
|
||||
if status != .Ok {
|
||||
return status
|
||||
}
|
||||
|
||||
self.handshake_pattern = pattern
|
||||
self.dh = dh
|
||||
self.cipher = cipher
|
||||
self.hash = hash
|
||||
|
||||
return .Ok
|
||||
}
|
||||
@@ -3132,12 +3132,14 @@ fmt_map :: proc(fi: ^Info, v: any, info: runtime.Type_Info_Map, verb: rune) {
|
||||
value := runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index)
|
||||
|
||||
fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, verb)
|
||||
if hash {
|
||||
io.write_string(fi.writer, " = ", &fi.n)
|
||||
} else {
|
||||
io.write_string(fi.writer, "=", &fi.n)
|
||||
if info.value.size > 0 {
|
||||
if hash {
|
||||
io.write_string(fi.writer, " = ", &fi.n)
|
||||
} else {
|
||||
io.write_string(fi.writer, "=", &fi.n)
|
||||
}
|
||||
fmt_arg(fi, any{rawptr(value), info.value.id}, verb)
|
||||
}
|
||||
fmt_arg(fi, any{rawptr(value), info.value.id}, verb)
|
||||
|
||||
if do_trailing_comma { io.write_string(fi.writer, ",\n", &fi.n) }
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import "core:bufio"
|
||||
fprint :: proc(f: ^os.File, args: ..any, sep := " ", flush := true) -> int {
|
||||
buf: [1024]byte
|
||||
b: bufio.Writer
|
||||
defer bufio.writer_flush(&b)
|
||||
|
||||
bufio.writer_init_with_buf(&b, os.to_stream(f), buf[:])
|
||||
w := bufio.writer_to_writer(&b)
|
||||
@@ -25,7 +24,6 @@ fprint :: proc(f: ^os.File, args: ..any, sep := " ", flush := true) -> int {
|
||||
fprintln :: proc(f: ^os.File, args: ..any, sep := " ", flush := true) -> int {
|
||||
buf: [1024]byte
|
||||
b: bufio.Writer
|
||||
defer bufio.writer_flush(&b)
|
||||
|
||||
bufio.writer_init_with_buf(&b, os.to_stream(f), buf[:])
|
||||
|
||||
@@ -36,7 +34,6 @@ fprintln :: proc(f: ^os.File, args: ..any, sep := " ", flush := true) -> int {
|
||||
fprintf :: proc(f: ^os.File, fmt: string, args: ..any, flush := true, newline := false) -> int {
|
||||
buf: [1024]byte
|
||||
b: bufio.Writer
|
||||
defer bufio.writer_flush(&b)
|
||||
|
||||
bufio.writer_init_with_buf(&b, os.to_stream(f), buf[:])
|
||||
|
||||
@@ -50,7 +47,6 @@ fprintfln :: proc(f: ^os.File, fmt: string, args: ..any, flush := true) -> int {
|
||||
fprint_type :: proc(f: ^os.File, info: ^runtime.Type_Info, flush := true) -> (n: int, err: io.Error) {
|
||||
buf: [1024]byte
|
||||
b: bufio.Writer
|
||||
defer bufio.writer_flush(&b)
|
||||
|
||||
bufio.writer_init_with_buf(&b, os.to_stream(f), buf[:])
|
||||
|
||||
@@ -60,7 +56,6 @@ fprint_type :: proc(f: ^os.File, info: ^runtime.Type_Info, flush := true) -> (n:
|
||||
fprint_typeid :: proc(f: ^os.File, id: typeid, flush := true) -> (n: int, err: io.Error) {
|
||||
buf: [1024]byte
|
||||
b: bufio.Writer
|
||||
defer bufio.writer_flush(&b)
|
||||
|
||||
bufio.writer_init_with_buf(&b, os.to_stream(f), buf[:])
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package linalg
|
||||
|
||||
import "base:builtin"
|
||||
import "base:intrinsics"
|
||||
import "core:math"
|
||||
|
||||
@(require_results)
|
||||
@@ -413,26 +414,12 @@ pow :: proc "contextless" (x, e: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
|
||||
@(require_results)
|
||||
ceil :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = #force_inline math.ceil(x[i])
|
||||
}
|
||||
} else {
|
||||
out = #force_inline math.ceil(x)
|
||||
}
|
||||
return
|
||||
return _from_simd4(T, intrinsics.simd_ceil(_to_simd4(x)))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
floor :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = #force_inline math.floor(x[i])
|
||||
}
|
||||
} else {
|
||||
out = #force_inline math.floor(x)
|
||||
}
|
||||
return
|
||||
return _from_simd4(T, intrinsics.simd_floor(_to_simd4(x)))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
@@ -447,6 +434,11 @@ round :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
trunc :: proc "contextless" (x: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
|
||||
return _from_simd4(T, intrinsics.simd_trunc(_to_simd4(x)))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
fract :: proc "contextless" (x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
f := #force_inline floor(x)
|
||||
@@ -613,3 +605,46 @@ not :: proc "contextless" (x: $A/[$N]bool) -> (out: A) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
_to_simd4 :: #force_inline proc "contextless" (a: $T) -> (out: #simd[4]ELEM_TYPE(T)) where IS_NUMERIC(ELEM_TYPE(T)) #no_bounds_check {
|
||||
when IS_ARRAY(T) {
|
||||
when len(T) == 1 {
|
||||
_a: [4]ELEM_TYPE(T)
|
||||
_a.x = a.x
|
||||
return transmute(#simd[4]ELEM_TYPE(T))_a
|
||||
} else when len(T) == 2 {
|
||||
_a: [4]ELEM_TYPE(T)
|
||||
_a.xy = a
|
||||
return transmute(#simd[4]ELEM_TYPE(T))_a
|
||||
} else when len(T) == 3 {
|
||||
_a: [4]ELEM_TYPE(T)
|
||||
_a.xyz = a
|
||||
return transmute(#simd[4]ELEM_TYPE(T))_a
|
||||
} else {
|
||||
return transmute(#simd[4]ELEM_TYPE(T))a
|
||||
}
|
||||
} else {
|
||||
_a: [4]ELEM_TYPE(T)
|
||||
_a.x = a
|
||||
return transmute(#simd[4]ELEM_TYPE(T))_a
|
||||
}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
_from_simd4 :: #force_inline proc "contextless" ($T: typeid, a: $V/#simd[4]$E) -> T where IS_NUMERIC(ELEM_TYPE(T)) #no_bounds_check {
|
||||
when IS_ARRAY(T) {
|
||||
when len(T) == 1 {
|
||||
return (transmute([4]ELEM_TYPE(T))a).x
|
||||
} else when len(T) == 2 {
|
||||
return (transmute([4]ELEM_TYPE(T))a).xy
|
||||
} else when len(T) == 3 {
|
||||
return (transmute([4]ELEM_TYPE(T))a).xyz
|
||||
} else {
|
||||
return transmute([4]ELEM_TYPE(T))a
|
||||
}
|
||||
} else {
|
||||
return (transmute([4]ELEM_TYPE(T))a).x
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,11 +46,23 @@ scalar_dot :: proc "contextless" (a, b: $T) -> T where IS_FLOAT(T), !IS_ARRAY(T)
|
||||
|
||||
@(require_results)
|
||||
vector_dot :: proc "contextless" (a, b: $T/[$N]$E) -> (c: E) where IS_NUMERIC(E) #no_bounds_check {
|
||||
for i in 0..<N {
|
||||
c += a[i] * b[i]
|
||||
ab := a * b
|
||||
when N == 1 {
|
||||
return ab.x
|
||||
} else when N == 2 {
|
||||
return ab.x + ab.y
|
||||
} else when N == 3 {
|
||||
return ab.x + ab.y + ab.z
|
||||
} else when N == 4 {
|
||||
return ab.x + ab.y + ab.z + ab.w
|
||||
} else {
|
||||
for elem in ab {
|
||||
c += elem
|
||||
}
|
||||
return c
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
quaternion64_dot :: proc "contextless" (a, b: $T/quaternion64) -> (c: f16) {
|
||||
return a.w*b.w + a.x*b.x + a.y*b.y + a.z*b.z
|
||||
@@ -86,11 +98,8 @@ vector_cross2 :: proc "contextless" (a, b: $T/[2]$E) -> E where IS_NUMERIC(E) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
vector_cross3 :: proc "contextless" (a, b: $T/[3]$E) -> (c: T) where IS_NUMERIC(E) {
|
||||
c[0] = a[1]*b[2] - b[1]*a[2]
|
||||
c[1] = a[2]*b[0] - b[2]*a[0]
|
||||
c[2] = a[0]*b[1] - b[0]*a[1]
|
||||
return
|
||||
vector_cross3 :: proc "contextless" (a, b: $T/[3]$E) -> (c: T) where IS_NUMERIC(E) #no_bounds_check {
|
||||
return a.yzx*b.zxy - b.yzx*a.zxy
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
@@ -130,12 +139,12 @@ normalize0 :: proc{vector_normalize0, quaternion_normalize0}
|
||||
|
||||
@(require_results)
|
||||
vector_length :: proc "contextless" (v: $T/[$N]$E) -> E where IS_FLOAT(E) {
|
||||
return math.sqrt(dot(v, v))
|
||||
return #force_inline math.sqrt(#force_inline dot(v, v))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
vector_length2 :: proc "contextless" (v: $T/[$N]$E) -> E where IS_NUMERIC(E) {
|
||||
return dot(v, v)
|
||||
return #force_inline dot(v, v)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
|
||||
@@ -141,9 +141,9 @@ arena_alloc_unguarded :: proc(arena: ^Arena, size: uint, alignment: uint, loc :=
|
||||
|
||||
needed := mem.align_forward_uint(size, alignment)
|
||||
needed = max(needed, arena.default_commit_size)
|
||||
block_size := max(needed, arena.minimum_block_size) + alignment
|
||||
block_size := max(needed, arena.minimum_block_size)
|
||||
|
||||
new_block := memory_block_alloc(needed, block_size) or_return
|
||||
new_block := memory_block_alloc(needed, block_size, alignment) or_return
|
||||
new_block.prev = arena.curr_block
|
||||
arena.curr_block = new_block
|
||||
arena.total_reserved += new_block.reserved
|
||||
|
||||
@@ -65,8 +65,8 @@ Example:
|
||||
import vmem "core:mem/virtual"
|
||||
|
||||
main :: proc() {
|
||||
data, err := virtual.map_file_from_path(#file, {.Read})
|
||||
defer virtual.unmap_file(data)
|
||||
data, err := vmem.map_file_from_path(#file, {.Read})
|
||||
defer vmem.unmap_file(data)
|
||||
fmt.printfln("Error: %v", err)
|
||||
fmt.printfln("Data: %s", data)
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ platform_memory_init :: proc "contextless" () {
|
||||
Allocator_Error :: mem.Allocator_Error
|
||||
|
||||
@(require_results, no_sanitize_address)
|
||||
reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||
return _reserve(size)
|
||||
reserve :: proc "contextless" (size: uint, address_hint := uintptr(0)) -> (data: []byte, err: Allocator_Error) {
|
||||
return _reserve(size, address_hint)
|
||||
}
|
||||
|
||||
@(no_sanitize_address)
|
||||
@@ -115,7 +115,7 @@ memory_block_alloc :: proc(committed, reserved: uint, alignment: uint = 0, flags
|
||||
}
|
||||
|
||||
pmblock.block.committed = committed
|
||||
pmblock.block.reserved = reserved
|
||||
pmblock.block.reserved = total_size - uint(base_offset)
|
||||
|
||||
|
||||
return &pmblock.block, nil
|
||||
|
||||
@@ -2,8 +2,8 @@ package mem_virtual
|
||||
|
||||
import "core:sys/posix"
|
||||
|
||||
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||
result := posix.mmap(nil, size, {}, {.ANONYMOUS, .PRIVATE})
|
||||
_reserve :: proc "contextless" (size: uint, address_hint: uintptr) -> (data: []byte, err: Allocator_Error) {
|
||||
result := posix.mmap(rawptr(address_hint), size, {}, {.ANONYMOUS, .PRIVATE})
|
||||
if result == posix.MAP_FAILED {
|
||||
assert_contextless(posix.errno() == .ENOMEM)
|
||||
return nil, .Out_Of_Memory
|
||||
|
||||
@@ -2,14 +2,14 @@ package mem_virtual
|
||||
|
||||
import "core:sys/posix"
|
||||
|
||||
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||
_reserve :: proc "contextless" (size: uint, address_hint: uintptr) -> (data: []byte, err: Allocator_Error) {
|
||||
|
||||
PROT_MAX :: proc "contextless" (flags: posix.Prot_Flags) -> posix.Prot_Flags {
|
||||
_PROT_MAX_SHIFT :: 16
|
||||
return transmute(posix.Prot_Flags)(transmute(i32)flags << _PROT_MAX_SHIFT)
|
||||
}
|
||||
|
||||
result := posix.mmap(nil, size, PROT_MAX({.READ, .WRITE, .EXEC}), {.ANONYMOUS, .PRIVATE})
|
||||
result := posix.mmap(rawptr(address_hint), size, PROT_MAX({.READ, .WRITE, .EXEC}), {.ANONYMOUS, .PRIVATE})
|
||||
if result == posix.MAP_FAILED {
|
||||
assert_contextless(posix.errno() == .ENOMEM)
|
||||
return nil, .Out_Of_Memory
|
||||
|
||||
@@ -4,8 +4,8 @@ package mem_virtual
|
||||
|
||||
import "core:sys/linux"
|
||||
|
||||
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||
addr, errno := linux.mmap(0, size, {}, {.PRIVATE, .ANONYMOUS})
|
||||
_reserve :: proc "contextless" (size: uint, address_hint: uintptr) -> (data: []byte, err: Allocator_Error) {
|
||||
addr, errno := linux.mmap(address_hint, size, {}, {.PRIVATE, .ANONYMOUS})
|
||||
if errno == .ENOMEM {
|
||||
return nil, .Out_Of_Memory
|
||||
} else if errno == .EINVAL {
|
||||
@@ -68,4 +68,4 @@ _map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags)
|
||||
|
||||
_unmap_file :: proc "contextless" (data: []byte) {
|
||||
_release(raw_data(data), uint(len(data)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,13 @@ package mem_virtual
|
||||
|
||||
import "core:sys/posix"
|
||||
|
||||
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||
_reserve :: proc "contextless" (size: uint, address_hint: uintptr) -> (data: []byte, err: Allocator_Error) {
|
||||
|
||||
PROT_MPROTECT :: proc "contextless" (flags: posix.Prot_Flags) -> posix.Prot_Flags {
|
||||
return transmute(posix.Prot_Flags)(transmute(i32)flags << 3)
|
||||
}
|
||||
|
||||
result := posix.mmap(nil, size, PROT_MPROTECT({.READ, .WRITE, .EXEC}), {.ANONYMOUS, .PRIVATE})
|
||||
result := posix.mmap(rawptr(address_hint), size, PROT_MPROTECT({.READ, .WRITE, .EXEC}), {.ANONYMOUS, .PRIVATE})
|
||||
if result == posix.MAP_FAILED {
|
||||
assert_contextless(posix.errno() == .ENOMEM)
|
||||
return nil, .Out_Of_Memory
|
||||
|
||||
@@ -2,8 +2,8 @@ package mem_virtual
|
||||
|
||||
import "core:sys/posix"
|
||||
|
||||
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||
result := posix.mmap(nil, size, {}, {.ANONYMOUS, .PRIVATE})
|
||||
_reserve :: proc "contextless" (size: uint, address_hint: uintptr) -> (data: []byte, err: Allocator_Error) {
|
||||
result := posix.mmap(rawptr(address_hint), size, {}, {.ANONYMOUS, .PRIVATE})
|
||||
if result == posix.MAP_FAILED {
|
||||
assert_contextless(posix.errno() == .ENOMEM)
|
||||
return nil, .Out_Of_Memory
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#+build !windows
|
||||
package mem_virtual
|
||||
|
||||
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||
_reserve :: proc "contextless" (size: uint, address_hint: uintptr) -> (data: []byte, err: Allocator_Error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -35,4 +35,4 @@ _map_file :: proc "contextless" (f: any, size: i64, flags: Map_File_Flags) -> (d
|
||||
|
||||
_unmap_file :: proc "contextless" (data: []byte) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,8 +87,8 @@ foreign Kernel32 {
|
||||
}
|
||||
|
||||
@(no_sanitize_address)
|
||||
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||
result := VirtualAlloc(nil, size, MEM_RESERVE, PAGE_READWRITE)
|
||||
_reserve :: proc "contextless" (size: uint, address_hint: uintptr) -> (data: []byte, err: Allocator_Error) {
|
||||
result := VirtualAlloc(rawptr(address_hint), size, MEM_RESERVE, PAGE_READWRITE)
|
||||
if result == nil {
|
||||
err = .Out_Of_Memory
|
||||
return
|
||||
@@ -191,4 +191,4 @@ _map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags)
|
||||
@(no_sanitize_address)
|
||||
_unmap_file :: proc "contextless" (data: []byte) {
|
||||
UnmapViewOfFile(raw_data(data))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -743,6 +743,11 @@ poll_exec :: proc(op: ^Operation) -> Op_Result {
|
||||
return .Done
|
||||
}
|
||||
|
||||
if .EOF in op._impl.flags {
|
||||
op.poll.result = .Ready
|
||||
return .Done
|
||||
}
|
||||
|
||||
filter: kq.Filter
|
||||
switch op.poll.event {
|
||||
case .Receive: filter = .Read
|
||||
|
||||
@@ -413,10 +413,15 @@ e.g.
|
||||
'name.tar.gz' -> 'name.tar'
|
||||
'name.txt' -> 'name'
|
||||
|
||||
Returns an empty string if the path is empty
|
||||
Returns an empty string if there is no stem. e.g: '.gitignore'.
|
||||
Returns an empty string if there's a trailing path separator.
|
||||
*/
|
||||
stem :: proc(path: string) -> string {
|
||||
if path == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
// If the last character is a path separator, there is no file.
|
||||
if is_path_separator(path[len(path) - 1]) {
|
||||
return ""
|
||||
|
||||
@@ -377,17 +377,25 @@ _volume_name_len :: proc(path: string) -> (length: int) {
|
||||
|
||||
// UNC path, minimum version of the volume is `\\h\s` for host, share.
|
||||
// Can also contain an IP address in the host position.
|
||||
// (path might be purely path separators)
|
||||
slash_count := 0
|
||||
found_host: bool
|
||||
for i in prefix..<len(path) {
|
||||
// Host needs to be at least 1 character
|
||||
if _is_path_separator(path[i]) && i > 0 {
|
||||
if _is_path_separator(path[i]) {
|
||||
slash_count += 1
|
||||
|
||||
if slash_count == 2 {
|
||||
if slash_count == 2 && found_host {
|
||||
return i
|
||||
}
|
||||
} else {
|
||||
found_host = true
|
||||
// Found a host but no trailing slash `\\h\s`
|
||||
if slash_count == 1 && i == len(path)-1 {
|
||||
return len(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return len(path)
|
||||
return 0
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
// To process paths such as URLs that depend on forward slashes regardless of the OS, use the slashpath package.
|
||||
package filepath
|
||||
|
||||
import "base:runtime"
|
||||
import "core:os"
|
||||
import "core:strings"
|
||||
|
||||
@@ -32,7 +33,15 @@ Join all `elems` with the system's path separator and normalize the result.
|
||||
|
||||
For example, `join_path({"/home", "foo", "bar.txt"})` will result in `"/home/foo/bar.txt"`.
|
||||
*/
|
||||
join :: os.join_path
|
||||
join :: proc(
|
||||
elems: []string,
|
||||
allocator := context.allocator,
|
||||
) -> (
|
||||
joined: string,
|
||||
err: runtime.Allocator_Error,
|
||||
) {
|
||||
return os.join_path(elems, allocator)
|
||||
}
|
||||
|
||||
/*
|
||||
Returns leading volume name.
|
||||
@@ -136,7 +145,15 @@ long_ext :: os.long_ext
|
||||
If the result of the path is an empty string, the returned path with be `"."`.
|
||||
|
||||
*/
|
||||
clean :: os.clean_path
|
||||
clean :: proc(
|
||||
path: string,
|
||||
allocator := context.allocator,
|
||||
) -> (
|
||||
cleaned: string,
|
||||
err: runtime.Allocator_Error,
|
||||
) {
|
||||
return os.clean_path(path, allocator)
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the result of replacing each path separator character in the path
|
||||
@@ -144,7 +161,16 @@ with the specific character `new_sep`.
|
||||
|
||||
*Allocates Using Provided Allocator*
|
||||
*/
|
||||
replace_path_separators := os.replace_path_separators
|
||||
replace_separators :: proc(
|
||||
path: string,
|
||||
new_sep: rune,
|
||||
allocator := context.allocator,
|
||||
) -> (
|
||||
new_path: string,
|
||||
err: os.Error,
|
||||
) {
|
||||
return os.replace_path_separators(path, new_sep, allocator)
|
||||
}
|
||||
|
||||
/*
|
||||
Return true if `path` is an absolute path as opposed to a relative one.
|
||||
@@ -156,7 +182,16 @@ Get the absolute path to `path` with respect to the process's current directory.
|
||||
|
||||
*Allocates Using Provided Allocator*
|
||||
*/
|
||||
abs :: os.get_absolute_path
|
||||
@(require_results)
|
||||
abs :: proc(
|
||||
path: string,
|
||||
allocator := context.allocator,
|
||||
) -> (
|
||||
absolute_path: string,
|
||||
error: os.Error,
|
||||
) {
|
||||
return os.get_absolute_path(path, allocator)
|
||||
}
|
||||
|
||||
Relative_Error :: enum {
|
||||
None,
|
||||
@@ -249,17 +284,7 @@ rel :: proc(base_path, target_path: string, allocator := context.allocator) -> (
|
||||
`dir` calls `clean` on the path and trailing separators are removed. If the path is empty or consists purely
|
||||
of separators, then `"."` is returned.
|
||||
*/
|
||||
dir :: proc(path: string, allocator := context.allocator) -> string {
|
||||
i := len(path) > 0 ? len(path) - 1 : 0
|
||||
for i > 0 && !is_separator(path[i]) {
|
||||
i -= 1
|
||||
}
|
||||
res, dir_err := clean(path[:i], allocator)
|
||||
|
||||
if dir_err != nil { return "" }
|
||||
return res
|
||||
}
|
||||
|
||||
dir :: os.dir
|
||||
|
||||
|
||||
// Splits the PATH-like `path` string, returning an array of its separated components (delete after use).
|
||||
|
||||
1854
core/simd/x86/avx.odin
Normal file
1854
core/simd/x86/avx.odin
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3,3 +3,17 @@ package sys_freebsd
|
||||
/* Get window size */
|
||||
TIOCGWINSZ :: 0x40087468
|
||||
|
||||
/*
|
||||
Standard input file descriptor
|
||||
*/
|
||||
STDIN_FILENO :: Fd(0)
|
||||
|
||||
/*
|
||||
Standard output file descriptor
|
||||
*/
|
||||
STDOUT_FILENO :: Fd(1)
|
||||
|
||||
/*
|
||||
Standard error file descriptor
|
||||
*/
|
||||
STDERR_FILENO :: Fd(2)
|
||||
|
||||
@@ -23,6 +23,7 @@ SYS_recvfrom : uintptr : 29
|
||||
SYS_accept : uintptr : 30
|
||||
SYS_getpeername: uintptr : 31
|
||||
SYS_getsockname: uintptr : 32
|
||||
SYS_ioctl : uintptr : 54
|
||||
SYS_fcntl : uintptr : 92
|
||||
SYS_fsync : uintptr : 95
|
||||
SYS_socket : uintptr : 97
|
||||
@@ -634,3 +635,13 @@ accept4_nil :: proc "contextless" (s: Fd, flags: Socket_Flags = {}) -> (Fd, Errn
|
||||
}
|
||||
|
||||
accept4 :: proc { accept4_nil, accept4_T }
|
||||
|
||||
ioctl :: proc "contextless" (fd: Fd, request: c.ulong, arg: uintptr) -> (int, Errno) {
|
||||
result, ok := intrinsics.syscall_bsd(SYS_ioctl, cast(uintptr)fd, arg)
|
||||
|
||||
if !ok {
|
||||
return -1, cast(Errno)result
|
||||
}
|
||||
|
||||
return cast(int)result, nil
|
||||
}
|
||||
|
||||
@@ -2207,7 +2207,7 @@ when ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 {
|
||||
Available since Linux 1.0.
|
||||
*/
|
||||
adjtimex :: proc "contextless" (buf: ^Timex) -> (Clock_State, Errno) {
|
||||
ret := syscall(SYS_adjtimex)
|
||||
ret := syscall(SYS_adjtimex, buf)
|
||||
return errno_unwrap(ret, Clock_State)
|
||||
}
|
||||
|
||||
|
||||
106
core/sys/windows/ioringapi.odin
Normal file
106
core/sys/windows/ioringapi.odin
Normal file
@@ -0,0 +1,106 @@
|
||||
#+build windows
|
||||
package sys_windows
|
||||
|
||||
foreign import ioringapi "system:kernel32.lib"
|
||||
|
||||
HIORING :: distinct rawptr
|
||||
|
||||
IORING_SQE_FLAG :: enum u32 {
|
||||
DRAIN_PRECEDING_OPS = 0, // 0x00000001
|
||||
}
|
||||
IORING_SQE_FLAGS :: bit_set[IORING_SQE_FLAG; u32]
|
||||
|
||||
// Reserved for future use; currently no flags defined
|
||||
IORING_CREATE_REQUIRED_FLAG :: enum u32 {}
|
||||
IORING_CREATE_REQUIRED_FLAGS :: bit_set[IORING_CREATE_REQUIRED_FLAG; u32]
|
||||
|
||||
IORING_CREATE_ADVISORY_FLAG :: enum u32 {
|
||||
SKIP_BUILDER_PARAM_CHECKS = 0, // 0x00000001
|
||||
}
|
||||
IORING_CREATE_ADVISORY_FLAGS :: bit_set[IORING_CREATE_ADVISORY_FLAG; u32]
|
||||
|
||||
IORING_CREATE_FLAGS :: struct {
|
||||
Required: IORING_CREATE_REQUIRED_FLAGS,
|
||||
Advisory: IORING_CREATE_ADVISORY_FLAGS,
|
||||
}
|
||||
|
||||
IORING_INFO :: struct {
|
||||
Version: IORING_VERSION,
|
||||
Flags: IORING_CREATE_FLAGS,
|
||||
SubmissionQueueSize: UINT32,
|
||||
CompletionQueueSize: UINT32,
|
||||
}
|
||||
|
||||
IORING_CAPABILITIES :: struct {
|
||||
MaxVersion: IORING_VERSION,
|
||||
MaxSubmissionQueueSize: UINT32,
|
||||
MaxCompletionQueueSize: UINT32,
|
||||
FeatureFlags: IORING_FEATURE_FLAGS,
|
||||
}
|
||||
|
||||
IORING_REF_KIND :: enum i32 {
|
||||
RAW,
|
||||
REGISTERED,
|
||||
}
|
||||
|
||||
IORING_HANDLE_REF :: struct {
|
||||
Kind: IORING_REF_KIND,
|
||||
HandleUnion: struct #raw_union {
|
||||
Handle: HANDLE,
|
||||
Index: UINT32,
|
||||
},
|
||||
}
|
||||
|
||||
IORING_BUFFER_REF :: struct {
|
||||
Kind: IORING_REF_KIND,
|
||||
BufferUnion: struct #raw_union {
|
||||
Address: rawptr,
|
||||
IndexAndOffset: IORING_REGISTERED_BUFFER,
|
||||
},
|
||||
}
|
||||
|
||||
IORING_CQE :: struct {
|
||||
UserData: UINT_PTR,
|
||||
ResultCode: HRESULT,
|
||||
Information: ULONG_PTR,
|
||||
}
|
||||
|
||||
// Types below are from winbase.h and winnt.h
|
||||
FILE_WRITE_FLAG :: enum u32 {
|
||||
WRITE_THROUGH = 0, // 0x000000001
|
||||
}
|
||||
FILE_WRITE_FLAGS :: bit_set[FILE_WRITE_FLAG ;u32]
|
||||
|
||||
FILE_FLUSH_MODE :: enum i32 {
|
||||
DEFAULT,
|
||||
DATA,
|
||||
MIN_METADATA,
|
||||
NO_SYNC,
|
||||
}
|
||||
|
||||
FILE_SEGMENT_ELEMENT :: struct #raw_union {
|
||||
Buffer: PVOID64,
|
||||
Alignment: ULONGLONG,
|
||||
}
|
||||
|
||||
|
||||
@(default_calling_convention="system")
|
||||
foreign ioringapi {
|
||||
QueryIoRingCapabilities :: proc(capabilities: ^IORING_CAPABILITIES) -> HRESULT ---
|
||||
IsIoRingOpSupported :: proc(ioRing: HIORING, op: IORING_OP_CODE) -> BOOL ---
|
||||
CreateIoRing :: proc(ioringVersion: IORING_VERSION, flags: IORING_CREATE_FLAGS, submissionQueueSize: UINT32, completionQueueSize: UINT32, h: ^HIORING) -> HRESULT ---
|
||||
GetIoRingInfo :: proc(ioRing: HIORING, info: ^IORING_INFO) -> HRESULT ---
|
||||
SubmitIoRing :: proc(ioRing: HIORING, waitOperations: UINT32, milliseconds: UINT32, submittedEntries: ^UINT32) -> HRESULT ---
|
||||
CloseIoRing :: proc(ioRing: HIORING) -> HRESULT ---
|
||||
PopIoRingCompletion :: proc(ioRing: HIORING, cqe: ^IORING_CQE) -> HRESULT ---
|
||||
SetIoRingCompletionEvent :: proc(ioRing: HIORING, hEvent: HANDLE) -> HRESULT ---
|
||||
|
||||
BuildIoRingCancelRequest :: proc(ioRing: HIORING, file: IORING_HANDLE_REF, opToCancel: UINT_PTR, userData: UINT_PTR) -> HRESULT ---
|
||||
BuildIoRingReadFile :: proc(ioRing: HIORING, fileRef: IORING_HANDLE_REF, dataRef: IORING_BUFFER_REF, numberOfBytesToRead: UINT32, fileOffset: UINT64, userData: UINT_PTR, sqeFlags: IORING_SQE_FLAGS) -> HRESULT ---
|
||||
BuildIoRingRegisterFileHandles :: proc(ioRing: HIORING, count: UINT32, handles: [^]HANDLE, userData: UINT_PTR) -> HRESULT ---
|
||||
BuildIoRingRegisterBuffers :: proc(ioRing: HIORING, count: UINT32, buffers: [^]IORING_BUFFER_INFO, userData: UINT_PTR) -> HRESULT ---
|
||||
BuildIoRingWriteFile :: proc(ioRing: HIORING, fileRef: IORING_HANDLE_REF, bufferRef: IORING_BUFFER_REF, numberOfBytesToWrite: UINT32, fileOffset: UINT64, writeFlags: FILE_WRITE_FLAGS, userData: UINT_PTR, sqeFlags: IORING_SQE_FLAGS) -> HRESULT ---
|
||||
BuildIoRingFlushFile :: proc(ioRing: HIORING, fileRef: IORING_HANDLE_REF, flushMode: FILE_FLUSH_MODE, userData: UINT_PTR, sqeFlags: IORING_SQE_FLAGS) -> HRESULT ---
|
||||
BuildIoRingReadFileScatter :: proc(ioRing: HIORING, fileRef: IORING_HANDLE_REF, segmentCount: UINT32, segmentArray: [^]FILE_SEGMENT_ELEMENT, numberOfBytesToRead: UINT32, fileOffset: UINT64, userData: UINT_PTR, sqeFlags: IORING_SQE_FLAGS) -> HRESULT ---
|
||||
BuildIoRingWriteFileGather :: proc(ioRing: HIORING, fileRef: IORING_HANDLE_REF, segmentCount: UINT32, segmentArray: [^]FILE_SEGMENT_ELEMENT, numberOfBytesToWrite: UINT32, fileOffset: UINT64, writeFlags: FILE_WRITE_FLAGS, userData: UINT_PTR, sqeFlags: IORING_SQE_FLAGS) -> HRESULT ---
|
||||
}
|
||||
@@ -50,6 +50,22 @@ foreign ntdll_lib {
|
||||
EaBuffer: PVOID,
|
||||
EaLength: ULONG,
|
||||
) -> NTSTATUS ---
|
||||
|
||||
|
||||
NtAssociateWaitCompletionPacket :: proc(
|
||||
WaitCompletionPacketHandle: HANDLE,
|
||||
IoCompletionHandle: HANDLE,
|
||||
TargetObjectHandle: HANDLE,
|
||||
KeyContext: PVOID,
|
||||
ApcContext: PVOID,
|
||||
IoStatus: NTSTATUS,
|
||||
IoStatusInformation: ULONG_PTR,
|
||||
AlreadySignaled: ^BOOLEAN,
|
||||
) -> NTSTATUS ---
|
||||
|
||||
NtDelayExecution :: proc(Alertable: BOOL, DelayInterval: PLARGE_INTEGER) -> NTSTATUS ---
|
||||
|
||||
ZwSetTimerResolution :: proc(RequestedResolution: ULONG, Set: BOOLEAN, ActualResolution: PULONG) -> NTSTATUS ---
|
||||
}
|
||||
|
||||
|
||||
|
||||
42
core/sys/windows/ntioring_x.odin
Normal file
42
core/sys/windows/ntioring_x.odin
Normal file
@@ -0,0 +1,42 @@
|
||||
#+build windows
|
||||
package sys_windows
|
||||
|
||||
|
||||
IORING_SUBMIT_WAIT_ALL :: max(u32)
|
||||
|
||||
IORING_VERSION :: enum i32 {
|
||||
INVALID,
|
||||
_1,
|
||||
_2,
|
||||
_3 = 300,
|
||||
_4 = 400,
|
||||
}
|
||||
|
||||
IORING_FEATURE_FLAG :: enum u32 {
|
||||
UM_EMULATION = 0, // 0x00000001
|
||||
SET_COMPLETION_EVENT = 1, // 0x00000002
|
||||
}
|
||||
|
||||
IORING_FEATURE_FLAGS :: bit_set[IORING_FEATURE_FLAG; u32]
|
||||
|
||||
IORING_OP_CODE :: enum i32 {
|
||||
NOP,
|
||||
READ,
|
||||
REGISTER_FILES,
|
||||
REGISTER_BUFFERS,
|
||||
CANCEL,
|
||||
WRITE,
|
||||
FLUSH,
|
||||
READ_SCATTER,
|
||||
WRITE_GATHER,
|
||||
}
|
||||
|
||||
IORING_BUFFER_INFO :: struct {
|
||||
Address: rawptr,
|
||||
Length: u32,
|
||||
}
|
||||
|
||||
IORING_REGISTERED_BUFFER :: struct {
|
||||
BufferIndex: u32,
|
||||
Offset: u32,
|
||||
}
|
||||
@@ -132,6 +132,7 @@ LPSTARTUPINFOW :: ^STARTUPINFOW
|
||||
LPTRACKMOUSEEVENT :: ^TRACKMOUSEEVENT
|
||||
VOID :: rawptr
|
||||
PVOID :: rawptr
|
||||
PVOID64 :: rawptr
|
||||
LPVOID :: rawptr
|
||||
PINT :: ^INT
|
||||
LPINT :: ^INT
|
||||
|
||||
@@ -3,44 +3,42 @@ The implementation of the `odin test` runner and procedures user tests can use f
|
||||
|
||||
Defineables through `#config`:
|
||||
|
||||
```odin
|
||||
// Specify how many threads to use when running tests.
|
||||
TEST_THREADS : int : #config(ODIN_TEST_THREADS, 0)
|
||||
// Track the memory used by each test.
|
||||
TRACKING_MEMORY : bool : #config(ODIN_TEST_TRACK_MEMORY, true)
|
||||
// Always report how much memory is used, even when there are no leaks or bad frees.
|
||||
ALWAYS_REPORT_MEMORY : bool : #config(ODIN_TEST_ALWAYS_REPORT_MEMORY, false)
|
||||
// Treat memory leaks and bad frees as errors.
|
||||
FAIL_ON_BAD_MEMORY : bool : #config(ODIN_TEST_FAIL_ON_BAD_MEMORY, false)
|
||||
// Specify how much memory each thread allocator starts with.
|
||||
PER_THREAD_MEMORY : int : #config(ODIN_TEST_THREAD_MEMORY, mem. ROLLBACK_STACK_DEFAULT_BLOCK_SIZE)
|
||||
// Select a specific set of tests to run by name.
|
||||
// Each test is separated by a comma and may optionally include the package name.
|
||||
// This may be useful when running tests on multiple packages with `-all-packages`.
|
||||
// The format is: `package.test_name,test_name_only,...`
|
||||
TEST_NAMES : string : #config(ODIN_TEST_NAMES, "")
|
||||
// Show the fancy animated progress report.
|
||||
// This requires terminal color support, as well as STDOUT to not be redirected to a file.
|
||||
FANCY_OUTPUT : bool : #config(ODIN_TEST_FANCY, true)
|
||||
// Copy failed tests to the clipboard when done.
|
||||
USE_CLIPBOARD : bool : #config(ODIN_TEST_CLIPBOARD, false)
|
||||
// How many test results to show at a time per package.
|
||||
PROGRESS_WIDTH : int : #config(ODIN_TEST_PROGRESS_WIDTH, 24)
|
||||
// This is the random seed that will be sent to each test.
|
||||
// If it is unspecified, it will be set to the system cycle counter at startup.
|
||||
SHARED_RANDOM_SEED : u64 : #config(ODIN_TEST_RANDOM_SEED, 0)
|
||||
// Set the lowest log level for this test run.
|
||||
LOG_LEVEL_DEFAULT : string : "debug" when ODIN_DEBUG else "info"
|
||||
LOG_LEVEL : string : #config(ODIN_TEST_LOG_LEVEL, LOG_LEVEL_DEFAULT)
|
||||
// Report a message at the info level when a test has changed its state.
|
||||
LOG_STATE_CHANGES : bool : #config(ODIN_TEST_LOG_STATE_CHANGES, false)
|
||||
// Show only the most necessary logging information.
|
||||
USING_SHORT_LOGS : bool : #config(ODIN_TEST_SHORT_LOGS, false)
|
||||
// Output a report of the tests to the given path.
|
||||
JSON_REPORT : string : #config(ODIN_TEST_JSON_REPORT, "")
|
||||
// Print the full file path for failed test cases on a new line
|
||||
// in a way that's friendly to regex capture for an editor's "go to error".
|
||||
GO_TO_ERROR : bool : #config(ODIN_TEST_GO_TO_ERROR, false)
|
||||
```
|
||||
// Specify how many threads to use when running tests.
|
||||
TEST_THREADS : int : #config(ODIN_TEST_THREADS, 0)
|
||||
// Track the memory used by each test.
|
||||
TRACKING_MEMORY : bool : #config(ODIN_TEST_TRACK_MEMORY, true)
|
||||
// Always report how much memory is used, even when there are no leaks or bad frees.
|
||||
ALWAYS_REPORT_MEMORY : bool : #config(ODIN_TEST_ALWAYS_REPORT_MEMORY, false)
|
||||
// Treat memory leaks and bad frees as errors.
|
||||
FAIL_ON_BAD_MEMORY : bool : #config(ODIN_TEST_FAIL_ON_BAD_MEMORY, false)
|
||||
// Specify how much memory each thread allocator starts with.
|
||||
PER_THREAD_MEMORY : int : #config(ODIN_TEST_THREAD_MEMORY, mem. ROLLBACK_STACK_DEFAULT_BLOCK_SIZE)
|
||||
// Select a specific set of tests to run by name.
|
||||
// Each test is separated by a comma and may optionally include the package name.
|
||||
// This may be useful when running tests on multiple packages with `-all-packages`.
|
||||
// The format is: `package.test_name,test_name_only,...`
|
||||
TEST_NAMES : string : #config(ODIN_TEST_NAMES, "")
|
||||
// Show the fancy animated progress report.
|
||||
// This requires terminal color support, as well as STDOUT to not be redirected to a file.
|
||||
FANCY_OUTPUT : bool : #config(ODIN_TEST_FANCY, true)
|
||||
// Copy failed tests to the clipboard when done.
|
||||
USE_CLIPBOARD : bool : #config(ODIN_TEST_CLIPBOARD, false)
|
||||
// How many test results to show at a time per package.
|
||||
PROGRESS_WIDTH : int : #config(ODIN_TEST_PROGRESS_WIDTH, 24)
|
||||
// This is the random seed that will be sent to each test.
|
||||
// If it is unspecified, it will be set to the system cycle counter at startup.
|
||||
SHARED_RANDOM_SEED : u64 : #config(ODIN_TEST_RANDOM_SEED, 0)
|
||||
// Set the lowest log level for this test run.
|
||||
LOG_LEVEL_DEFAULT : string : "debug" when ODIN_DEBUG else "info"
|
||||
LOG_LEVEL : string : #config(ODIN_TEST_LOG_LEVEL, LOG_LEVEL_DEFAULT)
|
||||
// Report a message at the info level when a test has changed its state.
|
||||
LOG_STATE_CHANGES : bool : #config(ODIN_TEST_LOG_STATE_CHANGES, false)
|
||||
// Show only the most necessary logging information.
|
||||
USING_SHORT_LOGS : bool : #config(ODIN_TEST_SHORT_LOGS, false)
|
||||
// Output a report of the tests to the given path.
|
||||
JSON_REPORT : string : #config(ODIN_TEST_JSON_REPORT, "")
|
||||
// Print the full file path for failed test cases on a new line
|
||||
// in a way that's friendly to regex capture for an editor's "go to error".
|
||||
GO_TO_ERROR : bool : #config(ODIN_TEST_GO_TO_ERROR, false)
|
||||
*/
|
||||
package testing
|
||||
package testing
|
||||
|
||||
@@ -134,7 +134,7 @@ This is a dire bug and should be reported to the Odin developers.
|
||||
}
|
||||
signal := local_test_expected_failures.signal
|
||||
switch signal {
|
||||
case libc.SIGILL: passed = code == win32.EXCEPTION_ILLEGAL_INSTRUCTION
|
||||
case libc.SIGILL: passed = code == win32.EXCEPTION_ILLEGAL_INSTRUCTION || code == win32.EXCEPTION_ARRAY_BOUNDS_EXCEEDED
|
||||
case libc.SIGSEGV: passed = code == win32.EXCEPTION_ACCESS_VIOLATION
|
||||
case libc.SIGFPE:
|
||||
switch code {
|
||||
@@ -163,6 +163,7 @@ _setup_signal_handler :: proc() {
|
||||
// For tests:
|
||||
// Catch the following:
|
||||
// - Asserts and panics;
|
||||
// - Out of Bounds exeptions;
|
||||
// - Arithmetic errors; and
|
||||
// - Segmentation faults (illegal memory access).
|
||||
win32.AddVectoredExceptionHandler(0, stop_test_callback)
|
||||
@@ -194,10 +195,11 @@ _should_stop_test :: proc() -> (test_index: int, reason: Stop_Reason, ok: bool)
|
||||
reason = .Successful_Stop
|
||||
} else {
|
||||
switch intrinsics.atomic_load(&stop_test_signal) {
|
||||
case win32.EXCEPTION_ILLEGAL_INSTRUCTION: reason = .Illegal_Instruction
|
||||
case win32.EXCEPTION_ACCESS_VIOLATION: reason = .Segmentation_Fault
|
||||
case win32.EXCEPTION_BREAKPOINT: reason = .Unhandled_Trap
|
||||
case win32.EXCEPTION_SINGLE_STEP: reason = .Unhandled_Trap
|
||||
case win32.EXCEPTION_ARRAY_BOUNDS_EXCEEDED: reason = .Illegal_Instruction
|
||||
case win32.EXCEPTION_ILLEGAL_INSTRUCTION: reason = .Illegal_Instruction
|
||||
case win32.EXCEPTION_ACCESS_VIOLATION: reason = .Segmentation_Fault
|
||||
case win32.EXCEPTION_BREAKPOINT: reason = .Unhandled_Trap
|
||||
case win32.EXCEPTION_SINGLE_STEP: reason = .Unhandled_Trap
|
||||
|
||||
case win32.EXCEPTION_FLT_DENORMAL_OPERAND ..= win32.EXCEPTION_INT_OVERFLOW:
|
||||
reason = .Arithmetic_Error
|
||||
|
||||
@@ -43,6 +43,7 @@ package all
|
||||
@(require) import "core:crypto/legacy/keccak"
|
||||
@(require) import "core:crypto/legacy/md5"
|
||||
@(require) import "core:crypto/legacy/sha1"
|
||||
@(require) import cnoise "core:crypto/noise"
|
||||
@(require) import "core:crypto/pbkdf2"
|
||||
@(require) import "core:crypto/poly1305"
|
||||
@(require) import "core:crypto/ristretto255"
|
||||
|
||||
@@ -48,6 +48,7 @@ package all
|
||||
@(require) import "core:crypto/legacy/keccak"
|
||||
@(require) import "core:crypto/legacy/md5"
|
||||
@(require) import "core:crypto/legacy/sha1"
|
||||
@(require) import cnoise "core:crypto/noise"
|
||||
@(require) import "core:crypto/pbkdf2"
|
||||
@(require) import "core:crypto/poly1305"
|
||||
@(require) import "core:crypto/ristretto255"
|
||||
|
||||
@@ -578,7 +578,7 @@ struct BuildContext {
|
||||
bool internal_by_value;
|
||||
bool internal_weak_monomorphization;
|
||||
bool internal_ignore_llvm_verification;
|
||||
bool internal_llvm_mem2reg;
|
||||
bool internal_llvm_no_sroa;
|
||||
|
||||
bool enable_rvo;
|
||||
|
||||
@@ -2215,18 +2215,18 @@ gb_internal bool check_target_feature_is_enabled(String const &feature, String *
|
||||
}
|
||||
if (feature_str == "") break;
|
||||
|
||||
if (!string_set_exists(&build_context.target_features_set, str)) {
|
||||
String plus_str = concatenate_strings(temporary_allocator(), make_string_c("+"), feature_str);
|
||||
|
||||
if (want_enabled && !string_set_exists(&build_context.target_features_set, plus_str)) {
|
||||
if (not_enabled) *not_enabled = str;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
String plus_str = concatenate_strings(temporary_allocator(), make_string_c("+"), feature_str);
|
||||
String minus_str = concatenate_strings(temporary_allocator(), make_string_c("-"), feature_str);
|
||||
|
||||
if (!want_enabled && !string_set_exists(&build_context.target_features_set, minus_str)) {
|
||||
|
||||
bool has_raw = string_set_exists(&build_context.target_features_set, feature_str);
|
||||
bool has_plus = string_set_exists(&build_context.target_features_set, plus_str);
|
||||
bool has_minus = string_set_exists(&build_context.target_features_set, minus_str);
|
||||
|
||||
// NOTE(jakubtomsu): this way "feature" and "+feature" is ALWAYS equivalent,
|
||||
// and also allows the minus sign to do a final override.
|
||||
bool is_enabled = (has_plus || has_raw) && !has_minus;
|
||||
|
||||
if (want_enabled != is_enabled) {
|
||||
if (not_enabled) *not_enabled = str;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1130,8 +1130,8 @@ gb_internal void check_unroll_range_stmt(CheckerContext *ctx, Ast *node, u32 mod
|
||||
}
|
||||
}
|
||||
|
||||
check_stmt(ctx, irs->body, mod_flags);
|
||||
|
||||
u32 new_flags = mod_flags & ~Stmt_BreakAllowed & ~Stmt_ContinueAllowed;
|
||||
check_stmt(ctx, irs->body, new_flags);
|
||||
}
|
||||
|
||||
gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
|
||||
|
||||
@@ -2840,6 +2840,12 @@ gb_internal i64 check_array_count(CheckerContext *ctx, Operand *o, Ast *e) {
|
||||
error(e, "Array count too large, %.*s", LIT(str));
|
||||
gb_free(a, str.text);
|
||||
return 0;
|
||||
} else if (o->value.kind == ExactValue_Float) {
|
||||
u64 u = cast(u64)o->value.value_float;
|
||||
f64 f = cast(f64)u;
|
||||
if (f == o->value.value_float) {
|
||||
return u;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
|
||||
#define NOMINMAX 1
|
||||
#define NOMINMAX 1
|
||||
#define WINDOWS_LEAN_AND_MEAN 1
|
||||
#define VC_EXTRALEAN 1
|
||||
#include <windows.h>
|
||||
#undef NOMINMAX
|
||||
#endif
|
||||
|
||||
@@ -2,6 +2,25 @@
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
// Bionic may not provide aligned_alloc.
|
||||
// Provide a fallback using posix_memalign and ensure the alignment
|
||||
// satisfies its requirement (multiple of pointer size).
|
||||
static void* android_aligned_alloc(size_t align, size_t size) {
|
||||
void* allocated;
|
||||
|
||||
const size_t ptr_size = sizeof(void*);
|
||||
align = (align + (ptr_size - 1)) / ptr_size * ptr_size;
|
||||
|
||||
if (posix_memalign(&allocated, align, size) != 0)
|
||||
return NULL;
|
||||
return allocated;
|
||||
}
|
||||
#define aligned_alloc android_aligned_alloc
|
||||
|
||||
#endif
|
||||
|
||||
template <typename U, typename V>
|
||||
gb_internal gb_inline U bit_cast(V &v) { return reinterpret_cast<U &>(v); }
|
||||
|
||||
|
||||
@@ -289,7 +289,7 @@ gb_internal void print_doc_package(CheckerInfo *info, AstPackage *pkg) {
|
||||
}
|
||||
curr_file = e->file;
|
||||
String filename = remove_directory_from_path(curr_file->fullpath);
|
||||
print_doc_line(1, "file: %s", filename);
|
||||
print_doc_line(1, "file: %.*s", LIT(filename));
|
||||
}
|
||||
} else {
|
||||
if (curr_entity_kind != e->kind) {
|
||||
|
||||
@@ -4680,6 +4680,8 @@ gb_internal lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) {
|
||||
}
|
||||
}
|
||||
lbValue val = lb_emit_ptr_offset(p, field, index);
|
||||
// make sure it's ^T and not [^]T
|
||||
val.type = alloc_type_multi_pointer_to_pointer(val.type);
|
||||
return lb_addr(val);
|
||||
}
|
||||
|
||||
@@ -6092,8 +6094,8 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
|
||||
if (sub_sel.index.count > 0) {
|
||||
item = lb_emit_deep_field_gep(p, item, sub_sel);
|
||||
}
|
||||
// make sure it's ^T and not [^]T
|
||||
item.type = alloc_type_multi_pointer_to_pointer(item.type);
|
||||
// make sure it's ^T and not [^]T
|
||||
item.type = alloc_type_multi_pointer_to_pointer(item.type);
|
||||
|
||||
return lb_addr(item);
|
||||
} else if (addr.kind == lbAddr_Swizzle) {
|
||||
|
||||
@@ -701,13 +701,20 @@ gb_internal void lb_set_file_line_col(lbProcedure *p, Array<lbValue> arr, TokenP
|
||||
arr[2] = lb_const_int(p->module, t_i32, col);
|
||||
}
|
||||
|
||||
gb_internal bool lb_bounds_check_short_circuit(lbProcedure *p, lbValue index, lbValue len) {
|
||||
gb_internal bool lb_bounds_check_disabled(lbProcedure *p) {
|
||||
if (build_context.no_bounds_check) {
|
||||
return true;
|
||||
}
|
||||
if ((p->state_flags & StateFlag_no_bounds_check) != 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal bool lb_bounds_check_short_circuit(lbProcedure *p, lbValue index, lbValue len) {
|
||||
if (lb_bounds_check_disabled(p)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (LLVMIsConstant(index.value) && LLVMIsConstant(len.value)) {
|
||||
i64 i = LLVMConstIntGetSExtValue(index.value);
|
||||
@@ -757,10 +764,7 @@ gb_internal void lb_emit_bounds_check(lbProcedure *p, Token token, lbValue index
|
||||
}
|
||||
|
||||
gb_internal void lb_emit_matrix_bounds_check(lbProcedure *p, Token token, lbValue row_index, lbValue column_index, lbValue row_count, lbValue column_count) {
|
||||
if (build_context.no_bounds_check) {
|
||||
return;
|
||||
}
|
||||
if ((p->state_flags & StateFlag_no_bounds_check) != 0) {
|
||||
if (lb_bounds_check_disabled(p)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -783,10 +787,7 @@ gb_internal void lb_emit_matrix_bounds_check(lbProcedure *p, Token token, lbValu
|
||||
|
||||
|
||||
gb_internal void lb_emit_multi_pointer_slice_bounds_check(lbProcedure *p, Token token, lbValue low, lbValue high) {
|
||||
if (build_context.no_bounds_check) {
|
||||
return;
|
||||
}
|
||||
if ((p->state_flags & StateFlag_no_bounds_check) != 0) {
|
||||
if (lb_bounds_check_disabled(p)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -811,6 +812,9 @@ gb_internal void lb_emit_multi_pointer_slice_bounds_check(lbProcedure *p, Token
|
||||
}
|
||||
|
||||
gb_internal void lb_emit_slice_bounds_check(lbProcedure *p, Token token, lbValue low, lbValue high, lbValue len, bool lower_value_used) {
|
||||
if (lb_bounds_check_disabled(p)) {
|
||||
return;
|
||||
}
|
||||
if (!lower_value_used && lb_bounds_check_short_circuit(p, high, len)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,17 @@
|
||||
array_add(&passes, "function(annotation-remarks)");
|
||||
break;
|
||||
case 0:
|
||||
array_add(&passes, "always-inline");
|
||||
if (build_context.internal_llvm_mem2reg) {
|
||||
array_add(&passes, "function<eager-inv>(mem2reg)");
|
||||
if (build_context.internal_llvm_no_sroa) {
|
||||
// Old -o:minimal behavior
|
||||
array_add(&passes, "always-inline");
|
||||
} else {
|
||||
array_add(&passes, "annotation2metadata");
|
||||
array_add(&passes, "inferattrs");
|
||||
array_add(&passes, "forceattrs");
|
||||
array_add(&passes, "function<eager-inv>(sroa<modify-cfg>,early-cse<>)");
|
||||
array_add(&passes, "always-inline");
|
||||
array_add(&passes, "function<eager-inv>(sroa<modify-cfg>,instsimplify,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>)");
|
||||
// array_add(&passes, "verify");
|
||||
}
|
||||
array_add(&passes, "function(annotation-remarks)");
|
||||
break;
|
||||
|
||||
42
src/main.cpp
42
src/main.cpp
@@ -402,7 +402,7 @@ enum BuildFlagKind {
|
||||
BuildFlag_InternalByValue,
|
||||
BuildFlag_InternalWeakMonomorphization,
|
||||
BuildFlag_InternalLLVMVerification,
|
||||
BuildFlag_InternalLLVMMem2Reg,
|
||||
BuildFlag_InternalLLVMNoSROA,
|
||||
BuildFlag_InternalEnableRVO,
|
||||
|
||||
BuildFlag_Sanitize,
|
||||
@@ -634,7 +634,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
add_flag(&build_flags, BuildFlag_InternalByValue, str_lit("internal-by-value"), BuildFlagParam_None, Command_all);
|
||||
add_flag(&build_flags, BuildFlag_InternalWeakMonomorphization, str_lit("internal-weak-monomorphization"), BuildFlagParam_None, Command_all);
|
||||
add_flag(&build_flags, BuildFlag_InternalLLVMVerification, str_lit("internal-ignore-llvm-verification"), BuildFlagParam_None, Command_all);
|
||||
add_flag(&build_flags, BuildFlag_InternalLLVMMem2Reg, str_lit("internal-llvm-mem2reg"), BuildFlagParam_None, Command_all);
|
||||
add_flag(&build_flags, BuildFlag_InternalLLVMNoSROA, str_lit("internal-llvm-no-sroa"), BuildFlagParam_None, Command_all);
|
||||
add_flag(&build_flags, BuildFlag_InternalEnableRVO, str_lit("internal-enable-rvo"), BuildFlagParam_None, Command_all);
|
||||
|
||||
|
||||
@@ -1628,8 +1628,8 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
case BuildFlag_InternalLLVMVerification:
|
||||
build_context.internal_ignore_llvm_verification = true;
|
||||
break;
|
||||
case BuildFlag_InternalLLVMMem2Reg:
|
||||
build_context.internal_llvm_mem2reg = true;
|
||||
case BuildFlag_InternalLLVMNoSROA:
|
||||
build_context.internal_llvm_no_sroa = true;
|
||||
break;
|
||||
case BuildFlag_InternalEnableRVO:
|
||||
build_context.enable_rvo = true;
|
||||
@@ -3973,15 +3973,16 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
for (;;) {
|
||||
String item = string_split_iterator(&target_it, ',');
|
||||
if (item == "") break;
|
||||
|
||||
if (*item.text == '+' || *item.text == '-') {
|
||||
item.text++;
|
||||
item.len--;
|
||||
|
||||
String stripped_item = item;
|
||||
if (*stripped_item.text == '+' || *stripped_item.text == '-') {
|
||||
stripped_item.text++;
|
||||
stripped_item.len--;
|
||||
}
|
||||
|
||||
String invalid;
|
||||
if (!check_target_feature_is_valid_for_target_arch(item, &invalid) && item != str_lit("help")) {
|
||||
if (item != str_lit("?")) {
|
||||
if (!check_target_feature_is_valid_for_target_arch(stripped_item, &invalid) && stripped_item != str_lit("help")) {
|
||||
if (stripped_item != str_lit("?")) {
|
||||
gb_printf_err("Unkown target feature '%.*s'.\n", LIT(invalid));
|
||||
}
|
||||
gb_printf("Possible -target-features for target %.*s are:\n", LIT(target_arch_names[build_context.metrics.arch]));
|
||||
@@ -4005,8 +4006,25 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
string_set_add(&build_context.target_features_set, item);
|
||||
|
||||
// Ensure the feature name always has +/- prefix. If there isn't, default to '+'
|
||||
String feature_str = item;
|
||||
if (*feature_str.text != '+' && *feature_str.text != '-') {
|
||||
feature_str = concatenate_strings(temporary_allocator(), make_string_c("+"), feature_str);
|
||||
}
|
||||
|
||||
// Ensure there is only a single entry for each feature in the target set.
|
||||
// If the negative exists, override the existing value with the current one.
|
||||
String neg_feature_str = clone_string(temporary_allocator(), feature_str);
|
||||
switch (*neg_feature_str.text) {
|
||||
case '+': *neg_feature_str.text = '-'; break;
|
||||
case '-': *neg_feature_str.text = '+'; break;
|
||||
default: GB_ASSERT(false); break;
|
||||
}
|
||||
|
||||
string_set_remove(&build_context.target_features_set, neg_feature_str);
|
||||
|
||||
string_set_add(&build_context.target_features_set, feature_str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -508,6 +508,13 @@ gb_internal String filename_without_directory(String s) {
|
||||
return substring(s, gb_max(j+1, 0), s.len);
|
||||
}
|
||||
|
||||
gb_internal String clone_string(gbAllocator a, String const &x) {
|
||||
u8 *data = gb_alloc_array(a, u8, x.len+1);
|
||||
gb_memmove(data, x.text, x.len);
|
||||
data[x.len] = 0;
|
||||
return make_string(data, x.len);
|
||||
}
|
||||
|
||||
gb_internal String concatenate_strings(gbAllocator a, String const &x, String const &y) {
|
||||
isize len = x.len+y.len;
|
||||
u8 *data = gb_alloc_array(a, u8, len+1);
|
||||
|
||||
1
tests/core/assets/Noise/.gitignore
vendored
Normal file
1
tests/core/assets/Noise/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.txt
|
||||
25
tests/core/crypto/common/common.odin
Normal file
25
tests/core/crypto/common/common.odin
Normal file
@@ -0,0 +1,25 @@
|
||||
package test_crypto_common
|
||||
|
||||
import "core:bytes"
|
||||
import "core:encoding/hex"
|
||||
|
||||
// Common helpers for cryptography tests.
|
||||
|
||||
Hex_Bytes :: string
|
||||
|
||||
hexbytes_compare :: proc(x: Hex_Bytes, b: []byte, allocator := context.allocator) -> bool {
|
||||
dst := hexbytes_decode(x)
|
||||
defer delete(dst)
|
||||
|
||||
return bytes.equal(dst, b)
|
||||
}
|
||||
|
||||
hexbytes_decode :: proc(x: Hex_Bytes, allocator := context.allocator) -> []byte {
|
||||
dst, ok := hex.decode(transmute([]byte)(x), allocator)
|
||||
if !ok {
|
||||
panic("Hex_Bytes: invalid hex encoding")
|
||||
}
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
440
tests/core/crypto/noise/main.odin
Normal file
440
tests/core/crypto/noise/main.odin
Normal file
@@ -0,0 +1,440 @@
|
||||
package test_noise
|
||||
|
||||
import "core:crypto/ecdh"
|
||||
import "core:crypto/noise"
|
||||
import "core:log"
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import "core:strings"
|
||||
import "core:testing"
|
||||
|
||||
import "../common"
|
||||
|
||||
ARENA_SIZE :: 8 * 1024 * 1024
|
||||
|
||||
BASE_PATH :: ODIN_ROOT + "tests/core/assets/Noise"
|
||||
|
||||
@(test)
|
||||
print_test_vector_path :: proc(t: ^testing.T) {
|
||||
log.infof("noise path: %s", BASE_PATH)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_vectors_snow :: proc(t: ^testing.T) {
|
||||
arena: mem.Arena
|
||||
arena_backing := make([]byte, ARENA_SIZE)
|
||||
defer delete(arena_backing)
|
||||
mem.arena_init(&arena, arena_backing)
|
||||
context.allocator = mem.arena_allocator(&arena)
|
||||
|
||||
log.debug("noise/snow: starting")
|
||||
|
||||
F :: "snow.txt"
|
||||
fn, _ := os.join_path([]string{BASE_PATH, F}, context.allocator)
|
||||
defer delete(fn)
|
||||
test_vectors: Test_Vectors
|
||||
testing.expectf(t, load(&test_vectors, fn), "unable to load {}", fn)
|
||||
|
||||
run_test_vectors(t, F, &test_vectors)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_vectors_noise_c_basic :: proc(t: ^testing.T) {
|
||||
arena: mem.Arena
|
||||
arena_backing := make([]byte, ARENA_SIZE)
|
||||
defer delete(arena_backing)
|
||||
mem.arena_init(&arena, arena_backing)
|
||||
context.allocator = mem.arena_allocator(&arena)
|
||||
|
||||
log.debug("noise/noise-c-basic: starting")
|
||||
|
||||
F :: "noise-c-basic.txt"
|
||||
fn, _ := os.join_path([]string{BASE_PATH, F}, context.allocator)
|
||||
defer delete(fn)
|
||||
test_vectors: Test_Vectors
|
||||
testing.expectf(t, load(&test_vectors, fn), "unable to load {}", fn)
|
||||
|
||||
run_test_vectors(t, F, &test_vectors)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_vectors_cacophony :: proc(t: ^testing.T) {
|
||||
arena: mem.Arena
|
||||
arena_backing := make([]byte, ARENA_SIZE)
|
||||
defer delete(arena_backing)
|
||||
mem.arena_init(&arena, arena_backing)
|
||||
context.allocator = mem.arena_allocator(&arena)
|
||||
|
||||
log.debug("noise/cacophony: starting")
|
||||
|
||||
F :: "cacophony.txt"
|
||||
fn, _ := os.join_path([]string{BASE_PATH, F}, context.allocator)
|
||||
defer delete(fn)
|
||||
test_vectors: Test_Vectors
|
||||
testing.expectf(t, load(&test_vectors, fn), "unable to load {}", fn)
|
||||
|
||||
run_test_vectors(t, F, &test_vectors)
|
||||
}
|
||||
|
||||
run_test_vectors :: proc(t: ^testing.T, f: string, tvs: ^Test_Vectors) {
|
||||
num_ran, num_passed, num_failed, num_skipped: int
|
||||
tv_loop: for &v, i in tvs.vectors {
|
||||
num_ran += 1
|
||||
|
||||
protocol_name: string
|
||||
switch {
|
||||
case v.protocol_name != "":
|
||||
protocol_name = v.protocol_name
|
||||
case v.name != "":
|
||||
// Old test vector format used by the C impl.
|
||||
v.protocol_name = v.name
|
||||
protocol_name = v.name
|
||||
}
|
||||
|
||||
// Skip unsupported test vectors
|
||||
if v.fail {
|
||||
num_skipped += 1
|
||||
log.debugf("%s[%d]: %s - skipped, fail tests not supported", f, i, protocol_name)
|
||||
continue
|
||||
}
|
||||
if v.fallback {
|
||||
num_skipped += 1
|
||||
log.debugf("%s[%d]: %s - skipped, fallback patterns not supported", f, i, protocol_name)
|
||||
continue
|
||||
}
|
||||
if strings.has_prefix(protocol_name, "NoisePSK") {
|
||||
num_skipped += 1
|
||||
log.debugf("%s[%d]: %s - skipped, Old PSK not supported", f, i, protocol_name)
|
||||
continue
|
||||
}
|
||||
if len(v.init_psks) > 1 || len(v.resp_psks) > 1 {
|
||||
num_skipped += 1
|
||||
log.debugf("%s[%d]: %s - skipped, Multi-PSK not supported", f, i, protocol_name)
|
||||
continue
|
||||
}
|
||||
|
||||
// Initialize Handshake_Statuses
|
||||
pattern, dh, _, _, status := noise.split_protocol_string(protocol_name)
|
||||
if !testing.expectf(t, status == .Ok, "%s[%d]: failed to parse protocol '%s': %v", f, i, protocol_name, status) {
|
||||
num_failed += 1
|
||||
continue
|
||||
}
|
||||
ini_hs, res_hs: noise.Handshake_State
|
||||
if !testing.expect(t, handshake_states_from_tv(t, &ini_hs, &res_hs, &v, dh, f, i)) {
|
||||
num_failed += 1
|
||||
continue
|
||||
}
|
||||
defer noise.handshake_reset(&ini_hs)
|
||||
defer noise.handshake_reset(&res_hs)
|
||||
|
||||
// Play back the messages
|
||||
if !testing.expectf(
|
||||
t,
|
||||
replay_messages_from_tv_rw(t, &ini_hs, &v, pattern, f),
|
||||
"%s[%d]: %s - failed to playback messages (initiator)", f, i, protocol_name,
|
||||
) {
|
||||
num_failed += 1
|
||||
continue
|
||||
}
|
||||
if !testing.expectf(
|
||||
t,
|
||||
replay_messages_from_tv_rw(t, &res_hs, &v, pattern, f),
|
||||
"%s[%d]: %s - failed to playback messages (responder)", f, i, protocol_name,
|
||||
) {
|
||||
num_failed += 1
|
||||
continue
|
||||
}
|
||||
|
||||
// Check handshake hash/peer identities.
|
||||
if v.handshake_hash != "" {
|
||||
ini_hash, _ := noise.handshake_hash(&ini_hs)
|
||||
res_hash, _ := noise.handshake_hash(&res_hs)
|
||||
if !testing.expectf(
|
||||
t,
|
||||
common.hexbytes_compare(v.handshake_hash, ini_hash),
|
||||
"%s[%d]: %s - invalid initiator handshake hash: %x expected: %s", f, i, protocol_name, ini_hash, v.handshake_hash,
|
||||
) {
|
||||
num_failed += 1
|
||||
continue
|
||||
}
|
||||
if !testing.expectf(
|
||||
t,
|
||||
common.hexbytes_compare(v.handshake_hash, res_hash),
|
||||
"%s[%d]: %s - invalid responder handshake hash: %x expected: %s", f, i, protocol_name, res_hash, v.handshake_hash,
|
||||
) {
|
||||
num_failed += 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
if ecdh.curve(&ini_hs.s) != .Invalid {
|
||||
pub_key, _ := noise.handshake_peer_identity(&res_hs)
|
||||
if !testing.expectf(
|
||||
t,
|
||||
pub_key != nil && ecdh.public_key_equal(&ini_hs.s._pub_key, pub_key),
|
||||
"%s[%d]: %s - invalid initiator static public key known by responder", f, i, protocol_name,
|
||||
) {
|
||||
num_failed += 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
if ecdh.curve(&res_hs.s) != .Invalid {
|
||||
pub_key, _ := noise.handshake_peer_identity(&ini_hs)
|
||||
if !testing.expectf(
|
||||
t,
|
||||
pub_key != nil && ecdh.public_key_equal(&res_hs.s._pub_key, pub_key),
|
||||
"%s[%d]: %s - invalid responder static public key known by initiator", f, i, protocol_name,
|
||||
) {
|
||||
num_failed += 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
log.debugf("%s[%d]: %s - Passed", f, i, protocol_name)
|
||||
num_passed += 1
|
||||
}
|
||||
|
||||
assert(num_ran == len(tvs.vectors))
|
||||
assert(num_passed + num_failed + num_skipped == num_ran)
|
||||
|
||||
log.infof(
|
||||
"%s: ran %d, passed %d, failed %d, skipped %d",
|
||||
f,
|
||||
num_ran,
|
||||
num_passed,
|
||||
num_failed,
|
||||
num_skipped,
|
||||
)
|
||||
}
|
||||
|
||||
handshake_states_from_tv :: proc(
|
||||
t: ^testing.T,
|
||||
ini_hs, res_hs: ^noise.Handshake_State,
|
||||
v: ^Vector,
|
||||
dh: ecdh.Curve,
|
||||
f: string,
|
||||
i: int,
|
||||
) -> bool {
|
||||
protocol_name := v.protocol_name
|
||||
|
||||
ini_static, ini_ephemeral: ecdh.Private_Key
|
||||
res_static, res_ephemeral: ecdh.Private_Key
|
||||
ini_res_static, res_ini_static: ecdh.Public_Key
|
||||
|
||||
ini_s, ini_e: ^ecdh.Private_Key
|
||||
ini_s_p, ini_e_p: ^ecdh.Public_Key
|
||||
ini_r_s: ^ecdh.Public_Key
|
||||
if len(v.init_static) != 0 {
|
||||
if !testing.expectf(
|
||||
t,
|
||||
ecdh.private_key_set_bytes(&ini_static, dh, common.hexbytes_decode(v.init_static)),
|
||||
"%s[%d]: %s - failed to parse init_static", f, i, protocol_name,
|
||||
) {
|
||||
return false
|
||||
}
|
||||
ini_s = &ini_static
|
||||
ini_s_p = &ini_s._pub_key
|
||||
}
|
||||
if len(v.init_ephemeral) != 0 {
|
||||
if !testing.expectf(
|
||||
t,
|
||||
ecdh.private_key_set_bytes(&ini_ephemeral, dh, common.hexbytes_decode(v.init_ephemeral)),
|
||||
"%s[%d]: %s - failed to parse init_ephemeral", f, i, protocol_name,
|
||||
) {
|
||||
return false
|
||||
}
|
||||
ini_e = &ini_ephemeral
|
||||
ini_e_p = &ini_e._pub_key
|
||||
}
|
||||
if len(v.init_remote_static) != 0 {
|
||||
if !testing.expectf(
|
||||
t,
|
||||
ecdh.public_key_set_bytes(&ini_res_static, dh, common.hexbytes_decode(v.init_remote_static)),
|
||||
"%s[%d]: %s - failed to parse init_remote_static", f, i, protocol_name,
|
||||
) {
|
||||
return false
|
||||
}
|
||||
ini_r_s = &ini_res_static
|
||||
}
|
||||
|
||||
res_s, res_e: ^ecdh.Private_Key
|
||||
res_s_p, res_e_p: ^ecdh.Public_Key
|
||||
res_i_s: ^ecdh.Public_Key
|
||||
if len(v.resp_static) != 0 {
|
||||
if !testing.expectf(
|
||||
t,
|
||||
ecdh.private_key_set_bytes(&res_static, dh, common.hexbytes_decode(v.resp_static)),
|
||||
"%s[%d]: %s - failed to parse resp_static", f, i, protocol_name,
|
||||
) {
|
||||
return false
|
||||
}
|
||||
res_s = &res_static
|
||||
res_s_p = &res_s._pub_key
|
||||
}
|
||||
if len(v.resp_ephemeral) != 0 {
|
||||
if !testing.expectf(
|
||||
t,
|
||||
ecdh.private_key_set_bytes(&res_ephemeral, dh, common.hexbytes_decode(v.resp_ephemeral)),
|
||||
"%s[%d]: %s - failed to parse resp_ephemeral", f, i, protocol_name,
|
||||
) {
|
||||
return false
|
||||
}
|
||||
res_e = &res_ephemeral
|
||||
res_e_p = &res_e._pub_key
|
||||
}
|
||||
if len(v.resp_remote_static) != 0 {
|
||||
if !testing.expectf(
|
||||
t,
|
||||
ecdh.public_key_set_bytes(&res_ini_static, dh, common.hexbytes_decode(v.resp_remote_static)),
|
||||
"%s[%d]: %s - failed to parse remote_init_static", f, i, protocol_name,
|
||||
) {
|
||||
return false
|
||||
}
|
||||
res_i_s = &res_ini_static
|
||||
}
|
||||
|
||||
ini_psk, res_psk: []byte
|
||||
if len(v.init_psks) > 0 {
|
||||
ini_psk = common.hexbytes_decode(v.init_psks[0])
|
||||
}
|
||||
if len(v.resp_psks) > 0 {
|
||||
res_psk = common.hexbytes_decode(v.resp_psks[0])
|
||||
}
|
||||
|
||||
status := noise.handshake_init(
|
||||
ini_hs,
|
||||
true,
|
||||
common.hexbytes_decode(v.init_prologue),
|
||||
ini_s,
|
||||
ini_r_s,
|
||||
protocol_name,
|
||||
ini_psk,
|
||||
ini_e,
|
||||
)
|
||||
if !testing.expectf(
|
||||
t,
|
||||
status == .Ok,
|
||||
"%s[%d]: %s - failed to initialize ini_hs: %v", f, i, protocol_name, status,
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
status = noise.handshake_init(
|
||||
res_hs,
|
||||
false,
|
||||
common.hexbytes_decode(v.resp_prologue),
|
||||
res_s,
|
||||
res_i_s,
|
||||
protocol_name,
|
||||
res_psk,
|
||||
res_e,
|
||||
)
|
||||
if !testing.expectf(
|
||||
t,
|
||||
status == .Ok,
|
||||
"%s[%d]: %s - failed to initialize res_hs: %v", f, i, protocol_name, status,
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
replay_messages_from_tv_rw :: proc(
|
||||
t: ^testing.T,
|
||||
hs: ^noise.Handshake_State,
|
||||
v: ^Vector,
|
||||
pattern: noise.Handshake_Pattern,
|
||||
f: string,
|
||||
) -> bool {
|
||||
protocol_name := v.protocol_name
|
||||
pattern_is_one_way := noise.pattern_is_one_way(pattern)
|
||||
is_initiator := hs.initiator
|
||||
|
||||
cs: noise.Cipher_States
|
||||
|
||||
defer noise.cipherstates_reset(&cs)
|
||||
|
||||
hs_done: bool
|
||||
for &msg, i in &v.messages {
|
||||
dst: []byte
|
||||
status: noise.Status
|
||||
expected: common.Hex_Bytes
|
||||
|
||||
switch hs_done {
|
||||
case false:
|
||||
if (i & 1 == 0) == is_initiator {
|
||||
dst, status = noise.handshake_write_message(hs, common.hexbytes_decode(msg.payload))
|
||||
expected = msg.ciphertext
|
||||
} else {
|
||||
dst, status = noise.handshake_read_message(hs, common.hexbytes_decode(msg.ciphertext))
|
||||
expected = msg.payload
|
||||
}
|
||||
defer delete(dst)
|
||||
|
||||
if !testing.expectf(
|
||||
t,
|
||||
status == .Handshake_Pending || status == .Handshake_Complete,
|
||||
"%s: %s[%d] - unexpected handshake status: %v", f, protocol_name, i, status,
|
||||
) {
|
||||
return false
|
||||
}
|
||||
if !testing.expectf(
|
||||
t,
|
||||
common.hexbytes_compare(expected, dst),
|
||||
"%s: %s[%d] - unexpected message/payload: %x expected: %s", f, protocol_name, i, dst, expected,
|
||||
) {
|
||||
return false
|
||||
}
|
||||
if status == .Handshake_Complete {
|
||||
status = noise.handshake_split(hs, &cs)
|
||||
if !testing.expectf(
|
||||
t,
|
||||
status == .Ok,
|
||||
"%s: %s[%d] - handshake_split failed: %v", f, protocol_name, i, status,
|
||||
) {
|
||||
return false
|
||||
}
|
||||
hs_done = true
|
||||
}
|
||||
case true:
|
||||
// The messages that use the derived cipherstates just follow the
|
||||
// handshake message(s), and the flow continues.
|
||||
if pattern_is_one_way {
|
||||
// Except one-way patterns which go from initiator to responder.
|
||||
if is_initiator {
|
||||
dst, status = noise.seal_message(&cs, nil, common.hexbytes_decode(msg.payload))
|
||||
expected = msg.ciphertext
|
||||
} else {
|
||||
dst, status = noise.open_message(&cs, nil, common.hexbytes_decode(msg.ciphertext))
|
||||
expected = msg.payload
|
||||
}
|
||||
} else {
|
||||
if (i & 1 == 0) == is_initiator {
|
||||
dst, status = noise.seal_message(&cs, nil, common.hexbytes_decode(msg.payload))
|
||||
expected = msg.ciphertext
|
||||
} else {
|
||||
dst, status = noise.open_message(&cs, nil, common.hexbytes_decode(msg.ciphertext))
|
||||
expected = msg.payload
|
||||
}
|
||||
}
|
||||
defer delete(dst)
|
||||
|
||||
if !testing.expectf(
|
||||
t,
|
||||
status == .Ok,
|
||||
"%s: %s[%d] - seal/open failed: %v", f, protocol_name, i, status,
|
||||
) {
|
||||
return false
|
||||
}
|
||||
if !testing.expectf(
|
||||
t,
|
||||
common.hexbytes_compare(expected, dst),
|
||||
"%s: %s[%d] - unexpected ciphertext/plaintext: %x expected: %s", f, protocol_name, i, dst, expected,
|
||||
) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
56
tests/core/crypto/noise/schemas.odin
Normal file
56
tests/core/crypto/noise/schemas.odin
Normal file
@@ -0,0 +1,56 @@
|
||||
package test_noise
|
||||
|
||||
import "core:encoding/json"
|
||||
import "core:log"
|
||||
import "core:os"
|
||||
|
||||
import "../common"
|
||||
|
||||
Message :: struct {
|
||||
payload: common.Hex_Bytes `json:"payload"`,
|
||||
ciphertext: common.Hex_Bytes `json:"ciphertext"`,
|
||||
}
|
||||
|
||||
Vector :: struct {
|
||||
name: string `json:"name"`,
|
||||
|
||||
protocol_name: string `json:"protocol_name"`,
|
||||
fail: bool `json:"fail"`,
|
||||
fallback: bool `json:"fallback"`,
|
||||
fallback_pattern: string `json:"fallback_pattern"`,
|
||||
|
||||
init_prologue: common.Hex_Bytes `json:"init_prologue"`,
|
||||
init_psks: []common.Hex_Bytes `json:"init_psks"`,
|
||||
init_static: common.Hex_Bytes `json:"init_static"`,
|
||||
init_ephemeral: common.Hex_Bytes `json:"init_ephemeral"`,
|
||||
init_remote_static: common.Hex_Bytes `json:"init_remote_static"`,
|
||||
|
||||
resp_prologue: common.Hex_Bytes `json:"resp_prologue"`,
|
||||
resp_psks: []common.Hex_Bytes `json:"resp_psks"`,
|
||||
resp_static: common.Hex_Bytes `json:"resp_static"`,
|
||||
resp_ephemeral: common.Hex_Bytes `json:"resp_ephemeral"`,
|
||||
resp_remote_static: common.Hex_Bytes `json:"resp_remote_static"`,
|
||||
|
||||
handshake_hash: common.Hex_Bytes `json:"handshake_hash"`,
|
||||
|
||||
messages: []Message `json:"messages"`,
|
||||
}
|
||||
|
||||
Test_Vectors :: struct {
|
||||
vectors: []Vector `json:"vectors"`,
|
||||
}
|
||||
|
||||
load :: proc(tvs: ^Test_Vectors, fn: string) -> bool {
|
||||
raw_json, err := os.read_entire_file_from_path(fn, context.allocator)
|
||||
if err != os.ERROR_NONE {
|
||||
log.error("failed to load raw JSON")
|
||||
return false
|
||||
}
|
||||
|
||||
if err := json.unmarshal(raw_json, tvs); err != nil {
|
||||
log.errorf("failed to parse JSON: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
306
tests/core/crypto/test_core_crypto_noise.odin
Normal file
306
tests/core/crypto/test_core_crypto_noise.odin
Normal file
@@ -0,0 +1,306 @@
|
||||
package test_core_crypto
|
||||
|
||||
import "core:bytes"
|
||||
import "core:crypto"
|
||||
import "core:crypto/aead"
|
||||
import "core:crypto/ecdh"
|
||||
import "core:crypto/hash"
|
||||
import "core:crypto/noise"
|
||||
import "core:fmt"
|
||||
import "core:log"
|
||||
import "core:math/rand"
|
||||
import "core:testing"
|
||||
|
||||
@(private = "file")
|
||||
DH_CURVES :: []ecdh.Curve {
|
||||
.X25519,
|
||||
.X448,
|
||||
}
|
||||
@(private = "file")
|
||||
CIPHERS :: []aead.Algorithm{
|
||||
.AES_GCM_256,
|
||||
.CHACHA20POLY1305,
|
||||
}
|
||||
@(private = "file")
|
||||
HASHES :: []hash.Algorithm{
|
||||
.SHA256,
|
||||
.SHA512,
|
||||
.BLAKE2S,
|
||||
.BLAKE2B,
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_supported_protocols :: proc(t: ^testing.T) {
|
||||
if !crypto.HAS_RAND_BYTES {
|
||||
log.info("rand_bytes not supported - skipping")
|
||||
return
|
||||
}
|
||||
|
||||
protocol: Test_Protocol
|
||||
for pattern in noise.Handshake_Pattern {
|
||||
if pattern == .Invalid {
|
||||
continue
|
||||
}
|
||||
protocol.handshake_pattern = pattern
|
||||
for dh in DH_CURVES {
|
||||
protocol.dh = dh
|
||||
for cipher in CIPHERS {
|
||||
protocol.cipher = cipher
|
||||
for hash in HASHES {
|
||||
protocol.hash = hash
|
||||
if !testing.expectf(
|
||||
t,
|
||||
test_noise_one_protocol(t, &protocol, context.temp_allocator),
|
||||
"Failed protocol: %v", protocol,
|
||||
) {
|
||||
testing.fail(t)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(private = "file")
|
||||
test_noise_one_protocol :: proc(t: ^testing.T, protocol: ^Test_Protocol, allocator := context.allocator) -> bool {
|
||||
protocol_name := test_protocol_string(protocol, allocator)
|
||||
defer delete(protocol_name, allocator)
|
||||
|
||||
log.debugf("crypto/noise: %s", protocol_name)
|
||||
|
||||
is_one_way := noise.pattern_is_one_way(protocol.handshake_pattern)
|
||||
|
||||
initiator_s, responder_s: ecdh.Private_Key
|
||||
ini_s, res_s: ^ecdh.Private_Key
|
||||
ini_s_pub, res_s_pub: ^ecdh.Public_Key
|
||||
|
||||
pre, hs := noise.pattern_requires_initiator_s(protocol.handshake_pattern)
|
||||
if pre || hs {
|
||||
if !testing.expect(t, ecdh.private_key_generate(&initiator_s, protocol.dh), "failed to generate initiator s") {
|
||||
return false
|
||||
}
|
||||
ini_s = &initiator_s
|
||||
if pre {
|
||||
ini_s_pub = &initiator_s._pub_key
|
||||
}
|
||||
}
|
||||
pre, hs = noise.pattern_requires_responder_s(protocol.handshake_pattern)
|
||||
if pre || hs {
|
||||
if !testing.expect(t, ecdh.private_key_generate(&responder_s, protocol.dh), "failed to generate responder s") {
|
||||
return false
|
||||
}
|
||||
res_s = &responder_s
|
||||
if pre {
|
||||
res_s_pub = &responder_s._pub_key
|
||||
}
|
||||
}
|
||||
|
||||
psk_buf: [32]byte = ---
|
||||
psk: []byte
|
||||
if noise.pattern_is_psk(protocol.handshake_pattern) {
|
||||
crypto.rand_bytes(psk_buf[:])
|
||||
psk = psk_buf[:]
|
||||
}
|
||||
|
||||
ini_hs, res_hs: noise.Handshake_State
|
||||
status := noise.handshake_init(&ini_hs, true, nil, ini_s, res_s_pub, protocol_name, psk)
|
||||
if !testing.expectf(t, status == .Ok, "failed to initialize initiator Handshake_State: %v", status) {
|
||||
return false
|
||||
}
|
||||
status = noise.handshake_init(&res_hs, false, nil, res_s, ini_s_pub, protocol_name, psk)
|
||||
if !testing.expectf(t, status == .Ok, "failed to initialize responder Handshake_State: %v", status) {
|
||||
return false
|
||||
}
|
||||
|
||||
ini_status, res_status: noise.Status
|
||||
ini_msg, res_msg: []byte
|
||||
ini_payload, res_payload: []byte
|
||||
hs_msg_buf: [noise.MAX_STEP_MSG_SIZE]byte
|
||||
for i := 0; ; i += 1 {
|
||||
if ini_status == .Handshake_Complete && res_status == .Handshake_Complete {
|
||||
break
|
||||
}
|
||||
|
||||
// Test the allocation path
|
||||
res_msg, res_payload, ini_status = noise.handshake_initiator_step(&ini_hs, ini_msg, allocator = allocator)
|
||||
ini_msg = nil
|
||||
|
||||
if ini_status == .Handshake_Complete && res_status == .Handshake_Complete {
|
||||
break
|
||||
}
|
||||
|
||||
if !testing.expectf(t, len(res_payload) == 0, "step %d: unexpected responder payload: %x", i, res_payload) {
|
||||
return false
|
||||
}
|
||||
if !testing.expectf(t, ini_status == .Handshake_Pending || ini_status == .Handshake_Complete, "step %d: initiator step failed: %v", i, ini_status) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Test the non-allocation path
|
||||
ini_msg, ini_payload, res_status = noise.handshake_responder_step(&res_hs, res_msg, nil, dst = hs_msg_buf[:])
|
||||
delete(res_msg, allocator)
|
||||
res_msg = nil
|
||||
|
||||
if !testing.expectf(t, len(ini_payload) == 0, "step %d: unexpected initiator payload: %x", i, ini_payload) {
|
||||
return false
|
||||
}
|
||||
if !testing.expectf(t, res_status == .Handshake_Pending || res_status == .Handshake_Complete, "step %d: responder step failed: %v", i, res_status) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
delete(res_msg, allocator)
|
||||
|
||||
hs_pub: ^ecdh.Public_Key
|
||||
if ini_s != nil {
|
||||
hs_pub, status = noise.handshake_peer_identity(&res_hs)
|
||||
if !testing.expect(t, status == .Ok) {
|
||||
return false
|
||||
}
|
||||
if !testing.expectf(t, ecdh.public_key_equal(&ini_s._pub_key, hs_pub), "responder has incorrect initiator identity") {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if res_s != nil {
|
||||
hs_pub, status = noise.handshake_peer_identity(&ini_hs)
|
||||
if !testing.expect(t, status == .Ok) {
|
||||
return false
|
||||
}
|
||||
if !testing.expectf(t, ecdh.public_key_equal(&res_s._pub_key, hs_pub), "initiator has incorrect responder identity") {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
h1, h2: []byte
|
||||
h1, status = noise.handshake_hash(&ini_hs)
|
||||
if !testing.expect(t, status == .Ok) {
|
||||
return false
|
||||
}
|
||||
h2, status = noise.handshake_hash(&res_hs)
|
||||
if !testing.expect(t, status == .Ok) {
|
||||
return false
|
||||
}
|
||||
if !testing.expectf(t, bytes.equal(h1, h2), "handshake hash mismatch: %x != %x", h1, h2) {
|
||||
return false
|
||||
}
|
||||
|
||||
ini_cs, res_cs: noise.Cipher_States
|
||||
if !testing.expectf(t, .Ok == noise.handshake_split(&ini_hs, &ini_cs), "failed to split initiator: %v") {
|
||||
return false
|
||||
}
|
||||
if !testing.expectf(t, .Ok == noise.handshake_split(&res_hs, &res_cs), "failed to split responder: %v") {
|
||||
return false
|
||||
}
|
||||
|
||||
noise.handshake_reset(&ini_hs)
|
||||
noise.handshake_reset(&res_hs)
|
||||
|
||||
if !testing.expect(t, test_messages(t, &ini_cs, &res_cs, is_one_way, allocator), "message tests failed") {
|
||||
return false
|
||||
}
|
||||
|
||||
noise.cipherstates_reset(&ini_cs)
|
||||
noise.cipherstates_reset(&res_cs)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@(private = "file")
|
||||
test_messages :: proc(t: ^testing.T, ini_cs, res_cs: ^noise.Cipher_States, is_one_way: bool, allocator := context.allocator) -> bool {
|
||||
ad_buf: [256]byte = ---
|
||||
payload_buf: [noise.MAX_PACKET_SIZE-noise.TAG_SIZE]byte = ---
|
||||
|
||||
for i in 0..<10 {
|
||||
ad := ad_buf[:rand.int_max(len(ad_buf))]
|
||||
payload := payload_buf[:rand.int_max(len(payload_buf))]
|
||||
|
||||
_ = rand.read(payload)
|
||||
_ = rand.read(ad)
|
||||
|
||||
// Initiator -> Responder (allocate buffers)
|
||||
tx_msg, status := noise.seal_message(ini_cs, ad, payload, allocator = allocator)
|
||||
defer delete(tx_msg, allocator)
|
||||
if !testing.expectf(t, status == .Ok, "i->r %d: seal failed: %v", i, status) {
|
||||
return false
|
||||
}
|
||||
|
||||
rx_dst: []byte
|
||||
rx_dst, status = noise.open_message(res_cs, ad, tx_msg, allocator = allocator)
|
||||
defer delete(rx_dst, allocator)
|
||||
if !testing.expectf(t, status == .Ok, "i->r %d: open failed: %v", i, status) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !testing.expectf(t, bytes.equal(rx_dst, payload), "i->r %d: payload mismatch") {
|
||||
return false
|
||||
}
|
||||
|
||||
if i == 5 {
|
||||
status = noise.cipherstates_rekey(ini_cs, true)
|
||||
if !testing.expectf(t, status == .Ok, "i %d: rekey failed: %v", i, status) {
|
||||
return false
|
||||
}
|
||||
status = noise.cipherstates_rekey(res_cs, false)
|
||||
if !testing.expectf(t, status == .Ok, "r %d: rekey failed: %v", i, status) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if is_one_way {
|
||||
continue
|
||||
}
|
||||
|
||||
// Responder -> Initiator (reuse allocated buffers)
|
||||
tx_msg, status = noise.seal_message(res_cs, ad, payload, tx_msg)
|
||||
if !testing.expectf(t, status == .Ok, "r->i %d: seal failed: %v", i, status) {
|
||||
return false
|
||||
}
|
||||
|
||||
_, status = noise.open_message(ini_cs, ad, tx_msg, rx_dst)
|
||||
if !testing.expectf(t, status == .Ok, "r->i %d: open failed: %v", i, status) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !testing.expectf(t, bytes.equal(rx_dst, payload), "r-i %d: payload mismatch") {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@(private = "file")
|
||||
Test_Protocol :: struct {
|
||||
handshake_pattern: noise.Handshake_Pattern,
|
||||
dh: ecdh.Curve,
|
||||
cipher: aead.Algorithm,
|
||||
hash: hash.Algorithm,
|
||||
}
|
||||
|
||||
@(private = "file")
|
||||
test_protocol_string :: proc(protocol: ^Test_Protocol, allocator := context.allocator) -> string {
|
||||
dh: string
|
||||
#partial switch protocol.dh {
|
||||
case .X25519: dh = "25519"
|
||||
case .X448: dh = "448"
|
||||
case: panic("crypto/noise: unsupported DH")
|
||||
}
|
||||
|
||||
cipher: string
|
||||
#partial switch protocol.cipher {
|
||||
case .AES_GCM_256: cipher = "AESGCM"
|
||||
case .CHACHA20POLY1305: cipher = "ChaChaPoly"
|
||||
case: panic("crypto/noise: unsupported cipher")
|
||||
}
|
||||
|
||||
hash: string
|
||||
#partial switch protocol.hash {
|
||||
case .SHA256: hash = "SHA256"
|
||||
case .SHA512: hash = "SHA512"
|
||||
case .BLAKE2S: hash = "BLAKE2s"
|
||||
case .BLAKE2B: hash = "BLAKE2b"
|
||||
case: panic("crypto/noise: unsupported hash")
|
||||
}
|
||||
|
||||
return fmt.aprintf("Noise_%v_%v_%v_%v", protocol.handshake_pattern, dh, cipher, hash, allocator = allocator)
|
||||
}
|
||||
@@ -4,8 +4,6 @@ import "core:crypto/hash"
|
||||
import "core:fmt"
|
||||
import "core:strings"
|
||||
|
||||
panic_fn :: proc(arg: any)
|
||||
|
||||
hash_name_to_algorithm :: proc(alg_str: string) -> (hash.Algorithm, bool) {
|
||||
alg_enums := [][hash.Algorithm]string {
|
||||
hash.ALGORITHM_NAMES,
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#+build !linux
|
||||
package test_wycheproof
|
||||
|
||||
case_should_panic :: proc(fn: panic_fn, fn_arg: any, panic_str: string) -> bool {
|
||||
panic("helpers: testing for panic is unsupported on this target")
|
||||
}
|
||||
|
||||
can_test_panic :: proc() -> bool {
|
||||
return false
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
#+build linux
|
||||
package test_wycheproof
|
||||
|
||||
import "core:log"
|
||||
import "core:os"
|
||||
import "core:strings"
|
||||
import "core:sys/linux"
|
||||
|
||||
@(private)
|
||||
PIPE_BUF :: 4096
|
||||
|
||||
case_should_panic :: proc(fn: panic_fn, fn_arg: any, panic_str: string) -> bool {
|
||||
stderr_pipe: [2]linux.Fd
|
||||
|
||||
if err := linux.pipe2(&stderr_pipe, linux.Open_Flags{}); err != .NONE {
|
||||
log.errorf("panic_case: failed to create pipe: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
pid, err := linux.fork()
|
||||
switch {
|
||||
case err != .NONE:
|
||||
log.errorf("panic_case: failed to fork: %v", err)
|
||||
return false
|
||||
case pid == 0:
|
||||
// In the child, redirect stderr to the pipe, run the function that
|
||||
// is supposed to panic, and exit normally.
|
||||
linux.dup2(stderr_pipe[1], 2)
|
||||
fn(fn_arg)
|
||||
os.exit(0)
|
||||
}
|
||||
|
||||
// Parent.
|
||||
defer linux.close(stderr_pipe[0])
|
||||
defer linux.close(stderr_pipe[1])
|
||||
|
||||
// Wait for the child to terminate, and ensure it terminated
|
||||
// abnormally (SIGILL/SIGTRAP).
|
||||
wait_status: u32
|
||||
linux.wait4(pid, &wait_status, linux.Wait_Options{}, nil)
|
||||
if !linux.WIFSIGNALED(wait_status) {
|
||||
log.errorf("panic_case: child did not terminate via signal: %x", wait_status)
|
||||
return false
|
||||
}
|
||||
term_sig := linux.Signal(linux.WTERMSIG(wait_status))
|
||||
if term_sig != .SIGILL && term_sig != .SIGTRAP {
|
||||
log.errorf("panic_case: child terminated via wrong signal: %v", term_sig)
|
||||
return false
|
||||
}
|
||||
|
||||
// Consume the child's stderr output from the pipe buffer.
|
||||
//
|
||||
// Note: POSIX requires PIPE_BUF be >= 512 bytes, Linux defaults
|
||||
// to 4096 bytes. Either is sufficient to buffer output for our
|
||||
// test cases.
|
||||
buf: [PIPE_BUF]byte
|
||||
n, _ := linux.read(stderr_pipe[0], buf[:])
|
||||
if n == 0 {
|
||||
log.errorf("panic_case: child stderr empty")
|
||||
return false
|
||||
}
|
||||
s := string(buf[:n])
|
||||
|
||||
log.debugf("panic case: child stderr: '%s'", s)
|
||||
|
||||
return strings.contains(s, panic_str)
|
||||
}
|
||||
|
||||
can_test_panic :: proc() -> bool {
|
||||
return true
|
||||
}
|
||||
@@ -25,6 +25,8 @@ import "core:crypto/pbkdf2"
|
||||
import "core:crypto/siphash"
|
||||
import "core:crypto/deoxysii"
|
||||
|
||||
import "../common"
|
||||
|
||||
// Covered:
|
||||
// - crypto/aegis
|
||||
// - aegis128L_test.json
|
||||
@@ -95,8 +97,6 @@ print_test_vector_path :: proc(t: ^testing.T) {
|
||||
log.infof("wycheproof path: %s", BASE_PATH)
|
||||
}
|
||||
|
||||
test_proc :: proc(_: string) -> bool
|
||||
|
||||
supported_aegis_impls :: proc() -> [dynamic]aes.Implementation {
|
||||
impls := make([dynamic]aes.Implementation, 0, 2, context.temp_allocator)
|
||||
append(&impls, aes.Implementation.Portable)
|
||||
@@ -167,12 +167,12 @@ test_aead_aegis_impl :: proc(
|
||||
)
|
||||
}
|
||||
|
||||
key := hexbytes_decode(test_vector.key)
|
||||
iv := hexbytes_decode(test_vector.iv)
|
||||
aad := hexbytes_decode(test_vector.aad)
|
||||
msg := hexbytes_decode(test_vector.msg)
|
||||
ct := hexbytes_decode(test_vector.ct)
|
||||
tag := hexbytes_decode(test_vector.tag)
|
||||
key := common.hexbytes_decode(test_vector.key)
|
||||
iv := common.hexbytes_decode(test_vector.iv)
|
||||
aad := common.hexbytes_decode(test_vector.aad)
|
||||
msg := common.hexbytes_decode(test_vector.msg)
|
||||
ct := common.hexbytes_decode(test_vector.ct)
|
||||
tag := common.hexbytes_decode(test_vector.tag)
|
||||
|
||||
if len(iv) == 0 {
|
||||
log.infof(
|
||||
@@ -192,7 +192,7 @@ test_aead_aegis_impl :: proc(
|
||||
tag_ := make([]byte, len(tag))
|
||||
aegis.seal(&ctx, ct_, tag_, iv, aad, msg)
|
||||
|
||||
ok := hexbytes_compare(test_vector.ct, ct_)
|
||||
ok := common.hexbytes_compare(test_vector.ct, ct_)
|
||||
if !result_check(test_vector.result, ok) {
|
||||
x := transmute(string)(hex.encode(ct_))
|
||||
log.errorf(
|
||||
@@ -206,7 +206,7 @@ test_aead_aegis_impl :: proc(
|
||||
continue
|
||||
}
|
||||
|
||||
ok = hexbytes_compare(test_vector.tag, tag_)
|
||||
ok = common.hexbytes_compare(test_vector.tag, tag_)
|
||||
if !result_check(test_vector.result, ok) {
|
||||
x := transmute(string)(hex.encode(tag_))
|
||||
log.errorf(
|
||||
@@ -229,7 +229,7 @@ test_aead_aegis_impl :: proc(
|
||||
continue
|
||||
}
|
||||
|
||||
if ok && !hexbytes_compare(test_vector.msg, msg_) {
|
||||
if ok && !common.hexbytes_compare(test_vector.msg, msg_) {
|
||||
x := transmute(string)(hex.encode(msg_))
|
||||
log.errorf(
|
||||
"aead/aegis/%v/%d: decrypt msg: expected %s actual %s",
|
||||
@@ -317,12 +317,12 @@ test_aead_aes_gcm_impl :: proc(
|
||||
)
|
||||
}
|
||||
|
||||
key := hexbytes_decode(test_vector.key)
|
||||
iv := hexbytes_decode(test_vector.iv)
|
||||
aad := hexbytes_decode(test_vector.aad)
|
||||
msg := hexbytes_decode(test_vector.msg)
|
||||
ct := hexbytes_decode(test_vector.ct)
|
||||
tag := hexbytes_decode(test_vector.tag)
|
||||
key := common.hexbytes_decode(test_vector.key)
|
||||
iv := common.hexbytes_decode(test_vector.iv)
|
||||
aad := common.hexbytes_decode(test_vector.aad)
|
||||
msg := common.hexbytes_decode(test_vector.msg)
|
||||
ct := common.hexbytes_decode(test_vector.ct)
|
||||
tag := common.hexbytes_decode(test_vector.tag)
|
||||
|
||||
if len(iv) == 0 {
|
||||
log.infof(
|
||||
@@ -342,7 +342,7 @@ test_aead_aes_gcm_impl :: proc(
|
||||
tag_ := make([]byte, len(tag))
|
||||
aes.seal_gcm(&ctx, ct_, tag_, iv, aad, msg)
|
||||
|
||||
ok := hexbytes_compare(test_vector.ct, ct_)
|
||||
ok := common.hexbytes_compare(test_vector.ct, ct_)
|
||||
if !result_check(test_vector.result, ok) {
|
||||
x := transmute(string)(hex.encode(ct_))
|
||||
log.errorf(
|
||||
@@ -356,7 +356,7 @@ test_aead_aes_gcm_impl :: proc(
|
||||
continue
|
||||
}
|
||||
|
||||
ok = hexbytes_compare(test_vector.tag, tag_)
|
||||
ok = common.hexbytes_compare(test_vector.tag, tag_)
|
||||
if !result_check(test_vector.result, ok) {
|
||||
x := transmute(string)(hex.encode(tag_))
|
||||
log.errorf(
|
||||
@@ -379,7 +379,7 @@ test_aead_aes_gcm_impl :: proc(
|
||||
continue
|
||||
}
|
||||
|
||||
if ok && !hexbytes_compare(test_vector.msg, msg_) {
|
||||
if ok && !common.hexbytes_compare(test_vector.msg, msg_) {
|
||||
x := transmute(string)(hex.encode(msg_))
|
||||
log.errorf(
|
||||
"aead/aes-gcm/%v/%d: decrypt msg: expected %s actual %s",
|
||||
@@ -488,12 +488,12 @@ test_aead_chacha20_poly1305_impl :: proc(
|
||||
)
|
||||
}
|
||||
|
||||
key := hexbytes_decode(test_vector.key)
|
||||
iv := hexbytes_decode(test_vector.iv)
|
||||
aad := hexbytes_decode(test_vector.aad)
|
||||
msg := hexbytes_decode(test_vector.msg)
|
||||
ct := hexbytes_decode(test_vector.ct)
|
||||
tag := hexbytes_decode(test_vector.tag)
|
||||
key := common.hexbytes_decode(test_vector.key)
|
||||
iv := common.hexbytes_decode(test_vector.iv)
|
||||
aad := common.hexbytes_decode(test_vector.aad)
|
||||
msg := common.hexbytes_decode(test_vector.msg)
|
||||
ct := common.hexbytes_decode(test_vector.ct)
|
||||
tag := common.hexbytes_decode(test_vector.tag)
|
||||
|
||||
if slice.contains(test_vector.flags, FLAG_INVALID_NONCE_SIZE) {
|
||||
log.infof(
|
||||
@@ -519,7 +519,7 @@ test_aead_chacha20_poly1305_impl :: proc(
|
||||
tag_ := make([]byte, len(tag))
|
||||
chacha20poly1305.seal(&ctx, ct_, tag_, iv, aad, msg)
|
||||
|
||||
ok := hexbytes_compare(test_vector.ct, ct_)
|
||||
ok := common.hexbytes_compare(test_vector.ct, ct_)
|
||||
if !result_check(test_vector.result, ok) {
|
||||
x := transmute(string)(hex.encode(ct_))
|
||||
log.errorf(
|
||||
@@ -534,7 +534,7 @@ test_aead_chacha20_poly1305_impl :: proc(
|
||||
continue
|
||||
}
|
||||
|
||||
ok = hexbytes_compare(test_vector.tag, tag_)
|
||||
ok = common.hexbytes_compare(test_vector.tag, tag_)
|
||||
if !result_check(test_vector.result, ok) {
|
||||
x := transmute(string)(hex.encode(tag_))
|
||||
log.errorf(
|
||||
@@ -562,7 +562,7 @@ test_aead_chacha20_poly1305_impl :: proc(
|
||||
continue
|
||||
}
|
||||
|
||||
if ok && !hexbytes_compare(test_vector.msg, msg_) {
|
||||
if ok && !common.hexbytes_compare(test_vector.msg, msg_) {
|
||||
x := transmute(string)(hex.encode(msg_))
|
||||
log.errorf(
|
||||
"aead/%s/%v/%d: decrypt msg: expected %s actual %s",
|
||||
@@ -628,7 +628,7 @@ test_eddsa_ed25519 :: proc(t: ^testing.T) {
|
||||
num_ran, num_passed, num_failed, num_skipped: int
|
||||
for &test_group, i in test_vectors.test_groups {
|
||||
mem.free_all() // Probably don't need this, but be safe.
|
||||
pk_bytes := hexbytes_decode(test_group.public_key.pk)
|
||||
pk_bytes := common.hexbytes_decode(test_group.public_key.pk)
|
||||
|
||||
pk: ed25519.Public_Key
|
||||
pk_ok := ed25519.public_key_set_bytes(&pk, pk_bytes)
|
||||
@@ -652,8 +652,8 @@ test_eddsa_ed25519 :: proc(t: ^testing.T) {
|
||||
log.debugf("eddsa/ed25519/%d: %+v", test_vector.tc_id, test_vector.flags)
|
||||
}
|
||||
|
||||
msg := hexbytes_decode(test_vector.msg)
|
||||
sig := hexbytes_decode(test_vector.sig)
|
||||
msg := common.hexbytes_decode(test_vector.msg)
|
||||
sig := common.hexbytes_decode(test_vector.sig)
|
||||
|
||||
verify_ok := ed25519.verify(&pk, msg, sig)
|
||||
result_ok := result_check(test_vector.result, verify_ok)
|
||||
@@ -747,7 +747,7 @@ test_ecdsa_impl :: proc(t: ^testing.T, test_vectors: ^Test_Vectors(Ecdsa_Test_Gr
|
||||
|
||||
num_ran, num_passed, num_failed, num_skipped: int
|
||||
for &test_group, i in test_vectors.test_groups {
|
||||
pk_bytes := hexbytes_decode(test_group.public_key.uncompressed)
|
||||
pk_bytes := common.hexbytes_decode(test_group.public_key.uncompressed)
|
||||
|
||||
pk: ecdsa.Public_Key
|
||||
pk_ok := ecdsa.public_key_set_bytes(&pk, curve_alg, pk_bytes)
|
||||
@@ -773,8 +773,8 @@ test_ecdsa_impl :: proc(t: ^testing.T, test_vectors: ^Test_Vectors(Ecdsa_Test_Gr
|
||||
log.debugf("ecdsa/%s/%s/%d: %+v", curve_str, hash_str, test_vector.tc_id, test_vector.flags)
|
||||
}
|
||||
|
||||
msg := hexbytes_decode(test_vector.msg)
|
||||
sig := hexbytes_decode(test_vector.sig)
|
||||
msg := common.hexbytes_decode(test_vector.msg)
|
||||
sig := common.hexbytes_decode(test_vector.sig)
|
||||
|
||||
verify_ok := ecdsa.verify_asn1(&pk, hash_alg, msg, sig)
|
||||
result_ok := result_check(test_vector.result, verify_ok)
|
||||
@@ -876,9 +876,9 @@ test_hkdf_impl :: proc(test_vectors: ^Test_Vectors(Hkdf_Test_Group)) -> bool {
|
||||
log.debugf("hkdf/%s/%d: %+v", alg_str, test_vector.tc_id, test_vector.flags)
|
||||
}
|
||||
|
||||
ikm := hexbytes_decode(test_vector.ikm)
|
||||
salt := hexbytes_decode(test_vector.salt)
|
||||
info := hexbytes_decode(test_vector.info)
|
||||
ikm := common.hexbytes_decode(test_vector.ikm)
|
||||
salt := common.hexbytes_decode(test_vector.salt)
|
||||
info := common.hexbytes_decode(test_vector.info)
|
||||
|
||||
if slice.contains(test_vector.flags, FLAG_SIZE_TOO_LARGE) {
|
||||
log.infof(
|
||||
@@ -893,7 +893,7 @@ test_hkdf_impl :: proc(test_vectors: ^Test_Vectors(Hkdf_Test_Group)) -> bool {
|
||||
okm_ := make([]byte, test_vector.size)
|
||||
hkdf.extract_and_expand(alg, salt, ikm, info, okm_)
|
||||
|
||||
ok = hexbytes_compare(test_vector.okm, okm_)
|
||||
ok = common.hexbytes_compare(test_vector.okm, okm_)
|
||||
if !result_check(test_vector.result, ok) {
|
||||
x := transmute(string)(hex.encode(okm_))
|
||||
log.errorf(
|
||||
@@ -1001,8 +1001,8 @@ test_mac_impl :: proc(test_vectors: ^Test_Vectors(Mac_Test_Group)) -> bool {
|
||||
log.debugf("%s/%d: %+v", alg_str, test_vector.tc_id, test_vector.flags)
|
||||
}
|
||||
|
||||
key := hexbytes_decode(test_vector.key)
|
||||
msg := hexbytes_decode(test_vector.msg)
|
||||
key := common.hexbytes_decode(test_vector.key)
|
||||
msg := common.hexbytes_decode(test_vector.msg)
|
||||
|
||||
tag_ := make([]byte, len(test_vector.tag) / 2)
|
||||
|
||||
@@ -1037,7 +1037,7 @@ test_mac_impl :: proc(test_vectors: ^Test_Vectors(Mac_Test_Group)) -> bool {
|
||||
siphash.sum_4_8(msg, key, tag_)
|
||||
}
|
||||
|
||||
ok = hexbytes_compare(test_vector.tag, tag_)
|
||||
ok = common.hexbytes_compare(test_vector.tag, tag_)
|
||||
if !result_check(test_vector.result, ok) {
|
||||
x := transmute(string)(hex.encode(tag_))
|
||||
log.errorf(
|
||||
@@ -1146,13 +1146,13 @@ test_pbkdf2_impl :: proc(
|
||||
continue
|
||||
}
|
||||
|
||||
password := hexbytes_decode(test_vector.password)
|
||||
salt := hexbytes_decode(test_vector.salt)
|
||||
password := common.hexbytes_decode(test_vector.password)
|
||||
salt := common.hexbytes_decode(test_vector.salt)
|
||||
|
||||
dk_ := make([]byte, test_vector.dk_len)
|
||||
pbkdf2.derive(alg, password, salt, test_vector.iteration_count, dk_)
|
||||
|
||||
ok = hexbytes_compare(test_vector.dk, dk_)
|
||||
ok = common.hexbytes_compare(test_vector.dk, dk_)
|
||||
if !result_check(test_vector.result, ok) {
|
||||
x := transmute(string)(hex.encode(dk_))
|
||||
log.errorf(
|
||||
@@ -1255,8 +1255,8 @@ test_ecdh_impl :: proc(
|
||||
log.debugf("ecdh/%s/%d: %+v", alg_str, test_vector.tc_id, test_vector.flags)
|
||||
}
|
||||
|
||||
raw_pub := hexbytes_decode(test_vector.public)
|
||||
raw_priv := hexbytes_decode(test_vector.private)
|
||||
raw_pub := common.hexbytes_decode(test_vector.public)
|
||||
raw_priv := common.hexbytes_decode(test_vector.private)
|
||||
|
||||
curve: ecdh.Curve
|
||||
priv_key: ecdh.Private_Key
|
||||
@@ -1366,7 +1366,7 @@ test_ecdh_impl :: proc(
|
||||
continue
|
||||
}
|
||||
|
||||
ok = hexbytes_compare(test_vector.shared, shared)
|
||||
ok = common.hexbytes_compare(test_vector.shared, shared)
|
||||
// "acceptable" results are fine from here because we have
|
||||
// checked for the all-zero shared secret XDH case already.
|
||||
if !result_check(test_vector.result, ok, false) {
|
||||
|
||||
@@ -1,28 +1,10 @@
|
||||
package test_wycheproof
|
||||
|
||||
import "core:bytes"
|
||||
import "core:encoding/hex"
|
||||
@(require) import "core:encoding/json"
|
||||
@(require) import "core:log"
|
||||
@(require) import "core:os"
|
||||
|
||||
Hex_Bytes :: string
|
||||
|
||||
hexbytes_compare :: proc(x: Hex_Bytes, b: []byte, allocator := context.allocator) -> bool {
|
||||
dst := hexbytes_decode(x)
|
||||
defer delete(dst)
|
||||
|
||||
return bytes.equal(dst, b)
|
||||
}
|
||||
|
||||
hexbytes_decode :: proc(x: Hex_Bytes, allocator := context.allocator) -> []byte {
|
||||
dst, ok := hex.decode(transmute([]byte)(x), allocator)
|
||||
if !ok {
|
||||
panic("wycheproof/common/Hex_Bytes: invalid hex encoding")
|
||||
}
|
||||
|
||||
return dst
|
||||
}
|
||||
import "../common"
|
||||
|
||||
Result :: string
|
||||
|
||||
@@ -90,16 +72,16 @@ Aead_Test_Group :: struct {
|
||||
}
|
||||
|
||||
Aead_Test_Vector :: struct {
|
||||
tc_id: int `json:"tcId"`,
|
||||
comment: string `json:"comment"`,
|
||||
key: Hex_Bytes `json:"key"`,
|
||||
iv: Hex_Bytes `json:"iv"`,
|
||||
aad: Hex_Bytes `json:"aad"`,
|
||||
msg: Hex_Bytes `json:"msg"`,
|
||||
ct: Hex_Bytes `json:"ct"`,
|
||||
tag: Hex_Bytes `json:"tag"`,
|
||||
result: Result `json:"result"`,
|
||||
flags: []string `json:"flags"`,
|
||||
tc_id: int `json:"tcId"`,
|
||||
comment: string `json:"comment"`,
|
||||
key: common.Hex_Bytes `json:"key"`,
|
||||
iv: common.Hex_Bytes `json:"iv"`,
|
||||
aad: common.Hex_Bytes `json:"aad"`,
|
||||
msg: common.Hex_Bytes `json:"msg"`,
|
||||
ct: common.Hex_Bytes `json:"ct"`,
|
||||
tag: common.Hex_Bytes `json:"tag"`,
|
||||
result: Result `json:"result"`,
|
||||
flags: []string `json:"flags"`,
|
||||
}
|
||||
|
||||
Hkdf_Test_Group :: struct {
|
||||
@@ -108,15 +90,15 @@ Hkdf_Test_Group :: struct {
|
||||
}
|
||||
|
||||
Hkdf_Test_Vector :: struct {
|
||||
tc_id: int `json:"tcId"`,
|
||||
comment: string `json:"comment"`,
|
||||
ikm: Hex_Bytes `json:"ikm"`,
|
||||
salt: Hex_Bytes `json:"salt"`,
|
||||
info: Hex_Bytes `json:"info"`,
|
||||
size: int `json:"size"`,
|
||||
okm: Hex_Bytes `json:"okm"`,
|
||||
result: Result `json:"result"`,
|
||||
flags: []string `json:"flags"`,
|
||||
tc_id: int `json:"tcId"`,
|
||||
comment: string `json:"comment"`,
|
||||
ikm: common.Hex_Bytes `json:"ikm"`,
|
||||
salt: common.Hex_Bytes `json:"salt"`,
|
||||
info: common.Hex_Bytes `json:"info"`,
|
||||
size: int `json:"size"`,
|
||||
okm: common.Hex_Bytes `json:"okm"`,
|
||||
result: Result `json:"result"`,
|
||||
flags: []string `json:"flags"`,
|
||||
}
|
||||
|
||||
Mac_Test_Group :: struct {
|
||||
@@ -126,13 +108,13 @@ Mac_Test_Group :: struct {
|
||||
}
|
||||
|
||||
Mac_Test_Vector :: struct {
|
||||
tc_id: int `json:"tcId"`,
|
||||
comment: string `json:"comment"`,
|
||||
key: Hex_Bytes `json:"key"`,
|
||||
msg: Hex_Bytes `json:"msg"`,
|
||||
tag: Hex_Bytes `json:"tag"`,
|
||||
result: Result `json:"result"`,
|
||||
flags: []string `json:"flags"`,
|
||||
tc_id: int `json:"tcId"`,
|
||||
comment: string `json:"comment"`,
|
||||
key: common.Hex_Bytes `json:"key"`,
|
||||
msg: common.Hex_Bytes `json:"msg"`,
|
||||
tag: common.Hex_Bytes `json:"tag"`,
|
||||
result: Result `json:"result"`,
|
||||
flags: []string `json:"flags"`,
|
||||
}
|
||||
|
||||
Ecdh_Test_Group :: struct {
|
||||
@@ -141,18 +123,18 @@ Ecdh_Test_Group :: struct {
|
||||
}
|
||||
|
||||
Ecdh_Test_Vector :: struct {
|
||||
tc_id: int `json:"tcId"`,
|
||||
comment: string `json:"comment"`,
|
||||
public: Hex_Bytes `json:"public"`,
|
||||
private: Hex_Bytes `json:"private"`,
|
||||
shared: Hex_Bytes `json:"shared"`,
|
||||
result: Result `json:"result"`,
|
||||
flags: []string `json:"flags"`,
|
||||
tc_id: int `json:"tcId"`,
|
||||
comment: string `json:"comment"`,
|
||||
public: common.Hex_Bytes `json:"public"`,
|
||||
private: common.Hex_Bytes `json:"private"`,
|
||||
shared: common.Hex_Bytes `json:"shared"`,
|
||||
result: Result `json:"result"`,
|
||||
flags: []string `json:"flags"`,
|
||||
}
|
||||
|
||||
Eddsa_Test_Group :: struct {
|
||||
public_key: Eddsa_Key `json:"publicKey"`,
|
||||
public_key_der: Hex_Bytes `json:"publicKeyDer"`,
|
||||
public_key_der: common.Hex_Bytes `json:"publicKeyDer"`,
|
||||
public_key_pem: string `json:"publicKeyPem"`,
|
||||
public_key_jwk: Eddsa_Jwk `json:"publicKeyJwk"`,
|
||||
type: string `json:"type"`,
|
||||
@@ -160,10 +142,10 @@ Eddsa_Test_Group :: struct {
|
||||
}
|
||||
|
||||
Eddsa_Key :: struct {
|
||||
type: string `json:"type"`,
|
||||
curve: string `json:"curve"`,
|
||||
key_size: int `json:"keySize"`,
|
||||
pk: Hex_Bytes `json:"pk"`,
|
||||
type: string `json:"type"`,
|
||||
curve: string `json:"curve"`,
|
||||
key_size: int `json:"keySize"`,
|
||||
pk: common.Hex_Bytes `json:"pk"`,
|
||||
}
|
||||
|
||||
Eddsa_Jwk :: struct {
|
||||
@@ -174,17 +156,17 @@ Eddsa_Jwk :: struct {
|
||||
}
|
||||
|
||||
Ecdsa_Key :: struct {
|
||||
type: string `json:"type"`,
|
||||
curve: string `json:"curve"`,
|
||||
key_size: int `json:"keySize"`,
|
||||
uncompressed: Hex_Bytes `json:"uncompressed"`,
|
||||
wx: Hex_Bytes `json:"wx"`,
|
||||
wy: Hex_Bytes `json:"wy"`,
|
||||
type: string `json:"type"`,
|
||||
curve: string `json:"curve"`,
|
||||
key_size: int `json:"keySize"`,
|
||||
uncompressed: common.Hex_Bytes `json:"uncompressed"`,
|
||||
wx: common.Hex_Bytes `json:"wx"`,
|
||||
wy: common.Hex_Bytes `json:"wy"`,
|
||||
}
|
||||
|
||||
Ecdsa_Test_Group :: struct {
|
||||
public_key: Ecdsa_Key `json:"publicKey"`,
|
||||
public_key_der: Hex_Bytes `json:"publicKeyDer"`,
|
||||
public_key_der: common.Hex_Bytes `json:"publicKeyDer"`,
|
||||
public_key_pem: string `json:"publicKeyPem"`,
|
||||
type: string `json:"type"`,
|
||||
sha: string `json:"sha"`,
|
||||
@@ -192,12 +174,12 @@ Ecdsa_Test_Group :: struct {
|
||||
}
|
||||
|
||||
Dsa_Test_Vector :: struct {
|
||||
tc_id: int `json:"tcId"`,
|
||||
comment: string `json:"comment"`,
|
||||
msg: Hex_Bytes `json:"msg"`,
|
||||
sig: Hex_Bytes `json:"sig"`,
|
||||
result: Result `json:"result"`,
|
||||
flags: []string `json:"flags"`,
|
||||
tc_id: int `json:"tcId"`,
|
||||
comment: string `json:"comment"`,
|
||||
msg: common.Hex_Bytes `json:"msg"`,
|
||||
sig: common.Hex_Bytes `json:"sig"`,
|
||||
result: Result `json:"result"`,
|
||||
flags: []string `json:"flags"`,
|
||||
}
|
||||
|
||||
Pbkdf_Test_Group :: struct {
|
||||
@@ -206,13 +188,13 @@ Pbkdf_Test_Group :: struct {
|
||||
}
|
||||
|
||||
Pbkdf_Test_Vector :: struct {
|
||||
tc_id: int `json:"tcId"`,
|
||||
comment: string `json:"comment"`,
|
||||
password: Hex_Bytes `json:"password"`,
|
||||
salt: Hex_Bytes `json:"salt"`,
|
||||
iteration_count: u32 `json:"iterationCount"`,
|
||||
dk_len: int `json:"dkLen"`,
|
||||
dk: Hex_Bytes `json:"dk"`,
|
||||
result: Result `json:"result"`,
|
||||
flags: []string `json:"flags"`,
|
||||
tc_id: int `json:"tcId"`,
|
||||
comment: string `json:"comment"`,
|
||||
password: common.Hex_Bytes `json:"password"`,
|
||||
salt: common.Hex_Bytes `json:"salt"`,
|
||||
iteration_count: u32 `json:"iterationCount"`,
|
||||
dk_len: int `json:"dkLen"`,
|
||||
dk: common.Hex_Bytes `json:"dk"`,
|
||||
result: Result `json:"result"`,
|
||||
flags: []string `json:"flags"`,
|
||||
}
|
||||
|
||||
@@ -10,7 +10,9 @@ import sys
|
||||
import io
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
||||
|
||||
TEST_SUITES = ['PNG', 'XML', 'BMP', 'JPG', 'Wycheproof']
|
||||
# The OpenSSL CLI tool installed can be used to easily generate digests:
|
||||
# $ openssl sha3-512 -mac HMAC -macopt key:"$HMAC_KEY" $FILE_NAME
|
||||
TEST_SUITES = ['PNG', 'XML', 'BMP', 'JPG', 'Wycheproof', 'Noise']
|
||||
ASSETS_BASE_URL = "https://raw.githubusercontent.com/odin-lang/test-assets/master/{}/{}"
|
||||
HMAC_KEY = "https://odin-lang.org"
|
||||
HMAC_HASH = hashlib.sha3_512
|
||||
@@ -625,6 +627,14 @@ HMAC_DIGESTS = {
|
||||
'x448_pem_test.json': "718ef327a5e8cc3a34467974193a051efe89323352f32f248c874457f28f6a2836cb6c2c15f40044df96428051591766fc0b44771236145a17835e714360ca51",
|
||||
'x448_test.json': "ca811349ede46fc253d656c26b058e2fe903aae8a225d7f6703bef3dec122df4c48af190a070df4c4066690f50ed4996d69089794b5484b57f2c4233216f98f5",
|
||||
'xchacha20_poly1305_test.json': "5debf381018af54fa2f9041044a257f8faff85b9a2eda314f21a05a55de2aacd2767a4962ecc58f69ed65404dc7d2b1eb8526038e7641610ce3a988456838fce",
|
||||
|
||||
'LICENSE-APACHE-snow.txt': "6d94a2c13b32d3c0db347511a19f71e5c2ec15025f3f3fdb333ae72e3c671b0b4d8796a9adf3e9b1ec941acea28a054e7108914ee93ebae673d894d2ac4a7cad",
|
||||
'LICENSE-MIT-snow.txt': "c0d9d5f399729560ffbabcb46a1e282e9a214d436a59b86dd8765556290a93c204c126234401f935f6a18aff50d69cc8af4decc6f0bb1574f899412c77fb9b31",
|
||||
'LICENSE-cacophony.txt': "cab56e8608950b3e9d5c0d8262f8c67e34254569726e78968896a964bab84e67fd66ccce43653118152fba5a3e670e0de23c725ecd85f65cacce4d987b1258e8",
|
||||
'LICENSE-noise-c.txt': "ca6ef9d13d0832659efa04f03d06998af3050b0f65886f1b4f33ef4bd9b6df639fcaac0c73a7c28bc47c502e7915e30608863340245eac7620ab50ee66e99ff6",
|
||||
'cacophony.txt': "d2c492f02287ee562a9cf43a4cd2a055f5235734779d091bc404aede4c37a552029e76c12e873632e52423804ad24b86138cd3d4fe54506108571d9f2f925033",
|
||||
'noise-c-basic.txt': "7e25c5d692c53dd045060f27a562332178ca030d17687fc2ab58216b4298fb577ddc9d703db11c920fb04c28604fdc1a30337f58aad3617e07201a62c3a9b295",
|
||||
'snow.txt': "35088303de90e6bac22656e1e0ac4a5ea73d8cdb5ada23a078d703e14714ffe19e08437bf46108e0419acd8b5be6fa797a68a621c20cdd063d94a4a970aa8634",
|
||||
}
|
||||
|
||||
def try_download_file(url, out_file):
|
||||
|
||||
@@ -50,3 +50,9 @@ test_expected_signal :: proc(t: ^testing.T) {
|
||||
testing.expect_signal(t, libc.SIGILL)
|
||||
libc.raise(libc.SIGILL)
|
||||
}
|
||||
|
||||
@test
|
||||
test_array_bounds_trap_signal :: proc(t: ^testing.T) {
|
||||
testing.expect_signal(t, libc.SIGILL)
|
||||
_ = make([]u8, -1)
|
||||
}
|
||||
|
||||
@@ -36,6 +36,8 @@ $ODIN test ../test_issue_5699.odin $COMMON
|
||||
$ODIN test ../test_issue_6068.odin $COMMON
|
||||
$ODIN test ../test_issue_6101.odin $COMMON
|
||||
$ODIN test ../test_issue_6165.odin $COMMON
|
||||
$ODIN test ../test_issue_6344.odin $COMMON
|
||||
$ODIN test ../test_issue_6344.odin $COMMON -o:speed
|
||||
$ODIN test ../test_issue_6396.odin $COMMON
|
||||
$ODIN test ../test_pr_6476.odin $COMMON
|
||||
|
||||
|
||||
72
tests/issues/test_issue_6344.odin
Normal file
72
tests/issues/test_issue_6344.odin
Normal file
@@ -0,0 +1,72 @@
|
||||
// Tests issue #6344 https://github.com/odin-lang/Odin/issues/6344
|
||||
package test_issues
|
||||
|
||||
import "core:testing"
|
||||
|
||||
@(test)
|
||||
test_soa_dynamic_field_write :: proc(t: ^testing.T) {
|
||||
V :: struct {
|
||||
x: f32,
|
||||
y: f32,
|
||||
}
|
||||
|
||||
array := make(#soa[dynamic]V, 4, 8)
|
||||
defer delete(array)
|
||||
|
||||
for i in 0 ..< 4 {
|
||||
array[i] = V{f32(i), f32(i) * 2}
|
||||
}
|
||||
|
||||
// Simple write through field-first indexing (was: compiler panic)
|
||||
for i in 0 ..< 4 {
|
||||
array.x[i] = f32(i) * 10
|
||||
}
|
||||
testing.expect_value(t, array[0].x, 0)
|
||||
testing.expect_value(t, array[1].x, 10)
|
||||
testing.expect_value(t, array[2].x, 20)
|
||||
testing.expect_value(t, array[3].x, 30)
|
||||
|
||||
// Compound write through field-first indexing (was: compiler panic)
|
||||
for i in 0 ..< 4 {
|
||||
array.x[i] += array.y[i]
|
||||
}
|
||||
testing.expect_value(t, array[0].x, 0)
|
||||
testing.expect_value(t, array[1].x, 12)
|
||||
testing.expect_value(t, array[2].x, 24)
|
||||
testing.expect_value(t, array[3].x, 36)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_soa_slice_field_write :: proc(t: ^testing.T) {
|
||||
V :: struct {
|
||||
x: f32,
|
||||
y: f32,
|
||||
}
|
||||
|
||||
array := make(#soa[dynamic]V, 4, 8)
|
||||
defer delete(array)
|
||||
|
||||
for i in 0 ..< 4 {
|
||||
array[i] = V{f32(i), f32(i) * 2}
|
||||
}
|
||||
|
||||
slice := array[:]
|
||||
|
||||
// Write through slice field-first indexing (was: compiler panic)
|
||||
for i in 0 ..< 4 {
|
||||
slice.x[i] = f32(i) * 10
|
||||
}
|
||||
testing.expect_value(t, array[0].x, 0)
|
||||
testing.expect_value(t, array[1].x, 10)
|
||||
testing.expect_value(t, array[2].x, 20)
|
||||
testing.expect_value(t, array[3].x, 30)
|
||||
|
||||
// Compound write through slice field-first indexing
|
||||
for i in 0 ..< 4 {
|
||||
slice.x[i] += slice.y[i]
|
||||
}
|
||||
testing.expect_value(t, array[0].x, 0)
|
||||
testing.expect_value(t, array[1].x, 12)
|
||||
testing.expect_value(t, array[2].x, 24)
|
||||
testing.expect_value(t, array[3].x, 36)
|
||||
}
|
||||
12
vendor/directx/d3d12/d3d12.odin
vendored
12
vendor/directx/d3d12/d3d12.odin
vendored
@@ -5458,12 +5458,12 @@ TEXTURE_BARRIER_FLAGS :: enum i32 {
|
||||
}
|
||||
|
||||
BARRIER_SUBRESOURCE_RANGE :: struct {
|
||||
IndexOrFirstMipLevel: uint,
|
||||
NumMipLevels: uint,
|
||||
FirstArraySlice: uint,
|
||||
NumArraySlices: uint,
|
||||
FirstPlane: uint,
|
||||
NumPlanes: uint,
|
||||
IndexOrFirstMipLevel: u32,
|
||||
NumMipLevels: u32,
|
||||
FirstArraySlice: u32,
|
||||
NumArraySlices: u32,
|
||||
FirstPlane: u32,
|
||||
NumPlanes: u32,
|
||||
}
|
||||
|
||||
GLOBAL_BARRIER :: struct {
|
||||
|
||||
8
vendor/sdl3/mixer/sdl3_mixer.odin
vendored
8
vendor/sdl3/mixer/sdl3_mixer.odin
vendored
@@ -76,8 +76,8 @@ foreign lib {
|
||||
Quit :: proc() ---
|
||||
GetNumAudioDecoders :: proc() -> c.int ---
|
||||
GetAudioDecoder :: proc(index: c.int) -> cstring ---
|
||||
CreateMixerDevice :: proc(devid: SDL.AudioDeviceID, #by_ptr spec: SDL.AudioSpec) -> ^Mixer ---
|
||||
CreateMixer :: proc(#by_ptr spec: SDL.AudioSpec) -> ^Mixer ---
|
||||
CreateMixerDevice :: proc(devid: SDL.AudioDeviceID, spec: Maybe(^SDL.AudioSpec)) -> ^Mixer ---
|
||||
CreateMixer :: proc(spec: Maybe(^SDL.AudioSpec)) -> ^Mixer ---
|
||||
DestroyMixer :: proc(mixer: ^Mixer) ---
|
||||
GetMixerProperties :: proc(mixer: ^Mixer) -> SDL.PropertiesID ---
|
||||
GetMixerFormat :: proc(mixer: ^Mixer, spec: ^SDL.AudioSpec) -> c.bool ---
|
||||
@@ -145,8 +145,8 @@ foreign lib {
|
||||
SetTrackFrequencyRatio :: proc(track: ^Track, ratio: c.float) -> c.bool ---
|
||||
GetTrackFrequencyRatio :: proc(track: ^Track) -> c.float ---
|
||||
SetTrackOutputChannelMap :: proc(track: ^Track, chmap: [^]c.int, count: c.int) -> c.bool ---
|
||||
SetTrackStereo :: proc(track: ^Track, #by_ptr gains: StereoGains) -> c.bool ---
|
||||
SetTrack3DPosition :: proc(track: ^Track, #by_ptr position: Point3D) -> c.bool ---
|
||||
SetTrackStereo :: proc(track: ^Track, gains: Maybe(^StereoGains)) -> c.bool ---
|
||||
SetTrack3DPosition :: proc(track: ^Track, position: Maybe(^Point3D)) -> c.bool ---
|
||||
GetTrack3DPosition :: proc(track: ^Track, position: ^Point3D) -> c.bool ---
|
||||
CreateGroup :: proc(mixer: ^Mixer) -> ^Group ---
|
||||
DestroyGroup :: proc(group: ^Group) ---
|
||||
|
||||
34
vendor/vulkan/_gen/create_vulkan_odin_wrapper.py
vendored
34
vendor/vulkan/_gen/create_vulkan_odin_wrapper.py
vendored
@@ -899,6 +899,7 @@ load_proc_addresses :: proc{
|
||||
with open("../core.odin", 'w', encoding='utf-8') as f:
|
||||
f.write(BASE)
|
||||
f.write(PACKAGE_LINE)
|
||||
f.write("\n")
|
||||
f.write("""
|
||||
// Core API
|
||||
API_VERSION_1_0 :: (1<<22) | (0<<12) | (0)
|
||||
@@ -907,10 +908,38 @@ API_VERSION_1_2 :: (1<<22) | (2<<12) | (0)
|
||||
API_VERSION_1_3 :: (1<<22) | (3<<12) | (0)
|
||||
API_VERSION_1_4 :: (1<<22) | (4<<12) | (0)
|
||||
|
||||
MAKE_API_VERSION :: proc "contextless" (variant, major, minor, patch: u32) -> u32 {
|
||||
\treturn (variant<<29) | (major<<22) | (minor<<12) | (patch)
|
||||
}
|
||||
|
||||
MAKE_VERSION :: proc "contextless" (major, minor, patch: u32) -> u32 {
|
||||
\treturn (major<<22) | (minor<<12) | (patch)
|
||||
}
|
||||
|
||||
API_VERSION_MAJOR :: proc "contextless" (version: u32) -> u32 {
|
||||
\treturn (version>>22) & 0x7F
|
||||
}
|
||||
|
||||
VERSION_MAJOR :: proc "contextless" (version: u32) -> u32 {
|
||||
\treturn (version>>22)
|
||||
}
|
||||
|
||||
API_VERSION_MINOR :: proc "contextless" (version: u32) -> u32 {
|
||||
\treturn (version>>12) & 0x3FF
|
||||
}
|
||||
|
||||
VERSION_MINOR :: API_VERSION_MINOR
|
||||
|
||||
API_VERSION_PATCH :: proc "contextless" (version: u32) -> u32 {
|
||||
\treturn (version & 0xFFF)
|
||||
}
|
||||
|
||||
VERSION_PATCH :: API_VERSION_PATCH
|
||||
|
||||
API_VERSION_VARIANT :: proc "contextless" (version: u32) -> u32 {
|
||||
\treturn (version>>29)
|
||||
}
|
||||
|
||||
// Base types
|
||||
Flags :: distinct u32
|
||||
Flags64 :: distinct u64
|
||||
@@ -973,12 +1002,13 @@ MAKE_VIDEO_STD_VERSION :: MAKE_VERSION
|
||||
parse_flags_def(f)
|
||||
with open("../enums.odin", 'w', encoding='utf-8') as f:
|
||||
f.write(PACKAGE_LINE)
|
||||
f.write("\n")
|
||||
f.write("\n\n")
|
||||
parse_enums(f)
|
||||
parse_fake_enums(f)
|
||||
f.write("\n\n")
|
||||
with open("../structs.odin", 'w', encoding='utf-8') as f:
|
||||
f.write(PACKAGE_LINE)
|
||||
f.write("\n")
|
||||
f.write("""
|
||||
import "core:c"
|
||||
|
||||
@@ -1040,7 +1070,7 @@ MTLCommandQueue_id :: rawptr
|
||||
f.write("\n\n")
|
||||
with open("../procedures.odin", 'w', encoding='utf-8') as f:
|
||||
f.write(PACKAGE_LINE)
|
||||
f.write("\n")
|
||||
f.write("\n\n")
|
||||
parse_procedures(f)
|
||||
f.write("\n")
|
||||
group_functions(f)
|
||||
|
||||
28
vendor/vulkan/core.odin
vendored
28
vendor/vulkan/core.odin
vendored
@@ -7,10 +7,38 @@ API_VERSION_1_2 :: (1<<22) | (2<<12) | (0)
|
||||
API_VERSION_1_3 :: (1<<22) | (3<<12) | (0)
|
||||
API_VERSION_1_4 :: (1<<22) | (4<<12) | (0)
|
||||
|
||||
MAKE_API_VERSION :: proc "contextless" (variant, major, minor, patch: u32) -> u32 {
|
||||
return (variant<<29) | (major<<22) | (minor<<12) | (patch)
|
||||
}
|
||||
|
||||
MAKE_VERSION :: proc "contextless" (major, minor, patch: u32) -> u32 {
|
||||
return (major<<22) | (minor<<12) | (patch)
|
||||
}
|
||||
|
||||
API_VERSION_MAJOR :: proc "contextless" (version: u32) -> u32 {
|
||||
return (version>>22) & 0x7F
|
||||
}
|
||||
|
||||
VERSION_MAJOR :: proc "contextless" (version: u32) -> u32 {
|
||||
return (version>>22)
|
||||
}
|
||||
|
||||
API_VERSION_MINOR :: proc "contextless" (version: u32) -> u32 {
|
||||
return (version>>12) & 0x3FF
|
||||
}
|
||||
|
||||
VERSION_MINOR :: API_VERSION_MINOR
|
||||
|
||||
API_VERSION_PATCH :: proc "contextless" (version: u32) -> u32 {
|
||||
return (version & 0xFFF)
|
||||
}
|
||||
|
||||
VERSION_PATCH :: API_VERSION_PATCH
|
||||
|
||||
API_VERSION_VARIANT :: proc "contextless" (version: u32) -> u32 {
|
||||
return (version>>29)
|
||||
}
|
||||
|
||||
// Base types
|
||||
Flags :: distinct u32
|
||||
Flags64 :: distinct u64
|
||||
|
||||
4
vendor/wgpu/doc.odin
vendored
4
vendor/wgpu/doc.odin
vendored
@@ -12,8 +12,8 @@ You can find a number of examples on [[Odin's official examples repository; http
|
||||
**Getting the wgpu-native libraries**
|
||||
|
||||
For native support (not the browser), some libraries are required. Fortunately this is
|
||||
extremely easy, just download them from the [[releases on GitHub; https://github.com/gfx-rs/wgpu-native/releases/tag/v27.0.2.0]].
|
||||
the bindings are for v27.0.2.0 at the moment.
|
||||
extremely easy, just download them from the [[releases on GitHub; https://github.com/gfx-rs/wgpu-native/releases/tag/v29.0.0.0]].
|
||||
the bindings are for v29.0.0.0 at the moment.
|
||||
|
||||
These are expected in the `lib` folder under the same name as they are released (just unzipped).
|
||||
By default it will look for a static release version (`wgpu-OS-ARCH-release.a|lib`),
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
275
vendor/wgpu/wgpu.js
vendored
275
vendor/wgpu/wgpu.js
vendored
@@ -4,7 +4,7 @@ const STATUS_SUCCESS = 1;
|
||||
const STATUS_ERROR = 2;
|
||||
|
||||
const ENUMS = {
|
||||
FeatureName: [undefined, "depth-clip-control", "depth32float-stencil8", "timestamp-query", "texture-compression-bc", "texture-compression-bc-sliced-3d", "texture-compression-etc2", "texture-compression-astc", "texture-compression-astc-sliced-3d", "indirect-first-instance", "shader-f16", "rg11b10ufloat-renderable", "bgra8unorm-storage", "float32-filterable", "float32-blendable", "clip-distances", "dual-source-blending" ],
|
||||
FeatureName: [undefined, "core-features-and-limits", "depth-clip-control", "depth32float-stencil8", "texture-compression-bc", "texture-compression-bc-sliced-3d", "texture-compression-etc2", "texture-compression-astc", "texture-compression-astc-sliced-3d", "timestamp-query", "indirect-first-instance", "shader-f16", "rg11b10ufloat-renderable", "bgra8unorm-storage", "float32-filterable", "float32-blendable", "clip-distances", "dual-source-blending", "subgroups", "texture-formats-tier1", "texture-formats-tier2", "primitive-index", "texture-component-swizzle" ],
|
||||
StoreOp: [undefined, "store", "discard", ],
|
||||
LoadOp: [undefined, "load", "clear", ],
|
||||
BufferBindingType: [null, undefined, "uniform", "storage", "read-only-storage", ],
|
||||
@@ -12,9 +12,9 @@ const ENUMS = {
|
||||
TextureSampleType: [null, undefined, "float", "unfilterable-float", "depth", "sint", "uint", ],
|
||||
TextureViewDimension: [undefined, "1d", "2d", "2d-array", "cube", "cube-array", "3d", ],
|
||||
StorageTextureAccess: [null, undefined, "write-only", "read-only", "read-write", ],
|
||||
TextureFormat: [undefined, "r8unorm", "r8snorm", "r8uint", "r8sint", "r16uint", "r16sint", "r16float", "rg8unorm", "rg8snorm", "rg8uint", "rg8sint", "r32float", "r32uint", "r32sint", "rg16uint", "rg16sint", "rg16float", "rgba8unorm", "rgba8unorm-srgb", "rgba8snorm", "rgba8uint", "rgba8sint", "bgra8unorm", "bgra8unorm-srgb", "rgb10a2uint", "rgb10a2unorm", "rg11b10ufloat", "rgb9e5ufloat", "rg32float", "rg32uint", "rg32sint", "rgba16uint", "rgba16sint", "rgba16float", "rgba32float", "rgba32uint", "rgba32sint", "stencil8", "depth16unorm", "depth24plus", "depth24plus-stencil8", "depth32float", "depth32float-stencil8", "bc1-rgba-unorm", "bc1-rgba-unorm-srgb", "bc2-rgba-unorm", "bc2-rgba-unorm-srgb", "bc3-rgba-unorm", "bc3-rgba-unorm-srgb", "bc4-r-unorm", "bc4-r-snorm", "bc5-rg-unorm", "bc5-rg-snorm", "bc6h-rgb-ufloat", "bc6h-rgb-float", "bc7-rgba-unorm", "bc7-rgba-unorm-srgb", "etc2-rgb8unorm", "etc2-rgb8unorm-srgb", "etc2-rgb8a1unorm", "etc2-rgb8a1unorm-srgb", "etc2-rgba8unorm", "etc2-rgba8unorm-srgb", "eac-r11unorm", "eac-r11snorm", "eac-rg11unorm", "eac-rg11snorm", "astc-4x4-unorm", "astc-4x4-unorm-srgb", "astc-5x4-unorm", "astc-5x4-unorm-srgb", "astc-5x5-unorm", "astc-5x5-unorm-srgb", "astc-6x5-unorm", "astc-6x5-unorm-srgb", "astc-6x6-unorm", "astc-6x6-unorm-srgb", "astc-8x5-unorm", "astc-8x5-unorm-srgb", "astc-8x6-unorm", "astc-8x6-unorm-srgb", "astc-8x8-unorm", "astc-8x8-unorm-srgb", "astc-10x5-unorm", "astc-10x5-unorm-srgb", "astc-10x6-unorm", "astc-10x6-unorm-srgb", "astc-10x8-unorm", "astc-10x8-unorm-srgb", "astc-10x10-unorm", "astc-10x10-unorm-srgb", "astc-12x10-unorm", "astc-12x10-unorm-srgb", "astc-12x12-unorm", "astc-12x12-unorm-srgb", ],
|
||||
TextureFormat: [undefined, "r8unorm", "r8snorm", "r8uint", "r8sint", "r16unorm", "r16snorm", "r16uint", "r16sint", "r16float", "rg8unorm", "rg8snorm", "rg8uint", "rg8sint", "r32float", "r32uint", "r32sint", "rg16unorm", "rg16snorm", "rg16uint", "rg16sint", "rg16float", "rgba8unorm", "rgba8unorm-srgb", "rgba8snorm", "rgba8uint", "rgba8sint", "bgra8unorm", "bgra8unorm-srgb", "rgb10a2uint", "rgb10a2unorm", "rg11b10ufloat", "rgb9e5ufloat", "rg32float", "rg32uint", "rg32sint", "rgba16unorm", "rgba16snorm", "rgba16uint", "rgba16sint", "rgba16float", "rgba32float", "rgba32uint", "rgba32sint", "stencil8", "depth16unorm", "depth24plus", "depth24plus-stencil8", "depth32float", "depth32float-stencil8", "bc1-rgba-unorm", "bc1-rgba-unorm-srgb", "bc2-rgba-unorm", "bc2-rgba-unorm-srgb", "bc3-rgba-unorm", "bc3-rgba-unorm-srgb", "bc4-r-unorm", "bc4-r-snorm", "bc5-rg-unorm", "bc5-rg-snorm", "bc6h-rgb-ufloat", "bc6h-rgb-float", "bc7-rgba-unorm", "bc7-rgba-unorm-srgb", "etc2-rgb8unorm", "etc2-rgb8unorm-srgb", "etc2-rgb8a1unorm", "etc2-rgb8a1unorm-srgb", "etc2-rgba8unorm", "etc2-rgba8unorm-srgb", "eac-r11unorm", "eac-r11snorm", "eac-rg11unorm", "eac-rg11snorm", "astc-4x4-unorm", "astc-4x4-unorm-srgb", "astc-5x4-unorm", "astc-5x4-unorm-srgb", "astc-5x5-unorm", "astc-5x5-unorm-srgb", "astc-6x5-unorm", "astc-6x5-unorm-srgb", "astc-6x6-unorm", "astc-6x6-unorm-srgb", "astc-8x5-unorm", "astc-8x5-unorm-srgb", "astc-8x6-unorm", "astc-8x6-unorm-srgb", "astc-8x8-unorm", "astc-8x8-unorm-srgb", "astc-10x5-unorm", "astc-10x5-unorm-srgb", "astc-10x6-unorm", "astc-10x6-unorm-srgb", "astc-10x8-unorm", "astc-10x8-unorm-srgb", "astc-10x10-unorm", "astc-10x10-unorm-srgb", "astc-12x10-unorm", "astc-12x10-unorm-srgb", "astc-12x12-unorm", "astc-12x12-unorm-srgb", ],
|
||||
QueryType: [undefined, "occlusion", "timestamp", ],
|
||||
VertexStepMode: [null, undefined, "vertex", "instance", ],
|
||||
VertexStepMode: [undefined, "vertex", "instance", ],
|
||||
VertexFormat: [undefined, "uint8", "uint8x2", "uint8x4", "sint8", "sint8x2", "sint8x4", "unorm8", "unorm8x2", "unorm8x4", "snorm8", "snorm8x2", "snorm8x4", "uint16", "uint16x2", "uint16x4", "sint16", "sint16x2", "sint16x4", "unorm16", "unorm16x2", "unorm16x4", "snorm16", "snorm16x2", "snorm16x4", "float16", "float16x2", "float16x4", "float32", "float32x2", "float32x3", "float32x4", "uint32", "uint32x2", "uint32x3", "uint32x4", "sint32", "sint32x2", "sint32x3", "sint32x4", "unorm10-10-2", "unorm8x4-bgra" ],
|
||||
PrimitiveTopology: [undefined, "point-list", "line-list", "line-strip", "triangle-list", "triangle-strip", ],
|
||||
IndexFormat: [undefined, "uint16", "uint32", ],
|
||||
@@ -26,7 +26,7 @@ const ENUMS = {
|
||||
CompareFunction: [undefined, "never", "less", "equal", "less-equal", "greater", "not-equal", "greater-equal", "always", ],
|
||||
TextureDimension: [undefined, "1d", "2d", "3d", ],
|
||||
ErrorType: [undefined, "no-error", "validation", "out-of-memory", "internal", "unknown", ],
|
||||
WGSLLanguageFeatureName: [undefined, "readonly_and_readwrite_storage_textures", "packed_4x8_integer_dot_product", "unrestricted_pointer_parameters", "pointer_composite_access", ],
|
||||
WGSLLanguageFeatureName: [undefined, "readonly_and_readwrite_storage_textures", "packed_4x8_integer_dot_product", "unrestricted_pointer_parameters", "pointer_composite_access", "uniform_buffer_standard_layout", "subgroup_id", "texture_and_sampler_let", "subgroup_uniformity", "texture_formats_tier1" ],
|
||||
PowerPreference: [undefined, "low-power", "high-performance", ],
|
||||
CompositeAlphaMode: ["auto", "opaque", "premultiplied", "unpremultiplied", "inherit", ],
|
||||
StencilOperation: [undefined, "keep", "zero", "replace", "invert", "increment-clamp", "decrement-clamp", "increment-wrap", "decrement-wrap", ],
|
||||
@@ -34,20 +34,25 @@ const ENUMS = {
|
||||
BlendFactor: [undefined, "zero", "one", "src", "one-minus-src", "src-alpha", "one-minus-src-alpha", "dst", "one-minus-dst", "dst-alpha", "one-minus-dst-alpha", "src-alpha-saturated", "constant", "one-minus-constant", "src1", "one-minus-src1", "src1-alpha", "one-minus-src1-alpha" ],
|
||||
PresentMode: [undefined, "fifo", "fifo-relaxed", "immediate", "mailbox", ],
|
||||
TextureAspect: [undefined, "all", "stencil-only", "depth-only"],
|
||||
DeviceLostReason: [undefined, "unknown", "destroyed", "instance-dropped", "failed-creation"],
|
||||
DeviceLostReason: [undefined, "unknown", "destroyed", "callback-cancelled", "failed-creation"],
|
||||
BufferMapState: [undefined, "unmapped", "pending", "mapped"],
|
||||
OptionalBool: [false, true, undefined],
|
||||
ComponentSwizzle: [undefined, "0", "1", "r", "g", "b", "a"],
|
||||
PredefinedColorSpace: [undefined, "srgb", "display-p3"],
|
||||
ToneMappingMode: [undefined, "standard", "extended"],
|
||||
FeatureLevel: [undefined, "compatibility", "core"],
|
||||
TextureViewDimensions: [undefined, "1d", "2d", "2d-array", "cube", "cube-array", "3d"],
|
||||
|
||||
// WARN: used with indexOf to pass to WASM, if we would pass to JS, this needs to use official naming convention (not like Odin enums) like the ones above.
|
||||
BackendType: [undefined, null, "WebGPU", "D3D11", "D3D12", "Metal", "Vulkan", "OpenGL", "OpenGLES"],
|
||||
AdapterType: [undefined, "DiscreteGPU", "IntegratedGPU", "CPU", "Unknown"],
|
||||
RequestDeviceStatus: [undefined, "Success", "InstanceDropped", "Error", "Unknown"],
|
||||
MapAsyncStatus: [undefined, "Success", "InstanceDropped", "Error", "Aborted", "Unknown"],
|
||||
CreatePipelineAsyncStatus: [undefined, "Success", "InstanceDropped", "ValidationError", "InternalError", "Unknown"],
|
||||
PopErrorScopeStatus: [undefined, "Success", "InstanceDropped", "EmptyStack"],
|
||||
RequestAdapterStatus: [undefined, "Success", "InstanceDropped", "Unavailable", "Error", "Unknown"],
|
||||
QueueWorkDoneStatus: [undefined, "Success", "InstanceDropped", "Error", "Unknown"],
|
||||
CompilationInfoRequestStatus: [undefined, "Success", "InstanceDropped", "Error", "Unknown"],
|
||||
RequestDeviceStatus: [undefined, "Success", "CallbackCancelled", "Error"],
|
||||
MapAsyncStatus: [undefined, "Success", "CallbackCancelled", "Error", "Aborted"],
|
||||
CreatePipelineAsyncStatus: [undefined, "Success", "CallbackCancelled", "ValidationError", "InternalError"],
|
||||
PopErrorScopeStatus: [undefined, "Success", "CallbackCancelled", "Error"],
|
||||
RequestAdapterStatus: [undefined, "Success", "CallbackCancelled", "Unavailable", "Error"],
|
||||
QueueWorkDoneStatus: [undefined, "Success", "CallbackCancelled", "Error"],
|
||||
CompilationInfoRequestStatus: [undefined, "Success", "CallbackCancelled"],
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -71,7 +76,7 @@ class WebGPUInterface {
|
||||
StorageTextureBindingLayout: [16, 4],
|
||||
StringView: [2*this.mem.intSize, this.mem.intSize],
|
||||
ConstantEntry: [this.mem.intSize === 8 ? 32 : 24, 8],
|
||||
ProgrammableStageDescriptor: [8 + this.mem.intSize*4, this.mem.intSize],
|
||||
ComputeState: [8 + this.mem.intSize*4, this.mem.intSize],
|
||||
VertexBufferLayout: [16 + this.mem.intSize*2, 8],
|
||||
VertexAttribute: [24, 8],
|
||||
VertexState: [8 + this.mem.intSize*6, this.mem.intSize],
|
||||
@@ -87,7 +92,7 @@ class WebGPUInterface {
|
||||
UncapturedErrorCallbackInfo: [16, 4],
|
||||
RenderPassColorAttachment: [56, 8],
|
||||
BindGroupEntry: [40, 8],
|
||||
BindGroupLayoutEntry: [80, 8],
|
||||
BindGroupLayoutEntry: [88, 8],
|
||||
Extent3D: [12, 4],
|
||||
CompilationMessage: [this.mem.intSize == 8 ? 64 : 48, 8],
|
||||
};
|
||||
@@ -158,6 +163,9 @@ class WebGPUInterface {
|
||||
/** @type {WebGPUObjectManager<GPUTextureView>} */
|
||||
this.textureViews = new WebGPUObjectManager("TextureView", this.mem);
|
||||
|
||||
/** @type {WebGPUObjectManager<GPUExternalTexture>} */
|
||||
this.externalTextures = new WebGPUObjectManager("ExternalTexture", this.mem);
|
||||
|
||||
this.zeroMessageAddr = 0;
|
||||
}
|
||||
|
||||
@@ -341,21 +349,38 @@ class WebGPUInterface {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
genericGetAdapterInfo(infoPtr) {
|
||||
/**
|
||||
* @param {number} infoPtr
|
||||
* @param {GPUAdapterInfo} info
|
||||
*/
|
||||
genericGetAdapterInfo(infoPtr, info) {
|
||||
this.assert(infoPtr != 0);
|
||||
|
||||
const off = this.struct(infoPtr);
|
||||
off(4); // nextInChain
|
||||
off(this.sizes.StringView); // vendor
|
||||
off(this.sizes.StringView); // architecture
|
||||
off(this.sizes.StringView); // device
|
||||
off(this.sizes.StringView); // description
|
||||
|
||||
const storeString = (start, str) => {
|
||||
const len = new TextEncoder().encode(str).length;
|
||||
const strAddr = this.mem.exports.wgpu_alloc(len);
|
||||
this.mem.storeString(strAddr, str);
|
||||
|
||||
this.mem.storeI32(start, strAddr);
|
||||
this.mem.storeUint(start + this.mem.intSize, len);
|
||||
};
|
||||
|
||||
storeString(off(this.sizes.StringView), info.vendor);
|
||||
storeString(off(this.sizes.StringView), info.architecture);
|
||||
storeString(off(this.sizes.StringView), info.device);
|
||||
storeString(off(this.sizes.StringView), info.description);
|
||||
|
||||
this.mem.storeI32(off(4), ENUMS.BackendType.indexOf("WebGPU"));
|
||||
this.mem.storeI32(off(4), ENUMS.AdapterType.indexOf("Unknown"));
|
||||
|
||||
// NOTE: I don't think getting the other fields in this struct is possible.
|
||||
// `adapter.requestAdapterInfo` is deprecated.
|
||||
off(4); // vendorID
|
||||
off(4); // deviceID
|
||||
|
||||
this.mem.storeI32(off(4), info.subGroupMinSize);
|
||||
this.mem.storeI32(off(4), info.subGroupMaxSize);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
@@ -461,15 +486,15 @@ class WebGPUInterface {
|
||||
|
||||
/**
|
||||
* @param {number} ptr
|
||||
* @returns {GPUComputePassTimestampWrites}
|
||||
*/
|
||||
ComputePassTimestampWritesPtr(ptr) {
|
||||
PassTimestampWritesPtr(ptr) {
|
||||
const start = this.mem.loadPtr(ptr);
|
||||
if (start == 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const off = this.struct(start);
|
||||
off(4); // nextInChain
|
||||
return {
|
||||
querySet: this.querySets.get(this.mem.loadPtr(off(4))),
|
||||
beginningOfPassWriteIndex: this.mem.loadU32(off(4)),
|
||||
@@ -529,7 +554,7 @@ class WebGPUInterface {
|
||||
}
|
||||
|
||||
const off = this.struct(start);
|
||||
|
||||
off(4); // nextInChain
|
||||
return {
|
||||
view: this.textureViews.get(this.mem.loadPtr(off(4))),
|
||||
depthLoadOp: this.enumeration("LoadOp", off(4)),
|
||||
@@ -556,14 +581,6 @@ class WebGPUInterface {
|
||||
return this.querySets.get(ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} ptr
|
||||
* @returns {GPURenderPassTimestampWrites}
|
||||
*/
|
||||
RenderPassTimestampWritesPtr(ptr) {
|
||||
return this.ComputePassTimestampWritesPtr(ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} start
|
||||
* @returns {GPUOrigin3D}
|
||||
@@ -626,12 +643,13 @@ class WebGPUInterface {
|
||||
off(4);
|
||||
|
||||
const entry = {
|
||||
binding: this.mem.loadU32(off(4)),
|
||||
visibility: this.mem.loadU64(off(8)),
|
||||
buffer: this.BufferBindingLayout(off(this.sizes.BufferBindingLayout)),
|
||||
sampler: this.SamplerBindingLayout(off(this.sizes.SamplerBindingLayout)),
|
||||
texture: this.TextureBindingLayout(off(this.sizes.TextureBindingLayout)),
|
||||
storageTexture: this.StorageTextureBindingLayout(off(this.sizes.StorageTextureBindingLayout)),
|
||||
binding: this.mem.loadU32(off(4)),
|
||||
visibility: this.mem.loadU64(off(8)),
|
||||
bindingArraySize: this.mem.loadU32(off(4)),
|
||||
buffer: this.BufferBindingLayout(off(this.sizes.BufferBindingLayout)),
|
||||
sampler: this.SamplerBindingLayout(off(this.sizes.SamplerBindingLayout)),
|
||||
texture: this.TextureBindingLayout(off(this.sizes.TextureBindingLayout)),
|
||||
storageTexture: this.StorageTextureBindingLayout(off(this.sizes.StorageTextureBindingLayout)),
|
||||
};
|
||||
if (!entry.buffer.type) {
|
||||
entry.buffer = undefined;
|
||||
@@ -696,9 +714,9 @@ class WebGPUInterface {
|
||||
|
||||
/**
|
||||
* @param {number} start
|
||||
* @returns {GPUProgrammableStage}
|
||||
* @returns {GPUComputeState}
|
||||
*/
|
||||
ProgrammableStageDescriptor(start) {
|
||||
ComputeState(start) {
|
||||
const off = this.struct(start);
|
||||
off(4);
|
||||
|
||||
@@ -749,7 +767,7 @@ class WebGPUInterface {
|
||||
return {
|
||||
label: label,
|
||||
layout: layoutIdx > 0 ? this.pipelineLayouts.get(layoutIdx) : "auto",
|
||||
compute: this.ProgrammableStageDescriptor(off(this.sizes.ProgrammableStageDescriptor)),
|
||||
compute: this.ComputeState(off(this.sizes.ComputeState)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -793,11 +811,9 @@ class WebGPUInterface {
|
||||
*/
|
||||
VertexBufferLayout(start) {
|
||||
const off = this.struct(start);
|
||||
off(4); // nextInChain
|
||||
|
||||
const stepMode = this.enumeration("VertexStepMode", off(4));
|
||||
if (stepMode == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
arrayStride: this.mem.loadU64(off(8)),
|
||||
@@ -817,6 +833,7 @@ class WebGPUInterface {
|
||||
*/
|
||||
VertexAttribute(start) {
|
||||
const off = this.struct(start);
|
||||
off(4); // nextInChain
|
||||
return {
|
||||
format: this.enumeration("VertexFormat", off(4)),
|
||||
offset: this.mem.loadU64(off(8)),
|
||||
@@ -1117,12 +1134,26 @@ class WebGPUInterface {
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {number} capabilitiesPtr
|
||||
* @returns {number}
|
||||
* @param {number} featuresPtr
|
||||
*/
|
||||
wgpuGetInstanceCapabilities: (capabilitiesPtr) => {
|
||||
wgpuGetInstanceFeatures: (featuresPtr) => {
|
||||
// TODO: implement (futures).
|
||||
return STATUS_ERROR;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {number} limitsPtr
|
||||
*/
|
||||
wgpuGetInstanceLimits: (limitsPtr) => {
|
||||
// TODO: implement (futures).
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {number} feature
|
||||
* @returns {boolean}
|
||||
*/
|
||||
wgpuHasInstanceFeature: (feature) => {
|
||||
// TODO: implement (futures).
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1389,6 +1420,30 @@ class WebGPUInterface {
|
||||
return BigInt(0);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {number} bufferIdx
|
||||
* @param {number|BigInt} offset
|
||||
* @param {number} ptr
|
||||
* @param {number|BigInt} size
|
||||
* @return {number}
|
||||
*/
|
||||
wgpuBufferReadMappedRange: (bufferIdx, offset, ptr, size) => {
|
||||
const buffer = this.buffers.get(bufferIdx);
|
||||
offset = this.unwrapBigInt(offset);
|
||||
size = this.unwrapBigInt(size);
|
||||
|
||||
this.assert(!buffer.mapping, "buffer already mapped");
|
||||
|
||||
const range = buffer.buffer.getMappedRange(offset, size);
|
||||
|
||||
const mapping = new Uint8Array(this.mem.memory.buffer, ptr, size);
|
||||
mapping.set(new Uint8Array(range));
|
||||
|
||||
buffer.mapping = { range: range, ptr: ptr, size: range.byteLength };
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {number} bufferIdx
|
||||
* @param {number} labelPtr
|
||||
@@ -1415,6 +1470,30 @@ class WebGPUInterface {
|
||||
buffer.mapping = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {number} bufferIdx
|
||||
* @param {number|BigInt} offset
|
||||
* @param {number} ptr
|
||||
* @param {number|BigInt} size
|
||||
* @return {number}
|
||||
*/
|
||||
wgpuBufferWriteMappedRange: (bufferIdx, offset, ptr, size) => {
|
||||
const buffer = this.buffers.get(bufferIdx);
|
||||
offset = this.unwrapBigInt(offset);
|
||||
size = this.unwrapBigInt(size);
|
||||
|
||||
this.assert(!buffer.mapping, "buffer already mapped");
|
||||
|
||||
const range = buffer.buffer.getMappedRange(offset, size);
|
||||
|
||||
const mapping = new Uint8Array(this.mem.memory.buffer, ptr, size);
|
||||
(new Uint8Array(range)).set(mapping);
|
||||
|
||||
buffer.mapping = { range: range, ptr: ptr, size: range.byteLength };
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
},
|
||||
|
||||
...this.buffers.interface(),
|
||||
|
||||
/* ---------------------- CommandBuffer ---------------------- */
|
||||
@@ -1438,7 +1517,7 @@ class WebGPUInterface {
|
||||
off(4);
|
||||
descriptor = {
|
||||
label: this.StringView(off(this.sizes.StringView)),
|
||||
timestampWrites: this.ComputePassTimestampWritesPtr(off(4)),
|
||||
timestampWrites: this.PassTimestampWritesPtr(off(4)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1478,7 +1557,7 @@ class WebGPUInterface {
|
||||
),
|
||||
depthStencilAttachment: this.RenderPassDepthStencilAttachmentPtr(off(4)),
|
||||
occlusionQuerySet: this.QuerySet(off(4)),
|
||||
timestampWrites: this.RenderPassTimestampWritesPtr(off(4)),
|
||||
timestampWrites: this.PassTimestampWritesPtr(off(4)),
|
||||
maxDrawCount: maxDrawCount,
|
||||
};
|
||||
|
||||
@@ -1881,7 +1960,7 @@ class WebGPUInterface {
|
||||
device.createComputePipelineAsync(this.ComputePipelineDescriptor(descriptorPtr))
|
||||
.catch((e) => {
|
||||
const messageAddr = this.makeMessageArg(e.message);
|
||||
this.callCallback(callbackInfo, [ENUMS.CreatePipelineAsyncStatus.indexOf("Unknown"), 0, messageAddr]);
|
||||
this.callCallback(callbackInfo, [ENUMS.CreatePipelineAsyncStatus.indexOf("ValidationError"), 0, messageAddr]);
|
||||
this.mem.exports.wgpu_free(messageAddr);
|
||||
})
|
||||
.then((computePipeline) => {
|
||||
@@ -2185,6 +2264,11 @@ class WebGPUInterface {
|
||||
|
||||
const callbackInfo = this.CallbackInfo(callbackInfoPtr);
|
||||
device.popErrorScope()
|
||||
.catch((e) => {
|
||||
const messageAddr = this.makeMessageArg(e.message);
|
||||
this.callCallback(callbackInfo, [ENUMS.PopErrorScopeStatus.indexOf("Error"), ENUMS.ErrorType.indexOf("unknown"), messageAddr]);
|
||||
this.mem.exports.wgpu_free(messageAddr);
|
||||
})
|
||||
.then((error) => {
|
||||
if (!error) {
|
||||
this.callCallback(callbackInfo, [ENUMS.PopErrorScopeStatus.indexOf("Success"), ENUMS.ErrorType.indexOf("no-error"), this.zeroMessageArg()]);
|
||||
@@ -2222,6 +2306,10 @@ class WebGPUInterface {
|
||||
|
||||
...this.devices.interface(true),
|
||||
|
||||
/* ---------------------- ExternalTexture ---------------------- */
|
||||
|
||||
...this.externalTextures.interface(true),
|
||||
|
||||
/* ---------------------- Instance ---------------------- */
|
||||
|
||||
/**
|
||||
@@ -2261,7 +2349,6 @@ class WebGPUInterface {
|
||||
/**
|
||||
* @param {number} instanceIdx
|
||||
* @param {number} featurePtr
|
||||
* @returns {number}
|
||||
*/
|
||||
wgpuInstanceGetWGSLLanguageFeatures: (instanceIdx, featuresPtr) => {
|
||||
this.assert(featuresPtr != 0);
|
||||
@@ -2292,8 +2379,6 @@ class WebGPUInterface {
|
||||
for (let i = 0; i < availableFeatures.length; i += 1) {
|
||||
this.mem.storeI32(off(4), availableFeatures[i]);
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -2325,11 +2410,22 @@ class WebGPUInterface {
|
||||
let options;
|
||||
if (optionsPtr != 0) {
|
||||
const off = this.struct(optionsPtr);
|
||||
off(4); // nextInChain
|
||||
off(4); // featureLevel
|
||||
|
||||
let xrCompatible = undefined;
|
||||
const nextInChain = this.mem.loadPtr(off(4));
|
||||
if (nextInChain != 0) {
|
||||
const nextInChainType = this.mem.loadI32(nextInChain + 4);
|
||||
// RequestAdapterWebXROptions = 0x0000000B,
|
||||
if (nextInChainType == 0x0000000B) {
|
||||
xrCompatible = this.mem.loadB32(nextInChain + 8);
|
||||
}
|
||||
}
|
||||
|
||||
options = {
|
||||
featureLevel: this.enumeration("FeatureLevel", off(4)),
|
||||
powerPreference: this.enumeration("PowerPreference", off(4)),
|
||||
forceFallbackAdapter: this.mem.loadB32(off(4)),
|
||||
xrCompatible: xrCompatible,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2405,10 +2501,12 @@ class WebGPUInterface {
|
||||
queue.onSubmittedWorkDone()
|
||||
.catch((e) => {
|
||||
console.warn(e);
|
||||
this.callCallback(callbackInfo, [ENUMS.QueueWorkDoneStatus.indexOf("Error")]);
|
||||
const messageAddr = this.makeMessageArg(e.message);
|
||||
this.callCallback(callbackInfo, [ENUMS.QueueWorkDoneStatus.indexOf("Error"), messageAddr]);
|
||||
this.mem.exports.wgpu_free(messageAddr);
|
||||
})
|
||||
.then(() => {
|
||||
this.callCallback(callbackInfo, [ENUMS.QueueWorkDoneStatus.indexOf("Success")]);
|
||||
this.callCallback(callbackInfo, [ENUMS.QueueWorkDoneStatus.indexOf("Success"), this.zeroMessageArg()]);
|
||||
});
|
||||
|
||||
// TODO: futures?
|
||||
@@ -2920,7 +3018,7 @@ class WebGPUInterface {
|
||||
shaderModule.getCompilationInfo()
|
||||
.catch((e) => {
|
||||
console.warn(e);
|
||||
this.callCallback(callbackInfo, [ENUMS.CompilationInfoRequestStatus.indexOf("Error"), null]);
|
||||
this.callCallback(callbackInfo, [ENUMS.CompilationInfoRequestStatus.indexOf("CallbackCancelled"), null]);
|
||||
})
|
||||
.then((compilationInfo) => {
|
||||
const ptrsToFree = [];
|
||||
@@ -2975,6 +3073,11 @@ class WebGPUInterface {
|
||||
this.mem.exports.wgpu_free(supportedFeaturesPtr);
|
||||
},
|
||||
|
||||
/* ---------------------- SupportedInstanceFeatures ---------------------- */
|
||||
|
||||
wgpuSupportedInstanceFeaturesFreeMembers: (supportedFeaturesCount, supportedFeaturesPtr) => {
|
||||
},
|
||||
|
||||
/* ---------------------- SupportedWGSLLanguageFeatures ---------------------- */
|
||||
|
||||
wgpuSupportedWGSLLanguageFeaturesFreeMembers: (supportedFeaturesCount, supportedFeaturesPtr) => {
|
||||
@@ -2992,7 +3095,23 @@ class WebGPUInterface {
|
||||
const context = surface.getContext("webgpu");
|
||||
|
||||
const off = this.struct(configPtr);
|
||||
off(4);
|
||||
|
||||
let colorSpace = undefined;
|
||||
let toneMapping = undefined;
|
||||
const nextInChain = this.mem.loadPtr(off(4));
|
||||
if (nextInChain != 0) {
|
||||
const colorManagementOff = this.struct(nextInChain);
|
||||
colorManagementOff(4); // next
|
||||
const nextInChainType = this.mem.loadI32(colorManagementOff(4));
|
||||
// SurfaceColorManagement = 0x0000000A,
|
||||
if (nextInChainType == 0x0000000A) {
|
||||
colorSpace = this.enumeration("PredefinedColorSpace", colorManagementOff(4)) ?? "srgb";
|
||||
toneMapping = {
|
||||
mode: this.enumeration("ToneMappingMode", colorManagementOff(4)) ?? "standard",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const device = this.devices.get(this.mem.loadPtr(off(4)));
|
||||
const format = this.enumeration("TextureFormat", off(4));
|
||||
const usage = this.mem.loadU64(off(8));
|
||||
@@ -3019,6 +3138,8 @@ class WebGPUInterface {
|
||||
viewFormats: viewFormats,
|
||||
alphaMode: alphaMode,
|
||||
presentMode: presentMode,
|
||||
colorSpace: colorSpace,
|
||||
toneMapping: toneMapping,
|
||||
};
|
||||
|
||||
context.configure(config);
|
||||
@@ -3071,7 +3192,7 @@ class WebGPUInterface {
|
||||
const textureIdx = this.textures.create(texture);
|
||||
this.mem.storeI32(texturePtr + 4, textureIdx);
|
||||
|
||||
// TODO: determine suboptimal and/or status.
|
||||
// TODO: determine status somehow?
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -3130,7 +3251,27 @@ class WebGPUInterface {
|
||||
let descriptor;
|
||||
if (descriptorPtr != 0) {
|
||||
const off = this.struct(descriptorPtr);
|
||||
off(4);
|
||||
|
||||
let swizzle = undefined;
|
||||
const nextInChain = this.mem.loadPtr(off(4));
|
||||
if (nextInChain != 0) {
|
||||
const swizzleOff = this.struct(nextInChain);
|
||||
swizzleOff(4); // next
|
||||
const nextInChainType = this.mem.loadI32(swizzleOff(4));
|
||||
// TextureComponentSwizzle = 0x00000016,
|
||||
if (nextInChainType == 0x00000016) {
|
||||
const r = this.enumeration("ComponentSwizzle", swizzleOff(4));
|
||||
this.assert(r !== undefined);
|
||||
const g = this.enumeration("ComponentSwizzle", swizzleOff(4));
|
||||
this.assert(g !== undefined);
|
||||
const b = this.enumeration("ComponentSwizzle", swizzleOff(4));
|
||||
this.assert(b !== undefined);
|
||||
const a = this.enumeration("ComponentSwizzle", swizzleOff(4));
|
||||
this.assert(a !== undefined);
|
||||
swizzle = r + g + b + a;
|
||||
}
|
||||
}
|
||||
|
||||
descriptor = {
|
||||
label: this.StringView(off(this.sizes.StringView)),
|
||||
format: this.enumeration("TextureFormat", off(4)),
|
||||
@@ -3141,6 +3282,7 @@ class WebGPUInterface {
|
||||
arrayLayerCount: this.mem.loadU32(off(4)),
|
||||
aspect: this.enumeration("TextureAspect", off(4)),
|
||||
usage: this.mem.loadU64(off(8)),
|
||||
swizzle: swizzle,
|
||||
};
|
||||
if (descriptor.arrayLayerCount == 0xFFFFFFFF) {
|
||||
descriptor.arrayLayerCount = undefined;
|
||||
@@ -3216,6 +3358,15 @@ class WebGPUInterface {
|
||||
return texture.sampleCount;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {number} textureIdx
|
||||
* @returns {number}
|
||||
*/
|
||||
wgpuTextureGetTextureBindingViewDimension: (textureIdx) => {
|
||||
const texture = this.textures.get(textureIdx);
|
||||
return ENUMS.TextureViewDimension.indexOf(texture.textureBindingViewDimension);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {number} textureIdx
|
||||
* @returns {number}
|
||||
|
||||
1232
vendor/wgpu/wgpu.odin
vendored
1232
vendor/wgpu/wgpu.odin
vendored
File diff suppressed because it is too large
Load Diff
15
vendor/wgpu/wgpu_native.odin
vendored
15
vendor/wgpu/wgpu_native.odin
vendored
@@ -11,6 +11,8 @@ foreign libwgpu {
|
||||
@(link_name="wgpuQueueSubmitForIndex")
|
||||
RawQueueSubmitForIndex :: proc(queue: Queue, commandCount: uint, commands: [^]CommandBuffer) -> SubmissionIndex ---
|
||||
|
||||
QueueGetTimestampPeriod :: proc(queue: Queue) -> f32 ---
|
||||
|
||||
// Returns true if the queue is empty, or false if there are more queue submissions still in flight.
|
||||
DevicePoll :: proc(device: Device, wait: b32, /* NULLABLE */ submissionIndex: /* const */ ^SubmissionIndex = nil) -> b32 ---
|
||||
DeviceCreateShaderModuleSpirV :: proc(device: Device, descriptor: ^ShaderModuleDescriptorSpirV) -> ShaderModule ---
|
||||
@@ -21,9 +23,13 @@ foreign libwgpu {
|
||||
|
||||
GetVersion :: proc() -> u32 ---
|
||||
|
||||
RenderPassEncoderSetPushConstants :: proc(encoder: RenderPassEncoder, stages: ShaderStageFlags, offset: u32, sizeBytes: u32, data: rawptr) ---
|
||||
ComputePassEncoderSetPushConstants :: proc(encoder: ComputePassEncoder, offset: u32, sizeBytes: u32, data: rawptr) ---
|
||||
RenderBundleEncoderSetPushConstants :: proc(encoder: RenderBundleEncoder, stages: ShaderStageFlags, offset: u32, sizeBytes: u32, data: rawptr) ---
|
||||
DeviceGetNativeMetalDevice :: proc(device: Device) -> rawptr ---
|
||||
DeviceGetNativeMetalCommandQueue :: proc(device: Device) -> rawptr ---
|
||||
DeviceGetNativeMetalTexture :: proc(device: Device) -> rawptr ---
|
||||
|
||||
RenderPassEncoderSetImmediates :: proc(encoder: RenderPassEncoder, stages: ShaderStageFlags, offset: u32, sizeBytes: u32, data: rawptr) ---
|
||||
ComputePassEncoderSetImmediates :: proc(encoder: ComputePassEncoder, offset: u32, sizeBytes: u32, data: rawptr) ---
|
||||
RenderBundleEncoderSetImmediates :: proc(encoder: RenderBundleEncoder, stages: ShaderStageFlags, offset: u32, sizeBytes: u32, data: rawptr) ---
|
||||
|
||||
RenderPassEncoderMultiDrawIndirect :: proc(encoder: RenderPassEncoder, buffer: Buffer, offset: u64, count: u32) ---
|
||||
RenderPassEncoderMultiDrawIndexedIndirect :: proc(encoder: RenderPassEncoder, buffer: Buffer, offset: u64, count: u32) ---
|
||||
@@ -38,6 +44,9 @@ foreign libwgpu {
|
||||
|
||||
ComputePassEncoderWriteTimestamp :: proc(computePassEncoder: ComputePassEncoder, querySet: QuerySet, queryIndex: u32) ---
|
||||
RenderPassEncoderWriteTimestamp :: proc(renderPassEncoder: RenderPassEncoder, querySet: QuerySet, queryIndex: u32) ---
|
||||
|
||||
DeviceStartGraphicsDebuggerCapture :: proc(device: Device) -> b32 ---
|
||||
DeviceStopGraphicsDebuggerCapture :: proc(device: Device) ---
|
||||
}
|
||||
|
||||
GenerateReport :: proc "c" (instance: Instance) -> (report: GlobalReport) {
|
||||
|
||||
72
vendor/wgpu/wgpu_native_types.odin
vendored
72
vendor/wgpu/wgpu_native_types.odin
vendored
@@ -2,8 +2,8 @@ package wgpu
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
BINDINGS_VERSION :: [4]u8{27, 0, 2, 0}
|
||||
BINDINGS_VERSION_STRING :: "27.0.2.0"
|
||||
BINDINGS_VERSION :: [4]u8{29, 0, 0, 0}
|
||||
BINDINGS_VERSION_STRING :: "29.0.0.0"
|
||||
|
||||
LogLevel :: enum i32 {
|
||||
Off,
|
||||
@@ -19,21 +19,29 @@ InstanceBackend :: enum i32 {
|
||||
GL,
|
||||
Metal,
|
||||
DX12,
|
||||
DX11,
|
||||
BrowserWebGPU,
|
||||
// DX11,
|
||||
BrowserWebGPU = 5,
|
||||
}
|
||||
InstanceBackendFlags :: bit_set[InstanceBackend; Flags]
|
||||
InstanceBackendFlags_All :: InstanceBackendFlags{}
|
||||
InstanceBackendFlags_Primary :: InstanceBackendFlags{ .Vulkan, .Metal, .DX12, .BrowserWebGPU }
|
||||
InstanceBackendFlags_Secondary :: InstanceBackendFlags{ .GL, .DX11 }
|
||||
InstanceBackendFlags_Secondary :: InstanceBackendFlags{ .GL }
|
||||
|
||||
InstanceFlag :: enum i32 {
|
||||
Debug,
|
||||
Validation,
|
||||
DiscardHalLabels,
|
||||
AllowUnderlyingNoncompliantAdapter,
|
||||
GPUBasedValidation,
|
||||
ValidationIndirectCall,
|
||||
AutomaticTimestampNormalization,
|
||||
Default = 24,
|
||||
Debugging,
|
||||
AdvancedDebugging,
|
||||
WithEnv,
|
||||
}
|
||||
InstanceFlags :: bit_set[InstanceFlag; Flags]
|
||||
InstanceFlags_Default :: InstanceFlags{}
|
||||
InstanceFlags_Empty :: InstanceFlags{}
|
||||
|
||||
Dx12Compiler :: enum i32 {
|
||||
Undefined,
|
||||
@@ -72,6 +80,42 @@ GLFenceBehaviour :: enum i32 {
|
||||
AutoFinish,
|
||||
}
|
||||
|
||||
Dx12SwapchainKind :: enum i32 {
|
||||
Undefined,
|
||||
DxgiFromHwnd,
|
||||
DxgiFromVisual,
|
||||
}
|
||||
|
||||
NativeDisplayHandleType :: enum i32 {
|
||||
None,
|
||||
Xlib,
|
||||
Xcb,
|
||||
Wayland,
|
||||
}
|
||||
|
||||
XlibDisplayHandle :: struct {
|
||||
display: rawptr,
|
||||
screen: i32,
|
||||
}
|
||||
|
||||
XcbDisplayHandle :: struct {
|
||||
connection: rawptr,
|
||||
screen: i32,
|
||||
}
|
||||
|
||||
WaylandDisplayHandle :: struct {
|
||||
display: rawptr,
|
||||
}
|
||||
|
||||
NativeDisplayHandle :: struct {
|
||||
type: NativeDisplayHandleType,
|
||||
using data: struct #raw_union {
|
||||
xlib: XlibDisplayHandle,
|
||||
xcb: XcbDisplayHandle,
|
||||
wayland: WaylandDisplayHandle,
|
||||
},
|
||||
}
|
||||
|
||||
InstanceExtras :: struct {
|
||||
using chain: ChainedStruct,
|
||||
backends: InstanceBackendFlags,
|
||||
@@ -81,8 +125,10 @@ InstanceExtras :: struct {
|
||||
glFenceBehaviour: GLFenceBehaviour,
|
||||
dxcPath: StringView,
|
||||
dcxMaxShaderModel: DxcMaxShaderModel,
|
||||
dx12PresentationSystem: Dx12SwapchainKind,
|
||||
budgetForDeviceCreation: ^u8,
|
||||
budgetForDeviceLoss: ^u8,
|
||||
displayHandle: NativeDisplayHandle,
|
||||
}
|
||||
|
||||
DeviceExtras :: struct {
|
||||
@@ -91,21 +137,15 @@ DeviceExtras :: struct {
|
||||
}
|
||||
|
||||
NativeLimits :: struct {
|
||||
using chain: ChainedStructOut,
|
||||
maxPushConstantSize: u32,
|
||||
using chain: ChainedStruct,
|
||||
maxImmediateSize: u32,
|
||||
maxNonSamplerBindings: u32,
|
||||
}
|
||||
|
||||
PushConstantRange :: struct {
|
||||
stages: ShaderStageFlags,
|
||||
start: u32,
|
||||
end: u32,
|
||||
maxBindingArrayElementsPerShaderStage: u32,
|
||||
}
|
||||
|
||||
PipelineLayoutExtras :: struct {
|
||||
using chain: ChainedStruct,
|
||||
pushConstantRangeCount: uint,
|
||||
pushConstantRanges: [^]PushConstantRange `fmt:"v,pushConstantRangeCount"`,
|
||||
immediateDataSize: u32,
|
||||
}
|
||||
|
||||
SubmissionIndex :: distinct u64
|
||||
|
||||
6
vendor/zlib/zlib.odin
vendored
6
vendor/zlib/zlib.odin
vendored
@@ -222,7 +222,7 @@ foreign zlib {
|
||||
@(default_calling_convention="c")
|
||||
foreign zlib {
|
||||
deflateInit_ :: proc(strm: z_streamp, level: c.int, version: cstring, stream_size: c.int) -> c.int ---
|
||||
inflateInit_ :: proc(strm: z_streamp, level: c.int, version: cstring, stream_size: c.int) -> c.int ---
|
||||
inflateInit_ :: proc(strm: z_streamp, version: cstring, stream_size: c.int) -> c.int ---
|
||||
deflateInit2_ :: proc(strm: z_streamp, level, method, windowBits, memLevel, strategy: c.int, version: cstring, stream_size: c.int) -> c.int ---
|
||||
inflateInit2_ :: proc(strm: z_streamp, windowBits: c.int, version: cstring, stream_size: c.int) -> c.int ---
|
||||
inflateBackInit_ :: proc(strm: z_streamp, windowBits: c.int, window: [^]c.uchar, version: cstring, stream_size: c.int) -> c.int ---
|
||||
@@ -236,8 +236,8 @@ deflateInit :: #force_inline proc "c" (strm: z_streamp, level: c.int) -> c.int {
|
||||
return deflateInit_(strm, level, VERSION, c.int(size_of(z_stream)))
|
||||
}
|
||||
|
||||
inflateInit :: #force_inline proc "c" (strm: z_streamp, level: c.int) -> c.int {
|
||||
return inflateInit_(strm, level, VERSION, c.int(size_of(z_stream)))
|
||||
inflateInit :: #force_inline proc "c" (strm: z_streamp) -> c.int {
|
||||
return inflateInit_(strm, VERSION, c.int(size_of(z_stream)))
|
||||
}
|
||||
|
||||
deflateInit2 :: #force_inline proc "c" (strm: z_streamp, level, method, windowBits, memLevel, strategy: c.int) -> c.int {
|
||||
|
||||
Reference in New Issue
Block a user