From f5c4671bfbad96bf346bd7e9a21fc4317b4959df Mon Sep 17 00:00:00 2001 From: Indrajith K L Date: Sat, 3 Dec 2022 17:00:20 +0530 Subject: Adds most of the tools --- v_windows/v/vlib/builtin/array.c.v | 1 + v_windows/v/vlib/builtin/array.v | 644 ++++++++ v_windows/v/vlib/builtin/array_d_gcboehm_opt.v | 267 ++++ v_windows/v/vlib/builtin/array_notd_gcboehm_opt.v | 10 + v_windows/v/vlib/builtin/array_test.v | 1507 ++++++++++++++++++ v_windows/v/vlib/builtin/builtin.c.v | 547 +++++++ v_windows/v/vlib/builtin/builtin.v | 128 ++ v_windows/v/vlib/builtin/builtin_d_gcboehm.c.v | 91 ++ v_windows/v/vlib/builtin/builtin_ios.c.v | 6 + v_windows/v/vlib/builtin/builtin_nix.c.v | 144 ++ v_windows/v/vlib/builtin/builtin_notd_gcboehm.c.v | 20 + v_windows/v/vlib/builtin/builtin_windows.c.v | 304 ++++ v_windows/v/vlib/builtin/byte_test.v | 22 + v_windows/v/vlib/builtin/cfns.c.v | 462 ++++++ v_windows/v/vlib/builtin/cfns_wrapper.c.v | 72 + v_windows/v/vlib/builtin/chan.v | 32 + v_windows/v/vlib/builtin/float.c.v | 205 +++ v_windows/v/vlib/builtin/float_test.v | 147 ++ v_windows/v/vlib/builtin/float_x64.v | 6 + v_windows/v/vlib/builtin/int.v | 481 ++++++ v_windows/v/vlib/builtin/int_test.v | 241 +++ v_windows/v/vlib/builtin/isnil_test.v | 19 + v_windows/v/vlib/builtin/js/array.js.v | 253 +++ v_windows/v/vlib/builtin/js/builtin.js.v | 61 + v_windows/v/vlib/builtin/js/builtin.v | 84 + v_windows/v/vlib/builtin/js/byte.js.v | 16 + v_windows/v/vlib/builtin/js/int.js.v | 8 + v_windows/v/vlib/builtin/js/jsfns.js.v | 125 ++ v_windows/v/vlib/builtin/js/jsfns_browser.js.v | 59 + v_windows/v/vlib/builtin/js/jsfns_node.js.v | 31 + v_windows/v/vlib/builtin/js/map.js.v | 26 + v_windows/v/vlib/builtin/js/string.js.v | 720 +++++++++ v_windows/v/vlib/builtin/linux_bare/libc_impl.v | 162 ++ .../v/vlib/builtin/linux_bare/linux_syscalls.v | 433 ++++++ .../v/vlib/builtin/linux_bare/memory_managment.v | 29 + .../vlib/builtin/linux_bare/old/.checks/.gitignore | 5 + .../v/vlib/builtin/linux_bare/old/.checks/checks.v | 32 + .../builtin/linux_bare/old/.checks/consts/consts.v | 22 + .../linux_bare/old/.checks/forkedtest/forkedtest.v | 49 + .../linux_bare/old/.checks/linuxsys/linuxsys.v | 300 ++++ .../vlib/builtin/linux_bare/old/.checks/readme.md | 5 + .../linux_bare/old/.checks/sample_text1.txt | 1 + .../builtin/linux_bare/old/.checks/string/string.v | 63 + .../linux_bare/old/.checks/structs/structs.v | 42 + .../v/vlib/builtin/linux_bare/old/array_bare.v | 53 + .../v/vlib/builtin/linux_bare/old/builtin_bare.v | 60 + .../v/vlib/builtin/linux_bare/old/linuxsys_bare.v | 759 +++++++++ v_windows/v/vlib/builtin/linux_bare/old/mm_bare.v | 58 + .../v/vlib/builtin/linux_bare/old/string_bare.v | 150 ++ .../builtin/linux_bare/old/syscallwrapper_test.v | 27 + v_windows/v/vlib/builtin/map.c.v | 79 + v_windows/v/vlib/builtin/map.v | 716 +++++++++ v_windows/v/vlib/builtin/map_d_gcboehm_opt.v | 146 ++ v_windows/v/vlib/builtin/map_of_floats_test.v | 27 + v_windows/v/vlib/builtin/map_test.v | 947 ++++++++++++ v_windows/v/vlib/builtin/option.c.v | 15 + v_windows/v/vlib/builtin/option.v | 89 ++ v_windows/v/vlib/builtin/prealloc.c.v | 114 ++ v_windows/v/vlib/builtin/rune.v | 65 + v_windows/v/vlib/builtin/sorted_map.v | 457 ++++++ v_windows/v/vlib/builtin/sorting_test.v | 84 + v_windows/v/vlib/builtin/string.v | 1604 ++++++++++++++++++++ .../vlib/builtin/string_charptr_byteptr_helpers.v | 104 ++ v_windows/v/vlib/builtin/string_int_test.v | 221 +++ v_windows/v/vlib/builtin/string_interpolation.v | 713 +++++++++ .../v/vlib/builtin/string_strip_margin_test.v | 95 ++ v_windows/v/vlib/builtin/string_test.v | 912 +++++++++++ v_windows/v/vlib/builtin/utf8.c.v | 79 + v_windows/v/vlib/builtin/utf8.v | 191 +++ v_windows/v/vlib/builtin/utf8_test.v | 28 + 70 files changed, 15675 insertions(+) create mode 100644 v_windows/v/vlib/builtin/array.c.v create mode 100644 v_windows/v/vlib/builtin/array.v create mode 100644 v_windows/v/vlib/builtin/array_d_gcboehm_opt.v create mode 100644 v_windows/v/vlib/builtin/array_notd_gcboehm_opt.v create mode 100644 v_windows/v/vlib/builtin/array_test.v create mode 100644 v_windows/v/vlib/builtin/builtin.c.v create mode 100644 v_windows/v/vlib/builtin/builtin.v create mode 100644 v_windows/v/vlib/builtin/builtin_d_gcboehm.c.v create mode 100644 v_windows/v/vlib/builtin/builtin_ios.c.v create mode 100644 v_windows/v/vlib/builtin/builtin_nix.c.v create mode 100644 v_windows/v/vlib/builtin/builtin_notd_gcboehm.c.v create mode 100644 v_windows/v/vlib/builtin/builtin_windows.c.v create mode 100644 v_windows/v/vlib/builtin/byte_test.v create mode 100644 v_windows/v/vlib/builtin/cfns.c.v create mode 100644 v_windows/v/vlib/builtin/cfns_wrapper.c.v create mode 100644 v_windows/v/vlib/builtin/chan.v create mode 100644 v_windows/v/vlib/builtin/float.c.v create mode 100644 v_windows/v/vlib/builtin/float_test.v create mode 100644 v_windows/v/vlib/builtin/float_x64.v create mode 100644 v_windows/v/vlib/builtin/int.v create mode 100644 v_windows/v/vlib/builtin/int_test.v create mode 100644 v_windows/v/vlib/builtin/isnil_test.v create mode 100644 v_windows/v/vlib/builtin/js/array.js.v create mode 100644 v_windows/v/vlib/builtin/js/builtin.js.v create mode 100644 v_windows/v/vlib/builtin/js/builtin.v create mode 100644 v_windows/v/vlib/builtin/js/byte.js.v create mode 100644 v_windows/v/vlib/builtin/js/int.js.v create mode 100644 v_windows/v/vlib/builtin/js/jsfns.js.v create mode 100644 v_windows/v/vlib/builtin/js/jsfns_browser.js.v create mode 100644 v_windows/v/vlib/builtin/js/jsfns_node.js.v create mode 100644 v_windows/v/vlib/builtin/js/map.js.v create mode 100644 v_windows/v/vlib/builtin/js/string.js.v create mode 100644 v_windows/v/vlib/builtin/linux_bare/libc_impl.v create mode 100644 v_windows/v/vlib/builtin/linux_bare/linux_syscalls.v create mode 100644 v_windows/v/vlib/builtin/linux_bare/memory_managment.v create mode 100644 v_windows/v/vlib/builtin/linux_bare/old/.checks/.gitignore create mode 100644 v_windows/v/vlib/builtin/linux_bare/old/.checks/checks.v create mode 100644 v_windows/v/vlib/builtin/linux_bare/old/.checks/consts/consts.v create mode 100644 v_windows/v/vlib/builtin/linux_bare/old/.checks/forkedtest/forkedtest.v create mode 100644 v_windows/v/vlib/builtin/linux_bare/old/.checks/linuxsys/linuxsys.v create mode 100644 v_windows/v/vlib/builtin/linux_bare/old/.checks/readme.md create mode 100644 v_windows/v/vlib/builtin/linux_bare/old/.checks/sample_text1.txt create mode 100644 v_windows/v/vlib/builtin/linux_bare/old/.checks/string/string.v create mode 100644 v_windows/v/vlib/builtin/linux_bare/old/.checks/structs/structs.v create mode 100644 v_windows/v/vlib/builtin/linux_bare/old/array_bare.v create mode 100644 v_windows/v/vlib/builtin/linux_bare/old/builtin_bare.v create mode 100644 v_windows/v/vlib/builtin/linux_bare/old/linuxsys_bare.v create mode 100644 v_windows/v/vlib/builtin/linux_bare/old/mm_bare.v create mode 100644 v_windows/v/vlib/builtin/linux_bare/old/string_bare.v create mode 100644 v_windows/v/vlib/builtin/linux_bare/old/syscallwrapper_test.v create mode 100644 v_windows/v/vlib/builtin/map.c.v create mode 100644 v_windows/v/vlib/builtin/map.v create mode 100644 v_windows/v/vlib/builtin/map_d_gcboehm_opt.v create mode 100644 v_windows/v/vlib/builtin/map_of_floats_test.v create mode 100644 v_windows/v/vlib/builtin/map_test.v create mode 100644 v_windows/v/vlib/builtin/option.c.v create mode 100644 v_windows/v/vlib/builtin/option.v create mode 100644 v_windows/v/vlib/builtin/prealloc.c.v create mode 100644 v_windows/v/vlib/builtin/rune.v create mode 100644 v_windows/v/vlib/builtin/sorted_map.v create mode 100644 v_windows/v/vlib/builtin/sorting_test.v create mode 100644 v_windows/v/vlib/builtin/string.v create mode 100644 v_windows/v/vlib/builtin/string_charptr_byteptr_helpers.v create mode 100644 v_windows/v/vlib/builtin/string_int_test.v create mode 100644 v_windows/v/vlib/builtin/string_interpolation.v create mode 100644 v_windows/v/vlib/builtin/string_strip_margin_test.v create mode 100644 v_windows/v/vlib/builtin/string_test.v create mode 100644 v_windows/v/vlib/builtin/utf8.c.v create mode 100644 v_windows/v/vlib/builtin/utf8.v create mode 100644 v_windows/v/vlib/builtin/utf8_test.v (limited to 'v_windows/v/vlib/builtin') diff --git a/v_windows/v/vlib/builtin/array.c.v b/v_windows/v/vlib/builtin/array.c.v new file mode 100644 index 0000000..b15fe4b --- /dev/null +++ b/v_windows/v/vlib/builtin/array.c.v @@ -0,0 +1 @@ +module builtin diff --git a/v_windows/v/vlib/builtin/array.v b/v_windows/v/vlib/builtin/array.v new file mode 100644 index 0000000..0a6f4e0 --- /dev/null +++ b/v_windows/v/vlib/builtin/array.v @@ -0,0 +1,644 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module builtin + +import strings + +// array is a struct used for denoting array types in V +pub struct array { +pub: + element_size int // size in bytes of one element in the array. +pub mut: + data voidptr + offset int // in bytes (should be `size_t`) + len int // length of the array. + cap int // capacity of the array. +} + +// array.data uses a void pointer, which allows implementing arrays without generics and without generating +// extra code for every type. +// Internal function, used by V (`nums := []int`) +fn __new_array(mylen int, cap int, elm_size int) array { + cap_ := if cap < mylen { mylen } else { cap } + arr := array{ + element_size: elm_size + data: vcalloc(cap_ * elm_size) + len: mylen + cap: cap_ + } + return arr +} + +fn __new_array_with_default(mylen int, cap int, elm_size int, val voidptr) array { + cap_ := if cap < mylen { mylen } else { cap } + mut arr := array{ + element_size: elm_size + data: vcalloc(cap_ * elm_size) + len: mylen + cap: cap_ + } + if val != 0 { + for i in 0 .. arr.len { + unsafe { arr.set_unsafe(i, val) } + } + } + return arr +} + +fn __new_array_with_array_default(mylen int, cap int, elm_size int, val array) array { + cap_ := if cap < mylen { mylen } else { cap } + mut arr := array{ + element_size: elm_size + data: vcalloc(cap_ * elm_size) + len: mylen + cap: cap_ + } + for i in 0 .. arr.len { + val_clone := unsafe { val.clone_to_depth(1) } + unsafe { arr.set_unsafe(i, &val_clone) } + } + return arr +} + +// Private function, used by V (`nums := [1, 2, 3]`) +fn new_array_from_c_array(len int, cap int, elm_size int, c_array voidptr) array { + cap_ := if cap < len { len } else { cap } + arr := array{ + element_size: elm_size + data: vcalloc(cap_ * elm_size) + len: len + cap: cap_ + } + // TODO Write all memory functions (like memcpy) in V + unsafe { vmemcpy(arr.data, c_array, len * elm_size) } + return arr +} + +// Private function, used by V (`nums := [1, 2, 3] !`) +fn new_array_from_c_array_no_alloc(len int, cap int, elm_size int, c_array voidptr) array { + arr := array{ + element_size: elm_size + data: c_array + len: len + cap: cap + } + return arr +} + +// Private function. Doubles array capacity if needed. +fn (mut a array) ensure_cap(required int) { + if required <= a.cap { + return + } + mut cap := if a.cap > 0 { a.cap } else { 2 } + for required > cap { + cap *= 2 + } + new_size := cap * a.element_size + new_data := vcalloc(new_size) + if a.data != voidptr(0) { + unsafe { vmemcpy(new_data, a.data, a.len * a.element_size) } + // TODO: the old data may be leaked when no GC is used (ref-counting?) + } + a.data = new_data + a.offset = 0 + a.cap = cap +} + +// repeat returns a new array with the given array elements repeated given times. +// `cgen` will replace this with an apropriate call to `repeat_to_depth()` + +// This is a dummy placeholder that will be overridden by `cgen` with an appropriate +// call to `repeat_to_depth()`. However the `checker` needs it here. +pub fn (a array) repeat(count int) array { + return unsafe { a.repeat_to_depth(count, 0) } +} + +// version of `repeat()` that handles multi dimensional arrays +// `unsafe` to call directly because `depth` is not checked +[unsafe] +pub fn (a array) repeat_to_depth(count int, depth int) array { + if count < 0 { + panic('array.repeat: count is negative: $count') + } + mut size := count * a.len * a.element_size + if size == 0 { + size = a.element_size + } + arr := array{ + element_size: a.element_size + data: vcalloc(size) + len: count * a.len + cap: count * a.len + } + if a.len > 0 { + for i in 0 .. count { + if depth > 0 { + ary_clone := unsafe { a.clone_to_depth(depth) } + unsafe { vmemcpy(arr.get_unsafe(i * a.len), &byte(ary_clone.data), a.len * a.element_size) } + } else { + unsafe { vmemcpy(arr.get_unsafe(i * a.len), &byte(a.data), a.len * a.element_size) } + } + } + } + return arr +} + +// sort_with_compare sorts array in-place using given `compare` function as comparator. +pub fn (mut a array) sort_with_compare(compare voidptr) { + $if freestanding { + panic('sort does not work with -freestanding') + } $else { + unsafe { vqsort(a.data, size_t(a.len), size_t(a.element_size), compare) } + } +} + +// insert inserts a value in the array at index `i` +pub fn (mut a array) insert(i int, val voidptr) { + $if !no_bounds_checking ? { + if i < 0 || i > a.len { + panic('array.insert: index out of range (i == $i, a.len == $a.len)') + } + } + a.ensure_cap(a.len + 1) + unsafe { + vmemmove(a.get_unsafe(i + 1), a.get_unsafe(i), (a.len - i) * a.element_size) + a.set_unsafe(i, val) + } + a.len++ +} + +// insert_many inserts many values into the array from index `i`. +[unsafe] +pub fn (mut a array) insert_many(i int, val voidptr, size int) { + $if !no_bounds_checking ? { + if i < 0 || i > a.len { + panic('array.insert_many: index out of range (i == $i, a.len == $a.len)') + } + } + a.ensure_cap(a.len + size) + elem_size := a.element_size + unsafe { + iptr := a.get_unsafe(i) + vmemmove(a.get_unsafe(i + size), iptr, (a.len - i) * elem_size) + vmemcpy(iptr, val, size * elem_size) + } + a.len += size +} + +// prepend prepends one value to the array. +pub fn (mut a array) prepend(val voidptr) { + a.insert(0, val) +} + +// prepend_many prepends another array to this array. +[unsafe] +pub fn (mut a array) prepend_many(val voidptr, size int) { + unsafe { a.insert_many(0, val, size) } +} + +// delete deletes array element at index `i`. +pub fn (mut a array) delete(i int) { + a.delete_many(i, 1) +} + +// delete_many deletes `size` elements beginning with index `i` +pub fn (mut a array) delete_many(i int, size int) { + $if !no_bounds_checking ? { + if i < 0 || i + size > a.len { + endidx := if size > 1 { '..${i + size}' } else { '' } + panic('array.delete: index out of range (i == $i$endidx, a.len == $a.len)') + } + } + // NB: if a is [12,34], a.len = 2, a.delete(0) + // should move (2-0-1) elements = 1 element (the 34) forward + old_data := a.data + new_size := a.len - size + new_cap := if new_size == 0 { 1 } else { new_size } + a.data = vcalloc(new_cap * a.element_size) + unsafe { vmemcpy(a.data, old_data, i * a.element_size) } + unsafe { + vmemcpy(&byte(a.data) + i * a.element_size, &byte(old_data) + (i + size) * a.element_size, + (a.len - i - size) * a.element_size) + } + a.len = new_size + a.cap = new_cap +} + +// clear clears the array without deallocating the allocated data. +pub fn (mut a array) clear() { + a.len = 0 +} + +// trim trims the array length to "index" without modifying the allocated data. If "index" is greater +// than len nothing will be changed. +pub fn (mut a array) trim(index int) { + if index < a.len { + a.len = index + } +} + +// we manually inline this for single operations for performance without -prod +[inline; unsafe] +fn (a array) get_unsafe(i int) voidptr { + unsafe { + return &byte(a.data) + i * a.element_size + } +} + +// Private function. Used to implement array[] operator. +fn (a array) get(i int) voidptr { + $if !no_bounds_checking ? { + if i < 0 || i >= a.len { + panic('array.get: index out of range (i == $i, a.len == $a.len)') + } + } + unsafe { + return &byte(a.data) + i * a.element_size + } +} + +// Private function. Used to implement x = a[i] or { ... } +fn (a array) get_with_check(i int) voidptr { + if i < 0 || i >= a.len { + return 0 + } + unsafe { + return &byte(a.data) + i * a.element_size + } +} + +// first returns the first element of the array. +pub fn (a array) first() voidptr { + $if !no_bounds_checking ? { + if a.len == 0 { + panic('array.first: array is empty') + } + } + return a.data +} + +// last returns the last element of the array. +pub fn (a array) last() voidptr { + $if !no_bounds_checking ? { + if a.len == 0 { + panic('array.last: array is empty') + } + } + unsafe { + return &byte(a.data) + (a.len - 1) * a.element_size + } +} + +// pop returns the last element of the array, and removes it. +pub fn (mut a array) pop() voidptr { + // in a sense, this is the opposite of `a << x` + $if !no_bounds_checking ? { + if a.len == 0 { + panic('array.pop: array is empty') + } + } + new_len := a.len - 1 + last_elem := unsafe { &byte(a.data) + new_len * a.element_size } + a.len = new_len + // NB: a.cap is not changed here *on purpose*, so that + // further << ops on that array will be more efficient. + return unsafe { memdup(last_elem, a.element_size) } +} + +// delete_last efficiently deletes the last element of the array. +pub fn (mut a array) delete_last() { + // copy pasting code for performance + $if !no_bounds_checking ? { + if a.len == 0 { + panic('array.pop: array is empty') + } + } + a.len-- +} + +// slice returns an array using the same buffer as original array +// but starting from the `start` element and ending with the element before +// the `end` element of the original array with the length and capacity +// set to the number of the elements in the slice. +fn (a array) slice(start int, _end int) array { + mut end := _end + $if !no_bounds_checking ? { + if start > end { + panic('array.slice: invalid slice index ($start > $end)') + } + if end > a.len { + panic('array.slice: slice bounds out of range ($end >= $a.len)') + } + if start < 0 { + panic('array.slice: slice bounds out of range ($start < 0)') + } + } + offset := start * a.element_size + data := unsafe { &byte(a.data) + offset } + l := end - start + res := array{ + element_size: a.element_size + data: data + offset: a.offset + offset + len: l + cap: l + } + return res +} + +// used internally for [2..4] +fn (a array) slice2(start int, _end int, end_max bool) array { + end := if end_max { a.len } else { _end } + return a.slice(start, end) +} + +// `clone_static_to_depth()` returns an independent copy of a given array. +// Unlike `clone_to_depth()` it has a value receiver and is used internally +// for slice-clone expressions like `a[2..4].clone()` and in -autofree generated code. +fn (a array) clone_static_to_depth(depth int) array { + return unsafe { a.clone_to_depth(depth) } +} + +// clone returns an independent copy of a given array. +// this will be overwritten by `cgen` with an apropriate call to `.clone_to_depth()` +// However the `checker` needs it here. +pub fn (a &array) clone() array { + return unsafe { a.clone_to_depth(0) } +} + +// recursively clone given array - `unsafe` when called directly because depth is not checked +[unsafe] +pub fn (a &array) clone_to_depth(depth int) array { + mut size := a.cap * a.element_size + if size == 0 { + size++ + } + mut arr := array{ + element_size: a.element_size + data: vcalloc(size) + len: a.len + cap: a.cap + } + // Recursively clone-generated elements if array element is array type + if depth > 0 && a.element_size == sizeof(array) && a.len >= 0 && a.cap >= a.len { + for i in 0 .. a.len { + ar := array{} + unsafe { vmemcpy(&ar, a.get_unsafe(i), int(sizeof(array))) } + ar_clone := unsafe { ar.clone_to_depth(depth - 1) } + unsafe { arr.set_unsafe(i, &ar_clone) } + } + return arr + } else { + if !isnil(a.data) { + unsafe { vmemcpy(&byte(arr.data), a.data, a.cap * a.element_size) } + } + return arr + } +} + +// we manually inline this for single operations for performance without -prod +[inline; unsafe] +fn (mut a array) set_unsafe(i int, val voidptr) { + unsafe { vmemcpy(&byte(a.data) + a.element_size * i, val, a.element_size) } +} + +// Private function. Used to implement assigment to the array element. +fn (mut a array) set(i int, val voidptr) { + $if !no_bounds_checking ? { + if i < 0 || i >= a.len { + panic('array.set: index out of range (i == $i, a.len == $a.len)') + } + } + unsafe { vmemcpy(&byte(a.data) + a.element_size * i, val, a.element_size) } +} + +fn (mut a array) push(val voidptr) { + a.ensure_cap(a.len + 1) + unsafe { vmemmove(&byte(a.data) + a.element_size * a.len, val, a.element_size) } + a.len++ +} + +// push_many implements the functionality for pushing another array. +// `val` is array.data and user facing usage is `a << [1,2,3]` +[unsafe] +pub fn (mut a3 array) push_many(val voidptr, size int) { + if a3.data == val && !isnil(a3.data) { + // handle `arr << arr` + copy := a3.clone() + a3.ensure_cap(a3.len + size) + unsafe { + // vmemcpy(a.data, copy.data, copy.element_size * copy.len) + vmemcpy(a3.get_unsafe(a3.len), copy.data, a3.element_size * size) + } + } else { + a3.ensure_cap(a3.len + size) + if !isnil(a3.data) && !isnil(val) { + unsafe { vmemcpy(a3.get_unsafe(a3.len), val, a3.element_size * size) } + } + } + a3.len += size +} + +// reverse_in_place reverses existing array data, modifying original array. +pub fn (mut a array) reverse_in_place() { + if a.len < 2 { + return + } + unsafe { + mut tmp_value := malloc(a.element_size) + for i in 0 .. a.len / 2 { + vmemcpy(tmp_value, &byte(a.data) + i * a.element_size, a.element_size) + vmemcpy(&byte(a.data) + i * a.element_size, &byte(a.data) + + (a.len - 1 - i) * a.element_size, a.element_size) + vmemcpy(&byte(a.data) + (a.len - 1 - i) * a.element_size, tmp_value, a.element_size) + } + free(tmp_value) + } +} + +// reverse returns a new array with the elements of the original array in reverse order. +pub fn (a array) reverse() array { + if a.len < 2 { + return a + } + mut arr := array{ + element_size: a.element_size + data: vcalloc(a.cap * a.element_size) + len: a.len + cap: a.cap + } + for i in 0 .. a.len { + unsafe { arr.set_unsafe(i, a.get_unsafe(a.len - 1 - i)) } + } + return arr +} + +// pub fn (a []int) free() { +// free frees all memory occupied by the array. +[unsafe] +pub fn (a &array) free() { + $if prealloc { + return + } + // if a.is_slice { + // return + // } + unsafe { free(&byte(a.data) - a.offset) } +} + +// filter creates a new array with all elements that pass the test implemented by the provided function +pub fn (a array) filter(predicate fn (voidptr) bool) array + +struct ZZZTmp1 {} + +// any tests whether at least one element in the array passes the test implemented by the +// provided function. It returns true if, in the array, it finds an element for which the provided +// function returns true; otherwise it returns false. It doesn't modify the array +pub fn (a array) any(predicate fn (voidptr) bool) bool + +struct ZZZTmp2 {} + +// all tests whether all elements in the array pass the test implemented by the provided function +pub fn (a array) all(predicate fn (voidptr) bool) bool + +struct ZZZTmp3 {} + +// map creates a new array populated with the results of calling a provided function +// on every element in the calling array +pub fn (a array) map(callback fn (voidptr) voidptr) array + +struct ZZZTmp4 {} + +// sort sorts an array in place in ascending order. +pub fn (mut a array) sort(callback fn (voidptr, voidptr) int) + +struct ZZZTmp5 {} + +// contains determines whether an array includes a certain value among its entries +pub fn (a array) contains(val voidptr) bool + +struct ZZZTmp6 {} + +// index returns the first index at which a given element can be found in the array +// or -1 if the value is not found. +pub fn (a array) index(value voidptr) int + +[unsafe] +pub fn (mut a []string) free() { + $if prealloc { + return + } + for s in a { + unsafe { s.free() } + } + unsafe { free(a.data) } +} + +// str returns a string representation of the array of strings +// => '["a", "b", "c"]'. +[manualfree] +pub fn (a []string) str() string { + mut sb := strings.new_builder(a.len * 3) + sb.write_string('[') + for i in 0 .. a.len { + val := a[i] + sb.write_string("'") + sb.write_string(val) + sb.write_string("'") + if i < a.len - 1 { + sb.write_string(', ') + } + } + sb.write_string(']') + res := sb.str() + unsafe { sb.free() } + return res +} + +// hex returns a string with the hexadecimal representation +// of the byte elements of the array. +pub fn (b []byte) hex() string { + mut hex := unsafe { malloc(b.len * 2 + 1) } + mut dst_i := 0 + for i in b { + n0 := i >> 4 + unsafe { + hex[dst_i] = if n0 < 10 { n0 + `0` } else { n0 + byte(87) } + dst_i++ + } + n1 := i & 0xF + unsafe { + hex[dst_i] = if n1 < 10 { n1 + `0` } else { n1 + byte(87) } + dst_i++ + } + } + unsafe { + hex[dst_i] = 0 + return tos(hex, dst_i) + } +} + +// copy copies the `src` byte array elements to the `dst` byte array. +// The number of the elements copied is the minimum of the length of both arrays. +// Returns the number of elements copied. +// TODO: implement for all types +pub fn copy(dst []byte, src []byte) int { + min := if dst.len < src.len { dst.len } else { src.len } + if min > 0 { + unsafe { vmemcpy(&byte(dst.data), src.data, min) } + } + return min +} + +// reduce executes a given reducer function on each element of the array, +// resulting in a single output value. +pub fn (a []int) reduce(iter fn (int, int) int, accum_start int) int { + mut accum_ := accum_start + for i in a { + accum_ = iter(accum_, i) + } + return accum_ +} + +// grow_cap grows the array's capacity by `amount` elements. +pub fn (mut a array) grow_cap(amount int) { + a.ensure_cap(a.cap + amount) +} + +// grow_len ensures that an array has a.len + amount of length +[unsafe] +pub fn (mut a array) grow_len(amount int) { + a.ensure_cap(a.len + amount) + a.len += amount +} + +// pointers returns a new array, where each element +// is the address of the corresponding element in the array. +[unsafe] +pub fn (a array) pointers() []voidptr { + mut res := []voidptr{} + for i in 0 .. a.len { + unsafe { res << a.get_unsafe(i) } + } + return res +} + +// voidptr.vbytes() - makes a V []byte structure from a C style memory buffer. NB: the data is reused, NOT copied! +[unsafe] +pub fn (data voidptr) vbytes(len int) []byte { + res := array{ + element_size: 1 + data: data + len: len + cap: len + } + return res +} + +// byteptr.vbytes() - makes a V []byte structure from a C style memory buffer. NB: the data is reused, NOT copied! +[unsafe] +pub fn (data &byte) vbytes(len int) []byte { + return unsafe { voidptr(data).vbytes(len) } +} diff --git a/v_windows/v/vlib/builtin/array_d_gcboehm_opt.v b/v_windows/v/vlib/builtin/array_d_gcboehm_opt.v new file mode 100644 index 0000000..132ddc7 --- /dev/null +++ b/v_windows/v/vlib/builtin/array_d_gcboehm_opt.v @@ -0,0 +1,267 @@ +// non-pub versions of array functions +// that allocale new memory using `GC_MALLOC_ATOMIC()` +// when `-gc boehm_*_opt` is used. These memory areas are not +// scanned for pointers. + +module builtin + +fn __new_array_noscan(mylen int, cap int, elm_size int) array { + cap_ := if cap < mylen { mylen } else { cap } + arr := array{ + element_size: elm_size + data: vcalloc_noscan(cap_ * elm_size) + len: mylen + cap: cap_ + } + return arr +} + +fn __new_array_with_default_noscan(mylen int, cap int, elm_size int, val voidptr) array { + cap_ := if cap < mylen { mylen } else { cap } + mut arr := array{ + element_size: elm_size + data: vcalloc_noscan(cap_ * elm_size) + len: mylen + cap: cap_ + } + if val != 0 { + for i in 0 .. arr.len { + unsafe { arr.set_unsafe(i, val) } + } + } + return arr +} + +fn __new_array_with_array_default_noscan(mylen int, cap int, elm_size int, val array) array { + cap_ := if cap < mylen { mylen } else { cap } + mut arr := array{ + element_size: elm_size + data: vcalloc_noscan(cap_ * elm_size) + len: mylen + cap: cap_ + } + for i in 0 .. arr.len { + val_clone := val.clone() + unsafe { arr.set_unsafe(i, &val_clone) } + } + return arr +} + +// Private function, used by V (`nums := [1, 2, 3]`) +fn new_array_from_c_array_noscan(len int, cap int, elm_size int, c_array voidptr) array { + cap_ := if cap < len { len } else { cap } + arr := array{ + element_size: elm_size + data: vcalloc_noscan(cap_ * elm_size) + len: len + cap: cap_ + } + // TODO Write all memory functions (like memcpy) in V + unsafe { vmemcpy(arr.data, c_array, len * elm_size) } + return arr +} + +// Private function. Doubles array capacity if needed. +fn (mut a array) ensure_cap_noscan(required int) { + if required <= a.cap { + return + } + mut cap := if a.cap > 0 { a.cap } else { 2 } + for required > cap { + cap *= 2 + } + new_size := cap * a.element_size + new_data := vcalloc_noscan(new_size) + if a.data != voidptr(0) { + unsafe { vmemcpy(new_data, a.data, a.len * a.element_size) } + // TODO: the old data may be leaked when no GC is used (ref-counting?) + } + a.data = new_data + a.offset = 0 + a.cap = cap +} + +// repeat returns a new array with the given array elements repeated given times. +// `cgen` will replace this with an apropriate call to `repeat_to_depth()` + +// version of `repeat()` that handles multi dimensional arrays +// `unsafe` to call directly because `depth` is not checked +[unsafe] +fn (a array) repeat_to_depth_noscan(count int, depth int) array { + if count < 0 { + panic('array.repeat: count is negative: $count') + } + mut size := count * a.len * a.element_size + if size == 0 { + size = a.element_size + } + arr := array{ + element_size: a.element_size + data: if depth > 0 { vcalloc(size) } else { vcalloc_noscan(size) } + len: count * a.len + cap: count * a.len + } + if a.len > 0 { + for i in 0 .. count { + if depth > 0 { + ary_clone := unsafe { a.clone_to_depth_noscan(depth) } + unsafe { vmemcpy(arr.get_unsafe(i * a.len), &byte(ary_clone.data), a.len * a.element_size) } + } else { + unsafe { vmemcpy(arr.get_unsafe(i * a.len), &byte(a.data), a.len * a.element_size) } + } + } + } + return arr +} + +// insert inserts a value in the array at index `i` +fn (mut a array) insert_noscan(i int, val voidptr) { + $if !no_bounds_checking ? { + if i < 0 || i > a.len { + panic('array.insert: index out of range (i == $i, a.len == $a.len)') + } + } + a.ensure_cap_noscan(a.len + 1) + unsafe { + vmemmove(a.get_unsafe(i + 1), a.get_unsafe(i), (a.len - i) * a.element_size) + a.set_unsafe(i, val) + } + a.len++ +} + +// insert_many inserts many values into the array from index `i`. +[unsafe] +fn (mut a array) insert_many_noscan(i int, val voidptr, size int) { + $if !no_bounds_checking ? { + if i < 0 || i > a.len { + panic('array.insert_many: index out of range (i == $i, a.len == $a.len)') + } + } + a.ensure_cap_noscan(a.len + size) + elem_size := a.element_size + unsafe { + iptr := a.get_unsafe(i) + vmemmove(a.get_unsafe(i + size), iptr, (a.len - i) * elem_size) + vmemcpy(iptr, val, size * elem_size) + } + a.len += size +} + +// prepend prepends one value to the array. +fn (mut a array) prepend_noscan(val voidptr) { + a.insert_noscan(0, val) +} + +// prepend_many prepends another array to this array. +[unsafe] +fn (mut a array) prepend_many_noscan(val voidptr, size int) { + unsafe { a.insert_many_noscan(0, val, size) } +} + +// pop returns the last element of the array, and removes it. +fn (mut a array) pop_noscan() voidptr { + // in a sense, this is the opposite of `a << x` + $if !no_bounds_checking ? { + if a.len == 0 { + panic('array.pop: array is empty') + } + } + new_len := a.len - 1 + last_elem := unsafe { &byte(a.data) + new_len * a.element_size } + a.len = new_len + // NB: a.cap is not changed here *on purpose*, so that + // further << ops on that array will be more efficient. + return unsafe { memdup_noscan(last_elem, a.element_size) } +} + +// `clone_static_to_depth_noscan()` returns an independent copy of a given array. +// Unlike `clone_to_depth_noscan()` it has a value receiver and is used internally +// for slice-clone expressions like `a[2..4].clone()` and in -autofree generated code. +fn (a array) clone_static_to_depth_noscan(depth int) array { + return unsafe { a.clone_to_depth_noscan(depth) } +} + +// recursively clone given array - `unsafe` when called directly because depth is not checked +[unsafe] +fn (a &array) clone_to_depth_noscan(depth int) array { + mut size := a.cap * a.element_size + if size == 0 { + size++ + } + mut arr := array{ + element_size: a.element_size + data: if depth == 0 { vcalloc_noscan(size) } else { vcalloc(size) } + len: a.len + cap: a.cap + } + // Recursively clone-generated elements if array element is array type + if depth > 0 { + for i in 0 .. a.len { + ar := array{} + unsafe { vmemcpy(&ar, a.get_unsafe(i), int(sizeof(array))) } + ar_clone := unsafe { ar.clone_to_depth_noscan(depth - 1) } + unsafe { arr.set_unsafe(i, &ar_clone) } + } + return arr + } else { + if !isnil(a.data) { + unsafe { vmemcpy(&byte(arr.data), a.data, a.cap * a.element_size) } + } + return arr + } +} + +fn (mut a array) push_noscan(val voidptr) { + a.ensure_cap_noscan(a.len + 1) + unsafe { vmemmove(&byte(a.data) + a.element_size * a.len, val, a.element_size) } + a.len++ +} + +// push_many implements the functionality for pushing another array. +// `val` is array.data and user facing usage is `a << [1,2,3]` +[unsafe] +fn (mut a3 array) push_many_noscan(val voidptr, size int) { + if a3.data == val && !isnil(a3.data) { + // handle `arr << arr` + copy := a3.clone() + a3.ensure_cap_noscan(a3.len + size) + unsafe { + vmemcpy(a3.get_unsafe(a3.len), copy.data, a3.element_size * size) + } + } else { + a3.ensure_cap_noscan(a3.len + size) + if !isnil(a3.data) && !isnil(val) { + unsafe { vmemcpy(a3.get_unsafe(a3.len), val, a3.element_size * size) } + } + } + a3.len += size +} + +// reverse returns a new array with the elements of the original array in reverse order. +fn (a array) reverse_noscan() array { + if a.len < 2 { + return a + } + mut arr := array{ + element_size: a.element_size + data: vcalloc_noscan(a.cap * a.element_size) + len: a.len + cap: a.cap + } + for i in 0 .. a.len { + unsafe { arr.set_unsafe(i, a.get_unsafe(a.len - 1 - i)) } + } + return arr +} + +// grow_cap grows the array's capacity by `amount` elements. +fn (mut a array) grow_cap_noscan(amount int) { + a.ensure_cap_noscan(a.cap + amount) +} + +// grow_len ensures that an array has a.len + amount of length +[unsafe] +fn (mut a array) grow_len_noscan(amount int) { + a.ensure_cap_noscan(a.len + amount) + a.len += amount +} diff --git a/v_windows/v/vlib/builtin/array_notd_gcboehm_opt.v b/v_windows/v/vlib/builtin/array_notd_gcboehm_opt.v new file mode 100644 index 0000000..1703167 --- /dev/null +++ b/v_windows/v/vlib/builtin/array_notd_gcboehm_opt.v @@ -0,0 +1,10 @@ +// dummy placeholder for functions from `array_d_gcboehm_opt.v` +// that might be needed for compile time +// `$if gcboehm_opt ? { ... } $else { ... }` + +module builtin + +// this is needed in `string.v` +fn __new_array_noscan(mylen int, cap int, elm_size int) array { + return array{} +} diff --git a/v_windows/v/vlib/builtin/array_test.v b/v_windows/v/vlib/builtin/array_test.v new file mode 100644 index 0000000..c27768a --- /dev/null +++ b/v_windows/v/vlib/builtin/array_test.v @@ -0,0 +1,1507 @@ +fn test_pointer() { + mut arr := []&int{} + a := 1 + b := 2 + c := 3 + arr << &a + arr << &b + arr << &c + assert *arr[0] == 1 + arr[1] = &c + assert *arr[1] == 3 + mut d_arr := [arr] // [][]&int + d_arr << arr + assert *d_arr[0][1] == 3 + println(*d_arr[0][1]) + assert *d_arr[1][0] == 1 +} + +fn test_assign() { + mut arr := [2, 4, 8, 16, 32, 64, 128] + arr[0] = 2 + arr[1] &= 255 + arr[2] |= 255 + arr[3] <<= 4 + arr[4] >>= 4 + arr[5] %= 5 + arr[6] ^= 3 + assert arr[0] == 2 + assert arr[1] == 4 & 255 + assert arr[2] == 8 | 255 + assert arr[3] == 16 << 4 + assert arr[4] == 32 >> 4 + assert arr[5] == 64 % 5 + assert arr[6] == 128 ^ 3 +} + +fn test_ints() { + mut a := [1, 5, 2, 3] + assert a.len == 4 + assert a[0] == 1 + assert a[2] == 2 + assert a.last() == 3 + a << 4 + assert a.len == 5 + assert a[4] == 4 + assert a.last() == 4 + s := a.str() + assert s == '[1, 5, 2, 3, 4]' + assert a[1] == 5 + assert a.last() == 4 +} + +fn test_deleting() { + mut a := [1, 5, 2, 3, 4] + assert a.len == 5 + assert a.str() == '[1, 5, 2, 3, 4]' + a.delete(0) + assert a.str() == '[5, 2, 3, 4]' + assert a.len == 4 + a.delete(1) + assert a.str() == '[5, 3, 4]' + assert a.len == 3 + a.delete(a.len - 1) + assert a.str() == '[5, 3]' + assert a.len == 2 +} + +fn test_slice_delete() { + mut a := [1.5, 2.5, 3.25, 4.5, 5.75] + b := a[2..4] + a.delete(0) + assert a == [2.5, 3.25, 4.5, 5.75] + assert b == [3.25, 4.5] + a = [3.75, 4.25, -1.5, 2.25, 6.0] + c := a[..3] + a.delete(2) + assert a == [3.75, 4.25, 2.25, 6.0] + assert c == [3.75, 4.25, -1.5] +} + +fn test_delete_many() { + mut a := [1, 2, 3, 4, 5, 6, 7, 8, 9] + b := a[2..6] + a.delete_many(4, 3) + assert a == [1, 2, 3, 4, 8, 9] + assert b == [3, 4, 5, 6] + c := a[..a.len] + a.delete_many(2, 0) // this should just clone + a[1] = 17 + assert a == [1, 17, 3, 4, 8, 9] + assert c == [1, 2, 3, 4, 8, 9] + a.delete_many(0, a.len) + assert a == []int{} +} + +fn test_short() { + a := [1, 2, 3] + assert a.len == 3 + assert a.cap == 3 + assert a[0] == 1 + assert a[1] == 2 + assert a[2] == 3 +} + +fn test_large() { + mut a := [0].repeat(0) + for i in 0 .. 10000 { + a << i + } + assert a.len == 10000 + assert a[234] == 234 +} + +struct Chunk { + val string +} + +struct Kkk { + q []Chunk +} + +fn test_empty() { + mut chunks := []Chunk{} + a := Chunk{} + assert chunks.len == 0 + chunks << a + assert chunks.len == 1 + chunks = [] + assert chunks.len == 0 + chunks << a + assert chunks.len == 1 +} + +fn test_push() { + mut a := []int{} + a << 1 + a << 3 + assert a[1] == 3 + assert a.str() == '[1, 3]' +} + +fn test_insert() { + mut a := [1, 2] + a.insert(0, 3) + assert a[0] == 3 + assert a[2] == 2 + assert a.len == 3 + a.insert(1, 4) + assert a[1] == 4 + assert a[2] == 1 + assert a.len == 4 + a.insert(4, 5) + assert a[4] == 5 + assert a[3] == 2 + assert a.len == 5 + mut b := []f64{} + assert b.len == 0 + b.insert(0, f64(1.1)) + assert b.len == 1 + assert b[0] == f64(1.1) +} + +fn test_insert_many() { + mut a := [3, 4] + a.insert(0, [1, 2]) + assert a == [1, 2, 3, 4] + b := [5, 6] + a.insert(1, b) + assert a == [1, 5, 6, 2, 3, 4] +} + +fn test_prepend() { + mut a := []int{} + assert a.len == 0 + a.prepend(1) + assert a.len == 1 + assert a[0] == 1 + mut b := []f64{} + assert b.len == 0 + b.prepend(f64(1.1)) + assert b.len == 1 + assert b[0] == f64(1.1) +} + +fn test_prepend_many() { + mut a := [3, 4] + a.prepend([1, 2]) + assert a == [1, 2, 3, 4] + b := [5, 6] + a.prepend(b) + assert a == [5, 6, 1, 2, 3, 4] +} + +fn test_strings() { + a := ['a', 'b', 'c'] + assert a.str() == "['a', 'b', 'c']" +} + +/* +fn test_compare_ints() { + assert compare_ints(1, 2) == -1 + assert compare_ints(2, 1) == 1 + assert compare_ints(0, 0) == 0 + + a := 1 + b := 2 + assert compare_ints(a, b) == -1 + assert compare_ints(b, a) == 1 + assert compare_ints(a, a) == 0 +} +*/ +fn test_repeat() { + { + a := [0].repeat(5) + assert a.len == 5 + assert a[0] == 0 && a[1] == 0 && a[2] == 0 && a[3] == 0 && a[4] == 0 + } + { + a := [1.1].repeat(10) + assert a[0] == 1.1 + assert a[5] == 1.1 + assert a[9] == 1.1 + } + { + a := [i64(-123)].repeat(10) + assert a[0] == -123 + assert a[5] == -123 + assert a[9] == -123 + } + { + a := [u64(123)].repeat(10) + assert a[0] == 123 + assert a[5] == 123 + assert a[9] == 123 + } + { + a := [1.1].repeat(10) + assert a[0] == 1.1 + assert a[5] == 1.1 + assert a[9] == 1.1 + } + { + a := [1, 2].repeat(2) + assert a[0] == 1 + assert a[1] == 2 + assert a[2] == 1 + assert a[3] == 2 + } + { + a := ['1', 'abc'].repeat(2) + assert a[0] == '1' + assert a[1] == 'abc' + assert a[2] == '1' + assert a[3] == 'abc' + } + { + mut a := ['1', 'abc'].repeat(0) + assert a.len == 0 + a << 'abc' + assert a[0] == 'abc' + } +} + +fn test_deep_repeat() { + mut a3 := [[[1, 1], [2, 2], [3, 3]], [[4, 4], [5, 5], [6, 6]]] + r := a3.repeat(3) + a3[1][1][0] = 17 + assert r == [ + [[1, 1], [2, 2], [3, 3]], + [[4, 4], [5, 5], [6, 6]], + [[1, 1], [2, 2], [3, 3]], + [[4, 4], [5, 5], [6, 6]], + [[1, 1], [2, 2], [3, 3]], + [[4, 4], [5, 5], [6, 6]], + ] + assert a3 == [[[1, 1], [2, 2], [3, 3]], [[4, 4], [17, 5], [6, 6]]] +} + +fn test_right() { + a := [1, 2, 3, 4] + c := a[1..a.len] + d := a[1..] + assert c[0] == 2 + assert c[1] == 3 + assert d[0] == 2 + assert d[1] == 3 +} + +fn test_left() { + a := [1, 2, 3] + c := a[0..2] + d := a[..2] + assert c[0] == 1 + assert c[1] == 2 + assert d[0] == 1 + assert d[1] == 2 +} + +fn test_slice() { + a := [1, 2, 3, 4] + b := a[2..4] + assert b.len == 2 + assert a[1..2].len == 1 + assert a.len == 4 +} + +fn test_push_many() { + mut a := [1, 2, 3] + b := [4, 5, 6] + a << b + assert a.len == 6 + assert a[0] == 1 + assert a[3] == 4 + assert a[5] == 6 +} + +fn test_reverse() { + a := [1, 2, 3, 4] + b := ['test', 'array', 'reverse'] + c := a.reverse() + println(c) + d := b.reverse() + for i, _ in c { + assert c[i] == a[a.len - i - 1] + } + for i, _ in d { + assert d[i] == b[b.len - i - 1] + } + e := []int{} + f := e.reverse() + assert f.len == 0 +} + +const ( + c_n = 5 +) + +struct Foooj { + a [5]int // c_n +} + +fn test_fixed() { + mut nums := [4]int{} + // x := nums[1..3] + // assert x.len == 2 + assert nums[0] == 0 + assert nums[1] == 0 + assert nums[2] == 0 + assert nums[3] == 0 + nums[1] = 7 + assert nums[1] == 7 + nums2 := [5]int{} // c_n + assert nums2[c_n - 1] == 0 +} + +fn modify(mut numbers []int) { + numbers[0] = 777 +} + +fn test_mut_slice() { + mut n := [1, 2, 3] + // modify(mut n) + modify(mut n[..2]) + assert n[0] == 777 + modify(mut n[2..]) + assert n[2] == 777 + println(n) +} + +fn double_up(mut a []int) { + for i := 0; i < a.len; i++ { + a[i] = a[i] * 2 + } +} + +fn double_up_v2(mut a []int) { + for i, _ in a { + a[i] = a[i] * 2 // or val*2, doesn't matter + } +} + +fn test_mut_arg() { + mut arr := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + double_up(mut arr) + assert arr.str() == '[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]' + arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + double_up_v2(mut arr) + assert arr.str() == '[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]' +} + +fn test_clone() { + nums := [1, 2, 3, 4, 100] + _ = nums + nums2 := nums.clone() + assert nums2.len == 5 + assert nums.str() == '[1, 2, 3, 4, 100]' + assert nums2.str() == '[1, 2, 3, 4, 100]' + assert nums[1..3].str() == '[2, 3]' +} + +/* +fn test_copy() { + a := [1, 2, 3] + b := a + assert b[0] == 1 + assert b[1] == 2 + assert b[2] == 3 +} +*/ +fn test_multi_array_clone() { + // 2d array_int + mut a2_1 := [[1, 2, 3], [4, 5, 6]] + mut a2_2 := a2_1.clone() + a2_1[0][1] = 0 + a2_2[1][0] = 0 + assert a2_1 == [[1, 0, 3], [4, 5, 6]] + assert a2_2 == [[1, 2, 3], [0, 5, 6]] + // 2d array_string + mut b2_1 := [['1', '2', '3'], ['4', '5', '6']] + mut b2_2 := b2_1.clone() + b2_1[0][1] = '0' + b2_2[1][0] = '0' + assert b2_1 == [['1', '0', '3'], ['4', '5', '6']] + assert b2_2 == [['1', '2', '3'], ['0', '5', '6']] + // 3d array_int + mut a3_1 := [[[1, 1], [2, 2], [3, 3]], [[4, 4], [5, 5], [6, 6]]] + mut a3_2 := a3_1.clone() + a3_1[0][0][1] = 0 + a3_2[0][1][0] = 0 + assert a3_1 == [[[1, 0], [2, 2], [3, 3]], [[4, 4], [5, 5], + [6, 6], + ]] + assert a3_2 == [[[1, 1], [0, 2], [3, 3]], [[4, 4], [5, 5], + [6, 6], + ]] + // 3d array_string + mut b3_1 := [[['1', '1'], ['2', '2'], ['3', '3']], [['4', '4'], + ['5', '5'], ['6', '6']]] + mut b3_2 := b3_1.clone() + b3_1[0][0][1] = '0' + b3_2[0][1][0] = '0' + assert b3_1 == [[['1', '0'], ['2', '2'], ['3', '3']], [['4', '4'], + ['5', '5'], ['6', '6']]] + assert b3_2 == [[['1', '1'], ['0', '2'], ['3', '3']], [['4', '4'], + ['5', '5'], ['6', '6']]] +} + +fn test_doubling() { + mut nums := [1, 2, 3, 4, 5] + for i in 0 .. nums.len { + nums[i] *= 2 + } + println(nums.str()) + assert nums.str() == '[2, 4, 6, 8, 10]' +} + +struct Test2 { + one int + two int +} + +struct Test { + a string +mut: + b []Test2 +} + +// TODO: default array/struct str methods +fn (ta []Test2) str() string { + mut s := '[' + for i, t in ta { + s += t.str() + if i < ta.len - 1 { + s += ', ' + } + } + s += ']' + return s +} + +fn (t Test2) str() string { + return '{$t.one $t.two}' +} + +fn (t Test) str() string { + return '{$t.a $t.b}' +} + +fn test_struct_print() { + mut a := Test{ + a: 'Test' + b: [] + } + b := Test2{ + one: 1 + two: 2 + } + a.b << b + a.b << b + assert a.str() == '{Test [{1 2}, {1 2}]}' + assert b.str() == '{1 2}' + assert a.b.str() == '[{1 2}, {1 2}]' +} + +fn test_single_element() { + mut a := [1] + a << 2 + assert a.len == 2 + assert a[0] == 1 + assert a[1] == 2 + println(a) +} + +fn test_find_index() { + // string + a := ['v', 'is', 'great'] + assert a.index('v') == 0 + assert a.index('is') == 1 + assert a.index('gre') == -1 + // int + b := [1, 2, 3, 4] + assert b.index(1) == 0 + assert b.index(4) == 3 + assert b.index(5) == -1 + // byte + c := [0x22, 0x33, 0x55] + assert c.index(0x22) == 0 + assert c.index(0x55) == 2 + assert c.index(0x99) == -1 + // char + d := [`a`, `b`, `c`] + assert d.index(`b`) == 1 + assert d.index(`c`) == 2 + assert d.index(`u`) == -1 +} + +fn test_multi() { + a := [[1, 2, 3], [4, 5, 6]] + assert a.len == 2 + assert a[0].len == 3 + assert a[0][0] == 1 + assert a[0][2] == 3 + assert a[1][2] == 6 + // TODO + // b := [ [[1,2,3],[4,5,6]], [[1,2]] ] + // assert b[0][0][0] == 1 +} + +fn test_in() { + a := [1, 2, 3] + assert 1 in a + assert 2 in a + assert 3 in a + assert 4 !in a + assert 0 !in a + assert 0 !in a + assert 4 !in a + b := [1, 4, 0] + c := [3, 6, 2, 0] + assert 0 in b + assert 0 in c +} + +fn sum(prev int, curr int) int { + return prev + curr +} + +fn sub(prev int, curr int) int { + return prev - curr +} + +fn test_reduce() { + a := [1, 2, 3, 4, 5] + b := a.reduce(sum, 0) + c := a.reduce(sum, 5) + d := a.reduce(sum, -1) + assert b == 15 + assert c == 20 + assert d == 14 + e := [1, 2, 3] + f := e.reduce(sub, 0) + g := e.reduce(sub, -1) + assert f == -6 + assert g == -7 +} + +fn filter_test_helper_1(a int) bool { + return a > 3 +} + +fn test_filter() { + a := [1, 2, 3, 4, 5, 6] + b := a.filter(it % 2 == 0) + assert b.len == 3 + assert b[0] == 2 + assert b[1] == 4 + assert b[2] == 6 + c := ['v', 'is', 'awesome'] + d := c.filter(it.len > 1) + assert d[0] == 'is' + assert d[1] == 'awesome' + //////// + arr := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + println(arr.filter(it % 2 == 0 || it % 3 == 0)) + assert true + assert [1, 2, 3].len == 3 + mut mut_arr := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + mut_arr = mut_arr.filter(it < 4) + assert mut_arr.len == 3 + assert a.filter(filter_test_helper_1) == [4, 5, 6] + assert [1, 5, 10].filter(filter_test_helper_1) == [5, 10] + // TODO + // assert arr.filter(arr % 2).len == 5 +} + +fn test_anon_fn_filter() { + filter_num := fn (i int) bool { + return i % 2 == 0 + } + assert [1, 2, 3, 4, 5].filter(filter_num) == [2, 4] +} + +fn test_anon_fn_arg_filter() { + a := [1, 2, 3, 4].filter(fn (i int) bool { + return i % 2 == 0 + }) + assert a == [2, 4] +} + +fn map_test_helper_1(i int) int { + return i * i +} + +fn map_test_helper_2(i int, b string) int { + return i + b.len +} + +fn map_test_helper_3(i int, b []string) int { + return i + b.map(it.len)[i % b.len] +} + +fn test_map() { + nums := [1, 2, 3, 4, 5, 6] + strs := ['v', 'is', 'awesome'] + // assert nums.map() == + // assert nums.map(it, 'excessive') == + // identity + assert nums.map(it) == [1, 2, 3, 4, 5, 6] + assert strs.map(it) == ['v', 'is', 'awesome'] + assert nums.map(it - it) == [0, 0, 0, 0, 0, 0] + assert nums.map(it - it)[0] == 0 + // type switch + assert nums.map(it * 10) == [10, 20, 30, 40, 50, 60] + assert nums.map(it * it) == [1, 4, 9, 16, 25, 36] + assert nums.map('$it') == ['1', '2', '3', '4', '5', '6'] + assert nums.map(it % 2 == 0) == [false, true, false, true, false, true] + assert strs.map(it.to_upper()) == ['V', 'IS', 'AWESOME'] + assert strs.map(it == 'awesome') == [false, false, true] + assert strs.map(it.len in nums) == [true, true, false] + assert strs.map(int(7)) == [7, 7, 7] + // external func + assert nums.map(map_test_helper_1(it)) == [1, 4, 9, 16, 25, 36] + assert nums.map(map_test_helper_2(it, 'bb')) == [3, 4, 5, 6, 7, 8] + assert nums.map(map_test_helper_3(it, strs)) == [3, 9, 4, 6, 12, 7] + // empty array as input + assert []int{len: 0}.map(it * 2) == [] + // nested maps (where it is of same type) + assert nums.map(strs.map(int(7)) == [7, 7, 7]) == [true, true, true, true, true, true] + assert nums.map('$it' + strs.map('a')[0]) == ['1a', '2a', '3a', '4a', '5a', '6a'] + assert nums.map(it + strs.map(int(7))[0]) == [8, 9, 10, 11, 12, 13] + assert nums.map(it + strs.map(it.len)[0]) == [2, 3, 4, 5, 6, 7] + assert strs.map(it.len + strs.map(it.len)[0]) == [2, 3, 8] + // nested (different it types) + assert strs.map(it[nums.map(it - it)[0]]) == [byte(`v`), `i`, `a`] + assert nums[0..3].map('$it' + strs.map(it)[it - 1]) == ['1v', '2is', '3awesome'] + assert nums.map(map_test_helper_1) == [1, 4, 9, 16, 25, 36] + assert [1, 5, 10].map(map_test_helper_1) == [1, 25, 100] + assert nums == [1, 2, 3, 4, 5, 6] + assert strs == ['v', 'is', 'awesome'] +} + +fn test_anon_fn_map() { + add_num := fn (i int) int { + return i + 1 + } + assert [1, 2, 3].map(add_num) == [2, 3, 4] +} + +fn test_multi_anon_fn_map() { + a := [1, 2, 3].map(fn (i int) int { + return i + 1 + }) + b := [1, 2, 3].map(fn (i int) int { + return i + 2 + }) + assert a == [2, 3, 4] + assert b == [3, 4, 5] +} + +fn test_anon_fn_arg_map() { + a := [1, 2, 3].map(fn (i int) int { + return i + 1 + }) + assert a == [2, 3, 4] +} + +fn test_anon_fn_arg_different_type_map() { + i_to_str := fn (i int) string { + return i.str() + } + a := [1, 2, 3].map(i_to_str) + assert a == ['1', '2', '3'] +} + +fn test_anon_fn_inline_different_type_map() { + a := [1, 2, 3].map(fn (i int) string { + return i.str() + }) + assert a == ['1', '2', '3'] +} + +fn test_array_str() { + numbers := [1, 2, 3] + assert numbers == [1, 2, 3] + numbers2 := [numbers, [4, 5, 6]] // dup str() bug + println(numbers2) + assert true + assert numbers.str() == '[1, 2, 3]' + // QTODO + // assert numbers2.str() == '[[1, 2, 3], [4, 5, 6]]' +} + +struct User { + age int + name string +} + +fn test_eq() { + assert [5, 6, 7] != [6, 7] + assert [`a`, `b`] == [`a`, `b`] + assert [User{ + age: 22 + name: 'bob' + }] == [User{ + age: 22 + name: 'bob' + }] + assert [{ + 'bob': 22 + }, { + 'tom': 33 + }] == [{ + 'bob': 22 + }, { + 'tom': 33 + }] + assert [[1, 2, 3], [4]] == [[1, 2, 3], [4]] +} + +fn test_fixed_array_eq() { + a1 := [1, 2, 3]! + assert a1 == [1, 2, 3]! + assert a1 != [2, 3, 4]! + + a2 := [[1, 2]!, [3, 4]!]! + assert a2 == [[1, 2]!, [3, 4]!]! + assert a2 != [[3, 4]!, [1, 2]!]! + + a3 := [[1, 2], [3, 4]]! + assert a3 == [[1, 2], [3, 4]]! + assert a3 != [[1, 1], [2, 2]]! + + a4 := [[`a`, `b`], [`c`, `d`]]! + assert a4 == [[`a`, `b`], [`c`, `d`]]! + assert a4 != [[`c`, `a`], [`a`, `b`]]! + + a5 := [['aaa', 'bbb'], ['ccc', 'ddd']]! + assert a5 == [['aaa', 'bbb'], ['ccc', 'ddd']]! + assert a5 != [['abc', 'def'], ['ccc', 'ddd']]! + + a6 := [['aaa', 'bbb']!, ['ccc', 'ddd']!]! + assert a6 == [['aaa', 'bbb']!, ['ccc', 'ddd']!]! + assert a6 != [['aaa', 'bbb']!, ['aaa', 'ddd']!]! + + a7 := [[1, 2]!, [3, 4]!] + assert a7 == [[1, 2]!, [3, 4]!] + assert a7 != [[2, 3]!, [1, 2]!] + + a8 := [['aaa', 'bbb']!, ['ccc', 'ddd']!] + assert a8 == [['aaa', 'bbb']!, ['ccc', 'ddd']!] + assert a8 != [['bbb', 'aaa']!, ['cccc', 'dddd']!] +} + +fn test_fixed_array_literal_eq() { + assert [1, 2, 3]! == [1, 2, 3]! + assert [1, 1, 1]! != [1, 2, 3]! + + assert [[1, 2], [3, 4]]! == [[1, 2], [3, 4]]! + assert [[1, 1], [2, 2]]! != [[1, 2], [3, 4]]! + + assert [[1, 1]!, [2, 2]!]! == [[1, 1]!, [2, 2]!]! + assert [[1, 1]!, [2, 2]!]! != [[1, 2]!, [2, 3]!]! + + assert [[1, 1]!, [2, 2]!] == [[1, 1]!, [2, 2]!] + assert [[1, 1]!, [2, 2]!] != [[1, 2]!, [2, 3]!] +} + +fn test_sort() { + mut a := ['hi', '1', '5', '3'] + a.sort() + assert a == ['1', '3', '5', 'hi'] + + mut nums := [67, -3, 108, 42, 7] + nums.sort() + assert nums == [-3, 7, 42, 67, 108] + + nums.sort(a < b) + assert nums == [-3, 7, 42, 67, 108] + + nums.sort(b < a) + assert nums == [108, 67, 42, 7, -3] + + mut users := [User{22, 'Peter'}, User{20, 'Bob'}, User{25, 'Alice'}] + users.sort(a.age < b.age) + assert users[0].age == 20 + assert users[1].age == 22 + assert users[2].age == 25 + assert users[0].name == 'Bob' + assert users[1].name == 'Peter' + assert users[2].name == 'Alice' + + users.sort(a.age > b.age) + assert users[0].age == 25 + assert users[1].age == 22 + assert users[2].age == 20 + + users.sort(b.age > a.age) + assert users[0].age == 20 + assert users[1].age == 22 + assert users[2].age == 25 + + users.sort(a.name < b.name) + assert users[0].name == 'Alice' + assert users[1].name == 'Bob' + assert users[2].name == 'Peter' +} + +fn test_rune_sort() { + mut bs := [`f`, `e`, `d`, `b`, `c`, `a`] + bs.sort() + println(bs) + assert bs == [`a`, `b`, `c`, `d`, `e`, `f`] + + bs.sort(a > b) + println(bs) + assert bs == [`f`, `e`, `d`, `c`, `b`, `a`] + + bs.sort(a < b) + println(bs) + assert bs == [`a`, `b`, `c`, `d`, `e`, `f`] +} + +fn test_sort_by_different_order_of_a_b() { + mut x := [1, 2, 3] + x.sort(a < b) + println(x) + assert x == [1, 2, 3] + + mut y := [1, 2, 3] + y.sort(b < a) + println(y) + assert y == [3, 2, 1] +} + +fn test_f32_sort() { + mut f := [f32(50.0), 15, 1, 79, 38, 0, 27] + f.sort() + assert f == [f32(0.0), 1, 15, 27, 38, 50, 79] + + f.sort(a < b) + assert f == [f32(0.0), 1, 15, 27, 38, 50, 79] + + f.sort(b > a) + assert f == [f32(0.0), 1, 15, 27, 38, 50, 79] + + f.sort(b < a) + assert f == [f32(79.0), 50, 38, 27, 15, 1, 0] + + f.sort(a > b) + assert f == [f32(79.0), 50, 38, 27, 15, 1, 0] +} + +fn test_f64_sort() { + mut f := [50.0, 15, 1, 79, 38, 0, 27] + f.sort() + assert f[0] == 0.0 + assert f[1] == 1.0 + assert f[6] == 79.0 +} + +fn test_i64_sort() { + mut f := [i64(50), 15, 1, 79, 38, 0, 27] + f.sort() + assert f[0] == 0 + assert f[1] == 1 + assert f[6] == 79 +} + +fn test_sort_index_expr() { + mut f := [[i64(50), 48], [i64(15)], [i64(1)], [i64(79)], [i64(38)], + [i64(0)], [i64(27)]] + // TODO This currently gives "indexing pointer" error without unsafe + unsafe { + f.sort(a[0] < b[0]) + } + assert f == [[i64(0)], [i64(1)], [i64(15)], [i64(27)], [i64(38)], + [i64(50), 48], [i64(79)]] +} + +fn test_a_b_paras_sort() { + mut arr_i := [1, 3, 2] + arr_i.sort(a < b) + println(arr_i) + assert arr_i == [1, 2, 3] + arr_i.sort(b < a) + println(arr_i) + assert arr_i == [3, 2, 1] + + mut arr_f := [1.1, 3.3, 2.2] + arr_f.sort(a < b) + println(arr_f) + assert arr_f == [1.1, 2.2, 3.3] + arr_f.sort(b < a) + println(arr_f) + assert arr_f == [3.3, 2.2, 1.1] +} + +/* +fn test_for_last() { + numbers := [1, 2, 3, 4] + mut s := '[' + for num in numbers { + s += '$num' + if !last { + s += ', ' + + } + } + s += ']' + assert s == '[1, 2, 3, 4]' +} +*/ +struct Foo { +mut: + bar []int +} + +fn test_in_struct() { + mut baz := Foo{ + bar: [0, 0, 0] + } + baz.bar[0] += 2 + baz.bar[0]++ + assert baz.bar[0] == 3 +} + +[direct_array_access] +fn test_direct_modification() { + mut foo := [2, 0, 5] + foo[1] = 3 + foo[0] *= 7 + foo[1]-- + foo[2] -= 2 + assert foo[0] == 14 + assert foo[1] == 2 + assert foo[2] == 3 +} + +fn test_bools() { + println('test b') + mut a := [true, false] + a << true + println(a) +} + +fn test_push_many_self() { + mut actual_arr := [1, 2, 3, 4] + actual_arr << actual_arr + expected_arr := [1, 2, 3, 4, 1, 2, 3, 4] + assert actual_arr.len == expected_arr.len + for i in 0 .. actual_arr.len { + assert actual_arr[i] == expected_arr[i] + } +} + +fn test_for() { + nums := [1, 2, 3] + mut sum := 0 + for num in nums { + sum += num + } + assert sum == 6 +} + +fn test_clear() { + mut arr := [1, 2, 3] + assert arr.len == 3 + arr.clear() + assert arr.len == 0 + arr << 3 + arr << 2 + arr << 1 + arr << 0 + assert arr.len == 4 + assert arr[0] == 3 + assert arr[1] == 2 + assert arr[2] == 1 + assert arr[3] == 0 + arr.clear() + assert arr.len == 0 +} + +fn test_trim() { + mut arr := [1, 2, 3, 4, 5, 6, 7, 8, 9] + assert arr.len == 9 + arr.trim(9) + assert arr.len == 9 + assert arr.last() == 9 + arr.trim(7) + assert arr.len == 7 + assert arr.last() == 7 + arr.trim(2) + assert arr.len == 2 + assert arr.last() == 2 +} + +fn test_hex() { + // array hex + st := [byte(`V`), `L`, `A`, `N`, `G`] + assert st.hex() == '564c414e47' + assert st.hex().len == 10 + st1 := [byte(0x41)].repeat(100) + assert st1.hex() == '41'.repeat(100) +} + +fn test_left_shift_precendence() { + mut arr := []int{} + arr << 1 + 1 + arr << 1 - 1 + arr << 2 / 1 + arr << 2 * 1 + assert arr[0] == 2 + assert arr[1] == 0 + assert arr[2] == 2 + assert arr[3] == 2 +} + +fn test_array_with_cap() { + a4 := []int{len: 1, cap: 10} + assert a4.len == 1 + assert a4.cap == 10 + a5 := []int{len: 1, cap: 10} + assert a5.len == 1 + assert a5.cap == 10 +} + +fn test_multi_array_index() { + mut a := [][]int{len: 2, init: []int{len: 3, init: 0}} + a[0][0] = 1 + assert '$a' == '[[1, 0, 0], [0, 0, 0]]' + mut b := [[0].repeat(3)].repeat(2) + b[0][0] = 1 + assert '$b' == '[[1, 0, 0], [0, 0, 0]]' +} + +fn test_plus_assign_string() { + mut a := [''] + a[0] += 'abc' + assert a == ['abc'] +} + +fn mut_arr_with_eq_in_fn(mut a []int) { + if a == [1, 2, 3, 4] { + a[0] = 0 + } + if [0, 2, 3, 4] == a { + a[1] = 0 + } + if !(a != [0, 0, 3, 4]) { + a[2] = 0 + } + if !([0, 0, 0, 4] != a) { + a[3] = 0 + } +} + +fn test_mut_arr_with_eq_in_fn() { + mut a := [1, 2, 3, 4] + mut_arr_with_eq_in_fn(mut a) + assert a == [0, 0, 0, 0] +} + +fn array_in_mut(mut a []int) { + if 1 in a { + a[0] = 2 + } +} + +fn test_array_in_mut() { + mut a := [1, 2] + array_in_mut(mut a) + assert a == [2, 2] +} + +// test array delete in function with mut argument +fn delete_nums(mut arr []int) { + arr.delete(0) +} + +fn test_array_delete_in_mut() { + mut nums := [1, 2, 3] + delete_nums(mut nums) + assert nums == [2, 3] +} + +// test array add in function with mut argument +fn add_nums(mut arr []int) { + arr << 4 +} + +fn test_array_add_in_mut() { + mut nums := [1, 2, 3] + add_nums(mut nums) + assert nums == [1, 2, 3, 4] +} + +fn test_reverse_in_place() { + mut a := [1, 2, 3, 4] + a.reverse_in_place() + assert a == [4, 3, 2, 1] + mut b := ['a', 'b', 'c'] + b.reverse_in_place() + assert b == ['c', 'b', 'a'] + mut c := [[1, 2], [3, 4], [5, 6]] + c.reverse_in_place() + assert c == [[5, 6], [3, 4], [1, 2]] +} + +fn test_array_int_pop() { + mut a := [1, 2, 3, 4, 5] + assert a.len == 5 + x := a.last() + y := a.pop() + assert x == y + assert a.len == 4 + z := a.pop() + assert a.len == 3 + assert z == 4 + x1 := a.pop() + x2 := a.pop() + final := a.pop() + assert final == 1 +} + +fn test_array_string_pop() { + mut a := ['abc', 'def', 'xyz'] + assert a.len == 3 + assert a.pop() == 'xyz' + assert a.pop() == 'def' + assert a.pop() == 'abc' + assert a.len == 0 + assert a.cap == 3 +} + +fn test_array_first() { + a := [3] + assert a.first() == 3 + b := [1, 2, 3, 4] + assert b.first() == 1 + c := ['abc', 'def'] + assert c.first()[0] == `a` + s := [Chunk{'a'}] + assert s.first().val == 'a' +} + +fn test_array_last() { + a := [3] + assert a.last() == 3 + b := [1, 2, 3, 4] + assert b.last() == 4 + c := ['abc', 'def'] + assert c.last()[0] == `d` + s := [Chunk{'a'}] + assert s.last().val == 'a' +} + +[direct_array_access] +fn test_direct_array_access() { + mut a := [11, 22, 33, 44] + assert a[0] == 11 + assert a[2] == 33 + x := a[0] + a[0] = 21 + a[1] += 2 + a[2] = x + 3 + a[3] -= a[1] + assert a == [21, 24, 14, 20] +} + +[direct_array_access] +fn test_direct_array_access_via_ptr() { + mut b := [11, 22, 33, 44] + unsafe { + mut a := &b + assert a[0] == 11 + assert a[2] == 33 + x := a[0] + a[0] = 21 + a[1] += 2 + a[2] = x + 3 + a[3] -= a[1] + assert a == [21, 24, 14, 20] + } +} + +fn test_push_arr_string_free() { + mut lines := ['hi'] + s := 'a' + 'b' + lines << s + // make sure the data in the array is valid after freeing the string + unsafe { s.free() } + // + println(lines) + assert lines.len == 2 + assert lines[0] == 'hi' + assert lines[1] == 'ab' +} + +const ( + grid_size_1 = 2 + grid_size_2 = 3 + grid_size_3 = 4 + cell_value = 123 +) + +fn test_multidimensional_array_initialization_with_consts() { + mut data := [][][]int{len: grid_size_1, init: [][]int{len: grid_size_2, init: []int{len: grid_size_3, init: cell_value}}} + assert data.len == grid_size_1 + assert data[0].len == grid_size_2 + assert data[0][0].len == grid_size_3 + assert data[0][0][0] == cell_value + assert data[1][1][1] == cell_value +} + +fn test_byteptr_vbytes() { + unsafe { + bp := malloc(5) + bp[0] = 1 + bp[1] = 2 + bp[2] = 3 + bp[3] = 4 + bp[4] = 255 + bytes := bp.vbytes(5) + println(bytes) + assert bytes.len == 5 + assert bytes[0] == 1 + assert bytes[1] == 2 + assert bytes[2] == 3 + assert bytes[3] == 4 + assert bytes[4] == 255 + } +} + +fn test_voidptr_vbytes() { + unsafe { + bp := malloc(3) + bp[0] = 4 + bp[1] = 5 + bp[2] = 6 + bytes := voidptr(bp).vbytes(3) + assert bytes.len == 3 + assert bytes[0] == 4 + assert bytes[1] == 5 + assert bytes[2] == 6 + println(bytes) + } +} + +fn test_multi_array_prepend() { + mut a := [][]int{} + a.prepend([1, 2, 3]) + assert a == [[1, 2, 3]] + mut b := [][]int{} + b.prepend([[1, 2, 3]]) + assert b == [[1, 2, 3]] +} + +fn test_multi_array_insert() { + mut a := [][]int{} + a.insert(0, [1, 2, 3]) + assert a == [[1, 2, 3]] + mut b := [][]int{} + b.insert(0, [[1, 2, 3]]) + assert b == [[1, 2, 3]] +} + +fn test_multi_array_in() { + a := [[1]] + println([1] in a) + assert [1] in a +} + +fn test_any_type_array_contains() { + a := [true, false] + assert a.contains(true) + assert true in a + assert a.contains(false) + assert false in a + b := [i64(2), 3, 4] + assert b.contains(i64(3)) + assert 5 !in b + c := [[1], [2]] + assert c.contains([1]) + assert [2] in c + assert [3] !in c +} + +struct Person { + name string + nums []int + kv map[string]string +} + +fn test_struct_array_of_multi_type_in() { + ivan := Person{ + name: 'ivan' + nums: [1, 2, 3] + kv: { + 'aaa': '111' + } + } + people := [Person{ + name: 'ivan' + nums: [1, 2, 3] + kv: { + 'aaa': '111' + } + }, Person{ + name: 'bob' + nums: [2] + kv: { + 'bbb': '222' + } + }] + println(ivan in people) + assert ivan in people +} + +fn test_struct_array_of_multi_type_index() { + ivan := Person{ + name: 'ivan' + nums: [1, 2, 3] + kv: { + 'aaa': '111' + } + } + people := [Person{ + name: 'ivan' + nums: [1, 2, 3] + kv: { + 'aaa': '111' + } + }, Person{ + name: 'bob' + nums: [2] + kv: { + 'bbb': '222' + } + }] + println(people.index(ivan)) + assert people.index(ivan) == 0 +} + +struct Coord { + x int + y int + z int +} + +fn test_array_struct_contains() { + mut coords := []Coord{} + coord_1 := Coord{ + x: 1 + y: 2 + z: -1 + } + coords << coord_1 + exists := coord_1 in coords + not_exists := coord_1 !in coords + println('`exists`: $exists and `not exists`: $not_exists') + assert exists == true + assert not_exists == false +} + +fn test_array_struct_ref_contains() { + mut coords := []&Coord{} + coord_1 := &Coord{ + x: 1 + y: 2 + z: -1 + } + coords << coord_1 + exists := coord_1 in coords + println(exists) + assert exists == true +} + +fn test_array_struct_ref_index() { + mut coords := []&Coord{} + coord_1 := &Coord{ + x: 1 + y: 2 + z: -1 + } + coords << coord_1 + println(coords.index(coord_1)) + assert coords.index(coord_1) == 0 +} + +fn test_array_of_array_append() { + mut x := [][]int{len: 4} + println(x) // OK + x[2] << 123 // RTE + println(x) + assert '$x' == '[[], [], [123], []]' +} + +fn test_array_of_map_insert() { + mut x := []map[string]int{len: 4} + println(x) // OK + x[2]['123'] = 123 // RTE + println(x) + assert '$x' == "[{}, {}, {'123': 123}, {}]" +} + +fn test_multi_fixed_array_init() { + a := [3][3]int{} + assert '$a' == '[[0, 0, 0], [0, 0, 0], [0, 0, 0]]' +} + +struct Numbers { + odds []int + evens []int +} + +fn test_array_of_multi_filter() { + arr := [1, 2, 3, 4, 5] + nums := Numbers{ + odds: arr.filter(it % 2 == 1) + evens: arr.filter(it % 2 == 0) + } + println(nums) + assert nums.odds == [1, 3, 5] + assert nums.evens == [2, 4] +} + +fn test_array_of_multi_map() { + arr := [1, 3, 5] + nums := Numbers{ + odds: arr.map(it + 2) + evens: arr.map(it * 2) + } + println(nums) + assert nums.odds == [3, 5, 7] + assert nums.evens == [2, 6, 10] +} + +fn test_multi_fixed_array_with_default_init() { + a := [3][3]int{init: [3]int{init: 10}} + println(a) + assert a == [[10, 10, 10]!, [10, 10, 10]!, [10, 10, 10]!]! +} + +struct Abc { +mut: + x i64 + y i64 + z i64 +} + +fn test_clone_of_same_elem_size_array() { + mut arr := []Abc{} + arr << Abc{1, 2, 3} + arr << Abc{2, 3, 4} + arr2 := arr.clone() + println(arr2) + assert arr2 == [Abc{1, 2, 3}, Abc{2, 3, 4}] +} + +pub fn example(mut arr []T) []T { + return arr.clone() +} + +fn test_generic_mutable_arrays() { + mut arr := [1, 2, 3] + assert example(mut arr) == [1, 2, 3] +} diff --git a/v_windows/v/vlib/builtin/builtin.c.v b/v_windows/v/vlib/builtin/builtin.c.v new file mode 100644 index 0000000..0379c83 --- /dev/null +++ b/v_windows/v/vlib/builtin/builtin.c.v @@ -0,0 +1,547 @@ +module builtin + +type FnExitCb = fn () + +fn C.atexit(f FnExitCb) int +fn C.strerror(int) &char + +[noreturn] +fn vhalt() { + for {} +} + +// exit terminates execution immediately and returns exit `code` to the shell. +[noreturn] +pub fn exit(code int) { + C.exit(code) +} + +fn vcommithash() string { + return unsafe { tos5(&char(C.V_CURRENT_COMMIT_HASH)) } +} + +// panic_debug private function that V uses for panics, -cg/-g is passed +// recent versions of tcc print nicer backtraces automatically +// NB: the duplication here is because tcc_backtrace should be called directly +// inside the panic functions. +[noreturn] +fn panic_debug(line_no int, file string, mod string, fn_name string, s string) { + // NB: the order here is important for a stabler test output + // module is less likely to change than function, etc... + // During edits, the line number will change most frequently, + // so it is last + $if freestanding { + bare_panic(s) + } $else { + eprintln('================ V panic ================') + eprintln(' module: $mod') + eprintln(' function: ${fn_name}()') + eprintln(' message: $s') + eprintln(' file: $file:$line_no') + eprintln(' v hash: $vcommithash()') + eprintln('=========================================') + $if exit_after_panic_message ? { + C.exit(1) + } $else $if no_backtrace ? { + C.exit(1) + } $else { + $if tinyc { + $if panics_break_into_debugger ? { + break_if_debugger_attached() + } $else { + C.tcc_backtrace(c'Backtrace') + } + C.exit(1) + } + print_backtrace_skipping_top_frames(1) + $if panics_break_into_debugger ? { + break_if_debugger_attached() + } + C.exit(1) + } + } + vhalt() +} + +[noreturn] +pub fn panic_optional_not_set(s string) { + panic('optional not set ($s)') +} + +// panic prints a nice error message, then exits the process with exit code of 1. +// It also shows a backtrace on most platforms. +[noreturn] +pub fn panic(s string) { + $if freestanding { + bare_panic(s) + } $else { + eprint('V panic: ') + eprintln(s) + eprintln('v hash: $vcommithash()') + $if exit_after_panic_message ? { + C.exit(1) + } $else $if no_backtrace ? { + C.exit(1) + } $else { + $if tinyc { + $if panics_break_into_debugger ? { + break_if_debugger_attached() + } $else { + C.tcc_backtrace(c'Backtrace') + } + C.exit(1) + } + print_backtrace_skipping_top_frames(1) + $if panics_break_into_debugger ? { + break_if_debugger_attached() + } + C.exit(1) + } + } + vhalt() +} + +// return a C-API error message matching to `errnum` +pub fn c_error_number_str(errnum int) string { + mut err_msg := '' + $if freestanding { + err_msg = 'error $errnum' + } $else { + $if !vinix { + c_msg := C.strerror(errnum) + err_msg = string{ + str: &byte(c_msg) + len: unsafe { C.strlen(c_msg) } + is_lit: 1 + } + } + } + return err_msg +} + +// panic with a C-API error message matching `errnum` +[noreturn] +pub fn panic_error_number(basestr string, errnum int) { + panic(basestr + c_error_number_str(errnum)) +} + +// eprintln prints a message with a line end, to stderr. Both stderr and stdout are flushed. +pub fn eprintln(s string) { + if s.str == 0 { + eprintln('eprintln(NIL)') + return + } + $if freestanding { + // flushing is only a thing with C.FILE from stdio.h, not on the syscall level + bare_eprint(s.str, u64(s.len)) + bare_eprint(c'\n', 1) + } $else $if ios { + C.WrappedNSLog(s.str) + } $else { + C.fflush(C.stdout) + C.fflush(C.stderr) + // eprintln is used in panics, so it should not fail at all + $if android { + C.fprintf(C.stderr, c'%.*s\n', s.len, s.str) + } + _writeln_to_fd(2, s) + C.fflush(C.stderr) + } +} + +// eprint prints a message to stderr. Both stderr and stdout are flushed. +pub fn eprint(s string) { + if s.str == 0 { + eprint('eprint(NIL)') + return + } + $if freestanding { + // flushing is only a thing with C.FILE from stdio.h, not on the syscall level + bare_eprint(s.str, u64(s.len)) + } $else $if ios { + // TODO: Implement a buffer as NSLog doesn't have a "print" + C.WrappedNSLog(s.str) + } $else { + C.fflush(C.stdout) + C.fflush(C.stderr) + $if android { + C.fprintf(C.stderr, c'%.*s', s.len, s.str) + } + _write_buf_to_fd(2, s.str, s.len) + C.fflush(C.stderr) + } +} + +// print prints a message to stdout. Unlike `println` stdout is not automatically flushed. +// A call to `flush()` will flush the output buffer to stdout. +[manualfree] +pub fn print(s string) { + $if android { + C.fprintf(C.stdout, c'%.*s', s.len, s.str) // logcat + } + // no else if for android termux support + $if ios { + // TODO: Implement a buffer as NSLog doesn't have a "print" + C.WrappedNSLog(s.str) + } $else $if freestanding { + bare_print(s.str, u64(s.len)) + } $else { + _write_buf_to_fd(1, s.str, s.len) + } +} + +// println prints a message with a line end, to stdout. stdout is flushed. +[manualfree] +pub fn println(s string) { + if s.str == 0 { + println('println(NIL)') + return + } + $if android { + C.fprintf(C.stdout, c'%.*s\n', s.len, s.str) // logcat + return + } + // no else if for android termux support + $if ios { + C.WrappedNSLog(s.str) + return + } $else $if freestanding { + bare_print(s.str, u64(s.len)) + bare_print(c'\n', 1) + return + } $else { + _writeln_to_fd(1, s) + } +} + +[manualfree] +fn _writeln_to_fd(fd int, s string) { + unsafe { + buf_len := s.len + 1 // space for \n + mut buf := malloc(buf_len) + defer { + free(buf) + } + C.memcpy(buf, s.str, s.len) + buf[s.len] = `\n` + _write_buf_to_fd(fd, buf, buf_len) + } +} + +[manualfree] +fn _write_buf_to_fd(fd int, buf &byte, buf_len int) { + if buf_len <= 0 { + return + } + unsafe { + mut ptr := buf + mut remaining_bytes := buf_len + for remaining_bytes > 0 { + x := C.write(fd, ptr, remaining_bytes) + ptr += x + remaining_bytes -= x + } + } +} + +__global total_m = i64(0) +// malloc dynamically allocates a `n` bytes block of memory on the heap. +// malloc returns a `byteptr` pointing to the memory address of the allocated space. +// unlike the `calloc` family of functions - malloc will not zero the memory block. +[unsafe] +pub fn malloc(n int) &byte { + if n <= 0 { + panic('> V malloc(<=0)') + } + $if vplayground ? { + if n > 10000 { + panic('allocating more than 10 KB at once is not allowed in the V playground') + } + if total_m > 50 * 1024 * 1024 { + panic('allocating more than 50 MB is not allowed in the V playground') + } + } + $if trace_malloc ? { + total_m += n + C.fprintf(C.stderr, c'_v_malloc %6d total %10d\n', n, total_m) + // print_backtrace() + } + mut res := &byte(0) + $if prealloc { + return unsafe { prealloc_malloc(n) } + } $else $if gcboehm ? { + unsafe { + res = C.GC_MALLOC(n) + } + } $else $if freestanding { + mut e := Errno{} + res, e = mm_alloc(u64(n)) + if e != .enoerror { + eprint('malloc() failed: ') + eprintln(e.str()) + panic('malloc() failed') + } + } $else { + res = unsafe { C.malloc(n) } + } + if res == 0 { + panic('malloc($n) failed') + } + $if debug_malloc ? { + // Fill in the memory with something != 0, so it is easier to spot + // when the calling code wrongly relies on it being zeroed. + unsafe { C.memset(res, 0x88, n) } + } + return res +} + +[unsafe] +pub fn malloc_noscan(n int) &byte { + if n <= 0 { + panic('> V malloc(<=0)') + } + $if vplayground ? { + if n > 10000 { + panic('allocating more than 10 KB at once is not allowed in the V playground') + } + if total_m > 50 * 1024 * 1024 { + panic('allocating more than 50 MB is not allowed in the V playground') + } + } + $if trace_malloc ? { + total_m += n + C.fprintf(C.stderr, c'_v_malloc %6d total %10d\n', n, total_m) + // print_backtrace() + } + mut res := &byte(0) + $if prealloc { + return unsafe { prealloc_malloc(n) } + } $else $if gcboehm ? { + $if gcboehm_opt ? { + unsafe { + res = C.GC_MALLOC_ATOMIC(n) + } + } $else { + unsafe { + res = C.GC_MALLOC(n) + } + } + } $else $if freestanding { + mut e := Errno{} + res, e = mm_alloc(u64(n)) + if e != .enoerror { + eprint('malloc() failed: ') + eprintln(e.str()) + panic('malloc() failed') + } + } $else { + res = unsafe { C.malloc(n) } + } + if res == 0 { + panic('malloc($n) failed') + } + $if debug_malloc ? { + // Fill in the memory with something != 0, so it is easier to spot + // when the calling code wrongly relies on it being zeroed. + unsafe { C.memset(res, 0x88, n) } + } + return res +} + +// v_realloc resizes the memory block `b` with `n` bytes. +// The `b byteptr` must be a pointer to an existing memory block +// previously allocated with `malloc`, `v_calloc` or `vcalloc`. +// Please, see also realloc_data, and use it instead if possible. +[unsafe] +pub fn v_realloc(b &byte, n int) &byte { + $if trace_realloc ? { + C.fprintf(C.stderr, c'v_realloc %6d\n', n) + } + mut new_ptr := &byte(0) + $if prealloc { + unsafe { + new_ptr = malloc(n) + C.memcpy(new_ptr, b, n) + } + return new_ptr + } $else $if gcboehm ? { + new_ptr = unsafe { C.GC_REALLOC(b, n) } + } $else { + new_ptr = unsafe { C.realloc(b, n) } + } + if new_ptr == 0 { + panic('realloc($n) failed') + } + return new_ptr +} + +// realloc_data resizes the memory block pointed by `old_data` to `new_size` +// bytes. `old_data` must be a pointer to an existing memory block, previously +// allocated with `malloc`, `v_calloc` or `vcalloc`, of size `old_data`. +// realloc_data returns a pointer to the new location of the block. +// NB: if you know the old data size, it is preferable to call `realloc_data`, +// instead of `v_realloc`, at least during development, because `realloc_data` +// can make debugging easier, when you compile your program with +// `-d debug_realloc`. +[unsafe] +pub fn realloc_data(old_data &byte, old_size int, new_size int) &byte { + $if trace_realloc ? { + C.fprintf(C.stderr, c'realloc_data old_size: %6d new_size: %6d\n', old_size, new_size) + } + $if prealloc { + return unsafe { prealloc_realloc(old_data, old_size, new_size) } + } + $if debug_realloc ? { + // NB: this is slower, but helps debugging memory problems. + // The main idea is to always force reallocating: + // 1) allocate a new memory block + // 2) copy the old to the new + // 3) fill the old with 0x57 (`W`) + // 4) free the old block + // => if there is still a pointer to the old block somewhere + // it will point to memory that is now filled with 0x57. + unsafe { + new_ptr := malloc(new_size) + min_size := if old_size < new_size { old_size } else { new_size } + C.memcpy(new_ptr, old_data, min_size) + C.memset(old_data, 0x57, old_size) + free(old_data) + return new_ptr + } + } + mut nptr := &byte(0) + $if gcboehm ? { + nptr = unsafe { C.GC_REALLOC(old_data, new_size) } + } $else { + nptr = unsafe { C.realloc(old_data, new_size) } + } + if nptr == 0 { + panic('realloc_data($old_data, $old_size, $new_size) failed') + } + return nptr +} + +// vcalloc dynamically allocates a zeroed `n` bytes block of memory on the heap. +// vcalloc returns a `byteptr` pointing to the memory address of the allocated space. +// Unlike `v_calloc` vcalloc checks for negative values given in `n`. +pub fn vcalloc(n int) &byte { + if n < 0 { + panic('calloc(<0)') + } else if n == 0 { + return &byte(0) + } + $if trace_vcalloc ? { + total_m += n + C.fprintf(C.stderr, c'vcalloc %6d total %10d\n', n, total_m) + } + $if prealloc { + return unsafe { prealloc_calloc(n) } + } $else $if gcboehm ? { + return unsafe { &byte(C.GC_MALLOC(n)) } + } $else { + return unsafe { C.calloc(1, n) } + } +} + +// special versions of the above that allocate memory which is not scanned +// for pointers (but is collected) when the Boehm garbage collection is used +pub fn vcalloc_noscan(n int) &byte { + $if trace_vcalloc ? { + total_m += n + C.fprintf(C.stderr, c'vcalloc_noscan %6d total %10d\n', n, total_m) + } + $if prealloc { + return unsafe { prealloc_calloc(n) } + } $else $if gcboehm ? { + $if vplayground ? { + if n > 10000 { + panic('allocating more than 10 KB is not allowed in the playground') + } + } + if n < 0 { + panic('calloc(<0)') + } + return $if gcboehm_opt ? { + unsafe { &byte(C.memset(C.GC_MALLOC_ATOMIC(n), 0, n)) } + } $else { + unsafe { &byte(C.GC_MALLOC(n)) } + } + } $else { + return unsafe { vcalloc(n) } + } +} + +// free allows for manually freeing memory allocated at the address `ptr`. +[unsafe] +pub fn free(ptr voidptr) { + $if prealloc { + return + } $else $if gcboehm ? { + // It is generally better to leave it to Boehm's gc to free things. + // Calling C.GC_FREE(ptr) was tried initially, but does not work + // well with programs that do manual management themselves. + // + // The exception is doing leak detection for manual memory management: + $if gcboehm_leak ? { + unsafe { C.GC_FREE(ptr) } + } + } $else { + C.free(ptr) + } +} + +// memdup dynamically allocates a `sz` bytes block of memory on the heap +// memdup then copies the contents of `src` into the allocated space and +// returns a pointer to the newly allocated space. +[unsafe] +pub fn memdup(src voidptr, sz int) voidptr { + if sz == 0 { + return vcalloc(1) + } + unsafe { + mem := malloc(sz) + return C.memcpy(mem, src, sz) + } +} + +[unsafe] +pub fn memdup_noscan(src voidptr, sz int) voidptr { + if sz == 0 { + return vcalloc_noscan(1) + } + unsafe { + mem := vcalloc_noscan(sz) + return C.memcpy(mem, src, sz) + } +} + +[inline] +fn v_fixed_index(i int, len int) int { + $if !no_bounds_checking ? { + if i < 0 || i >= len { + s := 'fixed array index out of range (index: $i, len: $len)' + panic(s) + } + } + return i +} + +// print_backtrace shows a backtrace of the current call stack on stdout +pub fn print_backtrace() { + // At the time of backtrace_symbols_fd call, the C stack would look something like this: + // * print_backtrace_skipping_top_frames + // * print_backtrace itself + // * the rest of the backtrace frames + // => top 2 frames should be skipped, since they will not be informative to the developer + $if !no_backtrace ? { + $if freestanding { + println(bare_backtrace()) + } $else { + $if tinyc { + C.tcc_backtrace(c'Backtrace') + } $else { + print_backtrace_skipping_top_frames(2) + } + } + } +} diff --git a/v_windows/v/vlib/builtin/builtin.v b/v_windows/v/vlib/builtin/builtin.v new file mode 100644 index 0000000..188e28d --- /dev/null +++ b/v_windows/v/vlib/builtin/builtin.v @@ -0,0 +1,128 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module builtin + +// isnil returns true if an object is nil (only for C objects). +[inline] +pub fn isnil(v voidptr) bool { + return v == 0 +} + +/* +fn on_panic(f fn(int)int) { + // TODO +} +*/ + +struct VCastTypeIndexName { + tindex int + tname string +} + +// will be filled in cgen +__global as_cast_type_indexes []VCastTypeIndexName + +fn __as_cast(obj voidptr, obj_type int, expected_type int) voidptr { + if obj_type != expected_type { + mut obj_name := as_cast_type_indexes[0].tname.clone() + mut expected_name := as_cast_type_indexes[0].tname.clone() + for x in as_cast_type_indexes { + if x.tindex == obj_type { + obj_name = x.tname.clone() + } + if x.tindex == expected_type { + expected_name = x.tname.clone() + } + } + panic('as cast: cannot cast `$obj_name` to `$expected_name`') + } + return obj +} + +// VAssertMetaInfo is used during assertions. An instance of it +// is filled in by compile time generated code, when an assertion fails. +pub struct VAssertMetaInfo { +pub: + fpath string // the source file path of the assertion + line_nr int // the line number of the assertion + fn_name string // the function name in which the assertion is + src string // the actual source line of the assertion + op string // the operation of the assertion, i.e. '==', '<', 'call', etc ... + llabel string // the left side of the infix expressions as source + rlabel string // the right side of the infix expressions as source + lvalue string // the stringified *actual value* of the left side of a failed assertion + rvalue string // the stringified *actual value* of the right side of a failed assertion +} + +// free is used to free the memory occupied by the assertion meta data. +// It is called by cb_assertion_failed, and cb_assertion_ok in the preludes, +// once they are done with reporting/formatting the meta data. +[manualfree; unsafe] +pub fn (ami &VAssertMetaInfo) free() { + unsafe { + ami.fpath.free() + ami.fn_name.free() + ami.src.free() + ami.op.free() + ami.llabel.free() + ami.rlabel.free() + ami.lvalue.free() + ami.rvalue.free() + } +} + +fn __print_assert_failure(i &VAssertMetaInfo) { + eprintln('$i.fpath:${i.line_nr + 1}: FAIL: fn $i.fn_name: assert $i.src') + if i.op.len > 0 && i.op != 'call' { + eprintln(' left value: $i.llabel = $i.lvalue') + if i.rlabel == i.rvalue { + eprintln(' right value: $i.rlabel') + } else { + eprintln(' right value: $i.rlabel = $i.rvalue') + } + } +} + +// MethodArgs holds type information for function and/or method arguments. +pub struct MethodArgs { +pub: + typ int + name string +} + +// FunctionData holds information about a parsed function. +pub struct FunctionData { +pub: + name string + attrs []string + args []MethodArgs + return_type int + typ int +} + +// FieldData holds information about a field. Fields reside on structs. +pub struct FieldData { +pub: + name string + attrs []string + is_pub bool + is_mut bool + is_shared bool + typ int +} + +pub enum AttributeKind { + plain // [name] + string // ['name'] + number // [123] + comptime_define // [if name] +} + +pub struct StructAttribute { +pub: + name string + has_arg bool + arg string + kind AttributeKind +} diff --git a/v_windows/v/vlib/builtin/builtin_d_gcboehm.c.v b/v_windows/v/vlib/builtin/builtin_d_gcboehm.c.v new file mode 100644 index 0000000..befec1d --- /dev/null +++ b/v_windows/v/vlib/builtin/builtin_d_gcboehm.c.v @@ -0,0 +1,91 @@ +module builtin + +#flag -DGC_THREADS=1 + +$if static_boehm ? { + $if macos { + #flag -I$first_existing("/opt/homebrew/include", "/usr/local/include") + #flag $first_existing("/opt/homebrew/lib/libgc.a", "/usr/local/lib/libgc.a") + } $else $if linux { + #flag -l:libgc.a + } $else $if openbsd { + #flag -I/usr/local/include + #flag /usr/local/lib/libgc.a + #flag -lpthread + } $else $if windows { + #flag -DGC_NOT_DLL=1 + $if tinyc { + #flag -I@VEXEROOT/thirdparty/libgc/include + #flag -L@VEXEROOT/thirdparty/libgc + #flag -lgc + } $else { + #flag -DGC_BUILTIN_ATOMIC=1 + #flag -I@VEXEROOT/thirdparty/libgc + #flag @VEXEROOT/thirdparty/libgc/gc.o + } + } $else { + #flag -lgc + } +} $else { + $if macos { + #pkgconfig bdw-gc + } $else $if openbsd || freebsd { + #flag -I/usr/local/include + #flag -L/usr/local/lib + } + $if windows { + $if tinyc { + #flag -I@VEXEROOT/thirdparty/libgc/include + #flag -L@VEXEROOT/thirdparty/libgc + #flag -lgc + } $else { + #flag -DGC_BUILTIN_ATOMIC=1 + #flag -I@VEXEROOT/thirdparty/libgc + #flag @VEXEROOT/thirdparty/libgc/gc.o + } + } $else { + #flag -lgc + } +} + +$if gcboehm_leak ? { + #flag -DGC_DEBUG=1 +} + +#include + +// replacements for `malloc()/calloc()`, `realloc()` and `free()` +// for use with Boehm-GC +// Do not use them manually. They are automatically chosen when +// compiled with `-gc boehm` or `-gc boehm_leak`. +fn C.GC_MALLOC(n size_t) voidptr + +fn C.GC_MALLOC_ATOMIC(n size_t) voidptr + +fn C.GC_MALLOC_UNCOLLECTABLE(n size_t) voidptr + +fn C.GC_REALLOC(ptr voidptr, n size_t) voidptr + +fn C.GC_FREE(ptr voidptr) + +// explicitely perform garbage collection now! Garbage collections +// are done automatically when needed, so this function is hardly needed +fn C.GC_gcollect() + +// functions to temporarily suspend/resume garbage collection +fn C.GC_disable() + +fn C.GC_enable() + +// returns non-zero if GC is disabled +fn C.GC_is_disabled() int + +// protect memory block from being freed before this call +fn C.GC_reachable_here(voidptr) + +// for leak detection it is advisable to do explicit garbage collections +pub fn gc_check_leaks() { + $if gcboehm_leak ? { + C.GC_gcollect() + } +} diff --git a/v_windows/v/vlib/builtin/builtin_ios.c.v b/v_windows/v/vlib/builtin/builtin_ios.c.v new file mode 100644 index 0000000..f745b4d --- /dev/null +++ b/v_windows/v/vlib/builtin/builtin_ios.c.v @@ -0,0 +1,6 @@ +module builtin + +// TODO: Remove this later, added to make sure v self works +$if ios { + #include "@VEXEROOT/thirdparty/ios/ios.m" +} diff --git a/v_windows/v/vlib/builtin/builtin_nix.c.v b/v_windows/v/vlib/builtin/builtin_nix.c.v new file mode 100644 index 0000000..0d79af4 --- /dev/null +++ b/v_windows/v/vlib/builtin/builtin_nix.c.v @@ -0,0 +1,144 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module builtin + +// pub fn vsyscall(id int +// + +/* +pub const ( + sys_write = 1 + sys_mkdir = 83 +) +const ( + stdin_value = 0 + stdout_value = 1 + stderr_value = 2 +) +*/ + +fn builtin_init() { + // Do nothing +} + +fn print_backtrace_skipping_top_frames(xskipframes int) bool { + $if no_backtrace ? { + return false + } $else { + skipframes := xskipframes + 2 + $if macos || freebsd || openbsd || netbsd { + return print_backtrace_skipping_top_frames_bsd(skipframes) + } $else $if linux { + return print_backtrace_skipping_top_frames_linux(skipframes) + } $else { + println('print_backtrace_skipping_top_frames is not implemented. skipframes: $skipframes') + } + } + return false +} + +// the functions below are not called outside this file, +// so there is no need to have their twins in builtin_windows.v +fn print_backtrace_skipping_top_frames_bsd(skipframes int) bool { + $if no_backtrace ? { + return false + } $else { + $if macos || freebsd || openbsd || netbsd { + buffer := [100]voidptr{} + nr_ptrs := C.backtrace(&buffer[0], 100) + if nr_ptrs < 2 { + eprintln('C.backtrace returned less than 2 frames') + return false + } + C.backtrace_symbols_fd(&buffer[skipframes], nr_ptrs - skipframes, 2) + } + return true + } +} + +fn C.tcc_backtrace(fmt &char) int +fn print_backtrace_skipping_top_frames_linux(skipframes int) bool { + $if android { + eprintln('On Android no backtrace is available.') + return false + } + $if !glibc { + eprintln('backtrace_symbols is missing => printing backtraces is not available.') + eprintln('Some libc implementations like musl simply do not provide it.') + return false + } + $if no_backtrace ? { + return false + } $else { + $if linux && !freestanding { + $if tinyc { + C.tcc_backtrace(c'Backtrace') + return false + } + buffer := [100]voidptr{} + nr_ptrs := C.backtrace(&buffer[0], 100) + if nr_ptrs < 2 { + eprintln('C.backtrace returned less than 2 frames') + return false + } + nr_actual_frames := nr_ptrs - skipframes + mut sframes := []string{} + //////csymbols := backtrace_symbols(*voidptr(&buffer[skipframes]), nr_actual_frames) + csymbols := C.backtrace_symbols(voidptr(&buffer[skipframes]), nr_actual_frames) + for i in 0 .. nr_actual_frames { + sframes << unsafe { tos2(&byte(csymbols[i])) } + } + for sframe in sframes { + executable := sframe.all_before('(') + addr := sframe.all_after('[').all_before(']') + beforeaddr := sframe.all_before('[') + cmd := 'addr2line -e $executable $addr' + // taken from os, to avoid depending on the os module inside builtin.v + f := C.popen(&char(cmd.str), c'r') + if isnil(f) { + eprintln(sframe) + continue + } + buf := [1000]byte{} + mut output := '' + unsafe { + bp := &buf[0] + for C.fgets(&char(bp), 1000, f) != 0 { + output += tos(bp, vstrlen(bp)) + } + } + output = output.trim_space() + ':' + if C.pclose(f) != 0 { + eprintln(sframe) + continue + } + if output in ['??:0:', '??:?:'] { + output = '' + } + // See http://wiki.dwarfstd.org/index.php?title=Path_Discriminators + // NB: it is shortened here to just d. , just so that it fits, and so + // that the common error file:lineno: line format is enforced. + output = output.replace(' (discriminator', ': (d.') + eprintln('${output:-55s} | ${addr:14s} | $beforeaddr') + } + } + } + return true +} + +fn break_if_debugger_attached() { + unsafe { + mut ptr := &voidptr(0) + *ptr = voidptr(0) + _ = ptr + } +} + +// These functions are Windows specific - provide dummys for *nix +pub fn winapi_lasterr_str() string { + return '' +} + +[noreturn] +pub fn panic_lasterr() {} diff --git a/v_windows/v/vlib/builtin/builtin_notd_gcboehm.c.v b/v_windows/v/vlib/builtin/builtin_notd_gcboehm.c.v new file mode 100644 index 0000000..4479072 --- /dev/null +++ b/v_windows/v/vlib/builtin/builtin_notd_gcboehm.c.v @@ -0,0 +1,20 @@ +module builtin + +// Just define the C functions, so that V does not error because of the missing definitions. + +// NB: they will NOT be used, since calls to them are wrapped with `$if gcboehm ? { }` + +fn C.GC_MALLOC(n size_t) voidptr + +fn C.GC_MALLOC_ATOMIC(n size_t) voidptr + +fn C.GC_MALLOC_UNCOLLECTABLE(n size_t) voidptr + +fn C.GC_REALLOC(ptr voidptr, n size_t) voidptr + +fn C.GC_FREE(ptr voidptr) + +// provide an empty function when manual memory management is used +// to simplify leak detection +// +pub fn gc_check_leaks() {} diff --git a/v_windows/v/vlib/builtin/builtin_windows.c.v b/v_windows/v/vlib/builtin/builtin_windows.c.v new file mode 100644 index 0000000..97592cf --- /dev/null +++ b/v_windows/v/vlib/builtin/builtin_windows.c.v @@ -0,0 +1,304 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module builtin + +// dbghelp.h is already included in cheaders.v +#flag windows -l dbghelp +// SymbolInfo is used by print_backtrace_skipping_top_frames_msvc +pub struct SymbolInfo { +pub mut: + f_size_of_struct u32 // must be 88 to be recognised by SymFromAddr + f_type_index u32 // Type Index of symbol + f_reserved [2]u64 + f_index u32 + f_size u32 + f_mod_base u64 // Base Address of module comtaining this symbol + f_flags u32 + f_value u64 // Value of symbol, ValuePresent should be 1 + f_address u64 // Address of symbol including base address of module + f_register u32 // register holding value or pointer to value + f_scope u32 // scope of the symbol + f_tag u32 // pdb classification + f_name_len u32 // Actual length of name + f_max_name_len u32 // must be manually set + f_name byte // must be calloc(f_max_name_len) +} + +pub struct SymbolInfoContainer { +pub mut: + syminfo SymbolInfo + f_name_rest [254]char +} + +pub struct Line64 { +pub mut: + f_size_of_struct u32 + f_key voidptr + f_line_number u32 + f_file_name &byte + f_address u64 +} + +// returns the current options mask +fn C.SymSetOptions(symoptions u32) u32 + +// returns handle +fn C.GetCurrentProcess() voidptr + +fn C.SymInitialize(h_process voidptr, p_user_search_path &byte, b_invade_process int) int + +fn C.CaptureStackBackTrace(frames_to_skip u32, frames_to_capture u32, p_backtrace voidptr, p_backtrace_hash voidptr) u16 + +fn C.SymFromAddr(h_process voidptr, address u64, p_displacement voidptr, p_symbol voidptr) int + +fn C.SymGetLineFromAddr64(h_process voidptr, address u64, p_displacement voidptr, p_line &Line64) int + +// Ref - https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-symsetoptions +const ( + symopt_undname = 0x00000002 + symopt_deferred_loads = 0x00000004 + symopt_no_cpp = 0x00000008 + symopt_load_lines = 0x00000010 + symopt_include_32bit_modules = 0x00002000 + symopt_allow_zero_address = 0x01000000 + symopt_debug = 0x80000000 +) + +// g_original_codepage - used to restore the original windows console code page when exiting +__global ( + g_original_codepage = u32(0) +) + +// utf8 to stdout needs C.SetConsoleOutputCP(C.CP_UTF8) +fn C.GetConsoleOutputCP() u32 + +fn C.SetConsoleOutputCP(wCodePageID u32) bool + +fn restore_codepage() { + C.SetConsoleOutputCP(g_original_codepage) +} + +fn is_terminal(fd int) int { + mut mode := u32(0) + osfh := voidptr(C._get_osfhandle(fd)) + C.GetConsoleMode(osfh, voidptr(&mode)) + return int(mode) +} + +fn builtin_init() { + g_original_codepage = C.GetConsoleOutputCP() + C.SetConsoleOutputCP(C.CP_UTF8) + C.atexit(restore_codepage) + if is_terminal(1) > 0 { + C.SetConsoleMode(C.GetStdHandle(C.STD_OUTPUT_HANDLE), C.ENABLE_PROCESSED_OUTPUT | C.ENABLE_WRAP_AT_EOL_OUTPUT | 0x0004) // enable_virtual_terminal_processing + C.SetConsoleMode(C.GetStdHandle(C.STD_ERROR_HANDLE), C.ENABLE_PROCESSED_OUTPUT | C.ENABLE_WRAP_AT_EOL_OUTPUT | 0x0004) // enable_virtual_terminal_processing + unsafe { + C.setbuf(C.stdout, 0) + C.setbuf(C.stderr, 0) + } + } + $if !no_backtrace ? { + add_unhandled_exception_handler() + } +} + +fn print_backtrace_skipping_top_frames(skipframes int) bool { + $if msvc { + return print_backtrace_skipping_top_frames_msvc(skipframes) + } + $if tinyc { + return print_backtrace_skipping_top_frames_tcc(skipframes) + } + $if mingw { + return print_backtrace_skipping_top_frames_mingw(skipframes) + } + eprintln('print_backtrace_skipping_top_frames is not implemented') + return false +} + +fn print_backtrace_skipping_top_frames_msvc(skipframes int) bool { + $if msvc { + mut offset := u64(0) + backtraces := [100]voidptr{} + sic := SymbolInfoContainer{} + mut si := &sic.syminfo + si.f_size_of_struct = sizeof(SymbolInfo) // Note: C.SYMBOL_INFO is 88 + si.f_max_name_len = sizeof(SymbolInfoContainer) - sizeof(SymbolInfo) - 1 + fname := &char(&si.f_name) + mut sline64 := Line64{ + f_file_name: &byte(0) + } + sline64.f_size_of_struct = sizeof(Line64) + + handle := C.GetCurrentProcess() + defer { + C.SymCleanup(handle) + } + + C.SymSetOptions(symopt_debug | symopt_load_lines | symopt_undname) + + syminitok := C.SymInitialize(handle, 0, 1) + if syminitok != 1 { + eprintln('Failed getting process: Aborting backtrace.\n') + return false + } + + frames := int(C.CaptureStackBackTrace(skipframes + 1, 100, &backtraces[0], 0)) + if frames < 2 { + eprintln('C.CaptureStackBackTrace returned less than 2 frames') + return false + } + for i in 0 .. frames { + frame_addr := backtraces[i] + if C.SymFromAddr(handle, frame_addr, &offset, si) == 1 { + nframe := frames - i - 1 + mut lineinfo := '' + if C.SymGetLineFromAddr64(handle, frame_addr, &offset, &sline64) == 1 { + file_name := unsafe { tos3(sline64.f_file_name) } + lnumber := sline64.f_line_number + lineinfo = '$file_name:$lnumber' + } else { + addr: + lineinfo = '?? : address = 0x${(&frame_addr):x}' + } + sfunc := unsafe { tos3(fname) } + eprintln('${nframe:-2d}: ${sfunc:-25s} $lineinfo') + } else { + // https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes + cerr := int(C.GetLastError()) + if cerr == 87 { + eprintln('SymFromAddr failure: $cerr = The parameter is incorrect)') + } else if cerr == 487 { + // probably caused because the .pdb isn't in the executable folder + eprintln('SymFromAddr failure: $cerr = Attempt to access invalid address (Verify that you have the .pdb file in the right folder.)') + } else { + eprintln('SymFromAddr failure: $cerr (see https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes)') + } + } + } + return true + } $else { + eprintln('print_backtrace_skipping_top_frames_msvc must be called only when the compiler is msvc') + return false + } +} + +fn print_backtrace_skipping_top_frames_mingw(skipframes int) bool { + eprintln('print_backtrace_skipping_top_frames_mingw is not implemented') + return false +} + +fn C.tcc_backtrace(fmt &char) int + +fn print_backtrace_skipping_top_frames_tcc(skipframes int) bool { + $if tinyc { + $if no_backtrace ? { + eprintln('backtraces are disabled') + return false + } $else { + C.tcc_backtrace(c'Backtrace') + return true + } + } $else { + eprintln('print_backtrace_skipping_top_frames_tcc must be called only when the compiler is tcc') + return false + } + // Not reachable, but it looks like it's not detectable by V + return false +} + +// TODO copypaste from os +// we want to be able to use this here without having to `import os` +struct ExceptionRecord { +pub: + // status_ constants + code u32 + flags u32 + record &ExceptionRecord + address voidptr + param_count u32 + // params []voidptr +} + +struct ContextRecord { + // TODO +} + +struct ExceptionPointers { +pub: + exception_record &ExceptionRecord + context_record &ContextRecord +} + +type VectoredExceptionHandler = fn (&ExceptionPointers) int + +fn C.AddVectoredExceptionHandler(int, C.PVECTORED_EXCEPTION_HANDLER) + +fn add_vectored_exception_handler(handler VectoredExceptionHandler) { + C.AddVectoredExceptionHandler(1, C.PVECTORED_EXCEPTION_HANDLER(handler)) +} + +[windows_stdcall] +fn unhandled_exception_handler(e &ExceptionPointers) int { + match e.exception_record.code { + // These are 'used' by the backtrace printer + // so we dont want to catch them... + 0x4001000A, 0x40010006 { + return 0 + } + else { + println('Unhandled Exception 0x${e.exception_record.code:X}') + print_backtrace_skipping_top_frames(5) + } + } + + return 0 +} + +fn add_unhandled_exception_handler() { + add_vectored_exception_handler(VectoredExceptionHandler(voidptr(unhandled_exception_handler))) +} + +fn C.IsDebuggerPresent() bool + +fn C.__debugbreak() + +fn break_if_debugger_attached() { + $if tinyc { + unsafe { + mut ptr := &voidptr(0) + *ptr = voidptr(0) + _ = ptr + } + } $else { + if C.IsDebuggerPresent() { + C.__debugbreak() + } + } +} + +// return an error message generated from WinAPI's `LastError` +pub fn winapi_lasterr_str() string { + err_msg_id := C.GetLastError() + if err_msg_id == 8 { + // handle this case special since `FormatMessage()` might not work anymore + return 'insufficient memory' + } + mut msgbuf := &u16(0) + res := C.FormatMessage(C.FORMAT_MESSAGE_ALLOCATE_BUFFER | C.FORMAT_MESSAGE_FROM_SYSTEM | C.FORMAT_MESSAGE_IGNORE_INSERTS, + C.NULL, err_msg_id, C.MAKELANGID(C.LANG_NEUTRAL, C.SUBLANG_DEFAULT), &msgbuf, + 0, C.NULL) + err_msg := if res == 0 { + 'Win-API error $err_msg_id' + } else { + unsafe { string_from_wide(msgbuf) } + } + return err_msg +} + +// panic with an error message generated from WinAPI's `LastError` +[noreturn] +pub fn panic_lasterr(base string) { + panic(base + winapi_lasterr_str()) +} diff --git a/v_windows/v/vlib/builtin/byte_test.v b/v_windows/v/vlib/builtin/byte_test.v new file mode 100644 index 0000000..8b38eb8 --- /dev/null +++ b/v_windows/v/vlib/builtin/byte_test.v @@ -0,0 +1,22 @@ +fn test_clone() { + a := [byte(0), 1, 2] + b := a.clone() + assert b.len == 3 + assert b[0] == 0 + assert b[1] == 1 + assert b[2] == 2 + assert b[1].str() == '1' + xx := byte(35) + assert xx.str() == '35' + assert xx.ascii_str() == '#' + println(typeof(`A`).name) + assert typeof(`A`).name == 'rune' + x := rune(`A`) + assert x.str() == 'A' + assert typeof(x).name == 'rune' + // + y := `Z` + assert typeof(y).name == 'rune' + assert y.str() == 'Z' + // assert b[1].str() == '1' TODO +} diff --git a/v_windows/v/vlib/builtin/cfns.c.v b/v_windows/v/vlib/builtin/cfns.c.v new file mode 100644 index 0000000..a45686d --- /dev/null +++ b/v_windows/v/vlib/builtin/cfns.c.v @@ -0,0 +1,462 @@ +module builtin + +// +fn C.memcpy(dest voidptr, const_src voidptr, n size_t) voidptr + +fn C.memcmp(const_s1 voidptr, const_s2 voidptr, n size_t) int + +fn C.memmove(dest voidptr, const_src voidptr, n size_t) voidptr + +fn C.memset(str voidptr, c int, n size_t) voidptr + +[trusted] +fn C.calloc(int, int) &byte + +fn C.malloc(int) &byte + +fn C.realloc(a &byte, b int) &byte + +fn C.free(ptr voidptr) + +[noreturn; trusted] +fn C.exit(code int) + +fn C.qsort(base voidptr, items size_t, item_size size_t, cb C.qsort_callback_func) + +fn C.sprintf(a ...voidptr) int + +fn C.strlen(s &char) int + +fn C.sscanf(&byte, &byte, ...&byte) int + +[trusted] +fn C.isdigit(c int) bool + +// stdio.h +fn C.popen(c &char, t &char) voidptr + +// +fn C.backtrace(a &voidptr, size int) int + +fn C.backtrace_symbols(a &voidptr, size int) &&char + +fn C.backtrace_symbols_fd(a &voidptr, size int, fd int) + +// +pub fn proc_pidpath(int, voidptr, int) int + +fn C.realpath(&char, &char) &char + +// fn C.chmod(byteptr, mode_t) int +fn C.chmod(&char, u32) int + +fn C.printf(&char, ...voidptr) int + +fn C.puts(&char) int + +fn C.fputs(str &char, stream &C.FILE) int + +fn C.fflush(&C.FILE) int + +// TODO define args in these functions +fn C.fseek(stream &C.FILE, offset int, whence int) int + +fn C.fopen(filename &char, mode &char) &C.FILE + +fn C.fileno(&C.FILE) int + +fn C.fread(ptr voidptr, item_size size_t, items size_t, stream &C.FILE) size_t + +fn C.fwrite(ptr voidptr, item_size size_t, items size_t, stream &C.FILE) size_t + +fn C.fclose(stream &C.FILE) int + +fn C.pclose(stream &C.FILE) int + +// process execution, os.process: +[trusted] +fn C.getpid() int + +fn C.getuid() int + +fn C.geteuid() int + +fn C.system(cmd &char) int + +fn C.posix_spawn(child_pid &int, path &char, file_actions voidptr, attrp voidptr, argv &&char, envp &&char) int + +fn C.posix_spawnp(child_pid &int, exefile &char, file_actions voidptr, attrp voidptr, argv &&char, envp &&char) int + +fn C.execve(cmd_path &char, args voidptr, envs voidptr) int + +fn C.execvp(cmd_path &char, args &&char) int + +fn C._execve(cmd_path &char, args voidptr, envs voidptr) int + +fn C._execvp(cmd_path &char, args &&char) int + +[trusted] +fn C.fork() int + +fn C.wait(status &int) int + +fn C.waitpid(pid int, status &int, options int) int + +[trusted] +fn C.kill(pid int, sig int) int + +fn C.setenv(&char, &char, int) int + +fn C.unsetenv(&char) int + +fn C.access(path &char, amode int) int + +fn C.remove(filename &char) int + +fn C.rmdir(path &char) int + +fn C.chdir(path &char) int + +fn C.rewind(stream &C.FILE) int + +fn C.stat(&char, voidptr) int + +fn C.lstat(path &char, buf &C.stat) int + +fn C.rename(old_filename &char, new_filename &char) int + +fn C.fgets(str &char, n int, stream &C.FILE) int + +[trusted] +fn C.sigemptyset() int + +fn C.getcwd(buf &char, size size_t) &char + +[trusted] +fn C.mktime() int + +fn C.gettimeofday(tv &C.timeval, tz &C.timezone) int + +[trusted] +fn C.sleep(seconds u32) u32 + +// fn C.usleep(usec useconds_t) int +[trusted] +fn C.usleep(usec u32) int + +fn C.opendir(&char) voidptr + +fn C.closedir(dirp &C.DIR) int + +// fn C.mkdir(path &char, mode mode_t) int +fn C.mkdir(path &char, mode u32) int + +// C.rand returns a pseudorandom integer from 0 (inclusive) to C.RAND_MAX (exclusive) +[trusted] +fn C.rand() int + +// C.srand seeds the internal PRNG with the given value. +[trusted] +fn C.srand(seed u32) + +fn C.atof(str &char) f64 + +[trusted] +fn C.tolower(c int) int + +[trusted] +fn C.toupper(c int) int + +[trusted] +fn C.getchar() int + +[trusted] +fn C.strerror(int) &char + +fn C.snprintf(str &char, size size_t, format &char, opt ...voidptr) int + +fn C.fprintf(voidptr, &char, ...voidptr) + +[trusted] +fn C.WIFEXITED(status int) bool + +[trusted] +fn C.WEXITSTATUS(status int) int + +[trusted] +fn C.WIFSIGNALED(status int) bool + +[trusted] +fn C.WTERMSIG(status int) int + +[trusted] +fn C.isatty(fd int) int + +fn C.syscall(number int, va ...voidptr) int + +fn C.sysctl(name &int, namelen u32, oldp voidptr, oldlenp voidptr, newp voidptr, newlen size_t) int + +[trusted] +fn C._fileno(int) int + +fn C._get_osfhandle(fd int) C.intptr_t + +fn C.GetModuleFileName(hModule voidptr, lpFilename &u16, nSize u32) int + +fn C.GetModuleFileNameW(hModule voidptr, lpFilename &u16, nSize u32) u32 + +fn C.CreateFile(lpFilename &u16, dwDesiredAccess u32, dwShareMode u32, lpSecurityAttributes &u16, dwCreationDisposition u32, dwFlagsAndAttributes u32, hTemplateFile voidptr) voidptr + +fn C.CreateFileW(lpFilename &u16, dwDesiredAccess u32, dwShareMode u32, lpSecurityAttributes &u16, dwCreationDisposition u32, dwFlagsAndAttributes u32, hTemplateFile voidptr) u32 + +fn C.GetFinalPathNameByHandleW(hFile voidptr, lpFilePath &u16, nSize u32, dwFlags u32) int + +fn C.CreatePipe(hReadPipe &voidptr, hWritePipe &voidptr, lpPipeAttributes voidptr, nSize u32) bool + +fn C.SetHandleInformation(hObject voidptr, dwMask u32, dw_flags u32) bool + +fn C.ExpandEnvironmentStringsW(lpSrc &u16, lpDst &u16, nSize u32) u32 + +fn C.GetComputerNameW(&u16, &u32) bool + +fn C.GetUserNameW(&u16, &u32) bool + +[trusted] +fn C.SendMessageTimeout() u32 + +fn C.SendMessageTimeoutW(hWnd voidptr, msg u32, wParam &u16, lParam &u32, fuFlags u32, uTimeout u32, lpdwResult &u64) u32 + +fn C.CreateProcessW(lpApplicationName &u16, lpCommandLine &u16, lpProcessAttributes voidptr, lpThreadAttributes voidptr, bInheritHandles bool, dwCreationFlags u32, lpEnvironment voidptr, lpCurrentDirectory &u16, lpStartupInfo voidptr, lpProcessInformation voidptr) bool + +fn C.ReadFile(hFile voidptr, lpBuffer voidptr, nNumberOfBytesToRead u32, lpNumberOfBytesRead C.LPDWORD, lpOverlapped voidptr) bool + +fn C.GetFileAttributesW(lpFileName &byte) u32 + +fn C.RegQueryValueEx(hKey voidptr, lpValueName &u16, lp_reserved &u32, lpType &u32, lpData &byte, lpcbData &u32) voidptr + +fn C.RegQueryValueExW(hKey voidptr, lpValueName &u16, lp_reserved &u32, lpType &u32, lpData &byte, lpcbData &u32) int + +fn C.RegOpenKeyEx(hKey voidptr, lpSubKey &u16, ulOptions u32, samDesired u32, phkResult voidptr) voidptr + +fn C.RegOpenKeyExW(hKey voidptr, lpSubKey &u16, ulOptions u32, samDesired u32, phkResult voidptr) int + +fn C.RegSetValueEx() voidptr + +fn C.RegSetValueExW(hKey voidptr, lpValueName &u16, reserved u32, dwType u32, lpData &byte, lpcbData u32) int + +fn C.RegCloseKey(hKey voidptr) + +fn C.RemoveDirectory(lpPathName &u16) int + +// fn C.GetStdHandle() voidptr +fn C.GetStdHandle(u32) voidptr + +// fn C.SetConsoleMode() +fn C.SetConsoleMode(voidptr, u32) int + +// fn C.GetConsoleMode() int +fn C.GetConsoleMode(voidptr, &u32) int + +[trusted] +fn C.GetCurrentProcessId() int + +fn C.wprintf() + +// fn C.setbuf() +fn C.setbuf(voidptr, &char) + +fn C.SymCleanup(hProcess voidptr) + +fn C.MultiByteToWideChar(codePage u32, dwFlags u32, lpMultiMyteStr &char, cbMultiByte int, lpWideCharStr &u16, cchWideChar int) int + +fn C.wcslen(str &u16) int + +fn C.WideCharToMultiByte(codePage u32, dwFlags u32, lpWideCharStr &u16, cchWideChar int, lpMultiByteStr &char, cbMultiByte int, lpDefaultChar &char, lpUsedDefaultChar &int) int + +fn C._wstat(path &u16, buffer &C._stat) int + +fn C._wrename(oldname &u16, newname &u16) int + +fn C._wfopen(filename &u16, mode &u16) voidptr + +fn C._wpopen(command &u16, mode &u16) voidptr + +fn C._pclose(stream &C.FILE) int + +fn C._wsystem(command &u16) int + +fn C._wgetenv(varname &u16) voidptr + +fn C._putenv(envstring &char) int + +fn C._waccess(path &u16, mode int) int + +fn C._wremove(path &u16) int + +fn C.ReadConsole(in_input_handle voidptr, out_buffer voidptr, in_chars_to_read u32, out_read_chars &u32, in_input_control voidptr) bool + +fn C.WriteConsole() voidptr + +fn C.WriteFile() voidptr + +fn C._wchdir(dirname &u16) + +fn C._wgetcwd(buffer &u16, maxlen int) int + +fn C._fullpath() int + +fn C.GetFullPathName(voidptr, u32, voidptr, voidptr) u32 + +[trusted] +fn C.GetCommandLine() voidptr + +fn C.LocalFree() + +fn C.FindFirstFileW(lpFileName &u16, lpFindFileData voidptr) voidptr + +fn C.FindFirstFile(lpFileName &byte, lpFindFileData voidptr) voidptr + +fn C.FindNextFile(hFindFile voidptr, lpFindFileData voidptr) int + +fn C.FindClose(hFindFile voidptr) + +// macro +fn C.MAKELANGID(lgid voidptr, srtid voidptr) int + +fn C.FormatMessage(dwFlags u32, lpSource voidptr, dwMessageId u32, dwLanguageId u32, lpBuffer voidptr, nSize int, arguments ...voidptr) voidptr + +fn C.CloseHandle(voidptr) int + +fn C.GetExitCodeProcess(hProcess voidptr, lpExitCode &u32) + +[trusted] +fn C.GetTickCount() i64 + +[trusted] +fn C.Sleep(dwMilliseconds u32) + +fn C.WSAStartup(u16, &voidptr) int + +fn C.WSAGetLastError() int + +fn C.closesocket(int) int + +fn C.vschannel_init(&C.TlsContext) + +fn C.request(&C.TlsContext, int, &u16, &byte, &&byte) int + +fn C.vschannel_cleanup(&C.TlsContext) + +fn C.URLDownloadToFile(int, &u16, &u16, int, int) + +fn C.GetLastError() u32 + +fn C.CreateDirectory(&byte, int) bool + +// win crypto +fn C.BCryptGenRandom(int, voidptr, int, int) int + +// win synchronization +fn C.CreateMutex(int, bool, &byte) voidptr + +fn C.WaitForSingleObject(voidptr, int) int + +fn C.ReleaseMutex(voidptr) bool + +fn C.CreateEvent(int, bool, bool, &byte) voidptr + +fn C.SetEvent(voidptr) int + +fn C.CreateSemaphore(voidptr, int, int, voidptr) voidptr + +fn C.ReleaseSemaphore(voidptr, int, voidptr) voidptr + +fn C.InitializeSRWLock(voidptr) + +fn C.AcquireSRWLockShared(voidptr) + +fn C.AcquireSRWLockExclusive(voidptr) + +fn C.ReleaseSRWLockShared(voidptr) + +fn C.ReleaseSRWLockExclusive(voidptr) + +// pthread.h +fn C.pthread_mutex_init(voidptr, voidptr) int + +fn C.pthread_mutex_lock(voidptr) int + +fn C.pthread_mutex_unlock(voidptr) int + +fn C.pthread_mutex_destroy(voidptr) int + +fn C.pthread_rwlockattr_init(voidptr) int + +fn C.pthread_rwlockattr_setkind_np(voidptr, int) int + +fn C.pthread_rwlockattr_setpshared(voidptr, int) int + +fn C.pthread_rwlock_init(voidptr, voidptr) int + +fn C.pthread_rwlock_rdlock(voidptr) int + +fn C.pthread_rwlock_wrlock(voidptr) int + +fn C.pthread_rwlock_unlock(voidptr) int + +fn C.pthread_condattr_init(voidptr) int + +fn C.pthread_condattr_setpshared(voidptr, int) int + +fn C.pthread_condattr_destroy(voidptr) int + +fn C.pthread_cond_init(voidptr, voidptr) int + +fn C.pthread_cond_signal(voidptr) int + +fn C.pthread_cond_wait(voidptr, voidptr) int + +fn C.pthread_cond_timedwait(voidptr, voidptr, voidptr) int + +fn C.pthread_cond_destroy(voidptr) int + +fn C.sem_init(voidptr, int, u32) int + +fn C.sem_post(voidptr) int + +fn C.sem_wait(voidptr) int + +fn C.sem_trywait(voidptr) int + +fn C.sem_timedwait(voidptr, voidptr) int + +fn C.sem_destroy(voidptr) int + +// MacOS semaphore functions +fn C.dispatch_semaphore_create(i64) voidptr + +fn C.dispatch_semaphore_signal(voidptr) i64 + +fn C.dispatch_semaphore_wait(voidptr, u64) i64 + +fn C.dispatch_time(u64, i64) u64 + +fn C.dispatch_release(voidptr) + +// file descriptor based reading/writing +fn C.read(fd int, buf voidptr, count size_t) int + +fn C.write(fd int, buf voidptr, count size_t) int + +fn C.close(fd int) int + +// pipes +fn C.pipe(pipefds &int) int + +fn C.dup2(oldfd int, newfd int) int + +// used by gl, stbi, freetype +fn C.glTexImage2D() + +// used by ios for println +fn C.WrappedNSLog(str &byte) diff --git a/v_windows/v/vlib/builtin/cfns_wrapper.c.v b/v_windows/v/vlib/builtin/cfns_wrapper.c.v new file mode 100644 index 0000000..06f1348 --- /dev/null +++ b/v_windows/v/vlib/builtin/cfns_wrapper.c.v @@ -0,0 +1,72 @@ +module builtin + +// vstrlen returns the V length of the C string `s` (0 terminator is not counted). +// The C string is expected to be a &byte pointer. +[inline; unsafe] +pub fn vstrlen(s &byte) int { + return unsafe { C.strlen(&char(s)) } +} + +// vstrlen_char returns the V length of the C string `s` (0 terminator is not counted). +// The C string is expected to be a &char pointer. +[inline; unsafe] +pub fn vstrlen_char(s &char) int { + return unsafe { C.strlen(s) } +} + +// vmemcpy copies n bytes from memory area src to memory area dest. +// The memory areas *MUST NOT OVERLAP*. Use vmemmove, if the memory +// areas do overlap. vmemcpy returns a pointer to `dest`. +[inline; unsafe] +pub fn vmemcpy(dest voidptr, const_src voidptr, n int) voidptr { + unsafe { + return C.memcpy(dest, const_src, n) + } +} + +// vmemmove copies n bytes from memory area `src` to memory area `dest`. +// The memory areas *MAY* overlap: copying takes place as though the bytes +// in `src` are first copied into a temporary array that does not overlap +// `src` or `dest`, and the bytes are then copied from the temporary array +// to `dest`. vmemmove returns a pointer to `dest`. +[inline; unsafe] +pub fn vmemmove(dest voidptr, const_src voidptr, n int) voidptr { + unsafe { + return C.memmove(dest, const_src, n) + } +} + +// vmemcmp compares the first n bytes (each interpreted as unsigned char) +// of the memory areas s1 and s2. It returns an integer less than, equal to, +// or greater than zero, if the first n bytes of s1 is found, respectively, +// to be less than, to match, or be greater than the first n bytes of s2. +// For a nonzero return value, the sign is determined by the sign of the +// difference between the first pair of bytes (interpreted as unsigned char) +// that differ in s1 and s2. +// If n is zero, the return value is zero. +// Do NOT use vmemcmp to compare security critical data, such as cryptographic +// secrets, because the required CPU time depends on the number of equal bytes. +// You should use a function that performs comparisons in constant time for +// this. +[inline; unsafe] +pub fn vmemcmp(const_s1 voidptr, const_s2 voidptr, n int) int { + unsafe { + return C.memcmp(const_s1, const_s2, n) + } +} + +// vmemset fills the first `n` bytes of the memory area pointed to by `s`, +// with the constant byte `c`. It returns a pointer to the memory area `s`. +[inline; unsafe] +pub fn vmemset(s voidptr, c int, n int) voidptr { + unsafe { + return C.memset(s, c, n) + } +} + +type FnSortCB = fn (const_a voidptr, const_b voidptr) int + +[inline; unsafe] +fn vqsort(base voidptr, nmemb size_t, size size_t, sort_cb FnSortCB) { + C.qsort(base, nmemb, size, voidptr(sort_cb)) +} diff --git a/v_windows/v/vlib/builtin/chan.v b/v_windows/v/vlib/builtin/chan.v new file mode 100644 index 0000000..1692d5f --- /dev/null +++ b/v_windows/v/vlib/builtin/chan.v @@ -0,0 +1,32 @@ +module builtin + +// ChanState describes the result of an attempted channel transaction. +pub enum ChanState { + success + not_ready // push()/pop() would have to wait, but no_block was requested + closed +} + +/* +The following methods are only stubs. +The real implementation is in `vlib/sync/channels.v` +*/ + +// close closes the channel for further push transactions. +// closed channels cannot be pushed to, however they can be popped +// from as long as there is still objects available in the channel buffer. +pub fn (ch chan) close() {} + +// try_pop returns `ChanState.success` if an object is popped from the channel. +// try_pop effectively pops from the channel without waiting for objects to become available. +// Both the test and pop transaction is done atomically. +pub fn (ch chan) try_pop(obj voidptr) ChanState { + return .success +} + +// try_push returns `ChanState.success` if the object is pushed to the channel. +// try_push effectively both push and test if the transaction `ch <- a` succeeded. +// Both the test and push transaction is done atomically. +pub fn (ch chan) try_push(obj voidptr) ChanState { + return .success +} diff --git a/v_windows/v/vlib/builtin/float.c.v b/v_windows/v/vlib/builtin/float.c.v new file mode 100644 index 0000000..99fe8b8 --- /dev/null +++ b/v_windows/v/vlib/builtin/float.c.v @@ -0,0 +1,205 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license that can be found in the LICENSE file. +module builtin + +import strconv + +#include +/* +----------------------------------- +----- f64 to string functions ----- +*/ +// str return a `f64` as `string` in suitable notation. +[inline] +pub fn (x f64) str() string { + unsafe { + f := strconv.Float64u{ + f: x + } + if f.u == strconv.double_minus_zero { + return '-0' + } + if f.u == strconv.double_plus_zero { + return '0' + } + } + abs_x := f64_abs(x) + if abs_x >= 0.0001 && abs_x < 1.0e6 { + return strconv.f64_to_str_l(x) + } else { + return strconv.ftoa_64(x) + } +} + +// strg return a `f64` as `string` in "g" printf format +[inline] +pub fn (x f64) strg() string { + if x == 0 { + return '0' + } + abs_x := f64_abs(x) + if abs_x >= 0.0001 && abs_x < 1.0e6 { + return strconv.f64_to_str_l_no_dot(x) + } else { + return strconv.ftoa_64(x) + } +} + +// str returns the value of the `float_literal` as a `string`. +[inline] +pub fn (d float_literal) str() string { + return f64(d).str() +} + +// strsci returns the `f64` as a `string` in scientific notation with `digit_num` decimals displayed, max 17 digits. +// Example: assert f64(1.234).strsci(3) == '1.234e+00' +[inline] +pub fn (x f64) strsci(digit_num int) string { + mut n_digit := digit_num + if n_digit < 1 { + n_digit = 1 + } else if n_digit > 17 { + n_digit = 17 + } + return strconv.f64_to_str(x, n_digit) +} + +// strlong returns a decimal notation of the `f64` as a `string`. +// Example: assert f64(1.23456).strlong() == '1.23456' +[inline] +pub fn (x f64) strlong() string { + return strconv.f64_to_str_l(x) +} + +/* +----------------------------------- +----- f32 to string functions ----- +*/ +// str returns a `f32` as `string` in suitable notation. +[inline] +pub fn (x f32) str() string { + unsafe { + f := strconv.Float32u{ + f: x + } + if f.u == strconv.single_minus_zero { + return '-0' + } + if f.u == strconv.single_plus_zero { + return '0' + } + } + abs_x := f32_abs(x) + if abs_x >= 0.0001 && abs_x < 1.0e6 { + return strconv.f32_to_str_l(x) + } else { + return strconv.ftoa_32(x) + } +} + +// strg return a `f32` as `string` in "g" printf format +[inline] +pub fn (x f32) strg() string { + if x == 0 { + return '0' + } + abs_x := f32_abs(x) + if abs_x >= 0.0001 && abs_x < 1.0e6 { + return strconv.f32_to_str_l_no_dot(x) + } else { + return strconv.ftoa_32(x) + } +} + +// strsci returns the `f32` as a `string` in scientific notation with `digit_num` deciamals displayed, max 8 digits. +// Example: assert f32(1.234).strsci(3) == '1.234e+00' +[inline] +pub fn (x f32) strsci(digit_num int) string { + mut n_digit := digit_num + if n_digit < 1 { + n_digit = 1 + } else if n_digit > 8 { + n_digit = 8 + } + return strconv.f32_to_str(x, n_digit) +} + +// strlong returns a decimal notation of the `f32` as a `string`. +[inline] +pub fn (x f32) strlong() string { + return strconv.f32_to_str_l(x) +} + +/* +----------------------- +----- C functions ----- +*/ +// f32_abs returns the absolute value of `a` as a `f32` value. +// Example: assert f32_abs(-2.0) == 2.0 +[inline] +pub fn f32_abs(a f32) f32 { + return if a < 0 { -a } else { a } +} + +// f64_abs returns the absolute value of `a` as a `f64` value. +// Example: assert f64_abs(-2.0) == f64(2.0) +[inline] +fn f64_abs(a f64) f64 { + return if a < 0 { -a } else { a } +} + +// f32_max returns the largest `f32` of input `a` and `b`. +// Example: assert f32_max(2.0,3.0) == 3.0 +[inline] +pub fn f32_max(a f32, b f32) f32 { + return if a > b { a } else { b } +} + +// f32_min returns the smallest `f32` of input `a` and `b`. +// Example: assert f32_min(2.0,3.0) == 2.0 +[inline] +pub fn f32_min(a f32, b f32) f32 { + return if a < b { a } else { b } +} + +// f64_max returns the largest `f64` of input `a` and `b`. +// Example: assert f64_max(2.0,3.0) == 3.0 +[inline] +pub fn f64_max(a f64, b f64) f64 { + return if a > b { a } else { b } +} + +// f64_min returns the smallest `f64` of input `a` and `b`. +// Example: assert f64_min(2.0,3.0) == 2.0 +[inline] +fn f64_min(a f64, b f64) f64 { + return if a < b { a } else { b } +} + +// eq_epsilon returns true if the `f32` is equal to input `b`. +// using an epsilon of typically 1E-5 or higher (backend/compiler dependent). +// Example: assert f32(2.0).eq_epsilon(2.0) +[inline] +pub fn (a f32) eq_epsilon(b f32) bool { + hi := f32_max(f32_abs(a), f32_abs(b)) + delta := f32_abs(a - b) + if hi > f32(1.0) { + return delta <= hi * (4 * f32(C.FLT_EPSILON)) + } else { + return (1 / (4 * f32(C.FLT_EPSILON))) * delta <= hi + } +} + +// eq_epsilon returns true if the `f64` is equal to input `b`. +// using an epsilon of typically 1E-9 or higher (backend/compiler dependent). +// Example: assert f64(2.0).eq_epsilon(2.0) +[inline] +pub fn (a f64) eq_epsilon(b f64) bool { + hi := f64_max(f64_abs(a), f64_abs(b)) + delta := f64_abs(a - b) + if hi > 1.0 { + return delta <= hi * (4 * f64(C.DBL_EPSILON)) + } else { + return (1 / (4 * f64(C.DBL_EPSILON))) * delta <= hi + } +} diff --git a/v_windows/v/vlib/builtin/float_test.v b/v_windows/v/vlib/builtin/float_test.v new file mode 100644 index 0000000..cad2799 --- /dev/null +++ b/v_windows/v/vlib/builtin/float_test.v @@ -0,0 +1,147 @@ +fn test_float_decl() { + // z := 1f + // assert z > 0 + x1 := 1e10 + x2 := -2e16 + x3 := 1e-15 + x4 := -9e-4 + assert typeof(x1).name == 'f64' + assert typeof(x2).name == 'f64' + assert typeof(x3).name == 'f64' + assert typeof(x4).name == 'f64' + x5 := 4e108 + x6 := -7e99 + x7 := 3e-205 + x8 := -6e-147 + assert typeof(x5).name == 'f64' + assert typeof(x6).name == 'f64' + assert typeof(x7).name == 'f64' + assert typeof(x8).name == 'f64' + x9 := 312874834.77 + x10 := -22399994.06 + x11 := 0.0000000019 + x12 := -0.00000000008 + assert typeof(x9).name == 'f64' + assert typeof(x10).name == 'f64' + assert typeof(x11).name == 'f64' + assert typeof(x12).name == 'f64' + x13 := 34234234809890890898903213154353453453253253243432413232228908902183918392183902432432438980380123021983901392183921389083913890389089031.0 + x14 := -39999999999999999999222212128182813294989082302832183928343325325233253242312331324392839238239829389038097438248932789371837218372837293.8 + x15 := 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002 + x16 := -0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004 + assert typeof(x13).name == 'f64' + assert typeof(x14).name == 'f64' + assert typeof(x15).name == 'f64' + assert typeof(x16).name == 'f64' +} + +fn test_f32_equal_operator() { + b := f32(1.0) + mut a := f32(1.0) + a += 0.0000019073486328125 + assert a != b + a -= 0.0000019073486328125 + assert a == b + assert -1 == 1 * -1 + assert -1.0 == 1.0 * -1.0 + a = 1 + a += 0.0000019073486328125 + a -= 0.0000019073486328125 + assert a == f32(1.0) + a += 0.000001 + assert !(a < f32(1)) + assert !(a <= f32(1)) + assert a > f32(1) + assert a >= 1 + assert a != 1 + f := 1.2 + ab := int(f) + assert ab == 1 + e := f32(-1.602176634e-19) + m := f32(9.1093837015e-31) + assert e < m + assert e <= m + assert e != m + assert !(e == m) + assert m >= e + assert m > e +} + +fn test_f64_equal_operator() { + b := 1.0 + mut a := 1.0 + a += 0.0000019073486328125 + assert a != b + a -= 0.0000019073486328125 + assert a == b + e := -1.602176634e-19 + m := 9.1093837015e-31 + assert e < m + assert e <= m + assert e != m + assert !(e == m) + assert m >= e + assert m > e +} + +fn test_f64_eq_epsilon() { + a := 1.662248544459347e308 + b := 1.662248544459348e308 + x := 1.662248544459352e308 + assert a != b + assert a.eq_epsilon(b) + assert b.eq_epsilon(a) + assert (-a).eq_epsilon(-b) + assert (-b).eq_epsilon(-a) + assert !a.eq_epsilon(x) + assert !x.eq_epsilon(a) + assert !a.eq_epsilon(-b) + assert !(-a).eq_epsilon(b) + c := 1.5367748374385438503 + d := -1.5367748374385447257 + z := 1.5367748378943546 + assert c != -d + assert c.eq_epsilon(-d) + assert d.eq_epsilon(-c) + assert !c.eq_epsilon(z) + assert !z.eq_epsilon(c) + e := 2.531434251587394233e-308 + f := 2.531434251587395675e-308 + y := 2.531434251587398934e-308 + assert e != f + assert e.eq_epsilon(f) + assert (-f).eq_epsilon(-e) + assert !e.eq_epsilon(y) + assert !(-y).eq_epsilon(-e) +} + +fn test_f32_eq_epsilon() { + a := f32(3.244331e38) + b := f32(3.244332e38) + x := f32(3.244338e38) + assert a != b + assert a.eq_epsilon(b) + assert b.eq_epsilon(a) + assert (-a).eq_epsilon(-b) + assert (-b).eq_epsilon(-a) + assert !a.eq_epsilon(x) + assert !(-x).eq_epsilon(-a) + assert !a.eq_epsilon(-b) + assert !(-a).eq_epsilon(b) + c := f32(0.9546742) + d := f32(-0.9546745) + z := f32(0.9546754) + assert c != -d + assert c.eq_epsilon(-d) + assert d.eq_epsilon(-c) + assert !c.eq_epsilon(z) + assert !z.eq_epsilon(c) + e := f32(-1.5004390e-38) + f := f32(-1.5004395e-38) + y := f32(-1.5004409e-38) + assert e != f + assert e.eq_epsilon(f) + assert (-f).eq_epsilon(-e) + assert !e.eq_epsilon(y) + assert !(-y).eq_epsilon(-e) +} diff --git a/v_windows/v/vlib/builtin/float_x64.v b/v_windows/v/vlib/builtin/float_x64.v new file mode 100644 index 0000000..4a491a0 --- /dev/null +++ b/v_windows/v/vlib/builtin/float_x64.v @@ -0,0 +1,6 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license that can be found in the LICENSE file. +module builtin + +fn float_test() { +} diff --git a/v_windows/v/vlib/builtin/int.v b/v_windows/v/vlib/builtin/int.v new file mode 100644 index 0000000..24e658f --- /dev/null +++ b/v_windows/v/vlib/builtin/int.v @@ -0,0 +1,481 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module builtin + +// +// ----- value to string functions ----- +// + +type u8 = byte + +// ptr_str returns the address of `ptr` as a `string`. +pub fn ptr_str(ptr voidptr) string { + buf1 := u64(ptr).hex() + return buf1 +} + +pub fn (x size_t) str() string { + return u64(x).str() +} + +pub fn (cptr &char) str() string { + return u64(cptr).hex() +} + +const ( + // digit pairs in reverse order + digit_pairs = '00102030405060708090011121314151617181910212223242526272829203132333435363738393041424344454647484940515253545556575859506162636465666768696071727374757677787970818283848586878889809192939495969798999' +) + +// This implementation is the quickest with gcc -O2 +// str_l returns the string representation of the integer nn with max chars. +[direct_array_access; inline] +fn (nn int) str_l(max int) string { + unsafe { + mut n := i64(nn) + mut d := 0 + if n == 0 { + return '0' + } + + mut is_neg := false + if n < 0 { + n = -n + is_neg = true + } + mut index := max + mut buf := malloc_noscan(max + 1) + buf[index] = 0 + index-- + + for n > 0 { + n1 := int(n / 100) + // calculate the digit_pairs start index + d = ((int(n) - (n1 * 100)) << 1) + n = n1 + buf[index] = digit_pairs.str[d] + index-- + d++ + buf[index] = digit_pairs.str[d] + index-- + } + index++ + // remove head zero + if d < 20 { + index++ + } + // Prepend - if it's negative + if is_neg { + index-- + buf[index] = `-` + } + diff := max - index + vmemmove(buf, buf + index, diff + 1) + /* + // === manual memory move for bare metal === + mut c:= 0 + for c < diff { + buf[c] = buf[c+index] + c++ + } + buf[c] = 0 + */ + return tos(buf, diff) + + // return tos(memdup(&buf[0] + index, (max - index)), (max - index)) + } +} + +// str returns the value of the `i8` as a `string`. +// Example: assert i8(-2).str() == '-2' +pub fn (n i8) str() string { + return int(n).str_l(5) +} + +// str returns the value of the `i16` as a `string`. +// Example: assert i16(-20).str() == '-20' +pub fn (n i16) str() string { + return int(n).str_l(7) +} + +// str returns the value of the `u16` as a `string`. +// Example: assert u16(20).str() == '20' +pub fn (n u16) str() string { + return int(n).str_l(7) +} + +// str returns the value of the `int` as a `string`. +// Example: assert int(-2020).str() == '-2020' +pub fn (n int) str() string { + return n.str_l(12) +} + +// str returns the value of the `u32` as a `string`. +// Example: assert u32(20000).str() == '20000' +[direct_array_access; inline] +pub fn (nn u32) str() string { + unsafe { + mut n := nn + mut d := u32(0) + if n == 0 { + return '0' + } + max := 12 + mut buf := malloc_noscan(max + 1) + mut index := max + buf[index] = 0 + index-- + for n > 0 { + n1 := n / u32(100) + d = ((n - (n1 * u32(100))) << u32(1)) + n = n1 + buf[index] = digit_pairs[d] + index-- + d++ + buf[index] = digit_pairs[d] + index-- + } + index++ + // remove head zero + if d < u32(20) { + index++ + } + diff := max - index + vmemmove(buf, buf + index, diff + 1) + return tos(buf, diff) + + // return tos(memdup(&buf[0] + index, (max - index)), (max - index)) + } +} + +// str returns the value of the `int_literal` as a `string`. +[inline] +pub fn (n int_literal) str() string { + return i64(n).str() +} + +// str returns the value of the `i64` as a `string`. +// Example: assert i64(-200000).str() == '-200000' +[direct_array_access; inline] +pub fn (nn i64) str() string { + unsafe { + mut n := nn + mut d := i64(0) + if n == 0 { + return '0' + } + max := 20 + mut buf := malloc_noscan(max + 1) + mut is_neg := false + if n < 0 { + n = -n + is_neg = true + } + mut index := max + buf[index] = 0 + index-- + for n > 0 { + n1 := n / i64(100) + d = ((n - (n1 * i64(100))) << i64(1)) + n = n1 + buf[index] = digit_pairs[d] + index-- + d++ + buf[index] = digit_pairs[d] + index-- + } + index++ + // remove head zero + if d < i64(20) { + index++ + } + // Prepend - if it's negative + if is_neg { + index-- + buf[index] = `-` + } + diff := max - index + vmemmove(buf, buf + index, diff + 1) + return tos(buf, diff) + // return tos(memdup(&buf[0] + index, (max - index)), (max - index)) + } +} + +// str returns the value of the `u64` as a `string`. +// Example: assert u64(2000000).str() == '2000000' +[direct_array_access; inline] +pub fn (nn u64) str() string { + unsafe { + mut n := nn + mut d := u64(0) + if n == 0 { + return '0' + } + max := 20 + mut buf := malloc_noscan(max + 1) + mut index := max + buf[index] = 0 + index-- + for n > 0 { + n1 := n / 100 + d = ((n - (n1 * 100)) << 1) + n = n1 + buf[index] = digit_pairs[d] + index-- + d++ + buf[index] = digit_pairs[d] + index-- + } + index++ + // remove head zero + if d < 20 { + index++ + } + diff := max - index + vmemmove(buf, buf + index, diff + 1) + return tos(buf, diff) + // return tos(memdup(&buf[0] + index, (max - index)), (max - index)) + } +} + +// str returns the value of the `bool` as a `string`. +// Example: assert (2 > 1).str() == 'true' +pub fn (b bool) str() string { + if b { + return 'true' + } + return 'false' +} + +// +// ----- value to hex string functions ----- +// + +// u64_to_hex converts the number `nn` to a (zero padded if necessary) hexadecimal `string`. +[direct_array_access; inline] +fn u64_to_hex(nn u64, len byte) string { + mut n := nn + mut buf := [17]byte{} + buf[len] = 0 + mut i := 0 + for i = len - 1; i >= 0; i-- { + d := byte(n & 0xF) + buf[i] = if d < 10 { d + `0` } else { d + 87 } + n = n >> 4 + } + return unsafe { tos(memdup(&buf[0], len + 1), len) } +} + +// u64_to_hex_no_leading_zeros converts the number `nn` to hexadecimal `string`. +[direct_array_access; inline] +fn u64_to_hex_no_leading_zeros(nn u64, len byte) string { + mut n := nn + mut buf := [17]byte{} + buf[len] = 0 + mut i := 0 + for i = len - 1; i >= 0; i-- { + d := byte(n & 0xF) + buf[i] = if d < 10 { d + `0` } else { d + 87 } + n = n >> 4 + if n == 0 { + break + } + } + res_len := len - i + return unsafe { tos(memdup(&buf[i], res_len + 1), res_len) } +} + +// hex returns the value of the `byte` as a hexadecimal `string`. +// Note that the output is zero padded for values below 16. +// Example: assert byte(2).hex() == '02' +// Example: assert byte(15).hex() == '0f' +// Example: assert byte(255).hex() == 'ff' +pub fn (nn byte) hex() string { + if nn == 0 { + return '00' + } + return u64_to_hex(nn, 2) +} + +// hex returns the value of the `i8` as a hexadecimal `string`. +// Note that the output is zero padded for values below 16. +// Example: assert i8(8).hex() == '08' +// Example: assert i8(10).hex() == '0a' +// Example: assert i8(15).hex() == '0f' +pub fn (nn i8) hex() string { + if nn == 0 { + return '00' + } + return u64_to_hex(u64(nn), 2) + //return byte(nn).hex() +} + +// hex returns the value of the `u16` as a hexadecimal `string`. +// Note that the output is ***not*** zero padded. +// Example: assert u16(2).hex() == '2' +// Example: assert u16(200).hex() == 'c8' +pub fn (nn u16) hex() string { + if nn == 0 { + return '0' + } + return u64_to_hex_no_leading_zeros(nn, 4) +} + +// hex returns the value of the `i16` as a hexadecimal `string`. +// Note that the output is ***not*** zero padded. +// Example: assert i16(2).hex() == '2' +// Example: assert i16(200).hex() == 'c8' +pub fn (nn i16) hex() string { + return u16(nn).hex() +} + +// hex returns the value of the `u32` as a hexadecimal `string`. +// Note that the output is ***not*** zero padded. +// Example: assert u32(2).hex() == '2' +// Example: assert u32(200).hex() == 'c8' +pub fn (nn u32) hex() string { + if nn == 0 { + return '0' + } + return u64_to_hex_no_leading_zeros(nn, 8) +} + +// hex returns the value of the `int` as a hexadecimal `string`. +// Note that the output is ***not*** zero padded. +// Example: assert int(2).hex() == '2' +// Example: assert int(200).hex() == 'c8' +pub fn (nn int) hex() string { + return u32(nn).hex() +} + +// hex2 returns the value of the `int` as a `0x`-prefixed hexadecimal `string`. +// Note that the output after `0x` is ***not*** zero padded. +// Example: assert int(8).hex2() == '0x8' +// Example: assert int(15).hex2() == '0xf' +// Example: assert int(18).hex2() == '0x12' +pub fn (n int) hex2() string { + return '0x' + n.hex() +} + +// hex returns the value of the `u64` as a hexadecimal `string`. +// Note that the output is ***not*** zero padded. +// Example: assert u64(2).hex() == '2' +// Example: assert u64(2000).hex() == '7d0' +pub fn (nn u64) hex() string { + if nn == 0 { + return '0' + } + return u64_to_hex_no_leading_zeros(nn, 16) +} + +// hex returns the value of the `i64` as a hexadecimal `string`. +// Note that the output is ***not*** zero padded. +// Example: assert i64(2).hex() == '2' +// Example: assert i64(-200).hex() == 'ffffffffffffff38' +// Example: assert i64(2021).hex() == '7e5' +pub fn (nn i64) hex() string { + return u64(nn).hex() +} + +// hex returns the value of the `int_literal` as a hexadecimal `string`. +// Note that the output is ***not*** zero padded. +pub fn (nn int_literal) hex() string { + return u64(nn).hex() +} + +// hex returns the value of the `voidptr` as a hexadecimal `string`. +// Note that the output is ***not*** zero padded. +pub fn (nn voidptr) str() string { + return u64(nn).hex() +} + +// hex returns the value of the `byteptr` as a hexadecimal `string`. +// Note that the output is ***not*** zero padded. +// pub fn (nn byteptr) str() string { +pub fn (nn byteptr) str() string { + return u64(nn).hex() +} + +pub fn (nn byte) hex_full() string { + return u64_to_hex(u64(nn), 2) +} + +pub fn (nn i8) hex_full() string { + return u64_to_hex(u64(nn), 2) +} + +pub fn (nn u16) hex_full() string { + return u64_to_hex(u64(nn), 4) +} + +pub fn (nn i16) hex_full() string { + return u64_to_hex(u64(nn), 4) +} + +pub fn (nn u32) hex_full() string { + return u64_to_hex(u64(nn), 8) +} + +pub fn (nn int) hex_full() string { + return u64_to_hex(u64(nn), 8) +} + +pub fn (nn i64) hex_full() string { + return u64_to_hex(u64(nn), 16) +} + +pub fn (nn voidptr) hex_full() string { + return u64_to_hex(u64(nn), 16) +} + +pub fn (nn int_literal) hex_full() string { + return u64_to_hex(u64(nn), 16) +} + +// hex_full returns the value of the `u64` as a *full* 16-digit hexadecimal `string`. +// Example: assert u64(2).hex_full() == '0000000000000002' +// Example: assert u64(255).hex_full() == '00000000000000ff' +pub fn (nn u64) hex_full() string { + return u64_to_hex(nn, 16) +} + +// str returns the contents of `byte` as a zero terminated `string`. +// Example: assert byte(111).str() == '111' +pub fn (b byte) str() string { + return int(b).str_l(7) +} + +// ascii_str returns the contents of `byte` as a zero terminated ASCII `string` character. +// Example: assert byte(97).ascii_str() == 'a' +pub fn (b byte) ascii_str() string { + mut str := string{ + str: unsafe { malloc_noscan(2) } + len: 1 + } + unsafe { + str.str[0] = b + str.str[1] = 0 + } + // println(str) + return str +} + +// str_escaped returns the contents of `byte` as an escaped `string`. +// Example: assert byte(0).str_escaped() == r'`\0`' +pub fn (b byte) str_escaped() string { + str := match b { + 0 { r'`\0`' } + 7 { r'`\a`' } + 8 { r'`\b`' } + 9 { r'`\t`' } + 10 { r'`\n`' } + 11 { r'`\v`' } + 12 { r'`\f`' } + 13 { r'`\r`' } + 27 { r'`\e`' } + 32...126 { b.ascii_str() } + else { '0x' + b.hex() } + } + return str +} diff --git a/v_windows/v/vlib/builtin/int_test.v b/v_windows/v/vlib/builtin/int_test.v new file mode 100644 index 0000000..3f233f1 --- /dev/null +++ b/v_windows/v/vlib/builtin/int_test.v @@ -0,0 +1,241 @@ +const ( + a = 3 + u = u64(1) +) + +fn test_const() { + b := (true && true) || false + assert b == true + assert a == 3 + assert u == u64(1) + assert u == 1 // make sure this works without the cast +} + +fn test_str_methods() { + assert i8(1).str() == '1' + assert i8(-1).str() == '-1' + assert i16(1).str() == '1' + assert i16(-1).str() == '-1' + assert int(1).str() == '1' + assert int(-1).str() == '-1' + assert int(2147483647).str() == '2147483647' + assert int(2147483648).str() == '-2147483648' + assert int(-2147483648).str() == '-2147483648' + assert i64(1).str() == '1' + assert i64(-1).str() == '-1' + assert u16(1).str() == '1' + assert u16(-1).str() == '65535' + assert u32(1).str() == '1' + assert u32(-1).str() == '4294967295' + assert u64(1).str() == '1' + assert u64(-1).str() == '18446744073709551615' + assert voidptr(-1).str() == 'ffffffffffffffff' + assert voidptr(1).str() == '1' + assert (&byte(-1)).str() == 'ffffffffffffffff' + assert (&byte(1)).str() == '1' + assert byteptr(-1).str() == 'ffffffffffffffff' + assert byteptr(1).str() == '1' +} + +fn test_and_precendence() { + assert (2 & 0 == 0) == ((2 & 0) == 0) + assert (2 & 0 != 0) == ((2 & 0) != 0) + assert (0 & 0 >= 0) == ((0 & 0) >= 0) + assert (0 & 0 <= 0) == ((0 & 0) <= 0) + assert (0 & 0 < 1) == ((0 & 0) < 1) + assert (1 & 2 > 0) == ((1 & 2) > 0) +} + +fn test_or_precendence() { + assert (1 | 0 == 0) == ((1 | 0) == 0) + assert (1 | 0 != 1) == ((1 | 0) != 1) + assert (1 | 0 >= 2) == ((1 | 0) >= 2) + assert (1 | 0 <= 0) == ((1 | 0) <= 0) + assert (1 | 0 < 0) == ((1 | 0) < 0) + assert (1 | 0 > 1) == ((1 | 0) > 1) +} + +fn test_xor_precendence() { + assert (1 ^ 0 == 2) == ((1 ^ 0) == 2) + assert (1 ^ 0 != 2) == ((1 ^ 0) != 2) + assert (1 ^ 0 >= 0) == ((1 ^ 0) >= 0) + assert (1 ^ 0 <= 1) == ((1 ^ 0) <= 1) + assert (1 ^ 0 < 0) == ((1 ^ 0) < 0) + assert (1 ^ 0 > 1) == ((1 ^ 0) > 1) +} + +fn test_left_shift_precendence() { + assert (2 << 4 | 3) == ((2 << 4) | 3) + assert (2 << 4 | 3) != (2 << (4 | 3)) +} + +fn test_right_shift_precendence() { + assert (256 >> 4 | 3) == ((256 >> 4) | 3) + assert (256 >> 4 | 3) != (256 >> (4 | 3)) +} + +fn test_i8_print() { + b := i8(0) + println(b) + c := i16(7) + println(c) + d := u16(6) + println(d) + assert true +} + +/* +fn test_cmp() { + assert 1 ≠ 2 + assert 1 ⩽ 2 + assert 1 ⩾ 0 +} +*/ +type MyInt = int + +fn test_int_alias() { + i := MyInt(2) + assert i + 10 == 12 +} + +fn test_hex() { + x := u64(10) + assert x.hex() == 'a' + b := 1234 + assert b.hex() == '4d2' + b1 := -1 + assert b1.hex() == 'ffffffff' + // unsigned tests + assert u8(12).hex() == '0c' + assert u8(255).hex() == 'ff' + assert u16(65535).hex() == 'ffff' + assert u32(-1).hex() == 'ffffffff' + assert u64(-1).hex() == 'ffffffffffffffff' + // signed tests + assert i8(-1).hex() == 'ff' + assert i8(12).hex() == '0c' + assert i16(32767).hex() == '7fff' + assert int(2147483647).hex() == '7fffffff' + assert i64(9223372036854775807).hex() == '7fffffffffffffff' +} + +fn test_bin() { + x1 := 0b10 + assert x1 == 2 + x2 := 0b10101010 + assert x2 == 0xAA + x3 := -0b0000001 + assert x3 == -1 + x4 := 0b11111111 + assert x4 == 255 + x5 := byte(0b11111111) + assert x5 == 255 + x6 := char(0b11111111) + assert int(x6) == -1 + x7 := 0b0 + assert x7 == 0 + x8 := -0b0 + assert x8 == 0 +} + +fn test_oct() { + x1 := 0o12 + assert x1 == 10 + x2 := 00000o350 + assert x2 == 232 + x3 := 000o00073 + assert x3 == 59 + x4 := 00000000 + assert x4 == 0 + x5 := 00000195 + assert x5 == 195 + x6 := -0o744 + assert x6 == -484 + x7 := -000o000042 + assert x7 == -34 + x8 := -0000112 + assert x8 == -112 + x9 := -000 + assert x9 == 0 +} + +fn test_num_separator() { + // int + assert 100_000_0 == 1000000 + assert -2_23_4_6 == -22346 + + // bin + assert 0b0_11 == 3 + assert -0b0_100 == -4 + + // oct + assert 0o1_73 == 123 + assert -0o17_5 == -125 + assert -0o175 == -125 + + // hex + assert 0xFF == 255 + assert 0xF_F == 255 + + // f32 or f64 + assert 312_2.55 == 3122.55 + assert 312_2.55 == 3122.55 +} + +fn test_int_decl() { + x1 := 0 + x2 := 1333 + x3 := -88955 + x4 := 2000000000 + x5 := -1999999999 + assert typeof(x1).name == 'int' + assert typeof(x2).name == 'int' + assert typeof(x3).name == 'int' + assert typeof(x4).name == 'int' + assert typeof(x5).name == 'int' + x7 := u64(-321314588900011) + assert typeof(x7).name == 'u64' +} + +fn test_int_to_hex() { + // array hex + st := [byte(`V`), `L`, `A`, `N`, `G`] + assert st.hex() == '564c414e47' + assert st.hex().len == 10 + st1 := [byte(0x41)].repeat(100) + assert st1.hex() == '41'.repeat(100) + // --- int to hex tests + c0 := 12 + // 8Bit + assert byte(0).hex() == '00' + assert byte(c0).hex() == '0c' + assert i8(c0).hex() == '0c' + assert byte(127).hex() == '7f' + assert i8(127).hex() == '7f' + assert byte(255).hex() == 'ff' + assert byte(-1).hex() == 'ff' + // 16bit + assert u16(0).hex() == '0' + assert i16(c0).hex() == 'c' + assert u16(c0).hex() == 'c' + assert i16(32767).hex() == '7fff' + assert u16(32767).hex() == '7fff' + assert i16(-1).hex() == 'ffff' + assert u16(65535).hex() == 'ffff' + // 32bit + assert u32(0).hex() == '0' + assert c0.hex() == 'c' + assert u32(c0).hex() == 'c' + assert 2147483647.hex() == '7fffffff' + assert u32(2147483647).hex() == '7fffffff' + assert (-1).hex() == 'ffffffffffffffff' + assert u32(4294967295).hex() == 'ffffffff' + // 64 bit + assert u64(0).hex() == '0' + assert i64(c0).hex() == 'c' + assert u64(c0).hex() == 'c' + assert i64(9223372036854775807).hex() == '7fffffffffffffff' + assert u64(9223372036854775807).hex() == '7fffffffffffffff' + assert i64(-1).hex() == 'ffffffffffffffff' + assert u64(18446744073709551615).hex() == 'ffffffffffffffff' +} diff --git a/v_windows/v/vlib/builtin/isnil_test.v b/v_windows/v/vlib/builtin/isnil_test.v new file mode 100644 index 0000000..15df9f0 --- /dev/null +++ b/v_windows/v/vlib/builtin/isnil_test.v @@ -0,0 +1,19 @@ +fn test_isnil_byteptr() { + pb := &byte(0) + assert isnil(pb) +} + +fn test_isnil_voidptr() { + pv := voidptr(0) + assert isnil(pv) +} + +fn test_isnil_charptr() { + pc := &char(0) + assert isnil(pc) +} + +fn test_isnil_intptr() { + pi := &int(0) + assert isnil(pi) +} diff --git a/v_windows/v/vlib/builtin/js/array.js.v b/v_windows/v/vlib/builtin/js/array.js.v new file mode 100644 index 0000000..495e656 --- /dev/null +++ b/v_windows/v/vlib/builtin/js/array.js.v @@ -0,0 +1,253 @@ +module builtin + +struct array { + arr JS.Array +pub: + len int + cap int +} + +#function flatIntoArray(target, source, sourceLength, targetIndex, depth) { +#"use strict"; +# +#for (var sourceIndex = 0; sourceIndex < sourceLength; ++sourceIndex) { +#if (sourceIndex in source) { +#var element = source[sourceIndex]; +#if (depth > 0 && Array.isArray(element)) +#targetIndex = flatIntoArray(target, element, element.length, targetIndex, depth - 1); +#else { +#target[targetIndex] = element; +#++targetIndex; +#} +#} +#} +#return targetIndex; +#} +#function flatArray(target,depth) { +#var array = target +#var length = array.length; +#var depthNum = 1; +# +#if (depth !== undefined) +#depthNum = +depth +# +#var result = [] +# +#flatIntoArray(result, array, length, 0, depthNum); +#return result; +#} + +[unsafe] +pub fn (a array) repeat_to_depth(count int, depth int) array { + if count < 0 { + panic('array.repeat: count is negative: $count') + } + mut arr := empty_array() + #let tmp = new Array(a.arr.length * +count); + #tmp.fill(a.arr); + # + #arr.arr = flatArray(tmp,depth+1); + + return arr +} + +// last returns the last element of the array. +pub fn (a array) last() voidptr { + mut res := voidptr(0) + #res = a.arr[a.len-1]; + + return res +} + +fn (a array) get(ix int) voidptr { + mut result := voidptr(0) + #result = a.arr[ix] + + return result +} + +pub fn (a array) repeat(count int) array { + unsafe { + return a.repeat_to_depth(count, 0) + } +} + +fn empty_array() array { + mut arr := array{} + #arr = new array([]) + + return arr +} + +fn (a &array) set_len(i int) { + #a.arr.length=i +} + +pub fn (mut a array) sort_with_compare(compare voidptr) { + #a.val.arr.sort(compare) +} + +pub fn (mut a array) sort() { + #a.val.arr.sort($sortComparator) +} + +pub fn (a array) index(v string) int { + for i in 0 .. a.len { + #if (a.arr[i].toString() == v.toString()) + + { + return i + } + } + return -1 +} + +pub fn (a array) slice(start int, end int) array { + mut result := a + #result = new array(a.arr.slice(start,end)) + + return result +} + +pub fn (mut a array) insert(i int, val voidptr) { + #a.val.arr.splice(i,0,val) +} + +pub fn (mut a array) insert_many(i int, val voidptr, size int) { + #a.val.arr.splice(i,0,...val.slice(0,+size)) +} + +pub fn (mut a array) join(separator string) string { + mut res := '' + #res = new builtin.string(a.val.arr.join(separator +'')); + + return res +} + +fn (a array) push(val voidptr) { + #a.arr.push(val) +} + +pub fn (a array) str() string { + mut res := '' + #res = new builtin.string(a + '') + + return res +} + +#array.prototype[Symbol.iterator] = function () { return this.arr[Symbol.iterator](); } +#array.prototype.entries = function () { let result = []; for (const [key,val] of this.arr.entries()) { result.push([new int(key), val]); } return result[Symbol.iterator](); } +#array.prototype.map = function(callback) { return new builtin.array(this.arr.map(callback)); } +#array.prototype.filter = function(callback) { return new array(this.arr.filter( function (it) { return (+callback(it)) != 0; } )); } +#Object.defineProperty(array.prototype,'cap',{ get: function () { return this.len; } }) +#array.prototype.any = function (value) { +#let val ;if (typeof value == 'function') { val = function (x) { return value(x); } } else { val = function (x) { return vEq(x,value); } } +#for (let i = 0;i < this.arr.length;i++) +#if (val(this.arr[i])) +#return true; +# +#return false; +#} + +#array.prototype.all = function (value) { +#let val ;if (typeof value == 'function') { val = function (x) { return value(x); } } else { val = function (x) { return vEq(x,value); } } +#for (let i = 0;i < this.arr.length;i++) +#if (!val(this.arr[i])) +#return false; +# +#return true; +#} +// delete deletes array element at index `i`. +pub fn (mut a array) delete(i int) { + a.delete_many(i, 1) +} + +// delete_many deletes `size` elements beginning with index `i` +pub fn (mut a array) delete_many(i int, size int) { + #a.val.arr.splice(i.valueOf(),size.valueOf()) +} + +// prepend prepends one value to the array. +pub fn (mut a array) prepend(val voidptr) { + a.insert(0, val) +} + +// prepend_many prepends another array to this array. +[unsafe] +pub fn (mut a array) prepend_many(val voidptr, size int) { + unsafe { a.insert_many(0, val, size) } +} + +pub fn (a array) reverse() array { + mut res := array{} + #res.arr = Array.from(a.arr).reverse() + + return res +} + +pub fn (mut a array) reverse_in_place() { + #a.val.arr.reverse() +} + +#array.prototype.$includes = function (elem) { return this.arr.find(function(e) { return vEq(elem,e); }) !== undefined;} + +// reduce executes a given reducer function on each element of the array, +// resulting in a single output value. +pub fn (a array) reduce(iter fn (int, int) int, accum_start int) int { + mut accum_ := accum_start + #for (let i = 0;i < a.arr.length;i++) { + #accum_ = iter(accum_, a.arr[i]) + #} + + return accum_ +} + +pub fn (mut a array) pop() voidptr { + mut res := voidptr(0) + #res = a.val.arr.pop() + + return res +} + +pub fn (a array) first() voidptr { + mut res := voidptr(0) + #res = a.arr[0] + + return res +} + +#array.prototype.toString = function () { +#let res = "[" +#for (let i = 0; i < this.arr.length;i++) { +#res += this.arr[i].toString(); +#if (i != this.arr.length-1) +#res += ', ' +#} +#res += ']' +#return res; +# +#} + +pub fn (a array) contains(key voidptr) bool { + #for (let i = 0; i < a.arr.length;i++) + #if (vEq(a.arr[i],key)) return new bool(true); + + return false +} + +// delete_last effectively removes last element of an array. +pub fn (mut a array) delete_last() { + #a.val.arr.pop(); +} + +[unsafe] +pub fn (a array) free() { +} + +// todo: once (a []byte) will work rewrite this +pub fn (a array) bytestr() string { + res := '' + #a.arr.forEach((item) => res.str += String.fromCharCode(+item)) + + return res +} diff --git a/v_windows/v/vlib/builtin/js/builtin.js.v b/v_windows/v/vlib/builtin/js/builtin.js.v new file mode 100644 index 0000000..f64da00 --- /dev/null +++ b/v_windows/v/vlib/builtin/js/builtin.js.v @@ -0,0 +1,61 @@ +module builtin + +// used to generate JS throw statements. +pub fn js_throw(s any) { + #throw s +} + +pub fn println(s string) { + $if js_freestanding { + #print(s.str) + } $else { + #console.log(s.str) + } +} + +pub fn print(s string) { + $if js_node { + #$process.stdout.write(s.str) + } $else { + panic('Cannot `print` in a browser, use `println` instead') + } +} + +pub fn eprintln(s string) { + $if js_freestanding { + #print(s.str) + } $else { + #console.error(s.str) + } +} + +pub fn eprint(s string) { + $if js_node { + #$process.stderr.write(s.str) + } $else { + panic('Cannot `eprint` in a browser, use `println` instead') + } +} + +// Exits the process in node, and halts execution in the browser +// because `process.exit` is undefined. Workaround for not having +// a 'real' way to exit in the browser. +pub fn exit(c int) { + JS.process.exit(c) + js_throw('exit($c)') +} + +fn opt_ok(data voidptr, option Option) { + #option.state = 0 + #option.err = none__ + #option.data = data +} + +pub fn unwrap(opt string) string { + mut o := Option{} + #o = opt + if o.state != 0 { + js_throw(o.err) + } + return opt +} diff --git a/v_windows/v/vlib/builtin/js/builtin.v b/v_windows/v/vlib/builtin/js/builtin.v new file mode 100644 index 0000000..7c82d1a --- /dev/null +++ b/v_windows/v/vlib/builtin/js/builtin.v @@ -0,0 +1,84 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +module builtin + +fn (a any) toString() + +pub fn panic(s string) { + eprintln('V panic: $s') + exit(1) +} + +// IError holds information about an error instance +pub interface IError { + msg string + code int +} + +// Error is the default implementation of IError, that is returned by e.g. `error()` +pub struct Error { +pub: + msg string + code int +} + +struct None__ { + msg string + code int +} + +fn (_ None__) str() string { + return 'none' +} + +pub const none__ = IError(None__{'', 0}) + +pub struct Option { + state byte + err IError = none__ +} + +pub fn (err IError) str() string { + return match err { + None__ { 'none' } + Error { err.msg } + else { '$err.type_name(): $err.msg' } + } +} + +pub fn (o Option) str() string { + if o.state == 0 { + return 'Option{ ok }' + } + if o.state == 1 { + return 'Option{ none }' + } + return 'Option{ error: "$o.err" }' +} + +fn trace_error(x string) { + eprintln('> ${@FN} | $x') +} + +// error returns a default error instance containing the error given in `message`. +// Example: `if ouch { return error('an error occurred') }` +[inline] +pub fn error(message string) IError { + // trace_error(message) + return &Error{ + msg: message + } +} + +// error_with_code returns a default error instance containing the given `message` and error `code`. +// `if ouch { return error_with_code('an error occurred', 1) }` +[inline] +pub fn error_with_code(message string, code int) IError { + // trace_error('$message | code: $code') + return &Error{ + msg: message + code: code + } +} diff --git a/v_windows/v/vlib/builtin/js/byte.js.v b/v_windows/v/vlib/builtin/js/byte.js.v new file mode 100644 index 0000000..af1803b --- /dev/null +++ b/v_windows/v/vlib/builtin/js/byte.js.v @@ -0,0 +1,16 @@ +module builtin + +pub fn (b byte) is_space() bool { + mut result := false + #result.val = /^\s*$/.test(String.fromCharCode(b)) + + return result +} + +pub fn (c byte) is_letter() bool { + result := false + + #result.val = (c.val >= `a`.charCodeAt() && c.val <= `z`.charCodeAt()) || (c.val >= `A`.charCodeAt() && c.val <= `Z`.charCodeAt()) + + return result +} diff --git a/v_windows/v/vlib/builtin/js/int.js.v b/v_windows/v/vlib/builtin/js/int.js.v new file mode 100644 index 0000000..08e52a9 --- /dev/null +++ b/v_windows/v/vlib/builtin/js/int.js.v @@ -0,0 +1,8 @@ +module builtin + +pub fn (i int) str() string { + mut res := '' + #res = new builtin.string( i ) + + return res +} diff --git a/v_windows/v/vlib/builtin/js/jsfns.js.v b/v_windows/v/vlib/builtin/js/jsfns.js.v new file mode 100644 index 0000000..277c702 --- /dev/null +++ b/v_windows/v/vlib/builtin/js/jsfns.js.v @@ -0,0 +1,125 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +// This file contains JS functions present in both node and the browser. +// They have been ported from their TypeScript definitions. + +module builtin + +pub struct JS.Number {} + +pub struct JS.String { + length JS.Number +} + +pub struct JS.Boolean {} + +pub struct JS.Array { + length JS.Number +} + +pub struct JS.Map {} + +// browser: https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Error +// node: https://nodejs.org/api/errors.html#errors_class_error +pub struct JS.Error { +pub: + name string + message string + stack string +} + +// Type prototype functions +fn (v JS.String) toString() JS.String +fn (v JS.Number) toString() JS.String +fn (v JS.Boolean) toString() JS.String +fn (v JS.Array) toString() JS.String +fn (v JS.Map) toString() JS.String + +// Hack for "`[]JS.String` is not a struct" when returning arr.length or arr.len +// TODO: Fix []JS.String not a struct error +fn native_str_arr_len(arr []JS.String) int { + len := 0 + #len = arr.length + + return len +} + +// Top level functions +fn JS.eval(string) any +fn JS.parseInt(string, f64) JS.Number +fn JS.parseFloat(string) JS.Number +fn JS.isNaN(f64) bool +fn JS.isFinite(f64) bool +fn JS.decodeURI(string) string +fn JS.decodeURIComponent(string) string +fn JS.encodeURI(string) string + +type EncodeURIComponentArg = bool | f64 | string + +fn JS.encodeURIComponent(EncodeURIComponentArg) string +fn JS.escape(string) string +fn JS.unescape(string) string + +// console +fn JS.console.assert(bool, ...any) +fn JS.console.clear() +fn JS.console.count(string) +fn JS.console.countReset(string) +fn JS.console.debug(...any) +fn JS.console.dir(any, any) +fn JS.console.dirxml(...any) +fn JS.console.error(...any) +fn JS.console.exception(string, ...any) +fn JS.console.group(...any) +fn JS.console.groupCollapsed(...any) +fn JS.console.groupEnd() +fn JS.console.info(...any) +fn JS.console.log(...any) +fn JS.console.table(any, []string) +fn JS.console.time(string) +fn JS.console.timeEnd(string) +fn JS.console.timeLog(string, ...any) +fn JS.console.timeStamp(string) +fn JS.console.trace(...any) +fn JS.console.warn(...any) + +// Math +fn JS.Math.abs(f64) f64 +fn JS.Math.acos(f64) f64 +fn JS.Math.asin(f64) f64 +fn JS.Math.atan(f64) f64 +fn JS.Math.atan2(f64, f64) f64 +fn JS.Math.ceil(f64) f64 +fn JS.Math.cos(f64) f64 +fn JS.Math.exp(f64) f64 +fn JS.Math.floor(f64) f64 +fn JS.Math.log(f64) f64 +fn JS.Math.max(...f64) f64 +fn JS.Math.min(...f64) f64 +fn JS.Math.pow(f64, f64) f64 +fn JS.Math.random() f64 +fn JS.Math.round(f64) f64 +fn JS.Math.sin(f64) f64 +fn JS.Math.sqrt(f64) f64 +fn JS.Math.tan(f64) f64 + +// JSON +fn JS.JSON.stringify(any) string +fn JS.JSON.parse(string) any + +// String +fn (v JS.String) slice(a int, b int) JS.String +fn (v JS.String) split(dot JS.String) []JS.String +fn (s JS.String) indexOf(needle JS.String) int +fn (s JS.String) lastIndexOf(needle JS.String) int + +fn (s JS.String) charAt(i int) JS.String +fn (s JS.String) charCodeAt(i int) byte +fn (s JS.String) toUpperCase() JS.String +fn (s JS.String) toLowerCase() JS.String +fn (s JS.String) concat(a JS.String) JS.String +fn (s JS.String) includes(substr JS.String) bool +fn (s JS.String) endsWith(substr JS.String) bool +fn (s JS.String) startsWith(substr JS.String) bool diff --git a/v_windows/v/vlib/builtin/js/jsfns_browser.js.v b/v_windows/v/vlib/builtin/js/jsfns_browser.js.v new file mode 100644 index 0000000..72daec3 --- /dev/null +++ b/v_windows/v/vlib/builtin/js/jsfns_browser.js.v @@ -0,0 +1,59 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +// This file contains JS functions only present in the browser. +// They have been ported from their TypeScript definitions. + +module builtin + +// Window +fn JS.atob(string) string +fn JS.btoa(string) string +fn JS.clearInterval(int) +fn JS.clearTimeout(int) + +// fn JS.createImageBitmap(ImageBitmapSource, ImageBitmapOptions) Promise +// fn JS.createImageBitmap(ImageBitmapSource, int, int, int, int, ImageBitmapOptions) Promise + +// TODO: js async attribute +// [js_async] +// fn JS.fetch(RequestInfo, RequestInit) Promise +fn JS.queueMicrotask(fn ()) +fn JS.setInterval(any, int, ...any) int +fn JS.setTimeout(any, int, ...any) int + +fn JS.alert(any) +fn JS.blur() +fn JS.captureEvents() +fn JS.close() +fn JS.confirm(string) bool + +// fn JS.departFocus(NavigationReason, FocusNavigationOrigin) +fn JS.focus() + +// fn JS.getComputedStyle(Element, string | null) CSSStyleDeclaration +// fn JS.getMatchedCSSRules(Element, string | null) CSSRuleList +// fn JS.getSelection() Selection | null +// fn JS.matchMedia(string) MediaQueryList +fn JS.moveBy(int, int) +fn JS.moveTo(int, int) +fn JS.msWriteProfilerMark(string) + +// fn JS.open(string, string, string, bool) ?Window +// fn JS.postMessage(any, string, []Transferable) +fn JS.print() +fn JS.prompt(string, string) ?string +fn JS.releaseEvents() +fn JS.resizeBy(int, int) +fn JS.resizeTo(int, int) + +// fn JS.scroll(ScrollToOptions) +fn JS.scroll(int, int) + +// fn JS.scrollBy(ScrollToOptions) +fn JS.scrollBy(int, int) + +// fn JS.scrollTo(ScrollToOptions) +fn JS.scrollTo(int, int) +fn JS.stop() diff --git a/v_windows/v/vlib/builtin/js/jsfns_node.js.v b/v_windows/v/vlib/builtin/js/jsfns_node.js.v new file mode 100644 index 0000000..6f65629 --- /dev/null +++ b/v_windows/v/vlib/builtin/js/jsfns_node.js.v @@ -0,0 +1,31 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +// This file contains JS functions only present in node.js. +// They have been ported from their TypeScript definitions. + +module builtin + +pub struct JS.node_process { +pub: + arch string + argsv []string + env []string + platform string + version string + // TODO: add all properties +} + +// hack to access process properties +pub fn js_node_process() JS.node_process { + #return process + + return JS.node_process{} +} + +fn JS.process.exit(int) +fn JS.process.stdout.write(string) bool +fn JS.process.stdout.writeln(string) bool +fn JS.process.stderr.write(string) bool +fn JS.process.stderr.writeln(string) bool diff --git a/v_windows/v/vlib/builtin/js/map.js.v b/v_windows/v/vlib/builtin/js/map.js.v new file mode 100644 index 0000000..033a5fd --- /dev/null +++ b/v_windows/v/vlib/builtin/js/map.js.v @@ -0,0 +1,26 @@ +module builtin + +struct map { + m JS.Map + len int +} + +// Removes the mapping of a particular key from the map. +[unsafe] +pub fn (mut m map) delete(key voidptr) { + #m.map.delete(key) +} + +pub fn (m &map) free() {} + +#map.prototype[Symbol.iterator] = function () { return this.map[Symbol.iterator](); } + +#map.prototype.toString = function () { +#function fmtKey(key) { return typeof key == 'string' ? '\'' + key + '\'' : key} +#let res = '{' +#for (const entry of this) { +#res += fmtKey(entry[0]) + ': ' + entry[0]; +#} +#res += '}' +#return res; +#} diff --git a/v_windows/v/vlib/builtin/js/string.js.v b/v_windows/v/vlib/builtin/js/string.js.v new file mode 100644 index 0000000..8b2933d --- /dev/null +++ b/v_windows/v/vlib/builtin/js/string.js.v @@ -0,0 +1,720 @@ +module builtin + +pub struct string { +pub: + str JS.String + len int +} + +pub fn (s string) slice(a int, b int) string { + return string(s.str.slice(a, b)) +} + +pub fn (s string) after(dot string) string { + return string(s.str.slice(s.str.lastIndexOf(dot.str) + 1, int(s.str.length))) +} + +pub fn (s string) after_char(dot byte) string { + // TODO: Implement after byte + return s +} + +pub fn (s string) all_after(dot string) string { + return string(s.str.slice(s.str.indexOf(dot.str) + 1, int(s.str.length))) +} + +// why does this exist? +pub fn (s string) all_after_last(dot string) string { + return s.after(dot) +} + +pub fn (s string) all_before(dot string) string { + return string(s.str.slice(0, s.str.indexOf(dot.str))) +} + +pub fn (s string) all_before_last(dot string) string { + return string(s.str.slice(0, s.str.lastIndexOf(dot.str))) +} + +pub fn (s string) bool() bool { + return s == 'true' +} + +pub fn (s string) split(dot string) []string { + mut arr := s.str.split(dot.str).map(string(it)) + #arr = new array(arr) + + return arr +} + +pub fn (s string) bytes() []byte { + sep := '' + mut arr := s.str.split(sep.str).map(it.charCodeAt(0)) + #arr = new array(arr) + + return arr +} + +pub fn (s string) capitalize() string { + part := string(s.str.slice(1, int(s.str.length))) + return string(s.str.charAt(0).toUpperCase().concat(part.str)) +} + +pub fn (s string) clone() string { + return string(s.str) +} + +pub fn (s string) contains(substr string) bool { + return s.str.includes(substr.str) +} + +pub fn (s string) contains_any(chars string) bool { + sep := '' + for x in chars.str.split(sep.str) { + if s.str.includes(x) { + return true + } + } + return false +} + +pub fn (s string) contains_any_substr(chars []string) bool { + for x in chars { + if s.str.includes(x.str) { + return true + } + } + return false +} + +pub fn (s string) count(substr string) int { + // TODO: "error: `[]JS.String` is not a struct" when returning arr.length or arr.len + arr := s.str.split(substr.str) + return native_str_arr_len(arr) +} + +pub fn (s string) ends_with(p string) bool { + mut res := false + #res.val = s.str.endsWith(p.str) + + return res +} + +pub fn (s string) starts_with(p string) bool { + return s.str.startsWith(p.str) +} + +pub fn (s string) fields() []string { + mut res := []string{} + mut word_start := 0 + mut word_len := 0 + mut is_in_word := false + mut is_space := false + for i, c in s { + is_space = c in [32, 9, 10] + if !is_space { + word_len++ + } + if !is_in_word && !is_space { + word_start = i + is_in_word = true + continue + } + if is_space && is_in_word { + res << s[word_start..word_start + word_len] + is_in_word = false + word_len = 0 + word_start = 0 + continue + } + } + if is_in_word && word_len > 0 { + // collect the remainder word at the end + res << s[word_start..s.len] + } + return res +} + +pub fn (s string) find_between(start string, end string) string { + return string(s.str.slice(s.str.indexOf(start.str) + 1, s.str.indexOf(end.str))) +} + +// unnecessary in the JS backend, implemented for api parity. +pub fn (s string) free() {} + +pub fn (s string) hash() int { + mut h := u32(0) + if h == 0 && s.len > 0 { + for c in s { + h = h * 31 + u32(c) + } + } + return int(h) +} + +// int returns the value of the string as an integer `'1'.int() == 1`. +pub fn (s string) int() int { + return int(JS.parseInt(s)) +} + +// i64 returns the value of the string as i64 `'1'.i64() == i64(1)`. +pub fn (s string) i64() i64 { + return i64(JS.parseInt(s)) +} + +// i8 returns the value of the string as i8 `'1'.i8() == i8(1)`. +pub fn (s string) i8() i8 { + return i8(JS.parseInt(s)) +} + +// i16 returns the value of the string as i16 `'1'.i16() == i16(1)`. +pub fn (s string) i16() i16 { + return i16(JS.parseInt(s)) +} + +// f32 returns the value of the string as f32 `'1.0'.f32() == f32(1)`. +pub fn (s string) f32() f32 { + // return C.atof(&char(s.str)) + return f32(JS.parseFloat(s)) +} + +// f64 returns the value of the string as f64 `'1.0'.f64() == f64(1)`. +pub fn (s string) f64() f64 { + return f64(JS.parseFloat(s)) +} + +// u16 returns the value of the string as u16 `'1'.u16() == u16(1)`. +pub fn (s string) u16() u16 { + return u16(JS.parseInt(s)) +} + +// u32 returns the value of the string as u32 `'1'.u32() == u32(1)`. +pub fn (s string) u32() u32 { + return u32(JS.parseInt(s)) +} + +// u64 returns the value of the string as u64 `'1'.u64() == u64(1)`. +pub fn (s string) u64() u64 { + return u64(JS.parseInt(s)) +} + +// trim_right strips any of the characters given in `cutset` from the right of the string. +// Example: assert ' Hello V d'.trim_right(' d') == ' Hello V' +pub fn (s string) trim_right(cutset string) string { + if s.len < 1 || cutset.len < 1 { + return s.clone() + } + + mut pos := s.len - 1 + + for pos >= 0 { + mut found := false + for cs in cutset { + if s[pos] == cs { + found = true + } + } + if !found { + break + } + pos-- + } + + if pos < 0 { + return '' + } + + return s[..pos + 1] +} + +// trim_left strips any of the characters given in `cutset` from the left of the string. +// Example: assert 'd Hello V developer'.trim_left(' d') == 'Hello V developer' +[direct_array_access] +pub fn (s string) trim_left(cutset string) string { + if s.len < 1 || cutset.len < 1 { + return s.clone() + } + mut pos := 0 + for pos < s.len { + mut found := false + for cs in cutset { + if s[pos] == cs { + found = true + break + } + } + if !found { + break + } + pos++ + } + return s[pos..] +} + +// trim_prefix strips `str` from the start of the string. +// Example: assert 'WorldHello V'.trim_prefix('World') == 'Hello V' +pub fn (s string) trim_prefix(str string) string { + if s.starts_with(str) { + return s[str.len..] + } + return s.clone() +} + +// trim_suffix strips `str` from the end of the string. +// Example: assert 'Hello VWorld'.trim_suffix('World') == 'Hello V' +pub fn (s string) trim_suffix(str string) string { + if s.ends_with(str) { + return s[..s.len - str.len] + } + return s.clone() +} + +// compare_strings returns `-1` if `a < b`, `1` if `a > b` else `0`. +pub fn compare_strings(a &string, b &string) int { + if a < b { + return -1 + } + if a > b { + return 1 + } + return 0 +} + +// compare_strings_reverse returns `1` if `a < b`, `-1` if `a > b` else `0`. +fn compare_strings_reverse(a &string, b &string) int { + if a < b { + return 1 + } + if a > b { + return -1 + } + return 0 +} + +// compare_strings_by_len returns `-1` if `a.len < b.len`, `1` if `a.len > b.len` else `0`. +fn compare_strings_by_len(a &string, b &string) int { + if a.len < b.len { + return -1 + } + if a.len > b.len { + return 1 + } + return 0 +} + +// compare_lower_strings returns the same as compare_strings but converts `a` and `b` to lower case before comparing. +fn compare_lower_strings(a &string, b &string) int { + aa := a.to_lower() + bb := b.to_lower() + return compare_strings(&aa, &bb) +} + +// at returns the byte at index `idx`. +// Example: assert 'ABC'.at(1) == byte(`B`) +fn (s string) at(idx int) byte { + mut result := byte(0) + #result = new byte(s.str.charCodeAt(result)) + + return result +} + +pub fn (s string) to_lower() string { + mut result := '' + #let str = s.str.toLowerCase() + #result = new string(str) + + return result +} + +// TODO: check if that behaves the same as V's own string.replace(old_sub,new_sub): +pub fn (s string) replace(old_sub string, new_sub string) string { + mut result := '' + #result = new string( s.str.replaceAll(old_sub.str, new_sub.str) ) + + return result +} + +pub fn (s string) to_upper() string { + mut result := '' + #let str = s.str.toUpperCase() + #result = new string(str) + + return result +} + +// sort sorts the string array. +pub fn (mut s []string) sort() { + s.sort_with_compare(compare_strings) +} + +// sort_ignore_case sorts the string array using case insesitive comparing. +pub fn (mut s []string) sort_ignore_case() { + s.sort_with_compare(compare_lower_strings) +} + +// sort_by_len sorts the the string array by each string's `.len` length. +pub fn (mut s []string) sort_by_len() { + s.sort_with_compare(compare_strings_by_len) +} + +// str returns a copy of the string +pub fn (s string) str() string { + return s.clone() +} + +pub fn (s string) repeat(count int) string { + mut result := '' + #result = new string(s.str.repeat(count)) + + return result +} + +// TODO(playX): Use this iterator instead of using .split('').map(c => byte(c)) +#function string_iterator(string) { this.stringIteratorFieldIndex = 0; this.stringIteratorIteratedString = string.str; } +#string_iterator.prototype.next = function next() { +#var done = true; +#var value = undefined; +#var position = this.stringIteratorFieldIndex; +#if (position !== -1) { +#var string = this.stringIteratorIteratedString; +#var length = string.length >>> 0; +#if (position >= length) { +#this.stringIteratorFieldIndex = -1; +#} else { +#done = false; +#var first = string.charCodeAt(position); +#if (first < 0xD800 || first > 0xDBFF || position + 1 === length) +#value = new byte(string[position]); +#else { +#value = new byte(string[position]+string[position+1]) +#} +#this.stringIteratorFieldIndex = position + value.length; +#} +#} +#return { +#value, done +#} +#} +#string.prototype[Symbol.iterator] = function () { return new string_iterator(this) } + +// TODO: Make these functions actually work. +// strip_margin allows multi-line strings to be formatted in a way that removes white-space +// before a delimeter. by default `|` is used. +// Note: the delimiter has to be a byte at this time. That means surrounding +// the value in ``. +// +// Example: +// st := 'Hello there, +// |this is a string, +// | Everything before the first | is removed'.strip_margin() +// Returns: +// Hello there, +// this is a string, +// Everything before the first | is removed +pub fn (s string) strip_margin() string { + return s.strip_margin_custom(`|`) +} + +// strip_margin_custom does the same as `strip_margin` but will use `del` as delimiter instead of `|` +[direct_array_access] +pub fn (s string) strip_margin_custom(del byte) string { + mut sep := del + if sep.is_space() { + eprintln('Warning: `strip_margin` cannot use white-space as a delimiter') + eprintln(' Defaulting to `|`') + sep = `|` + } + // don't know how much space the resulting string will be, but the max it + // can be is this big + mut ret := []byte{} + #ret = new array() + + mut count := 0 + for i := 0; i < s.len; i++ { + if s[i] in [10, 13] { + unsafe { + ret[count] = s[i] + } + count++ + // CRLF + if s[i] == 13 && i < s.len - 1 && s[i + 1] == 10 { + unsafe { + ret[count] = s[i + 1] + } + count++ + i++ + } + for s[i] != sep { + i++ + if i >= s.len { + break + } + } + } else { + unsafe { + ret[count] = s[i] + } + count++ + } + } + /* + unsafe { + ret[count] = 0 + return ret.vstring_with_len(count) + }*/ + mut result := '' + #for (let x of ret.arr) result.str += String.fromCharCode(x.val) + + return result +} + +// split_nth splits the string based on the passed `delim` substring. +// It returns the first Nth parts. When N=0, return all the splits. +// The last returned element has the remainder of the string, even if +// the remainder contains more `delim` substrings. +[direct_array_access] +pub fn (s string) split_nth(delim string, nth int) []string { + mut res := []string{} + mut i := 0 + + match delim.len { + 0 { + i = 1 + for ch in s { + if nth > 0 && i >= nth { + res << s[i..] + break + } + res << ch.str() + i++ + } + return res + } + 1 { + mut start := 0 + delim_byte := delim[0] + + for i < s.len { + if s[i] == delim_byte { + was_last := nth > 0 && res.len == nth - 1 + if was_last { + break + } + val := s[start..i] //.substr(start, i) + res << val + start = i + delim.len + i = start + } else { + i++ + } + } + + // Then the remaining right part of the string + if nth < 1 || res.len < nth { + res << s[start..] + } + return res + } + else { + mut start := 0 + // Take the left part for each delimiter occurence + for i <= s.len { + is_delim := i + delim.len <= s.len && s[i..i + delim.len] == delim + if is_delim { + was_last := nth > 0 && res.len == nth - 1 + if was_last { + break + } + val := s[start..i] // .substr(start, i) + res << val + start = i + delim.len + i = start + } else { + i++ + } + } + // Then the remaining right part of the string + if nth < 1 || res.len < nth { + res << s[start..] + } + return res + } + } +} + +struct RepIndex { + idx int + val_idx int +} + +// replace_each replaces all occurences of the string pairs given in `vals`. +// Example: assert 'ABCD'.replace_each(['B','C/','C','D','D','C']) == 'AC/DC' +[direct_array_access] +pub fn (s string) replace_each(vals []string) string { + if s.len == 0 || vals.len == 0 { + return s.clone() + } + + if vals.len % 2 != 0 { + eprintln('string.replace_each(): odd number of strings') + return s.clone() + } + + // `rep` - string to replace + // `with_` - string to replace with_ + // Remember positions of all rep strings, and calculate the length + // of the new string to do just one allocation. + + mut idxs := []RepIndex{} + mut idx := 0 + mut new_len := s.len + s_ := s.clone() + #function setCharAt(str,index,chr) { + #if(index > str.length-1) return str; + #return str.substring(0,index) + chr + str.substring(index+1); + #} + + for rep_i := 0; rep_i < vals.len; rep_i = rep_i + 2 { + rep := vals[rep_i] + + mut with_ := vals[rep_i + 1] + with_ = with_ + + for { + idx = s_.index_after(rep, idx) + if idx == -1 { + break + } + + for i in 0 .. rep.len { + mut j_ := i + j_ = j_ + #s_.str = setCharAt(s_.str,idx + i, String.fromCharCode(127)) + } + + rep_idx := RepIndex{ + idx: 0 + val_idx: 0 + } + // todo: primitives should always be copied + #rep_idx.idx = idx.val + #rep_idx.val_idx = new int(rep_i.val) + idxs << rep_idx + idx += rep.len + new_len += with_.len - rep.len + } + } + + if idxs.len == 0 { + return s.clone() + } + + idxs.sort(a.idx < b.idx) + + mut b := '' + #for (let i = 0; i < new_len.val;i++) b.str += String.fromCharCode(127) + + new_len = new_len + mut idx_pos := 0 + mut cur_idx := idxs[idx_pos] + mut b_i := 0 + for i := 0; i < s.len; i++ { + if i == cur_idx.idx { + rep := vals[cur_idx.val_idx] + with_ := vals[cur_idx.val_idx + 1] + for j in 0 .. with_.len { + mut j_ := j + + j_ = j_ + #b.str = setCharAt(b.str,b_i, with_.str[j]) + //#b.str[b_i] = with_.str[j] + b_i++ + } + i += rep.len - 1 + idx_pos++ + if idx_pos < idxs.len { + cur_idx = idxs[idx_pos] + } + } else { + #b.str = setCharAt(b.str,b_i,s.str[i]) //b.str[b_i] = s.str[i] + b_i++ + } + } + + return b +} + +// last_index returns the position of the last occurence of the input string. +fn (s string) last_index_(p string) int { + if p.len > s.len || p.len == 0 { + return -1 + } + mut i := s.len - p.len + for i >= 0 { + mut j := 0 + for j < p.len && s[i + j] == p[j] { + j++ + } + if j == p.len { + return i + } + i-- + } + return -1 +} + +// last_index returns the position of the last occurence of the input string. +pub fn (s string) last_index(p string) ?int { + idx := s.last_index_(p) + if idx == -1 { + return none + } + return idx +} + +pub fn (s string) trim_space() string { + res := '' + #res.str = s.str.trim() + + return res +} + +pub fn (s string) index_after(p string, start int) int { + if p.len > s.len { + return -1 + } + + mut strt := start + if start < 0 { + strt = 0 + } + if start >= s.len { + return -1 + } + mut i := strt + + for i < s.len { + mut j := 0 + mut ii := i + for j < p.len && s[ii] == p[j] { + j++ + ii++ + } + + if j == p.len { + return i + } + i++ + } + return -1 +} + +pub fn (s string) split_into_lines() []string { + mut res := []string{} + #let i = 0 + #s.str.split('\n').forEach((str) => { + #res.arr[i] = new string(str); + #}) + + return res +} diff --git a/v_windows/v/vlib/builtin/linux_bare/libc_impl.v b/v_windows/v/vlib/builtin/linux_bare/libc_impl.v new file mode 100644 index 0000000..a8d6d63 --- /dev/null +++ b/v_windows/v/vlib/builtin/linux_bare/libc_impl.v @@ -0,0 +1,162 @@ +module builtin + +[unsafe] +pub fn memcpy(dest &C.void, src &C.void, n size_t) &C.void { + dest_ := unsafe { &byte(dest) } + src_ := unsafe { &byte(src) } + unsafe { + for i in 0 .. int(n) { + dest_[i] = src_[i] + } + } + return unsafe { dest } +} + +[export: 'malloc'] +[unsafe] +fn __malloc(n size_t) &C.void { + return unsafe { malloc(int(n)) } +} + +[unsafe] +fn strlen(_s &C.void) size_t { + s := unsafe { &byte(_s) } + mut i := 0 + for ; unsafe { s[i] } != 0; i++ {} + return size_t(i) +} + +[unsafe] +fn realloc(old_area &C.void, new_size size_t) &C.void { + if old_area == 0 { + return unsafe { malloc(int(new_size)) } + } + if new_size == size_t(0) { + unsafe { free(old_area) } + return 0 + } + old_size := unsafe { *(&u64(old_area - sizeof(u64))) } + if u64(new_size) <= old_size { + return unsafe { old_area } + } else { + new_area := unsafe { malloc(int(new_size)) } + unsafe { memmove(new_area, old_area, size_t(old_size)) } + unsafe { free(old_area) } + return new_area + } +} + +[unsafe] +fn memset(s &C.void, c int, n size_t) &C.void { + mut s_ := unsafe { &char(s) } + for i in 0 .. int(n) { + unsafe { + s_[i] = char(c) + } + } + return unsafe { s } +} + +[unsafe] +fn memmove(dest &C.void, src &C.void, n size_t) &C.void { + dest_ := unsafe { &byte(dest) } + src_ := unsafe { &byte(src) } + mut temp_buf := unsafe { malloc(int(n)) } + for i in 0 .. int(n) { + unsafe { + temp_buf[i] = src_[i] + } + } + + for i in 0 .. int(n) { + unsafe { + dest_[i] = temp_buf[i] + } + } + unsafe { free(temp_buf) } + return unsafe { dest } +} + +[export: 'calloc'] +[unsafe] +fn __calloc(nmemb size_t, size size_t) &C.void { + new_area := unsafe { malloc(int(nmemb) * int(size)) } + unsafe { memset(new_area, 0, nmemb * size) } + return new_area +} + +fn getchar() int { + x := byte(0) + sys_read(0, &x, 1) + return int(x) +} + +fn memcmp(a &C.void, b &C.void, n size_t) int { + a_ := unsafe { &byte(a) } + b_ := unsafe { &byte(b) } + for i in 0 .. int(n) { + if unsafe { a_[i] != b_[i] } { + unsafe { + return a_[i] - b_[i] + } + } + } + return 0 +} + +[export: 'free'] +[unsafe] +fn __free(ptr &C.void) { + err := mm_free(ptr) + if err != .enoerror { + eprintln('free error:') + panic(err) + } +} + +fn vsprintf(str &char, format &char, ap &byte) int { + panic('vsprintf(): string interpolation is not supported in `-freestanding`') +} + +fn vsnprintf(str &char, size size_t, format &char, ap &byte) int { + panic('vsnprintf(): string interpolation is not supported in `-freestanding`') +} + +// not really needed +fn bare_read(buf &byte, count u64) (i64, Errno) { + return sys_read(0, buf, count) +} + +fn bare_print(buf &byte, len u64) { + sys_write(1, buf, len) +} + +fn bare_eprint(buf &byte, len u64) { + sys_write(2, buf, len) +} + +pub fn write(fd i64, buf &byte, count u64) i64 { + x, _ := sys_write(fd, buf, count) + return x +} + +[noreturn] +fn bare_panic(msg string) { + println('V panic' + msg) + exit(1) +} + +fn bare_backtrace() string { + return 'backtraces are not available with `-freestanding`' +} + +[export: 'exit'] +[noreturn] +fn __exit(code int) { + sys_exit(code) +} + +[export: 'qsort'] +fn __qsort(base voidptr, nmemb size_t, size size_t, sort_cb FnSortCB) { + panic('qsort() is not yet implemented in `-freestanding`') +} diff --git a/v_windows/v/vlib/builtin/linux_bare/linux_syscalls.v b/v_windows/v/vlib/builtin/linux_bare/linux_syscalls.v new file mode 100644 index 0000000..b0b2c47 --- /dev/null +++ b/v_windows/v/vlib/builtin/linux_bare/linux_syscalls.v @@ -0,0 +1,433 @@ +module builtin + +enum SigIndex { + si_signo = 0x00 + si_code = 0x02 + si_pid = 0x04 + si_uid = 0x05 + si_status = 0x06 + si_size = 0x80 +} + +enum Signo { + sighup = 1 // Hangup. + sigint = 2 // Interactive attention signal. + sigquit = 3 // Quit. + sigill = 4 // Illegal instruction. + sigtrap = 5 // Trace/breakpoint trap. + sigabrt = 6 // Abnormal termination. + sigbus = 7 + sigfpe = 8 // Erroneous arithmetic operation. + sigkill = 9 // Killed. + sigusr1 = 10 + sigsegv = 11 // Invalid access to memory. + sigusr2 = 12 + sigpipe = 13 // Broken pipe. + sigalrm = 14 // Alarm clock. + sigterm = 15 // Termination request. + sigstkflt = 16 + sigchld = 17 + sigcont = 18 + sigstop = 19 + sigtstp = 20 + sigttin = 21 // Background read from control terminal. + sigttou = 22 // Background write to control terminal. + sigurg = 23 + sigxcpu = 24 // CPU time limit exceeded. + sigxfsz = 25 // File size limit exceeded. + sigvtalrm = 26 // Virtual timer expired. + sigprof = 27 // Profiling timer expired. + sigwinch = 28 + sigpoll = 29 + sigsys = 31 +} + +// List of all the errors returned by syscalls +enum Errno { + enoerror = 0x00000000 + eperm = 0x00000001 + enoent = 0x00000002 + esrch = 0x00000003 + eintr = 0x00000004 + eio = 0x00000005 + enxio = 0x00000006 + e2big = 0x00000007 + enoexec = 0x00000008 + ebadf = 0x00000009 + echild = 0x0000000a + eagain = 0x0000000b + enomem = 0x0000000c + eacces = 0x0000000d + efault = 0x0000000e + enotblk = 0x0000000f + ebusy = 0x00000010 + eexist = 0x00000011 + exdev = 0x00000012 + enodev = 0x00000013 + enotdir = 0x00000014 + eisdir = 0x00000015 + einval = 0x00000016 + enfile = 0x00000017 + emfile = 0x00000018 + enotty = 0x00000019 + etxtbsy = 0x0000001a + efbig = 0x0000001b + enospc = 0x0000001c + espipe = 0x0000001d + erofs = 0x0000001e + emlink = 0x0000001f + epipe = 0x00000020 + edom = 0x00000021 + erange = 0x00000022 +} + +enum MemProt { + prot_read = 0x1 + prot_write = 0x2 + prot_exec = 0x4 + prot_none = 0x0 + prot_growsdown = 0x01000000 + prot_growsup = 0x02000000 +} + +enum MapFlags { + map_shared = 0x01 + map_private = 0x02 + map_shared_validate = 0x03 + map_type = 0x0f + map_fixed = 0x10 + map_file = 0x00 + map_anonymous = 0x20 + map_huge_shift = 26 + map_huge_mask = 0x3f +} + +// const ( +// fcntlf_dupfd = 0x00000000 +// fcntlf_exlck = 0x00000004 +// fcntlf_getfd = 0x00000001 +// fcntlf_getfl = 0x00000003 +// fcntlf_getlk = 0x00000005 +// fcntlf_getlk64 = 0x0000000c +// fcntlf_getown = 0x00000009 +// fcntlf_getowner_uids = 0x00000011 +// fcntlf_getown_ex = 0x00000010 +// fcntlf_getsig = 0x0000000b +// fcntlf_ofd_getlk = 0x00000024 +// fcntlf_ofd_setlk = 0x00000025 +// fcntlf_ofd_setlkw = 0x00000026 +// fcntlf_owner_pgrp = 0x00000002 +// fcntlf_owner_pid = 0x00000001 +// fcntlf_owner_tid = 0x00000000 +// fcntlf_rdlck = 0x00000000 +// fcntlf_setfd = 0x00000002 +// fcntlf_setfl = 0x00000004 +// fcntlf_setlk = 0x00000006 +// fcntlf_setlk64 = 0x0000000d +// fcntlf_setlkw = 0x00000007 +// fcntlf_setlkw64 = 0x0000000e +// fcntlf_setown = 0x00000008 +// fcntlf_setown_ex = 0x0000000f +// fcntlf_setsig = 0x0000000a +// fcntlf_shlck = 0x00000008 +// fcntlf_unlck = 0x00000002 +// fcntlf_wrlck = 0x00000001 +// fcntllock_ex = 0x00000002 +// fcntllock_mand = 0x00000020 +// fcntllock_nb = 0x00000004 +// fcntllock_read = 0x00000040 +// fcntllock_rw = 0x000000c0 +// fcntllock_sh = 0x00000001 +// fcntllock_un = 0x00000008 +// fcntllock_write = 0x00000080 +// fcntlo_accmode = 0x00000003 +// fcntlo_append = 0x00000400 +// fcntlo_cloexec = 0x00080000 +// fcntlo_creat = 0x00000040 +// fcntlo_direct = 0x00004000 +// fcntlo_directory = 0x00010000 +// fcntlo_dsync = 0x00001000 +// fcntlo_excl = 0x00000080 +// fcntlo_largefile = 0x00008000 +// fcntlo_ndelay = 0x00000800 +// fcntlo_noatime = 0x00040000 +// fcntlo_noctty = 0x00000100 +// fcntlo_nofollow = 0x00020000 +// fcntlo_nonblock = 0x00000800 +// fcntlo_path = 0x00200000 +// fcntlo_rdonly = 0x00000000 +// fcntlo_rdwr = 0x00000002 +// fcntlo_trunc = 0x00000200 +// fcntlo_wronly = 0x00000001 +// ) + +/* +Paraphrased from "man 2 waitid" on Linux + + Upon successful return, waitid() fills in the + following fields of the siginfo_t structure + pointed to by infop: + + si_pid, offset 0x10, int index 0x04: + The process ID of the child. + + si_uid: offset 0x14, int index 0x05 + The real user ID of the child. + + si_signo: offset 0x00, int index 0x00 + Always set to SIGCHLD. + + si_status: ofset 0x18, int index 0x06 + 1 the exit status of the child, as given to _exit(2) + (or exit(3)) (sc_sys.cld_exited) + 2 the signal that caused the child to terminate, stop, + or continue. + 3 The si_code field can be used to determine how to + interpret this field. + + si_code, set to one of (enum Wi_si_code), offset 0x08, int index 0x02: + CLD_EXITED (child called _exit(2)); + CLD_KILLED (child killed by signal); + CLD_DUMPED (child killed by signal, and dumped core); + CLD_STOPPED (child stopped by signal); + CLD_TRAPPED (traced child has trapped); + CLD_CONTINUED (child continued by SIGCONT). +*/ + +const ( + wp_sys_wnohang = u64(0x00000001) + wp_sys_wuntraced = u64(0x00000002) + wp_sys_wstopped = u64(0x00000002) + wp_sys_wexited = u64(0x00000004) + wp_sys_wcontinued = u64(0x00000008) + wp_sys_wnowait = u64(0x01000000) // don't reap, just poll status. + wp_sys___wnothread = u64(0x20000000) // don't wait on children of other threads in this group + wp_sys___wall = u64(0x40000000) // wait on all children, regardless of type + wp_sys___wclone = u64(0x80000000) // wait only on non-sigchld children +) + +// First argument to waitid: +enum WiWhich { + p_all = 0 + p_pid = 1 + p_pgid = 2 +} + +enum WiSiCode { + cld_exited = 1 // child has exited + cld_killed = 2 // child was killed + cld_dumped = 3 // child terminated abnormally + cld_trapped = 4 // traced child has trapped + cld_stopped = 5 // child has stopped + cld_continued = 6 // stopped child has continued +} + +fn split_int_errno(rc_in u64) (i64, Errno) { + rc := i64(rc_in) + if rc < 0 { + return i64(-1), Errno(-rc) + } + return rc, Errno.enoerror +} + +// 0 sys_read +fn sys_read(fd i64, buf &byte, count u64) (i64, Errno) { + return split_int_errno(sys_call3(0, u64(fd), u64(buf), count)) +} + +// 1 sys_write +pub fn sys_write(fd i64, buf &byte, count u64) (i64, Errno) { + return split_int_errno(sys_call3(1, u64(fd), u64(buf), count)) +} + +// 2 sys_open +fn sys_open(filename &byte, flags i64, mode int) (i64, Errno) { + return split_int_errno(sys_call3(2, u64(filename), u64(flags), u64(mode))) +} + +// 3 sys_close +fn sys_close(fd i64) Errno { + return Errno(-i64(sys_call1(3, u64(fd)))) +} + +// 9 sys_mmap +fn sys_mmap(addr &byte, len u64, prot MemProt, flags MapFlags, fildes u64, off u64) (&byte, Errno) { + rc := sys_call6(9, u64(addr), len, u64(prot), u64(flags), fildes, off) + a, e := split_int_errno(rc) + return &byte(a), e +} + +// 11 sys_munmap +fn sys_munmap(addr voidptr, len u64) Errno { + return Errno(-sys_call2(11, u64(addr), len)) +} + +// 22 sys_pipe +fn sys_pipe(filedes &int) Errno { + return Errno(sys_call1(22, u64(filedes))) +} + +// 24 sys_sched_yield +fn sys_sched_yield() Errno { + return Errno(sys_call0(24)) +} + +// 28 sys_madvise +fn sys_madvise(addr voidptr, len u64, advice int) Errno { + return Errno(sys_call3(28, u64(addr), len, u64(advice))) +} + +// 39 sys_getpid +fn sys_getpid() int { + return int(sys_call0(39)) +} + +// 57 sys_fork +fn sys_fork() int { + return int(sys_call0(57)) +} + +// 58 sys_vfork +fn sys_vfork() int { + return int(sys_call0(58)) +} + +// 33 sys_dup2 +fn sys_dup2(oldfd int, newfd int) (i64, Errno) { + return split_int_errno(sys_call2(33, u64(oldfd), u64(newfd))) +} + +// 59 sys_execve +fn sys_execve(filename &byte, argv []&byte, envp []&byte) int { + return int(sys_call3(59, u64(filename), argv.data, envp.data)) +} + +// 60 sys_exit +[noreturn] +fn sys_exit(ec int) { + sys_call1(60, u64(ec)) + for {} +} + +// 102 sys_getuid +fn sys_getuid() int { + return int(sys_call0(102)) +} + +// 247 sys_waitid +fn sys_waitid(which WiWhich, pid int, infop &int, options int, ru voidptr) Errno { + return Errno(sys_call5(247, u64(which), u64(pid), u64(infop), u64(options), u64(ru))) +} + +fn sys_call0(scn u64) u64 { + mut res := u64(0) + asm amd64 { + syscall + ; =a (res) + ; a (scn) + } + return res +} + +fn sys_call1(scn u64, arg1 u64) u64 { + mut res := u64(0) + asm amd64 { + syscall + ; =a (res) + ; a (scn) + D (arg1) + } + return res +} + +fn sys_call2(scn u64, arg1 u64, arg2 u64) u64 { + mut res := u64(0) + asm amd64 { + syscall + ; =a (res) + ; a (scn) + D (arg1) + S (arg2) + } + return res +} + +fn sys_call3(scn u64, arg1 u64, arg2 u64, arg3 u64) u64 { + mut res := u64(0) + asm amd64 { + syscall + ; =a (res) + ; a (scn) + D (arg1) + S (arg2) + d (arg3) + } + return res +} + +fn sys_call4(scn u64, arg1 u64, arg2 u64, arg3 u64, arg4 u64) u64 { + mut res := u64(0) + asm amd64 { + mov r10, arg4 + syscall + ; =a (res) + ; a (scn) + D (arg1) + S (arg2) + d (arg3) + r (arg4) + ; r10 + } + return res +} + +fn sys_call5(scn u64, arg1 u64, arg2 u64, arg3 u64, arg4 u64, arg5 u64) u64 { + mut res := u64(0) + asm amd64 { + mov r10, arg4 + mov r8, arg5 + syscall + ; =a (res) + ; a (scn) + D (arg1) + S (arg2) + d (arg3) + r (arg4) + r (arg5) + ; r10 + r8 + } + return res +} + +fn sys_call6(scn u64, arg1 u64, arg2 u64, arg3 u64, arg4 u64, arg5 u64, arg6 u64) u64 { + mut res := u64(0) + asm amd64 { + mov r10, arg4 + mov r8, arg5 + mov r9, arg6 + syscall + ; =a (res) + ; a (scn) + D (arg1) + S (arg2) + d (arg3) + r (arg4) + r (arg5) + r (arg6) + ; r10 + r8 + r9 + } + return res +} + +asm amd64 { + .globl _start + _start: + call main + mov rax, 60 + xor rdi, rdi + syscall + ret +} diff --git a/v_windows/v/vlib/builtin/linux_bare/memory_managment.v b/v_windows/v/vlib/builtin/linux_bare/memory_managment.v new file mode 100644 index 0000000..9c23dcf --- /dev/null +++ b/v_windows/v/vlib/builtin/linux_bare/memory_managment.v @@ -0,0 +1,29 @@ +module builtin + +fn mm_alloc(size u64) (&byte, Errno) { + // BEGIN CONSTS + // the constants need to be here, since the initialization of other constants, + // which happen before these ones would, require malloc + mem_prot := MemProt(int(MemProt.prot_read) | int(MemProt.prot_write)) + map_flags := MapFlags(int(MapFlags.map_private) | int(MapFlags.map_anonymous)) + // END CONSTS + + a, e := sys_mmap(&byte(0), size + sizeof(u64), mem_prot, map_flags, -1, 0) + if e == .enoerror { + unsafe { + mut ap := &u64(a) + *ap = size + x2 := &byte(a + sizeof(u64)) + return x2, e + } + } + return &byte(0), e +} + +fn mm_free(addr &byte) Errno { + unsafe { + ap := &u64(addr - sizeof(u64)) + size := *ap + return sys_munmap(addr - sizeof(u64), size + sizeof(u64)) + } +} diff --git a/v_windows/v/vlib/builtin/linux_bare/old/.checks/.gitignore b/v_windows/v/vlib/builtin/linux_bare/old/.checks/.gitignore new file mode 100644 index 0000000..4132964 --- /dev/null +++ b/v_windows/v/vlib/builtin/linux_bare/old/.checks/.gitignore @@ -0,0 +1,5 @@ +checks +linuxsys/linuxsys +string/string +consts/consts +structs/structs diff --git a/v_windows/v/vlib/builtin/linux_bare/old/.checks/checks.v b/v_windows/v/vlib/builtin/linux_bare/old/.checks/checks.v new file mode 100644 index 0000000..8fd3575 --- /dev/null +++ b/v_windows/v/vlib/builtin/linux_bare/old/.checks/checks.v @@ -0,0 +1,32 @@ +module main + +import os + +fn failed (msg string) { + println ("!!! failed: $msg") +} + +fn passed (msg string) { + println (">>> passed: $msg") +} + + +fn vcheck(vfile string) { + run_check := "v -user_mod_path . -freestanding run " + if 0 == os.system("$run_check $vfile/${vfile}.v") { + passed(run_check) + } else { + failed(run_check) + } + os.system("ls -lh $vfile/$vfile") + os.system("rm -f $vfile/$vfile") +} + +fn main() { + vcheck("linuxsys") + vcheck("string") + vcheck("consts") + vcheck("structs") + exit(0) +} + diff --git a/v_windows/v/vlib/builtin/linux_bare/old/.checks/consts/consts.v b/v_windows/v/vlib/builtin/linux_bare/old/.checks/consts/consts.v new file mode 100644 index 0000000..eee2c61 --- /dev/null +++ b/v_windows/v/vlib/builtin/linux_bare/old/.checks/consts/consts.v @@ -0,0 +1,22 @@ +module main +import forkedtest + +const ( + integer1 = 111 + integer2 = 222 + integer3 = integer1+integer2 + integer9 = integer3 * 3 + abc = "123" +) + +fn check_const_initialization() { + assert abc == "123" + assert integer9 == 999 +} + +fn main(){ + mut fails := 0 + fails += forkedtest.normal_run(check_const_initialization, "check_const_initialization") + assert fails == 0 + sys_exit(0) +} diff --git a/v_windows/v/vlib/builtin/linux_bare/old/.checks/forkedtest/forkedtest.v b/v_windows/v/vlib/builtin/linux_bare/old/.checks/forkedtest/forkedtest.v new file mode 100644 index 0000000..6d9272c --- /dev/null +++ b/v_windows/v/vlib/builtin/linux_bare/old/.checks/forkedtest/forkedtest.v @@ -0,0 +1,49 @@ +module forkedtest + +pub fn run (op fn(), label string, code Wi_si_code, status int) int { + child := sys_fork() + if child == 0 { + op() + sys_exit(0) + } + + siginfo := []int{len:int(Sig_index.si_size)} + + e := sys_waitid(.p_pid, child, intptr(&siginfo[0]), .wexited, 0) + + assert e == .enoerror + assert siginfo[int(Sig_index.si_pid)] == child + assert siginfo[int(Sig_index.si_signo)] == int(Signo.sigchld) + assert siginfo[int(Sig_index.si_uid)] == sys_getuid() + + r_code := siginfo[Sig_index.si_code] + r_status := siginfo[Sig_index.si_status] + + print("+++ ") + print(label) + if (int(code) == r_code) && (status == r_status) { + println(" PASSED") + return 0 + } + println(" FAILED") + + if int(code) != r_code { + print(">> Expecting si_code 0x") + println(i64_str(int(code),16)) + print(">> Got 0x") + println(i64_str(r_code,16)) + } + + if status != r_status { + print(">> Expecting status 0x") + println(i64_str(status,16)) + print(">> Got 0x") + println(i64_str(r_status,16)) + } + + return 1 +} + +pub fn normal_run (op fn(), label string) int { + return run (op, label, .cld_exited, 0) +} diff --git a/v_windows/v/vlib/builtin/linux_bare/old/.checks/linuxsys/linuxsys.v b/v_windows/v/vlib/builtin/linux_bare/old/.checks/linuxsys/linuxsys.v new file mode 100644 index 0000000..cca5cb6 --- /dev/null +++ b/v_windows/v/vlib/builtin/linux_bare/old/.checks/linuxsys/linuxsys.v @@ -0,0 +1,300 @@ +module main +import forkedtest + +const ( + sample_text_file1 = "" +) + +fn check_fork_minimal () { + child := sys_fork() + ec := 100 + if child == 0 { + println("child") + sys_exit(ec) + } + siginfo := [ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0] + + e := sys_waitid(.p_pid, child, intptr(siginfo.data) , .wexited, 0) + + assert e == .enoerror + //println(i64_tos(buffer0,80,siginfo[Sig_index.si_code],16)) + assert siginfo[Sig_index.si_code] == int(Wi_si_code.cld_exited) + assert siginfo[Sig_index.si_pid] == child + assert siginfo[Sig_index.si_status] == ec + assert siginfo[Sig_index.si_signo] == int(Signo.sigchld) + assert siginfo[Sig_index.si_uid] == sys_getuid() +} + +fn check_read_write_pipe() { + // Checks the following system calls: + // sys_pipe + // sys_write + // sys_read + // sys_close + // + buffer0 := []byte{len:(128)} + buffer := byteptr(buffer0.data) + + fd := [-1, -1] + + assert fd[0] == -1 + assert fd[1] == -1 + + a := sys_pipe(intptr(&fd[0])) + + assert a == .enoerror + + assert fd[0] != -1 + assert fd[1] != -1 + + test_data := "test_data" + b := test_data.len + 1 + c1, e1 := sys_write (fd[1], test_data.str, u64(b)) + + assert e1 == .enoerror + assert c1 == b + + c2, e2 := sys_read(fd[0], buffer, u64(b)) + + assert e2 == .enoerror + assert c2 == b + + assert buffer[b-1] == 0 + + for i in 0..b { + assert test_data[i] == buffer[i] + } + + assert sys_close(fd[0]) == .enoerror + assert sys_close(fd[1]) == .enoerror + + assert sys_close(-1) == .ebadf +} + +fn check_read_file() { + /* + Checks the following system calls: + sys_read + sys_write + sys_close + sys_open + */ + buffer0 := []byte{len:(128)} + buffer := byteptr(buffer0.data) + + test_file := "sample_text1.txt" + sample_text := "Do not change this text.\n" + fd, ec := sys_open(test_file.str, .o_rdonly, 0) + assert fd > 0 + assert ec == .enoerror + n := sample_text.len + c, e := sys_read(fd, buffer, u64(n*2)) + assert e == .enoerror + assert c == n + for i in 0..n { + assert sample_text[i] == buffer[i] + } + assert sys_close(fd) == .enoerror +} + +fn check_open_file_fail() { + fd1, ec1 := sys_open("./nofilehere".str, .o_rdonly, 0) + assert fd1 == -1 + assert ec1 == .enoent +} + +/* +fn check_print() { + println ("checking print and println") + + a := sys_pipe(intptr(fd)) + assert a != -1 + assert fd[0] != -1 + assert fd[1] != -1 + + //sys_dup2 + println ("print and println passed") +} +*/ + +fn check_munmap_fail() { + ec := sys_munmap(-16384,8192) + assert ec == .einval +} + +fn check_mmap_one_page() { + mp := int(Mm_prot.prot_read) | int(Mm_prot.prot_write) + mf := int(Map_flags.map_private) | int(Map_flags.map_anonymous) + mut a, e := sys_mmap(0, u64(Linux_mem.page_size), Mm_prot(mp), Map_flags(mf), -1, 0) + + assert e == .enoerror + assert a != byteptr(-1) + + for i in 0..int(Linux_mem.page_size) { + b := i & 0xFF + a[i] = b + assert a[i] == b + } + + ec := sys_munmap(a, u64(Linux_mem.page_size)) + assert ec == .enoerror +} + +fn check_mm_pages() { + for i in 0 .. int(Linux_mem.page_size)-4 { + assert u32(1) == mm_pages(u64(i)) + } + for i in int(Linux_mem.page_size)-3 .. (int(Linux_mem.page_size)*2)-4 { + assert u32(2) == mm_pages(u64(i)) + } + for i in (int(Linux_mem.page_size)*2)-3 .. (int(Linux_mem.page_size)*3)-4 { + assert u32(3) == mm_pages(u64(i)) + } +} + +//pub fn mm_alloc(size u64) (voidptr, Errno) + +fn check_mm_alloc() { + for i in 1 .. 2000 { + size := u64(i*1000) + pages := mm_pages(size) + mut a, e := mm_alloc(size) + + assert e == .enoerror + ap := intptr(a-4) + assert *ap == int(pages) + assert e == .enoerror + assert !isnil(a) + + if (i%111) == 0 { + for j in 0 .. int(size) { + b := j & 0xFF + a[j] = b + assert b == int(a[j]) + } + } + + mfa := mm_free(a) + + assert mfa == .enoerror + } +} + +fn check_int_array_ro() { + a := [100,110,120,130] + assert a.len == 4 + assert a[0] == 100 + assert a[1] == 110 + assert a[2] == 120 + assert a[3] == 130 +} + +fn check_int_array_rw() { + mut a := [-10,-11,-12,-13] + assert a.len == 4 + assert a[0] == -10 + assert a[1] == -11 + assert a[2] == -12 + assert a[3] == -13 + for i in 0..a.len { + b := -a[i] * 10 + a[i] = b + assert a[i] == b + } + assert a[3] == 130 +} + +fn check_int64_array_ro() { + a := [i64(1000),1100,1200,1300,1400] + assert a.len == 5 + assert a[0] == 1000 + assert a[1] == 1100 + assert a[2] == 1200 + assert a[3] == 1300 + assert a[4] == 1400 +} + +fn check_voidptr_array_ro() { + a := [ + voidptr(10000), + voidptr(11000), + voidptr(12000), + voidptr(13000), + voidptr(14000), + voidptr(15000) + ] + assert a.len == 6 + assert a[0] == voidptr(10000) + assert a[1] == voidptr(11000) + assert a[2] == voidptr(12000) + assert a[3] == voidptr(13000) + assert a[4] == voidptr(14000) + assert a[5] == voidptr(15000) +} + +fn check_voidptr_array_rw() { + mut a := [ + voidptr(-1), + voidptr(-1), + voidptr(-1), + voidptr(-1), + voidptr(-1), + voidptr(-1) + ] + assert a.len == 6 + + assert a[0] == voidptr(-1) + assert a[1] == voidptr(-1) + assert a[2] == voidptr(-1) + assert a[3] == voidptr(-1) + assert a[4] == voidptr(-1) + assert a[5] == voidptr(-1) + + a[0] = voidptr(100000) + assert a[0] == voidptr(100000) + + a[1] = voidptr(110000) + assert a[1] == voidptr(110000) + + a[2] = voidptr(120000) + assert a[2] == voidptr(120000) + + a[3] = voidptr(130000) + assert a[3] == voidptr(130000) + + a[4] = voidptr(140000) + assert a[4] == voidptr(140000) + + a[5] = voidptr(150000) + assert a[5] == voidptr(150000) +} + + +fn main() { + mut fails := 0 + fails += forkedtest.normal_run(check_fork_minimal, "check_fork_minimal") + fails += forkedtest.normal_run(check_munmap_fail, "check_munmap_fail") + fails += forkedtest.normal_run(check_mmap_one_page, "check_mmap_one_page") + fails += forkedtest.normal_run(check_mm_pages, "check_mm_pages") + fails += forkedtest.normal_run(check_mm_alloc, "check_mm_alloc") + fails += forkedtest.normal_run(check_read_write_pipe, "check_read_write_pipe") + fails += forkedtest.normal_run(check_read_file, "check_read_file") + // check_print() + fails += forkedtest.normal_run(check_open_file_fail, "check_open_file_fail") + fails += forkedtest.normal_run(check_int_array_ro, "check_int_array_ro") + fails += forkedtest.normal_run(check_int_array_rw, "check_int_array_rw") + fails += forkedtest.normal_run(check_int64_array_ro, "check_int64_array_ro") + fails += forkedtest.normal_run(check_voidptr_array_ro, "check_voidptr_array_ro") + fails += forkedtest.normal_run(check_voidptr_array_rw, "check_voidptr_array_rw") + + assert fails == 0 + sys_exit(0) +} diff --git a/v_windows/v/vlib/builtin/linux_bare/old/.checks/readme.md b/v_windows/v/vlib/builtin/linux_bare/old/.checks/readme.md new file mode 100644 index 0000000..03c1b30 --- /dev/null +++ b/v_windows/v/vlib/builtin/linux_bare/old/.checks/readme.md @@ -0,0 +1,5 @@ +In this directory: +``` +v run checks.v +``` + diff --git a/v_windows/v/vlib/builtin/linux_bare/old/.checks/sample_text1.txt b/v_windows/v/vlib/builtin/linux_bare/old/.checks/sample_text1.txt new file mode 100644 index 0000000..f06e75a --- /dev/null +++ b/v_windows/v/vlib/builtin/linux_bare/old/.checks/sample_text1.txt @@ -0,0 +1 @@ +Do not change this text. diff --git a/v_windows/v/vlib/builtin/linux_bare/old/.checks/string/string.v b/v_windows/v/vlib/builtin/linux_bare/old/.checks/string/string.v new file mode 100644 index 0000000..54c06e7 --- /dev/null +++ b/v_windows/v/vlib/builtin/linux_bare/old/.checks/string/string.v @@ -0,0 +1,63 @@ +module main +import forkedtest + +fn check_string_eq () { + assert "monkey" != "rat" + some_animal := "a bird" + assert some_animal == "a bird" +} + +fn check_i64_tos() { + buffer0 := []byte{len:(128)} + buffer := byteptr(buffer0.data) + + s0 := i64_tos(buffer, 70, 140, 10) + assert s0 == "140" + + s1 := i64_tos(buffer, 70, -160, 10) + assert s1 == "-160" + + s2 := i64_tos(buffer, 70, 65537, 16) + assert s2 == "10001" + + s3 := i64_tos(buffer, 70, -160000, 10) + assert s3 == "-160000" +} + +fn check_i64_str() { + assert "141" == i64_str(141, 10) + assert "-161" == i64_str(-161, 10) + assert "10002" == i64_str(65538, 16) + assert "-160001" == i64_str(-160001, 10) +} + +fn check_str_clone() { + a := i64_str(1234,10) + b := a.clone() + assert a == b + c := i64_str(-6789,10).clone() + assert c == "-6789" +} + +fn check_string_add_works(){ + abc := 'abc' + combined := 'a' + 'b' + 'c' + assert abc.len == combined.len + assert abc[0] == combined[0] + assert abc[1] == combined[1] + assert abc[2] == combined[2] + assert abc[0] == `a` + assert abc == combined +} + +fn main () { + mut fails := 0 + fails += forkedtest.normal_run(check_string_eq, "check_string_eq") + fails += forkedtest.normal_run(check_i64_tos, "check_i64_tos") + fails += forkedtest.normal_run(check_i64_str, "check_i64_str") + fails += forkedtest.normal_run(check_str_clone, "check_str_clone") + fails += forkedtest.normal_run(check_string_add_works, "check_string_add_works") + assert fails == 0 + sys_exit(0) +} + diff --git a/v_windows/v/vlib/builtin/linux_bare/old/.checks/structs/structs.v b/v_windows/v/vlib/builtin/linux_bare/old/.checks/structs/structs.v new file mode 100644 index 0000000..f63285d --- /dev/null +++ b/v_windows/v/vlib/builtin/linux_bare/old/.checks/structs/structs.v @@ -0,0 +1,42 @@ +module main +import forkedtest + +struct SimpleEmptyStruct{ +} + +struct NonEmptyStruct{ + x int + y int + z int +} + +fn check_simple_empty_struct(){ + s := SimpleEmptyStruct{} + addr_s := &s + str_addr_s := ptr_str( addr_s ) + assert !isnil(addr_s) + assert str_addr_s.len > 3 + println(str_addr_s) +} + +fn check_non_empty_struct(){ + a := NonEmptyStruct{1,2,3} + b := NonEmptyStruct{4,5,6} + assert sizeof(NonEmptyStruct) > 0 + assert sizeof(SimpleEmptyStruct) < sizeof(NonEmptyStruct) + assert a.x == 1 + assert a.y == 2 + assert a.z == 3 + assert b.x + b.y + b.z == 15 + assert ptr_str(&a) != ptr_str(&b) + println('sizeof SimpleEmptyStruct:' + i64_str( sizeof(SimpleEmptyStruct) , 10 )) + println('sizeof NonEmptyStruct:' + i64_str( sizeof(NonEmptyStruct) , 10 )) +} + +fn main(){ + mut fails := 0 + fails += forkedtest.normal_run(check_simple_empty_struct, "check_simple_empty_struct") + fails += forkedtest.normal_run(check_non_empty_struct, "check_non_empty_struct") + assert fails == 0 + sys_exit(0) +} diff --git a/v_windows/v/vlib/builtin/linux_bare/old/array_bare.v b/v_windows/v/vlib/builtin/linux_bare/old/array_bare.v new file mode 100644 index 0000000..b92214f --- /dev/null +++ b/v_windows/v/vlib/builtin/linux_bare/old/array_bare.v @@ -0,0 +1,53 @@ +module builtin + +pub struct array { +pub: + data voidptr + len int + cap int + element_size int +} + +// for now off the stack +fn new_array_from_c_array(len int, cap int, elm_size int, c_array voidptr) array { + arr := array{ + len: len + cap: cap + element_size: elm_size + data: c_array + } + return arr +} + +// Private function. Used to implement array[] operator +fn (a array) get(i int) voidptr { + if i < 0 || i >= a.len { + panic('array.get: index out of range') // FIXME: (i == $i, a.len == $a.len)') + } + return a.data + i * a.element_size +} + +// Private function. Used to implement assigment to the array element. +fn (mut a array) set(i int, val voidptr) { + if i < 0 || i >= a.len { + panic('array.set: index out of range') // FIXME: (i == $i, a.len == $a.len)') + } + mem_copy(a.data + a.element_size * i, val, a.element_size) +} + +// array.repeat returns new array with the given array elements +// repeated `nr_repeat` times +pub fn (a array) repeat(nr_repeats int) array { + assert nr_repeats >= 0 + + arr := array{ + len: nr_repeats * a.len + cap: nr_repeats * a.len + element_size: a.element_size + data: malloc(nr_repeats * a.len * a.element_size) + } + for i in 0 .. nr_repeats { + mem_copy(arr.data + i * a.len * a.element_size, a.data, a.len * a.element_size) + } + return arr +} diff --git a/v_windows/v/vlib/builtin/linux_bare/old/builtin_bare.v b/v_windows/v/vlib/builtin/linux_bare/old/builtin_bare.v new file mode 100644 index 0000000..a7be853 --- /dev/null +++ b/v_windows/v/vlib/builtin/linux_bare/old/builtin_bare.v @@ -0,0 +1,60 @@ +module builtin + +// called by the generated main/init +fn init() { +} + +pub fn isnil(p voidptr) bool { + return p == 0 +} + +pub fn print(s string) { + sys_write(1, s.str, u64(s.len)) +} + +pub fn println(s string) { + print(s) + print('\n') +} + +pub fn panic(s string) { + eprint('V panic: ') + eprintln(s) + sys_exit(1) +} + +// replaces panic when -debug arg is passed +fn panic_debug(line_no int, file string, mod string, fn_name string, s string) { + eprintln('================ V panic ================') + eprint(' module: ') + eprintln('mod') + eprint(' function: ') + eprint(fn_name) + eprintln('()') + eprintln(' file: ') + eprintln(file) + // println(' line: ${line_no}') + eprint(' message: ') + eprintln(s) + eprintln('=========================================') + sys_exit(1) +} + +pub fn eprint(s string) { + if isnil(s.str) { + panic('eprint(NIL)') + } + sys_write(2, s.str, u64(s.len)) +} + +pub fn eprint_ln(s string) { + eprint(s) + eprint('\n') +} + +pub fn eprintln(s string) { + if isnil(s.str) { + panic('eprintln(NIL)') + } + eprint_ln(s) +} diff --git a/v_windows/v/vlib/builtin/linux_bare/old/linuxsys_bare.v b/v_windows/v/vlib/builtin/linux_bare/old/linuxsys_bare.v new file mode 100644 index 0000000..ddec203 --- /dev/null +++ b/v_windows/v/vlib/builtin/linux_bare/old/linuxsys_bare.v @@ -0,0 +1,759 @@ +module builtin + +pub enum Linux_mem { + page_size = 4096 +} + +pub const ( + wp_sys_wnohang = u64(0x00000001) + wp_sys_wuntraced = u64(0x00000002) + wp_sys_wstopped = u64(0x00000002) + wp_sys_wexited = u64(0x00000004) + wp_sys_wcontinued = u64(0x00000008) + wp_sys_wnowait = u64(0x01000000) // don't reap, just poll status. + wp_sys___wnothread = u64(0x20000000) // don't wait on children of other threads in this group + wp_sys___wall = u64(0x40000000) // wait on all children, regardless of type + wp_sys___wclone = u64(0x80000000) // wait only on non-sigchld children +) + +// First argument to waitid: +pub enum Wi_which { + p_all = 0 + p_pid = 1 + p_pgid = 2 +} + +pub enum Wi_si_code { + cld_exited = 1 // child has exited + cld_killed = 2 // child was killed + cld_dumped = 3 // child terminated abnormally + cld_trapped = 4 // traced child has trapped + cld_stopped = 5 // child has stopped + cld_continued = 6 // stopped child has continued +} + +/* +Paraphrased from "man 2 waitid" on Linux + + Upon successful return, waitid() fills in the + following fields of the siginfo_t structure + pointed to by infop: + + si_pid, offset 0x10, int index 0x04: + The process ID of the child. + + si_uid: offset 0x14, int index 0x05 + The real user ID of the child. + + si_signo: offset 0x00, int index 0x00 + Always set to SIGCHLD. + + si_status: ofset 0x18, int index 0x06 + 1 the exit status of the child, as given to _exit(2) + (or exit(3)) (sc_sys.cld_exited) + 2 the signal that caused the child to terminate, stop, + or continue. + 3 The si_code field can be used to determine how to + interpret this field. + + si_code, set to one of (enum Wi_si_code), offset 0x08, int index 0x02: + CLD_EXITED (child called _exit(2)); + CLD_KILLED (child killed by signal); + CLD_DUMPED (child killed by signal, and dumped core); + CLD_STOPPED (child stopped by signal); + CLD_TRAPPED (traced child has trapped); + CLD_CONTINUED (child continued by SIGCONT). +*/ + +pub enum Sig_index { + si_signo = 0x00 + si_code = 0x02 + si_pid = 0x04 + si_uid = 0x05 + si_status = 0x06 + si_size = 0x80 +} + +pub enum Signo { + sighup = 1 // Hangup. + sigint = 2 // Interactive attention signal. + sigquit = 3 // Quit. + sigill = 4 // Illegal instruction. + sigtrap = 5 // Trace/breakpoint trap. + sigabrt = 6 // Abnormal termination. + sigbus = 7 + sigfpe = 8 // Erroneous arithmetic operation. + sigkill = 9 // Killed. + sigusr1 = 10 + sigsegv = 11 // Invalid access to storage. + sigusr2 = 12 + sigpipe = 13 // Broken pipe. + sigalrm = 14 // Alarm clock. + sigterm = 15 // Termination request. + sigstkflt = 16 + sigchld = 17 + sigcont = 18 + sigstop = 19 + sigtstp = 20 + sigttin = 21 // Background read from control terminal. + sigttou = 22 // Background write to control terminal. + sigurg = 23 + sigxcpu = 24 // CPU time limit exceeded. + sigxfsz = 25 // File size limit exceeded. + sigvtalrm = 26 // Virtual timer expired. + sigprof = 27 // Profiling timer expired. + sigwinch = 28 + sigpoll = 29 + sigsys = 31 +} + +pub const ( + fcntlf_dupfd = 0x00000000 + fcntlf_exlck = 0x00000004 + fcntlf_getfd = 0x00000001 + fcntlf_getfl = 0x00000003 + fcntlf_getlk = 0x00000005 + fcntlf_getlk64 = 0x0000000c + fcntlf_getown = 0x00000009 + fcntlf_getowner_uids = 0x00000011 + fcntlf_getown_ex = 0x00000010 + fcntlf_getsig = 0x0000000b + fcntlf_ofd_getlk = 0x00000024 + fcntlf_ofd_setlk = 0x00000025 + fcntlf_ofd_setlkw = 0x00000026 + fcntlf_owner_pgrp = 0x00000002 + fcntlf_owner_pid = 0x00000001 + fcntlf_owner_tid = 0x00000000 + fcntlf_rdlck = 0x00000000 + fcntlf_setfd = 0x00000002 + fcntlf_setfl = 0x00000004 + fcntlf_setlk = 0x00000006 + fcntlf_setlk64 = 0x0000000d + fcntlf_setlkw = 0x00000007 + fcntlf_setlkw64 = 0x0000000e + fcntlf_setown = 0x00000008 + fcntlf_setown_ex = 0x0000000f + fcntlf_setsig = 0x0000000a + fcntlf_shlck = 0x00000008 + fcntlf_unlck = 0x00000002 + fcntlf_wrlck = 0x00000001 + fcntllock_ex = 0x00000002 + fcntllock_mand = 0x00000020 + fcntllock_nb = 0x00000004 + fcntllock_read = 0x00000040 + fcntllock_rw = 0x000000c0 + fcntllock_sh = 0x00000001 + fcntllock_un = 0x00000008 + fcntllock_write = 0x00000080 + fcntlo_accmode = 0x00000003 + fcntlo_append = 0x00000400 + fcntlo_cloexec = 0x00080000 + fcntlo_creat = 0x00000040 + fcntlo_direct = 0x00004000 + fcntlo_directory = 0x00010000 + fcntlo_dsync = 0x00001000 + fcntlo_excl = 0x00000080 + fcntlo_largefile = 0x00008000 + fcntlo_ndelay = 0x00000800 + fcntlo_noatime = 0x00040000 + fcntlo_noctty = 0x00000100 + fcntlo_nofollow = 0x00020000 + fcntlo_nonblock = 0x00000800 + fcntlo_path = 0x00200000 + fcntlo_rdonly = 0x00000000 + fcntlo_rdwr = 0x00000002 + fcntlo_trunc = 0x00000200 + fcntlo_wronly = 0x00000001 +) + +pub enum Errno { + enoerror = 0x00000000 + e2big = 0x00000007 + eacces = 0x0000000d + eagain = 0x0000000b + ebadf = 0x00000009 + ebusy = 0x00000010 + echild = 0x0000000a + edom = 0x00000021 + eexist = 0x00000011 + efault = 0x0000000e + efbig = 0x0000001b + eintr = 0x00000004 + einval = 0x00000016 + eio = 0x00000005 + eisdir = 0x00000015 + emfile = 0x00000018 + emlink = 0x0000001f + enfile = 0x00000017 + enodev = 0x00000013 + enoent = 0x00000002 + enoexec = 0x00000008 + enomem = 0x0000000c + enospc = 0x0000001c + enotblk = 0x0000000f + enotdir = 0x00000014 + enotty = 0x00000019 + enxio = 0x00000006 + eperm = 0x00000001 + epipe = 0x00000020 + erange = 0x00000022 + erofs = 0x0000001e + espipe = 0x0000001d + esrch = 0x00000003 + etxtbsy = 0x0000001a + exdev = 0x00000012 +} + +pub enum Mm_prot { + prot_read = 0x1 + prot_write = 0x2 + prot_exec = 0x4 + prot_none = 0x0 + prot_growsdown = 0x01000000 + prot_growsup = 0x02000000 +} + +pub enum Map_flags { + map_shared = 0x01 + map_private = 0x02 + map_shared_validate = 0x03 + map_type = 0x0f + map_fixed = 0x10 + map_file = 0x00 + map_anonymous = 0x20 + map_huge_shift = 26 + map_huge_mask = 0x3f +} + +fn sys_call0(scn u64) u64 { + res := u64(0) + asm amd64 { + syscall + ; =a (res) + ; a (scn) + } + return res +} + +fn sys_call1(scn u64, arg1 u64) u64 { + res := u64(0) + asm amd64 { + syscall + ; =a (res) + ; a (scn) + D (arg1) + } + return res +} + +fn sys_call2(scn u64, arg1 u64, arg2 u64) u64 { + res := u64(0) + asm amd64 { + syscall + ; =a (res) + ; a (scn) + D (arg1) + S (arg2) + } + return res +} + +fn sys_call3(scn u64, arg1 u64, arg2 u64, arg3 u64) u64 { + res := u64(0) + asm amd64 { + syscall + ; =a (res) + ; a (scn) + D (arg1) + S (arg2) + d (arg3) + } + return res +} + +fn sys_call4(scn u64, arg1 u64, arg2 u64, arg3 u64, arg4 u64) u64 { + res := u64(0) + asm amd64 { + mov r10, arg4 + syscall + ; =a (res) + ; a (scn) + D (arg1) + S (arg2) + d (arg3) + r (arg4) + ; r10 + } + return res +} + +fn sys_call5(scn u64, arg1 u64, arg2 u64, arg3 u64, arg4 u64, arg5 u64) u64 { + res := u64(0) + asm amd64 { + mov r10, arg4 + mov r8, arg5 + syscall + ; =a (res) + ; a (scn) + D (arg1) + S (arg2) + d (arg3) + r (arg4) + r (arg5) + ; r10 + r8 + } + return res +} + +fn sys_call6(scn u64, arg1 u64, arg2 u64, arg3 u64, arg4 u64, arg5 u64, arg6 u64) u64 { + res := u64(0) + asm amd64 { + mov r10, arg4 + mov r8, arg5 + mov r9, arg6 + syscall + ; =a (res) + ; a (scn) + D (arg1) + S (arg2) + d (arg3) + r (arg4) + r (arg5) + r (arg6) + ; r10 + r8 + r9 + } + return res +} + +fn split_int_errno(rc_in u64) (i64, Errno) { + rc := i64(rc_in) + if rc < 0 { + return i64(-1), Errno(-rc) + } + return rc, Errno.enoerror +} + +// 0 sys_read unsigned int fd char *buf size_t count +pub fn sys_read(fd i64, buf &byte, count u64) (i64, Errno) { + return split_int_errno(sys_call3(0, u64(fd), u64(buf), count)) +} + +// 1 sys_write unsigned int fd, const char *buf, size_t count +pub fn sys_write(fd i64, buf &byte, count u64) (i64, Errno) { + return split_int_errno(sys_call3(1, u64(fd), u64(buf), count)) +} + +pub fn sys_open(filename &byte, flags i64, mode int) (i64, Errno) { + // 2 sys_open const char *filename int flags int mode + return split_int_errno(sys_call3(2, u64(filename), u64(flags), u64(mode))) +} + +pub fn sys_close(fd i64) Errno { + // 3 sys_close unsigned int fd + return Errno(-i64(sys_call1(3, u64(fd)))) +} + +// 9 sys_mmap unsigned long addr unsigned long len unsigned long prot unsigned long flags unsigned long fd unsigned long off +pub fn sys_mmap(addr &byte, len u64, prot Mm_prot, flags Map_flags, fildes u64, off u64) (&byte, Errno) { + rc := sys_call6(9, u64(addr), len, u64(prot), u64(flags), fildes, off) + a, e := split_int_errno(rc) + return &byte(a), e +} + +pub fn sys_munmap(addr voidptr, len u64) Errno { + // 11 sys_munmap unsigned long addr size_t len + return Errno(-sys_call2(11, u64(addr), len)) +} + +// 22 sys_pipe int *filedes +pub fn sys_pipe(filedes &int) Errno { + return Errno(sys_call1(22, u64(filedes))) +} + +// 24 sys_sched_yield +pub fn sys_sched_yield() Errno { + return Errno(sys_call0(24)) +} + +pub fn sys_madvise(addr voidptr, len u64, advice int) Errno { + // 28 sys_madvise unsigned long start size_t len_in int behavior + return Errno(sys_call3(28, u64(addr), len, u64(advice))) +} + +// 39 sys_getpid +pub fn sys_getpid() int { + return int(sys_call0(39)) +} + +// 57 sys_fork +pub fn sys_fork() int { + return int(sys_call0(57)) +} + +// 58 sys_vfork +pub fn sys_vfork() int { + return int(sys_call0(58)) +} + +// 33 sys_dup2 unsigned int oldfd unsigned int newfd +pub fn sys_dup2(oldfd int, newfd int) (i64, Errno) { + return split_int_errno(sys_call2(33, u64(oldfd), u64(newfd))) +} + +// 59 sys_execve const char *filename const char *const argv[] const char *const envp[] +// pub fn sys_execve(filename byteptr, argv []byteptr, envp []byteptr) int { +// return sys_call3(59, filename, argv, envp) +//} + +// 60 sys_exit int error_code +pub fn sys_exit(ec int) { + sys_call1(60, u64(ec)) +} + +// 102 sys_getuid +pub fn sys_getuid() int { + return int(sys_call0(102)) +} + +// 247 sys_waitid int which pid_t upid struct siginfo *infop int options struct rusage *ru +pub fn sys_waitid(which Wi_which, pid int, infop &int, options int, ru voidptr) Errno { + return Errno(sys_call5(247, u64(which), u64(pid), u64(infop), u64(options), u64(ru))) +} + +/* +A few years old, but still relevant +https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/ + +>0 sys_read unsigned int fd char *buf size_t count +>1 sys_write unsigned int fd const char *buf size_t count +>2 sys_open const char *filename int flags int mode +>3 sys_close unsigned int fd +4 sys_stat const char *filename struct stat *statbuf +5 sys_fstat unsigned int fd struct stat *statbuf +6 sys_lstat fconst char *filename struct stat *statbuf +7 sys_poll struct poll_fd *ufds unsigned int nfds long timeout_msecs +8 sys_lseek unsigned int fd off_t offset unsigned int origin +>9 sys_mmap unsigned long addr unsigned long len unsigned long prot unsigned long flags unsigned long fd unsigned long off +10 sys_mprotect unsigned long start size_t len unsigned long prot +>11 sys_munmap unsigned long addr size_t len +12 sys_brk unsigned long brk +13 sys_rt_sigaction int sig const struct sigaction *act struct sigaction *oact size_t sigsetsize +14 sys_rt_sigprocmask int how sigset_t *nset sigset_t *oset size_t sigsetsize +15 sys_rt_sigreturn unsigned long __unused +16 sys_ioctl unsigned int fd unsigned int cmd unsigned long arg +17 sys_pread64 unsigned long fd char *buf size_t count loff_t pos +18 sys_pwrite64 unsigned int fd const char *buf size_t count loff_t pos +19 sys_readv unsigned long fd const struct iovec *vec unsigned long vlen +20 sys_writev unsigned long fd const struct iovec *vec unsigned long vlen +21 sys_access const char *filename int mode +>22 sys_pipe int *filedes +23 sys_select int n fd_set *inp fd_set *outp fd_set*exp struct timeval *tvp +>24 sys_sched_yield +25 sys_mremap unsigned long addr unsigned long old_len unsigned long new_len unsigned long flags unsigned long new_addr +26 sys_msync unsigned long start size_t len int flags +27 sys_mincore unsigned long start size_t len unsigned char *vec +>28 sys_madvise unsigned long start size_t len_in int behavior +29 sys_shmget key_t key size_t size int shmflg +30 sys_shmat int shmid char *shmaddr int shmflg +31 sys_shmctl int shmid int cmd struct shmid_ds *buf +32 sys_dup unsigned int fildes +33 sys_dup2 unsigned int oldfd unsigned int newfd +34 sys_pause +35 sys_nanosleep struct timespec *rqtp struct timespec *rmtp +36 sys_getitimer int which struct itimerval *value +37 sys_alarm unsigned int seconds +38 sys_setitimer int which struct itimerval *value struct itimerval *ovalue +>39 sys_getpid +40 sys_sendfile int out_fd int in_fd off_t *offset size_t count +41 sys_socket int family int type int protocol +42 sys_connect int fd struct sockaddr *uservaddr int addrlen +43 sys_accept int fd struct sockaddr *upeer_sockaddr int *upeer_addrlen +44 sys_sendto int fd void *buff size_t len unsigned flags struct sockaddr *addr int addr_len +45 sys_recvfrom int fd void *ubuf size_t size unsigned flags struct sockaddr *addr int *addr_len +46 sys_sendmsg int fd struct msghdr *msg unsigned flags +47 sys_recvmsg int fd struct msghdr *msg unsigned int flags +48 sys_shutdown int fd int how +49 sys_bind int fd struct sokaddr *umyaddr int addrlen +50 sys_listen int fd int backlog +51 sys_getsockname int fd struct sockaddr *usockaddr int *usockaddr_len +52 sys_getpeername int fd struct sockaddr *usockaddr int *usockaddr_len +53 sys_socketpair int family int type int protocol int *usockvec +54 sys_setsockopt int fd int level int optname char *optval int optlen +55 sys_getsockopt int fd int level int optname char *optval int *optlen +56 sys_clone unsigned long clone_flags unsigned long newsp void *parent_tid void *child_tid +>57 sys_fork +>58 sys_vfork +>59 sys_execve const char *filename const char *const argv[] const char *const envp[] +>60 sys_exit int error_code +61 sys_wait4 pid_t upid int *stat_addr int options struct rusage *ru +62 sys_kill pid_t pid int sig +63 sys_uname struct old_utsname *name +64 sys_semget key_t key int nsems int semflg +65 sys_semop int semid struct sembuf *tsops unsigned nsops +66 sys_semctl int semid int semnum int cmd union semun arg +67 sys_shmdt char *shmaddr +68 sys_msgget key_t key int msgflg +69 sys_msgsnd int msqid struct msgbuf *msgp size_t msgsz int msgflg +70 sys_msgrcv int msqid struct msgbuf *msgp size_t msgsz long msgtyp int msgflg +71 sys_msgctl int msqid int cmd struct msqid_ds *buf +72 sys_fcntl unsigned int fd unsigned int cmd unsigned long arg +73 sys_flock unsigned int fd unsigned int cmd +74 sys_fsync unsigned int fd +75 sys_fdatasync unsigned int fd +76 sys_truncate const char *path long length +77 sys_ftruncate unsigned int fd unsigned long length +78 sys_getdents unsigned int fd struct linux_dirent *dirent unsigned int count +79 sys_getcwd char *buf unsigned long size +80 sys_chdir const char *filename +81 sys_fchdir unsigned int fd +82 sys_rename const char *oldname const char *newname +83 sys_mkdir const char *pathname int mode +84 sys_rmdir const char *pathname +85 sys_creat const char *pathname int mode +86 sys_link const char *oldname const char *newname +87 sys_unlink const char *pathname +88 sys_symlink const char *oldname const char *newname +89 sys_readlink const char *path char *buf int bufsiz +90 sys_chmod const char *filename mode_t mode +91 sys_fchmod unsigned int fd mode_t mode +92 sys_chown const char *filename uid_t user gid_t group +93 sys_fchown unsigned int fd uid_t user gid_t group +94 sys_lchown const char *filename uid_t user gid_t group +95 sys_umask int mask +96 sys_gettimeofday struct timeval *tv struct timezone *tz +97 sys_getrlimit unsigned int resource struct rlimit *rlim +98 sys_getrusage int who struct rusage *ru +99 sys_sysinfo struct sysinfo *info +100 sys_times struct sysinfo *info +101 sys_ptrace long request long pid unsigned long addr unsigned long data +>102 sys_getuid +103 sys_syslog int type char *buf int len +104 sys_getgid +105 sys_setuid uid_t uid +106 sys_setgid gid_t gid +107 sys_geteuid +108 sys_getegid +109 sys_setpgid pid_t pid pid_t pgid +110 sys_getppid +111 sys_getpgrp +112 sys_setsid +113 sys_setreuid uid_t ruid uid_t euid +114 sys_setregid gid_t rgid gid_t egid +115 sys_getgroups int gidsetsize gid_t *grouplist +116 sys_setgroups int gidsetsize gid_t *grouplist +117 sys_setresuid uid_t *ruid uid_t *euid uid_t *suid +118 sys_getresuid uid_t *ruid uid_t *euid uid_t *suid +119 sys_setresgid gid_t rgid gid_t egid gid_t sgid +120 sys_getresgid gid_t *rgid gid_t *egid gid_t *sgid +121 sys_getpgid pid_t pid +122 sys_setfsuid uid_t uid +123 sys_setfsgid gid_t gid +124 sys_getsid pid_t pid +125 sys_capget cap_user_header_t header cap_user_data_t dataptr +126 sys_capset cap_user_header_t header const cap_user_data_t data +127 sys_rt_sigpending sigset_t *set size_t sigsetsize +128 sys_rt_sigtimedwait const sigset_t *uthese siginfo_t *uinfo const struct timespec *uts size_t sigsetsize +129 sys_rt_sigqueueinfo pid_t pid int sig siginfo_t *uinfo +130 sys_rt_sigsuspend sigset_t *unewset size_t sigsetsize +131 sys_sigaltstack const stack_t *uss stack_t *uoss +132 sys_utime char *filename struct utimbuf *times +133 sys_mknod const char *filename umode_t mode unsigned dev +134 sys_uselib NOT IMPLEMENTED +135 sys_personality unsigned int personality +136 sys_ustat unsigned dev struct ustat *ubuf +137 sys_statfs const char *pathname struct statfs *buf +138 sys_fstatfs unsigned int fd struct statfs *buf +139 sys_sysfs int option unsigned long arg1 unsigned long arg2 +140 sys_getpriority int which int who +141 sys_setpriority int which int who int niceval +142 sys_sched_setparam pid_t pid struct sched_param *param +143 sys_sched_getparam pid_t pid struct sched_param *param +144 sys_sched_setscheduler pid_t pid int policy struct sched_param *param +145 sys_sched_getscheduler pid_t pid +146 sys_sched_get_priority_max int policy +147 sys_sched_get_priority_min int policy +148 sys_sched_rr_get_interval pid_t pid struct timespec *interval +149 sys_mlock unsigned long start size_t len +150 sys_munlock unsigned long start size_t len +151 sys_mlockall int flags +152 sys_munlockall +153 sys_vhangup +154 sys_modify_ldt int func void *ptr unsigned long bytecount +155 sys_pivot_root const char *new_root const char *put_old +156 sys__sysctl struct __sysctl_args *args +157 sys_prctl int option unsigned long arg2 unsigned long arg3 unsigned long arg4 unsigned long arg5 +158 sys_arch_prctl struct task_struct *task int code unsigned long *addr +159 sys_adjtimex struct timex *txc_p +160 sys_setrlimit unsigned int resource struct rlimit *rlim +161 sys_chroot const char *filename +162 sys_sync +163 sys_acct const char *name +164 sys_settimeofday struct timeval *tv struct timezone *tz +165 sys_mount char *dev_name char *dir_name char *type unsigned long flags void *data +166 sys_umount2 const char *target int flags +167 sys_swapon const char *specialfile int swap_flags +168 sys_swapoff const char *specialfile +169 sys_reboot int magic1 int magic2 unsigned int cmd void *arg +170 sys_sethostname char *name int len +171 sys_setdomainname char *name int len +172 sys_iopl unsigned int level struct pt_regs *regs +173 sys_ioperm unsigned long from unsigned long num int turn_on +174 sys_create_module REMOVED IN Linux 2.6 +175 sys_init_module void *umod unsigned long len const char *uargs +176 sys_delete_module const chat *name_user unsigned int flags +177 sys_get_kernel_syms REMOVED IN Linux 2.6 +178 sys_query_module REMOVED IN Linux 2.6 +179 sys_quotactl unsigned int cmd const char *special qid_t id void *addr +180 sys_nfsservctl NOT IMPLEMENTED +181 sys_getpmsg NOT IMPLEMENTED +182 sys_putpmsg NOT IMPLEMENTED +183 sys_afs_syscall NOT IMPLEMENTED +184 sys_tuxcall NOT IMPLEMENTED +185 sys_security NOT IMPLEMENTED +186 sys_gettid +187 sys_readahead int fd loff_t offset size_t count +188 sys_setxattr const char *pathname const char *name const void *value size_t size int flags +189 sys_lsetxattr const char *pathname const char *name const void *value size_t size int flags +190 sys_fsetxattr int fd const char *name const void *value size_t size int flags +191 sys_getxattr const char *pathname const char *name void *value size_t size +192 sys_lgetxattr const char *pathname const char *name void *value size_t size +193 sys_fgetxattr int fd const har *name void *value size_t size +194 sys_listxattr const char *pathname char *list size_t size +195 sys_llistxattr const char *pathname char *list size_t size +196 sys_flistxattr int fd char *list size_t size +197 sys_removexattr const char *pathname const char *name +198 sys_lremovexattr const char *pathname const char *name +199 sys_fremovexattr int fd const char *name +200 sys_tkill pid_t pid ing sig +201 sys_time time_t *tloc +202 sys_futex u32 *uaddr int op u32 val struct timespec *utime u32 *uaddr2 u32 val3 +203 sys_sched_setaffinity pid_t pid unsigned int len unsigned long *user_mask_ptr +204 sys_sched_getaffinity pid_t pid unsigned int len unsigned long *user_mask_ptr +205 sys_set_thread_area NOT IMPLEMENTED. Use arch_prctl +206 sys_io_setup unsigned nr_events aio_context_t *ctxp +207 sys_io_destroy aio_context_t ctx +208 sys_io_getevents aio_context_t ctx_id long min_nr long nr struct io_event *events +209 sys_io_submit aio_context_t ctx_id long nr struct iocb **iocbpp +210 sys_io_cancel aio_context_t ctx_id struct iocb *iocb struct io_event *result +211 sys_get_thread_area NOT IMPLEMENTED. Use arch_prctl +212 sys_lookup_dcookie u64 cookie64 long buf long len +213 sys_epoll_create int size +214 sys_epoll_ctl_old NOT IMPLEMENTED +215 sys_epoll_wait_old NOT IMPLEMENTED +216 sys_remap_file_pages unsigned long start unsigned long size unsigned long prot unsigned long pgoff unsigned long flags +217 sys_getdents64 unsigned int fd struct linux_dirent64 *dirent unsigned int count +218 sys_set_tid_address int *tidptr +219 sys_restart_syscall +220 sys_semtimedop int semid struct sembuf *tsops unsigned nsops const struct timespec *timeout +221 sys_fadvise64 int fd loff_t offset size_t len int advice +222 sys_timer_create const clockid_t which_clock struct sigevent *timer_event_spec timer_t *created_timer_id +223 sys_timer_settime timer_t timer_id int flags const struct itimerspec *new_setting struct itimerspec *old_setting +224 sys_timer_gettime timer_t timer_id struct itimerspec *setting +225 sys_timer_getoverrun timer_t timer_id +226 sys_timer_delete timer_t timer_id +227 sys_clock_settime const clockid_t which_clock const struct timespec *tp +228 sys_clock_gettime const clockid_t which_clock struct timespec *tp +229 sys_clock_getres const clockid_t which_clock struct timespec *tp +230 sys_clock_nanosleep const clockid_t which_clock int flags const struct timespec *rqtp struct timespec *rmtp +231 sys_exit_group int error_code +232 sys_epoll_wait int epfd struct epoll_event *events int maxevents int timeout +233 sys_epoll_ctl int epfd int op int fd struct epoll_event *event +234 sys_tgkill pid_t tgid pid_t pid int sig +235 sys_utimes char *filename struct timeval *utimes +236 sys_vserver NOT IMPLEMENTED +237 sys_mbind unsigned long start unsigned long len unsigned long mode unsigned long *nmask unsigned long maxnode unsigned flags +238 sys_set_mempolicy int mode unsigned long *nmask unsigned long maxnode +239 sys_get_mempolicy int *policy unsigned long *nmask unsigned long maxnode unsigned long addr unsigned long flags +240 sys_mq_open const char *u_name int oflag mode_t mode struct mq_attr *u_attr +241 sys_mq_unlink const char *u_name +242 sys_mq_timedsend mqd_t mqdes const char *u_msg_ptr size_t msg_len unsigned int msg_prio const stuct timespec *u_abs_timeout +243 sys_mq_timedreceive mqd_t mqdes char *u_msg_ptr size_t msg_len unsigned int *u_msg_prio const struct timespec *u_abs_timeout +244 sys_mq_notify mqd_t mqdes const struct sigevent *u_notification +245 sys_mq_getsetattr mqd_t mqdes const struct mq_attr *u_mqstat struct mq_attr *u_omqstat +246 sys_kexec_load unsigned long entry unsigned long nr_segments struct kexec_segment *segments unsigned long flags +>247 sys_waitid int which pid_t upid struct siginfo *infop int options struct rusage *ru +248 sys_add_key const char *_type const char *_description const void *_payload size_t plen +249 sys_request_key const char *_type const char *_description const char *_callout_info key_serial_t destringid +250 sys_keyctl int option unsigned long arg2 unsigned long arg3 unsigned long arg4 unsigned long arg5 +251 sys_ioprio_set int which int who int ioprio +252 sys_ioprio_get int which int who +253 sys_inotify_init +254 sys_inotify_add_watch int fd const char *pathname u32 mask +255 sys_inotify_rm_watch int fd __s32 wd +256 sys_migrate_pages pid_t pid unsigned long maxnode const unsigned long *old_nodes const unsigned long *new_nodes +257 sys_openat int dfd const char *filename int flags int mode +258 sys_mkdirat int dfd const char *pathname int mode +259 sys_mknodat int dfd const char *filename int mode unsigned dev +260 sys_fchownat int dfd const char *filename uid_t user gid_t group int flag +261 sys_futimesat int dfd const char *filename struct timeval *utimes +262 sys_newfstatat int dfd const char *filename struct stat *statbuf int flag +263 sys_unlinkat int dfd const char *pathname int flag +264 sys_renameat int oldfd const char *oldname int newfd const char *newname +265 sys_linkat int oldfd const char *oldname int newfd const char *newname int flags +266 sys_symlinkat const char *oldname int newfd const char *newname +267 sys_readlinkat int dfd const char *pathname char *buf int bufsiz +268 sys_fchmodat int dfd const char *filename mode_t mode +269 sys_faccessat int dfd const char *filename int mode +270 sys_pselect6 int n fd_set *inp fd_set *outp fd_set *exp struct timespec *tsp void *sig +271 sys_ppoll struct pollfd *ufds unsigned int nfds struct timespec *tsp const sigset_t *sigmask size_t sigsetsize +272 sys_unshare unsigned long unshare_flags +273 sys_set_robust_list struct robust_list_head *head size_t len +274 sys_get_robust_list int pid struct robust_list_head **head_ptr size_t *len_ptr +275 sys_splice int fd_in loff_t *off_in int fd_out loff_t *off_out size_t len unsigned int flags +276 sys_tee int fdin int fdout size_t len unsigned int flags +277 sys_sync_file_range long fd loff_t offset loff_t bytes long flags +278 sys_vmsplice int fd const struct iovec *iov unsigned long nr_segs unsigned int flags +279 sys_move_pages pid_t pid unsigned long nr_pages const void **pages const int *nodes int *status int flags +280 sys_utimensat int dfd const char *filename struct timespec *utimes int flags +281 sys_epoll_pwait int epfd struct epoll_event *events int maxevents int timeout const sigset_t *sigmask size_t sigsetsize +282 sys_signalfd int ufd sigset_t *user_mask size_t sizemask +283 sys_timerfd_create int clockid int flags +284 sys_eventfd unsigned int count +285 sys_fallocate long fd long mode loff_t offset loff_t len +286 sys_timerfd_settime int ufd int flags const struct itimerspec *utmr struct itimerspec *otmr +287 sys_timerfd_gettime int ufd struct itimerspec *otmr +288 sys_accept4 int fd struct sockaddr *upeer_sockaddr int *upeer_addrlen int flags +289 sys_signalfd4 int ufd sigset_t *user_mask size_t sizemask int flags +290 sys_eventfd2 unsigned int count int flags +291 sys_epoll_create1 int flags +292 sys_dup3 unsigned int oldfd unsigned int newfd int flags +293 sys_pipe2 int *filedes int flags +294 sys_inotify_init1 int flags +295 sys_preadv unsigned long fd const struct iovec *vec unsigned long vlen unsigned long pos_l unsigned long pos_h +296 sys_pwritev unsigned long fd const struct iovec *vec unsigned long vlen unsigned long pos_l unsigned long pos_h +297 sys_rt_tgsigqueueinfo pid_t tgid pid_t pid int sig siginfo_t *uinfo +298 sys_perf_event_open struct perf_event_attr *attr_uptr pid_t pid int cpu int group_fd unsigned long flags +299 sys_recvmmsg int fd struct msghdr *mmsg unsigned int vlen unsigned int flags struct timespec *timeout +300 sys_fanotify_init unsigned int flags unsigned int event_f_flags +301 sys_fanotify_mark long fanotify_fd long flags __u64 mask long dfd long pathname +302 sys_prlimit64 pid_t pid unsigned int resource const struct rlimit64 *new_rlim struct rlimit64 *old_rlim +303 sys_name_to_handle_at int dfd const char *name struct file_handle *handle int *mnt_id int flag +304 sys_open_by_handle_at int dfd const char *name struct file_handle *handle int *mnt_id int flags +305 sys_clock_adjtime clockid_t which_clock struct timex *tx +306 sys_syncfs int fd +307 sys_sendmmsg int fd struct mmsghdr *mmsg unsigned int vlen unsigned int flags +308 sys_setns int fd int nstype +309 sys_getcpu unsigned *cpup unsigned *nodep struct getcpu_cache *unused +310 sys_process_vm_readv pid_t pid const struct iovec *lvec unsigned long liovcnt const struct iovec *rvec unsigned long riovcnt unsigned long flags +311 sys_process_vm_writev pid_t pid const struct iovec *lvec unsigned long liovcnt const struct iovcc *rvec unsigned long riovcnt unsigned long flags +312 sys_kcmp pid_t pid1 pid_t pid2 int type unsigned long idx1 unsigned long idx2 +313 sys_finit_module int fd const char __user *uargs int flags +314 sys_sched_setattr pid_t pid struct sched_attr __user *attr unsigned int flags +315 sys_sched_getattr pid_t pid struct sched_attr __user *attr unsigned int size unsigned int flags +316 sys_renameat2 int olddfd const char __user *oldname int newdfd const char __user *newname unsigned int flags +317 sys_seccomp unsigned int op unsigned int flags const char __user *uargs +318 sys_getrandom char __user *buf size_t count unsigned int flags +319 sys_memfd_create const char __user *uname_ptr unsigned int flags +320 sys_kexec_file_load int kernel_fd int initrd_fd unsigned long cmdline_len const char __user *cmdline_ptr unsigned long flags +321 sys_bpf int cmd union bpf_attr *attr unsigned int size +322 stub_execveat int dfd const char __user *filename const char __user *const __user *argv const char __user *const __user *envp int flags +323 userfaultfd int flags +324 membarrier int cmd int flags +325 mlock2 unsigned long start size_t len int flags +326 copy_file_range int fd_in loff_t __user *off_in int fd_out loff_t __user * off_out size_t len unsigned int flags +327 preadv2 unsigned long fd const struct iovec __user *vec unsigned long vlen unsigned long pos_l unsigned long pos_h int flags +328 pwritev2 unsigned long fd const struct iovec __user *vec unsigned long vlen unsigned long pos_l unsigned long pos_h int flags +*/ diff --git a/v_windows/v/vlib/builtin/linux_bare/old/mm_bare.v b/v_windows/v/vlib/builtin/linux_bare/old/mm_bare.v new file mode 100644 index 0000000..cee5f99 --- /dev/null +++ b/v_windows/v/vlib/builtin/linux_bare/old/mm_bare.v @@ -0,0 +1,58 @@ +module builtin + +const ( + mem_prot = Mm_prot(int(Mm_prot.prot_read) | int(Mm_prot.prot_write)) + mem_flags = Map_flags(int(Map_flags.map_private) | int(Map_flags.map_anonymous)) + page_size = u64(Linux_mem.page_size) +) + +pub fn mm_pages(size u64) u32 { + pages := (size + u64(4) + page_size) / page_size + return u32(pages) +} + +pub fn mm_alloc(size u64) (&byte, Errno) { + pages := mm_pages(size) + n_bytes := u64(pages * u32(Linux_mem.page_size)) + + a, e := sys_mmap(0, n_bytes, mem_prot, mem_flags, -1, 0) + if e == .enoerror { + mut ap := &int(a) + *ap = pages + return &byte(a + 4), e + } + return &byte(0), e +} + +pub fn mm_free(addr &byte) Errno { + ap := &int(addr - 4) + size := u64(*ap) * u64(Linux_mem.page_size) + + return sys_munmap(ap, size) +} + +pub fn mem_copy(dest0 voidptr, src0 voidptr, n int) voidptr { + mut dest := &byte(dest0) + src := &byte(src0) + for i in 0 .. n { + dest[i] = src[i] + } + return dest0 +} + +[unsafe] +pub fn malloc(n int) &byte { + if n < 0 { + panic('malloc(<0)') + } + + ptr, e := mm_alloc(u64(n)) + assert e == .enoerror + assert !isnil(ptr) + return ptr +} + +[unsafe] +pub fn free(ptr voidptr) { + assert mm_free(ptr) == .enoerror +} diff --git a/v_windows/v/vlib/builtin/linux_bare/old/string_bare.v b/v_windows/v/vlib/builtin/linux_bare/old/string_bare.v new file mode 100644 index 0000000..8f7edfc --- /dev/null +++ b/v_windows/v/vlib/builtin/linux_bare/old/string_bare.v @@ -0,0 +1,150 @@ +module builtin + +pub struct string { +pub: + str &byte + len int +} + +pub fn strlen(s &byte) int { + mut i := 0 + for ; s[i] != 0; i++ {} + return i +} + +pub fn tos(s &byte, len int) string { + if s == 0 { + panic('tos(): nil string') + } + return string{ + str: s + len: len + } +} + +fn (s string) add(a string) string { + new_len := a.len + s.len + mut res := string{ + len: new_len + str: malloc(new_len + 1) + } + for j in 0 .. s.len { + res[j] = s[j] + } + for j in 0 .. a.len { + res[s.len + j] = a[j] + } + res[new_len] = 0 // V strings are not null terminated, but just in case + return res +} + +/* +pub fn tos_clone(s byteptr) string { + if s == 0 { + panic('tos: nil string') + } + return tos2(s).clone() +} +*/ + +// Same as `tos`, but calculates the length. Called by `string(bytes)` casts. +// Used only internally. +pub fn tos2(s &byte) string { + if s == 0 { + panic('tos2: nil string') + } + return string{ + str: s + len: strlen(s) + } +} + +pub fn tos3(s &char) string { + if s == 0 { + panic('tos3: nil string') + } + return string{ + str: &byte(s) + len: strlen(&byte(s)) + } +} + +pub fn string_eq(s1 string, s2 string) bool { + if s1.len != s2.len { + return false + } + for i in 0 .. s1.len { + if s1[i] != s2[i] { + return false + } + } + return true +} + +pub fn string_ne(s1 string, s2 string) bool { + return !string_eq(s1, s2) +} + +pub fn i64_tos(buf &byte, len int, n0 i64, base int) string { + if base < 2 { + panic('base must be >= 2') + } + if base > 36 { + panic('base must be <= 36') + } + + mut b := tos(buf, len) + mut i := len - 1 + + mut n := n0 + neg := n < 0 + if neg { + n = -n + } + + b[i--] = 0 + + for { + c := (n % base) + 48 + b[i--] = if c > 57 { c + 7 } else { c } + if i < 0 { + panic('buffer to small') + } + n /= base + if n < 1 { + break + } + } + if neg { + if i < 0 { + panic('buffer to small') + } + b[i--] = 45 + } + offset := i + 1 + b.str = b.str + offset + b.len -= (offset + 1) + return b +} + +pub fn i64_str(n0 i64, base int) string { + buf := malloc(80) + return i64_tos(buf, 79, n0, base) +} + +pub fn ptr_str(ptr voidptr) string { + buf := [16]byte{} + hex := i64_tos(buf, 15, i64(ptr), 16) + res := '0x' + hex + return res +} + +pub fn (a string) clone() string { + mut b := string{ + len: a.len + str: malloc(a.len + 1) + } + mem_copy(b.str, a.str, a.len) + b[a.len] = 0 + return b +} diff --git a/v_windows/v/vlib/builtin/linux_bare/old/syscallwrapper_test.v b/v_windows/v/vlib/builtin/linux_bare/old/syscallwrapper_test.v new file mode 100644 index 0000000..1bd79c9 --- /dev/null +++ b/v_windows/v/vlib/builtin/linux_bare/old/syscallwrapper_test.v @@ -0,0 +1,27 @@ +import os + +fn test_syscallwrappers() { + if true { + return + } + $if linux { + $if x64 { + exe := os.executable() + vdir := os.dir(exe) + if vdir.len > 1 { + dot_checks := vdir + '/.checks' + assert os.is_dir(dot_checks) + + os.chdir(dot_checks) or {} + checks_v := 'checks.v' + assert os.exists(checks_v) + rc := os.execute_or_exit('v run $checks_v') + assert rc.exit_code == 0 + assert !rc.output.contains('V panic: An assertion failed.') + assert !rc.output.contains('failed') + } else { + panic("Can't find test directory") + } + } + } +} diff --git a/v_windows/v/vlib/builtin/map.c.v b/v_windows/v/vlib/builtin/map.c.v new file mode 100644 index 0000000..c0fc60c --- /dev/null +++ b/v_windows/v/vlib/builtin/map.c.v @@ -0,0 +1,79 @@ +module builtin + +fn C.wyhash(&byte, u64, u64, &u64) u64 + +fn C.wyhash64(u64, u64) u64 + +// fast_string_eq is intended to be fast when +// the strings are very likely to be equal +// TODO: add branch prediction hints +[inline] +fn fast_string_eq(a string, b string) bool { + if a.len != b.len { + return false + } + unsafe { + return C.memcmp(a.str, b.str, b.len) == 0 + } +} + +fn map_hash_string(pkey voidptr) u64 { + key := *unsafe { &string(pkey) } + return C.wyhash(key.str, u64(key.len), 0, &u64(C._wyp)) +} + +fn map_hash_int_1(pkey voidptr) u64 { + return C.wyhash64(*unsafe { &byte(pkey) }, 0) +} + +fn map_hash_int_2(pkey voidptr) u64 { + return C.wyhash64(*unsafe { &u16(pkey) }, 0) +} + +fn map_hash_int_4(pkey voidptr) u64 { + return C.wyhash64(*unsafe { &u32(pkey) }, 0) +} + +fn map_hash_int_8(pkey voidptr) u64 { + return C.wyhash64(*unsafe { &u64(pkey) }, 0) +} + +// Move all zeros to the end of the array and resize array +fn (mut d DenseArray) zeros_to_end() { + // TODO alloca? + mut tmp_value := unsafe { malloc(d.value_bytes) } + mut tmp_key := unsafe { malloc(d.key_bytes) } + mut count := 0 + for i in 0 .. d.len { + if d.has_index(i) { + // swap (TODO: optimize) + unsafe { + if count != i { + // Swap keys + C.memcpy(tmp_key, d.key(count), d.key_bytes) + C.memcpy(d.key(count), d.key(i), d.key_bytes) + C.memcpy(d.key(i), tmp_key, d.key_bytes) + // Swap values + C.memcpy(tmp_value, d.value(count), d.value_bytes) + C.memcpy(d.value(count), d.value(i), d.value_bytes) + C.memcpy(d.value(i), tmp_value, d.value_bytes) + } + } + count++ + } + } + unsafe { + free(tmp_value) + free(tmp_key) + d.deletes = 0 + // TODO: reallocate instead as more deletes are likely + free(d.all_deleted) + } + d.len = count + old_cap := d.cap + d.cap = if count < 8 { 8 } else { count } + unsafe { + d.values = realloc_data(d.values, d.value_bytes * old_cap, d.value_bytes * d.cap) + d.keys = realloc_data(d.keys, d.key_bytes * old_cap, d.key_bytes * d.cap) + } +} diff --git a/v_windows/v/vlib/builtin/map.v b/v_windows/v/vlib/builtin/map.v new file mode 100644 index 0000000..5e67d79 --- /dev/null +++ b/v_windows/v/vlib/builtin/map.v @@ -0,0 +1,716 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module builtin + +/* +This is a highly optimized hashmap implementation. It has several traits that +in combination makes it very fast and memory efficient. Here is a short expl- +anation of each trait. After reading this you should have a basic understand- +ing of how it functions: + +1. Hash-function: Wyhash. Wyhash is the fastest hash-function for short keys +passing SMHasher, so it was an obvious choice. + +2. Open addressing: Robin Hood Hashing. With this method, a hash-collision is +resolved by probing. As opposed to linear probing, Robin Hood hashing has a +simple but clever twist: As new keys are inserted, old keys are shifted arou- +nd in a way such that all keys stay reasonably close to the slot they origin- +ally hash to. A new key may displace a key already inserted if its probe cou- +nt is larger than that of the key at the current position. + +3. Memory layout: key-value pairs are stored in a `DenseArray`. This is a dy- +namic array with a very low volume of unused memory, at the cost of more rea- +llocations when inserting elements. It also preserves the order of the key-v- +alues. This array is named `key_values`. Instead of probing a new key-value, +this map probes two 32-bit numbers collectively. The first number has its 8 +most significant bits reserved for the probe-count and the remaining 24 bits +are cached bits from the hash which are utilized for faster re-hashing. This +number is often referred to as `meta`. The other 32-bit number is the index +at which the key-value was pushed to in `key_values`. Both of these numbers +are stored in a sparse array `metas`. The `meta`s and `kv_index`s are stored +at even and odd indices, respectively: + +metas = [meta, kv_index, 0, 0, meta, kv_index, 0, 0, meta, kv_index, ...] +key_values = [kv, kv, kv, ...] + +4. The size of metas is a power of two. This enables the use of bitwise AND +to convert the 64-bit hash to a bucket/index that doesn't overflow metas. If +the size is power of two you can use "hash & (SIZE - 1)" instead of "hash % +SIZE". Modulo is extremely expensive so using '&' is a big performance impro- +vement. The general concern with this approach is that you only make use of +the lower bits of the hash which can cause more collisions. This is solved by +using a well-dispersed hash-function. + +5. The hashmap keeps track of the highest probe_count. The trick is to alloc- +ate `extra_metas` > max(probe_count), so you never have to do any bounds-che- +cking since the extra meta memory ensures that a meta will never go beyond +the last index. + +6. Cached rehashing. When the `load_factor` of the map exceeds the `max_load_ +factor` the size of metas is doubled and all the key-values are "rehashed" to +find the index for their meta's in the new array. Instead of rehashing compl- +etely, it simply uses the cached-hashbits stored in the meta, resulting in +much faster rehashing. +*/ +const ( + // Number of bits from the hash stored for each entry + hashbits = 24 + // Number of bits from the hash stored for rehashing + max_cached_hashbits = 16 + // Initial log-number of buckets in the hashtable + init_log_capicity = 5 + // Initial number of buckets in the hashtable + init_capicity = 1 << init_log_capicity + // Maximum load-factor (len / capacity) + max_load_factor = 0.8 + // Initial highest even index in metas + init_even_index = init_capicity - 2 + // Used for incrementing `extra_metas` when max + // probe count is too high, to avoid overflow + extra_metas_inc = 4 + // Bitmask to select all the hashbits + hash_mask = u32(0x00FFFFFF) + // Used for incrementing the probe-count + probe_inc = u32(0x01000000) +) + +// DenseArray represents a dynamic array with very low growth factor +struct DenseArray { + key_bytes int + value_bytes int +mut: + cap int + len int + deletes u32 // count + // array allocated (with `cap` bytes) on first deletion + // has non-zero element when key deleted + all_deleted &byte + values &byte + keys &byte +} + +[inline] +fn new_dense_array(key_bytes int, value_bytes int) DenseArray { + cap := 8 + return DenseArray{ + key_bytes: key_bytes + value_bytes: value_bytes + cap: cap + len: 0 + deletes: 0 + all_deleted: 0 + keys: unsafe { malloc(cap * key_bytes) } + values: unsafe { malloc(cap * value_bytes) } + } +} + +[inline] +fn (d &DenseArray) key(i int) voidptr { + return unsafe { d.keys + i * d.key_bytes } +} + +// for cgen +[inline] +fn (d &DenseArray) value(i int) voidptr { + return unsafe { d.values + i * d.value_bytes } +} + +[inline] +fn (d &DenseArray) has_index(i int) bool { + return d.deletes == 0 || unsafe { d.all_deleted[i] } == 0 +} + +// Make space to append an element and return index +// The growth-factor is roughly 1.125 `(x + (x >> 3))` +[inline] +fn (mut d DenseArray) expand() int { + old_cap := d.cap + old_value_size := d.value_bytes * old_cap + old_key_size := d.key_bytes * old_cap + if d.cap == d.len { + d.cap += d.cap >> 3 + unsafe { + d.keys = realloc_data(d.keys, old_key_size, d.key_bytes * d.cap) + d.values = realloc_data(d.values, old_value_size, d.value_bytes * d.cap) + if d.deletes != 0 { + d.all_deleted = realloc_data(d.all_deleted, old_cap, d.cap) + vmemset(d.all_deleted + d.len, 0, d.cap - d.len) + } + } + } + push_index := d.len + unsafe { + if d.deletes != 0 { + d.all_deleted[push_index] = 0 + } + } + d.len++ + return push_index +} + +type MapHashFn = fn (voidptr) u64 + +type MapEqFn = fn (voidptr, voidptr) bool + +type MapCloneFn = fn (voidptr, voidptr) + +type MapFreeFn = fn (voidptr) + +// map is the internal representation of a V `map` type. +pub struct map { + // Number of bytes of a key + key_bytes int + // Number of bytes of a value + value_bytes int +mut: + // Highest even index in the hashtable + even_index u32 + // Number of cached hashbits left for rehashing + cached_hashbits byte + // Used for right-shifting out used hashbits + shift byte + // Array storing key-values (ordered) + key_values DenseArray + // Pointer to meta-data: + // - Odd indices store kv_index. + // - Even indices store probe_count and hashbits. + metas &u32 + // Extra metas that allows for no ranging when incrementing + // index in the hashmap + extra_metas u32 + has_string_keys bool + hash_fn MapHashFn + key_eq_fn MapEqFn + clone_fn MapCloneFn + free_fn MapFreeFn +pub mut: + // Number of key-values currently in the hashmap + len int +} + +fn map_eq_string(a voidptr, b voidptr) bool { + return fast_string_eq(*unsafe { &string(a) }, *unsafe { &string(b) }) +} + +fn map_eq_int_1(a voidptr, b voidptr) bool { + return unsafe { *&byte(a) == *&byte(b) } +} + +fn map_eq_int_2(a voidptr, b voidptr) bool { + return unsafe { *&u16(a) == *&u16(b) } +} + +fn map_eq_int_4(a voidptr, b voidptr) bool { + return unsafe { *&u32(a) == *&u32(b) } +} + +fn map_eq_int_8(a voidptr, b voidptr) bool { + return unsafe { *&u64(a) == *&u64(b) } +} + +fn map_clone_string(dest voidptr, pkey voidptr) { + unsafe { + s := *&string(pkey) + (*&string(dest)) = s.clone() + } +} + +fn map_clone_int_1(dest voidptr, pkey voidptr) { + unsafe { + *&byte(dest) = *&byte(pkey) + } +} + +fn map_clone_int_2(dest voidptr, pkey voidptr) { + unsafe { + *&u16(dest) = *&u16(pkey) + } +} + +fn map_clone_int_4(dest voidptr, pkey voidptr) { + unsafe { + *&u32(dest) = *&u32(pkey) + } +} + +fn map_clone_int_8(dest voidptr, pkey voidptr) { + unsafe { + *&u64(dest) = *&u64(pkey) + } +} + +fn map_free_string(pkey voidptr) { + unsafe { + (*&string(pkey)).free() + } +} + +fn map_free_nop(_ voidptr) { +} + +fn new_map(key_bytes int, value_bytes int, hash_fn MapHashFn, key_eq_fn MapEqFn, clone_fn MapCloneFn, free_fn MapFreeFn) map { + metasize := int(sizeof(u32) * (init_capicity + extra_metas_inc)) + // for now assume anything bigger than a pointer is a string + has_string_keys := key_bytes > sizeof(voidptr) + return map{ + key_bytes: key_bytes + value_bytes: value_bytes + even_index: init_even_index + cached_hashbits: max_cached_hashbits + shift: init_log_capicity + key_values: new_dense_array(key_bytes, value_bytes) + metas: unsafe { &u32(vcalloc_noscan(metasize)) } + extra_metas: extra_metas_inc + len: 0 + has_string_keys: has_string_keys + hash_fn: hash_fn + key_eq_fn: key_eq_fn + clone_fn: clone_fn + free_fn: free_fn + } +} + +fn new_map_init(hash_fn MapHashFn, key_eq_fn MapEqFn, clone_fn MapCloneFn, free_fn MapFreeFn, n int, key_bytes int, value_bytes int, keys voidptr, values voidptr) map { + mut out := new_map(key_bytes, value_bytes, hash_fn, key_eq_fn, clone_fn, free_fn) + // TODO pre-allocate n slots + mut pkey := &byte(keys) + mut pval := &byte(values) + for _ in 0 .. n { + unsafe { + out.set(pkey, pval) + pkey = pkey + key_bytes + pval = pval + value_bytes + } + } + return out +} + +pub fn (mut m map) move() map { + r := *m + unsafe { + vmemset(m, 0, int(sizeof(map))) + } + return r +} + +[inline] +fn (m &map) key_to_index(pkey voidptr) (u32, u32) { + hash := m.hash_fn(pkey) + index := hash & m.even_index + meta := ((hash >> m.shift) & hash_mask) | probe_inc + return u32(index), u32(meta) +} + +[inline] +fn (m &map) meta_less(_index u32, _metas u32) (u32, u32) { + mut index := _index + mut meta := _metas + for meta < unsafe { m.metas[index] } { + index += 2 + meta += probe_inc + } + return index, meta +} + +[inline] +fn (mut m map) meta_greater(_index u32, _metas u32, kvi u32) { + mut meta := _metas + mut index := _index + mut kv_index := kvi + for unsafe { m.metas[index] } != 0 { + if meta > unsafe { m.metas[index] } { + unsafe { + tmp_meta := m.metas[index] + m.metas[index] = meta + meta = tmp_meta + tmp_index := m.metas[index + 1] + m.metas[index + 1] = kv_index + kv_index = tmp_index + } + } + index += 2 + meta += probe_inc + } + unsafe { + m.metas[index] = meta + m.metas[index + 1] = kv_index + } + probe_count := (meta >> hashbits) - 1 + m.ensure_extra_metas(probe_count) +} + +[inline] +fn (mut m map) ensure_extra_metas(probe_count u32) { + if (probe_count << 1) == m.extra_metas { + size_of_u32 := sizeof(u32) + old_mem_size := (m.even_index + 2 + m.extra_metas) + m.extra_metas += extra_metas_inc + mem_size := (m.even_index + 2 + m.extra_metas) + unsafe { + x := realloc_data(&byte(m.metas), int(size_of_u32 * old_mem_size), int(size_of_u32 * mem_size)) + m.metas = &u32(x) + vmemset(m.metas + mem_size - extra_metas_inc, 0, int(sizeof(u32) * extra_metas_inc)) + } + // Should almost never happen + if probe_count == 252 { + panic('Probe overflow') + } + } +} + +// Insert new element to the map. The element is inserted if its key is +// not equivalent to the key of any other element already in the container. +// If the key already exists, its value is changed to the value of the new element. +fn (mut m map) set(key voidptr, value voidptr) { + load_factor := f32(m.len << 1) / f32(m.even_index) + if load_factor > max_load_factor { + m.expand() + } + mut index, mut meta := m.key_to_index(key) + index, meta = m.meta_less(index, meta) + // While we might have a match + for meta == unsafe { m.metas[index] } { + kv_index := int(unsafe { m.metas[index + 1] }) + pkey := unsafe { m.key_values.key(kv_index) } + if m.key_eq_fn(key, pkey) { + unsafe { + pval := m.key_values.value(kv_index) + vmemcpy(pval, value, m.value_bytes) + } + return + } + index += 2 + meta += probe_inc + } + kv_index := m.key_values.expand() + unsafe { + pkey := m.key_values.key(kv_index) + pvalue := m.key_values.value(kv_index) + m.clone_fn(pkey, key) + vmemcpy(&byte(pvalue), value, m.value_bytes) + } + m.meta_greater(index, meta, u32(kv_index)) + m.len++ +} + +// Doubles the size of the hashmap +fn (mut m map) expand() { + old_cap := m.even_index + m.even_index = ((m.even_index + 2) << 1) - 2 + // Check if any hashbits are left + if m.cached_hashbits == 0 { + m.shift += max_cached_hashbits + m.cached_hashbits = max_cached_hashbits + m.rehash() + } else { + m.cached_rehash(old_cap) + m.cached_hashbits-- + } +} + +// A rehash is the reconstruction of the hash table: +// All the elements in the container are rearranged according +// to their hash value into the newly sized key-value container. +// Rehashes are performed when the load_factor is going to surpass +// the max_load_factor in an operation. +fn (mut m map) rehash() { + meta_bytes := sizeof(u32) * (m.even_index + 2 + m.extra_metas) + unsafe { + // TODO: use realloc_data here too + x := v_realloc(&byte(m.metas), int(meta_bytes)) + m.metas = &u32(x) + vmemset(m.metas, 0, int(meta_bytes)) + } + for i := 0; i < m.key_values.len; i++ { + if !m.key_values.has_index(i) { + continue + } + pkey := unsafe { m.key_values.key(i) } + mut index, mut meta := m.key_to_index(pkey) + index, meta = m.meta_less(index, meta) + m.meta_greater(index, meta, u32(i)) + } +} + +// This method works like rehash. However, instead of rehashing the +// key completely, it uses the bits cached in `metas`. +fn (mut m map) cached_rehash(old_cap u32) { + old_metas := m.metas + metasize := int(sizeof(u32) * (m.even_index + 2 + m.extra_metas)) + m.metas = unsafe { &u32(vcalloc(metasize)) } + old_extra_metas := m.extra_metas + for i := u32(0); i <= old_cap + old_extra_metas; i += 2 { + if unsafe { old_metas[i] } == 0 { + continue + } + old_meta := unsafe { old_metas[i] } + old_probe_count := ((old_meta >> hashbits) - 1) << 1 + old_index := (i - old_probe_count) & (m.even_index >> 1) + mut index := (old_index | (old_meta << m.shift)) & m.even_index + mut meta := (old_meta & hash_mask) | probe_inc + index, meta = m.meta_less(index, meta) + kv_index := unsafe { old_metas[i + 1] } + m.meta_greater(index, meta, kv_index) + } + unsafe { free(old_metas) } +} + +// This method is used for assignment operators. If the argument-key +// does not exist in the map, it's added to the map along with the zero/default value. +// If the key exists, its respective value is returned. +fn (mut m map) get_and_set(key voidptr, zero voidptr) voidptr { + for { + mut index, mut meta := m.key_to_index(key) + for { + if meta == unsafe { m.metas[index] } { + kv_index := int(unsafe { m.metas[index + 1] }) + pkey := unsafe { m.key_values.key(kv_index) } + if m.key_eq_fn(key, pkey) { + pval := unsafe { m.key_values.value(kv_index) } + return unsafe { &byte(pval) } + } + } + index += 2 + meta += probe_inc + if meta > unsafe { m.metas[index] } { + break + } + } + // Key not found, insert key with zero-value + m.set(key, zero) + } + assert false + return voidptr(0) +} + +// If `key` matches the key of an element in the container, +// the method returns a reference to its mapped value. +// If not, a zero/default value is returned. +fn (m &map) get(key voidptr, zero voidptr) voidptr { + mut index, mut meta := m.key_to_index(key) + for { + if meta == unsafe { m.metas[index] } { + kv_index := int(unsafe { m.metas[index + 1] }) + pkey := unsafe { m.key_values.key(kv_index) } + if m.key_eq_fn(key, pkey) { + pval := unsafe { m.key_values.value(kv_index) } + return unsafe { &byte(pval) } + } + } + index += 2 + meta += probe_inc + if meta > unsafe { m.metas[index] } { + break + } + } + return zero +} + +// If `key` matches the key of an element in the container, +// the method returns a reference to its mapped value. +// If not, a zero pointer is returned. +// This is used in `x := m['key'] or { ... }` +fn (m &map) get_check(key voidptr) voidptr { + mut index, mut meta := m.key_to_index(key) + for { + if meta == unsafe { m.metas[index] } { + kv_index := int(unsafe { m.metas[index + 1] }) + pkey := unsafe { m.key_values.key(kv_index) } + if m.key_eq_fn(key, pkey) { + pval := unsafe { m.key_values.value(kv_index) } + return unsafe { &byte(pval) } + } + } + index += 2 + meta += probe_inc + if meta > unsafe { m.metas[index] } { + break + } + } + return 0 +} + +// Checks whether a particular key exists in the map. +fn (m &map) exists(key voidptr) bool { + mut index, mut meta := m.key_to_index(key) + for { + if meta == unsafe { m.metas[index] } { + kv_index := int(unsafe { m.metas[index + 1] }) + pkey := unsafe { m.key_values.key(kv_index) } + if m.key_eq_fn(key, pkey) { + return true + } + } + index += 2 + meta += probe_inc + if meta > unsafe { m.metas[index] } { + break + } + } + return false +} + +[inline] +fn (mut d DenseArray) delete(i int) { + if d.deletes == 0 { + d.all_deleted = vcalloc(d.cap) // sets to 0 + } + d.deletes++ + unsafe { + d.all_deleted[i] = 1 + } +} + +// Removes the mapping of a particular key from the map. +[unsafe] +pub fn (mut m map) delete(key voidptr) { + mut index, mut meta := m.key_to_index(key) + index, meta = m.meta_less(index, meta) + // Perform backwards shifting + for meta == unsafe { m.metas[index] } { + kv_index := int(unsafe { m.metas[index + 1] }) + pkey := unsafe { m.key_values.key(kv_index) } + if m.key_eq_fn(key, pkey) { + for (unsafe { m.metas[index + 2] } >> hashbits) > 1 { + unsafe { + m.metas[index] = m.metas[index + 2] - probe_inc + m.metas[index + 1] = m.metas[index + 3] + } + index += 2 + } + m.len-- + m.key_values.delete(kv_index) + unsafe { + m.metas[index] = 0 + m.free_fn(pkey) + // Mark key as deleted + vmemset(pkey, 0, m.key_bytes) + } + if m.key_values.len <= 32 { + return + } + // Clean up key_values if too many have been deleted + if m.key_values.deletes >= (m.key_values.len >> 1) { + m.key_values.zeros_to_end() + m.rehash() + } + return + } + index += 2 + meta += probe_inc + } +} + +// Returns all keys in the map. +fn (m &map) keys() array { + mut keys := __new_array(m.len, 0, m.key_bytes) + mut item := unsafe { &byte(keys.data) } + if m.key_values.deletes == 0 { + for i := 0; i < m.key_values.len; i++ { + unsafe { + pkey := m.key_values.key(i) + m.clone_fn(item, pkey) + item = item + m.key_bytes + } + } + return keys + } + for i := 0; i < m.key_values.len; i++ { + if !m.key_values.has_index(i) { + continue + } + unsafe { + pkey := m.key_values.key(i) + m.clone_fn(item, pkey) + item = item + m.key_bytes + } + } + return keys +} + +// warning: only copies keys, does not clone +[unsafe] +fn (d &DenseArray) clone() DenseArray { + res := DenseArray{ + key_bytes: d.key_bytes + value_bytes: d.value_bytes + cap: d.cap + len: d.len + deletes: d.deletes + all_deleted: 0 + values: 0 + keys: 0 + } + unsafe { + if d.deletes != 0 { + res.all_deleted = memdup(d.all_deleted, d.cap) + } + res.keys = memdup(d.keys, d.cap * d.key_bytes) + res.values = memdup(d.values, d.cap * d.value_bytes) + } + return res +} + +// clone returns a clone of the `map`. +[unsafe] +pub fn (m &map) clone() map { + metasize := int(sizeof(u32) * (m.even_index + 2 + m.extra_metas)) + res := map{ + key_bytes: m.key_bytes + value_bytes: m.value_bytes + even_index: m.even_index + cached_hashbits: m.cached_hashbits + shift: m.shift + key_values: unsafe { m.key_values.clone() } + metas: unsafe { &u32(malloc(metasize)) } + extra_metas: m.extra_metas + len: m.len + has_string_keys: m.has_string_keys + hash_fn: m.hash_fn + key_eq_fn: m.key_eq_fn + clone_fn: m.clone_fn + free_fn: m.free_fn + } + unsafe { vmemcpy(res.metas, m.metas, metasize) } + if !m.has_string_keys { + return res + } + // clone keys + for i in 0 .. m.key_values.len { + if !m.key_values.has_index(i) { + continue + } + m.clone_fn(res.key_values.key(i), m.key_values.key(i)) + } + return res +} + +// free releases all memory resources occupied by the `map`. +[unsafe] +pub fn (m &map) free() { + unsafe { free(m.metas) } + if m.key_values.deletes == 0 { + for i := 0; i < m.key_values.len; i++ { + unsafe { + pkey := m.key_values.key(i) + m.free_fn(pkey) + } + } + } else { + for i := 0; i < m.key_values.len; i++ { + if !m.key_values.has_index(i) { + continue + } + unsafe { + pkey := m.key_values.key(i) + m.free_fn(pkey) + } + } + unsafe { free(m.key_values.all_deleted) } + } + unsafe { + free(m.key_values.keys) + free(m.key_values.values) + } +} diff --git a/v_windows/v/vlib/builtin/map_d_gcboehm_opt.v b/v_windows/v/vlib/builtin/map_d_gcboehm_opt.v new file mode 100644 index 0000000..93049b7 --- /dev/null +++ b/v_windows/v/vlib/builtin/map_d_gcboehm_opt.v @@ -0,0 +1,146 @@ +// "noscan" versions of `map` initialization routines +// +// They are used when the compiler can proof that either the keys or the values or both +// do not contain any pointers. Such objects can be placed in a memory area that is not +// scanned by the garbage collector + +module builtin + +[inline] +fn new_dense_array_noscan(key_bytes int, key_noscan bool, value_bytes int, value_noscan bool) DenseArray { + cap := 8 + keys := if key_noscan { + unsafe { malloc_noscan(cap * key_bytes) } + } else { + unsafe { malloc(cap * key_bytes) } + } + values := if value_noscan { + unsafe { malloc_noscan(cap * value_bytes) } + } else { + unsafe { malloc(cap * value_bytes) } + } + return DenseArray{ + key_bytes: key_bytes + value_bytes: value_bytes + cap: cap + len: 0 + deletes: 0 + all_deleted: 0 + keys: keys + values: values + } +} + +fn new_map_noscan_key(key_bytes int, value_bytes int, hash_fn MapHashFn, key_eq_fn MapEqFn, clone_fn MapCloneFn, free_fn MapFreeFn) map { + metasize := int(sizeof(u32) * (init_capicity + extra_metas_inc)) + // for now assume anything bigger than a pointer is a string + has_string_keys := key_bytes > sizeof(voidptr) + return map{ + key_bytes: key_bytes + value_bytes: value_bytes + even_index: init_even_index + cached_hashbits: max_cached_hashbits + shift: init_log_capicity + key_values: new_dense_array_noscan(key_bytes, true, value_bytes, false) + metas: unsafe { &u32(vcalloc_noscan(metasize)) } + extra_metas: extra_metas_inc + len: 0 + has_string_keys: has_string_keys + hash_fn: hash_fn + key_eq_fn: key_eq_fn + clone_fn: clone_fn + free_fn: free_fn + } +} + +fn new_map_noscan_value(key_bytes int, value_bytes int, hash_fn MapHashFn, key_eq_fn MapEqFn, clone_fn MapCloneFn, free_fn MapFreeFn) map { + metasize := int(sizeof(u32) * (init_capicity + extra_metas_inc)) + // for now assume anything bigger than a pointer is a string + has_string_keys := key_bytes > sizeof(voidptr) + return map{ + key_bytes: key_bytes + value_bytes: value_bytes + even_index: init_even_index + cached_hashbits: max_cached_hashbits + shift: init_log_capicity + key_values: new_dense_array_noscan(key_bytes, false, value_bytes, true) + metas: unsafe { &u32(vcalloc_noscan(metasize)) } + extra_metas: extra_metas_inc + len: 0 + has_string_keys: has_string_keys + hash_fn: hash_fn + key_eq_fn: key_eq_fn + clone_fn: clone_fn + free_fn: free_fn + } +} + +fn new_map_noscan_key_value(key_bytes int, value_bytes int, hash_fn MapHashFn, key_eq_fn MapEqFn, clone_fn MapCloneFn, free_fn MapFreeFn) map { + metasize := int(sizeof(u32) * (init_capicity + extra_metas_inc)) + // for now assume anything bigger than a pointer is a string + has_string_keys := key_bytes > sizeof(voidptr) + return map{ + key_bytes: key_bytes + value_bytes: value_bytes + even_index: init_even_index + cached_hashbits: max_cached_hashbits + shift: init_log_capicity + key_values: new_dense_array_noscan(key_bytes, true, value_bytes, true) + metas: unsafe { &u32(vcalloc_noscan(metasize)) } + extra_metas: extra_metas_inc + len: 0 + has_string_keys: has_string_keys + hash_fn: hash_fn + key_eq_fn: key_eq_fn + clone_fn: clone_fn + free_fn: free_fn + } +} + +fn new_map_init_noscan_key(hash_fn MapHashFn, key_eq_fn MapEqFn, clone_fn MapCloneFn, free_fn MapFreeFn, n int, key_bytes int, value_bytes int, keys voidptr, values voidptr) map { + mut out := new_map_noscan_key(key_bytes, value_bytes, hash_fn, key_eq_fn, clone_fn, + free_fn) + // TODO pre-allocate n slots + mut pkey := &byte(keys) + mut pval := &byte(values) + for _ in 0 .. n { + unsafe { + out.set(pkey, pval) + pkey = pkey + key_bytes + pval = pval + value_bytes + } + } + return out +} + +fn new_map_init_noscan_value(hash_fn MapHashFn, key_eq_fn MapEqFn, clone_fn MapCloneFn, free_fn MapFreeFn, n int, key_bytes int, value_bytes int, keys voidptr, values voidptr) map { + mut out := new_map_noscan_value(key_bytes, value_bytes, hash_fn, key_eq_fn, clone_fn, + free_fn) + // TODO pre-allocate n slots + mut pkey := &byte(keys) + mut pval := &byte(values) + for _ in 0 .. n { + unsafe { + out.set(pkey, pval) + pkey = pkey + key_bytes + pval = pval + value_bytes + } + } + return out +} + +fn new_map_init_noscan_key_value(hash_fn MapHashFn, key_eq_fn MapEqFn, clone_fn MapCloneFn, free_fn MapFreeFn, n int, key_bytes int, value_bytes int, keys voidptr, values voidptr) map { + mut out := new_map_noscan_key_value(key_bytes, value_bytes, hash_fn, key_eq_fn, clone_fn, + free_fn) + // TODO pre-allocate n slots + mut pkey := &byte(keys) + mut pval := &byte(values) + for _ in 0 .. n { + unsafe { + out.set(pkey, pval) + pkey = pkey + key_bytes + pval = pval + value_bytes + } + } + return out +} diff --git a/v_windows/v/vlib/builtin/map_of_floats_test.v b/v_windows/v/vlib/builtin/map_of_floats_test.v new file mode 100644 index 0000000..7481107 --- /dev/null +++ b/v_windows/v/vlib/builtin/map_of_floats_test.v @@ -0,0 +1,27 @@ +fn test_map_of_f32() { + mut m32 := map[f32]string{} + m32[1.0] = 'one' + println(m32) + assert '$m32' == r"{1.: 'one'}" + for k, v in m32 { + assert typeof(k).name == 'f32' + assert typeof(v).name == 'string' + assert k == 1.0 + assert v == 'one' + } +} + +fn test_map_of_f64() { + mut m64 := { + 3.14: 'pi' + } + m64[1.0] = 'one' + println(m64) + assert '$m64' == r"{3.14: 'pi', 1.: 'one'}" + for k, v in m64 { + assert typeof(k).name == 'f64' + assert typeof(v).name == 'string' + assert k in [1.0, 3.14] + assert v in ['pi', 'one'] + } +} diff --git a/v_windows/v/vlib/builtin/map_test.v b/v_windows/v/vlib/builtin/map_test.v new file mode 100644 index 0000000..f186dd5 --- /dev/null +++ b/v_windows/v/vlib/builtin/map_test.v @@ -0,0 +1,947 @@ +import rand + +const ( + strings = unique_strings(20000, 10) +) + +fn unique_strings(arr_len int, str_len int) []string { + mut arr := []string{cap: arr_len} + for arr.len < arr_len { + str := rand.string(str_len) + if str !in arr { + arr << str + } + } + return arr +} + +fn test_get_and_set_many() { + mut m := map[string]int{} + for i, s in strings { + m[s] = i + assert m[s] == i + assert m.len == i + 1 + } + for i, s in strings { + assert m[s] == i + } + assert m.len == strings.len +} + +fn test_for_in_many() { + mut m := map[string]int{} + for i, s in strings { + m[s] = i + } + for k, v in m { + assert m[k] == v + } +} + +fn test_keys_many() { + mut m := map[string]int{} + for i, s in strings { + m[s] = i + } + keys := m.keys() + assert keys.len == strings.len + assert keys.len == m.len + assert keys == strings +} + +fn test_deletes_many() { + mut m := map[string]int{} + for i, s in strings { + m[s] = i + } + for i, s in strings { + m.delete(s) + assert m[s] == 0 + assert m.len == strings.len - (i + 1) + } + assert m.len == 0 + assert m.keys().len == 0 +} + +struct User { +mut: + name string +} + +struct Aaa { +mut: + m map[string]int + users map[string]User +} + +fn (mut a Aaa) set(key string, val int) { + a.m[key] = val +} + +fn test_map() { + mut m := map[string]int{} + assert m.len == 0 + m['hi'] = 80 + m['hello'] = 101 + assert m['hi'] == 80 + assert m['hello'] == 101 + assert m.len == 2 + assert 'hi' in m + mut sum := 0 + // Test `for in` + for _, val in m { + sum += val + } + assert sum == 80 + 101 + // Test `.keys()` + keys := m.keys() + assert keys.len == 2 + assert 'hi' in keys + assert 'hello' in keys + m.delete('hi') + assert m.len == 1 + m.delete('aloha') + assert m.len == 1 + assert m['hi'] == 0 + assert m.keys().len == 1 + assert m.keys()[0] == 'hello' + // // + mut users := map[string]User{} + users['1'] = User{'Peter'} + peter := users['1'] + assert peter.name == 'Peter' + mut a := Aaa{ + m: map[string]int{} + users: map[string]User{} + } + a.users['Bob'] = User{'Bob'} + q := a.users['Bob'] + assert q.name == 'Bob' + // test struct field change + a.users['Bob'].name = 'bob' + q2 := a.users['Bob'] + assert q2.name == 'bob' + a.m['one'] = 1 + a.set('two', 2) + assert a.m['one'] == 1 + assert a.m['two'] == 2 +} + +fn test_map_init() { + one := 'one' + three := 'three' + m := { + one: 1 + 'two': 2 + three: 1 + 2 + } + assert m['one'] == 1 + assert m['two'] == 2 + assert m['three'] == 3 + assert m['unknown'] == 0 +} + +fn test_string_map() { + // m := map[string]Fn +} + +fn test_large_map() { + // ticks := time.ticks() + mut nums := map[string]int{} + n := 30 * 1000 + for i in 0 .. n { + key := i.str() + nums[key] = i + } + assert nums['1'] == 1 + assert nums['999'] == 999 + assert nums['1000000'] == 0 + // println(time.ticks() - ticks) +} + +fn test_various_map_value() { + mut m1 := map[string]int{} + m1['test'] = 1 + assert m1['test'] == 1 + mut m2 := map[string]string{} + m2['test'] = 'test' + assert m2['test'] == 'test' + mut m3 := map[string]i8{} + m3['test'] = i8(0) + assert m3['test'] == i8(0) + mut m4 := map[string]i16{} + m4['test'] = i16(0) + assert m4['test'] == i16(0) + mut m7 := map[string]u16{} + m7['test'] = u16(0) + assert m7['test'] == u16(0) + mut m8 := map[string]u32{} + m8['test'] = u32(0) + assert m8['test'] == u32(0) + mut m9 := map[string]bool{} + m9['test'] = true + assert m9['test'] == true + mut m10 := map[string]byte{} + m10['test'] = byte(0) + assert m10['test'] == byte(0) + mut m11 := map[string]f32{} + m11['test'] = f32(0.0) + assert m11['test'] == f32(0.0) + mut m12 := map[string]f64{} + m12['test'] = f64(0.0) + assert m12['test'] == f64(0.0) + // mut m13 := map[string]rune + // m13['test'] = rune(0) + // assert m13['test'] == rune(0) + mut m14 := map[string]voidptr{} + m14['test'] = voidptr(0) + assert m14['test'] == voidptr(0) + mut m15 := map[string]&byte{} + m15['test'] = &byte(0) + assert m15['test'] == &byte(0) + mut m16 := map[string]i64{} + m16['test'] = i64(0) + assert m16['test'] == i64(0) + mut m17 := map[string]u64{} + m17['test'] = u64(0) + assert m17['test'] == u64(0) + mut m18 := map[string]&int{} + m18['test'] = &int(0) + assert m18['test'] == &int(0) +} + +fn test_string_arr() { + mut m := map[string][]string{} + m['a'] = ['one', 'two'] + assert m['a'].len == 2 + assert m['a'][0] == 'one' + assert m['a'][1] == 'two' +} + +fn mut_map(mut m map[string]int) { + m['a'] = 10 +} + +fn test_mut_arg() { + mut m := map[string]int{} + mut_map(mut m) + a := m['a'] + assert a == 10 +} + +fn test_delete() { + mut m := map[string]int{} + m['one'] = 1 + m['two'] = 2 + println(m['two']) // => "2" + m.delete('two') + println(m['two'].str()) // => 0 + assert ('two' in m) == false + println('two' in m) // => true, on Linux and Windows <-- wrong ! +} + +fn test_delete_size() { + arr := ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] + mut m := map[string]int{} + for _ in 0 .. 10 { + for i in 0 .. 10 { + m[arr[i]] = i + } + assert m.len == 10 + println(m.len) + for i in 0 .. 10 { + m.delete(arr[i]) + } + } +} + +fn test_nested_for_in() { + mut m := map[string]int{} + for i in 0 .. 1000 { + m[i.str()] = i + } + mut i := 0 + for key1, _ in m { + assert key1 == i.str() + i++ + mut j := 0 + for key2, _ in m { + assert key2 == j.str() + j++ + } + } +} + +fn test_delete_in_for_in() { + mut m := map[string]string{} + for i in 0 .. 1000 { + m[i.str()] = i.str() + } + mut i := 0 + for key, _ in m { + assert key == i.str() + m.delete(key) + i++ + } + assert m.str() == '{}' + assert m.len == 0 +} + +fn test_set_in_for_in() { + mut m := map[string]string{} + for i in 0 .. 10 { + m[i.str()] = i.str() + } + mut last_key := '' + mut i := 0 + for key, _ in m { + m['10'] = '10' + assert key == i.str() + last_key = key + i++ + } + assert last_key == '10' +} + +fn test_delete_and_set_in_for_in() { + mut m := map[string]string{} + for i in 0 .. 1000 { + m[i.str()] = i.str() + } + mut i := 0 + for key, _ in m { + assert key == i.str() + m.delete(key) + m[key] = i.str() + if i == 999 { + break + } + i++ + } + assert m.len == 1000 + i = 0 + for key, _ in m { + assert m[key] == i.str() + i++ + } + assert i == 1000 +} + +struct Mstruct1 { +pub mut: + mymap map[string]int +} + +struct Mstruct2 { +pub mut: + mymap map[string]f64 +} + +struct Mstruct3 { +pub mut: + mymap map[string]u16 +} + +fn test_map_assign() { + mut a := map[string]f64{} + mut b := map[string]int{} + mut c := map[string]u16{} + a = { + 'x': 12.4 + 'y': 3 + } + b = { + 'u': -13 + 'v': 12 + } + c = { + 's': u16(5) + 't': 3 + } + _ := Mstruct1{{ + 'p': 12 + }} + _ := Mstruct2{{ + 'q': 1.7 + }} + _ := Mstruct3{{ + 'r': u16(6) + 's': 5 + }} +} + +fn test_postfix_op_directly() { + mut a := map[string]int{} + a['aaa']++ + assert a['aaa'] == 1 + a['aaa']++ + assert a['aaa'] == 2 + a['bbb']-- + assert a['bbb'] == -1 + a['bbb']-- + assert a['bbb'] == -2 +} + +fn test_map_push_directly() { + mut a := map[string][]string{} + a['aaa'] << ['a', 'b', 'c'] + assert a['aaa'].len == 3 + assert a['aaa'] == ['a', 'b', 'c'] +} + +fn test_assign_directly() { + mut a := map[string]int{} + a['aaa'] += 4 + assert a['aaa'] == 4 + a['aaa'] -= 2 + assert a['aaa'] == 2 +} + +fn test_map_in_directly() { + for k, v in { + 'aa': 1 + } { + assert k == 'aa' + assert v == 1 + } +} + +fn test_plus_assign_string() { + mut m := { + 'one': '' + } + m['one'] += '1' + assert m.len == 1 + assert m['one'] == '1' +} + +fn test_map_keys_to_array() { + m := { + 'a': 'b' + 'c': 'd' + } + mut arr := []string{} + for k, _ in m { + arr << k + } + sarr := arr.str() + println(sarr) + assert sarr == "['a', 'c']" +} + +fn map_in_mut(mut m map[string]int) { + if 'one' in m { + m['one'] = 2 + } +} + +fn test_map_in_mut() { + mut m := { + 'one': 1 + } + map_in_mut(mut m) + assert m['one'] == 2 +} + +fn test_map_in() { + m := { + 'Foo': 'bar' + } + if 'foo'.capitalize() in m { + println('ok') + } else { + assert false + } +} + +fn mut_map_with_relation_op_in_fn(mut m map[string]int) { + if m['one'] == 1 { + m['three'] = 3 + } + if m['two'] != 1 { + m['four'] = 4 + } + if m['one'] > 0 { + m['five'] = 5 + } + if m['one'] < 2 { + m['six'] = 6 + } + if m['two'] >= 2 { + m['seven'] = 7 + } + if m['two'] <= 2 { + m['eight'] = 8 + } +} + +fn test_mut_map_with_relation_op_in_fn() { + mut m := { + 'one': 1 + 'two': 2 + } + mut_map_with_relation_op_in_fn(mut m) + assert 'three' in m + assert 'four' in m + assert 'five' in m + assert 'six' in m + assert 'seven' in m + assert 'eight' in m +} + +fn test_map_str_after_delete() { + mut m := { + 'first': 1 + 'second': 2 + 'third': 3 + } + osm := '$m' + m.delete('second') + nsm := '$m' + println('m: $m') + assert osm == "{'first': 1, 'second': 2, 'third': 3}" + assert nsm == "{'first': 1, 'third': 3}" +} + +fn test_modify_map_value() { + mut m1 := { + 'foo': 3 + 'bar': -7 + } + m1['foo'] += 5 + m1['bar'] *= -2 + assert m1['foo'] == 8 + assert m1['bar'] == 14 +} + +fn test_map_clone() { + mut nums := { + 'foo': 1 + 'bar': 2 + } + mut nums2 := nums.clone() + nums2['foo']++ + nums2['bar'] *= 4 + assert nums['foo'] == 1 + assert nums['bar'] == 2 + assert nums2['foo'] == 2 + assert nums2['bar'] == 8 +} + +struct MValue { + name string + misc map[string]string +} + +fn test_map_default_zero() { + m := map[string]MValue{} + v := m['unknown'] + x := v.misc['x'] + println(x) + assert x == '' +} + +fn test_map_or() { + m := { + 'first': 1 + 'second': 2 + 'third': 3 + } + _ = m + // num := m['first'] or { return } +} + +fn test_int_keys() { + mut m := map[int]int{} + m[3] = 9 + m[4] = 16 + assert m.len == 2 + assert m[3] == 9 + assert m[4] == 16 + m[5] += 24 + m[5]++ + assert m[5] == 25 + mut m2 := { + 3: 9 + 4: 16 + 5: 25 + } + + four := 4 + m2.delete(3) + m2.delete(four) + m2.delete(5) + assert m2.len == 0 + assert m2[3] == 0 + assert m2[4] == 0 + assert m2[5] == 0 + assert m2.keys() == [] + + m2 = { + 3: 9 + 4: 16 + 5: 25 + } + + assert m2.len == 3 + // clone + mc := m.clone() + same := mc == m + assert same + assert mc.len == 3 + assert mc.keys() == [3, 4, 5] + mut all := []int{} + for k, v in mc { + assert m[k] == v + all << k + all << v + } + assert all == [3, 9, 4, 16, 5, 25] + + mut m3 := { + 1: 'one' + 2: 'two' + } + assert m3[1] == 'one' + m3.delete(1) +} + +enum Color { + red + green + blue +} + +type ColorAlias = Color + +fn test_alias_enum() { + mut m := map[ColorAlias]string{} + m[Color.red] = 'hi' + assert m[Color.red] == 'hi' +} + +fn test_enum_in_map() { + mut m := map[Color]string{} + m[Color.red] = 'hi' + assert Color.red in m + assert Color.green !in m + assert Color.blue !in m +} + +fn test_voidptr_keys() { + mut m := map[voidptr]string{} + v := 5 + m[&v] = 'var' + m[&m] = 'map' + assert m[&v] == 'var' + assert m[&m] == 'map' + assert m.len == 2 +} + +fn test_rune_keys() { + mut m := { + `!`: 2 + `%`: 3 + } + assert typeof(m).name == 'map[rune]int' + assert m[`!`] == 2 + m[`@`] = 7 + assert m.len == 3 + println(m) + assert '$m' == '{`!`: 2, `%`: 3, `@`: 7}' + + mut a := []rune{} + for k, v in m { + a << k + a << rune(v) + `0` + } + assert a == [`!`, `2`, `%`, `3`, `@`, `7`] +} + +fn test_eq() { + a := { + 'a': 1 + 'b': 2 + } + assert a == { + 'a': 1 + 'b': 2 + } + b := { + 'a': [[1]] + 'b': [[2]] + } + assert b == { + 'a': [[1]] + 'b': [[2]] + } + c := { + 'a': { + '11': 1 + } + 'b': { + '22': 2 + } + } + assert c == { + 'a': { + '11': 1 + } + 'b': { + '22': 2 + } + } + d := { + 'a': MValue{ + name: 'aa' + misc: { + '11': '1' + } + } + 'b': MValue{ + name: 'bb' + misc: { + '22': '2' + } + } + } + assert d == { + 'a': MValue{ + name: 'aa' + misc: { + '11': '1' + } + } + 'b': MValue{ + name: 'bb' + misc: { + '22': '2' + } + } + } +} + +fn test_non_string_key_map_str() { + assert { + 23: 4 + }.str() == '{23: 4}' + assert { + `a`: 12 + `b`: 13 + }.str() == '{`a`: 12, `b`: 13}' + assert { + 23: 'foo' + 25: 'bar' + }.str() == "{23: 'foo', 25: 'bar'}" +} + +fn test_map_assign_empty_map_init() { + mut a := { + 'one': 1 + } + a = {} + println(a) + assert a == map[string]int{} + assert '$a' == '{}' +} + +fn test_in_map_literal() { + assert 1 in { + 1: 'one' + } +} + +fn test_byte_keys() { + mut m := map[byte]byte{} + byte_max := byte(255) + for i in byte(0) .. byte_max { + m[i] = i + assert m[i] == i + } + for k, v in m { + assert k == v + } + for i in byte(0) .. 100 { + m[i]++ + assert m[i] == i + 1 + } + assert m.len == byte_max + keys := m.keys() + for i in byte(0) .. byte_max { + assert keys[i] == i + } + for i in byte(0) .. byte_max { + m.delete(i) + assert m[i] == 0 + } + assert m.len == 0 +} + +fn test_i16_keys() { + mut m := map[i16]i16{} + end := i16(1000) + for i in i16(0) .. end { + m[i] = i + assert m[i] == i + } + for k, v in m { + assert k == v + } + for i in i16(0) .. 500 { + m[i]++ + assert m[i] == i + 1 + } + assert m.len == end + keys := m.keys() + for i in i16(0) .. end { + assert keys[i] == i + } + for i in i16(0) .. end { + m.delete(i) + assert m[i] == 0 + } + assert m.len == 0 +} + +fn test_u16_keys() { + mut m := map[u16]u16{} + end := u16(1000) + for i in u16(0) .. end { + m[i] = i + assert m[i] == i + } + for k, v in m { + assert k == v + } + for i in u16(0) .. 500 { + m[i]++ + assert m[i] == i + 1 + } + assert m.len == end + keys := m.keys() + for i in u16(0) .. end { + assert keys[i] == i + } + for i in u16(0) .. end { + m.delete(i) + assert m[i] == 0 + } + assert m.len == 0 +} + +fn test_u32_keys() { + mut m := map[u32]u32{} + end := u32(1000) + for i in u32(0) .. end { + m[i] = i + assert m[i] == i + } + for k, v in m { + assert k == v + } + for i in u32(0) .. 500 { + m[i]++ + assert m[i] == i + 1 + } + assert m.len == end + keys := m.keys() + for i in u32(0) .. end { + assert keys[i] == i + } + for i in u32(0) .. end { + m.delete(i) + assert m[i] == 0 + } + assert m.len == 0 +} + +fn test_int_keys2() { + mut m := map[int]int{} + end := 1000 + for i in int(0) .. end { + m[i] = i + assert m[i] == i + } + for k, v in m { + assert k == v + } + for i in int(0) .. 500 { + m[i]++ + assert m[i] == i + 1 + } + assert m.len == end + keys := m.keys() + for i in int(0) .. end { + assert keys[i] == i + } + for i in int(0) .. end { + m.delete(i) + assert m[i] == 0 + } + assert m.len == 0 +} + +fn test_i64_keys() { + mut m := map[i64]i64{} + end := i64(1000) + for i in i64(0) .. end { + m[i] = i + assert m[i] == i + } + for k, v in m { + assert k == v + } + for i in i64(0) .. 500 { + m[i]++ + assert m[i] == i + 1 + } + assert m.len == end + keys := m.keys() + for i in i64(0) .. end { + assert keys[i] == i + } + for i in i64(0) .. end { + m.delete(i) + assert m[i] == 0 + } + assert m.len == 0 +} + +fn test_u64_keys() { + mut m := map[u64]u64{} + end := u64(1000) + for i in u64(0) .. end { + m[i] = i + assert m[i] == i + } + for k, v in m { + assert k == v + } + for i in u64(0) .. 500 { + m[i]++ + assert m[i] == i + 1 + } + assert m.len == end + keys := m.keys() + for i in u64(0) .. end { + assert keys[i] == i + } + for i in u64(0) .. end { + m.delete(i) + assert m[i] == 0 + } + assert m.len == 0 +} + +fn test_map_set_fixed_array_variable() { + mut m := map[string][2]f64{} + m['A'] = [1.1, 2.2]! + println(m) + assert '$m' == "{'A': [1.1, 2.2]}" + + mut m2 := map[string][2]f64{} + arr := [1.1, 2.2]! + m2['A'] = arr + println(m2) + assert '$m2' == "{'A': [1.1, 2.2]}" +} diff --git a/v_windows/v/vlib/builtin/option.c.v b/v_windows/v/vlib/builtin/option.c.v new file mode 100644 index 0000000..464cf87 --- /dev/null +++ b/v_windows/v/vlib/builtin/option.c.v @@ -0,0 +1,15 @@ +module builtin + +[typedef] +struct C.IError { + _object voidptr +} + +[unsafe] +pub fn (ie &IError) free() { + unsafe { + ie.msg.free() + cie := &C.IError(ie) + free(cie._object) + } +} diff --git a/v_windows/v/vlib/builtin/option.v b/v_windows/v/vlib/builtin/option.v new file mode 100644 index 0000000..1f1bdd9 --- /dev/null +++ b/v_windows/v/vlib/builtin/option.v @@ -0,0 +1,89 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module builtin + +// IError holds information about an error instance +pub interface IError { + msg string + code int +} + +// Error is the default implementation of IError, that is returned by e.g. `error()` +pub struct Error { +pub: + msg string + code int +} + +pub fn (err IError) str() string { + return match err { + None__ { 'none' } + Error { err.msg } + else { '$err.type_name(): $err.msg' } + } +} + +const none__ = IError(&None__{}) + +struct None__ { + msg string + code int +} + +fn (_ None__) str() string { + return 'none' +} + +[if trace_error ?] +fn trace_error(x string) { + eprintln('> ${@FN} | $x') +} + +// error returns a default error instance containing the error given in `message`. +// Example: `if ouch { return error('an error occurred') }` +[inline] +pub fn error(message string) IError { + trace_error(message) + return &Error{ + msg: message + } +} + +// error_with_code returns a default error instance containing the given `message` and error `code`. +// `if ouch { return error_with_code('an error occurred', 1) }` +[inline] +pub fn error_with_code(message string, code int) IError { + trace_error('$message | code: $code') + return &Error{ + msg: message + code: code + } +} + +// Option is the base of V's internal optional return system. +struct Option { + state byte + err IError = none__ + // Data is trailing after err + // and is not included in here but in the + // derived Option_xxx types +} + +fn opt_ok(data voidptr, mut option Option, size int) { + unsafe { + *option = Option{} + // use err to get the end of OptionBase and then memcpy into it + vmemcpy(&byte(&option.err) + sizeof(IError), data, size) + } +} + +[unsafe] +pub fn (e &Error) free() { + unsafe { e.msg.free() } +} + +[unsafe] +pub fn (n &None__) free() { + unsafe { n.msg.free() } +} diff --git a/v_windows/v/vlib/builtin/prealloc.c.v b/v_windows/v/vlib/builtin/prealloc.c.v new file mode 100644 index 0000000..0ef66fc --- /dev/null +++ b/v_windows/v/vlib/builtin/prealloc.c.v @@ -0,0 +1,114 @@ +module builtin + +// With -prealloc, V calls libc's malloc to get chunks, each at least 16MB +// in size, as needed. Once a chunk is available, all malloc() calls within +// V code, that can fit inside the chunk, will use it instead, each bumping a +// pointer, till the chunk is filled. Once a chunk is filled, a new chunk will +// be allocated by calling libc's malloc, and the process continues. +// Each new chunk has a pointer to the old one, and at the end of the program, +// the entire linked list of chunks is freed. +// The goal of all this is to amortize the cost of calling libc's malloc, +// trading higher memory usage for a compiler (or any single threaded batch +// mode program), for a ~8-10% speed increase. +// NB: `-prealloc` is NOT safe to be used for multithreaded programs! + +// size of the preallocated chunk +const prealloc_block_size = 16 * 1024 * 1024 + +__global g_memory_block &VMemoryBlock +[heap] +struct VMemoryBlock { +mut: + id int + cap int + start &byte = 0 + previous &VMemoryBlock = 0 + remaining int + current &byte = 0 + mallocs int +} + +[unsafe] +fn vmemory_block_new(prev &VMemoryBlock, at_least int) &VMemoryBlock { + mut v := unsafe { &VMemoryBlock(C.calloc(1, sizeof(VMemoryBlock))) } + if prev != 0 { + v.id = prev.id + 1 + } + v.previous = prev + block_size := if at_least < prealloc_block_size { prealloc_block_size } else { at_least } + v.start = unsafe { C.malloc(block_size) } + v.cap = block_size + v.remaining = block_size + v.current = v.start + return v +} + +[unsafe] +fn vmemory_block_malloc(n int) &byte { + unsafe { + if g_memory_block.remaining < n { + g_memory_block = vmemory_block_new(g_memory_block, n) + } + mut res := &byte(0) + res = g_memory_block.current + g_memory_block.remaining -= n + g_memory_block.mallocs++ + g_memory_block.current += n + return res + } +} + +///////////////////////////////////////////////// + +[unsafe] +fn prealloc_vinit() { + unsafe { + g_memory_block = vmemory_block_new(voidptr(0), prealloc_block_size) + $if !freestanding { + C.atexit(prealloc_vcleanup) + } + } +} + +[unsafe] +fn prealloc_vcleanup() { + $if prealloc_stats ? { + // NB: we do 2 loops here, because string interpolation + // in the first loop may still use g_memory_block + // The second loop however should *not* allocate at all. + mut nr_mallocs := i64(0) + mut mb := g_memory_block + for mb != 0 { + nr_mallocs += mb.mallocs + eprintln('> freeing mb.id: ${mb.id:3} | cap: ${mb.cap:7} | rem: ${mb.remaining:7} | start: ${voidptr(mb.start)} | current: ${voidptr(mb.current)} | diff: ${u64(mb.current) - u64(mb.start):7} bytes | mallocs: $mb.mallocs') + mb = mb.previous + } + eprintln('> nr_mallocs: $nr_mallocs') + } + unsafe { + for g_memory_block != 0 { + C.free(g_memory_block.start) + g_memory_block = g_memory_block.previous + } + } +} + +[unsafe] +fn prealloc_malloc(n int) &byte { + return unsafe { vmemory_block_malloc(n) } +} + +[unsafe] +fn prealloc_realloc(old_data &byte, old_size int, new_size int) &byte { + new_ptr := unsafe { vmemory_block_malloc(new_size) } + min_size := if old_size < new_size { old_size } else { new_size } + unsafe { C.memcpy(new_ptr, old_data, min_size) } + return new_ptr +} + +[unsafe] +fn prealloc_calloc(n int) &byte { + new_ptr := unsafe { vmemory_block_malloc(n) } + unsafe { C.memset(new_ptr, 0, n) } + return new_ptr +} diff --git a/v_windows/v/vlib/builtin/rune.v b/v_windows/v/vlib/builtin/rune.v new file mode 100644 index 0000000..3364d90 --- /dev/null +++ b/v_windows/v/vlib/builtin/rune.v @@ -0,0 +1,65 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license that can be found in the LICENSE file. +module builtin + +import strings + +// This was never working correctly, the issue is now +// fixed however the type checks in checker need to be +// updated. if you uncomment it you will see the issue +// type rune = int + +pub fn (c rune) str() string { + return utf32_to_str(u32(c)) + /* + unsafe { + fst_byte := int(c)>>8 * 3 & 0xff + len := utf8_char_len(byte(fst_byte)) + println('len=$len') + mut str := string{ + len: len + str: malloc(len + 1) + } + for i in 0..len { + str.str[i] = byte(int(c)>>8 * (3 - i) & 0xff) + } + str.str[len] = `\0` + println(str) + return str + } + */ +} + +// string converts a rune array to a string +[manualfree] +pub fn (ra []rune) string() string { + mut sb := strings.new_builder(ra.len) + sb.write_runes(ra) + res := sb.str() + unsafe { sb.free() } + return res +} + +// Define this on byte as well, so that we can do `s[0].is_capital()` +pub fn (c byte) is_capital() bool { + return c >= `A` && c <= `Z` +} + +pub fn (b []byte) clone() []byte { + mut res := []byte{len: b.len} + // mut res := make([]byte, {repeat:b.len}) + for i in 0 .. b.len { + res[i] = b[i] + } + return res +} + +// TODO: remove this once runes are implemented +pub fn (b []byte) bytestr() string { + unsafe { + buf := malloc_noscan(b.len + 1) + vmemcpy(buf, b.data, b.len) + buf[b.len] = 0 + return tos(buf, b.len) + } +} diff --git a/v_windows/v/vlib/builtin/sorted_map.v b/v_windows/v/vlib/builtin/sorted_map.v new file mode 100644 index 0000000..0f31424 --- /dev/null +++ b/v_windows/v/vlib/builtin/sorted_map.v @@ -0,0 +1,457 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +module builtin + +// import strings + +// B-trees are balanced search trees with all leaves at +// the same level. B-trees are generally faster than +// binary search trees due to the better locality of +// reference, since multiple keys are stored in one node. + +// The number for `degree` has been picked through vigor- +// ous benchmarking but can be changed to any number > 1. +// `degree` determines the maximum length of each node. +const ( + degree = 6 + mid_index = degree - 1 + max_len = 2 * degree - 1 + children_bytes = sizeof(voidptr) * (max_len + 1) +) + +pub struct SortedMap { + value_bytes int +mut: + root &mapnode +pub mut: + len int +} + +struct mapnode { +mut: + children &voidptr + len int + keys [11]string // TODO: Should use `max_len` + values [11]voidptr // TODO: Should use `max_len` +} + +fn new_sorted_map(n int, value_bytes int) SortedMap { // TODO: Remove `n` + return SortedMap{ + value_bytes: value_bytes + root: new_node() + len: 0 + } +} + +fn new_sorted_map_init(n int, value_bytes int, keys &string, values voidptr) SortedMap { + mut out := new_sorted_map(n, value_bytes) + for i in 0 .. n { + unsafe { + out.set(keys[i], &byte(values) + i * value_bytes) + } + } + return out +} + +// The tree is initialized with an empty node as root to +// avoid having to check whether the root is null for +// each insertion. +fn new_node() &mapnode { + return &mapnode{ + children: 0 + len: 0 + } +} + +// This implementation does proactive insertion, meaning +// that splits are done top-down and not bottom-up. +fn (mut m SortedMap) set(key string, value voidptr) { + mut node := m.root + mut child_index := 0 + mut parent := &mapnode(0) + for { + if node.len == max_len { + if isnil(parent) { + parent = new_node() + m.root = parent + } + parent.split_child(child_index, mut node) + if key == parent.keys[child_index] { + unsafe { + vmemcpy(parent.values[child_index], value, m.value_bytes) + } + return + } + if key < parent.keys[child_index] { + node = unsafe { &mapnode(parent.children[child_index]) } + } else { + node = unsafe { &mapnode(parent.children[child_index + 1]) } + } + } + mut i := 0 + for i < node.len && key > node.keys[i] { + i++ + } + if i != node.len && key == node.keys[i] { + unsafe { + vmemcpy(node.values[i], value, m.value_bytes) + } + return + } + if isnil(node.children) { + mut j := node.len - 1 + for j >= 0 && key < node.keys[j] { + node.keys[j + 1] = node.keys[j] + node.values[j + 1] = node.values[j] + j-- + } + node.keys[j + 1] = key + unsafe { + node.values[j + 1] = malloc(m.value_bytes) + vmemcpy(node.values[j + 1], value, m.value_bytes) + } + node.len++ + m.len++ + return + } + parent = node + child_index = i + node = unsafe { &mapnode(node.children[child_index]) } + } +} + +fn (mut n mapnode) split_child(child_index int, mut y mapnode) { + mut z := new_node() + z.len = mid_index + y.len = mid_index + for j := mid_index - 1; j >= 0; j-- { + z.keys[j] = y.keys[j + degree] + z.values[j] = y.values[j + degree] + } + if !isnil(y.children) { + z.children = unsafe { &voidptr(malloc(int(children_bytes))) } + for jj := degree - 1; jj >= 0; jj-- { + unsafe { + z.children[jj] = y.children[jj + degree] + } + } + } + unsafe { + if isnil(n.children) { + n.children = &voidptr(malloc(int(children_bytes))) + } + n.children[n.len + 1] = n.children[n.len] + } + for j := n.len; j > child_index; j-- { + n.keys[j] = n.keys[j - 1] + n.values[j] = n.values[j - 1] + unsafe { + n.children[j] = n.children[j - 1] + } + } + n.keys[child_index] = y.keys[mid_index] + n.values[child_index] = y.values[mid_index] + unsafe { + n.children[child_index] = voidptr(y) + n.children[child_index + 1] = voidptr(z) + } + n.len++ +} + +fn (m SortedMap) get(key string, out voidptr) bool { + mut node := m.root + for { + mut i := node.len - 1 + for i >= 0 && key < node.keys[i] { + i-- + } + if i != -1 && key == node.keys[i] { + unsafe { + vmemcpy(out, node.values[i], m.value_bytes) + } + return true + } + if isnil(node.children) { + break + } + node = unsafe { &mapnode(node.children[i + 1]) } + } + return false +} + +fn (m SortedMap) exists(key string) bool { + if isnil(m.root) { // TODO: find out why root can be nil + return false + } + mut node := m.root + for { + mut i := node.len - 1 + for i >= 0 && key < node.keys[i] { + i-- + } + if i != -1 && key == node.keys[i] { + return true + } + if isnil(node.children) { + break + } + node = unsafe { &mapnode(node.children[i + 1]) } + } + return false +} + +fn (n &mapnode) find_key(k string) int { + mut idx := 0 + for idx < n.len && n.keys[idx] < k { + idx++ + } + return idx +} + +fn (mut n mapnode) remove_key(k string) bool { + idx := n.find_key(k) + if idx < n.len && n.keys[idx] == k { + if isnil(n.children) { + n.remove_from_leaf(idx) + } else { + n.remove_from_non_leaf(idx) + } + return true + } else { + if isnil(n.children) { + return false + } + flag := if idx == n.len { true } else { false } + if unsafe { &mapnode(n.children[idx]) }.len < degree { + n.fill(idx) + } + + mut node := &mapnode(0) + if flag && idx > n.len { + node = unsafe { &mapnode(n.children[idx - 1]) } + } else { + node = unsafe { &mapnode(n.children[idx]) } + } + return node.remove_key(k) + } +} + +fn (mut n mapnode) remove_from_leaf(idx int) { + for i := idx + 1; i < n.len; i++ { + n.keys[i - 1] = n.keys[i] + n.values[i - 1] = n.values[i] + } + n.len-- +} + +fn (mut n mapnode) remove_from_non_leaf(idx int) { + k := n.keys[idx] + if unsafe { &mapnode(n.children[idx]) }.len >= degree { + mut current := unsafe { &mapnode(n.children[idx]) } + for !isnil(current.children) { + current = unsafe { &mapnode(current.children[current.len]) } + } + predecessor := current.keys[current.len - 1] + n.keys[idx] = predecessor + n.values[idx] = current.values[current.len - 1] + mut node := unsafe { &mapnode(n.children[idx]) } + node.remove_key(predecessor) + } else if unsafe { &mapnode(n.children[idx + 1]) }.len >= degree { + mut current := unsafe { &mapnode(n.children[idx + 1]) } + for !isnil(current.children) { + current = unsafe { &mapnode(current.children[0]) } + } + successor := current.keys[0] + n.keys[idx] = successor + n.values[idx] = current.values[0] + mut node := unsafe { &mapnode(n.children[idx + 1]) } + node.remove_key(successor) + } else { + n.merge(idx) + mut node := unsafe { &mapnode(n.children[idx]) } + node.remove_key(k) + } +} + +fn (mut n mapnode) fill(idx int) { + if idx != 0 && unsafe { &mapnode(n.children[idx - 1]) }.len >= degree { + n.borrow_from_prev(idx) + } else if idx != n.len && unsafe { &mapnode(n.children[idx + 1]) }.len >= degree { + n.borrow_from_next(idx) + } else if idx != n.len { + n.merge(idx) + } else { + n.merge(idx - 1) + } +} + +fn (mut n mapnode) borrow_from_prev(idx int) { + mut child := unsafe { &mapnode(n.children[idx]) } + mut sibling := unsafe { &mapnode(n.children[idx - 1]) } + for i := child.len - 1; i >= 0; i-- { + child.keys[i + 1] = child.keys[i] + child.values[i + 1] = child.values[i] + } + if !isnil(child.children) { + for i := child.len; i >= 0; i-- { + unsafe { + child.children[i + 1] = child.children[i] + } + } + } + child.keys[0] = n.keys[idx - 1] + child.values[0] = n.values[idx - 1] + if !isnil(child.children) { + unsafe { + child.children[0] = sibling.children[sibling.len] + } + } + n.keys[idx - 1] = sibling.keys[sibling.len - 1] + n.values[idx - 1] = sibling.values[sibling.len - 1] + child.len++ + sibling.len-- +} + +fn (mut n mapnode) borrow_from_next(idx int) { + mut child := unsafe { &mapnode(n.children[idx]) } + mut sibling := unsafe { &mapnode(n.children[idx + 1]) } + child.keys[child.len] = n.keys[idx] + child.values[child.len] = n.values[idx] + if !isnil(child.children) { + unsafe { + child.children[child.len + 1] = sibling.children[0] + } + } + n.keys[idx] = sibling.keys[0] + n.values[idx] = sibling.values[0] + for i := 1; i < sibling.len; i++ { + sibling.keys[i - 1] = sibling.keys[i] + sibling.values[i - 1] = sibling.values[i] + } + if !isnil(sibling.children) { + for i := 1; i <= sibling.len; i++ { + unsafe { + sibling.children[i - 1] = sibling.children[i] + } + } + } + child.len++ + sibling.len-- +} + +fn (mut n mapnode) merge(idx int) { + mut child := unsafe { &mapnode(n.children[idx]) } + sibling := unsafe { &mapnode(n.children[idx + 1]) } + child.keys[mid_index] = n.keys[idx] + child.values[mid_index] = n.values[idx] + for i in 0 .. sibling.len { + child.keys[i + degree] = sibling.keys[i] + child.values[i + degree] = sibling.values[i] + } + if !isnil(child.children) { + for i := 0; i <= sibling.len; i++ { + unsafe { + child.children[i + degree] = sibling.children[i] + } + } + } + for i := idx + 1; i < n.len; i++ { + n.keys[i - 1] = n.keys[i] + n.values[i - 1] = n.values[i] + } + for i := idx + 2; i <= n.len; i++ { + unsafe { + n.children[i - 1] = n.children[i] + } + } + child.len += sibling.len + 1 + n.len-- + // free(sibling) +} + +pub fn (mut m SortedMap) delete(key string) { + if m.root.len == 0 { + return + } + + removed := m.root.remove_key(key) + if removed { + m.len-- + } + + if m.root.len == 0 { + // tmp := t.root + if isnil(m.root.children) { + return + } else { + m.root = unsafe { &mapnode(m.root.children[0]) } + } + // free(tmp) + } +} + +// Insert all keys of the subtree into array `keys` +// starting at `at`. Keys are inserted in order. +fn (n &mapnode) subkeys(mut keys []string, at int) int { + mut position := at + if !isnil(n.children) { + // Traverse children and insert + // keys inbetween children + for i in 0 .. n.len { + child := unsafe { &mapnode(n.children[i]) } + position += child.subkeys(mut keys, position) + keys[position] = n.keys[i] + position++ + } + // Insert the keys of the last child + child := unsafe { &mapnode(n.children[n.len]) } + position += child.subkeys(mut keys, position) + } else { + // If leaf, insert keys + for i in 0 .. n.len { + keys[position + i] = n.keys[i] + } + position += n.len + } + // Return # of added keys + return position - at +} + +pub fn (m &SortedMap) keys() []string { + mut keys := []string{len: m.len} + if isnil(m.root) || m.root.len == 0 { + return keys + } + m.root.subkeys(mut keys, 0) + return keys +} + +fn (mut n mapnode) free() { + println('TODO') +} + +pub fn (mut m SortedMap) free() { + if isnil(m.root) { + return + } + m.root.free() +} + +pub fn (m SortedMap) print() { + println('TODO') +} + +// pub fn (m map_string) str() string { +// if m.len == 0 { +// return '{}' +// } +// mut sb := strings.new_builder(50) +// sb.writeln('{') +// for key, val in m { +// sb.writeln(' "$key" => "$val"') +// } +// sb.writeln('}') +// return sb.str() +// } diff --git a/v_windows/v/vlib/builtin/sorting_test.v b/v_windows/v/vlib/builtin/sorting_test.v new file mode 100644 index 0000000..4d0ff9f --- /dev/null +++ b/v_windows/v/vlib/builtin/sorting_test.v @@ -0,0 +1,84 @@ +const ( + unsorted = [2, 30, 10, 20, 1] + sorted_asc = [1, 2, 10, 20, 30] + sorted_desc = [30, 20, 10, 2, 1] +) + +fn test_sorting_simple() { + mut a := unsorted.clone() + a.sort() + eprintln(' a: $a') + assert a == sorted_asc +} + +fn test_sorting_with_condition_expression() { + mut a := unsorted.clone() + a.sort(a > b) + eprintln(' a: $a') + assert a == sorted_desc +} + +fn test_sorting_primitives_with_condition_expression() { + mut x := ['9', '87', '3210', '654'] + x.sort(a.len < b.len) + assert x == ['9', '87', '654', '3210'] +} + +// fn get_score(word string) int { +// mut total := 0 +// for letter in word { +// total += int(letter) - 97 +// } +// return total +// } + +// fn test_sorting_with_fn_call_in_condition_expression() { +// mut words := ['aaaa', 'a', 'b', 'foo', 'bar'] +// words.sort(get_score(a) < get_score(b)) +// } + +fn mysort(mut a []int) { + a.sort() +} + +fn test_sorting_by_passing_a_mut_array_to_a_function() { + mut a := unsorted.clone() + mysort(mut a) + eprintln(' a: $a') + assert a == sorted_asc +} + +/* +fn test_sorting_by_passing_an_anonymous_sorting_function() { + mut a := unsorted + a.sort(fn(a &int, b &int) int { return *b - *a }) + eprintln(' a: $a') + assert a == sort_desc +} +*/ +fn test_sorting_u64s() { + mut a := [u64(3), 2, 1, 9, 0, 8] + a.sort() + eprintln(' a: $a') + assert a == [u64(0), 1, 2, 3, 8, 9] + a.sort(a > b) + eprintln(' a: $a') + assert a == [u64(9), 8, 3, 2, 1, 0] +} + +struct User { + age int + name string +} + +fn g(mut users []User) { + users.sort(a.name > b.name) +} + +fn f(mut users []User) { + users.sort(a.name < b.name) +} + +fn z(mut users []User) { + users.sort(a.name < b.name) +} diff --git a/v_windows/v/vlib/builtin/string.v b/v_windows/v/vlib/builtin/string.v new file mode 100644 index 0000000..1a4815d --- /dev/null +++ b/v_windows/v/vlib/builtin/string.v @@ -0,0 +1,1604 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module builtin + +import strconv + +/* +NB: A V string should be/is immutable from the point of view of + V user programs after it is first created. A V string is + also slightly larger than the equivalent C string because + the V string also has an integer length attached. + + This tradeoff is made, since V strings are created just *once*, + but potentially used *many times* over their lifetime. + + The V string implementation uses a struct, that has a .str field, + which points to a C style 0 terminated memory block. Although not + strictly necessary from the V point of view, that additional 0 + is *very useful for C interoperability*. + + The V string implementation also has an integer .len field, + containing the length of the .str field, excluding the + terminating 0 (just like the C's strlen(s) would do). + + The 0 ending of .str, and the .len field, mean that in practice: + a) a V string s can be used very easily, wherever a + C string is needed, just by passing s.str, + without a need for further conversion/copying. + + b) where strlen(s) is needed, you can just pass s.len, + without having to constantly recompute the length of s + *over and over again* like some C programs do. This is because + V strings are immutable and so their length does not change. + + Ordinary V code *does not need* to be concerned with the + additional 0 in the .str field. The 0 *must* be put there by the + low level string creating functions inside this module. + + Failing to do this will lead to programs that work most of the + time, when used with pure V functions, but fail in strange ways, + when used with modules using C functions (for example os and so on). +*/ +pub struct string { +pub: + str &byte = 0 // points to a C style 0 terminated string of bytes. + len int // the length of the .str field, excluding the ending 0 byte. It is always equal to strlen(.str). + // NB string.is_lit is an enumeration of the following: + // .is_lit == 0 => a fresh string, should be freed by autofree + // .is_lit == 1 => a literal string from .rodata, should NOT be freed + // .is_lit == -98761234 => already freed string, protects against double frees. + // ---------> ^^^^^^^^^ calling free on these is a bug. + // Any other value means that the string has been corrupted. +mut: + is_lit int +} + +pub fn (s string) runes() []rune { + mut runes := []rune{cap: s.len} + for i := 0; i < s.len; i++ { + char_len := utf8_char_len(unsafe { s.str[i] }) + if char_len > 1 { + end := if s.len - 1 >= i + char_len { i + char_len } else { s.len } + mut r := unsafe { s[i..end] } + runes << r.utf32_code() + i += char_len - 1 + } else { + runes << unsafe { s.str[i] } + } + } + return runes +} + +// tos converts a C string to a V string. +// String data is reused, not copied. +[unsafe] +pub fn tos(s &byte, len int) string { + // This should never happen. + if s == 0 { + panic('tos(): nil string') + } + return string{ + str: unsafe { s } + len: len + } +} + +// tos_clone returns a copy of `s`. +[unsafe] +pub fn tos_clone(s &byte) string { + return unsafe { tos2(s) }.clone() +} + +// tos2 does the same as `tos`, but also calculates the length. Called by `string(bytes)` casts. +// Used only internally. +[unsafe] +pub fn tos2(s &byte) string { + if s == 0 { + panic('tos2: nil string') + } + return string{ + str: unsafe { s } + len: unsafe { vstrlen(s) } + } +} + +// tos3 does the same as `tos2`, but for char*, to avoid warnings. +[unsafe] +pub fn tos3(s &char) string { + if s == 0 { + panic('tos3: nil string') + } + return string{ + str: &byte(s) + len: unsafe { vstrlen_char(s) } + } +} + +// tos4 does the same as `tos2`, but returns an empty string on nil ptr. +[unsafe] +pub fn tos4(s &byte) string { + if s == 0 { + return '' + } + return unsafe { tos2(s) } +} + +// tos5 does the same as `tos4`, but for char*, to avoid warnings. +[unsafe] +pub fn tos5(s &char) string { + if s == 0 { + return '' + } + return unsafe { tos3(s) } +} + +// vstring converts a C style string to a V string. NB: the string data is reused, NOT copied. +// strings returned from this function will be normal V strings beside that (i.e. they would be +// freed by V's -autofree mechanism, when they are no longer used). +[unsafe] +pub fn (bp &byte) vstring() string { + return string{ + str: unsafe { bp } + len: unsafe { vstrlen(bp) } + } +} + +// vstring_with_len converts a C style string to a V string. +// NB: the string data is reused, NOT copied. +[unsafe] +pub fn (bp &byte) vstring_with_len(len int) string { + return string{ + str: unsafe { bp } + len: len + is_lit: 0 + } +} + +// vstring converts C char* to V string. +// NB: the string data is reused, NOT copied. +[unsafe] +pub fn (cp &char) vstring() string { + return string{ + str: &byte(cp) + len: unsafe { vstrlen_char(cp) } + is_lit: 0 + } +} + +// vstring_with_len converts C char* to V string. +// NB: the string data is reused, NOT copied. +[unsafe] +pub fn (cp &char) vstring_with_len(len int) string { + return string{ + str: &byte(cp) + len: len + is_lit: 0 + } +} + +// vstring_literal converts a C style string to a V string. +// NB: the string data is reused, NOT copied. +// NB2: unlike vstring, vstring_literal will mark the string +// as a literal, so it will not be freed by autofree. +// This is suitable for readonly strings, C string literals etc, +// that can be read by the V program, but that should not be +// managed by it, for example `os.args` is implemented using it. +[unsafe] +pub fn (bp &byte) vstring_literal() string { + return string{ + str: unsafe { bp } + len: unsafe { vstrlen(bp) } + is_lit: 1 + } +} + +// vstring_with_len converts a C style string to a V string. +// NB: the string data is reused, NOT copied. +[unsafe] +pub fn (bp &byte) vstring_literal_with_len(len int) string { + return string{ + str: unsafe { bp } + len: len + is_lit: 1 + } +} + +// vstring_literal converts C char* to V string. +// See also vstring_literal defined on byteptr for more details. +// NB: the string data is reused, NOT copied. +[unsafe] +pub fn (cp &char) vstring_literal() string { + return string{ + str: &byte(cp) + len: unsafe { vstrlen_char(cp) } + is_lit: 1 + } +} + +// vstring_literal_with_len converts C char* to V string. +// See also vstring_literal_with_len defined on byteptr. +// NB: the string data is reused, NOT copied. +[unsafe] +pub fn (cp &char) vstring_literal_with_len(len int) string { + return string{ + str: &byte(cp) + len: len + is_lit: 1 + } +} + +// clone_static returns an independent copy of a given array. +// It should be used only in -autofree generated code. +fn (a string) clone_static() string { + return a.clone() +} + +// clone returns a copy of the V string `a`. +pub fn (a string) clone() string { + if a.len == 0 { + return '' + } + mut b := string{ + str: unsafe { malloc_noscan(a.len + 1) } + len: a.len + } + unsafe { + vmemcpy(b.str, a.str, a.len) + b.str[a.len] = 0 + } + return b +} + +// cstring_to_vstring creates a copy of cstr and turns it into a v string. +[unsafe] +pub fn cstring_to_vstring(cstr &char) string { + return unsafe { tos_clone(&byte(cstr)) } +} + +// replace_once replaces the first occurence of `rep` with the string passed in `with`. +pub fn (s string) replace_once(rep string, with string) string { + idx := s.index_(rep) + if idx == -1 { + return s.clone() + } + return s.substr(0, idx) + with + s.substr(idx + rep.len, s.len) +} + +// replace replaces all occurences of `rep` with the string passed in `with`. +[direct_array_access] +pub fn (s string) replace(rep string, with string) string { + if s.len == 0 || rep.len == 0 || rep.len > s.len { + return s.clone() + } + if !s.contains(rep) { + return s.clone() + } + // TODO PERF Allocating ints is expensive. Should be a stack array + // Get locations of all reps within this string + mut idxs := []int{cap: s.len / rep.len} + defer { + unsafe { idxs.free() } + } + mut idx := 0 + for { + idx = s.index_after(rep, idx) + if idx == -1 { + break + } + idxs << idx + idx += rep.len + } + // Dont change the string if there's nothing to replace + if idxs.len == 0 { + return s.clone() + } + // Now we know the number of replacements we need to do and we can calc the len of the new string + new_len := s.len + idxs.len * (with.len - rep.len) + mut b := unsafe { malloc_noscan(new_len + 1) } // add space for the null byte at the end + // Fill the new string + mut b_i := 0 + mut s_idx := 0 + for _, rep_pos in idxs { + for i in s_idx .. rep_pos { // copy everything up to piece being replaced + unsafe { + b[b_i] = s[i] + } + b_i++ + } + s_idx = rep_pos + rep.len // move string index past replacement + for i in 0 .. with.len { // copy replacement piece + unsafe { + b[b_i] = with[i] + } + b_i++ + } + } + if s_idx < s.len { // if any original after last replacement, copy it + for i in s_idx .. s.len { + unsafe { + b[b_i] = s[i] + } + b_i++ + } + } + unsafe { + b[new_len] = 0 + return tos(b, new_len) + } +} + +struct RepIndex { + idx int + val_idx int +} + +// replace_each replaces all occurences of the string pairs given in `vals`. +// Example: assert 'ABCD'.replace_each(['B','C/','C','D','D','C']) == 'AC/DC' +[direct_array_access] +pub fn (s string) replace_each(vals []string) string { + if s.len == 0 || vals.len == 0 { + return s.clone() + } + if vals.len % 2 != 0 { + eprintln('string.replace_each(): odd number of strings') + return s.clone() + } + // `rep` - string to replace + // `with` - string to replace with + // Remember positions of all rep strings, and calculate the length + // of the new string to do just one allocation. + mut new_len := s.len + mut idxs := []RepIndex{} + mut idx := 0 + s_ := s.clone() + for rep_i := 0; rep_i < vals.len; rep_i += 2 { + // vals: ['rep1, 'with1', 'rep2', 'with2'] + rep := vals[rep_i] + with := vals[rep_i + 1] + + for { + idx = s_.index_after(rep, idx) + if idx == -1 { + break + } + // The string already found is set to `/del`, to avoid duplicate searches. + for i in 0 .. rep.len { + unsafe { + s_.str[idx + i] = 127 + } + } + // We need to remember both the position in the string, + // and which rep/with pair it refers to. + + idxs << RepIndex{ + idx: idx + val_idx: rep_i + } + + idx += rep.len + new_len += with.len - rep.len + } + } + + // Dont change the string if there's nothing to replace + if idxs.len == 0 { + return s.clone() + } + idxs.sort(a.idx < b.idx) + mut b := unsafe { malloc_noscan(new_len + 1) } // add space for 0 terminator + // Fill the new string + mut idx_pos := 0 + mut cur_idx := idxs[idx_pos] + mut b_i := 0 + for i := 0; i < s.len; i++ { + if i == cur_idx.idx { + // Reached the location of rep, replace it with "with" + rep := vals[cur_idx.val_idx] + with := vals[cur_idx.val_idx + 1] + for j in 0 .. with.len { + unsafe { + b[b_i] = with[j] + } + b_i++ + } + // Skip the length of rep, since we just replaced it with "with" + i += rep.len - 1 + // Go to the next index + idx_pos++ + if idx_pos < idxs.len { + cur_idx = idxs[idx_pos] + } + } else { + // Rep doesnt start here, just copy + unsafe { + b[b_i] = s.str[i] + } + b_i++ + } + } + unsafe { + b[new_len] = 0 + return tos(b, new_len) + } +} + +// bool returns `true` if the string equals the word "true" it will return `false` otherwise. +pub fn (s string) bool() bool { + return s == 'true' || s == 't' // TODO t for pg, remove +} + +// int returns the value of the string as an integer `'1'.int() == 1`. +pub fn (s string) int() int { + return int(strconv.common_parse_int(s, 0, 32, false, false) or { 0 }) +} + +// i64 returns the value of the string as i64 `'1'.i64() == i64(1)`. +pub fn (s string) i64() i64 { + return strconv.common_parse_int(s, 0, 64, false, false) or { 0 } +} + +// i8 returns the value of the string as i8 `'1'.i8() == i8(1)`. +pub fn (s string) i8() i8 { + return i8(strconv.common_parse_int(s, 0, 8, false, false) or { 0 }) +} + +// i16 returns the value of the string as i16 `'1'.i16() == i16(1)`. +pub fn (s string) i16() i16 { + return i16(strconv.common_parse_int(s, 0, 16, false, false) or { 0 }) +} + +// f32 returns the value of the string as f32 `'1.0'.f32() == f32(1)`. +pub fn (s string) f32() f32 { + return f32(strconv.atof64(s)) +} + +// f64 returns the value of the string as f64 `'1.0'.f64() == f64(1)`. +pub fn (s string) f64() f64 { + return strconv.atof64(s) +} + +// u16 returns the value of the string as u16 `'1'.u16() == u16(1)`. +pub fn (s string) u16() u16 { + return u16(strconv.common_parse_uint(s, 0, 16, false, false) or { 0 }) +} + +// u32 returns the value of the string as u32 `'1'.u32() == u32(1)`. +pub fn (s string) u32() u32 { + return u32(strconv.common_parse_uint(s, 0, 32, false, false) or { 0 }) +} + +// u64 returns the value of the string as u64 `'1'.u64() == u64(1)`. +pub fn (s string) u64() u64 { + return strconv.common_parse_uint(s, 0, 64, false, false) or { 0 } +} + +[direct_array_access] +fn (s string) == (a string) bool { + if s.str == 0 { + // should never happen + panic('string.eq(): nil string') + } + if s.len != a.len { + return false + } + if s.len > 0 { + last_idx := s.len - 1 + if s[last_idx] != a[last_idx] { + return false + } + } + unsafe { + return vmemcmp(s.str, a.str, a.len) == 0 + } +} + +fn (s string) < (a string) bool { + for i in 0 .. s.len { + if i >= a.len || s[i] > a[i] { + return false + } else if s[i] < a[i] { + return true + } + } + if s.len < a.len { + return true + } + return false +} + +fn (s string) + (a string) string { + new_len := a.len + s.len + mut res := string{ + str: unsafe { malloc_noscan(new_len + 1) } + len: new_len + } + for j in 0 .. s.len { + unsafe { + res.str[j] = s.str[j] + } + } + for j in 0 .. a.len { + unsafe { + res.str[s.len + j] = a.str[j] + } + } + unsafe { + res.str[new_len] = 0 // V strings are not null terminated, but just in case + } + return res +} + +// split splits the string to an array by `delim`. +// Example: assert 'A B C'.split(' ') == ['A','B','C'] +// If `delim` is empty the string is split by it's characters. +// Example: assert 'DEF'.split('') == ['D','E','F'] +pub fn (s string) split(delim string) []string { + return s.split_nth(delim, 0) +} + +// split_nth splits the string based on the passed `delim` substring. +// It returns the first Nth parts. When N=0, return all the splits. +// The last returned element has the remainder of the string, even if +// the remainder contains more `delim` substrings. +[direct_array_access] +pub fn (s string) split_nth(delim string, nth int) []string { + mut res := []string{} + mut i := 0 + + match delim.len { + 0 { + i = 1 + for ch in s { + if nth > 0 && i >= nth { + res << s[i..] + break + } + res << ch.ascii_str() + i++ + } + return res + } + 1 { + mut start := 0 + delim_byte := delim[0] + + for i < s.len { + if s[i] == delim_byte { + was_last := nth > 0 && res.len == nth - 1 + if was_last { + break + } + val := s.substr(start, i) + res << val + start = i + delim.len + i = start + } else { + i++ + } + } + + // Then the remaining right part of the string + if nth < 1 || res.len < nth { + res << s[start..] + } + return res + } + else { + mut start := 0 + // Take the left part for each delimiter occurence + for i <= s.len { + is_delim := i + delim.len <= s.len && s.substr(i, i + delim.len) == delim + if is_delim { + was_last := nth > 0 && res.len == nth - 1 + if was_last { + break + } + val := s.substr(start, i) + res << val + start = i + delim.len + i = start + } else { + i++ + } + } + // Then the remaining right part of the string + if nth < 1 || res.len < nth { + res << s[start..] + } + return res + } + } +} + +// split_into_lines splits the string by newline characters. +// newlines are stripped. +// Both `\n` and `\r\n` newline endings are supported. +[direct_array_access] +pub fn (s string) split_into_lines() []string { + mut res := []string{} + if s.len == 0 { + return res + } + mut start := 0 + mut end := 0 + for i := 0; i < s.len; i++ { + if s[i] == 10 { + end = if i > 0 && s[i - 1] == 13 { i - 1 } else { i } + res << if start == end { '' } else { s[start..end] } + start = i + 1 + } + } + if start < s.len { + res << s[start..] + } + return res +} + +// used internally for [2..4] +fn (s string) substr2(start int, _end int, end_max bool) string { + end := if end_max { s.len } else { _end } + return s.substr(start, end) +} + +// substr returns the string between index positions `start` and `end`. +// Example: assert 'ABCD'.substr(1,3) == 'BC' +pub fn (s string) substr(start int, end int) string { + $if !no_bounds_checking ? { + if start > end || start > s.len || end > s.len || start < 0 || end < 0 { + panic('substr($start, $end) out of bounds (len=$s.len)') + } + } + len := end - start + if len == s.len { + return s.clone() + } + mut res := string{ + str: unsafe { malloc_noscan(len + 1) } + len: len + } + for i in 0 .. len { + unsafe { + res.str[i] = s.str[start + i] + } + } + unsafe { + res.str[len] = 0 + } + return res +} + +// index returns the position of the first character of the input string. +// It will return `-1` if the input string can't be found. +fn (s string) index_(p string) int { + if p.len > s.len || p.len == 0 { + return -1 + } + if p.len > 2 { + return s.index_kmp(p) + } + mut i := 0 + for i < s.len { + mut j := 0 + for j < p.len && unsafe { s.str[i + j] == p.str[j] } { + j++ + } + if j == p.len { + return i + } + i++ + } + return -1 +} + +// index returns the position of the first character of the input string. +// It will return `none` if the input string can't be found. +pub fn (s string) index(p string) ?int { + idx := s.index_(p) + if idx == -1 { + return none + } + return idx +} + +// index_kmp does KMP search. +[direct_array_access; manualfree] +fn (s string) index_kmp(p string) int { + if p.len > s.len { + return -1 + } + mut prefix := []int{len: p.len} + defer { + unsafe { prefix.free() } + } + mut j := 0 + for i := 1; i < p.len; i++ { + for unsafe { p.str[j] != p.str[i] } && j > 0 { + j = prefix[j - 1] + } + if unsafe { p.str[j] == p.str[i] } { + j++ + } + prefix[i] = j + } + j = 0 + for i in 0 .. s.len { + for unsafe { p.str[j] != s.str[i] } && j > 0 { + j = prefix[j - 1] + } + if unsafe { p.str[j] == s.str[i] } { + j++ + } + if j == p.len { + return i - p.len + 1 + } + } + return -1 +} + +// index_any returns the position of any of the characters in the input string - if found. +pub fn (s string) index_any(chars string) int { + for c in chars { + idx := s.index_(c.ascii_str()) + if idx == -1 { + continue + } + return idx + } + return -1 +} + +// last_index returns the position of the last occurence of the input string. +fn (s string) last_index_(p string) int { + if p.len > s.len || p.len == 0 { + return -1 + } + mut i := s.len - p.len + for i >= 0 { + mut j := 0 + for j < p.len && unsafe { s.str[i + j] == p.str[j] } { + j++ + } + if j == p.len { + return i + } + i-- + } + return -1 +} + +// last_index returns the position of the last occurence of the input string. +pub fn (s string) last_index(p string) ?int { + idx := s.last_index_(p) + if idx == -1 { + return none + } + return idx +} + +// index_after returns the position of the input string, starting search from `start` position. +pub fn (s string) index_after(p string, start int) int { + if p.len > s.len { + return -1 + } + mut strt := start + if start < 0 { + strt = 0 + } + if start >= s.len { + return -1 + } + mut i := strt + for i < s.len { + mut j := 0 + mut ii := i + for j < p.len && unsafe { s.str[ii] == p.str[j] } { + j++ + ii++ + } + if j == p.len { + return i + } + i++ + } + return -1 +} + +// index_byte returns the index of byte `c` if found in the string. +// index_byte returns -1 if the byte can not be found. +pub fn (s string) index_byte(c byte) int { + for i in 0 .. s.len { + if unsafe { s.str[i] } == c { + return i + } + } + return -1 +} + +// last_index_byte returns the index of the last occurence of byte `c` if found in the string. +// last_index_byte returns -1 if the byte is not found. +pub fn (s string) last_index_byte(c byte) int { + for i := s.len - 1; i >= 0; i-- { + if unsafe { s.str[i] == c } { + return i + } + } + return -1 +} + +// count returns the number of occurrences of `substr` in the string. +// count returns -1 if no `substr` could be found. +pub fn (s string) count(substr string) int { + if s.len == 0 || substr.len == 0 { + return 0 + } + if substr.len > s.len { + return 0 + } + + mut n := 0 + + if substr.len == 1 { + target := substr[0] + + for letter in s { + if letter == target { + n++ + } + } + + return n + } + + mut i := 0 + for { + i = s.index_after(substr, i) + if i == -1 { + return n + } + i += substr.len + n++ + } + return 0 // TODO can never get here - v doesn't know that +} + +// contains returns `true` if the string contains `substr`. +pub fn (s string) contains(substr string) bool { + if substr.len == 0 { + return true + } + if s.index_(substr) == -1 { + return false + } + return true +} + +// contains_any returns `true` if the string contains any chars in `chars`. +pub fn (s string) contains_any(chars string) bool { + for c in chars { + if s.contains(c.ascii_str()) { + return true + } + } + return false +} + +// contains_any_substr returns `true` if the string contains any of the strings in `substrs`. +pub fn (s string) contains_any_substr(substrs []string) bool { + if substrs.len == 0 { + return true + } + for sub in substrs { + if s.contains(sub) { + return true + } + } + return false +} + +// starts_with returns `true` if the string starts with `p`. +pub fn (s string) starts_with(p string) bool { + if p.len > s.len { + return false + } + for i in 0 .. p.len { + if unsafe { s.str[i] != p.str[i] } { + return false + } + } + return true +} + +// ends_with returns `true` if the string ends with `p`. +pub fn (s string) ends_with(p string) bool { + if p.len > s.len { + return false + } + for i in 0 .. p.len { + if unsafe { p.str[i] != s.str[s.len - p.len + i] } { + return false + } + } + return true +} + +// to_lower returns the string in all lowercase characters. +// TODO only works with ASCII +pub fn (s string) to_lower() string { + unsafe { + mut b := malloc_noscan(s.len + 1) + for i in 0 .. s.len { + if s.str[i] >= `A` && s.str[i] <= `Z` { + b[i] = s.str[i] + 32 + } else { + b[i] = s.str[i] + } + } + b[s.len] = 0 + return tos(b, s.len) + } +} + +// is_lower returns `true` if all characters in the string is lowercase. +// Example: assert 'hello developer'.is_lower() == true +[direct_array_access] +pub fn (s string) is_lower() bool { + for i in 0 .. s.len { + if s[i] >= `A` && s[i] <= `Z` { + return false + } + } + return true +} + +// to_upper returns the string in all uppercase characters. +// Example: assert 'Hello V'.to_upper() == 'HELLO V' +pub fn (s string) to_upper() string { + unsafe { + mut b := malloc_noscan(s.len + 1) + for i in 0 .. s.len { + if s.str[i] >= `a` && s.str[i] <= `z` { + b[i] = s.str[i] - 32 + } else { + b[i] = s.str[i] + } + } + b[s.len] = 0 + return tos(b, s.len) + } +} + +// is_upper returns `true` if all characters in the string is uppercase. +// Example: assert 'HELLO V'.is_upper() == true +[direct_array_access] +pub fn (s string) is_upper() bool { + for i in 0 .. s.len { + if s[i] >= `a` && s[i] <= `z` { + return false + } + } + return true +} + +// capitalize returns the string with the first character capitalized. +// Example: assert 'hello'.capitalize() == 'Hello' +[direct_array_access] +pub fn (s string) capitalize() string { + if s.len == 0 { + return '' + } + s0 := s[0] + letter := s0.ascii_str() + uletter := letter.to_upper() + if s.len == 1 { + return uletter + } + srest := s[1..] + res := uletter + srest + return res +} + +// is_capital returns `true` if the first character in the string is a capital letter. +// Example: assert 'Hello'.is_capital() == true +[direct_array_access] +pub fn (s string) is_capital() bool { + if s.len == 0 || !(s[0] >= `A` && s[0] <= `Z`) { + return false + } + for i in 1 .. s.len { + if s[i] >= `A` && s[i] <= `Z` { + return false + } + } + return true +} + +// title returns the string with each word capitalized. +// Example: assert 'hello v developer'.title() == 'Hello V Developer' +pub fn (s string) title() string { + words := s.split(' ') + mut tit := []string{} + for word in words { + tit << word.capitalize() + } + title := tit.join(' ') + return title +} + +// is_title returns true if all words of the string is capitalized. +// Example: assert 'Hello V Developer'.is_title() == true +pub fn (s string) is_title() bool { + words := s.split(' ') + for word in words { + if !word.is_capital() { + return false + } + } + return true +} + +// find_between returns the string found between `start` string and `end` string. +// Example: assert 'hey [man] how you doin'.find_between('[', ']') == 'man' +pub fn (s string) find_between(start string, end string) string { + start_pos := s.index_(start) + if start_pos == -1 { + return '' + } + // First get everything to the right of 'start' + val := s[start_pos + start.len..] + end_pos := val.index_(end) + if end_pos == -1 { + return val + } + return val[..end_pos] +} + +// trim_space strips any of ` `, `\n`, `\t`, `\v`, `\f`, `\r` from the start and end of the string. +// Example: assert ' Hello V '.trim_space() == 'Hello V' +pub fn (s string) trim_space() string { + return s.trim(' \n\t\v\f\r') +} + +// trim strips any of the characters given in `cutset` from the start and end of the string. +// Example: assert ' ffHello V ffff'.trim(' f') == 'Hello V' +[direct_array_access] +pub fn (s string) trim(cutset string) string { + if s.len < 1 || cutset.len < 1 { + return s.clone() + } + mut pos_left := 0 + mut pos_right := s.len - 1 + mut cs_match := true + for pos_left <= s.len && pos_right >= -1 && cs_match { + cs_match = false + for cs in cutset { + if s[pos_left] == cs { + pos_left++ + cs_match = true + break + } + } + for cs in cutset { + if s[pos_right] == cs { + pos_right-- + cs_match = true + break + } + } + if pos_left > pos_right { + return '' + } + } + return s.substr(pos_left, pos_right + 1) +} + +// trim_left strips any of the characters given in `cutset` from the left of the string. +// Example: assert 'd Hello V developer'.trim_left(' d') == 'Hello V developer' +[direct_array_access] +pub fn (s string) trim_left(cutset string) string { + if s.len < 1 || cutset.len < 1 { + return s.clone() + } + mut pos := 0 + for pos < s.len { + mut found := false + for cs in cutset { + if s[pos] == cs { + found = true + break + } + } + if !found { + break + } + pos++ + } + return s[pos..] +} + +// trim_right strips any of the characters given in `cutset` from the right of the string. +// Example: assert ' Hello V d'.trim_right(' d') == ' Hello V' +[direct_array_access] +pub fn (s string) trim_right(cutset string) string { + if s.len < 1 || cutset.len < 1 { + return s.clone() + } + mut pos := s.len - 1 + for pos >= 0 { + mut found := false + for cs in cutset { + if s[pos] == cs { + found = true + } + } + if !found { + break + } + pos-- + } + if pos < 0 { + return '' + } + return s[..pos + 1] +} + +// trim_prefix strips `str` from the start of the string. +// Example: assert 'WorldHello V'.trim_prefix('World') == 'Hello V' +pub fn (s string) trim_prefix(str string) string { + if s.starts_with(str) { + return s[str.len..] + } + return s.clone() +} + +// trim_suffix strips `str` from the end of the string. +// Example: assert 'Hello VWorld'.trim_suffix('World') == 'Hello V' +pub fn (s string) trim_suffix(str string) string { + if s.ends_with(str) { + return s[..s.len - str.len] + } + return s.clone() +} + +// compare_strings returns `-1` if `a < b`, `1` if `a > b` else `0`. +pub fn compare_strings(a &string, b &string) int { + if a < b { + return -1 + } + if a > b { + return 1 + } + return 0 +} + +// compare_strings_by_len returns `-1` if `a.len < b.len`, `1` if `a.len > b.len` else `0`. +fn compare_strings_by_len(a &string, b &string) int { + if a.len < b.len { + return -1 + } + if a.len > b.len { + return 1 + } + return 0 +} + +// compare_lower_strings returns the same as compare_strings but converts `a` and `b` to lower case before comparing. +fn compare_lower_strings(a &string, b &string) int { + aa := a.to_lower() + bb := b.to_lower() + return compare_strings(&aa, &bb) +} + +// sort_ignore_case sorts the string array using case insesitive comparing. +pub fn (mut s []string) sort_ignore_case() { + s.sort_with_compare(compare_lower_strings) +} + +// sort_by_len sorts the the string array by each string's `.len` length. +pub fn (mut s []string) sort_by_len() { + s.sort_with_compare(compare_strings_by_len) +} + +// str returns a copy of the string +pub fn (s string) str() string { + return s.clone() +} + +// at returns the byte at index `idx`. +// Example: assert 'ABC'.at(1) == byte(`B`) +fn (s string) at(idx int) byte { + $if !no_bounds_checking ? { + if idx < 0 || idx >= s.len { + panic('string index out of range: $idx / $s.len') + } + } + unsafe { + return s.str[idx] + } +} + +// version of `at()` that is used in `a[i] or {` +// return an error when the index is out of range +fn (s string) at_with_check(idx int) ?byte { + if idx < 0 || idx >= s.len { + return error('string index out of range') + } + unsafe { + return s.str[idx] + } +} + +// is_space returns `true` if the byte is a white space character. +// The following list is considered white space characters: ` `, `\t`, `\n`, `\v`, `\f`, `\r`, 0x85, 0xa0 +// Example: assert byte(` `).is_space() == true +[inline] +pub fn (c byte) is_space() bool { + // 0x85 is NEXT LINE (NEL) + // 0xa0 is NO-BREAK SPACE + return c == 32 || (c > 8 && c < 14) || (c == 0x85) || (c == 0xa0) +} + +// is_digit returns `true` if the byte is in range 0-9 and `false` otherwise. +// Example: assert byte(`9`) == true +[inline] +pub fn (c byte) is_digit() bool { + return c >= `0` && c <= `9` +} + +// is_hex_digit returns `true` if the byte is either in range 0-9, a-f or A-F and `false` otherwise. +// Example: assert byte(`F`) == true +[inline] +pub fn (c byte) is_hex_digit() bool { + return c.is_digit() || (c >= `a` && c <= `f`) || (c >= `A` && c <= `F`) +} + +// is_oct_digit returns `true` if the byte is in range 0-7 and `false` otherwise. +// Example: assert byte(`7`) == true +[inline] +pub fn (c byte) is_oct_digit() bool { + return c >= `0` && c <= `7` +} + +// is_bin_digit returns `true` if the byte is a binary digit (0 or 1) and `false` otherwise. +// Example: assert byte(`0`) == true +[inline] +pub fn (c byte) is_bin_digit() bool { + return c == `0` || c == `1` +} + +// is_letter returns `true` if the byte is in range a-z or A-Z and `false` otherwise. +// Example: assert byte(`V`) == true +[inline] +pub fn (c byte) is_letter() bool { + return (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`) +} + +// free allows for manually freeing the memory occupied by the string +[manualfree; unsafe] +pub fn (s &string) free() { + $if prealloc { + return + } + if s.is_lit == -98761234 { + double_free_msg := unsafe { &byte(c'double string.free() detected\n') } + double_free_msg_len := unsafe { vstrlen(double_free_msg) } + $if freestanding { + bare_eprint(double_free_msg, u64(double_free_msg_len)) + } $else { + _write_buf_to_fd(1, double_free_msg, double_free_msg_len) + } + return + } + if s.is_lit == 1 || s.str == 0 { + return + } + unsafe { + free(s.str) + } + s.is_lit = -98761234 +} + +// before returns the contents before `sub` in the string. +// If the substring is not found, it returns the full input string. +// Example: assert '23:34:45.234'.before('.') == '23:34:45' +// Example: assert 'abcd'.before('.') == 'abcd' +// TODO: deprecate and remove either .before or .all_before +pub fn (s string) before(sub string) string { + pos := s.index_(sub) + if pos == -1 { + return s.clone() + } + return s[..pos] +} + +// all_before returns the contents before `sub` in the string. +// If the substring is not found, it returns the full input string. +// Example: assert '23:34:45.234'.all_before('.') == '23:34:45' +// Example: assert 'abcd'.all_before('.') == 'abcd' +pub fn (s string) all_before(sub string) string { + // TODO remove dup method + pos := s.index_(sub) + if pos == -1 { + return s.clone() + } + return s[..pos] +} + +// all_before_last returns the contents before the last occurence of `sub` in the string. +// If the substring is not found, it returns the full input string. +// Example: assert '23:34:45.234'.all_before_last(':') == '23:34' +// Example: assert 'abcd'.all_before_last('.') == 'abcd' +pub fn (s string) all_before_last(sub string) string { + pos := s.last_index_(sub) + if pos == -1 { + return s.clone() + } + return s[..pos] +} + +// all_after returns the contents after `sub` in the string. +// If the substring is not found, it returns the full input string. +// Example: assert '23:34:45.234'.all_after('.') == '234' +// Example: assert 'abcd'.all_after('z') == 'abcd' +pub fn (s string) all_after(sub string) string { + pos := s.index_(sub) + if pos == -1 { + return s.clone() + } + return s[pos + sub.len..] +} + +// all_after_last returns the contents after the last occurence of `sub` in the string. +// If the substring is not found, it returns the full input string. +// Example: assert '23:34:45.234'.all_after_last(':') == '45.234' +// Example: assert 'abcd'.all_after_last('z') == 'abcd' +pub fn (s string) all_after_last(sub string) string { + pos := s.last_index_(sub) + if pos == -1 { + return s.clone() + } + return s[pos + sub.len..] +} + +// after returns the contents after the last occurence of `sub` in the string. +// If the substring is not found, it returns the full input string. +// Example: assert '23:34:45.234'.after(':') == '45.234' +// Example: assert 'abcd'.after('z') == 'abcd' +// TODO: deprecate either .all_after_last or .after +pub fn (s string) after(sub string) string { + return s.all_after_last(sub) +} + +// after_char returns the contents after the first occurence of `sub` character in the string. +// If the substring is not found, it returns the full input string. +// Example: assert '23:34:45.234'.after_char(`:`) == '34:45.234' +// Example: assert 'abcd'.after_char(`:`) == 'abcd' +pub fn (s string) after_char(sub byte) string { + mut pos := -1 + for i, c in s { + if c == sub { + pos = i + break + } + } + if pos == -1 { + return s.clone() + } + return s[pos + 1..] +} + +// join joins a string array into a string using `sep` separator. +// Example: assert ['Hello','V'].join(' ') == 'Hello V' +pub fn (a []string) join(sep string) string { + if a.len == 0 { + return '' + } + mut len := 0 + for val in a { + len += val.len + sep.len + } + len -= sep.len + // Allocate enough memory + mut res := string{ + str: unsafe { malloc_noscan(len + 1) } + len: len + } + mut idx := 0 + for i, val in a { + unsafe { + vmemcpy(res.str + idx, val.str, val.len) + idx += val.len + } + // Add sep if it's not last + if i != a.len - 1 { + unsafe { + vmemcpy(res.str + idx, sep.str, sep.len) + idx += sep.len + } + } + } + unsafe { + res.str[res.len] = 0 + } + return res +} + +// join joins a string array into a string using a `\n` newline delimiter. +pub fn (s []string) join_lines() string { + return s.join('\n') +} + +// reverse returns a reversed string. +// Example: assert 'Hello V'.reverse() == 'V olleH' +pub fn (s string) reverse() string { + if s.len == 0 || s.len == 1 { + return s.clone() + } + mut res := string{ + str: unsafe { malloc_noscan(s.len + 1) } + len: s.len + } + for i := s.len - 1; i >= 0; i-- { + unsafe { + res.str[s.len - i - 1] = s[i] + } + } + unsafe { + res.str[res.len] = 0 + } + return res +} + +// limit returns a portion of the string, starting at `0` and extending for a given number of characters afterward. +// 'hello'.limit(2) => 'he' +// 'hi'.limit(10) => 'hi' +pub fn (s string) limit(max int) string { + u := s.runes() + if u.len <= max { + return s.clone() + } + return u[0..max].string() +} + +// hash returns an integer hash of the string. +pub fn (s string) hash() int { + mut h := u32(0) + if h == 0 && s.len > 0 { + for c in s { + h = h * 31 + u32(c) + } + } + return int(h) +} + +// bytes returns the string converted to a byte array. +pub fn (s string) bytes() []byte { + if s.len == 0 { + return [] + } + mut buf := []byte{len: s.len} + unsafe { vmemcpy(buf.data, s.str, s.len) } + return buf +} + +// repeat returns a new string with `count` number of copies of the string it was called on. +pub fn (s string) repeat(count int) string { + if count < 0 { + panic('string.repeat: count is negative: $count') + } else if count == 0 { + return '' + } else if count == 1 { + return s.clone() + } + mut ret := unsafe { malloc_noscan(s.len * count + 1) } + for i in 0 .. count { + for j in 0 .. s.len { + unsafe { + ret[i * s.len + j] = s[j] + } + } + } + new_len := s.len * count + unsafe { + ret[new_len] = 0 + } + return unsafe { ret.vstring_with_len(new_len) } +} + +// fields returns a string array of the string split by `\t` and ` ` +// Example: assert '\t\tv = v'.fields() == ['v', '=', 'v'] +// Example: assert ' sss ssss'.fields() == ['sss', 'ssss'] +pub fn (s string) fields() []string { + mut res := []string{} + mut word_start := 0 + mut word_len := 0 + mut is_in_word := false + mut is_space := false + for i, c in s { + is_space = c in [32, 9, 10] + if !is_space { + word_len++ + } + if !is_in_word && !is_space { + word_start = i + is_in_word = true + continue + } + if is_space && is_in_word { + res << s[word_start..word_start + word_len] + is_in_word = false + word_len = 0 + word_start = 0 + continue + } + } + if is_in_word && word_len > 0 { + // collect the remainder word at the end + res << s[word_start..s.len] + } + return res +} + +// strip_margin allows multi-line strings to be formatted in a way that removes white-space +// before a delimeter. by default `|` is used. +// Note: the delimiter has to be a byte at this time. That means surrounding +// the value in ``. +// +// Example: +// st := 'Hello there, +// |this is a string, +// | Everything before the first | is removed'.strip_margin() +// Returns: +// Hello there, +// this is a string, +// Everything before the first | is removed +pub fn (s string) strip_margin() string { + return s.strip_margin_custom(`|`) +} + +// strip_margin_custom does the same as `strip_margin` but will use `del` as delimiter instead of `|` +[direct_array_access] +pub fn (s string) strip_margin_custom(del byte) string { + mut sep := del + if sep.is_space() { + eprintln('Warning: `strip_margin` cannot use white-space as a delimiter') + eprintln(' Defaulting to `|`') + sep = `|` + } + // don't know how much space the resulting string will be, but the max it + // can be is this big + mut ret := unsafe { malloc_noscan(s.len + 1) } + mut count := 0 + for i := 0; i < s.len; i++ { + if s[i] in [10, 13] { + unsafe { + ret[count] = s[i] + } + count++ + // CRLF + if s[i] == 13 && i < s.len - 1 && s[i + 1] == 10 { + unsafe { + ret[count] = s[i + 1] + } + count++ + i++ + } + for s[i] != sep { + i++ + if i >= s.len { + break + } + } + } else { + unsafe { + ret[count] = s[i] + } + count++ + } + } + unsafe { + ret[count] = 0 + return ret.vstring_with_len(count) + } +} diff --git a/v_windows/v/vlib/builtin/string_charptr_byteptr_helpers.v b/v_windows/v/vlib/builtin/string_charptr_byteptr_helpers.v new file mode 100644 index 0000000..5b81b39 --- /dev/null +++ b/v_windows/v/vlib/builtin/string_charptr_byteptr_helpers.v @@ -0,0 +1,104 @@ +module builtin + +// NB: this file will be removed soon + +// byteptr.vbytes() - makes a V []byte structure from a C style memory buffer. NB: the data is reused, NOT copied! +[unsafe] +pub fn (data byteptr) vbytes(len int) []byte { + return unsafe { voidptr(data).vbytes(len) } +} + +// vstring converts a C style string to a V string. NB: the string data is reused, NOT copied. +// strings returned from this function will be normal V strings beside that (i.e. they would be +// freed by V's -autofree mechanism, when they are no longer used). +[unsafe] +pub fn (bp byteptr) vstring() string { + return string{ + str: bp + len: unsafe { vstrlen(bp) } + } +} + +// vstring_with_len converts a C style string to a V string. +// NB: the string data is reused, NOT copied. +[unsafe] +pub fn (bp byteptr) vstring_with_len(len int) string { + return string{ + str: bp + len: len + is_lit: 0 + } +} + +// vstring converts C char* to V string. +// NB: the string data is reused, NOT copied. +[unsafe] +pub fn (cp charptr) vstring() string { + return string{ + str: byteptr(cp) + len: unsafe { vstrlen_char(cp) } + is_lit: 0 + } +} + +// vstring_with_len converts C char* to V string. +// NB: the string data is reused, NOT copied. +[unsafe] +pub fn (cp charptr) vstring_with_len(len int) string { + return string{ + str: byteptr(cp) + len: len + is_lit: 0 + } +} + +// vstring_literal converts a C style string to a V string. +// NB: the string data is reused, NOT copied. +// NB2: unlike vstring, vstring_literal will mark the string +// as a literal, so it will not be freed by autofree. +// This is suitable for readonly strings, C string literals etc, +// that can be read by the V program, but that should not be +// managed by it, for example `os.args` is implemented using it. +[unsafe] +pub fn (bp byteptr) vstring_literal() string { + return string{ + str: bp + len: unsafe { vstrlen(bp) } + is_lit: 1 + } +} + +// vstring_with_len converts a C style string to a V string. +// NB: the string data is reused, NOT copied. +[unsafe] +pub fn (bp byteptr) vstring_literal_with_len(len int) string { + return string{ + str: bp + len: len + is_lit: 1 + } +} + +// vstring_literal converts C char* to V string. +// See also vstring_literal defined on byteptr for more details. +// NB: the string data is reused, NOT copied. +[unsafe] +pub fn (cp charptr) vstring_literal() string { + return string{ + str: byteptr(cp) + len: unsafe { vstrlen_char(cp) } + is_lit: 1 + } +} + +// vstring_literal_with_len converts C char* to V string. +// See also vstring_literal_with_len defined on byteptr. +// NB: the string data is reused, NOT copied. +[unsafe] +pub fn (cp charptr) vstring_literal_with_len(len int) string { + return string{ + str: byteptr(cp) + len: len + is_lit: 1 + } +} diff --git a/v_windows/v/vlib/builtin/string_int_test.v b/v_windows/v/vlib/builtin/string_int_test.v new file mode 100644 index 0000000..a17563d --- /dev/null +++ b/v_windows/v/vlib/builtin/string_int_test.v @@ -0,0 +1,221 @@ +import strconv + +fn test_common_atoi() { + // test common cases + assert '70zzz'.int() == 70 + assert '2901issue'.int() == 2901 + assert '234232w'.int() == 234232 + assert '-9009x'.int() == -9009 + assert '0y'.int() == 0 + + // test lead zeros + assert '0000012'.int() == 12 + assert '-0000012'.int() == -12 + assert '0x001F'.int() == 31 + assert '-0x001F'.int() == -31 + assert '0x001f'.int() == 31 + assert '0o00011'.int() == 9 + assert '0b00001001'.int() == 9 + + // test underscore in string + assert '-10_000'.int() == -10000 + assert '-0x00_0_f_ff'.int() == -0xfff + assert '10_000_000'.int() == 10000000 + + for n in -10000 .. 100000 { + s := n.str() + 'z' + assert s.int() == n + } +} + +fn test_unsigned_cast() { + // tests for u16 + + // test common cases + assert '70zzz'.u16() == 70 + assert '2901issue'.u16() == 2901 + assert '0y'.u16() == 0 + + // test lead zeros + assert '0000012'.u16() == 12 + assert '0x001F'.u16() == 31 + assert '0x001f'.u16() == 31 + assert '0o00011'.u16() == 9 + assert '0b00001001'.u16() == 9 + + // tests for u32 + + // test common cases + assert '70zzz'.u32() == 70 + assert '2901issue'.u32() == 2901 + assert '234232w'.u32() == 234232 + assert '-9009x'.u32() == 0 + assert '0y'.u32() == 0 + + // test lead zeros + assert '0000012'.u32() == 12 + assert '-0000012'.u32() == 0 + assert '0x001F'.u32() == 31 + assert '-0x001F'.u32() == 0 + assert '0x001f'.u32() == 31 + assert '0o00011'.u32() == 9 + assert '0b00001001'.u32() == 9 + + // test underscore in string + assert '-10_000'.u32() == 0 + assert '-0x00_0_f_ff'.u32() == 0 + assert '10_000_000'.u32() == 10000000 + + for n in 0 .. u32(100) { + s := n.str() + 'z' + assert s.u32() == n + } + + // tests for u64 + + // test common cases + assert '70zzz'.u64() == 70 + assert '2901issue'.u64() == 2901 + assert '234232w'.u64() == 234232 + assert '-9009x'.u64() == 0 + assert '0y'.u64() == 0 + + // test lead zeros + assert '0000012'.u64() == 12 + assert '-0000012'.u64() == 0 + assert '0x001F'.u64() == 31 + assert '-0x001F'.u64() == 0 + assert '0x001f'.u64() == 31 + assert '0o00011'.u64() == 9 + assert '0b00001001'.u64() == 9 + + // test underscore in string + assert '-10_000'.u64() == 0 + assert '-0x00_0_f_ff'.u64() == 0 + assert '10_000_000'.u64() == 10000000 + + for n in 0 .. u64(10000) { + s := n.str() + 'z' + assert s.u64() == n + } +} + +fn test_signed_cast() { + // tests for i64 + + // test common cases + assert '70zzz'.i64() == 70 + assert '2901issue'.i64() == 2901 + assert '234232w'.i64() == 234232 + assert '-9009x'.i64() == -9009 + assert '0y'.i64() == 0 + + // test lead zeros + assert '0000012'.i64() == 12 + assert '-0000012'.i64() == -12 + assert '0x001F'.i64() == 31 + assert '-0x001F'.i64() == -31 + assert '0x001f'.i64() == 31 + assert '0o00011'.i64() == 9 + assert '0b00001001'.i64() == 9 + + // test underscore in string + assert '-10_000'.i64() == -10000 + assert '-0x00_0_f_ff'.i64() == -0xfff + assert '10_000_000'.i64() == 10000000 + + for n in -10000 .. 100000 { + s := n.str() + 'z' + assert s.i64() == n + } + + // tests for i8 + + // test common cases + assert '70zzz'.i8() == 70 + assert '29issue'.i8() == 29 + assert '22w'.i8() == 22 + assert '-90x'.i8() == -90 + assert '0y'.i8() == 0 + + // test lead zeros + assert '0000012'.i8() == 12 + assert '-0000012'.i8() == -12 + assert '0x001F'.i8() == 31 + assert '-0x001F'.i8() == -31 + assert '0x001f'.i8() == 31 + assert '0o00011'.i8() == 9 + assert '0b000011'.i8() == 3 + + // test underscore in string + assert '-10_0'.i8() == -100 + assert '-0x0_0_f'.i8() == -0xf + assert '10_0'.i8() == 100 + + for n in -10 .. 100 { + s := n.str() + 'z' + assert s.i8() == n + } + + // tests for i16 + + // test common cases + assert '70zzz'.i16() == 70 + assert '2901issue'.i16() == 2901 + assert '2342w'.i16() == 2342 + assert '-9009x'.i16() == -9009 + assert '0y'.i16() == 0 + + // test lead zeros + assert '0000012'.i16() == 12 + assert '-0000012'.i16() == -12 + assert '0x001F'.i16() == 31 + assert '-0x001F'.i16() == -31 + assert '0x001f'.i16() == 31 + assert '0o00011'.i16() == 9 + assert '0b00001001'.i16() == 9 + + // test underscore in string + assert '-10_0'.i16() == -100 + assert '-0x00_0_fff'.i16() == -0xfff + assert '10_0'.i16() == 100 + + for n in -100 .. 100 { + s := n.str() + 'z' + assert s.i16() == n + } + + // test g format + unsafe { + mut u := strconv.Float64u{ + u: strconv.double_plus_zero + } + assert '${u.f:g}' == '0' + assert '${u.f:G}' == '0' + u.u = strconv.double_minus_zero + assert '${u.f:g}' == '0' + assert '${u.f:G}' == '0' + u.u = strconv.double_plus_infinity + assert '${u.f:g}' == '+inf' + assert '${u.f:G}' == '+INF' + u.u = strconv.double_minus_infinity + assert '${u.f:g}' == '-inf' + assert '${u.f:G}' == '-INF' + } + unsafe { + mut u := strconv.Float32u{ + u: strconv.single_plus_zero + } + assert '${u.f:g}' == '0' + assert '${u.f:G}' == '0' + u.u = strconv.single_minus_zero + assert '${u.f:g}' == '0' + assert '${u.f:G}' == '0' + u.u = strconv.single_plus_infinity + assert '${u.f:g}' == '+inf' + assert '${u.f:G}' == '+INF' + u.u = strconv.single_minus_infinity + assert '${u.f:g}' == '-inf' + assert '${u.f:G}' == '-INF' + } +} diff --git a/v_windows/v/vlib/builtin/string_interpolation.v b/v_windows/v/vlib/builtin/string_interpolation.v new file mode 100644 index 0000000..10064ac --- /dev/null +++ b/v_windows/v/vlib/builtin/string_interpolation.v @@ -0,0 +1,713 @@ +module builtin + +import strconv +import strings + +/*============================================================================= +Copyright (c) 2019-2021 Dario Deledda. All rights reserved. +Use of this source code is governed by an MIT license +that can be found in the LICENSE file. + +This file contains string interpolation V functions +=============================================================================*/ + +//============================================================================= +// Enum format types max 0x1F => 32 types +//============================================================================= +pub enum StrIntpType { + si_no_str = 0 // no parameter to print only fix string + si_c + si_u8 + si_i8 + si_u16 + si_i16 + si_u32 + si_i32 + si_u64 + si_i64 + si_e32 + si_e64 + si_f32 + si_f64 + si_g32 + si_g64 + si_s + si_p + si_vp +} + +pub fn (x StrIntpType) str() string { + match x { + .si_no_str { return 'no_str' } + .si_c { return 'c' } + .si_u8 { return 'u8' } + .si_i8 { return 'i8' } + .si_u16 { return 'u16' } + .si_i16 { return 'i16' } + .si_u32 { return 'u32' } + .si_i32 { return 'i32' } + .si_u64 { return 'u64' } + .si_i64 { return 'i64' } + .si_f32 { return 'f32' } + .si_f64 { return 'f64' } + .si_g32 { return 'f32' } // g32 format use f32 data + .si_g64 { return 'f64' } // g64 format use f64 data + .si_e32 { return 'f32' } // e32 format use f32 data + .si_e64 { return 'f64' } // e64 format use f64 data + .si_s { return 's' } + .si_p { return 'p' } + .si_vp { return 'vp' } + } +} + +//============================================================================= +// Union data +//============================================================================= +pub union StrIntpMem { +pub mut: + d_c u32 + d_u8 byte + d_i8 i8 + d_u16 u16 + d_i16 i16 + d_u32 u32 + d_i32 int + d_u64 u64 + d_i64 i64 + d_f32 f32 + d_f64 f64 + d_s string + d_p voidptr + d_vp voidptr +} + +[inline] +fn fabs32(x f32) f32 { + return if x < 0 { -x } else { x } +} + +[inline] +fn fabs64(x f64) f64 { + return if x < 0 { -x } else { x } +} + +[inline] +fn abs64(x i64) u64 { + return if x < 0 { u64(-x) } else { u64(x) } +} + +//========================================= +// +// u32/u64 bit compact format +// +//___ 32 24 16 8 +//___ | | | | +//_3333333333222222222211111111110000000000 +//_9876543210987654321098765432109876543210 +//_nPPPPPPPPBBBBWWWWWWWWWWTDDDDDDDSUAA===== +// = data type 5 bit max 32 data type +// A allign 2 bit Note: for now only 1 used! +// U uppercase 1 bit 0 do nothing, 1 do to_upper() +// S sign 1 bit show the sign if positive +// D decimals 7 bit number of decimals digit to show +// T tail zeros 1 bit 1 remove tail zeros, 0 do nothing +// W Width 10 bit number of char for padding and indentation +// B num base 4 bit start from 2, 0 for base 10 +// P pad char 1/8 bit padding char (in u32 format reduced to 1 bit as flag for `0` padding) +// -------------- +// TOTAL: 39/32 bit +//========================================= + +// convert from data format to compact u64 +pub fn get_str_intp_u64_format(fmt_type StrIntpType, in_width int, in_precision int, in_tail_zeros bool, in_sign bool, in_pad_ch byte, in_base int, in_upper_case bool) u64 { + width := if in_width != 0 { abs64(in_width) } else { u64(0) } + allign := if in_width > 0 { u64(1 << 5) } else { u64(0) } // two bit 0 .left 1 .rigth, for now we use only one + upper_case := if in_upper_case { u64(1 << 7) } else { u64(0) } + sign := if in_sign { u64(1 << 8) } else { u64(0) } + precision := if in_precision != 987698 { + (u64(in_precision & 0x7F) << 9) + } else { + u64(0x7F) << 9 + } + tail_zeros := if in_tail_zeros { u32(1) << 16 } else { u32(0) } + base := u64((in_base & 0xf) << 27) + res := u64((u64(fmt_type) & 0x1F) | allign | upper_case | sign | precision | tail_zeros | (u64(width & 0x3FF) << 17) | base | (u64(in_pad_ch) << 31)) + return res +} + +// convert from data format to compact u32 +pub fn get_str_intp_u32_format(fmt_type StrIntpType, in_width int, in_precision int, in_tail_zeros bool, in_sign bool, in_pad_ch byte, in_base int, in_upper_case bool) u32 { + width := if in_width != 0 { abs64(in_width) } else { u32(0) } + allign := if in_width > 0 { u32(1 << 5) } else { u32(0) } // two bit 0 .left 1 .rigth, for now we use only one + upper_case := if in_upper_case { u32(1 << 7) } else { u32(0) } + sign := if in_sign { u32(1 << 8) } else { u32(0) } + precision := if in_precision != 987698 { + (u32(in_precision & 0x7F) << 9) + } else { + u32(0x7F) << 9 + } + tail_zeros := if in_tail_zeros { u32(1) << 16 } else { u32(0) } + base := u32((in_base & 0xf) << 27) + res := u32((u32(fmt_type) & 0x1F) | allign | upper_case | sign | precision | tail_zeros | (u32(width & 0x3FF) << 17) | base | (u32(in_pad_ch & 1) << 31)) + return res +} + +// convert from struct to formated string +[manualfree] +fn (data StrIntpData) get_fmt_format(mut sb strings.Builder) { + x := data.fmt + typ := StrIntpType(x & 0x1F) + allign := int((x >> 5) & 0x01) + upper_case := if ((x >> 7) & 0x01) > 0 { true } else { false } + sign := int((x >> 8) & 0x01) + precision := int((x >> 9) & 0x7F) + tail_zeros := if ((x >> 16) & 0x01) > 0 { true } else { false } + width := int(i16((x >> 17) & 0x3FF)) + mut base := int(x >> 27) & 0xF + fmt_pad_ch := byte((x >> 31) & 0xFF) + + // no string interpolation is needed, return empty string + if typ == .si_no_str { + return + } + + // if width > 0 { println("${x.hex()} Type: ${x & 0x7F} Width: ${width} Precision: ${precision} allign:${allign}") } + + // manage base if any + if base > 0 { + base += 2 // we start from 2, 0 == base 10 + } + + // mange pad char, for now only 0 allowed + mut pad_ch := byte(` `) + if fmt_pad_ch > 0 { + // pad_ch = fmt_pad_ch + pad_ch = `0` + } + + len0_set := if width > 0 { width } else { -1 } + len1_set := if precision == 0x7F { -1 } else { precision } + sign_set := if sign == 1 { true } else { false } + + mut bf := strconv.BF_param{ + pad_ch: pad_ch // padding char + len0: len0_set // default len for whole the number or string + len1: len1_set // number of decimal digits, if needed + positive: true // mandatory: the sign of the number passed + sign_flag: sign_set // flag for print sign as prefix in padding + allign: .left // alignment of the string + rm_tail_zero: tail_zeros // false // remove the tail zeros from floats + } + + // allign + if fmt_pad_ch == 0 { + match allign { + 0 { bf.allign = .left } + 1 { bf.allign = .right } + // 2 { bf.allign = .center } + else { bf.allign = .left } + } + } else { + bf.allign = .right + } + + unsafe { + // strings + if typ == .si_s { + mut s := '' + if upper_case { + s = data.d.d_s.to_upper() + } else { + s = data.d.d_s.clone() + } + if width == 0 { + sb.write_string(s) + } else { + strconv.format_str_sb(s, bf, mut sb) + } + s.free() + return + } + + // signed int + if typ in [.si_i8, .si_i16, .si_i32, .si_i64] { + mut d := data.d.d_i64 + if typ == .si_i8 { + d = i64(data.d.d_i8) + } else if typ == .si_i16 { + d = i64(data.d.d_i16) + } else if typ == .si_i32 { + d = i64(data.d.d_i32) + } + + if base == 0 { + if width == 0 { + d_str := d.str() + sb.write_string(d_str) + d_str.free() + return + } + if d < 0 { + bf.positive = false + } + strconv.format_dec_sb(abs64(d), bf, mut sb) + } else { + mut hx := strconv.format_int(d, base) + if upper_case { + tmp := hx + hx = hx.to_upper() + tmp.free() + } + if width == 0 { + sb.write_string(hx) + } else { + strconv.format_str_sb(hx, bf, mut sb) + } + hx.free() + } + return + } + + // unsigned int and pointers + if typ in [.si_u8, .si_u16, .si_u32, .si_u64] { + mut d := data.d.d_u64 + if typ == .si_u8 { + d = u64(data.d.d_u8) + } else if typ == .si_u16 { + d = u64(data.d.d_u16) + } else if typ == .si_u32 { + d = u64(data.d.d_u32) + } + if base == 0 { + if width == 0 { + d_str := d.str() + sb.write_string(d_str) + d_str.free() + return + } + strconv.format_dec_sb(d, bf, mut sb) + } else { + mut hx := strconv.format_uint(d, base) + if upper_case { + tmp := hx + hx = hx.to_upper() + tmp.free() + } + if width == 0 { + sb.write_string(hx) + } else { + strconv.format_str_sb(hx, bf, mut sb) + } + hx.free() + } + return + } + + // pointers + if typ == .si_p { + mut d := data.d.d_u64 + base = 16 // TODO: **** decide the behaviour of this flag! **** + if base == 0 { + if width == 0 { + d_str := d.str() + sb.write_string(d_str) + d_str.free() + return + } + strconv.format_dec_sb(d, bf, mut sb) + } else { + mut hx := strconv.format_uint(d, base) + if upper_case { + tmp := hx + hx = hx.to_upper() + tmp.free() + } + if width == 0 { + sb.write_string(hx) + } else { + strconv.format_str_sb(hx, bf, mut sb) + } + hx.free() + } + return + } + + // default settings for floats + mut use_default_str := false + if width == 0 && precision == 0x7F { + bf.len1 = 3 + use_default_str = true + } + if bf.len1 < 0 { + bf.len1 = 3 + } + + match typ { + // floating point + .si_f32 { + // println("HERE: f32") + if use_default_str { + mut f := data.d.d_f32.str() + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + } else { + // println("HERE: f32 format") + // println(data.d.d_f32) + if data.d.d_f32 < 0 { + bf.positive = false + } + mut f := strconv.format_fl(data.d.d_f32, bf) + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + } + } + .si_f64 { + // println("HERE: f64") + if use_default_str { + mut f := data.d.d_f64.str() + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + } else { + if data.d.d_f64 < 0 { + bf.positive = false + } + f_union := strconv.Float64u{ + f: data.d.d_f64 + } + if f_union.u == strconv.double_minus_zero { + bf.positive = false + } + + mut f := strconv.format_fl(data.d.d_f64, bf) + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + } + } + .si_g32 { + // println("HERE: g32") + if use_default_str { + mut f := data.d.d_f32.strg() + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + } else { + // Manage +/-0 + if data.d.d_f32 == strconv.single_plus_zero { + tmp_str := '0' + strconv.format_str_sb(tmp_str, bf, mut sb) + tmp_str.free() + return + } + if data.d.d_f32 == strconv.single_minus_zero { + tmp_str := '-0' + strconv.format_str_sb(tmp_str, bf, mut sb) + tmp_str.free() + return + } + // Manage +/-INF + if data.d.d_f32 == strconv.single_plus_infinity { + mut tmp_str := '+inf' + if upper_case { + tmp_str = '+INF' + } + strconv.format_str_sb(tmp_str, bf, mut sb) + tmp_str.free() + } + if data.d.d_f32 == strconv.single_minus_infinity { + mut tmp_str := '-inf' + if upper_case { + tmp_str = '-INF' + } + strconv.format_str_sb(tmp_str, bf, mut sb) + tmp_str.free() + } + + if data.d.d_f32 < 0 { + bf.positive = false + } + d := fabs32(data.d.d_f32) + if d < 999_999.0 && d >= 0.00001 { + mut f := strconv.format_fl(data.d.d_f32, bf) + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + return + } + mut f := strconv.format_es(data.d.d_f32, bf) + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + } + } + .si_g64 { + // println("HERE: g64") + if use_default_str { + mut f := data.d.d_f64.strg() + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + } else { + // Manage +/-0 + if data.d.d_f64 == strconv.double_plus_zero { + tmp_str := '0' + strconv.format_str_sb(tmp_str, bf, mut sb) + tmp_str.free() + return + } + if data.d.d_f64 == strconv.double_minus_zero { + tmp_str := '-0' + strconv.format_str_sb(tmp_str, bf, mut sb) + tmp_str.free() + return + } + // Manage +/-INF + if data.d.d_f64 == strconv.double_plus_infinity { + mut tmp_str := '+inf' + if upper_case { + tmp_str = '+INF' + } + strconv.format_str_sb(tmp_str, bf, mut sb) + tmp_str.free() + } + if data.d.d_f64 == strconv.double_minus_infinity { + mut tmp_str := '-inf' + if upper_case { + tmp_str = '-INF' + } + strconv.format_str_sb(tmp_str, bf, mut sb) + tmp_str.free() + } + + if data.d.d_f64 < 0 { + bf.positive = false + } + d := fabs64(data.d.d_f64) + if d < 999_999.0 && d >= 0.00001 { + mut f := strconv.format_fl(data.d.d_f64, bf) + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + return + } + mut f := strconv.format_es(data.d.d_f64, bf) + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + } + } + .si_e32 { + // println("HERE: e32") + bf.len1 = 6 + if use_default_str { + mut f := data.d.d_f32.str() + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + } else { + if data.d.d_f32 < 0 { + bf.positive = false + } + mut f := strconv.format_es(data.d.d_f32, bf) + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + } + } + .si_e64 { + // println("HERE: e64") + bf.len1 = 6 + if use_default_str { + mut f := data.d.d_f64.str() + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + } else { + if data.d.d_f64 < 0 { + bf.positive = false + } + mut f := strconv.format_es(data.d.d_f64, bf) + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + } + } + // runes + .si_c { + ss := utf32_to_str(data.d.d_c) + sb.write_string(ss) + ss.free() + } + // v pointers + .si_vp { + ss := u64(data.d.d_vp).hex() + sb.write_string(ss) + ss.free() + } + else { + sb.write_string('***ERROR!***') + } + } + } +} + +//==================================================================================== + +// storing struct used by cgen +pub struct StrIntpCgenData { +pub: + str string + fmt string + d string +} + +// NOTE: LOW LEVEL struct +// storing struct passed to V in the C code +pub struct StrIntpData { +pub: + str string + // fmt u64 // expanded version for future use, 64 bit + fmt u32 + d StrIntpMem +} + +// interpolation function +[manualfree] +pub fn str_intp(data_len int, in_data voidptr) string { + mut res := strings.new_builder(256) + unsafe { + mut i := 0 + for i < data_len { + data := &StrIntpData(&byte(in_data) + (int(sizeof(StrIntpData)) * i)) + // avoid empty strings + if data.str.len != 0 { + res.write_string(data.str) + } + // skip empty data + if data.fmt != 0 { + data.get_fmt_format(mut &res) + } + i++ + } + } + ret := res.str() + unsafe { res.free() } + return ret +} + +//==================================================================================== +// Utility for the compiler "auto_str_methods.v" +//==================================================================================== + +// substitute old _STR calls + +pub const ( + // BUG: this const is not released from the memory! use a const for now + // si_s_code = "0x" + int(StrIntpType.si_s).hex() // code for a simple string + si_s_code = '0xfe10' + si_g32_code = '0xfe0e' + si_g64_code = '0xfe0f' +) + +[inline] +pub fn str_intp_sq(in_str string) string { + return 'str_intp(2, _MOV((StrIntpData[]){{_SLIT("\'"), $si_s_code, {.d_s = $in_str}},{_SLIT("\'"), 0, {.d_c = 0 }}}))' +} + +[inline] +pub fn str_intp_rune(in_str string) string { + return 'str_intp(2, _MOV((StrIntpData[]){{_SLIT("\`"), $si_s_code, {.d_s = $in_str}},{_SLIT("\`"), 0, {.d_c = 0 }}}))' +} + +[inline] +pub fn str_intp_g32(in_str string) string { + return 'str_intp(1, _MOV((StrIntpData[]){{_SLIT0, $si_g32_code, {.d_f32 = $in_str }}}))' +} + +[inline] +pub fn str_intp_g64(in_str string) string { + return 'str_intp(1, _MOV((StrIntpData[]){{_SLIT0, $si_g64_code, {.d_f64 = $in_str }}}))' +} + +// replace %% with the in_str +[manualfree] +pub fn str_intp_sub(base_str string, in_str string) string { + index := base_str.index('%%') or { + eprintln('No strin interpolation %% parameteres') + exit(1) + } + // return base_str[..index] + in_str + base_str[index+2..] + + unsafe { + st_str := base_str[..index] + if index + 2 < base_str.len { + en_str := base_str[index + 2..] + res_str := 'str_intp(2, _MOV((StrIntpData[]){{_SLIT("$st_str"), $si_s_code, {.d_s = $in_str }},{_SLIT("$en_str"), 0, {.d_c = 0}}}))' + st_str.free() + en_str.free() + return res_str + } + res2_str := 'str_intp(1, _MOV((StrIntpData[]){{_SLIT("$st_str"), $si_s_code, {.d_s = $in_str }}}))' + st_str.free() + return res2_str + } +} diff --git a/v_windows/v/vlib/builtin/string_strip_margin_test.v b/v_windows/v/vlib/builtin/string_strip_margin_test.v new file mode 100644 index 0000000..372b8af --- /dev/null +++ b/v_windows/v/vlib/builtin/string_strip_margin_test.v @@ -0,0 +1,95 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +fn test_strip_margins_no_tabs() { + no_tabs := ['Hello there', 'This is a string', 'With multiple lines'].join('\n') + no_tabs_stripped := 'Hello there + |This is a string + |With multiple lines'.strip_margin() + assert no_tabs == no_tabs_stripped +} + +fn test_strip_margins_text_before() { + text_before := ['There is text', 'before the delimiter', 'that should be removed as well'].join('\n') + text_before_stripped := 'There is text + f lasj asldfj j lksjdf |before the delimiter + Which is removed hello |that should be removed as well'.strip_margin() + assert text_before_stripped == text_before +} + +fn test_strip_margins_white_space_after_delim() { + tabs := [' Tab', ' spaces', ' another tab'].join('\n') + tabs_stripped := ' Tab + | spaces + | another tab'.strip_margin() + assert tabs == tabs_stripped +} + +fn test_strip_margins_alternate_delim() { + alternate_delimiter := ['This has a different delim,', 'but that is ok', + 'because everything works', + ].join('\n') + alternate_delimiter_stripped := 'This has a different delim, + #but that is ok + #because everything works'.strip_margin_custom(`#`) + assert alternate_delimiter_stripped == alternate_delimiter +} + +fn test_strip_margins_multiple_delims_after_first() { + delim_after_first_instance := ['The delimiter used', + 'only matters the |||| First time it is seen', 'not any | other | times'].join('\n') + delim_after_first_instance_stripped := 'The delimiter used + |only matters the |||| First time it is seen + |not any | other | times'.strip_margin() + assert delim_after_first_instance_stripped == delim_after_first_instance +} + +fn test_strip_margins_uneven_delims() { + uneven_delims := ["It doesn't matter if the delims are uneven,", + 'The text will still be delimited correctly.', 'Maybe not everything needs 3 lines?', + 'Let us go for 4 then', + ].join('\n') + uneven_delims_stripped := "It doesn't matter if the delims are uneven, + |The text will still be delimited correctly. + |Maybe not everything needs 3 lines? + |Let us go for 4 then".strip_margin() + assert uneven_delims_stripped == uneven_delims +} + +fn test_strip_margins_multiple_blank_lines() { + multi_blank_lines := ['Multiple blank lines will be removed.', + ' I actually consider this a feature.', + ].join('\n') + multi_blank_lines_stripped := 'Multiple blank lines will be removed. + + + + | I actually consider this a feature.'.strip_margin() + assert multi_blank_lines == multi_blank_lines_stripped +} + +fn test_strip_margins_end_newline() { + end_with_newline := ['This line will end with a newline', 'Something cool or something.', ''].join('\n') + end_with_newline_stripped := 'This line will end with a newline + |Something cool or something. + + '.strip_margin() + assert end_with_newline_stripped == end_with_newline +} + +fn test_strip_margins_space_delimiter() { + space_delimiter := ['Using a white-space char will', 'revert back to default behavior.'].join('\n') + space_delimiter_stripped := 'Using a white-space char will + |revert back to default behavior.'.strip_margin_custom(`\n`) + assert space_delimiter == space_delimiter_stripped +} + +fn test_strip_margins_crlf() { + crlf := ["This string's line endings have CR as well as LFs.", 'This should pass', 'Definitely'].join('\r\n') + crlf_stripped := "This string's line endings have CR as well as LFs.\r + |This should pass\r + |Definitely".strip_margin() + + assert crlf == crlf_stripped +} diff --git a/v_windows/v/vlib/builtin/string_test.v b/v_windows/v/vlib/builtin/string_test.v new file mode 100644 index 0000000..5936aff --- /dev/null +++ b/v_windows/v/vlib/builtin/string_test.v @@ -0,0 +1,912 @@ +import strings + +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +struct Foo { + bar int +mut: + str string +} + +fn test_add() { + mut a := 'a' + a += 'b' + assert a == ('ab') + a = 'a' + for i := 1; i < 1000; i++ { + a += 'b' + } + assert a.len == 1000 + assert a.ends_with('bbbbb') + a += '123' + assert a.ends_with('3') +} + +fn test_ends_with() { + a := 'browser.v' + assert a.ends_with('.v') + + s := 'V Programming Language' + assert s.ends_with('guage') == true + assert s.ends_with('Language') == true + assert s.ends_with('Programming Language') == true + assert s.ends_with('V') == false +} + +fn test_between() { + s := 'hello [man] how you doing' + assert s.find_between('[', ']') == 'man' +} + +fn test_compare() { + a := 'Music' + b := 'src' + assert b >= a +} + +fn test_lt() { + a := '' + b := 'a' + c := 'a' + d := 'b' + e := 'aa' + f := 'ab' + assert a < b + assert !(b < c) + assert c < d + assert !(d < e) + assert c < e + assert e < f +} + +fn test_ge() { + a := 'aa' + b := 'aa' + c := 'ab' + d := 'abc' + e := 'aaa' + assert b >= a + assert c >= b + assert d >= c + assert !(c >= d) + assert e >= a +} + +fn test_compare_strings() { + a := 'aa' + b := 'aa' + c := 'ab' + d := 'abc' + e := 'aaa' + assert compare_strings(a, b) == 0 + assert compare_strings(b, c) == -1 + assert compare_strings(c, d) == -1 + assert compare_strings(d, e) == 1 + assert compare_strings(a, e) == -1 + assert compare_strings(e, a) == 1 +} + +fn test_sort() { + mut vals := [ + 'arr', + 'an', + 'a', + 'any', + ] + len := vals.len + vals.sort() + assert len == vals.len + assert vals[0] == 'a' + assert vals[1] == 'an' + assert vals[2] == 'any' + assert vals[3] == 'arr' +} + +fn test_sort_reverse() { + mut vals := [ + 'arr', + 'an', + 'a', + 'any', + ] + len := vals.len + vals.sort(b > a) + assert len == vals.len + assert vals[0] == 'a' + assert vals[1] == 'an' + assert vals[2] == 'any' + assert vals[3] == 'arr' +} + +fn test_split_nth() { + a := '1,2,3' + assert a.split(',').len == 3 + assert a.split_nth(',', -1).len == 3 + assert a.split_nth(',', 0).len == 3 + assert a.split_nth(',', 1).len == 1 + assert a.split_nth(',', 2).len == 2 + assert a.split_nth(',', 10).len == 3 + b := '1::2::3' + assert b.split('::').len == 3 + assert b.split_nth('::', -1).len == 3 + assert b.split_nth('::', 0).len == 3 + assert b.split_nth('::', 1).len == 1 + assert b.split_nth('::', 2).len == 2 + assert b.split_nth('::', 10).len == 3 + c := 'ABCDEF' + println(c.split('').len) + assert c.split('').len == 6 + assert c.split_nth('', 3).len == 3 + assert c.split_nth('BC', -1).len == 2 + d := ',' + assert d.split(',').len == 2 + assert d.split_nth('', 3).len == 1 + assert d.split_nth(',', -1).len == 2 + assert d.split_nth(',', 3).len == 2 + e := ',,,0,,,,,a,,b,' + assert e.split(',,').len == 5 + assert e.split_nth(',,', 3).len == 3 + assert e.split_nth(',', -1).len == 12 + assert e.split_nth(',', 3).len == 3 +} + +fn test_split_nth_values() { + line := 'CMD=eprintln(phase=1)' + + a0 := line.split_nth('=', 0) + assert a0.len == 3 + assert a0[0] == 'CMD' + assert a0[1] == 'eprintln(phase' + assert a0[2] == '1)' + + a1 := line.split_nth('=', 1) + assert a1.len == 1 + assert a1[0] == 'CMD=eprintln(phase=1)' + + a2 := line.split_nth('=', 2) + assert a2.len == 2 + assert a2[0] == 'CMD' + assert a2[1] == 'eprintln(phase=1)' + + a3 := line.split_nth('=', 3) + assert a3.len == 3 + assert a3[0] == 'CMD' + assert a3[1] == 'eprintln(phase' + assert a3[2] == '1)' + + a4 := line.split_nth('=', 4) + assert a4.len == 3 + assert a4[0] == 'CMD' + assert a4[1] == 'eprintln(phase' + assert a4[2] == '1)' +} + +fn test_split() { + mut s := 'volt/twitch.v:34' + mut vals := s.split(':') + assert vals.len == 2 + assert vals[0] == 'volt/twitch.v' + assert vals[1] == '34' + // ///////// + s = '2018-01-01z13:01:02' + vals = s.split('z') + assert vals.len == 2 + assert vals[0] == '2018-01-01' + assert vals[1] == '13:01:02' + // ////////// + s = '4627a862c3dec29fb3182a06b8965e0025759e18___1530207969___blue' + vals = s.split('___') + assert vals.len == 3 + assert vals[0] == '4627a862c3dec29fb3182a06b8965e0025759e18' + assert vals[1] == '1530207969' + assert vals[2] == 'blue' + // ///////// + s = 'lalala' + vals = s.split('a') + assert vals.len == 4 + assert vals[0] == 'l' + assert vals[1] == 'l' + assert vals[2] == 'l' + assert vals[3] == '' + // ///////// + s = 'awesome' + a := s.split('') + assert a.len == 7 + assert a[0] == 'a' + assert a[1] == 'w' + assert a[2] == 'e' + assert a[3] == 's' + assert a[4] == 'o' + assert a[5] == 'm' + assert a[6] == 'e' + // ///////// + s = 'wavy turquoise bags' + vals = s.split(' bags') + assert vals.len == 2 + assert vals[0] == 'wavy turquoise' + assert vals[1] == '' +} + +fn test_trim_space() { + a := ' a ' + assert a.trim_space() == 'a' + code := ' + +fn main() { + println(2) +} + +' + code_clean := 'fn main() { + println(2) +}' + assert code.trim_space() == code_clean +} + +fn test_join() { + mut strings := ['a', 'b', 'c'] + mut s := strings.join(' ') + assert s == 'a b c' + strings = [ + 'one +two ', + 'three! +four!', + ] + s = strings.join(' ') + assert s.contains('one') && s.contains('two ') && s.contains('four') + empty := []string{len: 0} + assert empty.join('A') == '' +} + +fn test_clone() { + mut a := 'a' + a += 'a' + a += 'a' + b := a + c := a.clone() + assert c == a + assert c == 'aaa' + assert b == 'aaa' +} + +fn test_replace() { + a := 'hello man!' + mut b := a.replace('man', 'world') + assert b == ('hello world!') + b = b.replace('!', '') + assert b == ('hello world') + b = b.replace('h', 'H') + assert b == ('Hello world') + b = b.replace('foo', 'bar') + assert b == ('Hello world') + s := 'hey man how are you' + assert s.replace('man ', '') == 'hey how are you' + lol := 'lol lol lol' + assert lol.replace('lol', 'LOL') == 'LOL LOL LOL' + b = 'oneBtwoBBthree' + assert b.replace('B', '') == 'onetwothree' + b = '*charptr' + assert b.replace('charptr', 'byteptr') == '*byteptr' + c := 'abc' + assert c.replace('', '-') == c + v := 'a b c d' + assert v.replace(' ', ' ') == 'a b c d' +} + +fn test_replace_each() { + s := 'hello man man :)' + q := s.replace_each([ + 'man', + 'dude', + 'hello', + 'hey', + ]) + assert q == 'hey dude dude :)' + bb := '[b]bold[/b] [code]code[/code]' + assert bb.replace_each([ + '[b]', + '', + '[/b]', + '', + '[code]', + '', + '[/code]', + '', + ]) == 'bold code' + bb2 := '[b]cool[/b]' + assert bb2.replace_each([ + '[b]', + '', + '[/b]', + '', + ]) == 'cool' + t := 'aaaaaaaa' + y := t.replace_each([ + 'aa', + 'b', + ]) + assert y == 'bbbb' + s2 := 'hello_world hello' + assert s2.replace_each(['hello_world', 'aaa', 'hello', 'bbb']) == 'aaa bbb' +} + +fn test_itoa() { + num := 777 + assert num.str() == '777' + big := 7779998 + assert big.str() == '7779998' + a := 3 + assert a.str() == '3' + b := 5555 + assert b.str() == '5555' + zero := 0 + assert zero.str() == '0' + neg := -7 + assert neg.str() == '-7' +} + +fn test_reassign() { + a := 'hi' + mut b := a + b += '!' + assert a == 'hi' + assert b == 'hi!' +} + +fn test_runes() { + s := 'привет' + assert s.len == 12 + s2 := 'privet' + assert s2.len == 6 + u := s.runes() + assert u.len == 6 + assert s2.substr(1, 4).len == 3 + assert s2.substr(1, 4) == 'riv' + assert s2[1..4].len == 3 + assert s2[1..4] == 'riv' + assert s2[..4].len == 4 + assert s2[..4] == 'priv' + assert s2[2..].len == 4 + assert s2[2..] == 'ivet' + assert u[1..4].string().len == 6 + assert u[1..4].string() == 'рив' + assert s2.substr(1, 2) == 'r' + assert u[1..2].string() == 'р' + assert s2.runes()[1] == `r` + assert u[1] == `р` + first := u[0] + last := u[u.len - 1] + assert first.str().len == 2 + assert last.str().len == 2 +} + +fn test_contains() { + s := 'view.v' + assert s.contains('vi') + assert !s.contains('random') + assert ''.contains('') + assert 'abc'.contains('') +} + +fn test_contains_any() { + assert !'team'.contains_any('i') + assert 'fail'.contains_any('ui') + assert 'ure'.contains_any('ui') + assert 'failure'.contains_any('ui') + assert !'foo'.contains_any('') + assert !''.contains_any('') +} + +fn test_contains_any_substr() { + s := 'Some random text' + assert s.contains_any_substr(['false', 'not', 'rand']) + assert !s.contains_any_substr(['ABC', 'invalid']) + assert ''.contains_any_substr([]) + assert 'abc'.contains_any_substr(['']) +} + +fn test_arr_contains() { + a := ['a', 'b', 'c'] + assert a.contains('b') + ints := [1, 2, 3] + assert ints.contains(2) +} + +fn test_to_num() { + s := '7' + assert s.int() == 7 + assert s.u64() == 7 + f := '71.5 hasdf' + // QTODO + assert f.f32() == 71.5 + vals := ['9'] + assert vals[0].int() == 9 + big := '93993993939322' + assert big.u64() == 93993993939322 + assert big.i64() == 93993993939322 +} + +fn test_inter_format_string() { + float_num := 1.52345 + float_num_string := '-${float_num:.3f}-' + assert float_num_string == '-1.523-' + int_num := 7 + int_num_string := '-${int_num:03d}-' + assert int_num_string == '-007-' + ch := `a` + ch_string := '-${ch:c}-' + assert ch_string == '-a-' + hex_n := 192 + hex_n_string := '-${hex_n:x}-' + assert hex_n_string == '-c0-' + oct_n := 192 + oct_n_string := '-${oct_n:o}-' + assert oct_n_string == '-300-' + str := 'abc' + str_string := '-${str:s}-' + assert str_string == '-abc-' +} + +fn test_hash() { + s := '10000' + assert s.hash() == 46730161 + s2 := '24640' + assert s2.hash() == 47778736 + s3 := 'Content-Type' + assert s3.hash() == 949037134 + s4 := 'bad_key' + assert s4.hash() == -346636507 + s5 := '24640' + // From a map collision test + assert s5.hash() % ((1 << 20) - 1) == s.hash() % ((1 << 20) - 1) + assert s5.hash() % ((1 << 20) - 1) == 592861 +} + +fn test_trim() { + assert 'banana'.trim('bna') == '' + assert 'abc'.trim('ac') == 'b' + assert 'aaabccc'.trim('ac') == 'b' +} + +fn test_trim_left() { + mut s := 'module main' + assert s.trim_left(' ') == 'module main' + s = ' module main' + assert s.trim_left(' ') == 'module main' + // test cutset + s = 'banana' + assert s.trim_left('ba') == 'nana' + assert s.trim_left('ban') == '' +} + +fn test_trim_right() { + mut s := 'module main' + assert s.trim_right(' ') == 'module main' + s = 'module main ' + assert s.trim_right(' ') == 'module main' + // test cutset + s = 'banana' + assert s.trim_right('na') == 'b' + assert s.trim_right('ban') == '' +} + +fn test_all_before() { + s := 'fn hello fn' + assert s.all_before(' ') == 'fn' + assert s.all_before('2') == s + assert s.all_before('') == s +} + +fn test_all_before_last() { + s := 'fn hello fn' + assert s.all_before_last(' ') == 'fn hello' + assert s.all_before_last('2') == s + assert s.all_before_last('') == s +} + +fn test_all_after() { + s := 'fn hello' + assert s.all_after('fn ') == 'hello' + assert s.all_after('test') == s + assert s.all_after('') == s + assert s.after('e') == 'llo' + x := s.after('e') + assert x == 'llo' +} + +fn test_reverse() { + assert 'hello'.reverse() == 'olleh' + assert ''.reverse() == '' + assert 'a'.reverse() == 'a' +} + +fn test_bytes_to_string() { + mut buf := vcalloc(10) + unsafe { + buf[0] = `h` + buf[1] = `e` + buf[2] = `l` + buf[3] = `l` + buf[4] = `o` + } + assert unsafe { buf.vstring() } == 'hello' + assert unsafe { buf.vstring_with_len(2) } == 'he' + bytes := [byte(`h`), `e`, `l`, `l`, `o`] + assert bytes.bytestr() == 'hello' +} + +fn test_charptr() { + foo := &char('VLANG'.str) + println(typeof(foo).name) + assert typeof(foo).name == '&char' + assert unsafe { foo.vstring() } == 'VLANG' + assert unsafe { foo.vstring_with_len(3) } == 'VLA' +} + +fn test_count() { + assert ''.count('') == 0 + assert ''.count('a') == 0 + assert 'a'.count('') == 0 + assert 'aa'.count('a') == 2 + assert 'aa'.count('aa') == 1 + assert 'aabbaa'.count('aa') == 2 + assert 'bbaabb'.count('aa') == 1 +} + +fn test_lower() { + mut s := 'A' + assert !s.is_lower() + assert s.to_lower() == 'a' + assert s.to_lower().len == 1 + s = 'HELLO' + assert !s.is_lower() + assert s.to_lower() == 'hello' + assert s.to_lower().len == 5 + s = 'Aloha' + assert !s.is_lower() + assert s.to_lower() == 'aloha' + s = 'Have A nice Day!' + assert !s.is_lower() + assert s.to_lower() == 'have a nice day!' + s = 'hi' + assert s.is_lower() + assert s.to_lower() == 'hi' + assert 'aloha!'[0] == `a` + assert 'aloha!'[5] == `!` +} + +fn test_upper() { + mut s := 'a' + assert !s.is_upper() + assert s.to_upper() == 'A' + assert s.to_upper().len == 1 + s = 'hello' + assert !s.is_upper() + assert s.to_upper() == 'HELLO' + assert s.to_upper().len == 5 + s = 'Aloha' + assert !s.is_upper() + assert s.to_upper() == 'ALOHA' + s = 'have a nice day!' + assert !s.is_upper() + assert s.to_upper() == 'HAVE A NICE DAY!' + s = 'HI' + assert s.is_upper() + assert s.to_upper() == 'HI' +} + +fn test_capitalize() { + mut s := 'hello' + assert !s.is_capital() + assert s.capitalize() == 'Hello' + s = 'test' + assert !s.is_capital() + assert s.capitalize() == 'Test' + s = 'i am ray' + assert !s.is_capital() + assert s.capitalize() == 'I am ray' + s = '' + assert !s.is_capital() + assert s.capitalize() == '' + s = 'TEST IT' + assert !s.is_capital() + assert s.capitalize() == 'TEST IT' + s = 'Test it' + assert s.is_capital() + assert s.capitalize() == 'Test it' + assert 'GameMission_t'.capitalize() == 'GameMission_t' +} + +fn test_title() { + mut s := 'hello world' + assert !s.is_title() + assert s.title() == 'Hello World' + s = 'HELLO WORLD' + assert !s.is_title() + assert s.title() == 'HELLO WORLD' + s = 'Hello World' + assert s.is_title() + assert s.title() == 'Hello World' +} + +fn test_for_loop() { + mut i := 0 + s := 'abcd' + + for c in s { + assert c == s[i] + i++ + } +} + +fn test_for_loop_two() { + s := 'abcd' + + for i, c in s { + assert c == s[i] + } +} + +fn test_quote() { + a := `'` + println('testing double quotes') + b := 'hi' + assert b == 'hi' + assert a.str() == "'" +} + +fn test_limit() { + s := 'hello' + assert s.limit(2) == 'he' + assert s.limit(9) == s + assert s.limit(0) == '' + // assert s.limit(-1) == '' +} + +fn test_repeat() { + s1 := 'V! ' + assert s1.repeat(5) == 'V! V! V! V! V! ' + assert s1.repeat(1) == s1 + assert s1.repeat(0) == '' + s2 := '' + assert s2.repeat(5) == s2 + assert s2.repeat(1) == s2 + assert s2.repeat(0) == s2 + // TODO Add test for negative values +} + +fn test_starts_with() { + s := 'V Programming Language' + assert s.starts_with('V') == true + assert s.starts_with('V Programming') == true + assert s.starts_with('Language') == false +} + +fn test_trim_prefix() { + s := 'V Programming Language' + assert s.trim_prefix('V ') == 'Programming Language' + assert s.trim_prefix('V Programming ') == 'Language' + assert s.trim_prefix('Language') == s + + s2 := 'TestTestTest' + assert s2.trim_prefix('Test') == 'TestTest' + assert s2.trim_prefix('TestTest') == 'Test' + + s3 := '123Test123Test' + assert s3.trim_prefix('123') == 'Test123Test' + assert s3.trim_prefix('123Test') == '123Test' +} + +fn test_trim_suffix() { + s := 'V Programming Language' + assert s.trim_suffix(' Language') == 'V Programming' + assert s.trim_suffix(' Programming Language') == 'V' + assert s.trim_suffix('V') == s + + s2 := 'TestTestTest' + assert s2.trim_suffix('Test') == 'TestTest' + assert s2.trim_suffix('TestTest') == 'Test' + + s3 := '123Test123Test' + assert s3.trim_suffix('123') == s3 + assert s3.trim_suffix('123Test') == '123Test' +} + +fn test_raw() { + raw := r'raw\nstring' + lines := raw.split('\n') + println(lines) + assert lines.len == 1 + println('raw string: "$raw"') + + raw2 := r'Hello V\0' + assert raw2[7] == `\\` + assert raw2[8] == `0` + + raw3 := r'Hello V\x00' + assert raw3[7] == `\\` + assert raw3[8] == `x` + assert raw3[9] == `0` + assert raw3[10] == `0` +} + +fn test_raw_with_quotes() { + raw := r"some'" + r'"thing' // " should be escaped in the generated C code + assert raw[0] == `s` + assert raw[5] == `"` + assert raw[6] == `t` +} + +fn test_escape() { + a := 10 + println("\"$a") + assert "\"$a" == '"10' +} + +fn test_atoi() { + assert '234232'.int() == 234232 + assert '-9009'.int() == -9009 + assert '0'.int() == 0 + for n in -10000 .. 100000 { + s := n.str() + assert s.int() == n + } +} + +fn test_raw_inter() { + world := 'world' + println(world) + s := r'hello\n$world' + assert s == r'hello\n$world' + assert s.contains('$') +} + +fn test_c_r() { + // This used to break because of r'' and c'' + c := 42 + println('$c') + r := 50 + println('$r') +} + +fn test_inter_before_comp_if() { + s := '123' + // This used to break ('123 $....') + $if linux { + println(s) + } + assert s == '123' +} + +fn test_double_quote_inter() { + a := 1 + b := 2 + println('$a $b') + assert '$a $b' == '1 2' + assert '$a $b' == '1 2' +} + +fn foo(b byte) byte { + return b - 10 +} + +fn filter(b byte) bool { + return b != `a` +} + +fn test_split_into_lines() { + line_content := 'Line' + text_crlf := '$line_content\r\n$line_content\r\n$line_content' + lines_crlf := text_crlf.split_into_lines() + + assert lines_crlf.len == 3 + for line in lines_crlf { + assert line == line_content + } + + text_lf := '$line_content\n$line_content\n$line_content' + lines_lf := text_lf.split_into_lines() + + assert lines_lf.len == 3 + for line in lines_lf { + assert line == line_content + } +} + +fn test_string_literal_with_backslash() { + a := 'HelloWorld' + assert a == 'HelloWorld' + + b := 'OneTwoThree' + assert b == 'OneTwoThree' +} + +/* +type MyString = string + +fn test_string_alias() { + s := MyString('hi') + ss := s + '!' +} +*/ + +// sort an array of structs, by their string field values + +struct Ka { + s string + i int +} + +fn test_sorter() { + mut arr := [ + Ka{ + s: 'bbb' + i: 100 + }, + Ka{ + s: 'aaa' + i: 101 + }, + Ka{ + s: 'ccc' + i: 102 + }, + ] + cmp := fn (a &Ka, b &Ka) int { + return compare_strings(a.s, b.s) + } + arr.sort_with_compare(cmp) + assert arr[0].s == 'aaa' + assert arr[0].i == 101 + assert arr[1].s == 'bbb' + assert arr[1].i == 100 + assert arr[2].s == 'ccc' + assert arr[2].i == 102 +} + +fn test_fields() { + assert 'a bcde'.fields() == ['a', 'bcde'] + assert ' sss \t ssss '.fields() == ['sss', 'ssss'] + assert '\n xyz \t abc def'.fields() == ['xyz', 'abc', 'def'] + assert 'hello'.fields() == ['hello'] + assert ''.fields() == [] +} + +fn test_interpolation_after_quoted_variable_still_works() { + rr := 'abc' + tt := 'xyz' + + // Basic interpolation, no internal quotes + yy := 'Replacing $rr with $tt' + assert yy == 'Replacing abc with xyz' + + // Interpolation after quoted variable ending with 'r'quote + // that may be mistaken with the start of a raw string, + // ensure that it is not. + ss := 'Replacing "$rr" with "$tt"' + assert ss == 'Replacing "abc" with "xyz"' + zz := "Replacing '$rr' with '$tt'" + assert zz == "Replacing 'abc' with 'xyz'" + + // Interpolation after quoted variable ending with 'c'quote + // may be mistaken with the start of a c string, so + // check it is not. + cc := 'abc' + ccc := "Replacing '$cc' with '$tt'" + assert ccc == "Replacing 'abc' with 'xyz'" + cccq := 'Replacing "$cc" with "$tt"' + assert cccq == 'Replacing "abc" with "xyz"' +} + +fn test_emoji_to_runes() { + x := '👋' + assert x.runes()[0] == `👋` +} + +fn test_string_to_rune() { + x := 'Hello World 👋' + assert x.runes().len == 13 +} diff --git a/v_windows/v/vlib/builtin/utf8.c.v b/v_windows/v/vlib/builtin/utf8.c.v new file mode 100644 index 0000000..f2ead3a --- /dev/null +++ b/v_windows/v/vlib/builtin/utf8.c.v @@ -0,0 +1,79 @@ +module builtin + +const ( + cp_utf8 = 65001 +) + +pub fn (_str string) to_wide() &u16 { + $if windows { + unsafe { + num_chars := (C.MultiByteToWideChar(cp_utf8, 0, &char(_str.str), _str.len, + 0, 0)) + mut wstr := &u16(malloc_noscan((num_chars + 1) * 2)) // sizeof(wchar_t) + if wstr != 0 { + C.MultiByteToWideChar(cp_utf8, 0, &char(_str.str), _str.len, wstr, num_chars) + C.memset(&byte(wstr) + num_chars * 2, 0, 2) + } + return wstr + } + } $else { + return 0 + } +} + +[unsafe] +pub fn string_from_wide(_wstr &u16) string { + $if windows { + unsafe { + wstr_len := C.wcslen(_wstr) + return string_from_wide2(_wstr, wstr_len) + } + } $else { + return '' + } +} + +[unsafe] +pub fn string_from_wide2(_wstr &u16, len int) string { + $if windows { + unsafe { + num_chars := C.WideCharToMultiByte(cp_utf8, 0, _wstr, len, 0, 0, 0, 0) + mut str_to := malloc_noscan(num_chars + 1) + if str_to != 0 { + C.WideCharToMultiByte(cp_utf8, 0, _wstr, len, &char(str_to), num_chars, + 0, 0) + C.memset(str_to + num_chars, 0, 1) + } + return tos2(str_to) + } + } $else { + return '' + } +} + +// Reads an utf8 character from standard input +pub fn utf8_getchar() int { + c := C.getchar() + len := utf8_len(byte(~c)) + if c < 0 { + return 0 + } else if len == 0 { + return c + } else if len == 1 { + return -1 + } else { + mut uc := c & ((1 << (7 - len)) - 1) + for i := 0; i + 1 < len; i++ { + c2 := C.getchar() + if c2 != -1 && (c2 >> 6) == 2 { + uc <<= 6 + uc |= (c2 & 63) + } else if c2 == -1 { + return 0 + } else { + return -1 + } + } + return uc + } +} diff --git a/v_windows/v/vlib/builtin/utf8.v b/v_windows/v/vlib/builtin/utf8.v new file mode 100644 index 0000000..839c249 --- /dev/null +++ b/v_windows/v/vlib/builtin/utf8.v @@ -0,0 +1,191 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module builtin + +pub fn utf8_char_len(b byte) int { + return ((0xe5000000 >> ((b >> 3) & 0x1e)) & 3) + 1 +} + +// Convert utf32 to utf8 +// utf32 == Codepoint +pub fn utf32_to_str(code u32) string { + unsafe { + mut buffer := malloc_noscan(5) + res := utf32_to_str_no_malloc(code, buffer) + if res.len == 0 { + // the buffer was not used at all + free(buffer) + } + return res + } +} + +[manualfree; unsafe] +pub fn utf32_to_str_no_malloc(code u32, buf &byte) string { + unsafe { + icode := int(code) // Prevents doing casts everywhere + mut buffer := &byte(buf) + if icode <= 127 { + // 0x7F + buffer[0] = byte(icode) + buffer[1] = 0 + return tos(buffer, 1) + } else if icode <= 2047 { + // 0x7FF + buffer[0] = 192 | byte(icode >> 6) // 0xC0 - 110xxxxx + buffer[1] = 128 | byte(icode & 63) // 0x80 - 0x3F - 10xxxxxx + buffer[2] = 0 + return tos(buffer, 2) + } else if icode <= 65535 { + // 0xFFFF + buffer[0] = 224 | byte(icode >> 12) // 0xE0 - 1110xxxx + buffer[1] = 128 | (byte(icode >> 6) & 63) // 0x80 - 0x3F - 10xxxxxx + buffer[2] = 128 | byte(icode & 63) // 0x80 - 0x3F - 10xxxxxx + buffer[3] = 0 + return tos(buffer, 3) + } + // 0x10FFFF + else if icode <= 1114111 { + buffer[0] = 240 | byte(icode >> 18) // 0xF0 - 11110xxx + buffer[1] = 128 | (byte(icode >> 12) & 63) // 0x80 - 0x3F - 10xxxxxx + buffer[2] = 128 | (byte(icode >> 6) & 63) // 0x80 - 0x3F - 10xxxxxx + buffer[3] = 128 | byte(icode & 63) // 0x80 - 0x3F - 10xxxxxx + buffer[4] = 0 + return tos(buffer, 4) + } + } + return '' +} + +// Convert utf8 to utf32 +pub fn (_rune string) utf32_code() int { + if _rune.len == 0 { + return 0 + } + // save ASC symbol as is + if _rune.len == 1 { + return int(_rune[0]) + } + mut b := byte(int(_rune[0])) + // TODO should be + // res := int( rune[0] << rune.len) + b = b << _rune.len + mut res := int(b) + mut shift := 6 - _rune.len + for i := 1; i < _rune.len; i++ { + c := int(_rune[i]) + res = res << shift + res |= c & 63 // 0x3f + shift = 6 + } + return res +} + +// Calculate length to read from the first byte +fn utf8_len(c byte) int { + mut b := 0 + mut x := c + if (x & 240) != 0 { + // 0xF0 + x >>= 4 + } else { + b += 4 + } + if (x & 12) != 0 { + // 0x0C + x >>= 2 + } else { + b += 2 + } + if (x & 2) == 0 { + // 0x02 + b++ + } + return b +} + +// Calculate string length for in number of codepoints +pub fn utf8_str_len(s string) int { + mut l := 0 + mut i := 0 + for i < s.len { + l++ + i += ((0xe5000000 >> ((unsafe { s.str[i] } >> 3) & 0x1e)) & 3) + 1 + } + return l +} + +// Calculate string length for formatting, i.e. number of "characters" +// This is simplified implementation. if you need specification compliant width, +// use utf8.east_asian.display_width. +pub fn utf8_str_visible_length(s string) int { + mut l := 0 + mut ul := 1 + for i := 0; i < s.len; i += ul { + c := unsafe { s.str[i] } + ul = ((0xe5000000 >> ((unsafe { s.str[i] } >> 3) & 0x1e)) & 3) + 1 + if i + ul > s.len { // incomplete UTF-8 sequence + return l + } + l++ + // avoid the match if not needed + if ul == 1 { + continue + } + // recognize combining characters and wide characters + match ul { + 2 { + r := u64((u16(c) << 8) | unsafe { s.str[i + 1] }) + if r >= 0xcc80 && r < 0xcdb0 { + // diacritical marks + l-- + } + } + 3 { + r := u64((u32(c) << 16) | unsafe { (u32(s.str[i + 1]) << 8) | s.str[i + 2] }) + // diacritical marks extended + // diacritical marks supplement + // diacritical marks for symbols + if (r >= 0xe1aab0 && r <= 0xe1ac7f) + || (r >= 0xe1b780 && r <= 0xe1b87f) + || (r >= 0xe28390 && r <= 0xe2847f) + || (r >= 0xefb8a0 && r <= 0xefb8af) { + // diacritical marks + l-- + } + // Hangru + // CJK Unified Ideographics + // Hangru + // CJK + else if (r >= 0xe18480 && r <= 0xe1859f) + || (r >= 0xe2ba80 && r <= 0xe2bf95) + || (r >= 0xe38080 && r <= 0xe4b77f) + || (r >= 0xe4b880 && r <= 0xea807f) + || (r >= 0xeaa5a0 && r <= 0xeaa79f) + || (r >= 0xeab080 && r <= 0xed9eaf) + || (r >= 0xefa480 && r <= 0xefac7f) + || (r >= 0xefb8b8 && r <= 0xefb9af) { + // half marks + l++ + } + } + 4 { + r := u64((u32(c) << 24) | unsafe { + (u32(s.str[i + 1]) << 16) | (u32(s.str[i + 2]) << 8) | s.str[i + 3] + }) + // Enclosed Ideographic Supplement + // Emoji + // CJK Unified Ideographs Extension B-G + if (r >= 0x0f9f8880 && r <= 0xf09f8a8f) + || (r >= 0xf09f8c80 && r <= 0xf09f9c90) + || (r >= 0xf09fa490 && r <= 0xf09fa7af) + || (r >= 0xf0a08080 && r <= 0xf180807f) { + l++ + } + } + else {} + } + } + return l +} diff --git a/v_windows/v/vlib/builtin/utf8_test.v b/v_windows/v/vlib/builtin/utf8_test.v new file mode 100644 index 0000000..46983f1 --- /dev/null +++ b/v_windows/v/vlib/builtin/utf8_test.v @@ -0,0 +1,28 @@ +fn test_utf8_char_len() { + assert utf8_char_len(`a`) == 1 + println(utf8_char_len(`a`)) + s := 'п' + assert utf8_char_len(s[0]) == 2 +} + +fn test_utf8_wide_char() { + $if msvc { + // TODO: make this test pass msvc too + return + } + r := `✔` + s := '✔' + println('r: $r') + println('s: $s') + rstr := r.str() + println('rstr: $rstr') + assert utf8_char_len(r) == 1 + assert utf8_char_len(s[0]) == 3 + assert s == rstr + val := rstr.str + unsafe { + assert val[0].hex() == 'e2' + assert val[1].hex() == '9c' + assert val[2].hex() == '94' + } +} -- cgit v1.2.3