@@ -77,17 +77,30 @@ struct IdOffsetRange{T<:Integer,I<:AbstractUnitRange{T}} <: AbstractUnitRange{T}
7777 parent:: I
7878 offset:: T
7979
80- IdOffsetRange {T,I} (r:: I , offset:: T ) where {T<: Integer ,I<: AbstractUnitRange{T} } = new {T,I} (r, offset)
80+ function IdOffsetRange {T,I} (r:: I , offset:: T ) where {T<: Integer ,I<: AbstractUnitRange{T} }
81+ _bool_check (T, r, offset)
82+ new {T,I} (r, offset)
83+ end
8184
8285 #= This method is necessary to avoid a StackOverflowError in IdOffsetRange{T,I}(r::IdOffsetRange, offset::Integer).
8386 The type signature in that method is more specific than IdOffsetRange{T,I}(r::I, offset::T),
8487 so it ends up calling itself if I <: IdOffsetRange.
8588 =#
8689 function IdOffsetRange {T,IdOffsetRange{T,I}} (r:: IdOffsetRange{T,I} , offset:: T ) where {T<: Integer ,I<: AbstractUnitRange{T} }
90+ _bool_check (T, r, offset)
8791 new {T,IdOffsetRange{T,I}} (r, offset)
8892 end
8993end
9094
95+ function _bool_check (:: Type{Bool} , r, offset)
96+ # disallow the construction of IdOffsetRange{Bool, UnitRange{Bool}}(true:true, true)
97+ if offset && (first (r) || last (r))
98+ throw (ArgumentError (" values = $r and offset = $offset can not produce a boolean range" ))
99+ end
100+ return nothing
101+ end
102+ _bool_check (:: Type , r, offset) = nothing
103+
91104# Construction/coercion from arbitrary AbstractUnitRanges
92105function IdOffsetRange {T,I} (r:: AbstractUnitRange , offset:: Integer = 0 ) where {T<: Integer ,I<: AbstractUnitRange{T} }
93106 rc, o = offset_coerce (I, r)
@@ -157,35 +170,61 @@ offset_coerce(::Type{I}, r::AbstractUnitRange) where I<:AbstractUnitRange =
157170Base. reduced_index (i:: IdOffsetRange ) = typeof (i)(first (i): first (i))
158171# Workaround for #92 on Julia < 1.4
159172Base. reduced_index (i:: IdentityUnitRange{<:IdOffsetRange} ) = typeof (i)(first (i): first (i))
160- for f in [:firstindex , :lastindex , :first , :last ]
173+ for f in [:firstindex , :lastindex ]
161174 @eval @inline Base.$ f (r:: IdOffsetRange ) = $ f (r. parent) + r. offset
162175end
176+ for f in [:first , :last ]
177+ # coerce the type to deal with values that get promoted on addition (eg. Bool)
178+ @eval @inline Base.$ f (r:: IdOffsetRange ) = eltype (r)($ f (r. parent) + r. offset)
179+ end
163180
164181@inline function Base. iterate (r:: IdOffsetRange )
165182 ret = iterate (r. parent)
166183 ret === nothing && return nothing
167- return (ret[1 ] + r. offset, ret[2 ])
184+ return (eltype (r)( ret[1 ] + r. offset) , ret[2 ])
168185end
169186@inline function Base. iterate (r:: IdOffsetRange , i)
170187 ret = iterate (r. parent, i)
171188 ret === nothing && return nothing
172- return (ret[1 ] + r. offset, ret[2 ])
189+ return (eltype (r)( ret[1 ] + r. offset) , ret[2 ])
173190end
174191
175192@inline function Base. getindex (r:: IdOffsetRange , i:: Integer )
193+ i isa Bool && throw (ArgumentError (" invalid index: $i of type Bool" ))
176194 @boundscheck checkbounds (r, i)
177- @inbounds r. parent[i - r. offset] + r. offset
178- end
179- @inline function Base. getindex (r:: IdOffsetRange , s:: AbstractUnitRange{<:Integer} )
180- @boundscheck checkbounds (r, s)
181- @inbounds pr = r. parent[_subtractoffset (s, r. offset)] .+ r. offset
182- _indexedby (pr, axes (s))
183- end
184- # The following method is required to avoid falling back to getindex(::AbstractUnitRange, ::StepRange{<:Integer})
185- @inline function Base. getindex (r:: IdOffsetRange , s:: StepRange{<:Integer} )
186- @boundscheck checkbounds (r, s)
187- @inbounds rs = r. parent[s .- r. offset] .+ r. offset
188- return no_offset_view (rs)
195+ @inbounds eltype (r)(r. parent[i - r. offset] + r. offset)
196+ end
197+
198+ # Logical indexing following https://github.com/JuliaLang/julia/pull/31829
199+ #= Helper function to perform logical indxeing for boolean ranges
200+ The code implemented is a branch-free version of the following:
201+
202+ range(first(s) ? first(r) : last(r), length=Int(last(s)))
203+
204+ See https://github.com/JuliaArrays/OffsetArrays.jl/pull/224#discussion_r595635143
205+
206+ Logical indexing does not preserve indices, unlike other forms of vector indexing
207+ =#
208+ @inline function _getindex (r, s:: AbstractUnitRange{Bool} )
209+ range (first (r) * first (s) + last (r) * ! first (s), length= Int (last (s)))
210+ end
211+ @inline function _getindex (r, s:: StepRange{Bool} )
212+ range (first (r) * first (s) + last (r) * ! first (s), step = oneunit (step (s)), length= Int (last (s)))
213+ end
214+ @inline function _getindex (r, s:: AbstractUnitRange )
215+ @inbounds rs = r. parent[_subtractoffset (s, r. offset)] .+ r. offset
216+ _indexedby (rs, axes (s))
217+ end
218+ @inline function _getindex (r, s:: StepRange )
219+ rs = @inbounds r. parent[s .- r. offset] .+ r. offset
220+ _indexedby (rs, axes (s))
221+ end
222+
223+ for T in [:AbstractUnitRange , :StepRange ]
224+ @eval @inline function Base. getindex (r:: IdOffsetRange , s:: $T{<:Integer} )
225+ @boundscheck checkbounds (r, s)
226+ return _getindex (r, s)
227+ end
189228end
190229
191230# offset-preserve broadcasting
0 commit comments