v / vlib / builtin
Raw file | 305 loc (284 sloc) | 7.94 KB | Latest commit hash f9043c84a
1// non-pub versions of array functions
2// that allocale new memory using `GC_MALLOC_ATOMIC()`
3// when `-gc boehm_*_opt` is used. These memory areas are not
4// scanned for pointers.
5
6module builtin
7
8fn __new_array_noscan(mylen int, cap int, elm_size int) array {
9 cap_ := if cap < mylen { mylen } else { cap }
10 arr := array{
11 element_size: elm_size
12 data: vcalloc_noscan(u64(cap_) * u64(elm_size))
13 len: mylen
14 cap: cap_
15 }
16 return arr
17}
18
19fn __new_array_with_default_noscan(mylen int, cap int, elm_size int, val voidptr) array {
20 cap_ := if cap < mylen { mylen } else { cap }
21 mut arr := array{
22 element_size: elm_size
23 data: vcalloc_noscan(u64(cap_) * u64(elm_size))
24 len: mylen
25 cap: cap_
26 }
27 if val != 0 && arr.data != unsafe { nil } {
28 if elm_size == 1 {
29 byte_value := *(&u8(val))
30 dptr := &u8(arr.data)
31 for i in 0 .. arr.len {
32 unsafe {
33 dptr[i] = byte_value
34 }
35 }
36 } else {
37 for i in 0 .. arr.len {
38 unsafe { arr.set_unsafe(i, val) }
39 }
40 }
41 }
42 return arr
43}
44
45fn __new_array_with_multi_default_noscan(mylen int, cap int, elm_size int, val voidptr) array {
46 cap_ := if cap < mylen { mylen } else { cap }
47 mut arr := array{
48 element_size: elm_size
49 data: vcalloc_noscan(u64(cap_) * u64(elm_size))
50 len: mylen
51 cap: cap_
52 }
53 if val != 0 && arr.data != unsafe { nil } {
54 for i in 0 .. arr.len {
55 unsafe { arr.set_unsafe(i, charptr(val) + i * elm_size) }
56 }
57 }
58 return arr
59}
60
61fn __new_array_with_array_default_noscan(mylen int, cap int, elm_size int, val array) array {
62 cap_ := if cap < mylen { mylen } else { cap }
63 mut arr := array{
64 element_size: elm_size
65 data: vcalloc_noscan(u64(cap_) * u64(elm_size))
66 len: mylen
67 cap: cap_
68 }
69 for i in 0 .. arr.len {
70 val_clone := val.clone()
71 unsafe { arr.set_unsafe(i, &val_clone) }
72 }
73 return arr
74}
75
76// Private function, used by V (`nums := [1, 2, 3]`)
77fn new_array_from_c_array_noscan(len int, cap int, elm_size int, c_array voidptr) array {
78 cap_ := if cap < len { len } else { cap }
79 arr := array{
80 element_size: elm_size
81 data: vcalloc_noscan(u64(cap_) * u64(elm_size))
82 len: len
83 cap: cap_
84 }
85 // TODO Write all memory functions (like memcpy) in V
86 unsafe { vmemcpy(arr.data, c_array, u64(len) * u64(elm_size)) }
87 return arr
88}
89
90// Private function. Doubles array capacity if needed.
91fn (mut a array) ensure_cap_noscan(required int) {
92 if required <= a.cap {
93 return
94 }
95 if a.flags.has(.nogrow) {
96 panic('array.ensure_cap_noscan: array with the flag `.nogrow` cannot grow in size, array required new size: ${required}')
97 }
98 mut cap := if a.cap > 0 { a.cap } else { 2 }
99 for required > cap {
100 cap *= 2
101 }
102 new_size := u64(cap) * u64(a.element_size)
103 new_data := vcalloc_noscan(new_size)
104 if a.data != unsafe { nil } {
105 unsafe { vmemcpy(new_data, a.data, u64(a.len) * u64(a.element_size)) }
106 // TODO: the old data may be leaked when no GC is used (ref-counting?)
107 }
108 a.data = new_data
109 a.offset = 0
110 a.cap = cap
111}
112
113// repeat returns a new array with the given array elements repeated given times.
114// `cgen` will replace this with an apropriate call to `repeat_to_depth()`
115
116// version of `repeat()` that handles multi dimensional arrays
117// `unsafe` to call directly because `depth` is not checked
118[unsafe]
119fn (a array) repeat_to_depth_noscan(count int, depth int) array {
120 if count < 0 {
121 panic('array.repeat: count is negative: ${count}')
122 }
123 mut size := u64(count) * u64(a.len) * u64(a.element_size)
124 if size == 0 {
125 size = u64(a.element_size)
126 }
127 arr := array{
128 element_size: a.element_size
129 data: if depth > 0 { vcalloc(size) } else { vcalloc_noscan(size) }
130 len: count * a.len
131 cap: count * a.len
132 }
133 if a.len > 0 {
134 a_total_size := u64(a.len) * u64(a.element_size)
135 arr_step_size := u64(a.len) * u64(arr.element_size)
136 mut eptr := &u8(arr.data)
137 unsafe {
138 for _ in 0 .. count {
139 if depth > 0 {
140 ary_clone := a.clone_to_depth_noscan(depth)
141 vmemcpy(eptr, &u8(ary_clone.data), a_total_size)
142 } else {
143 vmemcpy(eptr, &u8(a.data), a_total_size)
144 }
145 eptr += arr_step_size
146 }
147 }
148 }
149 return arr
150}
151
152// insert inserts a value in the array at index `i`
153fn (mut a array) insert_noscan(i int, val voidptr) {
154 $if !no_bounds_checking {
155 if i < 0 || i > a.len {
156 panic('array.insert: index out of range (i == ${i}, a.len == ${a.len})')
157 }
158 }
159 a.ensure_cap_noscan(a.len + 1)
160 unsafe {
161 vmemmove(a.get_unsafe(i + 1), a.get_unsafe(i), u64(a.len - i) * u64(a.element_size))
162 a.set_unsafe(i, val)
163 }
164 a.len++
165}
166
167// insert_many inserts many values into the array from index `i`.
168[unsafe]
169fn (mut a array) insert_many_noscan(i int, val voidptr, size int) {
170 $if !no_bounds_checking {
171 if i < 0 || i > a.len {
172 panic('array.insert_many: index out of range (i == ${i}, a.len == ${a.len})')
173 }
174 }
175 a.ensure_cap_noscan(a.len + size)
176 elem_size := a.element_size
177 unsafe {
178 iptr := a.get_unsafe(i)
179 vmemmove(a.get_unsafe(i + size), iptr, u64(a.len - i) * u64(elem_size))
180 vmemcpy(iptr, val, u64(size) * u64(elem_size))
181 }
182 a.len += size
183}
184
185// prepend prepends one value to the array.
186fn (mut a array) prepend_noscan(val voidptr) {
187 a.insert_noscan(0, val)
188}
189
190// prepend_many prepends another array to this array.
191[unsafe]
192fn (mut a array) prepend_many_noscan(val voidptr, size int) {
193 unsafe { a.insert_many_noscan(0, val, size) }
194}
195
196// pop returns the last element of the array, and removes it.
197fn (mut a array) pop_noscan() voidptr {
198 // in a sense, this is the opposite of `a << x`
199 $if !no_bounds_checking {
200 if a.len == 0 {
201 panic('array.pop: array is empty')
202 }
203 }
204 new_len := a.len - 1
205 last_elem := unsafe { &u8(a.data) + u64(new_len) * u64(a.element_size) }
206 a.len = new_len
207 // Note: a.cap is not changed here *on purpose*, so that
208 // further << ops on that array will be more efficient.
209 return unsafe { memdup_noscan(last_elem, a.element_size) }
210}
211
212// `clone_static_to_depth_noscan()` returns an independent copy of a given array.
213// Unlike `clone_to_depth_noscan()` it has a value receiver and is used internally
214// for slice-clone expressions like `a[2..4].clone()` and in -autofree generated code.
215fn (a array) clone_static_to_depth_noscan(depth int) array {
216 return unsafe { a.clone_to_depth_noscan(depth) }
217}
218
219// recursively clone given array - `unsafe` when called directly because depth is not checked
220[unsafe]
221fn (a &array) clone_to_depth_noscan(depth int) array {
222 mut size := u64(a.cap) * u64(a.element_size)
223 if size == 0 {
224 size++
225 }
226 mut arr := array{
227 element_size: a.element_size
228 data: if depth == 0 { vcalloc_noscan(size) } else { vcalloc(size) }
229 len: a.len
230 cap: a.cap
231 }
232 // Recursively clone-generated elements if array element is array type
233 if depth > 0 {
234 for i in 0 .. a.len {
235 ar := array{}
236 unsafe { vmemcpy(&ar, a.get_unsafe(i), int(sizeof(array))) }
237 ar_clone := unsafe { ar.clone_to_depth_noscan(depth - 1) }
238 unsafe { arr.set_unsafe(i, &ar_clone) }
239 }
240 return arr
241 } else {
242 if a.data != 0 {
243 unsafe { vmemcpy(&u8(arr.data), a.data, u64(a.cap) * u64(a.element_size)) }
244 }
245 return arr
246 }
247}
248
249fn (mut a array) push_noscan(val voidptr) {
250 a.ensure_cap_noscan(a.len + 1)
251 unsafe { vmemcpy(&u8(a.data) + u64(a.element_size) * u64(a.len), val, a.element_size) }
252 a.len++
253}
254
255// push_many implements the functionality for pushing another array.
256// `val` is array.data and user facing usage is `a << [1,2,3]`
257[unsafe]
258fn (mut a3 array) push_many_noscan(val voidptr, size int) {
259 if size <= 0 || val == unsafe { nil } {
260 return
261 }
262 if a3.data == val && a3.data != 0 {
263 // handle `arr << arr`
264 copy := a3.clone()
265 a3.ensure_cap_noscan(a3.len + size)
266 unsafe {
267 vmemcpy(a3.get_unsafe(a3.len), copy.data, u64(a3.element_size) * u64(size))
268 }
269 } else {
270 a3.ensure_cap_noscan(a3.len + size)
271 if a3.data != 0 && val != 0 {
272 unsafe { vmemcpy(a3.get_unsafe(a3.len), val, u64(a3.element_size) * u64(size)) }
273 }
274 }
275 a3.len += size
276}
277
278// reverse returns a new array with the elements of the original array in reverse order.
279fn (a array) reverse_noscan() array {
280 if a.len < 2 {
281 return a
282 }
283 mut arr := array{
284 element_size: a.element_size
285 data: vcalloc_noscan(u64(a.cap) * u64(a.element_size))
286 len: a.len
287 cap: a.cap
288 }
289 for i in 0 .. a.len {
290 unsafe { arr.set_unsafe(i, a.get_unsafe(a.len - 1 - i)) }
291 }
292 return arr
293}
294
295// grow_cap grows the array's capacity by `amount` elements.
296fn (mut a array) grow_cap_noscan(amount int) {
297 a.ensure_cap_noscan(a.cap + amount)
298}
299
300// grow_len ensures that an array has a.len + amount of length
301[unsafe]
302fn (mut a array) grow_len_noscan(amount int) {
303 a.ensure_cap_noscan(a.len + amount)
304 a.len += amount
305}