@@ -404,3 +404,122 @@ function Base.iterate(x::ReverseMemoryView, state = length(x))
404404 iszero (state) && return nothing
405405 return (@inbounds (x. mem[state]), state - 1 )
406406end
407+
408+ """
409+ split_first(v::MemoryView{T}) -> Tuple{T, MemoryView{T}}
410+
411+ Return the first element of `v` and all other elements as a new memory view.
412+
413+ This function will throw a `BoundsError` if `v` is empty.
414+
415+ See also: [`split_last`](@ref)
416+
417+ # Examples
418+ ```jldoctest
419+ julia> v = MemoryView([0x01, 0x02, 0x03]);
420+
421+ julia> split_first(v)
422+ (0x01, UInt8[0x02, 0x03])
423+
424+ julia> split_first(v[1:1])
425+ (0x01, UInt8[])
426+
427+ julia> split_first(v[1:0])
428+ ERROR: BoundsError: attempt to access 0-element MutableMemoryView{UInt8} at index [1]
429+ [...]
430+ ```
431+ """
432+ function split_first (v:: MemoryView )
433+ @boundscheck checkbounds (v, 1 )
434+ return (@inbounds (v[1 ]), @inbounds (truncate_start (v, 2 )))
435+ end
436+
437+ """
438+ split_last(v::MemoryView{T}) -> Tuple{T, MemoryView{T}}
439+
440+ Return the last element of `v` and all other elements as a new memory view.
441+
442+ This function will throw a `BoundsError` if `v` is empty.
443+
444+ See also: [`split_first`](@ref)
445+
446+ # Examples
447+ ```jldoctest
448+ julia> v = MemoryView([0x01, 0x02, 0x03]);
449+
450+ julia> split_last(v)
451+ (0x03, UInt8[0x01, 0x02])
452+
453+ julia> split_last(v[1:1])
454+ (0x01, UInt8[])
455+
456+ julia> split_last(v[1:0])
457+ ERROR: BoundsError: attempt to access 0-element MutableMemoryView{UInt8} at index [1]
458+ [...]
459+ ```
460+ """
461+ function split_last (v:: MemoryView )
462+ @boundscheck checkbounds (v, 1 )
463+ return (@inbounds (v[end ]), @inbounds (truncate (v, length (v) - 1 )))
464+ end
465+
466+ """
467+ split_at(v::T, i::Int) -> Tuple{T, T} where {T <: MemoryView}
468+
469+ Split a memory view into two at an index.
470+
471+ The first will contain all indices in `1:i-1`, the second `i:end`.
472+ This function will throw a `BoundsError` if `i` is not in `1:end+1`.
473+
474+ # Examples
475+ ```jldocstest
476+ julia> split_at(MemoryView([1,2,3,4,5]), 2)
477+ ([1], [2, 3, 4, 5])
478+
479+ julia> split_at(MemoryView(Int8[1, 2, 3]), 4)
480+ (Int8[1, 2, 3], Int8[])
481+ ```
482+ """
483+ function split_at (v:: MemoryView , i:: Int )
484+ @boundscheck checkbounds (1 : (lastindex (v) + 1 ), i)
485+ return (@inbounds (truncate (v, i - 1 )), @inbounds (truncate_start (v, i)))
486+ end
487+
488+ """
489+ split_unaligned(v::T, ::Val{A}) -> Tuple{T, T} where {T <: MemoryView}
490+
491+ Split memory view `v` into two views `a` and `b`, where `a` is the smallest prefix of `v`
492+ that guarantees the starting memory address of `b` is is aligned to the integer value `A`.
493+ `A` must be a normal bit-integer, and a power of two in the range 1:64.
494+
495+ If `v` is empty or already aligned, `a` will be empty.
496+ If no elements of `v` is aligned, `b` will be empty and `a` will be equal to `v`.
497+ The element type of `v` must be a bitstype.
498+
499+ !!! warning
500+ When using this function, make sure to `GC.@preserve v`, to make sure Julia
501+ does not move `v` in memory.
502+
503+ # Examples:
504+ ```
505+ julia> split_unaligned(MemoryView(Int16[1, 2, 3]), Val(8))
506+ (Int16[], Int16[1, 2, 3])
507+
508+ julia> split_unaligned(MemoryView(collect(0x01:0x20))[6:13], Val(8))
509+ (UInt8[0x06, 0x07, 0x08], UInt8[0x09, 0x0a, 0x0b, 0x0c, 0x0d])
510+ ```
511+ """
512+ function split_unaligned (v:: MemoryView{T, M} , :: Val{A} ) where {A, T, M}
513+ isbitstype (eltype (v)) || error (" Alignment can only be computed for views of bitstypes" )
514+ A isa Bits || error (" Invalid alignment" )
515+ in (A, (1 , 2 , 4 , 8 , 16 , 32 , 64 )) || error (" Invalid alignment" )
516+ alignment = A % UInt
517+ mask = alignment - 1
518+ sz = Base. elsize (v)
519+ # Early return here to avoid division by zero: Size sz is statically known,
520+ # this will be compiled away
521+ iszero (sz) && return (unsafe_new_memoryview (M, v. ref, 0 ), v)
522+ unaligned_bytes = ((alignment - (UInt (pointer (v)) & mask)) & mask)
523+ n_elements = min (length (v), div (unaligned_bytes, sz % UInt) % Int)
524+ return @inbounds split_at (v, n_elements + 1 )
525+ end
0 commit comments