Class: Net::IMAP::SequenceSet
- Inherits:
-
Object
- Object
- Net::IMAP::SequenceSet
- 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 IMAP#search, IMAP#fetch, and IMAP#store.
Creating sequence sets
SequenceSet.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"
SequenceSet.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 SequenceSet.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 Net::IMAP::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 Net::IMAP::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:
- Creating a SequenceSet
- Comparing
- Querying
- Iterating
- Set Operations
- Assigning
- Deleting
- 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_seton an object and verifies that the result is a SequenceSet. - Net::IMAP::SequenceSet(): Coerce an input using ::try_convert or ::new.
- ::empty: Returns a frozen empty (invalid) SequenceSet.
- ::full: Returns a frozen SequenceSet containing 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.
Comparison to a number:
- #all_above?:
Returns whether every number in
selfis greater than a given number. - #all_below?:
Returns whether every number in
selfis less than a given number. - #any_above?:
Returns whether
selfcontains any numbers greater than a given number. - #any_below?:
Returns whether
selfcontains any numbers less than a given number. - #none_above?:
Returns whether
selfcontains no numbers greater than a given number. - #none_below?:
Returns whether
selfcontains no numbers less than a given number.
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 number. - #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; returns
self.
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-set string 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 collapse
- UINT32_MAX =
The largest possible non-zero unsigned 32-bit integer
2**32 - 1
Class Method Summary collapse
-
.[](first, *rest) ⇒ Object
:call-seq: SequenceSet -> valid frozen sequence set.
-
.empty ⇒ Object
Returns a frozen empty set singleton.
-
.full ⇒ Object
Returns a frozen full set singleton: "1:*".
-
.try_convert(obj) ⇒ Object
:call-seq: SequenceSet.try_convert(obj) -> sequence set or nil.
Instance Method Summary collapse
-
#&(other) ⇒ Object
(also: #intersection)
:call-seq: self & other -> sequence set intersection(other) -> sequence set.
-
#-(other) ⇒ Object
(also: #difference)
:call-seq: self - other -> sequence set difference(other) -> sequence set.
-
#==(other) ⇒ Object
:call-seq: self == other -> true or false.
-
#===(other) ⇒ Object
:call-seq: self === other -> true | false | nil.
- #[](index, length = nil) ⇒ Object (also: #slice)
-
#^(other) ⇒ Object
(also: #xor)
:call-seq: self ^ other -> sequence set xor(other) -> sequence set.
-
#above(num) ⇒ Object
Returns a copy of
selfwhich only contains the numbers abovenum. -
#add(element) ⇒ Object
(also: #<<)
:call-seq: add(element) -> self self << other -> self.
-
#add?(element) ⇒ Boolean
:call-seq: add?(element) -> self or nil.
-
#all_above?(number) ⇒ Boolean
:call-seq: all_above?(number) -> true or false.
-
#all_below?(number) ⇒ Boolean
:call-seq: all_below?(number) -> true or false.
-
#any_above?(number) ⇒ Boolean
:call-seq: any_above?(number) -> true or false.
-
#any_below?(number) ⇒ Boolean
:call-seq: any_below?(number) -> true or false.
-
#append(entry) ⇒ Object
Adds a range or number to the set and returns
self. -
#at(index) ⇒ Object
:call-seq: at(index) -> integer or nil.
-
#below(num) ⇒ Object
Returns a copy of
selfwhich only contains numbers belownum. -
#cardinality ⇒ Object
Returns the number of members in the set.
-
#clear ⇒ Object
Removes all elements and returns self.
-
#complement! ⇒ Object
:call-seq: complement! -> self.
-
#count ⇒ Object
Returns the count of distinct #numbers in the set.
-
#count_duplicates ⇒ Object
Returns the count of repeated numbers in the ordered #entries, the difference between #count_with_duplicates and #count.
-
#count_with_duplicates ⇒ Object
Returns the count of numbers in the ordered #entries, including any repeated numbers.
-
#cover?(other) ⇒ Boolean
:call-seq: cover?(other) -> true | false | nil.
-
#deconstruct ⇒ Object
Returns an array with #normalized_string when valid and an empty array otherwise.
-
#delete(element) ⇒ Object
:call-seq: delete(element) -> self.
-
#delete?(element) ⇒ Boolean
:call-seq: delete?(number) -> integer or nil delete?(star) -> :* or nil delete?(range) -> sequence set or nil.
-
#delete_at(index) ⇒ Object
:call-seq: delete_at(index) -> number or :* or nil.
-
#disjoint?(other) ⇒ Boolean
Returns
trueif the set and a given object have no common elements,falseotherwise. -
#each_element ⇒ Object
Yields each number or range (or :*) in #elements to the block and returns self.
-
#each_entry(&block) ⇒ Object
Yields each number or range in #string to the block and returns
self. -
#each_number(&block) ⇒ Object
Yields each number in #numbers to the block and returns self.
-
#each_ordered_number(&block) ⇒ Object
Yields each number in #entries to the block and returns self.
-
#each_range ⇒ Object
Yields each range in #ranges to the block and returns self.
-
#elements ⇒ Object
(also: #to_a)
Returns an array of ranges and integers and :*.
-
#empty? ⇒ Boolean
Returns true if the set contains no elements.
-
#encode_with(coder) ⇒ Object
For YAML serialization.
-
#entries ⇒ Object
Returns an array of ranges and integers and :*.
-
#eql?(other) ⇒ Boolean
:call-seq: eql?(other) -> true or false.
-
#find_index(number) ⇒ Object
Returns the (sorted and deduplicated) index of
numberin the set, ornilifnumberisn't in the set. -
#find_ordered_index(number) ⇒ Object
Returns the first index of
numberin the ordered #entries, ornilifnumberisn't in the set. -
#freeze ⇒ Object
Freezes and returns the set.
-
#full? ⇒ Boolean
Returns true if the set contains every possible element.
-
#has_duplicates? ⇒ Boolean
:call-seq: has_duplicates? -> true | false.
-
#hash ⇒ Object
See #eql?.
-
#include?(element) ⇒ Boolean
(also: #member?)
Returns
truewhen a given number or range is inself, andfalseotherwise. -
#include_star? ⇒ Boolean
Returns
truewhen the set contains *. -
#init_with(coder) ⇒ Object
For YAML deserialization.
-
#initialize(input = nil) ⇒ SequenceSet
constructor
Create a new SequenceSet object from
input, which may be another SequenceSet, an IMAP formattedsequence-setstring, a non-zero 32 bit unsigned integer, a range, :, a Set of numbers or , an object that responds toto_sequence_set(such as SearchResult) or an Array of these (array inputs may be nested). -
#inspect ⇒ Object
Returns an inspection string for the SequenceSet.
-
#intersect!(other) ⇒ Object
In-place set #intersection.
-
#intersect?(other) ⇒ Boolean
(also: #overlap?)
Returns
trueif the set and a given object have any common elements,falseotherwise. -
#limit(max:) ⇒ Object
Returns a frozen SequenceSet with * converted to
max, numbers and ranges overmaxremoved, and ranges containingmaxconverted to end atmax. -
#limit!(max:) ⇒ Object
Removes all members over
maxand returns self. -
#max(count = nil, star: :*) ⇒ Object
:call-seq: max(star: :*) => integer or star or nil max(count) => SequenceSet.
-
#merge(*sets) ⇒ Object
In-place set #union.
-
#min(count = nil, star: :*) ⇒ Object
:call-seq: min(star: :*) => integer or star or nil min(count) => SequenceSet.
-
#minmax(star: :*) ⇒ Object
:call-seq: minmax(star: :*) => [min, max] or nil.
-
#none_above?(number) ⇒ Boolean
:call-seq: none_above?(number) -> true or false.
-
#none_below?(number) ⇒ Boolean
:call-seq: none_below?(number) -> true or false.
-
#normalize ⇒ Object
Returns a SequenceSet with a normalized string representation: entries have been sorted, deduplicated, and coalesced, and all entries are in normal form.
-
#normalize! ⇒ Object
Resets #string to be sorted, deduplicated, and coalesced.
-
#normalized? ⇒ Boolean
Returns whether #string is fully normalized: entries have been sorted, deduplicated, and coalesced, and all entries are in normal form.
-
#normalized_string ⇒ Object
Returns a normalized
sequence-setstring representation, sorted and deduplicated. -
#numbers ⇒ Object
Returns a sorted array of all of the number values in the sequence set.
-
#ordered_at(index) ⇒ Object
:call-seq: ordered_at(index) -> integer or nil.
-
#ranges ⇒ Object
Returns an array of ranges.
-
#replace(other) ⇒ Object
Replace the contents of the set with the contents of
otherand returnsself. -
#send_data(imap, tag) ⇒ Object
Unstable API: for internal use only (Net::IMAP#send_data).
-
#size ⇒ Object
Returns the combined size of the ordered #entries, including any repeated numbers.
-
#slice!(index, length = nil) ⇒ Object
:call-seq: slice!(index) -> integer or :* or nil slice!(start, length) -> sequence set or nil slice!(range) -> sequence set or nil.
-
#string ⇒ Object
Returns the IMAP
sequence-setstring representation, ornilwhen the set is empty. -
#string=(input) ⇒ Object
Assigns a new string to #string and resets #elements to match.
-
#subtract(*sets) ⇒ Object
In-place set #difference.
-
#to_s ⇒ Object
Returns the IMAP
sequence-setstring representation, or an empty string when the set is empty. -
#to_set ⇒ Object
Returns a Set with all of the #numbers in the sequence set.
-
#valid? ⇒ Boolean
Returns false when the set is empty.
-
#valid_string ⇒ Object
Returns the IMAP
sequence-setstring representation, or raises a DataFormatError when the set is empty. -
#validate ⇒ Object
Unstable API: currently for internal use only (Net::IMAP#validate_data).
-
#xor!(other) ⇒ Object
In-place set #xor.
-
#|(other) ⇒ Object
(also: #+, #union)
:call-seq: self + other -> sequence set self | other -> sequence set union(other) -> sequence set.
-
#~ ⇒ Object
(also: #complement)
:call-seq: ~ self -> sequence set complement -> sequence set.
Constructor Details
#initialize(input = nil) ⇒ SequenceSet
Create a new SequenceSet object from input, which may be another
SequenceSet, an 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.
- Net::IMAP::SequenceSet() coerces an input to SequenceSet, without allocating a new object when the input is already a SequenceSet.
- ::try_convert calls
to_sequence_seton inputs that support it and returnsnilfor 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.
572 573 574 575 576 |
# File 'lib/net/imap/sequence_set.rb', line 572 def initialize(input = nil) @set_data = new_set_data @string = nil replace(input) unless input.nil? end |
Class Method Details
.[](first, *rest) ⇒ Object
:call-seq:
SequenceSet[*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, Net::IMAP::SequenceSet(), ::try_convert
469 470 471 472 473 474 475 476 |
# File 'lib/net/imap/sequence_set.rb', line 469 def [](first, *rest) if rest.empty? set = try_convert(first)&.validate set&.frozen? ? set : (set&.dup || new(first).validate).freeze else new(first).merge(*rest).validate.freeze end end |
.empty ⇒ Object
Returns a frozen empty set singleton. Note that valid IMAP sequence sets cannot be empty, so this set is invalid.
499 |
# File 'lib/net/imap/sequence_set.rb', line 499 def empty; EMPTY end |
.full ⇒ Object
Returns a frozen full set singleton: "1:*"
502 |
# File 'lib/net/imap/sequence_set.rb', line 502 def full; FULL end |
.try_convert(obj) ⇒ Object
:call-seq:
SequenceSet.try_convert(obj) -> sequence set or nil
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.
Related: Net::IMAP::SequenceSet(), ::new, ::[]
489 490 491 492 493 494 495 |
# File 'lib/net/imap/sequence_set.rb', line 489 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 Method Details
#&(other) ⇒ Object Also known as: intersection
:call-seq:
self & other -> 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)
1151 |
# File 'lib/net/imap/sequence_set.rb', line 1151 def &(other) remain_frozen dup.intersect! other end |
#-(other) ⇒ Object Also known as: difference
:call-seq:
self - other -> 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)
1125 |
# File 'lib/net/imap/sequence_set.rb', line 1125 def -(other) remain_frozen dup.subtract other end |
#==(other) ⇒ Object
:call-seq: self == other -> true or false
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
689 690 691 692 |
# File 'lib/net/imap/sequence_set.rb', line 689 def ==(other) self.class == other.class && (to_s == other.to_s || set_data == other.set_data) end |
#===(other) ⇒ Object
:call-seq: self === other -> true | false | nil
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?
720 721 722 723 724 |
# File 'lib/net/imap/sequence_set.rb', line 720 def ===(other) cover?(other) rescue nil end |
#[](index, length = nil) ⇒ Object Also known as: slice
:call-seq:
seqset[index] -> integer or :* or nil
slice(index) -> integer or :* or nil
seqset[start, length] -> sequence set or nil
slice(start, length) -> sequence set or nil
seqset[range] -> sequence set or nil
slice(range) -> sequence set or nil
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
set #=> 26
set #=> 22
set #=> 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
1778 1779 1780 1781 1782 1783 |
# File 'lib/net/imap/sequence_set.rb', line 1778 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) ⇒ Object Also known as: xor
:call-seq:
self ^ other -> 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"]
Related: #|, #&, #-, #~
Set identities
lhs ^ rhs is equivalent to:
- rhs ^ lhs (commutative)
- ~lhs ^ ~rhs
- (lhs | rhs) - (lhs & rhs)
- (lhs - rhs) | (rhs - lhs)
- (lhs ^ other) ^ (other ^ rhs)
1176 |
# File 'lib/net/imap/sequence_set.rb', line 1176 def ^(other) remain_frozen dup.xor! other end |
#above(num) ⇒ Object
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"
Related: #below, other set operations, #all_above?, #any_above?, #none_above?
1804 1805 1806 1807 1808 |
# File 'lib/net/imap/sequence_set.rb', line 1804 def above(num) NumValidator.valid_nz_number?(num) or raise ArgumentError, "not a valid sequence set number" difference(..num) end |
#add(element) ⇒ Object Also known as: <<
:call-seq:
add(element) -> self
self << other -> self
Adds a range or number to the set and returns self.
#string will be regenerated. Use #merge to add many elements at once.
Use #append to append new elements to #string. See SequenceSet@Ordered+and+Normalized+sets.
Related: #add?, #merge, #union, #append
1214 1215 1216 1217 1218 |
# File 'lib/net/imap/sequence_set.rb', line 1214 def add(element) # short-circuit before import_run add_run import_run element normalize! end |
#add?(element) ⇒ Boolean
:call-seq: add?(element) -> self or nil
Adds a range or number to the set and returns self. Returns nil
when the element is already included in the set.
#string will be regenerated. Use #merge to add many elements at once.
Related: #add, #merge, #union, #include?
1286 1287 1288 1289 |
# File 'lib/net/imap/sequence_set.rb', line 1286 def add?(element) # short-circuit before include? add element unless include? element end |
#all_above?(number) ⇒ Boolean
:call-seq:
all_above?(number) -> true or false
Returns whether the set only contains numbers greater than number (not
inclusive of number).
Net::IMAP::SequenceSet["5:8"].all_above?(9) => true
Net::IMAP::SequenceSet["5:8"].all_above?(7) => false
Net::IMAP::SequenceSet["5:8"].all_above?(5) => false
"*" is evaluated as UINT32_MAX:
Net::IMAP::SequenceSet.all_above?(UINT32_MAX - 1) => true
Net::IMAP::SequenceSet.all_above?(UINT32_MAX) => false
This is roughly equivalent to several other comparisons:
# Given the following assumptions:
set.empty? => false
set.include_star? => false
number => ^(1...UINT32_MAX)
# Then these expressions will always return the same result:
set.all_above?(number) => result
set.none_below?(number + 1) => ^result
!set.any_below?(number + 1) => ^result
set == set.above(number) => ^result
set.disjoint?(..number) => ^result
number < set.min => ^result
Related: other comparison methods, #above
823 824 825 826 |
# File 'lib/net/imap/sequence_set.rb', line 823 def all_above?(number) number = nz_number(number) empty? || number < min(star: UINT32_MAX) end |
#all_below?(number) ⇒ Boolean
:call-seq:
all_below?(number) -> true or false
Returns whether the set only contains numbers less than number (not
inclusive of number).
Net::IMAP::SequenceSet["5:8"].all_below?(9) => true
Net::IMAP::SequenceSet["5:8"].all_below?(8) => false
Net::IMAP::SequenceSet["5:8"].all_below?(5) => false
"*" is evaluated as UINT32_MAX:
Net::IMAP::SequenceSet.all_below?(UINT32_MAX) => false
This is roughly equivalent to several other comparisons:
# Given the following assumptions:
set.empty? => false
set.include_star? => false
number => ^(2..UINT32_MAX)
# Then these expressions will always return the same result:
set.all_below?(number) => result
set.none_above?(number - 1) => ^result
!set.any_above?(number - 1) => ^result
set == set.below(number) => ^result
set.disjoint?(number..) => ^result
set.max < number => ^result
Related: other comparison methods, #below
859 860 861 862 |
# File 'lib/net/imap/sequence_set.rb', line 859 def all_below?(number) number = nz_number(number) empty? || max(star: UINT32_MAX) < number end |
#any_above?(number) ⇒ Boolean
:call-seq:
any_above?(number) -> true or false
Returns whether the set contains any numbers greater than number (not
inclusive of number).
Net::IMAP::SequenceSet["5:8"].any_above?(1) => true
Net::IMAP::SequenceSet["5:8"].any_above?(7) => true
Net::IMAP::SequenceSet["5:8"].any_above?(8) => false
"*" is evaluated as UINT32_MAX:
Net::IMAP::SequenceSet.any_above?(UINT32_MAX - 1) => true
Net::IMAP::SequenceSet.any_above?(UINT32_MAX) => false
This is roughly equivalent to several other comparisons:
# Given the following assumptions:
set.empty? => false
set.include_star? => false
number => ^(1...UINT32_MAX)
# Then these expressions will always return the same result:
set.any_above?(number) => result
!set.none_above?(number) => ^result
!set.all_below?(number + 1) => ^result
set.cover?(set.above(number)) => ^result
set.intersect?(number + 1..) => ^result
number < set.max => ^result
Related: other comparison methods, #above
896 897 898 899 |
# File 'lib/net/imap/sequence_set.rb', line 896 def any_above?(number) number = nz_number(number) valid? && number < max(star: UINT32_MAX) end |
#any_below?(number) ⇒ Boolean
:call-seq:
any_below?(number) -> true or false
Returns whether the set contains any numbers less than number (not
inclusive of number).
Net::IMAP::SequenceSet["5:8"].any_below? 5 => false
Net::IMAP::SequenceSet["5:8"].any_below? 6 => true
Net::IMAP::SequenceSet["5:8"].any_below? 9 => true
"*" is evaluated as UINT32_MAX:
Net::IMAP::SequenceSet.any_below?(UINT32_MAX) => false
This is roughly equivalent to several other comparisons:
# Given the following assumptions:
set.empty? => false
set.include_star? => false
number => ^(2..UINT32_MAX)
# Then these expressions will always return the same result:
set.any_below?(number) => result
!set.none_below?(number) => ^result
!set.all_above?(number - 1) => ^result
set.cover?(set.below(number)) => ^result
set.intersect?(...number) => ^result
set.min < number => ^result
Related: other comparison methods, #below
932 933 934 935 |
# File 'lib/net/imap/sequence_set.rb', line 932 def any_below?(number) number = nz_number(number) valid? && min(star: UINT32_MAX) < number end |
#append(entry) ⇒ Object
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.
Related: #add, #merge, #union
1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 |
# File 'lib/net/imap/sequence_set.rb', line 1249 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 |
#at(index) ⇒ Object
:call-seq: at(index) -> integer or nil
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
1722 1723 1724 |
# File 'lib/net/imap/sequence_set.rb', line 1722 def at(index) seek_number_in_minmaxes(minmaxes, index) end |
#below(num) ⇒ Object
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 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"
Related: #above, other set operations, #all_below?, #any_below?, #none_below?
1837 1838 1839 1840 1841 |
# File 'lib/net/imap/sequence_set.rb', line 1837 def below(num) NumValidator.valid_nz_number?(num) or raise ArgumentError, "not a valid sequence set number" difference(num..) end |
#cardinality ⇒ Object
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
1584 |
# File 'lib/net/imap/sequence_set.rb', line 1584 def cardinality = minmaxes.sum(runs.count) { _2 - _1 } |
#clear ⇒ Object
Removes all elements and returns self.
579 580 581 582 583 584 |
# File 'lib/net/imap/sequence_set.rb', line 579 def clear # redundant check (normalizes the error message for JRuby) set_data.clear @string = nil self end |
#complement! ⇒ Object
:call-seq: 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
1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 |
# File 'lib/net/imap/sequence_set.rb', line 1890 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 ⇒ Object
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
1604 1605 1606 |
# File 'lib/net/imap/sequence_set.rb', line 1604 def count cardinality + (include_star? && include?(UINT32_MAX) ? -1 : 0) end |
#count_duplicates ⇒ Object
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?
1669 1670 1671 1672 |
# File 'lib/net/imap/sequence_set.rb', line 1669 def count_duplicates return 0 unless @string count_with_duplicates - count end |
#count_with_duplicates ⇒ Object
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 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 set.count_with_duplicates #=> 2 set.size #=> 2 set.count #=> 1 set.cardinality #=> 2
Related: #count, #cardinality, #size, #count_duplicates, #has_duplicates?, #entries
1631 1632 1633 1634 1635 1636 |
# File 'lib/net/imap/sequence_set.rb', line 1631 def count_with_duplicates return count unless @string each_entry_minmax.sum {|min, max| max - min + ((max == STAR_INT && min != STAR_INT) ? 0 : 1) } end |
#cover?(other) ⇒ Boolean
:call-seq: cover?(other) -> true | false | nil
Returns whether other is contained within the set. other may be any
object that would be accepted by ::new.
Related: #===, #include?, #include_star?, #intersect?
732 |
# File 'lib/net/imap/sequence_set.rb', line 732 def cover?(other) import_runs(other).none? { !include_run?(_1) } end |
#deconstruct ⇒ Object
Returns an array with #normalized_string when valid and an empty array otherwise.
630 |
# File 'lib/net/imap/sequence_set.rb', line 630 def deconstruct; valid? ? [normalized_string] : [] end |
#delete(element) ⇒ Object
:call-seq: 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
1299 1300 1301 1302 1303 |
# File 'lib/net/imap/sequence_set.rb', line 1299 def delete(element) # short-circuit before import_run subtract_run import_run element normalize! end |
#delete?(element) ⇒ Boolean
:call-seq:
delete?(number) -> integer or nil
delete?(star) -> :* or nil
delete?(range) -> sequence set or nil
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?
1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 |
# File 'lib/net/imap/sequence_set.rb', line 1337 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) ⇒ Object
:call-seq: delete_at(index) -> number or :* or nil
Deletes a number the set, indicated by the given index. Returns the
number that was removed, or nil if nothing was removed.
#string will be regenerated after deletion.
Related: #delete, #delete?, #slice!, #subtract, #difference
1362 1363 1364 |
# File 'lib/net/imap/sequence_set.rb', line 1362 def delete_at(index) slice! Integer(index.to_int) end |
#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?
787 788 789 |
# File 'lib/net/imap/sequence_set.rb', line 787 def disjoint?(other) empty? || import_runs(other).none? { intersect_run? _1 } end |
#each_element ⇒ Object
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
1509 1510 1511 1512 1513 |
# File 'lib/net/imap/sequence_set.rb', line 1509 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) ⇒ Object
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
1497 1498 1499 1500 |
# File 'lib/net/imap/sequence_set.rb', line 1497 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_number(&block) ⇒ Object
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
1537 1538 1539 1540 1541 1542 |
# File 'lib/net/imap/sequence_set.rb', line 1537 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_ordered_number(&block) ⇒ Object
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
1551 1552 1553 1554 1555 |
# File 'lib/net/imap/sequence_set.rb', line 1551 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_range ⇒ Object
Yields each range in #ranges to the block and returns self. Returns an enumerator when called without a block.
Related: #ranges
1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 |
# File 'lib/net/imap/sequence_set.rb', line 1519 def each_range # :yields: range return to_enum(__method__) unless block_given? minmaxes.each do |min, max| if min == STAR_INT then yield :*.. elsif max == STAR_INT then yield min.. else yield min..max end end self end |
#elements ⇒ Object 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
1438 |
# File 'lib/net/imap/sequence_set.rb', line 1438 def elements; each_element.to_a end |
#empty? ⇒ Boolean
Returns true if the set contains no elements
1072 |
# File 'lib/net/imap/sequence_set.rb', line 1072 def empty?; runs.empty? end |
#encode_with(coder) ⇒ Object
For YAML serialization
2089 2090 2091 2092 |
# File 'lib/net/imap/sequence_set.rb', line 2089 def encode_with(coder) # :nodoc: # we can perfectly reconstruct from the string coder['string'] = to_s end |
#entries ⇒ Object
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 IMAP#sort.
See SequenceSet@Ordered+and+Normalized+sets.
Related: #each_entry, #elements
1422 |
# File 'lib/net/imap/sequence_set.rb', line 1422 def entries; each_entry.to_a end |
#eql?(other) ⇒ Boolean
:call-seq: eql?(other) -> true or false
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
708 |
# File 'lib/net/imap/sequence_set.rb', line 708 def eql?(other) self.class == other.class && string == other.string end |
#find_index(number) ⇒ Object
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
1690 1691 1692 1693 1694 1695 1696 1697 |
# File 'lib/net/imap/sequence_set.rb', line 1690 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 idx_min + (number - min) end nil end |
#find_ordered_index(number) ⇒ Object
Returns the first index of number in the ordered #entries, or
nil if number isn't in the set.
Related: #find_index
1703 1704 1705 1706 1707 1708 1709 1710 1711 |
# File 'lib/net/imap/sequence_set.rb', line 1703 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 idx_min + (number - min) end end nil end |
#freeze ⇒ Object
Freezes and returns the set. A frozen SequenceSet is Ractor-safe.
667 668 669 670 671 |
# File 'lib/net/imap/sequence_set.rb', line 667 def freeze return self if frozen? freeze_set_data super end |
#full? ⇒ Boolean
Returns true if the set contains every possible element.
1075 |
# File 'lib/net/imap/sequence_set.rb', line 1075 def full?; set_data == FULL_SET_DATA end |
#has_duplicates? ⇒ Boolean
:call-seq: has_duplicates? -> true | false
Returns whether or not the ordered #entries repeat any numbers.
Always returns false when #string is normalized.
Related: #entries, #count_with_duplicates, #count_duplicates
1681 1682 1683 1684 |
# File 'lib/net/imap/sequence_set.rb', line 1681 def has_duplicates? return false unless @string count_with_duplicates != count end |
#hash ⇒ Object
See #eql?
711 |
# File 'lib/net/imap/sequence_set.rb', line 711 def hash; [self.class, string].hash 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?
758 759 760 761 |
# File 'lib/net/imap/sequence_set.rb', line 758 def include?(element) run = import_run element rescue nil !!include_run?(run) if run end |
#include_star? ⇒ Boolean
Returns true when the set contains *.
766 |
# File 'lib/net/imap/sequence_set.rb', line 766 def include_star?; max_num == STAR_INT end |
#init_with(coder) ⇒ Object
For YAML deserialization
2095 2096 2097 2098 |
# File 'lib/net/imap/sequence_set.rb', line 2095 def init_with(coder) # :nodoc: @set_data = new_set_data self.string = coder['string'] end |
#inspect ⇒ Object
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">
Related: #to_s, #string
2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 |
# File 'lib/net/imap/sequence_set.rb', line 2044 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) ⇒ Object
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?
1912 1913 1914 1915 |
# File 'lib/net/imap/sequence_set.rb', line 1912 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?
775 776 777 |
# File 'lib/net/imap/sequence_set.rb', line 775 def intersect?(other) valid? && import_runs(other).any? { intersect_run? _1 } end |
#limit(max:) ⇒ Object
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!
1861 1862 1863 1864 1865 1866 1867 1868 |
# File 'lib/net/imap/sequence_set.rb', line 1861 def limit(max:) max = import_num(max) if empty? then self.class.empty elsif !include_star? && max < min then self.class.empty elsif max(star: STAR_INT) <= max then frozen? ? self : dup.freeze else dup.limit!(max: max).freeze end end |
#limit!(max:) ⇒ Object
Removes all members over max and returns self. If * is a
member, it will be converted to max.
Related: #limit
1874 1875 1876 1877 1878 1879 1880 1881 |
# File 'lib/net/imap/sequence_set.rb', line 1874 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(count = nil, star: :*) ⇒ Object
:call-seq:
max(star: :*) => integer or star or nil
max(count) => SequenceSet
Returns the maximum value in self, star when the set includes
*, or nil when the set is empty.
When count is given, a new SequenceSet is returned, containing only
the last count numbers. An empty SequenceSet is returned when self
is empty. (+star+ is ignored when count is given.)
Related: #min, #minmax, #slice
1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 |
# File 'lib/net/imap/sequence_set.rb', line 1022 def max(count = nil, star: :*) if count count = Integer(count.to_int) raise ArgumentError, 'negative count' if count < 0 return remain_frozen_empty if count == 0 if cardinality <= count frozen? ? self : dup else slice(-count..) || remain_frozen_empty end elsif (val = max_num) val == STAR_INT ? star : val end end |
#merge(*sets) ⇒ Object
In-place set #union. Merges all of the elements that appear in any of
the sets into this set, and returns self.
The sets may be any objects that would be accepted by ::new.
#string will be regenerated after all sets have been merged.
Related: #add, #add?, #union
1393 1394 1395 1396 1397 |
# File 'lib/net/imap/sequence_set.rb', line 1393 def merge(*sets) # short-circuit before import_runs add_runs import_runs sets normalize! end |
#min(count = nil, star: :*) ⇒ Object
:call-seq:
min(star: :*) => integer or star or nil
min(count) => SequenceSet
Returns the minimum value in self, star when the only value in the
set is *, or nil when the set is empty.
When count is given, a new SequenceSet is returned, containing only
the first count numbers. An empty SequenceSet is returned when self
is empty. (+star+ is ignored when count is given.)
Related: #max, #minmax, #slice
1049 1050 1051 1052 1053 1054 1055 1056 1057 |
# File 'lib/net/imap/sequence_set.rb', line 1049 def min(count = nil, star: :*) if count count = Integer(count.to_int) raise ArgumentError, 'negative count' if count < 0 slice(0...count) || remain_frozen_empty elsif (val = min_num) val != STAR_INT ? val : star end end |
#minmax(star: :*) ⇒ Object
:call-seq: minmax(star: :*) => [min, max] or nil
Returns a 2-element array containing the minimum and maximum numbers in
self, or nil when the set is empty. star is handled the same way
as by #min and #max.
Related: #min, #max
1066 |
# File 'lib/net/imap/sequence_set.rb', line 1066 def minmax(star: :*); [min(star: star), max(star: star)] unless empty? end |
#none_above?(number) ⇒ Boolean
:call-seq:
none_above?(number) -> true or false
Returns whether the set contains no numbers greater than number (not
inclusive of number).
Net::IMAP::SequenceSet["5:8"].none_above?(9) => true
Net::IMAP::SequenceSet["5:8"].none_above?(8) => true
Net::IMAP::SequenceSet["5:8"].none_above?(7) => false
"*" is evaluated as UINT32_MAX:
Net::IMAP::SequenceSet.none_above?(UINT32_MAX - 1) => false
Net::IMAP::SequenceSet.none_above?(UINT32_MAX) => true
This is roughly equivalent to several other comparisons:
# Given the following assumptions:
set.empty? => false
set.include_star? => false
number => ^(1...UINT32_MAX)
# Then these expressions will always return the same result:
set.none_above?(number) => result
!set.any_above?(number) => ^result
set.all_below?(number + 1) => ^result
set.above(number).empty? => ^result
set.disjoint?(number..) => ^result
set.max <= number => ^result
Related: other comparison methods, #above
969 970 971 972 |
# File 'lib/net/imap/sequence_set.rb', line 969 def none_above?(number) number = nz_number(number) empty? || max(star: UINT32_MAX) <= number end |
#none_below?(number) ⇒ Boolean
:call-seq:
none_below?(number) -> true or false
Returns whether the set contains no numbers less than number (not
inclusive of number).
Net::IMAP::SequenceSet["5:8"].none_below?(4) => true
Net::IMAP::SequenceSet["5:8"].none_below?(5) => true
Net::IMAP::SequenceSet["5:8"].none_below?(6) => false
"*" is evaluated as UINT32_MAX:
Net::IMAP::SequenceSet.none_below?(UINT32_MAX) => true
This is roughly equivalent to several other comparisons:
# Given the following assumptions:
set.empty? => false
set.include_star? => false
number => ^(2..UINT32_MAX)
# Then these expressions will always return the same result:
set.none_below?(number) => result
!set.any_below?(number) => ^result
set.all_above?(number - 1) => ^result
set.above(number).empty? => ^result
set.disjoint?(...number) => ^result
number <= set.min => ^result
Related: other comparison methods, #below
1005 1006 1007 1008 |
# File 'lib/net/imap/sequence_set.rb', line 1005 def none_below?(number) number = nz_number(number) empty? || number <= min(star: UINT32_MAX) end |
#normalize ⇒ Object
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?
1993 1994 1995 |
# File 'lib/net/imap/sequence_set.rb', line 1993 def normalize frozen? && normalized? ? self : remain_frozen(dup.normalize!) end |
#normalize! ⇒ Object
Resets #string to be sorted, deduplicated, and coalesced. Returns
self. See SequenceSet@Ordered+and+Normalized+sets.
Related: #normalize, #normalized_string, #normalized?
2001 2002 2003 2004 2005 |
# File 'lib/net/imap/sequence_set.rb', line 2001 def normalize! # redundant check (normalizes the error message for JRuby) @string = nil self end |
#normalized? ⇒ Boolean
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
1978 1979 1980 |
# File 'lib/net/imap/sequence_set.rb', line 1978 def normalized? @string.nil? || normal_string?(@string) end |
#normalized_string ⇒ Object
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?
2017 2018 2019 |
# File 'lib/net/imap/sequence_set.rb', line 2017 def normalized_string export_runs(runs) unless runs.empty? end |
#numbers ⇒ Object
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.
Related: #elements, #ranges, #to_set
1485 |
# File 'lib/net/imap/sequence_set.rb', line 1485 def numbers; each_number.to_a end |
#ordered_at(index) ⇒ Object
:call-seq: ordered_at(index) -> integer or nil
Returns the number at the given index in the ordered #entries, without
modifying the set.
index is interpreted the same as in #at (and #[]), except that
#ordered_at applies to the ordered #entries, not the sorted set.
Related: #[], #slice, #ordered_at
1735 1736 1737 |
# File 'lib/net/imap/sequence_set.rb', line 1735 def ordered_at(index) seek_number_in_minmaxes(each_entry_minmax, index) end |
#ranges ⇒ Object
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
1457 |
# File 'lib/net/imap/sequence_set.rb', line 1457 def ranges; each_range.to_a end |
#replace(other) ⇒ Object
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.
591 592 593 594 595 596 597 598 599 600 601 |
# File 'lib/net/imap/sequence_set.rb', line 591 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 |
#send_data(imap, tag) ⇒ Object
Unstable API: for internal use only (Net::IMAP#send_data)
2084 2085 2086 |
# File 'lib/net/imap/sequence_set.rb', line 2084 def send_data(imap, tag) # :nodoc: imap.__send__(:put_string, valid_string) end |
#size ⇒ Object
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
1658 1659 1660 1661 |
# File 'lib/net/imap/sequence_set.rb', line 1658 def size return cardinality unless @string each_entry_minmax.sum {|min, max| max - min + 1 } end |
#slice!(index, length = nil) ⇒ Object
:call-seq:
slice!(index) -> integer or :* or nil
slice!(start, length) -> sequence set or nil
slice!(range) -> sequence set or nil
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
1379 1380 1381 1382 1383 |
# File 'lib/net/imap/sequence_set.rb', line 1379 def slice!(index, length = nil) # short-circuit before slice deleted = slice(index, length) and subtract deleted deleted end |
#string ⇒ Object
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
626 |
# File 'lib/net/imap/sequence_set.rb', line 626 def string; @string || normalized_string if valid? end |
#string=(input) ⇒ Object
Assigns a new string to #string and resets #elements to match.
Assigning nil or an empty string are equivalent to calling #clear.
Non-empty strings are validated but not normalized.
Use #add, #merge, or #append to add a string to an existing set.
Related: #replace, #clear
640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 |
# File 'lib/net/imap/sequence_set.rb', line 640 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 |
#subtract(*sets) ⇒ Object
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
1405 1406 1407 1408 1409 |
# File 'lib/net/imap/sequence_set.rb', line 1405 def subtract(*sets) # short-circuit before import_runs subtract_runs import_runs sets normalize! end |
#to_s ⇒ Object
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
664 |
# File 'lib/net/imap/sequence_set.rb', line 664 def to_s; string || "" end |
#to_set ⇒ Object
Returns a Set with all of the #numbers in the sequence set.
If the set contains a *, RangeError will be raised.
See #numbers for the warning about very large sets.
Related: #elements, #ranges, #numbers
1564 |
# File 'lib/net/imap/sequence_set.rb', line 1564 def to_set; Set.new(numbers) end |
#valid? ⇒ Boolean
Returns false when the set is empty.
1069 |
# File 'lib/net/imap/sequence_set.rb', line 1069 def valid?; !empty? end |
#valid_string ⇒ Object
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
610 611 612 613 |
# File 'lib/net/imap/sequence_set.rb', line 610 def valid_string raise DataFormatError, "empty sequence-set" if empty? string end |
#validate ⇒ Object
Unstable API: currently for internal use only (Net::IMAP#validate_data)
2078 2079 2080 2081 |
# File 'lib/net/imap/sequence_set.rb', line 2078 def validate # :nodoc: empty? and raise DataFormatError, "empty sequence-set is invalid" self end |
#xor!(other) ⇒ Object
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"]
Related: #xor, #merge, #subtract
1928 1929 1930 1931 1932 1933 |
# File 'lib/net/imap/sequence_set.rb', line 1928 def xor!(other) # short-circuit before processing input other = SequenceSet.new(other) copy = dup merge(other).subtract(other.subtract(copy.complement!)) end |
#|(other) ⇒ Object Also known as: +, union
:call-seq:
self + other -> sequence set
self | 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)
1098 |
# File 'lib/net/imap/sequence_set.rb', line 1098 def |(other) remain_frozen dup.merge other end |
#~ ⇒ Object Also known as: complement
:call-seq:
~ self -> 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 Net::IMAP::SequenceSet.full
1199 |
# File 'lib/net/imap/sequence_set.rb', line 1199 def ~; remain_frozen dup.complement! end |