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