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