I have implemented a simple circular buffer type similar to this, but with cumulative indexing, so that it has offset field that grows each time an element is pushed back to the full buffer. (That’s why I did not used immutable OffsetArray with its offsets tuple).
mutable struct SlidingBuffer{T} <: AbstractVector{T}
buffer::Vector{T}
len::Int
contentLen::Int
first::Int
last::Int
offset::Int
function SlidingBuffer{T}(len::Int) where T
new{T}(Vector{T}(undef, len), len, 0, 0, 1, 0, 0)
end
end
# custom indexing
@inline Base.axes(A::SlidingBuffer) =
(UnitRange{Int}(A.offset + 1, A.offset + A.contentLen),)
# and other methods from AbstractArray interface ...
It behaves in the following way:
b = SlidingBuffer{Int}(10)
> SlidingBuffer{Int64} with indices 1:0
[push!(b, 123) for _=1:15]
b
> SlidingBuffer{Int64} with indices 6:15:
123
123
123
123
⋮
123
123
123
123
Then, I figured that I miss some similar methods from Interfaces · The Julia Language
and maybe used UnitRange instead of AbstractUnitRange.
And on some operations, like b = b*3 these methods are taken from OffsetArray.jl package, automatically converting my buffer to OffsetArray, due to this method:
Base.similar(::Type{T}, shape::Tuple{OffsetAxis,Vararg{OffsetAxis}}) where {T<:AbstractArray} =
OffsetArray(T(undef, map(indexlength, shape)), map(indexoffset, shape))
with OffsetAxis broadly defined as:
const OffsetAxis = Union{Integer, UnitRange, Base.OneTo, IdentityUnitRange}
All these function recalls with different types are somewhat confusing. How should I write those similar and axes methods for my simple buffer type to not have any type inference between packages?