@@ -77,17 +77,30 @@ struct IdOffsetRange{T<:Integer,I<:AbstractUnitRange{T}} <: AbstractUnitRange{T}
77
77
parent:: I
78
78
offset:: T
79
79
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
81
84
82
85
#= This method is necessary to avoid a StackOverflowError in IdOffsetRange{T,I}(r::IdOffsetRange, offset::Integer).
83
86
The type signature in that method is more specific than IdOffsetRange{T,I}(r::I, offset::T),
84
87
so it ends up calling itself if I <: IdOffsetRange.
85
88
=#
86
89
function IdOffsetRange {T,IdOffsetRange{T,I}} (r:: IdOffsetRange{T,I} , offset:: T ) where {T<: Integer ,I<: AbstractUnitRange{T} }
90
+ _bool_check (T, r, offset)
87
91
new {T,IdOffsetRange{T,I}} (r, offset)
88
92
end
89
93
end
90
94
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
+
91
104
# Construction/coercion from arbitrary AbstractUnitRanges
92
105
function IdOffsetRange {T,I} (r:: AbstractUnitRange , offset:: Integer = 0 ) where {T<: Integer ,I<: AbstractUnitRange{T} }
93
106
rc, o = offset_coerce (I, r)
@@ -157,35 +170,61 @@ offset_coerce(::Type{I}, r::AbstractUnitRange) where I<:AbstractUnitRange =
157
170
Base. reduced_index (i:: IdOffsetRange ) = typeof (i)(first (i): first (i))
158
171
# Workaround for #92 on Julia < 1.4
159
172
Base. 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 ]
161
174
@eval @inline Base.$ f (r:: IdOffsetRange ) = $ f (r. parent) + r. offset
162
175
end
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
163
180
164
181
@inline function Base. iterate (r:: IdOffsetRange )
165
182
ret = iterate (r. parent)
166
183
ret === nothing && return nothing
167
- return (ret[1 ] + r. offset, ret[2 ])
184
+ return (eltype (r)( ret[1 ] + r. offset) , ret[2 ])
168
185
end
169
186
@inline function Base. iterate (r:: IdOffsetRange , i)
170
187
ret = iterate (r. parent, i)
171
188
ret === nothing && return nothing
172
- return (ret[1 ] + r. offset, ret[2 ])
189
+ return (eltype (r)( ret[1 ] + r. offset) , ret[2 ])
173
190
end
174
191
175
192
@inline function Base. getindex (r:: IdOffsetRange , i:: Integer )
193
+ i isa Bool && throw (ArgumentError (" invalid index: $i of type Bool" ))
176
194
@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
189
228
end
190
229
191
230
# offset-preserve broadcasting
0 commit comments