'Get a half-open [lower, upper) range of byte offsets spanned by
the indexer with the given strides and itemsize.  The indexer is
guaranteed to not go past those bounds.'

def get_offset_bounds(self, strides, itemsize):

    assert (len(strides) == self.aryty.ndim)
    builder = self.builder
    is_empty = cgutils.false_bit
    zero = self.ll_intp(0)
    one = self.ll_intp(1)
    lower = zero
    upper = zero
    for (indexer, shape, stride) in zip(self.indexers, self.indexers_shape, strides):
        is_empty = builder.or_(is_empty, builder.icmp_unsigned('==', shape, zero))
        (lower_index, upper_index) = indexer.get_index_bounds()
        lower_offset = builder.mul(stride, lower_index)
        upper_offset = builder.mul(stride, builder.sub(upper_index, one))
        is_downwards = builder.icmp_signed('<', stride, zero)
        lower = builder.add(lower, builder.select(is_downwards, upper_offset, lower_offset))
        upper = builder.add(upper, builder.select(is_downwards, lower_offset, upper_offset))
    upper = builder.add(upper, itemsize)
    lower = builder.select(is_empty, zero, lower)
    upper = builder.select(is_empty, zero, upper)
    return (lower, upper)
