# Copying bytes from AbstractVector to Ptr

**URL:** <https://discourse.julialang.org/t/copying-bytes-from-abstractvector-to-ptr/119408>\
**Category:** Performance\
**Created:** [September 14, 2024, 4:57pm UTC](https://discourse.julialang.org/t/copying-bytes-from-abstractvector-to-ptr/119408 "2024-09-14T16:57:49Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![nhz2](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nhz2/32/44428_2.png) [@nhz2](https://discourse.julialang.org/u/nhz2)\
**Post date:** [September 14, 2024, 4:57pm UTC](https://discourse.julialang.org/t/copying-bytes-from-abstractvector-to-ptr/119408/1 "2024-09-14T16:57:49Z")

</div>

I am trying to efficiently copy data from an `AbstractVector{UInt8}` into a `Ptr{UInt8}`

I currently have tried the following:

```julia
function unsafe_read1!(
        dest::Ptr{UInt8},
        src::AbstractVector{UInt8},
        so::Integer,
        nbytes::UInt
    )::Nothing
    temp = Vector{UInt8}(undef, nbytes)
    copyto!(temp, 1, src, so, nbytes)
    GC.@preserve temp unsafe_copyto!(dest, pointer(temp), nbytes)
    nothing
end

```

A new `AbstractVector{UInt8}` would then need to implement `Base.copyto!(dest::Vector{UInt8} ...` to make this performant.

Is there a way to avoid allocating the temporary vector?

---

<div class="post-metadata">

**Author:** ![matthias314](https://avatars.discourse-cdn.com/v4/letter/m/a88e4f/32.png) [@matthias314](https://discourse.julialang.org/u/matthias314)\
**Post date:** [September 14, 2024, 5:24pm UTC](https://discourse.julialang.org/t/copying-bytes-from-abstractvector-to-ptr/119408/2 "2024-09-14T17:24:10Z")

</div>

You could copy directly:

```julia
function unsafe_read2!(
        dest::Ptr{T},
        src::AbstractVector{T},
        so::Integer,
        nbytes::Integer) where T
    for i in 1:nbytes
        unsafe_store!(dest, src[so+i-1], i)
    end
end

```

---

<div class="post-metadata">

**Author:** ![nhz2](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nhz2/32/44428_2.png) [@nhz2](https://discourse.julialang.org/u/nhz2)\
**Post date:** [September 14, 2024, 5:36pm UTC](https://discourse.julialang.org/t/copying-bytes-from-abstractvector-to-ptr/119408/3 "2024-09-14T17:36:45Z")

</div>

That removes the allocation but is significantly slower. It also fails with `CuArray` with `ERROR: Scalar indexing is disallowed.`

```julia
julia> using BenchmarkTools

julia> s = @view(rand(UInt8, 10000)[1:2:end]);

julia> dest = zeros(UInt8, length(s));

julia> d = pointer(dest);

julia> @btime unsafe_read1!($d, $s, 1, $nbytes)
  1.719 μs (3 allocations: 5.00 KiB)

julia> @btime unsafe_read2!($d, $s, 1, $nbytes)
  10.329 μs (0 allocations: 0 bytes)

```

---

<div class="post-metadata">

**Author:** ![oheil](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oheil/32/220745_2.png) [@oheil](https://discourse.julialang.org/u/oheil)\
**Post date:** [September 14, 2024, 5:42pm UTC](https://discourse.julialang.org/t/copying-bytes-from-abstractvector-to-ptr/119408/4 "2024-09-14T17:42:08Z")

</div>

It seems I don’t understand the question, because this works:

```julia
function unsafe_read1!(dest::Ptr{UInt8},src::AbstractVector{UInt8},nbytes::UInt)
    unsafe_copyto!(dest, pointer(src), nbytes)
    nothing
end
nbytes=UInt(10)

```

```julia
julia> temp=Vector{UInt8}(undef, nbytes)
10-element Vector{UInt8}:
 0x00
 0x27
 0xf3
 0x64
 0x4c
 0x02
 0x00
 0x00
 0x03
 0x00

```

```julia
dest=pointer(temp)
src=zeros(UInt8,nbytes)
unsafe_read1!(dest,src,nbytes)

```

```julia
julia> temp
10-element Vector{UInt8}:
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00

```

---

<div class="post-metadata">

**Author:** ![nhz2](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nhz2/32/44428_2.png) [@nhz2](https://discourse.julialang.org/u/nhz2)\
**Post date:** [September 14, 2024, 5:48pm UTC](https://discourse.julialang.org/t/copying-bytes-from-abstractvector-to-ptr/119408/5 "2024-09-14T17:48:11Z")

</div>

I want to support reading a specific range of bytes that are not contiguous in CPU memory, for example, the bytes in `src` may be compressed, in a hard drive, or in GPU memory.

---

<div class="post-metadata">

**Author:** ![eldee](https://avatars.discourse-cdn.com/v4/letter/e/b5a626/32.png) [@eldee](https://discourse.julialang.org/u/eldee)\
**Post date:** [September 14, 2024, 6:05pm UTC](https://discourse.julialang.org/t/copying-bytes-from-abstractvector-to-ptr/119408/6 "2024-09-14T18:05:53Z")

</div>

You could use `unsafe_wrap` to associate a `Vector` to `dest` without allocating:

```julia
function unsafe_read3!(
    dest::Ptr{UInt8},
    src::AbstractVector{UInt8},
    so::Integer,
    nbytes::Integer
    )::Nothing

    dest_array = unsafe_wrap(Vector{UInt8}, dest, nbytes)
    copyto!(dest_array, 1, src, so, nbytes)
    return nothing
end

```

After the setup from @nhz2’s second post, and with `nbytes = length(s)` (and the original `unsafe_read1!` modified to accept `nbytes::Integer`, like in @matthias314 's `unsafe_read2!`):

```julia-repl
julia> unsafe_read3!(d, s, 1, nbytes)

julia> all(dest .== s)
true

julia> @btime unsafe_read1!($d, $s, 1, $nbytes)
  1.370 μs (1 allocation: 5.06 KiB)

julia> @btime unsafe_read2!($d, $s, 1, $nbytes)
  20.000 μs (0 allocations: 0 bytes)

julia> @btime unsafe_read3!($d, $s, 1, $nbytes)
  1.200 μs (1 allocation: 48 bytes)

```

(Okay, apparently this allocated a little, but I imagine you can live with 48 bytes. 🙂 )

---

<div class="post-metadata">

**Author:** ![matthias314](https://avatars.discourse-cdn.com/v4/letter/m/a88e4f/32.png) [@matthias314](https://discourse.julialang.org/u/matthias314)\
**Post date:** [September 14, 2024, 6:18pm UTC](https://discourse.julialang.org/t/copying-bytes-from-abstractvector-to-ptr/119408/7 "2024-09-14T18:18:52Z")

</div>

If you want to have it faster, you can use `@inbounds`. Ignoring manual bounds checking, that gives

```julia
function unsafe_read3!(
        dest::Ptr{T},
        src::AbstractVector{T},
        so::Integer,
        nbytes::Integer) where T
    for i in 1:nbytes
        unsafe_store!(dest, @inbounds(src[so+i-1]), i)
    end
end

```

With `nbytes = 4000`, I get

```julia
julia> @b unsafe_read1!($d, $s, 1, $nbytes)
2.992 μs (1 allocs: 4.062 KiB)

julia> @b unsafe_read3!($d, $s, 1, $nbytes)
1.845 μs

```

---

<div class="post-metadata">

**Author:** ![nhz2](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nhz2/32/44428_2.png) [@nhz2](https://discourse.julialang.org/u/nhz2)\
**Post date:** [June 21, 2025, 8:48pm UTC](https://discourse.julialang.org/t/copying-bytes-from-abstractvector-to-ptr/119408/8 "2025-06-21T20:48:13Z")

</div>

From discussions on slack, using `unsafe_wrap` here can cause undefined behavior if the `Ptr` might come from memory managed by Julia.
