Skip to content

Commit 63fbdd9

Browse files
committed
🐛 Fix SequenceSet count dups with multiple "*"
In `#count`, "*" is treated as if it is effectively UINT32_MAX. That was also the intention for `#count_with_duplicates`. Unlike `#count`, which can assume that `*` only appears at most once, `#count_with_duplicates` needs to check each entry. This means that, e.g: SequenceSet["#{UINT32_MAX}:*"].count_with_duplicates == 1 SequenceSet["#{UINT32_MAX},*"].count_with_duplicates == 2
1 parent a2cc018 commit 63fbdd9

File tree

2 files changed

+27
-10
lines changed

2 files changed

+27
-10
lines changed

lib/net/imap/sequence_set.rb

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,26 +1033,35 @@ def to_set; Set.new(numbers) end
10331033

10341034
# Returns the count of #numbers in the set.
10351035
#
1036-
# If <tt>*</tt> and <tt>2**32 - 1</tt> (the maximum 32-bit unsigned
1037-
# integer value) are both in the set, they will only be counted once.
1036+
# <tt>*</tt> will be counted as <tt>2**32 - 1</tt> (the maximum 32-bit
1037+
# unsigned integer value).
1038+
#
1039+
# Related: #count_with_duplicates
10381040
def count
1039-
count_numbers_in_tuples(@tuples)
1041+
@tuples.sum(@tuples.count) { _2 - _1 } +
1042+
(include_star? && include?(UINT32_MAX) ? -1 : 0)
10401043
end
10411044

10421045
alias size count
10431046

10441047
# Returns the count of numbers in the ordered #entries, including any
10451048
# repeated numbers.
10461049
#
1050+
# <tt>*</tt> will be counted as <tt>2**32 - 1</tt> (the maximum 32-bit
1051+
# unsigned integer value).
1052+
#
10471053
# When #string is normalized, this behaves the same as #count.
10481054
#
10491055
# Related: #entries, #count_duplicates, #has_duplicates?
10501056
def count_with_duplicates
10511057
return count unless @string
1052-
count_numbers_in_tuples(each_entry_tuple)
1058+
each_entry_tuple.sum {|min, max|
1059+
max - min + ((max == STAR_INT && min != STAR_INT) ? 0 : 1)
1060+
}
10531061
end
10541062

1055-
# Returns the count of repeated numbers in the ordered #entries.
1063+
# Returns the count of repeated numbers in the ordered #entries, the
1064+
# difference between #count_with_duplicates and #count.
10561065
#
10571066
# When #string is normalized, this is zero.
10581067
#
@@ -1074,11 +1083,6 @@ def has_duplicates?
10741083
count_with_duplicates != count
10751084
end
10761085

1077-
private def count_numbers_in_tuples(tuples)
1078-
tuples.sum(tuples.count) { _2 - _1 } +
1079-
(include_star? && include?(UINT32_MAX) ? -1 : 0)
1080-
end
1081-
10821086
# Returns the index of +number+ in the set, or +nil+ if +number+ isn't in
10831087
# the set.
10841088
#

test/net/imap/test_sequence_set.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,19 @@ def test_inspect((expected, input, freeze))
730730
complement: "6:8,12:*",
731731
}, keep: true
732732

733+
data "multiple *", {
734+
input: "2:*,3:*,*",
735+
elements: [2..],
736+
entries: [2.., 3.., :*],
737+
ranges: [2..],
738+
numbers: RangeError,
739+
to_s: "2:*,3:*,*",
740+
normalize: "2:*",
741+
count: 2**32 - 2,
742+
count_dups: 2**32 - 2,
743+
complement: "1",
744+
}, keep: true
745+
733746
data "array", {
734747
input: ["1:5,3:4", 9..11, "10", 99, :*],
735748
elements: [1..5, 9..11, 99, :*],

0 commit comments

Comments
 (0)