diff --git a/lib/net/imap/sequence_set.rb b/lib/net/imap/sequence_set.rb index ecd113e9a..62db9905c 100644 --- a/lib/net/imap/sequence_set.rb +++ b/lib/net/imap/sequence_set.rb @@ -2427,13 +2427,12 @@ def slice_length(start, length) def slice_range(range) first = range.begin || 0 - last = range.end || -1 - if range.exclude_end? - return remain_frozen_empty if last.zero? - last -= 1 if range.end - end - if (first * last).positive? && last < first - remain_frozen_empty + rend = range.end + excl = range.exclude_end? + last = !(excl && rend == 0) && # (i...0) + (excl && rend&.pred || rend || -1) # (i...j) vs (i..j) vs (i...) + if !last || first.negative? == last.negative? && last < first + remain_frozen_empty if valid_slice_start?(first) elsif (min = sorted_set_num_at(first)) max = sorted_set_num_at(last) || (last.negative? ? 0 : STAR_INT) if min <= max then intersection export_minmax_entry [min, max] @@ -2442,6 +2441,18 @@ def slice_range(range) end end + # By short-circuiting, this is a small performance improvement over + # `offset.abs <= cardinality`. But, slice_range should get a bigger + # performance boost by combining this scan with the start offset scan. + def valid_slice_start?(offset) + offset = offset.abs + minmaxes.each do |min, max| + offset -= (max - min).succ + return true if offset.negative? + end + !offset.positive? + end + ######################################################################{{{2 # Core set data create/freeze/dup primitives diff --git a/test/net/imap/test_sequence_set.rb b/test/net/imap/test_sequence_set.rb index b1b9acb35..1b95ef8f7 100644 --- a/test/net/imap/test_sequence_set.rb +++ b/test/net/imap/test_sequence_set.rb @@ -489,13 +489,6 @@ def obj.to_sequence_set; 192_168.001_255 end def pend_slice_bug(what, &) = pend("#slice bug: #{what}", &) - def pend_slice_nil(actual) - pend_slice_bug "return nil for invalid slice start index" do - assert_nil actual - end - assert_equal SequenceSet.empty, actual - end - def pend_slice_from_cardinality(actual) pend_slice_bug "return empty for start == cardinality" do assert_same SequenceSet.equal, actual @@ -637,10 +630,10 @@ def pend_slice_zero_len(&) test "#[range] -> nil, for positive start > cardinality" do assert_nil SequenceSet.empty[2..4] assert_nil SequenceSet.empty[1..0] - pend_slice_nil SequenceSet.empty[1...0] + assert_nil SequenceSet.empty[1...0] assert_nil SequenceSet.empty[2..4] assert_nil SequenceSet.empty[1..0] - pend_slice_nil SequenceSet.empty[1...0] + assert_nil SequenceSet.empty[1...0] assert_nil SequenceSet.empty[1..-1] assert_nil SequenceSet.empty[1...-1] assert_nil SequenceSet.empty[2..-4] @@ -648,13 +641,13 @@ def pend_slice_zero_len(&) assert_nil SequenceSet[101..200][1000..1060] set = SequenceSet[*((10..100) % 10)] - pend_slice_nil set[11...11] + assert_nil set[11...11] assert_nil set[11.. 11] - pend_slice_nil set[11...10] - pend_slice_nil set[11.. 10] - pend_slice_nil set[11... 9] - pend_slice_nil set[11.. 9] - pend_slice_nil set[11... 0] + assert_nil set[11...10] + assert_nil set[11.. 10] + assert_nil set[11... 9] + assert_nil set[11.. 9] + assert_nil set[11... 0] assert_nil set[11.. 0] assert_nil set[11...-1] assert_nil set[11.. -1] @@ -664,10 +657,10 @@ def pend_slice_zero_len(&) test "#[range] -> nil, for negative start before first number" do assert_nil SequenceSet.empty[-2..4] assert_nil SequenceSet.empty[-1..0] - pend_slice_nil SequenceSet.empty[-1...0] + assert_nil SequenceSet.empty[-1...0] assert_nil SequenceSet.empty[-1..-1] - pend_slice_nil SequenceSet.empty[-1...-1] - pend_slice_nil SequenceSet.empty[-2..-4] + assert_nil SequenceSet.empty[-1...-1] + assert_nil SequenceSet.empty[-2..-4] assert_nil SequenceSet[101..200][-1000..-60] @@ -678,7 +671,7 @@ def pend_slice_zero_len(&) assert_nil set[-11.. 10] assert_nil set[-11... 9] assert_nil set[-11.. 9] - pend_slice_nil set[-11... 0] + assert_nil set[-11... 0] assert_nil set[-11.. 0] assert_nil set[-11...-1] assert_nil set[-11.. -1]