net-imap 0.4.22 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +12 -2
- data/README.md +10 -4
- data/docs/styles.css +75 -14
- data/lib/net/imap/authenticators.rb +2 -2
- data/lib/net/imap/command_data.rb +40 -95
- data/lib/net/imap/config/attr_accessors.rb +8 -9
- data/lib/net/imap/config/attr_inheritance.rb +64 -1
- data/lib/net/imap/config/attr_type_coercion.rb +22 -10
- data/lib/net/imap/config/attr_version_defaults.rb +90 -0
- data/lib/net/imap/config.rb +241 -125
- data/lib/net/imap/connection_state.rb +48 -0
- data/lib/net/imap/data_encoding.rb +80 -31
- data/lib/net/imap/deprecated_client_options.rb +6 -3
- data/lib/net/imap/errors.rb +158 -0
- data/lib/net/imap/esearch_result.rb +225 -0
- data/lib/net/imap/fetch_data.rb +126 -47
- data/lib/net/imap/flags.rb +1 -1
- data/lib/net/imap/response_data.rb +123 -187
- data/lib/net/imap/response_parser/parser_utils.rb +19 -23
- data/lib/net/imap/response_parser.rb +182 -38
- data/lib/net/imap/response_reader.rb +10 -12
- data/lib/net/imap/sasl/anonymous_authenticator.rb +3 -3
- data/lib/net/imap/sasl/authentication_exchange.rb +52 -20
- data/lib/net/imap/sasl/authenticators.rb +8 -4
- data/lib/net/imap/sasl/client_adapter.rb +77 -26
- data/lib/net/imap/sasl/cram_md5_authenticator.rb +4 -4
- data/lib/net/imap/sasl/digest_md5_authenticator.rb +218 -56
- data/lib/net/imap/sasl/external_authenticator.rb +2 -2
- data/lib/net/imap/sasl/gs2_header.rb +7 -7
- data/lib/net/imap/sasl/login_authenticator.rb +4 -3
- data/lib/net/imap/sasl/oauthbearer_authenticator.rb +6 -6
- data/lib/net/imap/sasl/plain_authenticator.rb +7 -7
- data/lib/net/imap/sasl/protocol_adapters.rb +60 -4
- data/lib/net/imap/sasl/scram_authenticator.rb +8 -8
- data/lib/net/imap/sasl.rb +7 -4
- data/lib/net/imap/sasl_adapter.rb +0 -1
- data/lib/net/imap/search_result.rb +10 -5
- data/lib/net/imap/sequence_set.rb +1104 -421
- data/lib/net/imap/stringprep/nameprep.rb +1 -1
- data/lib/net/imap/stringprep/trace.rb +4 -4
- data/lib/net/imap/uidplus_data.rb +4 -147
- data/lib/net/imap/vanished_data.rb +65 -0
- data/lib/net/imap.rb +1002 -313
- data/net-imap.gemspec +1 -1
- data/rakelib/rfcs.rake +2 -0
- data/rakelib/string_prep_tables_generator.rb +6 -2
- metadata +7 -3
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "set" unless defined?(::Set)
|
|
4
|
+
|
|
3
5
|
module Net
|
|
4
6
|
class IMAP
|
|
5
7
|
|
|
@@ -14,30 +16,12 @@ module Net
|
|
|
14
16
|
# receive a SequenceSet as an argument, for example IMAP#search, IMAP#fetch,
|
|
15
17
|
# and IMAP#store.
|
|
16
18
|
#
|
|
17
|
-
# == EXPERIMENTAL API
|
|
18
|
-
#
|
|
19
|
-
# SequenceSet is currently experimental. Only two methods, ::[] and
|
|
20
|
-
# #valid_string, are considered stable. Although the API isn't expected to
|
|
21
|
-
# change much, any other methods may be removed or changed without
|
|
22
|
-
# deprecation.
|
|
23
|
-
#
|
|
24
19
|
# == Creating sequence sets
|
|
25
20
|
#
|
|
26
|
-
# SequenceSet.new with no arguments creates an empty sequence set. Note
|
|
27
|
-
# that an empty sequence set is invalid in the \IMAP grammar.
|
|
28
|
-
#
|
|
29
|
-
# set = Net::IMAP::SequenceSet.new
|
|
30
|
-
# set.empty? #=> true
|
|
31
|
-
# set.valid? #=> false
|
|
32
|
-
# set.valid_string #!> raises DataFormatError
|
|
33
|
-
# set << 1..10
|
|
34
|
-
# set.empty? #=> false
|
|
35
|
-
# set.valid? #=> true
|
|
36
|
-
# set.valid_string #=> "1:10"
|
|
37
|
-
#
|
|
38
21
|
# SequenceSet.new may receive a single optional argument: a non-zero 32 bit
|
|
39
22
|
# unsigned integer, a range, a <tt>sequence-set</tt> formatted string,
|
|
40
|
-
# another
|
|
23
|
+
# another SequenceSet, a Set (containing only numbers or <tt>*</tt>), or an
|
|
24
|
+
# Array containing any of these (array inputs may be nested).
|
|
41
25
|
#
|
|
42
26
|
# set = Net::IMAP::SequenceSet.new(1)
|
|
43
27
|
# set.valid_string #=> "1"
|
|
@@ -52,30 +36,118 @@ module Net
|
|
|
52
36
|
# set = Net::IMAP::SequenceSet.new(1, 2, 3..7, 5, 6..10, 2048, 1024)
|
|
53
37
|
# set.valid_string #=> "1:10,55,1024:2048"
|
|
54
38
|
#
|
|
55
|
-
#
|
|
56
|
-
#
|
|
39
|
+
# SequenceSet.new with no arguments creates an empty sequence set. Note
|
|
40
|
+
# that an empty sequence set is invalid in the \IMAP grammar.
|
|
41
|
+
#
|
|
42
|
+
# set = Net::IMAP::SequenceSet.new
|
|
43
|
+
# set.empty? #=> true
|
|
44
|
+
# set.valid? #=> false
|
|
45
|
+
# set.valid_string #!> raises DataFormatError
|
|
46
|
+
# set << 1..10
|
|
47
|
+
# set.empty? #=> false
|
|
48
|
+
# set.valid? #=> true
|
|
49
|
+
# set.valid_string #=> "1:10"
|
|
50
|
+
#
|
|
51
|
+
# Using SequenceSet.new with another SequenceSet input behaves the same as
|
|
52
|
+
# calling #dup on the other set. The input's #string will be preserved.
|
|
53
|
+
#
|
|
54
|
+
# input = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
|
|
55
|
+
# copy = Net::IMAP::SequenceSet.new(input)
|
|
56
|
+
# input.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
|
|
57
|
+
# copy.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
|
|
58
|
+
# copy2 = input.dup # same as calling new with a SequenceSet input
|
|
59
|
+
# copy == input #=> true, same set membership
|
|
60
|
+
# copy.eql? input #=> true, same string value
|
|
61
|
+
# copy.equal? input #=> false, different objects
|
|
62
|
+
#
|
|
63
|
+
# copy.normalize!
|
|
64
|
+
# copy.valid_string #=> "1:10,1024,2048"
|
|
65
|
+
# copy == input #=> true, same set membership
|
|
66
|
+
# copy.eql? input #=> false, different string value
|
|
67
|
+
#
|
|
68
|
+
# copy << 999
|
|
69
|
+
# copy.valid_string #=> "1:10,999,1024,2048"
|
|
70
|
+
# copy == input #=> false, different set membership
|
|
71
|
+
# copy.eql? input #=> false, different string value
|
|
72
|
+
#
|
|
73
|
+
# Use Net::IMAP::SequenceSet() to coerce a single (optional) input.
|
|
74
|
+
# A SequenceSet input is returned without duplication, even when frozen.
|
|
75
|
+
#
|
|
76
|
+
# set = Net::IMAP::SequenceSet()
|
|
77
|
+
# set.string #=> nil
|
|
78
|
+
# set.frozen? #=> false
|
|
79
|
+
#
|
|
80
|
+
# # String order is preserved
|
|
81
|
+
# set = Net::IMAP::SequenceSet("1,2,3:7,5,6:10,2048,1024")
|
|
82
|
+
# set.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
|
|
83
|
+
# set.frozen? #=> false
|
|
84
|
+
#
|
|
85
|
+
# # Other inputs are normalized
|
|
86
|
+
# set = Net::IMAP::SequenceSet([1, 2, [3..7, 5], 6..10, 2048, 1024])
|
|
87
|
+
# set.valid_string #=> "1:10,1024,2048"
|
|
88
|
+
# set.frozen? #=> false
|
|
89
|
+
#
|
|
90
|
+
# unfrozen = set
|
|
91
|
+
# frozen = set.dup.freeze
|
|
92
|
+
# unfrozen.equal? Net::IMAP::SequenceSet(unfrozen) #=> true
|
|
93
|
+
# frozen.equal? Net::IMAP::SequenceSet(frozen) #=> true
|
|
57
94
|
#
|
|
95
|
+
# Use ::[] to coerce one or more arguments into a valid frozen SequenceSet.
|
|
96
|
+
# A valid frozen SequenceSet is returned directly, without allocating a new
|
|
97
|
+
# object. ::[] will not create an invalid (empty) set.
|
|
98
|
+
#
|
|
99
|
+
# Net::IMAP::SequenceSet[] #!> raises ArgumentError
|
|
100
|
+
# Net::IMAP::SequenceSet[nil] #!> raises DataFormatError
|
|
101
|
+
# Net::IMAP::SequenceSet[""] #!> raises DataFormatError
|
|
102
|
+
#
|
|
103
|
+
# # String order is preserved
|
|
58
104
|
# set = Net::IMAP::SequenceSet["1,2,3:7,5,6:10,2048,1024"]
|
|
59
105
|
# set.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
|
|
106
|
+
# set.frozen? #=> true
|
|
107
|
+
#
|
|
108
|
+
# # Other inputs are normalized
|
|
60
109
|
# set = Net::IMAP::SequenceSet[1, 2, [3..7, 5], 6..10, 2048, 1024]
|
|
61
|
-
# set.valid_string #=> "1:10,
|
|
110
|
+
# set.valid_string #=> "1:10,1024,2048"
|
|
111
|
+
# set.frozen? #=> true
|
|
112
|
+
#
|
|
113
|
+
# frozen = set
|
|
114
|
+
# unfrozen = set.dup
|
|
115
|
+
# frozen.equal? Net::IMAP::SequenceSet[frozen] #=> true
|
|
116
|
+
# unfrozen.equal? Net::IMAP::SequenceSet[unfrozen] #=> false
|
|
117
|
+
#
|
|
118
|
+
# Objects which respond to +to_sequence_set+ (such as SearchResult and
|
|
119
|
+
# ThreadMember) can be coerced to a SequenceSet with ::new, ::try_convert,
|
|
120
|
+
# ::[], or Net::IMAP::SequenceSet.
|
|
121
|
+
#
|
|
122
|
+
# search = imap.uid_search(["SUBJECT", "hello", "NOT", "SEEN"])
|
|
123
|
+
# seqset = Net::IMAP::SequenceSet(search) - already_fetched
|
|
124
|
+
# fetch = imap.uid_fetch(seqset, "FAST")
|
|
62
125
|
#
|
|
63
126
|
# == Ordered and Normalized sets
|
|
64
127
|
#
|
|
65
128
|
# Sometimes the order of the set's members is significant, such as with the
|
|
66
129
|
# +ESORT+, <tt>CONTEXT=SORT</tt>, and +UIDPLUS+ extensions. So, when a
|
|
67
|
-
# sequence set is created
|
|
68
|
-
# #string representation is preserved.
|
|
130
|
+
# sequence set is created from a single string (such as by the parser), that
|
|
131
|
+
# #string representation is preserved. Assigning a string with #string= or
|
|
132
|
+
# #replace will also preserve that string. Use #each_entry, #entries, or
|
|
133
|
+
# #each_ordered_number to enumerate the entries in their #string order.
|
|
134
|
+
# Hash equality (using #eql?) is based on the string representation.
|
|
135
|
+
#
|
|
136
|
+
# Internally, SequenceSet uses a normalized uint32 set representation which
|
|
137
|
+
# sorts and de-duplicates all numbers and coalesces adjacent or overlapping
|
|
138
|
+
# entries. Many methods use this sorted set representation for <tt>O(lg
|
|
139
|
+
# n)</tt> searches. Use #each_element, #elements, #each_range, #ranges,
|
|
140
|
+
# #each_number, or #numbers to enumerate the set in sorted order. Basic
|
|
141
|
+
# object equality (using #==) is based on set membership, without regard to
|
|
142
|
+
# #entry order or #string normalization.
|
|
69
143
|
#
|
|
70
|
-
#
|
|
71
|
-
# entries
|
|
72
|
-
#
|
|
73
|
-
# <tt>O(lg n)</tt> porformance. Use #entries or #each_entry to enumerate
|
|
74
|
-
# the set in its original order.
|
|
144
|
+
# Most modification methods reset #string to its #normalized form, so that
|
|
145
|
+
# #entries and #elements are identical. Use #append to preserve #entries
|
|
146
|
+
# order while modifying a set.
|
|
75
147
|
#
|
|
76
|
-
#
|
|
77
|
-
#
|
|
78
|
-
# #
|
|
148
|
+
# Non-normalized sets store both representations of the set, which can more
|
|
149
|
+
# than double memory usage. Very large sequence sets should avoid
|
|
150
|
+
# denormalizing methods (such as #append) unless order is significant.
|
|
79
151
|
#
|
|
80
152
|
# == Using <tt>*</tt>
|
|
81
153
|
#
|
|
@@ -111,12 +183,16 @@ module Net
|
|
|
111
183
|
#
|
|
112
184
|
# When a set includes <tt>*</tt>, some methods may have surprising behavior.
|
|
113
185
|
#
|
|
114
|
-
# For example, #complement treats <tt>*</tt> as its own
|
|
115
|
-
# the #intersection of a set and its #complement will always be empty.
|
|
116
|
-
#
|
|
117
|
-
#
|
|
118
|
-
#
|
|
119
|
-
#
|
|
186
|
+
# For example, #complement treats <tt>*</tt> as its own member. This way,
|
|
187
|
+
# the #intersection of a set and its #complement will always be empty. And
|
|
188
|
+
# <tt>*</tt> is sorted as greater than any other number in the set. This is
|
|
189
|
+
# not how an \IMAP server interprets the set: it will convert <tt>*</tt> to
|
|
190
|
+
# the number of messages in the mailbox, the +UID+ of the last message in
|
|
191
|
+
# the mailbox, or +UIDNEXT+, as appropriate. Several methods have an
|
|
192
|
+
# argument for how <tt>*</tt> should be interpreted.
|
|
193
|
+
#
|
|
194
|
+
# But, for example, this means that there may be overlap between a set and
|
|
195
|
+
# its complement after #limit is applied to each:
|
|
120
196
|
#
|
|
121
197
|
# ~Net::IMAP::SequenceSet["*"] == Net::IMAP::SequenceSet[1..(2**32-1)]
|
|
122
198
|
# ~Net::IMAP::SequenceSet[1..5] == Net::IMAP::SequenceSet["6:*"]
|
|
@@ -127,7 +203,7 @@ module Net
|
|
|
127
203
|
# (set.limit(max: 4) & (~set).limit(max: 4)).to_a => [4]
|
|
128
204
|
#
|
|
129
205
|
# When counting the number of numbers in a set, <tt>*</tt> will be counted
|
|
130
|
-
#
|
|
206
|
+
# as if it were equal to UINT32_MAX:
|
|
131
207
|
# UINT32_MAX = 2**32 - 1
|
|
132
208
|
# Net::IMAP::SequenceSet["*"].count => 1
|
|
133
209
|
# Net::IMAP::SequenceSet[1..UINT32_MAX - 1, :*].count => UINT32_MAX
|
|
@@ -136,6 +212,12 @@ module Net
|
|
|
136
212
|
# Net::IMAP::SequenceSet[UINT32_MAX, :*].count => 1
|
|
137
213
|
# Net::IMAP::SequenceSet[UINT32_MAX..].count => 1
|
|
138
214
|
#
|
|
215
|
+
# Use #cardinality to count the set members wxth <tt>*</tt> counted as a
|
|
216
|
+
# distinct member:
|
|
217
|
+
# Net::IMAP::SequenceSet[1..].cardinality #=> UINT32_MAX + 1
|
|
218
|
+
# Net::IMAP::SequenceSet[UINT32_MAX, :*].cardinality #=> 2
|
|
219
|
+
# Net::IMAP::SequenceSet[UINT32_MAX..].cardinality #=> 2
|
|
220
|
+
#
|
|
139
221
|
# == What's here?
|
|
140
222
|
#
|
|
141
223
|
# SequenceSet provides methods for:
|
|
@@ -153,6 +235,7 @@ module Net
|
|
|
153
235
|
# * ::new: Creates a new mutable sequence set, which may be empty (invalid).
|
|
154
236
|
# * ::try_convert: Calls +to_sequence_set+ on an object and verifies that
|
|
155
237
|
# the result is a SequenceSet.
|
|
238
|
+
# * Net::IMAP::SequenceSet(): Coerce an input using ::try_convert or ::new.
|
|
156
239
|
# * ::empty: Returns a frozen empty (invalid) SequenceSet.
|
|
157
240
|
# * ::full: Returns a frozen SequenceSet containing every possible number.
|
|
158
241
|
#
|
|
@@ -178,14 +261,13 @@ module Net
|
|
|
178
261
|
#
|
|
179
262
|
# <i>Set membership:</i>
|
|
180
263
|
# - #include? (aliased as #member?):
|
|
181
|
-
# Returns whether a given element
|
|
182
|
-
# contained by the set.
|
|
264
|
+
# Returns whether a given element is contained by the set.
|
|
183
265
|
# - #include_star?: Returns whether the set contains <tt>*</tt>.
|
|
184
266
|
#
|
|
185
267
|
# <i>Minimum and maximum value elements:</i>
|
|
186
|
-
# - #min: Returns the
|
|
187
|
-
# - #max: Returns the
|
|
188
|
-
# - #minmax: Returns the
|
|
268
|
+
# - #min: Returns one or more of the lowest numbers in the set.
|
|
269
|
+
# - #max: Returns one or more of the highest numbers in the set.
|
|
270
|
+
# - #minmax: Returns the lowest and highest numbers in the set.
|
|
189
271
|
#
|
|
190
272
|
# <i>Accessing value by offset in sorted set:</i>
|
|
191
273
|
# - #[] (aliased as #slice): Returns the number or consecutive subset at a
|
|
@@ -199,8 +281,10 @@ module Net
|
|
|
199
281
|
# occurrence in entries.
|
|
200
282
|
#
|
|
201
283
|
# <i>Set cardinality:</i>
|
|
202
|
-
# - #
|
|
203
|
-
#
|
|
284
|
+
# - #cardinality: Returns the number of distinct members in the set.
|
|
285
|
+
# <tt>*</tt> is counted as its own member, distinct from UINT32_MAX.
|
|
286
|
+
# - #count: Returns the count of distinct numbers in the set.
|
|
287
|
+
# <tt>*</tt> is counted as equal to UINT32_MAX.
|
|
204
288
|
# - #empty?: Returns whether the set has no members. \IMAP syntax does not
|
|
205
289
|
# allow empty sequence sets.
|
|
206
290
|
# - #valid?: Returns whether the set has any members.
|
|
@@ -208,12 +292,18 @@ module Net
|
|
|
208
292
|
# <tt>*</tt>.
|
|
209
293
|
#
|
|
210
294
|
# <i>Denormalized properties:</i>
|
|
295
|
+
# - #normalized?: Returns whether #entries are sorted, deduplicated, and
|
|
296
|
+
# coalesced, and all #string entries are in normalized form.
|
|
211
297
|
# - #has_duplicates?: Returns whether the ordered entries repeat any
|
|
212
298
|
# numbers.
|
|
213
|
-
# - #
|
|
214
|
-
#
|
|
299
|
+
# - #size: Returns the total size of all #entries, including repeated
|
|
300
|
+
# numbers. <tt>*</tt> is counted as its own member, distinct from
|
|
301
|
+
# UINT32_MAX.
|
|
215
302
|
# - #count_with_duplicates: Returns the count of numbers in the ordered
|
|
216
|
-
# entries, including
|
|
303
|
+
# #entries, including repeated numbers. <tt>*</tt> is counted as
|
|
304
|
+
# equal to UINT32_MAX.
|
|
305
|
+
# - #count_duplicates: Returns the count of repeated numbers in the ordered
|
|
306
|
+
# #entries. <tt>*</tt> is counted as equal to UINT32_MAX.
|
|
217
307
|
#
|
|
218
308
|
# === Methods for Iterating
|
|
219
309
|
#
|
|
@@ -252,11 +342,15 @@ module Net
|
|
|
252
342
|
# +self+ and the other set except those common to both.
|
|
253
343
|
# - #~ (aliased as #complement): Returns a new set containing all members
|
|
254
344
|
# that are not in +self+
|
|
345
|
+
# - #above: Return a copy of +self+ which only contains numbers above a
|
|
346
|
+
# given number.
|
|
347
|
+
# - #below: Return a copy of +self+ which only contains numbers below a
|
|
348
|
+
# given value.
|
|
255
349
|
# - #limit: Returns a copy of +self+ which has replaced <tt>*</tt> with a
|
|
256
350
|
# given maximum value and removed all members over that maximum.
|
|
257
351
|
#
|
|
258
352
|
# === Methods for Assigning
|
|
259
|
-
# These methods add or replace
|
|
353
|
+
# These methods add or replace numbers in +self+.
|
|
260
354
|
#
|
|
261
355
|
# <i>Normalized (sorted and coalesced):</i>
|
|
262
356
|
#
|
|
@@ -265,8 +359,12 @@ module Net
|
|
|
265
359
|
# - #add (aliased as #<<): Adds a given element to the set; returns +self+.
|
|
266
360
|
# - #add?: If the given element is not fully included the set, adds it and
|
|
267
361
|
# returns +self+; otherwise, returns +nil+.
|
|
268
|
-
# - #merge: Adds all members of the given sets into
|
|
269
|
-
#
|
|
362
|
+
# - #merge: In-place set #union. Adds all members of the given sets into
|
|
363
|
+
# this set; returns +self+.
|
|
364
|
+
# - #complement!: In-place set #complement. Replaces the contents of this
|
|
365
|
+
# set with its own #complement; returns +self+.
|
|
366
|
+
# - #xor!: In-place +XOR+ operation. Adds numbers that are unique to the
|
|
367
|
+
# other set and removes numbers that are common to both; returns +self+.
|
|
270
368
|
#
|
|
271
369
|
# <i>Order preserving:</i>
|
|
272
370
|
#
|
|
@@ -279,7 +377,7 @@ module Net
|
|
|
279
377
|
# of a given object.
|
|
280
378
|
#
|
|
281
379
|
# === Methods for Deleting
|
|
282
|
-
# These methods remove
|
|
380
|
+
# These methods remove numbers from +self+, and update #string to be fully
|
|
283
381
|
# sorted and coalesced.
|
|
284
382
|
#
|
|
285
383
|
# - #clear: Removes all elements in the set; returns +self+.
|
|
@@ -287,10 +385,12 @@ module Net
|
|
|
287
385
|
# - #delete?: If the given element is included in the set, removes it and
|
|
288
386
|
# returns it; otherwise, returns +nil+.
|
|
289
387
|
# - #delete_at: Removes the number at a given offset.
|
|
388
|
+
# - #intersect!: In-place set #intersection. Removes numbers that are not
|
|
389
|
+
# in the given set; returns +self+.
|
|
290
390
|
# - #slice!: Removes the number or consecutive numbers at a given offset or
|
|
291
391
|
# range of offsets.
|
|
292
|
-
# - #subtract: Removes all members of the given
|
|
293
|
-
# +self+.
|
|
392
|
+
# - #subtract: In-place set #difference. Removes all members of the given
|
|
393
|
+
# sets from this set; returns +self+.
|
|
294
394
|
# - #limit!: Replaces <tt>*</tt> with a given maximum value and removes all
|
|
295
395
|
# members over that maximum; returns +self+.
|
|
296
396
|
#
|
|
@@ -318,11 +418,24 @@ module Net
|
|
|
318
418
|
|
|
319
419
|
# valid inputs for "*"
|
|
320
420
|
STARS = [:*, ?*, -1].freeze
|
|
321
|
-
private_constant :
|
|
421
|
+
private_constant :STARS
|
|
322
422
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
private_constant :
|
|
423
|
+
INSPECT_MAX_LEN = 512
|
|
424
|
+
INSPECT_TRUNCATE_LEN = 16
|
|
425
|
+
private_constant :INSPECT_MAX_LEN, :INSPECT_TRUNCATE_LEN
|
|
426
|
+
|
|
427
|
+
# /(,\d+){100}\z/ is shockingly slow on huge strings.
|
|
428
|
+
# /(,\d{0,10}){100}\z/ is ok, but ironically, Regexp.linear_time? is false.
|
|
429
|
+
#
|
|
430
|
+
# This unrolls all nested quantifiers. It's much harder to read, but it's
|
|
431
|
+
# also the fastest out of all the versions I tested.
|
|
432
|
+
nz_uint32 = /[1-9](?:\d(?:\d(?:\d(?:\d(?:\d(?:\d(?:\d(?:\d(?:\d)?)?)?)?)?)?)?)?)?/
|
|
433
|
+
num_or_star = /#{nz_uint32}|\*/
|
|
434
|
+
entry = /#{num_or_star}(?::#{num_or_star})?/
|
|
435
|
+
entries = ([entry] * INSPECT_TRUNCATE_LEN).join(",")
|
|
436
|
+
INSPECT_ABRIDGED_HEAD_RE = /\A#{entries},/
|
|
437
|
+
INSPECT_ABRIDGED_TAIL_RE = /,#{entries}\z/
|
|
438
|
+
private_constant :INSPECT_ABRIDGED_HEAD_RE, :INSPECT_ABRIDGED_TAIL_RE
|
|
326
439
|
|
|
327
440
|
class << self
|
|
328
441
|
|
|
@@ -337,13 +450,12 @@ module Net
|
|
|
337
450
|
# An empty SequenceSet is invalid and will raise a DataFormatError.
|
|
338
451
|
#
|
|
339
452
|
# Use ::new to create a mutable or empty SequenceSet.
|
|
453
|
+
#
|
|
454
|
+
# Related: ::new, Net::IMAP::SequenceSet(), ::try_convert
|
|
340
455
|
def [](first, *rest)
|
|
341
456
|
if rest.empty?
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
else
|
|
345
|
-
new(first).validate.freeze
|
|
346
|
-
end
|
|
457
|
+
set = try_convert(first)&.validate
|
|
458
|
+
set&.frozen? ? set : (set&.dup || new(first).validate).freeze
|
|
347
459
|
else
|
|
348
460
|
new(first).merge(*rest).validate.freeze
|
|
349
461
|
end
|
|
@@ -356,12 +468,14 @@ module Net
|
|
|
356
468
|
# +to_sequence_set+, calls +obj.to_sequence_set+ and returns the result.
|
|
357
469
|
# Otherwise returns +nil+.
|
|
358
470
|
#
|
|
359
|
-
# If +obj.to_sequence_set+ doesn't return a SequenceSet
|
|
360
|
-
# raised.
|
|
471
|
+
# If +obj.to_sequence_set+ doesn't return a SequenceSet or +nil+, an
|
|
472
|
+
# exception is raised.
|
|
473
|
+
#
|
|
474
|
+
# Related: Net::IMAP::SequenceSet(), ::new, ::[]
|
|
361
475
|
def try_convert(obj)
|
|
362
476
|
return obj if obj.is_a?(SequenceSet)
|
|
363
477
|
return nil unless obj.respond_to?(:to_sequence_set)
|
|
364
|
-
obj = obj.to_sequence_set
|
|
478
|
+
return nil unless obj = obj.to_sequence_set
|
|
365
479
|
return obj if obj.is_a?(SequenceSet)
|
|
366
480
|
raise DataFormatError, "invalid object returned from to_sequence_set"
|
|
367
481
|
end
|
|
@@ -376,23 +490,96 @@ module Net
|
|
|
376
490
|
end
|
|
377
491
|
|
|
378
492
|
# Create a new SequenceSet object from +input+, which may be another
|
|
379
|
-
# SequenceSet, an IMAP formatted +sequence-set+ string, a
|
|
380
|
-
# range, <tt>:*</tt>,
|
|
381
|
-
#
|
|
382
|
-
#
|
|
383
|
-
|
|
493
|
+
# SequenceSet, an IMAP formatted +sequence-set+ string, a non-zero 32 bit
|
|
494
|
+
# unsigned integer, a range, <tt>:*</tt>, a Set of numbers or <tt>*</tt>,
|
|
495
|
+
# an object that responds to +to_sequence_set+ (such as SearchResult) or
|
|
496
|
+
# an Array of these (array inputs may be nested).
|
|
497
|
+
#
|
|
498
|
+
# set = Net::IMAP::SequenceSet.new(1)
|
|
499
|
+
# set.valid_string #=> "1"
|
|
500
|
+
# set = Net::IMAP::SequenceSet.new(1..100)
|
|
501
|
+
# set.valid_string #=> "1:100"
|
|
502
|
+
# set = Net::IMAP::SequenceSet.new(1...100)
|
|
503
|
+
# set.valid_string #=> "1:99"
|
|
504
|
+
# set = Net::IMAP::SequenceSet.new([1, 2, 5..])
|
|
505
|
+
# set.valid_string #=> "1:2,5:*"
|
|
506
|
+
# set = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
|
|
507
|
+
# set.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
|
|
508
|
+
# set = Net::IMAP::SequenceSet.new(1, 2, 3..7, 5, 6..10, 2048, 1024)
|
|
509
|
+
# set.valid_string #=> "1:10,1024,2048"
|
|
510
|
+
#
|
|
511
|
+
# With no arguments (or +nil+) creates an empty sequence set. Note that
|
|
512
|
+
# an empty sequence set is invalid in the \IMAP grammar.
|
|
513
|
+
#
|
|
514
|
+
# set = Net::IMAP::SequenceSet.new
|
|
515
|
+
# set.empty? #=> true
|
|
516
|
+
# set.valid? #=> false
|
|
517
|
+
# set.valid_string #!> raises DataFormatError
|
|
518
|
+
# set << 1..10
|
|
519
|
+
# set.empty? #=> false
|
|
520
|
+
# set.valid? #=> true
|
|
521
|
+
# set.valid_string #=> "1:10"
|
|
522
|
+
#
|
|
523
|
+
# When +input+ is a SequenceSet, ::new behaves the same as calling #dup on
|
|
524
|
+
# that other set. The input's #string will be preserved.
|
|
525
|
+
#
|
|
526
|
+
# input = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
|
|
527
|
+
# copy = Net::IMAP::SequenceSet.new(input)
|
|
528
|
+
# input.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
|
|
529
|
+
# copy.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
|
|
530
|
+
# copy2 = input.dup # same as calling new with a SequenceSet input
|
|
531
|
+
# copy == input #=> true, same set membership
|
|
532
|
+
# copy.eql? input #=> true, same string value
|
|
533
|
+
# copy.equal? input #=> false, different objects
|
|
534
|
+
#
|
|
535
|
+
# copy.normalize!
|
|
536
|
+
# copy.valid_string #=> "1:10,1024,2048"
|
|
537
|
+
# copy == input #=> true, same set membership
|
|
538
|
+
# copy.eql? input #=> false, different string value
|
|
539
|
+
#
|
|
540
|
+
# copy << 999
|
|
541
|
+
# copy.valid_string #=> "1:10,999,1024,2048"
|
|
542
|
+
# copy == input #=> false, different set membership
|
|
543
|
+
# copy.eql? input #=> false, different string value
|
|
544
|
+
#
|
|
545
|
+
# === Alternative set creation methods
|
|
546
|
+
#
|
|
547
|
+
# * ::[] returns a frozen validated (non-empty) SequenceSet, without
|
|
548
|
+
# allocating a new object when the input is already a valid frozen
|
|
549
|
+
# SequenceSet.
|
|
550
|
+
# * Net::IMAP::SequenceSet() coerces an input to SequenceSet, without
|
|
551
|
+
# allocating a new object when the input is already a SequenceSet.
|
|
552
|
+
# * ::try_convert calls +to_sequence_set+ on inputs that support it and
|
|
553
|
+
# returns +nil+ for inputs that don't.
|
|
554
|
+
# * ::empty and ::full both return frozen singleton sets which can be
|
|
555
|
+
# combined with set operations (#|, #&, #^, #-, etc) to make new sets.
|
|
556
|
+
#
|
|
557
|
+
# See SequenceSet@Creating+sequence+sets.
|
|
558
|
+
def initialize(input = nil)
|
|
559
|
+
@set_data = new_set_data
|
|
560
|
+
@string = nil
|
|
561
|
+
replace(input) unless input.nil?
|
|
562
|
+
end
|
|
384
563
|
|
|
385
564
|
# Removes all elements and returns self.
|
|
386
|
-
def clear
|
|
565
|
+
def clear
|
|
566
|
+
modifying! # redundant check (normalizes the error message for JRuby)
|
|
567
|
+
set_data.clear
|
|
568
|
+
@string = nil
|
|
569
|
+
self
|
|
570
|
+
end
|
|
387
571
|
|
|
388
572
|
# Replace the contents of the set with the contents of +other+ and returns
|
|
389
573
|
# +self+.
|
|
390
574
|
#
|
|
391
|
-
# +other+ may be another SequenceSet
|
|
392
|
-
#
|
|
575
|
+
# +other+ may be another SequenceSet or any other object that would be
|
|
576
|
+
# accepted by ::new.
|
|
393
577
|
def replace(other)
|
|
394
578
|
case other
|
|
395
|
-
when SequenceSet then
|
|
579
|
+
when SequenceSet then
|
|
580
|
+
modifying! # short circuit before doing any work
|
|
581
|
+
@set_data = other.dup_set_data
|
|
582
|
+
@string = other.instance_variable_get(:@string)
|
|
396
583
|
when String then self.string = other
|
|
397
584
|
else clear; merge other
|
|
398
585
|
end
|
|
@@ -421,39 +608,51 @@ module Net
|
|
|
421
608
|
# If the set was created from a single string, it is not normalized. If
|
|
422
609
|
# the set is updated the string will be normalized.
|
|
423
610
|
#
|
|
424
|
-
# Related: #valid_string, #normalized_string, #to_s
|
|
425
|
-
def string; @string
|
|
611
|
+
# Related: #valid_string, #normalized_string, #to_s, #inspect
|
|
612
|
+
def string; @string || normalized_string if valid? end
|
|
613
|
+
|
|
614
|
+
# Returns an array with #normalized_string when valid and an empty array
|
|
615
|
+
# otherwise.
|
|
616
|
+
def deconstruct; valid? ? [normalized_string] : [] end
|
|
426
617
|
|
|
427
|
-
# Assigns a new string to #string and resets #elements to match.
|
|
428
|
-
#
|
|
429
|
-
#
|
|
618
|
+
# Assigns a new string to #string and resets #elements to match.
|
|
619
|
+
# Assigning +nil+ or an empty string are equivalent to calling #clear.
|
|
620
|
+
#
|
|
621
|
+
# Non-empty strings are validated but not normalized.
|
|
430
622
|
#
|
|
431
|
-
# Use #add or #
|
|
623
|
+
# Use #add, #merge, or #append to add a string to an existing set.
|
|
432
624
|
#
|
|
433
625
|
# Related: #replace, #clear
|
|
434
|
-
def string=(
|
|
435
|
-
if
|
|
626
|
+
def string=(input)
|
|
627
|
+
if input.nil?
|
|
436
628
|
clear
|
|
629
|
+
elsif (str = String.try_convert(input))
|
|
630
|
+
modifying! # short-circuit before parsing the string
|
|
631
|
+
entries = each_parsed_entry(str).to_a
|
|
632
|
+
clear
|
|
633
|
+
if normalized_entries?(entries)
|
|
634
|
+
replace_minmaxes entries.map!(&:minmax)
|
|
635
|
+
else
|
|
636
|
+
add_minmaxes entries.map!(&:minmax)
|
|
637
|
+
@string = -str
|
|
638
|
+
end
|
|
437
639
|
else
|
|
438
|
-
|
|
439
|
-
tuples = str_to_tuples str
|
|
440
|
-
@tuples, @string = [], -str
|
|
441
|
-
tuples_add tuples
|
|
640
|
+
raise ArgumentError, "expected a string or nil, got #{input.class}"
|
|
442
641
|
end
|
|
642
|
+
input
|
|
443
643
|
end
|
|
444
644
|
|
|
445
645
|
# Returns the \IMAP +sequence-set+ string representation, or an empty
|
|
446
646
|
# string when the set is empty. Note that an empty set is invalid in the
|
|
447
647
|
# \IMAP syntax.
|
|
448
648
|
#
|
|
449
|
-
# Related: #valid_string, #normalized_string, #
|
|
649
|
+
# Related: #string, #valid_string, #normalized_string, #inspect
|
|
450
650
|
def to_s; string || "" end
|
|
451
651
|
|
|
452
652
|
# Freezes and returns the set. A frozen SequenceSet is Ractor-safe.
|
|
453
653
|
def freeze
|
|
454
654
|
return self if frozen?
|
|
455
|
-
|
|
456
|
-
@tuples.each(&:freeze).freeze
|
|
655
|
+
freeze_set_data
|
|
457
656
|
super
|
|
458
657
|
end
|
|
459
658
|
|
|
@@ -475,7 +674,7 @@ module Net
|
|
|
475
674
|
# Related: #eql?, #normalize
|
|
476
675
|
def ==(other)
|
|
477
676
|
self.class == other.class &&
|
|
478
|
-
(to_s == other.to_s ||
|
|
677
|
+
(to_s == other.to_s || set_data == other.set_data)
|
|
479
678
|
end
|
|
480
679
|
|
|
481
680
|
# :call-seq: eql?(other) -> true or false
|
|
@@ -499,8 +698,9 @@ module Net
|
|
|
499
698
|
|
|
500
699
|
# :call-seq: self === other -> true | false | nil
|
|
501
700
|
#
|
|
502
|
-
# Returns whether +other+ is contained within the set.
|
|
503
|
-
#
|
|
701
|
+
# Returns whether +other+ is contained within the set. +other+ may be any
|
|
702
|
+
# object that would be accepted by ::new. Returns +nil+ if StandardError
|
|
703
|
+
# is raised while converting +other+ to a comparable type.
|
|
504
704
|
#
|
|
505
705
|
# Related: #cover?, #include?, #include_star?
|
|
506
706
|
def ===(other)
|
|
@@ -514,12 +714,12 @@ module Net
|
|
|
514
714
|
# Returns whether +other+ is contained within the set. +other+ may be any
|
|
515
715
|
# object that would be accepted by ::new.
|
|
516
716
|
#
|
|
517
|
-
# Related: #===, #include?, #include_star?
|
|
518
|
-
def cover?(other)
|
|
717
|
+
# Related: #===, #include?, #include_star?, #intersect?
|
|
718
|
+
def cover?(other) import_runs(other).none? { !include_run?(_1) } end
|
|
519
719
|
|
|
520
720
|
# Returns +true+ when a given number or range is in +self+, and +false+
|
|
521
|
-
# otherwise. Returns +
|
|
522
|
-
# <tt>*</tt
|
|
721
|
+
# otherwise. Returns +nil+ when +number+ isn't a valid SequenceSet
|
|
722
|
+
# element (Integer, Range, <tt>*</tt>, +sequence-set+ string).
|
|
523
723
|
#
|
|
524
724
|
# set = Net::IMAP::SequenceSet["5:10,100,111:115"]
|
|
525
725
|
# set.include? 1 #=> false
|
|
@@ -527,8 +727,8 @@ module Net
|
|
|
527
727
|
# set.include? 11..20 #=> false
|
|
528
728
|
# set.include? 100 #=> true
|
|
529
729
|
# set.include? 6 #=> true, covered by "5:10"
|
|
530
|
-
# set.include?
|
|
531
|
-
# set.include? "
|
|
730
|
+
# set.include? 6..9 #=> true, covered by "5:10"
|
|
731
|
+
# set.include? "6:9" #=> true, strings are parsed
|
|
532
732
|
# set.include? 4..9 #=> false, intersection is not sufficient
|
|
533
733
|
# set.include? "*" #=> false, use #limit to re-interpret "*"
|
|
534
734
|
# set.include? -1 #=> false, -1 is interpreted as "*"
|
|
@@ -537,16 +737,19 @@ module Net
|
|
|
537
737
|
# set.include? :* #=> true
|
|
538
738
|
# set.include? "*" #=> true
|
|
539
739
|
# set.include? -1 #=> true
|
|
540
|
-
# set.include?
|
|
541
|
-
# set.include?
|
|
740
|
+
# set.include?(200..) #=> true
|
|
741
|
+
# set.include?(100..) #=> false
|
|
542
742
|
#
|
|
543
|
-
# Related: #include_star?, #cover?,
|
|
544
|
-
def include?(element)
|
|
743
|
+
# Related: #include_star?, #cover?, #===, #intersect?
|
|
744
|
+
def include?(element)
|
|
745
|
+
run = import_run element rescue nil
|
|
746
|
+
!!include_run?(run) if run
|
|
747
|
+
end
|
|
545
748
|
|
|
546
749
|
alias member? include?
|
|
547
750
|
|
|
548
751
|
# Returns +true+ when the set contains <tt>*</tt>.
|
|
549
|
-
def include_star?;
|
|
752
|
+
def include_star?; max_num == STAR_INT end
|
|
550
753
|
|
|
551
754
|
# Returns +true+ if the set and a given object have any common elements,
|
|
552
755
|
# +false+ otherwise.
|
|
@@ -554,9 +757,9 @@ module Net
|
|
|
554
757
|
# Net::IMAP::SequenceSet["5:10"].intersect? "7,9,11" #=> true
|
|
555
758
|
# Net::IMAP::SequenceSet["5:10"].intersect? "11:33" #=> false
|
|
556
759
|
#
|
|
557
|
-
# Related: #intersection, #disjoint?
|
|
760
|
+
# Related: #intersection, #disjoint?, #cover?, #include?
|
|
558
761
|
def intersect?(other)
|
|
559
|
-
valid? &&
|
|
762
|
+
valid? && import_runs(other).any? { intersect_run? _1 }
|
|
560
763
|
end
|
|
561
764
|
alias overlap? intersect?
|
|
562
765
|
|
|
@@ -568,39 +771,70 @@ module Net
|
|
|
568
771
|
#
|
|
569
772
|
# Related: #intersection, #intersect?
|
|
570
773
|
def disjoint?(other)
|
|
571
|
-
empty? ||
|
|
774
|
+
empty? || import_runs(other).none? { intersect_run? _1 }
|
|
572
775
|
end
|
|
573
776
|
|
|
574
|
-
# :call-seq:
|
|
777
|
+
# :call-seq:
|
|
778
|
+
# max(star: :*) => integer or star or nil
|
|
779
|
+
# max(count) => SequenceSet
|
|
575
780
|
#
|
|
576
781
|
# Returns the maximum value in +self+, +star+ when the set includes
|
|
577
782
|
# <tt>*</tt>, or +nil+ when the set is empty.
|
|
578
|
-
|
|
579
|
-
|
|
783
|
+
#
|
|
784
|
+
# When +count+ is given, a new SequenceSet is returned, containing only
|
|
785
|
+
# the last +count+ numbers. An empty SequenceSet is returned when +self+
|
|
786
|
+
# is empty. (+star+ is ignored when +count+ is given.)
|
|
787
|
+
#
|
|
788
|
+
# Related: #min, #minmax, #slice
|
|
789
|
+
def max(count = nil, star: :*)
|
|
790
|
+
if count
|
|
791
|
+
if cardinality <= count
|
|
792
|
+
frozen? ? self : dup
|
|
793
|
+
else
|
|
794
|
+
slice(-count..) || remain_frozen_empty
|
|
795
|
+
end
|
|
796
|
+
elsif (val = max_num)
|
|
797
|
+
val == STAR_INT ? star : val
|
|
798
|
+
end
|
|
580
799
|
end
|
|
581
800
|
|
|
582
|
-
# :call-seq:
|
|
801
|
+
# :call-seq:
|
|
802
|
+
# min(star: :*) => integer or star or nil
|
|
803
|
+
# min(count) => SequenceSet
|
|
583
804
|
#
|
|
584
805
|
# Returns the minimum value in +self+, +star+ when the only value in the
|
|
585
806
|
# set is <tt>*</tt>, or +nil+ when the set is empty.
|
|
586
|
-
|
|
587
|
-
|
|
807
|
+
#
|
|
808
|
+
# When +count+ is given, a new SequenceSet is returned, containing only
|
|
809
|
+
# the first +count+ numbers. An empty SequenceSet is returned when +self+
|
|
810
|
+
# is empty. (+star+ is ignored when +count+ is given.)
|
|
811
|
+
#
|
|
812
|
+
# Related: #max, #minmax, #slice
|
|
813
|
+
def min(count = nil, star: :*)
|
|
814
|
+
if count
|
|
815
|
+
slice(0...count) || remain_frozen_empty
|
|
816
|
+
elsif (val = min_num)
|
|
817
|
+
val != STAR_INT ? val : star
|
|
818
|
+
end
|
|
588
819
|
end
|
|
589
820
|
|
|
590
|
-
# :call-seq: minmax(star: :*) =>
|
|
821
|
+
# :call-seq: minmax(star: :*) => [min, max] or nil
|
|
591
822
|
#
|
|
592
823
|
# Returns a 2-element array containing the minimum and maximum numbers in
|
|
593
|
-
# +self+, or +nil+ when the set is empty.
|
|
824
|
+
# +self+, or +nil+ when the set is empty. +star+ is handled the same way
|
|
825
|
+
# as by #min and #max.
|
|
826
|
+
#
|
|
827
|
+
# Related: #min, #max
|
|
594
828
|
def minmax(star: :*); [min(star: star), max(star: star)] unless empty? end
|
|
595
829
|
|
|
596
830
|
# Returns false when the set is empty.
|
|
597
831
|
def valid?; !empty? end
|
|
598
832
|
|
|
599
833
|
# Returns true if the set contains no elements
|
|
600
|
-
def empty?;
|
|
834
|
+
def empty?; runs.empty? end
|
|
601
835
|
|
|
602
836
|
# Returns true if the set contains every possible element.
|
|
603
|
-
def full?;
|
|
837
|
+
def full?; set_data == FULL_SET_DATA end
|
|
604
838
|
|
|
605
839
|
# :call-seq:
|
|
606
840
|
# self + other -> sequence set
|
|
@@ -610,14 +844,19 @@ module Net
|
|
|
610
844
|
# Returns a new sequence set that has every number in the +other+ object
|
|
611
845
|
# added.
|
|
612
846
|
#
|
|
613
|
-
# +other+ may be any object that would be accepted by ::new
|
|
614
|
-
# bit unsigned integer, range, <tt>sequence-set</tt> formatted string,
|
|
615
|
-
# another sequence set, or an enumerable containing any of these.
|
|
847
|
+
# +other+ may be any object that would be accepted by ::new.
|
|
616
848
|
#
|
|
617
849
|
# Net::IMAP::SequenceSet["1:5"] | 2 | [4..6, 99]
|
|
618
850
|
# #=> Net::IMAP::SequenceSet["1:6,99"]
|
|
619
851
|
#
|
|
620
|
-
# Related: #add, #merge
|
|
852
|
+
# Related: #add, #merge, #&, #-, #^, #~
|
|
853
|
+
#
|
|
854
|
+
# ==== Set identities
|
|
855
|
+
#
|
|
856
|
+
# <tt>lhs | rhs</tt> is equivalent to:
|
|
857
|
+
# * <tt>rhs | lhs</tt> (commutative)
|
|
858
|
+
# * <tt>~(~lhs & ~rhs)</tt> (De Morgan's Law)
|
|
859
|
+
# * <tt>(lhs & rhs) ^ (lhs ^ rhs)</tt>
|
|
621
860
|
def |(other) remain_frozen dup.merge other end
|
|
622
861
|
alias :+ :|
|
|
623
862
|
alias union :|
|
|
@@ -629,14 +868,22 @@ module Net
|
|
|
629
868
|
# Returns a new sequence set built by duplicating this set and removing
|
|
630
869
|
# every number that appears in +other+.
|
|
631
870
|
#
|
|
632
|
-
# +other+ may be any object that would be accepted by ::new
|
|
633
|
-
# bit unsigned integer, range, <tt>sequence-set</tt> formatted string,
|
|
634
|
-
# another sequence set, or an enumerable containing any of these.
|
|
871
|
+
# +other+ may be any object that would be accepted by ::new.
|
|
635
872
|
#
|
|
636
873
|
# Net::IMAP::SequenceSet[1..5] - 2 - 4 - 6
|
|
637
874
|
# #=> Net::IMAP::SequenceSet["1,3,5"]
|
|
638
875
|
#
|
|
639
|
-
# Related: #subtract
|
|
876
|
+
# Related: #subtract, #|, #&, #^, #~
|
|
877
|
+
#
|
|
878
|
+
# ==== Set identities
|
|
879
|
+
#
|
|
880
|
+
# <tt>lhs - rhs</tt> is equivalent to:
|
|
881
|
+
# * <tt>~rhs - ~lhs</tt>
|
|
882
|
+
# * <tt>lhs & ~rhs</tt>
|
|
883
|
+
# * <tt>~(~lhs | rhs)</tt>
|
|
884
|
+
# * <tt>lhs & (lhs ^ rhs)</tt>
|
|
885
|
+
# * <tt>lhs ^ (lhs & rhs)</tt>
|
|
886
|
+
# * <tt>rhs ^ (lhs | rhs)</tt>
|
|
640
887
|
def -(other) remain_frozen dup.subtract other end
|
|
641
888
|
alias difference :-
|
|
642
889
|
|
|
@@ -647,17 +894,23 @@ module Net
|
|
|
647
894
|
# Returns a new sequence set containing only the numbers common to this
|
|
648
895
|
# set and +other+.
|
|
649
896
|
#
|
|
650
|
-
# +other+ may be any object that would be accepted by ::new
|
|
651
|
-
# bit unsigned integer, range, <tt>sequence-set</tt> formatted string,
|
|
652
|
-
# another sequence set, or an enumerable containing any of these.
|
|
897
|
+
# +other+ may be any object that would be accepted by ::new.
|
|
653
898
|
#
|
|
654
899
|
# Net::IMAP::SequenceSet[1..5] & [2, 4, 6]
|
|
655
900
|
# #=> Net::IMAP::SequenceSet["2,4"]
|
|
656
901
|
#
|
|
657
|
-
#
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
902
|
+
# Related: #intersect?, #|, #-, #^, #~
|
|
903
|
+
#
|
|
904
|
+
# ==== Set identities
|
|
905
|
+
#
|
|
906
|
+
# <tt>lhs & rhs</tt> is equivalent to:
|
|
907
|
+
# * <tt>rhs & lhs</tt> (commutative)
|
|
908
|
+
# * <tt>~(~lhs | ~rhs)</tt> (De Morgan's Law)
|
|
909
|
+
# * <tt>lhs - ~rhs</tt>
|
|
910
|
+
# * <tt>lhs - (lhs - rhs)</tt>
|
|
911
|
+
# * <tt>lhs - (lhs ^ rhs)</tt>
|
|
912
|
+
# * <tt>lhs ^ (lhs - rhs)</tt>
|
|
913
|
+
def &(other) remain_frozen dup.intersect! other end
|
|
661
914
|
alias intersection :&
|
|
662
915
|
|
|
663
916
|
# :call-seq:
|
|
@@ -667,16 +920,22 @@ module Net
|
|
|
667
920
|
# Returns a new sequence set containing numbers that are exclusive between
|
|
668
921
|
# this set and +other+.
|
|
669
922
|
#
|
|
670
|
-
# +other+ may be any object that would be accepted by ::new
|
|
671
|
-
# bit unsigned integer, range, <tt>sequence-set</tt> formatted string,
|
|
672
|
-
# another sequence set, or an enumerable containing any of these.
|
|
923
|
+
# +other+ may be any object that would be accepted by ::new.
|
|
673
924
|
#
|
|
674
925
|
# Net::IMAP::SequenceSet[1..5] ^ [2, 4, 6]
|
|
675
926
|
# #=> Net::IMAP::SequenceSet["1,3,5:6"]
|
|
676
927
|
#
|
|
677
|
-
#
|
|
678
|
-
#
|
|
679
|
-
|
|
928
|
+
# Related: #|, #&, #-, #~
|
|
929
|
+
#
|
|
930
|
+
# ==== Set identities
|
|
931
|
+
#
|
|
932
|
+
# <tt>lhs ^ rhs</tt> is equivalent to:
|
|
933
|
+
# * <tt>rhs ^ lhs</tt> (commutative)
|
|
934
|
+
# * <tt>~lhs ^ ~rhs</tt>
|
|
935
|
+
# * <tt>(lhs | rhs) - (lhs & rhs)</tt>
|
|
936
|
+
# * <tt>(lhs - rhs) | (rhs - lhs)</tt>
|
|
937
|
+
# * <tt>(lhs ^ other) ^ (other ^ rhs)</tt>
|
|
938
|
+
def ^(other) remain_frozen dup.xor! other end
|
|
680
939
|
alias xor :^
|
|
681
940
|
|
|
682
941
|
# :call-seq:
|
|
@@ -693,7 +952,12 @@ module Net
|
|
|
693
952
|
# ~Net::IMAP::SequenceSet["6:99,223:*"]
|
|
694
953
|
# #=> Net::IMAP::SequenceSet["1:5,100:222"]
|
|
695
954
|
#
|
|
696
|
-
# Related: #complement
|
|
955
|
+
# Related: #complement!, #|, #&, #-, #^
|
|
956
|
+
#
|
|
957
|
+
# ==== Set identities
|
|
958
|
+
#
|
|
959
|
+
# <tt>~set</tt> is equivalent to:
|
|
960
|
+
# * <tt>full - set</tt>, where "full" is Net::IMAP::SequenceSet.full
|
|
697
961
|
def ~; remain_frozen dup.complement! end
|
|
698
962
|
alias complement :~
|
|
699
963
|
|
|
@@ -705,9 +969,13 @@ module Net
|
|
|
705
969
|
#
|
|
706
970
|
# #string will be regenerated. Use #merge to add many elements at once.
|
|
707
971
|
#
|
|
708
|
-
#
|
|
972
|
+
# Use #append to append new elements to #string. See
|
|
973
|
+
# SequenceSet@Ordered+and+Normalized+sets.
|
|
974
|
+
#
|
|
975
|
+
# Related: #add?, #merge, #union, #append
|
|
709
976
|
def add(element)
|
|
710
|
-
|
|
977
|
+
modifying! # short-circuit before import_run
|
|
978
|
+
add_run import_run element
|
|
711
979
|
normalize!
|
|
712
980
|
end
|
|
713
981
|
alias << add
|
|
@@ -716,13 +984,56 @@ module Net
|
|
|
716
984
|
#
|
|
717
985
|
# Unlike #add, #merge, or #union, the new value is appended to #string.
|
|
718
986
|
# This may result in a #string which has duplicates or is out-of-order.
|
|
987
|
+
#
|
|
988
|
+
# set = Net::IMAP::SequenceSet.new
|
|
989
|
+
# set.append(1..2) # => Net::IMAP::SequenceSet("1:2")
|
|
990
|
+
# set.append(5) # => Net::IMAP::SequenceSet("1:2,5")
|
|
991
|
+
# set.append(4) # => Net::IMAP::SequenceSet("1:2,5,4")
|
|
992
|
+
# set.append(3) # => Net::IMAP::SequenceSet("1:2,5,4,3")
|
|
993
|
+
# set.append(2) # => Net::IMAP::SequenceSet("1:2,5,4,3,2")
|
|
994
|
+
#
|
|
995
|
+
# If +entry+ is a string, it will be converted into normal form.
|
|
996
|
+
#
|
|
997
|
+
# set = Net::IMAP::SequenceSet("4:5,1:2")
|
|
998
|
+
# set.append("6:6") # => Net::IMAP::SequenceSet("4:5,1:2,6")
|
|
999
|
+
# set.append("9:8") # => Net::IMAP::SequenceSet("4:5,1:2,6,8:9")
|
|
1000
|
+
#
|
|
1001
|
+
# If +entry+ adjacently follows the last entry, they will coalesced:
|
|
1002
|
+
# set = Net::IMAP::SequenceSet.new("2,1,9:10")
|
|
1003
|
+
# set.append(11..12) # => Net::IMAP::SequenceSet("2,1,9:12")
|
|
1004
|
+
#
|
|
1005
|
+
# Non-normalized sets store the string <em>in addition to</em> an internal
|
|
1006
|
+
# normalized uint32 set representation. This can more than double memory
|
|
1007
|
+
# usage, so large sets should avoid using #append unless preserving order
|
|
1008
|
+
# is required. See SequenceSet@Ordered+and+Normalized+sets.
|
|
1009
|
+
#
|
|
1010
|
+
# Related: #add, #merge, #union
|
|
719
1011
|
def append(entry)
|
|
720
|
-
modifying!
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
string
|
|
724
|
-
|
|
725
|
-
|
|
1012
|
+
modifying! # short-circuit before import_minmax
|
|
1013
|
+
minmax = import_minmax entry
|
|
1014
|
+
adj = minmax.first - 1
|
|
1015
|
+
if @string.nil? && (runs.empty? || max_num <= adj)
|
|
1016
|
+
# append to elements or coalesce with last element
|
|
1017
|
+
add_minmax minmax
|
|
1018
|
+
return self
|
|
1019
|
+
elsif @string.nil?
|
|
1020
|
+
# generate string for out-of-order append
|
|
1021
|
+
head, comma = normalized_string, ","
|
|
1022
|
+
else
|
|
1023
|
+
# @string already exists... maybe coalesce with last entry
|
|
1024
|
+
head, comma, last_entry = @string.rpartition(",")
|
|
1025
|
+
last_min, last_max = import_minmax last_entry
|
|
1026
|
+
if last_max == adj
|
|
1027
|
+
# coalesce with last entry
|
|
1028
|
+
minmax[0] = last_min
|
|
1029
|
+
else
|
|
1030
|
+
# append to existing string
|
|
1031
|
+
head, comma = @string, ","
|
|
1032
|
+
end
|
|
1033
|
+
end
|
|
1034
|
+
entry = export_minmax minmax
|
|
1035
|
+
add_minmax minmax
|
|
1036
|
+
@string = -"#{head}#{comma}#{entry}"
|
|
726
1037
|
self
|
|
727
1038
|
end
|
|
728
1039
|
|
|
@@ -735,6 +1046,7 @@ module Net
|
|
|
735
1046
|
#
|
|
736
1047
|
# Related: #add, #merge, #union, #include?
|
|
737
1048
|
def add?(element)
|
|
1049
|
+
modifying! # short-circuit before include?
|
|
738
1050
|
add element unless include? element
|
|
739
1051
|
end
|
|
740
1052
|
|
|
@@ -747,7 +1059,8 @@ module Net
|
|
|
747
1059
|
#
|
|
748
1060
|
# Related: #delete?, #delete_at, #subtract, #difference
|
|
749
1061
|
def delete(element)
|
|
750
|
-
|
|
1062
|
+
modifying! # short-circuit before import_run
|
|
1063
|
+
subtract_run import_run element
|
|
751
1064
|
normalize!
|
|
752
1065
|
end
|
|
753
1066
|
|
|
@@ -784,15 +1097,17 @@ module Net
|
|
|
784
1097
|
#
|
|
785
1098
|
# Related: #delete, #delete_at, #subtract, #difference, #disjoint?
|
|
786
1099
|
def delete?(element)
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
1100
|
+
modifying! # short-circuit before import_minmax
|
|
1101
|
+
element = input_try_convert(element)
|
|
1102
|
+
minmax = import_minmax element
|
|
1103
|
+
if number_input?(element)
|
|
1104
|
+
return unless include_minmax? minmax
|
|
1105
|
+
subtract_minmax minmax
|
|
791
1106
|
normalize!
|
|
792
|
-
|
|
1107
|
+
export_num minmax.first
|
|
793
1108
|
else
|
|
794
1109
|
copy = dup
|
|
795
|
-
|
|
1110
|
+
subtract_minmax minmax
|
|
796
1111
|
normalize!
|
|
797
1112
|
copy if copy.subtract(self).valid?
|
|
798
1113
|
end
|
|
@@ -824,35 +1139,34 @@ module Net
|
|
|
824
1139
|
#
|
|
825
1140
|
# Related: #slice, #delete_at, #delete, #delete?, #subtract, #difference
|
|
826
1141
|
def slice!(index, length = nil)
|
|
1142
|
+
modifying! # short-circuit before slice
|
|
827
1143
|
deleted = slice(index, length) and subtract deleted
|
|
828
1144
|
deleted
|
|
829
1145
|
end
|
|
830
1146
|
|
|
831
|
-
# Merges all of the elements that appear in any of
|
|
832
|
-
# set, and returns +self+.
|
|
1147
|
+
# In-place set #union. Merges all of the elements that appear in any of
|
|
1148
|
+
# the +sets+ into this set, and returns +self+.
|
|
833
1149
|
#
|
|
834
|
-
# The +sets+ may be any objects that would be accepted by ::new
|
|
835
|
-
# 32 bit unsigned integers, ranges, <tt>sequence-set</tt> formatted
|
|
836
|
-
# strings, other sequence sets, or enumerables containing any of these.
|
|
1150
|
+
# The +sets+ may be any objects that would be accepted by ::new.
|
|
837
1151
|
#
|
|
838
1152
|
# #string will be regenerated after all sets have been merged.
|
|
839
1153
|
#
|
|
840
1154
|
# Related: #add, #add?, #union
|
|
841
1155
|
def merge(*sets)
|
|
842
|
-
|
|
1156
|
+
modifying! # short-circuit before import_runs
|
|
1157
|
+
add_runs import_runs sets
|
|
843
1158
|
normalize!
|
|
844
1159
|
end
|
|
845
1160
|
|
|
846
|
-
# Removes all of the elements that appear in
|
|
847
|
-
# the set, and returns +self+.
|
|
1161
|
+
# In-place set #difference. Removes all of the elements that appear in
|
|
1162
|
+
# any of the given +sets+ from this set, and returns +self+.
|
|
848
1163
|
#
|
|
849
|
-
# The +sets+ may be any objects that would be accepted by ::new
|
|
850
|
-
# 32 bit unsigned integers, ranges, <tt>sequence-set</tt> formatted
|
|
851
|
-
# strings, other sequence sets, or enumerables containing any of these.
|
|
1164
|
+
# The +sets+ may be any objects that would be accepted by ::new.
|
|
852
1165
|
#
|
|
853
1166
|
# Related: #difference
|
|
854
1167
|
def subtract(*sets)
|
|
855
|
-
|
|
1168
|
+
modifying! # short-circuit before import_runs
|
|
1169
|
+
subtract_runs import_runs sets
|
|
856
1170
|
normalize!
|
|
857
1171
|
end
|
|
858
1172
|
|
|
@@ -864,21 +1178,21 @@ module Net
|
|
|
864
1178
|
# This is useful when the given order is significant, for example in a
|
|
865
1179
|
# ESEARCH response to IMAP#sort.
|
|
866
1180
|
#
|
|
1181
|
+
# See SequenceSet@Ordered+and+Normalized+sets.
|
|
1182
|
+
#
|
|
867
1183
|
# Related: #each_entry, #elements
|
|
868
1184
|
def entries; each_entry.to_a end
|
|
869
1185
|
|
|
870
1186
|
# Returns an array of ranges and integers and <tt>:*</tt>.
|
|
871
1187
|
#
|
|
872
1188
|
# The returned elements are sorted and coalesced, even when the input
|
|
873
|
-
# #string is not. <tt>*</tt> will sort last. See #normalize
|
|
1189
|
+
# #string is not. <tt>*</tt> will sort last. See #normalize,
|
|
1190
|
+
# SequenceSet@Ordered+and+Normalized+sets.
|
|
874
1191
|
#
|
|
875
1192
|
# By itself, <tt>*</tt> translates to <tt>:*</tt>. A range containing
|
|
876
1193
|
# <tt>*</tt> translates to an endless range. Use #limit to translate both
|
|
877
1194
|
# cases to a maximum value.
|
|
878
1195
|
#
|
|
879
|
-
# The returned elements will be sorted and coalesced, even when the input
|
|
880
|
-
# #string is not. <tt>*</tt> will sort last. See #normalize.
|
|
881
|
-
#
|
|
882
1196
|
# Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].elements
|
|
883
1197
|
# #=> [2, 5..9, 11..12, :*]
|
|
884
1198
|
#
|
|
@@ -889,15 +1203,13 @@ module Net
|
|
|
889
1203
|
# Returns an array of ranges
|
|
890
1204
|
#
|
|
891
1205
|
# The returned elements are sorted and coalesced, even when the input
|
|
892
|
-
# #string is not. <tt>*</tt> will sort last. See #normalize
|
|
1206
|
+
# #string is not. <tt>*</tt> will sort last. See #normalize,
|
|
1207
|
+
# SequenceSet@Ordered+and+Normalized+sets.
|
|
893
1208
|
#
|
|
894
1209
|
# <tt>*</tt> translates to an endless range. By itself, <tt>*</tt>
|
|
895
1210
|
# translates to <tt>:*..</tt>. Use #limit to set <tt>*</tt> to a maximum
|
|
896
1211
|
# value.
|
|
897
1212
|
#
|
|
898
|
-
# The returned ranges will be sorted and coalesced, even when the input
|
|
899
|
-
# #string is not. <tt>*</tt> will sort last. See #normalize.
|
|
900
|
-
#
|
|
901
1213
|
# Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].ranges
|
|
902
1214
|
# #=> [2..2, 5..9, 11..12, :*..]
|
|
903
1215
|
# Net::IMAP::SequenceSet["123,999:*,456:789"].ranges
|
|
@@ -909,7 +1221,7 @@ module Net
|
|
|
909
1221
|
# Returns a sorted array of all of the number values in the sequence set.
|
|
910
1222
|
#
|
|
911
1223
|
# The returned numbers are sorted and de-duplicated, even when the input
|
|
912
|
-
# #string is not. See #normalize.
|
|
1224
|
+
# #string is not. See #normalize, SequenceSet@Ordered+and+Normalized+sets.
|
|
913
1225
|
#
|
|
914
1226
|
# Net::IMAP::SequenceSet["2,5:9,6,12:11"].numbers
|
|
915
1227
|
# #=> [2, 5, 6, 7, 8, 9, 11, 12]
|
|
@@ -941,54 +1253,34 @@ module Net
|
|
|
941
1253
|
# no sorting, deduplication, or coalescing. When #string is in its
|
|
942
1254
|
# normalized form, this will yield the same values as #each_element.
|
|
943
1255
|
#
|
|
1256
|
+
# See SequenceSet@Ordered+and+Normalized+sets.
|
|
1257
|
+
#
|
|
944
1258
|
# Related: #entries, #each_element
|
|
945
1259
|
def each_entry(&block) # :yields: integer or range or :*
|
|
946
1260
|
return to_enum(__method__) unless block_given?
|
|
947
|
-
|
|
1261
|
+
each_entry_run do yield export_run_entry _1 end
|
|
948
1262
|
end
|
|
949
1263
|
|
|
950
1264
|
# Yields each number or range (or <tt>:*</tt>) in #elements to the block
|
|
951
1265
|
# and returns self. Returns an enumerator when called without a block.
|
|
952
1266
|
#
|
|
953
1267
|
# The returned numbers are sorted and de-duplicated, even when the input
|
|
954
|
-
# #string is not. See #normalize.
|
|
1268
|
+
# #string is not. See #normalize, SequenceSet@Ordered+and+Normalized+sets.
|
|
955
1269
|
#
|
|
956
1270
|
# Related: #elements, #each_entry
|
|
957
1271
|
def each_element # :yields: integer or range or :*
|
|
958
1272
|
return to_enum(__method__) unless block_given?
|
|
959
|
-
|
|
960
|
-
self
|
|
961
|
-
end
|
|
962
|
-
|
|
963
|
-
private
|
|
964
|
-
|
|
965
|
-
def each_entry_tuple(&block)
|
|
966
|
-
return to_enum(__method__) unless block_given?
|
|
967
|
-
if @string
|
|
968
|
-
@string.split(",") do block.call str_to_tuple _1 end
|
|
969
|
-
else
|
|
970
|
-
@tuples.each(&block)
|
|
971
|
-
end
|
|
1273
|
+
runs.each do yield export_run_entry _1 end
|
|
972
1274
|
self
|
|
973
1275
|
end
|
|
974
1276
|
|
|
975
|
-
def tuple_to_entry((min, max))
|
|
976
|
-
if min == STAR_INT then :*
|
|
977
|
-
elsif max == STAR_INT then min..
|
|
978
|
-
elsif min == max then min
|
|
979
|
-
else min..max
|
|
980
|
-
end
|
|
981
|
-
end
|
|
982
|
-
|
|
983
|
-
public
|
|
984
|
-
|
|
985
1277
|
# Yields each range in #ranges to the block and returns self.
|
|
986
1278
|
# Returns an enumerator when called without a block.
|
|
987
1279
|
#
|
|
988
1280
|
# Related: #ranges
|
|
989
1281
|
def each_range # :yields: range
|
|
990
1282
|
return to_enum(__method__) unless block_given?
|
|
991
|
-
|
|
1283
|
+
minmaxes.each do |min, max|
|
|
992
1284
|
if min == STAR_INT then yield :*..
|
|
993
1285
|
elsif max == STAR_INT then yield min..
|
|
994
1286
|
else yield min..max
|
|
@@ -1007,7 +1299,7 @@ module Net
|
|
|
1007
1299
|
def each_number(&block) # :yields: integer
|
|
1008
1300
|
return to_enum(__method__) unless block_given?
|
|
1009
1301
|
raise RangeError, '%s contains "*"' % [self.class] if include_star?
|
|
1010
|
-
|
|
1302
|
+
minmaxes.each do each_number_in_minmax _1, _2, &block end
|
|
1011
1303
|
self
|
|
1012
1304
|
end
|
|
1013
1305
|
|
|
@@ -1021,16 +1313,7 @@ module Net
|
|
|
1021
1313
|
def each_ordered_number(&block)
|
|
1022
1314
|
return to_enum(__method__) unless block_given?
|
|
1023
1315
|
raise RangeError, '%s contains "*"' % [self.class] if include_star?
|
|
1024
|
-
|
|
1025
|
-
end
|
|
1026
|
-
|
|
1027
|
-
private def each_number_in_tuple(min, max, &block)
|
|
1028
|
-
if min == STAR_INT then yield :*
|
|
1029
|
-
elsif min == max then yield min
|
|
1030
|
-
elsif max != STAR_INT then (min..max).each(&block)
|
|
1031
|
-
else
|
|
1032
|
-
raise RangeError, "#{SequenceSet} cannot enumerate range with '*'"
|
|
1033
|
-
end
|
|
1316
|
+
each_entry_minmax do each_number_in_minmax _1, _2, &block end
|
|
1034
1317
|
end
|
|
1035
1318
|
|
|
1036
1319
|
# Returns a Set with all of the #numbers in the sequence set.
|
|
@@ -1042,35 +1325,103 @@ module Net
|
|
|
1042
1325
|
# Related: #elements, #ranges, #numbers
|
|
1043
1326
|
def to_set; Set.new(numbers) end
|
|
1044
1327
|
|
|
1045
|
-
# Returns the
|
|
1328
|
+
# Returns the number of members in the set.
|
|
1329
|
+
#
|
|
1330
|
+
# Unlike #count, <tt>"*"</tt> is considered to be distinct from
|
|
1331
|
+
# <tt>2³² - 1</tt> (the maximum 32-bit unsigned integer value).
|
|
1332
|
+
#
|
|
1333
|
+
# set = Net::IMAP::SequenceSet[1..10]
|
|
1334
|
+
# set.count #=> 10
|
|
1335
|
+
# set.cardinality #=> 10
|
|
1046
1336
|
#
|
|
1047
|
-
#
|
|
1048
|
-
#
|
|
1337
|
+
# set = Net::IMAP::SequenceSet["4294967295,*"]
|
|
1338
|
+
# set.count #=> 1
|
|
1339
|
+
# set.cardinality #=> 2
|
|
1049
1340
|
#
|
|
1050
|
-
#
|
|
1341
|
+
# set = Net::IMAP::SequenceSet[1..]
|
|
1342
|
+
# set.count #=> 4294967295
|
|
1343
|
+
# set.cardinality #=> 4294967296
|
|
1344
|
+
#
|
|
1345
|
+
# Related: #count, #count_with_duplicates
|
|
1346
|
+
def cardinality = minmaxes.sum(runs.count) { _2 - _1 }
|
|
1347
|
+
|
|
1348
|
+
# Returns the count of distinct #numbers in the set.
|
|
1349
|
+
#
|
|
1350
|
+
# Unlike #cardinality, <tt>"*"</tt> is considered to be equal to
|
|
1351
|
+
# <tt>2³² - 1</tt> (the maximum 32-bit unsigned integer value).
|
|
1352
|
+
#
|
|
1353
|
+
# set = Net::IMAP::SequenceSet[1..10]
|
|
1354
|
+
# set.count #=> 10
|
|
1355
|
+
# set.cardinality #=> 10
|
|
1356
|
+
#
|
|
1357
|
+
# set = Net::IMAP::SequenceSet["4294967295,*"]
|
|
1358
|
+
# set.count #=> 1
|
|
1359
|
+
# set.cardinality #=> 2
|
|
1360
|
+
#
|
|
1361
|
+
# set = Net::IMAP::SequenceSet[1..]
|
|
1362
|
+
# set.count #=> 4294967295
|
|
1363
|
+
# set.cardinality #=> 4294967296
|
|
1364
|
+
#
|
|
1365
|
+
# Related: #cardinality, #count_with_duplicates
|
|
1051
1366
|
def count
|
|
1052
|
-
|
|
1053
|
-
(include_star? && include?(UINT32_MAX) ? -1 : 0)
|
|
1367
|
+
cardinality + (include_star? && include?(UINT32_MAX) ? -1 : 0)
|
|
1054
1368
|
end
|
|
1055
1369
|
|
|
1056
|
-
alias size count
|
|
1057
|
-
|
|
1058
1370
|
# Returns the count of numbers in the ordered #entries, including any
|
|
1059
1371
|
# repeated numbers.
|
|
1060
1372
|
#
|
|
1061
|
-
#
|
|
1062
|
-
#
|
|
1063
|
-
#
|
|
1064
|
-
#
|
|
1065
|
-
#
|
|
1066
|
-
#
|
|
1373
|
+
# When #string is normalized, this returns the same as #count. Like
|
|
1374
|
+
# #count, <tt>"*"</tt> is considered to be equal to <tt>2³² - 1</tt> (the
|
|
1375
|
+
# maximum 32-bit unsigned integer value).
|
|
1376
|
+
#
|
|
1377
|
+
# In a range, <tt>"*"</tt> is _not_ considered a duplicate:
|
|
1378
|
+
# set = Net::IMAP::SequenceSet["4294967295:*"]
|
|
1379
|
+
# set.count_with_duplicates #=> 1
|
|
1380
|
+
# set.size #=> 2
|
|
1381
|
+
# set.count #=> 1
|
|
1382
|
+
# set.cardinality #=> 2
|
|
1383
|
+
#
|
|
1384
|
+
# In a separate entry, <tt>"*"</tt> _is_ considered a duplicate:
|
|
1385
|
+
# set = Net::IMAP::SequenceSet["4294967295,*"]
|
|
1386
|
+
# set.count_with_duplicates #=> 2
|
|
1387
|
+
# set.size #=> 2
|
|
1388
|
+
# set.count #=> 1
|
|
1389
|
+
# set.cardinality #=> 2
|
|
1390
|
+
#
|
|
1391
|
+
# Related: #count, #cardinality, #size, #count_duplicates,
|
|
1392
|
+
# #has_duplicates?, #entries
|
|
1067
1393
|
def count_with_duplicates
|
|
1068
1394
|
return count unless @string
|
|
1069
|
-
|
|
1395
|
+
each_entry_minmax.sum {|min, max|
|
|
1070
1396
|
max - min + ((max == STAR_INT && min != STAR_INT) ? 0 : 1)
|
|
1071
1397
|
}
|
|
1072
1398
|
end
|
|
1073
1399
|
|
|
1400
|
+
# Returns the combined size of the ordered #entries, including any
|
|
1401
|
+
# repeated numbers.
|
|
1402
|
+
#
|
|
1403
|
+
# When #string is normalized, this returns the same as #cardinality.
|
|
1404
|
+
# Like #cardinality, <tt>"*"</tt> is considered to be be distinct from
|
|
1405
|
+
# <tt>2³² - 1</tt> (the maximum 32-bit unsigned integer value).
|
|
1406
|
+
#
|
|
1407
|
+
# set = Net::IMAP::SequenceSet["4294967295:*"]
|
|
1408
|
+
# set.size #=> 2
|
|
1409
|
+
# set.count_with_duplicates #=> 1
|
|
1410
|
+
# set.count #=> 1
|
|
1411
|
+
# set.cardinality #=> 2
|
|
1412
|
+
#
|
|
1413
|
+
# set = Net::IMAP::SequenceSet["4294967295,*"]
|
|
1414
|
+
# set.size #=> 2
|
|
1415
|
+
# set.count_with_duplicates #=> 2
|
|
1416
|
+
# set.count #=> 1
|
|
1417
|
+
# set.cardinality #=> 2
|
|
1418
|
+
#
|
|
1419
|
+
# Related: #cardinality, #count_with_duplicates, #count, #entries
|
|
1420
|
+
def size
|
|
1421
|
+
return cardinality unless @string
|
|
1422
|
+
each_entry_minmax.sum {|min, max| max - min + 1 }
|
|
1423
|
+
end
|
|
1424
|
+
|
|
1074
1425
|
# Returns the count of repeated numbers in the ordered #entries, the
|
|
1075
1426
|
# difference between #count_with_duplicates and #count.
|
|
1076
1427
|
#
|
|
@@ -1088,7 +1439,7 @@ module Net
|
|
|
1088
1439
|
#
|
|
1089
1440
|
# Always returns +false+ when #string is normalized.
|
|
1090
1441
|
#
|
|
1091
|
-
# Related: #entries, #count_with_duplicates, #count_duplicates
|
|
1442
|
+
# Related: #entries, #count_with_duplicates, #count_duplicates
|
|
1092
1443
|
def has_duplicates?
|
|
1093
1444
|
return false unless @string
|
|
1094
1445
|
count_with_duplicates != count
|
|
@@ -1099,10 +1450,10 @@ module Net
|
|
|
1099
1450
|
#
|
|
1100
1451
|
# Related: #[], #at, #find_ordered_index
|
|
1101
1452
|
def find_index(number)
|
|
1102
|
-
number =
|
|
1103
|
-
|
|
1453
|
+
number = import_num number
|
|
1454
|
+
each_minmax_with_index(minmaxes) do |min, max, idx_min|
|
|
1104
1455
|
number < min and return nil
|
|
1105
|
-
number <= max and return
|
|
1456
|
+
number <= max and return export_num(idx_min + (number - min))
|
|
1106
1457
|
end
|
|
1107
1458
|
nil
|
|
1108
1459
|
end
|
|
@@ -1112,38 +1463,15 @@ module Net
|
|
|
1112
1463
|
#
|
|
1113
1464
|
# Related: #find_index
|
|
1114
1465
|
def find_ordered_index(number)
|
|
1115
|
-
number =
|
|
1116
|
-
|
|
1466
|
+
number = import_num number
|
|
1467
|
+
each_minmax_with_index(each_entry_minmax) do |min, max, idx_min|
|
|
1117
1468
|
if min <= number && number <= max
|
|
1118
|
-
return
|
|
1469
|
+
return export_num(idx_min + (number - min))
|
|
1119
1470
|
end
|
|
1120
1471
|
end
|
|
1121
1472
|
nil
|
|
1122
1473
|
end
|
|
1123
1474
|
|
|
1124
|
-
private
|
|
1125
|
-
|
|
1126
|
-
def each_tuple_with_index(tuples)
|
|
1127
|
-
idx_min = 0
|
|
1128
|
-
tuples.each do |min, max|
|
|
1129
|
-
idx_max = idx_min + (max - min)
|
|
1130
|
-
yield min, max, idx_min, idx_max
|
|
1131
|
-
idx_min = idx_max + 1
|
|
1132
|
-
end
|
|
1133
|
-
idx_min
|
|
1134
|
-
end
|
|
1135
|
-
|
|
1136
|
-
def reverse_each_tuple_with_index(tuples)
|
|
1137
|
-
idx_max = -1
|
|
1138
|
-
tuples.reverse_each do |min, max|
|
|
1139
|
-
yield min, max, (idx_min = idx_max - (max - min)), idx_max
|
|
1140
|
-
idx_max = idx_min - 1
|
|
1141
|
-
end
|
|
1142
|
-
idx_max
|
|
1143
|
-
end
|
|
1144
|
-
|
|
1145
|
-
public
|
|
1146
|
-
|
|
1147
1475
|
# :call-seq: at(index) -> integer or nil
|
|
1148
1476
|
#
|
|
1149
1477
|
# Returns the number at the given +index+ in the sorted set, without
|
|
@@ -1154,7 +1482,7 @@ module Net
|
|
|
1154
1482
|
#
|
|
1155
1483
|
# Related: #[], #slice, #ordered_at
|
|
1156
1484
|
def at(index)
|
|
1157
|
-
|
|
1485
|
+
seek_number_in_minmaxes(minmaxes, index)
|
|
1158
1486
|
end
|
|
1159
1487
|
|
|
1160
1488
|
# :call-seq: ordered_at(index) -> integer or nil
|
|
@@ -1167,21 +1495,7 @@ module Net
|
|
|
1167
1495
|
#
|
|
1168
1496
|
# Related: #[], #slice, #ordered_at
|
|
1169
1497
|
def ordered_at(index)
|
|
1170
|
-
|
|
1171
|
-
end
|
|
1172
|
-
|
|
1173
|
-
private def lookup_number_by_tuple_index(tuples, index)
|
|
1174
|
-
index = Integer(index.to_int)
|
|
1175
|
-
if index.negative?
|
|
1176
|
-
reverse_each_tuple_with_index(tuples) do |min, max, idx_min, idx_max|
|
|
1177
|
-
idx_min <= index and return from_tuple_int(min + (index - idx_min))
|
|
1178
|
-
end
|
|
1179
|
-
else
|
|
1180
|
-
each_tuple_with_index(tuples) do |min, _, idx_min, idx_max|
|
|
1181
|
-
index <= idx_max and return from_tuple_int(min + (index - idx_min))
|
|
1182
|
-
end
|
|
1183
|
-
end
|
|
1184
|
-
nil
|
|
1498
|
+
seek_number_in_minmaxes(each_entry_minmax, index)
|
|
1185
1499
|
end
|
|
1186
1500
|
|
|
1187
1501
|
# :call-seq:
|
|
@@ -1232,37 +1546,58 @@ module Net
|
|
|
1232
1546
|
|
|
1233
1547
|
alias slice :[]
|
|
1234
1548
|
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1549
|
+
# Returns a copy of +self+ which only contains the numbers above +num+.
|
|
1550
|
+
#
|
|
1551
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].above(10) # to_s => "11:22,50"
|
|
1552
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].above(20) # to_s => "21:22,50
|
|
1553
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].above(30) # to_s => "50"
|
|
1554
|
+
#
|
|
1555
|
+
# This returns the same result as #intersection with <tt>((num+1)..)</tt>
|
|
1556
|
+
# or #difference with <tt>(..num)</tt>.
|
|
1557
|
+
#
|
|
1558
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] & (11..) # to_s => "11:22,50"
|
|
1559
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] - (..10) # to_s => "11:22,50"
|
|
1560
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] & (21..) # to_s => "21:22,50"
|
|
1561
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] - (..20) # to_s => "21:22,50"
|
|
1562
|
+
#
|
|
1563
|
+
# Related: #above, #-, #&
|
|
1564
|
+
def above(num)
|
|
1565
|
+
NumValidator.valid_nz_number?(num) or
|
|
1566
|
+
raise ArgumentError, "not a valid sequence set number"
|
|
1567
|
+
difference(..num)
|
|
1243
1568
|
end
|
|
1244
1569
|
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1570
|
+
# Returns a copy of +self+ which only contains numbers below +num+.
|
|
1571
|
+
#
|
|
1572
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].below(10) # to_s => "5"
|
|
1573
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].below(20) # to_s => "5,10:19"
|
|
1574
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].below(30) # to_s => "5,10:22"
|
|
1575
|
+
#
|
|
1576
|
+
# This returns the same result as #intersection with <tt>(..(num-1))</tt>
|
|
1577
|
+
# or #difference with <tt>(num..)</tt>.
|
|
1578
|
+
#
|
|
1579
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] & (..9) # to_s => "5"
|
|
1580
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] - (10..) # to_s => "5"
|
|
1581
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] & (..19) # to_s => "5,10:19"
|
|
1582
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] - (20..) # to_s => "5,10:19"
|
|
1583
|
+
#
|
|
1584
|
+
# When the set does not contain <tt>*</tt>, #below is identical to #limit
|
|
1585
|
+
# with <tt>max: num - 1</tt>. When the set does contain <tt>*</tt>,
|
|
1586
|
+
# #below always drops it from the result. Use #limit when the IMAP
|
|
1587
|
+
# semantics for <tt>*</tt> must be enforced.
|
|
1588
|
+
#
|
|
1589
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].below(30) # to_s => "5,10:22"
|
|
1590
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].limit(max: 29) # to_s => "5,10:22"
|
|
1591
|
+
# Net::IMAP::SequenceSet["5,10:22,*"].below(30) # to_s => "5,10:22"
|
|
1592
|
+
# Net::IMAP::SequenceSet["5,10:22,*"].limit(max: 29) # to_s => "5,10:22,29"
|
|
1593
|
+
#
|
|
1594
|
+
# Related: #above, #-, #&, #limit
|
|
1595
|
+
def below(num)
|
|
1596
|
+
NumValidator.valid_nz_number?(num) or
|
|
1597
|
+
raise ArgumentError, "not a valid sequence set number"
|
|
1598
|
+
difference(num..)
|
|
1262
1599
|
end
|
|
1263
1600
|
|
|
1264
|
-
public
|
|
1265
|
-
|
|
1266
1601
|
# Returns a frozen SequenceSet with <tt>*</tt> converted to +max+, numbers
|
|
1267
1602
|
# and ranges over +max+ removed, and ranges containing +max+ converted to
|
|
1268
1603
|
# end at +max+.
|
|
@@ -1280,8 +1615,9 @@ module Net
|
|
|
1280
1615
|
# Net::IMAP::SequenceSet["500:*"].limit(max: 37)
|
|
1281
1616
|
# #=> Net::IMAP::SequenceSet["37"]
|
|
1282
1617
|
#
|
|
1618
|
+
# Related: #limit!
|
|
1283
1619
|
def limit(max:)
|
|
1284
|
-
max =
|
|
1620
|
+
max = import_num(max)
|
|
1285
1621
|
if empty? then self.class.empty
|
|
1286
1622
|
elsif !include_star? && max < min then self.class.empty
|
|
1287
1623
|
elsif max(star: STAR_INT) <= max then frozen? ? self : dup.freeze
|
|
@@ -1294,76 +1630,206 @@ module Net
|
|
|
1294
1630
|
#
|
|
1295
1631
|
# Related: #limit
|
|
1296
1632
|
def limit!(max:)
|
|
1633
|
+
modifying! # short-circuit before querying
|
|
1297
1634
|
star = include_star?
|
|
1298
|
-
max =
|
|
1299
|
-
|
|
1300
|
-
|
|
1635
|
+
max = import_num(max)
|
|
1636
|
+
subtract_minmax [max + 1, STAR_INT]
|
|
1637
|
+
add_minmax [max, max ] if star
|
|
1301
1638
|
normalize!
|
|
1302
1639
|
end
|
|
1303
1640
|
|
|
1304
1641
|
# :call-seq: complement! -> self
|
|
1305
1642
|
#
|
|
1306
|
-
#
|
|
1307
|
-
# possible values _except_ for those
|
|
1643
|
+
# In-place set #complement. Replaces the contents of this set with its
|
|
1644
|
+
# own #complement. It will contain all possible values _except_ for those
|
|
1645
|
+
# currently in the set.
|
|
1308
1646
|
#
|
|
1309
1647
|
# Related: #complement
|
|
1310
1648
|
def complement!
|
|
1649
|
+
modifying! # short-circuit before querying
|
|
1311
1650
|
return replace(self.class.full) if empty?
|
|
1312
1651
|
return clear if full?
|
|
1313
|
-
flat =
|
|
1652
|
+
flat = minmaxes.flat_map { [_1 - 1, _2 + 1] }
|
|
1314
1653
|
if flat.first < 1 then flat.shift else flat.unshift 1 end
|
|
1315
1654
|
if STAR_INT < flat.last then flat.pop else flat.push STAR_INT end
|
|
1316
|
-
|
|
1655
|
+
replace_minmaxes flat.each_slice(2).to_a
|
|
1317
1656
|
normalize!
|
|
1318
1657
|
end
|
|
1319
1658
|
|
|
1320
|
-
#
|
|
1659
|
+
# In-place set #intersection. Removes any elements that are missing from
|
|
1660
|
+
# +other+ from this set, keeping only the #intersection, and returns
|
|
1661
|
+
# +self+.
|
|
1662
|
+
#
|
|
1663
|
+
# +other+ can be any object that would be accepted by ::new.
|
|
1321
1664
|
#
|
|
1322
|
-
#
|
|
1323
|
-
#
|
|
1665
|
+
# set = Net::IMAP::SequenceSet.new(1..5)
|
|
1666
|
+
# set.intersect! [2, 4, 6]
|
|
1667
|
+
# set #=> Net::IMAP::SequenceSet("2,4")
|
|
1668
|
+
#
|
|
1669
|
+
# Related: #intersection, #intersect?
|
|
1670
|
+
def intersect!(other)
|
|
1671
|
+
modifying! # short-circuit before processing input
|
|
1672
|
+
subtract SequenceSet.new(other).complement!
|
|
1673
|
+
end
|
|
1674
|
+
|
|
1675
|
+
# In-place set #xor. Adds any numbers in +other+ that are missing from
|
|
1676
|
+
# this set, removes any numbers in +other+ that are already in this set,
|
|
1677
|
+
# and returns +self+.
|
|
1678
|
+
#
|
|
1679
|
+
# +other+ can be any object that would be accepted by ::new.
|
|
1680
|
+
#
|
|
1681
|
+
# set = Net::IMAP::SequenceSet.new(1..5)
|
|
1682
|
+
# set.xor! [2, 4, 6]
|
|
1683
|
+
# set #=> Net::IMAP::SequenceSet["1,3,5:6"]
|
|
1684
|
+
#
|
|
1685
|
+
# Related: #xor, #merge, #subtract
|
|
1686
|
+
def xor!(other)
|
|
1687
|
+
modifying! # short-circuit before processing input
|
|
1688
|
+
other = SequenceSet.new(other)
|
|
1689
|
+
copy = dup
|
|
1690
|
+
merge(other).subtract(other.subtract(copy.complement!))
|
|
1691
|
+
end
|
|
1692
|
+
|
|
1693
|
+
# Returns whether #string is fully normalized: entries have been sorted,
|
|
1694
|
+
# deduplicated, and coalesced, and all entries are in normal form. See
|
|
1695
|
+
# SequenceSet@Ordered+and+Normalized+sets.
|
|
1696
|
+
#
|
|
1697
|
+
# Net::IMAP::SequenceSet["1,3,5"].normalized? #=> true
|
|
1698
|
+
# Net::IMAP::SequenceSet["20:30"].normalized? #=> true
|
|
1699
|
+
#
|
|
1700
|
+
# Net::IMAP::SequenceSet["3,5,1"].normalized? #=> false, not sorted
|
|
1701
|
+
# Net::IMAP::SequenceSet["1,2,3"].normalized? #=> false, not coalesced
|
|
1702
|
+
# Net::IMAP::SequenceSet["1:5,2"].normalized? #=> false, repeated number
|
|
1703
|
+
#
|
|
1704
|
+
# Net::IMAP::SequenceSet["1:1"].normalized? #=> false, number as range
|
|
1705
|
+
# Net::IMAP::SequenceSet["5:1"].normalized? #=> false, backwards range
|
|
1706
|
+
#
|
|
1707
|
+
# Returns +true+ if (and only if) #string is equal to #normalized_string:
|
|
1708
|
+
# seqset = Net::IMAP::SequenceSet["1:3,5"]
|
|
1709
|
+
# seqset.string #=> "1:3,5"
|
|
1710
|
+
# seqset.normalized_string #=> "1:3,5"
|
|
1711
|
+
# seqset.entries #=> [1..3, 5]
|
|
1712
|
+
# seqset.elements #=> [1..3, 5]
|
|
1713
|
+
# seqset.normalized? #=> true
|
|
1714
|
+
#
|
|
1715
|
+
# seqset = Net::IMAP::SequenceSet["3,1,2"]
|
|
1716
|
+
# seqset.string #=> "3,1,2"
|
|
1717
|
+
# seqset.normalized_string #=> "1:3"
|
|
1718
|
+
# seqset.entries #=> [3, 1, 2]
|
|
1719
|
+
# seqset.elements #=> [1..3]
|
|
1720
|
+
# seqset.normalized? #=> false
|
|
1721
|
+
#
|
|
1722
|
+
# Can return +false+ even when #entries and #elements are the same:
|
|
1723
|
+
# seqset = Net::IMAP::SequenceSet["5:1"]
|
|
1724
|
+
# seqset.string #=> "5:1"
|
|
1725
|
+
# seqset.normalized_string #=> "1:5"
|
|
1726
|
+
# seqset.entries #=> [1..5]
|
|
1727
|
+
# seqset.elements #=> [1..5]
|
|
1728
|
+
# seqset.normalized? #=> false
|
|
1729
|
+
#
|
|
1730
|
+
# Note that empty sets are normalized, even though they are not #valid?:
|
|
1731
|
+
# seqset = Net::IMAP::SequenceSet.empty
|
|
1732
|
+
# seqset.normalized? #=> true
|
|
1733
|
+
# seqset.valid? #=> false
|
|
1734
|
+
#
|
|
1735
|
+
# Related: #normalize, #normalize!, #normalized_string
|
|
1736
|
+
def normalized?
|
|
1737
|
+
@string.nil? || normal_string?(@string)
|
|
1738
|
+
end
|
|
1739
|
+
|
|
1740
|
+
# Returns a SequenceSet with a normalized string representation: entries
|
|
1741
|
+
# have been sorted, deduplicated, and coalesced, and all entries
|
|
1742
|
+
# are in normal form. Returns +self+ for frozen normalized sets, and a
|
|
1743
|
+
# normalized duplicate otherwise.
|
|
1744
|
+
#
|
|
1745
|
+
# See SequenceSet@Ordered+and+Normalized+sets.
|
|
1324
1746
|
#
|
|
1325
1747
|
# Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalize
|
|
1326
1748
|
# #=> Net::IMAP::SequenceSet["1:7,9:11"]
|
|
1327
1749
|
#
|
|
1328
|
-
# Related: #normalize!, #normalized_string
|
|
1750
|
+
# Related: #normalize!, #normalized_string, #normalized?
|
|
1329
1751
|
def normalize
|
|
1330
|
-
|
|
1331
|
-
return self if frozen? && str == string
|
|
1332
|
-
remain_frozen dup.instance_exec { @string = str&.-@; self }
|
|
1752
|
+
frozen? && normalized? ? self : remain_frozen(dup.normalize!)
|
|
1333
1753
|
end
|
|
1334
1754
|
|
|
1335
1755
|
# Resets #string to be sorted, deduplicated, and coalesced. Returns
|
|
1336
|
-
# +self+.
|
|
1756
|
+
# +self+. See SequenceSet@Ordered+and+Normalized+sets.
|
|
1337
1757
|
#
|
|
1338
|
-
# Related: #normalize, #normalized_string
|
|
1758
|
+
# Related: #normalize, #normalized_string, #normalized?
|
|
1339
1759
|
def normalize!
|
|
1760
|
+
modifying! # redundant check (normalizes the error message for JRuby)
|
|
1340
1761
|
@string = nil
|
|
1341
1762
|
self
|
|
1342
1763
|
end
|
|
1343
1764
|
|
|
1344
1765
|
# Returns a normalized +sequence-set+ string representation, sorted
|
|
1345
1766
|
# and deduplicated. Adjacent or overlapping elements will be merged into
|
|
1346
|
-
# a single larger range.
|
|
1767
|
+
# a single larger range. See SequenceSet@Ordered+and+Normalized+sets.
|
|
1347
1768
|
#
|
|
1348
1769
|
# Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalized_string
|
|
1349
1770
|
# #=> "1:7,9:11"
|
|
1350
1771
|
#
|
|
1351
|
-
#
|
|
1772
|
+
# Returns +nil+ when the set is empty.
|
|
1773
|
+
#
|
|
1774
|
+
# Related: #normalize!, #normalize, #string, #to_s, #normalized?
|
|
1352
1775
|
def normalized_string
|
|
1353
|
-
|
|
1776
|
+
export_runs(runs) unless runs.empty?
|
|
1354
1777
|
end
|
|
1355
1778
|
|
|
1779
|
+
# Returns an inspection string for the SequenceSet.
|
|
1780
|
+
#
|
|
1781
|
+
# Net::IMAP::SequenceSet.new.inspect
|
|
1782
|
+
# #=> "Net::IMAP::SequenceSet()"
|
|
1783
|
+
#
|
|
1784
|
+
# Net::IMAP::SequenceSet(1..5, 1024, 15, 2000).inspect
|
|
1785
|
+
# #=> 'Net::IMAP::SequenceSet("1:5,15,1024,2000")'
|
|
1786
|
+
#
|
|
1787
|
+
# Frozen sets have slightly different output:
|
|
1788
|
+
#
|
|
1789
|
+
# Net::IMAP::SequenceSet.empty.inspect
|
|
1790
|
+
# #=> "Net::IMAP::SequenceSet.empty"
|
|
1791
|
+
#
|
|
1792
|
+
# Net::IMAP::SequenceSet[1..5, 1024, 15, 2000].inspect
|
|
1793
|
+
# #=> 'Net::IMAP::SequenceSet["1:5,15,1024,2000"]'
|
|
1794
|
+
#
|
|
1795
|
+
# Large sets (by number of #entries) have abridged output, with only the
|
|
1796
|
+
# first and last entries:
|
|
1797
|
+
#
|
|
1798
|
+
# Net::IMAP::SequenceSet(((1..5000) % 2).to_a).inspect
|
|
1799
|
+
# #=> #<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">
|
|
1800
|
+
#
|
|
1801
|
+
# Related: #to_s, #string
|
|
1356
1802
|
def inspect
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1803
|
+
case (count = count_entries)
|
|
1804
|
+
when 0
|
|
1805
|
+
(frozen? ? "%s.empty" : "%s()") % [self.class]
|
|
1806
|
+
when ..INSPECT_MAX_LEN
|
|
1807
|
+
(frozen? ? "%s[%p]" : "%s(%p)") % [self.class, to_s]
|
|
1361
1808
|
else
|
|
1362
|
-
|
|
1809
|
+
if @string
|
|
1810
|
+
head = @string[INSPECT_ABRIDGED_HEAD_RE]
|
|
1811
|
+
tail = @string[INSPECT_ABRIDGED_TAIL_RE]
|
|
1812
|
+
else
|
|
1813
|
+
head = export_runs(runs.first(INSPECT_TRUNCATE_LEN)) + ","
|
|
1814
|
+
tail = "," + export_runs(runs.last(INSPECT_TRUNCATE_LEN))
|
|
1815
|
+
end
|
|
1816
|
+
'#<%s %d entries "%s...(%d entries omitted)...%s"%s>' % [
|
|
1817
|
+
self.class, count,
|
|
1818
|
+
head, count - INSPECT_TRUNCATE_LEN * 2, tail,
|
|
1819
|
+
frozen? ? " (frozen)" : "",
|
|
1820
|
+
]
|
|
1363
1821
|
end
|
|
1364
1822
|
end
|
|
1365
1823
|
|
|
1366
|
-
|
|
1824
|
+
##
|
|
1825
|
+
# :method: to_sequence_set
|
|
1826
|
+
# :call-seq: to_sequence_set -> self
|
|
1827
|
+
#
|
|
1828
|
+
# Returns +self+
|
|
1829
|
+
#
|
|
1830
|
+
# Related: ::try_convert
|
|
1831
|
+
|
|
1832
|
+
# :nodoc: (work around rdoc bug)
|
|
1367
1833
|
alias to_sequence_set itself
|
|
1368
1834
|
|
|
1369
1835
|
# Unstable API: currently for internal use only (Net::IMAP#validate_data)
|
|
@@ -1377,9 +1843,25 @@ module Net
|
|
|
1377
1843
|
imap.__send__(:put_string, valid_string)
|
|
1378
1844
|
end
|
|
1379
1845
|
|
|
1846
|
+
# For YAML serialization
|
|
1847
|
+
def encode_with(coder) # :nodoc:
|
|
1848
|
+
# we can perfectly reconstruct from the string
|
|
1849
|
+
coder['string'] = to_s
|
|
1850
|
+
end
|
|
1851
|
+
|
|
1852
|
+
# For YAML deserialization
|
|
1853
|
+
def init_with(coder) # :nodoc:
|
|
1854
|
+
@set_data = new_set_data
|
|
1855
|
+
self.string = coder['string']
|
|
1856
|
+
end
|
|
1857
|
+
|
|
1858
|
+
# :stopdoc:
|
|
1380
1859
|
protected
|
|
1381
1860
|
|
|
1382
|
-
attr_reader :
|
|
1861
|
+
attr_reader :set_data
|
|
1862
|
+
|
|
1863
|
+
alias runs set_data
|
|
1864
|
+
alias minmaxes runs
|
|
1383
1865
|
|
|
1384
1866
|
private
|
|
1385
1867
|
|
|
@@ -1388,38 +1870,42 @@ module Net
|
|
|
1388
1870
|
|
|
1389
1871
|
# frozen clones are shallow copied
|
|
1390
1872
|
def initialize_clone(other)
|
|
1391
|
-
other.
|
|
1873
|
+
@set_data = other.dup_set_data unless other.frozen?
|
|
1874
|
+
super
|
|
1392
1875
|
end
|
|
1393
1876
|
|
|
1394
1877
|
def initialize_dup(other)
|
|
1395
|
-
@
|
|
1396
|
-
@string = other.string&.-@
|
|
1878
|
+
@set_data = other.dup_set_data
|
|
1397
1879
|
super
|
|
1398
1880
|
end
|
|
1399
1881
|
|
|
1400
|
-
|
|
1401
|
-
|
|
1882
|
+
######################################################################{{{2
|
|
1883
|
+
# Import methods
|
|
1884
|
+
|
|
1885
|
+
def import_minmax(input)
|
|
1886
|
+
entry = input_try_convert input
|
|
1402
1887
|
case entry
|
|
1403
|
-
when *STARS, Integer then [int =
|
|
1404
|
-
when Range then
|
|
1405
|
-
when String then
|
|
1888
|
+
when *STARS, Integer then [int = import_num(entry), int]
|
|
1889
|
+
when Range then import_range_minmax(entry)
|
|
1890
|
+
when String then parse_minmax(entry)
|
|
1406
1891
|
else
|
|
1407
|
-
raise DataFormatError, "expected number or range, got %p" % [
|
|
1892
|
+
raise DataFormatError, "expected number or range, got %p" % [input]
|
|
1408
1893
|
end
|
|
1409
1894
|
end
|
|
1895
|
+
alias import_run import_minmax
|
|
1410
1896
|
|
|
1411
|
-
def
|
|
1412
|
-
set = input_try_convert
|
|
1897
|
+
def import_runs(input)
|
|
1898
|
+
set = input_try_convert input
|
|
1413
1899
|
case set
|
|
1414
|
-
when *STARS, Integer, Range then [
|
|
1415
|
-
when String then
|
|
1416
|
-
when SequenceSet then set.
|
|
1417
|
-
when
|
|
1900
|
+
when *STARS, Integer, Range then [import_run(set)]
|
|
1901
|
+
when String then parse_runs set
|
|
1902
|
+
when SequenceSet then set.runs
|
|
1903
|
+
when Set then set.map { [import_num(_1)] * 2 }
|
|
1904
|
+
when Array then set.flat_map { import_runs _1 }
|
|
1418
1905
|
when nil then []
|
|
1419
1906
|
else
|
|
1420
|
-
raise DataFormatError,
|
|
1421
|
-
|
|
1422
|
-
"got %p" % [set]
|
|
1907
|
+
raise DataFormatError, "expected nz-number, range, '*', Set, Array; " \
|
|
1908
|
+
"got %p" % [input]
|
|
1423
1909
|
end
|
|
1424
1910
|
end
|
|
1425
1911
|
|
|
@@ -1427,15 +1913,22 @@ module Net
|
|
|
1427
1913
|
# String, Set, Array, or... any type of object.
|
|
1428
1914
|
def input_try_convert(input)
|
|
1429
1915
|
SequenceSet.try_convert(input) ||
|
|
1430
|
-
|
|
1431
|
-
input.respond_to?(:to_int) && Integer(input.to_int) ||
|
|
1916
|
+
Integer.try_convert(input) ||
|
|
1432
1917
|
String.try_convert(input) ||
|
|
1433
1918
|
input
|
|
1434
1919
|
end
|
|
1435
1920
|
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1921
|
+
# NOTE: input_try_convert must be called on input first
|
|
1922
|
+
def number_input?(input)
|
|
1923
|
+
case input
|
|
1924
|
+
when *STARS, Integer then true
|
|
1925
|
+
when String then !input.include?(/[:,]/)
|
|
1926
|
+
end
|
|
1927
|
+
end
|
|
1928
|
+
|
|
1929
|
+
def import_range_minmax(range)
|
|
1930
|
+
first = import_num(range.begin || 1)
|
|
1931
|
+
last = import_num(range.end || :*)
|
|
1439
1932
|
last -= 1 if range.exclude_end? && range.end && last != STAR_INT
|
|
1440
1933
|
unless first <= last
|
|
1441
1934
|
raise DataFormatError, "invalid range for sequence-set: %p" % [range]
|
|
@@ -1443,67 +1936,260 @@ module Net
|
|
|
1443
1936
|
[first, last]
|
|
1444
1937
|
end
|
|
1445
1938
|
|
|
1446
|
-
def
|
|
1447
|
-
def
|
|
1939
|
+
def import_num(obj) STARS.include?(obj) ? STAR_INT : nz_number(obj) end
|
|
1940
|
+
def nz_number(num) = NumValidator.coerce_nz_number(num)
|
|
1941
|
+
|
|
1942
|
+
######################################################################{{{2
|
|
1943
|
+
# Export methods
|
|
1944
|
+
|
|
1945
|
+
def export_num(num) num == STAR_INT ? :* : num end
|
|
1448
1946
|
|
|
1449
|
-
def
|
|
1450
|
-
|
|
1451
|
-
|
|
1947
|
+
def export_minmaxes(minmaxes)
|
|
1948
|
+
-minmaxes.map { export_minmax _1 }.join(",")
|
|
1949
|
+
end
|
|
1950
|
+
|
|
1951
|
+
def export_minmax(minmax) minmax.uniq.map { export_num _1 }.join(":") end
|
|
1952
|
+
|
|
1953
|
+
alias export_runs export_minmaxes
|
|
1954
|
+
alias export_run export_minmax
|
|
1955
|
+
|
|
1956
|
+
def export_minmax_entry((min, max))
|
|
1957
|
+
if min == STAR_INT then :*
|
|
1958
|
+
elsif max == STAR_INT then min..
|
|
1959
|
+
elsif min == max then min
|
|
1960
|
+
else min..max
|
|
1961
|
+
end
|
|
1962
|
+
end
|
|
1963
|
+
alias export_run_entry export_minmax_entry
|
|
1964
|
+
|
|
1965
|
+
def each_number_in_minmax(min, max, &block)
|
|
1966
|
+
if min == STAR_INT then yield :*
|
|
1967
|
+
elsif min == max then yield min
|
|
1968
|
+
elsif max != STAR_INT then (min..max).each(&block)
|
|
1969
|
+
else
|
|
1970
|
+
raise RangeError, "#{SequenceSet} cannot enumerate range with '*'"
|
|
1971
|
+
end
|
|
1972
|
+
end
|
|
1973
|
+
|
|
1974
|
+
######################################################################{{{2
|
|
1975
|
+
# Parse methods
|
|
1976
|
+
|
|
1977
|
+
def parse_runs(str) str.split(",", -1).map! { parse_run _1 } end
|
|
1978
|
+
def parse_minmax(str) parse_entry(str).minmax end
|
|
1979
|
+
alias parse_run parse_minmax
|
|
1980
|
+
|
|
1981
|
+
def parse_entry(str)
|
|
1452
1982
|
raise DataFormatError, "invalid sequence set string" if str.empty?
|
|
1453
|
-
str.split(":", 2).map! {
|
|
1983
|
+
str.split(":", 2).map! { import_num _1 }
|
|
1984
|
+
end
|
|
1985
|
+
|
|
1986
|
+
# yields validated but unsorted [num] or [num, num]
|
|
1987
|
+
def each_parsed_entry(str)
|
|
1988
|
+
return to_enum(__method__, str) unless block_given?
|
|
1989
|
+
str&.split(",", -1) do |entry| yield parse_entry(entry) end
|
|
1454
1990
|
end
|
|
1455
1991
|
|
|
1456
|
-
def
|
|
1992
|
+
def normal_string?(str) normalized_entries? each_parsed_entry str end
|
|
1457
1993
|
|
|
1458
|
-
def
|
|
1459
|
-
|
|
1994
|
+
def normalized_entries?(entries)
|
|
1995
|
+
max = nil
|
|
1996
|
+
entries.each do |first, last|
|
|
1997
|
+
return false if last && last <= first # 1:1 or 2:1
|
|
1998
|
+
return false if max && first <= max + 1 # 2,1 or 1,1 or 1,2
|
|
1999
|
+
max = last || first
|
|
2000
|
+
end
|
|
2001
|
+
true
|
|
2002
|
+
end
|
|
2003
|
+
|
|
2004
|
+
######################################################################{{{2
|
|
2005
|
+
# Ordered entry methods
|
|
2006
|
+
|
|
2007
|
+
def count_entries
|
|
2008
|
+
@string ? @string.count(",") + 1 : runs.count
|
|
2009
|
+
end
|
|
2010
|
+
|
|
2011
|
+
def each_entry_minmax(&block)
|
|
2012
|
+
return to_enum(__method__) unless block_given?
|
|
2013
|
+
if @string
|
|
2014
|
+
@string.split(",") do block.call parse_minmax _1 end
|
|
2015
|
+
else
|
|
2016
|
+
minmaxes.each(&block)
|
|
2017
|
+
end
|
|
2018
|
+
self
|
|
2019
|
+
end
|
|
2020
|
+
alias each_entry_run each_entry_minmax
|
|
2021
|
+
|
|
2022
|
+
######################################################################{{{2
|
|
2023
|
+
# Search methods
|
|
2024
|
+
|
|
2025
|
+
def include_minmax?((min, max)) bsearch_range(min)&.cover?(min..max) end
|
|
2026
|
+
|
|
2027
|
+
def intersect_minmax?((min, max))
|
|
2028
|
+
range = bsearch_range(min) and
|
|
1460
2029
|
range.include?(min) || range.include?(max) || (min..max).cover?(range)
|
|
1461
2030
|
end
|
|
1462
2031
|
|
|
2032
|
+
alias include_run? include_minmax?
|
|
2033
|
+
alias intersect_run? intersect_minmax?
|
|
2034
|
+
|
|
2035
|
+
def bsearch_index(num) = minmaxes.bsearch_index { _2 >= num }
|
|
2036
|
+
def bsearch_minmax(num) = minmaxes.bsearch { _2 >= num }
|
|
2037
|
+
def bsearch_range(num) = (min, max = bsearch_minmax(num)) && (min..max)
|
|
2038
|
+
|
|
2039
|
+
######################################################################{{{2
|
|
2040
|
+
# Number indexing methods
|
|
2041
|
+
|
|
2042
|
+
def seek_number_in_minmaxes(minmaxes, index)
|
|
2043
|
+
index = Integer(index.to_int)
|
|
2044
|
+
if index.negative?
|
|
2045
|
+
reverse_each_minmax_with_index(minmaxes) do |min, max, idx_min, idx_max|
|
|
2046
|
+
idx_min <= index and return export_num(min + (index - idx_min))
|
|
2047
|
+
end
|
|
2048
|
+
else
|
|
2049
|
+
each_minmax_with_index(minmaxes) do |min, _, idx_min, idx_max|
|
|
2050
|
+
index <= idx_max and return export_num(min + (index - idx_min))
|
|
2051
|
+
end
|
|
2052
|
+
end
|
|
2053
|
+
nil
|
|
2054
|
+
end
|
|
2055
|
+
|
|
2056
|
+
def each_minmax_with_index(minmaxes)
|
|
2057
|
+
idx_min = 0
|
|
2058
|
+
minmaxes.each do |min, max|
|
|
2059
|
+
idx_max = idx_min + (max - min)
|
|
2060
|
+
yield min, max, idx_min, idx_max
|
|
2061
|
+
idx_min = idx_max + 1
|
|
2062
|
+
end
|
|
2063
|
+
idx_min
|
|
2064
|
+
end
|
|
2065
|
+
|
|
2066
|
+
def reverse_each_minmax_with_index(minmaxes)
|
|
2067
|
+
idx_max = -1
|
|
2068
|
+
minmaxes.reverse_each do |min, max|
|
|
2069
|
+
yield min, max, (idx_min = idx_max - (max - min)), idx_max
|
|
2070
|
+
idx_max = idx_min - 1
|
|
2071
|
+
end
|
|
2072
|
+
idx_max
|
|
2073
|
+
end
|
|
2074
|
+
|
|
2075
|
+
def slice_length(start, length)
|
|
2076
|
+
start = Integer(start.to_int)
|
|
2077
|
+
length = Integer(length.to_int)
|
|
2078
|
+
raise ArgumentError, "length must be positive" unless length.positive?
|
|
2079
|
+
last = start + length - 1 unless start.negative? && start.abs <= length
|
|
2080
|
+
slice_range(start..last)
|
|
2081
|
+
end
|
|
2082
|
+
|
|
2083
|
+
def slice_range(range)
|
|
2084
|
+
first = range.begin || 0
|
|
2085
|
+
last = range.end || -1
|
|
2086
|
+
if range.exclude_end?
|
|
2087
|
+
return remain_frozen_empty if last.zero?
|
|
2088
|
+
last -= 1 if range.end && last != STAR_INT
|
|
2089
|
+
end
|
|
2090
|
+
if (first * last).positive? && last < first
|
|
2091
|
+
remain_frozen_empty
|
|
2092
|
+
elsif (min = at(first))
|
|
2093
|
+
max = at(last)
|
|
2094
|
+
max = :* if max.nil?
|
|
2095
|
+
if max == :* then self & (min..)
|
|
2096
|
+
elsif min <= max then self & (min..max)
|
|
2097
|
+
else remain_frozen_empty
|
|
2098
|
+
end
|
|
2099
|
+
end
|
|
2100
|
+
end
|
|
2101
|
+
|
|
2102
|
+
######################################################################{{{2
|
|
2103
|
+
# Core set data create/freeze/dup primitives
|
|
2104
|
+
|
|
2105
|
+
def new_set_data = []
|
|
2106
|
+
def freeze_set_data = set_data.each(&:freeze).freeze
|
|
2107
|
+
def dup_set_data = set_data.map { _1.dup }
|
|
2108
|
+
protected :dup_set_data
|
|
2109
|
+
|
|
2110
|
+
######################################################################{{{2
|
|
2111
|
+
# Core set data query/enumeration primitives
|
|
2112
|
+
|
|
2113
|
+
def min_num = minmaxes.first&.first
|
|
2114
|
+
def max_num = minmaxes.last&.last
|
|
2115
|
+
|
|
2116
|
+
def min_at(idx) = minmaxes[idx][0]
|
|
2117
|
+
def max_at(idx) = minmaxes[idx][1]
|
|
2118
|
+
|
|
2119
|
+
######################################################################{{{2
|
|
2120
|
+
# Core set data modification primitives
|
|
2121
|
+
|
|
2122
|
+
def set_min_at(idx, min) = minmaxes[idx][0] = min
|
|
2123
|
+
def set_max_at(idx, max) = minmaxes[idx][1] = max
|
|
2124
|
+
def replace_minmaxes(other) = minmaxes.replace(other)
|
|
2125
|
+
def append_minmax(min, max) = minmaxes << [min, max]
|
|
2126
|
+
def insert_minmax(idx, min, max) = minmaxes.insert idx, [min, max]
|
|
2127
|
+
def delete_run_at(idx) = runs.delete_at(idx)
|
|
2128
|
+
def slice_runs!(...) = runs.slice!(...)
|
|
2129
|
+
def truncate_runs!(idx) = runs.slice!(idx..)
|
|
2130
|
+
|
|
2131
|
+
######################################################################{{{2
|
|
2132
|
+
# Update methods
|
|
2133
|
+
|
|
1463
2134
|
def modifying!
|
|
1464
2135
|
if frozen?
|
|
1465
2136
|
raise FrozenError, "can't modify frozen #{self.class}: %p" % [self]
|
|
1466
2137
|
end
|
|
1467
2138
|
end
|
|
1468
2139
|
|
|
1469
|
-
def
|
|
1470
|
-
|
|
2140
|
+
def add_minmaxes(minmaxes)
|
|
2141
|
+
minmaxes.each do |minmax|
|
|
2142
|
+
add_minmax minmax
|
|
2143
|
+
end
|
|
2144
|
+
self
|
|
2145
|
+
end
|
|
2146
|
+
|
|
2147
|
+
def subtract_minmaxes(minmaxes)
|
|
2148
|
+
minmaxes.each do |minmax|
|
|
2149
|
+
subtract_minmax minmax
|
|
2150
|
+
end
|
|
2151
|
+
self
|
|
2152
|
+
end
|
|
1471
2153
|
|
|
1472
2154
|
#
|
|
1473
|
-
# --|=====| |=====new
|
|
1474
|
-
# ?????????-|=====new
|
|
2155
|
+
# --|=====| |=====new run=======| append
|
|
2156
|
+
# ?????????-|=====new run=======|-|===lower===|-- insert
|
|
1475
2157
|
#
|
|
1476
|
-
# |=====new
|
|
2158
|
+
# |=====new run=======|
|
|
1477
2159
|
# ---------??=======lower=======??--------------- noop
|
|
1478
2160
|
#
|
|
1479
2161
|
# ---------??===lower==|--|==| join remaining
|
|
1480
2162
|
# ---------??===lower==|--|==|----|===upper===|-- join until upper
|
|
1481
2163
|
# ---------??===lower==|--|==|--|=====upper===|-- join to upper
|
|
1482
|
-
def
|
|
2164
|
+
def add_minmax(minmax)
|
|
1483
2165
|
modifying!
|
|
1484
|
-
min, max
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
2166
|
+
min, max = minmax
|
|
2167
|
+
lower_idx = bsearch_index(min - 1)
|
|
2168
|
+
lmin, lmax = min_at(lower_idx), max_at(lower_idx) if lower_idx
|
|
2169
|
+
if lmin.nil? then append_minmax min, max
|
|
2170
|
+
elsif (max + 1) < lmin then insert_minmax lower_idx, min, max
|
|
2171
|
+
else add_coalesced_minmax(lower_idx, lmin, lmax, min, max)
|
|
1489
2172
|
end
|
|
1490
2173
|
end
|
|
1491
2174
|
|
|
1492
|
-
def
|
|
1493
|
-
return if
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
lower_idx
|
|
1497
|
-
return if
|
|
1498
|
-
tmax_adj =
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
2175
|
+
def add_coalesced_minmax(lower_idx, lmin, lmax, min, max)
|
|
2176
|
+
return if lmin <= min && max <= lmax
|
|
2177
|
+
set_min_at lower_idx, (lmin = min) if min < lmin
|
|
2178
|
+
set_max_at lower_idx, (lmax = max) if lmax < max
|
|
2179
|
+
next_idx = lower_idx + 1
|
|
2180
|
+
return if next_idx == runs.count
|
|
2181
|
+
tmax_adj = lmax + 1
|
|
2182
|
+
if (upper_idx = bsearch_index(tmax_adj))
|
|
2183
|
+
if tmax_adj < min_at(upper_idx)
|
|
2184
|
+
upper_idx -= 1
|
|
2185
|
+
else
|
|
2186
|
+
set_max_at lower_idx, max_at(upper_idx)
|
|
2187
|
+
end
|
|
1502
2188
|
end
|
|
1503
|
-
|
|
2189
|
+
slice_runs! next_idx..upper_idx
|
|
1504
2190
|
end
|
|
1505
2191
|
|
|
1506
|
-
# |====
|
|
2192
|
+
# |====subtracted run=======|
|
|
1507
2193
|
# --|====| no more 1. noop
|
|
1508
2194
|
# --|====|---------------------------|====lower====|-- 2. noop
|
|
1509
2195
|
# -------|======lower================|---------------- 3. split
|
|
@@ -1516,62 +2202,59 @@ module Net
|
|
|
1516
2202
|
# -------??=====lower====|--|====| no more 6. delete rest
|
|
1517
2203
|
# -------??=====lower====|--|====|---|====upper====|-- 7. delete until
|
|
1518
2204
|
# -------??=====lower====|--|====|--|=====upper====|-- 8. delete and trim
|
|
1519
|
-
def
|
|
2205
|
+
def subtract_minmax(minmax)
|
|
1520
2206
|
modifying!
|
|
1521
|
-
min, max
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
elsif max <
|
|
1526
|
-
|
|
2207
|
+
min, max = minmax
|
|
2208
|
+
idx = bsearch_index(min)
|
|
2209
|
+
lmin, lmax = min_at(idx), max_at(idx) if idx
|
|
2210
|
+
if lmin.nil? then nil # case 1.
|
|
2211
|
+
elsif max < lmin then nil # case 2.
|
|
2212
|
+
elsif max < lmax then trim_or_split_minmax idx, lmin, min, max
|
|
2213
|
+
else trim_or_delete_minmax idx, lmin, lmax, min, max
|
|
1527
2214
|
end
|
|
1528
2215
|
end
|
|
1529
2216
|
|
|
1530
|
-
def
|
|
1531
|
-
|
|
1532
|
-
|
|
2217
|
+
def trim_or_split_minmax(idx, lmin, tmin, tmax)
|
|
2218
|
+
set_min_at idx, tmax + 1
|
|
2219
|
+
if lmin < tmin # split
|
|
2220
|
+
insert_minmax idx, lmin, tmin - 1
|
|
1533
2221
|
end
|
|
1534
|
-
lower[0] = tmax + 1
|
|
1535
2222
|
end
|
|
1536
2223
|
|
|
1537
|
-
def
|
|
1538
|
-
if
|
|
1539
|
-
|
|
2224
|
+
def trim_or_delete_minmax(lower_idx, lmin, lmax, tmin, tmax)
|
|
2225
|
+
if lmin < tmin # trim lower
|
|
2226
|
+
lmax = set_max_at lower_idx, tmin - 1
|
|
1540
2227
|
lower_idx += 1
|
|
1541
2228
|
end
|
|
1542
|
-
if tmax ==
|
|
1543
|
-
|
|
1544
|
-
elsif (
|
|
1545
|
-
upper_idx
|
|
1546
|
-
|
|
2229
|
+
if tmax == lmax # case 5
|
|
2230
|
+
delete_run_at lower_idx
|
|
2231
|
+
elsif (upper_idx = bsearch_index(tmax + 1))
|
|
2232
|
+
if min_at(upper_idx) <= tmax # case 8
|
|
2233
|
+
set_min_at upper_idx, tmax + 1
|
|
2234
|
+
end
|
|
2235
|
+
slice_runs! lower_idx..upper_idx - 1 # cases 7 and 8
|
|
2236
|
+
else # case 6
|
|
2237
|
+
truncate_runs! lower_idx
|
|
1547
2238
|
end
|
|
1548
|
-
tuples.slice!(lower_idx..upper_idx)
|
|
1549
2239
|
end
|
|
1550
2240
|
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
def range_gte_to(num)
|
|
1556
|
-
first, last = tuples.bsearch { _2 >= num }
|
|
1557
|
-
first..last if first
|
|
1558
|
-
end
|
|
1559
|
-
|
|
1560
|
-
def nz_number(num)
|
|
1561
|
-
case num
|
|
1562
|
-
when Integer, /\A[1-9]\d*\z/ then num = Integer(num)
|
|
1563
|
-
else raise DataFormatError, "%p is not a valid nz-number" % [num]
|
|
1564
|
-
end
|
|
1565
|
-
NumValidator.ensure_nz_number(num)
|
|
1566
|
-
num
|
|
1567
|
-
end
|
|
2241
|
+
alias add_runs add_minmaxes
|
|
2242
|
+
alias add_run add_minmax
|
|
2243
|
+
alias subtract_runs subtract_minmaxes
|
|
2244
|
+
alias subtract_run subtract_minmax
|
|
1568
2245
|
|
|
2246
|
+
######################################################################{{{2
|
|
1569
2247
|
# intentionally defined after the class implementation
|
|
1570
2248
|
|
|
2249
|
+
FULL_SET_DATA = [[1, STAR_INT].freeze].freeze
|
|
2250
|
+
private_constant :FULL_SET_DATA
|
|
2251
|
+
|
|
1571
2252
|
EMPTY = new.freeze
|
|
1572
2253
|
FULL = self["1:*"]
|
|
1573
2254
|
private_constant :EMPTY, :FULL
|
|
1574
2255
|
|
|
2256
|
+
# }}}
|
|
2257
|
+
# vim:foldmethod=marker
|
|
1575
2258
|
end
|
|
1576
2259
|
end
|
|
1577
2260
|
end
|