v2 / vlib / builtin / array_d_gcboehm_opt.v
450 lines · 425 sloc · 12.67 KB · a5e45bf44de100b9d8fc1781e9fe09837161e202
Raw
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
8@[inline]
9fn alloc_array_data_noscan(total_size u64) voidptr {
10 raw := vcalloc_noscan(array_data_allocation_size(total_size))
11 return unsafe { &u8(raw) + array_data_header_size() }
12}
13
14@[inline]
15fn alloc_array_data_noscan_uninit(total_size u64) voidptr {
16 raw := unsafe { malloc_noscan_uninit(array_data_allocation_size(total_size)) }
17 unsafe {
18 (&ArrayDataHeader(raw)).has_slices = false
19 return &u8(raw) + array_data_header_size()
20 }
21}
22
23@[inline]
24fn (mut a array) clone_shallow_to_cap_noscan(new_cap int) {
25 if new_cap <= 0 {
26 unsafe { a.flags.clear(.managed | .noscan_data | .is_slice) }
27 a.data = unsafe { nil }
28 a.offset = 0
29 a.cap = 0
30 return
31 }
32 total_size := u64(new_cap) * u64(a.element_size)
33 new_data := alloc_array_data_noscan_uninit(total_size)
34 copy_size := u64(a.len) * u64(a.element_size)
35 if a.data != unsafe { nil } && copy_size > 0 {
36 unsafe { vmemcpy(new_data, a.data, copy_size) }
37 }
38 a.data = new_data
39 a.offset = 0
40 a.cap = new_cap
41 unsafe { a.flags.set(.noscan_data) }
42 a.set_managed_flags(false)
43}
44
45fn __new_array_noscan(mylen int, cap int, elm_size int) array {
46 panic_on_negative_len(mylen)
47 panic_on_negative_cap(cap)
48 cap_ := if cap < mylen { mylen } else { cap }
49 total_size := u64(cap_) * u64(elm_size)
50 mut data := unsafe { nil }
51 if cap_ > 0 && mylen == 0 {
52 data = alloc_array_data_noscan_uninit(total_size)
53 } else {
54 data = alloc_array_data_noscan(total_size)
55 }
56 arr := array{
57 element_size: elm_size
58 data: data
59 len: mylen
60 cap: cap_
61 flags: .managed | .noscan_data
62 }
63 return arr
64}
65
66fn __new_array_with_default_noscan(mylen int, cap int, elm_size int, val voidptr) array {
67 panic_on_negative_len(mylen)
68 panic_on_negative_cap(cap)
69 cap_ := if cap < mylen { mylen } else { cap }
70 mut arr := array{
71 element_size: elm_size
72 data: alloc_array_data_noscan(u64(cap_) * u64(elm_size))
73 len: mylen
74 cap: cap_
75 flags: .managed | .noscan_data
76 }
77 if val != 0 && arr.data != unsafe { nil } {
78 if elm_size == 1 {
79 byte_value := *(&u8(val))
80 dptr := &u8(arr.data)
81 for i in 0 .. arr.len {
82 unsafe {
83 dptr[i] = byte_value
84 }
85 }
86 } else {
87 for i in 0 .. arr.len {
88 unsafe { arr.set_unsafe(i, val) }
89 }
90 }
91 }
92 return arr
93}
94
95fn __new_array_with_multi_default_noscan(mylen int, cap int, elm_size int, val voidptr) array {
96 panic_on_negative_len(mylen)
97 panic_on_negative_cap(cap)
98 cap_ := if cap < mylen { mylen } else { cap }
99 mut arr := array{
100 element_size: elm_size
101 data: alloc_array_data_noscan(u64(cap_) * u64(elm_size))
102 len: mylen
103 cap: cap_
104 flags: .managed | .noscan_data
105 }
106 if val != 0 && arr.data != unsafe { nil } {
107 for i in 0 .. arr.len {
108 unsafe { arr.set_unsafe(i, charptr(val) + i * elm_size) }
109 }
110 }
111 return arr
112}
113
114fn __new_array_with_array_default_noscan(mylen int, cap int, elm_size int, val array) array {
115 panic_on_negative_len(mylen)
116 panic_on_negative_cap(cap)
117 cap_ := if cap < mylen { mylen } else { cap }
118 mut arr := array{
119 element_size: elm_size
120 data: alloc_array_data_noscan(u64(cap_) * u64(elm_size))
121 len: mylen
122 cap: cap_
123 flags: .managed | .noscan_data
124 }
125 for i in 0 .. arr.len {
126 val_clone := val.clone()
127 unsafe { arr.set_unsafe(i, &val_clone) }
128 }
129 return arr
130}
131
132// Private function, used by V (`nums := [1, 2, 3]`)
133fn new_array_from_c_array_noscan(len int, cap int, elm_size int, c_array voidptr) array {
134 panic_on_negative_len(len)
135 panic_on_negative_cap(cap)
136 cap_ := if cap < len { len } else { cap }
137 arr := array{
138 element_size: elm_size
139 data: alloc_array_data_noscan(u64(cap_) * u64(elm_size))
140 len: len
141 cap: cap_
142 flags: .managed | .noscan_data
143 }
144 // TODO: Write all memory functions (like memcpy) in V
145 unsafe { vmemcpy(arr.data, c_array, u64(len) * u64(elm_size)) }
146 return arr
147}
148
149// Private function. Doubles array capacity if needed.
150fn (mut a array) ensure_cap_noscan(required int) {
151 if required <= a.cap {
152 return
153 }
154 if a.flags.has(.nogrow) {
155 panic_n('array.ensure_cap_noscan: array with the flag `.nogrow` cannot grow in size, array required new size:',
156 required)
157 }
158 mut cap := if a.cap > 0 { i64(a.cap) } else { i64(2) }
159 for required > cap {
160 cap *= 2
161 }
162 if cap > max_int {
163 if a.cap < max_int {
164 // limit the capacity, since bigger values, will overflow the 32bit integer used to store it
165 cap = max_int
166 } else {
167 panic_n('array.ensure_cap_noscan: array needs to grow to cap (which is > 2^31):', cap)
168 }
169 }
170 new_size := u64(cap) * u64(a.element_size)
171 new_data := alloc_array_data_noscan_uninit(new_size)
172 if a.data != unsafe { nil } {
173 unsafe { vmemcpy(new_data, a.data, u64(a.len) * u64(a.element_size)) }
174 // TODO: the old data may be leaked when no GC is used (ref-counting?)
175 }
176 a.data = new_data
177 a.offset = 0
178 a.cap = int(cap)
179 a.set_managed_flags(false)
180}
181
182// repeat returns a new array with the given array elements repeated given times.
183// `cgen` will replace this with an appropriate call to `repeat_to_depth()`
184
185// version of `repeat()` that handles multi dimensional arrays
186// `unsafe` to call directly because `depth` is not checked
187@[unsafe]
188fn (a array) repeat_to_depth_noscan(count int, depth int) array {
189 if count < 0 {
190 panic_n('array.repeat: count is negative:', count)
191 }
192 mut size := u64(count) * u64(a.len) * u64(a.element_size)
193 if size == 0 {
194 size = u64(a.element_size)
195 }
196 mut data := unsafe { nil }
197 if depth > 0 {
198 data = alloc_array_data(size)
199 } else {
200 data = alloc_array_data_noscan(size)
201 }
202 arr := array{
203 element_size: a.element_size
204 data: data
205 len: count * a.len
206 cap: count * a.len
207 flags: if depth == 0 { .managed | .noscan_data } else { .managed }
208 }
209 if a.len > 0 {
210 a_total_size := u64(a.len) * u64(a.element_size)
211 arr_step_size := u64(a.len) * u64(arr.element_size)
212 mut eptr := &u8(arr.data)
213 unsafe {
214 for _ in 0 .. count {
215 if depth > 0 {
216 ary_clone := a.clone_to_depth_noscan(depth)
217 vmemcpy(eptr, &u8(ary_clone.data), a_total_size)
218 } else {
219 vmemcpy(eptr, &u8(a.data), a_total_size)
220 }
221 eptr += arr_step_size
222 }
223 }
224 }
225 return arr
226}
227
228// insert inserts a value in the array at index `i`
229fn (mut a array) insert_noscan(i int, val voidptr) {
230 if i < 0 || i > a.len {
231 panic_n2('array.insert_noscan: index out of range (i,a.len):', i, a.len)
232 }
233 if a.len == max_int {
234 panic('array.insert_noscan: a.len reached max_int')
235 }
236 required := a.len + 1
237 if a.needs_unique_shift(required) {
238 a.clone_shallow_to_cap_noscan(a.cap)
239 } else if required > a.cap {
240 a.ensure_cap_noscan(required)
241 }
242 unsafe {
243 vmemmove(a.get_unsafe(i + 1), a.get_unsafe(i), u64(a.len - i) * u64(a.element_size))
244 a.set_unsafe(i, val)
245 }
246 a.len++
247}
248
249// insert_many inserts many values into the array from index `i`.
250@[unsafe]
251fn (mut a array) insert_many_noscan(i int, val voidptr, size int) {
252 if i < 0 || i > a.len {
253 panic_n2('array.insert_many: index out of range (i, a.len):', i, a.len)
254 }
255 new_len := i64(a.len) + i64(size)
256 if new_len > max_int {
257 panic_n('array.insert_many_noscan: max_int will be exceeded by a.len:', new_len)
258 }
259 if a.needs_unique_shift(int(new_len)) {
260 a.clone_shallow_to_cap_noscan(a.cap)
261 } else if int(new_len) > a.cap {
262 a.ensure_cap_noscan(int(new_len))
263 }
264 elem_size := a.element_size
265 unsafe {
266 iptr := a.get_unsafe(i)
267 vmemmove(a.get_unsafe(i + size), iptr, u64(a.len - i) * u64(elem_size))
268 vmemcpy(iptr, val, u64(size) * u64(elem_size))
269 }
270 a.len = int(new_len)
271}
272
273// prepend prepends one value to the array.
274fn (mut a array) prepend_noscan(val voidptr) {
275 a.insert_noscan(0, val)
276}
277
278// prepend_many prepends another array to this array.
279@[unsafe]
280fn (mut a array) prepend_many_noscan(val voidptr, size int) {
281 unsafe { a.insert_many_noscan(0, val, size) }
282}
283
284// pop_left returns the first element of the array and removes it by advancing the data pointer.
285fn (mut a array) pop_left_noscan() voidptr {
286 if a.len == 0 {
287 panic('array.pop_left: array is empty')
288 }
289 first_elem := a.data
290 unsafe {
291 a.data = &u8(a.data) + u64(a.element_size)
292 }
293 a.offset += a.element_size
294 a.len--
295 a.cap--
296 return unsafe { memdup_noscan(first_elem, a.element_size) }
297}
298
299// pop returns the last element of the array, and removes it.
300fn (mut a array) pop_noscan() voidptr {
301 // in a sense, this is the opposite of `a << x`
302 if a.len == 0 {
303 panic('array.pop: array is empty')
304 }
305 new_len := a.len - 1
306 last_elem := unsafe { &u8(a.data) + u64(new_len) * u64(a.element_size) }
307 if a.needs_unique_shrink() {
308 copy := unsafe { memdup_noscan(last_elem, a.element_size) }
309 a.delete_many(new_len, 1)
310 return copy
311 }
312 a.len = new_len
313 // Note: a.cap is not changed here *on purpose*, so that
314 // further << ops on that array will be more efficient.
315 return unsafe { memdup_noscan(last_elem, a.element_size) }
316}
317
318// `clone_static_to_depth_noscan()` returns an independent copy of a given array.
319// Unlike `clone_to_depth_noscan()` it has a value receiver and is used internally
320// for slice-clone expressions like `a[2..4].clone()` and in -autofree generated code.
321fn (a array) clone_static_to_depth_noscan(depth int) array {
322 return unsafe { a.clone_to_depth_noscan(depth) }
323}
324
325// recursively clone given array - `unsafe` when called directly because depth is not checked
326@[unsafe]
327fn (a &array) clone_to_depth_noscan(depth int) array {
328 mut size := u64(a.cap) * u64(a.element_size)
329 if size == 0 {
330 size++
331 }
332 mut data := unsafe { nil }
333 if depth == 0 {
334 data = alloc_array_data_noscan(size)
335 } else {
336 data = alloc_array_data(size)
337 }
338 mut arr := array{
339 element_size: a.element_size
340 data: data
341 len: a.len
342 cap: a.cap
343 flags: if depth == 0 { .managed | .noscan_data } else { .managed }
344 }
345 // Recursively clone-generated elements if array element is array type
346 if depth > 0 {
347 for i in 0 .. a.len {
348 ar := array{}
349 unsafe { vmemcpy(&ar, a.get_unsafe(i), int(sizeof(array))) }
350 ar_clone := unsafe { ar.clone_to_depth_noscan(depth - 1) }
351 unsafe { arr.set_unsafe(i, &ar_clone) }
352 }
353 return arr
354 } else {
355 if a.data != 0 {
356 unsafe { vmemcpy(&u8(arr.data), a.data, u64(a.cap) * u64(a.element_size)) }
357 }
358 return arr
359 }
360}
361
362fn (mut a array) push_noscan(val voidptr) {
363 if a.len < 0 {
364 panic('array.push_noscan: negative len')
365 }
366 if a.len >= max_int {
367 panic('array.push_noscan: len bigger than max_int')
368 }
369 required := a.len + 1
370 if a.needs_unique_append(required) {
371 a.clone_shallow_to_cap_noscan(a.cap)
372 } else if required > a.cap {
373 a.ensure_cap_noscan(required)
374 }
375 unsafe { vmemcpy(&u8(a.data) + u64(a.element_size) * u64(a.len), val, a.element_size) }
376 a.len++
377}
378
379// push_many implements the functionality for pushing another array.
380// `val` is array.data and user facing usage is `a << [1,2,3]`
381@[unsafe]
382fn (mut a array) push_many_noscan(val voidptr, size int) {
383 if size == 0 || val == unsafe { nil } {
384 return
385 }
386 new_len := i64(a.len) + i64(size)
387 if new_len > max_int {
388 // string interpolation also uses <<; avoid it, use a fixed string for the panic
389 panic('array.push_many_noscan: new len exceeds max_int')
390 }
391 if a.needs_unique_append(int(new_len)) {
392 a.clone_shallow_to_cap_noscan(a.cap)
393 }
394 if a.data == val && a.data != 0 {
395 // handle `arr << arr`
396 copy := a.clone()
397 if int(new_len) > a.cap {
398 a.ensure_cap_noscan(int(new_len))
399 }
400 unsafe {
401 vmemcpy(a.get_unsafe(a.len), copy.data, u64(a.element_size) * u64(size))
402 }
403 } else {
404 if int(new_len) > a.cap {
405 a.ensure_cap_noscan(int(new_len))
406 }
407 if a.data != 0 && val != 0 {
408 unsafe { vmemcpy(a.get_unsafe(a.len), val, u64(a.element_size) * u64(size)) }
409 }
410 }
411 a.len = int(new_len)
412}
413
414// reverse returns a new array with the elements of the original array in reverse order.
415fn (a array) reverse_noscan() array {
416 if a.len < 2 {
417 return a
418 }
419 mut arr := array{
420 element_size: a.element_size
421 data: alloc_array_data_noscan(u64(a.cap) * u64(a.element_size))
422 len: a.len
423 cap: a.cap
424 flags: .managed | .noscan_data
425 }
426 for i in 0 .. a.len {
427 unsafe { arr.set_unsafe(i, a.get_unsafe(a.len - 1 - i)) }
428 }
429 return arr
430}
431
432// grow_cap grows the array's capacity by `amount` elements.
433fn (mut a array) grow_cap_noscan(amount int) {
434 new_cap := i64(amount) + i64(a.cap)
435 if new_cap > max_int {
436 panic_n('array.grow_cap: max_int will be exceeded by new cap:', new_cap)
437 }
438 a.ensure_cap_noscan(int(new_cap))
439}
440
441// grow_len ensures that an array has a.len + amount of length
442@[unsafe]
443fn (mut a array) grow_len_noscan(amount int) {
444 new_len := i64(amount) + i64(a.len)
445 if new_len > max_int {
446 panic_n('array.grow_len: max_int will be exceeded by new len:', new_len)
447 }
448 a.ensure_cap_noscan(int(new_len))
449 a.len = int(new_len)
450}
451