# Easier way to copy concatenate array and insert into a (dynamically allocated) array?

Hi fellas, Zig noob here;
I was wondering if there was an easier way to copy part of an array (say for example half of it), and then copy the remaining half after some operation? I’ve tried copying via using for example `indx..len` but I’d get a compilation error “error: invalid left-hand side to assignment”
Here’s what I came up with using for-loops. So again, I’m just wondering if there’s an easier way.

``````        pub fn insert(vec: *Self, indx: usize, val: T) !void {
if (indx > vec.m_len) return error.IndexOutOfBounds;

var temp_vec = try allocator.alloc(T, vec.m_len + 1);
//temp_vec = vec.m_buffer[0..indx];
//temp_vec[indx] = val;
//temp_vec[indx..] = vec.m_buffer[indx..];
for (0..indx) |i| {
temp_vec[i] = vec.m_buffer[i];
}
temp_vec[indx] = val;
for (indx..vec.m_len) |i| {
temp_vec[i + 1] = vec.m_buffer[i];
}
vec.m_buffer = try allocator.realloc(vec.m_buffer, vec.m_len + 1);
vec.m_len += 1;
for (0..vec.m_len) |i| {
vec.m_buffer[i] = temp_vec[i];
}
allocator.free(temp_vec);
}
``````

2 Likes

Looking at the documentation for `@memcpy`, it doesn’t appear to provide an argument for a number of bytes to copy. So I’d probably have to do the copy operation, then shift elements to the right and then do the insertion.

Or possibly, just reallocate the original slice, and do a shift right operation then insert into the index, removing the need for an additional allocation of a “temp” slice.

This is probably an easier solution. I had initially thought that I could do what was commented out in my original code segment. Will check back.

`@memcpy`’s arguments are slices, and they embed information about length.

1 Like

Right, but say the goal is to only copy N number of bytes or “elements”, does `@memcpy` provide this functionality?

Yes, so what you’re looking for is something like this:

``````dst: []T -> buffer for output of copy
src: []T -> slice of input elements
@memcpy(dst[M..N], src[M..N]);
``````
1 Like

Cool! Yep that’s what I was looking for.
I ended up doing:

``````            var temp_vec = try allocator.alloc(T, vec.m_len + 1);
@memcpy(temp_vec[0..indx], vec.m_buffer[0..indx]);
temp_vec[indx] = val;
@memcpy(temp_vec[indx + 1 ..], vec.m_buffer[indx..]);
vec.m_buffer = try allocator.realloc(vec.m_buffer, vec.m_len + 1);
vec.m_len += 1;
@memcpy(vec.m_buffer, temp_vec);
allocator.free(temp_vec);
``````

Do I actually even need to allocate for a new buffer and do all this copying if `realloc` from the `std.heap.GeneralPurposeAllocator` preserves the contents?

I’m thinking, if I can just reallocate the size of the original buffer, and shift right all the elements from the index of insertion, to the size of the slice, then there’d be no need for a “temporary” slice and do all this copying.

What do you think?

A more detailed example:

``````const std = @import("std");

test "memcpy" {
const vowels = "aeiou";

var digits: [10]u8 = [_]u8{0} ** 10;
for (0..10) |p| {
const c: u8 = @intCast(p);
digits[p] = c + '0';
}
try std.testing.expectEqualStrings("0123456789", &digits);

@memcpy(digits[3..6], vowels[1..4]);
try std.testing.expectEqualStrings("012eio6789", &digits);
}
``````

It seems like you’re trying to implement something similar to `ArrayList` but are intentionally not using `ArrayList` as a reference, so I’ll just mention some general ideas/strategies it employs and you can do with them what you will:

Instead of allocating precisely the amount of memory necessary when adding a new element, it’s better to allocate a bit more memory than necessary (and probably super-linearly, meaning you allocate more and more extra space as the buffer grows larger) to amortize the cost of allocation while the buffer grows.

From something I’ve said before in an ArrayList-related PR:

In general, better_capacity is a tradeoff between:

• Number of reallocations needed
• Amount of memory allocated that is never actually used

I think the ideal is that better_capacity is tuned so that both are as low as possible in “real” usage.

Doing this would mean that you would first check to see if you already have enough space and therefore would just need to move some elements and then insert the new one.

(note that when moving elements you will likely want `std.mem.copyBackwards` instead of `@memcpy`, since `@memcpy` does not allow the src and dest to overlap)

When you do need to resize, you can take advantage of the `Allocator.resize` API which will return `true` if it was possible to resize-in-place, and `false` if it was not possible. If it returns true, this again allows an opportunity to just move elements and insert.

If you can’t resize-in-place, then you can allocate a new slice, copy things while leaving a gap for the new element, then free the old slice and set the buffer to the new slice.

Something unrelated that you might want to consider is switching from:

``````/// Allocated buffer
m_buffer: []T,
/// Length of the valid/used part of the buffer
m_len: usize,
``````

to

``````/// Valid/used slice of the allocated buffer
m_items: []T,
/// Full capacity of the allocated buffer
m_capacity: usize,
``````

(although I’d also remove the `m_` prefix)

This would make it so `items.len` is always the length of the valid/used part of the allocated buffer, and `items[0..capacity]` is the allocated buffer. This means that working with the valid/used part of the buffer is the easier thing (and doesn’t need a helper function), while working with the full allocated length of the buffer is slightly harder (but is the less common use-case)

(here’s the issue for this change being made in Zig for context)

1 Like

Oh, also, this:

``````var temp_vec = try allocator.alloc(T, vec.m_len + 1);
// ...
vec.m_buffer = try allocator.realloc(vec.m_buffer, vec.m_len + 1);
// ...
allocator.free(temp_vec);
``````

is a memory leak waiting to happen. If the first `alloc` call succeeds, but the `realloc` call fails, then the `free` call never happens and the `temp_vec` slice will be leaked. Instead, you want to use `defer` like so:

``````var temp_vec = try allocator.alloc(T, vec.m_len + 1);
defer allocator.free(temp_vec);
// ...
vec.m_buffer = try allocator.realloc(vec.m_buffer, vec.m_len + 1);
// ...
``````

(this is just a general memory management thing to watch out for, see my last post for avoiding the `alloc` + `realloc` combo in this scenario)

1 Like