aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/vlib/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'v_windows/v/vlib/builtin')
-rw-r--r--v_windows/v/vlib/builtin/array.c.v1
-rw-r--r--v_windows/v/vlib/builtin/array.v644
-rw-r--r--v_windows/v/vlib/builtin/array_d_gcboehm_opt.v267
-rw-r--r--v_windows/v/vlib/builtin/array_notd_gcboehm_opt.v10
-rw-r--r--v_windows/v/vlib/builtin/array_test.v1507
-rw-r--r--v_windows/v/vlib/builtin/builtin.c.v547
-rw-r--r--v_windows/v/vlib/builtin/builtin.v128
-rw-r--r--v_windows/v/vlib/builtin/builtin_d_gcboehm.c.v91
-rw-r--r--v_windows/v/vlib/builtin/builtin_ios.c.v6
-rw-r--r--v_windows/v/vlib/builtin/builtin_nix.c.v144
-rw-r--r--v_windows/v/vlib/builtin/builtin_notd_gcboehm.c.v20
-rw-r--r--v_windows/v/vlib/builtin/builtin_windows.c.v304
-rw-r--r--v_windows/v/vlib/builtin/byte_test.v22
-rw-r--r--v_windows/v/vlib/builtin/cfns.c.v462
-rw-r--r--v_windows/v/vlib/builtin/cfns_wrapper.c.v72
-rw-r--r--v_windows/v/vlib/builtin/chan.v32
-rw-r--r--v_windows/v/vlib/builtin/float.c.v205
-rw-r--r--v_windows/v/vlib/builtin/float_test.v147
-rw-r--r--v_windows/v/vlib/builtin/float_x64.v6
-rw-r--r--v_windows/v/vlib/builtin/int.v481
-rw-r--r--v_windows/v/vlib/builtin/int_test.v241
-rw-r--r--v_windows/v/vlib/builtin/isnil_test.v19
-rw-r--r--v_windows/v/vlib/builtin/js/array.js.v253
-rw-r--r--v_windows/v/vlib/builtin/js/builtin.js.v61
-rw-r--r--v_windows/v/vlib/builtin/js/builtin.v84
-rw-r--r--v_windows/v/vlib/builtin/js/byte.js.v16
-rw-r--r--v_windows/v/vlib/builtin/js/int.js.v8
-rw-r--r--v_windows/v/vlib/builtin/js/jsfns.js.v125
-rw-r--r--v_windows/v/vlib/builtin/js/jsfns_browser.js.v59
-rw-r--r--v_windows/v/vlib/builtin/js/jsfns_node.js.v31
-rw-r--r--v_windows/v/vlib/builtin/js/map.js.v26
-rw-r--r--v_windows/v/vlib/builtin/js/string.js.v720
-rw-r--r--v_windows/v/vlib/builtin/linux_bare/libc_impl.v162
-rw-r--r--v_windows/v/vlib/builtin/linux_bare/linux_syscalls.v433
-rw-r--r--v_windows/v/vlib/builtin/linux_bare/memory_managment.v29
-rw-r--r--v_windows/v/vlib/builtin/linux_bare/old/.checks/.gitignore5
-rw-r--r--v_windows/v/vlib/builtin/linux_bare/old/.checks/checks.v32
-rw-r--r--v_windows/v/vlib/builtin/linux_bare/old/.checks/consts/consts.v22
-rw-r--r--v_windows/v/vlib/builtin/linux_bare/old/.checks/forkedtest/forkedtest.v49
-rw-r--r--v_windows/v/vlib/builtin/linux_bare/old/.checks/linuxsys/linuxsys.v300
-rw-r--r--v_windows/v/vlib/builtin/linux_bare/old/.checks/readme.md5
-rw-r--r--v_windows/v/vlib/builtin/linux_bare/old/.checks/sample_text1.txt1
-rw-r--r--v_windows/v/vlib/builtin/linux_bare/old/.checks/string/string.v63
-rw-r--r--v_windows/v/vlib/builtin/linux_bare/old/.checks/structs/structs.v42
-rw-r--r--v_windows/v/vlib/builtin/linux_bare/old/array_bare.v53
-rw-r--r--v_windows/v/vlib/builtin/linux_bare/old/builtin_bare.v60
-rw-r--r--v_windows/v/vlib/builtin/linux_bare/old/linuxsys_bare.v759
-rw-r--r--v_windows/v/vlib/builtin/linux_bare/old/mm_bare.v58
-rw-r--r--v_windows/v/vlib/builtin/linux_bare/old/string_bare.v150
-rw-r--r--v_windows/v/vlib/builtin/linux_bare/old/syscallwrapper_test.v27
-rw-r--r--v_windows/v/vlib/builtin/map.c.v79
-rw-r--r--v_windows/v/vlib/builtin/map.v716
-rw-r--r--v_windows/v/vlib/builtin/map_d_gcboehm_opt.v146
-rw-r--r--v_windows/v/vlib/builtin/map_of_floats_test.v27
-rw-r--r--v_windows/v/vlib/builtin/map_test.v947
-rw-r--r--v_windows/v/vlib/builtin/option.c.v15
-rw-r--r--v_windows/v/vlib/builtin/option.v89
-rw-r--r--v_windows/v/vlib/builtin/prealloc.c.v114
-rw-r--r--v_windows/v/vlib/builtin/rune.v65
-rw-r--r--v_windows/v/vlib/builtin/sorted_map.v457
-rw-r--r--v_windows/v/vlib/builtin/sorting_test.v84
-rw-r--r--v_windows/v/vlib/builtin/string.v1604
-rw-r--r--v_windows/v/vlib/builtin/string_charptr_byteptr_helpers.v104
-rw-r--r--v_windows/v/vlib/builtin/string_int_test.v221
-rw-r--r--v_windows/v/vlib/builtin/string_interpolation.v713
-rw-r--r--v_windows/v/vlib/builtin/string_strip_margin_test.v95
-rw-r--r--v_windows/v/vlib/builtin/string_test.v912
-rw-r--r--v_windows/v/vlib/builtin/utf8.c.v79
-rw-r--r--v_windows/v/vlib/builtin/utf8.v191
-rw-r--r--v_windows/v/vlib/builtin/utf8_test.v28
70 files changed, 15675 insertions, 0 deletions
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() == <error>
+ // assert nums.map(it, 'excessive') == <error>
+ // 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<T>(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 <gc.h>
+
+// 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
+
+// <string.h>
+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
+
+// <execinfo.h>
+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)
+
+// <libproc.h>
+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 <float.h>
+/*
+-----------------------------------
+----- 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<ImageBitmap>
+// fn JS.createImageBitmap(ImageBitmapSource, int, int, int, int, ImageBitmapOptions) Promise<ImageBitmap>
+
+// TODO: js async attribute
+// [js_async]
+// fn JS.fetch(RequestInfo, RequestInit) Promise<Response>
+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>',
+ '[/b]',
+ '</b>',
+ '[code]',
+ '<code>',
+ '[/code]',
+ '</code>',
+ ]) == '<b>bold</b> <code>code</code>'
+ bb2 := '[b]cool[/b]'
+ assert bb2.replace_each([
+ '[b]',
+ '<b>',
+ '[/b]',
+ '</b>',
+ ]) == '<b>cool</b>'
+ 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'
+ }
+}