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