mirror of
https://github.com/odin-lang/Odin.git
synced 2026-04-18 20:40:28 +00:00
Merge branch 'master' into bill/feature-using-stmt
This commit is contained in:
16
.github/workflows/ci.yml
vendored
16
.github/workflows/ci.yml
vendored
@@ -6,7 +6,7 @@ jobs:
|
||||
name: NetBSD Build, Check, and Test
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
PKGSRC_BRANCH: 2025Q2
|
||||
PKGSRC_BRANCH: 2025Q4
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build, Check, and Test
|
||||
@@ -156,18 +156,14 @@ jobs:
|
||||
|
||||
- name: Check benchmarks
|
||||
run: ./odin check tests/benchmark -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point
|
||||
|
||||
- name: Odin check examples/all for Linux i386
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:linux_i386
|
||||
- name: Odin check examples/all for Linux arm64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:linux_arm64
|
||||
- name: Odin check examples/all for FreeBSD amd64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freebsd_amd64
|
||||
- name: Odin check examples/all for OpenBSD amd64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:openbsd_amd64
|
||||
|
||||
- name: Odin check examples/all for js_wasm32
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point -target:js_wasm32
|
||||
@@ -178,12 +174,6 @@ jobs:
|
||||
- name: Odin check examples/all/sdl3 for Linux i386
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all/sdl3 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point -target:linux_i386
|
||||
- name: Odin check examples/all/sdl3 for Linux arm64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all/sdl3 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point -target:linux_arm64
|
||||
- name: Odin check examples/all/sdl3 for FreeBSD amd64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all/sdl3 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point -target:freebsd_amd64
|
||||
- name: Odin check examples/all/sdl3 for OpenBSD amd64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all/sdl3 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point -target:openbsd_amd64
|
||||
|
||||
@@ -122,6 +122,7 @@ Type_Info_Struct_Flag :: enum u8 {
|
||||
raw_union = 1,
|
||||
all_or_none = 2,
|
||||
align = 3,
|
||||
simple = 4,
|
||||
}
|
||||
|
||||
Type_Info_Struct :: struct {
|
||||
|
||||
@@ -360,7 +360,7 @@ new_aligned :: proc($T: typeid, alignment: int, allocator := context.allocator,
|
||||
|
||||
@(builtin, require_results)
|
||||
new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) #optional_allocator_error {
|
||||
t = (^T)(raw_data(mem_alloc_bytes(size_of(T), align_of(T), allocator, loc) or_return))
|
||||
t = (^T)(raw_data(mem_alloc_non_zeroed(size_of(T), align_of(T), allocator, loc) or_return))
|
||||
if t != nil {
|
||||
t^ = data
|
||||
}
|
||||
@@ -433,7 +433,6 @@ _make_dynamic_array_len_cap :: proc(array: ^Raw_Dynamic_Array, size_of_elem, ali
|
||||
array.data = raw_data(data)
|
||||
array.len = 0 if use_zero else len
|
||||
array.cap = 0 if use_zero else cap
|
||||
array.allocator = allocator
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ bounds_trap :: proc "contextless" () -> ! {
|
||||
}
|
||||
|
||||
@(no_instrumentation)
|
||||
type_assertion_trap :: proc "contextless" () -> ! {
|
||||
type_assertion_trap_contextless :: proc "contextless" () -> ! {
|
||||
when ODIN_OS == .Windows {
|
||||
windows_trap_type_assertion()
|
||||
} else when ODIN_OS == .Orca {
|
||||
@@ -137,20 +137,22 @@ matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32
|
||||
|
||||
|
||||
when ODIN_NO_RTTI {
|
||||
type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32) {
|
||||
type_assertion_check_with_context :: proc "odin" (ok: bool, file: string, line, column: i32) {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
|
||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||
print_string(" Invalid type assertion\n")
|
||||
type_assertion_trap()
|
||||
handle_error :: proc "odin" (file: string, line, column: i32) -> ! {
|
||||
p := context.assertion_failure_proc
|
||||
if p == nil {
|
||||
p = default_assertion_failure_proc
|
||||
}
|
||||
p("type assertion", "Invalid type assertion", Source_Code_Location{file, line, column, ""})
|
||||
}
|
||||
handle_error(file, line, column)
|
||||
}
|
||||
|
||||
type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32) {
|
||||
type_assertion_check_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32) {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
@@ -158,12 +160,71 @@ when ODIN_NO_RTTI {
|
||||
handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
|
||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||
print_string(" Invalid type assertion\n")
|
||||
type_assertion_trap()
|
||||
type_assertion_trap_contextless()
|
||||
}
|
||||
handle_error(file, line, column)
|
||||
}
|
||||
|
||||
type_assertion_check2_with_context :: proc "odin" (ok: bool, file: string, line, column: i32) {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "odin" (file: string, line, column: i32) -> ! {
|
||||
p := context.assertion_failure_proc
|
||||
if p == nil {
|
||||
p = default_assertion_failure_proc
|
||||
}
|
||||
p("type assertion", "Invalid type assertion", Source_Code_Location{file, line, column, ""})
|
||||
}
|
||||
|
||||
handle_error(file, line, column)
|
||||
}
|
||||
|
||||
type_assertion_check2_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32) {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
|
||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||
print_string(" Invalid type assertion\n")
|
||||
type_assertion_trap_contextless()
|
||||
}
|
||||
handle_error(file, line, column)
|
||||
}
|
||||
} else {
|
||||
type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid) {
|
||||
@(private="file")
|
||||
TYPE_ASSERTION_BUFFER_SIZE :: 1024
|
||||
|
||||
type_assertion_check_with_context :: proc "odin" (ok: bool, file: string, line, column: i32, from, to: typeid) {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "odin" (file: string, line, column: i32, from, to: typeid) -> ! {
|
||||
do_msg :: proc "contextless" (i: ^int, buf: []byte, file: string, line, column: i32, from, to: typeid) -> bool {
|
||||
write_string(i, buf, "Invalid type assertion from ") or_return
|
||||
write_typeid(i, buf, from) or_return
|
||||
write_string(i, buf, " to ") or_return
|
||||
write_typeid(i, buf, to) or_return
|
||||
return true
|
||||
}
|
||||
|
||||
buf: [TYPE_ASSERTION_BUFFER_SIZE]byte
|
||||
i := 0
|
||||
_ = do_msg(&i, buf[:], file, line, column, from, to)
|
||||
|
||||
p := context.assertion_failure_proc
|
||||
if p == nil {
|
||||
p = default_assertion_failure_proc
|
||||
}
|
||||
p("type assertion", string(buf[:i]), Source_Code_Location{file, line, column, ""})
|
||||
}
|
||||
handle_error(file, line, column, from, to)
|
||||
}
|
||||
|
||||
type_assertion_check_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid) {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
@@ -175,47 +236,90 @@ when ODIN_NO_RTTI {
|
||||
print_string(" to ")
|
||||
print_typeid(to)
|
||||
print_byte('\n')
|
||||
type_assertion_trap()
|
||||
type_assertion_trap_contextless()
|
||||
}
|
||||
handle_error(file, line, column, from, to)
|
||||
}
|
||||
|
||||
type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
|
||||
@(private="file")
|
||||
type_assertion_variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid {
|
||||
if id == nil || data == nil {
|
||||
return id
|
||||
}
|
||||
ti := type_info_base(type_info_of(id))
|
||||
#partial switch v in ti.variant {
|
||||
case Type_Info_Any:
|
||||
return (^any)(data).id
|
||||
case Type_Info_Union:
|
||||
if v.tag_type == nil {
|
||||
if (^rawptr)(data)^ == nil {
|
||||
return nil
|
||||
}
|
||||
return v.variants[0].id
|
||||
|
||||
}
|
||||
|
||||
tag_ptr := uintptr(data) + v.tag_offset
|
||||
idx := 0
|
||||
switch v.tag_type.size {
|
||||
case 1: idx = int( (^u8)(tag_ptr)^); if !v.no_nil { idx -= 1 }
|
||||
case 2: idx = int( (^u16)(tag_ptr)^); if !v.no_nil { idx -= 1 }
|
||||
case 4: idx = int( (^u32)(tag_ptr)^); if !v.no_nil { idx -= 1 }
|
||||
case 8: idx = int( (^u64)(tag_ptr)^); if !v.no_nil { idx -= 1 }
|
||||
case 16: idx = int((^u128)(tag_ptr)^); if !v.no_nil { idx -= 1 }
|
||||
}
|
||||
if idx < 0 {
|
||||
return nil
|
||||
} else if idx < len(v.variants) {
|
||||
return v.variants[idx].id
|
||||
}
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
type_assertion_check2_with_context :: proc "odin" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
|
||||
variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid {
|
||||
if id == nil || data == nil {
|
||||
return id
|
||||
}
|
||||
ti := type_info_base(type_info_of(id))
|
||||
#partial switch v in ti.variant {
|
||||
case Type_Info_Any:
|
||||
return (^any)(data).id
|
||||
case Type_Info_Union:
|
||||
tag_ptr := uintptr(data) + v.tag_offset
|
||||
idx := 0
|
||||
switch v.tag_type.size {
|
||||
case 1: idx = int((^u8)(tag_ptr)^) - 1
|
||||
case 2: idx = int((^u16)(tag_ptr)^) - 1
|
||||
case 4: idx = int((^u32)(tag_ptr)^) - 1
|
||||
case 8: idx = int((^u64)(tag_ptr)^) - 1
|
||||
case 16: idx = int((^u128)(tag_ptr)^) - 1
|
||||
}
|
||||
if idx < 0 {
|
||||
return nil
|
||||
} else if idx < len(v.variants) {
|
||||
return v.variants[idx].id
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "odin" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! {
|
||||
do_msg :: proc "contextless" (i: ^int, buf: []byte, file: string, line, column: i32, from, to, actual: typeid) -> bool {
|
||||
write_string(i, buf, "Invalid type assertion from ") or_return
|
||||
write_typeid(i, buf, from) or_return
|
||||
write_string(i, buf, " to ") or_return
|
||||
write_typeid(i, buf, to) or_return
|
||||
if actual != from {
|
||||
write_string(i, buf, ", actual type: ") or_return
|
||||
write_typeid(i, buf, actual) or_return
|
||||
}
|
||||
return true
|
||||
}
|
||||
return id
|
||||
|
||||
actual := type_assertion_variant_type(from, from_data)
|
||||
|
||||
buf: [TYPE_ASSERTION_BUFFER_SIZE]byte
|
||||
i := 0
|
||||
_ = do_msg(&i, buf[:], file, line, column, from, to, actual)
|
||||
|
||||
p := context.assertion_failure_proc
|
||||
if p == nil {
|
||||
p = default_assertion_failure_proc
|
||||
}
|
||||
p("type assertion", string(buf[:i]), Source_Code_Location{file, line, column, ""})
|
||||
}
|
||||
handle_error(file, line, column, from, to, from_data)
|
||||
}
|
||||
|
||||
type_assertion_check2_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! {
|
||||
|
||||
actual := variant_type(from, from_data)
|
||||
actual := type_assertion_variant_type(from, from_data)
|
||||
|
||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||
print_string(" Invalid type assertion from ")
|
||||
@@ -227,7 +331,7 @@ when ODIN_NO_RTTI {
|
||||
print_typeid(actual)
|
||||
}
|
||||
print_byte('\n')
|
||||
type_assertion_trap()
|
||||
type_assertion_trap_contextless()
|
||||
}
|
||||
handle_error(file, line, column, from, to, from_data)
|
||||
}
|
||||
|
||||
@@ -280,11 +280,22 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||
print_byte('i' if info.signed else 'u')
|
||||
print_u64(u64(8*ti.size))
|
||||
}
|
||||
switch info.endianness {
|
||||
case .Platform: // nothing
|
||||
case .Little: print_string("le")
|
||||
case .Big: print_string("be")
|
||||
}
|
||||
|
||||
case Type_Info_Rune:
|
||||
print_string("rune")
|
||||
case Type_Info_Float:
|
||||
print_byte('f')
|
||||
print_u64(u64(8*ti.size))
|
||||
switch info.endianness {
|
||||
case .Platform: // nothing
|
||||
case .Little: print_string("le")
|
||||
case .Big: print_string("be")
|
||||
}
|
||||
case Type_Info_Complex:
|
||||
print_string("complex")
|
||||
print_u64(u64(8*ti.size))
|
||||
@@ -410,6 +421,7 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||
if .packed in info.flags { print_string("#packed ") }
|
||||
if .raw_union in info.flags { print_string("#raw_union ") }
|
||||
if .all_or_none in info.flags { print_string("#all_or_none ") }
|
||||
if .simple in info.flags { print_string("#simple ") }
|
||||
if .align in info.flags {
|
||||
print_string("#align(")
|
||||
print_u64(u64(ti.align))
|
||||
@@ -494,6 +506,9 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||
print_type(info.elem)
|
||||
|
||||
case Type_Info_Matrix:
|
||||
if info.layout == .Row_Major {
|
||||
print_string("#row_major ")
|
||||
}
|
||||
print_string("matrix[")
|
||||
print_u64(u64(info.row_count))
|
||||
print_string(", ")
|
||||
@@ -502,3 +517,419 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||
print_type(info.elem)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
write_string :: proc "contextless" (i: ^int, dst: []byte, src: string) -> bool {
|
||||
if i^ < len(dst) {
|
||||
i^ += copy(dst[i^:], src)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
write_byte :: proc "contextless" (i: ^int, dst: []byte, src: byte) -> bool {
|
||||
if i^ < len(dst) {
|
||||
dst[i^] = src
|
||||
i^ += 1
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
write_u64 :: proc "contextless" (j: ^int, dst: []byte, x: u64) -> bool {
|
||||
if j^ < len(dst) {
|
||||
b :: u64(10)
|
||||
u := x
|
||||
|
||||
a: [129]byte
|
||||
i := len(a)
|
||||
for u >= b {
|
||||
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
|
||||
u /= b
|
||||
}
|
||||
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
|
||||
|
||||
return write_string(j, dst, string(a[i:]))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
write_i64 :: proc "contextless" (j: ^int, dst: []byte, x: i64) -> bool {
|
||||
if j^ < len(dst) {
|
||||
b :: u64(10)
|
||||
u := u64(abs(x))
|
||||
neg := x < 0
|
||||
|
||||
a: [129]byte
|
||||
i := len(a)
|
||||
for u >= b {
|
||||
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
|
||||
u /= b
|
||||
}
|
||||
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
|
||||
if neg {
|
||||
i -= 1; a[i] = '-'
|
||||
}
|
||||
|
||||
return write_string(j, dst, string(a[i:]))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
write_caller_location :: #force_no_inline proc "contextless" (i: ^int, buf: []byte, loc: Source_Code_Location) -> bool {
|
||||
write_string(i, buf, loc.file_path) or_return
|
||||
|
||||
when ODIN_ERROR_POS_STYLE == .Default {
|
||||
write_byte(i, buf, '(') or_return
|
||||
write_u64 (i, buf, u64(loc.line)) or_return
|
||||
if loc.column != 0 {
|
||||
write_byte(i, buf, ':') or_return
|
||||
write_u64 (i, buf, u64(loc.column)) or_return
|
||||
}
|
||||
write_byte(i, buf, ')') or_return
|
||||
return true
|
||||
} else when ODIN_ERROR_POS_STYLE == .Unix {
|
||||
write_byte(i, buf, ':') or_return
|
||||
write_u64 (i, buf, u64(loc.line)) or_return
|
||||
if loc.column != 0 {
|
||||
write_byte(i, buf, ':') or_return
|
||||
write_u64 (i, buf, u64(loc.column)) or_return
|
||||
}
|
||||
write_byte(i, buf, ':') or_return
|
||||
return true
|
||||
} else {
|
||||
#panic("unhandled ODIN_ERROR_POS_STYLE")
|
||||
}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
write_typeid :: #force_no_inline proc "contextless" (i: ^int, buf: []byte, id: typeid) -> bool {
|
||||
when ODIN_NO_RTTI {
|
||||
if id == nil {
|
||||
write_string(i, buf, "nil") or_return
|
||||
} else {
|
||||
write_string(i, buf, "<unknown type>") or_return
|
||||
}
|
||||
} else {
|
||||
if id == nil {
|
||||
write_string(i, buf, "nil") or_return
|
||||
} else {
|
||||
ti := type_info_of(id)
|
||||
write_write_type(i, buf, ti) or_return
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
write_rune :: #force_no_inline proc "contextless" (i: ^int, buf: []byte, r: rune) -> (written: int, ok: bool) #no_bounds_check {
|
||||
RUNE_SELF :: 0x80
|
||||
|
||||
if r < RUNE_SELF {
|
||||
write_byte(i, buf,byte(r)) or_return
|
||||
return 1, true
|
||||
}
|
||||
|
||||
b, n := encode_rune(r)
|
||||
prev := i^
|
||||
write_string(i, buf, string(b[:n])) or_return
|
||||
return i^ - prev, true
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
write_encoded_rune :: #force_no_inline proc "contextless" (i: ^int, buf: []byte, r: rune) -> bool {
|
||||
write_byte(i, buf, '\'') or_return
|
||||
|
||||
switch r {
|
||||
case '\a': write_string(i, buf, "\\a") or_return
|
||||
case '\b': write_string(i, buf, "\\b") or_return
|
||||
case '\e': write_string(i, buf, "\\e") or_return
|
||||
case '\f': write_string(i, buf, "\\f") or_return
|
||||
case '\n': write_string(i, buf, "\\n") or_return
|
||||
case '\r': write_string(i, buf, "\\r") or_return
|
||||
case '\t': write_string(i, buf, "\\t") or_return
|
||||
case '\v': write_string(i, buf, "\\v") or_return
|
||||
case:
|
||||
if r <= 0 {
|
||||
write_string(i, buf, "\\x00") or_return
|
||||
} else if r < 32 {
|
||||
n0, n1 := u8(r) >> 4, u8(r) & 0xf
|
||||
write_string(i, buf, "\\x") or_return
|
||||
write_byte (i, buf, _INTEGER_DIGITS_VAR[n0]) or_return
|
||||
write_byte (i, buf, _INTEGER_DIGITS_VAR[n1]) or_return
|
||||
} else {
|
||||
_ = write_rune(i, buf, r) or_return
|
||||
}
|
||||
}
|
||||
|
||||
write_byte(i, buf, '\'') or_return
|
||||
return true
|
||||
}
|
||||
|
||||
@(optimization_mode="favor_size")
|
||||
write_write_type :: #force_no_inline proc "contextless" (i: ^int, buf: []byte, ti: ^Type_Info) -> bool {
|
||||
if ti == nil {
|
||||
write_string(i, buf, "nil") or_return
|
||||
return true
|
||||
}
|
||||
|
||||
switch info in ti.variant {
|
||||
case Type_Info_Named:
|
||||
write_string(i, buf, info.name) or_return
|
||||
case Type_Info_Integer:
|
||||
switch ti.id {
|
||||
case int: write_string(i, buf, "int") or_return
|
||||
case uint: write_string(i, buf, "uint") or_return
|
||||
case uintptr: write_string(i, buf, "uintptr") or_return
|
||||
case:
|
||||
write_byte(i, buf, 'i' if info.signed else 'u') or_return
|
||||
write_u64 (i, buf, u64(8*ti.size)) or_return
|
||||
}
|
||||
switch info.endianness {
|
||||
case .Platform: // nothing
|
||||
case .Little: write_string(i, buf, "le") or_return
|
||||
case .Big: write_string(i, buf, "be") or_return
|
||||
}
|
||||
|
||||
case Type_Info_Rune:
|
||||
write_string(i, buf, "rune") or_return
|
||||
case Type_Info_Float:
|
||||
write_byte(i, buf, 'f') or_return
|
||||
write_u64(i, buf, u64(8*ti.size)) or_return
|
||||
switch info.endianness {
|
||||
case .Platform: // nothing
|
||||
case .Little: write_string(i, buf, "le") or_return
|
||||
case .Big: write_string(i, buf, "be") or_return
|
||||
}
|
||||
|
||||
case Type_Info_Complex:
|
||||
write_string(i, buf, "complex") or_return
|
||||
write_u64 (i, buf, u64(8*ti.size)) or_return
|
||||
case Type_Info_Quaternion:
|
||||
write_string(i, buf, "quaternion") or_return
|
||||
write_u64 (i, buf, u64(8*ti.size)) or_return
|
||||
case Type_Info_String:
|
||||
if info.is_cstring {
|
||||
write_byte(i, buf, 'c') or_return
|
||||
}
|
||||
write_string(i, buf, "string") or_return
|
||||
switch info.encoding {
|
||||
case .UTF_8: /**/
|
||||
case .UTF_16: write_string(i, buf, "16") or_return
|
||||
}
|
||||
case Type_Info_Boolean:
|
||||
switch ti.id {
|
||||
case bool: write_string(i, buf, "bool") or_return
|
||||
case:
|
||||
write_byte(i, buf, 'b') or_return
|
||||
write_u64 (i, buf, u64(8*ti.size)) or_return
|
||||
}
|
||||
case Type_Info_Any:
|
||||
write_string(i, buf, "any") or_return
|
||||
case Type_Info_Type_Id:
|
||||
write_string(i, buf, "typeid") or_return
|
||||
|
||||
case Type_Info_Pointer:
|
||||
if info.elem == nil {
|
||||
write_string(i, buf, "rawptr") or_return
|
||||
} else {
|
||||
write_string (i, buf, "^") or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
}
|
||||
case Type_Info_Multi_Pointer:
|
||||
write_string (i, buf, "[^]") or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
case Type_Info_Soa_Pointer:
|
||||
write_string (i, buf, "#soa ^") or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
case Type_Info_Procedure:
|
||||
write_string(i, buf, "proc") or_return
|
||||
if info.params == nil {
|
||||
write_string(i, buf, "()") or_return
|
||||
} else {
|
||||
t := info.params.variant.(Type_Info_Parameters)
|
||||
write_byte(i, buf, '(') or_return
|
||||
for t, j in t.types {
|
||||
if j > 0 { write_string(i, buf, ", ") or_return }
|
||||
write_write_type(i, buf, t) or_return
|
||||
}
|
||||
write_string(i, buf, ")") or_return
|
||||
}
|
||||
if info.results != nil {
|
||||
write_string (i, buf, " -> ") or_return
|
||||
write_write_type(i, buf, info.results) or_return
|
||||
}
|
||||
case Type_Info_Parameters:
|
||||
count := len(info.names)
|
||||
if count != 1 { write_byte(i, buf, '(') or_return }
|
||||
for name, j in info.names {
|
||||
if j > 0 { write_string(i, buf, ", ") or_return }
|
||||
|
||||
t := info.types[j]
|
||||
|
||||
if len(name) > 0 {
|
||||
write_string(i, buf, name) or_return
|
||||
write_string(i, buf, ": ") or_return
|
||||
}
|
||||
write_write_type(i, buf, t) or_return
|
||||
}
|
||||
if count != 1 { write_string(i, buf, ")") or_return }
|
||||
|
||||
case Type_Info_Array:
|
||||
write_byte (i, buf, '[') or_return
|
||||
write_u64 (i, buf, u64(info.count)) or_return
|
||||
write_byte (i, buf, ']') or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
|
||||
case Type_Info_Enumerated_Array:
|
||||
if info.is_sparse {
|
||||
write_string(i, buf, "#sparse") or_return
|
||||
}
|
||||
write_byte (i, buf, '[') or_return
|
||||
write_write_type(i, buf, info.index) or_return
|
||||
write_byte (i, buf, ']') or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
|
||||
|
||||
case Type_Info_Dynamic_Array:
|
||||
write_string (i, buf, "[dynamic]") or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
case Type_Info_Slice:
|
||||
write_string (i, buf, "[]") or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
|
||||
case Type_Info_Map:
|
||||
write_string (i, buf, "map[") or_return
|
||||
write_write_type(i, buf, info.key) or_return
|
||||
write_byte (i, buf, ']') or_return
|
||||
write_write_type(i, buf, info.value) or_return
|
||||
|
||||
case Type_Info_Struct:
|
||||
switch info.soa_kind {
|
||||
case .None: // Ignore
|
||||
case .Fixed:
|
||||
write_string (i, buf, "#soa[") or_return
|
||||
write_u64 (i, buf, u64(info.soa_len)) or_return
|
||||
write_byte (i, buf, ']') or_return
|
||||
write_write_type(i, buf, info.soa_base_type) or_return
|
||||
return true
|
||||
case .Slice:
|
||||
write_string (i, buf, "#soa[]") or_return
|
||||
write_write_type(i, buf, info.soa_base_type) or_return
|
||||
return true
|
||||
case .Dynamic:
|
||||
write_string (i, buf, "#soa[dynamic]") or_return
|
||||
write_write_type(i, buf, info.soa_base_type) or_return
|
||||
return true
|
||||
}
|
||||
|
||||
write_string(i, buf, "struct ") or_return
|
||||
if .packed in info.flags { write_string(i, buf, "#packed ") or_return }
|
||||
if .raw_union in info.flags { write_string(i, buf, "#raw_union ") or_return }
|
||||
if .all_or_none in info.flags { write_string(i, buf, "#all_or_none ") or_return }
|
||||
if .simple in info.flags { write_string(i, buf, "#simple ") or_return }
|
||||
if .align in info.flags {
|
||||
write_string(i, buf, "#align(") or_return
|
||||
write_u64(i, buf, u64(ti.align)) or_return
|
||||
write_string(i, buf, ") ") or_return
|
||||
}
|
||||
write_byte(i, buf, '{') or_return
|
||||
for name, j in info.names[:info.field_count] {
|
||||
if j > 0 { write_string(i, buf, ", ") or_return }
|
||||
write_string (i, buf, name) or_return
|
||||
write_string (i, buf, ": ") or_return
|
||||
write_write_type(i, buf, info.types[j]) or_return
|
||||
}
|
||||
write_byte(i, buf, '}') or_return
|
||||
|
||||
case Type_Info_Union:
|
||||
write_string(i, buf, "union ") or_return
|
||||
if info.custom_align {
|
||||
write_string(i, buf, "#align(") or_return
|
||||
write_u64 (i, buf, u64(ti.align)) or_return
|
||||
write_string(i, buf, ") ") or_return
|
||||
}
|
||||
if info.no_nil {
|
||||
write_string(i, buf, "#no_nil ") or_return
|
||||
}
|
||||
write_byte(i, buf, '{') or_return
|
||||
for variant, j in info.variants {
|
||||
if j > 0 { write_string(i, buf, ", ") or_return }
|
||||
write_write_type(i, buf, variant) or_return
|
||||
}
|
||||
write_string(i, buf, "}") or_return
|
||||
|
||||
case Type_Info_Enum:
|
||||
write_string (i, buf, "enum ") or_return
|
||||
write_write_type(i, buf, info.base) or_return
|
||||
write_string (i, buf, " {") or_return
|
||||
for name, j in info.names {
|
||||
if j > 0 { write_string(i, buf, ", ") or_return }
|
||||
write_string(i, buf, name) or_return
|
||||
}
|
||||
write_string(i, buf, "}") or_return
|
||||
|
||||
case Type_Info_Bit_Set:
|
||||
write_string(i, buf, "bit_set[") or_return
|
||||
|
||||
#partial switch elem in type_info_base(info.elem).variant {
|
||||
case Type_Info_Enum:
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
case Type_Info_Rune:
|
||||
write_encoded_rune(i, buf, rune(info.lower)) or_return
|
||||
write_string (i, buf, "..") or_return
|
||||
write_encoded_rune(i, buf, rune(info.upper)) or_return
|
||||
case:
|
||||
write_i64 (i, buf, info.lower) or_return
|
||||
write_string(i, buf, "..") or_return
|
||||
write_i64 (i, buf, info.upper) or_return
|
||||
}
|
||||
if info.underlying != nil {
|
||||
write_string (i, buf, "; ") or_return
|
||||
write_write_type(i, buf, info.underlying) or_return
|
||||
}
|
||||
write_byte(i, buf, ']') or_return
|
||||
|
||||
case Type_Info_Bit_Field:
|
||||
write_string (i, buf, "bit_field ") or_return
|
||||
write_write_type(i, buf, info.backing_type) or_return
|
||||
write_string (i, buf, " {") or_return
|
||||
for name, j in info.names[:info.field_count] {
|
||||
if j > 0 { write_string(i, buf, ", ") or_return }
|
||||
write_string (i, buf, name) or_return
|
||||
write_string (i, buf, ": ") or_return
|
||||
write_write_type(i, buf, info.types[j]) or_return
|
||||
write_string (i, buf, " | ") or_return
|
||||
write_u64 (i, buf, u64(info.bit_sizes[j])) or_return
|
||||
}
|
||||
write_byte(i, buf, '}') or_return
|
||||
|
||||
|
||||
case Type_Info_Simd_Vector:
|
||||
write_string (i, buf, "#simd[") or_return
|
||||
write_u64 (i, buf, u64(info.count)) or_return
|
||||
write_byte (i, buf, ']') or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
|
||||
case Type_Info_Matrix:
|
||||
if info.layout == .Row_Major {
|
||||
write_string(i, buf, "#row_major ") or_return
|
||||
}
|
||||
write_string (i, buf, "matrix[") or_return
|
||||
write_u64 (i, buf, u64(info.row_count)) or_return
|
||||
write_string (i, buf, ", ") or_return
|
||||
write_u64 (i, buf, u64(info.column_count)) or_return
|
||||
write_string (i, buf, "]") or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
}
|
||||
return true
|
||||
}
|
||||
56
core/container/handle_map/doc.odin
Normal file
56
core/container/handle_map/doc.odin
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
Handle-based map using fixed-length arrays.
|
||||
|
||||
Example:
|
||||
import hm "core:container/handle_map"
|
||||
|
||||
Handle :: hm.Handle32
|
||||
|
||||
Entity :: struct {
|
||||
handle: Handle,
|
||||
pos: [2]f32,
|
||||
}
|
||||
|
||||
{ // static map
|
||||
entities: hm.Static_Handle_Map(1024, Entity, Handle)
|
||||
|
||||
h1 := hm.add(&entities, Entity{pos = {1, 4}})
|
||||
h2 := hm.add(&entities, Entity{pos = {9, 16}})
|
||||
|
||||
if e, ok := hm.get(&entities, h2); ok {
|
||||
e.pos.x += 32
|
||||
}
|
||||
|
||||
hm.remove(&entities, h1)
|
||||
|
||||
h3 := hm.add(&entities, Entity{pos = {6, 7}})
|
||||
|
||||
it := hm.iterator_make(&entities)
|
||||
for e, h in hm.iterate(&it) {
|
||||
e.pos += {1, 2}
|
||||
}
|
||||
}
|
||||
|
||||
{ // dynamic map
|
||||
entities: hm.Dynamic_Handle_Map(Entity, Handle)
|
||||
hm.dynamic_init(&entities, context.allocator)
|
||||
defer hm.dynamic_destroy(&entities)
|
||||
|
||||
h1 := hm.add(&entities, Entity{pos = {1, 4}})
|
||||
h2 := hm.add(&entities, Entity{pos = {9, 16}})
|
||||
|
||||
if e, ok := hm.get(&entities, h2); ok {
|
||||
e.pos.x += 32
|
||||
}
|
||||
|
||||
hm.remove(&entities, h1)
|
||||
|
||||
h3 := hm.add(&entities, Entity{pos = {6, 7}})
|
||||
|
||||
it := hm.iterator_make(&entities)
|
||||
for e, h in hm.iterate(&it) {
|
||||
e.pos += {1, 2}
|
||||
}
|
||||
}
|
||||
*/
|
||||
package container_handle_map
|
||||
141
core/container/handle_map/dynamic_handle_map.odin
Normal file
141
core/container/handle_map/dynamic_handle_map.odin
Normal file
@@ -0,0 +1,141 @@
|
||||
package container_handle_map
|
||||
|
||||
import "base:runtime"
|
||||
import "base:builtin"
|
||||
import "base:intrinsics"
|
||||
@(require) import "core:container/xar"
|
||||
|
||||
Dynamic_Handle_Map :: struct($T: typeid, $Handle_Type: typeid)
|
||||
where
|
||||
intrinsics.type_has_field(Handle_Type, "idx"),
|
||||
intrinsics.type_has_field(Handle_Type, "gen"),
|
||||
intrinsics.type_is_unsigned(intrinsics.type_field_type(Handle_Type, "idx")),
|
||||
intrinsics.type_is_unsigned(intrinsics.type_field_type(Handle_Type, "gen")),
|
||||
intrinsics.type_field_type(Handle_Type, "idx") == intrinsics.type_field_type(Handle_Type, "gen"),
|
||||
|
||||
intrinsics.type_has_field (T, "handle"),
|
||||
intrinsics.type_field_type(T, "handle") == Handle_Type {
|
||||
|
||||
items: xar.Array(T, 4),
|
||||
unused_items: xar.Array(u32, 4),
|
||||
}
|
||||
|
||||
dynamic_init :: proc(m: ^$D/Dynamic_Handle_Map($T, $Handle_Type), allocator: runtime.Allocator) {
|
||||
xar.init(&m.items, allocator)
|
||||
xar.init(&m.unused_items, allocator)
|
||||
}
|
||||
|
||||
dynamic_destroy :: proc(m: ^$D/Dynamic_Handle_Map($T, $Handle_Type)) {
|
||||
xar.destroy(&m.unused_items)
|
||||
xar.destroy(&m.items)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
dynamic_add :: proc(m: ^$D/Dynamic_Handle_Map($T, $Handle_Type), item: T, loc := #caller_location) -> (handle: Handle_Type, err: runtime.Allocator_Error) {
|
||||
if xar.len(m.unused_items) > 0 {
|
||||
i := xar.pop(&m.unused_items)
|
||||
ptr := xar.get_ptr_unsafe(&m.items, i)
|
||||
prev_gen := ptr.handle.gen
|
||||
ptr^ = item
|
||||
|
||||
ptr.handle.idx = auto_cast i
|
||||
ptr.handle.gen = auto_cast (prev_gen + 1)
|
||||
return ptr.handle, nil
|
||||
}
|
||||
|
||||
if xar.len(m.items) == 0 {
|
||||
// initialize the zero-value sentinel
|
||||
xar.append(&m.items, T{}, loc) or_return
|
||||
}
|
||||
|
||||
i := xar.append(&m.items, item, loc) or_return
|
||||
|
||||
ptr := xar.get_ptr_unsafe(&m.items, i)
|
||||
ptr^ = item
|
||||
|
||||
ptr.handle.idx = auto_cast i
|
||||
ptr.handle.gen = 1
|
||||
return ptr.handle, nil
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
dynamic_get :: proc "contextless" (m: ^$D/Dynamic_Handle_Map($T, $Handle_Type), h: Handle_Type) -> (^T, bool) #optional_ok {
|
||||
if h.idx <= 0 || int(u32(h.idx)) >= xar.len(m.items) {
|
||||
return nil, false
|
||||
}
|
||||
if e := xar.get_ptr_unsafe(&m.items, h.idx); e.handle == h {
|
||||
return e, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
dynamic_remove :: proc(m: ^$D/Dynamic_Handle_Map($T, $Handle_Type), h: Handle_Type, loc := #caller_location) -> (found: bool, err: runtime.Allocator_Error) {
|
||||
if h.idx <= 0 || int(u32(h.idx)) >= xar.len(m.items) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if item := xar.get_ptr(&m.items, h.idx); item.handle == h {
|
||||
xar.append(&m.unused_items, u32(h.idx), loc) or_return
|
||||
item.handle.idx = 0
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
dynamic_is_valid :: proc "contextless" (m: ^$D/Dynamic_Handle_Map($T, $Handle_Type), h: Handle_Type) -> bool {
|
||||
return h.idx > 0 && int(u32(h.idx)) < xar.len(m.items) && xar.get_ptr_unsafe(&m.items, h.idx).handle == h
|
||||
}
|
||||
|
||||
// Returns the number of possibly valid items in the handle map.
|
||||
@(require_results)
|
||||
dynamic_len :: proc "contextless" (m: $D/Dynamic_Handle_Map($T, $Handle_Type)) -> uint {
|
||||
n := xar.len(m.items) - xar.len(m.unused_items)
|
||||
return uint(n-1 if n > 0 else 0)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
dynamic_cap :: proc "contextless" (m: $D/Dynamic_Handle_Map($T, $Handle_Type)) -> uint {
|
||||
n := xar.cap(m.items)
|
||||
return uint(n-1 if n > 0 else 0)
|
||||
}
|
||||
|
||||
dynamic_clear :: proc "contextless" (m: ^$D/Dynamic_Handle_Map($T, $Handle_Type)) {
|
||||
xar.clear(&m.items)
|
||||
xar.clear(&m.unused_items)
|
||||
}
|
||||
|
||||
|
||||
// An iterator for a handle map.
|
||||
Dynamic_Handle_Map_Iterator :: struct($D: typeid) {
|
||||
m: ^D,
|
||||
index: int,
|
||||
}
|
||||
|
||||
// Makes an iterator from a handle map.
|
||||
@(require_results)
|
||||
dynamic_iterator_make :: proc "contextless" (m: ^$D/Dynamic_Handle_Map($T, $Handle_Type)) -> Dynamic_Handle_Map_Iterator(D) {
|
||||
return {m, 1}
|
||||
}
|
||||
|
||||
/*
|
||||
Iterate over a handle map. It will skip over unused item slots (e.g. handle.idx == 0).
|
||||
Usage:
|
||||
it := hm.dynamic_iterator_make(&the_dynamic_handle_map)
|
||||
for item, handle in hm.iterate(&it) {
|
||||
...
|
||||
}
|
||||
*/
|
||||
@(require_results)
|
||||
dynamic_iterate :: proc "contextless" (it: ^$DHI/Dynamic_Handle_Map_Iterator($D/Dynamic_Handle_Map($T, $Handle_Type))) -> (val: ^T, h: Handle_Type, ok: bool) {
|
||||
for _ in it.index..<xar.len(it.m.items) {
|
||||
e := xar.get_ptr_unsafe(&it.m.items, it.index)
|
||||
it.index += 1
|
||||
|
||||
if e.handle.idx != 0 {
|
||||
return e, e.handle, true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
221
core/container/handle_map/static_handle_map.odin
Normal file
221
core/container/handle_map/static_handle_map.odin
Normal file
@@ -0,0 +1,221 @@
|
||||
package container_handle_map
|
||||
|
||||
import "base:builtin"
|
||||
import "base:intrinsics"
|
||||
|
||||
// Default 16-bit Handle type which can be used for handle maps which only need a maximum of 254 (1<<8 - 2) items
|
||||
Handle16 :: struct {
|
||||
idx: u8,
|
||||
gen: u8,
|
||||
}
|
||||
|
||||
// Default 32-bit Handle type which can be used for handle maps which only need a maximum of 65534 (1<<16 - 2) items
|
||||
Handle32 :: struct {
|
||||
idx: u16,
|
||||
gen: u16,
|
||||
}
|
||||
|
||||
// Default 64-bit Handle type which can be used for handle maps which only need a maximum of 4294967294 (1<<32 - 2) items
|
||||
Handle64 :: struct {
|
||||
idx: u32,
|
||||
gen: u32,
|
||||
}
|
||||
|
||||
Static_Handle_Map :: struct($N: uint, $T: typeid, $Handle_Type: typeid)
|
||||
where
|
||||
0 < N, N < uint(1<<31 - 1),
|
||||
|
||||
intrinsics.type_has_field(Handle_Type, "idx"),
|
||||
intrinsics.type_has_field(Handle_Type, "gen"),
|
||||
intrinsics.type_is_unsigned(intrinsics.type_field_type(Handle_Type, "idx")),
|
||||
intrinsics.type_is_unsigned(intrinsics.type_field_type(Handle_Type, "gen")),
|
||||
intrinsics.type_field_type(Handle_Type, "idx") == intrinsics.type_field_type(Handle_Type, "gen"),
|
||||
|
||||
N < uint(max(intrinsics.type_field_type(Handle_Type, "idx"))),
|
||||
|
||||
intrinsics.type_has_field (T, "handle"),
|
||||
intrinsics.type_field_type(T, "handle") == Handle_Type {
|
||||
|
||||
// The zero element represent a zero-value sentinel (dummy value), allowing for `idx == 0` to mean a no-handle.
|
||||
// This means the capacity is actually N-1 items.
|
||||
items: [N]T,
|
||||
|
||||
used_len: u32, // How many of the items are in use
|
||||
unused_len: u32, // Use to calculate the number of valid items
|
||||
unused_items: [N]u32,
|
||||
next_unused: u32,
|
||||
}
|
||||
|
||||
|
||||
// `add` a value of type `T` to the handle map. This will return a pointer to the item and an optional boolean to check for validity.
|
||||
@(require_results)
|
||||
static_add :: proc "contextless" (m: ^$H/Static_Handle_Map($N, $T, $Handle_Type), item: T) -> (handle: Handle_Type, ok: bool) #optional_ok {
|
||||
if i := m.next_unused; i != 0 {
|
||||
ptr := &m.items[i]
|
||||
|
||||
m.next_unused = m.unused_items[i]
|
||||
m.unused_items[i] = 0
|
||||
|
||||
prev_gen := ptr.handle.gen
|
||||
ptr^ = item
|
||||
|
||||
ptr.handle.idx = auto_cast i
|
||||
ptr.handle.gen = auto_cast (prev_gen + 1)
|
||||
m.unused_len -= 1
|
||||
return ptr.handle, true
|
||||
}
|
||||
|
||||
if m.used_len == 0 {
|
||||
// initialize the zero-value sentinel
|
||||
m.items[0] = {}
|
||||
m.used_len += 1
|
||||
}
|
||||
|
||||
if m.used_len == builtin.len(m.items) {
|
||||
return {}, false
|
||||
}
|
||||
|
||||
ptr := &m.items[m.used_len]
|
||||
ptr^ = item
|
||||
|
||||
ptr.handle.idx = auto_cast m.used_len
|
||||
ptr.handle.gen = 1
|
||||
m.used_len += 1
|
||||
return ptr.handle, true
|
||||
}
|
||||
|
||||
// `get` a stable pointer of type `^T` by resolving the handle `h`. If the handle is not valid, then `nil, false` is returned.
|
||||
@(require_results)
|
||||
static_get :: proc "contextless" (m: ^$H/Static_Handle_Map($N, $T, $Handle_Type), h: Handle_Type) -> (^T, bool) #optional_ok {
|
||||
if h.idx <= 0 || u32(h.idx) >= m.used_len {
|
||||
return nil, false
|
||||
}
|
||||
if e := &m.items[h.idx]; e.handle == h {
|
||||
return e, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// `remove` an item from the handle map from the handle `h`.
|
||||
static_remove :: proc "contextless" (m: ^$H/Static_Handle_Map($N, $T, $Handle_Type), h: Handle_Type) -> bool {
|
||||
if h.idx <= 0 || u32(h.idx) >= m.used_len {
|
||||
return false
|
||||
}
|
||||
|
||||
if item := &m.items[h.idx]; item.handle == h {
|
||||
m.unused_items[h.idx] = m.next_unused
|
||||
m.next_unused = u32(h.idx)
|
||||
m.unused_len += 1
|
||||
item.handle.idx = 0
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Returns true when the handle `h` is valid relating to the handle map.
|
||||
@(require_results)
|
||||
static_is_valid :: proc "contextless" (m: $H/Static_Handle_Map($N, $T, $Handle_Type), h: Handle_Type) -> bool {
|
||||
return h.idx > 0 && u32(h.idx) < m.used_len && m.items[h.idx].handle == h
|
||||
}
|
||||
|
||||
// Returns the number of possibly valid items in the handle map.
|
||||
@(require_results)
|
||||
static_len :: proc "contextless" (m: $H/Static_Handle_Map($N, $T, $Handle_Type)) -> uint {
|
||||
n := uint(m.used_len) - uint(m.unused_len)
|
||||
return n-1 if n > 0 else 0
|
||||
}
|
||||
|
||||
// Returns the capacity of the items in a handle map.
|
||||
// This is equivalent to `N-1` as the zero value is reserved for the zero-value sentinel.
|
||||
@(require_results)
|
||||
static_cap :: proc "contextless" (m: $H/Static_Handle_Map($N, $T, $Handle_Type)) -> uint {
|
||||
// We could just return `N` but I am doing this for clarity
|
||||
return builtin.len(m.items)-1
|
||||
}
|
||||
|
||||
// `clear` the handle map by zeroing all of the memory.
|
||||
// Internally this does not do `m^ = {}` but rather uses `intrinsics.mem_zero` explicitly improve performance.
|
||||
static_clear :: proc "contextless" (m: ^$H/Static_Handle_Map($N, $T, $Handle_Type)) {
|
||||
intrinsics.mem_zero(m, size_of(m^))
|
||||
}
|
||||
|
||||
// An iterator for a handle map.
|
||||
Static_Handle_Map_Iterator :: struct($H: typeid) {
|
||||
m: ^H,
|
||||
index: u32,
|
||||
}
|
||||
|
||||
// Makes an iterator from a handle map.
|
||||
@(require_results)
|
||||
static_iterator_make :: proc "contextless" (m: ^$H/Static_Handle_Map($N, $T, $Handle_Type)) -> Static_Handle_Map_Iterator(H) {
|
||||
return {m, 1}
|
||||
}
|
||||
|
||||
/*
|
||||
Iterate over a handle map. It will skip over unused item slots (e.g. handle.idx == 0).
|
||||
Usage:
|
||||
it := hm.iterator_make(&the_handle_map)
|
||||
for item, handle in hm.iterate(&it) {
|
||||
...
|
||||
}
|
||||
*/
|
||||
@(require_results)
|
||||
static_iterate :: proc "contextless" (it: ^$HI/Static_Handle_Map_Iterator($H/Static_Handle_Map($N, $T, $Handle_Type))) -> (val: ^T, h: Handle_Type, ok: bool) {
|
||||
for _ in it.index..<it.m.used_len {
|
||||
e := &it.m.items[it.index]
|
||||
it.index += 1
|
||||
|
||||
if e.handle.idx != 0 {
|
||||
return e, e.handle, true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
add :: proc{
|
||||
static_add,
|
||||
dynamic_add,
|
||||
}
|
||||
|
||||
get :: proc{
|
||||
static_get,
|
||||
dynamic_get,
|
||||
}
|
||||
|
||||
remove :: proc{
|
||||
static_remove,
|
||||
dynamic_remove,
|
||||
}
|
||||
|
||||
is_valid :: proc{
|
||||
static_is_valid,
|
||||
dynamic_is_valid,
|
||||
}
|
||||
|
||||
len :: proc{
|
||||
static_len,
|
||||
dynamic_len,
|
||||
}
|
||||
|
||||
cap :: proc{
|
||||
static_cap,
|
||||
dynamic_cap,
|
||||
}
|
||||
|
||||
clear :: proc{
|
||||
static_clear,
|
||||
dynamic_clear,
|
||||
}
|
||||
|
||||
iterator_make :: proc{
|
||||
static_iterator_make,
|
||||
dynamic_iterator_make,
|
||||
}
|
||||
|
||||
iterate :: proc{
|
||||
static_iterate,
|
||||
dynamic_iterate,
|
||||
}
|
||||
140
core/container/pool/pool.odin
Normal file
140
core/container/pool/pool.odin
Normal file
@@ -0,0 +1,140 @@
|
||||
package container_pool
|
||||
|
||||
import "base:intrinsics"
|
||||
import "base:sanitizer"
|
||||
|
||||
import "core:mem"
|
||||
import "core:sync"
|
||||
|
||||
_ :: sanitizer
|
||||
|
||||
DEFAULT_BLOCK_SIZE :: _DEFAULT_BLOCK_SIZE
|
||||
|
||||
Pool_Arena :: _Pool_Arena
|
||||
|
||||
/*
|
||||
A thread-safe (between init and destroy) object pool backed by virtual growing arena returning stable pointers.
|
||||
The element type requires an intrusive link node.
|
||||
|
||||
Example:
|
||||
Elem :: struct {
|
||||
link: ^Elem,
|
||||
}
|
||||
|
||||
p: pool.Pool(Elem)
|
||||
pool.init(&p, "link")
|
||||
*/
|
||||
Pool :: struct($T: typeid) {
|
||||
arena: Pool_Arena,
|
||||
num_outstanding: int,
|
||||
num_ready: int,
|
||||
link_off: uintptr,
|
||||
free_list: ^T,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
init :: proc(p: ^Pool($T), $link_field: string, block_size: uint = DEFAULT_BLOCK_SIZE) -> (err: mem.Allocator_Error)
|
||||
where intrinsics.type_has_field(T, link_field),
|
||||
intrinsics.type_field_type(T, link_field) == ^T {
|
||||
p.link_off = offset_of_by_string(T, link_field)
|
||||
return _pool_arena_init(&p.arena, block_size)
|
||||
}
|
||||
|
||||
destroy :: proc(p: ^Pool($T)) {
|
||||
elem := sync.atomic_exchange_explicit(&p.free_list, nil, .Acquire)
|
||||
|
||||
sync.atomic_store_explicit(&p.num_ready, 0, .Relaxed)
|
||||
|
||||
when .Address in ODIN_SANITIZER_FLAGS {
|
||||
for ; elem != nil; elem = _get_next(p, elem) {
|
||||
_unpoison_elem(p, elem)
|
||||
}
|
||||
} else {
|
||||
_ = elem
|
||||
}
|
||||
|
||||
_pool_arena_destroy(&p.arena)
|
||||
p.arena = {}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get :: proc(p: ^Pool($T)) -> (elem: ^T, err: mem.Allocator_Error) #optional_allocator_error {
|
||||
defer sync.atomic_add_explicit(&p.num_outstanding, 1, .Relaxed)
|
||||
|
||||
for {
|
||||
elem = sync.atomic_load_explicit(&p.free_list, .Acquire)
|
||||
if elem == nil {
|
||||
// NOTE: pool arena has an internal lock.
|
||||
return new(T, _pool_arena_allocator(&p.arena))
|
||||
}
|
||||
|
||||
if _, ok := sync.atomic_compare_exchange_weak_explicit(&p.free_list, elem, _get_next(p, elem), .Acquire, .Relaxed); ok {
|
||||
_set_next(p, elem, nil)
|
||||
_unpoison_elem(p, elem)
|
||||
sync.atomic_sub_explicit(&p.num_ready, 1, .Relaxed)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
put :: proc(p: ^Pool($T), elem: ^T) {
|
||||
mem.zero_item(elem)
|
||||
_poison_elem(p, elem)
|
||||
|
||||
defer sync.atomic_sub_explicit(&p.num_outstanding, 1, .Relaxed)
|
||||
defer sync.atomic_add_explicit(&p.num_ready, 1, .Relaxed)
|
||||
|
||||
for {
|
||||
head := sync.atomic_load_explicit(&p.free_list, .Relaxed)
|
||||
_set_next(p, elem, head)
|
||||
if _, ok := sync.atomic_compare_exchange_weak_explicit(&p.free_list, head, elem, .Release, .Relaxed); ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
num_outstanding :: proc(p: ^Pool($T)) -> int {
|
||||
return sync.atomic_load(&p.num_outstanding)
|
||||
}
|
||||
|
||||
num_ready :: proc(p: ^Pool($T)) -> int {
|
||||
return sync.atomic_load(&p.num_ready)
|
||||
}
|
||||
|
||||
cap :: proc(p: ^Pool($T)) -> int {
|
||||
return sync.atomic_load(&p.num_ready) + sync.atomic_load(&p.num_outstanding)
|
||||
}
|
||||
|
||||
_get_next :: proc(p: ^Pool($T), elem: ^T) -> ^T {
|
||||
return (^^T)(uintptr(elem) + p.link_off)^
|
||||
}
|
||||
|
||||
_set_next :: proc(p: ^Pool($T), elem: ^T, next: ^T) {
|
||||
(^^T)(uintptr(elem) + p.link_off)^ = next
|
||||
}
|
||||
|
||||
@(disabled=.Address not_in ODIN_SANITIZER_FLAGS)
|
||||
_poison_elem :: proc(p: ^Pool($T), elem: ^T) {
|
||||
if p.link_off > 0 {
|
||||
sanitizer.address_poison_rawptr(elem, int(p.link_off))
|
||||
}
|
||||
|
||||
len := size_of(T) - p.link_off - size_of(rawptr)
|
||||
if len > 0 {
|
||||
ptr := rawptr(uintptr(elem) + p.link_off + size_of(rawptr))
|
||||
sanitizer.address_poison_rawptr(ptr, int(len))
|
||||
}
|
||||
}
|
||||
|
||||
@(disabled=.Address not_in ODIN_SANITIZER_FLAGS)
|
||||
_unpoison_elem :: proc(p: ^Pool($T), elem: ^T) {
|
||||
if p.link_off > 0 {
|
||||
sanitizer.address_unpoison_rawptr(elem, int(p.link_off))
|
||||
}
|
||||
|
||||
len := size_of(T) - p.link_off - size_of(rawptr)
|
||||
if len > 0 {
|
||||
ptr := rawptr(uintptr(elem) + p.link_off + size_of(rawptr))
|
||||
sanitizer.address_unpoison_rawptr(ptr, int(len))
|
||||
}
|
||||
}
|
||||
29
core/container/pool/pool_arena_others.odin
Normal file
29
core/container/pool/pool_arena_others.odin
Normal file
@@ -0,0 +1,29 @@
|
||||
#+build !darwin
|
||||
#+build !freebsd
|
||||
#+build !openbsd
|
||||
#+build !netbsd
|
||||
#+build !linux
|
||||
#+build !windows
|
||||
#+private
|
||||
package container_pool
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
import "core:mem"
|
||||
|
||||
_Pool_Arena :: runtime.Arena
|
||||
|
||||
_DEFAULT_BLOCK_SIZE :: mem.Megabyte
|
||||
|
||||
_pool_arena_init :: proc(arena: ^Pool_Arena, block_size: uint = DEFAULT_BLOCK_SIZE) -> (err: runtime.Allocator_Error) {
|
||||
runtime.arena_init(arena, block_size, runtime.default_allocator()) or_return
|
||||
return
|
||||
}
|
||||
|
||||
_pool_arena_allocator :: proc(arena: ^Pool_Arena) -> runtime.Allocator {
|
||||
return runtime.arena_allocator(arena)
|
||||
}
|
||||
|
||||
_pool_arena_destroy :: proc(arena: ^Pool_Arena) {
|
||||
runtime.arena_destroy(arena)
|
||||
}
|
||||
24
core/container/pool/pool_arena_virtual.odin
Normal file
24
core/container/pool/pool_arena_virtual.odin
Normal file
@@ -0,0 +1,24 @@
|
||||
#+build darwin, freebsd, openbsd, netbsd, linux, windows
|
||||
package container_pool
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
import "core:mem"
|
||||
import "core:mem/virtual"
|
||||
|
||||
_Pool_Arena :: virtual.Arena
|
||||
|
||||
_DEFAULT_BLOCK_SIZE :: mem.Gigabyte
|
||||
|
||||
_pool_arena_init :: proc(arena: ^Pool_Arena, block_size: uint = DEFAULT_BLOCK_SIZE) -> (err: runtime.Allocator_Error) {
|
||||
virtual.arena_init_growing(arena, block_size) or_return
|
||||
return
|
||||
}
|
||||
|
||||
_pool_arena_allocator :: proc(arena: ^Pool_Arena) -> runtime.Allocator {
|
||||
return virtual.arena_allocator(arena)
|
||||
}
|
||||
|
||||
_pool_arena_destroy :: proc(arena: ^Pool_Arena) {
|
||||
virtual.arena_destroy(arena)
|
||||
}
|
||||
@@ -91,7 +91,7 @@ destroy :: proc(t: ^$T/Tree($Key, $Value), call_on_remove: bool = true) {
|
||||
}
|
||||
}
|
||||
|
||||
len :: proc "contextless" (t: ^$T/Tree($Key, $Value)) -> (node_count: int) {
|
||||
len :: proc "contextless" (t: $T/Tree($Key, $Value)) -> (node_count: int) {
|
||||
return t._size
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ last :: proc "contextless" (t: ^$T/Tree($Key, $Value)) -> ^Node(Key, Value) {
|
||||
}
|
||||
|
||||
// find finds the key in the tree, and returns the corresponding node, or nil iff the value is not present.
|
||||
find :: proc(t: ^$T/Tree($Key, $Value), key: Key) -> (node: ^Node(Key, Value)) {
|
||||
find :: proc(t: $T/Tree($Key, $Value), key: Key) -> (node: ^Node(Key, Value)) {
|
||||
node = t._root
|
||||
for node != nil {
|
||||
switch t._cmp_fn(key, node.key) {
|
||||
@@ -121,7 +121,7 @@ find :: proc(t: ^$T/Tree($Key, $Value), key: Key) -> (node: ^Node(Key, Value)) {
|
||||
}
|
||||
|
||||
// find_value finds the key in the tree, and returns the corresponding value, or nil iff the value is not present.
|
||||
find_value :: proc(t: ^$T/Tree($Key, $Value), key: Key) -> (value: Value, ok: bool) #optional_ok {
|
||||
find_value :: proc(t: $T/Tree($Key, $Value), key: Key) -> (value: Value, ok: bool) #optional_ok {
|
||||
if n := find(t, key); n != nil {
|
||||
return n.value, true
|
||||
}
|
||||
@@ -166,7 +166,7 @@ remove :: proc {
|
||||
// removal was successful. While the node's key + value will be left intact,
|
||||
// the node itself will be freed via the tree's node allocator.
|
||||
remove_key :: proc(t: ^$T/Tree($Key, $Value), key: Key, call_on_remove := true) -> bool {
|
||||
n := find(t, key)
|
||||
n := find(t^, key)
|
||||
if n == nil {
|
||||
return false // Key not found, nothing to do
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import "core:container/xar"
|
||||
|
||||
example :: proc() {
|
||||
x: xar.Xar(int, 4)
|
||||
x: xar.Array(int, 4)
|
||||
defer xar.destroy(&x)
|
||||
|
||||
xar.push_back(&x, 10)
|
||||
@@ -38,7 +38,7 @@ MAX_SHIFT :: PLATFORM_BITS>>1
|
||||
/*
|
||||
An Exponential Array with stable element addresses.
|
||||
|
||||
Unlike `[dynamic]T` which reallocates and moves elements when growing, `Xar`
|
||||
Unlike `[dynamic]T` which reallocates and moves elements when growing, `Array`
|
||||
allocates separate chunks of exponentially increasing size. This guarantees
|
||||
that pointers to elements remain valid for the lifetime of the container.
|
||||
|
||||
@@ -66,11 +66,11 @@ MAX_SHIFT :: PLATFORM_BITS>>1
|
||||
|
||||
example :: proc() {
|
||||
// Xar with initial chunk size of 16 (1 << 4)
|
||||
x: xar.Xar(My_Struct, 4)
|
||||
x: xar.Array(My_Struct, 4)
|
||||
defer xar.destroy(&x)
|
||||
}
|
||||
*/
|
||||
Xar :: struct($T: typeid, $SHIFT: uint) where 0 < SHIFT, SHIFT <= MAX_SHIFT {
|
||||
Array :: struct($T: typeid, $SHIFT: uint) where 0 < SHIFT, SHIFT <= MAX_SHIFT {
|
||||
chunks: [(1 << (_LOG2_PLATFORM_BITS - intrinsics.constant_log2(SHIFT))) + 1][^]T,
|
||||
len: int,
|
||||
allocator: mem.Allocator,
|
||||
@@ -84,7 +84,7 @@ Initializes an exponential array with the given allocator.
|
||||
- `x`: Pointer to the exponential array to initialize
|
||||
- `allocator`: Allocator to use for chunk allocations (defaults to context.allocator)
|
||||
*/
|
||||
init :: proc(x: ^$X/Xar($T, $SHIFT), allocator := context.allocator) {
|
||||
init :: proc(x: ^$X/Array($T, $SHIFT), allocator := context.allocator) {
|
||||
x^ = {allocator = allocator}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ Frees all allocated chunks and resets the exponential array.
|
||||
**Inputs**
|
||||
- `x`: Pointer to the exponential array to destroy
|
||||
*/
|
||||
destroy :: proc(x: ^$X/Xar($T, $SHIFT)) {
|
||||
destroy :: proc(x: ^$X/Array($T, $SHIFT)) {
|
||||
#reverse for c, i in x.chunks {
|
||||
if c != nil {
|
||||
n := 1 << (SHIFT + uint(i if i > 0 else 1) - 1)
|
||||
@@ -109,19 +109,19 @@ destroy :: proc(x: ^$X/Xar($T, $SHIFT)) {
|
||||
Resets the array's length to zero without freeing memory.
|
||||
Allocated chunks are retained for reuse.
|
||||
*/
|
||||
clear :: proc(x: ^$X/Xar($T, $SHIFT)) {
|
||||
clear :: proc "contextless" (x: ^$X/Array($T, $SHIFT)) {
|
||||
x.len = 0
|
||||
}
|
||||
|
||||
// Returns the length of the exponential-array
|
||||
@(require_results)
|
||||
len :: proc(x: $X/Xar($T, $SHIFT)) -> int {
|
||||
len :: proc "contextless" (x: $X/Array($T, $SHIFT)) -> int {
|
||||
return x.len
|
||||
}
|
||||
|
||||
// Returns the number of allocated elements
|
||||
@(require_results)
|
||||
cap :: proc(x: $X/Xar($T, $SHIFT)) -> int {
|
||||
cap :: proc "contextless" (x: $X/Array($T, $SHIFT)) -> int {
|
||||
#reverse for c, i in x.chunks {
|
||||
if c != nil {
|
||||
return 1 << (SHIFT + uint(i if i > 0 else 1))
|
||||
@@ -132,7 +132,7 @@ cap :: proc(x: $X/Xar($T, $SHIFT)) -> int {
|
||||
|
||||
// Internal: computes chunk index, element index within chunk, and chunk capacity for a given index.
|
||||
@(require_results)
|
||||
_meta_get :: #force_inline proc($SHIFT: uint, index: uint) -> (chunk_idx, elem_idx, chunk_cap: uint) {
|
||||
_meta_get :: #force_inline proc "contextless" ($SHIFT: uint, index: uint) -> (chunk_idx, elem_idx, chunk_cap: uint) {
|
||||
elem_idx = index
|
||||
chunk_cap = uint(1) << SHIFT
|
||||
chunk_idx = 0
|
||||
@@ -161,7 +161,7 @@ Get a copy of the element at the specified index.
|
||||
- a copy of the element
|
||||
*/
|
||||
@(require_results)
|
||||
get :: proc(x: ^$X/Xar($T, $SHIFT), #any_int index: int, loc := #caller_location) -> (val: T) #no_bounds_check {
|
||||
get :: proc(x: ^$X/Array($T, $SHIFT), #any_int index: int, loc := #caller_location) -> (val: T) #no_bounds_check {
|
||||
runtime.bounds_check_error_loc(loc, index, x.len)
|
||||
chunk_idx, elem_idx, _ := _meta_get(SHIFT, uint(index))
|
||||
return x.chunks[chunk_idx][elem_idx]
|
||||
@@ -185,7 +185,7 @@ Example:
|
||||
import "core:container/xar"
|
||||
|
||||
get_ptr_example :: proc() {
|
||||
x: xar.Xar(int, 4)
|
||||
x: xar.Array(int, 4)
|
||||
defer xar.destroy(&x)
|
||||
|
||||
xar.push_back(&x, 100)
|
||||
@@ -200,12 +200,19 @@ Example:
|
||||
}
|
||||
*/
|
||||
@(require_results)
|
||||
get_ptr :: proc(x: ^$X/Xar($T, $SHIFT), #any_int index: int, loc := #caller_location) -> (val: ^T) #no_bounds_check {
|
||||
get_ptr :: proc(x: ^$X/Array($T, $SHIFT), #any_int index: int, loc := #caller_location) -> (val: ^T) #no_bounds_check {
|
||||
runtime.bounds_check_error_loc(loc, index, x.len)
|
||||
chunk_idx, elem_idx, _ := _meta_get(SHIFT, uint(index))
|
||||
return &x.chunks[chunk_idx][elem_idx]
|
||||
}
|
||||
|
||||
// No bounds checking
|
||||
@(require_results)
|
||||
get_ptr_unsafe :: proc "contextless" (x: ^$X/Array($T, $SHIFT), #any_int index: int) -> (val: ^T) #no_bounds_check {
|
||||
chunk_idx, elem_idx, _ := _meta_get(SHIFT, uint(index))
|
||||
return &x.chunks[chunk_idx][elem_idx]
|
||||
}
|
||||
|
||||
/*
|
||||
Set the element at the specified index to the given value.
|
||||
|
||||
@@ -214,7 +221,7 @@ Set the element at the specified index to the given value.
|
||||
- `index`: Position of the element (0-indexed)
|
||||
- `value`: The value to set
|
||||
*/
|
||||
set :: proc(x: ^$X/Xar($T, $SHIFT), #any_int index: int, value: T, loc := #caller_location) #no_bounds_check {
|
||||
set :: proc(x: ^$X/Array($T, $SHIFT), #any_int index: int, value: T, loc := #caller_location) #no_bounds_check {
|
||||
runtime.bounds_check_error_loc(loc, index, x.len)
|
||||
chunk_idx, elem_idx, _ := _meta_get(SHIFT, uint(index))
|
||||
x.chunks[chunk_idx][elem_idx] = value
|
||||
@@ -240,7 +247,7 @@ Example:
|
||||
import "core:container/xar"
|
||||
|
||||
push_back_example :: proc() {
|
||||
x: xar.Xar(string, 4)
|
||||
x: xar.Array(string, 4)
|
||||
defer xar.destroy(&x)
|
||||
|
||||
xar.push_back(&x, "hello")
|
||||
@@ -250,7 +257,7 @@ Example:
|
||||
fmt.println(xar.get(&x, 1)) // world
|
||||
}
|
||||
*/
|
||||
push_back_elem :: proc(x: ^$X/Xar($T, $SHIFT), value: T, loc := #caller_location) -> (n: int, err: mem.Allocator_Error) {
|
||||
push_back_elem :: proc(x: ^$X/Array($T, $SHIFT), value: T, loc := #caller_location) -> (n: int, err: mem.Allocator_Error) {
|
||||
if x.allocator.procedure == nil {
|
||||
// to minic `[dynamic]T` behaviour
|
||||
x.allocator = context.allocator
|
||||
@@ -277,7 +284,7 @@ Append multiple elements to the end of the exponential array.
|
||||
- number of elements successfully added
|
||||
- allocation error if chunk allocation failed (partial append possible)
|
||||
*/
|
||||
push_back_elems :: proc(x: ^$X/Xar($T, $SHIFT), values: ..T, loc := #caller_location) -> (n: int, err: mem.Allocator_Error) {
|
||||
push_back_elems :: proc(x: ^$X/Array($T, $SHIFT), values: ..T, loc := #caller_location) -> (n: int, err: mem.Allocator_Error) {
|
||||
for value in values {
|
||||
n += push_back_elem(x, value, loc) or_return
|
||||
}
|
||||
@@ -303,7 +310,7 @@ Example:
|
||||
import "core:container/xar"
|
||||
|
||||
push_back_and_get_ptr_example :: proc() {
|
||||
x: xar.Xar(My_Struct, 4)
|
||||
x: xar.Array(My_Struct, 4)
|
||||
defer xar.destroy(&x)
|
||||
|
||||
ptr := xar.push_back_elem_and_get_ptr(&x, My_Struct{}) or_else panic("alloc failed")
|
||||
@@ -311,7 +318,7 @@ Example:
|
||||
}
|
||||
*/
|
||||
@(require_results)
|
||||
push_back_elem_and_get_ptr :: proc(x: ^$X/Xar($T, $SHIFT), value: T, loc := #caller_location) -> (ptr: ^T, err: mem.Allocator_Error) {
|
||||
push_back_elem_and_get_ptr :: proc(x: ^$X/Array($T, $SHIFT), value: T, loc := #caller_location) -> (ptr: ^T, err: mem.Allocator_Error) {
|
||||
if x.allocator.procedure == nil {
|
||||
// to minic `[dynamic]T` behaviour
|
||||
x.allocator = context.allocator
|
||||
@@ -323,7 +330,6 @@ push_back_elem_and_get_ptr :: proc(x: ^$X/Xar($T, $SHIFT), value: T, loc := #cal
|
||||
}
|
||||
x.chunks[chunk_idx][elem_idx] = value
|
||||
x.len += 1
|
||||
n = 1
|
||||
ptr = &x.chunks[chunk_idx][elem_idx]
|
||||
return
|
||||
}
|
||||
@@ -331,7 +337,7 @@ push_back_elem_and_get_ptr :: proc(x: ^$X/Xar($T, $SHIFT), value: T, loc := #cal
|
||||
// `pop` will remove and return the end value of an exponential array `x` and reduces the length of the array by 1.
|
||||
//
|
||||
// Note: If the exponential array has no elements (`xar.len(x) == 0`), this procedure will panic.
|
||||
pop :: proc(x: ^$X/Xar($T, $SHIFT), loc := #caller_location) -> (val: T) {
|
||||
pop :: proc(x: ^$X/Array($T, $SHIFT), loc := #caller_location) -> (val: T) {
|
||||
assert(x.len > 0, loc=loc)
|
||||
index := uint(x.len-1)
|
||||
chunk_idx, elem_idx, _ := _meta_get(SHIFT, index)
|
||||
@@ -342,7 +348,7 @@ pop :: proc(x: ^$X/Xar($T, $SHIFT), loc := #caller_location) -> (val: T) {
|
||||
// `pop_safe` trys to remove and return the end value of dynamic array `x` and reduces the length of the array by 1.
|
||||
// If the operation is not possible, it will return false.
|
||||
@(require_results)
|
||||
pop_safe :: proc(x: ^$X/Xar($T, $SHIFT)) -> (val: T, ok: bool) {
|
||||
pop_safe :: proc(x: ^$X/Array($T, $SHIFT)) -> (val: T, ok: bool) {
|
||||
if x.len == 0 {
|
||||
return
|
||||
}
|
||||
@@ -370,7 +376,7 @@ pop_safe :: proc(x: ^$X/Xar($T, $SHIFT)) -> (val: T, ok: bool) {
|
||||
import "core:encoding/xar"
|
||||
|
||||
unordered_remove_example :: proc() {
|
||||
x: xar.Xar(int, 4)
|
||||
x: xar.Array(int, 4)
|
||||
defer xar.destroy(&x)
|
||||
|
||||
xar.push_back(&x, 10)
|
||||
@@ -384,7 +390,7 @@ pop_safe :: proc(x: ^$X/Xar($T, $SHIFT)) -> (val: T, ok: bool) {
|
||||
fmt.println(xar.get(&x, 1)) // 20
|
||||
}
|
||||
*/
|
||||
unordered_remove :: proc(x: ^$X/Xar($T, $SHIFT), #any_int index: int, loc := #caller_location) {
|
||||
unordered_remove :: proc(x: ^$X/Array($T, $SHIFT), #any_int index: int, loc := #caller_location) {
|
||||
runtime.bounds_check_error_loc(loc, index, x.len)
|
||||
n := x.len-1
|
||||
if index != n {
|
||||
@@ -403,7 +409,7 @@ Fields:
|
||||
- `idx`: Current iteration index
|
||||
*/
|
||||
Iterator :: struct($T: typeid, $SHIFT: uint) {
|
||||
xar: ^Xar(T, SHIFT),
|
||||
xar: ^Array(T, SHIFT),
|
||||
idx: int,
|
||||
}
|
||||
|
||||
@@ -418,10 +424,11 @@ Create an iterator for traversing the exponential array.
|
||||
|
||||
Example:
|
||||
|
||||
import "lib:xar"
|
||||
import "core:container/xar"
|
||||
import "core:fmt"
|
||||
|
||||
iteration_example :: proc() {
|
||||
x: xar.Xar(int, 4)
|
||||
iterator_example :: proc() {
|
||||
x: xar.Array(int, 4)
|
||||
defer xar.destroy(&x)
|
||||
|
||||
xar.push_back(&x, 10)
|
||||
@@ -440,7 +447,7 @@ Output:
|
||||
20
|
||||
30
|
||||
*/
|
||||
iterator :: proc(xar: ^$X/Xar($T, $SHIFT)) -> Iterator(T, SHIFT) {
|
||||
iterator :: proc(xar: ^$X/Array($T, $SHIFT)) -> Iterator(T, SHIFT) {
|
||||
return {xar = auto_cast xar, idx = 0}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,133 +4,68 @@ import "core:crypto/_chacha20"
|
||||
import "core:encoding/endian"
|
||||
import "core:math/bits"
|
||||
|
||||
// At least with LLVM21 force_inline produces identical perf to
|
||||
// manual inlining, yay.
|
||||
@(private)
|
||||
quarter_round :: #force_inline proc "contextless" (a, b, c, d: u32) -> (u32, u32, u32, u32) {
|
||||
a, b, c, d := a, b, c, d
|
||||
|
||||
a += b
|
||||
d ~= a
|
||||
d = bits.rotate_left32(d, 16)
|
||||
|
||||
c += d
|
||||
b ~= c
|
||||
b = bits.rotate_left32(b, 12)
|
||||
|
||||
a += b
|
||||
d ~= a
|
||||
d = bits.rotate_left32(d, 8)
|
||||
|
||||
c += d
|
||||
b ~= c
|
||||
b = bits.rotate_left32(b, 7)
|
||||
|
||||
return a, b, c, d
|
||||
}
|
||||
|
||||
stream_blocks :: proc(ctx: ^_chacha20.Context, dst, src: []byte, nr_blocks: int) {
|
||||
// Enforce the maximum consumed keystream per IV.
|
||||
_chacha20.check_counter_limit(ctx, nr_blocks)
|
||||
|
||||
dst, src := dst, src
|
||||
x := &ctx._s
|
||||
|
||||
|
||||
// Filippo Valsorda made an observation that only one of the column
|
||||
// round depends on the counter (s12), so it is worth precomputing
|
||||
// and reusing across multiple blocks. As far as I know, only Go's
|
||||
// chacha implementation does this.
|
||||
|
||||
p1, p5, p9, p13 := quarter_round(_chacha20.SIGMA_1, x[5], x[9], x[13])
|
||||
p2, p6, p10, p14 := quarter_round(_chacha20.SIGMA_2, x[6], x[10], x[14])
|
||||
p3, p7, p11, p15 := quarter_round(_chacha20.SIGMA_3, x[7], x[11], x[15])
|
||||
|
||||
for n := 0; n < nr_blocks; n = n + 1 {
|
||||
x0, x1, x2, x3 :=
|
||||
_chacha20.SIGMA_0, _chacha20.SIGMA_1, _chacha20.SIGMA_2, _chacha20.SIGMA_3
|
||||
x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15 :=
|
||||
x[4], x[5], x[6], x[7], x[8], x[9], x[10], x[11], x[12], x[13], x[14], x[15]
|
||||
// First column round that depends on the counter
|
||||
p0, p4, p8, p12 := quarter_round(_chacha20.SIGMA_0, x[4], x[8], x[12])
|
||||
|
||||
for i := _chacha20.ROUNDS; i > 0; i = i - 2 {
|
||||
// Even when forcing inlining manually inlining all of
|
||||
// these is decently faster.
|
||||
// First diagonal round
|
||||
x0, x5, x10, x15 := quarter_round(p0, p5, p10, p15)
|
||||
x1, x6, x11, x12 := quarter_round(p1, p6, p11, p12)
|
||||
x2, x7, x8, x13 := quarter_round(p2, p7, p8, p13)
|
||||
x3, x4, x9, x14 := quarter_round(p3, p4, p9, p14)
|
||||
|
||||
// quarterround(x, 0, 4, 8, 12)
|
||||
x0 += x4
|
||||
x12 ~= x0
|
||||
x12 = bits.rotate_left32(x12, 16)
|
||||
x8 += x12
|
||||
x4 ~= x8
|
||||
x4 = bits.rotate_left32(x4, 12)
|
||||
x0 += x4
|
||||
x12 ~= x0
|
||||
x12 = bits.rotate_left32(x12, 8)
|
||||
x8 += x12
|
||||
x4 ~= x8
|
||||
x4 = bits.rotate_left32(x4, 7)
|
||||
for i := _chacha20.ROUNDS - 2; i > 0; i = i - 2 {
|
||||
x0, x4, x8, x12 = quarter_round(x0, x4, x8, x12)
|
||||
x1, x5, x9, x13 = quarter_round(x1, x5, x9, x13)
|
||||
x2, x6, x10, x14 = quarter_round(x2, x6, x10, x14)
|
||||
x3, x7, x11, x15 = quarter_round(x3, x7, x11, x15)
|
||||
|
||||
// quarterround(x, 1, 5, 9, 13)
|
||||
x1 += x5
|
||||
x13 ~= x1
|
||||
x13 = bits.rotate_left32(x13, 16)
|
||||
x9 += x13
|
||||
x5 ~= x9
|
||||
x5 = bits.rotate_left32(x5, 12)
|
||||
x1 += x5
|
||||
x13 ~= x1
|
||||
x13 = bits.rotate_left32(x13, 8)
|
||||
x9 += x13
|
||||
x5 ~= x9
|
||||
x5 = bits.rotate_left32(x5, 7)
|
||||
|
||||
// quarterround(x, 2, 6, 10, 14)
|
||||
x2 += x6
|
||||
x14 ~= x2
|
||||
x14 = bits.rotate_left32(x14, 16)
|
||||
x10 += x14
|
||||
x6 ~= x10
|
||||
x6 = bits.rotate_left32(x6, 12)
|
||||
x2 += x6
|
||||
x14 ~= x2
|
||||
x14 = bits.rotate_left32(x14, 8)
|
||||
x10 += x14
|
||||
x6 ~= x10
|
||||
x6 = bits.rotate_left32(x6, 7)
|
||||
|
||||
// quarterround(x, 3, 7, 11, 15)
|
||||
x3 += x7
|
||||
x15 ~= x3
|
||||
x15 = bits.rotate_left32(x15, 16)
|
||||
x11 += x15
|
||||
x7 ~= x11
|
||||
x7 = bits.rotate_left32(x7, 12)
|
||||
x3 += x7
|
||||
x15 ~= x3
|
||||
x15 = bits.rotate_left32(x15, 8)
|
||||
x11 += x15
|
||||
x7 ~= x11
|
||||
x7 = bits.rotate_left32(x7, 7)
|
||||
|
||||
// quarterround(x, 0, 5, 10, 15)
|
||||
x0 += x5
|
||||
x15 ~= x0
|
||||
x15 = bits.rotate_left32(x15, 16)
|
||||
x10 += x15
|
||||
x5 ~= x10
|
||||
x5 = bits.rotate_left32(x5, 12)
|
||||
x0 += x5
|
||||
x15 ~= x0
|
||||
x15 = bits.rotate_left32(x15, 8)
|
||||
x10 += x15
|
||||
x5 ~= x10
|
||||
x5 = bits.rotate_left32(x5, 7)
|
||||
|
||||
// quarterround(x, 1, 6, 11, 12)
|
||||
x1 += x6
|
||||
x12 ~= x1
|
||||
x12 = bits.rotate_left32(x12, 16)
|
||||
x11 += x12
|
||||
x6 ~= x11
|
||||
x6 = bits.rotate_left32(x6, 12)
|
||||
x1 += x6
|
||||
x12 ~= x1
|
||||
x12 = bits.rotate_left32(x12, 8)
|
||||
x11 += x12
|
||||
x6 ~= x11
|
||||
x6 = bits.rotate_left32(x6, 7)
|
||||
|
||||
// quarterround(x, 2, 7, 8, 13)
|
||||
x2 += x7
|
||||
x13 ~= x2
|
||||
x13 = bits.rotate_left32(x13, 16)
|
||||
x8 += x13
|
||||
x7 ~= x8
|
||||
x7 = bits.rotate_left32(x7, 12)
|
||||
x2 += x7
|
||||
x13 ~= x2
|
||||
x13 = bits.rotate_left32(x13, 8)
|
||||
x8 += x13
|
||||
x7 ~= x8
|
||||
x7 = bits.rotate_left32(x7, 7)
|
||||
|
||||
// quarterround(x, 3, 4, 9, 14)
|
||||
x3 += x4
|
||||
x14 ~= x3
|
||||
x14 = bits.rotate_left32(x14, 16)
|
||||
x9 += x14
|
||||
x4 ~= x9
|
||||
x4 = bits.rotate_left32(x4, 12)
|
||||
x3 += x4
|
||||
x14 ~= x3
|
||||
x14 = bits.rotate_left32(x14, 8)
|
||||
x9 += x14
|
||||
x4 ~= x9
|
||||
x4 = bits.rotate_left32(x4, 7)
|
||||
x0, x5, x10, x15 = quarter_round(x0, x5, x10, x15)
|
||||
x1, x6, x11, x12 = quarter_round(x1, x6, x11, x12)
|
||||
x2, x7, x8, x13 = quarter_round(x2, x7, x8, x13)
|
||||
x3, x4, x9, x14 = quarter_round(x3, x4, x9, x14)
|
||||
}
|
||||
|
||||
x0 += _chacha20.SIGMA_0
|
||||
@@ -236,117 +171,15 @@ hchacha20 :: proc "contextless" (dst, key, iv: []byte) {
|
||||
x15 := endian.unchecked_get_u32le(iv[12:16])
|
||||
|
||||
for i := _chacha20.ROUNDS; i > 0; i = i - 2 {
|
||||
// quarterround(x, 0, 4, 8, 12)
|
||||
x0 += x4
|
||||
x12 ~= x0
|
||||
x12 = bits.rotate_left32(x12, 16)
|
||||
x8 += x12
|
||||
x4 ~= x8
|
||||
x4 = bits.rotate_left32(x4, 12)
|
||||
x0 += x4
|
||||
x12 ~= x0
|
||||
x12 = bits.rotate_left32(x12, 8)
|
||||
x8 += x12
|
||||
x4 ~= x8
|
||||
x4 = bits.rotate_left32(x4, 7)
|
||||
x0, x4, x8, x12 = quarter_round(x0, x4, x8, x12)
|
||||
x1, x5, x9, x13 = quarter_round(x1, x5, x9, x13)
|
||||
x2, x6, x10, x14 = quarter_round(x2, x6, x10, x14)
|
||||
x3, x7, x11, x15 = quarter_round(x3, x7, x11, x15)
|
||||
|
||||
// quarterround(x, 1, 5, 9, 13)
|
||||
x1 += x5
|
||||
x13 ~= x1
|
||||
x13 = bits.rotate_left32(x13, 16)
|
||||
x9 += x13
|
||||
x5 ~= x9
|
||||
x5 = bits.rotate_left32(x5, 12)
|
||||
x1 += x5
|
||||
x13 ~= x1
|
||||
x13 = bits.rotate_left32(x13, 8)
|
||||
x9 += x13
|
||||
x5 ~= x9
|
||||
x5 = bits.rotate_left32(x5, 7)
|
||||
|
||||
// quarterround(x, 2, 6, 10, 14)
|
||||
x2 += x6
|
||||
x14 ~= x2
|
||||
x14 = bits.rotate_left32(x14, 16)
|
||||
x10 += x14
|
||||
x6 ~= x10
|
||||
x6 = bits.rotate_left32(x6, 12)
|
||||
x2 += x6
|
||||
x14 ~= x2
|
||||
x14 = bits.rotate_left32(x14, 8)
|
||||
x10 += x14
|
||||
x6 ~= x10
|
||||
x6 = bits.rotate_left32(x6, 7)
|
||||
|
||||
// quarterround(x, 3, 7, 11, 15)
|
||||
x3 += x7
|
||||
x15 ~= x3
|
||||
x15 = bits.rotate_left32(x15, 16)
|
||||
x11 += x15
|
||||
x7 ~= x11
|
||||
x7 = bits.rotate_left32(x7, 12)
|
||||
x3 += x7
|
||||
x15 ~= x3
|
||||
x15 = bits.rotate_left32(x15, 8)
|
||||
x11 += x15
|
||||
x7 ~= x11
|
||||
x7 = bits.rotate_left32(x7, 7)
|
||||
|
||||
// quarterround(x, 0, 5, 10, 15)
|
||||
x0 += x5
|
||||
x15 ~= x0
|
||||
x15 = bits.rotate_left32(x15, 16)
|
||||
x10 += x15
|
||||
x5 ~= x10
|
||||
x5 = bits.rotate_left32(x5, 12)
|
||||
x0 += x5
|
||||
x15 ~= x0
|
||||
x15 = bits.rotate_left32(x15, 8)
|
||||
x10 += x15
|
||||
x5 ~= x10
|
||||
x5 = bits.rotate_left32(x5, 7)
|
||||
|
||||
// quarterround(x, 1, 6, 11, 12)
|
||||
x1 += x6
|
||||
x12 ~= x1
|
||||
x12 = bits.rotate_left32(x12, 16)
|
||||
x11 += x12
|
||||
x6 ~= x11
|
||||
x6 = bits.rotate_left32(x6, 12)
|
||||
x1 += x6
|
||||
x12 ~= x1
|
||||
x12 = bits.rotate_left32(x12, 8)
|
||||
x11 += x12
|
||||
x6 ~= x11
|
||||
x6 = bits.rotate_left32(x6, 7)
|
||||
|
||||
// quarterround(x, 2, 7, 8, 13)
|
||||
x2 += x7
|
||||
x13 ~= x2
|
||||
x13 = bits.rotate_left32(x13, 16)
|
||||
x8 += x13
|
||||
x7 ~= x8
|
||||
x7 = bits.rotate_left32(x7, 12)
|
||||
x2 += x7
|
||||
x13 ~= x2
|
||||
x13 = bits.rotate_left32(x13, 8)
|
||||
x8 += x13
|
||||
x7 ~= x8
|
||||
x7 = bits.rotate_left32(x7, 7)
|
||||
|
||||
// quarterround(x, 3, 4, 9, 14)
|
||||
x3 += x4
|
||||
x14 ~= x3
|
||||
x14 = bits.rotate_left32(x14, 16)
|
||||
x9 += x14
|
||||
x4 ~= x9
|
||||
x4 = bits.rotate_left32(x4, 12)
|
||||
x3 += x4
|
||||
x14 ~= x3
|
||||
x14 = bits.rotate_left32(x14, 8)
|
||||
x9 += x14
|
||||
x4 ~= x9
|
||||
x4 = bits.rotate_left32(x4, 7)
|
||||
x0, x5, x10, x15 = quarter_round(x0, x5, x10, x15)
|
||||
x1, x6, x11, x12 = quarter_round(x1, x6, x11, x12)
|
||||
x2, x7, x8, x13 = quarter_round(x2, x7, x8, x13)
|
||||
x3, x4, x9, x14 = quarter_round(x3, x4, x9, x14)
|
||||
}
|
||||
|
||||
endian.unchecked_put_u32le(dst[0:4], x0)
|
||||
|
||||
@@ -195,7 +195,6 @@ ge_generator :: proc "contextless" (ge: ^Group_Element) {
|
||||
ge_set(ge, &GE_BASEPOINT)
|
||||
}
|
||||
|
||||
@(private)
|
||||
Addend_Group_Element :: struct {
|
||||
y2_minus_x2: field.Loose_Field_Element, // t1
|
||||
y2_plus_x2: field.Loose_Field_Element, // t3
|
||||
@@ -203,7 +202,6 @@ Addend_Group_Element :: struct {
|
||||
two_times_z2: field.Loose_Field_Element, // t5
|
||||
}
|
||||
|
||||
@(private)
|
||||
ge_addend_set :: proc "contextless" (ge_a: ^Addend_Group_Element, ge: ^Group_Element) {
|
||||
field.fe_sub(&ge_a.y2_minus_x2, &ge.y, &ge.x)
|
||||
field.fe_add(&ge_a.y2_plus_x2, &ge.y, &ge.x)
|
||||
@@ -420,6 +418,6 @@ ge_in_prime_order_subgroup_vartime :: proc "contextless" (ge: ^Group_Element) ->
|
||||
// that is a ~50% speedup, and a lot of added complexity for something
|
||||
// that is better solved by "just use ristretto255".
|
||||
tmp: Group_Element = ---
|
||||
_ge_scalarmult(&tmp, ge, &SC_ELL, true)
|
||||
ge_scalarmult_raw(&tmp, ge, &SC_ELL, true)
|
||||
return ge_equal(&tmp, &GE_IDENTITY) == 1
|
||||
}
|
||||
|
||||
@@ -1,130 +1,24 @@
|
||||
package _edwards25519
|
||||
|
||||
import "core:crypto"
|
||||
import field "core:crypto/_fiat/field_scalar25519"
|
||||
import "core:math/bits"
|
||||
import subtle "core:crypto/_subtle"
|
||||
import "core:mem"
|
||||
|
||||
// GE_BASEPOINT_TABLE is 1 * G, ... 15 * G, in precomputed format.
|
||||
//
|
||||
// Note: When generating, the values were reduced to Tight_Field_Element
|
||||
// ranges, even though that is not required.
|
||||
@(private)
|
||||
GE_BASEPOINT_TABLE := Multiply_Table {
|
||||
{
|
||||
{62697248952638, 204681361388450, 631292143396476, 338455783676468, 1213667448819585},
|
||||
{1288382639258501, 245678601348599, 269427782077623, 1462984067271730, 137412439391563},
|
||||
{301289933810280, 1259582250014073, 1422107436869536, 796239922652654, 1953934009299142},
|
||||
{2, 0, 0, 0, 0},
|
||||
},
|
||||
{
|
||||
{1519297034332653, 1098796920435767, 1823476547744119, 808144629470969, 2110930855619772},
|
||||
{338005982828284, 1667856962156925, 100399270107451, 1604566703601691, 1950338038771369},
|
||||
{1920505767731247, 1443759578976892, 1659852098357048, 1484431291070208, 275018744912646},
|
||||
{763163817085987, 2195095074806923, 2167883174351839, 1868059999999762, 911071066608705},
|
||||
},
|
||||
{
|
||||
{960627541894068, 1314966688943942, 1126875971034044, 2059608312958945, 605975666152586},
|
||||
{1714478358025626, 2209607666607510, 1600912834284834, 496072478982142, 481970031861896},
|
||||
{851735079403194, 1088965826757164, 141569479297499, 602804610059257, 2004026468601520},
|
||||
{197585529552380, 324719066578543, 564481854250498, 1173818332764578, 35452976395676},
|
||||
},
|
||||
{
|
||||
{1152980410747203, 2196804280851952, 25745194962557, 1915167295473129, 1266299690309224},
|
||||
{809905889679060, 979732230071345, 1509972345538142, 188492426534402, 818965583123815},
|
||||
{997685409185036, 1451818320876327, 2126681166774509, 2000509606057528, 235432372486854},
|
||||
{887734189279642, 1460338685162044, 877378220074262, 102436391401299, 153369156847490},
|
||||
},
|
||||
{
|
||||
{2056621900836770, 1821657694132497, 1627986892909426, 1163363868678833, 1108873376459226},
|
||||
{1187697490593623, 1066539945237335, 885654531892000, 1357534489491782, 359370291392448},
|
||||
{1509033452137525, 1305318174298508, 613642471748944, 1987256352550234, 1044283663101541},
|
||||
{220105720697037, 387661783287620, 328296827867762, 360035589590664, 795213236824054},
|
||||
},
|
||||
{
|
||||
{1820794733038396, 1612235121681074, 757405923441402, 1094031020892801, 231025333128907},
|
||||
{1639067873254194, 1484176557946322, 300800382144789, 1329915446659183, 1211704578730455},
|
||||
{641900794791527, 1711751746971612, 179044712319955, 576455585963824, 1852617592509865},
|
||||
{743549047192397, 685091042550147, 1952415336873496, 1965124675654685, 513364998442917},
|
||||
},
|
||||
{
|
||||
{1004557076870448, 1762911374844520, 1330807633622723, 384072910939787, 953849032243810},
|
||||
{2178275058221458, 257933183722891, 376684351537894, 2010189102001786, 1981824297484148},
|
||||
{1332915663881114, 1286540505502549, 1741691283561518, 977214932156314, 1764059494778091},
|
||||
{429702949064027, 1368332611650677, 2019867176450999, 2212258376161746, 526160996742554},
|
||||
},
|
||||
{
|
||||
{2098932988258576, 2203688382075948, 2120400160059479, 1748488020948146, 1203264167282624},
|
||||
{677131386735829, 1850249298025188, 672782146532031, 2144145693078904, 2088656272813787},
|
||||
{1065622343976192, 1573853211848116, 223560413590068, 333846833073379, 27832122205830},
|
||||
{1781008836504573, 917619542051793, 544322748939913, 882577394308384, 1720521246471195},
|
||||
},
|
||||
{
|
||||
{660120928379860, 2081944024858618, 1878411111349191, 424587356517195, 2111317439894005},
|
||||
{1834193977811532, 1864164086863319, 797334633289424, 150410812403062, 2085177078466389},
|
||||
{1438117271371866, 783915531014482, 388731514584658, 292113935417795, 1945855002546714},
|
||||
{1678140823166658, 679103239148744, 614102761596238, 1052962498997885, 1863983323810390},
|
||||
},
|
||||
{
|
||||
{1690309392496233, 1116333140326275, 1377242323631039, 717196888780674, 82724646713353},
|
||||
{1722370213432106, 74265192976253, 264239578448472, 1714909985012994, 2216984958602173},
|
||||
{2010482366920922, 1294036471886319, 566466395005815, 1631955803657320, 1751698647538458},
|
||||
{1073230604155753, 1159087041338551, 1664057985455483, 127472702826203, 1339591128522371},
|
||||
},
|
||||
{
|
||||
{478053307175577, 2179515791720985, 21146535423512, 1831683844029536, 462805561553981},
|
||||
{1945267486565588, 1298536818409655, 2214511796262989, 1904981051429012, 252904800782086},
|
||||
{268945954671210, 222740425595395, 1208025911856230, 1080418823003555, 75929831922483},
|
||||
{1884784014268948, 643868448202966, 978736549726821, 46385971089796, 1296884812292320},
|
||||
},
|
||||
{
|
||||
{1861159462859103, 7077532564710, 963010365896826, 1938780006785270, 766241051941647},
|
||||
{1778966986051906, 1713995999765361, 1394565822271816, 1366699246468722, 1213407027149475},
|
||||
{1978989286560907, 2135084162045594, 1951565508865477, 671788336314416, 293123929458176},
|
||||
{902608944504080, 2167765718046481, 1285718473078022, 1222562171329269, 492109027844479},
|
||||
},
|
||||
{
|
||||
{1820807832746213, 1029220580458586, 1101997555432203, 1039081975563572, 202477981158221},
|
||||
{1866134980680205, 2222325502763386, 1830284629571201, 1046966214478970, 418381946936795},
|
||||
{1783460633291322, 1719505443254998, 1810489639976220, 877049370713018, 2187801198742619},
|
||||
{197118243000763, 305493867565736, 518814410156522, 1656246186645170, 901894734874934},
|
||||
},
|
||||
{
|
||||
{225454942125915, 478410476654509, 600524586037746, 643450007230715, 1018615928259319},
|
||||
{1733330584845708, 881092297970296, 507039890129464, 496397090721598, 2230888519577628},
|
||||
{690155664737246, 1010454785646677, 753170144375012, 1651277613844874, 1622648796364156},
|
||||
{1321310321891618, 1089655277873603, 235891750867089, 815878279563688, 1709264240047556},
|
||||
},
|
||||
{
|
||||
{805027036551342, 1387174275567452, 1156538511461704, 1465897486692171, 1208567094120903},
|
||||
{2228417017817483, 202885584970535, 2182114782271881, 2077405042592934, 1029684358182774},
|
||||
{460447547653983, 627817697755692, 524899434670834, 1228019344939427, 740684787777653},
|
||||
{849757462467675, 447476306919899, 422618957298818, 302134659227815, 675831828440895},
|
||||
},
|
||||
}
|
||||
|
||||
ge_scalarmult :: proc "contextless" (ge, p: ^Group_Element, sc: ^Scalar) {
|
||||
tmp: field.Non_Montgomery_Domain_Field_Element
|
||||
field.fe_from_montgomery(&tmp, sc)
|
||||
|
||||
_ge_scalarmult(ge, p, &tmp)
|
||||
ge_scalarmult_raw(ge, p, &tmp)
|
||||
|
||||
mem.zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
|
||||
ge_scalarmult_basepoint :: proc "contextless" (ge: ^Group_Element, sc: ^Scalar) {
|
||||
// Something like the comb method from "Fast and compact elliptic-curve
|
||||
// cryptography" Section 3.3, would be more performant, but more
|
||||
// complex.
|
||||
//
|
||||
// - https://eprint.iacr.org/2012/309
|
||||
ge_scalarmult(ge, &GE_BASEPOINT, sc)
|
||||
}
|
||||
|
||||
ge_scalarmult_vartime :: proc "contextless" (ge, p: ^Group_Element, sc: ^Scalar) {
|
||||
tmp: field.Non_Montgomery_Domain_Field_Element
|
||||
field.fe_from_montgomery(&tmp, sc)
|
||||
|
||||
_ge_scalarmult(ge, p, &tmp, true)
|
||||
ge_scalarmult_raw(ge, p, &tmp, true)
|
||||
}
|
||||
|
||||
ge_double_scalarmult_basepoint_vartime :: proc "contextless" (
|
||||
@@ -147,6 +41,12 @@ ge_double_scalarmult_basepoint_vartime :: proc "contextless" (
|
||||
|
||||
A_tbl: Multiply_Table = ---
|
||||
mul_tbl_set(&A_tbl, A, &tmp_add)
|
||||
when crypto.COMPACT_IMPLS == true {
|
||||
G_tbl: Multiply_Table = ---
|
||||
mul_tbl_set(&G_tbl, &GE_BASEPOINT, &tmp_add)
|
||||
} else {
|
||||
tmp_bp_addend: Basepoint_Addend_Group_Element = ---
|
||||
}
|
||||
|
||||
sc_a, sc_b: field.Non_Montgomery_Domain_Field_Element
|
||||
field.fe_from_montgomery(&sc_a, a)
|
||||
@@ -170,21 +70,28 @@ ge_double_scalarmult_basepoint_vartime :: proc "contextless" (
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
}
|
||||
mul_tbl_add(&tmp, &A_tbl, hi_a, &tmp_add, &tmp_addend, true)
|
||||
mul_tbl_add(&tmp, &GE_BASEPOINT_TABLE, hi_b, &tmp_add, &tmp_addend, true)
|
||||
when crypto.COMPACT_IMPLS == true {
|
||||
mul_tbl_add(&tmp, &G_tbl, hi_b, &tmp_add, &tmp_addend, true)
|
||||
} else {
|
||||
mul_bp_tbl_add(&tmp, GE_BASEPOINT_TABLE, hi_b, &tmp_add, &tmp_bp_addend, true)
|
||||
}
|
||||
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
mul_tbl_add(&tmp, &A_tbl, lo_a, &tmp_add, &tmp_addend, true)
|
||||
mul_tbl_add(&tmp, &GE_BASEPOINT_TABLE, lo_b, &tmp_add, &tmp_addend, true)
|
||||
when crypto.COMPACT_IMPLS == true {
|
||||
mul_tbl_add(&tmp, &G_tbl, lo_b, &tmp_add, &tmp_addend, true)
|
||||
} else {
|
||||
mul_bp_tbl_add(&tmp, GE_BASEPOINT_TABLE, lo_b, &tmp_add, &tmp_bp_addend, true)
|
||||
}
|
||||
}
|
||||
|
||||
ge_set(ge, &tmp)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_ge_scalarmult :: proc "contextless" (
|
||||
ge_scalarmult_raw :: proc "contextless" (
|
||||
ge, p: ^Group_Element,
|
||||
sc: ^field.Non_Montgomery_Domain_Field_Element,
|
||||
unsafe_is_vartime := false,
|
||||
@@ -281,8 +188,8 @@ mul_tbl_add :: proc "contextless" (
|
||||
{2, 0, 0, 0, 0}, // z * 2
|
||||
}
|
||||
for i := u64(1); i < 16; i = i + 1 {
|
||||
_, ctrl := bits.sub_u64(0, (i ~ idx), 0)
|
||||
ge_addend_conditional_assign(tmp_addend, &tbl[i - 1], int(~ctrl) & 1)
|
||||
ctrl := subtle.eq(i, idx)
|
||||
ge_addend_conditional_assign(tmp_addend, &tbl[i - 1], int(ctrl))
|
||||
}
|
||||
ge_add_addend(ge, ge, tmp_addend, tmp_add)
|
||||
}
|
||||
|
||||
147
core/crypto/_edwards25519/edwards25519_scalar_mul_base.odin
Normal file
147
core/crypto/_edwards25519/edwards25519_scalar_mul_base.odin
Normal file
@@ -0,0 +1,147 @@
|
||||
package _edwards25519
|
||||
|
||||
import "core:crypto"
|
||||
import field "core:crypto/_fiat/field_curve25519"
|
||||
import scalar "core:crypto/_fiat/field_scalar25519"
|
||||
import subtle "core:crypto/_subtle"
|
||||
import "core:mem"
|
||||
|
||||
ge_scalarmult_basepoint :: proc "contextless" (ge: ^Group_Element, sc: ^Scalar) {
|
||||
when crypto.COMPACT_IMPLS == true {
|
||||
ge_scalarmult(ge, &GE_BASEPOINT, sc)
|
||||
} else {
|
||||
tmp_sc: scalar.Non_Montgomery_Domain_Field_Element
|
||||
scalar.fe_from_montgomery(&tmp_sc, sc)
|
||||
|
||||
tmp_add: Add_Scratch = ---
|
||||
tmp_addend: Basepoint_Addend_Group_Element = ---
|
||||
|
||||
ge_identity(ge)
|
||||
for i in 0..<32 {
|
||||
limb := i / 8
|
||||
shift := uint(i & 7) * 8
|
||||
limb_byte := tmp_sc[limb] >> shift
|
||||
|
||||
hi, lo := (limb_byte >> 4) & 0x0f, limb_byte & 0x0f
|
||||
mul_bp_tbl_add(ge, &Gen_Multiply_Table_edwards25519_lo[i], lo, &tmp_add, &tmp_addend, false)
|
||||
mul_bp_tbl_add(ge, &Gen_Multiply_Table_edwards25519_hi[i], hi, &tmp_add, &tmp_addend, false)
|
||||
}
|
||||
|
||||
mem.zero_explicit(&tmp_sc, size_of(tmp_sc))
|
||||
mem.zero_explicit(&tmp_add, size_of(Add_Scratch))
|
||||
mem.zero_explicit(&tmp_addend, size_of(Basepoint_Addend_Group_Element))
|
||||
}
|
||||
}
|
||||
|
||||
when crypto.COMPACT_IMPLS == false {
|
||||
@(private="file",rodata)
|
||||
TWO_TIMES_Z2 := field.Loose_Field_Element{2, 0, 0, 0, 0}
|
||||
|
||||
@(private)
|
||||
Basepoint_Addend_Group_Element :: struct {
|
||||
y2_minus_x2: field.Loose_Field_Element, // t1
|
||||
y2_plus_x2: field.Loose_Field_Element, // t3
|
||||
k_times_t2: field.Tight_Field_Element, // t4
|
||||
}
|
||||
|
||||
@(private)
|
||||
Basepoint_Multiply_Table :: [15]Basepoint_Addend_Group_Element
|
||||
|
||||
@(private)
|
||||
ge_bp_addend_conditional_assign :: proc "contextless" (ge_a, a: ^Basepoint_Addend_Group_Element, ctrl: int) {
|
||||
field.fe_cond_select(&ge_a.y2_minus_x2, &ge_a.y2_minus_x2, &a.y2_minus_x2, ctrl)
|
||||
field.fe_cond_select(&ge_a.y2_plus_x2, &ge_a.y2_plus_x2, &a.y2_plus_x2, ctrl)
|
||||
field.fe_cond_select(&ge_a.k_times_t2, &ge_a.k_times_t2, &a.k_times_t2, ctrl)
|
||||
}
|
||||
|
||||
@(private)
|
||||
ge_add_bp_addend :: proc "contextless" (
|
||||
ge, a: ^Group_Element,
|
||||
b: ^Basepoint_Addend_Group_Element,
|
||||
scratch: ^Add_Scratch,
|
||||
) {
|
||||
// https://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-3
|
||||
// Assumptions: k=2*d, z = 1 (precomputation ftw)
|
||||
//
|
||||
// t0 = Y1-X1
|
||||
// t1 = Y2-X2
|
||||
// A = t0*t1
|
||||
// t2 = Y1+X1
|
||||
// t3 = Y2+X2
|
||||
// B = t2*t3
|
||||
// t4 = k*T2
|
||||
// C = T1*t4
|
||||
// t5 = 2*Z2
|
||||
// D = Z1*t5
|
||||
// E = B-A
|
||||
// F = D-C
|
||||
// G = D+C
|
||||
// H = B+A
|
||||
// X3 = E*F
|
||||
// Y3 = G*H
|
||||
// T3 = E*H
|
||||
// Z3 = F*G
|
||||
//
|
||||
// In order to make the scalar multiply faster, the addend is provided
|
||||
// as a `Addend_Group_Element` with t1, t3, t4, and t5 precomputed, as
|
||||
// it is trivially obvious that those are the only values used by the
|
||||
// formula that are directly dependent on `b`, and are only dependent
|
||||
// on `b` and constants. This saves 1 sub, 2 adds, and 1 multiply,
|
||||
// each time the intermediate representation can be reused.
|
||||
|
||||
A, B, C, D := &scratch.A, &scratch.B, &scratch.C, &scratch.D
|
||||
E, F, G, H := &scratch.E, &scratch.F, &scratch.G, &scratch.H
|
||||
t0, t2 := &scratch.t0, &scratch.t2
|
||||
|
||||
field.fe_sub(t0, &a.y, &a.x)
|
||||
t1 := &b.y2_minus_x2
|
||||
field.fe_carry_mul(A, t0, t1)
|
||||
field.fe_add(t2, &a.y, &a.x)
|
||||
t3 := &b.y2_plus_x2
|
||||
field.fe_carry_mul(B, t2, t3)
|
||||
t4 := &b.k_times_t2
|
||||
field.fe_carry_mul(C, field.fe_relax_cast(&a.t), field.fe_relax_cast(t4))
|
||||
field.fe_carry_mul(D, field.fe_relax_cast(&a.z), &TWO_TIMES_Z2)
|
||||
field.fe_sub(E, B, A)
|
||||
field.fe_sub(F, D, C)
|
||||
field.fe_add(G, D, C)
|
||||
field.fe_add(H, B, A)
|
||||
field.fe_carry_mul(&ge.x, E, F)
|
||||
field.fe_carry_mul(&ge.y, G, H)
|
||||
field.fe_carry_mul(&ge.t, E, H)
|
||||
field.fe_carry_mul(&ge.z, F, G)
|
||||
}
|
||||
|
||||
@(private)
|
||||
mul_bp_tbl_add :: proc "contextless" (
|
||||
ge: ^Group_Element,
|
||||
tbl: ^Basepoint_Multiply_Table,
|
||||
idx: u64,
|
||||
tmp_add: ^Add_Scratch,
|
||||
tmp_addend: ^Basepoint_Addend_Group_Element,
|
||||
unsafe_is_vartime: bool,
|
||||
) {
|
||||
// Variable time lookup, with the addition omitted entirely if idx == 0.
|
||||
if unsafe_is_vartime {
|
||||
// Skip adding the point at infinity.
|
||||
if idx != 0 {
|
||||
ge_add_bp_addend(ge, ge, &tbl[idx-1], tmp_add)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Constant time lookup.
|
||||
tmp_addend^ = {
|
||||
// Point at infinity (0, 1, 1, 0) in precomputed form, note
|
||||
// that the precomputed tables rescale so that `Z = 1`.
|
||||
{1, 0, 0, 0, 0}, // y - x
|
||||
{1, 0, 0, 0, 0}, // y + x
|
||||
{0, 0, 0, 0, 0}, // t * 2d
|
||||
}
|
||||
for i := u64(1); i < 16; i = i + 1 {
|
||||
ctrl := subtle.eq(i, idx)
|
||||
ge_bp_addend_conditional_assign(tmp_addend, &tbl[i - 1], int(ctrl))
|
||||
}
|
||||
ge_add_bp_addend(ge, ge, tmp_addend, tmp_add)
|
||||
}
|
||||
}
|
||||
4947
core/crypto/_edwards25519/edwards25519_table.odin
Normal file
4947
core/crypto/_edwards25519/edwards25519_table.odin
Normal file
File diff suppressed because it is too large
Load Diff
134
core/crypto/_edwards25519/tools/edwards_gen_tables.odin
Normal file
134
core/crypto/_edwards25519/tools/edwards_gen_tables.odin
Normal file
@@ -0,0 +1,134 @@
|
||||
package weistrass_tools
|
||||
|
||||
import ed "core:crypto/_edwards25519"
|
||||
import field "core:crypto/_fiat/field_curve25519"
|
||||
import scalar "core:crypto/_fiat/field_scalar25519"
|
||||
import "core:encoding/endian"
|
||||
import "core:fmt"
|
||||
import path "core:path/filepath"
|
||||
import "core:os"
|
||||
import "core:strings"
|
||||
|
||||
// Yes this leaks memory, fite me IRL.
|
||||
|
||||
GENERATED :: `/*
|
||||
------ GENERATED ------ DO NOT EDIT ------ GENERATED ------ DO NOT EDIT ------ GENERATED ------
|
||||
*/`
|
||||
|
||||
@(private, rodata)
|
||||
FE_D2 := field.Tight_Field_Element {
|
||||
1859910466990425,
|
||||
932731440258426,
|
||||
1072319116312658,
|
||||
1815898335770999,
|
||||
633789495995903,
|
||||
}
|
||||
|
||||
main :: proc() {
|
||||
Basepoint_Addend_Group_Element :: struct {
|
||||
y2_minus_x2: field.Loose_Field_Element, // t1
|
||||
y2_plus_x2: field.Loose_Field_Element, // t3
|
||||
k_times_t2: field.Tight_Field_Element, // t4
|
||||
}
|
||||
Basepoint_Multiply_Table :: [15]Basepoint_Addend_Group_Element
|
||||
|
||||
ge_bp_addend_set := proc(ge_a: ^Basepoint_Addend_Group_Element, ge: ^ed.Group_Element) {
|
||||
// We rescale so Z == 1, so T = X * Y
|
||||
x_, y_, z_inv: field.Tight_Field_Element
|
||||
field.fe_carry_inv(&z_inv, field.fe_relax_cast(&ge.z))
|
||||
field.fe_carry_mul(&x_, field.fe_relax_cast(&ge.x), field.fe_relax_cast(&z_inv))
|
||||
field.fe_carry_mul(&y_, field.fe_relax_cast(&ge.y), field.fe_relax_cast(&z_inv))
|
||||
|
||||
field.fe_sub(&ge_a.y2_minus_x2, &y_, &x_)
|
||||
field.fe_add(&ge_a.y2_plus_x2, &y_, &x_)
|
||||
field.fe_carry_mul(&ge_a.k_times_t2, field.fe_relax_cast(&x_), field.fe_relax_cast(&y_))
|
||||
field.fe_carry_mul(&ge_a.k_times_t2, field.fe_relax_cast(&ge_a.k_times_t2), field.fe_relax_cast(&FE_D2))
|
||||
}
|
||||
|
||||
Multiply_Table_hi: [32]Basepoint_Multiply_Table
|
||||
Multiply_Table_lo: [32]Basepoint_Multiply_Table
|
||||
|
||||
sc_set_unchecked := proc(sc: ^scalar.Non_Montgomery_Domain_Field_Element, b: []byte) {
|
||||
sc[0] = endian.unchecked_get_u64le(b[0:])
|
||||
sc[1] = endian.unchecked_get_u64le(b[8:])
|
||||
sc[2] = endian.unchecked_get_u64le(b[16:])
|
||||
sc[3] = endian.unchecked_get_u64le(b[24:])
|
||||
}
|
||||
|
||||
g, p: ed.Group_Element
|
||||
ed.ge_generator(&g)
|
||||
|
||||
sc: scalar.Non_Montgomery_Domain_Field_Element
|
||||
|
||||
// Precompute ([1,15] << n) * G multiples of G, LSB->MSB
|
||||
for i in 0..<32 {
|
||||
b: [32]byte
|
||||
for j in 1..<16 {
|
||||
b[i] = u8(j)
|
||||
sc_set_unchecked(&sc, b[:])
|
||||
ed.ge_scalarmult_raw(&p, &g, &sc, true)
|
||||
ge_bp_addend_set(&Multiply_Table_lo[i][j-1], &p)
|
||||
|
||||
b[i] = u8(j) << 4
|
||||
sc_set_unchecked(&sc, b[:])
|
||||
ed.ge_scalarmult_raw(&p, &g, &sc, true)
|
||||
ge_bp_addend_set(&Multiply_Table_hi[i][j-1], &p)
|
||||
|
||||
b[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
fn := path.join({ODIN_ROOT, "core", "crypto", "_edwards25519", "edwards25519_table.odin"})
|
||||
bld: strings.Builder
|
||||
w := strings.to_writer(&bld)
|
||||
|
||||
fmt.wprintln(w, "package _edwards25519")
|
||||
fmt.wprintln(w, "")
|
||||
fmt.wprintln(w, GENERATED)
|
||||
fmt.wprintln(w, "")
|
||||
fmt.wprintln(w, "import \"core:crypto\"")
|
||||
fmt.wprintln(w, "")
|
||||
fmt.wprintln(w, "when crypto.COMPACT_IMPLS == false {")
|
||||
|
||||
fmt.wprintln(w, "\t@(private,rodata)")
|
||||
fmt.wprintln(w, "\tGen_Multiply_Table_edwards25519_lo := [32]Basepoint_Multiply_Table {")
|
||||
for &v in Multiply_Table_lo {
|
||||
fmt.wprintln(w, "\t\t{")
|
||||
for &ap in v {
|
||||
fmt.wprintln(w, "\t\t\t{")
|
||||
|
||||
t1, t3, t4 := &ap.y2_minus_x2, &ap.y2_plus_x2, &ap.k_times_t2
|
||||
fmt.wprintf(w, "\t\t\t\t{{%d, %d, %d, %d, %d},\n", t1[0], t1[1], t1[2], t1[3], t1[4])
|
||||
fmt.wprintf(w, "\t\t\t\t{{%d, %d, %d, %d, %d},\n", t3[0], t3[1], t3[2], t3[3], t3[4])
|
||||
fmt.wprintf(w, "\t\t\t\t{{%d, %d, %d, %d, %d},\n", t4[0], t4[1], t4[2], t4[3], t4[4])
|
||||
|
||||
fmt.wprintln(w, "\t\t\t},")
|
||||
}
|
||||
fmt.wprintln(w, "\t\t},")
|
||||
}
|
||||
fmt.wprintln(w, "\t}\n")
|
||||
|
||||
fmt.wprintln(w, "\t@(private,rodata)")
|
||||
fmt.wprintln(w, "\tGen_Multiply_Table_edwards25519_hi := [32]Basepoint_Multiply_Table {")
|
||||
for &v in Multiply_Table_hi {
|
||||
fmt.wprintln(w, "\t\t{")
|
||||
for &ap in v {
|
||||
fmt.wprintln(w, "\t\t\t{")
|
||||
|
||||
t1, t3, t4 := &ap.y2_minus_x2, &ap.y2_plus_x2, &ap.k_times_t2
|
||||
fmt.wprintf(w, "\t\t\t\t{{%d, %d, %d, %d, %d},\n", t1[0], t1[1], t1[2], t1[3], t1[4])
|
||||
fmt.wprintf(w, "\t\t\t\t{{%d, %d, %d, %d, %d},\n", t3[0], t3[1], t3[2], t3[3], t3[4])
|
||||
fmt.wprintf(w, "\t\t\t\t{{%d, %d, %d, %d, %d},\n", t4[0], t4[1], t4[2], t4[3], t4[4])
|
||||
|
||||
fmt.wprintln(w, "\t\t\t},")
|
||||
}
|
||||
fmt.wprintln(w, "\t\t},")
|
||||
}
|
||||
fmt.wprintln(w, "\t}\n")
|
||||
|
||||
fmt.wprintln(w, "\tGE_BASEPOINT_TABLE := &Gen_Multiply_Table_edwards25519_lo[0]")
|
||||
|
||||
fmt.wprintln(w, "}")
|
||||
|
||||
_ = os.write_entire_file(fn, transmute([]byte)(strings.to_string(bld)))
|
||||
}
|
||||
346
core/crypto/_fiat/field_p256r1/field.odin
Normal file
346
core/crypto/_fiat/field_p256r1/field.odin
Normal file
@@ -0,0 +1,346 @@
|
||||
package field_p256r1
|
||||
|
||||
import subtle "core:crypto/_subtle"
|
||||
import "core:encoding/endian"
|
||||
import "core:math/bits"
|
||||
import "core:mem"
|
||||
|
||||
fe_clear :: proc "contextless" (arg1: ^Montgomery_Domain_Field_Element) {
|
||||
mem.zero_explicit(arg1, size_of(Montgomery_Domain_Field_Element))
|
||||
}
|
||||
|
||||
fe_clear_vec :: proc "contextless" (
|
||||
arg1: []^Montgomery_Domain_Field_Element,
|
||||
) {
|
||||
for fe in arg1 {
|
||||
fe_clear(fe)
|
||||
}
|
||||
}
|
||||
|
||||
fe_from_bytes :: proc "contextless" (
|
||||
out1: ^Montgomery_Domain_Field_Element,
|
||||
arg1: []byte,
|
||||
unsafe_assume_canonical := false,
|
||||
) -> bool {
|
||||
ensure_contextless(len(arg1) == 32, "p256r1: invalid fe input buffer")
|
||||
|
||||
// Note: We assume the input is in big-endian.
|
||||
tmp := Non_Montgomery_Domain_Field_Element {
|
||||
endian.unchecked_get_u64be(arg1[24:]),
|
||||
endian.unchecked_get_u64be(arg1[16:]),
|
||||
endian.unchecked_get_u64be(arg1[8:]),
|
||||
endian.unchecked_get_u64be(arg1[0:]),
|
||||
}
|
||||
defer mem.zero_explicit(&tmp, size_of(tmp))
|
||||
|
||||
// Check that tmp is in the the range [0, ELL).
|
||||
if !unsafe_assume_canonical {
|
||||
_, borrow := bits.sub_u64(ELL[0] - 1, tmp[0], 0)
|
||||
_, borrow = bits.sub_u64(ELL[1], tmp[1], borrow)
|
||||
_, borrow = bits.sub_u64(ELL[2], tmp[2], borrow)
|
||||
_, borrow = bits.sub_u64(ELL[3], tmp[3], borrow)
|
||||
if borrow != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
fe_to_montgomery(out1, &tmp)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fe_to_bytes :: proc "contextless" (out1: []byte, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
ensure_contextless(len(out1) == 32, "p256r1: invalid fe output buffer")
|
||||
|
||||
tmp: Non_Montgomery_Domain_Field_Element
|
||||
fe_from_montgomery(&tmp, arg1)
|
||||
|
||||
// Note: Likewise, output in big-endian.
|
||||
endian.unchecked_put_u64be(out1[24:], tmp[0])
|
||||
endian.unchecked_put_u64be(out1[16:], tmp[1])
|
||||
endian.unchecked_put_u64be(out1[8:], tmp[2])
|
||||
endian.unchecked_put_u64be(out1[0:], tmp[3])
|
||||
|
||||
mem.zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
fe_equal :: proc "contextless" (arg1, arg2: ^Montgomery_Domain_Field_Element) -> int {
|
||||
tmp: Montgomery_Domain_Field_Element
|
||||
fe_sub(&tmp, arg1, arg2)
|
||||
|
||||
// This will only underflow iff arg1 == arg2, and we return the borrow,
|
||||
// which will be 1.
|
||||
is_eq := subtle.u64_is_zero(fe_non_zero(&tmp))
|
||||
|
||||
fe_clear(&tmp)
|
||||
|
||||
return int(is_eq)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
fe_is_odd :: proc "contextless" (arg1: ^Montgomery_Domain_Field_Element) -> int {
|
||||
tmp: Non_Montgomery_Domain_Field_Element
|
||||
defer mem.zero_explicit(&tmp, size_of(tmp))
|
||||
|
||||
fe_from_montgomery(&tmp, arg1)
|
||||
return int(tmp[0] & 1)
|
||||
}
|
||||
|
||||
fe_pow2k :: proc "contextless" (
|
||||
out1: ^Montgomery_Domain_Field_Element,
|
||||
arg1: ^Montgomery_Domain_Field_Element,
|
||||
arg2: uint,
|
||||
) {
|
||||
// Special case: `arg1^(2 * 0) = 1`, though this should never happen.
|
||||
if arg2 == 0 {
|
||||
fe_one(out1)
|
||||
return
|
||||
}
|
||||
|
||||
fe_square(out1, arg1)
|
||||
for _ in 1 ..< arg2 {
|
||||
fe_square(out1, out1)
|
||||
}
|
||||
}
|
||||
|
||||
fe_inv :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
// Inversion computation is derived from the addition chain:
|
||||
//
|
||||
// _10 = 2*1
|
||||
// _11 = 1 + _10
|
||||
// _110 = 2*_11
|
||||
// _111 = 1 + _110
|
||||
// _111000 = _111 << 3
|
||||
// _111111 = _111 + _111000
|
||||
// x12 = _111111 << 6 + _111111
|
||||
// x15 = x12 << 3 + _111
|
||||
// x16 = 2*x15 + 1
|
||||
// x32 = x16 << 16 + x16
|
||||
// i53 = x32 << 15
|
||||
// x47 = x15 + i53
|
||||
// i263 = ((i53 << 17 + 1) << 143 + x47) << 47
|
||||
// return (x47 + i263) << 2
|
||||
//
|
||||
// Operations: 255 squares 11 multiplies
|
||||
//
|
||||
// Generated by github.com/mmcloughlin/addchain v0.4.0.
|
||||
|
||||
// Note: Need to stash `arg1` (`xx`) in the case that `out1`/`arg1` alias,
|
||||
// as `arg1` is used after `out1` has been altered.
|
||||
t0, t1, xx: Montgomery_Domain_Field_Element = ---, ---, arg1^
|
||||
|
||||
// Step 1: z = x^0x2
|
||||
fe_square(out1, arg1)
|
||||
|
||||
// Step 2: z = x^0x3
|
||||
fe_mul(out1, &xx, out1)
|
||||
|
||||
// Step 3: z = x^0x6
|
||||
fe_square(out1, out1)
|
||||
|
||||
// Step 4: z = x^0x7
|
||||
fe_mul(out1, &xx, out1)
|
||||
|
||||
// Step 7: t0 = x^0x38
|
||||
fe_pow2k(&t0, out1, 3)
|
||||
|
||||
// Step 8: t0 = x^0x3f
|
||||
fe_mul(&t0, out1, &t0)
|
||||
|
||||
// Step 14: t1 = x^0xfc0
|
||||
fe_pow2k(&t1, &t0, 6)
|
||||
|
||||
// Step 15: t0 = x^0xfff
|
||||
fe_mul(&t0, &t0, &t1)
|
||||
|
||||
// Step 18: t0 = x^0x7ff8
|
||||
fe_pow2k(&t0, &t0, 3)
|
||||
|
||||
// Step 19: z = x^0x7fff
|
||||
fe_mul(out1, out1, &t0)
|
||||
|
||||
// Step 20: t0 = x^0xfffe
|
||||
fe_square(&t0, out1)
|
||||
|
||||
// Step 21: t0 = x^0xffff
|
||||
fe_mul(&t0, &xx, &t0)
|
||||
|
||||
// Step 37: t1 = x^0xffff0000
|
||||
fe_pow2k(&t1, &t0, 16)
|
||||
|
||||
// Step 38: t0 = x^0xffffffff
|
||||
fe_mul(&t0, &t0, &t1)
|
||||
|
||||
// Step 53: t0 = x^0x7fffffff8000
|
||||
fe_pow2k(&t0, &t0, 15)
|
||||
|
||||
// Step 54: z = x^0x7fffffffffff
|
||||
fe_mul(out1, out1, &t0)
|
||||
|
||||
// Step 71: t0 = x^0xffffffff00000000
|
||||
fe_pow2k(&t0, &t0, 17)
|
||||
|
||||
// Step 72: t0 = x^0xffffffff00000001
|
||||
fe_mul(&t0, &xx, &t0)
|
||||
|
||||
// Step 215: t0 = x^0x7fffffff80000000800000000000000000000000000000000000
|
||||
fe_pow2k(&t0, &t0, 143)
|
||||
|
||||
// Step 216: t0 = x^0x7fffffff800000008000000000000000000000007fffffffffff
|
||||
fe_mul(&t0, out1, &t0)
|
||||
|
||||
// Step 263: t0 = x^0x3fffffffc00000004000000000000000000000003fffffffffff800000000000
|
||||
fe_pow2k(&t0, &t0, 47)
|
||||
|
||||
// Step 264: z = x^0x3fffffffc00000004000000000000000000000003fffffffffffffffffffffff
|
||||
fe_mul(out1, out1, &t0)
|
||||
|
||||
// Step 266: z = x^0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc
|
||||
fe_pow2k(out1, out1, 2)
|
||||
|
||||
fe_mul(out1, out1, &xx)
|
||||
|
||||
fe_clear_vec([]^Montgomery_Domain_Field_Element{&t0, &t1, &xx})
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
fe_sqrt :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) -> int {
|
||||
// Square root candidate can be derived via exponentiation by `(p + 1) / 4`
|
||||
// From sage: 28948022302589062190674361737351893382521535853822578548883407827216774463488
|
||||
//
|
||||
// // Inversion computation is derived from the addition chain:
|
||||
//
|
||||
// _10 = 2*1
|
||||
// _11 = 1 + _10
|
||||
// _1100 = _11 << 2
|
||||
// _1111 = _11 + _1100
|
||||
// _11110000 = _1111 << 4
|
||||
// _11111111 = _1111 + _11110000
|
||||
// x16 = _11111111 << 8 + _11111111
|
||||
// x32 = x16 << 16 + x16
|
||||
// return ((x32 << 32 + 1) << 96 + 1) << 94
|
||||
//
|
||||
// Operations: 253 squares 7 multiplies
|
||||
//
|
||||
// Generated by github.com/mmcloughlin/addchain v0.4.0.
|
||||
|
||||
// Likewise this tramples over arg1, so stash another copy.
|
||||
t0, xx: Montgomery_Domain_Field_Element = ---, arg1^
|
||||
|
||||
// Step 1: z = x^0x2
|
||||
fe_square(out1, arg1)
|
||||
|
||||
// Step 2: z = x^0x3
|
||||
fe_mul(out1, &xx, out1)
|
||||
|
||||
// Step 4: t0 = x^0xc
|
||||
fe_pow2k(&t0, &xx, 2)
|
||||
|
||||
// Step 5: z = x^0xf
|
||||
fe_mul(out1, out1, &t0)
|
||||
|
||||
// Step 9: t0 = x^0xf0
|
||||
fe_pow2k(&t0, out1, 4)
|
||||
|
||||
// Step 10: z = x^0xff
|
||||
fe_mul(out1, out1, &t0)
|
||||
|
||||
// Step 18: t0 = x^0xff00
|
||||
fe_pow2k(&t0, out1, 8)
|
||||
|
||||
// Step 19: z = x^0xffff
|
||||
fe_mul(out1, out1, &t0)
|
||||
|
||||
// Step 35: t0 = x^0xffff0000
|
||||
fe_pow2k(&t0, out1, 16)
|
||||
|
||||
// Step 36: z = x^0xffffffff
|
||||
fe_mul(out1, out1, &t0)
|
||||
|
||||
// Step 68: z = x^0xffffffff00000000
|
||||
fe_pow2k(out1, out1, 32)
|
||||
|
||||
// Step 69: z = x^0xffffffff00000001
|
||||
fe_mul(out1, &xx, out1)
|
||||
|
||||
// Step 165: z = x^0xffffffff00000001000000000000000000000000
|
||||
fe_pow2k(out1, out1, 96)
|
||||
|
||||
// Step 166: z = x^0xffffffff00000001000000000000000000000001
|
||||
fe_mul(out1, &xx, out1)
|
||||
|
||||
// Step 260: z = x^0x3fffffffc0000000400000000000000000000000400000000000000000000000
|
||||
fe_pow2k(out1, out1, 94)
|
||||
|
||||
// Ensure that our candidate is actually the square root.
|
||||
check, zero: Montgomery_Domain_Field_Element
|
||||
fe_square(&check, out1)
|
||||
|
||||
is_valid := fe_equal(&check, &xx)
|
||||
fe_cond_select(out1, &zero, out1, is_valid)
|
||||
|
||||
fe_clear_vec([]^Montgomery_Domain_Field_Element{&t0, &xx, &check})
|
||||
|
||||
return is_valid
|
||||
|
||||
}
|
||||
|
||||
fe_zero :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element) {
|
||||
out1[0] = 0
|
||||
out1[1] = 0
|
||||
out1[2] = 0
|
||||
out1[3] = 0
|
||||
}
|
||||
|
||||
fe_set :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
x1 := arg1[0]
|
||||
x2 := arg1[1]
|
||||
x3 := arg1[2]
|
||||
x4 := arg1[3]
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
}
|
||||
|
||||
@(optimization_mode = "none")
|
||||
fe_cond_swap :: #force_no_inline proc "contextless" (out1, out2: ^Montgomery_Domain_Field_Element, arg1: int) {
|
||||
mask := (u64(arg1) * 0xffffffffffffffff)
|
||||
x := (out1[0] ~ out2[0]) & mask
|
||||
x1, y1 := out1[0] ~ x, out2[0] ~ x
|
||||
x = (out1[1] ~ out2[1]) & mask
|
||||
x2, y2 := out1[1] ~ x, out2[1] ~ x
|
||||
x = (out1[2] ~ out2[2]) & mask
|
||||
x3, y3 := out1[2] ~ x, out2[2] ~ x
|
||||
x = (out1[3] ~ out2[3]) & mask
|
||||
x4, y4 := out1[3] ~ x, out2[3] ~ x
|
||||
out1[0], out2[0] = x1, y1
|
||||
out1[1], out2[1] = x2, y2
|
||||
out1[2], out2[2] = x3, y3
|
||||
out1[3], out2[3] = x4, y4
|
||||
}
|
||||
|
||||
@(optimization_mode = "none")
|
||||
fe_cond_select :: #force_no_inline proc "contextless" (
|
||||
out1, arg1, arg2: ^Montgomery_Domain_Field_Element,
|
||||
arg3: int,
|
||||
) {
|
||||
mask := (u64(arg3) * 0xffffffffffffffff)
|
||||
x1 := ((mask & arg2[0]) | ((~mask) & arg1[0]))
|
||||
x2 := ((mask & arg2[1]) | ((~mask) & arg1[1]))
|
||||
x3 := ((mask & arg2[2]) | ((~mask) & arg1[2]))
|
||||
x4 := ((mask & arg2[3]) | ((~mask) & arg1[3]))
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
}
|
||||
|
||||
fe_cond_negate :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element, ctrl: int) {
|
||||
tmp1: Montgomery_Domain_Field_Element = ---
|
||||
fe_opp(&tmp1, arg1)
|
||||
fe_cond_select(out1, arg1, &tmp1, ctrl)
|
||||
|
||||
fe_clear(&tmp1)
|
||||
}
|
||||
501
core/crypto/_fiat/field_p256r1/field64.odin
Normal file
501
core/crypto/_fiat/field_p256r1/field64.odin
Normal file
@@ -0,0 +1,501 @@
|
||||
// The BSD 1-Clause License (BSD-1-Clause)
|
||||
//
|
||||
// Copyright (c) 2015-2020 the fiat-crypto authors (see the AUTHORS file)
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY the fiat-crypto authors "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design,
|
||||
// Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package field_p256r1
|
||||
|
||||
// The file provides arithmetic on the field Z/(2^256 - 2^224 + 2^192 + 2^96 - 1)
|
||||
// using a 64-bit Montgomery form internal representation. It is derived
|
||||
// primarily from the machine generated Golang output from the fiat-crypto
|
||||
// project.
|
||||
//
|
||||
// While the base implementation is provably correct, this implementation
|
||||
// makes no such claims as the port and optimizations were done by hand.
|
||||
//
|
||||
// WARNING: While big-endian is the common representation used for this
|
||||
// curve, the fiat output uses least-significant-limb first.
|
||||
|
||||
import fiat "core:crypto/_fiat"
|
||||
import "core:math/bits"
|
||||
|
||||
// ELL is the saturated representation of the field order, least-significant
|
||||
// limb first.
|
||||
ELL :: [4]u64{0xffffffffffffffff, 0xffffffff, 0x0, 0xffffffff00000001}
|
||||
|
||||
Montgomery_Domain_Field_Element :: distinct [4]u64
|
||||
Non_Montgomery_Domain_Field_Element :: distinct [4]u64
|
||||
|
||||
fe_mul :: proc "contextless" (out1, arg1, arg2: ^Montgomery_Domain_Field_Element) {
|
||||
x1 := arg1[1]
|
||||
x2 := arg1[2]
|
||||
x3 := arg1[3]
|
||||
x4 := arg1[0]
|
||||
x6, x5 := bits.mul_u64(x4, arg2[3])
|
||||
x8, x7 := bits.mul_u64(x4, arg2[2])
|
||||
x10, x9 := bits.mul_u64(x4, arg2[1])
|
||||
x12, x11 := bits.mul_u64(x4, arg2[0])
|
||||
x13, x14 := bits.add_u64(x12, x9, u64(0x0))
|
||||
x15, x16 := bits.add_u64(x10, x7, u64(fiat.u1(x14)))
|
||||
x17, x18 := bits.add_u64(x8, x5, u64(fiat.u1(x16)))
|
||||
x19 := (u64(fiat.u1(x18)) + x6)
|
||||
x21, x20 := bits.mul_u64(x11, 0xffffffff00000001)
|
||||
x23, x22 := bits.mul_u64(x11, 0xffffffff)
|
||||
x25, x24 := bits.mul_u64(x11, 0xffffffffffffffff)
|
||||
x26, x27 := bits.add_u64(x25, x22, u64(0x0))
|
||||
x28 := (u64(fiat.u1(x27)) + x23)
|
||||
_, x30 := bits.add_u64(x11, x24, u64(0x0))
|
||||
x31, x32 := bits.add_u64(x13, x26, u64(fiat.u1(x30)))
|
||||
x33, x34 := bits.add_u64(x15, x28, u64(fiat.u1(x32)))
|
||||
x35, x36 := bits.add_u64(x17, x20, u64(fiat.u1(x34)))
|
||||
x37, x38 := bits.add_u64(x19, x21, u64(fiat.u1(x36)))
|
||||
x40, x39 := bits.mul_u64(x1, arg2[3])
|
||||
x42, x41 := bits.mul_u64(x1, arg2[2])
|
||||
x44, x43 := bits.mul_u64(x1, arg2[1])
|
||||
x46, x45 := bits.mul_u64(x1, arg2[0])
|
||||
x47, x48 := bits.add_u64(x46, x43, u64(0x0))
|
||||
x49, x50 := bits.add_u64(x44, x41, u64(fiat.u1(x48)))
|
||||
x51, x52 := bits.add_u64(x42, x39, u64(fiat.u1(x50)))
|
||||
x53 := (u64(fiat.u1(x52)) + x40)
|
||||
x54, x55 := bits.add_u64(x31, x45, u64(0x0))
|
||||
x56, x57 := bits.add_u64(x33, x47, u64(fiat.u1(x55)))
|
||||
x58, x59 := bits.add_u64(x35, x49, u64(fiat.u1(x57)))
|
||||
x60, x61 := bits.add_u64(x37, x51, u64(fiat.u1(x59)))
|
||||
x62, x63 := bits.add_u64(u64(fiat.u1(x38)), x53, u64(fiat.u1(x61)))
|
||||
x65, x64 := bits.mul_u64(x54, 0xffffffff00000001)
|
||||
x67, x66 := bits.mul_u64(x54, 0xffffffff)
|
||||
x69, x68 := bits.mul_u64(x54, 0xffffffffffffffff)
|
||||
x70, x71 := bits.add_u64(x69, x66, u64(0x0))
|
||||
x72 := (u64(fiat.u1(x71)) + x67)
|
||||
_, x74 := bits.add_u64(x54, x68, u64(0x0))
|
||||
x75, x76 := bits.add_u64(x56, x70, u64(fiat.u1(x74)))
|
||||
x77, x78 := bits.add_u64(x58, x72, u64(fiat.u1(x76)))
|
||||
x79, x80 := bits.add_u64(x60, x64, u64(fiat.u1(x78)))
|
||||
x81, x82 := bits.add_u64(x62, x65, u64(fiat.u1(x80)))
|
||||
x83 := (u64(fiat.u1(x82)) + u64(fiat.u1(x63)))
|
||||
x85, x84 := bits.mul_u64(x2, arg2[3])
|
||||
x87, x86 := bits.mul_u64(x2, arg2[2])
|
||||
x89, x88 := bits.mul_u64(x2, arg2[1])
|
||||
x91, x90 := bits.mul_u64(x2, arg2[0])
|
||||
x92, x93 := bits.add_u64(x91, x88, u64(0x0))
|
||||
x94, x95 := bits.add_u64(x89, x86, u64(fiat.u1(x93)))
|
||||
x96, x97 := bits.add_u64(x87, x84, u64(fiat.u1(x95)))
|
||||
x98 := (u64(fiat.u1(x97)) + x85)
|
||||
x99, x100 := bits.add_u64(x75, x90, u64(0x0))
|
||||
x101, x102 := bits.add_u64(x77, x92, u64(fiat.u1(x100)))
|
||||
x103, x104 := bits.add_u64(x79, x94, u64(fiat.u1(x102)))
|
||||
x105, x106 := bits.add_u64(x81, x96, u64(fiat.u1(x104)))
|
||||
x107, x108 := bits.add_u64(x83, x98, u64(fiat.u1(x106)))
|
||||
x110, x109 := bits.mul_u64(x99, 0xffffffff00000001)
|
||||
x112, x111 := bits.mul_u64(x99, 0xffffffff)
|
||||
x114, x113 := bits.mul_u64(x99, 0xffffffffffffffff)
|
||||
x115, x116 := bits.add_u64(x114, x111, u64(0x0))
|
||||
x117 := (u64(fiat.u1(x116)) + x112)
|
||||
_, x119 := bits.add_u64(x99, x113, u64(0x0))
|
||||
x120, x121 := bits.add_u64(x101, x115, u64(fiat.u1(x119)))
|
||||
x122, x123 := bits.add_u64(x103, x117, u64(fiat.u1(x121)))
|
||||
x124, x125 := bits.add_u64(x105, x109, u64(fiat.u1(x123)))
|
||||
x126, x127 := bits.add_u64(x107, x110, u64(fiat.u1(x125)))
|
||||
x128 := (u64(fiat.u1(x127)) + u64(fiat.u1(x108)))
|
||||
x130, x129 := bits.mul_u64(x3, arg2[3])
|
||||
x132, x131 := bits.mul_u64(x3, arg2[2])
|
||||
x134, x133 := bits.mul_u64(x3, arg2[1])
|
||||
x136, x135 := bits.mul_u64(x3, arg2[0])
|
||||
x137, x138 := bits.add_u64(x136, x133, u64(0x0))
|
||||
x139, x140 := bits.add_u64(x134, x131, u64(fiat.u1(x138)))
|
||||
x141, x142 := bits.add_u64(x132, x129, u64(fiat.u1(x140)))
|
||||
x143 := (u64(fiat.u1(x142)) + x130)
|
||||
x144, x145 := bits.add_u64(x120, x135, u64(0x0))
|
||||
x146, x147 := bits.add_u64(x122, x137, u64(fiat.u1(x145)))
|
||||
x148, x149 := bits.add_u64(x124, x139, u64(fiat.u1(x147)))
|
||||
x150, x151 := bits.add_u64(x126, x141, u64(fiat.u1(x149)))
|
||||
x152, x153 := bits.add_u64(x128, x143, u64(fiat.u1(x151)))
|
||||
x155, x154 := bits.mul_u64(x144, 0xffffffff00000001)
|
||||
x157, x156 := bits.mul_u64(x144, 0xffffffff)
|
||||
x159, x158 := bits.mul_u64(x144, 0xffffffffffffffff)
|
||||
x160, x161 := bits.add_u64(x159, x156, u64(0x0))
|
||||
x162 := (u64(fiat.u1(x161)) + x157)
|
||||
_, x164 := bits.add_u64(x144, x158, u64(0x0))
|
||||
x165, x166 := bits.add_u64(x146, x160, u64(fiat.u1(x164)))
|
||||
x167, x168 := bits.add_u64(x148, x162, u64(fiat.u1(x166)))
|
||||
x169, x170 := bits.add_u64(x150, x154, u64(fiat.u1(x168)))
|
||||
x171, x172 := bits.add_u64(x152, x155, u64(fiat.u1(x170)))
|
||||
x173 := (u64(fiat.u1(x172)) + u64(fiat.u1(x153)))
|
||||
x174, x175 := bits.sub_u64(x165, 0xffffffffffffffff, u64(0x0))
|
||||
x176, x177 := bits.sub_u64(x167, 0xffffffff, u64(fiat.u1(x175)))
|
||||
x178, x179 := bits.sub_u64(x169, u64(0x0), u64(fiat.u1(x177)))
|
||||
x180, x181 := bits.sub_u64(x171, 0xffffffff00000001, u64(fiat.u1(x179)))
|
||||
_, x183 := bits.sub_u64(x173, u64(0x0), u64(fiat.u1(x181)))
|
||||
x184 := fiat.cmovznz_u64(fiat.u1(x183), x174, x165)
|
||||
x185 := fiat.cmovznz_u64(fiat.u1(x183), x176, x167)
|
||||
x186 := fiat.cmovznz_u64(fiat.u1(x183), x178, x169)
|
||||
x187 := fiat.cmovznz_u64(fiat.u1(x183), x180, x171)
|
||||
out1[0] = x184
|
||||
out1[1] = x185
|
||||
out1[2] = x186
|
||||
out1[3] = x187
|
||||
}
|
||||
|
||||
fe_square :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
x1 := arg1[1]
|
||||
x2 := arg1[2]
|
||||
x3 := arg1[3]
|
||||
x4 := arg1[0]
|
||||
x6, x5 := bits.mul_u64(x4, arg1[3])
|
||||
x8, x7 := bits.mul_u64(x4, arg1[2])
|
||||
x10, x9 := bits.mul_u64(x4, arg1[1])
|
||||
x12, x11 := bits.mul_u64(x4, arg1[0])
|
||||
x13, x14 := bits.add_u64(x12, x9, u64(0x0))
|
||||
x15, x16 := bits.add_u64(x10, x7, u64(fiat.u1(x14)))
|
||||
x17, x18 := bits.add_u64(x8, x5, u64(fiat.u1(x16)))
|
||||
x19 := (u64(fiat.u1(x18)) + x6)
|
||||
x21, x20 := bits.mul_u64(x11, 0xffffffff00000001)
|
||||
x23, x22 := bits.mul_u64(x11, 0xffffffff)
|
||||
x25, x24 := bits.mul_u64(x11, 0xffffffffffffffff)
|
||||
x26, x27 := bits.add_u64(x25, x22, u64(0x0))
|
||||
x28 := (u64(fiat.u1(x27)) + x23)
|
||||
_, x30 := bits.add_u64(x11, x24, u64(0x0))
|
||||
x31, x32 := bits.add_u64(x13, x26, u64(fiat.u1(x30)))
|
||||
x33, x34 := bits.add_u64(x15, x28, u64(fiat.u1(x32)))
|
||||
x35, x36 := bits.add_u64(x17, x20, u64(fiat.u1(x34)))
|
||||
x37, x38 := bits.add_u64(x19, x21, u64(fiat.u1(x36)))
|
||||
x40, x39 := bits.mul_u64(x1, arg1[3])
|
||||
x42, x41 := bits.mul_u64(x1, arg1[2])
|
||||
x44, x43 := bits.mul_u64(x1, arg1[1])
|
||||
x46, x45 := bits.mul_u64(x1, arg1[0])
|
||||
x47, x48 := bits.add_u64(x46, x43, u64(0x0))
|
||||
x49, x50 := bits.add_u64(x44, x41, u64(fiat.u1(x48)))
|
||||
x51, x52 := bits.add_u64(x42, x39, u64(fiat.u1(x50)))
|
||||
x53 := (u64(fiat.u1(x52)) + x40)
|
||||
x54, x55 := bits.add_u64(x31, x45, u64(0x0))
|
||||
x56, x57 := bits.add_u64(x33, x47, u64(fiat.u1(x55)))
|
||||
x58, x59 := bits.add_u64(x35, x49, u64(fiat.u1(x57)))
|
||||
x60, x61 := bits.add_u64(x37, x51, u64(fiat.u1(x59)))
|
||||
x62, x63 := bits.add_u64(u64(fiat.u1(x38)), x53, u64(fiat.u1(x61)))
|
||||
x65, x64 := bits.mul_u64(x54, 0xffffffff00000001)
|
||||
x67, x66 := bits.mul_u64(x54, 0xffffffff)
|
||||
x69, x68 := bits.mul_u64(x54, 0xffffffffffffffff)
|
||||
x70, x71 := bits.add_u64(x69, x66, u64(0x0))
|
||||
x72 := (u64(fiat.u1(x71)) + x67)
|
||||
_, x74 := bits.add_u64(x54, x68, u64(0x0))
|
||||
x75, x76 := bits.add_u64(x56, x70, u64(fiat.u1(x74)))
|
||||
x77, x78 := bits.add_u64(x58, x72, u64(fiat.u1(x76)))
|
||||
x79, x80 := bits.add_u64(x60, x64, u64(fiat.u1(x78)))
|
||||
x81, x82 := bits.add_u64(x62, x65, u64(fiat.u1(x80)))
|
||||
x83 := (u64(fiat.u1(x82)) + u64(fiat.u1(x63)))
|
||||
x85, x84 := bits.mul_u64(x2, arg1[3])
|
||||
x87, x86 := bits.mul_u64(x2, arg1[2])
|
||||
x89, x88 := bits.mul_u64(x2, arg1[1])
|
||||
x91, x90 := bits.mul_u64(x2, arg1[0])
|
||||
x92, x93 := bits.add_u64(x91, x88, u64(0x0))
|
||||
x94, x95 := bits.add_u64(x89, x86, u64(fiat.u1(x93)))
|
||||
x96, x97 := bits.add_u64(x87, x84, u64(fiat.u1(x95)))
|
||||
x98 := (u64(fiat.u1(x97)) + x85)
|
||||
x99, x100 := bits.add_u64(x75, x90, u64(0x0))
|
||||
x101, x102 := bits.add_u64(x77, x92, u64(fiat.u1(x100)))
|
||||
x103, x104 := bits.add_u64(x79, x94, u64(fiat.u1(x102)))
|
||||
x105, x106 := bits.add_u64(x81, x96, u64(fiat.u1(x104)))
|
||||
x107, x108 := bits.add_u64(x83, x98, u64(fiat.u1(x106)))
|
||||
x110, x109 := bits.mul_u64(x99, 0xffffffff00000001)
|
||||
x112, x111 := bits.mul_u64(x99, 0xffffffff)
|
||||
x114, x113 := bits.mul_u64(x99, 0xffffffffffffffff)
|
||||
x115, x116 := bits.add_u64(x114, x111, u64(0x0))
|
||||
x117 := (u64(fiat.u1(x116)) + x112)
|
||||
_, x119 := bits.add_u64(x99, x113, u64(0x0))
|
||||
x120, x121 := bits.add_u64(x101, x115, u64(fiat.u1(x119)))
|
||||
x122, x123 := bits.add_u64(x103, x117, u64(fiat.u1(x121)))
|
||||
x124, x125 := bits.add_u64(x105, x109, u64(fiat.u1(x123)))
|
||||
x126, x127 := bits.add_u64(x107, x110, u64(fiat.u1(x125)))
|
||||
x128 := (u64(fiat.u1(x127)) + u64(fiat.u1(x108)))
|
||||
x130, x129 := bits.mul_u64(x3, arg1[3])
|
||||
x132, x131 := bits.mul_u64(x3, arg1[2])
|
||||
x134, x133 := bits.mul_u64(x3, arg1[1])
|
||||
x136, x135 := bits.mul_u64(x3, arg1[0])
|
||||
x137, x138 := bits.add_u64(x136, x133, u64(0x0))
|
||||
x139, x140 := bits.add_u64(x134, x131, u64(fiat.u1(x138)))
|
||||
x141, x142 := bits.add_u64(x132, x129, u64(fiat.u1(x140)))
|
||||
x143 := (u64(fiat.u1(x142)) + x130)
|
||||
x144, x145 := bits.add_u64(x120, x135, u64(0x0))
|
||||
x146, x147 := bits.add_u64(x122, x137, u64(fiat.u1(x145)))
|
||||
x148, x149 := bits.add_u64(x124, x139, u64(fiat.u1(x147)))
|
||||
x150, x151 := bits.add_u64(x126, x141, u64(fiat.u1(x149)))
|
||||
x152, x153 := bits.add_u64(x128, x143, u64(fiat.u1(x151)))
|
||||
x155, x154 := bits.mul_u64(x144, 0xffffffff00000001)
|
||||
x157, x156 := bits.mul_u64(x144, 0xffffffff)
|
||||
x159, x158 := bits.mul_u64(x144, 0xffffffffffffffff)
|
||||
x160, x161 := bits.add_u64(x159, x156, u64(0x0))
|
||||
x162 := (u64(fiat.u1(x161)) + x157)
|
||||
_, x164 := bits.add_u64(x144, x158, u64(0x0))
|
||||
x165, x166 := bits.add_u64(x146, x160, u64(fiat.u1(x164)))
|
||||
x167, x168 := bits.add_u64(x148, x162, u64(fiat.u1(x166)))
|
||||
x169, x170 := bits.add_u64(x150, x154, u64(fiat.u1(x168)))
|
||||
x171, x172 := bits.add_u64(x152, x155, u64(fiat.u1(x170)))
|
||||
x173 := (u64(fiat.u1(x172)) + u64(fiat.u1(x153)))
|
||||
x174, x175 := bits.sub_u64(x165, 0xffffffffffffffff, u64(0x0))
|
||||
x176, x177 := bits.sub_u64(x167, 0xffffffff, u64(fiat.u1(x175)))
|
||||
x178, x179 := bits.sub_u64(x169, u64(0x0), u64(fiat.u1(x177)))
|
||||
x180, x181 := bits.sub_u64(x171, 0xffffffff00000001, u64(fiat.u1(x179)))
|
||||
_, x183 := bits.sub_u64(x173, u64(0x0), u64(fiat.u1(x181)))
|
||||
x184 := fiat.cmovznz_u64(fiat.u1(x183), x174, x165)
|
||||
x185 := fiat.cmovznz_u64(fiat.u1(x183), x176, x167)
|
||||
x186 := fiat.cmovznz_u64(fiat.u1(x183), x178, x169)
|
||||
x187 := fiat.cmovznz_u64(fiat.u1(x183), x180, x171)
|
||||
out1[0] = x184
|
||||
out1[1] = x185
|
||||
out1[2] = x186
|
||||
out1[3] = x187
|
||||
}
|
||||
|
||||
fe_add :: proc "contextless" (out1, arg1, arg2: ^Montgomery_Domain_Field_Element) {
|
||||
x1, x2 := bits.add_u64(arg1[0], arg2[0], u64(0x0))
|
||||
x3, x4 := bits.add_u64(arg1[1], arg2[1], u64(fiat.u1(x2)))
|
||||
x5, x6 := bits.add_u64(arg1[2], arg2[2], u64(fiat.u1(x4)))
|
||||
x7, x8 := bits.add_u64(arg1[3], arg2[3], u64(fiat.u1(x6)))
|
||||
x9, x10 := bits.sub_u64(x1, 0xffffffffffffffff, u64(0x0))
|
||||
x11, x12 := bits.sub_u64(x3, 0xffffffff, u64(fiat.u1(x10)))
|
||||
x13, x14 := bits.sub_u64(x5, u64(0x0), u64(fiat.u1(x12)))
|
||||
x15, x16 := bits.sub_u64(x7, 0xffffffff00000001, u64(fiat.u1(x14)))
|
||||
_, x18 := bits.sub_u64(u64(fiat.u1(x8)), u64(0x0), u64(fiat.u1(x16)))
|
||||
x19 := fiat.cmovznz_u64(fiat.u1(x18), x9, x1)
|
||||
x20 := fiat.cmovznz_u64(fiat.u1(x18), x11, x3)
|
||||
x21 := fiat.cmovznz_u64(fiat.u1(x18), x13, x5)
|
||||
x22 := fiat.cmovznz_u64(fiat.u1(x18), x15, x7)
|
||||
out1[0] = x19
|
||||
out1[1] = x20
|
||||
out1[2] = x21
|
||||
out1[3] = x22
|
||||
}
|
||||
|
||||
fe_sub :: proc "contextless" (out1, arg1, arg2: ^Montgomery_Domain_Field_Element) {
|
||||
x1, x2 := bits.sub_u64(arg1[0], arg2[0], u64(0x0))
|
||||
x3, x4 := bits.sub_u64(arg1[1], arg2[1], u64(fiat.u1(x2)))
|
||||
x5, x6 := bits.sub_u64(arg1[2], arg2[2], u64(fiat.u1(x4)))
|
||||
x7, x8 := bits.sub_u64(arg1[3], arg2[3], u64(fiat.u1(x6)))
|
||||
x9 := fiat.cmovznz_u64(fiat.u1(x8), u64(0x0), 0xffffffffffffffff)
|
||||
x10, x11 := bits.add_u64(x1, x9, u64(0x0))
|
||||
x12, x13 := bits.add_u64(x3, (x9 & 0xffffffff), u64(fiat.u1(x11)))
|
||||
x14, x15 := bits.add_u64(x5, u64(0x0), u64(fiat.u1(x13)))
|
||||
x16, _ := bits.add_u64(x7, (x9 & 0xffffffff00000001), u64(fiat.u1(x15)))
|
||||
out1[0] = x10
|
||||
out1[1] = x12
|
||||
out1[2] = x14
|
||||
out1[3] = x16
|
||||
}
|
||||
|
||||
fe_opp :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
x1, x2 := bits.sub_u64(u64(0x0), arg1[0], u64(0x0))
|
||||
x3, x4 := bits.sub_u64(u64(0x0), arg1[1], u64(fiat.u1(x2)))
|
||||
x5, x6 := bits.sub_u64(u64(0x0), arg1[2], u64(fiat.u1(x4)))
|
||||
x7, x8 := bits.sub_u64(u64(0x0), arg1[3], u64(fiat.u1(x6)))
|
||||
x9 := fiat.cmovznz_u64(fiat.u1(x8), u64(0x0), 0xffffffffffffffff)
|
||||
x10, x11 := bits.add_u64(x1, x9, u64(0x0))
|
||||
x12, x13 := bits.add_u64(x3, (x9 & 0xffffffff), u64(fiat.u1(x11)))
|
||||
x14, x15 := bits.add_u64(x5, u64(0x0), u64(fiat.u1(x13)))
|
||||
x16, _ := bits.add_u64(x7, (x9 & 0xffffffff00000001), u64(fiat.u1(x15)))
|
||||
out1[0] = x10
|
||||
out1[1] = x12
|
||||
out1[2] = x14
|
||||
out1[3] = x16
|
||||
}
|
||||
|
||||
fe_one :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element) {
|
||||
out1[0] = 0x1
|
||||
out1[1] = 0xffffffff00000000
|
||||
out1[2] = 0xffffffffffffffff
|
||||
out1[3] = 0xfffffffe
|
||||
}
|
||||
|
||||
fe_non_zero :: proc "contextless" (arg1: ^Montgomery_Domain_Field_Element) -> u64 {
|
||||
return arg1[0] | (arg1[1] | (arg1[2] | arg1[3]))
|
||||
}
|
||||
|
||||
@(optimization_mode = "none")
|
||||
fe_cond_assign :: #force_no_inline proc "contextless" (
|
||||
out1, arg1: ^Montgomery_Domain_Field_Element,
|
||||
arg2: int,
|
||||
) {
|
||||
x1 := fiat.cmovznz_u64(fiat.u1(arg2), out1[0], arg1[0])
|
||||
x2 := fiat.cmovznz_u64(fiat.u1(arg2), out1[1], arg1[1])
|
||||
x3 := fiat.cmovznz_u64(fiat.u1(arg2), out1[2], arg1[2])
|
||||
x4 := fiat.cmovznz_u64(fiat.u1(arg2), out1[3], arg1[3])
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
}
|
||||
|
||||
fe_from_montgomery :: proc "contextless" (
|
||||
out1: ^Non_Montgomery_Domain_Field_Element,
|
||||
arg1: ^Montgomery_Domain_Field_Element,
|
||||
) {
|
||||
x1 := arg1[0]
|
||||
x3, x2 := bits.mul_u64(x1, 0xffffffff00000001)
|
||||
x5, x4 := bits.mul_u64(x1, 0xffffffff)
|
||||
x7, x6 := bits.mul_u64(x1, 0xffffffffffffffff)
|
||||
x8, x9 := bits.add_u64(x7, x4, u64(0x0))
|
||||
_, x11 := bits.add_u64(x1, x6, u64(0x0))
|
||||
x12, x13 := bits.add_u64(u64(0x0), x8, u64(fiat.u1(x11)))
|
||||
x14, x15 := bits.add_u64(x12, arg1[1], u64(0x0))
|
||||
x17, x16 := bits.mul_u64(x14, 0xffffffff00000001)
|
||||
x19, x18 := bits.mul_u64(x14, 0xffffffff)
|
||||
x21, x20 := bits.mul_u64(x14, 0xffffffffffffffff)
|
||||
x22, x23 := bits.add_u64(x21, x18, u64(0x0))
|
||||
_, x25 := bits.add_u64(x14, x20, u64(0x0))
|
||||
x26, x27 := bits.add_u64((u64(fiat.u1(x15)) + (u64(fiat.u1(x13)) + (u64(fiat.u1(x9)) + x5))), x22, u64(fiat.u1(x25)))
|
||||
x28, x29 := bits.add_u64(x2, (u64(fiat.u1(x23)) + x19), u64(fiat.u1(x27)))
|
||||
x30, x31 := bits.add_u64(x3, x16, u64(fiat.u1(x29)))
|
||||
x32, x33 := bits.add_u64(x26, arg1[2], u64(0x0))
|
||||
x34, x35 := bits.add_u64(x28, u64(0x0), u64(fiat.u1(x33)))
|
||||
x36, x37 := bits.add_u64(x30, u64(0x0), u64(fiat.u1(x35)))
|
||||
x39, x38 := bits.mul_u64(x32, 0xffffffff00000001)
|
||||
x41, x40 := bits.mul_u64(x32, 0xffffffff)
|
||||
x43, x42 := bits.mul_u64(x32, 0xffffffffffffffff)
|
||||
x44, x45 := bits.add_u64(x43, x40, u64(0x0))
|
||||
_, x47 := bits.add_u64(x32, x42, u64(0x0))
|
||||
x48, x49 := bits.add_u64(x34, x44, u64(fiat.u1(x47)))
|
||||
x50, x51 := bits.add_u64(x36, (u64(fiat.u1(x45)) + x41), u64(fiat.u1(x49)))
|
||||
x52, x53 := bits.add_u64((u64(fiat.u1(x37)) + (u64(fiat.u1(x31)) + x17)), x38, u64(fiat.u1(x51)))
|
||||
x54, x55 := bits.add_u64(x48, arg1[3], u64(0x0))
|
||||
x56, x57 := bits.add_u64(x50, u64(0x0), u64(fiat.u1(x55)))
|
||||
x58, x59 := bits.add_u64(x52, u64(0x0), u64(fiat.u1(x57)))
|
||||
x61, x60 := bits.mul_u64(x54, 0xffffffff00000001)
|
||||
x63, x62 := bits.mul_u64(x54, 0xffffffff)
|
||||
x65, x64 := bits.mul_u64(x54, 0xffffffffffffffff)
|
||||
x66, x67 := bits.add_u64(x65, x62, u64(0x0))
|
||||
_, x69 := bits.add_u64(x54, x64, u64(0x0))
|
||||
x70, x71 := bits.add_u64(x56, x66, u64(fiat.u1(x69)))
|
||||
x72, x73 := bits.add_u64(x58, (u64(fiat.u1(x67)) + x63), u64(fiat.u1(x71)))
|
||||
x74, x75 := bits.add_u64((u64(fiat.u1(x59)) + (u64(fiat.u1(x53)) + x39)), x60, u64(fiat.u1(x73)))
|
||||
x76 := (u64(fiat.u1(x75)) + x61)
|
||||
x77, x78 := bits.sub_u64(x70, 0xffffffffffffffff, u64(0x0))
|
||||
x79, x80 := bits.sub_u64(x72, 0xffffffff, u64(fiat.u1(x78)))
|
||||
x81, x82 := bits.sub_u64(x74, u64(0x0), u64(fiat.u1(x80)))
|
||||
x83, x84 := bits.sub_u64(x76, 0xffffffff00000001, u64(fiat.u1(x82)))
|
||||
_, x86 := bits.sub_u64(u64(0x0), u64(0x0), u64(fiat.u1(x84)))
|
||||
x87 := fiat.cmovznz_u64(fiat.u1(x86), x77, x70)
|
||||
x88 := fiat.cmovznz_u64(fiat.u1(x86), x79, x72)
|
||||
x89 := fiat.cmovznz_u64(fiat.u1(x86), x81, x74)
|
||||
x90 := fiat.cmovznz_u64(fiat.u1(x86), x83, x76)
|
||||
out1[0] = x87
|
||||
out1[1] = x88
|
||||
out1[2] = x89
|
||||
out1[3] = x90
|
||||
}
|
||||
|
||||
fe_to_montgomery :: proc "contextless" (
|
||||
out1: ^Montgomery_Domain_Field_Element,
|
||||
arg1: ^Non_Montgomery_Domain_Field_Element,
|
||||
) {
|
||||
x1 := arg1[1]
|
||||
x2 := arg1[2]
|
||||
x3 := arg1[3]
|
||||
x4 := arg1[0]
|
||||
x6, x5 := bits.mul_u64(x4, 0x4fffffffd)
|
||||
x8, x7 := bits.mul_u64(x4, 0xfffffffffffffffe)
|
||||
x10, x9 := bits.mul_u64(x4, 0xfffffffbffffffff)
|
||||
x12, x11 := bits.mul_u64(x4, 0x3)
|
||||
x13, x14 := bits.add_u64(x12, x9, u64(0x0))
|
||||
x15, x16 := bits.add_u64(x10, x7, u64(fiat.u1(x14)))
|
||||
x17, x18 := bits.add_u64(x8, x5, u64(fiat.u1(x16)))
|
||||
x20, x19 := bits.mul_u64(x11, 0xffffffff00000001)
|
||||
x22, x21 := bits.mul_u64(x11, 0xffffffff)
|
||||
x24, x23 := bits.mul_u64(x11, 0xffffffffffffffff)
|
||||
x25, x26 := bits.add_u64(x24, x21, u64(0x0))
|
||||
_, x28 := bits.add_u64(x11, x23, u64(0x0))
|
||||
x29, x30 := bits.add_u64(x13, x25, u64(fiat.u1(x28)))
|
||||
x31, x32 := bits.add_u64(x15, (u64(fiat.u1(x26)) + x22), u64(fiat.u1(x30)))
|
||||
x33, x34 := bits.add_u64(x17, x19, u64(fiat.u1(x32)))
|
||||
x35, x36 := bits.add_u64((u64(fiat.u1(x18)) + x6), x20, u64(fiat.u1(x34)))
|
||||
x38, x37 := bits.mul_u64(x1, 0x4fffffffd)
|
||||
x40, x39 := bits.mul_u64(x1, 0xfffffffffffffffe)
|
||||
x42, x41 := bits.mul_u64(x1, 0xfffffffbffffffff)
|
||||
x44, x43 := bits.mul_u64(x1, 0x3)
|
||||
x45, x46 := bits.add_u64(x44, x41, u64(0x0))
|
||||
x47, x48 := bits.add_u64(x42, x39, u64(fiat.u1(x46)))
|
||||
x49, x50 := bits.add_u64(x40, x37, u64(fiat.u1(x48)))
|
||||
x51, x52 := bits.add_u64(x29, x43, u64(0x0))
|
||||
x53, x54 := bits.add_u64(x31, x45, u64(fiat.u1(x52)))
|
||||
x55, x56 := bits.add_u64(x33, x47, u64(fiat.u1(x54)))
|
||||
x57, x58 := bits.add_u64(x35, x49, u64(fiat.u1(x56)))
|
||||
x60, x59 := bits.mul_u64(x51, 0xffffffff00000001)
|
||||
x62, x61 := bits.mul_u64(x51, 0xffffffff)
|
||||
x64, x63 := bits.mul_u64(x51, 0xffffffffffffffff)
|
||||
x65, x66 := bits.add_u64(x64, x61, u64(0x0))
|
||||
_, x68 := bits.add_u64(x51, x63, u64(0x0))
|
||||
x69, x70 := bits.add_u64(x53, x65, u64(fiat.u1(x68)))
|
||||
x71, x72 := bits.add_u64(x55, (u64(fiat.u1(x66)) + x62), u64(fiat.u1(x70)))
|
||||
x73, x74 := bits.add_u64(x57, x59, u64(fiat.u1(x72)))
|
||||
x75, x76 := bits.add_u64(((u64(fiat.u1(x58)) + u64(fiat.u1(x36))) + (u64(fiat.u1(x50)) + x38)), x60, u64(fiat.u1(x74)))
|
||||
x78, x77 := bits.mul_u64(x2, 0x4fffffffd)
|
||||
x80, x79 := bits.mul_u64(x2, 0xfffffffffffffffe)
|
||||
x82, x81 := bits.mul_u64(x2, 0xfffffffbffffffff)
|
||||
x84, x83 := bits.mul_u64(x2, 0x3)
|
||||
x85, x86 := bits.add_u64(x84, x81, u64(0x0))
|
||||
x87, x88 := bits.add_u64(x82, x79, u64(fiat.u1(x86)))
|
||||
x89, x90 := bits.add_u64(x80, x77, u64(fiat.u1(x88)))
|
||||
x91, x92 := bits.add_u64(x69, x83, u64(0x0))
|
||||
x93, x94 := bits.add_u64(x71, x85, u64(fiat.u1(x92)))
|
||||
x95, x96 := bits.add_u64(x73, x87, u64(fiat.u1(x94)))
|
||||
x97, x98 := bits.add_u64(x75, x89, u64(fiat.u1(x96)))
|
||||
x100, x99 := bits.mul_u64(x91, 0xffffffff00000001)
|
||||
x102, x101 := bits.mul_u64(x91, 0xffffffff)
|
||||
x104, x103 := bits.mul_u64(x91, 0xffffffffffffffff)
|
||||
x105, x106 := bits.add_u64(x104, x101, u64(0x0))
|
||||
_, x108 := bits.add_u64(x91, x103, u64(0x0))
|
||||
x109, x110 := bits.add_u64(x93, x105, u64(fiat.u1(x108)))
|
||||
x111, x112 := bits.add_u64(x95, (u64(fiat.u1(x106)) + x102), u64(fiat.u1(x110)))
|
||||
x113, x114 := bits.add_u64(x97, x99, u64(fiat.u1(x112)))
|
||||
x115, x116 := bits.add_u64(((u64(fiat.u1(x98)) + u64(fiat.u1(x76))) + (u64(fiat.u1(x90)) + x78)), x100, u64(fiat.u1(x114)))
|
||||
x118, x117 := bits.mul_u64(x3, 0x4fffffffd)
|
||||
x120, x119 := bits.mul_u64(x3, 0xfffffffffffffffe)
|
||||
x122, x121 := bits.mul_u64(x3, 0xfffffffbffffffff)
|
||||
x124, x123 := bits.mul_u64(x3, 0x3)
|
||||
x125, x126 := bits.add_u64(x124, x121, u64(0x0))
|
||||
x127, x128 := bits.add_u64(x122, x119, u64(fiat.u1(x126)))
|
||||
x129, x130 := bits.add_u64(x120, x117, u64(fiat.u1(x128)))
|
||||
x131, x132 := bits.add_u64(x109, x123, u64(0x0))
|
||||
x133, x134 := bits.add_u64(x111, x125, u64(fiat.u1(x132)))
|
||||
x135, x136 := bits.add_u64(x113, x127, u64(fiat.u1(x134)))
|
||||
x137, x138 := bits.add_u64(x115, x129, u64(fiat.u1(x136)))
|
||||
x140, x139 := bits.mul_u64(x131, 0xffffffff00000001)
|
||||
x142, x141 := bits.mul_u64(x131, 0xffffffff)
|
||||
x144, x143 := bits.mul_u64(x131, 0xffffffffffffffff)
|
||||
x145, x146 := bits.add_u64(x144, x141, u64(0x0))
|
||||
_, x148 := bits.add_u64(x131, x143, u64(0x0))
|
||||
x149, x150 := bits.add_u64(x133, x145, u64(fiat.u1(x148)))
|
||||
x151, x152 := bits.add_u64(x135, (u64(fiat.u1(x146)) + x142), u64(fiat.u1(x150)))
|
||||
x153, x154 := bits.add_u64(x137, x139, u64(fiat.u1(x152)))
|
||||
x155, x156 := bits.add_u64(((u64(fiat.u1(x138)) + u64(fiat.u1(x116))) + (u64(fiat.u1(x130)) + x118)), x140, u64(fiat.u1(x154)))
|
||||
x157, x158 := bits.sub_u64(x149, 0xffffffffffffffff, u64(0x0))
|
||||
x159, x160 := bits.sub_u64(x151, 0xffffffff, u64(fiat.u1(x158)))
|
||||
x161, x162 := bits.sub_u64(x153, u64(0x0), u64(fiat.u1(x160)))
|
||||
x163, x164 := bits.sub_u64(x155, 0xffffffff00000001, u64(fiat.u1(x162)))
|
||||
_, x166 := bits.sub_u64(u64(fiat.u1(x156)), u64(0x0), u64(fiat.u1(x164)))
|
||||
x167 := fiat.cmovznz_u64(fiat.u1(x166), x157, x149)
|
||||
x168 := fiat.cmovznz_u64(fiat.u1(x166), x159, x151)
|
||||
x169 := fiat.cmovznz_u64(fiat.u1(x166), x161, x153)
|
||||
x170 := fiat.cmovznz_u64(fiat.u1(x166), x163, x155)
|
||||
out1[0] = x167
|
||||
out1[1] = x168
|
||||
out1[2] = x169
|
||||
out1[3] = x170
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package field_scalar25519
|
||||
|
||||
import subtle "core:crypto/_subtle"
|
||||
import "core:encoding/endian"
|
||||
import "core:math/bits"
|
||||
import "core:mem"
|
||||
@@ -121,13 +122,11 @@ fe_equal :: proc "contextless" (arg1, arg2: ^Montgomery_Domain_Field_Element) ->
|
||||
tmp: Montgomery_Domain_Field_Element
|
||||
fe_sub(&tmp, arg1, arg2)
|
||||
|
||||
// This will only underflow iff arg1 == arg2, and we return the borrow,
|
||||
// which will be 1.
|
||||
_, borrow := bits.sub_u64(fe_non_zero(&tmp), 1, 0)
|
||||
is_eq := subtle.eq(fe_non_zero(&tmp), 0)
|
||||
|
||||
fe_clear(&tmp)
|
||||
|
||||
return int(borrow)
|
||||
return int(is_eq)
|
||||
}
|
||||
|
||||
fe_zero :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element) {
|
||||
|
||||
210
core/crypto/_fiat/field_scalarp256r1/field.odin
Normal file
210
core/crypto/_fiat/field_scalarp256r1/field.odin
Normal file
@@ -0,0 +1,210 @@
|
||||
package field_scalarp256r1
|
||||
|
||||
import subtle "core:crypto/_subtle"
|
||||
import "core:encoding/endian"
|
||||
import "core:math/bits"
|
||||
import "core:mem"
|
||||
|
||||
@(private, rodata)
|
||||
TWO_192 := Montgomery_Domain_Field_Element{
|
||||
2482910415990817935,
|
||||
2879494685571067143,
|
||||
8732918506673730078,
|
||||
85565669603516024,
|
||||
}
|
||||
@(private, rodata)
|
||||
TWO_384 := Montgomery_Domain_Field_Element{
|
||||
2127524300190691059,
|
||||
17014302137236182484,
|
||||
16604910261202196099,
|
||||
3621421107472562910,
|
||||
}
|
||||
// 2^384 % p (From sage)
|
||||
// 0x431905529c0166ce652e96b7ccca0a99679b73e19ad16947f01cf013fc632551
|
||||
|
||||
fe_clear :: proc "contextless" (arg1: ^Montgomery_Domain_Field_Element) {
|
||||
mem.zero_explicit(arg1, size_of(Montgomery_Domain_Field_Element))
|
||||
}
|
||||
|
||||
fe_clear_vec :: proc "contextless" (
|
||||
arg1: []^Montgomery_Domain_Field_Element,
|
||||
) {
|
||||
for fe in arg1 {
|
||||
fe_clear(fe)
|
||||
}
|
||||
}
|
||||
|
||||
fe_from_bytes :: proc "contextless" (
|
||||
out1: ^Montgomery_Domain_Field_Element,
|
||||
arg1: []byte,
|
||||
) -> bool {
|
||||
ensure_contextless(len(out1) <= 64, "p256r1: invalid scalar input buffer")
|
||||
|
||||
is_canonical := false
|
||||
s_len := len(arg1)
|
||||
switch {
|
||||
case s_len < 32:
|
||||
// No way this can be greater than the order.
|
||||
fe_unchecked_set(out1, arg1)
|
||||
is_canonical = true
|
||||
case s_len == 32:
|
||||
// It is quite likely that a reduction mod p is required,
|
||||
// as the order of the curve is sufficiently smaller than
|
||||
// 2^256-1, so just check if we actually needed to reduced
|
||||
// and do the reduction anyway, so that things that require
|
||||
// canonical scalars can reject non-canonical encodings.
|
||||
is_canonical = fe_is_canonical(arg1)
|
||||
fallthrough
|
||||
case:
|
||||
// Use Frank Denis' trick, as documented by Filippo Valsorda
|
||||
// at https://words.filippo.io/dispatches/wide-reduction/
|
||||
//
|
||||
// "I represent the value as a+b*2^192+c*2^384"
|
||||
//
|
||||
// Note: Omitting the `c` computation is fine as, reduction
|
||||
// being length dependent provides no useful timing information.
|
||||
|
||||
// Zero extend to 512-bits.
|
||||
src_512: [64]byte
|
||||
copy(src_512[64-s_len:], arg1)
|
||||
defer mem.zero_explicit(&src_512, size_of(src_512))
|
||||
|
||||
fe_unchecked_set(out1, src_512[40:]) // a
|
||||
b: Montgomery_Domain_Field_Element
|
||||
fe_unchecked_set(&b, src_512[16:40]) // b
|
||||
|
||||
fe_mul(&b, &b, &TWO_192)
|
||||
fe_add(out1, out1, &b)
|
||||
if s_len >= 48 {
|
||||
c: Montgomery_Domain_Field_Element
|
||||
fe_unchecked_set(&c, src_512[:16]) // c
|
||||
fe_mul(&c, &c, &TWO_384)
|
||||
fe_add(out1, out1, &c)
|
||||
|
||||
fe_clear(&c)
|
||||
}
|
||||
|
||||
fe_clear(&b)
|
||||
}
|
||||
|
||||
return !is_canonical
|
||||
}
|
||||
|
||||
@(private)
|
||||
fe_is_canonical :: proc "contextless" (arg1: []byte) -> bool {
|
||||
_, borrow := bits.sub_u64(ELL[0] - 1, endian.unchecked_get_u64be(arg1[24:]), 0)
|
||||
_, borrow = bits.sub_u64(ELL[1], endian.unchecked_get_u64be(arg1[16:]), borrow)
|
||||
_, borrow = bits.sub_u64(ELL[2], endian.unchecked_get_u64be(arg1[8:]), borrow)
|
||||
_, borrow = bits.sub_u64(ELL[3], endian.unchecked_get_u64be(arg1[0:]), borrow)
|
||||
return borrow == 0
|
||||
}
|
||||
|
||||
@(private)
|
||||
fe_unchecked_set :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element, arg1: []byte) {
|
||||
arg1_256: [32]byte
|
||||
defer mem.zero_explicit(&arg1_256, size_of(arg1_256))
|
||||
copy(arg1_256[32-len(arg1):], arg1)
|
||||
|
||||
tmp := Non_Montgomery_Domain_Field_Element {
|
||||
endian.unchecked_get_u64be(arg1_256[24:]),
|
||||
endian.unchecked_get_u64be(arg1_256[16:]),
|
||||
endian.unchecked_get_u64be(arg1_256[8:]),
|
||||
endian.unchecked_get_u64be(arg1_256[0:]),
|
||||
}
|
||||
defer mem.zero_explicit(&tmp, size_of(tmp))
|
||||
|
||||
fe_to_montgomery(out1, &tmp)
|
||||
}
|
||||
|
||||
fe_to_bytes :: proc "contextless" (out1: []byte, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
ensure_contextless(len(out1) == 32, "p256r1: invalid scalar output buffer")
|
||||
|
||||
tmp: Non_Montgomery_Domain_Field_Element
|
||||
fe_from_montgomery(&tmp, arg1)
|
||||
|
||||
// Note: Likewise, output in big-endian.
|
||||
endian.unchecked_put_u64be(out1[24:], tmp[0])
|
||||
endian.unchecked_put_u64be(out1[16:], tmp[1])
|
||||
endian.unchecked_put_u64be(out1[8:], tmp[2])
|
||||
endian.unchecked_put_u64be(out1[0:], tmp[3])
|
||||
|
||||
mem.zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
|
||||
fe_equal :: proc "contextless" (arg1, arg2: ^Montgomery_Domain_Field_Element) -> int {
|
||||
tmp: Montgomery_Domain_Field_Element
|
||||
fe_sub(&tmp, arg1, arg2)
|
||||
|
||||
is_eq := subtle.u64_is_zero(fe_non_zero(&tmp))
|
||||
|
||||
fe_clear(&tmp)
|
||||
|
||||
return int(is_eq)
|
||||
}
|
||||
|
||||
fe_is_odd :: proc "contextless" (arg1: ^Montgomery_Domain_Field_Element) -> int {
|
||||
tmp: Non_Montgomery_Domain_Field_Element
|
||||
defer mem.zero_explicit(&tmp, size_of(tmp))
|
||||
|
||||
fe_from_montgomery(&tmp, arg1)
|
||||
return int(tmp[0] & 1)
|
||||
}
|
||||
|
||||
fe_zero :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element) {
|
||||
out1[0] = 0
|
||||
out1[1] = 0
|
||||
out1[2] = 0
|
||||
out1[3] = 0
|
||||
}
|
||||
|
||||
fe_set :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
x1 := arg1[0]
|
||||
x2 := arg1[1]
|
||||
x3 := arg1[2]
|
||||
x4 := arg1[3]
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
}
|
||||
|
||||
@(optimization_mode = "none")
|
||||
fe_cond_swap :: #force_no_inline proc "contextless" (out1, out2: ^Montgomery_Domain_Field_Element, arg1: int) {
|
||||
mask := (u64(arg1) * 0xffffffffffffffff)
|
||||
x := (out1[0] ~ out2[0]) & mask
|
||||
x1, y1 := out1[0] ~ x, out2[0] ~ x
|
||||
x = (out1[1] ~ out2[1]) & mask
|
||||
x2, y2 := out1[1] ~ x, out2[1] ~ x
|
||||
x = (out1[2] ~ out2[2]) & mask
|
||||
x3, y3 := out1[2] ~ x, out2[2] ~ x
|
||||
x = (out1[3] ~ out2[3]) & mask
|
||||
x4, y4 := out1[3] ~ x, out2[3] ~ x
|
||||
out1[0], out2[0] = x1, y1
|
||||
out1[1], out2[1] = x2, y2
|
||||
out1[2], out2[2] = x3, y3
|
||||
out1[3], out2[3] = x4, y4
|
||||
}
|
||||
|
||||
@(optimization_mode = "none")
|
||||
fe_cond_select :: #force_no_inline proc "contextless" (
|
||||
out1, arg1, arg2: ^Montgomery_Domain_Field_Element,
|
||||
arg3: int,
|
||||
) {
|
||||
mask := (u64(arg3) * 0xffffffffffffffff)
|
||||
x1 := ((mask & arg2[0]) | ((~mask) & arg1[0]))
|
||||
x2 := ((mask & arg2[1]) | ((~mask) & arg1[1]))
|
||||
x3 := ((mask & arg2[2]) | ((~mask) & arg1[2]))
|
||||
x4 := ((mask & arg2[3]) | ((~mask) & arg1[3]))
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
}
|
||||
|
||||
fe_cond_negate :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element, ctrl: int) {
|
||||
tmp1: Montgomery_Domain_Field_Element = ---
|
||||
fe_opp(&tmp1, arg1)
|
||||
fe_cond_select(out1, arg1, &tmp1, ctrl)
|
||||
|
||||
fe_clear(&tmp1)
|
||||
}
|
||||
569
core/crypto/_fiat/field_scalarp256r1/field64.odin
Normal file
569
core/crypto/_fiat/field_scalarp256r1/field64.odin
Normal file
@@ -0,0 +1,569 @@
|
||||
// The BSD 1-Clause License (BSD-1-Clause)
|
||||
//
|
||||
// Copyright (c) 2015-2020 the fiat-crypto authors (see the AUTHORS file)
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY the fiat-crypto authors "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design,
|
||||
// Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package field_scalarp256r1
|
||||
|
||||
// The file provides arithmetic on the field Z/(2^256 - 2^224 + 2^192 -
|
||||
// 89188191075325690597107910205041859247) using a 64-bit Montgomery form
|
||||
// internal representation. It is derived primarily from the machine
|
||||
// generated Golang output from the fiat-crypto project.
|
||||
//
|
||||
// While the base implementation is provably correct, this implementation
|
||||
// makes no such claims as the port and optimizations were done by hand.
|
||||
//
|
||||
// WARNING: While big-endian is the common representation used for this
|
||||
// curve, the fiat output uses least-significant-limb first.
|
||||
|
||||
import fiat "core:crypto/_fiat"
|
||||
import "core:math/bits"
|
||||
|
||||
// ELL is the saturated representation of the field order, least-significant
|
||||
// limb first.
|
||||
ELL :: [4]u64{0xf3b9cac2fc632551, 0xbce6faada7179e84, 0xffffffffffffffff, 0xffffffff00000000}
|
||||
|
||||
Montgomery_Domain_Field_Element :: distinct [4]u64
|
||||
Non_Montgomery_Domain_Field_Element :: distinct [4]u64
|
||||
|
||||
fe_mul :: proc "contextless" (out1, arg1, arg2: ^Montgomery_Domain_Field_Element) {
|
||||
x1 := arg1[1]
|
||||
x2 := arg1[2]
|
||||
x3 := arg1[3]
|
||||
x4 := arg1[0]
|
||||
x6, x5 := bits.mul_u64(x4, arg2[3])
|
||||
x8, x7 := bits.mul_u64(x4, arg2[2])
|
||||
x10, x9 := bits.mul_u64(x4, arg2[1])
|
||||
x12, x11 := bits.mul_u64(x4, arg2[0])
|
||||
x13, x14 := bits.add_u64(x12, x9, u64(0x0))
|
||||
x15, x16 := bits.add_u64(x10, x7, u64(fiat.u1(x14)))
|
||||
x17, x18 := bits.add_u64(x8, x5, u64(fiat.u1(x16)))
|
||||
x19 := (u64(fiat.u1(x18)) + x6)
|
||||
_, x20 := bits.mul_u64(x11, 0xccd1c8aaee00bc4f)
|
||||
x23, x22 := bits.mul_u64(x20, 0xffffffff00000000)
|
||||
x25, x24 := bits.mul_u64(x20, 0xffffffffffffffff)
|
||||
x27, x26 := bits.mul_u64(x20, 0xbce6faada7179e84)
|
||||
x29, x28 := bits.mul_u64(x20, 0xf3b9cac2fc632551)
|
||||
x30, x31 := bits.add_u64(x29, x26, u64(0x0))
|
||||
x32, x33 := bits.add_u64(x27, x24, u64(fiat.u1(x31)))
|
||||
x34, x35 := bits.add_u64(x25, x22, u64(fiat.u1(x33)))
|
||||
x36 := (u64(fiat.u1(x35)) + x23)
|
||||
_, x38 := bits.add_u64(x11, x28, u64(0x0))
|
||||
x39, x40 := bits.add_u64(x13, x30, u64(fiat.u1(x38)))
|
||||
x41, x42 := bits.add_u64(x15, x32, u64(fiat.u1(x40)))
|
||||
x43, x44 := bits.add_u64(x17, x34, u64(fiat.u1(x42)))
|
||||
x45, x46 := bits.add_u64(x19, x36, u64(fiat.u1(x44)))
|
||||
x48, x47 := bits.mul_u64(x1, arg2[3])
|
||||
x50, x49 := bits.mul_u64(x1, arg2[2])
|
||||
x52, x51 := bits.mul_u64(x1, arg2[1])
|
||||
x54, x53 := bits.mul_u64(x1, arg2[0])
|
||||
x55, x56 := bits.add_u64(x54, x51, u64(0x0))
|
||||
x57, x58 := bits.add_u64(x52, x49, u64(fiat.u1(x56)))
|
||||
x59, x60 := bits.add_u64(x50, x47, u64(fiat.u1(x58)))
|
||||
x61 := (u64(fiat.u1(x60)) + x48)
|
||||
x62, x63 := bits.add_u64(x39, x53, u64(0x0))
|
||||
x64, x65 := bits.add_u64(x41, x55, u64(fiat.u1(x63)))
|
||||
x66, x67 := bits.add_u64(x43, x57, u64(fiat.u1(x65)))
|
||||
x68, x69 := bits.add_u64(x45, x59, u64(fiat.u1(x67)))
|
||||
x70, x71 := bits.add_u64(u64(fiat.u1(x46)), x61, u64(fiat.u1(x69)))
|
||||
_, x72 := bits.mul_u64(x62, 0xccd1c8aaee00bc4f)
|
||||
x75, x74 := bits.mul_u64(x72, 0xffffffff00000000)
|
||||
x77, x76 := bits.mul_u64(x72, 0xffffffffffffffff)
|
||||
x79, x78 := bits.mul_u64(x72, 0xbce6faada7179e84)
|
||||
x81, x80 := bits.mul_u64(x72, 0xf3b9cac2fc632551)
|
||||
x82, x83 := bits.add_u64(x81, x78, u64(0x0))
|
||||
x84, x85 := bits.add_u64(x79, x76, u64(fiat.u1(x83)))
|
||||
x86, x87 := bits.add_u64(x77, x74, u64(fiat.u1(x85)))
|
||||
x88 := (u64(fiat.u1(x87)) + x75)
|
||||
_, x90 := bits.add_u64(x62, x80, u64(0x0))
|
||||
x91, x92 := bits.add_u64(x64, x82, u64(fiat.u1(x90)))
|
||||
x93, x94 := bits.add_u64(x66, x84, u64(fiat.u1(x92)))
|
||||
x95, x96 := bits.add_u64(x68, x86, u64(fiat.u1(x94)))
|
||||
x97, x98 := bits.add_u64(x70, x88, u64(fiat.u1(x96)))
|
||||
x99 := (u64(fiat.u1(x98)) + u64(fiat.u1(x71)))
|
||||
x101, x100 := bits.mul_u64(x2, arg2[3])
|
||||
x103, x102 := bits.mul_u64(x2, arg2[2])
|
||||
x105, x104 := bits.mul_u64(x2, arg2[1])
|
||||
x107, x106 := bits.mul_u64(x2, arg2[0])
|
||||
x108, x109 := bits.add_u64(x107, x104, u64(0x0))
|
||||
x110, x111 := bits.add_u64(x105, x102, u64(fiat.u1(x109)))
|
||||
x112, x113 := bits.add_u64(x103, x100, u64(fiat.u1(x111)))
|
||||
x114 := (u64(fiat.u1(x113)) + x101)
|
||||
x115, x116 := bits.add_u64(x91, x106, u64(0x0))
|
||||
x117, x118 := bits.add_u64(x93, x108, u64(fiat.u1(x116)))
|
||||
x119, x120 := bits.add_u64(x95, x110, u64(fiat.u1(x118)))
|
||||
x121, x122 := bits.add_u64(x97, x112, u64(fiat.u1(x120)))
|
||||
x123, x124 := bits.add_u64(x99, x114, u64(fiat.u1(x122)))
|
||||
_, x125 := bits.mul_u64(x115, 0xccd1c8aaee00bc4f)
|
||||
x128, x127 := bits.mul_u64(x125, 0xffffffff00000000)
|
||||
x130, x129 := bits.mul_u64(x125, 0xffffffffffffffff)
|
||||
x132, x131 := bits.mul_u64(x125, 0xbce6faada7179e84)
|
||||
x134, x133 := bits.mul_u64(x125, 0xf3b9cac2fc632551)
|
||||
x135, x136 := bits.add_u64(x134, x131, u64(0x0))
|
||||
x137, x138 := bits.add_u64(x132, x129, u64(fiat.u1(x136)))
|
||||
x139, x140 := bits.add_u64(x130, x127, u64(fiat.u1(x138)))
|
||||
x141 := (u64(fiat.u1(x140)) + x128)
|
||||
_, x143 := bits.add_u64(x115, x133, u64(0x0))
|
||||
x144, x145 := bits.add_u64(x117, x135, u64(fiat.u1(x143)))
|
||||
x146, x147 := bits.add_u64(x119, x137, u64(fiat.u1(x145)))
|
||||
x148, x149 := bits.add_u64(x121, x139, u64(fiat.u1(x147)))
|
||||
x150, x151 := bits.add_u64(x123, x141, u64(fiat.u1(x149)))
|
||||
x152 := (u64(fiat.u1(x151)) + u64(fiat.u1(x124)))
|
||||
x154, x153 := bits.mul_u64(x3, arg2[3])
|
||||
x156, x155 := bits.mul_u64(x3, arg2[2])
|
||||
x158, x157 := bits.mul_u64(x3, arg2[1])
|
||||
x160, x159 := bits.mul_u64(x3, arg2[0])
|
||||
x161, x162 := bits.add_u64(x160, x157, u64(0x0))
|
||||
x163, x164 := bits.add_u64(x158, x155, u64(fiat.u1(x162)))
|
||||
x165, x166 := bits.add_u64(x156, x153, u64(fiat.u1(x164)))
|
||||
x167 := (u64(fiat.u1(x166)) + x154)
|
||||
x168, x169 := bits.add_u64(x144, x159, u64(0x0))
|
||||
x170, x171 := bits.add_u64(x146, x161, u64(fiat.u1(x169)))
|
||||
x172, x173 := bits.add_u64(x148, x163, u64(fiat.u1(x171)))
|
||||
x174, x175 := bits.add_u64(x150, x165, u64(fiat.u1(x173)))
|
||||
x176, x177 := bits.add_u64(x152, x167, u64(fiat.u1(x175)))
|
||||
_, x178 := bits.mul_u64(x168, 0xccd1c8aaee00bc4f)
|
||||
x181, x180 := bits.mul_u64(x178, 0xffffffff00000000)
|
||||
x183, x182 := bits.mul_u64(x178, 0xffffffffffffffff)
|
||||
x185, x184 := bits.mul_u64(x178, 0xbce6faada7179e84)
|
||||
x187, x186 := bits.mul_u64(x178, 0xf3b9cac2fc632551)
|
||||
x188, x189 := bits.add_u64(x187, x184, u64(0x0))
|
||||
x190, x191 := bits.add_u64(x185, x182, u64(fiat.u1(x189)))
|
||||
x192, x193 := bits.add_u64(x183, x180, u64(fiat.u1(x191)))
|
||||
x194 := (u64(fiat.u1(x193)) + x181)
|
||||
_, x196 := bits.add_u64(x168, x186, u64(0x0))
|
||||
x197, x198 := bits.add_u64(x170, x188, u64(fiat.u1(x196)))
|
||||
x199, x200 := bits.add_u64(x172, x190, u64(fiat.u1(x198)))
|
||||
x201, x202 := bits.add_u64(x174, x192, u64(fiat.u1(x200)))
|
||||
x203, x204 := bits.add_u64(x176, x194, u64(fiat.u1(x202)))
|
||||
x205 := (u64(fiat.u1(x204)) + u64(fiat.u1(x177)))
|
||||
x206, x207 := bits.sub_u64(x197, 0xf3b9cac2fc632551, u64(0x0))
|
||||
x208, x209 := bits.sub_u64(x199, 0xbce6faada7179e84, u64(fiat.u1(x207)))
|
||||
x210, x211 := bits.sub_u64(x201, 0xffffffffffffffff, u64(fiat.u1(x209)))
|
||||
x212, x213 := bits.sub_u64(x203, 0xffffffff00000000, u64(fiat.u1(x211)))
|
||||
_, x215 := bits.sub_u64(x205, u64(0x0), u64(fiat.u1(x213)))
|
||||
x216 := fiat.cmovznz_u64(fiat.u1(x215), x206, x197)
|
||||
x217 := fiat.cmovznz_u64(fiat.u1(x215), x208, x199)
|
||||
x218 := fiat.cmovznz_u64(fiat.u1(x215), x210, x201)
|
||||
x219 := fiat.cmovznz_u64(fiat.u1(x215), x212, x203)
|
||||
out1[0] = x216
|
||||
out1[1] = x217
|
||||
out1[2] = x218
|
||||
out1[3] = x219
|
||||
}
|
||||
|
||||
fe_square :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
x1 := arg1[1]
|
||||
x2 := arg1[2]
|
||||
x3 := arg1[3]
|
||||
x4 := arg1[0]
|
||||
x6, x5 := bits.mul_u64(x4, arg1[3])
|
||||
x8, x7 := bits.mul_u64(x4, arg1[2])
|
||||
x10, x9 := bits.mul_u64(x4, arg1[1])
|
||||
x12, x11 := bits.mul_u64(x4, arg1[0])
|
||||
x13, x14 := bits.add_u64(x12, x9, u64(0x0))
|
||||
x15, x16 := bits.add_u64(x10, x7, u64(fiat.u1(x14)))
|
||||
x17, x18 := bits.add_u64(x8, x5, u64(fiat.u1(x16)))
|
||||
x19 := (u64(fiat.u1(x18)) + x6)
|
||||
_, x20 := bits.mul_u64(x11, 0xccd1c8aaee00bc4f)
|
||||
x23, x22 := bits.mul_u64(x20, 0xffffffff00000000)
|
||||
x25, x24 := bits.mul_u64(x20, 0xffffffffffffffff)
|
||||
x27, x26 := bits.mul_u64(x20, 0xbce6faada7179e84)
|
||||
x29, x28 := bits.mul_u64(x20, 0xf3b9cac2fc632551)
|
||||
x30, x31 := bits.add_u64(x29, x26, u64(0x0))
|
||||
x32, x33 := bits.add_u64(x27, x24, u64(fiat.u1(x31)))
|
||||
x34, x35 := bits.add_u64(x25, x22, u64(fiat.u1(x33)))
|
||||
x36 := (u64(fiat.u1(x35)) + x23)
|
||||
_, x38 := bits.add_u64(x11, x28, u64(0x0))
|
||||
x39, x40 := bits.add_u64(x13, x30, u64(fiat.u1(x38)))
|
||||
x41, x42 := bits.add_u64(x15, x32, u64(fiat.u1(x40)))
|
||||
x43, x44 := bits.add_u64(x17, x34, u64(fiat.u1(x42)))
|
||||
x45, x46 := bits.add_u64(x19, x36, u64(fiat.u1(x44)))
|
||||
x48, x47 := bits.mul_u64(x1, arg1[3])
|
||||
x50, x49 := bits.mul_u64(x1, arg1[2])
|
||||
x52, x51 := bits.mul_u64(x1, arg1[1])
|
||||
x54, x53 := bits.mul_u64(x1, arg1[0])
|
||||
x55, x56 := bits.add_u64(x54, x51, u64(0x0))
|
||||
x57, x58 := bits.add_u64(x52, x49, u64(fiat.u1(x56)))
|
||||
x59, x60 := bits.add_u64(x50, x47, u64(fiat.u1(x58)))
|
||||
x61 := (u64(fiat.u1(x60)) + x48)
|
||||
x62, x63 := bits.add_u64(x39, x53, u64(0x0))
|
||||
x64, x65 := bits.add_u64(x41, x55, u64(fiat.u1(x63)))
|
||||
x66, x67 := bits.add_u64(x43, x57, u64(fiat.u1(x65)))
|
||||
x68, x69 := bits.add_u64(x45, x59, u64(fiat.u1(x67)))
|
||||
x70, x71 := bits.add_u64(u64(fiat.u1(x46)), x61, u64(fiat.u1(x69)))
|
||||
_, x72 := bits.mul_u64(x62, 0xccd1c8aaee00bc4f)
|
||||
x75, x74 := bits.mul_u64(x72, 0xffffffff00000000)
|
||||
x77, x76 := bits.mul_u64(x72, 0xffffffffffffffff)
|
||||
x79, x78 := bits.mul_u64(x72, 0xbce6faada7179e84)
|
||||
x81, x80 := bits.mul_u64(x72, 0xf3b9cac2fc632551)
|
||||
x82, x83 := bits.add_u64(x81, x78, u64(0x0))
|
||||
x84, x85 := bits.add_u64(x79, x76, u64(fiat.u1(x83)))
|
||||
x86, x87 := bits.add_u64(x77, x74, u64(fiat.u1(x85)))
|
||||
x88 := (u64(fiat.u1(x87)) + x75)
|
||||
_, x90 := bits.add_u64(x62, x80, u64(0x0))
|
||||
x91, x92 := bits.add_u64(x64, x82, u64(fiat.u1(x90)))
|
||||
x93, x94 := bits.add_u64(x66, x84, u64(fiat.u1(x92)))
|
||||
x95, x96 := bits.add_u64(x68, x86, u64(fiat.u1(x94)))
|
||||
x97, x98 := bits.add_u64(x70, x88, u64(fiat.u1(x96)))
|
||||
x99 := (u64(fiat.u1(x98)) + u64(fiat.u1(x71)))
|
||||
x101, x100 := bits.mul_u64(x2, arg1[3])
|
||||
x103, x102 := bits.mul_u64(x2, arg1[2])
|
||||
x105, x104 := bits.mul_u64(x2, arg1[1])
|
||||
x107, x106 := bits.mul_u64(x2, arg1[0])
|
||||
x108, x109 := bits.add_u64(x107, x104, u64(0x0))
|
||||
x110, x111 := bits.add_u64(x105, x102, u64(fiat.u1(x109)))
|
||||
x112, x113 := bits.add_u64(x103, x100, u64(fiat.u1(x111)))
|
||||
x114 := (u64(fiat.u1(x113)) + x101)
|
||||
x115, x116 := bits.add_u64(x91, x106, u64(0x0))
|
||||
x117, x118 := bits.add_u64(x93, x108, u64(fiat.u1(x116)))
|
||||
x119, x120 := bits.add_u64(x95, x110, u64(fiat.u1(x118)))
|
||||
x121, x122 := bits.add_u64(x97, x112, u64(fiat.u1(x120)))
|
||||
x123, x124 := bits.add_u64(x99, x114, u64(fiat.u1(x122)))
|
||||
_, x125 := bits.mul_u64(x115, 0xccd1c8aaee00bc4f)
|
||||
x128, x127 := bits.mul_u64(x125, 0xffffffff00000000)
|
||||
x130, x129 := bits.mul_u64(x125, 0xffffffffffffffff)
|
||||
x132, x131 := bits.mul_u64(x125, 0xbce6faada7179e84)
|
||||
x134, x133 := bits.mul_u64(x125, 0xf3b9cac2fc632551)
|
||||
x135, x136 := bits.add_u64(x134, x131, u64(0x0))
|
||||
x137, x138 := bits.add_u64(x132, x129, u64(fiat.u1(x136)))
|
||||
x139, x140 := bits.add_u64(x130, x127, u64(fiat.u1(x138)))
|
||||
x141 := (u64(fiat.u1(x140)) + x128)
|
||||
_, x143 := bits.add_u64(x115, x133, u64(0x0))
|
||||
x144, x145 := bits.add_u64(x117, x135, u64(fiat.u1(x143)))
|
||||
x146, x147 := bits.add_u64(x119, x137, u64(fiat.u1(x145)))
|
||||
x148, x149 := bits.add_u64(x121, x139, u64(fiat.u1(x147)))
|
||||
x150, x151 := bits.add_u64(x123, x141, u64(fiat.u1(x149)))
|
||||
x152 := (u64(fiat.u1(x151)) + u64(fiat.u1(x124)))
|
||||
x154, x153 := bits.mul_u64(x3, arg1[3])
|
||||
x156, x155 := bits.mul_u64(x3, arg1[2])
|
||||
x158, x157 := bits.mul_u64(x3, arg1[1])
|
||||
x160, x159 := bits.mul_u64(x3, arg1[0])
|
||||
x161, x162 := bits.add_u64(x160, x157, u64(0x0))
|
||||
x163, x164 := bits.add_u64(x158, x155, u64(fiat.u1(x162)))
|
||||
x165, x166 := bits.add_u64(x156, x153, u64(fiat.u1(x164)))
|
||||
x167 := (u64(fiat.u1(x166)) + x154)
|
||||
x168, x169 := bits.add_u64(x144, x159, u64(0x0))
|
||||
x170, x171 := bits.add_u64(x146, x161, u64(fiat.u1(x169)))
|
||||
x172, x173 := bits.add_u64(x148, x163, u64(fiat.u1(x171)))
|
||||
x174, x175 := bits.add_u64(x150, x165, u64(fiat.u1(x173)))
|
||||
x176, x177 := bits.add_u64(x152, x167, u64(fiat.u1(x175)))
|
||||
_, x178 := bits.mul_u64(x168, 0xccd1c8aaee00bc4f)
|
||||
x181, x180 := bits.mul_u64(x178, 0xffffffff00000000)
|
||||
x183, x182 := bits.mul_u64(x178, 0xffffffffffffffff)
|
||||
x185, x184 := bits.mul_u64(x178, 0xbce6faada7179e84)
|
||||
x187, x186 := bits.mul_u64(x178, 0xf3b9cac2fc632551)
|
||||
x188, x189 := bits.add_u64(x187, x184, u64(0x0))
|
||||
x190, x191 := bits.add_u64(x185, x182, u64(fiat.u1(x189)))
|
||||
x192, x193 := bits.add_u64(x183, x180, u64(fiat.u1(x191)))
|
||||
x194 := (u64(fiat.u1(x193)) + x181)
|
||||
_, x196 := bits.add_u64(x168, x186, u64(0x0))
|
||||
x197, x198 := bits.add_u64(x170, x188, u64(fiat.u1(x196)))
|
||||
x199, x200 := bits.add_u64(x172, x190, u64(fiat.u1(x198)))
|
||||
x201, x202 := bits.add_u64(x174, x192, u64(fiat.u1(x200)))
|
||||
x203, x204 := bits.add_u64(x176, x194, u64(fiat.u1(x202)))
|
||||
x205 := (u64(fiat.u1(x204)) + u64(fiat.u1(x177)))
|
||||
x206, x207 := bits.sub_u64(x197, 0xf3b9cac2fc632551, u64(0x0))
|
||||
x208, x209 := bits.sub_u64(x199, 0xbce6faada7179e84, u64(fiat.u1(x207)))
|
||||
x210, x211 := bits.sub_u64(x201, 0xffffffffffffffff, u64(fiat.u1(x209)))
|
||||
x212, x213 := bits.sub_u64(x203, 0xffffffff00000000, u64(fiat.u1(x211)))
|
||||
_, x215 := bits.sub_u64(x205, u64(0x0), u64(fiat.u1(x213)))
|
||||
x216 := fiat.cmovznz_u64(fiat.u1(x215), x206, x197)
|
||||
x217 := fiat.cmovznz_u64(fiat.u1(x215), x208, x199)
|
||||
x218 := fiat.cmovznz_u64(fiat.u1(x215), x210, x201)
|
||||
x219 := fiat.cmovznz_u64(fiat.u1(x215), x212, x203)
|
||||
out1[0] = x216
|
||||
out1[1] = x217
|
||||
out1[2] = x218
|
||||
out1[3] = x219
|
||||
}
|
||||
|
||||
fe_add :: proc "contextless" (out1, arg1, arg2: ^Montgomery_Domain_Field_Element) {
|
||||
x1, x2 := bits.add_u64(arg1[0], arg2[0], u64(0x0))
|
||||
x3, x4 := bits.add_u64(arg1[1], arg2[1], u64(fiat.u1(x2)))
|
||||
x5, x6 := bits.add_u64(arg1[2], arg2[2], u64(fiat.u1(x4)))
|
||||
x7, x8 := bits.add_u64(arg1[3], arg2[3], u64(fiat.u1(x6)))
|
||||
x9, x10 := bits.sub_u64(x1, 0xf3b9cac2fc632551, u64(0x0))
|
||||
x11, x12 := bits.sub_u64(x3, 0xbce6faada7179e84, u64(fiat.u1(x10)))
|
||||
x13, x14 := bits.sub_u64(x5, 0xffffffffffffffff, u64(fiat.u1(x12)))
|
||||
x15, x16 := bits.sub_u64(x7, 0xffffffff00000000, u64(fiat.u1(x14)))
|
||||
_, x18 := bits.sub_u64(u64(fiat.u1(x8)), u64(0x0), u64(fiat.u1(x16)))
|
||||
x19 := fiat.cmovznz_u64(fiat.u1(x18), x9, x1)
|
||||
x20 := fiat.cmovznz_u64(fiat.u1(x18), x11, x3)
|
||||
x21 := fiat.cmovznz_u64(fiat.u1(x18), x13, x5)
|
||||
x22 := fiat.cmovznz_u64(fiat.u1(x18), x15, x7)
|
||||
out1[0] = x19
|
||||
out1[1] = x20
|
||||
out1[2] = x21
|
||||
out1[3] = x22
|
||||
}
|
||||
|
||||
fe_sub :: proc "contextless" (out1, arg1, arg2: ^Montgomery_Domain_Field_Element) {
|
||||
x1, x2 := bits.sub_u64(arg1[0], arg2[0], u64(0x0))
|
||||
x3, x4 := bits.sub_u64(arg1[1], arg2[1], u64(fiat.u1(x2)))
|
||||
x5, x6 := bits.sub_u64(arg1[2], arg2[2], u64(fiat.u1(x4)))
|
||||
x7, x8 := bits.sub_u64(arg1[3], arg2[3], u64(fiat.u1(x6)))
|
||||
x9 := fiat.cmovznz_u64(fiat.u1(x8), u64(0x0), 0xffffffffffffffff)
|
||||
x10, x11 := bits.add_u64(x1, (x9 & 0xf3b9cac2fc632551), u64(0x0))
|
||||
x12, x13 := bits.add_u64(x3, (x9 & 0xbce6faada7179e84), u64(fiat.u1(x11)))
|
||||
x14, x15 := bits.add_u64(x5, x9, u64(fiat.u1(x13)))
|
||||
x16, _ := bits.add_u64(x7, (x9 & 0xffffffff00000000), u64(fiat.u1(x15)))
|
||||
out1[0] = x10
|
||||
out1[1] = x12
|
||||
out1[2] = x14
|
||||
out1[3] = x16
|
||||
}
|
||||
|
||||
fe_opp :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
x1, x2 := bits.sub_u64(u64(0x0), arg1[0], u64(0x0))
|
||||
x3, x4 := bits.sub_u64(u64(0x0), arg1[1], u64(fiat.u1(x2)))
|
||||
x5, x6 := bits.sub_u64(u64(0x0), arg1[2], u64(fiat.u1(x4)))
|
||||
x7, x8 := bits.sub_u64(u64(0x0), arg1[3], u64(fiat.u1(x6)))
|
||||
x9 := fiat.cmovznz_u64(fiat.u1(x8), u64(0x0), 0xffffffffffffffff)
|
||||
x10, x11 := bits.add_u64(x1, (x9 & 0xf3b9cac2fc632551), u64(0x0))
|
||||
x12, x13 := bits.add_u64(x3, (x9 & 0xbce6faada7179e84), u64(fiat.u1(x11)))
|
||||
x14, x15 := bits.add_u64(x5, x9, u64(fiat.u1(x13)))
|
||||
x16, _ := bits.add_u64(x7, (x9 & 0xffffffff00000000), u64(fiat.u1(x15)))
|
||||
out1[0] = x10
|
||||
out1[1] = x12
|
||||
out1[2] = x14
|
||||
out1[3] = x16
|
||||
}
|
||||
|
||||
fe_one :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element) {
|
||||
out1[0] = 0xc46353d039cdaaf
|
||||
out1[1] = 0x4319055258e8617b
|
||||
out1[2] = u64(0x0)
|
||||
out1[3] = 0xffffffff
|
||||
}
|
||||
|
||||
fe_non_zero :: proc "contextless" (arg1: ^Montgomery_Domain_Field_Element) -> u64 {
|
||||
return arg1[0] | (arg1[1] | (arg1[2] | arg1[3]))
|
||||
}
|
||||
|
||||
@(optimization_mode = "none")
|
||||
fe_cond_assign :: #force_no_inline proc "contextless" (
|
||||
out1, arg1: ^Montgomery_Domain_Field_Element,
|
||||
arg2: int,
|
||||
) {
|
||||
x1 := fiat.cmovznz_u64(fiat.u1(arg2), out1[0], arg1[0])
|
||||
x2 := fiat.cmovznz_u64(fiat.u1(arg2), out1[1], arg1[1])
|
||||
x3 := fiat.cmovznz_u64(fiat.u1(arg2), out1[2], arg1[2])
|
||||
x4 := fiat.cmovznz_u64(fiat.u1(arg2), out1[3], arg1[3])
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
}
|
||||
|
||||
fe_from_montgomery :: proc "contextless" (
|
||||
out1: ^Non_Montgomery_Domain_Field_Element,
|
||||
arg1: ^Montgomery_Domain_Field_Element,
|
||||
) {
|
||||
x1 := arg1[0]
|
||||
_, x2 := bits.mul_u64(x1, 0xccd1c8aaee00bc4f)
|
||||
x5, x4 := bits.mul_u64(x2, 0xffffffff00000000)
|
||||
x7, x6 := bits.mul_u64(x2, 0xffffffffffffffff)
|
||||
x9, x8 := bits.mul_u64(x2, 0xbce6faada7179e84)
|
||||
x11, x10 := bits.mul_u64(x2, 0xf3b9cac2fc632551)
|
||||
x12, x13 := bits.add_u64(x11, x8, u64(0x0))
|
||||
x14, x15 := bits.add_u64(x9, x6, u64(fiat.u1(x13)))
|
||||
x16, x17 := bits.add_u64(x7, x4, u64(fiat.u1(x15)))
|
||||
_, x19 := bits.add_u64(x1, x10, u64(0x0))
|
||||
x20, x21 := bits.add_u64(u64(0x0), x12, u64(fiat.u1(x19)))
|
||||
x22, x23 := bits.add_u64(u64(0x0), x14, u64(fiat.u1(x21)))
|
||||
x24, x25 := bits.add_u64(u64(0x0), x16, u64(fiat.u1(x23)))
|
||||
x26, x27 := bits.add_u64(x20, arg1[1], u64(0x0))
|
||||
x28, x29 := bits.add_u64(x22, u64(0x0), u64(fiat.u1(x27)))
|
||||
x30, x31 := bits.add_u64(x24, u64(0x0), u64(fiat.u1(x29)))
|
||||
_, x32 := bits.mul_u64(x26, 0xccd1c8aaee00bc4f)
|
||||
x35, x34 := bits.mul_u64(x32, 0xffffffff00000000)
|
||||
x37, x36 := bits.mul_u64(x32, 0xffffffffffffffff)
|
||||
x39, x38 := bits.mul_u64(x32, 0xbce6faada7179e84)
|
||||
x41, x40 := bits.mul_u64(x32, 0xf3b9cac2fc632551)
|
||||
x42, x43 := bits.add_u64(x41, x38, u64(0x0))
|
||||
x44, x45 := bits.add_u64(x39, x36, u64(fiat.u1(x43)))
|
||||
x46, x47 := bits.add_u64(x37, x34, u64(fiat.u1(x45)))
|
||||
_, x49 := bits.add_u64(x26, x40, u64(0x0))
|
||||
x50, x51 := bits.add_u64(x28, x42, u64(fiat.u1(x49)))
|
||||
x52, x53 := bits.add_u64(x30, x44, u64(fiat.u1(x51)))
|
||||
x54, x55 := bits.add_u64((u64(fiat.u1(x31)) + (u64(fiat.u1(x25)) + (u64(fiat.u1(x17)) + x5))), x46, u64(fiat.u1(x53)))
|
||||
x56, x57 := bits.add_u64(x50, arg1[2], u64(0x0))
|
||||
x58, x59 := bits.add_u64(x52, u64(0x0), u64(fiat.u1(x57)))
|
||||
x60, x61 := bits.add_u64(x54, u64(0x0), u64(fiat.u1(x59)))
|
||||
_, x62 := bits.mul_u64(x56, 0xccd1c8aaee00bc4f)
|
||||
x65, x64 := bits.mul_u64(x62, 0xffffffff00000000)
|
||||
x67, x66 := bits.mul_u64(x62, 0xffffffffffffffff)
|
||||
x69, x68 := bits.mul_u64(x62, 0xbce6faada7179e84)
|
||||
x71, x70 := bits.mul_u64(x62, 0xf3b9cac2fc632551)
|
||||
x72, x73 := bits.add_u64(x71, x68, u64(0x0))
|
||||
x74, x75 := bits.add_u64(x69, x66, u64(fiat.u1(x73)))
|
||||
x76, x77 := bits.add_u64(x67, x64, u64(fiat.u1(x75)))
|
||||
_, x79 := bits.add_u64(x56, x70, u64(0x0))
|
||||
x80, x81 := bits.add_u64(x58, x72, u64(fiat.u1(x79)))
|
||||
x82, x83 := bits.add_u64(x60, x74, u64(fiat.u1(x81)))
|
||||
x84, x85 := bits.add_u64((u64(fiat.u1(x61)) + (u64(fiat.u1(x55)) + (u64(fiat.u1(x47)) + x35))), x76, u64(fiat.u1(x83)))
|
||||
x86, x87 := bits.add_u64(x80, arg1[3], u64(0x0))
|
||||
x88, x89 := bits.add_u64(x82, u64(0x0), u64(fiat.u1(x87)))
|
||||
x90, x91 := bits.add_u64(x84, u64(0x0), u64(fiat.u1(x89)))
|
||||
_, x92 := bits.mul_u64(x86, 0xccd1c8aaee00bc4f)
|
||||
x95, x94 := bits.mul_u64(x92, 0xffffffff00000000)
|
||||
x97, x96 := bits.mul_u64(x92, 0xffffffffffffffff)
|
||||
x99, x98 := bits.mul_u64(x92, 0xbce6faada7179e84)
|
||||
x101, x100 := bits.mul_u64(x92, 0xf3b9cac2fc632551)
|
||||
x102, x103 := bits.add_u64(x101, x98, u64(0x0))
|
||||
x104, x105 := bits.add_u64(x99, x96, u64(fiat.u1(x103)))
|
||||
x106, x107 := bits.add_u64(x97, x94, u64(fiat.u1(x105)))
|
||||
_, x109 := bits.add_u64(x86, x100, u64(0x0))
|
||||
x110, x111 := bits.add_u64(x88, x102, u64(fiat.u1(x109)))
|
||||
x112, x113 := bits.add_u64(x90, x104, u64(fiat.u1(x111)))
|
||||
x114, x115 := bits.add_u64((u64(fiat.u1(x91)) + (u64(fiat.u1(x85)) + (u64(fiat.u1(x77)) + x65))), x106, u64(fiat.u1(x113)))
|
||||
x116 := (u64(fiat.u1(x115)) + (u64(fiat.u1(x107)) + x95))
|
||||
x117, x118 := bits.sub_u64(x110, 0xf3b9cac2fc632551, u64(0x0))
|
||||
x119, x120 := bits.sub_u64(x112, 0xbce6faada7179e84, u64(fiat.u1(x118)))
|
||||
x121, x122 := bits.sub_u64(x114, 0xffffffffffffffff, u64(fiat.u1(x120)))
|
||||
x123, x124 := bits.sub_u64(x116, 0xffffffff00000000, u64(fiat.u1(x122)))
|
||||
_, x126 := bits.sub_u64(u64(0x0), u64(0x0), u64(fiat.u1(x124)))
|
||||
x127 := fiat.cmovznz_u64(fiat.u1(x126), x117, x110)
|
||||
x128 := fiat.cmovznz_u64(fiat.u1(x126), x119, x112)
|
||||
x129 := fiat.cmovznz_u64(fiat.u1(x126), x121, x114)
|
||||
x130 := fiat.cmovznz_u64(fiat.u1(x126), x123, x116)
|
||||
out1[0] = x127
|
||||
out1[1] = x128
|
||||
out1[2] = x129
|
||||
out1[3] = x130
|
||||
}
|
||||
|
||||
fe_to_montgomery :: proc "contextless" (
|
||||
out1: ^Montgomery_Domain_Field_Element,
|
||||
arg1: ^Non_Montgomery_Domain_Field_Element,
|
||||
) {
|
||||
x1 := arg1[1]
|
||||
x2 := arg1[2]
|
||||
x3 := arg1[3]
|
||||
x4 := arg1[0]
|
||||
x6, x5 := bits.mul_u64(x4, 0x66e12d94f3d95620)
|
||||
x8, x7 := bits.mul_u64(x4, 0x2845b2392b6bec59)
|
||||
x10, x9 := bits.mul_u64(x4, 0x4699799c49bd6fa6)
|
||||
x12, x11 := bits.mul_u64(x4, 0x83244c95be79eea2)
|
||||
x13, x14 := bits.add_u64(x12, x9, u64(0x0))
|
||||
x15, x16 := bits.add_u64(x10, x7, u64(fiat.u1(x14)))
|
||||
x17, x18 := bits.add_u64(x8, x5, u64(fiat.u1(x16)))
|
||||
_, x19 := bits.mul_u64(x11, 0xccd1c8aaee00bc4f)
|
||||
x22, x21 := bits.mul_u64(x19, 0xffffffff00000000)
|
||||
x24, x23 := bits.mul_u64(x19, 0xffffffffffffffff)
|
||||
x26, x25 := bits.mul_u64(x19, 0xbce6faada7179e84)
|
||||
x28, x27 := bits.mul_u64(x19, 0xf3b9cac2fc632551)
|
||||
x29, x30 := bits.add_u64(x28, x25, u64(0x0))
|
||||
x31, x32 := bits.add_u64(x26, x23, u64(fiat.u1(x30)))
|
||||
x33, x34 := bits.add_u64(x24, x21, u64(fiat.u1(x32)))
|
||||
_, x36 := bits.add_u64(x11, x27, u64(0x0))
|
||||
x37, x38 := bits.add_u64(x13, x29, u64(fiat.u1(x36)))
|
||||
x39, x40 := bits.add_u64(x15, x31, u64(fiat.u1(x38)))
|
||||
x41, x42 := bits.add_u64(x17, x33, u64(fiat.u1(x40)))
|
||||
x43, x44 := bits.add_u64((u64(fiat.u1(x18)) + x6), (u64(fiat.u1(x34)) + x22), u64(fiat.u1(x42)))
|
||||
x46, x45 := bits.mul_u64(x1, 0x66e12d94f3d95620)
|
||||
x48, x47 := bits.mul_u64(x1, 0x2845b2392b6bec59)
|
||||
x50, x49 := bits.mul_u64(x1, 0x4699799c49bd6fa6)
|
||||
x52, x51 := bits.mul_u64(x1, 0x83244c95be79eea2)
|
||||
x53, x54 := bits.add_u64(x52, x49, u64(0x0))
|
||||
x55, x56 := bits.add_u64(x50, x47, u64(fiat.u1(x54)))
|
||||
x57, x58 := bits.add_u64(x48, x45, u64(fiat.u1(x56)))
|
||||
x59, x60 := bits.add_u64(x37, x51, u64(0x0))
|
||||
x61, x62 := bits.add_u64(x39, x53, u64(fiat.u1(x60)))
|
||||
x63, x64 := bits.add_u64(x41, x55, u64(fiat.u1(x62)))
|
||||
x65, x66 := bits.add_u64(x43, x57, u64(fiat.u1(x64)))
|
||||
_, x67 := bits.mul_u64(x59, 0xccd1c8aaee00bc4f)
|
||||
x70, x69 := bits.mul_u64(x67, 0xffffffff00000000)
|
||||
x72, x71 := bits.mul_u64(x67, 0xffffffffffffffff)
|
||||
x74, x73 := bits.mul_u64(x67, 0xbce6faada7179e84)
|
||||
x76, x75 := bits.mul_u64(x67, 0xf3b9cac2fc632551)
|
||||
x77, x78 := bits.add_u64(x76, x73, u64(0x0))
|
||||
x79, x80 := bits.add_u64(x74, x71, u64(fiat.u1(x78)))
|
||||
x81, x82 := bits.add_u64(x72, x69, u64(fiat.u1(x80)))
|
||||
_, x84 := bits.add_u64(x59, x75, u64(0x0))
|
||||
x85, x86 := bits.add_u64(x61, x77, u64(fiat.u1(x84)))
|
||||
x87, x88 := bits.add_u64(x63, x79, u64(fiat.u1(x86)))
|
||||
x89, x90 := bits.add_u64(x65, x81, u64(fiat.u1(x88)))
|
||||
x91, x92 := bits.add_u64(((u64(fiat.u1(x66)) + u64(fiat.u1(x44))) + (u64(fiat.u1(x58)) + x46)), (u64(fiat.u1(x82)) + x70), u64(fiat.u1(x90)))
|
||||
x94, x93 := bits.mul_u64(x2, 0x66e12d94f3d95620)
|
||||
x96, x95 := bits.mul_u64(x2, 0x2845b2392b6bec59)
|
||||
x98, x97 := bits.mul_u64(x2, 0x4699799c49bd6fa6)
|
||||
x100, x99 := bits.mul_u64(x2, 0x83244c95be79eea2)
|
||||
x101, x102 := bits.add_u64(x100, x97, u64(0x0))
|
||||
x103, x104 := bits.add_u64(x98, x95, u64(fiat.u1(x102)))
|
||||
x105, x106 := bits.add_u64(x96, x93, u64(fiat.u1(x104)))
|
||||
x107, x108 := bits.add_u64(x85, x99, u64(0x0))
|
||||
x109, x110 := bits.add_u64(x87, x101, u64(fiat.u1(x108)))
|
||||
x111, x112 := bits.add_u64(x89, x103, u64(fiat.u1(x110)))
|
||||
x113, x114 := bits.add_u64(x91, x105, u64(fiat.u1(x112)))
|
||||
_, x115 := bits.mul_u64(x107, 0xccd1c8aaee00bc4f)
|
||||
x118, x117 := bits.mul_u64(x115, 0xffffffff00000000)
|
||||
x120, x119 := bits.mul_u64(x115, 0xffffffffffffffff)
|
||||
x122, x121 := bits.mul_u64(x115, 0xbce6faada7179e84)
|
||||
x124, x123 := bits.mul_u64(x115, 0xf3b9cac2fc632551)
|
||||
x125, x126 := bits.add_u64(x124, x121, u64(0x0))
|
||||
x127, x128 := bits.add_u64(x122, x119, u64(fiat.u1(x126)))
|
||||
x129, x130 := bits.add_u64(x120, x117, u64(fiat.u1(x128)))
|
||||
_, x132 := bits.add_u64(x107, x123, u64(0x0))
|
||||
x133, x134 := bits.add_u64(x109, x125, u64(fiat.u1(x132)))
|
||||
x135, x136 := bits.add_u64(x111, x127, u64(fiat.u1(x134)))
|
||||
x137, x138 := bits.add_u64(x113, x129, u64(fiat.u1(x136)))
|
||||
x139, x140 := bits.add_u64(((u64(fiat.u1(x114)) + u64(fiat.u1(x92))) + (u64(fiat.u1(x106)) + x94)), (u64(fiat.u1(x130)) + x118), u64(fiat.u1(x138)))
|
||||
x142, x141 := bits.mul_u64(x3, 0x66e12d94f3d95620)
|
||||
x144, x143 := bits.mul_u64(x3, 0x2845b2392b6bec59)
|
||||
x146, x145 := bits.mul_u64(x3, 0x4699799c49bd6fa6)
|
||||
x148, x147 := bits.mul_u64(x3, 0x83244c95be79eea2)
|
||||
x149, x150 := bits.add_u64(x148, x145, u64(0x0))
|
||||
x151, x152 := bits.add_u64(x146, x143, u64(fiat.u1(x150)))
|
||||
x153, x154 := bits.add_u64(x144, x141, u64(fiat.u1(x152)))
|
||||
x155, x156 := bits.add_u64(x133, x147, u64(0x0))
|
||||
x157, x158 := bits.add_u64(x135, x149, u64(fiat.u1(x156)))
|
||||
x159, x160 := bits.add_u64(x137, x151, u64(fiat.u1(x158)))
|
||||
x161, x162 := bits.add_u64(x139, x153, u64(fiat.u1(x160)))
|
||||
_, x163 := bits.mul_u64(x155, 0xccd1c8aaee00bc4f)
|
||||
x166, x165 := bits.mul_u64(x163, 0xffffffff00000000)
|
||||
x168, x167 := bits.mul_u64(x163, 0xffffffffffffffff)
|
||||
x170, x169 := bits.mul_u64(x163, 0xbce6faada7179e84)
|
||||
x172, x171 := bits.mul_u64(x163, 0xf3b9cac2fc632551)
|
||||
x173, x174 := bits.add_u64(x172, x169, u64(0x0))
|
||||
x175, x176 := bits.add_u64(x170, x167, u64(fiat.u1(x174)))
|
||||
x177, x178 := bits.add_u64(x168, x165, u64(fiat.u1(x176)))
|
||||
_, x180 := bits.add_u64(x155, x171, u64(0x0))
|
||||
x181, x182 := bits.add_u64(x157, x173, u64(fiat.u1(x180)))
|
||||
x183, x184 := bits.add_u64(x159, x175, u64(fiat.u1(x182)))
|
||||
x185, x186 := bits.add_u64(x161, x177, u64(fiat.u1(x184)))
|
||||
x187, x188 := bits.add_u64(((u64(fiat.u1(x162)) + u64(fiat.u1(x140))) + (u64(fiat.u1(x154)) + x142)), (u64(fiat.u1(x178)) + x166), u64(fiat.u1(x186)))
|
||||
x189, x190 := bits.sub_u64(x181, 0xf3b9cac2fc632551, u64(0x0))
|
||||
x191, x192 := bits.sub_u64(x183, 0xbce6faada7179e84, u64(fiat.u1(x190)))
|
||||
x193, x194 := bits.sub_u64(x185, 0xffffffffffffffff, u64(fiat.u1(x192)))
|
||||
x195, x196 := bits.sub_u64(x187, 0xffffffff00000000, u64(fiat.u1(x194)))
|
||||
_, x198 := bits.sub_u64(u64(fiat.u1(x188)), u64(0x0), u64(fiat.u1(x196)))
|
||||
x199 := fiat.cmovznz_u64(fiat.u1(x198), x189, x181)
|
||||
x200 := fiat.cmovznz_u64(fiat.u1(x198), x191, x183)
|
||||
x201 := fiat.cmovznz_u64(fiat.u1(x198), x193, x185)
|
||||
x202 := fiat.cmovznz_u64(fiat.u1(x198), x195, x187)
|
||||
out1[0] = x199
|
||||
out1[1] = x200
|
||||
out1[2] = x201
|
||||
out1[3] = x202
|
||||
}
|
||||
42
core/crypto/_subtle/subtle.odin
Normal file
42
core/crypto/_subtle/subtle.odin
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
Various useful bit operations in constant time.
|
||||
*/
|
||||
package _subtle
|
||||
|
||||
import "core:math/bits"
|
||||
|
||||
// byte_eq returns 1 iff a == b, 0 otherwise.
|
||||
@(optimization_mode="none")
|
||||
byte_eq :: proc "contextless" (a, b: byte) -> int {
|
||||
v := a ~ b
|
||||
|
||||
// v == 0 iff a == b. The subtraction will underflow, setting the
|
||||
// sign bit, which will get returned.
|
||||
return int((u32(v)-1) >> 31)
|
||||
}
|
||||
|
||||
// u64_eq returns 1 iff a == b, 0 otherwise.
|
||||
@(optimization_mode="none")
|
||||
u64_eq :: proc "contextless" (a, b: u64) -> u64 {
|
||||
_, borrow := bits.sub_u64(0, a ~ b, 0)
|
||||
return (~borrow) & 1
|
||||
}
|
||||
|
||||
eq :: proc {
|
||||
byte_eq,
|
||||
u64_eq,
|
||||
}
|
||||
|
||||
// u64_is_zero returns 1 iff a == 0, 0 otherwise.
|
||||
@(optimization_mode="none")
|
||||
u64_is_zero :: proc "contextless" (a: u64) -> u64 {
|
||||
_, borrow := bits.sub_u64(a, 1, 0)
|
||||
return borrow
|
||||
}
|
||||
|
||||
// u64_is_non_zero returns 1 iff a != 0, 0 otherwise.
|
||||
@(optimization_mode="none")
|
||||
u64_is_non_zero :: proc "contextless" (a: u64) -> u64 {
|
||||
is_zero := u64_is_zero(a)
|
||||
return (~is_zero) & 1
|
||||
}
|
||||
135
core/crypto/_weierstrass/fe.odin
Normal file
135
core/crypto/_weierstrass/fe.odin
Normal file
@@ -0,0 +1,135 @@
|
||||
package _weierstrass
|
||||
|
||||
import p256r1 "core:crypto/_fiat/field_p256r1"
|
||||
import "core:math/bits"
|
||||
|
||||
Field_Element_p256r1 :: p256r1.Montgomery_Domain_Field_Element
|
||||
|
||||
FE_SIZE_P256R1 :: 32
|
||||
|
||||
fe_clear :: proc {
|
||||
p256r1.fe_clear,
|
||||
}
|
||||
|
||||
fe_clear_vec :: proc {
|
||||
p256r1.fe_clear_vec,
|
||||
}
|
||||
|
||||
fe_set_bytes :: proc {
|
||||
p256r1.fe_from_bytes,
|
||||
}
|
||||
fe_bytes :: proc {
|
||||
p256r1.fe_to_bytes,
|
||||
}
|
||||
|
||||
fe_set :: proc {
|
||||
p256r1.fe_set,
|
||||
}
|
||||
|
||||
fe_zero :: proc {
|
||||
p256r1.fe_zero,
|
||||
}
|
||||
|
||||
fe_a :: proc {
|
||||
fe_a_p256r1,
|
||||
}
|
||||
|
||||
fe_b :: proc {
|
||||
fe_b_p256r1,
|
||||
}
|
||||
|
||||
fe_gen_x :: proc {
|
||||
fe_gen_x_p256r1,
|
||||
}
|
||||
|
||||
fe_gen_y :: proc {
|
||||
fe_gen_y_p256r1,
|
||||
}
|
||||
|
||||
fe_one :: proc {
|
||||
p256r1.fe_one,
|
||||
}
|
||||
|
||||
fe_add :: proc {
|
||||
p256r1.fe_add,
|
||||
}
|
||||
|
||||
fe_sub :: proc {
|
||||
p256r1.fe_sub,
|
||||
}
|
||||
|
||||
fe_negate :: proc {
|
||||
p256r1.fe_opp,
|
||||
}
|
||||
|
||||
fe_mul :: proc {
|
||||
p256r1.fe_mul,
|
||||
}
|
||||
|
||||
fe_square :: proc {
|
||||
p256r1.fe_square,
|
||||
}
|
||||
|
||||
fe_inv :: proc {
|
||||
p256r1.fe_inv,
|
||||
}
|
||||
|
||||
fe_sqrt :: proc {
|
||||
p256r1.fe_sqrt,
|
||||
}
|
||||
|
||||
fe_equal :: proc {
|
||||
p256r1.fe_equal,
|
||||
}
|
||||
|
||||
fe_is_odd :: proc {
|
||||
p256r1.fe_is_odd,
|
||||
}
|
||||
|
||||
fe_is_zero :: proc {
|
||||
fe_is_zero_p256r1,
|
||||
}
|
||||
|
||||
fe_cond_select :: proc {
|
||||
p256r1.fe_cond_select,
|
||||
}
|
||||
|
||||
fe_a_p256r1 :: proc "contextless" (fe: ^Field_Element_p256r1) {
|
||||
// a = 0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc
|
||||
// = -3 mod p
|
||||
fe[0] = 18446744073709551612
|
||||
fe[1] = 17179869183
|
||||
fe[2] = 0
|
||||
fe[3] = 18446744056529682436
|
||||
}
|
||||
|
||||
fe_b_p256r1 :: proc "contextless" (fe: ^Field_Element_p256r1) {
|
||||
// b = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b
|
||||
fe[0] = 15608596021259845087
|
||||
fe[1] = 12461466548982526096
|
||||
fe[2] = 16546823903870267094
|
||||
fe[3] = 15866188208926050356
|
||||
}
|
||||
|
||||
fe_gen_x_p256r1 :: proc "contextless" (fe: ^Field_Element_p256r1) {
|
||||
// G_x = 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296
|
||||
fe[0] = 8784043285714375740
|
||||
fe[1] = 8483257759279461889
|
||||
fe[2] = 8789745728267363600
|
||||
fe[3] = 1770019616739251654
|
||||
}
|
||||
|
||||
fe_gen_y_p256r1 :: proc "contextless" (fe: ^Field_Element_p256r1) {
|
||||
// G_y = 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5
|
||||
fe[0] = 15992936863339206154
|
||||
fe[1] = 10037038012062884956
|
||||
fe[2] = 15197544864945402661
|
||||
fe[3] = 9615747158586711429
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
fe_is_zero_p256r1 :: proc "contextless" (fe: ^Field_Element_p256r1) -> int {
|
||||
ctrl := p256r1.fe_non_zero(fe)
|
||||
_, borrow := bits.sub_u64(ctrl, 1, 0)
|
||||
return int(borrow)
|
||||
}
|
||||
548
core/crypto/_weierstrass/point.odin
Normal file
548
core/crypto/_weierstrass/point.odin
Normal file
@@ -0,0 +1,548 @@
|
||||
package _weierstrass
|
||||
|
||||
/*
|
||||
This implements prime order short Weierstrass curves defined over a field
|
||||
k with char(k) != 2, 3 (`y^2 = x^3 + ax + b`). for the purpose of
|
||||
implementing ECDH and ECDSA. Use of this package for other purposes is
|
||||
NOT RECOMMENDED.
|
||||
|
||||
As an explicit simplicity/performance tradeoff, projective representation
|
||||
was chosen so that it is possible to use the complete addition
|
||||
formulas.
|
||||
|
||||
See:
|
||||
- https://eprint.iacr.org/2015/1060.pdf
|
||||
- https://hyperelliptic.org/EFD/g1p/auto-shortw-projective.html
|
||||
|
||||
WARNING: The point addition and doubling formulas are specialized for
|
||||
`a = -3`, which covers secp256r1, secp384r1, secp521r1, FRP256v1, SM2,
|
||||
and GOST 34.10. The brainpool curves and secp256k1 are NOT SUPPORTED
|
||||
and would require slightly different formulas.
|
||||
*/
|
||||
|
||||
Point_p256r1 :: struct {
|
||||
x: Field_Element_p256r1,
|
||||
y: Field_Element_p256r1,
|
||||
z: Field_Element_p256r1,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
pt_set_xy_bytes :: proc "contextless" (p: ^$T, x_raw, y_raw: []byte) -> bool {
|
||||
when T == Point_p256r1 {
|
||||
FE_SZ :: FE_SIZE_P256R1
|
||||
x, y: Field_Element_p256r1
|
||||
defer fe_clear_vec([]^Field_Element_p256r1{&x, &y})
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
if len(x_raw) != FE_SZ || len(y_raw) != FE_SZ {
|
||||
return false
|
||||
}
|
||||
|
||||
if !fe_set_bytes(&x, x_raw) {
|
||||
return false
|
||||
}
|
||||
if !fe_set_bytes(&y, y_raw) {
|
||||
return false
|
||||
}
|
||||
if !is_on_curve(&x, &y) {
|
||||
return false
|
||||
}
|
||||
|
||||
fe_set(&p.x, &x)
|
||||
fe_set(&p.y, &y)
|
||||
fe_one(&p.z)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
pt_set_x_bytes :: proc "contextless" (p: ^$T, x_raw: []byte, y_is_odd: int) -> bool {
|
||||
when T == Point_p256r1 {
|
||||
FE_SZ :: FE_SIZE_P256R1
|
||||
x, y, yy, y_neg: Field_Element_p256r1
|
||||
defer fe_clear_vec([]^Field_Element_p256r1{&x, &y, &yy, &y_neg})
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
if len(x_raw) != FE_SZ {
|
||||
return false
|
||||
}
|
||||
|
||||
if !fe_set_bytes(&x, x_raw) {
|
||||
return false
|
||||
}
|
||||
set_yy_candidate(&yy, &x)
|
||||
if fe_sqrt(&y, &yy) != 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Pick the correct y-coordinate.
|
||||
fe_negate(&y_neg, &y)
|
||||
parity_neq := (y_is_odd ~ fe_is_odd(&y)) & 1
|
||||
|
||||
fe_set(&p.x, &x)
|
||||
fe_cond_select(&p.y, &y, &y_neg, parity_neq)
|
||||
fe_one(&p.z)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
pt_bytes :: proc "contextless" (x, y: []byte, p: ^$T) -> bool {
|
||||
when T == Point_p256r1 {
|
||||
FE_SZ :: FE_SIZE_P256R1
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
if pt_is_identity(p) == 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Convert to affine coordinates.
|
||||
pt_rescale(p, p)
|
||||
|
||||
switch len(x) {
|
||||
case 0:
|
||||
case FE_SZ:
|
||||
fe_bytes(x, &p.x)
|
||||
case:
|
||||
panic_contextless("weierstrass: invalid x buffer")
|
||||
}
|
||||
switch len(y) {
|
||||
case 0:
|
||||
case FE_SZ:
|
||||
fe_bytes(y, &p.y)
|
||||
case:
|
||||
panic_contextless("weierstrass: invalid y buffer")
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
pt_set :: proc "contextless" (p, q: ^$T) {
|
||||
fe_set(&p.x, &q.x)
|
||||
fe_set(&p.y, &q.y)
|
||||
fe_set(&p.z, &q.z)
|
||||
}
|
||||
|
||||
pt_identity :: proc "contextless" (p: ^$T) {
|
||||
fe_zero(&p.x)
|
||||
fe_one(&p.y)
|
||||
fe_zero(&p.z)
|
||||
}
|
||||
|
||||
pt_generator :: proc "contextless" (p: ^$T) {
|
||||
fe_gen_x(&p.x)
|
||||
fe_gen_y(&p.y)
|
||||
fe_one(&p.z)
|
||||
}
|
||||
|
||||
pt_clear :: proc "contextless" (p: ^$T) {
|
||||
fe_clear(&p.x)
|
||||
fe_clear(&p.y)
|
||||
fe_clear(&p.z)
|
||||
}
|
||||
|
||||
pt_clear_vec :: proc "contextless" (arg: []^$T) {
|
||||
for p in arg {
|
||||
pt_clear(p)
|
||||
}
|
||||
}
|
||||
|
||||
pt_add :: proc "contextless" (p, a, b: ^$T) {
|
||||
// Algorithm 4 from "Complete addition formulas for prime
|
||||
// order elliptic curves" by Renes, Costello, and Batina.
|
||||
//
|
||||
// The formula is complete in that it is valid for all a and b,
|
||||
// without exceptions or extra assumptions about the inputs.
|
||||
//
|
||||
// The operation costs are `12M + 2mb + 29a`.
|
||||
|
||||
when T == Point_p256r1 {
|
||||
t0, t1, t2, t3, t4, b_fe: Field_Element_p256r1
|
||||
x3, y3, z3: Field_Element_p256r1
|
||||
defer fe_clear_vec([]^Field_Element_p256r1{&t0, &t1, &t2, &t3, &t4, &x3, &y3, &z3})
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
x1, y1, z1 := &a.x, &a.y, &a.z
|
||||
x2, y2, z2 := &b.x, &b.y, &b.z
|
||||
|
||||
fe_b(&b_fe)
|
||||
|
||||
// t0 := X1 * X2 ; t1 := Y1 * Y2 ; t2 := Z1 * Z2 ;
|
||||
fe_mul(&t0, x1, x2)
|
||||
fe_mul(&t1, y1, y2)
|
||||
fe_mul(&t2, z1, z2)
|
||||
|
||||
// t3 := X1 + Y1 ; t4 := X2 + Y2 ; t3 := t3 * t4 ;
|
||||
fe_add(&t3, x1, y1)
|
||||
fe_add(&t4, x2, y2)
|
||||
fe_mul(&t3, &t3, &t4)
|
||||
|
||||
// t4 := t0 + t1 ; t3 := t3 - t4 ; t4 := Y1 + Z1 ;
|
||||
fe_add(&t4, &t0, &t1)
|
||||
fe_sub(&t3, &t3, &t4)
|
||||
fe_add(&t4, y1, z1)
|
||||
|
||||
// X3 := Y2 + Z2 ; t4 := t4 * X3 ; X3 := t1 + t2 ;
|
||||
fe_add(&x3, y2, z2)
|
||||
fe_mul(&t4, &t4, &x3)
|
||||
fe_add(&x3, &t1, &t2)
|
||||
|
||||
// t4 := t4 - X3 ; X3 := X1 + Z1 ; Y3 := X2 + Z2 ;
|
||||
fe_sub(&t4, &t4, &x3)
|
||||
fe_add(&x3, x1, z1)
|
||||
fe_add(&y3, x2, z2)
|
||||
|
||||
// X3 := X3 * Y3 ; Y3 := t0 + t2 ; Y3 := X3 - Y3 ;
|
||||
fe_mul(&x3, &x3, &y3)
|
||||
fe_add(&y3, &t0, &t2)
|
||||
fe_sub(&y3, &x3, &y3)
|
||||
|
||||
// Z3 := b * t2 ; X3 := Y3 - Z3 ; Z3 := X3 + X3 ;
|
||||
fe_mul(&z3, &b_fe, &t2)
|
||||
fe_sub(&x3, &y3, &z3)
|
||||
fe_add(&z3, &x3, &x3)
|
||||
|
||||
// X3 := X3 + Z3 ; Z3 := t1 - X3 ; X3 := t1 + X3 ;
|
||||
fe_add(&x3, &x3, &z3)
|
||||
fe_sub(&z3, &t1, &x3)
|
||||
fe_add(&x3, &t1, &x3)
|
||||
|
||||
// Y3 := b * Y3 ; t1 := t2 + t2 ; t2 := t1 + t2 ;
|
||||
fe_mul(&y3, &b_fe, &y3)
|
||||
fe_add(&t1, &t2, &t2)
|
||||
fe_add(&t2, &t1, &t2)
|
||||
|
||||
// Y3 := Y3 - t2 ; Y3 := Y3 - t0 ; t1 := Y3 + Y3 ;
|
||||
fe_sub(&y3, &y3, &t2)
|
||||
fe_sub(&y3, &y3, &t0)
|
||||
fe_add(&t1, &y3, &y3)
|
||||
|
||||
// Y3 := t1 + Y3 ; t1 := t0 + t0 ; t0 := t1 + t0 ;
|
||||
fe_add(&y3, &t1, &y3)
|
||||
fe_add(&t1, &t0, &t0)
|
||||
fe_add(&t0, &t1, &t0)
|
||||
|
||||
// t0 := t0 - t2 ; t1 := t4 * Y3 ; t2 := t0 * Y3 ;
|
||||
fe_sub(&t0, &t0, &t2)
|
||||
fe_mul(&t1, &t4, &y3)
|
||||
fe_mul(&t2, &t0, &y3)
|
||||
|
||||
// Y3 := X3 * Z3 ; Y3 := Y3 + t2 ; X3 := t3 * X3 ;
|
||||
fe_mul(&y3, &x3, &z3)
|
||||
fe_add(&y3, &y3, &t2)
|
||||
fe_mul(&x3, &t3, &x3)
|
||||
|
||||
// X3 := X3 - t1 ; Z3 := t4 * Z3 ; t1 := t3 * t0 ;
|
||||
fe_sub(&x3, &x3, &t1)
|
||||
fe_mul(&z3, &t4, &z3)
|
||||
fe_mul(&t1, &t3, &t0)
|
||||
|
||||
// Z3 := Z3 + t1 ;
|
||||
fe_add(&z3, &z3, &t1)
|
||||
|
||||
// return X3 , Y3 , Z3 ;
|
||||
fe_set(&p.x, &x3)
|
||||
fe_set(&p.y, &y3)
|
||||
fe_set(&p.z, &z3)
|
||||
}
|
||||
|
||||
@(private)
|
||||
pt_add_mixed :: proc "contextless" (p, a: ^$T, x2, y2: ^$U) {
|
||||
// Algorithm 5 from "Complete addition formulas for prime
|
||||
// order elliptic curves" by Renes, Costello, and Batina.
|
||||
//
|
||||
// The formula is mixed in that it assumes the z-coordinate
|
||||
// of the addend (`Z2`) is `1`, meaning that it CAN NOT
|
||||
// handle the addend being the point at infinity.
|
||||
//
|
||||
// The operation costs are `11M + 2mb + 23a` saving
|
||||
// `1M + 6a` over `pt_add`.
|
||||
|
||||
when T == Point_p256r1 && U == Field_Element_p256r1 {
|
||||
t0, t1, t2, t3, t4, b_fe: Field_Element_p256r1
|
||||
x3, y3, z3: Field_Element_p256r1
|
||||
defer fe_clear_vec([]^Field_Element_p256r1{&t0, &t1, &t2, &t3, &t4, &x3, &y3, &z3})
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
x1, y1, z1 := &a.x, &a.y, &a.z
|
||||
|
||||
fe_b(&b_fe)
|
||||
|
||||
// t0 := X1 * X2 ; t1 := Y1 * Y2 ; t3 := X2 + Y2 ;
|
||||
fe_mul(&t0, x1, x2)
|
||||
fe_mul(&t1, y1, y2)
|
||||
fe_add(&t3, x2, y2)
|
||||
|
||||
// t4 := X1 + Y1 ; t3 := t3 * t4 ; t4 := t0 + t1 ;
|
||||
fe_add(&t4, x1, y1)
|
||||
fe_mul(&t3, &t3, &t4)
|
||||
fe_add(&t4, &t0, &t1)
|
||||
|
||||
// t3 := t3 − t4 ; t4 := Y2 * Z1 ; t4 := t4 + Y1 ;
|
||||
fe_sub(&t3, &t3, &t4)
|
||||
fe_mul(&t4, y2, z1)
|
||||
fe_add(&t4, &t4, y1)
|
||||
|
||||
// Y3 := X2 * Z1 ; Y3 := Y3 + X1 ; Z3 := b * Z1 ;
|
||||
fe_mul(&y3, x2, z1)
|
||||
fe_add(&y3, &y3, x1)
|
||||
fe_mul(&z3, &b_fe, z1)
|
||||
|
||||
// X3 := Y3 − Z3 ; Z3 := X3 + X3 ; X3 := X3 + Z3 ;
|
||||
fe_sub(&x3, &y3, &z3)
|
||||
fe_add(&z3, &x3, &x3)
|
||||
fe_add(&x3, &x3, &z3)
|
||||
|
||||
// Z3 := t1 − X3 ; X3 := t1 + X3 ;. Y3 := b * Y3 ;
|
||||
fe_sub(&z3, &t1, &x3)
|
||||
fe_add(&x3, &t1, &x3)
|
||||
fe_mul(&y3, &b_fe, &y3)
|
||||
|
||||
// t1 := Z1 + Z1 ; t2 := t1 + Z1 ; Y3 := Y3 − t2 ;
|
||||
fe_add(&t1, z1, z1)
|
||||
fe_add(&t2, &t1, z1)
|
||||
fe_sub(&y3, &y3, &t2)
|
||||
|
||||
// Y3 := Y3 − t0 ; t1 := Y3 + Y3 ; Y3 := t1 + Y3 ;
|
||||
fe_sub(&y3, &y3, &t0)
|
||||
fe_add(&t1, &y3, &y3)
|
||||
fe_add(&y3, &t1, &y3)
|
||||
|
||||
// t1 := t0 + t0 ; t0 := t1 + t0 ; t0 := t0 − t2 ;
|
||||
fe_add(&t1, &t0, &t0)
|
||||
fe_add(&t0, &t1, &t0)
|
||||
fe_sub(&t0, &t0, &t2)
|
||||
|
||||
// t1 := t4 * Y3 ; t2 := t0 * Y3 ; Y3 := X3 * Z3 ;
|
||||
fe_mul(&t1, &t4, &y3)
|
||||
fe_mul(&t2, &t0, &y3)
|
||||
fe_mul(&y3, &x3, &z3)
|
||||
|
||||
// Y3 := Y3 + t2 ; X3 := t3 * X3 ; X3 := X3 − t1 ;
|
||||
fe_add(&y3, &y3, &t2)
|
||||
fe_mul(&x3, &t3, &x3)
|
||||
fe_sub(&x3, &x3, &t1)
|
||||
|
||||
// Z3 := t4 * Z3 ; t1 := t3 * t0 ; Z3 := Z3 + t1 ;
|
||||
fe_mul(&z3, &t4, &z3)
|
||||
fe_mul(&t1, &t3, &t0)
|
||||
fe_add(&z3, &z3, &t1)
|
||||
|
||||
// return X3 , Y3 , Z3 ;
|
||||
fe_set(&p.x, &x3)
|
||||
fe_set(&p.y, &y3)
|
||||
fe_set(&p.z, &z3)
|
||||
}
|
||||
|
||||
pt_double :: proc "contextless" (p, a: ^$T) {
|
||||
// Algorithm 6 from "Complete addition formulas for prime
|
||||
// order elliptic curves" by Renes, Costello, and Batina.
|
||||
//
|
||||
// The formula is complete in that it is valid for all a,
|
||||
// without exceptions or extra assumptions about the inputs.
|
||||
//
|
||||
// The operation costs are `8M + 3S + 2mb + 21a`.
|
||||
|
||||
when T == Point_p256r1 {
|
||||
t0, t1, t2, t3, b_fe: Field_Element_p256r1
|
||||
x3, y3, z3: Field_Element_p256r1
|
||||
defer fe_clear_vec([]^Field_Element_p256r1{&t0, &t1, &t2, &t3, &x3, &y3, &z3})
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
x, y, z := &a.x, &a.y, &a.z
|
||||
|
||||
fe_b(&b_fe)
|
||||
|
||||
// t0 := X ^2; t1 := Y ^2; t2 := Z ^2;
|
||||
fe_square(&t0, x)
|
||||
fe_square(&t1, y)
|
||||
fe_square(&t2, z)
|
||||
|
||||
// t3 := X * Y ; t3 := t3 + t3 ; Z3 := X * Z ;
|
||||
fe_mul(&t3, x, y)
|
||||
fe_add(&t3, &t3, &t3)
|
||||
fe_mul(&z3, x, z)
|
||||
|
||||
// Z3 := Z3 + Z3 ; Y3 := b * t2 ; Y3 := Y3 - Z3 ;
|
||||
fe_add(&z3, &z3, &z3)
|
||||
fe_mul(&y3, &b_fe, &t2)
|
||||
fe_sub(&y3, &y3, &z3)
|
||||
|
||||
// X3 := Y3 + Y3 ; Y3 := X3 + Y3 ; X3 := t1 - Y3 ;
|
||||
fe_add(&x3, &y3, &y3)
|
||||
fe_add(&y3, &x3, &y3)
|
||||
fe_sub(&x3, &t1, &y3)
|
||||
|
||||
// Y3 := t1 + Y3 ; Y3 := X3 * Y3 ; X3 := X3 * t3 ;
|
||||
fe_add(&y3, &t1, &y3)
|
||||
fe_mul(&y3, &x3, &y3)
|
||||
fe_mul(&x3, &x3, &t3)
|
||||
|
||||
// t3 := t2 + t2 ; t2 := t2 + t3 ; Z3 := b * Z3 ;
|
||||
fe_add(&t3, &t2, &t2)
|
||||
fe_add(&t2, &t2, &t3)
|
||||
fe_mul(&z3, &b_fe, &z3)
|
||||
|
||||
// Z3 := Z3 - t2 ; Z3 := Z3 - t0 ; t3 := Z3 + Z3 ;
|
||||
fe_sub(&z3, &z3, &t2)
|
||||
fe_sub(&z3, &z3, &t0)
|
||||
fe_add(&t3, &z3, &z3)
|
||||
|
||||
// Z3 := Z3 + t3 ; t3 := t0 + t0 ; t0 := t3 + t0 ;
|
||||
fe_add(&z3, &z3, &t3)
|
||||
fe_add(&t3, &t0, &t0)
|
||||
fe_add(&t0, &t3, &t0)
|
||||
|
||||
// t0 := t0 - t2 ; t0 := t0 * Z3 ; Y3 := Y3 + t0 ;
|
||||
fe_sub(&t0, &t0, &t2)
|
||||
fe_mul(&t0, &t0, &z3)
|
||||
fe_add(&y3, &y3, &t0)
|
||||
|
||||
// t0 := Y * Z ; t0 := t0 + t0 ; Z3 := t0 * Z3 ;
|
||||
fe_mul(&t0, y, z)
|
||||
fe_add(&t0, &t0, &t0)
|
||||
fe_mul(&z3, &t0, &z3)
|
||||
|
||||
// X3 := X3 - Z3 ; Z3 := t0 * t1 ; Z3 := Z3 + Z3 ;
|
||||
fe_sub(&x3, &x3, &z3)
|
||||
fe_mul(&z3, &t0, &t1)
|
||||
fe_add(&z3, &z3, &z3)
|
||||
|
||||
// Z3 := Z3 + Z3 ;
|
||||
fe_add(&z3, &z3, &z3)
|
||||
|
||||
// return X3 , Y3 , Z3 ;
|
||||
fe_set(&p.x, &x3)
|
||||
fe_set(&p.y, &y3)
|
||||
fe_set(&p.z, &z3)
|
||||
}
|
||||
|
||||
pt_sub :: proc "contextless" (p, a, b: ^$T) {
|
||||
b_neg: T
|
||||
pt_negate(&b_neg, b)
|
||||
pt_add(p, a, &b_neg)
|
||||
|
||||
fe_clear(&b_neg)
|
||||
}
|
||||
|
||||
pt_negate :: proc "contextless" (p, a: ^$T) {
|
||||
fe_set(&p.x, &a.x)
|
||||
fe_negate(&p.y, &a.y)
|
||||
fe_set(&p.z, &a.z)
|
||||
}
|
||||
|
||||
pt_rescale :: proc "contextless" (p, a: ^$T) {
|
||||
// A = 1/Z1
|
||||
// X3 = A*X1
|
||||
// Y3 = A*Y1
|
||||
// Z3 = 1
|
||||
//
|
||||
// As per "From A to Z: Projective coordinates leakage in the wild"
|
||||
// leaking the Z-coordinate is bad. The modular inversion algorithm
|
||||
// used in this library is based on Fermat's Little Theorem.
|
||||
//
|
||||
// See: https://eprint.iacr.org/2020/432.pdf
|
||||
|
||||
was_identity := pt_is_identity(a)
|
||||
|
||||
when T == Point_p256r1 {
|
||||
z_inv: Field_Element_p256r1
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
ident: T
|
||||
fe_inv(&z_inv, &a.z)
|
||||
fe_mul(&p.x, &a.x, &z_inv)
|
||||
fe_mul(&p.y, &a.y, &z_inv)
|
||||
fe_one(&p.z)
|
||||
|
||||
pt_identity(&ident)
|
||||
pt_cond_select(p, p, &ident, was_identity)
|
||||
|
||||
fe_clear(&z_inv)
|
||||
}
|
||||
|
||||
pt_cond_select :: proc "contextless" (p, a, b: ^$T, ctrl: int) {
|
||||
fe_cond_select(&p.x, &a.x, &b.x, ctrl)
|
||||
fe_cond_select(&p.y, &a.y, &b.y, ctrl)
|
||||
fe_cond_select(&p.z, &a.z, &b.z, ctrl)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
pt_equal :: proc "contextless" (a, b: ^$T) -> int {
|
||||
when T == Point_p256r1 {
|
||||
x1z2, x2z1, y1z2, y2z1: Field_Element_p256r1
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
// Check X1Z2 == X2Z1 && Y1Z2 == Y2Z1
|
||||
fe_mul(&x1z2, &a.x, &b.z)
|
||||
fe_mul(&x2z1, &b.x, &a.z)
|
||||
|
||||
fe_mul(&y1z2, &a.y, &b.z)
|
||||
fe_mul(&y2z1, &b.y, &a.z)
|
||||
|
||||
return fe_equal(&x1z2, &x2z1) & fe_equal(&y1z2, &y2z1)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
pt_is_identity :: proc "contextless" (p: ^$T) -> int {
|
||||
return fe_is_zero(&p.z)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
pt_is_y_odd :: proc "contextless" (p: ^$T) -> int {
|
||||
tmp: T
|
||||
defer pt_clear(&tmp)
|
||||
|
||||
fe_set(&tmp, p)
|
||||
pt_rescale(&tmp)
|
||||
|
||||
return fe_is_odd(&tmp.y)
|
||||
}
|
||||
|
||||
@(private)
|
||||
is_on_curve :: proc "contextless" (x, y: ^$T) -> bool {
|
||||
maybe_yy, yy: T
|
||||
defer fe_clear_vec([]^T{&maybe_yy, &yy})
|
||||
|
||||
// RHS: x^3 + ax + b
|
||||
set_yy_candidate(&maybe_yy, x)
|
||||
|
||||
// LHS: y^2
|
||||
fe_square(&yy, y)
|
||||
|
||||
return fe_equal(&maybe_yy, &yy) == 1
|
||||
}
|
||||
|
||||
@(private)
|
||||
set_yy_candidate :: proc "contextless" (maybe_yy, x: ^$T) {
|
||||
// RHS: x^3 + ax + b
|
||||
rhs, tmp: T
|
||||
|
||||
fe_square(&tmp, x)
|
||||
fe_mul(&rhs, &tmp, x)
|
||||
|
||||
fe_a(&tmp)
|
||||
fe_mul(&tmp, &tmp, x)
|
||||
fe_add(&rhs, &rhs, &tmp)
|
||||
|
||||
fe_b(&tmp)
|
||||
fe_add(maybe_yy, &rhs, &tmp)
|
||||
|
||||
fe_clear(&rhs)
|
||||
}
|
||||
95
core/crypto/_weierstrass/point_s11n_sec.odin
Normal file
95
core/crypto/_weierstrass/point_s11n_sec.odin
Normal file
@@ -0,0 +1,95 @@
|
||||
package _weierstrass
|
||||
|
||||
@(require) import "core:mem"
|
||||
|
||||
@(private)
|
||||
SEC_PREFIX_IDENTITY :: 0x00
|
||||
@(private)
|
||||
SEC_PREFIX_COMPRESSED_EVEN :: 0x02
|
||||
@(private)
|
||||
SEC_PREFIX_COMPRESSED_ODD :: 0x03
|
||||
SEC_PREFIX_UNCOMPRESSED :: 0x04
|
||||
|
||||
@(require_results)
|
||||
pt_set_sec_bytes :: proc "contextless" (p: ^$T, b: []byte) -> bool {
|
||||
when T == Point_p256r1 {
|
||||
FE_SZ :: FE_SIZE_P256R1
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
b_len := len(b)
|
||||
if b_len < 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
switch b[0] {
|
||||
case SEC_PREFIX_IDENTITY:
|
||||
if b_len != 1 {
|
||||
return false
|
||||
}
|
||||
pt_identity(p)
|
||||
return true
|
||||
case SEC_PREFIX_COMPRESSED_EVEN, SEC_PREFIX_COMPRESSED_ODD:
|
||||
if b_len != 1 + FE_SZ {
|
||||
return false
|
||||
}
|
||||
y_is_odd := b[0] - SEC_PREFIX_COMPRESSED_EVEN
|
||||
return pt_set_x_bytes(p, b[1:], int(y_is_odd))
|
||||
case SEC_PREFIX_UNCOMPRESSED:
|
||||
if b_len != 1 + 2 * FE_SZ {
|
||||
return false
|
||||
}
|
||||
x, y := b[1:1+FE_SZ], b[1+FE_SZ:]
|
||||
return pt_set_xy_bytes(p, x, y)
|
||||
case:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
pt_sec_bytes :: proc "contextless" (b: []byte, p: ^$T, compressed: bool) -> bool {
|
||||
when T == Point_p256r1 {
|
||||
FE_SZ :: FE_SIZE_P256R1
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
b_len := len(b)
|
||||
if pt_is_identity(p) == 1 {
|
||||
if b_len != 1 {
|
||||
return false
|
||||
}
|
||||
b[0] = SEC_PREFIX_IDENTITY
|
||||
return true
|
||||
}
|
||||
|
||||
x, y: []byte
|
||||
y_: [FE_SZ]byte
|
||||
switch compressed {
|
||||
case true:
|
||||
if b_len != 1 + FE_SZ {
|
||||
return false
|
||||
}
|
||||
x, y = b[1:], y_[:]
|
||||
case false:
|
||||
if b_len != 1 + 2 * FE_SZ {
|
||||
return false
|
||||
}
|
||||
b[0]= SEC_PREFIX_UNCOMPRESSED
|
||||
x, y = b[1:1+FE_SZ], b[1+FE_SZ:]
|
||||
}
|
||||
if !pt_bytes(x, y, p) {
|
||||
return false
|
||||
}
|
||||
if compressed {
|
||||
// Instead of calling pt_is_y_odd, just serializing
|
||||
// y into a temp buffer and checking the parity saves
|
||||
// 1 redundant rescale call.
|
||||
y_is_odd := byte(y[FE_SZ-1] & 1)
|
||||
b[0] = SEC_PREFIX_COMPRESSED_EVEN + y_is_odd
|
||||
mem.zero_explicit(&y_, size_of(y_))
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
76
core/crypto/_weierstrass/sc.odin
Normal file
76
core/crypto/_weierstrass/sc.odin
Normal file
@@ -0,0 +1,76 @@
|
||||
package _weierstrass
|
||||
|
||||
import p256r1 "core:crypto/_fiat/field_scalarp256r1"
|
||||
import subtle "core:crypto/_subtle"
|
||||
|
||||
Scalar_p256r1 :: p256r1.Montgomery_Domain_Field_Element
|
||||
|
||||
SC_SIZE_P256R1 :: 32
|
||||
|
||||
sc_clear :: proc {
|
||||
p256r1.fe_clear,
|
||||
}
|
||||
|
||||
sc_clear_vec :: proc {
|
||||
p256r1.fe_clear_vec,
|
||||
}
|
||||
|
||||
sc_set_bytes :: proc {
|
||||
p256r1.fe_from_bytes,
|
||||
}
|
||||
sc_bytes :: proc {
|
||||
p256r1.fe_to_bytes,
|
||||
}
|
||||
|
||||
sc_set :: proc {
|
||||
p256r1.fe_set,
|
||||
}
|
||||
|
||||
sc_zero :: proc {
|
||||
p256r1.fe_zero,
|
||||
}
|
||||
|
||||
sc_one_p256r1 :: proc {
|
||||
p256r1.fe_one,
|
||||
}
|
||||
|
||||
sc_add :: proc {
|
||||
p256r1.fe_add,
|
||||
}
|
||||
|
||||
sc_sub :: proc {
|
||||
p256r1.fe_sub,
|
||||
}
|
||||
|
||||
sc_negate :: proc {
|
||||
p256r1.fe_opp,
|
||||
}
|
||||
|
||||
sc_mul :: proc {
|
||||
p256r1.fe_mul,
|
||||
}
|
||||
|
||||
sc_square :: proc {
|
||||
p256r1.fe_square,
|
||||
}
|
||||
|
||||
sc_cond_assign :: proc {
|
||||
p256r1.fe_cond_assign,
|
||||
}
|
||||
|
||||
sc_equal :: proc {
|
||||
p256r1.fe_equal,
|
||||
}
|
||||
|
||||
sc_is_odd :: proc {
|
||||
p256r1.fe_is_odd,
|
||||
}
|
||||
|
||||
sc_is_zero :: proc {
|
||||
sc_is_zero_p256r1,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
sc_is_zero_p256r1 :: proc "contextless" (fe: ^Scalar_p256r1) -> int {
|
||||
return int(subtle.u64_is_zero(p256r1.fe_non_zero(fe)))
|
||||
}
|
||||
204
core/crypto/_weierstrass/scalar_mul.odin
Normal file
204
core/crypto/_weierstrass/scalar_mul.odin
Normal file
@@ -0,0 +1,204 @@
|
||||
package _weierstrass
|
||||
|
||||
import "core:crypto"
|
||||
import subtle "core:crypto/_subtle"
|
||||
import "core:mem"
|
||||
|
||||
pt_scalar_mul :: proc "contextless" (
|
||||
p, a: ^$T,
|
||||
sc: ^$S,
|
||||
unsafe_is_vartime: bool = false,
|
||||
) {
|
||||
when T == Point_p256r1 && S == Scalar_p256r1 {
|
||||
SC_SZ :: SC_SIZE_P256R1
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
b: [SC_SZ]byte = ---
|
||||
sc_bytes(b[:], sc)
|
||||
|
||||
pt_scalar_mul_bytes(p, a, b[:], unsafe_is_vartime)
|
||||
|
||||
if !unsafe_is_vartime {
|
||||
mem.zero_explicit(&b, size_of(b))
|
||||
}
|
||||
}
|
||||
|
||||
pt_scalar_mul_bytes :: proc "contextless" (
|
||||
p, a: ^$T,
|
||||
sc: []byte,
|
||||
unsafe_is_vartime: bool = false,
|
||||
) {
|
||||
when T == Point_p256r1 {
|
||||
p_tbl: Multiply_Table_p256r1 = ---
|
||||
q, tmp: Point_p256r1 = ---, ---
|
||||
SC_SZ :: SC_SIZE_P256R1
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
assert_contextless(len(sc) == SC_SZ, "weierstrass: invalid scalar size")
|
||||
mul_tbl_set(&p_tbl, a, unsafe_is_vartime)
|
||||
|
||||
pt_identity(&q)
|
||||
for limb_byte, i in sc {
|
||||
hi, lo := (limb_byte >> 4) & 0x0f, limb_byte & 0x0f
|
||||
|
||||
if i != 0 {
|
||||
pt_double(&q, &q)
|
||||
pt_double(&q, &q)
|
||||
pt_double(&q, &q)
|
||||
pt_double(&q, &q)
|
||||
}
|
||||
mul_tbl_lookup_add(&q, &tmp, &p_tbl, u64(hi), unsafe_is_vartime)
|
||||
|
||||
pt_double(&q, &q)
|
||||
pt_double(&q, &q)
|
||||
pt_double(&q, &q)
|
||||
pt_double(&q, &q)
|
||||
mul_tbl_lookup_add(&q, &tmp, &p_tbl, u64(lo), unsafe_is_vartime)
|
||||
}
|
||||
|
||||
pt_set(p, &q)
|
||||
|
||||
if !unsafe_is_vartime {
|
||||
mem.zero_explicit(&p_tbl, size_of(p_tbl))
|
||||
pt_clear_vec([]^T{&q, &tmp})
|
||||
}
|
||||
}
|
||||
|
||||
when crypto.COMPACT_IMPLS == true {
|
||||
pt_scalar_mul_generator :: proc "contextless" (
|
||||
p: ^$T,
|
||||
sc: ^$S,
|
||||
unsafe_is_vartime: bool = false,
|
||||
) {
|
||||
g: T
|
||||
pt_generator(&g)
|
||||
|
||||
pt_scalar_mul(p, &g, sc, unsafe_is_vartime)
|
||||
}
|
||||
} else {
|
||||
pt_scalar_mul_generator :: proc "contextless" (
|
||||
p: ^$T,
|
||||
sc: ^$S,
|
||||
unsafe_is_vartime: bool = false,
|
||||
) {
|
||||
when T == Point_p256r1 && S == Scalar_p256r1 {
|
||||
p_tbl_hi := &Gen_Multiply_Table_p256r1_hi
|
||||
p_tbl_lo := &Gen_Multiply_Table_p256r1_lo
|
||||
tmp: Point_p256r1 = ---
|
||||
SC_SZ :: SC_SIZE_P256R1
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
b: [SC_SZ]byte
|
||||
sc_bytes(b[:], sc)
|
||||
|
||||
pt_identity(p)
|
||||
for limb_byte, i in b {
|
||||
hi, lo := (limb_byte >> 4) & 0x0f, limb_byte & 0x0f
|
||||
mul_affine_tbl_lookup_add(p, &tmp, &p_tbl_hi[i], u64(hi), unsafe_is_vartime)
|
||||
mul_affine_tbl_lookup_add(p, &tmp, &p_tbl_lo[i], u64(lo), unsafe_is_vartime)
|
||||
}
|
||||
|
||||
if !unsafe_is_vartime {
|
||||
mem.zero_explicit(&b, size_of(b))
|
||||
pt_clear(&tmp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
Multiply_Table_p256r1 :: [15]Point_p256r1
|
||||
|
||||
@(private="file")
|
||||
mul_tbl_set :: proc "contextless"(
|
||||
tbl: ^$T,
|
||||
point: ^$U,
|
||||
unsafe_is_vartime: bool,
|
||||
) {
|
||||
when T == Multiply_Table_p256r1 && U == Point_p256r1{
|
||||
tmp: Point_p256r1
|
||||
pt_set(&tmp, point)
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
pt_set(&tbl[0], &tmp)
|
||||
for i in 1 ..<15 {
|
||||
pt_add(&tmp, &tmp, point)
|
||||
pt_set(&tbl[i], &tmp)
|
||||
}
|
||||
|
||||
if !unsafe_is_vartime {
|
||||
pt_clear(&tmp)
|
||||
}
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
mul_tbl_lookup_add :: proc "contextless" (
|
||||
point, tmp: ^$T,
|
||||
tbl: ^$U,
|
||||
idx: u64,
|
||||
unsafe_is_vartime: bool,
|
||||
) {
|
||||
if unsafe_is_vartime {
|
||||
switch idx {
|
||||
case 0:
|
||||
case:
|
||||
pt_add(point, point, &tbl[idx - 1])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
pt_identity(tmp)
|
||||
for i in u64(1)..<16 {
|
||||
ctrl := subtle.eq(i, idx)
|
||||
pt_cond_select(tmp, tmp, &tbl[i - 1], int(ctrl))
|
||||
}
|
||||
|
||||
pt_add(point, point, tmp)
|
||||
}
|
||||
|
||||
when crypto.COMPACT_IMPLS == false {
|
||||
@(private)
|
||||
Affine_Point_p256r1 :: struct {
|
||||
x: Field_Element_p256r1,
|
||||
y: Field_Element_p256r1,
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
mul_affine_tbl_lookup_add :: proc "contextless" (
|
||||
point, tmp: ^$T,
|
||||
tbl: ^$U,
|
||||
idx: u64,
|
||||
unsafe_is_vartime: bool,
|
||||
) {
|
||||
if unsafe_is_vartime {
|
||||
switch idx {
|
||||
case 0:
|
||||
case:
|
||||
pt_add_mixed(point, point, &tbl[idx - 1].x, &tbl[idx - 1].y)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
pt_identity(tmp)
|
||||
for i in u64(1)..<16 {
|
||||
ctrl := int(subtle.eq(i, idx))
|
||||
fe_cond_select(&tmp.x, &tmp.x, &tbl[i - 1].x, ctrl)
|
||||
fe_cond_select(&tmp.y, &tmp.y, &tbl[i - 1].y, ctrl)
|
||||
}
|
||||
|
||||
// The mixed addition formula assumes that the addend is not
|
||||
// the neutral element. Do the addition regardless, and then
|
||||
// conditionally select the right result.
|
||||
pt_add_mixed(tmp, point, &tmp.x, &tmp.y)
|
||||
|
||||
ctrl := subtle.u64_is_non_zero(idx)
|
||||
pt_cond_select(point, point, tmp, int(ctrl))
|
||||
}
|
||||
}
|
||||
3985
core/crypto/_weierstrass/secp256r1_table.odin
Normal file
3985
core/crypto/_weierstrass/secp256r1_table.odin
Normal file
File diff suppressed because it is too large
Load Diff
99
core/crypto/_weierstrass/tools/ecc_gen_tables.odin
Normal file
99
core/crypto/_weierstrass/tools/ecc_gen_tables.odin
Normal file
@@ -0,0 +1,99 @@
|
||||
package weistrass_tools
|
||||
|
||||
import secec "core:crypto/_weierstrass"
|
||||
import "core:fmt"
|
||||
import path "core:path/filepath"
|
||||
import "core:os"
|
||||
import "core:strings"
|
||||
|
||||
// Yes this leaks memory, fite me IRL.
|
||||
|
||||
GENERATED :: `/*
|
||||
------ GENERATED ------ DO NOT EDIT ------ GENERATED ------ DO NOT EDIT ------ GENERATED ------
|
||||
*/`
|
||||
|
||||
main :: proc() {
|
||||
gen_p256r1_tables()
|
||||
}
|
||||
|
||||
gen_p256r1_tables :: proc() {
|
||||
Affine_Point_p256r1 :: struct {
|
||||
x: secec.Field_Element_p256r1,
|
||||
y: secec.Field_Element_p256r1,
|
||||
}
|
||||
Multiply_Table_p256r1_hi: [32][15]Affine_Point_p256r1
|
||||
Multiply_Table_p256r1_lo: [32][15]Affine_Point_p256r1
|
||||
|
||||
g, p: secec.Point_p256r1
|
||||
secec.pt_generator(&g)
|
||||
|
||||
// Precompute ([1,15] << n) * G multiples of G, MSB->LSB
|
||||
for i in 0..<32 {
|
||||
b: [32]byte
|
||||
for j in 1..<16 {
|
||||
b[i] = u8(j) << 4
|
||||
secec.pt_scalar_mul_bytes(&p, &g, b[:], true)
|
||||
secec.pt_rescale(&p, &p)
|
||||
secec.fe_set(&Multiply_Table_p256r1_hi[i][j-1].x, &p.x)
|
||||
secec.fe_set(&Multiply_Table_p256r1_hi[i][j-1].y, &p.y)
|
||||
|
||||
b[i] = u8(j)
|
||||
secec.pt_scalar_mul_bytes(&p, &g, b[:], true)
|
||||
secec.pt_rescale(&p, &p)
|
||||
secec.fe_set(&Multiply_Table_p256r1_lo[i][j-1].x, &p.x)
|
||||
secec.fe_set(&Multiply_Table_p256r1_lo[i][j-1].y, &p.y)
|
||||
|
||||
b[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
fn := path.join({ODIN_ROOT, "core", "crypto", "_weierstrass", "secp256r1_table.odin"})
|
||||
bld: strings.Builder
|
||||
w := strings.to_writer(&bld)
|
||||
|
||||
fmt.wprintln(w, "package _weierstrass")
|
||||
fmt.wprintln(w, "")
|
||||
fmt.wprintln(w, GENERATED)
|
||||
fmt.wprintln(w, "")
|
||||
fmt.wprintln(w, "import \"core:crypto\"")
|
||||
fmt.wprintln(w, "")
|
||||
fmt.wprintln(w, "when crypto.COMPACT_IMPLS == false {")
|
||||
|
||||
fmt.wprintln(w, "\t@(private,rodata)")
|
||||
fmt.wprintln(w, "\tGen_Multiply_Table_p256r1_hi := [32][15]Affine_Point_p256r1 {")
|
||||
for &v, i in Multiply_Table_p256r1_hi {
|
||||
fmt.wprintln(w, "\t\t{")
|
||||
for &ap, j in v {
|
||||
fmt.wprintln(w, "\t\t\t{")
|
||||
|
||||
x, y := &ap.x, &ap.y
|
||||
fmt.wprintf(w, "\t\t\t\t{{%d, %d, %d, %d},\n", x[0], x[1], x[2], x[3])
|
||||
fmt.wprintf(w, "\t\t\t\t{{%d, %d, %d, %d},\n", y[0], y[1], y[2], y[3])
|
||||
|
||||
fmt.wprintln(w, "\t\t\t},")
|
||||
}
|
||||
fmt.wprintln(w, "\t\t},")
|
||||
}
|
||||
fmt.wprintln(w, "\t}\n")
|
||||
|
||||
fmt.wprintln(w, "\t@(private,rodata)")
|
||||
fmt.wprintln(w, "\tGen_Multiply_Table_p256r1_lo := [32][15]Affine_Point_p256r1 {")
|
||||
for &v, i in Multiply_Table_p256r1_lo {
|
||||
fmt.wprintln(w, "\t\t{")
|
||||
for &ap, j in v {
|
||||
fmt.wprintln(w, "\t\t\t{")
|
||||
|
||||
x, y := &ap.x, &ap.y
|
||||
fmt.wprintf(w, "\t\t\t\t{{%d, %d, %d, %d},\n", x[0], x[1], x[2], x[3])
|
||||
fmt.wprintf(w, "\t\t\t\t{{%d, %d, %d, %d},\n", y[0], y[1], y[2], y[3])
|
||||
|
||||
fmt.wprintln(w, "\t\t\t},")
|
||||
}
|
||||
fmt.wprintln(w, "\t\t},")
|
||||
}
|
||||
fmt.wprintln(w, "\t}")
|
||||
|
||||
fmt.wprintln(w, "}")
|
||||
|
||||
_ = os.write_entire_file(fn, transmute([]byte)(strings.to_string(bld)))
|
||||
}
|
||||
@@ -2,8 +2,12 @@
|
||||
package crypto
|
||||
|
||||
import "base:runtime"
|
||||
import subtle "core:crypto/_subtle"
|
||||
import "core:mem"
|
||||
|
||||
// Omit large precomputed tables, trading off performance for size.
|
||||
COMPACT_IMPLS: bool : #config(ODIN_CRYPTO_COMPACT, false)
|
||||
|
||||
// HAS_RAND_BYTES is true iff the runtime provides a cryptographic
|
||||
// entropy source.
|
||||
HAS_RAND_BYTES :: runtime.HAS_RAND_BYTES
|
||||
@@ -44,7 +48,17 @@ compare_byte_ptrs_constant_time :: proc "contextless" (a, b: ^byte, n: int) -> i
|
||||
|
||||
// After the loop, v == 0 iff a == b. The subtraction will underflow
|
||||
// iff v == 0, setting the sign-bit, which gets returned.
|
||||
return int((u32(v)-1) >> 31)
|
||||
return subtle.eq(0, v)
|
||||
}
|
||||
|
||||
// is_zero_constant_time returns 1 iff b is all 0s, 0 otherwise.
|
||||
is_zero_constant_time :: proc "contextless" (b: []byte) -> int {
|
||||
v: byte
|
||||
for b_ in b {
|
||||
v |= b_
|
||||
}
|
||||
|
||||
return subtle.byte_eq(0, v)
|
||||
}
|
||||
|
||||
// rand_bytes fills the dst buffer with cryptographic entropy taken from
|
||||
|
||||
4
core/crypto/ecdh/doc.odin
Normal file
4
core/crypto/ecdh/doc.odin
Normal file
@@ -0,0 +1,4 @@
|
||||
/*
|
||||
A generic interface to Elliptic Curve Diffie-Hellman key exchange.
|
||||
*/
|
||||
package ecdh
|
||||
404
core/crypto/ecdh/ecdh.odin
Normal file
404
core/crypto/ecdh/ecdh.odin
Normal file
@@ -0,0 +1,404 @@
|
||||
package ecdh
|
||||
|
||||
import "core:crypto"
|
||||
import secec "core:crypto/_weierstrass"
|
||||
import "core:crypto/x25519"
|
||||
import "core:crypto/x448"
|
||||
import "core:mem"
|
||||
import "core:reflect"
|
||||
|
||||
// Note: For these primitives scalar size = point size
|
||||
@(private="file")
|
||||
X25519_Buf :: [x25519.SCALAR_SIZE]byte
|
||||
@(private="file")
|
||||
X448_Buf :: [x448.SCALAR_SIZE]byte
|
||||
|
||||
// Curve the curve identifier associated with a given Private_Key
|
||||
// or Public_Key
|
||||
Curve :: enum {
|
||||
Invalid,
|
||||
SECP256R1,
|
||||
X25519,
|
||||
X448,
|
||||
}
|
||||
|
||||
// CURVE_NAMES is the Curve to curve name string.
|
||||
CURVE_NAMES := [Curve]string {
|
||||
.Invalid = "Invalid",
|
||||
.SECP256R1 = "secp256r1",
|
||||
.X25519 = "X25519",
|
||||
.X448 = "X448",
|
||||
}
|
||||
|
||||
// PRIVATE_KEY_SIZES is the Curve to private key size in bytes.
|
||||
PRIVATE_KEY_SIZES := [Curve]int {
|
||||
.Invalid = 0,
|
||||
.SECP256R1 = secec.SC_SIZE_P256R1,
|
||||
.X25519 = x25519.SCALAR_SIZE,
|
||||
.X448 = x448.SCALAR_SIZE,
|
||||
}
|
||||
|
||||
// PUBLIC_KEY_SIZES is the Curve to public key size in bytes.
|
||||
PUBLIC_KEY_SIZES := [Curve]int {
|
||||
.Invalid = 0,
|
||||
.SECP256R1 = 1 + 2 * secec.FE_SIZE_P256R1,
|
||||
.X25519 = x25519.POINT_SIZE,
|
||||
.X448 = x448.POINT_SIZE,
|
||||
}
|
||||
|
||||
// SHARED_SECRET_SIZES is the Curve to shared secret size in bytes.
|
||||
SHARED_SECRET_SIZES := [Curve]int {
|
||||
.Invalid = 0,
|
||||
.SECP256R1 = secec.FE_SIZE_P256R1,
|
||||
.X25519 = x25519.POINT_SIZE,
|
||||
.X448 = x448.POINT_SIZE,
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
_PRIV_IMPL_IDS := [Curve]typeid {
|
||||
.Invalid = nil,
|
||||
.SECP256R1 = typeid_of(secec.Scalar_p256r1),
|
||||
.X25519 = typeid_of(X25519_Buf),
|
||||
.X448 = typeid_of(X448_Buf),
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
_PUB_IMPL_IDS := [Curve]typeid {
|
||||
.Invalid = nil,
|
||||
.SECP256R1 = typeid_of(secec.Point_p256r1),
|
||||
.X25519 = typeid_of(X25519_Buf),
|
||||
.X448 = typeid_of(X448_Buf),
|
||||
}
|
||||
|
||||
// Private_Key is an ECDH private key.
|
||||
Private_Key :: struct {
|
||||
// WARNING: All of the members are to be treated as internal (ie:
|
||||
// the Private_Key structure is intended to be opaque).
|
||||
_curve: Curve,
|
||||
_impl: union {
|
||||
secec.Scalar_p256r1,
|
||||
X25519_Buf,
|
||||
X448_Buf,
|
||||
},
|
||||
_pub_key: Public_Key,
|
||||
}
|
||||
|
||||
// Public_Key is an ECDH public key.
|
||||
Public_Key :: struct {
|
||||
// WARNING: All of the members are to be treated as internal (ie:
|
||||
// the Public_Key structure is intended to be opaque).
|
||||
_curve: Curve,
|
||||
_impl: union {
|
||||
secec.Point_p256r1,
|
||||
X25519_Buf,
|
||||
X448_Buf,
|
||||
},
|
||||
}
|
||||
|
||||
// private_key_generate uses the system entropy source to generate a new
|
||||
// Private_Key. This will only fail iff the system entropy source is
|
||||
// missing or broken.
|
||||
private_key_generate :: proc(priv_key: ^Private_Key, curve: Curve) -> bool {
|
||||
private_key_clear(priv_key)
|
||||
|
||||
if !crypto.HAS_RAND_BYTES {
|
||||
return false
|
||||
}
|
||||
|
||||
reflect.set_union_variant_typeid(
|
||||
priv_key._impl,
|
||||
_PRIV_IMPL_IDS[curve],
|
||||
)
|
||||
|
||||
#partial switch curve {
|
||||
case .SECP256R1:
|
||||
sc := &priv_key._impl.(secec.Scalar_p256r1)
|
||||
|
||||
// 384-bits reduced makes the modulo bias insignificant
|
||||
b: [48]byte = ---
|
||||
defer (mem.zero_explicit(&b, size_of(b)))
|
||||
for {
|
||||
crypto.rand_bytes(b[:])
|
||||
_ = secec.sc_set_bytes(sc, b[:])
|
||||
if secec.sc_is_zero(sc) == 0 { // Likely
|
||||
break
|
||||
}
|
||||
}
|
||||
case .X25519:
|
||||
sc := &priv_key._impl.(X25519_Buf)
|
||||
crypto.rand_bytes(sc[:])
|
||||
case .X448:
|
||||
sc := &priv_key._impl.(X448_Buf)
|
||||
crypto.rand_bytes(sc[:])
|
||||
case:
|
||||
panic("crypto/ecdh: invalid curve")
|
||||
}
|
||||
|
||||
priv_key._curve = curve
|
||||
private_key_generate_public(priv_key)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// private_key_set_bytes decodes a byte-encoded private key, and returns
|
||||
// true iff the operation was successful.
|
||||
private_key_set_bytes :: proc(priv_key: ^Private_Key, curve: Curve, b: []byte) -> bool {
|
||||
private_key_clear(priv_key)
|
||||
|
||||
if len(b) != PRIVATE_KEY_SIZES[curve] {
|
||||
return false
|
||||
}
|
||||
|
||||
reflect.set_union_variant_typeid(
|
||||
priv_key._impl,
|
||||
_PRIV_IMPL_IDS[curve],
|
||||
)
|
||||
|
||||
#partial switch curve {
|
||||
case .SECP256R1:
|
||||
sc := &priv_key._impl.(secec.Scalar_p256r1)
|
||||
did_reduce := secec.sc_set_bytes(sc, b)
|
||||
is_zero := secec.sc_is_zero(sc) == 1
|
||||
|
||||
// Reject `0` and scalars that are not less than the
|
||||
// curve order.
|
||||
if did_reduce || is_zero {
|
||||
private_key_clear(priv_key)
|
||||
return false
|
||||
}
|
||||
case .X25519:
|
||||
sc := &priv_key._impl.(X25519_Buf)
|
||||
copy(sc[:], b)
|
||||
case .X448:
|
||||
sc := &priv_key._impl.(X448_Buf)
|
||||
copy(sc[:], b)
|
||||
case:
|
||||
panic("crypto/ecdh: invalid curve")
|
||||
}
|
||||
|
||||
priv_key._curve = curve
|
||||
private_key_generate_public(priv_key)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
private_key_generate_public :: proc(priv_key: ^Private_Key) {
|
||||
switch &sc in priv_key._impl {
|
||||
case secec.Scalar_p256r1:
|
||||
pub_key: secec.Point_p256r1 = ---
|
||||
secec.pt_scalar_mul_generator(&pub_key, &sc)
|
||||
secec.pt_rescale(&pub_key, &pub_key)
|
||||
priv_key._pub_key._impl = pub_key
|
||||
case X25519_Buf:
|
||||
pub_key: X25519_Buf = ---
|
||||
x25519.scalarmult_basepoint(pub_key[:], sc[:])
|
||||
priv_key._pub_key._impl = pub_key
|
||||
case X448_Buf:
|
||||
pub_key: X448_Buf = ---
|
||||
x448.scalarmult_basepoint(pub_key[:], sc[:])
|
||||
priv_key._pub_key._impl = pub_key
|
||||
case:
|
||||
panic("crypto/ecdh: invalid curve")
|
||||
}
|
||||
|
||||
priv_key._pub_key._curve = priv_key._curve
|
||||
}
|
||||
|
||||
// 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")
|
||||
ensure(len(dst) == PRIVATE_KEY_SIZES[priv_key._curve], "crypto/ecdh: invalid destination size")
|
||||
|
||||
#partial switch priv_key._curve {
|
||||
case .SECP256R1:
|
||||
sc := &priv_key._impl.(secec.Scalar_p256r1)
|
||||
secec.sc_bytes(dst, sc)
|
||||
case .X25519:
|
||||
sc := &priv_key._impl.(X25519_Buf)
|
||||
copy(dst, sc[:])
|
||||
case .X448:
|
||||
sc := &priv_key._impl.(X448_Buf)
|
||||
copy(dst, sc[:])
|
||||
case:
|
||||
panic("crypto/ecdh: invalid curve")
|
||||
}
|
||||
}
|
||||
|
||||
// private_key_equal returns true iff the private keys are equal,
|
||||
// in constant time.
|
||||
private_key_equal :: proc(p, q: ^Private_Key) -> bool {
|
||||
if p._curve != q._curve {
|
||||
return false
|
||||
}
|
||||
|
||||
#partial switch p._curve {
|
||||
case .SECP256R1:
|
||||
sc_p, sc_q := &p._impl.(secec.Scalar_p256r1), &q._impl.(secec.Scalar_p256r1)
|
||||
return secec.sc_equal(sc_p, sc_q) == 1
|
||||
case .X25519:
|
||||
b_p, b_q := &p._impl.(X25519_Buf), &q._impl.(X25519_Buf)
|
||||
return crypto.compare_constant_time(b_p[:], b_q[:]) == 1
|
||||
case .X448:
|
||||
b_p, b_q := &p._impl.(X448_Buf), &q._impl.(X448_Buf)
|
||||
return crypto.compare_constant_time(b_p[:], b_q[:]) == 1
|
||||
case:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// private_key_clear clears priv_key to the uninitialized state.
|
||||
private_key_clear :: proc "contextless" (priv_key: ^Private_Key) {
|
||||
mem.zero_explicit(priv_key, size_of(Private_Key))
|
||||
}
|
||||
|
||||
// public_key_set_bytes decodes a byte-encoded public key, and returns
|
||||
// true iff the operation was successful.
|
||||
public_key_set_bytes :: proc(pub_key: ^Public_Key, curve: Curve, b: []byte) -> bool {
|
||||
public_key_clear(pub_key)
|
||||
|
||||
if len(b) != PUBLIC_KEY_SIZES[curve] {
|
||||
return false
|
||||
}
|
||||
|
||||
reflect.set_union_variant_typeid(
|
||||
pub_key._impl,
|
||||
_PUB_IMPL_IDS[curve],
|
||||
)
|
||||
|
||||
#partial switch curve {
|
||||
case .SECP256R1:
|
||||
if b[0] != secec.SEC_PREFIX_UNCOMPRESSED {
|
||||
return false
|
||||
}
|
||||
|
||||
pt := &pub_key._impl.(secec.Point_p256r1)
|
||||
ok := secec.pt_set_sec_bytes(pt, b)
|
||||
if !ok || secec.pt_is_identity(pt) == 1 {
|
||||
return false
|
||||
}
|
||||
case .X25519:
|
||||
pt := &pub_key._impl.(X25519_Buf)
|
||||
copy(pt[:], b)
|
||||
case .X448:
|
||||
pt := &pub_key._impl.(X448_Buf)
|
||||
copy(pt[:], b)
|
||||
case:
|
||||
panic("crypto/ecdh: invalid curve")
|
||||
}
|
||||
|
||||
pub_key._curve = curve
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// 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")
|
||||
public_key_clear(pub_key)
|
||||
pub_key^ = priv_key._pub_key
|
||||
}
|
||||
|
||||
// public_key_bytes sets dst to byte-encoding of pub_key.
|
||||
public_key_bytes :: proc(pub_key: ^Public_Key, dst: []byte) {
|
||||
ensure(pub_key._curve != .Invalid, "crypto/ecdh: uninitialized public key")
|
||||
ensure(len(dst) == PUBLIC_KEY_SIZES[pub_key._curve], "crypto/ecdh: invalid destination size")
|
||||
|
||||
#partial switch pub_key._curve {
|
||||
case .SECP256R1:
|
||||
// Invariant: Unless the caller is manually building pub_key
|
||||
// `Z = 1`, so we can skip the rescale.
|
||||
pt := &pub_key._impl.(secec.Point_p256r1)
|
||||
|
||||
dst[0] = secec.SEC_PREFIX_UNCOMPRESSED
|
||||
secec.fe_bytes(dst[1:1+secec.FE_SIZE_P256R1], &pt.x)
|
||||
secec.fe_bytes(dst[1+secec.FE_SIZE_P256R1:], &pt.y)
|
||||
case .X25519:
|
||||
pt := &pub_key._impl.(X25519_Buf)
|
||||
copy(dst, pt[:])
|
||||
case .X448:
|
||||
pt := &pub_key._impl.(X448_Buf)
|
||||
copy(dst, pt[:])
|
||||
case:
|
||||
panic("crypto/ecdh: invalid curve")
|
||||
}
|
||||
}
|
||||
|
||||
// public_key_equal returns true iff the public keys are equal,
|
||||
// in constant time.
|
||||
public_key_equal :: proc(p, q: ^Public_Key) -> bool {
|
||||
if p._curve != q._curve {
|
||||
return false
|
||||
}
|
||||
|
||||
#partial switch p._curve {
|
||||
case .SECP256R1:
|
||||
pt_p, pt_q := &p._impl.(secec.Point_p256r1), &q._impl.(secec.Point_p256r1)
|
||||
return secec.pt_equal(pt_p, pt_q) == 1
|
||||
case .X25519:
|
||||
b_p, b_q := &p._impl.(X25519_Buf), &q._impl.(X25519_Buf)
|
||||
return crypto.compare_constant_time(b_p[:], b_q[:]) == 1
|
||||
case .X448:
|
||||
b_p, b_q := &p._impl.(X448_Buf), &q._impl.(X448_Buf)
|
||||
return crypto.compare_constant_time(b_p[:], b_q[:]) == 1
|
||||
case:
|
||||
panic("crypto/ecdh: invalid curve")
|
||||
}
|
||||
}
|
||||
|
||||
// public_key_clear clears pub_key to the uninitialized state.
|
||||
public_key_clear :: proc "contextless" (pub_key: ^Public_Key) {
|
||||
mem.zero_explicit(pub_key, size_of(Public_Key))
|
||||
}
|
||||
|
||||
// ecdh performs an Elliptic Curve Diffie-Hellman key exchange betwween
|
||||
// the Private_Key and Public_Key, writing the shared secret to dst.
|
||||
//
|
||||
// The neutral element is rejected as an error.
|
||||
@(require_results)
|
||||
ecdh :: proc(priv_key: ^Private_Key, pub_key: ^Public_Key, dst: []byte) -> bool {
|
||||
ensure(priv_key._curve == pub_key._curve, "crypto/ecdh: curve mismatch")
|
||||
ensure(pub_key._curve != .Invalid, "crypto/ecdh: uninitialized public key")
|
||||
ensure(len(dst) == SHARED_SECRET_SIZES[priv_key._curve], "crypto/ecdh: invalid shared secret size")
|
||||
|
||||
#partial switch priv_key._curve {
|
||||
case .SECP256R1:
|
||||
sc, pt := &priv_key._impl.(secec.Scalar_p256r1), &pub_key._impl.(secec.Point_p256r1)
|
||||
ss: secec.Point_p256r1
|
||||
defer secec.pt_clear(&ss)
|
||||
|
||||
secec.pt_scalar_mul(&ss, pt, sc)
|
||||
return secec.pt_bytes(dst, nil, &ss)
|
||||
case .X25519:
|
||||
sc, pt := &priv_key._impl.(X25519_Buf), &pub_key._impl.(X25519_Buf)
|
||||
x25519.scalarmult(dst, sc[:], pt[:])
|
||||
case .X448:
|
||||
sc, pt := &priv_key._impl.(X448_Buf), &pub_key._impl.(X448_Buf)
|
||||
x448.scalarmult(dst, sc[:], pt[:])
|
||||
case:
|
||||
panic("crypto/ecdh: invalid curve")
|
||||
}
|
||||
|
||||
// X25519/X448 check for all zero digest.
|
||||
return crypto.is_zero_constant_time(dst) == 0
|
||||
}
|
||||
|
||||
// curve returns the Curve used by a Private_Key or Public_Key instance.
|
||||
curve :: proc(k: ^$T) -> Curve where(T == Private_Key || T == Public_Key) {
|
||||
return k._curve
|
||||
}
|
||||
|
||||
// key_size returns the key size of a Private_Key or Public_Key in bytes.
|
||||
key_size :: proc(k: ^$T) -> int where(T == Private_Key || T == Public_Key) {
|
||||
when T == Private_Key {
|
||||
return PRIVATE_KEY_SIZES[k._curve]
|
||||
} else {
|
||||
return PUBLIC_KEY_SIZES[k._curve]
|
||||
}
|
||||
}
|
||||
|
||||
// shared_secret_size returns the shared secret size of a key exchange
|
||||
// in bytes.
|
||||
shared_secret_size :: proc(k: ^$T) -> int where(T == Private_Key || T == Public_Key) {
|
||||
return SHARED_SECRET_SIZES[k._curve]
|
||||
}
|
||||
@@ -170,7 +170,7 @@ public_key_set_bytes :: proc "contextless" (pub_key: ^Public_Key, b: []byte) ->
|
||||
|
||||
// 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 public key")
|
||||
ensure(priv_key._is_initialized, "crypto/ed25519: uninitialized private key")
|
||||
|
||||
src := &priv_key._pub_key
|
||||
copy(pub_key._b[:], src._b[:])
|
||||
|
||||
@@ -6,6 +6,8 @@ See:
|
||||
*/
|
||||
package x25519
|
||||
|
||||
import "core:crypto"
|
||||
import ed "core:crypto/_edwards25519"
|
||||
import field "core:crypto/_fiat/field_curve25519"
|
||||
import "core:mem"
|
||||
|
||||
@@ -14,8 +16,10 @@ SCALAR_SIZE :: 32
|
||||
// POINT_SIZE is the size of a X25519 point (public key/shared secret) in bytes.
|
||||
POINT_SIZE :: 32
|
||||
|
||||
@(private, rodata)
|
||||
_BASE_POINT: [32]byte = {9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
when crypto.COMPACT_IMPLS == true {
|
||||
@(private,rodata)
|
||||
_BASE_POINT: [32]byte = {9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_scalar_bit :: #force_inline proc "contextless" (s: ^[32]byte, i: int) -> u8 {
|
||||
@@ -111,19 +115,44 @@ scalarmult :: proc(dst, scalar, point: []byte) {
|
||||
e[31] &= 127
|
||||
e[31] |= 64
|
||||
|
||||
p: [32]byte = ---
|
||||
copy_slice(p[:], point)
|
||||
|
||||
d: [32]byte = ---
|
||||
_scalarmult(&d, &e, &p)
|
||||
copy_slice(dst, d[:])
|
||||
p := (^[32]byte)(raw_data(point))
|
||||
d := (^[32]byte)(raw_data(dst))
|
||||
_scalarmult(d, &e, p)
|
||||
|
||||
mem.zero_explicit(&e, size_of(e))
|
||||
mem.zero_explicit(&d, size_of(d))
|
||||
}
|
||||
|
||||
// scalarmult_basepoint "multiplies" the provided scalar with the X25519
|
||||
// base point and writes the resulting point to dst.
|
||||
scalarmult_basepoint :: proc(dst, scalar: []byte) {
|
||||
scalarmult(dst, scalar, _BASE_POINT[:])
|
||||
when crypto.COMPACT_IMPLS == true {
|
||||
scalarmult(dst, scalar, _BASE_POINT[:])
|
||||
} else {
|
||||
ensure(len(scalar) == SCALAR_SIZE, "crypto/x25519: invalid scalar size")
|
||||
ensure(len(dst) == POINT_SIZE, "crypto/x25519: invalid destination point size")
|
||||
|
||||
sc: ed.Scalar = ---
|
||||
ed.sc_set_bytes_rfc8032(&sc, scalar)
|
||||
|
||||
ge: ed.Group_Element = ---
|
||||
ed.ge_scalarmult_basepoint(&ge, &sc)
|
||||
|
||||
// u = (y + z)/(z - y)
|
||||
y_plus_z: field.Loose_Field_Element = ---
|
||||
z_minus_y: field.Loose_Field_Element = ---
|
||||
u: field.Tight_Field_Element = ---
|
||||
|
||||
field.fe_add(&y_plus_z, &ge.y, &ge.z)
|
||||
field.fe_sub(&z_minus_y, &ge.z, &ge.y)
|
||||
field.fe_carry_inv(&u, &z_minus_y)
|
||||
field.fe_carry_mul(&u, &y_plus_z, field.fe_relax_cast(&u))
|
||||
|
||||
dst_ := (^[32]byte)(raw_data(dst))
|
||||
field.fe_to_bytes(dst_, &u)
|
||||
|
||||
field.fe_clear_vec([]^field.Loose_Field_Element{&y_plus_z, &z_minus_y})
|
||||
field.fe_clear(&u)
|
||||
ed.sc_clear(&sc)
|
||||
ed.ge_clear(&ge)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,15 +153,15 @@ decode :: proc(
|
||||
padding_count += 1
|
||||
}
|
||||
|
||||
// Verify no padding in the middle
|
||||
for i := 0; i < data_len - padding_count; i += 1 {
|
||||
if data[i] == byte(PADDING) {
|
||||
return nil, .Malformed_Input
|
||||
}
|
||||
}
|
||||
|
||||
// Check for proper padding and length combinations
|
||||
if padding_count > 0 {
|
||||
// Verify no padding in the middle
|
||||
for i := 0; i < data_len - padding_count; i += 1 {
|
||||
if data[i] == byte(PADDING) {
|
||||
return nil, .Malformed_Input
|
||||
}
|
||||
}
|
||||
|
||||
content_len := data_len - padding_count
|
||||
mod8 := content_len % 8
|
||||
required_padding: int
|
||||
|
||||
@@ -24,6 +24,18 @@ ENC_TABLE := [64]byte {
|
||||
'4', '5', '6', '7', '8', '9', '+', '/',
|
||||
}
|
||||
|
||||
// Encoding table for Base64url variant
|
||||
ENC_URL_TABLE := [64]byte {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
||||
'4', '5', '6', '7', '8', '9', '-', '_',
|
||||
}
|
||||
|
||||
PADDING :: '='
|
||||
|
||||
DEC_TABLE := [256]u8 {
|
||||
@@ -61,6 +73,43 @@ DEC_TABLE := [256]u8 {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
}
|
||||
|
||||
// Decoding table for Base64url variant
|
||||
DEC_URL_TABLE := [256]u8 {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 62, 0, 0,
|
||||
52, 53, 54, 55, 56, 57, 58, 59,
|
||||
60, 61, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 2, 3, 4, 5, 6,
|
||||
7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22,
|
||||
23, 24, 25, 0, 0, 0, 0, 63,
|
||||
0, 26, 27, 28, 29, 30, 31, 32,
|
||||
33, 34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48,
|
||||
49, 50, 51, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
}
|
||||
|
||||
|
||||
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> (encoded: string, err: mem.Allocator_Error) #optional_allocator_error {
|
||||
out_length := encoded_len(data)
|
||||
if out_length == 0 {
|
||||
|
||||
@@ -21,6 +21,7 @@ package encoding_unicode_entity
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
*/
|
||||
|
||||
import "base:runtime"
|
||||
import "core:unicode/utf8"
|
||||
import "core:unicode"
|
||||
import "core:strings"
|
||||
@@ -141,8 +142,10 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
|
||||
write_string(&builder, entity)
|
||||
} else {
|
||||
if .No_Entity_Decode not_in options {
|
||||
if decoded, ok := xml_decode_entity(entity); ok {
|
||||
write_rune(&builder, decoded)
|
||||
if decoded, count, ok := xml_decode_entity(entity); ok {
|
||||
for i in 0..<count {
|
||||
write_rune(&builder, decoded[i])
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -212,17 +215,16 @@ advance :: proc(t: ^Tokenizer) -> (err: Error) {
|
||||
}
|
||||
}
|
||||
|
||||
xml_decode_entity :: proc(entity: string) -> (decoded: rune, ok: bool) {
|
||||
xml_decode_entity :: proc(entity: string) -> (decoded: [2]rune, rune_count: int, ok: bool) {
|
||||
entity := entity
|
||||
if len(entity) == 0 { return -1, false }
|
||||
if len(entity) == 0 { return }
|
||||
|
||||
switch entity[0] {
|
||||
case '#':
|
||||
if entity[0] == '#' {
|
||||
base := 10
|
||||
val := 0
|
||||
entity = entity[1:]
|
||||
|
||||
if len(entity) == 0 { return -1, false }
|
||||
if len(entity) == 0 { return }
|
||||
|
||||
if entity[0] == 'x' || entity[0] == 'X' {
|
||||
base = 16
|
||||
@@ -237,30 +239,275 @@ xml_decode_entity :: proc(entity: string) -> (decoded: rune, ok: bool) {
|
||||
val += int(r - '0')
|
||||
|
||||
case 'a'..='f':
|
||||
if base == 10 { return -1, false }
|
||||
if base == 10 { return }
|
||||
val *= base
|
||||
val += int(r - 'a' + 10)
|
||||
|
||||
case 'A'..='F':
|
||||
if base == 10 { return -1, false }
|
||||
if base == 10 { return }
|
||||
val *= base
|
||||
val += int(r - 'A' + 10)
|
||||
|
||||
case:
|
||||
return -1, false
|
||||
return
|
||||
}
|
||||
|
||||
if val > MAX_RUNE_CODEPOINT { return -1, false }
|
||||
if val > MAX_RUNE_CODEPOINT { return }
|
||||
entity = entity[1:]
|
||||
}
|
||||
return rune(val), true
|
||||
|
||||
case:
|
||||
// Named entity.
|
||||
return named_xml_entity_to_rune(entity)
|
||||
return rune(val), 1, true
|
||||
}
|
||||
// Named entity.
|
||||
return named_xml_entity_to_rune(entity)
|
||||
}
|
||||
|
||||
|
||||
// escape_html escapes special characters like '&' to become '&'.
|
||||
// It escapes only 5 different characters: & ' < > and "
|
||||
@(require_results)
|
||||
escape_html :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> (output: string, was_allocation: bool) {
|
||||
/*
|
||||
& -> &
|
||||
' -> ' // ' is shorter than ' (NOTE: ' was not available until HTML 5)
|
||||
< -> <
|
||||
> -> >
|
||||
" -> " // " is shorter than "
|
||||
*/
|
||||
|
||||
b := transmute([]byte)s
|
||||
|
||||
extra_bytes_needed := 0
|
||||
|
||||
for c in b {
|
||||
switch c {
|
||||
case '&': extra_bytes_needed += 4
|
||||
case '\'': extra_bytes_needed += 4
|
||||
case '<': extra_bytes_needed += 3
|
||||
case '>': extra_bytes_needed += 3
|
||||
case '"': extra_bytes_needed += 4
|
||||
}
|
||||
}
|
||||
|
||||
if extra_bytes_needed == 0 {
|
||||
return s, false
|
||||
}
|
||||
|
||||
t, err := make([]byte, len(s) + extra_bytes_needed, allocator, loc)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
was_allocation = true
|
||||
|
||||
w := 0
|
||||
for c in b {
|
||||
x := ""
|
||||
switch c {
|
||||
case '&': x = "&"
|
||||
case '\'': x = "'"
|
||||
case '<': x = "<"
|
||||
case '>': x = ">"
|
||||
case '"': x = """
|
||||
}
|
||||
if x != "" {
|
||||
copy(t[w:], x)
|
||||
w += len(x)
|
||||
} else {
|
||||
t[w] = c
|
||||
w += 1
|
||||
}
|
||||
}
|
||||
output = string(t[0:w])
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
unescape_html :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> (output: string, was_allocation: bool, err: runtime.Allocator_Error) {
|
||||
@(require_results)
|
||||
do_append :: proc(s: string, amp_idx: int, buf: ^[dynamic]byte) -> (n: int) {
|
||||
s, amp_idx := s, amp_idx
|
||||
|
||||
n += len(s[:amp_idx])
|
||||
if buf != nil { append(buf, s[:amp_idx]) }
|
||||
s = s[amp_idx:]
|
||||
for len(s) > 0 {
|
||||
b, w, j := unescape_entity(s)
|
||||
n += w
|
||||
if buf != nil { append(buf, ..b[:w]) }
|
||||
|
||||
s = s[j:]
|
||||
|
||||
amp_idx = strings.index_byte(s, '&')
|
||||
if amp_idx < 0 {
|
||||
n += len(s)
|
||||
if buf != nil { append(buf, s) }
|
||||
break
|
||||
}
|
||||
n += amp_idx
|
||||
if buf != nil { append(buf, s[:amp_idx]) }
|
||||
s = s[amp_idx:]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
s := s
|
||||
amp_idx := strings.index_byte(s, '&')
|
||||
if amp_idx < 0 {
|
||||
return s, false, nil
|
||||
}
|
||||
|
||||
// NOTE(bill): this does a two pass in order to minimize the allocations required
|
||||
bytes_required := do_append(s, amp_idx, nil)
|
||||
|
||||
buf := make([dynamic]byte, 0, bytes_required, allocator, loc) or_return
|
||||
was_allocation = true
|
||||
|
||||
_ = do_append(s, amp_idx, &buf)
|
||||
|
||||
assert(len(buf) == cap(buf))
|
||||
output = string(buf[:])
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Returns an unescaped string of an encoded XML/HTML entity.
|
||||
@(require_results)
|
||||
unescape_entity :: proc(s: string) -> (b: [8]byte, w: int, j: int) {
|
||||
s := s
|
||||
if len(s) < 2 {
|
||||
return
|
||||
}
|
||||
if s[0] != '&' {
|
||||
return
|
||||
}
|
||||
j = 1
|
||||
|
||||
if s[j] == '#' { // scan numbers
|
||||
j += 1
|
||||
if len(s) <= 3 { // remove `&#.`
|
||||
return
|
||||
}
|
||||
c := s[j]
|
||||
hex := false
|
||||
if c == 'x' || c == 'X' {
|
||||
hex = true
|
||||
j += 1
|
||||
}
|
||||
|
||||
x := rune(0)
|
||||
scan_number: for j < len(s) {
|
||||
c = s[j]
|
||||
j += 1
|
||||
if hex {
|
||||
switch c {
|
||||
case '0'..='9': x = 16*x + rune(c) - '0'; continue scan_number
|
||||
case 'a'..='f': x = 16*x + rune(c) - 'a' + 10; continue scan_number
|
||||
case 'A'..='F': x = 16*x + rune(c) - 'A' + 10; continue scan_number
|
||||
}
|
||||
} else {
|
||||
switch c {
|
||||
case '0'..='9': x = 10*x + rune(c) - '0'; continue scan_number
|
||||
}
|
||||
}
|
||||
|
||||
// Keep the ';' to check for cases which require it and cases which might not
|
||||
if c != ';' {
|
||||
j -= 1
|
||||
}
|
||||
break scan_number
|
||||
}
|
||||
|
||||
|
||||
if j <= 3 { // no replacement characters found
|
||||
return
|
||||
}
|
||||
|
||||
@(static, rodata)
|
||||
windows_1252_replacement_table := [0xa0 - 0x80]rune{ // Windows-1252 -> UTF-8
|
||||
'\u20ac', '\u0081', '\u201a', '\u0192',
|
||||
'\u201e', '\u2026', '\u2020', '\u2021',
|
||||
'\u02c6', '\u2030', '\u0160', '\u2039',
|
||||
'\u0152', '\u008d', '\u017d', '\u008f',
|
||||
'\u0090', '\u2018', '\u2019', '\u201c',
|
||||
'\u201d', '\u2022', '\u2013', '\u2014',
|
||||
'\u02dc', '\u2122', '\u0161', '\u203a',
|
||||
'\u0153', '\u009d', '\u017e', '\u0178',
|
||||
}
|
||||
|
||||
switch x {
|
||||
case 0x80..<0xa0:
|
||||
x = windows_1252_replacement_table[x-0x80]
|
||||
case 0, 0xd800..=0xdfff:
|
||||
x = utf8.RUNE_ERROR
|
||||
case:
|
||||
if x > 0x10ffff {
|
||||
x = utf8.RUNE_ERROR
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
b1, w1 := utf8.encode_rune(x)
|
||||
w += copy(b[:], b1[:w1])
|
||||
return
|
||||
}
|
||||
|
||||
// Lookup by entity names
|
||||
|
||||
scan_ident: for j < len(s) { // scan over letters and digits
|
||||
c := s[j]
|
||||
j += 1
|
||||
|
||||
switch c {
|
||||
case 'a'..='z', 'A'..='Z', '0'..='9':
|
||||
continue scan_ident
|
||||
}
|
||||
// Keep the ';' to check for cases which require it and cases which might not
|
||||
if c != ';' {
|
||||
j -= 1
|
||||
}
|
||||
break scan_ident
|
||||
}
|
||||
|
||||
entity_name := s[1:j]
|
||||
if len(entity_name) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if entity_name[len(entity_name)-1] == ';' {
|
||||
entity_name = entity_name[:len(entity_name)-1]
|
||||
}
|
||||
|
||||
if r2, _, ok := named_xml_entity_to_rune(entity_name); ok {
|
||||
b1, w1 := utf8.encode_rune(r2[0])
|
||||
w += copy(b[w:], b1[:w1])
|
||||
if r2[1] != 0 {
|
||||
b2, w2 := utf8.encode_rune(r2[1])
|
||||
w += copy(b[w:], b2[:w2])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// The longest entities that do not end with a semicolon are <=6 bytes long
|
||||
LONGEST_ENTITY_WITHOUT_SEMICOLON :: 6
|
||||
|
||||
n := min(len(entity_name)-1, LONGEST_ENTITY_WITHOUT_SEMICOLON)
|
||||
for i := n; i > 1; i -= 1 {
|
||||
if r2, _, ok := named_xml_entity_to_rune(entity_name[:i]); ok {
|
||||
b1, w1 := utf8.encode_rune(r2[0])
|
||||
w += copy(b[w:], b1[:w1])
|
||||
if r2[1] != 0 {
|
||||
b2, w2 := utf8.encode_rune(r2[1])
|
||||
w += copy(b[w:], b2[:w2])
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Private XML helper to extract `&<stuff>;` entity.
|
||||
@(private="file")
|
||||
_extract_xml_entity :: proc(t: ^Tokenizer) -> (entity: string, err: Error) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -62,6 +62,78 @@ Marshal_Options :: struct {
|
||||
mjson_skipped_first_braces_end: bool,
|
||||
}
|
||||
|
||||
User_Marshaler :: #type proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> Marshal_Error
|
||||
|
||||
Register_User_Marshaler_Error :: enum {
|
||||
None,
|
||||
No_User_Marshaler,
|
||||
Marshaler_Previously_Found,
|
||||
}
|
||||
|
||||
// Example User Marshaler:
|
||||
// Custom Marshaler for `int`
|
||||
// Some_Marshaler :: proc(w: io.Writer, v: any, opt: ^json.Marshal_Options) -> json.Marshal_Error {
|
||||
// io.write_string(w, fmt.tprintf("%b", v))
|
||||
// return json.Marshal_Data_Error.None
|
||||
// }
|
||||
//
|
||||
// main :: proc() {
|
||||
// // Ensure the json._user_marshaler map is initialized
|
||||
// json.set_user_marshalers(new(map[typeid]json.User_Marshaler))
|
||||
// reg_err := json.register_user_marshaler(type_info_of(int).id, Some_Marshaler)
|
||||
// assert(reg_err == .None)
|
||||
//
|
||||
//
|
||||
// // Use the custom marshaler
|
||||
// SomeType :: struct {
|
||||
// value: int,
|
||||
// }
|
||||
//
|
||||
// x := SomeType{42}
|
||||
// data, marshal_err := json.marshal(x)
|
||||
// assert(marshal_err == nil)
|
||||
// defer delete(data)
|
||||
//
|
||||
// fmt.println("Custom output:", string(data)) // Custom output: {"value":101010}
|
||||
// }
|
||||
|
||||
// NOTE(Jeroen): This is a pointer to prevent accidental additions
|
||||
// it is prefixed with `_` rather than marked with a private attribute so that users can access it if necessary
|
||||
_user_marshalers: ^map[typeid]User_Marshaler
|
||||
|
||||
// Sets user-defined marshalers for custom json marshaling of specific types
|
||||
//
|
||||
// Inputs:
|
||||
// - m: A pointer to a map of typeids to User_Marshaler procs.
|
||||
//
|
||||
// NOTE: Must be called before using register_user_marshaler.
|
||||
//
|
||||
set_user_marshalers :: proc(m: ^map[typeid]User_Marshaler) {
|
||||
assert(_user_marshalers == nil, "set_user_marshalers must not be called more than once.")
|
||||
_user_marshalers = m
|
||||
}
|
||||
|
||||
// Registers a user-defined marshaler for a specific typeid
|
||||
//
|
||||
// Inputs:
|
||||
// - id: The typeid of the custom type.
|
||||
// - formatter: The User_Marshaler function for the custom type.
|
||||
//
|
||||
// Returns: A Register_User_Marshaler_Error value indicating the success or failure of the operation.
|
||||
//
|
||||
// WARNING: set_user_marshalers must be called before using this procedure.
|
||||
//
|
||||
register_user_marshaler :: proc(id: typeid, marshaler: User_Marshaler) -> Register_User_Marshaler_Error {
|
||||
if _user_marshalers == nil {
|
||||
return .No_User_Marshaler
|
||||
}
|
||||
if prev, found := _user_marshalers[id]; found && prev != nil {
|
||||
return .Marshaler_Previously_Found
|
||||
}
|
||||
_user_marshalers[id] = marshaler
|
||||
return .None
|
||||
}
|
||||
|
||||
marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Marshal_Error) {
|
||||
b := strings.builder_make(allocator, loc)
|
||||
defer if err != nil {
|
||||
@@ -91,6 +163,13 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
return
|
||||
}
|
||||
|
||||
if _user_marshalers != nil {
|
||||
marshaler := _user_marshalers[v.id]
|
||||
if marshaler != nil {
|
||||
return marshaler(w, v, opt)
|
||||
}
|
||||
}
|
||||
|
||||
ti := runtime.type_info_base(type_info_of(v.id))
|
||||
a := any{v.data, ti.id}
|
||||
|
||||
@@ -122,9 +201,9 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
|
||||
case runtime.Type_Info_Rune:
|
||||
r := a.(rune)
|
||||
io.write_byte(w, '"') or_return
|
||||
io.write_escaped_rune(w, r, '"', true) or_return
|
||||
io.write_byte(w, '"') or_return
|
||||
io.write_byte(w, '"') or_return
|
||||
io.write_escaped_rune(w, r, '"', for_json = true) or_return
|
||||
io.write_byte(w, '"') or_return
|
||||
|
||||
case runtime.Type_Info_Float:
|
||||
switch f in a {
|
||||
@@ -414,6 +493,12 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
|
||||
opt_write_iteration(w, opt, first_iteration) or_return
|
||||
first_iteration = false
|
||||
|
||||
if opt.pretty {
|
||||
comment := reflect.struct_tag_get(reflect.Struct_Tag(info.tags[i]), "jsoncomment")
|
||||
opt_write_comment(w, opt, &comment) or_return
|
||||
}
|
||||
|
||||
if json_name != "" {
|
||||
opt_write_key(w, opt, json_name) or_return
|
||||
} else {
|
||||
@@ -533,6 +618,26 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
return
|
||||
}
|
||||
|
||||
// Newlines are split into multiple comment lines
|
||||
opt_write_comment :: proc(w: io.Writer, opt: ^Marshal_Options, comment: ^string) -> (err: io.Error) {
|
||||
if comment^ == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch opt.spec {
|
||||
case .JSON5, .MJSON:
|
||||
for line in strings.split_iterator(comment, "\n") {
|
||||
io.write_string(w, "// ") or_return
|
||||
io.write_string(w, line) or_return
|
||||
io.write_rune(w, '\n') or_return
|
||||
opt_write_indentation(w, opt) or_return
|
||||
}
|
||||
case .JSON: return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// write key as quoted string or with optional quotes in mjson
|
||||
opt_write_key :: proc(w: io.Writer, opt: ^Marshal_Options, name: string) -> (err: io.Error) {
|
||||
switch opt.spec {
|
||||
|
||||
@@ -38,7 +38,7 @@ parse_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers
|
||||
|
||||
switch p.spec {
|
||||
case .JSON:
|
||||
return parse_object(&p, loc)
|
||||
return parse_value(&p, loc)
|
||||
case .JSON5:
|
||||
return parse_value(&p, loc)
|
||||
case .SJSON:
|
||||
@@ -84,7 +84,7 @@ expect_token :: proc(p: ^Parser, kind: Token_Kind) -> Error {
|
||||
|
||||
|
||||
parse_colon :: proc(p: ^Parser) -> (err: Error) {
|
||||
colon_err := expect_token(p, .Colon)
|
||||
colon_err := expect_token(p, .Colon)
|
||||
if colon_err == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -133,13 +133,13 @@ parse_value :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err:
|
||||
f, _ := strconv.parse_f64(token.text)
|
||||
value = Float(f)
|
||||
return
|
||||
|
||||
|
||||
case .Ident:
|
||||
if p.spec == .MJSON {
|
||||
advance_token(p)
|
||||
return clone_string(token.text, p.allocator, loc)
|
||||
}
|
||||
|
||||
|
||||
case .String:
|
||||
advance_token(p)
|
||||
return unquote_string(token, p.spec, p.allocator, loc)
|
||||
@@ -192,7 +192,7 @@ parse_array :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err:
|
||||
for p.curr_token.kind != .Close_Bracket {
|
||||
elem := parse_value(p, loc) or_return
|
||||
append(&array, elem, loc)
|
||||
|
||||
|
||||
if parse_comma(p) {
|
||||
break
|
||||
}
|
||||
@@ -278,7 +278,7 @@ parse_object_body :: proc(p: ^Parser, end_token: Token_Kind, loc := #caller_loca
|
||||
if parse_comma(p) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return obj, .None
|
||||
}
|
||||
|
||||
@@ -481,4 +481,4 @@ unquote_string :: proc(token: Token, spec: Specification, allocator := context.a
|
||||
}
|
||||
|
||||
return string(b[:w]), nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ Error :: enum {
|
||||
Invalid_Number,
|
||||
String_Not_Terminated,
|
||||
Invalid_String,
|
||||
Invalid_Rune,
|
||||
|
||||
|
||||
// Parsing Errors
|
||||
|
||||
@@ -26,6 +26,80 @@ Unmarshal_Error :: union {
|
||||
Unsupported_Type_Error,
|
||||
}
|
||||
|
||||
User_Unmarshaler :: #type proc(p: ^Parser, v: any) -> Unmarshal_Error
|
||||
|
||||
Register_User_Unmarshaler_Error :: enum {
|
||||
None,
|
||||
No_User_Unmarshaler,
|
||||
Unmarshaler_Previously_Found,
|
||||
}
|
||||
|
||||
// Example User Unmarshaler:
|
||||
// Custom Unmarshaler for `int`
|
||||
// Some_Unmarshaler :: proc(p: ^json.Parser, v: any) -> json.Unmarshal_Error {
|
||||
// token := p.curr_token.text
|
||||
// i, ok := strconv.parse_i64_of_base(token, 2)
|
||||
// if !ok {
|
||||
// return .Invalid_Data
|
||||
//
|
||||
// }
|
||||
// (^int)(v.data)^ = int(i)
|
||||
// return .None
|
||||
// }
|
||||
//
|
||||
// _main :: proc() {
|
||||
// // Ensure the json._user_unmarshaler map is initialized
|
||||
// json.set_user_unmarshalers(new(map[typeid]json.User_Unmarshaler))
|
||||
// reg_err := json.register_user_unmarshaler(type_info_of(int).id, Some_Unmarshaler)
|
||||
// assert(reg_err == .None)
|
||||
//
|
||||
// data := `{"value":101010}`
|
||||
// SomeType :: struct {
|
||||
// value: int,
|
||||
// }
|
||||
// y: SomeType
|
||||
//
|
||||
// unmarshal_err := json.unmarshal(transmute([]byte)data, &y)
|
||||
// fmt.println(y, unmarshal_err)
|
||||
// }
|
||||
|
||||
// NOTE(Jeroen): This is a pointer to prevent accidental additions
|
||||
// it is prefixed with `_` rather than marked with a private attribute so that users can access it if necessary
|
||||
_user_unmarshalers: ^map[typeid]User_Unmarshaler
|
||||
|
||||
// Sets user-defined unmarshalers for custom json unmarshaling of specific types
|
||||
//
|
||||
// Inputs:
|
||||
// - m: A pointer to a map of typeids to User_Unmarshaler procs.
|
||||
//
|
||||
// NOTE: Must be called before using register_user_unmarshaler.
|
||||
//
|
||||
set_user_unmarshalers :: proc(m: ^map[typeid]User_Unmarshaler) {
|
||||
assert(_user_unmarshalers == nil, "set_user_unmarshalers must not be called more than once.")
|
||||
_user_unmarshalers = m
|
||||
}
|
||||
|
||||
// Registers a user-defined unmarshaler for a specific typeid
|
||||
//
|
||||
// Inputs:
|
||||
// - id: The typeid of the custom type.
|
||||
// - unmarshaler: The User_Unmarshaler function for the custom type.
|
||||
//
|
||||
// Returns: A Register_User_Unmarshaler_Error value indicating the success or failure of the operation.
|
||||
//
|
||||
// WARNING: set_user_unmarshalers must be called before using this procedure.
|
||||
//
|
||||
register_user_unmarshaler :: proc(id: typeid, unmarshaler: User_Unmarshaler) -> Register_User_Unmarshaler_Error {
|
||||
if _user_unmarshalers == nil {
|
||||
return .No_User_Unmarshaler
|
||||
}
|
||||
if prev, found := _user_unmarshalers[id]; found && prev != nil {
|
||||
return .Unmarshaler_Previously_Found
|
||||
}
|
||||
_user_unmarshalers[id] = unmarshaler
|
||||
return .None
|
||||
}
|
||||
|
||||
unmarshal_any :: proc(data: []byte, v: any, spec := DEFAULT_SPECIFICATION, allocator := context.allocator) -> Unmarshal_Error {
|
||||
v := v
|
||||
if v == nil || v.id == nil {
|
||||
@@ -37,8 +111,10 @@ unmarshal_any :: proc(data: []byte, v: any, spec := DEFAULT_SPECIFICATION, alloc
|
||||
return .Non_Pointer_Parameter
|
||||
}
|
||||
PARSE_INTEGERS :: true
|
||||
|
||||
if !is_valid(data, spec, PARSE_INTEGERS) {
|
||||
|
||||
// If we have custom unmarshalers, we skip validation in case the custom data is not quite up to spec.
|
||||
have_custom := _user_unmarshalers != nil && len(_user_unmarshalers) > 0
|
||||
if !have_custom && !is_valid(data, spec, PARSE_INTEGERS) {
|
||||
return .Invalid_Data
|
||||
}
|
||||
p := make_parser(data, spec, PARSE_INTEGERS, allocator)
|
||||
@@ -225,6 +301,15 @@ unmarshal_string_token :: proc(p: ^Parser, val: any, token: Token, ti: ^reflect.
|
||||
}
|
||||
ok = true
|
||||
return
|
||||
case rune:
|
||||
for rne, i in str {
|
||||
if i > 0 {
|
||||
dst = {}
|
||||
return false, .Invalid_Rune
|
||||
}
|
||||
dst = rne
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
#partial switch variant in ti.variant {
|
||||
@@ -265,12 +350,18 @@ unmarshal_string_token :: proc(p: ^Parser, val: any, token: Token, ti: ^reflect.
|
||||
return false, nil
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
|
||||
UNSUPPORTED_TYPE := Unsupported_Type_Error{v.id, p.curr_token}
|
||||
token := p.curr_token
|
||||
|
||||
if _user_unmarshalers != nil {
|
||||
unmarshaler := _user_unmarshalers[v.id]
|
||||
if unmarshaler != nil {
|
||||
return unmarshaler(p, v)
|
||||
}
|
||||
}
|
||||
|
||||
v := v
|
||||
ti := reflect.type_info_base(type_info_of(v.id))
|
||||
if u, ok := ti.variant.(reflect.Type_Info_Union); ok && token.kind != .Null {
|
||||
|
||||
@@ -5,10 +5,10 @@ import "core:mem"
|
||||
// NOTE(bill): is_valid will not check for duplicate keys
|
||||
is_valid :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false) -> bool {
|
||||
p := make_parser(data, spec, parse_integers, mem.nil_allocator())
|
||||
|
||||
|
||||
switch p.spec {
|
||||
case .JSON:
|
||||
return validate_object(&p)
|
||||
return validate_value(&p)
|
||||
case .JSON5:
|
||||
return validate_value(&p)
|
||||
case .MJSON:
|
||||
@@ -52,7 +52,7 @@ validate_object :: proc(p: ^Parser) -> bool {
|
||||
if err := expect_token(p, .Open_Brace); err != .None {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
validate_object_body(p, .Close_Brace) or_return
|
||||
|
||||
if err := expect_token(p, .Close_Brace); err != .None {
|
||||
@@ -102,7 +102,7 @@ validate_value :: proc(p: ^Parser) -> bool {
|
||||
|
||||
case .Open_Bracket:
|
||||
return validate_array(p)
|
||||
|
||||
|
||||
case .Ident:
|
||||
if p.spec == .MJSON {
|
||||
advance_token(p)
|
||||
|
||||
@@ -264,7 +264,7 @@ scan_comment :: proc(t: ^Tokenizer) -> (comment: string, err: Error) {
|
||||
expect(t, .Dash)
|
||||
expect(t, .Gt)
|
||||
|
||||
return string(t.src[offset : t.offset - 1]), .None
|
||||
return string(t.src[offset : t.offset - 3]), .None
|
||||
}
|
||||
|
||||
// Skip CDATA
|
||||
|
||||
@@ -294,7 +294,7 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
||||
comment := scan_comment(t) or_return
|
||||
|
||||
if .Intern_Comments in opts.flags {
|
||||
if len(doc.elements) == 0 {
|
||||
if doc.element_count == 0 {
|
||||
append(&doc.comments, comment)
|
||||
} else {
|
||||
el := new_element(doc)
|
||||
@@ -308,6 +308,7 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
||||
case .Open_Bracket:
|
||||
// This could be a CDATA tag part of a tag's body. Unread the `<![`
|
||||
t.offset -= 3
|
||||
t.read_offset = t.offset
|
||||
|
||||
// Instead of calling `parse_body` here, we could also `continue loop`
|
||||
// and fall through to the `case:` at the bottom of the outer loop.
|
||||
@@ -385,7 +386,8 @@ load_from_file :: proc(filename: string, options := DEFAULT_OPTIONS, error_handl
|
||||
return parse_bytes(data, options, filename, error_handler, allocator)
|
||||
}
|
||||
|
||||
destroy :: proc(doc: ^Document) {
|
||||
destroy :: proc(doc: ^Document, allocator := context.allocator) {
|
||||
context.allocator = allocator
|
||||
if doc == nil { return }
|
||||
|
||||
for el in doc.elements {
|
||||
@@ -625,4 +627,4 @@ new_element :: proc(doc: ^Document) -> (id: Element_ID) {
|
||||
cur := doc.element_count
|
||||
doc.element_count += 1
|
||||
return cur
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,7 @@ NO_CORE_NAMED_TYPES :: #config(ODIN_CORE_FLAGS_NO_CORE_NAMED_TYPES, false)
|
||||
IMPORTING_TIME :: #config(ODIN_CORE_FLAGS_USE_TIME, time.IS_SUPPORTED)
|
||||
|
||||
// Override support for parsing `net` types.
|
||||
// TODO: Update this when the BSDs are supported.
|
||||
IMPORTING_NET :: #config(ODIN_CORE_FLAGS_USE_NET, ODIN_OS == .Windows || ODIN_OS == .Linux || ODIN_OS == .Darwin || ODIN_OS == .FreeBSD)
|
||||
IMPORTING_NET :: #config(ODIN_CORE_FLAGS_USE_NET, ODIN_OS == .Windows || ODIN_OS == .Linux || ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD)
|
||||
|
||||
TAG_ARGS :: "args"
|
||||
SUBTAG_NAME :: "name"
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package flags
|
||||
|
||||
import "base:runtime"
|
||||
import "core:net"
|
||||
import "core:os"
|
||||
|
||||
Parse_Error_Reason :: enum {
|
||||
@@ -24,6 +26,12 @@ Parse_Error :: struct {
|
||||
message: string,
|
||||
}
|
||||
|
||||
Unified_Parse_Error_Reason :: union #shared_nil {
|
||||
Parse_Error_Reason,
|
||||
runtime.Allocator_Error,
|
||||
net.Parse_Endpoint_Error,
|
||||
}
|
||||
|
||||
// Raised during parsing.
|
||||
// Provides more granular information than what just a string could hold.
|
||||
Open_File_Error :: struct {
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
#+build netbsd, openbsd
|
||||
package flags
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
Unified_Parse_Error_Reason :: union #shared_nil {
|
||||
Parse_Error_Reason,
|
||||
runtime.Allocator_Error,
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
#+build !netbsd
|
||||
#+build !openbsd
|
||||
package flags
|
||||
|
||||
import "base:runtime"
|
||||
import "core:net"
|
||||
|
||||
Unified_Parse_Error_Reason :: union #shared_nil {
|
||||
Parse_Error_Reason,
|
||||
runtime.Allocator_Error,
|
||||
net.Parse_Endpoint_Error,
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
import "core:fmt"
|
||||
import "core:mem"
|
||||
import "core:net"
|
||||
import "core:os"
|
||||
import "core:reflect"
|
||||
import "core:strconv"
|
||||
@@ -310,7 +311,18 @@ parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type:
|
||||
}
|
||||
|
||||
when IMPORTING_NET {
|
||||
if try_net_parse_workaround(data_type, str, ptr, out_error) {
|
||||
if data_type == net.Host_Or_Endpoint {
|
||||
addr, net_error := net.parse_hostname_or_endpoint(str)
|
||||
if net_error != nil {
|
||||
// We pass along `net.Error` here.
|
||||
out_error^ = Parse_Error {
|
||||
net_error,
|
||||
"Invalid Host/Endpoint.",
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
(cast(^net.Host_Or_Endpoint)ptr)^ = addr
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
#+private
|
||||
#+build !netbsd
|
||||
#+build !openbsd
|
||||
package flags
|
||||
|
||||
import "core:net"
|
||||
|
||||
// This proc exists purely as a workaround for import restrictions.
|
||||
// Returns true if caller should return early.
|
||||
try_net_parse_workaround :: #force_inline proc (
|
||||
data_type: typeid,
|
||||
str: string,
|
||||
ptr: rawptr,
|
||||
out_error: ^Error,
|
||||
) -> bool {
|
||||
if data_type == net.Host_Or_Endpoint {
|
||||
addr, net_error := net.parse_hostname_or_endpoint(str)
|
||||
if net_error != nil {
|
||||
// We pass along `net.Error` here.
|
||||
out_error^ = Parse_Error {
|
||||
net_error,
|
||||
"Invalid Host/Endpoint.",
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
(cast(^net.Host_Or_Endpoint)ptr)^ = addr
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
1050
core/fmt/fmt.odin
1050
core/fmt/fmt.odin
File diff suppressed because it is too large
Load Diff
@@ -86,7 +86,7 @@ which_bytes :: proc(data: []byte) -> Which_File_Type {
|
||||
return v
|
||||
}
|
||||
get16le :: #force_inline proc(s: ^string) -> u16 {
|
||||
v := u16(s[0]) | u16(s[1])<<16
|
||||
v := u16(s[0]) | u16(s[1])<<8
|
||||
s^ = s[2:]
|
||||
return v
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
// Intentionally blank
|
||||
case .Uncompressed_Black_White:
|
||||
black_white = true
|
||||
dest_depth = 24
|
||||
dest_depth = 8 if .do_not_expand_grayscale in options else 24
|
||||
case .Uncompressed_Color_Mapped:
|
||||
color_mapped = true
|
||||
case .Compressed_Color_Mapped:
|
||||
@@ -161,7 +161,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
case .Compressed_Black_White:
|
||||
black_white = true
|
||||
rle_encoding = true
|
||||
dest_depth = 24
|
||||
dest_depth = 8 if .do_not_expand_grayscale in options else 24
|
||||
|
||||
case:
|
||||
return nil, .Unsupported_Format
|
||||
@@ -180,6 +180,9 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
}
|
||||
|
||||
switch dest_depth {
|
||||
case 8: // R8
|
||||
src_channels = 1
|
||||
dest_channels = 1
|
||||
case 15: // B5G5R5
|
||||
src_channels = 2
|
||||
dest_channels = 3
|
||||
|
||||
@@ -21,12 +21,12 @@ write_ptr_at :: proc(w: Writer_At, p: rawptr, byte_size: int, offset: i64, n_wri
|
||||
}
|
||||
|
||||
write_u64 :: proc(w: Writer, i: u64, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
|
||||
buf: [32]byte
|
||||
buf: [64]byte
|
||||
s := strconv.write_bits(buf[:], i, base, false, 64, strconv.digits, nil)
|
||||
return write_string(w, s, n_written)
|
||||
}
|
||||
write_i64 :: proc(w: Writer, i: i64, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
|
||||
buf: [32]byte
|
||||
buf: [65]byte
|
||||
s := strconv.write_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil)
|
||||
return write_string(w, s, n_written)
|
||||
}
|
||||
@@ -39,12 +39,12 @@ write_int :: proc(w: Writer, i: int, base: int = 10, n_written: ^int = nil) -> (
|
||||
}
|
||||
|
||||
write_u128 :: proc(w: Writer, i: u128, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
|
||||
buf: [39]byte
|
||||
buf: [128]byte
|
||||
s := strconv.write_bits_128(buf[:], i, base, false, 128, strconv.digits, nil)
|
||||
return write_string(w, s, n_written)
|
||||
}
|
||||
write_i128 :: proc(w: Writer, i: i128, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
|
||||
buf: [40]byte
|
||||
buf: [129]byte
|
||||
s := strconv.write_bits_128(buf[:], u128(i), base, true, 128, strconv.digits, nil)
|
||||
return write_string(w, s, n_written)
|
||||
}
|
||||
|
||||
@@ -260,7 +260,7 @@ Example:
|
||||
Possible Output:
|
||||
|
||||
6
|
||||
500
|
||||
13
|
||||
|
||||
*/
|
||||
@(require_results)
|
||||
@@ -301,7 +301,7 @@ Example:
|
||||
Possible Output:
|
||||
|
||||
6
|
||||
500
|
||||
13
|
||||
|
||||
*/
|
||||
@(require_results)
|
||||
@@ -342,7 +342,7 @@ Example:
|
||||
Possible Output:
|
||||
|
||||
6
|
||||
500
|
||||
13
|
||||
|
||||
*/
|
||||
@(require_results)
|
||||
|
||||
@@ -55,16 +55,20 @@ pcg_random_generator_proc :: proc(data: rawptr, mode: runtime.Random_Generator_M
|
||||
intrinsics.unaligned_store((^u64)(raw_data(p)), read_u64(r))
|
||||
case:
|
||||
// All other cases.
|
||||
pos := i8(0)
|
||||
val := u64(0)
|
||||
for &v in p {
|
||||
if pos == 0 {
|
||||
val = read_u64(r)
|
||||
pos = 8
|
||||
n := len(p) / size_of(u64)
|
||||
buff := ([^]u64)(raw_data(p))[:n]
|
||||
for &e in buff {
|
||||
intrinsics.unaligned_store(&e, read_u64(r))
|
||||
}
|
||||
// Handle remaining bytes
|
||||
rem := len(p) % size_of(u64)
|
||||
if rem > 0 {
|
||||
val := read_u64(r)
|
||||
tail := p[len(p) - rem:]
|
||||
for &b in tail {
|
||||
b = byte(val)
|
||||
val >>= 8
|
||||
}
|
||||
v = byte(val)
|
||||
val >>= 8
|
||||
pos -= 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -74,16 +74,20 @@ xoshiro256_random_generator_proc :: proc(data: rawptr, mode: runtime.Random_Gene
|
||||
intrinsics.unaligned_store((^u64)(raw_data(p)), read_u64(r))
|
||||
case:
|
||||
// All other cases.
|
||||
pos := i8(0)
|
||||
val := u64(0)
|
||||
for &v in p {
|
||||
if pos == 0 {
|
||||
val = read_u64(r)
|
||||
pos = 8
|
||||
n := len(p) / size_of(u64)
|
||||
buff := ([^]u64)(raw_data(p))[:n]
|
||||
for &e in buff {
|
||||
intrinsics.unaligned_store(&e, read_u64(r))
|
||||
}
|
||||
// Handle remaining bytes
|
||||
rem := len(p) % size_of(u64)
|
||||
if rem > 0 {
|
||||
val := read_u64(r)
|
||||
tail := p[len(p) - rem:]
|
||||
for &b in tail {
|
||||
b = byte(val)
|
||||
val >>= 8
|
||||
}
|
||||
v = byte(val)
|
||||
val >>= 8
|
||||
pos -= 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
213
core/nbio/doc.odin
Normal file
213
core/nbio/doc.odin
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
package nbio implements a non-blocking I/O and event loop abstraction layer
|
||||
over several platform-specific asynchronous I/O APIs.
|
||||
|
||||
More examples can be found in Odin's examples repository
|
||||
at [[ examples/nbio ; https://github.com/odin-lang/examples/tree/master/nbio ]].
|
||||
|
||||
**Event Loop**:
|
||||
|
||||
Each thread may have at most one event loop associated with it.
|
||||
This is enforced by the package, as running multiple event loops on a single
|
||||
thread does not make sense.
|
||||
|
||||
Event loops are reference counted and managed by the package.
|
||||
|
||||
`acquire_thread_event_loop` and `release_thread_event_loop` can be used
|
||||
to acquire and release a reference. Acquiring must be done before any operation
|
||||
is done.
|
||||
|
||||
The event loop progresses in ticks. A tick checks if any work is to be done,
|
||||
and based on the given timeout may block waiting for work.
|
||||
|
||||
Ticks are typically done using the `tick`, `run`, and `run_until` procedures.
|
||||
|
||||
Example:
|
||||
package main
|
||||
|
||||
import "core:nbio"
|
||||
import "core:time"
|
||||
import "core:fmt"
|
||||
|
||||
main :: proc() {
|
||||
err := nbio.acquire_thread_event_loop()
|
||||
assert(err == nil)
|
||||
defer nbio.release_thread_event_loop()
|
||||
|
||||
nbio.timeout(time.Second, proc(_: ^nbio.Operation) {
|
||||
fmt.println("Hellope after 1 second!")
|
||||
})
|
||||
|
||||
err = nbio.run()
|
||||
assert(err == nil)
|
||||
}
|
||||
|
||||
|
||||
**Time and timeouts**:
|
||||
|
||||
Timeouts are intentionally *slightly inaccurate* by design.
|
||||
|
||||
A timeout is not checked continuously, instead, it is evaluated only when
|
||||
a tick occurs. This means if a tick took a long time, your timeout may be ready
|
||||
for a bit of time already before the callback is called.
|
||||
|
||||
The function `now` returns the current time as perceived by the event
|
||||
loop. This value is cached at least once per tick so it is fast to retrieve.
|
||||
|
||||
Most operations also take an optional timeout when executed.
|
||||
If the timeout completes before the operation, the operation is cancelled and
|
||||
called back with a `.Timeout` error.
|
||||
|
||||
|
||||
**Threading**:
|
||||
|
||||
The package has a concept of I/O threads (threads that are ticking) and worker
|
||||
threads (any other thread).
|
||||
|
||||
An I/O thread is mostly self contained, operations are executed on it, and
|
||||
callbacks run on it.
|
||||
|
||||
If you try to execute an operation on a thread that has no running event loop
|
||||
a panic will be executed. Instead a worker thread can execute operations onto
|
||||
a running event loop by taking it's reference and executing operations with
|
||||
that reference.
|
||||
|
||||
In this case:
|
||||
- The operation is enqueued from the worker thread
|
||||
- The I/O thread is optionally woken up from blocking for work with `wake_up`
|
||||
- The next tick, the operation is executed by the I/O thread
|
||||
- The callback is invoked on the I/O thread
|
||||
|
||||
Example:
|
||||
package main
|
||||
|
||||
import "core:nbio"
|
||||
import "core:net"
|
||||
import "core:thread"
|
||||
import "core:time"
|
||||
|
||||
Connection :: struct {
|
||||
loop: ^nbio.Event_Loop,
|
||||
socket: net.TCP_Socket,
|
||||
}
|
||||
|
||||
main :: proc() {
|
||||
workers: thread.Pool
|
||||
thread.pool_init(&workers, context.allocator, 2)
|
||||
thread.pool_start(&workers)
|
||||
|
||||
err := nbio.acquire_thread_event_loop()
|
||||
defer nbio.release_thread_event_loop()
|
||||
assert(err == nil)
|
||||
|
||||
server, listen_err := nbio.listen_tcp({nbio.IP4_Any, 1234})
|
||||
assert(listen_err == nil)
|
||||
nbio.accept_poly(server, &workers, on_accept)
|
||||
|
||||
err = nbio.run()
|
||||
assert(err == nil)
|
||||
|
||||
on_accept :: proc(op: ^nbio.Operation, workers: ^thread.Pool) {
|
||||
assert(op.accept.err == nil)
|
||||
|
||||
nbio.accept_poly(op.accept.socket, workers, on_accept)
|
||||
|
||||
thread.pool_add_task(workers, context.allocator, do_work, new_clone(Connection{
|
||||
loop = op.l,
|
||||
socket = op.accept.client,
|
||||
}))
|
||||
}
|
||||
|
||||
do_work :: proc(t: thread.Task) {
|
||||
connection := (^Connection)(t.data)
|
||||
|
||||
// Imagine CPU intensive work that's been ofloaded to a worker thread.
|
||||
time.sleep(time.Second * 1)
|
||||
|
||||
nbio.send_poly(connection.socket, {transmute([]byte)string("Hellope!\n")}, connection, on_sent, l=connection.loop)
|
||||
}
|
||||
|
||||
on_sent :: proc(op: ^nbio.Operation, connection: ^Connection) {
|
||||
assert(op.send.err == nil)
|
||||
// Client got our message, clean up.
|
||||
nbio.close(connection.socket)
|
||||
free(connection)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
**Handle and socket association**:
|
||||
|
||||
Most platforms require handles (files, sockets, etc.) to be explicitly
|
||||
associated with an event loop or configured for non-blocking/asynchronous
|
||||
operation.
|
||||
|
||||
On some platforms (notably Windows), this requires a specific flag at open
|
||||
time (`.Non_Blocking` for `core:os`) and association may fail if the handle was
|
||||
not created correctly.
|
||||
|
||||
For this reason, prefer `open` and `create_socket` from this package instead.
|
||||
|
||||
`associate_handle`, `associate_file`, and `associate_socket` can be used for
|
||||
externally opened files/sockets.
|
||||
|
||||
|
||||
**Offsets and positional I/O**:
|
||||
|
||||
Operations do not implicitly use or modify a handle’s internal file
|
||||
offset.
|
||||
|
||||
Instead, operations such as `read` and `write` are *positional* and require
|
||||
an explicit offset.
|
||||
|
||||
This avoids ambiguity and subtle bugs when multiple asynchronous operations
|
||||
are issued concurrently against the same handle.
|
||||
|
||||
|
||||
**Contexts and callbacks**:
|
||||
|
||||
The `context` inside a callback is *not* the context that submitted the
|
||||
operation.
|
||||
|
||||
Instead, the callback receives the context that was active when the event
|
||||
loop function (`tick`, `run`, etc.) was called.
|
||||
|
||||
This is because otherwise the context would have to be copied and held onto for
|
||||
each operation.
|
||||
|
||||
If the submitting context is required inside the callback, it must be copied
|
||||
into the operation’s user data explicitly.
|
||||
|
||||
Example:
|
||||
nbio.timeout_poly(time.Second, new_clone(context), proc(_: ^Operation, ctx: ^runtime.Context) {
|
||||
context = ctx^
|
||||
free(ctx)
|
||||
})
|
||||
|
||||
|
||||
**Callback scheduling guarantees**:
|
||||
|
||||
Callbacks are guaranteed to be invoked in a later tick, never synchronously.
|
||||
This means that the operation returned from a procedure is at least valid till
|
||||
the end of the current tick, because an operation is freed after it's callback
|
||||
is called. Thus you can set user data after an execution is queued, or call
|
||||
`remove`, removing subtle "race" conditions and simplifying control flow.
|
||||
|
||||
**Why does the design of this package use callbacks?**
|
||||
|
||||
Callbacks are the simplest interface an event loop can reasonably expose: "Run
|
||||
this when the operation completes". This means the loop itself doesn’t need to
|
||||
know how the result is consumed (You could give control of the entire loop to
|
||||
the user, but that comes with all the problems in that).
|
||||
|
||||
Other mechanisms can be built on top of this, such as coroutines or even a queue
|
||||
you comsume at your own time.
|
||||
|
||||
The choice of one `Operation` type and one callback type for all the operations
|
||||
is partly so it is easy to bind into Lua-like or native coroutines.
|
||||
|
||||
Callbacks also allow multiple independent users to share the same event loop.
|
||||
A package can register its own operations, and application code can register
|
||||
others, without either seeing or handling the other’s completions.
|
||||
*/
|
||||
package nbio
|
||||
84
core/nbio/errors.odin
Normal file
84
core/nbio/errors.odin
Normal file
@@ -0,0 +1,84 @@
|
||||
package nbio
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
import "core:reflect"
|
||||
|
||||
Error :: intrinsics.type_merge(
|
||||
Network_Error,
|
||||
union #shared_nil {
|
||||
General_Error,
|
||||
FS_Error,
|
||||
},
|
||||
)
|
||||
#assert(size_of(Error) == 8)
|
||||
|
||||
// Errors regarding general usage of the event loop.
|
||||
General_Error :: enum i32 {
|
||||
None,
|
||||
|
||||
Allocation_Failed = i32(PLATFORM_ERR_ALLOCATION_FAILED),
|
||||
Unsupported = i32(PLATFORM_ERR_UNSUPPORTED),
|
||||
}
|
||||
|
||||
// Errors gotten from file system operations.
|
||||
FS_Error :: enum i32 {
|
||||
None,
|
||||
Unsupported = i32(PLATFORM_ERR_UNSUPPORTED),
|
||||
Allocation_Failed = i32(PLATFORM_ERR_ALLOCATION_FAILED),
|
||||
Timeout = i32(PLATFORM_ERR_TIMEOUT),
|
||||
Invalid_Argument = i32(PLATFORM_ERR_INVALID_ARGUMENT),
|
||||
Permission_Denied = i32(PLATFORM_ERR_PERMISSION_DENIED),
|
||||
EOF = i32(PLATFORM_ERR_EOF),
|
||||
Exists = i32(PLATFORM_ERR_EXISTS),
|
||||
Not_Found = i32(PLATFORM_ERR_NOT_FOUND),
|
||||
}
|
||||
|
||||
Platform_Error :: _Platform_Error
|
||||
|
||||
error_string :: proc(err: Error) -> string {
|
||||
err := err
|
||||
variant := any{
|
||||
id = reflect.union_variant_typeid(err),
|
||||
data = &err,
|
||||
}
|
||||
str := reflect.enum_string(variant)
|
||||
|
||||
if str == "" {
|
||||
#partial switch uerr in err {
|
||||
case FS_Error:
|
||||
str, _ = reflect.enum_name_from_value(Platform_Error(uerr))
|
||||
case General_Error:
|
||||
str, _ = reflect.enum_name_from_value(Platform_Error(uerr))
|
||||
}
|
||||
}
|
||||
if str == "" {
|
||||
str = "Unknown"
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
error_string_recv :: proc(recv_err: Recv_Error) -> string {
|
||||
switch err in recv_err {
|
||||
case TCP_Recv_Error: return error_string(err)
|
||||
case UDP_Recv_Error: return error_string(err)
|
||||
case: return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
error_string_send :: proc(send_err: Send_Error) -> string {
|
||||
switch err in send_err {
|
||||
case TCP_Send_Error: return error_string(err)
|
||||
case UDP_Send_Error: return error_string(err)
|
||||
case: return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
error_string_sendfile :: proc(send_err: Send_File_Error) -> string {
|
||||
switch err in send_err {
|
||||
case TCP_Send_Error: return error_string(err)
|
||||
case FS_Error: return error_string(err)
|
||||
case: return "Unknown"
|
||||
}
|
||||
}
|
||||
16
core/nbio/errors_linux.odin
Normal file
16
core/nbio/errors_linux.odin
Normal file
@@ -0,0 +1,16 @@
|
||||
#+private
|
||||
package nbio
|
||||
|
||||
import "core:sys/linux"
|
||||
|
||||
PLATFORM_ERR_UNSUPPORTED :: linux.Errno.ENOSYS
|
||||
PLATFORM_ERR_ALLOCATION_FAILED :: linux.Errno.ENOMEM
|
||||
PLATFORM_ERR_TIMEOUT :: linux.Errno.ECANCELED
|
||||
PLATFORM_ERR_INVALID_ARGUMENT :: linux.Errno.EINVAL
|
||||
PLATFORM_ERR_OVERFLOW :: linux.Errno.E2BIG
|
||||
PLATFORM_ERR_NOT_FOUND :: linux.Errno.ENOENT
|
||||
PLATFORM_ERR_EXISTS :: linux.Errno.EEXIST
|
||||
PLATFORM_ERR_PERMISSION_DENIED :: linux.Errno.EPERM
|
||||
PLATFORM_ERR_EOF :: -100 // There is no EOF errno, we use negative for our own error codes.
|
||||
|
||||
_Platform_Error :: linux.Errno
|
||||
20
core/nbio/errors_others.odin
Normal file
20
core/nbio/errors_others.odin
Normal file
@@ -0,0 +1,20 @@
|
||||
#+build !darwin
|
||||
#+build !freebsd
|
||||
#+build !openbsd
|
||||
#+build !netbsd
|
||||
#+build !linux
|
||||
#+build !windows
|
||||
#+private
|
||||
package nbio
|
||||
|
||||
PLATFORM_ERR_UNSUPPORTED :: 1
|
||||
PLATFORM_ERR_ALLOCATION_FAILED :: 2
|
||||
PLATFORM_ERR_TIMEOUT :: 3
|
||||
PLATFORM_ERR_INVALID_ARGUMENT :: 4
|
||||
PLATFORM_ERR_OVERFLOW :: 5
|
||||
PLATFORM_ERR_NOT_FOUND :: 6
|
||||
PLATFORM_ERR_EXISTS :: 7
|
||||
PLATFORM_ERR_PERMISSION_DENIED :: 8
|
||||
PLATFORM_ERR_EOF :: 9
|
||||
|
||||
_Platform_Error :: enum i32 {}
|
||||
17
core/nbio/errors_posix.odin
Normal file
17
core/nbio/errors_posix.odin
Normal file
@@ -0,0 +1,17 @@
|
||||
#+build darwin, freebsd, netbsd, openbsd
|
||||
#+private
|
||||
package nbio
|
||||
|
||||
import "core:sys/posix"
|
||||
|
||||
PLATFORM_ERR_UNSUPPORTED :: posix.Errno.ENOSYS
|
||||
PLATFORM_ERR_ALLOCATION_FAILED :: posix.Errno.ENOMEM
|
||||
PLATFORM_ERR_TIMEOUT :: posix.Errno.ECANCELED
|
||||
PLATFORM_ERR_INVALID_ARGUMENT :: posix.Errno.EINVAL
|
||||
PLATFORM_ERR_OVERFLOW :: posix.Errno.E2BIG
|
||||
PLATFORM_ERR_NOT_FOUND :: posix.Errno.ENOENT
|
||||
PLATFORM_ERR_EXISTS :: posix.Errno.EEXIST
|
||||
PLATFORM_ERR_PERMISSION_DENIED :: posix.Errno.EPERM
|
||||
PLATFORM_ERR_EOF :: -100 // There is no EOF errno, we use negative for our own error codes.
|
||||
|
||||
_Platform_Error :: posix.Errno
|
||||
17
core/nbio/errors_windows.odin
Normal file
17
core/nbio/errors_windows.odin
Normal file
@@ -0,0 +1,17 @@
|
||||
#+private
|
||||
package nbio
|
||||
|
||||
import win "core:sys/windows"
|
||||
|
||||
PLATFORM_ERR_UNSUPPORTED :: win.System_Error.NOT_SUPPORTED
|
||||
|
||||
PLATFORM_ERR_ALLOCATION_FAILED :: win.System_Error.OUTOFMEMORY
|
||||
PLATFORM_ERR_TIMEOUT :: win.System_Error.WAIT_TIMEOUT
|
||||
PLATFORM_ERR_INVALID_ARGUMENT :: win.System_Error.BAD_ARGUMENTS
|
||||
PLATFORM_ERR_OVERFLOW :: win.System_Error.BUFFER_OVERFLOW
|
||||
PLATFORM_ERR_NOT_FOUND :: win.System_Error.FILE_NOT_FOUND
|
||||
PLATFORM_ERR_EXISTS :: win.System_Error.FILE_EXISTS
|
||||
PLATFORM_ERR_PERMISSION_DENIED :: win.System_Error.ACCESS_DENIED
|
||||
PLATFORM_ERR_EOF :: win.System_Error.HANDLE_EOF
|
||||
|
||||
_Platform_Error :: win.System_Error
|
||||
259
core/nbio/impl.odin
Normal file
259
core/nbio/impl.odin
Normal file
@@ -0,0 +1,259 @@
|
||||
#+private
|
||||
package nbio
|
||||
|
||||
import "base:runtime"
|
||||
import "base:intrinsics"
|
||||
|
||||
import "core:container/pool"
|
||||
import "core:net"
|
||||
import "core:strings"
|
||||
import "core:time"
|
||||
import "core:reflect"
|
||||
|
||||
@(init, private)
|
||||
init_thread_local_cleaner :: proc "contextless" () {
|
||||
runtime.add_thread_local_cleaner(proc() {
|
||||
l := &_tls_event_loop
|
||||
if l.refs > 0 {
|
||||
l.refs = 1
|
||||
_release_thread_event_loop()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@(thread_local)
|
||||
_tls_event_loop: Event_Loop
|
||||
|
||||
_acquire_thread_event_loop :: proc() -> General_Error {
|
||||
l := &_tls_event_loop
|
||||
if l.err == nil && l.refs == 0 {
|
||||
when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 && ODIN_OS != .Orca {
|
||||
allocator := runtime.default_wasm_allocator()
|
||||
} else {
|
||||
allocator := runtime.heap_allocator()
|
||||
}
|
||||
|
||||
l.allocator = allocator
|
||||
|
||||
if alloc_err := mpsc_init(&l.queue, 128, l.allocator); alloc_err != nil {
|
||||
l.err = .Allocation_Failed
|
||||
return l.err
|
||||
}
|
||||
defer if l.err != nil { mpsc_destroy(&l.queue, l.allocator) }
|
||||
|
||||
if pool_err := pool.init(&l.operation_pool, "_pool_link"); pool_err != nil {
|
||||
l.err = .Allocation_Failed
|
||||
return l.err
|
||||
}
|
||||
defer if l.err != nil { pool.destroy(&l.operation_pool) }
|
||||
|
||||
l.err = _init(l, allocator)
|
||||
l.now = time.now()
|
||||
}
|
||||
|
||||
if l.err != nil {
|
||||
return l.err
|
||||
}
|
||||
|
||||
l.refs += 1
|
||||
return nil
|
||||
}
|
||||
|
||||
_release_thread_event_loop :: proc() {
|
||||
l := &_tls_event_loop
|
||||
if l.err != nil {
|
||||
assert(l.refs == 0)
|
||||
return
|
||||
}
|
||||
|
||||
if l.refs > 0 {
|
||||
l.refs -= 1
|
||||
if l.refs == 0 {
|
||||
mpsc_destroy(&l.queue, l.allocator)
|
||||
pool.destroy(&l.operation_pool)
|
||||
_destroy(l)
|
||||
l^ = {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_current_thread_event_loop :: #force_inline proc(loc := #caller_location) -> (^Event_Loop) {
|
||||
l := &_tls_event_loop
|
||||
|
||||
if intrinsics.expect(l.refs == 0, false) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
_tick :: proc(l: ^Event_Loop, timeout: time.Duration) -> (err: General_Error) {
|
||||
// Receive operations queued from other threads first.
|
||||
for {
|
||||
op := (^Operation)(mpsc_dequeue(&l.queue))
|
||||
if op == nil { break }
|
||||
_exec(op)
|
||||
}
|
||||
|
||||
return __tick(l, timeout)
|
||||
}
|
||||
|
||||
_listen_tcp :: proc(
|
||||
l: ^Event_Loop,
|
||||
endpoint: Endpoint,
|
||||
backlog := 1000,
|
||||
loc := #caller_location,
|
||||
) -> (
|
||||
socket: TCP_Socket,
|
||||
err: Network_Error,
|
||||
) {
|
||||
family := family_from_endpoint(endpoint)
|
||||
socket = create_tcp_socket(family, l, loc) or_return
|
||||
defer if err != nil { close(socket, l=l) }
|
||||
|
||||
net.set_option(socket, .Reuse_Address, true)
|
||||
|
||||
bind(socket, endpoint) or_return
|
||||
|
||||
_listen(socket, backlog) or_return
|
||||
return
|
||||
}
|
||||
|
||||
_read_entire_file :: proc(l: ^Event_Loop, path: string, user_data: rawptr, cb: Read_Entire_File_Callback, allocator := context.allocator, dir := CWD) {
|
||||
open_poly3(path, user_data, cb, allocator, on_open, dir=dir, l=l)
|
||||
|
||||
on_open :: proc(op: ^Operation, user_data: rawptr, cb: Read_Entire_File_Callback, allocator: runtime.Allocator) {
|
||||
if op.open.err != nil {
|
||||
cb(user_data, nil, {.Open, op.open.err})
|
||||
return
|
||||
}
|
||||
|
||||
stat_poly3(op.open.handle, user_data, cb, allocator, on_stat)
|
||||
}
|
||||
|
||||
on_stat :: proc(op: ^Operation, user_data: rawptr, cb: Read_Entire_File_Callback, allocator: runtime.Allocator) {
|
||||
if op.stat.err != nil {
|
||||
close(op.stat.handle)
|
||||
cb(user_data, nil, {.Stat, op.stat.err})
|
||||
return
|
||||
}
|
||||
|
||||
if op.stat.type != .Regular {
|
||||
close(op.stat.handle)
|
||||
cb(user_data, nil, {.Stat, .Unsupported})
|
||||
return
|
||||
}
|
||||
|
||||
buf, err := make([]byte, op.stat.size, allocator)
|
||||
if err != nil {
|
||||
close(op.stat.handle)
|
||||
cb(user_data, nil, {.Read, .Allocation_Failed})
|
||||
return
|
||||
}
|
||||
|
||||
read_poly3(op.stat.handle, 0, buf, user_data, cb, allocator, on_read, all=true)
|
||||
}
|
||||
|
||||
on_read :: proc(op: ^Operation, user_data: rawptr, cb: Read_Entire_File_Callback, allocator: runtime.Allocator) {
|
||||
close(op.read.handle)
|
||||
|
||||
if op.read.err != nil {
|
||||
delete(op.read.buf, allocator)
|
||||
cb(user_data, nil, {.Read, op.read.err})
|
||||
return
|
||||
}
|
||||
|
||||
assert(op.read.read == len(op.read.buf))
|
||||
cb(user_data, op.read.buf, {})
|
||||
}
|
||||
}
|
||||
|
||||
NBIO_DEBUG :: #config(NBIO_DEBUG, false)
|
||||
|
||||
Debuggable :: union {
|
||||
Operation_Type,
|
||||
string,
|
||||
int,
|
||||
time.Time,
|
||||
time.Duration,
|
||||
}
|
||||
|
||||
@(disabled=!NBIO_DEBUG)
|
||||
debug :: proc(contents: ..Debuggable, location := #caller_location) {
|
||||
if context.logger.procedure == nil || .Debug < context.logger.lowest_level {
|
||||
return
|
||||
}
|
||||
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
|
||||
b: strings.Builder
|
||||
b.buf.allocator = context.temp_allocator
|
||||
|
||||
strings.write_string(&b, "[nbio] ")
|
||||
|
||||
for content, i in contents {
|
||||
switch val in content {
|
||||
case Operation_Type:
|
||||
name, _ := reflect.enum_name_from_value(val)
|
||||
strings.write_string(&b, name)
|
||||
case string:
|
||||
strings.write_string(&b, val)
|
||||
case int:
|
||||
strings.write_int(&b, val)
|
||||
case time.Duration:
|
||||
ms := time.duration_milliseconds(val)
|
||||
strings.write_f64(&b, ms, 'f')
|
||||
strings.write_string(&b, "ms")
|
||||
|
||||
case time.Time:
|
||||
buf: [time.MIN_HMS_LEN+1]byte
|
||||
h, m, s, ns := time.precise_clock_from_time(val)
|
||||
buf[8] = '.'
|
||||
buf[7] = '0' + u8(s % 10); s /= 10
|
||||
buf[6] = '0' + u8(s)
|
||||
buf[5] = ':'
|
||||
buf[4] = '0' + u8(m % 10); m /= 10
|
||||
buf[3] = '0' + u8(m)
|
||||
buf[2] = ':'
|
||||
buf[1] = '0' + u8(h % 10); h /= 10
|
||||
buf[0] = '0' + u8(h)
|
||||
|
||||
strings.write_string(&b, string(buf[:]))
|
||||
strings.write_int(&b, ns)
|
||||
}
|
||||
|
||||
if i < len(contents)-1 {
|
||||
strings.write_byte(&b, ' ')
|
||||
}
|
||||
}
|
||||
|
||||
context.logger.procedure(context.logger.data, .Debug, strings.to_string(b), context.logger.options, location)
|
||||
}
|
||||
|
||||
warn :: proc(text: string, location := #caller_location) {
|
||||
if context.logger.procedure == nil || .Warning < context.logger.lowest_level {
|
||||
return
|
||||
}
|
||||
|
||||
context.logger.procedure(context.logger.data, .Warning, text, context.logger.options, location)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
constraint_bufs_to_max_rw :: proc(bufs: [][]byte) -> (constrained: [][]byte, total: int) {
|
||||
for buf in bufs {
|
||||
total += len(buf)
|
||||
}
|
||||
|
||||
constrained = bufs
|
||||
for n := total; n > MAX_RW; {
|
||||
last := &constrained[len(constrained)-1]
|
||||
take := min(len(last), n-MAX_RW)
|
||||
last^ = last[:take]
|
||||
if len(last) == 0 {
|
||||
constrained = constrained[:len(constrained)-1]
|
||||
}
|
||||
n -= take
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
1423
core/nbio/impl_linux.odin
Normal file
1423
core/nbio/impl_linux.odin
Normal file
File diff suppressed because it is too large
Load Diff
218
core/nbio/impl_others.odin
Normal file
218
core/nbio/impl_others.odin
Normal file
@@ -0,0 +1,218 @@
|
||||
#+build !darwin
|
||||
#+build !freebsd
|
||||
#+build !openbsd
|
||||
#+build !netbsd
|
||||
#+build !linux
|
||||
#+build !windows
|
||||
#+private
|
||||
package nbio
|
||||
|
||||
import "core:container/avl"
|
||||
import "core:container/pool"
|
||||
import "core:container/queue"
|
||||
import "core:mem"
|
||||
import "core:slice"
|
||||
import "core:time"
|
||||
|
||||
_FULLY_SUPPORTED :: false
|
||||
|
||||
_Event_Loop :: struct {
|
||||
completed: queue.Queue(^Operation),
|
||||
timeouts: avl.Tree(^Operation),
|
||||
}
|
||||
|
||||
_Handle :: uintptr
|
||||
|
||||
_CWD :: Handle(-100)
|
||||
|
||||
MAX_RW :: mem.Gigabyte
|
||||
|
||||
_Operation :: struct {
|
||||
removed: bool,
|
||||
}
|
||||
|
||||
_Accept :: struct {}
|
||||
|
||||
_Close :: struct {}
|
||||
|
||||
_Dial :: struct {}
|
||||
|
||||
_Recv :: struct {
|
||||
small_bufs: [1][]byte,
|
||||
}
|
||||
|
||||
_Send :: struct {
|
||||
small_bufs: [1][]byte,
|
||||
}
|
||||
|
||||
_Read :: struct {}
|
||||
|
||||
_Write :: struct {}
|
||||
|
||||
_Timeout :: struct {
|
||||
expires: time.Time,
|
||||
}
|
||||
|
||||
_Poll :: struct {}
|
||||
|
||||
_Send_File :: struct {}
|
||||
|
||||
_Open :: struct {}
|
||||
|
||||
_Stat :: struct {}
|
||||
|
||||
_Splice :: struct {}
|
||||
|
||||
_Remove :: struct {}
|
||||
|
||||
_Link_Timeout :: struct {}
|
||||
|
||||
_init :: proc(l: ^Event_Loop, allocator: mem.Allocator) -> (rerr: General_Error) {
|
||||
l.completed.data.allocator = allocator
|
||||
|
||||
avl.init_cmp(&l.timeouts, timeouts_cmp, allocator)
|
||||
|
||||
return nil
|
||||
|
||||
timeouts_cmp :: #force_inline proc(a, b: ^Operation) -> slice.Ordering {
|
||||
switch {
|
||||
case a.timeout._impl.expires._nsec < b.timeout._impl.expires._nsec:
|
||||
return .Less
|
||||
case a.timeout._impl.expires._nsec > b.timeout._impl.expires._nsec:
|
||||
return .Greater
|
||||
case uintptr(a) < uintptr(b):
|
||||
return .Less
|
||||
case uintptr(a) > uintptr(b):
|
||||
return .Greater
|
||||
case:
|
||||
assert(a == b)
|
||||
return .Equal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_destroy :: proc(l: ^Event_Loop) {
|
||||
queue.destroy(&l.completed)
|
||||
avl.destroy(&l.timeouts, false)
|
||||
}
|
||||
|
||||
__tick :: proc(l: ^Event_Loop, timeout: time.Duration) -> General_Error {
|
||||
l.now = time.now()
|
||||
|
||||
for op in queue.pop_front_safe(&l.completed) {
|
||||
if !op._impl.removed {
|
||||
op.cb(op)
|
||||
}
|
||||
if !op.detached {
|
||||
pool.put(&l.operation_pool, op)
|
||||
}
|
||||
}
|
||||
|
||||
iter := avl.iterator(&l.timeouts, .Forward)
|
||||
for node in avl.iterator_next(&iter) {
|
||||
op := node.value
|
||||
cexpires := time.diff(l.now, op.timeout._impl.expires)
|
||||
|
||||
done := cexpires <= 0
|
||||
if done {
|
||||
op.cb(op)
|
||||
avl.remove_node(&l.timeouts, node)
|
||||
if !op.detached {
|
||||
pool.put(&l.operation_pool, op)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
_create_socket :: proc(l: ^Event_Loop, family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Create_Socket_Error) {
|
||||
return nil, .Network_Unreachable
|
||||
}
|
||||
|
||||
_listen :: proc(socket: TCP_Socket, backlog := 1000) -> Listen_Error {
|
||||
return .Network_Unreachable
|
||||
}
|
||||
|
||||
_exec :: proc(op: ^Operation) {
|
||||
switch op.type {
|
||||
case .Timeout:
|
||||
_, _, err := avl.find_or_insert(&op.l.timeouts, op)
|
||||
if err != nil {
|
||||
panic("nbio: allocation failure")
|
||||
}
|
||||
return
|
||||
case .Accept:
|
||||
op.accept.err = .Network_Unreachable
|
||||
case .Close:
|
||||
op.close.err = .Unsupported
|
||||
case .Dial:
|
||||
op.dial.err = Dial_Error.Network_Unreachable
|
||||
case .Recv:
|
||||
switch _ in op.recv.socket {
|
||||
case TCP_Socket: op.recv.err = TCP_Recv_Error.Network_Unreachable
|
||||
case UDP_Socket: op.recv.err = UDP_Recv_Error.Network_Unreachable
|
||||
case: op.recv.err = TCP_Recv_Error.Network_Unreachable
|
||||
}
|
||||
case .Send:
|
||||
switch _ in op.send.socket {
|
||||
case TCP_Socket: op.send.err = TCP_Send_Error.Network_Unreachable
|
||||
case UDP_Socket: op.send.err = UDP_Send_Error.Network_Unreachable
|
||||
case: op.send.err = TCP_Send_Error.Network_Unreachable
|
||||
}
|
||||
case .Send_File:
|
||||
op.sendfile.err = .Network_Unreachable
|
||||
case .Read:
|
||||
op.read.err = .Unsupported
|
||||
case .Write:
|
||||
op.write.err = .Unsupported
|
||||
case .Poll:
|
||||
op.poll.result = .Error
|
||||
case .Open:
|
||||
op.open.err = .Unsupported
|
||||
case .Stat:
|
||||
op.stat.err = .Unsupported
|
||||
case .None, ._Link_Timeout, ._Remove, ._Splice:
|
||||
fallthrough
|
||||
case:
|
||||
unreachable()
|
||||
}
|
||||
|
||||
_, err := queue.push_back(&op.l.completed, op)
|
||||
if err != nil {
|
||||
panic("nbio: allocation failure")
|
||||
}
|
||||
}
|
||||
|
||||
_remove :: proc(target: ^Operation) {
|
||||
#partial switch target.type {
|
||||
case .Timeout:
|
||||
avl.remove_value(&target.l.timeouts, target)
|
||||
if !target.detached {
|
||||
pool.put(&target.l.operation_pool, target)
|
||||
}
|
||||
case:
|
||||
target._impl.removed = true
|
||||
}
|
||||
}
|
||||
|
||||
_open_sync :: proc(l: ^Event_Loop, path: string, dir: Handle, mode: File_Flags, perm: Permissions) -> (handle: Handle, err: FS_Error) {
|
||||
return 0, FS_Error.Unsupported
|
||||
}
|
||||
|
||||
_associate_handle :: proc(handle: uintptr, l: ^Event_Loop) -> (Handle, Association_Error) {
|
||||
return Handle(handle), nil
|
||||
}
|
||||
|
||||
_associate_socket :: proc(socket: Any_Socket, l: ^Event_Loop) -> Association_Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
_wake_up :: proc(l: ^Event_Loop) {
|
||||
}
|
||||
|
||||
_yield :: proc() {
|
||||
}
|
||||
1407
core/nbio/impl_posix.odin
Normal file
1407
core/nbio/impl_posix.odin
Normal file
File diff suppressed because it is too large
Load Diff
29
core/nbio/impl_posix_darwin.odin
Normal file
29
core/nbio/impl_posix_darwin.odin
Normal file
@@ -0,0 +1,29 @@
|
||||
#+private
|
||||
package nbio
|
||||
|
||||
import "core:net"
|
||||
import "core:sys/posix"
|
||||
|
||||
foreign import lib "system:System"
|
||||
|
||||
posix_sendfile :: proc(fd: Handle, s: TCP_Socket, offset, nbytes: int) -> (sent: int, ok := true) {
|
||||
foreign lib {
|
||||
@(link_name="sendfile")
|
||||
_posix_sendfile :: proc (fd, s: posix.FD, offset: posix.off_t, len: ^posix.off_t, hdtr: rawptr, flags: i32) -> posix.result ---
|
||||
}
|
||||
|
||||
len := posix.off_t(nbytes)
|
||||
if _posix_sendfile(posix.FD(fd), posix.FD(s), posix.off_t(offset), &len, nil, 0) != .OK {
|
||||
ok = false
|
||||
}
|
||||
sent = int(len)
|
||||
return
|
||||
}
|
||||
|
||||
posix_listen_error :: net._listen_error
|
||||
posix_accept_error :: net._accept_error
|
||||
posix_dial_error :: net._dial_error
|
||||
posix_tcp_send_error :: net._tcp_send_error
|
||||
posix_udp_send_error :: net._udp_send_error
|
||||
posix_tcp_recv_error :: net._tcp_recv_error
|
||||
posix_udp_recv_error :: net._udp_recv_error
|
||||
52
core/nbio/impl_posix_freebsd.odin
Normal file
52
core/nbio/impl_posix_freebsd.odin
Normal file
@@ -0,0 +1,52 @@
|
||||
#+private
|
||||
package nbio
|
||||
|
||||
import "core:net"
|
||||
import "core:sys/posix"
|
||||
import "core:sys/freebsd"
|
||||
|
||||
foreign import lib "system:c"
|
||||
|
||||
// TODO: rewrite freebsd implementation to use `sys/freebsd` instead of `sys/posix`.
|
||||
|
||||
posix_sendfile :: proc(fd: Handle, s: TCP_Socket, offset, nbytes: int) -> (sent: int, ok := true) {
|
||||
foreign lib {
|
||||
@(link_name="sendfile")
|
||||
_posix_sendfile :: proc (fd, s: posix.FD, offset: posix.off_t, nbytes: uint, hdtr: rawptr, sbytes: ^posix.off_t, flags: i32) -> posix.result ---
|
||||
}
|
||||
|
||||
len: posix.off_t
|
||||
if _posix_sendfile(posix.FD(fd), posix.FD(s), posix.off_t(offset), uint(nbytes), nil, &len, 0) != .OK {
|
||||
ok = false
|
||||
}
|
||||
sent = int(len)
|
||||
return
|
||||
}
|
||||
|
||||
posix_listen_error :: proc() -> Listen_Error {
|
||||
return net._listen_error(freebsd.Errno(posix.errno()))
|
||||
}
|
||||
|
||||
posix_accept_error :: proc() -> Accept_Error {
|
||||
return net._accept_error(freebsd.Errno(posix.errno()))
|
||||
}
|
||||
|
||||
posix_dial_error :: proc() -> Dial_Error {
|
||||
return net._dial_error(freebsd.Errno(posix.errno()))
|
||||
}
|
||||
|
||||
posix_tcp_send_error :: proc() -> TCP_Send_Error {
|
||||
return net._tcp_send_error(freebsd.Errno(posix.errno()))
|
||||
}
|
||||
|
||||
posix_udp_send_error :: proc() -> UDP_Send_Error {
|
||||
return net._udp_send_error(freebsd.Errno(posix.errno()))
|
||||
}
|
||||
|
||||
posix_tcp_recv_error :: proc() -> TCP_Recv_Error {
|
||||
return net._tcp_recv_error(freebsd.Errno(posix.errno()))
|
||||
}
|
||||
|
||||
posix_udp_recv_error :: proc() -> UDP_Recv_Error {
|
||||
return net._udp_recv_error(freebsd.Errno(posix.errno()))
|
||||
}
|
||||
12
core/nbio/impl_posix_netbsd.odin
Normal file
12
core/nbio/impl_posix_netbsd.odin
Normal file
@@ -0,0 +1,12 @@
|
||||
#+private
|
||||
package nbio
|
||||
|
||||
import "core:net"
|
||||
|
||||
posix_listen_error :: net._listen_error
|
||||
posix_accept_error :: net._accept_error
|
||||
posix_dial_error :: net._dial_error
|
||||
posix_tcp_send_error :: net._tcp_send_error
|
||||
posix_udp_send_error :: net._udp_send_error
|
||||
posix_tcp_recv_error :: net._tcp_recv_error
|
||||
posix_udp_recv_error :: net._udp_recv_error
|
||||
12
core/nbio/impl_posix_openbsd.odin
Normal file
12
core/nbio/impl_posix_openbsd.odin
Normal file
@@ -0,0 +1,12 @@
|
||||
#+private
|
||||
package nbio
|
||||
|
||||
import "core:net"
|
||||
|
||||
posix_listen_error :: net._listen_error
|
||||
posix_accept_error :: net._accept_error
|
||||
posix_dial_error :: net._dial_error
|
||||
posix_tcp_send_error :: net._tcp_send_error
|
||||
posix_udp_send_error :: net._udp_send_error
|
||||
posix_tcp_recv_error :: net._tcp_recv_error
|
||||
posix_udp_recv_error :: net._udp_recv_error
|
||||
1856
core/nbio/impl_windows.odin
Normal file
1856
core/nbio/impl_windows.odin
Normal file
File diff suppressed because it is too large
Load Diff
63
core/nbio/mpsc.odin
Normal file
63
core/nbio/mpsc.odin
Normal file
@@ -0,0 +1,63 @@
|
||||
#+private
|
||||
package nbio
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
import "core:sync"
|
||||
|
||||
Multi_Producer_Single_Consumer :: struct {
|
||||
count: int,
|
||||
head: int,
|
||||
tail: int,
|
||||
buffer: []rawptr,
|
||||
mask: int,
|
||||
}
|
||||
|
||||
mpsc_init :: proc(mpscq: ^Multi_Producer_Single_Consumer, cap: int, allocator: runtime.Allocator) -> runtime.Allocator_Error {
|
||||
assert(runtime.is_power_of_two_int(cap), "cap must be a power of 2")
|
||||
mpscq.buffer = make([]rawptr, cap, allocator) or_return
|
||||
mpscq.mask = cap-1
|
||||
sync.atomic_thread_fence(.Release)
|
||||
return nil
|
||||
}
|
||||
|
||||
mpsc_destroy :: proc(mpscq: ^Multi_Producer_Single_Consumer, allocator: runtime.Allocator) {
|
||||
delete(mpscq.buffer, allocator)
|
||||
}
|
||||
|
||||
mpsc_enqueue :: proc(mpscq: ^Multi_Producer_Single_Consumer, obj: rawptr) -> bool {
|
||||
count := sync.atomic_add_explicit(&mpscq.count, 1, .Acquire)
|
||||
if count >= len(mpscq.buffer) {
|
||||
sync.atomic_sub_explicit(&mpscq.count, 1, .Release)
|
||||
return false
|
||||
}
|
||||
|
||||
head := sync.atomic_add_explicit(&mpscq.head, 1, .Acquire)
|
||||
assert(mpscq.buffer[head & mpscq.mask] == nil)
|
||||
rv := sync.atomic_exchange_explicit(&mpscq.buffer[head & mpscq.mask], obj, .Release)
|
||||
assert(rv == nil)
|
||||
return true
|
||||
}
|
||||
|
||||
mpsc_dequeue :: proc(mpscq: ^Multi_Producer_Single_Consumer) -> rawptr {
|
||||
ret := sync.atomic_exchange_explicit(&mpscq.buffer[mpscq.tail], nil, .Acquire)
|
||||
if ret == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
mpscq.tail += 1
|
||||
if mpscq.tail >= len(mpscq.buffer) {
|
||||
mpscq.tail = 0
|
||||
}
|
||||
r := sync.atomic_sub_explicit(&mpscq.count, 1, .Release)
|
||||
assert(r > 0)
|
||||
return ret
|
||||
}
|
||||
|
||||
mpsc_count :: proc(mpscq: ^Multi_Producer_Single_Consumer) -> int {
|
||||
return sync.atomic_load_explicit(&mpscq.count, .Relaxed)
|
||||
}
|
||||
|
||||
mpsc_cap :: proc(mpscq: ^Multi_Producer_Single_Consumer) -> int {
|
||||
return len(mpscq.buffer)
|
||||
}
|
||||
431
core/nbio/nbio.odin
Normal file
431
core/nbio/nbio.odin
Normal file
@@ -0,0 +1,431 @@
|
||||
package nbio
|
||||
|
||||
import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
|
||||
import "core:container/pool"
|
||||
import "core:net"
|
||||
import "core:time"
|
||||
|
||||
/*
|
||||
If the package is fully supported on the current target. If it is not it will compile but work
|
||||
in a matter where things are unimplemented.
|
||||
|
||||
Additionally if it is `FULLY_SUPPORTED` it may still return `.Unsupported` in `acquire_thread_event_loop`
|
||||
If the target does not support the needed syscalls for operating the package.
|
||||
*/
|
||||
FULLY_SUPPORTED :: _FULLY_SUPPORTED
|
||||
|
||||
/*
|
||||
An event loop, one per thread, consider the fields private.
|
||||
Do not copy.
|
||||
*/
|
||||
Event_Loop :: struct /* #no_copy */ {
|
||||
using impl: _Event_Loop,
|
||||
allocator: runtime.Allocator,
|
||||
err: General_Error,
|
||||
refs: int,
|
||||
now: time.Time,
|
||||
|
||||
// Queue that is used to queue operations from another thread to be executed on this thread.
|
||||
queue: Multi_Producer_Single_Consumer,
|
||||
|
||||
operation_pool: pool.Pool(Operation),
|
||||
}
|
||||
|
||||
Handle :: _Handle
|
||||
|
||||
// The maximum size of user arguments for an operation, can be increased at the cost of more RAM.
|
||||
MAX_USER_ARGUMENTS :: #config(NBIO_MAX_USER_ARGUMENTS, 4)
|
||||
#assert(MAX_USER_ARGUMENTS >= 4)
|
||||
|
||||
Operation :: struct {
|
||||
cb: Callback,
|
||||
user_data: [MAX_USER_ARGUMENTS + 1]rawptr,
|
||||
detached: bool,
|
||||
type: Operation_Type,
|
||||
using specifics: Specifics,
|
||||
|
||||
_impl: _Operation `fmt:"-"`,
|
||||
using _: struct #raw_union {
|
||||
_pool_link: ^Operation,
|
||||
l: ^Event_Loop,
|
||||
},
|
||||
}
|
||||
|
||||
Specifics :: struct #raw_union {
|
||||
accept: Accept `raw_union_tag:"type=.Accept"`,
|
||||
close: Close `raw_union_tag:"type=.Close"`,
|
||||
dial: Dial `raw_union_tag:"type=.Dial"`,
|
||||
read: Read `raw_union_tag:"type=.Read"`,
|
||||
recv: Recv `raw_union_tag:"type=.Recv"`,
|
||||
send: Send `raw_union_tag:"type=.Send"`,
|
||||
write: Write `raw_union_tag:"type=.Write"`,
|
||||
timeout: Timeout `raw_union_tag:"type=.Timeout"`,
|
||||
poll: Poll `raw_union_tag:"type=.Poll"`,
|
||||
sendfile: Send_File `raw_union_tag:"type=.Send_File"`,
|
||||
open: Open `raw_union_tag:"type=.Open"`,
|
||||
stat: Stat `raw_union_tag:"type=.Stat"`,
|
||||
|
||||
_remove: _Remove `raw_union_tag:"type=._Remove"`,
|
||||
_link_timeout: _Link_Timeout `raw_union_tag:"type=._Link_Timeout"`,
|
||||
_splice: _Splice `raw_union_tag:"type=._Splice"`,
|
||||
}
|
||||
|
||||
Operation_Type :: enum i32 {
|
||||
None,
|
||||
Accept,
|
||||
Close,
|
||||
Dial,
|
||||
Read,
|
||||
Recv,
|
||||
Send,
|
||||
Write,
|
||||
Timeout,
|
||||
Poll,
|
||||
Send_File,
|
||||
Open,
|
||||
Stat,
|
||||
|
||||
_Link_Timeout,
|
||||
_Remove,
|
||||
_Splice,
|
||||
}
|
||||
|
||||
Callback :: #type proc(op: ^Operation)
|
||||
|
||||
/*
|
||||
Initialize or increment the reference counted event loop for the current thread.
|
||||
*/
|
||||
acquire_thread_event_loop :: proc() -> General_Error {
|
||||
return _acquire_thread_event_loop()
|
||||
}
|
||||
|
||||
/*
|
||||
Destroy or decrease the reference counted event loop for the current thread.
|
||||
*/
|
||||
release_thread_event_loop :: proc() {
|
||||
_release_thread_event_loop()
|
||||
}
|
||||
|
||||
current_thread_event_loop :: proc(loc := #caller_location) -> ^Event_Loop {
|
||||
return _current_thread_event_loop(loc)
|
||||
}
|
||||
|
||||
/*
|
||||
Each time you call this the implementation checks its state
|
||||
and calls any callbacks which are ready. You would typically call this in a loop.
|
||||
|
||||
Blocks for up-to timeout waiting for events if there is nothing to do.
|
||||
*/
|
||||
tick :: proc(timeout: time.Duration = NO_TIMEOUT) -> General_Error {
|
||||
l := &_tls_event_loop
|
||||
if l.refs == 0 { return nil }
|
||||
return _tick(l, timeout)
|
||||
}
|
||||
|
||||
/*
|
||||
Runs the event loop by ticking in a loop until there is no more work to be done.
|
||||
*/
|
||||
run :: proc() -> General_Error {
|
||||
l := &_tls_event_loop
|
||||
if l.refs == 0 { return nil }
|
||||
|
||||
acquire_thread_event_loop()
|
||||
defer release_thread_event_loop()
|
||||
|
||||
for num_waiting() > 0 {
|
||||
if errno := _tick(l, NO_TIMEOUT); errno != nil {
|
||||
return errno
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
Runs the event loop by ticking in a loop until there is no more work to be done, or the flag `done` is `true`.
|
||||
*/
|
||||
run_until :: proc(done: ^bool) -> General_Error {
|
||||
l := &_tls_event_loop
|
||||
if l.refs == 0 { return nil }
|
||||
|
||||
acquire_thread_event_loop()
|
||||
defer release_thread_event_loop()
|
||||
|
||||
for num_waiting() > 0 && !intrinsics.volatile_load(done) {
|
||||
if errno := _tick(l, NO_TIMEOUT); errno != nil {
|
||||
return errno
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the number of in-progress operations to be completed on the event loop.
|
||||
*/
|
||||
num_waiting :: proc(l: Maybe(^Event_Loop) = nil) -> int {
|
||||
l_ := l.? or_else &_tls_event_loop
|
||||
if l_.refs == 0 { return 0 }
|
||||
return pool.num_outstanding(&l_.operation_pool)
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the current time (cached at most at the beginning of the current tick).
|
||||
*/
|
||||
now :: proc() -> time.Time {
|
||||
if _tls_event_loop.now == {} {
|
||||
return time.now()
|
||||
}
|
||||
return _tls_event_loop.now
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the given operation from the event loop. The callback of it won't be called and resources are freed.
|
||||
|
||||
Calling `remove`:
|
||||
- Cancels the operation if it has not yet completed
|
||||
- Prevents the callback from being called
|
||||
|
||||
Cancellation via `remove` is *final* and silent:
|
||||
- The callback will never be invoked
|
||||
- No error is delivered
|
||||
- The operation must be considered dead after removal
|
||||
|
||||
WARN: the operation could have already been (partially or completely) completed.
|
||||
A send with `all` set to true could have sent a portion already.
|
||||
But also, a send that could be completed without blocking could have been completed.
|
||||
You just won't get a callback.
|
||||
|
||||
WARN: once an operation's callback is called it can not be removed anymore (use after free).
|
||||
|
||||
WARN: needs to be called from the thread of the event loop the target belongs to.
|
||||
|
||||
Common use would be to cancel a timeout, remove a polling, or remove an `accept` before calling `close` on it's socket.
|
||||
*/
|
||||
remove :: proc(target: ^Operation) {
|
||||
if target == nil {
|
||||
return
|
||||
}
|
||||
|
||||
assert(target.type != .None)
|
||||
|
||||
if target.l != &_tls_event_loop {
|
||||
panic("nbio.remove called on different thread")
|
||||
}
|
||||
|
||||
_remove(target)
|
||||
}
|
||||
|
||||
/*
|
||||
Creates a socket for use in `nbio` and relates it to the given event loop.
|
||||
|
||||
Inputs:
|
||||
- family: Should this be an IP4 or IP6 socket
|
||||
- protocol: The type of socket (TCP or UDP)
|
||||
- l: The event loop to associate it with, defaults to the current thread's loop
|
||||
|
||||
Returns:
|
||||
- socket: The created socket, consider `create_{udp|tcp}_socket` for a typed socket instead of the union
|
||||
- err: A network error (`Create_Socket_Error`, or `Set_Blocking_Error`) which happened while opening
|
||||
*/
|
||||
create_socket :: proc(
|
||||
family: Address_Family,
|
||||
protocol: Socket_Protocol,
|
||||
l: ^Event_Loop = nil,
|
||||
loc := #caller_location,
|
||||
) -> (
|
||||
socket: Any_Socket,
|
||||
err: Create_Socket_Error,
|
||||
) {
|
||||
return _create_socket(l if l != nil else _current_thread_event_loop(loc), family, protocol)
|
||||
}
|
||||
|
||||
/*
|
||||
Creates a UDP socket for use in `nbio` and relates it to the given event loop.
|
||||
|
||||
Inputs:
|
||||
- family: Should this be an IP4 or IP6 socket
|
||||
- l: The event loop to associate it with, defaults to the current thread's loop
|
||||
|
||||
Returns:
|
||||
- socket: The created UDP socket
|
||||
- err: A network error (`Create_Socket_Error`, or `Set_Blocking_Error`) which happened while opening
|
||||
*/
|
||||
create_udp_socket :: proc(family: Address_Family, l: ^Event_Loop = nil, loc := #caller_location) -> (net.UDP_Socket, Create_Socket_Error) {
|
||||
socket, err := create_socket(family, .UDP, l, loc)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
return socket.(UDP_Socket), nil
|
||||
}
|
||||
|
||||
/*
|
||||
Creates a TCP socket for use in `nbio` and relates it to the given event loop.
|
||||
|
||||
Inputs:
|
||||
- family: Should this be an IP4 or IP6 socket
|
||||
- l: The event loop to associate it with, defaults to the current thread's loop
|
||||
|
||||
Returns:
|
||||
- socket: The created TCP socket
|
||||
- err: A network error (`Create_Socket_Error`, or `Set_Blocking_Error`) which happened while opening
|
||||
*/
|
||||
create_tcp_socket :: proc(family: Address_Family, l: ^Event_Loop = nil, loc := #caller_location) -> (net.TCP_Socket, Create_Socket_Error) {
|
||||
socket, err := create_socket(family, .TCP, l, loc)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
return socket.(TCP_Socket), nil
|
||||
}
|
||||
|
||||
/*
|
||||
Creates a socket, sets non blocking mode, relates it to the given IO, binds the socket to the given endpoint and starts listening.
|
||||
|
||||
Inputs:
|
||||
- endpoint: Where to bind the socket to
|
||||
- backlog: The maximum length to which the queue of pending connections may grow, before refusing connections
|
||||
- l: The event loop to associate the socket with, defaults to the current thread's loop
|
||||
|
||||
Returns:
|
||||
- socket: The opened, bound and listening socket
|
||||
- err: A network error (`Create_Socket_Error`, `Bind_Error`, or `Listen_Error`) that has happened
|
||||
*/
|
||||
listen_tcp :: proc(endpoint: Endpoint, backlog := 1000, l: ^Event_Loop = nil, loc := #caller_location) -> (socket: TCP_Socket, err: net.Network_Error) {
|
||||
assert(backlog > 0 && backlog < int(max(i32)))
|
||||
return _listen_tcp(l if l != nil else _current_thread_event_loop(loc), endpoint, backlog)
|
||||
}
|
||||
|
||||
/*
|
||||
Opens a file and associates it with the event loop.
|
||||
|
||||
Inputs:
|
||||
- path: path to the file, if not absolute: relative from `dir`
|
||||
- dir: directory that `path` is relative from (if it is relative), defaults to the current working directory
|
||||
- mode: open mode, defaults to read-only
|
||||
- perm: permissions to use when creating a file, defaults to read+write for everybody
|
||||
- l: event loop to associate the file with, defaults to the current thread's
|
||||
|
||||
Returns:
|
||||
- handle: The file handle
|
||||
- err: An error if it occurred
|
||||
*/
|
||||
open_sync :: proc(path: string, dir: Handle = CWD, mode: File_Flags = {.Read}, perm := Permissions_Default_File, l: ^Event_Loop = nil, loc := #caller_location) -> (handle: Handle, err: FS_Error) {
|
||||
return _open_sync(l if l != nil else _current_thread_event_loop(loc), path, dir, mode, perm)
|
||||
}
|
||||
|
||||
Association_Error :: enum {
|
||||
None,
|
||||
// The given file/handle/socket was not opened in a mode that it can be made non-blocking afterwards.
|
||||
//
|
||||
// On Windows, this can happen when a file is not opened with the `FILE_FLAG_OVERLAPPED` flag.
|
||||
// If using `core:os`, that is set when you specify the `O_NONBLOCK` flag.
|
||||
// There is no way to add that after the fact.
|
||||
Not_Possible_To_Associate,
|
||||
// The given handle is not a valid handle.
|
||||
Invalid_Handle,
|
||||
// No network connection, or the network stack is not initialized.
|
||||
Network_Unreachable,
|
||||
}
|
||||
|
||||
/*
|
||||
Associate the given OS handle, not opened through this package, with the event loop.
|
||||
|
||||
Consider using this package's `open` or `open_sync` directly instead.
|
||||
|
||||
The handle returned is for convenience, it is actually still the same handle as given.
|
||||
Thus you should not close the given handle.
|
||||
|
||||
On Windows, this can error when a file is not opened with the `FILE_FLAG_OVERLAPPED` flag.
|
||||
If using `core:os`, that is set when you specify the `O_NONBLOCK` flag.
|
||||
There is no way to add that after the fact.
|
||||
*/
|
||||
associate_handle :: proc(handle: uintptr, l: ^Event_Loop = nil, loc := #caller_location) -> (Handle, Association_Error) {
|
||||
return _associate_handle(handle, l if l != nil else _current_thread_event_loop(loc))
|
||||
}
|
||||
|
||||
/*
|
||||
Associate the given socket, not created through this package, with the event loop.
|
||||
|
||||
Consider using this package's `create_socket` directly instead.
|
||||
*/
|
||||
associate_socket :: proc(socket: Any_Socket, l: ^Event_Loop = nil, loc := #caller_location) -> Association_Error {
|
||||
return _associate_socket(socket, l if l != nil else _current_thread_event_loop(loc))
|
||||
}
|
||||
|
||||
Read_Entire_File_Error :: struct {
|
||||
operation: Operation_Type,
|
||||
value: FS_Error,
|
||||
}
|
||||
|
||||
Read_Entire_File_Callback :: #type proc(user_data: rawptr, data: []byte, err: Read_Entire_File_Error)
|
||||
|
||||
/*
|
||||
Combines multiple operations (open, stat, read, close) into one that reads an entire regular file.
|
||||
|
||||
The error contains the `operation` that the error happened on.
|
||||
|
||||
Inputs:
|
||||
- path: path to the file, if not absolute: relative from `dir`
|
||||
- user_data: a pointer passed through into the callback
|
||||
- cb: the callback to call once completed, called with the user data, file data, and an optional error
|
||||
- allocator: the allocator to allocate the file's contents onto
|
||||
- dir: directory that `path` is relative from (if it is relative), defaults to the current working directory
|
||||
- l: event loop to execute the operation on
|
||||
*/
|
||||
read_entire_file :: proc(path: string, user_data: rawptr, cb: Read_Entire_File_Callback, allocator := context.allocator, dir := CWD, l: ^Event_Loop = nil, loc := #caller_location) {
|
||||
_read_entire_file(l if l != nil else _current_thread_event_loop(loc), path, user_data, cb, allocator, dir)
|
||||
}
|
||||
|
||||
/*
|
||||
Detach an operation from the package's lifetime management.
|
||||
|
||||
By default the operation's lifetime is managed by the package and freed after a callback is called.
|
||||
Calling this function detaches the operation from this lifetime.
|
||||
You are expected to call `reattach` to give the package back this operation.
|
||||
*/
|
||||
detach :: proc(op: ^Operation) {
|
||||
op.detached = true
|
||||
}
|
||||
|
||||
/*
|
||||
Reattach an operation to the package's lifetime management.
|
||||
*/
|
||||
reattach :: proc(op: ^Operation) {
|
||||
pool.put(&op.l.operation_pool, op)
|
||||
}
|
||||
|
||||
/*
|
||||
Execute an operation.
|
||||
|
||||
If the operation is attached to another thread's event loop, it is queued to be executed on that event loop,
|
||||
optionally waking that loop up (from a blocking `tick`) with `trigger_wake_up`.
|
||||
*/
|
||||
exec :: proc(op: ^Operation, trigger_wake_up := true) {
|
||||
if op.l == &_tls_event_loop {
|
||||
_exec(op)
|
||||
} else {
|
||||
for !mpsc_enqueue(&op.l.queue, op) {
|
||||
warn("operation queue on event loop filled up")
|
||||
wake_up(op.l)
|
||||
_yield()
|
||||
}
|
||||
if trigger_wake_up {
|
||||
wake_up(op.l)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Wake up an event loop on another thread which may be blocking for completed operations.
|
||||
|
||||
Commonly used with `exec` from a worker thread to have the event loop pick up that work.
|
||||
Note that by default `exec` already calls this procedure.
|
||||
*/
|
||||
wake_up :: proc(l: ^Event_Loop) {
|
||||
if l == &_tls_event_loop {
|
||||
return
|
||||
}
|
||||
_wake_up(l)
|
||||
}
|
||||
39
core/nbio/net.odin
Normal file
39
core/nbio/net.odin
Normal file
@@ -0,0 +1,39 @@
|
||||
package nbio
|
||||
|
||||
import "core:net"
|
||||
|
||||
Network_Error :: net.Network_Error
|
||||
Accept_Error :: net.Accept_Error
|
||||
Dial_Error :: net.Dial_Error
|
||||
Send_Error :: net.Send_Error
|
||||
TCP_Send_Error :: net.TCP_Send_Error
|
||||
UDP_Send_Error :: net.UDP_Send_Error
|
||||
Recv_Error :: net.Recv_Error
|
||||
TCP_Recv_Error :: net.TCP_Recv_Error
|
||||
UDP_Recv_Error :: net.UDP_Recv_Error
|
||||
Listen_Error :: net.Listen_Error
|
||||
Create_Socket_Error :: net.Create_Socket_Error
|
||||
|
||||
Address_Family :: net.Address_Family
|
||||
Socket_Protocol :: net.Socket_Protocol
|
||||
|
||||
Address :: net.Address
|
||||
IP4_Address :: net.IP4_Address
|
||||
IP6_Address :: net.IP6_Address
|
||||
|
||||
Endpoint :: net.Endpoint
|
||||
|
||||
TCP_Socket :: net.TCP_Socket
|
||||
UDP_Socket :: net.UDP_Socket
|
||||
Any_Socket :: net.Any_Socket
|
||||
|
||||
IP4_Any :: net.IP4_Any
|
||||
IP6_Any :: net.IP6_Any
|
||||
IP4_Loopback :: net.IP4_Loopback
|
||||
IP6_Loopback :: net.IP6_Loopback
|
||||
|
||||
family_from_endpoint :: net.family_from_endpoint
|
||||
bind :: net.bind
|
||||
bound_endpoint :: net.bound_endpoint
|
||||
parse_endpoint :: net.parse_endpoint
|
||||
endpoint_to_string :: net.endpoint_to_string
|
||||
2473
core/nbio/ops.odin
Normal file
2473
core/nbio/ops.odin
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,3 @@
|
||||
#+build windows, linux, darwin, freebsd
|
||||
package net
|
||||
|
||||
/*
|
||||
@@ -22,7 +21,6 @@ package net
|
||||
|
||||
import "core:strconv"
|
||||
import "core:strings"
|
||||
import "core:fmt"
|
||||
|
||||
/*
|
||||
Expects an IPv4 address with no leading or trailing whitespace:
|
||||
@@ -473,13 +471,20 @@ join_port :: proc(address_or_host: string, port: int, allocator := context.alloc
|
||||
addr := parse_address(addr_or_host)
|
||||
if addr == nil {
|
||||
// hostname
|
||||
fmt.sbprintf(&b, "%v:%v", addr_or_host, port)
|
||||
strings.write_string(&b, addr_or_host)
|
||||
strings.write_string(&b, ":")
|
||||
strings.write_int(&b, port)
|
||||
} else {
|
||||
switch _ in addr {
|
||||
case IP4_Address:
|
||||
fmt.sbprintf(&b, "%v:%v", address_to_string(addr), port)
|
||||
strings.write_string(&b, address_to_string(addr))
|
||||
strings.write_string(&b, ":")
|
||||
strings.write_int(&b, port)
|
||||
case IP6_Address:
|
||||
fmt.sbprintf(&b, "[%v]:%v", address_to_string(addr), port)
|
||||
strings.write_string(&b, "[")
|
||||
strings.write_string(&b, address_to_string(addr))
|
||||
strings.write_string(&b, "]:")
|
||||
strings.write_int(&b, port)
|
||||
}
|
||||
}
|
||||
return strings.to_string(b)
|
||||
@@ -495,8 +500,8 @@ map_to_ip6 :: proc(addr: Address) -> Address {
|
||||
addr4 := addr.(IP4_Address)
|
||||
addr4_u16 := transmute([2]u16be) addr4
|
||||
addr6: IP6_Address
|
||||
addr6[4] = 0xffff
|
||||
copy(addr6[5:], addr4_u16[:])
|
||||
addr6[5] = 0xffff
|
||||
copy(addr6[6:], addr4_u16[:])
|
||||
return addr6
|
||||
}
|
||||
|
||||
@@ -509,7 +514,13 @@ address_to_string :: proc(addr: Address, allocator := context.temp_allocator) ->
|
||||
b := strings.builder_make(allocator)
|
||||
switch v in addr {
|
||||
case IP4_Address:
|
||||
fmt.sbprintf(&b, "%v.%v.%v.%v", v[0], v[1], v[2], v[3])
|
||||
strings.write_uint(&b, uint(v[0]))
|
||||
strings.write_byte(&b, '.')
|
||||
strings.write_uint(&b, uint(v[1]))
|
||||
strings.write_byte(&b, '.')
|
||||
strings.write_uint(&b, uint(v[2]))
|
||||
strings.write_byte(&b, '.')
|
||||
strings.write_uint(&b, uint(v[3]))
|
||||
case IP6_Address:
|
||||
// First find the longest run of zeroes.
|
||||
Zero_Run :: struct {
|
||||
@@ -563,25 +574,33 @@ address_to_string :: proc(addr: Address, allocator := context.temp_allocator) ->
|
||||
for val, i in v {
|
||||
if best.start == i || best.end == i {
|
||||
// For the left and right side of the best zero run, print a `:`.
|
||||
fmt.sbprint(&b, ":")
|
||||
strings.write_string(&b, ":")
|
||||
} else if i < best.start {
|
||||
/*
|
||||
If we haven't made it to the best run yet, print the digit.
|
||||
Make sure we only print a `:` after the digit if it's not
|
||||
immediately followed by the run's own leftmost `:`.
|
||||
*/
|
||||
fmt.sbprintf(&b, "%x", val)
|
||||
|
||||
buf: [32]byte
|
||||
str := strconv.write_bits(buf[:], u64(val), 16, false, size_of(val), strconv.digits, {})
|
||||
strings.write_string(&b, str)
|
||||
|
||||
if i < best.start - 1 {
|
||||
fmt.sbprintf(&b, ":")
|
||||
strings.write_string(&b, ":")
|
||||
}
|
||||
} else if i > best.end {
|
||||
/*
|
||||
If there are any digits after the zero run, print them.
|
||||
But don't print the `:` at the end of the IP number.
|
||||
*/
|
||||
fmt.sbprintf(&b, "%x", val)
|
||||
|
||||
buf: [32]byte
|
||||
str := strconv.write_bits(buf[:], u64(val), 16, false, size_of(val), strconv.digits, {})
|
||||
strings.write_string(&b, str)
|
||||
|
||||
if i != 7 {
|
||||
fmt.sbprintf(&b, ":")
|
||||
strings.write_string(&b, ":")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -598,8 +617,15 @@ endpoint_to_string :: proc(ep: Endpoint, allocator := context.temp_allocator) ->
|
||||
s := address_to_string(ep.address, context.temp_allocator)
|
||||
b := strings.builder_make(allocator)
|
||||
switch a in ep.address {
|
||||
case IP4_Address: fmt.sbprintf(&b, "%v:%v", s, ep.port)
|
||||
case IP6_Address: fmt.sbprintf(&b, "[%v]:%v", s, ep.port)
|
||||
case IP4_Address:
|
||||
strings.write_string(&b, s)
|
||||
strings.write_string(&b, ":")
|
||||
strings.write_int(&b, ep.port)
|
||||
case IP6_Address:
|
||||
strings.write_string(&b, "[")
|
||||
strings.write_string(&b, s)
|
||||
strings.write_string(&b, "]:")
|
||||
strings.write_int(&b, ep.port)
|
||||
}
|
||||
return strings.to_string(b)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#+build windows, linux, darwin, freebsd
|
||||
package net
|
||||
|
||||
/*
|
||||
@@ -91,6 +90,7 @@ Parse_Endpoint_Error :: enum u32 {
|
||||
Resolve_Error :: enum u32 {
|
||||
None = 0,
|
||||
Unable_To_Resolve = 1,
|
||||
Allocation_Failure,
|
||||
}
|
||||
|
||||
DNS_Error :: enum u32 {
|
||||
@@ -144,11 +144,11 @@ Address :: union {IP4_Address, IP6_Address}
|
||||
IP4_Loopback :: IP4_Address{127, 0, 0, 1}
|
||||
IP6_Loopback :: IP6_Address{0, 0, 0, 0, 0, 0, 0, 1}
|
||||
|
||||
IP4_Any := IP4_Address{}
|
||||
IP6_Any := IP6_Address{}
|
||||
IP4_Any :: IP4_Address{}
|
||||
IP6_Any :: IP6_Address{}
|
||||
|
||||
IP4_mDNS_Broadcast := Endpoint{address=IP4_Address{224, 0, 0, 251}, port=5353}
|
||||
IP6_mDNS_Broadcast := Endpoint{address=IP6_Address{65282, 0, 0, 0, 0, 0, 0, 251}, port = 5353}
|
||||
IP4_mDNS_Broadcast :: Endpoint{address=IP4_Address{224, 0, 0, 251}, port=5353}
|
||||
IP6_mDNS_Broadcast :: Endpoint{address=IP6_Address{65282, 0, 0, 0, 0, 0, 0, 251}, port = 5353}
|
||||
|
||||
Endpoint :: struct {
|
||||
address: Address,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#+build windows, linux, darwin, freebsd
|
||||
package net
|
||||
|
||||
/*
|
||||
@@ -22,13 +21,18 @@ package net
|
||||
Haesbaert: Security fixes
|
||||
*/
|
||||
|
||||
@(require) import "base:runtime"
|
||||
@(require)
|
||||
import "base:runtime"
|
||||
|
||||
import "core:bufio"
|
||||
import "core:io"
|
||||
import "core:math/rand"
|
||||
import "core:mem"
|
||||
import "core:strings"
|
||||
import "core:time"
|
||||
import "core:os"
|
||||
import "core:math/rand"
|
||||
@(require) import "core:sync"
|
||||
|
||||
@(require)
|
||||
import "core:sync"
|
||||
|
||||
dns_config_initialized: sync.Once
|
||||
when ODIN_OS == .Windows {
|
||||
@@ -42,20 +46,12 @@ when ODIN_OS == .Windows {
|
||||
hosts_file = "/etc/hosts",
|
||||
}
|
||||
} else {
|
||||
#panic("Please add a configuration for this OS.")
|
||||
DEFAULT_DNS_CONFIGURATION :: DNS_Configuration{}
|
||||
}
|
||||
|
||||
/*
|
||||
Replaces environment placeholders in `dns_configuration`. Only necessary on Windows.
|
||||
Is automatically called, once, by `get_dns_records_*`.
|
||||
*/
|
||||
@(private)
|
||||
init_dns_configuration :: proc() {
|
||||
when ODIN_OS == .Windows {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
val := os.replace_environment_placeholders(dns_configuration.hosts_file, context.temp_allocator)
|
||||
copy(dns_configuration.hosts_file_buf[:], val)
|
||||
dns_configuration.hosts_file = string(dns_configuration.hosts_file_buf[:len(val)])
|
||||
_init_dns_configuration()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,9 +174,7 @@ resolve_ip6 :: proc(hostname_and_maybe_port: string) -> (ep6: Endpoint, err: Net
|
||||
See `destroy_records`.
|
||||
*/
|
||||
get_dns_records_from_os :: proc(hostname: string, type: DNS_Record_Type, allocator := context.allocator) -> (records: []DNS_Record, err: DNS_Error) {
|
||||
when ODIN_OS == .Windows {
|
||||
sync.once_do(&dns_config_initialized, init_dns_configuration)
|
||||
}
|
||||
init_dns_configuration()
|
||||
return _get_dns_records_os(hostname, type, allocator)
|
||||
}
|
||||
|
||||
@@ -196,51 +190,14 @@ get_dns_records_from_os :: proc(hostname: string, type: DNS_Record_Type, allocat
|
||||
See `destroy_records`.
|
||||
*/
|
||||
get_dns_records_from_nameservers :: proc(hostname: string, type: DNS_Record_Type, name_servers: []Endpoint, host_overrides: []DNS_Record, allocator := context.allocator) -> (records: []DNS_Record, err: DNS_Error) {
|
||||
when ODIN_OS == .Windows {
|
||||
sync.once_do(&dns_config_initialized, init_dns_configuration)
|
||||
}
|
||||
init_dns_configuration()
|
||||
context.allocator = allocator
|
||||
|
||||
if type != .SRV {
|
||||
// NOTE(tetra): 'hostname' can contain underscores when querying SRV records
|
||||
ok := validate_hostname(hostname)
|
||||
if !ok {
|
||||
return nil, .Invalid_Hostname_Error
|
||||
}
|
||||
}
|
||||
id := u16be(rand.uint32())
|
||||
dns_packet_buf: [DNS_PACKET_MIN_LEN]byte = ---
|
||||
dns_packet := make_dns_packet(dns_packet_buf[:], id, hostname, type) or_return
|
||||
|
||||
hdr := DNS_Header{
|
||||
id = u16be(rand.uint32()),
|
||||
is_response = false,
|
||||
opcode = 0,
|
||||
is_authoritative = false,
|
||||
is_truncated = false,
|
||||
is_recursion_desired = true,
|
||||
is_recursion_available = false,
|
||||
response_code = DNS_Response_Code.No_Error,
|
||||
}
|
||||
|
||||
id, bits := pack_dns_header(hdr)
|
||||
dns_hdr := [6]u16be{}
|
||||
dns_hdr[0] = id
|
||||
dns_hdr[1] = bits
|
||||
dns_hdr[2] = 1
|
||||
|
||||
dns_query := [2]u16be{ u16be(type), 1 }
|
||||
|
||||
output := [(size_of(u16be) * 6) + NAME_MAX + (size_of(u16be) * 2)]u8{}
|
||||
b := strings.builder_from_slice(output[:])
|
||||
|
||||
strings.write_bytes(&b, mem.slice_data_cast([]u8, dns_hdr[:]))
|
||||
ok := encode_hostname(&b, hostname)
|
||||
if !ok {
|
||||
return nil, .Invalid_Hostname_Error
|
||||
}
|
||||
strings.write_bytes(&b, mem.slice_data_cast([]u8, dns_query[:]))
|
||||
|
||||
dns_packet := output[:strings.builder_len(b)]
|
||||
|
||||
dns_response_buf := [4096]u8{}
|
||||
dns_response_buf: [4096]u8 = ---
|
||||
dns_response: []u8
|
||||
for name_server in name_servers {
|
||||
conn, sock_err := make_unbound_udp_socket(family_from_endpoint(name_server))
|
||||
@@ -283,6 +240,42 @@ get_dns_records_from_nameservers :: proc(hostname: string, type: DNS_Record_Type
|
||||
return
|
||||
}
|
||||
|
||||
DNS_PACKET_MIN_LEN :: (size_of(u16be) * 6) + NAME_MAX + (size_of(u16be) * 2)
|
||||
|
||||
make_dns_packet :: proc(buf: []byte, id: u16be, hostname: string, type: DNS_Record_Type) -> (packet: []byte, err: DNS_Error) {
|
||||
assert(len(buf) >= DNS_PACKET_MIN_LEN)
|
||||
|
||||
hdr := DNS_Header{
|
||||
id = id,
|
||||
is_response = false,
|
||||
opcode = 0,
|
||||
is_authoritative = false,
|
||||
is_truncated = false,
|
||||
is_recursion_desired = true,
|
||||
is_recursion_available = false,
|
||||
response_code = DNS_Response_Code.No_Error,
|
||||
}
|
||||
|
||||
_, bits := pack_dns_header(hdr)
|
||||
dns_hdr := [6]u16be{}
|
||||
dns_hdr[0] = id
|
||||
dns_hdr[1] = bits
|
||||
dns_hdr[2] = 1
|
||||
|
||||
dns_query := [2]u16be{ u16be(type), 1 }
|
||||
|
||||
b := strings.builder_from_slice(buf[:])
|
||||
|
||||
strings.write_bytes(&b, mem.slice_data_cast([]u8, dns_hdr[:]))
|
||||
ok := encode_hostname(&b, hostname)
|
||||
if !ok {
|
||||
return nil, .Invalid_Hostname_Error
|
||||
}
|
||||
strings.write_bytes(&b, mem.slice_data_cast([]u8, dns_query[:]))
|
||||
|
||||
return buf[:strings.builder_len(b)], nil
|
||||
}
|
||||
|
||||
// `records` slice is also destroyed.
|
||||
destroy_dns_records :: proc(records: []DNS_Record, allocator := context.allocator) {
|
||||
context.allocator = allocator
|
||||
@@ -364,13 +357,8 @@ unpack_dns_header :: proc(id: u16be, bits: u16be) -> (hdr: DNS_Header) {
|
||||
return hdr
|
||||
}
|
||||
|
||||
load_resolv_conf :: proc(resolv_conf_path: string, allocator := context.allocator) -> (name_servers: []Endpoint, ok: bool) {
|
||||
context.allocator = allocator
|
||||
|
||||
res := os.read_entire_file_from_filename(resolv_conf_path) or_return
|
||||
defer delete(res)
|
||||
resolv_str := string(res)
|
||||
|
||||
parse_resolv_conf :: proc(resolv_str: string, allocator := context.allocator) -> (name_servers: []Endpoint) {
|
||||
resolv_str := resolv_str
|
||||
id_str := "nameserver"
|
||||
id_len := len(id_str)
|
||||
|
||||
@@ -401,41 +389,51 @@ load_resolv_conf :: proc(resolv_conf_path: string, allocator := context.allocato
|
||||
append(&_name_servers, endpoint)
|
||||
}
|
||||
|
||||
return _name_servers[:], true
|
||||
return _name_servers[:]
|
||||
}
|
||||
|
||||
load_hosts :: proc(hosts_file_path: string, allocator := context.allocator) -> (hosts: []DNS_Host_Entry, ok: bool) {
|
||||
context.allocator = allocator
|
||||
parse_hosts :: proc(stream: io.Stream, allocator := context.allocator) -> (hosts: []DNS_Host_Entry, ok: bool) {
|
||||
s := bufio.scanner_init(&{}, stream, allocator)
|
||||
defer bufio.scanner_destroy(s)
|
||||
|
||||
res := os.read_entire_file_from_filename(hosts_file_path, allocator) or_return
|
||||
defer delete(res)
|
||||
resize(&s.buf, 256)
|
||||
|
||||
_hosts := make([dynamic]DNS_Host_Entry, 0, allocator)
|
||||
hosts_str := string(res)
|
||||
for line in strings.split_lines_iterator(&hosts_str) {
|
||||
if len(line) == 0 || line[0] == '#' {
|
||||
continue
|
||||
_hosts: [dynamic]DNS_Host_Entry
|
||||
_hosts.allocator = allocator
|
||||
defer if !ok {
|
||||
for host in _hosts {
|
||||
delete(host.name, allocator)
|
||||
}
|
||||
delete(_hosts)
|
||||
}
|
||||
|
||||
splits := strings.fields(line)
|
||||
defer delete(splits)
|
||||
for bufio.scanner_scan(s) {
|
||||
line := bufio.scanner_text(s)
|
||||
|
||||
(len(splits) >= 2) or_continue
|
||||
line, _, _ = strings.partition(line, "#")
|
||||
(len(line) > 0) or_continue
|
||||
|
||||
ip_str := strings.fields_iterator(&line) or_continue
|
||||
|
||||
ip_str := splits[0]
|
||||
addr := parse_address(ip_str)
|
||||
if addr == nil {
|
||||
continue
|
||||
}
|
||||
(addr != nil) or_continue
|
||||
|
||||
for hostname in splits[1:] {
|
||||
if len(hostname) != 0 {
|
||||
append(&_hosts, DNS_Host_Entry{hostname, addr})
|
||||
}
|
||||
for hostname in strings.fields_iterator(&line) {
|
||||
(len(hostname) > 0) or_continue
|
||||
|
||||
clone, alloc_err := strings.clone(hostname, allocator)
|
||||
if alloc_err != nil { return }
|
||||
|
||||
_, alloc_err = append(&_hosts, DNS_Host_Entry{clone, addr})
|
||||
if alloc_err != nil { return }
|
||||
}
|
||||
}
|
||||
|
||||
return _hosts[:], true
|
||||
if bufio.scanner_error(s) != nil { return }
|
||||
|
||||
hosts = _hosts[:]
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
// www.google.com -> 3www6google3com0
|
||||
@@ -594,7 +592,7 @@ decode_hostname :: proc(packet: []u8, start_idx: int, allocator := context.alloc
|
||||
|
||||
// Uses RFC 952 & RFC 1123
|
||||
validate_hostname :: proc(hostname: string) -> (ok: bool) {
|
||||
if len(hostname) > 255 || len(hostname) == 0 {
|
||||
if len(hostname) > NAME_MAX || len(hostname) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -604,7 +602,7 @@ validate_hostname :: proc(hostname: string) -> (ok: bool) {
|
||||
|
||||
_hostname := hostname
|
||||
for label in strings.split_iterator(&_hostname, ".") {
|
||||
if len(label) > 63 || len(label) == 0 {
|
||||
if len(label) > LABEL_MAX || len(label) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -868,4 +866,4 @@ parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator
|
||||
xid = hdr.id
|
||||
|
||||
return _records[:], xid, true
|
||||
}
|
||||
}
|
||||
|
||||
24
core/net/dns_os.odin
Normal file
24
core/net/dns_os.odin
Normal file
@@ -0,0 +1,24 @@
|
||||
#+build darwin, freebsd, openbsd, netbsd, linux, windows, wasi
|
||||
#+private
|
||||
package net
|
||||
|
||||
import "core:os"
|
||||
|
||||
load_resolv_conf :: proc(resolv_conf_path: string, allocator := context.allocator) -> (name_servers: []Endpoint, ok: bool) {
|
||||
context.allocator = allocator
|
||||
|
||||
res := os.read_entire_file_from_filename(resolv_conf_path) or_return
|
||||
defer delete(res)
|
||||
resolv_str := string(res)
|
||||
|
||||
return parse_resolv_conf(resolv_str), true
|
||||
}
|
||||
|
||||
load_hosts :: proc(hosts_file_path: string, allocator := context.allocator) -> (hosts: []DNS_Host_Entry, ok: bool) {
|
||||
hosts_file, err := os.open(hosts_file_path)
|
||||
if err != nil { return }
|
||||
defer os.close(hosts_file)
|
||||
|
||||
return parse_hosts(os.stream_from_handle(hosts_file), allocator)
|
||||
}
|
||||
|
||||
12
core/net/dns_others.odin
Normal file
12
core/net/dns_others.odin
Normal file
@@ -0,0 +1,12 @@
|
||||
#+build !windows
|
||||
#+build !linux
|
||||
#+build !darwin
|
||||
#+build !freebsd
|
||||
#+build !netbsd
|
||||
#+build !openbsd
|
||||
package net
|
||||
|
||||
@(private)
|
||||
_get_dns_records_os :: proc(hostname: string, type: DNS_Record_Type, allocator := context.allocator) -> (records: []DNS_Record, err: DNS_Error) {
|
||||
return
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#+build linux, darwin, freebsd
|
||||
#+build linux, darwin, freebsd, openbsd, netbsd
|
||||
package net
|
||||
/*
|
||||
Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
|
||||
@@ -42,14 +42,19 @@ _get_dns_records_os :: proc(hostname: string, type: DNS_Record_Type, allocator :
|
||||
}
|
||||
|
||||
hosts, hosts_ok := load_hosts(dns_configuration.hosts_file)
|
||||
defer delete(hosts)
|
||||
if !hosts_ok {
|
||||
return nil, .Invalid_Hosts_Config_Error
|
||||
}
|
||||
defer {
|
||||
for h in hosts {
|
||||
delete(h.name)
|
||||
}
|
||||
delete(hosts)
|
||||
}
|
||||
|
||||
host_overrides := make([dynamic]DNS_Record)
|
||||
for host in hosts {
|
||||
if strings.compare(host.name, hostname) != 0 {
|
||||
if host.name != hostname {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -79,4 +84,4 @@ _get_dns_records_os :: proc(hostname: string, type: DNS_Record_Type, allocator :
|
||||
}
|
||||
|
||||
return get_dns_records_from_nameservers(hostname, type, name_servers, host_overrides[:])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,11 +20,29 @@ package net
|
||||
Feoramund: FreeBSD platform code
|
||||
*/
|
||||
|
||||
import "core:strings"
|
||||
import "base:runtime"
|
||||
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import "core:strings"
|
||||
import "core:sync"
|
||||
|
||||
import win "core:sys/windows"
|
||||
|
||||
/*
|
||||
Replaces environment placeholders in `dns_configuration`. Only necessary on Windows.
|
||||
Is automatically called, once, by `get_dns_records_*`.
|
||||
*/
|
||||
@(private)
|
||||
_init_dns_configuration :: proc() {
|
||||
sync.once_do(&dns_config_initialized, proc() {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
val := os.replace_environment_placeholders(dns_configuration.hosts_file, context.temp_allocator)
|
||||
copy(dns_configuration.hosts_file_buf[:], val)
|
||||
dns_configuration.hosts_file = string(dns_configuration.hosts_file_buf[:len(val)])
|
||||
})
|
||||
}
|
||||
|
||||
@(private)
|
||||
_get_dns_records_os :: proc(hostname: string, type: DNS_Record_Type, allocator := context.allocator) -> (records: []DNS_Record, err: DNS_Error) {
|
||||
context.allocator = allocator
|
||||
@@ -171,4 +189,4 @@ _get_dns_records_os :: proc(hostname: string, type: DNS_Record_Type, allocator :
|
||||
|
||||
records = recs[:]
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,6 +139,11 @@ Accept_Error :: enum i32 {
|
||||
Unknown,
|
||||
}
|
||||
|
||||
Recv_Error :: union #shared_nil {
|
||||
TCP_Recv_Error,
|
||||
UDP_Recv_Error,
|
||||
}
|
||||
|
||||
TCP_Recv_Error :: enum i32 {
|
||||
None,
|
||||
// No network connection, or the network stack is not initialized.
|
||||
@@ -149,7 +154,8 @@ TCP_Recv_Error :: enum i32 {
|
||||
Invalid_Argument,
|
||||
// The socket is not connected.
|
||||
Not_Connected,
|
||||
// Connection was closed/broken/shutdown while receiving data.
|
||||
// Connection was closed due to an error or shutdown.
|
||||
// NOTE: a graceful close is indicated by a `0, nil` (0 bytes received and no error) return.
|
||||
Connection_Closed,
|
||||
// Timed out before being able to receive any data.
|
||||
Timeout,
|
||||
@@ -170,7 +176,8 @@ UDP_Recv_Error :: enum i32 {
|
||||
Insufficient_Resources,
|
||||
// Invalid socket or buffer given.
|
||||
Invalid_Argument,
|
||||
// "Connection" was refused by remote, or closed/broken/shutdown while receiving data.
|
||||
// "Connection" was refused, or closed due to an error.
|
||||
// NOTE: a graceful close is indicated by a `0, nil` (0 bytes received and no error) return.
|
||||
Connection_Refused,
|
||||
// Timed out before being able to receive any data.
|
||||
Timeout,
|
||||
@@ -185,6 +192,11 @@ UDP_Recv_Error :: enum i32 {
|
||||
Unknown,
|
||||
}
|
||||
|
||||
Send_Error :: union #shared_nil {
|
||||
TCP_Send_Error,
|
||||
UDP_Send_Error,
|
||||
}
|
||||
|
||||
TCP_Send_Error :: enum i32 {
|
||||
None,
|
||||
// No network connection, or the network stack is not initialized.
|
||||
@@ -193,7 +205,7 @@ TCP_Send_Error :: enum i32 {
|
||||
Insufficient_Resources,
|
||||
// Invalid socket or buffer given.
|
||||
Invalid_Argument,
|
||||
// Connection was closed/broken/shutdown while receiving data.
|
||||
// Connection was closed/broken/shutdown while sending data.
|
||||
Connection_Closed,
|
||||
// The socket is not connected.
|
||||
Not_Connected,
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#+build !linux
|
||||
#+build !freebsd
|
||||
#+build !windows
|
||||
#+build !netbsd
|
||||
#+build !openbsd
|
||||
package net
|
||||
|
||||
@(private="file", thread_local)
|
||||
@@ -18,10 +20,3 @@ _last_platform_error_string :: proc() -> string {
|
||||
_set_last_platform_error :: proc(err: i32) {
|
||||
_last_error = err
|
||||
}
|
||||
|
||||
Parse_Endpoint_Error :: enum u32 {
|
||||
None = 0,
|
||||
Bad_Port = 1,
|
||||
Bad_Address,
|
||||
Bad_Hostname,
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#+build darwin
|
||||
#+build darwin, netbsd, openbsd
|
||||
package net
|
||||
|
||||
/*
|
||||
@@ -63,7 +63,7 @@ _dial_error :: proc() -> Dial_Error {
|
||||
return .Already_Connecting
|
||||
case .WSAEADDRNOTAVAIL, .WSAEAFNOSUPPORT, .WSAEFAULT, .WSAENOTSOCK, .WSAEINPROGRESS, .WSAEINVAL:
|
||||
return .Invalid_Argument
|
||||
case .WSAECONNREFUSED:
|
||||
case .WSAECONNREFUSED, .CONNECTION_REFUSED:
|
||||
return .Refused
|
||||
case .WSAEISCONN:
|
||||
return .Already_Connected
|
||||
@@ -122,7 +122,7 @@ _accept_error :: proc() -> Accept_Error {
|
||||
return .Aborted
|
||||
case .WSAEFAULT, .WSAEINPROGRESS, .WSAENOTSOCK:
|
||||
return .Invalid_Argument
|
||||
case .WSAEINTR:
|
||||
case .WSAEINTR, .OPERATION_ABORTED:
|
||||
return .Interrupted
|
||||
case .WSAEINVAL:
|
||||
return .Not_Listening
|
||||
|
||||
11
core/net/interface_others.odin
Normal file
11
core/net/interface_others.odin
Normal file
@@ -0,0 +1,11 @@
|
||||
#+build !darwin
|
||||
#+build !linux
|
||||
#+build !freebsd
|
||||
#+build !windows
|
||||
#+build !netbsd
|
||||
#+build !openbsd
|
||||
package net
|
||||
|
||||
_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Interfaces_Error) {
|
||||
return
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#+build darwin
|
||||
#+build darwin, openbsd, netbsd
|
||||
package net
|
||||
|
||||
/*
|
||||
@@ -117,32 +117,47 @@ IF_Flag :: enum u32 {
|
||||
BROADCAST,
|
||||
DEBUG,
|
||||
LOOPBACK,
|
||||
POINTTOPOINT,
|
||||
NOTRAILERS,
|
||||
RUNNING,
|
||||
NOARP,
|
||||
PROMISC,
|
||||
ALLMULTI,
|
||||
OACTIVE,
|
||||
SIMPLEX,
|
||||
LINK0,
|
||||
LINK1,
|
||||
LINK2,
|
||||
MULTICAST,
|
||||
// NOTE: different order on other BSDs but we don't even need these.
|
||||
// POINTTOPOINT,
|
||||
// NOTRAILERS,
|
||||
// RUNNING,
|
||||
// NOARP,
|
||||
// PROMISC,
|
||||
// ALLMULTI,
|
||||
// OACTIVE,
|
||||
// SIMPLEX,
|
||||
// LINK0,
|
||||
// LINK1,
|
||||
// LINK2,
|
||||
// MULTICAST,
|
||||
}
|
||||
|
||||
@(private)
|
||||
IF_Flags :: bit_set[IF_Flag; u32]
|
||||
|
||||
@(private)
|
||||
ifaddrs :: struct {
|
||||
next: ^ifaddrs,
|
||||
name: cstring,
|
||||
flags: IF_Flags,
|
||||
addr: ^posix.sockaddr,
|
||||
netmask: ^posix.sockaddr,
|
||||
dstaddr: ^posix.sockaddr,
|
||||
data: rawptr,
|
||||
when ODIN_OS == .Darwin || ODIN_OS == .OpenBSD {
|
||||
@(private)
|
||||
ifaddrs :: struct {
|
||||
next: ^ifaddrs,
|
||||
name: cstring,
|
||||
flags: IF_Flags,
|
||||
addr: ^posix.sockaddr,
|
||||
netmask: ^posix.sockaddr,
|
||||
dstaddr: ^posix.sockaddr,
|
||||
data: rawptr,
|
||||
}
|
||||
} else when ODIN_OS == .NetBSD {
|
||||
@(private)
|
||||
ifaddrs :: struct {
|
||||
next: ^ifaddrs,
|
||||
name: cstring,
|
||||
flags: IF_Flags,
|
||||
addr: ^posix.sockaddr,
|
||||
netmask: ^posix.sockaddr,
|
||||
dstaddr: ^posix.sockaddr,
|
||||
data: rawptr,
|
||||
addrflags: u32,
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
@@ -1,4 +1,3 @@
|
||||
#+build windows, linux, darwin, freebsd
|
||||
package net
|
||||
|
||||
/*
|
||||
@@ -20,6 +19,35 @@ package net
|
||||
Feoramund: FreeBSD platform code
|
||||
*/
|
||||
|
||||
Socket_Option :: enum i32 {
|
||||
Broadcast = i32(_SOCKET_OPTION_BROADCAST),
|
||||
Reuse_Address = i32(_SOCKET_OPTION_REUSE_ADDRESS),
|
||||
Keep_Alive = i32(_SOCKET_OPTION_KEEP_ALIVE),
|
||||
Out_Of_Bounds_Data_Inline = i32(_SOCKET_OPTION_OUT_OF_BOUNDS_DATA_INLINE),
|
||||
Linger = i32(_SOCKET_OPTION_LINGER),
|
||||
Receive_Buffer_Size = i32(_SOCKET_OPTION_RECEIVE_BUFFER_SIZE),
|
||||
Send_Buffer_Size = i32(_SOCKET_OPTION_SEND_BUFFER_SIZE),
|
||||
Receive_Timeout = i32(_SOCKET_OPTION_RECEIVE_TIMEOUT),
|
||||
Send_Timeout = i32(_SOCKET_OPTION_SEND_TIMEOUT),
|
||||
|
||||
TCP_Nodelay = i32(_SOCKET_OPTION_TCP_NODELAY),
|
||||
|
||||
Use_Loopback = i32(_SOCKET_OPTION_USE_LOOPBACK),
|
||||
Reuse_Port = i32(_SOCKET_OPTION_REUSE_PORT),
|
||||
No_SIGPIPE_From_EPIPE = i32(_SOCKET_OPTION_NO_SIGPIPE_FROM_EPIPE),
|
||||
Reuse_Port_Load_Balancing = i32(_SOCKET_OPTION_REUSE_PORT_LOAD_BALANCING),
|
||||
|
||||
Exclusive_Addr_Use = i32(_SOCKET_OPTION_EXCLUSIVE_ADDR_USE),
|
||||
Conditional_Accept = i32(_SOCKET_OPTION_CONDITIONAL_ACCEPT),
|
||||
Dont_Linger = i32(_SOCKET_OPTION_DONT_LINGER),
|
||||
}
|
||||
|
||||
Shutdown_Manner :: enum i32 {
|
||||
Receive = i32(_SHUTDOWN_MANNER_RECEIVE),
|
||||
Send = i32(_SHUTDOWN_MANNER_SEND),
|
||||
Both = i32(_SHUTDOWN_MANNER_BOTH),
|
||||
}
|
||||
|
||||
any_socket_to_socket :: proc "contextless" (socket: Any_Socket) -> Socket {
|
||||
switch s in socket {
|
||||
case TCP_Socket: return Socket(s)
|
||||
@@ -193,21 +221,36 @@ close :: proc(socket: Any_Socket) {
|
||||
_close(socket)
|
||||
}
|
||||
|
||||
/*
|
||||
Receive data into a buffer from a TCP socket.
|
||||
|
||||
If no error occurs, `recv_tcp` returns the number of bytes received and `buf` will contain this data received.
|
||||
If the connection has been gracefully closed, the return value is `0, nil` (0 bytes read and no error).
|
||||
*/
|
||||
recv_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_read: int, err: TCP_Recv_Error) {
|
||||
return _recv_tcp(socket, buf)
|
||||
}
|
||||
|
||||
/*
|
||||
Receive data into a buffer from a UDP socket.
|
||||
|
||||
If no error occurs, `recv_udp` returns the number of bytes received and `buf` will contain this data received.
|
||||
If the "connection" has been gracefully closed, the return value is `0, nil` (0 bytes read and no error).
|
||||
*/
|
||||
recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: UDP_Recv_Error) {
|
||||
return _recv_udp(socket, buf)
|
||||
}
|
||||
|
||||
/*
|
||||
Receive data from into a buffer from any socket.
|
||||
Receive data into a buffer from any socket.
|
||||
|
||||
Note: `remote_endpoint` parameter is non-nil only if the socket type is UDP. On TCP sockets it
|
||||
will always return `nil`.
|
||||
|
||||
Errors that can be returned: `TCP_Recv_Error`, or `UDP_Recv_Error`
|
||||
Errors that can be returned: `TCP_Recv_Error`, or `UDP_Recv_Error`.
|
||||
|
||||
If no error occurs, `recv_any` returns the number of bytes received and `buf` will contain this data received.
|
||||
If the connection has been gracefully closed, the return value is `0, nil, nil` (0 bytes read and no error).
|
||||
*/
|
||||
recv_any :: proc(socket: Any_Socket, buf: []byte) -> (
|
||||
bytes_read: int,
|
||||
|
||||
@@ -20,45 +20,35 @@ package net
|
||||
Feoramund: FreeBSD platform code
|
||||
*/
|
||||
|
||||
import "core:c"
|
||||
import "core:sys/freebsd"
|
||||
import "core:time"
|
||||
|
||||
Fd :: freebsd.Fd
|
||||
|
||||
Socket_Option :: enum c.int {
|
||||
// TODO: Test and implement more socket options.
|
||||
// DEBUG
|
||||
Reuse_Address = cast(c.int)freebsd.Socket_Option.REUSEADDR,
|
||||
Keep_Alive = cast(c.int)freebsd.Socket_Option.KEEPALIVE,
|
||||
// DONTROUTE
|
||||
Broadcast = cast(c.int)freebsd.Socket_Option.BROADCAST,
|
||||
Use_Loopback = cast(c.int)freebsd.Socket_Option.USELOOPBACK,
|
||||
Linger = cast(c.int)freebsd.Socket_Option.LINGER,
|
||||
Out_Of_Bounds_Data_Inline = cast(c.int)freebsd.Socket_Option.OOBINLINE,
|
||||
Reuse_Port = cast(c.int)freebsd.Socket_Option.REUSEPORT,
|
||||
// TIMESTAMP
|
||||
No_SIGPIPE_From_EPIPE = cast(c.int)freebsd.Socket_Option.NOSIGPIPE,
|
||||
// ACCEPTFILTER
|
||||
// BINTIME
|
||||
// NO_OFFLOAD
|
||||
// NO_DDP
|
||||
Reuse_Port_Load_Balancing = cast(c.int)freebsd.Socket_Option.REUSEPORT_LB,
|
||||
// RERROR
|
||||
_SOCKET_OPTION_BROADCAST :: freebsd.Socket_Option.BROADCAST
|
||||
_SOCKET_OPTION_REUSE_ADDRESS :: freebsd.Socket_Option.REUSEADDR
|
||||
_SOCKET_OPTION_KEEP_ALIVE :: freebsd.Socket_Option.KEEPALIVE
|
||||
_SOCKET_OPTION_OUT_OF_BOUNDS_DATA_INLINE :: freebsd.Socket_Option.OOBINLINE
|
||||
_SOCKET_OPTION_LINGER :: freebsd.Socket_Option.LINGER
|
||||
_SOCKET_OPTION_RECEIVE_BUFFER_SIZE :: freebsd.Socket_Option.RCVBUF
|
||||
_SOCKET_OPTION_SEND_BUFFER_SIZE :: freebsd.Socket_Option.SNDBUF
|
||||
_SOCKET_OPTION_RECEIVE_TIMEOUT :: freebsd.Socket_Option.RCVTIMEO
|
||||
_SOCKET_OPTION_SEND_TIMEOUT :: freebsd.Socket_Option.SNDTIMEO
|
||||
|
||||
Send_Buffer_Size = cast(c.int)freebsd.Socket_Option.SNDBUF,
|
||||
Receive_Buffer_Size = cast(c.int)freebsd.Socket_Option.RCVBUF,
|
||||
// SNDLOWAT
|
||||
// RCVLOWAT
|
||||
Send_Timeout = cast(c.int)freebsd.Socket_Option.SNDTIMEO,
|
||||
Receive_Timeout = cast(c.int)freebsd.Socket_Option.RCVTIMEO,
|
||||
}
|
||||
_SOCKET_OPTION_TCP_NODELAY :: -1
|
||||
|
||||
Shutdown_Manner :: enum c.int {
|
||||
Receive = cast(c.int)freebsd.Shutdown_Method.RD,
|
||||
Send = cast(c.int)freebsd.Shutdown_Method.WR,
|
||||
Both = cast(c.int)freebsd.Shutdown_Method.RDWR,
|
||||
}
|
||||
_SOCKET_OPTION_USE_LOOPBACK :: freebsd.Socket_Option.USELOOPBACK
|
||||
_SOCKET_OPTION_REUSE_PORT :: freebsd.Socket_Option.REUSEPORT
|
||||
_SOCKET_OPTION_NO_SIGPIPE_FROM_EPIPE :: freebsd.Socket_Option.NOSIGPIPE
|
||||
_SOCKET_OPTION_REUSE_PORT_LOAD_BALANCING :: freebsd.Socket_Option.REUSEPORT_LB
|
||||
|
||||
_SOCKET_OPTION_EXCLUSIVE_ADDR_USE :: -1
|
||||
_SOCKET_OPTION_CONDITIONAL_ACCEPT :: -1
|
||||
_SOCKET_OPTION_DONT_LINGER :: -1
|
||||
|
||||
_SHUTDOWN_MANNER_RECEIVE :: freebsd.Shutdown_Method.RD
|
||||
_SHUTDOWN_MANNER_SEND :: freebsd.Shutdown_Method.WR
|
||||
_SHUTDOWN_MANNER_BOTH :: freebsd.Shutdown_Method.RDWR
|
||||
|
||||
@(private)
|
||||
_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Create_Socket_Error) {
|
||||
@@ -272,7 +262,7 @@ _set_option :: proc(socket: Any_Socket, option: Socket_Option, value: any, loc :
|
||||
ptr: rawptr
|
||||
len: freebsd.socklen_t
|
||||
|
||||
switch option {
|
||||
#partial switch option {
|
||||
case
|
||||
.Reuse_Address,
|
||||
.Keep_Alive,
|
||||
@@ -344,7 +334,7 @@ _set_option :: proc(socket: Any_Socket, option: Socket_Option, value: any, loc :
|
||||
ptr = &int_value
|
||||
len = size_of(int_value)
|
||||
case:
|
||||
unimplemented("set_option() option not yet implemented", loc)
|
||||
return .Invalid_Option
|
||||
}
|
||||
|
||||
real_socket := any_socket_to_socket(socket)
|
||||
@@ -391,7 +381,7 @@ _endpoint_to_sockaddr :: proc(ep: Endpoint) -> (sockaddr: freebsd.Socket_Address
|
||||
}
|
||||
case IP6_Address:
|
||||
(cast(^freebsd.Socket_Address_Internet6)(&sockaddr))^ = {
|
||||
len = size_of(freebsd.Socket_Address_Internet),
|
||||
len = size_of(freebsd.Socket_Address_Internet6),
|
||||
family = .INET6,
|
||||
port = cast(freebsd.in_port_t)ep.port,
|
||||
addr = transmute(freebsd.IP6_Address)addr,
|
||||
|
||||
@@ -21,28 +21,33 @@ package net
|
||||
Feoramund: FreeBSD platform code
|
||||
*/
|
||||
|
||||
import "core:c"
|
||||
import "core:time"
|
||||
import "core:sys/linux"
|
||||
|
||||
Socket_Option :: enum c.int {
|
||||
Reuse_Address = c.int(linux.Socket_Option.REUSEADDR),
|
||||
Keep_Alive = c.int(linux.Socket_Option.KEEPALIVE),
|
||||
Out_Of_Bounds_Data_Inline = c.int(linux.Socket_Option.OOBINLINE),
|
||||
TCP_Nodelay = c.int(linux.Socket_TCP_Option.NODELAY),
|
||||
Linger = c.int(linux.Socket_Option.LINGER),
|
||||
Receive_Buffer_Size = c.int(linux.Socket_Option.RCVBUF),
|
||||
Send_Buffer_Size = c.int(linux.Socket_Option.SNDBUF),
|
||||
Receive_Timeout = c.int(linux.Socket_Option.RCVTIMEO),
|
||||
Send_Timeout = c.int(linux.Socket_Option.SNDTIMEO),
|
||||
Broadcast = c.int(linux.Socket_Option.BROADCAST),
|
||||
}
|
||||
_SOCKET_OPTION_BROADCAST :: linux.Socket_Option.BROADCAST
|
||||
_SOCKET_OPTION_REUSE_ADDRESS :: linux.Socket_Option.REUSEADDR
|
||||
_SOCKET_OPTION_KEEP_ALIVE :: linux.Socket_Option.KEEPALIVE
|
||||
_SOCKET_OPTION_OUT_OF_BOUNDS_DATA_INLINE :: linux.Socket_Option.OOBINLINE
|
||||
_SOCKET_OPTION_LINGER :: linux.Socket_Option.LINGER
|
||||
_SOCKET_OPTION_RECEIVE_BUFFER_SIZE :: linux.Socket_Option.RCVBUF
|
||||
_SOCKET_OPTION_SEND_BUFFER_SIZE :: linux.Socket_Option.SNDBUF
|
||||
_SOCKET_OPTION_RECEIVE_TIMEOUT :: linux.Socket_Option.RCVTIMEO
|
||||
_SOCKET_OPTION_SEND_TIMEOUT :: linux.Socket_Option.SNDTIMEO
|
||||
|
||||
Shutdown_Manner :: enum c.int {
|
||||
Receive = c.int(linux.Shutdown_How.RD),
|
||||
Send = c.int(linux.Shutdown_How.WR),
|
||||
Both = c.int(linux.Shutdown_How.RDWR),
|
||||
}
|
||||
_SOCKET_OPTION_TCP_NODELAY :: linux.Socket_TCP_Option.NODELAY
|
||||
|
||||
_SOCKET_OPTION_USE_LOOPBACK :: -1
|
||||
_SOCKET_OPTION_REUSE_PORT :: -1
|
||||
_SOCKET_OPTION_NO_SIGPIPE_FROM_EPIPE :: -1
|
||||
_SOCKET_OPTION_REUSE_PORT_LOAD_BALANCING :: -1
|
||||
|
||||
_SOCKET_OPTION_EXCLUSIVE_ADDR_USE :: -1
|
||||
_SOCKET_OPTION_CONDITIONAL_ACCEPT :: -1
|
||||
_SOCKET_OPTION_DONT_LINGER :: -1
|
||||
|
||||
_SHUTDOWN_MANNER_RECEIVE :: linux.Shutdown_How.RD
|
||||
_SHUTDOWN_MANNER_SEND :: linux.Shutdown_How.WR
|
||||
_SHUTDOWN_MANNER_BOTH :: linux.Shutdown_How.RDWR
|
||||
|
||||
// Wrappers and unwrappers for system-native types
|
||||
|
||||
@@ -347,7 +352,7 @@ _set_option :: proc(sock: Any_Socket, option: Socket_Option, value: any, loc :=
|
||||
int_value: i32
|
||||
timeval_value: linux.Time_Val
|
||||
errno: linux.Errno
|
||||
switch option {
|
||||
#partial switch option {
|
||||
case
|
||||
.Reuse_Address,
|
||||
.Keep_Alive,
|
||||
@@ -400,10 +405,14 @@ _set_option :: proc(sock: Any_Socket, option: Socket_Option, value: any, loc :=
|
||||
panic("set_option() value must be an integer here", loc)
|
||||
}
|
||||
errno = linux.setsockopt(os_sock, level, int(option), &int_value)
|
||||
case:
|
||||
return .Invalid_Socket
|
||||
}
|
||||
|
||||
if errno != .NONE {
|
||||
return _socket_option_error(errno)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
105
core/net/socket_others.odin
Normal file
105
core/net/socket_others.odin
Normal file
@@ -0,0 +1,105 @@
|
||||
#+build !darwin
|
||||
#+build !linux
|
||||
#+build !freebsd
|
||||
#+build !windows
|
||||
#+build !netbsd
|
||||
#+build !openbsd
|
||||
#+private
|
||||
package net
|
||||
|
||||
_SOCKET_OPTION_BROADCAST :: -1
|
||||
_SOCKET_OPTION_REUSE_ADDRESS :: -1
|
||||
_SOCKET_OPTION_KEEP_ALIVE :: -1
|
||||
_SOCKET_OPTION_OUT_OF_BOUNDS_DATA_INLINE :: -1
|
||||
_SOCKET_OPTION_LINGER :: -1
|
||||
_SOCKET_OPTION_RECEIVE_BUFFER_SIZE :: -1
|
||||
_SOCKET_OPTION_SEND_BUFFER_SIZE :: -1
|
||||
_SOCKET_OPTION_RECEIVE_TIMEOUT :: -1
|
||||
_SOCKET_OPTION_SEND_TIMEOUT :: -1
|
||||
|
||||
_SOCKET_OPTION_TCP_NODELAY :: -1
|
||||
|
||||
_SOCKET_OPTION_USE_LOOPBACK :: -1
|
||||
_SOCKET_OPTION_REUSE_PORT :: -1
|
||||
_SOCKET_OPTION_NO_SIGPIPE_FROM_EPIPE :: -1
|
||||
_SOCKET_OPTION_REUSE_PORT_LOAD_BALANCING :: -1
|
||||
|
||||
_SOCKET_OPTION_EXCLUSIVE_ADDR_USE :: -1
|
||||
_SOCKET_OPTION_CONDITIONAL_ACCEPT :: -1
|
||||
_SOCKET_OPTION_DONT_LINGER :: -1
|
||||
|
||||
_SHUTDOWN_MANNER_RECEIVE :: -1
|
||||
_SHUTDOWN_MANNER_SEND :: -1
|
||||
_SHUTDOWN_MANNER_BOTH :: -1
|
||||
|
||||
_dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := DEFAULT_TCP_OPTIONS) -> (sock: TCP_Socket, err: Network_Error) {
|
||||
err = Create_Socket_Error.Network_Unreachable
|
||||
return
|
||||
}
|
||||
|
||||
_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (sock: Any_Socket, err: Create_Socket_Error) {
|
||||
err = .Network_Unreachable
|
||||
return
|
||||
}
|
||||
|
||||
_bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Bind_Error) {
|
||||
err = .Network_Unreachable
|
||||
return
|
||||
}
|
||||
|
||||
_listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_Socket, err: Network_Error) {
|
||||
err = Create_Socket_Error.Network_Unreachable
|
||||
return
|
||||
}
|
||||
|
||||
_bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Socket_Info_Error) {
|
||||
err = .Network_Unreachable
|
||||
return
|
||||
}
|
||||
|
||||
_peer_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Socket_Info_Error) {
|
||||
err = .Network_Unreachable
|
||||
return
|
||||
}
|
||||
|
||||
_accept_tcp :: proc(sock: TCP_Socket, options := DEFAULT_TCP_OPTIONS) -> (client: TCP_Socket, source: Endpoint, err: Accept_Error) {
|
||||
err = .Network_Unreachable
|
||||
return
|
||||
}
|
||||
|
||||
_close :: proc(skt: Any_Socket) {
|
||||
}
|
||||
|
||||
_recv_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_read: int, err: TCP_Recv_Error) {
|
||||
err = .Network_Unreachable
|
||||
return
|
||||
}
|
||||
|
||||
_recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: UDP_Recv_Error) {
|
||||
err = .Network_Unreachable
|
||||
return
|
||||
}
|
||||
|
||||
_send_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_written: int, err: TCP_Send_Error) {
|
||||
err = .Network_Unreachable
|
||||
return
|
||||
}
|
||||
|
||||
_send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: UDP_Send_Error) {
|
||||
err = .Network_Unreachable
|
||||
return
|
||||
}
|
||||
|
||||
_shutdown :: proc(skt: Any_Socket, manner: Shutdown_Manner) -> (err: Shutdown_Error) {
|
||||
err = .Network_Unreachable
|
||||
return
|
||||
}
|
||||
|
||||
_set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Socket_Option_Error {
|
||||
return .Network_Unreachable
|
||||
}
|
||||
|
||||
_set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Set_Blocking_Error) {
|
||||
err = .Network_Unreachable
|
||||
return
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#+build darwin
|
||||
#+build darwin, netbsd, openbsd
|
||||
package net
|
||||
|
||||
/*
|
||||
@@ -20,28 +20,33 @@ package net
|
||||
Feoramund: FreeBSD platform code
|
||||
*/
|
||||
|
||||
import "core:c"
|
||||
import "core:sys/posix"
|
||||
import "core:time"
|
||||
|
||||
Socket_Option :: enum c.int {
|
||||
Broadcast = c.int(posix.Sock_Option.BROADCAST),
|
||||
Reuse_Address = c.int(posix.Sock_Option.REUSEADDR),
|
||||
Keep_Alive = c.int(posix.Sock_Option.KEEPALIVE),
|
||||
Out_Of_Bounds_Data_Inline = c.int(posix.Sock_Option.OOBINLINE),
|
||||
TCP_Nodelay = c.int(posix.TCP_NODELAY),
|
||||
Linger = c.int(posix.Sock_Option.LINGER),
|
||||
Receive_Buffer_Size = c.int(posix.Sock_Option.RCVBUF),
|
||||
Send_Buffer_Size = c.int(posix.Sock_Option.SNDBUF),
|
||||
Receive_Timeout = c.int(posix.Sock_Option.RCVTIMEO),
|
||||
Send_Timeout = c.int(posix.Sock_Option.SNDTIMEO),
|
||||
}
|
||||
_SOCKET_OPTION_BROADCAST :: posix.Sock_Option.BROADCAST
|
||||
_SOCKET_OPTION_REUSE_ADDRESS :: posix.Sock_Option.REUSEADDR
|
||||
_SOCKET_OPTION_KEEP_ALIVE :: posix.Sock_Option.KEEPALIVE
|
||||
_SOCKET_OPTION_OUT_OF_BOUNDS_DATA_INLINE :: posix.Sock_Option.OOBINLINE
|
||||
_SOCKET_OPTION_LINGER :: posix.Sock_Option.LINGER
|
||||
_SOCKET_OPTION_RECEIVE_BUFFER_SIZE :: posix.Sock_Option.RCVBUF
|
||||
_SOCKET_OPTION_SEND_BUFFER_SIZE :: posix.Sock_Option.SNDBUF
|
||||
_SOCKET_OPTION_RECEIVE_TIMEOUT :: posix.Sock_Option.RCVTIMEO
|
||||
_SOCKET_OPTION_SEND_TIMEOUT :: posix.Sock_Option.SNDTIMEO
|
||||
|
||||
Shutdown_Manner :: enum c.int {
|
||||
Receive = c.int(posix.SHUT_RD),
|
||||
Send = c.int(posix.SHUT_WR),
|
||||
Both = c.int(posix.SHUT_RDWR),
|
||||
}
|
||||
_SOCKET_OPTION_TCP_NODELAY :: posix.TCP_NODELAY
|
||||
|
||||
_SOCKET_OPTION_USE_LOOPBACK :: -1
|
||||
_SOCKET_OPTION_REUSE_PORT :: -1
|
||||
_SOCKET_OPTION_NO_SIGPIPE_FROM_EPIPE :: -1
|
||||
_SOCKET_OPTION_REUSE_PORT_LOAD_BALANCING :: -1
|
||||
|
||||
_SOCKET_OPTION_EXCLUSIVE_ADDR_USE :: -1
|
||||
_SOCKET_OPTION_CONDITIONAL_ACCEPT :: -1
|
||||
_SOCKET_OPTION_DONT_LINGER :: -1
|
||||
|
||||
_SHUTDOWN_MANNER_RECEIVE :: posix.SHUT_RD
|
||||
_SHUTDOWN_MANNER_SEND :: posix.SHUT_WR
|
||||
_SHUTDOWN_MANNER_BOTH :: posix.SHUT_RDWR
|
||||
|
||||
@(private)
|
||||
_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Create_Socket_Error) {
|
||||
@@ -273,7 +278,7 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
|
||||
ptr: rawptr
|
||||
len: posix.socklen_t
|
||||
|
||||
switch option {
|
||||
#partial switch option {
|
||||
case
|
||||
.Broadcast,
|
||||
.Reuse_Address,
|
||||
@@ -327,6 +332,8 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
|
||||
}
|
||||
ptr = &int_value
|
||||
len = size_of(int_value)
|
||||
case:
|
||||
return .Invalid_Option
|
||||
}
|
||||
|
||||
skt := any_socket_to_socket(s)
|
||||
@@ -24,59 +24,30 @@ import "core:c"
|
||||
import win "core:sys/windows"
|
||||
import "core:time"
|
||||
|
||||
Socket_Option :: enum c.int {
|
||||
// bool: Whether the address that this socket is bound to can be reused by other sockets.
|
||||
// This allows you to bypass the cooldown period if a program dies while the socket is bound.
|
||||
Reuse_Address = win.SO_REUSEADDR,
|
||||
_SOCKET_OPTION_BROADCAST :: win.SO_BROADCAST
|
||||
_SOCKET_OPTION_REUSE_ADDRESS :: win.SO_REUSEADDR
|
||||
_SOCKET_OPTION_KEEP_ALIVE :: win.SO_KEEPALIVE
|
||||
_SOCKET_OPTION_OUT_OF_BOUNDS_DATA_INLINE :: win.SO_OOBINLINE
|
||||
_SOCKET_OPTION_LINGER :: win.SO_LINGER
|
||||
_SOCKET_OPTION_RECEIVE_BUFFER_SIZE :: win.SO_RCVBUF
|
||||
_SOCKET_OPTION_SEND_BUFFER_SIZE :: win.SO_SNDBUF
|
||||
_SOCKET_OPTION_RECEIVE_TIMEOUT :: win.SO_RCVTIMEO
|
||||
_SOCKET_OPTION_SEND_TIMEOUT :: win.SO_SNDTIMEO
|
||||
|
||||
// bool: Whether other programs will be inhibited from binding the same endpoint as this socket.
|
||||
Exclusive_Addr_Use = win.SO_EXCLUSIVEADDRUSE,
|
||||
_SOCKET_OPTION_TCP_NODELAY :: win.TCP_NODELAY
|
||||
|
||||
// bool: When true, keepalive packets will be automatically be sent for this connection. TODO: verify this understanding
|
||||
Keep_Alive = win.SO_KEEPALIVE,
|
||||
_SOCKET_OPTION_USE_LOOPBACK :: -1
|
||||
_SOCKET_OPTION_REUSE_PORT :: -1
|
||||
_SOCKET_OPTION_NO_SIGPIPE_FROM_EPIPE :: -1
|
||||
_SOCKET_OPTION_REUSE_PORT_LOAD_BALANCING :: -1
|
||||
|
||||
// bool: When true, client connections will immediately be sent a TCP/IP RST response, rather than being accepted.
|
||||
Conditional_Accept = win.SO_CONDITIONAL_ACCEPT,
|
||||
_SOCKET_OPTION_EXCLUSIVE_ADDR_USE :: win.SO_EXCLUSIVEADDRUSE
|
||||
_SOCKET_OPTION_CONDITIONAL_ACCEPT :: win.SO_CONDITIONAL_ACCEPT
|
||||
_SOCKET_OPTION_DONT_LINGER :: win.SO_DONTLINGER
|
||||
|
||||
// bool: If true, when the socket is closed, but data is still waiting to be sent, discard that data.
|
||||
Dont_Linger = win.SO_DONTLINGER,
|
||||
|
||||
// bool: When true, 'out-of-band' data sent over the socket will be read by a normal net.recv() call, the same as normal 'in-band' data.
|
||||
Out_Of_Bounds_Data_Inline = win.SO_OOBINLINE,
|
||||
|
||||
// bool: When true, disables send-coalescing, therefore reducing latency.
|
||||
TCP_Nodelay = win.TCP_NODELAY,
|
||||
|
||||
// win.LINGER: Customizes how long (if at all) the socket will remain open when there
|
||||
// is some remaining data waiting to be sent, and net.close() is called.
|
||||
Linger = win.SO_LINGER,
|
||||
|
||||
// win.DWORD: The size, in bytes, of the OS-managed receive-buffer for this socket.
|
||||
Receive_Buffer_Size = win.SO_RCVBUF,
|
||||
|
||||
// win.DWORD: The size, in bytes, of the OS-managed send-buffer for this socket.
|
||||
Send_Buffer_Size = win.SO_SNDBUF,
|
||||
|
||||
// win.DWORD: For blocking sockets, the time in milliseconds to wait for incoming data to be received, before giving up and returning .Timeout.
|
||||
// For non-blocking sockets, ignored.
|
||||
// Use a value of zero to potentially wait forever.
|
||||
Receive_Timeout = win.SO_RCVTIMEO,
|
||||
|
||||
// win.DWORD: For blocking sockets, the time in milliseconds to wait for outgoing data to be sent, before giving up and returning .Timeout.
|
||||
// For non-blocking sockets, ignored.
|
||||
// Use a value of zero to potentially wait forever.
|
||||
Send_Timeout = win.SO_SNDTIMEO,
|
||||
|
||||
// bool: Allow sending to, receiving from, and binding to, a broadcast address.
|
||||
Broadcast = win.SO_BROADCAST,
|
||||
}
|
||||
|
||||
|
||||
Shutdown_Manner :: enum c.int {
|
||||
Receive = win.SD_RECEIVE,
|
||||
Send = win.SD_SEND,
|
||||
Both = win.SD_BOTH,
|
||||
}
|
||||
_SHUTDOWN_MANNER_RECEIVE :: win.SD_RECEIVE
|
||||
_SHUTDOWN_MANNER_SEND :: win.SD_SEND
|
||||
_SHUTDOWN_MANNER_BOTH :: win.SD_BOTH
|
||||
|
||||
@(init, private)
|
||||
ensure_winsock_initialized :: proc "contextless" () {
|
||||
@@ -322,7 +293,7 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
|
||||
ptr: rawptr
|
||||
len: c.int
|
||||
|
||||
switch option {
|
||||
#partial switch option {
|
||||
case
|
||||
.Reuse_Address,
|
||||
.Exclusive_Addr_Use,
|
||||
@@ -383,6 +354,8 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
|
||||
}
|
||||
ptr = &int_value
|
||||
len = size_of(int_value)
|
||||
case:
|
||||
return .Invalid_Option
|
||||
}
|
||||
|
||||
socket := any_socket_to_socket(s)
|
||||
|
||||
@@ -17,6 +17,11 @@ Proc_Inlining :: enum u32 {
|
||||
No_Inline = 2,
|
||||
}
|
||||
|
||||
Proc_Tailing :: enum u32 {
|
||||
None = 0,
|
||||
Must_Tail = 1,
|
||||
}
|
||||
|
||||
Proc_Calling_Convention_Extra :: enum i32 {
|
||||
Foreign_Block_Default,
|
||||
}
|
||||
@@ -147,6 +152,7 @@ Proc_Lit :: struct {
|
||||
body: ^Stmt, // nil when it represents a foreign procedure
|
||||
tags: Proc_Tags,
|
||||
inlining: Proc_Inlining,
|
||||
tailing: Proc_Tailing,
|
||||
where_token: tokenizer.Token,
|
||||
where_clauses: []^Expr,
|
||||
}
|
||||
@@ -243,6 +249,7 @@ Matrix_Index_Expr :: struct {
|
||||
Call_Expr :: struct {
|
||||
using node: Expr,
|
||||
inlining: Proc_Inlining,
|
||||
tailing: Proc_Tailing,
|
||||
expr: ^Expr,
|
||||
open: tokenizer.Pos,
|
||||
args: []^Expr,
|
||||
@@ -791,6 +798,7 @@ Struct_Type :: struct {
|
||||
is_raw_union: bool,
|
||||
is_no_copy: bool,
|
||||
is_all_or_none: bool,
|
||||
is_simple: bool,
|
||||
fields: ^Field_List,
|
||||
name_count: int,
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user