diff --git a/vlib/builtin/array.v b/vlib/builtin/array.v index 16952c13ff..c974160b3f 100644 --- a/vlib/builtin/array.v +++ b/vlib/builtin/array.v @@ -107,7 +107,15 @@ fn (mut a array) ensure_cap(required int) { } // 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()` 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') } @@ -121,13 +129,10 @@ pub fn (a array) repeat(count int) array { len: count * a.len cap: count * a.len } - size_of_array := int(sizeof(array)) for i in 0 .. count { - if a.len > 0 && a.element_size == size_of_array { - ary := array{} - unsafe { C.memcpy(&ary, a.data, size_of_array) } - ary_clone := ary.clone() - unsafe { C.memcpy(arr.get_unsafe(i * a.len), &ary_clone, a.len * a.element_size) } + if a.len > 0 && depth > 0 { + ary_clone := unsafe { a.clone_to_depth(depth - 1) } + unsafe { C.memcpy(arr.get_unsafe(i * a.len), &byte(ary_clone.data), a.len * a.element_size) } } else { unsafe { C.memcpy(arr.get_unsafe(i * a.len), &byte(a.data), a.len * a.element_size) } } @@ -335,8 +340,19 @@ fn (a array) clone_static() array { return a.clone() } +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()` 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++ @@ -348,57 +364,20 @@ pub fn (a &array) clone() array { cap: a.cap } // Recursively clone-generated elements if array element is array type - size_of_array := int(sizeof(array)) - if a.element_size == size_of_array { - mut is_elem_array := true + if depth > 0 { for i in 0 .. a.len { ar := array{} - unsafe { C.memcpy(&ar, a.get_unsafe(i), size_of_array) } - if ar.len > ar.cap || ar.cap <= 0 || ar.element_size <= 0 { - is_elem_array = false - break - } - ar_clone := ar.clone() + unsafe { C.memcpy(&ar, a.get_unsafe(i), int(sizeof(array))) } + ar_clone := unsafe { ar.clone_to_depth(depth - 1) } unsafe { arr.set_unsafe(i, &ar_clone) } } - if is_elem_array { - return arr + return arr + } else { + if !isnil(a.data) { + unsafe { C.memcpy(&byte(arr.data), a.data, a.cap * a.element_size) } } + return arr } - - if !isnil(a.data) { - unsafe { C.memcpy(&byte(arr.data), a.data, a.cap * a.element_size) } - } - return arr -} - -fn (a &array) slice_clone(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)') - } - } - mut data := &byte(0) - offset := start * a.element_size - unsafe { - data = &byte(a.data) + offset - } - l := end - start - res := array{ - element_size: a.element_size - data: data - offset: offset - len: l - cap: l - } - return res.clone() } // we manually inline this for single operations for performance without -prod @@ -722,7 +701,16 @@ fn new_array_from_c_array_noscan(len int, cap int, elm_size int, c_array voidptr return arr } -fn (a array) repeat_noscan(count int) array { +// 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()` +pub fn (a array) repeat_noscan(count int) array { + return unsafe { a.repeat_to_depth_noscan(count, 0) } +} + +// 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') } @@ -732,16 +720,16 @@ fn (a array) repeat_noscan(count int) array { } arr := array{ element_size: a.element_size - data: vcalloc_noscan(size) + data: if depth > 0 { vcalloc(size) } else { vcalloc_noscan(size) } len: count * a.len cap: count * a.len } size_of_array := int(sizeof(array)) for i in 0 .. count { - if a.len > 0 && a.element_size == size_of_array { + if a.len > 0 && depth > 0 { ary := array{} unsafe { C.memcpy(&ary, a.data, size_of_array) } - ary_clone := ary.clone() + ary_clone := unsafe { ary.clone_to_depth_noscan(depth - 1) } unsafe { C.memcpy(arr.get_unsafe(i * a.len), &ary_clone, a.len * a.element_size) } } else { unsafe { C.memcpy(arr.get_unsafe(i * a.len), &byte(a.data), a.len * a.element_size) } @@ -750,67 +738,32 @@ fn (a array) repeat_noscan(count int) array { return arr } -pub fn (a &array) clone_noscan() array { +pub 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: vcalloc_noscan(size) + data: if depth > 0 { vcalloc(size) } else { vcalloc_noscan(size) } len: a.len cap: a.cap } // Recursively clone-generated elements if array element is array type - size_of_array := int(sizeof(array)) - if a.element_size == size_of_array { - mut is_elem_array := true + if depth > 0 { for i in 0 .. a.len { ar := array{} - unsafe { C.memcpy(&ar, a.get_unsafe(i), size_of_array) } - if ar.len > ar.cap || ar.cap <= 0 || ar.element_size <= 0 { - is_elem_array = false - break - } - ar_clone := ar.clone() + unsafe { C.memcpy(&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) } } - if is_elem_array { - return arr + return arr + } else { + if !isnil(a.data) { + unsafe { C.memcpy(&byte(arr.data), a.data, a.cap * a.element_size) } } + return arr } - - if !isnil(a.data) { - unsafe { C.memcpy(&byte(arr.data), a.data, a.cap * a.element_size) } - } - return arr -} - -fn (a &array) slice_clone_noscan(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)') - } - } - mut data := &byte(0) - unsafe { - data = &byte(a.data) + start * a.element_size - } - l := end - start - res := array{ - element_size: a.element_size - data: data - len: l - cap: l - } - return res.clone_noscan() } fn (a array) reverse_noscan() array { diff --git a/vlib/builtin/array_test.v b/vlib/builtin/array_test.v index 54646b007a..ad84541a46 100644 --- a/vlib/builtin/array_test.v +++ b/vlib/builtin/array_test.v @@ -233,6 +233,21 @@ fn test_repeat() { } } +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] @@ -367,7 +382,7 @@ fn test_copy() { assert b[2] == 3 } */ -fn test_mutli_array_clone() { +fn test_multi_array_clone() { // 2d array_int mut a2_1 := [[1, 2, 3], [4, 5, 6]] mut a2_2 := a2_1.clone() @@ -647,7 +662,7 @@ fn test_anon_fn_map() { assert [1, 2, 3].map(add_num) == [2, 3, 4] } -fn test_mutli_anon_fn_map() { +fn test_multi_anon_fn_map() { a := [1, 2, 3].map(fn (i int) int { return i + 1 }) @@ -987,7 +1002,7 @@ fn test_array_with_cap() { assert a5.cap == 10 } -fn test_mutli_array_index() { +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]]' diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index d890834a7b..84627eb058 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -2775,9 +2775,11 @@ fn (mut g Gen) gen_clone_assignment(val ast.Expr, right_sym ast.TypeSymbol, add_ if add_eq { g.write('=') } - g.write(' array_clone_static(') + g.write(' array_clone_static_to_depth(') g.expr(val) - g.write(')') + elem_type := (right_sym.info as ast.Array).elem_type + array_depth := g.get_array_depth(elem_type) + g.write(', $array_depth)') } else if g.is_autofree && right_sym.kind == .string { if add_eq { g.write('=') @@ -6601,6 +6603,17 @@ fn (mut g Gen) trace(fbase string, message string) { } } +pub fn (mut g Gen) get_array_depth(el_typ ast.Type) int { + typ := g.unwrap_generic(el_typ) + sym := g.table.get_final_type_symbol(typ) + if sym.kind == .array { + info := sym.info as ast.Array + return 1 + g.get_array_depth(info.elem_type) + } else { + return 0 + } +} + // returns true if `t` includes any pointer(s) - during garbage collection heap regions // that contain no pointers do not have to be scanned pub fn (mut g Gen) contains_ptr(el_typ ast.Type) bool { diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index c8f7dcf24b..edfe0223e3 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -649,7 +649,16 @@ fn (mut g Gen) method_call(node ast.CallExpr) { } } mut name := util.no_dots('${receiver_type_name}_$node.name') - if left_sym.kind == .chan { + mut array_depth := -1 + if left_sym.kind == .array { + if node.name in ['clone', 'repeat'] { + elem_type := (left_sym.info as ast.Array).elem_type + array_depth = g.get_array_depth(elem_type) + if node.name == 'repeat' { + array_depth++ + } + } + } else if left_sym.kind == .chan { if node.name in ['close', 'try_pop', 'try_push'] { name = 'sync__Channel_$node.name' } @@ -702,6 +711,9 @@ fn (mut g Gen) method_call(node ast.CallExpr) { if !node.receiver_type.is_ptr() && node.left_type.is_ptr() && node.name == 'str' { g.write('ptr_str(') } else { + if array_depth >= 0 { + name = name + '_to_depth' + } g.write('${name}(') } if node.receiver_type.is_ptr() && (!node.left_type.is_ptr() @@ -773,6 +785,9 @@ fn (mut g Gen) method_call(node ast.CallExpr) { */ // /////// g.call_args(node) + if array_depth >= 0 { + g.write(', $array_depth') + } g.write(')') } diff --git a/vlib/v/markused/markused.v b/vlib/v/markused/markused.v index bfddb8528a..ff8b53a021 100644 --- a/vlib/v/markused/markused.v +++ b/vlib/v/markused/markused.v @@ -83,11 +83,14 @@ pub fn mark_used(mut table ast.Table, pref &pref.Preferences, ast_files []&ast.F '21.set_unsafe', '21.get_with_check' /* used for `x := a[i] or {}` */, '21.clone_static', + '21.clone_static_to_depth', + '21.clone_to_depth', '21.first', '21.last', '21.pointers' /* TODO: handle generic methods calling array primitives more precisely in pool_test.v */, '21.reverse', '21.repeat', + '21.repeat_to_depth', '21.slice', '21.slice2', '59.get',