v / vlib / builtin / array.v
1557 lines · 1456 sloc · 45.24 KB · 4a6645fd15543b5295ca56a658240f47341aa049
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module builtin
5
6import strings
7
8// array is a struct, used for denoting all array types in V.
9// `.data` is a void pointer to the backing heap memory block,
10// which avoids using generics and thus without generating extra
11// code for every type.
12pub struct array {
13pub mut:
14 data voidptr
15 offset int // in bytes (should be `usize`), to avoid copying data while making slices, unless it starts changing
16 len int // length of the array in elements.
17 cap int // capacity of the array in elements.
18 flags ArrayFlags
19pub:
20 element_size int // size in bytes of one element in the array.
21}
22
23@[flag]
24pub enum ArrayFlags {
25 noslices // when <<, `.noslices` will free the old data block immediately (you have to be sure, that there are *no slices* to that specific array). TODO: integrate with reference counting/compiler support for the static cases.
26 noshrink // kept for compatibility; shrinking array operations preserve capacity by default
27 nogrow // the array will never be allowed to grow past `.cap`
28 nofree // `.data` will never be freed
29 managed // `.data` uses the builtin managed array allocation with a metadata header
30 noscan_data // `.data` was allocated in a no-scan heap block and can stay atomic when cloned or resized
31 is_slice // this array is a slice view into another array's managed buffer
32}
33
34@[_packed]
35struct ArrayDataHeader {
36mut:
37 has_slices bool
38}
39
40// Must be aligned to at least the maximum fundamental type alignment (pointer size)
41// so that the array data following the header is properly aligned.
42//
43// Keep this as a function, not a const. When V bootstraps from generated C, a const
44// would bake in the snapshot generator's pointer size instead of the target C ABI.
45@[inline]
46fn array_data_header_size() int {
47 return int(sizeof(voidptr))
48}
49
50@[inline]
51fn array_data_allocation_size(total_size u64) u64 {
52 return u64(array_data_header_size()) + __at_least_one(total_size)
53}
54
55@[inline]
56fn alloc_array_data(total_size u64) voidptr {
57 raw := vcalloc(array_data_allocation_size(total_size))
58 return unsafe { &u8(raw) + array_data_header_size() }
59}
60
61@[inline]
62fn alloc_array_data_uninit(total_size u64) voidptr {
63 raw := unsafe { malloc_uninit(array_data_allocation_size(total_size)) }
64 unsafe {
65 (&ArrayDataHeader(raw)).has_slices = false
66 return &u8(raw) + array_data_header_size()
67 }
68}
69
70@[inline]
71fn (a array) uses_noscan_data() bool {
72 return a.flags.has(.noscan_data)
73}
74
75@[inline]
76fn (a array) alloc_array_data_like(total_size u64) voidptr {
77 $if gcboehm_opt ? {
78 if a.uses_noscan_data() {
79 return alloc_array_data_noscan(total_size)
80 }
81 }
82 return alloc_array_data(total_size)
83}
84
85@[inline]
86fn (a array) alloc_array_data_like_uninit(total_size u64) voidptr {
87 $if gcboehm_opt ? {
88 if a.uses_noscan_data() {
89 return alloc_array_data_noscan_uninit(total_size)
90 }
91 }
92 return alloc_array_data_uninit(total_size)
93}
94
95@[inline; unsafe]
96fn (a array) data_header() &ArrayDataHeader {
97 if !a.flags.has(.managed) || a.data == unsafe { nil } {
98 return unsafe { nil }
99 }
100 base_data := unsafe { &u8(a.data) - u64(a.offset) }
101 return unsafe { &ArrayDataHeader(base_data - array_data_header_size()) }
102}
103
104@[inline]
105fn (a array) buffer_has_slices() bool {
106 if !a.flags.has(.managed) || a.data == unsafe { nil } {
107 return false
108 }
109 header := unsafe { a.data_header() }
110 if header == unsafe { nil } {
111 return false
112 }
113 return unsafe { header.has_slices }
114}
115
116@[inline]
117fn (mut a array) mark_buffer_has_slices() {
118 if !a.flags.has(.managed) || a.data == unsafe { nil } {
119 return
120 }
121 unsafe {
122 header := a.data_header()
123 if header != nil {
124 header.has_slices = true
125 }
126 }
127}
128
129@[inline]
130fn (mut a array) set_managed_flags(is_slice bool) {
131 unsafe {
132 a.flags.set(.managed)
133 if is_slice {
134 a.flags.set(.is_slice)
135 } else {
136 a.flags.clear(.is_slice)
137 }
138 }
139}
140
141@[inline]
142fn (mut a array) clone_shallow_to_cap(new_cap int) {
143 if new_cap <= 0 {
144 unsafe { a.flags.clear(.managed | .noscan_data | .is_slice) }
145 a.data = unsafe { nil }
146 a.offset = 0
147 a.cap = 0
148 return
149 }
150 use_noscan_data := a.uses_noscan_data()
151 total_size := u64(new_cap) * u64(a.element_size)
152 new_data := a.alloc_array_data_like_uninit(total_size)
153 copy_size := u64(a.len) * u64(a.element_size)
154 if a.data != unsafe { nil } && copy_size > 0 {
155 unsafe { vmemcpy(new_data, a.data, copy_size) }
156 }
157 a.data = new_data
158 a.offset = 0
159 a.cap = new_cap
160 unsafe {
161 if use_noscan_data {
162 a.flags.set(.noscan_data)
163 } else {
164 a.flags.clear(.noscan_data)
165 }
166 }
167 a.set_managed_flags(false)
168}
169
170@[inline]
171fn v_ni_index(i int, len int) int {
172 return if i < 0 { len + i } else { i }
173}
174
175// Internal function, used by V (`nums := []int`)
176fn __new_array(mylen int, cap int, elm_size int) array {
177 panic_on_negative_len(mylen)
178 panic_on_negative_cap(cap)
179 cap_ := if cap < mylen { mylen } else { cap }
180 total_size := u64(cap_) * u64(elm_size)
181 mut data := unsafe { nil }
182 if cap_ > 0 && mylen == 0 {
183 data = alloc_array_data_uninit(total_size)
184 } else {
185 data = alloc_array_data(total_size)
186 }
187 arr := array{
188 element_size: elm_size
189 data: data
190 len: mylen
191 cap: cap_
192 flags: .managed
193 }
194 return arr
195}
196
197fn __new_array_with_default(mylen int, cap int, elm_size int, val voidptr) array {
198 panic_on_negative_len(mylen)
199 panic_on_negative_cap(cap)
200 cap_ := if cap < mylen { mylen } else { cap }
201 mut arr := array{
202 element_size: elm_size
203 len: mylen
204 cap: cap_
205 flags: .managed
206 }
207 // x := []EmptyStruct{cap:5} ; for clang/gcc with -gc none,
208 // -> sizeof(EmptyStruct) == 0 -> elm_size == 0
209 // -> total_size == 0 -> malloc(0) -> panic;
210 // to avoid it, just allocate a single byte
211 total_size := u64(cap_) * u64(elm_size)
212 if cap_ > 0 && mylen == 0 {
213 arr.data = alloc_array_data_uninit(total_size)
214 } else {
215 arr.data = alloc_array_data(total_size)
216 }
217 if val != 0 {
218 mut eptr := &u8(arr.data)
219 unsafe {
220 if eptr != nil {
221 if arr.element_size == 1 {
222 byte_value := *(&u8(val))
223 for i in 0 .. arr.len {
224 eptr[i] = byte_value
225 }
226 } else {
227 for _ in 0 .. arr.len {
228 vmemcpy(eptr, val, arr.element_size)
229 eptr += arr.element_size
230 }
231 }
232 }
233 }
234 }
235 return arr
236}
237
238fn __new_array_with_multi_default(mylen int, cap int, elm_size int, val voidptr) array {
239 panic_on_negative_len(mylen)
240 panic_on_negative_cap(cap)
241 cap_ := if cap < mylen { mylen } else { cap }
242 mut arr := array{
243 element_size: elm_size
244 len: mylen
245 cap: cap_
246 flags: .managed
247 }
248 // x := []EmptyStruct{cap:5} ; for clang/gcc with -gc none,
249 // -> sizeof(EmptyStruct) == 0 -> elm_size == 0
250 // -> total_size == 0 -> malloc(0) -> panic;
251 // to avoid it, just allocate a single byte
252 total_size := u64(cap_) * u64(elm_size)
253 arr.data = alloc_array_data(total_size)
254 if val != 0 {
255 mut eptr := &u8(arr.data)
256 unsafe {
257 if eptr != nil {
258 for i in 0 .. arr.len {
259 vmemcpy(eptr, charptr(val) + i * arr.element_size, arr.element_size)
260 eptr += arr.element_size
261 }
262 }
263 }
264 }
265 return arr
266}
267
268fn __new_array_with_array_default(mylen int, cap int, elm_size int, val array, depth int) array {
269 panic_on_negative_len(mylen)
270 panic_on_negative_cap(cap)
271 cap_ := if cap < mylen { mylen } else { cap }
272 mut arr := array{
273 element_size: elm_size
274 data: alloc_array_data(u64(cap_) * u64(elm_size))
275 len: mylen
276 cap: cap_
277 flags: .managed
278 }
279 mut eptr := &u8(arr.data)
280 unsafe {
281 if eptr != nil {
282 for _ in 0 .. arr.len {
283 val_clone := val.clone_to_depth(depth)
284 vmemcpy(eptr, &val_clone, arr.element_size)
285 eptr += arr.element_size
286 }
287 }
288 }
289 return arr
290}
291
292fn __new_array_with_map_default(mylen int, cap int, elm_size int, val map) array {
293 panic_on_negative_len(mylen)
294 panic_on_negative_cap(cap)
295 cap_ := if cap < mylen { mylen } else { cap }
296 mut arr := array{
297 element_size: elm_size
298 data: alloc_array_data(u64(cap_) * u64(elm_size))
299 len: mylen
300 cap: cap_
301 flags: .managed
302 }
303 mut eptr := &u8(arr.data)
304 unsafe {
305 if eptr != nil {
306 for _ in 0 .. arr.len {
307 val_clone := val.clone()
308 vmemcpy(eptr, &val_clone, arr.element_size)
309 eptr += arr.element_size
310 }
311 }
312 }
313 return arr
314}
315
316// Private function, used by V (`nums := [1, 2, 3]`)
317fn new_array_from_c_array(len int, cap int, elm_size int, c_array voidptr) array {
318 panic_on_negative_len(len)
319 panic_on_negative_cap(cap)
320 mut cap_ := cap
321 if cap < len {
322 cap_ = len
323 }
324 arr := array{
325 element_size: elm_size
326 data: alloc_array_data(u64(cap_) * u64(elm_size))
327 len: len
328 cap: cap_
329 flags: .managed
330 }
331 // TODO: Write all memory functions (like memcpy) in V
332 unsafe { vmemcpy(arr.data, c_array, u64(len) * u64(elm_size)) }
333 return arr
334}
335
336// Private function, used by V (`merged := [...base, 4, 5]`).
337// `base` is already an independent (typed/deep) clone produced by the caller
338// (cgen emits `array_clone_static_to_depth(base, depth)`), so this helper just
339// appends `new_count` additional elements stored contiguously at `c_array`.
340fn new_array_from_array_and_c_array(base array, new_count int, elm_size int, c_array voidptr) array {
341 panic_on_negative_len(new_count)
342 mut arr := base
343 if new_count > 0 && c_array != unsafe { nil } {
344 unsafe { arr.push_many(c_array, new_count) }
345 }
346 return arr
347}
348
349// Private function, used by V (`nums := [1, 2, 3] !`)
350fn new_array_from_c_array_no_alloc(len int, cap int, elm_size int, c_array voidptr) array {
351 panic_on_negative_len(len)
352 panic_on_negative_cap(cap)
353 arr := array{
354 element_size: elm_size
355 data: c_array
356 len: len
357 cap: cap
358 }
359 return arr
360}
361
362// ensure_cap increases the `cap` of an array to the required value, if needed.
363// It does so by copying the data to a new memory location (creating a clone),
364// unless `a.cap` is already large enough.
365pub fn (mut a array) ensure_cap(required int) {
366 if required <= a.cap {
367 return
368 }
369 if a.flags.has(.nogrow) {
370 panic_n('array.ensure_cap: array with the flag `.nogrow` cannot grow in size, array required new size:',
371 required)
372 }
373 mut cap := if a.cap > 0 { i64(a.cap) } else { i64(2) }
374 for required > cap {
375 cap *= 2
376 }
377 if cap > max_int {
378 if a.cap < max_int {
379 // limit the capacity, since bigger values, will overflow the 32bit integer used to store it
380 cap = max_int
381 } else {
382 panic_n('array.ensure_cap: array needs to grow to cap (which is > 2^31):', cap)
383 }
384 }
385 new_size := u64(cap) * u64(a.element_size)
386 use_noscan_data := a.uses_noscan_data()
387 new_data := a.alloc_array_data_like_uninit(new_size)
388 if a.data != unsafe { nil } {
389 unsafe { vmemcpy(new_data, a.data, u64(a.len) * u64(a.element_size)) }
390 // TODO: the old data may be leaked when no GC is used (ref-counting?)
391 if a.flags.has(.noslices) && !a.flags.has(.is_slice) && !a.buffer_has_slices() {
392 unsafe {
393 if a.flags.has(.managed) {
394 free(&u8(a.data) - u64(array_data_header_size()))
395 } else {
396 free(a.data)
397 }
398 }
399 }
400 }
401 a.data = new_data
402 a.offset = 0
403 a.cap = int(cap)
404 unsafe {
405 if use_noscan_data {
406 a.flags.set(.noscan_data)
407 } else {
408 a.flags.clear(.noscan_data)
409 }
410 }
411 a.set_managed_flags(false)
412}
413
414// repeat returns a new array with the given array elements repeated given times.
415// `cgen` will replace this with an appropriate call to `repeat_to_depth()`
416//
417// This is a dummy placeholder that will be overridden by `cgen` with an appropriate
418// call to `repeat_to_depth()`. However the `checker` needs it here.
419pub fn (a array) repeat(count int) array {
420 return unsafe { a.repeat_to_depth(count, 0) }
421}
422
423// repeat_to_depth is an unsafe version of `repeat()` that handles
424// multi-dimensional arrays.
425//
426// It is `unsafe` to call directly because `depth` is not checked
427@[direct_array_access; unsafe]
428pub fn (a array) repeat_to_depth(count int, depth int) array {
429 if count < 0 {
430 panic_n('array.repeat: count is negative:', count)
431 }
432 mut size := u64(count) * u64(a.len) * u64(a.element_size)
433 if size == 0 {
434 size = u64(a.element_size)
435 }
436 use_noscan_data := depth == 0 && a.uses_noscan_data()
437 mut data := unsafe { nil }
438 if use_noscan_data {
439 data = a.alloc_array_data_like(size)
440 } else {
441 data = alloc_array_data(size)
442 }
443 arr := array{
444 element_size: a.element_size
445 data: data
446 len: count * a.len
447 cap: count * a.len
448 flags: if use_noscan_data { .managed | .noscan_data } else { .managed }
449 }
450 if a.len > 0 {
451 a_total_size := u64(a.len) * u64(a.element_size)
452 arr_step_size := u64(a.len) * u64(arr.element_size)
453 mut eptr := &u8(arr.data)
454 unsafe {
455 if eptr != nil {
456 for _ in 0 .. count {
457 if depth > 0 {
458 ary_clone := a.clone_to_depth(depth)
459 vmemcpy(eptr, ary_clone.data, a_total_size)
460 } else {
461 vmemcpy(eptr, a.data, a_total_size)
462 }
463 eptr += arr_step_size
464 }
465 }
466 }
467 }
468 return arr
469}
470
471@[inline]
472fn (a array) needs_unique_shift(required int) bool {
473 return required <= a.cap && (a.flags.has(.is_slice) || a.buffer_has_slices())
474}
475
476@[inline]
477fn (a array) needs_unique_append(required int) bool {
478 return required <= a.cap && a.flags.has(.is_slice)
479}
480
481@[inline]
482fn (a array) needs_unique_shrink() bool {
483 return a.flags.has(.is_slice) || a.buffer_has_slices()
484}
485
486// insert inserts a value in the array at index `i` and increases
487// the index of subsequent elements by 1.
488//
489// This function is type-aware and can insert items of the same
490// or lower dimensionality as the original array. That is, if
491// the original array is `[]int`, then the insert `val` may be
492// `int` or `[]int`. If the original array is `[][]int`, then `val`
493// may be `[]int` or `[][]int`. Consider the examples.
494//
495// Example:
496// ```v
497// mut a := [1, 2, 4]
498// a.insert(2, 3) // a now is [1, 2, 3, 4]
499// mut b := [3, 4]
500// b.insert(0, [1, 2]) // b now is [1, 2, 3, 4]
501// mut c := [[3, 4]]
502// c.insert(0, [1, 2]) // c now is [[1, 2], [3, 4]]
503// ```
504pub fn (mut a array) insert(i int, val voidptr) {
505 if i < 0 || i > a.len {
506 panic_n2('array.insert: index out of range (i,a.len):', i, a.len)
507 }
508 if a.len == max_int {
509 panic('array.insert: a.len reached max_int')
510 }
511 required := a.len + 1
512 if a.needs_unique_shift(required) {
513 a.clone_shallow_to_cap(a.cap)
514 } else if required > a.cap {
515 a.ensure_cap(required)
516 }
517 unsafe {
518 vmemmove(a.get_unsafe(i + 1), a.get_unsafe(i), u64((a.len - i)) * u64(a.element_size))
519 a.set_unsafe(i, val)
520 }
521 a.len++
522}
523
524// insert_many is used internally to implement inserting many values
525// into an the array beginning at `i`.
526@[unsafe]
527fn (mut a array) insert_many(i int, val voidptr, size int) {
528 if i < 0 || i > a.len {
529 panic_n2('array.insert_many: index out of range (i,a.len):', i, a.len)
530 }
531 new_len := i64(a.len) + i64(size)
532 if new_len > max_int {
533 panic_n('array.insert_many: max_int will be exceeded by a.len:', new_len)
534 }
535 if a.needs_unique_shift(int(new_len)) {
536 a.clone_shallow_to_cap(a.cap)
537 } else if int(new_len) > a.cap {
538 a.ensure_cap(int(new_len))
539 }
540 elem_size := a.element_size
541 unsafe {
542 iptr := a.get_unsafe(i)
543 vmemmove(a.get_unsafe(i + size), iptr, u64(a.len - i) * u64(elem_size))
544 vmemcpy(iptr, val, u64(size) * u64(elem_size))
545 }
546 a.len = int(new_len)
547}
548
549// prepend prepends one or more elements to an array.
550// It is shorthand for `.insert(0, val)`
551pub fn (mut a array) prepend(val voidptr) {
552 a.insert(0, val)
553}
554
555// prepend_many prepends another array to this array.
556// NOTE: `.prepend` is probably all you need.
557// NOTE: This code is never called in all of vlib
558@[unsafe]
559fn (mut a array) prepend_many(val voidptr, size int) {
560 unsafe { a.insert_many(0, val, size) }
561}
562
563// delete deletes array element at index `i`, preserving element order.
564// For unique arrays, it shifts elements left in-place, preserves spare
565// capacity, and clears the obsolete tail slot.
566// If the backing buffer is shared with slices, delete creates a detached
567// array, so existing slices keep their view of the old contents.
568//
569// Example:
570// ```v
571// mut a := ['0', '1', '2', '3', '4', '5']
572// a.delete(1) // a is now ['0', '2', '3', '4', '5']
573// ```
574pub fn (mut a array) delete(i int) {
575 if i < 0 || i >= a.len {
576 panic_n2('array.delete: index out of range (i,a.len):', i, a.len)
577 }
578 if i == a.len - 1 && !a.needs_unique_shrink() {
579 a.len--
580 unsafe {
581 vmemset(&u8(a.data) + u64(a.len) * u64(a.element_size), 0, u64(a.element_size))
582 }
583 return
584 }
585 a.delete_many(i, 1)
586}
587
588// delete_many deletes `size` elements beginning with index `i`
589// For unique arrays, it shifts elements left in-place, preserves spare
590// capacity, and clears the obsolete tail range.
591// If the backing buffer is shared with slices, delete_many creates a
592// detached array, so existing slices keep their view of the old contents.
593//
594// Example:
595// ```v
596// mut a := [1, 2, 3, 4, 5, 6, 7, 8, 9]
597// b := unsafe { a[..9] } // creates a `slice` of `a`, not a clone
598// a.delete_many(4, 3) // replaces `a` with a modified clone
599// dump(a) // a: [1, 2, 3, 4, 8, 9] // `a` is now different
600// dump(b) // b: [1, 2, 3, 4, 5, 6, 7, 8, 9] // `b` is still the same
601// ```
602pub fn (mut a array) delete_many(i int, size int) {
603 if i < 0 || i64(i) + i64(size) > i64(a.len) {
604 if size > 1 {
605 panic_n3('array.delete: index out of range (i,i+size,a.len):', i, i + size, a.len)
606 } else {
607 panic_n2('array.delete: index out of range (i,a.len):', i, a.len)
608 }
609 }
610 if size == 0 {
611 if a.needs_unique_shrink() {
612 a.clone_shallow_to_cap(a.len)
613 }
614 return
615 }
616 if !a.needs_unique_shrink() {
617 new_len := a.len - size
618 unsafe {
619 vmemmove(&u8(a.data) + u64(i) * u64(a.element_size), &u8(a.data) + u64(i +
620 size) * u64(a.element_size), u64(a.len - i - size) * u64(a.element_size))
621 vmemset(&u8(a.data) + u64(new_len) * u64(a.element_size), 0,
622 u64(size) * u64(a.element_size))
623 }
624 a.len = new_len
625 return
626 }
627 // Note: if a is [12,34], a.len = 2, a.delete(0)
628 // should move (2-0-1) elements = 1 element (the 34) forward
629 old_data := a.data
630 new_size := a.len - size
631 if new_size == 0 {
632 unsafe { a.flags.clear(.managed | .noscan_data | .is_slice) }
633 a.data = unsafe { nil }
634 a.offset = 0
635 a.len = 0
636 a.cap = 0
637 return
638 }
639 new_cap := new_size
640 use_noscan_data := a.uses_noscan_data()
641 a.data = a.alloc_array_data_like(u64(new_cap) * u64(a.element_size))
642 unsafe { vmemcpy(a.data, old_data, u64(i) * u64(a.element_size)) }
643 unsafe {
644 vmemcpy(&u8(a.data) + u64(i) * u64(a.element_size), &u8(old_data) + u64(i +
645 size) * u64(a.element_size), u64(a.len - i - size) * u64(a.element_size))
646 }
647 if a.flags.has(.noslices) && !a.flags.has(.managed) {
648 unsafe {
649 free(old_data)
650 }
651 }
652 a.len = new_size
653 a.cap = new_cap
654 a.offset = 0
655 unsafe {
656 if use_noscan_data {
657 a.flags.set(.noscan_data)
658 } else {
659 a.flags.clear(.noscan_data)
660 }
661 }
662 a.set_managed_flags(false)
663}
664
665// clear clears the array without deallocating the allocated data.
666// It does it by setting the array length to `0`
667// Example: mut a := [1,2]; a.clear(); assert a.len == 0
668pub fn (mut a array) clear() {
669 if a.needs_unique_shrink() {
670 unsafe { a.flags.clear(.managed | .noscan_data | .is_slice) }
671 a.data = unsafe { nil }
672 a.offset = 0
673 a.cap = 0
674 }
675 a.len = 0
676}
677
678// reset quickly sets the bytes of all elements of the array to 0.
679// Useful mainly for numeric arrays. Note, that calling reset()
680// is not safe, when your array contains more complex elements,
681// like structs, maps, pointers etc, since setting them to 0,
682// can later lead to hard to find bugs.
683@[unsafe]
684pub fn (mut a array) reset() {
685 unsafe { vmemset(a.data, 0, a.len * a.element_size) }
686}
687
688// trim trims the array length to `index` without modifying the allocated data.
689// If `index` is greater than `len` nothing will be changed.
690// Example: mut a := [1,2,3,4]; a.trim(3); assert a.len == 3
691pub fn (mut a array) trim(index int) {
692 if index < a.len {
693 if index >= 0 && a.needs_unique_shrink() {
694 a.delete_many(index, a.len - index)
695 return
696 }
697 a.len = index
698 }
699}
700
701// drop advances the array past the first `num` elements whilst preserving spare capacity.
702// If `num` is greater than `len` the array will be emptied.
703// Example:
704// ```v
705// mut a := [1,2]
706// a << 3
707// a.drop(2)
708// assert a == [3]
709// assert a.cap > a.len
710// ```
711pub fn (mut a array) drop(num int) {
712 if num <= 0 {
713 return
714 }
715 n := if num <= a.len { num } else { a.len }
716 blen := u64(n) * u64(a.element_size)
717 a.data = unsafe { &u8(a.data) + blen }
718 a.offset += int(blen) // TODO: offset should become 64bit as well
719 a.len -= n
720 a.cap -= n
721}
722
723// we manually inline this for single operations for performance without -prod
724@[inline; unsafe]
725fn (a array) get_unsafe(i int) voidptr {
726 unsafe {
727 return &u8(a.data) + u64(i) * u64(a.element_size)
728 }
729}
730
731// Private function. Used to implement array[] operator.
732fn (a array) get(i int) voidptr {
733 $if !no_bounds_checking {
734 if i < 0 || i >= a.len {
735 panic_n2('array.get: index out of range (i,a.len):', i, a.len)
736 }
737 }
738 unsafe {
739 return &u8(a.data) + u64(i) * u64(a.element_size)
740 }
741}
742
743@[markused]
744fn (a array) get_i64(i i64) voidptr {
745 $if !no_bounds_checking {
746 if i < 0 || i >= i64(a.len) {
747 panic_n2('array.get: index out of range (i,a.len):', i, a.len)
748 }
749 }
750 unsafe {
751 return &u8(a.data) + u64(i) * u64(a.element_size)
752 }
753}
754
755@[markused]
756fn (a array) get_u64(i u64) voidptr {
757 $if !no_bounds_checking {
758 if i >= u64(a.len) {
759 panic('array.get: index out of range (i,a.len): ' + i.str() + ', ' +
760 impl_i64_to_string(a.len))
761 }
762 }
763 unsafe {
764 return &u8(a.data) + i * u64(a.element_size)
765 }
766}
767
768@[markused]
769fn (a array) get_ni(i int) voidptr {
770 return a.get(v_ni_index(i, a.len))
771}
772
773// Private function. Used to implement x = a[i] or { ... }
774fn (a array) get_with_check(i int) voidptr {
775 if i < 0 || i >= a.len {
776 return 0
777 }
778 unsafe {
779 return &u8(a.data) + u64(i) * u64(a.element_size)
780 }
781}
782
783@[markused]
784fn (a array) get_with_check_i64(i i64) voidptr {
785 if i < 0 || i >= i64(a.len) {
786 return 0
787 }
788 unsafe {
789 return &u8(a.data) + u64(i) * u64(a.element_size)
790 }
791}
792
793@[markused]
794fn (a array) get_with_check_u64(i u64) voidptr {
795 if i >= u64(a.len) {
796 return 0
797 }
798 unsafe {
799 return &u8(a.data) + i * u64(a.element_size)
800 }
801}
802
803@[markused]
804fn (a array) get_with_check_ni(i int) voidptr {
805 return a.get_with_check(v_ni_index(i, a.len))
806}
807
808// first returns the first element of the `array`.
809// If the `array` is empty, this will panic.
810// However, `a[0]` returns an error object
811// so it can be handled with an `or` block.
812pub fn (a array) first() voidptr {
813 if a.len == 0 {
814 panic('array.first: array is empty')
815 }
816 return a.data
817}
818
819// last returns the last element of the `array`.
820// If the `array` is empty, this will panic.
821pub fn (a array) last() voidptr {
822 if a.len == 0 {
823 panic('array.last: array is empty')
824 }
825 unsafe {
826 return &u8(a.data) + u64(a.len - 1) * u64(a.element_size)
827 }
828}
829
830// pop_left returns the first element of the array and removes it by advancing the data pointer.
831// If the `array` is empty, this will panic.
832// NOTE: This function:
833// - Reduces both length and capacity by 1
834// - Advances the underlying data pointer by one element
835// - Leaves subsequent elements in-place (no memory copying)
836// Sliced views will retain access to the original first element position,
837// which is now detached from the array's active memory range.
838//
839// Example:
840// ```v
841// mut a := [1, 2, 3, 4, 5]
842// b := unsafe { a[..5] } // full slice view
843// first := a.pop_left()
844//
845// // Array now starts from second element
846// dump(a) // a: [2, 3, 4, 5]
847// assert a.len == 4
848// assert a.cap == 4
849//
850// // Slice retains original memory layout
851// dump(b) // b: [1, 2, 3, 4, 5]
852// assert b.len == 5
853//
854// assert first == 1
855//
856// // Modifications affect both array and slice views
857// a[0] = 99
858// assert b[1] == 99 // changed in both
859// ```
860pub fn (mut a array) pop_left() voidptr {
861 if a.len == 0 {
862 panic('array.pop_left: array is empty')
863 }
864 first_elem := a.data
865 unsafe {
866 a.data = &u8(a.data) + u64(a.element_size)
867 }
868 a.offset += a.element_size
869 a.len--
870 a.cap--
871 return first_elem
872}
873
874// pop returns the last element of the array, and removes it.
875// If the `array` is empty, this will panic.
876// NOTE: this function reduces the length of the given array,
877// but arrays sliced from this one will not change. They still
878// retain their "view" of the underlying memory.
879//
880// Example:
881// ```v
882// mut a := [1, 2, 3, 4, 5, 6, 7, 8, 9]
883// b := unsafe{ a[..9] } // creates a "view" (also called a slice) into the same memory
884// c := a.pop()
885// assert c == 9
886// a[1] = 5
887// dump(a) // a: [1, 5, 3, 4, 5, 6, 7, 8]
888// dump(b) // b: [1, 5, 3, 4, 5, 6, 7, 8, 9]
889// assert a.len == 8
890// assert b.len == 9
891// ```
892pub fn (mut a array) pop() voidptr {
893 // in a sense, this is the opposite of `a << x`
894 if a.len == 0 {
895 panic('array.pop: array is empty')
896 }
897 new_len := a.len - 1
898 last_elem := unsafe { &u8(a.data) + u64(new_len) * u64(a.element_size) }
899 if a.needs_unique_shrink() {
900 a.delete_many(new_len, 1)
901 return last_elem
902 }
903 a.len = new_len
904 // Note: a.cap is not changed here *on purpose*, so that
905 // further << ops on that array will be more efficient.
906 return last_elem
907}
908
909// delete_last efficiently deletes the last element of the array.
910// It does it simply by reducing the length of the array by 1.
911// If the array is empty, this will panic.
912// For unique arrays, it also clears the obsolete tail slot.
913// See also: [trim](#array.trim)
914pub fn (mut a array) delete_last() {
915 if a.len == 0 {
916 panic('array.delete_last: array is empty')
917 }
918 if a.needs_unique_shrink() {
919 a.delete_many(a.len - 1, 1)
920 return
921 }
922 a.len--
923 unsafe {
924 vmemset(&u8(a.data) + u64(a.len) * u64(a.element_size), 0, u64(a.element_size))
925 }
926}
927
928// slice returns an array using the same buffer as original array
929// but starting from the `start` element and ending with the element before
930// the `end` element of the original array with the length and capacity
931// set to the number of the elements in the slice.
932// It will remain tied to the same memory location until the length increases
933// (copy on grow) or `.clone()` is called on it.
934// If `start` and `end` are invalid this function will panic.
935// Alternative: Slices can also be made with [start..end] notation
936// Alternative: `.slice_ni()` will always return an array.
937fn (a array) slice(start int, _end int) array {
938 // WARNNING: The is a temp solution for bootstrap!
939 end := if _end == max_i64 || _end == max_i32 { a.len } else { _end } // max_int
940 $if !no_bounds_checking {
941 if start > end {
942 panic(
943 'array.slice: invalid slice index (start>end):' + impl_i64_to_string(i64(start)) +
944 ', ' + impl_i64_to_string(end))
945 }
946 if end > a.len {
947 panic('array.slice: slice bounds out of range (' + impl_i64_to_string(end) + ' >= ' +
948 impl_i64_to_string(a.len) + ')')
949 }
950 if start < 0 {
951 panic('array.slice: slice bounds out of range (start<0):' + impl_i64_to_string(start))
952 }
953 }
954 // TODO: integrate reference counting
955 unsafe { a.mark_buffer_has_slices() }
956 offset := u64(start) * u64(a.element_size)
957 data := unsafe { &u8(a.data) + offset }
958 l := end - start
959 mut flags := ArrayFlags.is_slice
960 if a.uses_noscan_data() {
961 unsafe { flags.set(.noscan_data) }
962 }
963 res := array{
964 element_size: a.element_size
965 data: data
966 offset: a.offset + int(offset) // TODO: offset should become 64bit
967 len: l
968 cap: l
969 flags: flags
970 }
971 return res
972}
973
974// slice_ni returns an array using the same buffer as original array
975// but starting from the `start` element and ending with the element before
976// the `end` element of the original array.
977// This function can use negative indexes `a.slice_ni(-3, a.len)`
978// that get the last 3 elements of the array otherwise it return an empty array.
979// This function always return a valid array.
980fn (a array) slice_ni(_start int, _end int) array {
981 unsafe { a.mark_buffer_has_slices() }
982 mut flags := ArrayFlags.is_slice
983 if a.uses_noscan_data() {
984 unsafe { flags.set(.noscan_data) }
985 }
986 // WARNNING: The is a temp solution for bootstrap!
987 mut end := if _end == max_i64 || _end == max_i32 { a.len } else { _end } // max_int
988 mut start := _start
989
990 if start < 0 {
991 start = a.len + start
992 if start < 0 {
993 start = 0
994 }
995 }
996
997 if end < 0 {
998 end = a.len + end
999 if end < 0 {
1000 end = 0
1001 }
1002 }
1003 if end >= a.len {
1004 end = a.len
1005 }
1006
1007 if start >= a.len || start > end {
1008 res := array{
1009 element_size: a.element_size
1010 data: a.data
1011 offset: 0
1012 len: 0
1013 cap: 0
1014 flags: flags
1015 }
1016 return res
1017 }
1018
1019 offset := u64(start) * u64(a.element_size)
1020 data := unsafe { &u8(a.data) + offset }
1021 l := end - start
1022 res := array{
1023 element_size: a.element_size
1024 data: data
1025 offset: a.offset + int(offset) // TODO: offset should be 64bit
1026 len: l
1027 cap: l
1028 flags: flags
1029 }
1030 return res
1031}
1032
1033// clone_static_to_depth() returns an independent copy of a given array.
1034// Unlike `clone_to_depth()` it has a value receiver and is used internally
1035// for slice-clone expressions like `a[2..4].clone()` and in -autofree generated code.
1036fn (a array) clone_static_to_depth(depth int) array {
1037 return unsafe { a.clone_to_depth(depth) }
1038}
1039
1040// clone returns an independent copy of a given array.
1041// this will be overwritten by `cgen` with an appropriate call to `.clone_to_depth()`
1042// However the `checker` needs it here.
1043pub fn (a &array) clone() array {
1044 return unsafe { a.clone_to_depth(0) }
1045}
1046
1047// recursively clone given array - `unsafe` when called directly because depth is not checked
1048@[unsafe]
1049pub fn (a &array) clone_to_depth(depth int) array {
1050 source_capacity_in_bytes := u64(a.cap) * u64(a.element_size)
1051 use_noscan_data := depth == 0 && a.uses_noscan_data()
1052 mut data := unsafe { nil }
1053 if use_noscan_data {
1054 data = a.alloc_array_data_like(source_capacity_in_bytes)
1055 } else {
1056 data = alloc_array_data(source_capacity_in_bytes)
1057 }
1058 mut arr := array{
1059 element_size: a.element_size
1060 data: data
1061 len: a.len
1062 cap: a.cap
1063 flags: if use_noscan_data { .managed | .noscan_data } else { .managed }
1064 }
1065 // Recursively clone-generated elements if array element is array type
1066 if depth > 0 && a.element_size == sizeof(array) && a.len >= 0 && a.cap >= a.len {
1067 ar := array{}
1068 asize := int(sizeof(array))
1069 for i in 0 .. a.len {
1070 unsafe { vmemcpy(&ar, a.get_unsafe(i), asize) }
1071 ar_clone := unsafe { ar.clone_to_depth(depth - 1) }
1072 unsafe { arr.set_unsafe(i, &ar_clone) }
1073 }
1074 return arr
1075 } else if depth > 0 && a.element_size == sizeof(string) && a.len >= 0 && a.cap >= a.len {
1076 for i in 0 .. a.len {
1077 str_ptr := unsafe { &string(a.get_unsafe(i)) }
1078 str_clone := (*str_ptr).clone()
1079 unsafe { arr.set_unsafe(i, &str_clone) }
1080 }
1081 return arr
1082 }
1083 if a.data != 0 && source_capacity_in_bytes > 0 {
1084 unsafe { vmemcpy(arr.data, a.data, source_capacity_in_bytes) }
1085 }
1086 return arr
1087}
1088
1089// we manually inline this for single operations for performance without -prod
1090@[inline; unsafe]
1091fn (mut a array) set_unsafe(i int, val voidptr) {
1092 unsafe { vmemcpy(&u8(a.data) + u64(a.element_size) * u64(i), val, a.element_size) }
1093}
1094
1095// Private function. Used to implement assignment to the array element.
1096fn (mut a array) set(i int, val voidptr) {
1097 $if !no_bounds_checking {
1098 if i < 0 || i >= a.len {
1099 panic_n2('array.set: index out of range (i,a.len):', i, a.len)
1100 }
1101 }
1102 unsafe { vmemcpy(&u8(a.data) + u64(a.element_size) * u64(i), val, a.element_size) }
1103}
1104
1105@[markused]
1106fn (mut a array) set_i64(i i64, val voidptr) {
1107 $if !no_bounds_checking {
1108 if i < 0 || i >= i64(a.len) {
1109 panic_n2('array.set: index out of range (i,a.len):', i, a.len)
1110 }
1111 }
1112 unsafe { vmemcpy(&u8(a.data) + u64(a.element_size) * u64(i), val, a.element_size) }
1113}
1114
1115@[markused]
1116fn (mut a array) set_u64(i u64, val voidptr) {
1117 $if !no_bounds_checking {
1118 if i >= u64(a.len) {
1119 panic('array.set: index out of range (i,a.len): ' + i.str() + ', ' +
1120 impl_i64_to_string(a.len))
1121 }
1122 }
1123 unsafe { vmemcpy(&u8(a.data) + u64(a.element_size) * i, val, a.element_size) }
1124}
1125
1126@[markused]
1127fn (mut a array) set_ni(i int, val voidptr) {
1128 a.set(v_ni_index(i, a.len), val)
1129}
1130
1131// copy_element_to copies a single `element_size` byte element from `src` to `dest`.
1132// It dispatches the common small element sizes to `vmemcpy` with a *compile time constant*
1133// size: since `vmemcpy` is inlined and forwards to `C.memcpy`, a literal size lets the C
1134// backend inline the copy as plain loads/stores instead of a `memcpy` library call, which
1135// otherwise dominates the cost of single element pushes (`a << x`) in hot loops.
1136// The copy semantics stay exactly those of `memcpy`, so it is safe for arbitrary element
1137// types regardless of their alignment, padding or aliasing.
1138@[inline; unsafe]
1139fn copy_element_to(dest voidptr, src voidptr, element_size int) {
1140 unsafe {
1141 match element_size {
1142 1 { vmemcpy(dest, src, 1) }
1143 2 { vmemcpy(dest, src, 2) }
1144 4 { vmemcpy(dest, src, 4) }
1145 8 { vmemcpy(dest, src, 8) }
1146 16 { vmemcpy(dest, src, 16) }
1147 else { vmemcpy(dest, src, element_size) }
1148 }
1149 }
1150}
1151
1152fn (mut a array) push(val voidptr) {
1153 $if !no_bounds_checking {
1154 if a.len < 0 {
1155 panic('array.push: negative len')
1156 }
1157 }
1158 if a.len >= max_int {
1159 panic('array.push: len bigger than max_int')
1160 }
1161 required := a.len + 1
1162 if required > a.cap {
1163 a.ensure_cap(required)
1164 } else if a.flags.has(.is_slice) {
1165 // `required <= a.cap` here, so this is the `needs_unique_append` case
1166 a.clone_shallow_to_cap(a.cap)
1167 }
1168 unsafe {
1169 copy_element_to(&u8(a.data) + u64(a.element_size) * u64(a.len), val, a.element_size)
1170 }
1171 a.len++
1172}
1173
1174// push_many implements the functionality for pushing another array.
1175// `val` is array.data and user facing usage is `a << [1,2,3]`
1176@[unsafe]
1177pub fn (mut a array) push_many(val voidptr, size int) {
1178 if size <= 0 || val == unsafe { nil } {
1179 return
1180 }
1181 new_len := i64(a.len) + i64(size)
1182 if new_len > max_int {
1183 // string interpolation also uses <<; avoid it, use a fixed string for the panic
1184 panic('array.push_many: new len exceeds max_int')
1185 }
1186 if a.needs_unique_append(int(new_len)) {
1187 a.clone_shallow_to_cap(a.cap)
1188 }
1189 is_self_append := a.data == val && a.data != 0
1190 if int(new_len) > a.cap {
1191 a.ensure_cap(int(new_len))
1192 }
1193 if is_self_append {
1194 // handle `arr << arr`
1195 cloned := a.clone()
1196 unsafe {
1197 vmemcpy(&u8(a.data) + u64(a.element_size) * u64(a.len), cloned.data,
1198 u64(a.element_size) * u64(size))
1199 }
1200 } else {
1201 if a.data != 0 && val != 0 {
1202 unsafe {
1203 vmemcpy(&u8(a.data) + u64(a.element_size) * u64(a.len), val,
1204 u64(a.element_size) * u64(size))
1205 }
1206 }
1207 }
1208 a.len = int(new_len)
1209}
1210
1211// reverse_in_place reverses existing array data, modifying original array.
1212pub fn (mut a array) reverse_in_place() {
1213 if a.len < 2 || a.element_size == 0 {
1214 return
1215 }
1216 unsafe {
1217 mut tmp_value := malloc(a.element_size)
1218 for i in 0 .. a.len / 2 {
1219 vmemcpy(tmp_value, &u8(a.data) + u64(i) * u64(a.element_size), a.element_size)
1220 vmemcpy(&u8(a.data) + u64(i) * u64(a.element_size), &u8(a.data) + u64(a.len - 1 -
1221 i) * u64(a.element_size), a.element_size)
1222 vmemcpy(&u8(a.data) + u64(a.len - 1 - i) * u64(a.element_size), tmp_value,
1223 a.element_size)
1224 }
1225 free(tmp_value)
1226 }
1227}
1228
1229// reverse returns a new array with the elements of the original array in reverse order.
1230pub fn (a array) reverse() array {
1231 if a.len < 2 {
1232 return a
1233 }
1234 use_noscan_data := a.uses_noscan_data()
1235 mut arr := array{
1236 element_size: a.element_size
1237 data: a.alloc_array_data_like(u64(a.cap) * u64(a.element_size))
1238 len: a.len
1239 cap: a.cap
1240 flags: if use_noscan_data { .managed | .noscan_data } else { .managed }
1241 }
1242 for i in 0 .. a.len {
1243 unsafe { arr.set_unsafe(i, a.get_unsafe(a.len - 1 - i)) }
1244 }
1245 return arr
1246}
1247
1248// free frees all memory occupied by the array.
1249@[unsafe]
1250pub fn (a &array) free() {
1251 $if prealloc {
1252 return
1253 }
1254 // if a.is_slice {
1255 // return
1256 // }
1257 if a.flags.has(.nofree) {
1258 return
1259 }
1260 mblock_ptr := &u8(u64(a.data) - u64(a.offset))
1261 if mblock_ptr != unsafe { nil } {
1262 unsafe {
1263 if a.flags.has(.managed) {
1264 free(mblock_ptr - array_data_header_size())
1265 } else {
1266 free(mblock_ptr)
1267 }
1268 }
1269 }
1270 unsafe {
1271 a.data = nil
1272 a.offset = 0
1273 a.len = 0
1274 a.cap = 0
1275 }
1276}
1277
1278// Some of the following functions have no implementation in V and exist here
1279// to expose them to the array namespace. Their implementation is compiler
1280// specific because of their use of `it` and `a < b` expressions.
1281// Therefore, the implementation is left to the backend.
1282
1283// filter creates a new array with all elements that pass the test.
1284// Ignore the function signature. `filter` does not take an actual callback. Rather, it
1285// takes an `it` expression.
1286//
1287// Certain array functions (`filter` `any` `all`) support a simplified
1288// domain-specific-language by the backend compiler to make these operations
1289// more idiomatic to V. These functions are described here, but their implementation
1290// is compiler specific.
1291//
1292// Each function takes a boolean test expression as its single argument.
1293// These test expressions may use `it` as a pointer to a single element at a time.
1294//
1295// Example: a := [10,20,30,3,5,99]; assert a.filter(it < 5) == [3] // create an array of elements less than 5
1296// Example: a := [10,20,30,3,5,99]; assert a.filter(it % 2 == 1) == [3,5,99] // create an array of only odd elements
1297// Example: struct Named { name string }; a := [Named{'Abc'}, Named{'Bcd'}, Named{'Az'}]; assert a.filter(it.name[0] == `A`).len == 2
1298pub fn (a array) filter(predicate fn (voidptr) bool) array
1299
1300// any tests whether at least one element in the array passes the test.
1301// Ignore the function signature. `any` does not take an actual callback. Rather, it
1302// takes an `it` expression.
1303// It returns `true` if it finds an element passing the test. Otherwise,
1304// it returns `false`. It doesn't modify the array.
1305//
1306// Example: a := [2,3,4]; assert a.any(it % 2 == 1) // 3 is odd, so this will pass
1307// Example: struct Named { name string }; a := [Named{'Bob'}, Named{'Bilbo'}]; assert a.any(it.name == 'Bob') // the first element will match
1308pub fn (a array) any(predicate fn (voidptr) bool) bool
1309
1310// count counts how many elements in array pass the test.
1311// Ignore the function signature. `count` does not take an actual callback. Rather, it
1312// takes an `it` expression.
1313//
1314// Example: a := [10,3,5,7]; assert a.count(it % 2 == 1) == 3 // will return how many elements are odd
1315pub fn (a array) count(predicate fn (voidptr) bool) int
1316
1317// all tests whether all elements in the array pass the test.
1318// Ignore the function signature. `all` does not take an actual callback. Rather, it
1319// takes an `it` expression.
1320// It returns `false` if any element fails the test. Otherwise,
1321// it returns `true`. It doesn't modify the array.
1322//
1323// Example: a := [3,5,7,9]; assert a.all(it % 2 == 1) // will return true if every element is odd
1324pub fn (a array) all(predicate fn (voidptr) bool) bool
1325
1326// map creates a new array populated with the results of calling a provided function
1327// on every element in the calling array.
1328// It also accepts an `it` expression.
1329//
1330// Example:
1331// ```v
1332// words := ['hello', 'world']
1333// r1 := words.map(it.to_upper())
1334// assert r1 == ['HELLO', 'WORLD']
1335//
1336// // map can also accept anonymous functions
1337// r2 := words.map(fn (w string) string {
1338// return w.to_upper()
1339// })
1340// assert r2 == ['HELLO', 'WORLD']
1341// ```
1342pub fn (a array) map(callback fn (voidptr) voidptr) array
1343
1344// sort sorts the array in place.
1345// Ignore the function signature. Passing a callback to `.sort` is not supported
1346// for now. Consider using the `.sort_with_compare` method if you need it.
1347//
1348// sort can take a boolean test expression as its single argument.
1349// The expression uses 2 'magic' variables `a` and `b` as pointers to the two elements
1350// being compared.
1351// Equal elements keep their original relative order.
1352//
1353// Example: mut aa := [5,2,1,10]; aa.sort(); assert aa == [1,2,5,10] // will sort the array in ascending order
1354// Example: mut aa := [5,2,1,10]; aa.sort(b < a); assert aa == [10,5,2,1] // will sort the array in descending order
1355// Example: struct Named { name string }; mut aa := [Named{'Abc'}, Named{'Xyz'}]; aa.sort(b.name < a.name); assert aa.map(it.name) == ['Xyz','Abc'] // will sort descending by the .name field
1356pub fn (mut a array) sort(callback fn (voidptr, voidptr) int)
1357
1358// sorted returns a sorted copy of the original array. The original array is *NOT* modified.
1359// See also .sort() .
1360// Equal elements keep their original relative order.
1361// Example: assert [9,1,6,3,9].sorted() == [1,3,6,9,9]
1362// Example: assert [9,1,6,3,9].sorted(b < a) == [9,9,6,3,1]
1363pub fn (a &array) sorted(callback fn (voidptr, voidptr) int) array
1364
1365// sort_with_compare sorts the array in-place using the results of the
1366// given function to determine sort order.
1367//
1368// The function should return one of three values:
1369// - `-1` when `a` should come before `b` ( `a < b` )
1370// - `1` when `b` should come before `a` ( `b < a` )
1371// - `0` when the order cannot be determined ( `a == b` )
1372// Returning `0` keeps equal elements in their original relative order.
1373//
1374// Example:
1375// ```v
1376// mut a := ['hi', '1', '5', '3']
1377// a.sort_with_compare(fn (a &string, b &string) int {
1378// if a < b {
1379// return -1
1380// }
1381// if a > b {
1382// return 1
1383// }
1384// return 0
1385// })
1386// assert a == ['1', '3', '5', 'hi']
1387// ```
1388pub fn (mut a array) sort_with_compare(callback FnSortCB) {
1389 $if freestanding {
1390 panic('sort_with_compare does not work with -freestanding')
1391 } $else {
1392 unsafe { vqsort(a.data, usize(a.len), usize(a.element_size), callback) }
1393 }
1394}
1395
1396// sorted_with_compare sorts a clone of the array. The original array is not modified.
1397// It uses the results of the given function to determine sort order.
1398// See also .sort_with_compare()
1399// Equal elements keep their original relative order.
1400pub fn (a &array) sorted_with_compare(callback FnSortCB) array {
1401 mut r := a.clone()
1402 unsafe { vqsort(r.data, usize(r.len), usize(r.element_size), callback) }
1403 return r
1404}
1405
1406// contains determines whether an array includes a certain value among its elements.
1407// It will return `true` if the array contains an element with this value.
1408// It is similar to `.any` but does not take an `it` expression.
1409//
1410// Example: assert [1, 2, 3].contains(4) == false
1411pub fn (a array) contains(value voidptr) bool
1412
1413// index returns the first index at which a given element can be found in the array or `-1` if the value is not found.
1414pub fn (a array) index(value voidptr) int
1415
1416// last_index returns the last index at which a given element can be found in the array or `-1` if the value is not found.
1417pub fn (a array) last_index(value voidptr) int
1418
1419@[direct_array_access; unsafe]
1420pub fn (mut a []string) free() {
1421 $if prealloc {
1422 return
1423 }
1424 for mut s in a {
1425 unsafe { s.free() }
1426 }
1427 mut arr := unsafe { &array(a) }
1428 unsafe { arr.free() }
1429}
1430
1431// The following functions are type-specific functions that apply
1432// to arrays of different types in different ways.
1433
1434// str returns a string representation of an array of strings.
1435// Example: assert ['a', 'b', 'c'].str() == "['a', 'b', 'c']"
1436@[direct_array_access; manualfree]
1437pub fn (a []string) str() string {
1438 mut sb_len := 4 // 2x" + 1x, + 1xspace
1439 if a.len > 0 {
1440 // assume that most strings will be ~large as the first
1441 sb_len += a[0].len
1442 sb_len *= a.len
1443 }
1444 sb_len += 2 // 1x[ + 1x]
1445 mut sb := strings.new_builder(sb_len)
1446 sb.write_u8(`[`)
1447 for i in 0 .. a.len {
1448 val := a[i]
1449 sb.write_u8(`'`)
1450 sb.write_string(val)
1451 sb.write_u8(`'`)
1452 if i < a.len - 1 {
1453 sb.write_string(', ')
1454 }
1455 }
1456 sb.write_u8(`]`)
1457 res := sb.str()
1458 unsafe { sb.free() }
1459 return res
1460}
1461
1462// hex returns a string with the hexadecimal representation of the byte elements of the array `b`.
1463pub fn (b []u8) hex() string {
1464 if b.len == 0 {
1465 return ''
1466 }
1467 return unsafe { data_to_hex_string(b.data, b.len) }
1468}
1469
1470// copy copies the `src` byte array elements to the `dst` byte array.
1471// The number of the elements copied is the minimum of the length of both arrays.
1472// Returns the number of elements copied.
1473// NOTE: This is not an `array` method. It is a function that takes two arrays of bytes.
1474// See also: `arrays.copy`.
1475pub fn copy(mut dst []u8, src []u8) int {
1476 min := if dst.len < src.len { dst.len } else { src.len }
1477 if min > 0 {
1478 unsafe { vmemmove(dst.data, src.data, min) }
1479 }
1480 return min
1481}
1482
1483// grow_cap grows the array's capacity by `amount` elements.
1484// Internally, it does this by copying the entire array to
1485// a new memory location (creating a clone).
1486pub fn (mut a array) grow_cap(amount int) {
1487 new_cap := i64(amount) + i64(a.cap)
1488 if new_cap > max_int {
1489 panic_n('array.grow_cap: max_int will be exceeded by new cap:', new_cap)
1490 }
1491 a.ensure_cap(int(new_cap))
1492}
1493
1494// grow_len ensures that an array has a.len + amount of length
1495// Internally, it does this by copying the entire array to
1496// a new memory location (creating a clone) unless the array.cap
1497// is already large enough.
1498@[unsafe]
1499pub fn (mut a array) grow_len(amount int) {
1500 new_len := i64(amount) + i64(a.len)
1501 if new_len > max_int {
1502 panic_n('array.grow_len: max_int will be exceeded by new len:', new_len)
1503 }
1504 a.ensure_cap(int(new_len))
1505 a.len = int(new_len)
1506}
1507
1508// pointers returns a new array, where each element
1509// is the address of the corresponding element in the array.
1510@[unsafe]
1511pub fn (a array) pointers() []voidptr {
1512 mut res := []voidptr{}
1513 for i in 0 .. a.len {
1514 unsafe { res << a.get_unsafe(i) }
1515 }
1516 return res
1517}
1518
1519// vbytes on`voidptr` makes a V []u8 structure from a C style memory buffer.
1520// NOTE: the data is reused, NOT copied!
1521@[unsafe]
1522pub fn (data voidptr) vbytes(len int) []u8 {
1523 res := array{
1524 element_size: 1
1525 data: data
1526 len: len
1527 cap: len
1528 }
1529 return res
1530}
1531
1532// vbytes on `&u8` makes a V []u8 structure from a C style memory buffer.
1533// NOTE: the data is reused, NOT copied!
1534@[unsafe]
1535pub fn (data &u8) vbytes(len int) []u8 {
1536 return unsafe { voidptr(data).vbytes(len) }
1537}
1538
1539// free frees the memory allocated
1540@[unsafe]
1541pub fn (data &u8) free() {
1542 unsafe { free(data) }
1543}
1544
1545@[if !no_bounds_checking ?; inline]
1546fn panic_on_negative_len(len int) {
1547 if len < 0 {
1548 panic_n('negative .len:', len)
1549 }
1550}
1551
1552@[if !no_bounds_checking ?; inline]
1553fn panic_on_negative_cap(cap int) {
1554 if cap < 0 {
1555 panic_n('negative .cap:', cap)
1556 }
1557}
1558