| 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. |
| 4 | module builtin |
| 5 | |
| 6 | import 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. |
| 12 | pub struct array { |
| 13 | pub 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 |
| 19 | pub: |
| 20 | element_size int // size in bytes of one element in the array. |
| 21 | } |
| 22 | |
| 23 | @[flag] |
| 24 | pub 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] |
| 35 | struct ArrayDataHeader { |
| 36 | mut: |
| 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] |
| 46 | fn array_data_header_size() int { |
| 47 | return int(sizeof(voidptr)) |
| 48 | } |
| 49 | |
| 50 | @[inline] |
| 51 | fn array_data_allocation_size(total_size u64) u64 { |
| 52 | return u64(array_data_header_size()) + __at_least_one(total_size) |
| 53 | } |
| 54 | |
| 55 | @[inline] |
| 56 | fn 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] |
| 62 | fn 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] |
| 71 | fn (a array) uses_noscan_data() bool { |
| 72 | return a.flags.has(.noscan_data) |
| 73 | } |
| 74 | |
| 75 | @[inline] |
| 76 | fn (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] |
| 86 | fn (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] |
| 96 | fn (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] |
| 105 | fn (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] |
| 117 | fn (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] |
| 130 | fn (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] |
| 142 | fn (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] |
| 171 | fn 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`) |
| 176 | fn __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 | |
| 197 | fn __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 | |
| 238 | fn __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 | |
| 268 | fn __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 | |
| 292 | fn __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]`) |
| 317 | fn 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] !`) |
| 337 | fn 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. |
| 352 | pub 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. |
| 406 | pub 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] |
| 415 | pub 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] |
| 459 | fn (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] |
| 464 | fn (a array) needs_unique_append(required int) bool { |
| 465 | return required <= a.cap && a.flags.has(.is_slice) |
| 466 | } |
| 467 | |
| 468 | @[inline] |
| 469 | fn (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 | // ``` |
| 491 | pub 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] |
| 514 | fn (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)` |
| 538 | pub 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] |
| 546 | fn (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 | // ``` |
| 562 | pub 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 | // ``` |
| 587 | pub 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 |
| 659 | pub 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] |
| 675 | pub 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 |
| 682 | pub 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 | // ``` |
| 702 | pub 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] |
| 716 | fn (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. |
| 723 | fn (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] |
| 735 | fn (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] |
| 747 | fn (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] |
| 760 | fn (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 { ... } |
| 765 | fn (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] |
| 775 | fn (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] |
| 785 | fn (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] |
| 795 | fn (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. |
| 803 | pub 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. |
| 812 | pub 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 | // ``` |
| 851 | pub 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 | // ``` |
| 883 | pub 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) |
| 904 | pub 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. |
| 924 | fn (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. |
| 967 | fn (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. |
| 1023 | fn (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. |
| 1030 | pub 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] |
| 1036 | pub 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] |
| 1078 | fn (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. |
| 1083 | fn (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] |
| 1093 | fn (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] |
| 1103 | fn (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] |
| 1114 | fn (mut a array) set_ni(i int, val voidptr) { |
| 1115 | a.set(v_ni_index(i, a.len), val) |
| 1116 | } |
| 1117 | |
| 1118 | fn (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] |
| 1138 | pub 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. |
| 1173 | pub 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. |
| 1191 | pub 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] |
| 1211 | pub 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 |
| 1259 | pub 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 |
| 1269 | pub 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 |
| 1276 | pub 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 |
| 1285 | pub 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 | // ``` |
| 1303 | pub 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 |
| 1317 | pub 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] |
| 1324 | pub 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 | // ``` |
| 1349 | pub 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. |
| 1361 | pub 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 |
| 1372 | pub 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. |
| 1375 | pub 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. |
| 1378 | pub fn (a array) last_index(value voidptr) int |
| 1379 | |
| 1380 | @[direct_array_access; unsafe] |
| 1381 | pub 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] |
| 1398 | pub 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`. |
| 1424 | pub 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`. |
| 1436 | pub 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). |
| 1447 | pub 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] |
| 1460 | pub 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] |
| 1472 | pub 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] |
| 1483 | pub 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] |
| 1496 | pub fn (data &u8) vbytes(len int) []u8 { |
| 1497 | return unsafe { voidptr(data).vbytes(len) } |
| 1498 | } |
| 1499 | |
| 1500 | // free frees the memory allocated |
| 1501 | @[unsafe] |
| 1502 | pub fn (data &u8) free() { |
| 1503 | unsafe { free(data) } |
| 1504 | } |
| 1505 | |
| 1506 | @[if !no_bounds_checking ?; inline] |
| 1507 | fn 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] |
| 1514 | fn panic_on_negative_cap(cap int) { |
| 1515 | if cap < 0 { |
| 1516 | panic_n('negative .cap:', cap) |
| 1517 | } |
| 1518 | } |
| 1519 | |