Class: Net::IMAP::SequenceSet
| Relationships & Source Files | |
| Inherits: | Object |
| Defined in: | lib/net/imap/sequence_set.rb |
Overview
An IMAP sequence set is a set of message sequence numbers or unique identifier numbers (“UIDs”). It contains numbers and ranges of numbers. The numbers are all non-zero unsigned 32-bit integers and one special value ("*") that represents the largest value in the mailbox.
Certain types of IMAP responses will contain a SequenceSet, for example the data for a "MODIFIED" ResponseCode. Some IMAP commands may receive a SequenceSet as an argument, for example #search, #fetch, and #store.
Creating sequence sets
.new may receive a single optional argument: a non-zero 32 bit unsigned integer, a range, a sequence-set formatted string, another SequenceSet, a Set (containing only numbers or *), or an Array containing any of these (array inputs may be nested).
set = Net::IMAP::SequenceSet.new(1)
set.valid_string #=> "1"
set = Net::IMAP::SequenceSet.new(1..100)
set.valid_string #=> "1:100"
set = Net::IMAP::SequenceSet.new(1...100)
set.valid_string #=> "1:99"
set = Net::IMAP::SequenceSet.new([1, 2, 5..])
set.valid_string #=> "1:2,5:*"
set = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
set.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
set = Net::IMAP::SequenceSet.new(1, 2, 3..7, 5, 6..10, 2048, 1024)
set.valid_string #=> "1:10,55,1024:2048"
.new with no arguments creates an empty sequence set. Note that an empty sequence set is invalid in the IMAP grammar.
set = Net::IMAP::SequenceSet.new
set.empty? #=> true
set.valid? #=> false
set.valid_string #!> raises DataFormatError
set << 1..10
set.empty? #=> false
set.valid? #=> true
set.valid_string #=> "1:10"
Using .new with another SequenceSet input behaves the same as calling #dup on the other set. The input’s #string will be preserved.
input = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
copy = Net::IMAP::SequenceSet.new(input)
input.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
copy.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
copy2 = input.dup # same as calling new with a SequenceSet input
copy == input #=> true, same set membership
copy.eql? input #=> true, same string value
copy.equal? input #=> false, different objects
copy.normalize!
copy.valid_string #=> "1:10,1024,2048"
copy == input #=> true, same set membership
copy.eql? input #=> false, different string value
copy << 999
copy.valid_string #=> "1:10,999,1024,2048"
copy == input #=> false, different set membership
copy.eql? input #=> false, different string value
Use SequenceSet() to coerce a single (optional) input. A SequenceSet input is returned without duplication, even when frozen.
set = Net::IMAP::SequenceSet()
set.string #=> nil
set.frozen? #=> false
# String order is preserved
set = Net::IMAP::SequenceSet("1,2,3:7,5,6:10,2048,1024")
set.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
set.frozen? #=> false
# Other inputs are normalized
set = Net::IMAP::SequenceSet([1, 2, [3..7, 5], 6..10, 2048, 1024])
set.valid_string #=> "1:10,1024,2048"
set.frozen? #=> false
unfrozen = set
frozen = set.dup.freeze
unfrozen.equal? Net::IMAP::SequenceSet(unfrozen) #=> true
frozen.equal? Net::IMAP::SequenceSet(frozen) #=> true
Use .[] to coerce one or more arguments into a valid frozen SequenceSet. A valid frozen SequenceSet is returned directly, without allocating a new object. .[] will not create an invalid (empty) set.
Net::IMAP::SequenceSet[] #!> raises ArgumentError
Net::IMAP::SequenceSet[nil] #!> raises DataFormatError
Net::IMAP::SequenceSet[""] #!> raises DataFormatError
# String order is preserved
set = Net::IMAP::SequenceSet["1,2,3:7,5,6:10,2048,1024"]
set.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
set.frozen? #=> true
# Other inputs are normalized
set = Net::IMAP::SequenceSet[1, 2, [3..7, 5], 6..10, 2048, 1024]
set.valid_string #=> "1:10,1024,2048"
set.frozen? #=> true
frozen = set
unfrozen = set.dup
frozen.equal? Net::IMAP::SequenceSet[frozen] #=> true
unfrozen.equal? Net::IMAP::SequenceSet[unfrozen] #=> false
Objects which respond to #to_sequence_set (such as SearchResult and ThreadMember) can be coerced to a SequenceSet with .new, .try_convert, .[], or SequenceSet.
search = imap.uid_search(["SUBJECT", "hello", "NOT", "SEEN"])
seqset = Net::IMAP::SequenceSet(search) - already_fetched
fetch = imap.uid_fetch(seqset, "FAST")
Ordered and Normalized sets
Sometimes the order of the set’s members is significant, such as with the ESORT, CONTEXT=SORT, and UIDPLUS extensions. So, when a sequence set is created from a single string (such as by the parser), that #string representation is preserved. Assigning a string with #string= or #replace will also preserve that string. Use #each_entry, #entries, or #each_ordered_number to enumerate the entries in their #string order. Hash equality (using #eql?) is based on the string representation.
Internally, SequenceSet uses a normalized uint32 set representation which sorts and de-duplicates all numbers and coalesces adjacent or overlapping entries. Many methods use this sorted set representation for O(lg n) searches. Use #each_element, #elements, #each_range, #ranges, #each_number, or #numbers to enumerate the set in sorted order. Basic object equality (using #==) is based on set membership, without regard to #entry order or #string normalization.
Most modification methods reset #string to its #normalized form, so that #entries and #elements are identical. Use #append to preserve #entries order while modifying a set.
Non-normalized sets store both representations of the set, which can more than double memory usage. Very large sequence sets should avoid denormalizing methods (such as #append) unless order is significant.
Using *
IMAP sequence sets may contain a special value "*", which represents the largest number in use. From seq-number in RFC9051 §9:
In the case of message sequence numbers, it is the number of messages in a non-empty mailbox. In the case of unique identifiers, it is the unique identifier of the last message in the mailbox or, if the mailbox is empty, the mailbox’s current UIDNEXT value.
When creating a SequenceSet, * may be input as -1, "*", :*, an endless range, or a range ending in -1. When converting to #elements, #ranges, or #numbers, it will output as either :* or an endless range. For example:
Net::IMAP::SequenceSet["1,3,*"].to_a #=> [1, 3, :*]
Net::IMAP::SequenceSet["1,234:*"].to_a #=> [1, 234..]
Net::IMAP::SequenceSet[1234..-1].to_a #=> [1234..]
Net::IMAP::SequenceSet[1234..].to_a #=> [1234..]
Net::IMAP::SequenceSet[1234..].to_s #=> "1234:*"
Net::IMAP::SequenceSet[1234..-1].to_s #=> "1234:*"
Use #limit to convert "*" to a maximum value. When a range includes "*", the maximum value will always be matched:
Net::IMAP::SequenceSet["9999:*"].limit(max: 25)
#=> Net::IMAP::SequenceSet["25"]
Surprising * behavior
When a set includes *, some methods may have surprising behavior.
For example, #complement treats * as its own member. This way, the #intersection of a set and its #complement will always be empty. And * is sorted as greater than any other number in the set. This is not how an IMAP server interprets the set: it will convert * to the number of messages in the mailbox, the UID of the last message in the mailbox, or UIDNEXT, as appropriate. Several methods have an argument for how * should be interpreted.
But, for example, this means that there may be overlap between a set and its complement after #limit is applied to each:
~Net::IMAP::SequenceSet["*"] == Net::IMAP::SequenceSet[1..(2**32-1)]
~Net::IMAP::SequenceSet[1..5] == Net::IMAP::SequenceSet["6:*"]
set = Net::IMAP::SequenceSet[1..5]
(set & ~set).empty? => true
(set.limit(max: 4) & (~set).limit(max: 4)).to_a => [4]
When counting the number of numbers in a set, * will be counted as if it were equal to UINT32_MAX:
UINT32_MAX = 2**32 - 1
Net::IMAP::SequenceSet["*"].count => 1
Net::IMAP::SequenceSet[1..UINT32_MAX - 1, :*].count => UINT32_MAX
Net::IMAP::SequenceSet["1:*"].count => UINT32_MAX
Net::IMAP::SequenceSet[UINT32_MAX, :*].count => 1
Net::IMAP::SequenceSet[UINT32_MAX..].count => 1
Use #cardinality to count the set members wxth * counted as a distinct member:
Net::IMAP::SequenceSet[1..].cardinality #=> UINT32_MAX + 1
Net::IMAP::SequenceSet[UINT32_MAX, :*].cardinality #=> 2
Net::IMAP::SequenceSet[UINT32_MAX..].cardinality #=> 2
What’s here?
SequenceSet provides methods for:
-
{Net::IMAP String Formatting}
Methods for Creating a SequenceSet
-
.[]: Creates a validated frozen sequence set from one or more inputs.
-
.new: Creates a new mutable sequence set, which may be empty (invalid).
-
.try_convert: Calls #to_sequence_set on an object and verifies that the result is a SequenceSet.
-
SequenceSet(): Coerce an input using .try_convert or .new. -
.empty: Returns a frozen empty (invalid)
SequenceSet. -
.full: Returns a frozen
SequenceSetcontaining every possible number.
Methods for Comparing
Comparison to another SequenceSet:
-
#==: Returns whether a given set contains the same numbers as
self. -
#eql?: Returns whether a given set uses the same #string as
self.
Comparison to objects which are convertible to SequenceSet:
-
#===: Returns whether a given object is fully contained within
self, ornilif the object cannot be converted to a compatible type. -
#cover?: Returns whether a given object is fully contained within
self. -
#intersect? (aliased as #overlap?): Returns whether
selfand a given object have any common elements. -
#disjoint?: Returns whether
selfand a given object have no common elements.
Methods for Querying
These methods do not modify self.
Set membership:
-
#include? (aliased as #member?): Returns whether a given element is contained by the set.
-
#include_star?: Returns whether the set contains
*.
Minimum and maximum value elements:
-
#min: Returns one or more of the lowest numbers in the set.
-
#max: Returns one or more of the highest numbers in the set.
-
#minmax: Returns the lowest and highest numbers in the set.
Accessing value by offset in sorted set:
-
#[] (aliased as #slice): Returns the number or consecutive subset at a given offset or range of offsets in the sorted set.
-
#at: Returns the number at a given offset in the sorted set.
-
#find_index: Returns the given number’s offset in the sorted set.
Accessing value by offset in ordered entries
-
#ordered_at: Returns the number at a given offset in the ordered entries.
-
#find_ordered_index: Returns the index of the given number’s first occurrence in entries.
Set cardinality:
-
#cardinality: Returns the number of distinct members in the set.
*is counted as its own member, distinct from UINT32_MAX. -
#count: Returns the count of distinct numbers in the set.
*is counted as equal to UINT32_MAX. -
#empty?: Returns whether the set has no members. IMAP syntax does not allow empty sequence sets.
-
#valid?: Returns whether the set has any members.
-
#full?: Returns whether the set contains every possible value, including
*.
Denormalized properties:
-
#normalized?: Returns whether #entries are sorted, deduplicated, and coalesced, and all #string entries are in normalized form.
-
#has_duplicates?: Returns whether the ordered entries repeat any numbers.
-
#size: Returns the total size of all #entries, including repeated numbers.
*is counted as its own member, distinct from UINT32_MAX. -
#count_with_duplicates: Returns the count of numbers in the ordered #entries, including repeated numbers.
*is counted as equal to UINT32_MAX. -
#count_duplicates: Returns the count of repeated numbers in the ordered #entries.
*is counted as equal to UINT32_MAX.
Methods for Iterating
Normalized (sorted and coalesced):
-
#each_element: Yields each number and range in the set, sorted and coalesced, and returns
self. -
#elements (aliased as #to_a): Returns an Array of every number and range in the set, sorted and coalesced.
-
#each_range: Yields each element in the set as a Range and returns
self. -
#ranges: Returns an Array of every element in the set, converting numbers into ranges of a single value.
-
#each_number: Yields each number in the set and returns
self. -
#numbers: Returns an Array with every number in the set, expanding ranges into all of their contained numbers.
-
#to_set: Returns a Set containing all of the #numbers in the set.
Order preserving:
-
#each_entry: Yields each number and range in the set, unsorted and without deduplicating numbers or coalescing ranges, and returns
self. -
#entries: Returns an Array of every number and range in the set, unsorted and without deduplicating numbers or coalescing ranges.
-
#each_ordered_number: Yields each number in the ordered entries and returns
self.
Methods for Set Operations
These methods do not modify self.
-
#| (aliased as #union and #+): Returns a new set combining all members from
selfwith all members from the other set. -
#& (aliased as #intersection): Returns a new set containing all members common to
selfand the other set. -
#- (aliased as #difference): Returns a copy of
selfwith all members in the other set removed. -
#^ (aliased as #xor): Returns a new set containing all members from
selfand the other set except those common to both. -
#~ (aliased as #complement): Returns a new set containing all members that are not in
self -
#above: Return a copy of
selfwhich only contains numbers above a given number. -
#below: Return a copy of
selfwhich only contains numbers below a given value. -
#limit: Returns a copy of
selfwhich has replaced*with a given maximum value and removed all members over that maximum.
Methods for Assigning
These methods add or replace numbers in self.
Normalized (sorted and coalesced):
These methods always update #string to be fully sorted and coalesced.
-
#add (aliased as #<<): Adds a given element to the set; returns
self. -
#add?: If the given element is not fully included the set, adds it and returns
self; otherwise, returnsnil. -
#merge: In-place set #union. Adds all members of the given sets into this set; returns
self. -
#complement!: In-place set #complement. Replaces the contents of this set with its own #complement; returns
self. -
#xor!: In-place
XORoperation. Adds numbers that are unique to the other set and removes numbers that are common to both; returnsself.
Order preserving:
These methods may cause #string to not be sorted or coalesced.
-
#append: Adds the given entry to the set, appending it to the existing string, and returns
self. -
#string=: Assigns a new #string value and replaces #elements to match.
-
#replace: Replaces the contents of the set with the contents of a given object.
Methods for Deleting
These methods remove numbers from self, and update #string to be fully sorted and coalesced.
-
#clear: Removes all elements in the set; returns
self. -
#delete: Removes a given element from the set; returns
self. -
#delete?: If the given element is included in the set, removes it and returns it; otherwise, returns
nil. -
#delete_at: Removes the number at a given offset.
-
#intersect!: In-place set #intersection. Removes numbers that are not in the given set; returns
self. -
#slice!: Removes the number or consecutive numbers at a given offset or range of offsets.
-
#subtract: In-place set #difference. Removes all members of the given sets from this set; returns
self. -
#limit!: Replaces
*with a given maximum value and removes all members over that maximum; returnsself.
Methods for IMAP String Formatting
-
#to_s: Returns the
sequence-setstring, or an empty string when the set is empty. -
#string: Returns the
sequence-setstring, or nil when empty. -
#valid_string: Returns the
sequence-setstring, or raises DataFormatError when the set is empty. -
#normalized_string: Returns a
sequence-setstring with its elements sorted and coalesced, or nil when the set is empty. -
#normalize: Returns a new set with this set’s normalized
sequence-setrepresentation. -
#normalize!: Updates #string to its normalized
sequence-setrepresentation and returnsself.
Constant Summary
-
EMPTY =
private
Internal use only
# File 'lib/net/imap/sequence_set.rb', line 2252new.freeze
-
FULL =
private
Internal use only
# File 'lib/net/imap/sequence_set.rb', line 2253 -
FULL_SET_DATA =
private
Internal use only
# File 'lib/net/imap/sequence_set.rb', line 2249
{{{2 intentionally defined after the class implementation
[[1, STAR_INT].freeze].freeze
-
INSPECT_ABRIDGED_HEAD_RE =
private
# File 'lib/net/imap/sequence_set.rb', line 436/\A#{entries},/ -
INSPECT_ABRIDGED_TAIL_RE =
private
# File 'lib/net/imap/sequence_set.rb', line 437/,#{entries}\z/ -
INSPECT_MAX_LEN =
private
# File 'lib/net/imap/sequence_set.rb', line 423512 -
INSPECT_TRUNCATE_LEN =
private
# File 'lib/net/imap/sequence_set.rb', line 42416 -
STARS =
private
# File 'lib/net/imap/sequence_set.rb', line 420
valid inputs for “*”
[:*, ?*, -1].freeze
-
STAR_INT =
private
# File 'lib/net/imap/sequence_set.rb', line 416
represents “*” internally, to simplify sorting (etc)
UINT32_MAX + 1
-
UINT32_MAX =
# File 'lib/net/imap/sequence_set.rb', line 413
The largest possible non-zero unsigned 32-bit integer
2**32 - 1
Class Method Summary
-
.[](*inputs) ⇒ valid frozen sequence set
Returns a frozen
SequenceSet, constructed frominputs. -
.empty
Returns a frozen empty set singleton.
-
.full
Returns a frozen full set singleton:
"1:*". -
.new(input = nil) ⇒ SequenceSet
constructor
Create a new
SequenceSetobject frominput, which may be anotherSequenceSet, an::Net::IMAPformattedsequence-setstring, a non-zero 32 bit unsigned integer, a range,:*, a Set of numbers or*, an object that responds to #to_sequence_set (such asSearchResult) or an Array of these (array inputs may be nested). -
.try_convert(obj) ⇒ sequence set?
If
objis aSequenceSet, returnsobj.
Instance Attribute Summary
-
#empty? ⇒ Boolean
readonly
Returns true if the set contains no elements.
-
#full? ⇒ Boolean
readonly
Returns true if the set contains every possible element.
-
#has_duplicates? ⇒ Boolean
readonly
Returns whether or not the ordered #entries repeat any numbers.
-
#include_star? ⇒ Boolean
readonly
Returns
truewhen the set contains*. -
#normalized? ⇒ Boolean
readonly
Returns whether #string is fully normalized: entries have been sorted, deduplicated, and coalesced, and all entries are in normal form.
-
#string
rw
Returns the IMAP
sequence-setstring representation, ornilwhen the set is empty. - #string=(input) rw
-
#valid? ⇒ Boolean
readonly
Returns false when the set is empty.
-
#minmaxes
readonly
protected
Internal use only
Alias for #runs.
-
#runs
(also: #minmaxes)
readonly
protected
Internal use only
Alias for #set_data.
- #set_data (also: #runs) readonly protected Internal use only
Instance Method Summary
-
#&(other) ⇒ sequence set
(also: #intersection)
Returns a new sequence set containing only the numbers common to this set and
other. -
#+(other)
Alias for #|.
-
#-(other) ⇒ sequence set
(also: #difference)
Returns a new sequence set built by duplicating this set and removing every number that appears in
other. -
#<<(element)
Alias for #add.
-
#==(other) ⇒ Boolean
Returns true when the other
SequenceSetrepresents the same message identifiers. -
#===(other) ⇒ Boolean
Returns whether
otheris contained within the set. -
#[](index) ⇒ Integer, ...
(also: #slice)
Returns a number or a subset from the sorted set, without modifying the set.
-
#^(other) ⇒ sequence set
(also: #xor)
Returns a new sequence set containing numbers that are exclusive between this set and
other. -
#above(num)
Returns a copy of
selfwhich only contains the numbers abovenum. -
#add(element) ⇒ self
(also: #<<)
Adds a range or number to the set and returns
self. -
#add?(element) ⇒ self?
Adds a range or number to the set and returns
self. -
#append(entry)
Adds a range or number to the set and returns
self. -
#at(index) ⇒ Integer?
Returns the number at the given
indexin the sorted set, without modifying the set. -
#below(num)
Returns a copy of
selfwhich only contains numbers belownum. -
#cardinality
Returns the number of members in the set.
-
#clear
Removes all elements and returns self.
-
#complement
Alias for #~.
-
#complement! ⇒ self
In-place set #complement.
-
#count
Returns the count of distinct #numbers in the set.
-
#count_duplicates
Returns the count of repeated numbers in the ordered #entries, the difference between #count_with_duplicates and #count.
-
#count_with_duplicates
Returns the count of numbers in the ordered #entries, including any repeated numbers.
-
#cover?(other) ⇒ Boolean
Returns whether
otheris contained within the set. -
#deconstruct
Returns an array with #normalized_string when valid and an empty array otherwise.
-
#delete(element) ⇒ self
Deletes the given range or number from the set and returns
self. -
#delete?(number) ⇒ Integer?
Removes a specified value from the set, and returns the removed value.
-
#delete_at(index) ⇒ Numeric, ...
Deletes a number the set, indicated by the given
index. -
#difference(other)
Alias for #-.
-
#disjoint?(other) ⇒ Boolean
Returns
trueif the set and a given object have no common elements,falseotherwise. -
#each_element
Yields each number or range (or
:*) in #elements to the block and returns self. -
#each_entry(&block)
Yields each number or range in #string to the block and returns
self. -
#each_number(&block)
Yields each number in #numbers to the block and returns self.
-
#each_ordered_number(&block)
Yields each number in #entries to the block and returns self.
-
#each_range
Yields each range in #ranges to the block and returns self.
-
#elements
(also: #to_a)
Returns an array of ranges and integers and
:*. -
#entries
Returns an array of ranges and integers and
:*. -
#eql?(other) ⇒ Boolean
Hash equality requires the same encoded #string representation.
-
#find_index(number)
Returns the (sorted and deduplicated) index of
numberin the set, ornilifnumberisn’t in the set. -
#find_ordered_index(number)
Returns the first index of
numberin the ordered #entries, ornilifnumberisn’t in the set. -
#freeze
Freezes and returns the set.
-
#hash
See #eql?
-
#include?(element) ⇒ Boolean
(also: #member?)
Returns
truewhen a given number or range is inself, andfalseotherwise. -
#inspect
Returns an inspection string for the
SequenceSet. -
#intersect!(other)
In-place set #intersection.
-
#intersect?(other) ⇒ Boolean
(also: #overlap?)
Returns
trueif the set and a given object have any common elements,falseotherwise. -
#intersection(other)
Alias for #&.
- #limit(max:)
-
#limit!(max:)
Removes all members over #max and returns self.
-
#max(star: :*) ⇒ Integer, ...
Returns the maximum value in
self,starwhen the set includes*, ornilwhen the set is empty. -
#member?(element)
Alias for #include?.
-
#merge(*sets)
In-place set #union.
-
#min(star: :*) ⇒ Integer, ...
Returns the minimum value in
self,starwhen the only value in the set is*, ornilwhen the set is empty. -
#minmax(star: :*) ⇒ Array, max
Returns a 2-element array containing the minimum and maximum numbers in
self, ornilwhen the set is empty. -
#normalize
Returns a
SequenceSetwith a normalized string representation: entries have been sorted, deduplicated, and coalesced, and all entries are in normal form. -
#normalize!
Resets #string to be sorted, deduplicated, and coalesced.
-
#normalized_string
Returns a normalized
sequence-setstring representation, sorted and deduplicated. -
#numbers
Returns a sorted array of all of the number values in the sequence set.
-
#ordered_at(index) ⇒ Integer?
Returns the number at the given
indexin the ordered #entries, without modifying the set. -
#overlap?(other)
Alias for #intersect?.
-
#ranges
Returns an array of ranges.
-
#replace(other)
Replace the contents of the set with the contents of
otherand returnsself. -
#size
Returns the combined size of the ordered #entries, including any repeated numbers.
-
#slice(index, length = nil)
Alias for #[].
-
#slice!(index) ⇒ Integer, ...
Deletes a number or consecutive numbers from the set, indicated by the given
index,startandlength, orrangeof offsets. -
#subtract(*sets)
In-place set #difference.
-
#to_a
Alias for #elements.
-
#to_s
Returns the IMAP
sequence-setstring representation, or an empty string when the set is empty. -
#to_sequence_set ⇒ self
Returns
self -
#to_set
Returns a Set with all of the #numbers in the sequence set.
-
#union(other)
Alias for #|.
-
#valid_string
Returns the IMAP
sequence-setstring representation, or raises aDataFormatErrorwhen the set is empty. -
#xor(other)
Alias for #^.
-
#xor!(other)
In-place set #xor.
-
#|(other) ⇒ sequence set
(also: #+, #union)
Returns a new sequence set that has every number in the
otherobject added. -
#~(self) ⇒ sequence set
(also: #complement)
Returns the complement of self, a
SequenceSetwhich contains all numbers except for those in this set. -
#encode_with(coder)
Internal use only
For YAML serialization.
-
#init_with(coder)
Internal use only
For YAML deserialization.
-
#send_data(imap, tag)
Internal use only
Unstable API: for internal use only (Net::IMAP#send_data).
-
#validate
Internal use only
Unstable API: currently for internal use only (Net::IMAP#validate_data).
- #dup_set_data protected Internal use only
- #add_coalesced_minmax(lower_idx, lmin, lmax, min, max) private Internal use only
-
#add_minmax(minmax)
(also: #add_run)
private
Internal use only
–|=====| |=====new run=======| append ?????????-|=====new run=======|-|===lower===|– insert.
- #add_minmaxes(minmaxes) (also: #add_runs) private Internal use only
-
#add_run(minmax)
private
Internal use only
Alias for #add_minmax.
-
#add_runs(minmaxes)
private
Internal use only
Alias for #add_minmaxes.
- #append_minmax(min, max) private Internal use only
- #bsearch_index(num) private Internal use only
- #bsearch_minmax(num) private Internal use only
- #bsearch_range(num) private Internal use only
-
#count_entries
private
Internal use only
{{{2 Ordered entry methods.
- #delete_run_at(idx) private Internal use only
- #each_entry_minmax(&block) (also: #each_entry_run) private Internal use only
-
#each_entry_run(&block)
private
Internal use only
Alias for #each_entry_minmax.
- #each_minmax_with_index(minmaxes) private Internal use only
- #each_number_in_minmax(min, max, &block) private Internal use only
-
#each_parsed_entry(str)
private
Internal use only
yields validated but unsorted [num] or [num, num].
- #export_minmax(minmax) (also: #export_run) private Internal use only
- #export_minmax_entry(min, max) (also: #export_run_entry) private Internal use only
- #export_minmaxes(minmaxes) (also: #export_runs) private Internal use only
-
#export_num(num)
private
Internal use only
{{{2 Export methods.
-
#export_run(minmax)
private
Internal use only
Alias for #export_minmax.
-
#export_run_entry(min, max)
private
Internal use only
Alias for #export_minmax_entry.
-
#export_runs(minmaxes)
private
Internal use only
Alias for #export_minmaxes.
- #freeze_set_data private Internal use only
-
#import_minmax(input)
(also: #import_run)
private
Internal use only
{{{2 Import methods.
- #import_num(obj) private Internal use only
- #import_range_minmax(range) private Internal use only
-
#import_run(input)
private
Internal use only
Alias for #import_minmax.
- #import_runs(input) private Internal use only
-
#include_minmax?(min, max) ⇒ Boolean
(also: #include_run?)
private
Internal use only
{{{2 Search methods.
-
#include_run?(min, max)
private
Internal use only
Alias for #include_minmax?.
-
#initialize_clone(other)
private
Internal use only
frozen clones are shallow copied.
- #initialize_dup(other) private Internal use only
-
#input_try_convert(input)
private
Internal use only
unlike
SequenceSet#try_convert, this returns an Integer, Range, String, Set, Array, or… - #insert_minmax(idx, min, max) private Internal use only
- #intersect_minmax?(min, max) ⇒ Boolean (also: #intersect_run?) private Internal use only
-
#intersect_run?(min, max)
private
Internal use only
Alias for #intersect_minmax?.
- #max_at(idx) private Internal use only
- #max_num private Internal use only
- #min_at(idx) private Internal use only
-
#min_num
private
Internal use only
{{{2 Core set data query/enumeration primitives.
-
#modifying!
private
Internal use only
{{{2 Update methods.
-
#new_set_data
private
Internal use only
{{{2 Core set data create/freeze/dup primitives.
- #normal_string?(str) ⇒ Boolean private Internal use only
- #normalized_entries?(entries) ⇒ Boolean private Internal use only
-
#number_input?(input) ⇒ Boolean
private
Internal use only
NOTE: input_try_convert must be called on input first.
- #nz_number(num) private Internal use only
- #parse_entry(str) private Internal use only
- #parse_minmax(str) (also: #parse_run) private Internal use only
-
#parse_run(str)
private
Internal use only
Alias for #parse_minmax.
-
#parse_runs(str)
private
Internal use only
{{{2 Parse methods.
- #remain_frozen(set) private Internal use only
- #remain_frozen_empty private Internal use only
- #replace_minmaxes(other) private Internal use only
- #reverse_each_minmax_with_index(minmaxes) private Internal use only
-
#seek_number_in_minmaxes(minmaxes, index)
private
Internal use only
{{{2 Number indexing methods.
- #set_max_at(idx, max) private Internal use only
-
#set_min_at(idx, min)
private
Internal use only
{{{2 Core set data modification primitives.
- #slice_length(start, length) private Internal use only
- #slice_range(range) private Internal use only
- #slice_runs! private Internal use only
-
#subtract_minmax(minmax)
(also: #subtract_run)
private
Internal use only
|====subtracted run=======| –|====| no more 1.
- #subtract_minmaxes(minmaxes) (also: #subtract_runs) private Internal use only
-
#subtract_run(minmax)
private
Internal use only
Alias for #subtract_minmax.
-
#subtract_runs(minmaxes)
private
Internal use only
Alias for #subtract_minmaxes.
- #trim_or_delete_minmax(lower_idx, lmin, lmax, tmin, tmax) private Internal use only
- #trim_or_split_minmax(idx, lmin, tmin, tmax) private Internal use only
- #truncate_runs!(idx) private Internal use only
Constructor Details
.new(input = nil) ⇒ SequenceSet
Create a new SequenceSet object from input, which may be another SequenceSet, an ::Net::IMAP formatted sequence-set string, a non-zero 32 bit unsigned integer, a range, :*, a Set of numbers or *, an object that responds to #to_sequence_set (such as SearchResult) or an Array of these (array inputs may be nested).
set = Net::IMAP::SequenceSet.new(1)
set.valid_string #=> "1"
set = Net::IMAP::SequenceSet.new(1..100)
set.valid_string #=> "1:100"
set = Net::IMAP::SequenceSet.new(1...100)
set.valid_string #=> "1:99"
set = Net::IMAP::SequenceSet.new([1, 2, 5..])
set.valid_string #=> "1:2,5:*"
set = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
set.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
set = Net::IMAP::SequenceSet.new(1, 2, 3..7, 5, 6..10, 2048, 1024)
set.valid_string #=> "1:10,1024,2048"
With no arguments (or nil) creates an empty sequence set. Note that an empty sequence set is invalid in the IMAP grammar.
set = Net::IMAP::SequenceSet.new
set.empty? #=> true
set.valid? #=> false
set.valid_string #!> raises DataFormatError
set << 1..10
set.empty? #=> false
set.valid? #=> true
set.valid_string #=> "1:10"
When input is a SequenceSet, .new behaves the same as calling #dup on that other set. The input’s #string will be preserved.
input = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
copy = Net::IMAP::SequenceSet.new(input)
input.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
copy.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
copy2 = input.dup # same as calling new with a SequenceSet input
copy == input #=> true, same set membership
copy.eql? input #=> true, same string value
copy.equal? input #=> false, different objects
copy.normalize!
copy.valid_string #=> "1:10,1024,2048"
copy == input #=> true, same set membership
copy.eql? input #=> false, different string value
copy << 999
copy.valid_string #=> "1:10,999,1024,2048"
copy == input #=> false, different set membership
copy.eql? input #=> false, different string value
Alternative set creation methods
-
.[] returns a frozen validated (non-empty)
SequenceSet, without allocating a new object when the input is already a valid frozen SequenceSet. -
SequenceSet() coerces an input toSequenceSet, without allocating a new object when the input is already a SequenceSet. -
.try_convert calls #to_sequence_set on inputs that support it and returns
nilfor inputs that don’t. -
.empty and .full both return frozen singleton sets which can be combined with set operations (#|, #&, #^, #-, etc) to make new sets.
See SequenceSet@Creating+sequence+sets.
# File 'lib/net/imap/sequence_set.rb', line 558
def initialize(input = nil) @set_data = new_set_data @string = nil replace(input) unless input.nil? end
Class Method Details
.[](*inputs) ⇒ valid frozen sequence set
Returns a frozen SequenceSet, constructed from inputs.
When only a single valid frozen SequenceSet is given, that same set is returned.
An empty SequenceSet is invalid and will raise a DataFormatError.
Use .new to create a mutable or empty SequenceSet.
Related: .new, SequenceSet(), .try_convert
.empty
Returns a frozen empty set singleton. Note that valid IMAP sequence sets cannot be empty, so this set is invalid.
# File 'lib/net/imap/sequence_set.rb', line 485
def empty; EMPTY end
.full
Returns a frozen full set singleton: "1:*"
# File 'lib/net/imap/sequence_set.rb', line 488
def full; FULL end
.try_convert(obj) ⇒ sequence set?
If obj is a SequenceSet, returns obj. If obj responds_to #to_sequence_set, calls obj.to_sequence_set and returns the result. Otherwise returns nil.
If obj.to_sequence_set doesn’t return a SequenceSet or nil, an exception is raised.
# File 'lib/net/imap/sequence_set.rb', line 475
def try_convert(obj) return obj if obj.is_a?(SequenceSet) return nil unless obj.respond_to?(:to_sequence_set) return nil unless obj = obj.to_sequence_set return obj if obj.is_a?(SequenceSet) raise DataFormatError, "invalid object returned from to_sequence_set" end
Instance Attribute Details
#empty? ⇒ Boolean (readonly)
Returns true if the set contains no elements
# File 'lib/net/imap/sequence_set.rb', line 834
def empty?; runs.empty? end
#full? ⇒ Boolean (readonly)
Returns true if the set contains every possible element.
# File 'lib/net/imap/sequence_set.rb', line 837
def full?; set_data == FULL_SET_DATA end
#has_duplicates? ⇒ Boolean (readonly)
Returns whether or not the ordered #entries repeat any numbers.
Always returns false when #string is normalized.
Related: #entries, #count_with_duplicates, #count_duplicates
# File 'lib/net/imap/sequence_set.rb', line 1443
def has_duplicates? return false unless @string count_with_duplicates != count end
#include_star? ⇒ Boolean (readonly)
Returns true when the set contains *.
#minmaxes (readonly, protected)
Alias for #runs.
# File 'lib/net/imap/sequence_set.rb', line 1864
alias minmaxes runs
#normalized? ⇒ Boolean (readonly)
Returns whether #string is fully normalized: entries have been sorted, deduplicated, and coalesced, and all entries are in normal form. See SequenceSet@Ordered+and+Normalized+sets.
Net::IMAP::SequenceSet["1,3,5"].normalized? #=> true
Net::IMAP::SequenceSet["20:30"].normalized? #=> true
Net::IMAP::SequenceSet["3,5,1"].normalized? #=> false, not sorted
Net::IMAP::SequenceSet["1,2,3"].normalized? #=> false, not coalesced
Net::IMAP::SequenceSet["1:5,2"].normalized? #=> false, repeated number
Net::IMAP::SequenceSet["1:1"].normalized? #=> false, number as range
Net::IMAP::SequenceSet["5:1"].normalized? #=> false, backwards range
Returns true if (and only if) #string is equal to #normalized_string:
seqset = Net::IMAP::SequenceSet["1:3,5"]
seqset.string #=> "1:3,5"
seqset.normalized_string #=> "1:3,5"
seqset.entries #=> [1..3, 5]
seqset.elements #=> [1..3, 5]
seqset.normalized? #=> true
seqset = Net::IMAP::SequenceSet["3,1,2"]
seqset.string #=> "3,1,2"
seqset.normalized_string #=> "1:3"
seqset.entries #=> [3, 1, 2]
seqset.elements #=> [1..3]
seqset.normalized? #=> false
Can return false even when #entries and #elements are the same:
seqset = Net::IMAP::SequenceSet["5:1"]
seqset.string #=> "5:1"
seqset.normalized_string #=> "1:5"
seqset.entries #=> [1..5]
seqset.elements #=> [1..5]
seqset.normalized? #=> false
Note that empty sets are normalized, even though they are not #valid?:
seqset = Net::IMAP::SequenceSet.empty
seqset.normalized? #=> true
seqset.valid? #=> false
Related: #normalize, #normalize!, #normalized_string
# File 'lib/net/imap/sequence_set.rb', line 1736
def normalized? @string.nil? || normal_string?(@string) end
#runs (readonly, protected) Also known as: #minmaxes
Alias for #set_data.
# File 'lib/net/imap/sequence_set.rb', line 1863
alias runs set_data
#set_data (readonly, protected) Also known as: #runs
# File 'lib/net/imap/sequence_set.rb', line 1861
attr_reader :set_data
#string (rw)
Returns the IMAP sequence-set string representation, or nil when the set is empty. Note that an empty set is invalid in the IMAP syntax.
Use #valid_string to raise an exception when the set is empty, or #to_s to return an empty string.
If the set was created from a single string, it is not normalized. If the set is updated the string will be normalized.
Related: #valid_string, #normalized_string, #to_s, #inspect
# File 'lib/net/imap/sequence_set.rb', line 612
def string; @string || normalized_string if valid? end
#string=(input) (rw)
# File 'lib/net/imap/sequence_set.rb', line 626
def string=(input) if input.nil? clear elsif (str = String.try_convert(input)) # short-circuit before parsing the string entries = each_parsed_entry(str).to_a clear if normalized_entries?(entries) replace_minmaxes entries.map!(&:minmax) else add_minmaxes entries.map!(&:minmax) @string = -str end else raise ArgumentError, "expected a string or nil, got #{input.class}" end input end
#valid? ⇒ Boolean (readonly)
Returns false when the set is empty.
# File 'lib/net/imap/sequence_set.rb', line 831
def valid?; !empty? end
Instance Method Details
#&(other) ⇒ sequence set
#intersection(other) ⇒ sequence set
Also known as: #intersection
sequence set
#intersection(other) ⇒ sequence set
Returns a new sequence set containing only the numbers common to this set and other.
other may be any object that would be accepted by .new.
Net::IMAP::SequenceSet[1..5] & [2, 4, 6]
#=> Net::IMAP::SequenceSet["2,4"]
Related: #intersect?, #|, #-, #^, #~
Set identities
lhs & rhs is equivalent to:
-
rhs & lhs(commutative) -
~(~lhs | ~rhs)(De Morgan’s Law) -
lhs - ~rhs -
lhs - (lhs - rhs) -
lhs - (lhs ^ rhs) -
lhs ^ (lhs - rhs)
# File 'lib/net/imap/sequence_set.rb', line 913
def &(other) remain_frozen dup.intersect! other end
#+(other)
Alias for #|.
# File 'lib/net/imap/sequence_set.rb', line 861
alias :+ :|
#-(other) ⇒ sequence set
#difference(other) ⇒ sequence set
Also known as: #difference
sequence set
#difference(other) ⇒ sequence set
Returns a new sequence set built by duplicating this set and removing every number that appears in other.
other may be any object that would be accepted by .new.
Net::IMAP::SequenceSet[1..5] - 2 - 4 - 6
#=> Net::IMAP::SequenceSet["1,3,5"]
Related: #subtract, #|, #&, #^, #~
Set identities
lhs - rhs is equivalent to:
-
~rhs - ~lhs -
lhs & ~rhs -
~(~lhs | rhs) -
lhs & (lhs ^ rhs) -
lhs ^ (lhs & rhs) -
rhs ^ (lhs | rhs)
# File 'lib/net/imap/sequence_set.rb', line 887
def -(other) remain_frozen dup.subtract other end
#<<(element)
Alias for #add.
# File 'lib/net/imap/sequence_set.rb', line 981
alias << add
#==(other) ⇒ Boolean
Returns true when the other SequenceSet represents the same message identifiers. Encoding difference—such as order, overlaps, or duplicates—are ignored.
Net::IMAP::SequenceSet["1:3"] == Net::IMAP::SequenceSet["1:3"]
#=> true
Net::IMAP::SequenceSet["1,2,3"] == Net::IMAP::SequenceSet["1:3"]
#=> true
Net::IMAP::SequenceSet["1,3"] == Net::IMAP::SequenceSet["3,1"]
#=> true
Net::IMAP::SequenceSet["9,1:*"] == Net::IMAP::SequenceSet["1:*"]
#=> true
Related: #eql?, #normalize
#===(other) ⇒ Boolean
Returns whether other is contained within the set. other may be any object that would be accepted by .new. Returns nil if StandardError is raised while converting other to a comparable type.
Related: #cover?, #include?, #include_star?
# File 'lib/net/imap/sequence_set.rb', line 706
def ===(other) cover?(other) rescue nil end
#[](index) ⇒ Integer, ...
#slice(index) ⇒ Integer, ...
#[](start, length) ⇒ sequence set?
#slice(start, length) ⇒ sequence set?
#[](range) ⇒ sequence set?
#slice(range) ⇒ sequence set?
Also known as: #slice
Integer, ...
#slice(index) ⇒ Integer, ...
#[](start, length) ⇒ sequence set?
#slice(start, length) ⇒ sequence set?
#[](range) ⇒ sequence set?
#slice(range) ⇒ sequence set?
Returns a number or a subset from the sorted set, without modifying the set.
When an Integer argument index is given, the number at offset index in the sorted set is returned:
set = Net::IMAP::SequenceSet["10:15,20:23,26"]
set[0] #=> 10
set[5] #=> 15
set[10] #=> 26
If index is negative, it counts relative to the end of the sorted set:
set = Net::IMAP::SequenceSet["10:15,20:23,26"]
set[-1] #=> 26
set[-3] #=> 22
set[-6] #=> 15
If index is out of range, nil is returned.
set = Net::IMAP::SequenceSet["10:15,20:23,26"]
set[11] #=> nil
set[-12] #=> nil
The result is based on the sorted and de-duplicated set, not on the ordered #entries in #string.
set = Net::IMAP::SequenceSet["12,20:23,11:16,21"]
set[0] #=> 11
set[-1] #=> 23
Related: #at
# File 'lib/net/imap/sequence_set.rb', line 1540
def [](index, length = nil) if length then slice_length(index, length) elsif index.is_a?(Range) then slice_range(index) else at(index) end end
#^(other) ⇒ sequence set
#xor(other) ⇒ sequence set
Also known as: #xor
sequence set
#xor(other) ⇒ sequence set
Returns a new sequence set containing numbers that are exclusive between this set and other.
other may be any object that would be accepted by .new.
Net::IMAP::SequenceSet[1..5] ^ [2, 4, 6]
#=> Net::IMAP::SequenceSet["1,3,5:6"]
Set identities
lhs ^ rhs is equivalent to:
-
rhs ^ lhs(commutative) -
~lhs ^ ~rhs -
(lhs | rhs) - (lhs & rhs) -
(lhs - rhs) | (rhs - lhs) -
(lhs ^ other) ^ (other ^ rhs)
# File 'lib/net/imap/sequence_set.rb', line 938
def ^(other) remain_frozen dup.xor! other end
#above(num)
Returns a copy of self which only contains the numbers above num.
Net::IMAP::SequenceSet["5,10:22,50"].above(10) # to_s => "11:22,50"
Net::IMAP::SequenceSet["5,10:22,50"].above(20) # to_s => "21:22,50
Net::IMAP::SequenceSet["5,10:22,50"].above(30) # to_s => "50"
This returns the same result as #intersection with ((num+1)..) or #difference with (..num).
Net::IMAP::SequenceSet["5,10:22,50"] & (11..) # to_s => "11:22,50"
Net::IMAP::SequenceSet["5,10:22,50"] - (..10) # to_s => "11:22,50"
Net::IMAP::SequenceSet["5,10:22,50"] & (21..) # to_s => "21:22,50"
Net::IMAP::SequenceSet["5,10:22,50"] - (..20) # to_s => "21:22,50"
# File 'lib/net/imap/sequence_set.rb', line 1564
def above(num) NumValidator.valid_nz_number?(num) or raise ArgumentError, "not a valid sequence set number" difference(..num) end
#add(element) ⇒ self
#<<(other) ⇒ self
Also known as: #<<
self
#<<(other) ⇒ self
# File 'lib/net/imap/sequence_set.rb', line 976
def add(element) # short-circuit before import_run add_run import_run element normalize! end
#add?(element) ⇒ self?
#add_coalesced_minmax(lower_idx, lmin, lmax, min, max) (private)
# File 'lib/net/imap/sequence_set.rb', line 2175
def add_coalesced_minmax(lower_idx, lmin, lmax, min, max) return if lmin <= min && max <= lmax set_min_at lower_idx, (lmin = min) if min < lmin set_max_at lower_idx, (lmax = max) if lmax < max next_idx = lower_idx + 1 return if next_idx == runs.count tmax_adj = lmax + 1 if (upper_idx = bsearch_index(tmax_adj)) if tmax_adj < min_at(upper_idx) upper_idx -= 1 else set_max_at lower_idx, max_at(upper_idx) end end slice_runs! next_idx..upper_idx end
#add_minmax(minmax) (private) Also known as: #add_run
–|=====| |=====new run=======| append ?????????-|=====new run=======|-|===lower===|– insert
|=====new run=======|
———??=======lower=======??————— noop
———??===lower==|–|==| join remaining ———??===lower==|–|==|—-|===upper===|– join until upper ———??===lower==|–|==|–|=====upper===|– join to upper
# File 'lib/net/imap/sequence_set.rb', line 2164
def add_minmax(minmax) min, max = minmax lower_idx = bsearch_index(min - 1) lmin, lmax = min_at(lower_idx), max_at(lower_idx) if lower_idx if lmin.nil? then append_minmax min, max elsif (max + 1) < lmin then insert_minmax lower_idx, min, max else add_coalesced_minmax(lower_idx, lmin, lmax, min, max) end end
#add_minmaxes(minmaxes) (private) Also known as: #add_runs
# File 'lib/net/imap/sequence_set.rb', line 2140
def add_minmaxes(minmaxes) minmaxes.each do |minmax| add_minmax minmax end self end
#add_run(minmax) (private)
Alias for #add_minmax.
# File 'lib/net/imap/sequence_set.rb', line 2242
alias add_run add_minmax
#add_runs(minmaxes) (private)
Alias for #add_minmaxes.
# File 'lib/net/imap/sequence_set.rb', line 2241
alias add_runs add_minmaxes
#append(entry)
Adds a range or number to the set and returns self.
Unlike #add, #merge, or #union, the new value is appended to #string. This may result in a #string which has duplicates or is out-of-order.
set = Net::IMAP::SequenceSet.new
set.append(1..2) # => Net::IMAP::SequenceSet("1:2")
set.append(5) # => Net::IMAP::SequenceSet("1:2,5")
set.append(4) # => Net::IMAP::SequenceSet("1:2,5,4")
set.append(3) # => Net::IMAP::SequenceSet("1:2,5,4,3")
set.append(2) # => Net::IMAP::SequenceSet("1:2,5,4,3,2")
If entry is a string, it will be converted into normal form.
set = Net::IMAP::SequenceSet("4:5,1:2")
set.append("6:6") # => Net::IMAP::SequenceSet("4:5,1:2,6")
set.append("9:8") # => Net::IMAP::SequenceSet("4:5,1:2,6,8:9")
If entry adjacently follows the last entry, they will coalesced:
set = Net::IMAP::SequenceSet.new("2,1,9:10")
set.append(11..12) # => Net::IMAP::SequenceSet("2,1,9:12")
Non-normalized sets store the string in addition to an internal normalized uint32 set representation. This can more than double memory usage, so large sets should avoid using #append unless preserving order is required. See SequenceSet@Ordered+and+Normalized+sets.
# File 'lib/net/imap/sequence_set.rb', line 1011
def append(entry) # short-circuit before import_minmax minmax = import_minmax entry adj = minmax.first - 1 if @string.nil? && (runs.empty? || max_num <= adj) # append to elements or coalesce with last element add_minmax minmax return self elsif @string.nil? # generate string for out-of-order append head, comma = normalized_string, "," else # @string already exists... maybe coalesce with last entry head, comma, last_entry = @string.rpartition(",") last_min, last_max = import_minmax last_entry if last_max == adj # coalesce with last entry minmax[0] = last_min else # append to existing string head, comma = @string, "," end end entry = export_minmax minmax add_minmax minmax @string = -"#{head}#{comma}#{entry}" self end
#append_minmax(min, max) (private)
#at(index) ⇒ Integer?
Returns the number at the given index in the sorted set, without modifying the set.
index is interpreted the same as in #[], except that #at only allows a single integer argument.
Related: #[], #slice, #ordered_at
# File 'lib/net/imap/sequence_set.rb', line 1484
def at(index) seek_number_in_minmaxes(minmaxes, index) end
#below(num)
Returns a copy of self which only contains numbers below num.
Net::IMAP::SequenceSet["5,10:22,50"].below(10) # to_s => "5"
Net::IMAP::SequenceSet["5,10:22,50"].below(20) # to_s => "5,10:19"
Net::IMAP::SequenceSet["5,10:22,50"].below(30) # to_s => "5,10:22"
This returns the same result as #intersection with (..(num-1)) or #difference with (num..).
Net::IMAP::SequenceSet["5,10:22,50"] & (..9) # to_s => "5"
Net::IMAP::SequenceSet["5,10:22,50"] - (10..) # to_s => "5"
Net::IMAP::SequenceSet["5,10:22,50"] & (..19) # to_s => "5,10:19"
Net::IMAP::SequenceSet["5,10:22,50"] - (20..) # to_s => "5,10:19"
When the set does not contain *, #below is identical to #limit with max: num - 1. When the set does contain *, #below always drops it from the result. Use #limit when the ::Net::IMAP semantics for * must be enforced.
Net::IMAP::SequenceSet["5,10:22,50"].below(30) # to_s => "5,10:22"
Net::IMAP::SequenceSet["5,10:22,50"].limit(max: 29) # to_s => "5,10:22"
Net::IMAP::SequenceSet["5,10:22,*"].below(30) # to_s => "5,10:22"
Net::IMAP::SequenceSet["5,10:22,*"].limit(max: 29) # to_s => "5,10:22,29"
# File 'lib/net/imap/sequence_set.rb', line 1595
def below(num) NumValidator.valid_nz_number?(num) or raise ArgumentError, "not a valid sequence set number" difference(num..) end
#bsearch_index(num) (private)
# File 'lib/net/imap/sequence_set.rb', line 2035
def bsearch_index(num) = minmaxes.bsearch_index { _2 >= num }
#bsearch_minmax(num) (private)
# File 'lib/net/imap/sequence_set.rb', line 2036
def bsearch_minmax(num) = minmaxes.bsearch { _2 >= num }
#bsearch_range(num) (private)
# File 'lib/net/imap/sequence_set.rb', line 2037
def bsearch_range(num) = (min, max = bsearch_minmax(num)) && (min..max)
#cardinality
Returns the number of members in the set.
Unlike #count, "*" is considered to be distinct from 2³² - 1 (the maximum 32-bit unsigned integer value).
set = Net::IMAP::SequenceSet[1..10]
set.count #=> 10
set.cardinality #=> 10
set = Net::IMAP::SequenceSet["4294967295,*"]
set.count #=> 1
set.cardinality #=> 2
set = Net::IMAP::SequenceSet[1..]
set.count #=> 4294967295
set.cardinality #=> 4294967296
Related: #count, #count_with_duplicates
#clear
Removes all elements and returns self.
# File 'lib/net/imap/sequence_set.rb', line 565
def clear # redundant check (normalizes the error message for JRuby) set_data.clear @string = nil self end
#complement
Alias for #~.
# File 'lib/net/imap/sequence_set.rb', line 962
alias complement :~
#complement! ⇒ self
In-place set #complement. Replaces the contents of this set with its own #complement. It will contain all possible values except for those currently in the set.
Related: #complement
# File 'lib/net/imap/sequence_set.rb', line 1648
def complement! # short-circuit before querying return replace(self.class.full) if empty? return clear if full? flat = minmaxes.flat_map { [_1 - 1, _2 + 1] } if flat.first < 1 then flat.shift else flat.unshift 1 end if STAR_INT < flat.last then flat.pop else flat.push STAR_INT end replace_minmaxes flat.each_slice(2).to_a normalize! end
#count
Returns the count of distinct #numbers in the set.
Unlike #cardinality, "*" is considered to be equal to 2³² - 1 (the maximum 32-bit unsigned integer value).
set = Net::IMAP::SequenceSet[1..10]
set.count #=> 10
set.cardinality #=> 10
set = Net::IMAP::SequenceSet["4294967295,*"]
set.count #=> 1
set.cardinality #=> 2
set = Net::IMAP::SequenceSet[1..]
set.count #=> 4294967295
set.cardinality #=> 4294967296
Related: #cardinality, #count_with_duplicates
# File 'lib/net/imap/sequence_set.rb', line 1366
def count cardinality + (include_star? && include?(UINT32_MAX) ? -1 : 0) end
#count_duplicates
Returns the count of repeated numbers in the ordered #entries, the difference between #count_with_duplicates and #count.
When #string is normalized, this is zero.
Related: #entries, #count_with_duplicates, #has_duplicates?
# File 'lib/net/imap/sequence_set.rb', line 1431
def count_duplicates return 0 unless @string count_with_duplicates - count end
#count_entries (private)
{{{2 Ordered entry methods
#count_with_duplicates
Returns the count of numbers in the ordered #entries, including any repeated numbers.
When #string is normalized, this returns the same as #count. Like #count, "*" is considered to be equal to 2³² - 1 (the maximum 32-bit unsigned integer value).
In a range, "*" is not considered a duplicate:
set = Net::IMAP::SequenceSet["4294967295:*"]
set.count_with_duplicates #=> 1
set.size #=> 2
set.count #=> 1
set.cardinality #=> 2
In a separate entry, "*" is considered a duplicate:
set = Net::IMAP::SequenceSet["4294967295,*"]
set.count_with_duplicates #=> 2
set.size #=> 2
set.count #=> 1
set.cardinality #=> 2
Related: #count, #cardinality, #size, #count_duplicates, #has_duplicates?, #entries
#cover?(other) ⇒ Boolean
Returns whether other is contained within the set. other may be any object that would be accepted by .new.
Related: #===, #include?, #include_star?, #intersect?
# File 'lib/net/imap/sequence_set.rb', line 718
def cover?(other) import_runs(other).none? { !include_run?(_1) } end
#deconstruct
Returns an array with #normalized_string when valid and an empty array otherwise.
# File 'lib/net/imap/sequence_set.rb', line 616
def deconstruct; valid? ? [normalized_string] : [] end
#delete(element) ⇒ self
Deletes the given range or number from the set and returns self.
#string will be regenerated after deletion. Use #subtract to remove many elements at once.
Related: #delete?, #delete_at, #subtract, #difference
# File 'lib/net/imap/sequence_set.rb', line 1061
def delete(element) # short-circuit before import_run subtract_run import_run element normalize! end
#delete?(number) ⇒ Integer?
#delete?(star) ⇒ :*?
#delete?(range) ⇒ sequence set?
Integer?
#delete?(star) ⇒ :*?
#delete?(range) ⇒ sequence set?
Removes a specified value from the set, and returns the removed value. Returns nil if nothing was removed.
Returns an integer when the specified number argument was removed:
set = Net::IMAP::SequenceSet.new [5..10, 20]
set.delete?(7) #=> 7
set #=> #<Net::IMAP::SequenceSet "5:6,8:10,20">
set.delete?("20") #=> 20
set #=> #<Net::IMAP::SequenceSet "5:6,8:10">
set.delete?(30) #=> nil
Returns :* when * or -1 is specified and removed:
set = Net::IMAP::SequenceSet.new "5:9,20,35,*"
set.delete?(-1) #=> :*
set #=> #<Net::IMAP::SequenceSet "5:9,20,35">
And returns a new SequenceSet when a range is specified:
set = Net::IMAP::SequenceSet.new [5..10, 20]
set.delete?(9..) #=> #<Net::IMAP::SequenceSet "9:10,20">
set #=> #<Net::IMAP::SequenceSet "5:8">
set.delete?(21..) #=> nil
#string will be regenerated after deletion.
Related: #delete, #delete_at, #subtract, #difference, #disjoint?
# File 'lib/net/imap/sequence_set.rb', line 1099
def delete?(element) # short-circuit before import_minmax element = input_try_convert(element) minmax = import_minmax element if number_input?(element) return unless include_minmax? minmax subtract_minmax minmax normalize! export_num minmax.first else copy = dup subtract_minmax minmax normalize! copy if copy.subtract(self).valid? end end
#delete_at(index) ⇒ Numeric, ...
# File 'lib/net/imap/sequence_set.rb', line 1124
def delete_at(index) slice! Integer(index.to_int) end
#delete_run_at(idx) (private)
#difference(other)
Alias for #-.
# File 'lib/net/imap/sequence_set.rb', line 888
alias difference :-
#disjoint?(other) ⇒ Boolean
Returns true if the set and a given object have no common elements, false otherwise.
Net::IMAP::SequenceSet["5:10"].disjoint? "7,9,11" #=> false
Net::IMAP::SequenceSet["5:10"].disjoint? "11:33" #=> true
Related: #intersection, #intersect?
# File 'lib/net/imap/sequence_set.rb', line 773
def disjoint?(other) empty? || import_runs(other).none? { intersect_run? _1 } end
#dup_set_data (protected)
# File 'lib/net/imap/sequence_set.rb', line 2107
def dup_set_data = set_data.map { _1.dup }
#each_element
Yields each number or range (or :*) in #elements to the block and returns self. Returns an enumerator when called without a block.
The returned numbers are sorted and de-duplicated, even when the input #string is not. See #normalize, SequenceSet@Ordered+and+Normalized+sets.
Related: #elements, #each_entry
# File 'lib/net/imap/sequence_set.rb', line 1271
def each_element # :yields: integer or range or :* return to_enum(__method__) unless block_given? runs.each do yield export_run_entry _1 end self end
#each_entry(&block)
Yields each number or range in #string to the block and returns self. Returns an enumerator when called without a block.
The entries are yielded in the same order they appear in #string, with no sorting, deduplication, or coalescing. When #string is in its normalized form, this will yield the same values as #each_element.
See SequenceSet@Ordered+and+Normalized+sets.
Related: #entries, #each_element
# File 'lib/net/imap/sequence_set.rb', line 1259
def each_entry(&block) # :yields: integer or range or :* return to_enum(__method__) unless block_given? each_entry_run do yield export_run_entry _1 end end
#each_entry_minmax(&block) (private) Also known as: #each_entry_run
# File 'lib/net/imap/sequence_set.rb', line 2011
def each_entry_minmax(&block) return to_enum(__method__) unless block_given? if @string @string.split(",") do block.call parse_minmax _1 end else minmaxes.each(&block) end self end
#each_entry_run(&block) (private)
Alias for #each_entry_minmax.
# File 'lib/net/imap/sequence_set.rb', line 2020
alias each_entry_run each_entry_minmax
#each_minmax_with_index(minmaxes) (private)
#each_number(&block)
Yields each number in #numbers to the block and returns self. If the set contains a *, RangeError will be raised.
Returns an enumerator when called without a block (even if the set contains *).
Related: #numbers, #each_ordered_number
# File 'lib/net/imap/sequence_set.rb', line 1299
def each_number(&block) # :yields: integer return to_enum(__method__) unless block_given? raise RangeError, '%s contains "*"' % [self.class] if include_star? minmaxes.each do each_number_in_minmax _1, _2, &block end self end
#each_number_in_minmax(min, max, &block) (private)
#each_ordered_number(&block)
Yields each number in #entries to the block and returns self. If the set contains a *, RangeError will be raised.
Returns an enumerator when called without a block (even if the set contains *).
Related: #entries, #each_number
# File 'lib/net/imap/sequence_set.rb', line 1313
def each_ordered_number(&block) return to_enum(__method__) unless block_given? raise RangeError, '%s contains "*"' % [self.class] if include_star? each_entry_minmax do each_number_in_minmax _1, _2, &block end end
#each_parsed_entry(str) (private)
yields validated but unsorted [num] or [num, num]
# File 'lib/net/imap/sequence_set.rb', line 1987
def each_parsed_entry(str) return to_enum(__method__, str) unless block_given? str&.split(",", -1) do |entry| yield parse_entry(entry) end end
#each_range
#elements Also known as: #to_a
Returns an array of ranges and integers and :*.
The returned elements are sorted and coalesced, even when the input #string is not. * will sort last. See #normalize, SequenceSet@Ordered+and+Normalized+sets.
By itself, * translates to :*. A range containing * translates to an endless range. Use #limit to translate both cases to a maximum value.
Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].elements
#=> [2, 5..9, 11..12, :*]
Related: #each_element, #ranges, #numbers
# File 'lib/net/imap/sequence_set.rb', line 1200
def elements; each_element.to_a end
#encode_with(coder)
For YAML serialization
# File 'lib/net/imap/sequence_set.rb', line 1847
def encode_with(coder) # :nodoc: # we can perfectly reconstruct from the string coder['string'] = to_s end
#entries
Returns an array of ranges and integers and :*.
The entries are in the same order they appear in #string, with no sorting, deduplication, or coalescing. When #string is in its normalized form, this will return the same result as #elements. This is useful when the given order is significant, for example in a ESEARCH response to Net::IMAP#sort.
See SequenceSet@Ordered+and+Normalized+sets.
Related: #each_entry, #elements
# File 'lib/net/imap/sequence_set.rb', line 1184
def entries; each_entry.to_a end
#eql?(other) ⇒ Boolean
Hash equality requires the same encoded #string representation.
Net::IMAP::SequenceSet["1:3"] .eql? Net::IMAP::SequenceSet["1:3"]
#=> true
Net::IMAP::SequenceSet["1,2,3"].eql? Net::IMAP::SequenceSet["1:3"]
#=> false
Net::IMAP::SequenceSet["1,3"] .eql? Net::IMAP::SequenceSet["3,1"]
#=> false
Net::IMAP::SequenceSet["9,1:*"].eql? Net::IMAP::SequenceSet["1:*"]
#=> false
Related: #==, #normalize
#export_minmax(minmax) (private) Also known as: #export_run
# File 'lib/net/imap/sequence_set.rb', line 1951
def export_minmax(minmax) minmax.uniq.map { export_num _1 }.join(":") end
#export_minmax_entry(min, max) (private) Also known as: #export_run_entry
#export_minmaxes(minmaxes) (private) Also known as: #export_runs
# File 'lib/net/imap/sequence_set.rb', line 1947
def export_minmaxes(minmaxes) -minmaxes.map { export_minmax _1 }.join(",") end
#export_num(num) (private)
{{{2 Export methods
# File 'lib/net/imap/sequence_set.rb', line 1945
def export_num(num) num == STAR_INT ? :* : num end
#export_run(minmax) (private)
Alias for #export_minmax.
# File 'lib/net/imap/sequence_set.rb', line 1954
alias export_run export_minmax
#export_run_entry(min, max) (private)
Alias for #export_minmax_entry.
# File 'lib/net/imap/sequence_set.rb', line 1963
alias export_run_entry export_minmax_entry
#export_runs(minmaxes) (private)
Alias for #export_minmaxes.
# File 'lib/net/imap/sequence_set.rb', line 1953
alias export_runs export_minmaxes
#find_index(number)
Returns the (sorted and deduplicated) index of number in the set, or nil if number isn’t in the set.
Related: #[], #at, #find_ordered_index
# File 'lib/net/imap/sequence_set.rb', line 1452
def find_index(number) number = import_num number each_minmax_with_index(minmaxes) do |min, max, idx_min| number < min and return nil number <= max and return export_num(idx_min + (number - min)) end nil end
#find_ordered_index(number)
Returns the first index of number in the ordered #entries, or nil if number isn’t in the set.
Related: #find_index
# File 'lib/net/imap/sequence_set.rb', line 1465
def find_ordered_index(number) number = import_num number each_minmax_with_index(each_entry_minmax) do |min, max, idx_min| if min <= number && number <= max return export_num(idx_min + (number - min)) end end nil end
#freeze
Freezes and returns the set. A frozen SequenceSet is Ractor-safe.
# File 'lib/net/imap/sequence_set.rb', line 653
def freeze return self if frozen? freeze_set_data super end
#freeze_set_data (private)
#hash
See #eql?
# File 'lib/net/imap/sequence_set.rb', line 697
def hash; [self.class, string].hash end
#import_minmax(input) (private) Also known as: #import_run
{{{2 Import methods
# File 'lib/net/imap/sequence_set.rb', line 1885
def import_minmax(input) entry = input_try_convert input case entry when *STARS, Integer then [int = import_num(entry), int] when Range then import_range_minmax(entry) when String then parse_minmax(entry) else raise DataFormatError, "expected number or range, got %p" % [input] end end
#import_num(obj) (private)
#import_range_minmax(range) (private)
# File 'lib/net/imap/sequence_set.rb', line 1929
def import_range_minmax(range) first = import_num(range.begin || 1) last = import_num(range.end || :*) last -= 1 if range.exclude_end? && range.end && last != STAR_INT unless first <= last raise DataFormatError, "invalid range for sequence-set: %p" % [range] end [first, last] end
#import_run(input) (private)
Alias for #import_minmax.
# File 'lib/net/imap/sequence_set.rb', line 1895
alias import_run import_minmax
#import_runs(input) (private)
# File 'lib/net/imap/sequence_set.rb', line 1897
def import_runs(input) set = input_try_convert input case set when *STARS, Integer, Range then [import_run(set)] when String then parse_runs set when SequenceSet then set.runs when Set then set.map { [import_num(_1)] * 2 } when Array then set.flat_map { import_runs _1 } when nil then [] else raise DataFormatError, "expected nz-number, range, '*', Set, Array; " \ "got %p" % [input] end end
#include?(element) ⇒ Boolean
Also known as: #member?
Returns true when a given number or range is in self, and false otherwise. Returns nil when number isn’t a valid SequenceSet element (Integer, Range, *, sequence-set string).
set = Net::IMAP::SequenceSet["5:10,100,111:115"]
set.include? 1 #=> false
set.include? 5..10 #=> true
set.include? 11..20 #=> false
set.include? 100 #=> true
set.include? 6 #=> true, covered by "5:10"
set.include? 6..9 #=> true, covered by "5:10"
set.include? "6:9" #=> true, strings are parsed
set.include? 4..9 #=> false, intersection is not sufficient
set.include? "*" #=> false, use #limit to re-interpret "*"
set.include? -1 #=> false, -1 is interpreted as "*"
set = Net::IMAP::SequenceSet["5:10,100,111:*"]
set.include? :* #=> true
set.include? "*" #=> true
set.include? -1 #=> true
set.include?(200..) #=> true
set.include?(100..) #=> false
Related: #include_star?, #cover?, #===, #intersect?
# File 'lib/net/imap/sequence_set.rb', line 744
def include?(element) run = import_run element rescue nil !!include_run?(run) if run end
#include_minmax?(min, max) ⇒ Boolean (private)
Also known as: #include_run?
{{{2 Search methods
#include_run?(min, max) (private)
Alias for #include_minmax?.
# File 'lib/net/imap/sequence_set.rb', line 2032
alias include_run? include_minmax?
#init_with(coder)
For YAML deserialization
# File 'lib/net/imap/sequence_set.rb', line 1853
def init_with(coder) # :nodoc: @set_data = new_set_data self.string = coder['string'] end
#initialize_clone(other) (private)
frozen clones are shallow copied
# File 'lib/net/imap/sequence_set.rb', line 1872
def initialize_clone(other) @set_data = other.dup_set_data unless other.frozen? super end
#initialize_dup(other) (private)
# File 'lib/net/imap/sequence_set.rb', line 1877
def initialize_dup(other) @set_data = other.dup_set_data super end
#input_try_convert(input) (private)
unlike SequenceSet#try_convert, this returns an Integer, Range, String, Set, Array, or… any type of object.
# File 'lib/net/imap/sequence_set.rb', line 1914
def input_try_convert(input) SequenceSet.try_convert(input) || Integer.try_convert(input) || String.try_convert(input) || input end
#insert_minmax(idx, min, max) (private)
#inspect
Returns an inspection string for the SequenceSet.
Net::IMAP::SequenceSet.new.inspect
#=> "Net::IMAP::SequenceSet()"
Net::IMAP::SequenceSet(1..5, 1024, 15, 2000).inspect
#=> 'Net::IMAP::SequenceSet("1:5,15,1024,2000")'
Frozen sets have slightly different output:
Net::IMAP::SequenceSet.empty.inspect
#=> "Net::IMAP::SequenceSet.empty"
Net::IMAP::SequenceSet[1..5, 1024, 15, 2000].inspect
#=> 'Net::IMAP::SequenceSet["1:5,15,1024,2000"]'
Large sets (by number of #entries) have abridged output, with only the first and last entries:
Net::IMAP::SequenceSet(((1..5000) % 2).to_a).inspect
#=> #<Net::IMAP::SequenceSet 2500 entries "1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,...(2468 entries omitted)...,4969,4971,4973,4975,4977,4979,4981,4983,4985,4987,4989,4991,4993,4995,4997,4999">
# File 'lib/net/imap/sequence_set.rb', line 1802
def inspect case (count = count_entries) when 0 (frozen? ? "%s.empty" : "%s()") % [self.class] when ..INSPECT_MAX_LEN (frozen? ? "%s[%p]" : "%s(%p)") % [self.class, to_s] else if @string head = @string[INSPECT_ABRIDGED_HEAD_RE] tail = @string[INSPECT_ABRIDGED_TAIL_RE] else head = export_runs(runs.first(INSPECT_TRUNCATE_LEN)) + "," tail = "," + export_runs(runs.last(INSPECT_TRUNCATE_LEN)) end '#<%s %d entries "%s...(%d entries omitted)...%s"%s>' % [ self.class, count, head, count - INSPECT_TRUNCATE_LEN * 2, tail, frozen? ? " (frozen)" : "", ] end end
#intersect!(other)
In-place set #intersection. Removes any elements that are missing from other from this set, keeping only the #intersection, and returns self.
other can be any object that would be accepted by .new.
set = Net::IMAP::SequenceSet.new(1..5)
set.intersect! [2, 4, 6]
set #=> Net::IMAP::SequenceSet("2,4")
Related: #intersection, #intersect?
# File 'lib/net/imap/sequence_set.rb', line 1670
def intersect!(other) # short-circuit before processing input subtract SequenceSet.new(other).complement! end
#intersect?(other) ⇒ Boolean
Also known as: #overlap?
Returns true if the set and a given object have any common elements, false otherwise.
Net::IMAP::SequenceSet["5:10"].intersect? "7,9,11" #=> true
Net::IMAP::SequenceSet["5:10"].intersect? "11:33" #=> false
Related: #intersection, #disjoint?, #cover?, #include?
# File 'lib/net/imap/sequence_set.rb', line 761
def intersect?(other) valid? && import_runs(other).any? { intersect_run? _1 } end
#intersect_minmax?(min, max) ⇒ Boolean (private)
Also known as: #intersect_run?
#intersect_run?(min, max) (private)
Alias for #intersect_minmax?.
# File 'lib/net/imap/sequence_set.rb', line 2033
alias intersect_run? intersect_minmax?
#intersection(other)
Alias for #&.
# File 'lib/net/imap/sequence_set.rb', line 914
alias intersection :&
#limit(max:)
Returns a frozen SequenceSet with * converted to #max, numbers and ranges over #max removed, and ranges containing #max converted to end at #max.
Net::IMAP::SequenceSet["5,10:22,50"].limit(max: 20).to_s
#=> "5,10:20"
* is always interpreted as the maximum value. When the set contains *, it will be set equal to the limit.
Net::IMAP::SequenceSet["*"].limit(max: 37)
#=> Net::IMAP::SequenceSet["37"]
Net::IMAP::SequenceSet["5:*"].limit(max: 37)
#=> Net::IMAP::SequenceSet["5:37"]
Net::IMAP::SequenceSet["500:*"].limit(max: 37)
#=> Net::IMAP::SequenceSet["37"]
Related: #limit!
#limit!(max:)
# File 'lib/net/imap/sequence_set.rb', line 1632
def limit!(max:) # short-circuit before querying star = include_star? max = import_num(max) subtract_minmax [max + 1, STAR_INT] add_minmax [max, max ] if star normalize! end
#max(star: :*) ⇒ Integer, ...
#max(count) ⇒ SequenceSet
Integer, ...
#max(count) ⇒ SequenceSet
# File 'lib/net/imap/sequence_set.rb', line 789
def max(count = nil, star: :*) if count if cardinality <= count frozen? ? self : dup else slice(-count..) || remain_frozen_empty end elsif (val = max_num) val == STAR_INT ? star : val end end
#max_at(idx) (private)
# File 'lib/net/imap/sequence_set.rb', line 2117
def max_at(idx) = minmaxes[idx][1]
#max_num (private)
# File 'lib/net/imap/sequence_set.rb', line 2114
def max_num = minmaxes.last&.last
#member?(element)
Alias for #include?.
# File 'lib/net/imap/sequence_set.rb', line 749
alias member? include?
#merge(*sets)
# File 'lib/net/imap/sequence_set.rb', line 1155
def merge(*sets) # short-circuit before import_runs add_runs import_runs sets normalize! end
#min(star: :*) ⇒ Integer, ...
#min(count) ⇒ SequenceSet
Integer, ...
#min(count) ⇒ SequenceSet
#min_at(idx) (private)
# File 'lib/net/imap/sequence_set.rb', line 2116
def min_at(idx) = minmaxes[idx][0]
#min_num (private)
{{{2 Core set data query/enumeration primitives
# File 'lib/net/imap/sequence_set.rb', line 2113
def min_num = minmaxes.first&.first
#minmax(star: :*) ⇒ Array, max
#modifying! (private)
{{{2 Update methods
# File 'lib/net/imap/sequence_set.rb', line 2134
def if frozen? raise FrozenError, "can't modify frozen #{self.class}: %p" % [self] end end
#new_set_data (private)
{{{2 Core set data create/freeze/dup primitives
# File 'lib/net/imap/sequence_set.rb', line 2105
def new_set_data = []
#normal_string?(str) ⇒ Boolean (private)
# File 'lib/net/imap/sequence_set.rb', line 1992
def normal_string?(str) normalized_entries? each_parsed_entry str end
#normalize
Returns a SequenceSet with a normalized string representation: entries have been sorted, deduplicated, and coalesced, and all entries are in normal form. Returns self for frozen normalized sets, and a normalized duplicate otherwise.
See SequenceSet@Ordered+and+Normalized+sets.
Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalize
#=> Net::IMAP::SequenceSet["1:7,9:11"]
Related: #normalize!, #normalized_string, #normalized?
# File 'lib/net/imap/sequence_set.rb', line 1751
def normalize frozen? && normalized? ? self : remain_frozen(dup.normalize!) end
#normalize!
Resets #string to be sorted, deduplicated, and coalesced. Returns self. See SequenceSet@Ordered+and+Normalized+sets.
Related: #normalize, #normalized_string, #normalized?
# File 'lib/net/imap/sequence_set.rb', line 1759
def normalize! # redundant check (normalizes the error message for JRuby) @string = nil self end
#normalized_entries?(entries) ⇒ Boolean (private)
#normalized_string
Returns a normalized sequence-set string representation, sorted and deduplicated. Adjacent or overlapping elements will be merged into a single larger range. See SequenceSet@Ordered+and+Normalized+sets.
Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalized_string
#=> "1:7,9:11"
Returns nil when the set is empty.
Related: #normalize!, #normalize, #string, #to_s, #normalized?
# File 'lib/net/imap/sequence_set.rb', line 1775
def normalized_string export_runs(runs) unless runs.empty? end
#number_input?(input) ⇒ Boolean (private)
NOTE: input_try_convert must be called on input first
#numbers
Returns a sorted array of all of the number values in the sequence set.
The returned numbers are sorted and de-duplicated, even when the input #string is not. See #normalize, SequenceSet@Ordered+and+Normalized+sets.
Net::IMAP::SequenceSet["2,5:9,6,12:11"].numbers
#=> [2, 5, 6, 7, 8, 9, 11, 12]
If the set contains a *, RangeError is raised. See #limit.
Net::IMAP::SequenceSet["10000:*"].numbers
#!> RangeError
WARNING: Even excluding sets with *, an enormous result can easily be created. An array with over 4 billion integers could be returned, requiring up to 32GiB of memory on a 64-bit architecture.
Net::IMAP::SequenceSet[10000..2**32-1].numbers
# ...probably freezes the process for a while...
#!> NoMemoryError (probably)
For safety, consider using #limit or #intersection to set an upper bound. Alternatively, use #each_element, #each_range, or even #each_number to avoid allocation of a result array.
# File 'lib/net/imap/sequence_set.rb', line 1247
def numbers; each_number.to_a end
#nz_number(num) (private)
# File 'lib/net/imap/sequence_set.rb', line 1940
def nz_number(num) = NumValidator.coerce_nz_number(num)
#ordered_at(index) ⇒ Integer?
# File 'lib/net/imap/sequence_set.rb', line 1497
def ordered_at(index) seek_number_in_minmaxes(each_entry_minmax, index) end
#overlap?(other)
Alias for #intersect?.
# File 'lib/net/imap/sequence_set.rb', line 764
alias overlap? intersect?
#parse_entry(str) (private)
# File 'lib/net/imap/sequence_set.rb', line 1981
def parse_entry(str) raise DataFormatError, "invalid sequence set string" if str.empty? str.split(":", 2).map! { import_num _1 } end
#parse_minmax(str) (private) Also known as: #parse_run
# File 'lib/net/imap/sequence_set.rb', line 1978
def parse_minmax(str) parse_entry(str).minmax end
#parse_run(str) (private)
Alias for #parse_minmax.
# File 'lib/net/imap/sequence_set.rb', line 1979
alias parse_run parse_minmax
#parse_runs(str) (private)
{{{2 Parse methods
# File 'lib/net/imap/sequence_set.rb', line 1977
def parse_runs(str) str.split(",", -1).map! { parse_run _1 } end
#ranges
Returns an array of ranges
The returned elements are sorted and coalesced, even when the input #string is not. * will sort last. See #normalize, SequenceSet@Ordered+and+Normalized+sets.
* translates to an endless range. By itself, * translates to :*... Use #limit to set * to a maximum value.
Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].ranges
#=> [2..2, 5..9, 11..12, :*..]
Net::IMAP::SequenceSet["123,999:*,456:789"].ranges
#=> [123..123, 456..789, 999..]
Related: #each_range, #elements, #numbers, #to_set
# File 'lib/net/imap/sequence_set.rb', line 1219
def ranges; each_range.to_a end
#remain_frozen(set) (private)
# File 'lib/net/imap/sequence_set.rb', line 1868
def remain_frozen(set) frozen? ? set.freeze : set end
#remain_frozen_empty (private)
#replace(other)
Replace the contents of the set with the contents of other and returns self.
other may be another SequenceSet or any other object that would be accepted by .new.
# File 'lib/net/imap/sequence_set.rb', line 577
def replace(other) case other when SequenceSet then # short circuit before doing any work @set_data = other.dup_set_data @string = other.instance_variable_get(:@string) when String then self.string = other else clear; merge other end self end
#replace_minmaxes(other) (private)
#reverse_each_minmax_with_index(minmaxes) (private)
#seek_number_in_minmaxes(minmaxes, index) (private)
{{{2 Number indexing methods
# File 'lib/net/imap/sequence_set.rb', line 2042
def seek_number_in_minmaxes(minmaxes, index) index = Integer(index.to_int) if index.negative? reverse_each_minmax_with_index(minmaxes) do |min, max, idx_min, idx_max| idx_min <= index and return export_num(min + (index - idx_min)) end else each_minmax_with_index(minmaxes) do |min, _, idx_min, idx_max| index <= idx_max and return export_num(min + (index - idx_min)) end end nil end
#send_data(imap, tag)
Unstable API: for internal use only (Net::IMAP#send_data)
# File 'lib/net/imap/sequence_set.rb', line 1842
def send_data(imap, tag) # :nodoc: imap.__send__(:put_string, valid_string) end
#set_max_at(idx, max) (private)
#set_min_at(idx, min) (private)
{{{2 Core set data modification primitives
#size
Returns the combined size of the ordered #entries, including any repeated numbers.
When #string is normalized, this returns the same as #cardinality. Like #cardinality, "*" is considered to be be distinct from 2³² - 1 (the maximum 32-bit unsigned integer value).
set = Net::IMAP::SequenceSet["4294967295:*"]
set.size #=> 2
set.count_with_duplicates #=> 1
set.count #=> 1
set.cardinality #=> 2
set = Net::IMAP::SequenceSet["4294967295,*"]
set.size #=> 2
set.count_with_duplicates #=> 2
set.count #=> 1
set.cardinality #=> 2
Related: #cardinality, #count_with_duplicates, #count, #entries
# File 'lib/net/imap/sequence_set.rb', line 1420
def size return cardinality unless @string each_entry_minmax.sum {|min, max| max - min + 1 } end
#slice(index, length = nil)
Alias for #[].
# File 'lib/net/imap/sequence_set.rb', line 1547
alias slice :[]
#slice!(index) ⇒ Integer, ...
#slice!(start, length) ⇒ sequence set?
#slice!(range) ⇒ sequence set?
Integer, ...
#slice!(start, length) ⇒ sequence set?
#slice!(range) ⇒ sequence set?
Deletes a number or consecutive numbers from the set, indicated by the given index, start and length, or range of offsets. Returns the number or sequence set that was removed, or nil if nothing was removed. Arguments are interpreted the same as for #slice or #[].
#string will be regenerated after deletion.
Related: #slice, #delete_at, #delete, #delete?, #subtract, #difference
#slice_length(start, length) (private)
# File 'lib/net/imap/sequence_set.rb', line 2075
def slice_length(start, length) start = Integer(start.to_int) length = Integer(length.to_int) raise ArgumentError, "length must be positive" unless length.positive? last = start + length - 1 unless start.negative? && start.abs <= length slice_range(start..last) end
#slice_range(range) (private)
# File 'lib/net/imap/sequence_set.rb', line 2083
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 && last != STAR_INT end if (first * last).positive? && last < first remain_frozen_empty elsif (min = at(first)) max = at(last) max = :* if max.nil? if max == :* then self & (min..) elsif min <= max then self & (min..max) else remain_frozen_empty end end end
#slice_runs! (private)
#subtract(*sets)
In-place set #difference. Removes all of the elements that appear in any of the given sets from this set, and returns self.
The sets may be any objects that would be accepted by .new.
Related: #difference
# File 'lib/net/imap/sequence_set.rb', line 1167
def subtract(*sets) # short-circuit before import_runs subtract_runs import_runs sets normalize! end
#subtract_minmax(minmax) (private) Also known as: #subtract_run
|====subtracted run=======| –|====| no more 1. noop –|====|—————————|====lower====|– 2. noop ——-|======lower================|—————- 3. split ——–|=====lower================|—————- 4. trim beginning
——-|======lower====????????????—————– trim lower ——–|=====lower====????????????—————– delete lower
——-??=====lower===============|—————– 5. trim/delete one ——-??=====lower====|–|====| no more 6. delete rest ——-??=====lower====|–|====|—|====upper====|– 7. delete until ——-??=====lower====|–|====|–|=====upper====|– 8. delete and trim
# File 'lib/net/imap/sequence_set.rb', line 2205
def subtract_minmax(minmax) min, max = minmax idx = bsearch_index(min) lmin, lmax = min_at(idx), max_at(idx) if idx if lmin.nil? then nil # case 1. elsif max < lmin then nil # case 2. elsif max < lmax then trim_or_split_minmax idx, lmin, min, max else trim_or_delete_minmax idx, lmin, lmax, min, max end end
#subtract_minmaxes(minmaxes) (private) Also known as: #subtract_runs
# File 'lib/net/imap/sequence_set.rb', line 2147
def subtract_minmaxes(minmaxes) minmaxes.each do |minmax| subtract_minmax minmax end self end
#subtract_run(minmax) (private)
Alias for #subtract_minmax.
# File 'lib/net/imap/sequence_set.rb', line 2244
alias subtract_run subtract_minmax
#subtract_runs(minmaxes) (private)
Alias for #subtract_minmaxes.
# File 'lib/net/imap/sequence_set.rb', line 2243
alias subtract_runs subtract_minmaxes
#to_a
Alias for #elements.
# File 'lib/net/imap/sequence_set.rb', line 1201
alias to_a elements
#to_s
Returns the IMAP sequence-set string representation, or an empty string when the set is empty. Note that an empty set is invalid in the IMAP syntax.
Related: #string, #valid_string, #normalized_string, #inspect
# File 'lib/net/imap/sequence_set.rb', line 650
def to_s; string || "" end
#to_sequence_set ⇒ self
Returns self
Related: .try_convert
# File 'lib/net/imap/sequence_set.rb', line 1825
rdoc_method :method: to_sequence_set
#to_set
#trim_or_delete_minmax(lower_idx, lmin, lmax, tmin, tmax) (private)
# File 'lib/net/imap/sequence_set.rb', line 2224
def trim_or_delete_minmax(lower_idx, lmin, lmax, tmin, tmax) if lmin < tmin # trim lower lmax = set_max_at lower_idx, tmin - 1 lower_idx += 1 end if tmax == lmax # case 5 delete_run_at lower_idx elsif (upper_idx = bsearch_index(tmax + 1)) if min_at(upper_idx) <= tmax # case 8 set_min_at upper_idx, tmax + 1 end slice_runs! lower_idx..upper_idx - 1 # cases 7 and 8 else # case 6 truncate_runs! lower_idx end end
#trim_or_split_minmax(idx, lmin, tmin, tmax) (private)
# File 'lib/net/imap/sequence_set.rb', line 2217
def trim_or_split_minmax(idx, lmin, tmin, tmax) set_min_at idx, tmax + 1 if lmin < tmin # split insert_minmax idx, lmin, tmin - 1 end end
#truncate_runs!(idx) (private)
#union(other)
Alias for #|.
# File 'lib/net/imap/sequence_set.rb', line 862
alias union :|
#valid_string
Returns the IMAP sequence-set string representation, or raises a DataFormatError when the set is empty.
Use #string to return nil or #to_s to return an empty string without error.
Related: #string, #normalized_string, #to_s
# File 'lib/net/imap/sequence_set.rb', line 596
def valid_string raise DataFormatError, "empty sequence-set" if empty? string end
#validate
Unstable API: currently for internal use only (Net::IMAP#validate_data)
# File 'lib/net/imap/sequence_set.rb', line 1836
def validate # :nodoc: empty? and raise DataFormatError, "empty sequence-set is invalid" self end
#xor(other)
Alias for #^.
# File 'lib/net/imap/sequence_set.rb', line 939
alias xor :^
#xor!(other)
In-place set #xor. Adds any numbers in other that are missing from this set, removes any numbers in other that are already in this set, and returns self.
other can be any object that would be accepted by .new.
set = Net::IMAP::SequenceSet.new(1..5)
set.xor! [2, 4, 6]
set #=> Net::IMAP::SequenceSet["1,3,5:6"]
#+(other) ⇒ sequence set
#|(other) ⇒ sequence set
#union(other) ⇒ sequence set
Also known as: #+, #union
sequence set
#|(other) ⇒ sequence set
#union(other) ⇒ sequence set
Returns a new sequence set that has every number in the other object added.
other may be any object that would be accepted by .new.
Net::IMAP::SequenceSet["1:5"] | 2 | [4..6, 99]
#=> Net::IMAP::SequenceSet["1:6,99"]
Related: #add, #merge, #&, #-, #^, #~
Set identities
lhs | rhs is equivalent to:
-
rhs | lhs(commutative) -
~(~lhs & ~rhs)(De Morgan’s Law) -
(lhs & rhs) ^ (lhs ^ rhs)
# File 'lib/net/imap/sequence_set.rb', line 860
def |(other) remain_frozen dup.merge other end
#~(self) ⇒ sequence set
#complement ⇒ sequence set
Also known as: #complement
sequence set
#complement ⇒ sequence set
Returns the complement of self, a SequenceSet which contains all numbers except for those in this set.
~Net::IMAP::SequenceSet.full #=> Net::IMAP::SequenceSet.empty
~Net::IMAP::SequenceSet.empty #=> Net::IMAP::SequenceSet.full
~Net::IMAP::SequenceSet["1:5,100:222"]
#=> Net::IMAP::SequenceSet["6:99,223:*"]
~Net::IMAP::SequenceSet["6:99,223:*"]
#=> Net::IMAP::SequenceSet["1:5,100:222"]
Related: #complement!, #|, #&, #-, #^
Set identities
~set is equivalent to:
-
full - set, where “full” is .full
# File 'lib/net/imap/sequence_set.rb', line 961
def ~; remain_frozen dup.complement! end