net-imap 0.5.9 → 0.6.4
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/.document +3 -0
- data/.rdoc_options +7 -0
- data/Gemfile +7 -5
- data/README.md +1 -1
- data/lib/net/imap/command_data.rb +170 -80
- 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 +18 -6
- data/lib/net/imap/config/attr_version_defaults.rb +90 -0
- data/lib/net/imap/config.rb +244 -122
- data/lib/net/imap/connection_state.rb +1 -1
- data/lib/net/imap/data_encoding.rb +126 -27
- data/lib/net/imap/errors.rb +189 -0
- data/lib/net/imap/esearch_result.rb +48 -3
- data/lib/net/imap/flags.rb +1 -1
- data/lib/net/imap/response_data.rb +110 -14
- data/lib/net/imap/response_parser/parser_utils.rb +14 -23
- data/lib/net/imap/response_parser.rb +40 -17
- data/lib/net/imap/response_reader.rb +25 -16
- data/lib/net/imap/sasl/scram_authenticator.rb +74 -0
- data/lib/net/imap/search_result.rb +13 -4
- data/lib/net/imap/sequence_set.rb +715 -326
- data/lib/net/imap/uidplus_data.rb +2 -63
- data/lib/net/imap/vanished_data.rb +10 -1
- data/lib/net/imap.rb +201 -86
- data/net-imap.gemspec +1 -1
- data/rakelib/rdoc.rake +1 -18
- metadata +6 -4
- data/lib/net/imap/data_lite.rb +0 -226
|
@@ -84,7 +84,7 @@ module Net
|
|
|
84
84
|
#
|
|
85
85
|
# # Other inputs are normalized
|
|
86
86
|
# set = Net::IMAP::SequenceSet([1, 2, [3..7, 5], 6..10, 2048, 1024])
|
|
87
|
-
# set.valid_string #=> "1:10,
|
|
87
|
+
# set.valid_string #=> "1:10,1024,2048"
|
|
88
88
|
# set.frozen? #=> false
|
|
89
89
|
#
|
|
90
90
|
# unfrozen = set
|
|
@@ -107,7 +107,7 @@ module Net
|
|
|
107
107
|
#
|
|
108
108
|
# # Other inputs are normalized
|
|
109
109
|
# set = Net::IMAP::SequenceSet[1, 2, [3..7, 5], 6..10, 2048, 1024]
|
|
110
|
-
# set.valid_string #=> "1:10,
|
|
110
|
+
# set.valid_string #=> "1:10,1024,2048"
|
|
111
111
|
# set.frozen? #=> true
|
|
112
112
|
#
|
|
113
113
|
# frozen = set
|
|
@@ -145,6 +145,10 @@ module Net
|
|
|
145
145
|
# #entries and #elements are identical. Use #append to preserve #entries
|
|
146
146
|
# order while modifying a set.
|
|
147
147
|
#
|
|
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.
|
|
151
|
+
#
|
|
148
152
|
# == Using <tt>*</tt>
|
|
149
153
|
#
|
|
150
154
|
# \IMAP sequence sets may contain a special value <tt>"*"</tt>, which
|
|
@@ -179,7 +183,7 @@ module Net
|
|
|
179
183
|
#
|
|
180
184
|
# When a set includes <tt>*</tt>, some methods may have surprising behavior.
|
|
181
185
|
#
|
|
182
|
-
# For example, #complement treats <tt>*</tt> as its own
|
|
186
|
+
# For example, #complement treats <tt>*</tt> as its own member. This way,
|
|
183
187
|
# the #intersection of a set and its #complement will always be empty. And
|
|
184
188
|
# <tt>*</tt> is sorted as greater than any other number in the set. This is
|
|
185
189
|
# not how an \IMAP server interprets the set: it will convert <tt>*</tt> to
|
|
@@ -199,7 +203,7 @@ module Net
|
|
|
199
203
|
# (set.limit(max: 4) & (~set).limit(max: 4)).to_a => [4]
|
|
200
204
|
#
|
|
201
205
|
# When counting the number of numbers in a set, <tt>*</tt> will be counted
|
|
202
|
-
#
|
|
206
|
+
# as if it were equal to UINT32_MAX:
|
|
203
207
|
# UINT32_MAX = 2**32 - 1
|
|
204
208
|
# Net::IMAP::SequenceSet["*"].count => 1
|
|
205
209
|
# Net::IMAP::SequenceSet[1..UINT32_MAX - 1, :*].count => UINT32_MAX
|
|
@@ -208,6 +212,12 @@ module Net
|
|
|
208
212
|
# Net::IMAP::SequenceSet[UINT32_MAX, :*].count => 1
|
|
209
213
|
# Net::IMAP::SequenceSet[UINT32_MAX..].count => 1
|
|
210
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
|
+
#
|
|
211
221
|
# == What's here?
|
|
212
222
|
#
|
|
213
223
|
# SequenceSet provides methods for:
|
|
@@ -271,8 +281,10 @@ module Net
|
|
|
271
281
|
# occurrence in entries.
|
|
272
282
|
#
|
|
273
283
|
# <i>Set cardinality:</i>
|
|
274
|
-
# - #
|
|
275
|
-
#
|
|
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.
|
|
276
288
|
# - #empty?: Returns whether the set has no members. \IMAP syntax does not
|
|
277
289
|
# allow empty sequence sets.
|
|
278
290
|
# - #valid?: Returns whether the set has any members.
|
|
@@ -280,12 +292,18 @@ module Net
|
|
|
280
292
|
# <tt>*</tt>.
|
|
281
293
|
#
|
|
282
294
|
# <i>Denormalized properties:</i>
|
|
295
|
+
# - #normalized?: Returns whether #entries are sorted, deduplicated, and
|
|
296
|
+
# coalesced, and all #string entries are in normalized form.
|
|
283
297
|
# - #has_duplicates?: Returns whether the ordered entries repeat any
|
|
284
298
|
# numbers.
|
|
285
|
-
# - #
|
|
286
|
-
#
|
|
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.
|
|
287
302
|
# - #count_with_duplicates: Returns the count of numbers in the ordered
|
|
288
|
-
# 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.
|
|
289
307
|
#
|
|
290
308
|
# === Methods for Iterating
|
|
291
309
|
#
|
|
@@ -332,7 +350,7 @@ module Net
|
|
|
332
350
|
# given maximum value and removed all members over that maximum.
|
|
333
351
|
#
|
|
334
352
|
# === Methods for Assigning
|
|
335
|
-
# These methods add or replace
|
|
353
|
+
# These methods add or replace numbers in +self+.
|
|
336
354
|
#
|
|
337
355
|
# <i>Normalized (sorted and coalesced):</i>
|
|
338
356
|
#
|
|
@@ -341,8 +359,12 @@ module Net
|
|
|
341
359
|
# - #add (aliased as #<<): Adds a given element to the set; returns +self+.
|
|
342
360
|
# - #add?: If the given element is not fully included the set, adds it and
|
|
343
361
|
# returns +self+; otherwise, returns +nil+.
|
|
344
|
-
# - #merge: Adds all members of the given sets into
|
|
345
|
-
#
|
|
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+.
|
|
346
368
|
#
|
|
347
369
|
# <i>Order preserving:</i>
|
|
348
370
|
#
|
|
@@ -355,7 +377,7 @@ module Net
|
|
|
355
377
|
# of a given object.
|
|
356
378
|
#
|
|
357
379
|
# === Methods for Deleting
|
|
358
|
-
# These methods remove
|
|
380
|
+
# These methods remove numbers from +self+, and update #string to be fully
|
|
359
381
|
# sorted and coalesced.
|
|
360
382
|
#
|
|
361
383
|
# - #clear: Removes all elements in the set; returns +self+.
|
|
@@ -363,10 +385,12 @@ module Net
|
|
|
363
385
|
# - #delete?: If the given element is included in the set, removes it and
|
|
364
386
|
# returns it; otherwise, returns +nil+.
|
|
365
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+.
|
|
366
390
|
# - #slice!: Removes the number or consecutive numbers at a given offset or
|
|
367
391
|
# range of offsets.
|
|
368
|
-
# - #subtract: Removes all members of the given
|
|
369
|
-
# +self+.
|
|
392
|
+
# - #subtract: In-place set #difference. Removes all members of the given
|
|
393
|
+
# sets from this set; returns +self+.
|
|
370
394
|
# - #limit!: Replaces <tt>*</tt> with a given maximum value and removes all
|
|
371
395
|
# members over that maximum; returns +self+.
|
|
372
396
|
#
|
|
@@ -396,6 +420,23 @@ module Net
|
|
|
396
420
|
STARS = [:*, ?*, -1].freeze
|
|
397
421
|
private_constant :STARS
|
|
398
422
|
|
|
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
|
|
439
|
+
|
|
399
440
|
class << self
|
|
400
441
|
|
|
401
442
|
# :call-seq:
|
|
@@ -427,14 +468,14 @@ module Net
|
|
|
427
468
|
# +to_sequence_set+, calls +obj.to_sequence_set+ and returns the result.
|
|
428
469
|
# Otherwise returns +nil+.
|
|
429
470
|
#
|
|
430
|
-
# If +obj.to_sequence_set+ doesn't return a SequenceSet
|
|
431
|
-
# raised.
|
|
471
|
+
# If +obj.to_sequence_set+ doesn't return a SequenceSet or +nil+, an
|
|
472
|
+
# exception is raised.
|
|
432
473
|
#
|
|
433
474
|
# Related: Net::IMAP::SequenceSet(), ::new, ::[]
|
|
434
475
|
def try_convert(obj)
|
|
435
476
|
return obj if obj.is_a?(SequenceSet)
|
|
436
477
|
return nil unless obj.respond_to?(:to_sequence_set)
|
|
437
|
-
obj = obj.to_sequence_set
|
|
478
|
+
return nil unless obj = obj.to_sequence_set
|
|
438
479
|
return obj if obj.is_a?(SequenceSet)
|
|
439
480
|
raise DataFormatError, "invalid object returned from to_sequence_set"
|
|
440
481
|
end
|
|
@@ -465,7 +506,7 @@ module Net
|
|
|
465
506
|
# set = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
|
|
466
507
|
# set.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
|
|
467
508
|
# set = Net::IMAP::SequenceSet.new(1, 2, 3..7, 5, 6..10, 2048, 1024)
|
|
468
|
-
# set.valid_string #=> "1:10,
|
|
509
|
+
# set.valid_string #=> "1:10,1024,2048"
|
|
469
510
|
#
|
|
470
511
|
# With no arguments (or +nil+) creates an empty sequence set. Note that
|
|
471
512
|
# an empty sequence set is invalid in the \IMAP grammar.
|
|
@@ -514,12 +555,17 @@ module Net
|
|
|
514
555
|
# combined with set operations (#|, #&, #^, #-, etc) to make new sets.
|
|
515
556
|
#
|
|
516
557
|
# See SequenceSet@Creating+sequence+sets.
|
|
517
|
-
def initialize(input = nil)
|
|
558
|
+
def initialize(input = nil)
|
|
559
|
+
@set_data = new_set_data
|
|
560
|
+
@string = nil
|
|
561
|
+
replace(input) unless input.nil?
|
|
562
|
+
end
|
|
518
563
|
|
|
519
564
|
# Removes all elements and returns self.
|
|
520
565
|
def clear
|
|
521
|
-
modifying! # redundant check
|
|
522
|
-
|
|
566
|
+
modifying! # redundant check (normalizes the error message for JRuby)
|
|
567
|
+
set_data.clear
|
|
568
|
+
@string = nil
|
|
523
569
|
self
|
|
524
570
|
end
|
|
525
571
|
|
|
@@ -530,7 +576,10 @@ module Net
|
|
|
530
576
|
# accepted by ::new.
|
|
531
577
|
def replace(other)
|
|
532
578
|
case other
|
|
533
|
-
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)
|
|
534
583
|
when String then self.string = other
|
|
535
584
|
else clear; merge other
|
|
536
585
|
end
|
|
@@ -559,45 +608,51 @@ module Net
|
|
|
559
608
|
# If the set was created from a single string, it is not normalized. If
|
|
560
609
|
# the set is updated the string will be normalized.
|
|
561
610
|
#
|
|
562
|
-
# Related: #valid_string, #normalized_string, #to_s
|
|
563
|
-
def string; @string
|
|
611
|
+
# Related: #valid_string, #normalized_string, #to_s, #inspect
|
|
612
|
+
def string; @string || normalized_string if valid? end
|
|
564
613
|
|
|
565
614
|
# Returns an array with #normalized_string when valid and an empty array
|
|
566
615
|
# otherwise.
|
|
567
616
|
def deconstruct; valid? ? [normalized_string] : [] end
|
|
568
617
|
|
|
569
|
-
# Assigns a new string to #string and resets #elements to match.
|
|
570
|
-
#
|
|
571
|
-
# The string is validated but not normalized.
|
|
618
|
+
# Assigns a new string to #string and resets #elements to match.
|
|
619
|
+
# Assigning +nil+ or an empty string are equivalent to calling #clear.
|
|
572
620
|
#
|
|
573
|
-
#
|
|
621
|
+
# Non-empty strings are validated but not normalized.
|
|
622
|
+
#
|
|
623
|
+
# Use #add, #merge, or #append to add a string to an existing set.
|
|
574
624
|
#
|
|
575
625
|
# Related: #replace, #clear
|
|
576
|
-
def string=(
|
|
577
|
-
if
|
|
626
|
+
def string=(input)
|
|
627
|
+
if input.nil?
|
|
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
|
|
578
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
|
|
579
639
|
else
|
|
580
|
-
|
|
581
|
-
str = String.try_convert(str) or raise ArgumentError, "not a string"
|
|
582
|
-
tuples = str_to_tuples str
|
|
583
|
-
@tuples, @string = [], -str
|
|
584
|
-
tuples_add tuples
|
|
640
|
+
raise ArgumentError, "expected a string or nil, got #{input.class}"
|
|
585
641
|
end
|
|
586
|
-
|
|
642
|
+
input
|
|
587
643
|
end
|
|
588
644
|
|
|
589
645
|
# Returns the \IMAP +sequence-set+ string representation, or an empty
|
|
590
646
|
# string when the set is empty. Note that an empty set is invalid in the
|
|
591
647
|
# \IMAP syntax.
|
|
592
648
|
#
|
|
593
|
-
# Related: #valid_string, #normalized_string, #
|
|
649
|
+
# Related: #string, #valid_string, #normalized_string, #inspect
|
|
594
650
|
def to_s; string || "" end
|
|
595
651
|
|
|
596
652
|
# Freezes and returns the set. A frozen SequenceSet is Ractor-safe.
|
|
597
653
|
def freeze
|
|
598
654
|
return self if frozen?
|
|
599
|
-
|
|
600
|
-
@tuples.each(&:freeze).freeze
|
|
655
|
+
freeze_set_data
|
|
601
656
|
super
|
|
602
657
|
end
|
|
603
658
|
|
|
@@ -619,7 +674,7 @@ module Net
|
|
|
619
674
|
# Related: #eql?, #normalize
|
|
620
675
|
def ==(other)
|
|
621
676
|
self.class == other.class &&
|
|
622
|
-
(to_s == other.to_s ||
|
|
677
|
+
(to_s == other.to_s || set_data == other.set_data)
|
|
623
678
|
end
|
|
624
679
|
|
|
625
680
|
# :call-seq: eql?(other) -> true or false
|
|
@@ -660,7 +715,7 @@ module Net
|
|
|
660
715
|
# object that would be accepted by ::new.
|
|
661
716
|
#
|
|
662
717
|
# Related: #===, #include?, #include_star?, #intersect?
|
|
663
|
-
def cover?(other)
|
|
718
|
+
def cover?(other) import_runs(other).none? { !include_run?(_1) } end
|
|
664
719
|
|
|
665
720
|
# Returns +true+ when a given number or range is in +self+, and +false+
|
|
666
721
|
# otherwise. Returns +nil+ when +number+ isn't a valid SequenceSet
|
|
@@ -687,14 +742,14 @@ module Net
|
|
|
687
742
|
#
|
|
688
743
|
# Related: #include_star?, #cover?, #===, #intersect?
|
|
689
744
|
def include?(element)
|
|
690
|
-
|
|
691
|
-
!!
|
|
745
|
+
run = import_run element rescue nil
|
|
746
|
+
!!include_run?(run) if run
|
|
692
747
|
end
|
|
693
748
|
|
|
694
749
|
alias member? include?
|
|
695
750
|
|
|
696
751
|
# Returns +true+ when the set contains <tt>*</tt>.
|
|
697
|
-
def include_star?;
|
|
752
|
+
def include_star?; max_num == STAR_INT end
|
|
698
753
|
|
|
699
754
|
# Returns +true+ if the set and a given object have any common elements,
|
|
700
755
|
# +false+ otherwise.
|
|
@@ -704,7 +759,7 @@ module Net
|
|
|
704
759
|
#
|
|
705
760
|
# Related: #intersection, #disjoint?, #cover?, #include?
|
|
706
761
|
def intersect?(other)
|
|
707
|
-
valid? &&
|
|
762
|
+
valid? && import_runs(other).any? { intersect_run? _1 }
|
|
708
763
|
end
|
|
709
764
|
alias overlap? intersect?
|
|
710
765
|
|
|
@@ -716,7 +771,7 @@ module Net
|
|
|
716
771
|
#
|
|
717
772
|
# Related: #intersection, #intersect?
|
|
718
773
|
def disjoint?(other)
|
|
719
|
-
empty? ||
|
|
774
|
+
empty? || import_runs(other).none? { intersect_run? _1 }
|
|
720
775
|
end
|
|
721
776
|
|
|
722
777
|
# :call-seq:
|
|
@@ -733,8 +788,12 @@ module Net
|
|
|
733
788
|
# Related: #min, #minmax, #slice
|
|
734
789
|
def max(count = nil, star: :*)
|
|
735
790
|
if count
|
|
736
|
-
|
|
737
|
-
|
|
791
|
+
if cardinality <= count
|
|
792
|
+
frozen? ? self : dup
|
|
793
|
+
else
|
|
794
|
+
slice(-count..) || remain_frozen_empty
|
|
795
|
+
end
|
|
796
|
+
elsif (val = max_num)
|
|
738
797
|
val == STAR_INT ? star : val
|
|
739
798
|
end
|
|
740
799
|
end
|
|
@@ -754,7 +813,7 @@ module Net
|
|
|
754
813
|
def min(count = nil, star: :*)
|
|
755
814
|
if count
|
|
756
815
|
slice(0...count) || remain_frozen_empty
|
|
757
|
-
elsif (val =
|
|
816
|
+
elsif (val = min_num)
|
|
758
817
|
val != STAR_INT ? val : star
|
|
759
818
|
end
|
|
760
819
|
end
|
|
@@ -772,10 +831,10 @@ module Net
|
|
|
772
831
|
def valid?; !empty? end
|
|
773
832
|
|
|
774
833
|
# Returns true if the set contains no elements
|
|
775
|
-
def empty?;
|
|
834
|
+
def empty?; runs.empty? end
|
|
776
835
|
|
|
777
836
|
# Returns true if the set contains every possible element.
|
|
778
|
-
def full?;
|
|
837
|
+
def full?; set_data == FULL_SET_DATA end
|
|
779
838
|
|
|
780
839
|
# :call-seq:
|
|
781
840
|
# self + other -> sequence set
|
|
@@ -851,9 +910,7 @@ module Net
|
|
|
851
910
|
# * <tt>lhs - (lhs - rhs)</tt>
|
|
852
911
|
# * <tt>lhs - (lhs ^ rhs)</tt>
|
|
853
912
|
# * <tt>lhs ^ (lhs - rhs)</tt>
|
|
854
|
-
def &(other)
|
|
855
|
-
remain_frozen dup.subtract SequenceSet.new(other).complement!
|
|
856
|
-
end
|
|
913
|
+
def &(other) remain_frozen dup.intersect! other end
|
|
857
914
|
alias intersection :&
|
|
858
915
|
|
|
859
916
|
# :call-seq:
|
|
@@ -878,7 +935,7 @@ module Net
|
|
|
878
935
|
# * <tt>(lhs | rhs) - (lhs & rhs)</tt>
|
|
879
936
|
# * <tt>(lhs - rhs) | (rhs - lhs)</tt>
|
|
880
937
|
# * <tt>(lhs ^ other) ^ (other ^ rhs)</tt>
|
|
881
|
-
def ^(other) remain_frozen
|
|
938
|
+
def ^(other) remain_frozen dup.xor! other end
|
|
882
939
|
alias xor :^
|
|
883
940
|
|
|
884
941
|
# :call-seq:
|
|
@@ -917,8 +974,8 @@ module Net
|
|
|
917
974
|
#
|
|
918
975
|
# Related: #add?, #merge, #union, #append
|
|
919
976
|
def add(element)
|
|
920
|
-
modifying! # short-circuit before
|
|
921
|
-
|
|
977
|
+
modifying! # short-circuit before import_run
|
|
978
|
+
add_run import_run element
|
|
922
979
|
normalize!
|
|
923
980
|
end
|
|
924
981
|
alias << add
|
|
@@ -928,16 +985,55 @@ module Net
|
|
|
928
985
|
# Unlike #add, #merge, or #union, the new value is appended to #string.
|
|
929
986
|
# This may result in a #string which has duplicates or is out-of-order.
|
|
930
987
|
#
|
|
931
|
-
#
|
|
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.
|
|
932
1009
|
#
|
|
933
1010
|
# Related: #add, #merge, #union
|
|
934
1011
|
def append(entry)
|
|
935
|
-
modifying! # short-circuit before
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
string
|
|
939
|
-
|
|
940
|
-
|
|
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}"
|
|
941
1037
|
self
|
|
942
1038
|
end
|
|
943
1039
|
|
|
@@ -963,8 +1059,8 @@ module Net
|
|
|
963
1059
|
#
|
|
964
1060
|
# Related: #delete?, #delete_at, #subtract, #difference
|
|
965
1061
|
def delete(element)
|
|
966
|
-
modifying! # short-circuit before
|
|
967
|
-
|
|
1062
|
+
modifying! # short-circuit before import_run
|
|
1063
|
+
subtract_run import_run element
|
|
968
1064
|
normalize!
|
|
969
1065
|
end
|
|
970
1066
|
|
|
@@ -1001,16 +1097,17 @@ module Net
|
|
|
1001
1097
|
#
|
|
1002
1098
|
# Related: #delete, #delete_at, #subtract, #difference, #disjoint?
|
|
1003
1099
|
def delete?(element)
|
|
1004
|
-
modifying! # short-circuit before
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
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
|
|
1009
1106
|
normalize!
|
|
1010
|
-
|
|
1107
|
+
export_num minmax.first
|
|
1011
1108
|
else
|
|
1012
1109
|
copy = dup
|
|
1013
|
-
|
|
1110
|
+
subtract_minmax minmax
|
|
1014
1111
|
normalize!
|
|
1015
1112
|
copy if copy.subtract(self).valid?
|
|
1016
1113
|
end
|
|
@@ -1047,8 +1144,8 @@ module Net
|
|
|
1047
1144
|
deleted
|
|
1048
1145
|
end
|
|
1049
1146
|
|
|
1050
|
-
# Merges all of the elements that appear in any of
|
|
1051
|
-
# 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+.
|
|
1052
1149
|
#
|
|
1053
1150
|
# The +sets+ may be any objects that would be accepted by ::new.
|
|
1054
1151
|
#
|
|
@@ -1056,19 +1153,20 @@ module Net
|
|
|
1056
1153
|
#
|
|
1057
1154
|
# Related: #add, #add?, #union
|
|
1058
1155
|
def merge(*sets)
|
|
1059
|
-
modifying! # short-circuit before
|
|
1060
|
-
|
|
1156
|
+
modifying! # short-circuit before import_runs
|
|
1157
|
+
add_runs import_runs sets
|
|
1061
1158
|
normalize!
|
|
1062
1159
|
end
|
|
1063
1160
|
|
|
1064
|
-
# Removes all of the elements that appear in
|
|
1065
|
-
# 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+.
|
|
1066
1163
|
#
|
|
1067
1164
|
# The +sets+ may be any objects that would be accepted by ::new.
|
|
1068
1165
|
#
|
|
1069
1166
|
# Related: #difference
|
|
1070
1167
|
def subtract(*sets)
|
|
1071
|
-
|
|
1168
|
+
modifying! # short-circuit before import_runs
|
|
1169
|
+
subtract_runs import_runs sets
|
|
1072
1170
|
normalize!
|
|
1073
1171
|
end
|
|
1074
1172
|
|
|
@@ -1160,7 +1258,7 @@ module Net
|
|
|
1160
1258
|
# Related: #entries, #each_element
|
|
1161
1259
|
def each_entry(&block) # :yields: integer or range or :*
|
|
1162
1260
|
return to_enum(__method__) unless block_given?
|
|
1163
|
-
|
|
1261
|
+
each_entry_run do yield export_run_entry _1 end
|
|
1164
1262
|
end
|
|
1165
1263
|
|
|
1166
1264
|
# Yields each number or range (or <tt>:*</tt>) in #elements to the block
|
|
@@ -1172,39 +1270,17 @@ module Net
|
|
|
1172
1270
|
# Related: #elements, #each_entry
|
|
1173
1271
|
def each_element # :yields: integer or range or :*
|
|
1174
1272
|
return to_enum(__method__) unless block_given?
|
|
1175
|
-
|
|
1176
|
-
self
|
|
1177
|
-
end
|
|
1178
|
-
|
|
1179
|
-
private
|
|
1180
|
-
|
|
1181
|
-
def each_entry_tuple(&block)
|
|
1182
|
-
return to_enum(__method__) unless block_given?
|
|
1183
|
-
if @string
|
|
1184
|
-
@string.split(",") do block.call str_to_tuple _1 end
|
|
1185
|
-
else
|
|
1186
|
-
@tuples.each(&block)
|
|
1187
|
-
end
|
|
1273
|
+
runs.each do yield export_run_entry _1 end
|
|
1188
1274
|
self
|
|
1189
1275
|
end
|
|
1190
1276
|
|
|
1191
|
-
def tuple_to_entry((min, max))
|
|
1192
|
-
if min == STAR_INT then :*
|
|
1193
|
-
elsif max == STAR_INT then min..
|
|
1194
|
-
elsif min == max then min
|
|
1195
|
-
else min..max
|
|
1196
|
-
end
|
|
1197
|
-
end
|
|
1198
|
-
|
|
1199
|
-
public
|
|
1200
|
-
|
|
1201
1277
|
# Yields each range in #ranges to the block and returns self.
|
|
1202
1278
|
# Returns an enumerator when called without a block.
|
|
1203
1279
|
#
|
|
1204
1280
|
# Related: #ranges
|
|
1205
1281
|
def each_range # :yields: range
|
|
1206
1282
|
return to_enum(__method__) unless block_given?
|
|
1207
|
-
|
|
1283
|
+
minmaxes.each do |min, max|
|
|
1208
1284
|
if min == STAR_INT then yield :*..
|
|
1209
1285
|
elsif max == STAR_INT then yield min..
|
|
1210
1286
|
else yield min..max
|
|
@@ -1223,7 +1299,7 @@ module Net
|
|
|
1223
1299
|
def each_number(&block) # :yields: integer
|
|
1224
1300
|
return to_enum(__method__) unless block_given?
|
|
1225
1301
|
raise RangeError, '%s contains "*"' % [self.class] if include_star?
|
|
1226
|
-
|
|
1302
|
+
minmaxes.each do each_number_in_minmax _1, _2, &block end
|
|
1227
1303
|
self
|
|
1228
1304
|
end
|
|
1229
1305
|
|
|
@@ -1237,16 +1313,7 @@ module Net
|
|
|
1237
1313
|
def each_ordered_number(&block)
|
|
1238
1314
|
return to_enum(__method__) unless block_given?
|
|
1239
1315
|
raise RangeError, '%s contains "*"' % [self.class] if include_star?
|
|
1240
|
-
|
|
1241
|
-
end
|
|
1242
|
-
|
|
1243
|
-
private def each_number_in_tuple(min, max, &block)
|
|
1244
|
-
if min == STAR_INT then yield :*
|
|
1245
|
-
elsif min == max then yield min
|
|
1246
|
-
elsif max != STAR_INT then (min..max).each(&block)
|
|
1247
|
-
else
|
|
1248
|
-
raise RangeError, "#{SequenceSet} cannot enumerate range with '*'"
|
|
1249
|
-
end
|
|
1316
|
+
each_entry_minmax do each_number_in_minmax _1, _2, &block end
|
|
1250
1317
|
end
|
|
1251
1318
|
|
|
1252
1319
|
# Returns a Set with all of the #numbers in the sequence set.
|
|
@@ -1258,35 +1325,103 @@ module Net
|
|
|
1258
1325
|
# Related: #elements, #ranges, #numbers
|
|
1259
1326
|
def to_set; Set.new(numbers) end
|
|
1260
1327
|
|
|
1261
|
-
# 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
|
|
1336
|
+
#
|
|
1337
|
+
# set = Net::IMAP::SequenceSet["4294967295,*"]
|
|
1338
|
+
# set.count #=> 1
|
|
1339
|
+
# set.cardinality #=> 2
|
|
1340
|
+
#
|
|
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
|
|
1262
1356
|
#
|
|
1263
|
-
#
|
|
1264
|
-
#
|
|
1357
|
+
# set = Net::IMAP::SequenceSet["4294967295,*"]
|
|
1358
|
+
# set.count #=> 1
|
|
1359
|
+
# set.cardinality #=> 2
|
|
1265
1360
|
#
|
|
1266
|
-
#
|
|
1361
|
+
# set = Net::IMAP::SequenceSet[1..]
|
|
1362
|
+
# set.count #=> 4294967295
|
|
1363
|
+
# set.cardinality #=> 4294967296
|
|
1364
|
+
#
|
|
1365
|
+
# Related: #cardinality, #count_with_duplicates
|
|
1267
1366
|
def count
|
|
1268
|
-
|
|
1269
|
-
(include_star? && include?(UINT32_MAX) ? -1 : 0)
|
|
1367
|
+
cardinality + (include_star? && include?(UINT32_MAX) ? -1 : 0)
|
|
1270
1368
|
end
|
|
1271
1369
|
|
|
1272
|
-
alias size count
|
|
1273
|
-
|
|
1274
1370
|
# Returns the count of numbers in the ordered #entries, including any
|
|
1275
1371
|
# repeated numbers.
|
|
1276
1372
|
#
|
|
1277
|
-
#
|
|
1278
|
-
#
|
|
1279
|
-
#
|
|
1280
|
-
#
|
|
1281
|
-
#
|
|
1282
|
-
#
|
|
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
|
|
1283
1393
|
def count_with_duplicates
|
|
1284
1394
|
return count unless @string
|
|
1285
|
-
|
|
1395
|
+
each_entry_minmax.sum {|min, max|
|
|
1286
1396
|
max - min + ((max == STAR_INT && min != STAR_INT) ? 0 : 1)
|
|
1287
1397
|
}
|
|
1288
1398
|
end
|
|
1289
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
|
+
|
|
1290
1425
|
# Returns the count of repeated numbers in the ordered #entries, the
|
|
1291
1426
|
# difference between #count_with_duplicates and #count.
|
|
1292
1427
|
#
|
|
@@ -1304,7 +1439,7 @@ module Net
|
|
|
1304
1439
|
#
|
|
1305
1440
|
# Always returns +false+ when #string is normalized.
|
|
1306
1441
|
#
|
|
1307
|
-
# Related: #entries, #count_with_duplicates, #count_duplicates
|
|
1442
|
+
# Related: #entries, #count_with_duplicates, #count_duplicates
|
|
1308
1443
|
def has_duplicates?
|
|
1309
1444
|
return false unless @string
|
|
1310
1445
|
count_with_duplicates != count
|
|
@@ -1315,10 +1450,10 @@ module Net
|
|
|
1315
1450
|
#
|
|
1316
1451
|
# Related: #[], #at, #find_ordered_index
|
|
1317
1452
|
def find_index(number)
|
|
1318
|
-
number =
|
|
1319
|
-
|
|
1453
|
+
number = import_num number
|
|
1454
|
+
each_minmax_with_index(minmaxes) do |min, max, idx_min|
|
|
1320
1455
|
number < min and return nil
|
|
1321
|
-
number <= max and return
|
|
1456
|
+
number <= max and return export_num(idx_min + (number - min))
|
|
1322
1457
|
end
|
|
1323
1458
|
nil
|
|
1324
1459
|
end
|
|
@@ -1328,38 +1463,15 @@ module Net
|
|
|
1328
1463
|
#
|
|
1329
1464
|
# Related: #find_index
|
|
1330
1465
|
def find_ordered_index(number)
|
|
1331
|
-
number =
|
|
1332
|
-
|
|
1466
|
+
number = import_num number
|
|
1467
|
+
each_minmax_with_index(each_entry_minmax) do |min, max, idx_min|
|
|
1333
1468
|
if min <= number && number <= max
|
|
1334
|
-
return
|
|
1469
|
+
return export_num(idx_min + (number - min))
|
|
1335
1470
|
end
|
|
1336
1471
|
end
|
|
1337
1472
|
nil
|
|
1338
1473
|
end
|
|
1339
1474
|
|
|
1340
|
-
private
|
|
1341
|
-
|
|
1342
|
-
def each_tuple_with_index(tuples)
|
|
1343
|
-
idx_min = 0
|
|
1344
|
-
tuples.each do |min, max|
|
|
1345
|
-
idx_max = idx_min + (max - min)
|
|
1346
|
-
yield min, max, idx_min, idx_max
|
|
1347
|
-
idx_min = idx_max + 1
|
|
1348
|
-
end
|
|
1349
|
-
idx_min
|
|
1350
|
-
end
|
|
1351
|
-
|
|
1352
|
-
def reverse_each_tuple_with_index(tuples)
|
|
1353
|
-
idx_max = -1
|
|
1354
|
-
tuples.reverse_each do |min, max|
|
|
1355
|
-
yield min, max, (idx_min = idx_max - (max - min)), idx_max
|
|
1356
|
-
idx_max = idx_min - 1
|
|
1357
|
-
end
|
|
1358
|
-
idx_max
|
|
1359
|
-
end
|
|
1360
|
-
|
|
1361
|
-
public
|
|
1362
|
-
|
|
1363
1475
|
# :call-seq: at(index) -> integer or nil
|
|
1364
1476
|
#
|
|
1365
1477
|
# Returns the number at the given +index+ in the sorted set, without
|
|
@@ -1370,7 +1482,7 @@ module Net
|
|
|
1370
1482
|
#
|
|
1371
1483
|
# Related: #[], #slice, #ordered_at
|
|
1372
1484
|
def at(index)
|
|
1373
|
-
|
|
1485
|
+
seek_number_in_minmaxes(minmaxes, index)
|
|
1374
1486
|
end
|
|
1375
1487
|
|
|
1376
1488
|
# :call-seq: ordered_at(index) -> integer or nil
|
|
@@ -1383,21 +1495,7 @@ module Net
|
|
|
1383
1495
|
#
|
|
1384
1496
|
# Related: #[], #slice, #ordered_at
|
|
1385
1497
|
def ordered_at(index)
|
|
1386
|
-
|
|
1387
|
-
end
|
|
1388
|
-
|
|
1389
|
-
private def lookup_number_by_tuple_index(tuples, index)
|
|
1390
|
-
index = Integer(index.to_int)
|
|
1391
|
-
if index.negative?
|
|
1392
|
-
reverse_each_tuple_with_index(tuples) do |min, max, idx_min, idx_max|
|
|
1393
|
-
idx_min <= index and return from_tuple_int(min + (index - idx_min))
|
|
1394
|
-
end
|
|
1395
|
-
else
|
|
1396
|
-
each_tuple_with_index(tuples) do |min, _, idx_min, idx_max|
|
|
1397
|
-
index <= idx_max and return from_tuple_int(min + (index - idx_min))
|
|
1398
|
-
end
|
|
1399
|
-
end
|
|
1400
|
-
nil
|
|
1498
|
+
seek_number_in_minmaxes(each_entry_minmax, index)
|
|
1401
1499
|
end
|
|
1402
1500
|
|
|
1403
1501
|
# :call-seq:
|
|
@@ -1448,37 +1546,6 @@ module Net
|
|
|
1448
1546
|
|
|
1449
1547
|
alias slice :[]
|
|
1450
1548
|
|
|
1451
|
-
private
|
|
1452
|
-
|
|
1453
|
-
def slice_length(start, length)
|
|
1454
|
-
start = Integer(start.to_int)
|
|
1455
|
-
length = Integer(length.to_int)
|
|
1456
|
-
raise ArgumentError, "length must be positive" unless length.positive?
|
|
1457
|
-
last = start + length - 1 unless start.negative? && start.abs <= length
|
|
1458
|
-
slice_range(start..last)
|
|
1459
|
-
end
|
|
1460
|
-
|
|
1461
|
-
def slice_range(range)
|
|
1462
|
-
first = range.begin || 0
|
|
1463
|
-
last = range.end || -1
|
|
1464
|
-
if range.exclude_end?
|
|
1465
|
-
return remain_frozen_empty if last.zero?
|
|
1466
|
-
last -= 1 if range.end && last != STAR_INT
|
|
1467
|
-
end
|
|
1468
|
-
if (first * last).positive? && last < first
|
|
1469
|
-
remain_frozen_empty
|
|
1470
|
-
elsif (min = at(first))
|
|
1471
|
-
max = at(last)
|
|
1472
|
-
max = :* if max.nil?
|
|
1473
|
-
if max == :* then self & (min..)
|
|
1474
|
-
elsif min <= max then self & (min..max)
|
|
1475
|
-
else remain_frozen_empty
|
|
1476
|
-
end
|
|
1477
|
-
end
|
|
1478
|
-
end
|
|
1479
|
-
|
|
1480
|
-
public
|
|
1481
|
-
|
|
1482
1549
|
# Returns a copy of +self+ which only contains the numbers above +num+.
|
|
1483
1550
|
#
|
|
1484
1551
|
# Net::IMAP::SequenceSet["5,10:22,50"].above(10) # to_s => "11:22,50"
|
|
@@ -1550,7 +1617,7 @@ module Net
|
|
|
1550
1617
|
#
|
|
1551
1618
|
# Related: #limit!
|
|
1552
1619
|
def limit(max:)
|
|
1553
|
-
max =
|
|
1620
|
+
max = import_num(max)
|
|
1554
1621
|
if empty? then self.class.empty
|
|
1555
1622
|
elsif !include_star? && max < min then self.class.empty
|
|
1556
1623
|
elsif max(star: STAR_INT) <= max then frozen? ? self : dup.freeze
|
|
@@ -1563,53 +1630,134 @@ module Net
|
|
|
1563
1630
|
#
|
|
1564
1631
|
# Related: #limit
|
|
1565
1632
|
def limit!(max:)
|
|
1566
|
-
modifying! # short-circuit
|
|
1633
|
+
modifying! # short-circuit before querying
|
|
1567
1634
|
star = include_star?
|
|
1568
|
-
max =
|
|
1569
|
-
|
|
1570
|
-
|
|
1635
|
+
max = import_num(max)
|
|
1636
|
+
subtract_minmax [max + 1, STAR_INT]
|
|
1637
|
+
add_minmax [max, max ] if star
|
|
1571
1638
|
normalize!
|
|
1572
1639
|
end
|
|
1573
1640
|
|
|
1574
1641
|
# :call-seq: complement! -> self
|
|
1575
1642
|
#
|
|
1576
|
-
#
|
|
1577
|
-
# 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.
|
|
1578
1646
|
#
|
|
1579
1647
|
# Related: #complement
|
|
1580
1648
|
def complement!
|
|
1581
|
-
modifying! # short-circuit
|
|
1649
|
+
modifying! # short-circuit before querying
|
|
1582
1650
|
return replace(self.class.full) if empty?
|
|
1583
1651
|
return clear if full?
|
|
1584
|
-
flat =
|
|
1652
|
+
flat = minmaxes.flat_map { [_1 - 1, _2 + 1] }
|
|
1585
1653
|
if flat.first < 1 then flat.shift else flat.unshift 1 end
|
|
1586
1654
|
if STAR_INT < flat.last then flat.pop else flat.push STAR_INT end
|
|
1587
|
-
|
|
1655
|
+
replace_minmaxes flat.each_slice(2).to_a
|
|
1588
1656
|
normalize!
|
|
1589
1657
|
end
|
|
1590
1658
|
|
|
1591
|
-
#
|
|
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.
|
|
1664
|
+
#
|
|
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.
|
|
1592
1744
|
#
|
|
1593
|
-
# The returned set's #string is sorted and deduplicated. Adjacent or
|
|
1594
|
-
# overlapping elements will be merged into a single larger range.
|
|
1595
1745
|
# See SequenceSet@Ordered+and+Normalized+sets.
|
|
1596
1746
|
#
|
|
1597
1747
|
# Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalize
|
|
1598
1748
|
# #=> Net::IMAP::SequenceSet["1:7,9:11"]
|
|
1599
1749
|
#
|
|
1600
|
-
# Related: #normalize!, #normalized_string
|
|
1750
|
+
# Related: #normalize!, #normalized_string, #normalized?
|
|
1601
1751
|
def normalize
|
|
1602
|
-
|
|
1603
|
-
return self if frozen? && str == string
|
|
1604
|
-
remain_frozen dup.instance_exec { @string = str&.-@; self }
|
|
1752
|
+
frozen? && normalized? ? self : remain_frozen(dup.normalize!)
|
|
1605
1753
|
end
|
|
1606
1754
|
|
|
1607
1755
|
# Resets #string to be sorted, deduplicated, and coalesced. Returns
|
|
1608
1756
|
# +self+. See SequenceSet@Ordered+and+Normalized+sets.
|
|
1609
1757
|
#
|
|
1610
|
-
# Related: #normalize, #normalized_string
|
|
1758
|
+
# Related: #normalize, #normalized_string, #normalized?
|
|
1611
1759
|
def normalize!
|
|
1612
|
-
modifying! # redundant check
|
|
1760
|
+
modifying! # redundant check (normalizes the error message for JRuby)
|
|
1613
1761
|
@string = nil
|
|
1614
1762
|
self
|
|
1615
1763
|
end
|
|
@@ -1623,18 +1771,53 @@ module Net
|
|
|
1623
1771
|
#
|
|
1624
1772
|
# Returns +nil+ when the set is empty.
|
|
1625
1773
|
#
|
|
1626
|
-
# Related: #normalize!, #normalize
|
|
1774
|
+
# Related: #normalize!, #normalize, #string, #to_s, #normalized?
|
|
1627
1775
|
def normalized_string
|
|
1628
|
-
|
|
1776
|
+
export_runs(runs) unless runs.empty?
|
|
1629
1777
|
end
|
|
1630
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
|
|
1631
1802
|
def inspect
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
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]
|
|
1636
1808
|
else
|
|
1637
|
-
|
|
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
|
+
]
|
|
1638
1821
|
end
|
|
1639
1822
|
end
|
|
1640
1823
|
|
|
@@ -1668,13 +1851,17 @@ module Net
|
|
|
1668
1851
|
|
|
1669
1852
|
# For YAML deserialization
|
|
1670
1853
|
def init_with(coder) # :nodoc:
|
|
1671
|
-
@
|
|
1854
|
+
@set_data = new_set_data
|
|
1672
1855
|
self.string = coder['string']
|
|
1673
1856
|
end
|
|
1674
1857
|
|
|
1858
|
+
# :stopdoc:
|
|
1675
1859
|
protected
|
|
1676
1860
|
|
|
1677
|
-
attr_reader :
|
|
1861
|
+
attr_reader :set_data
|
|
1862
|
+
|
|
1863
|
+
alias runs set_data
|
|
1864
|
+
alias minmaxes runs
|
|
1678
1865
|
|
|
1679
1866
|
private
|
|
1680
1867
|
|
|
@@ -1683,39 +1870,42 @@ module Net
|
|
|
1683
1870
|
|
|
1684
1871
|
# frozen clones are shallow copied
|
|
1685
1872
|
def initialize_clone(other)
|
|
1686
|
-
other.
|
|
1873
|
+
@set_data = other.dup_set_data unless other.frozen?
|
|
1874
|
+
super
|
|
1687
1875
|
end
|
|
1688
1876
|
|
|
1689
1877
|
def initialize_dup(other)
|
|
1690
|
-
|
|
1691
|
-
@tuples = other.tuples.map(&:dup)
|
|
1692
|
-
@string = other.string&.-@
|
|
1878
|
+
@set_data = other.dup_set_data
|
|
1693
1879
|
super
|
|
1694
1880
|
end
|
|
1695
1881
|
|
|
1696
|
-
|
|
1697
|
-
|
|
1882
|
+
######################################################################{{{2
|
|
1883
|
+
# Import methods
|
|
1884
|
+
|
|
1885
|
+
def import_minmax(input)
|
|
1886
|
+
entry = input_try_convert input
|
|
1698
1887
|
case entry
|
|
1699
|
-
when *STARS, Integer then [int =
|
|
1700
|
-
when Range then
|
|
1701
|
-
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)
|
|
1702
1891
|
else
|
|
1703
|
-
raise DataFormatError, "expected number or range, got %p" % [
|
|
1892
|
+
raise DataFormatError, "expected number or range, got %p" % [input]
|
|
1704
1893
|
end
|
|
1705
1894
|
end
|
|
1895
|
+
alias import_run import_minmax
|
|
1706
1896
|
|
|
1707
|
-
def
|
|
1708
|
-
set = input_try_convert
|
|
1897
|
+
def import_runs(input)
|
|
1898
|
+
set = input_try_convert input
|
|
1709
1899
|
case set
|
|
1710
|
-
when *STARS, Integer, Range then [
|
|
1711
|
-
when String then
|
|
1712
|
-
when SequenceSet then set.
|
|
1713
|
-
when Set then set.map { [
|
|
1714
|
-
when Array then set.flat_map {
|
|
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 }
|
|
1715
1905
|
when nil then []
|
|
1716
1906
|
else
|
|
1717
1907
|
raise DataFormatError, "expected nz-number, range, '*', Set, Array; " \
|
|
1718
|
-
"got %p" % [
|
|
1908
|
+
"got %p" % [input]
|
|
1719
1909
|
end
|
|
1720
1910
|
end
|
|
1721
1911
|
|
|
@@ -1728,9 +1918,17 @@ module Net
|
|
|
1728
1918
|
input
|
|
1729
1919
|
end
|
|
1730
1920
|
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
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 || :*)
|
|
1734
1932
|
last -= 1 if range.exclude_end? && range.end && last != STAR_INT
|
|
1735
1933
|
unless first <= last
|
|
1736
1934
|
raise DataFormatError, "invalid range for sequence-set: %p" % [range]
|
|
@@ -1738,67 +1936,260 @@ module Net
|
|
|
1738
1936
|
[first, last]
|
|
1739
1937
|
end
|
|
1740
1938
|
|
|
1741
|
-
def
|
|
1742
|
-
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
|
|
1946
|
+
|
|
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
|
|
1743
1980
|
|
|
1744
|
-
def
|
|
1745
|
-
def str_to_tuples(str) str.split(",", -1).map! { str_to_tuple _1 } end
|
|
1746
|
-
def str_to_tuple(str)
|
|
1981
|
+
def parse_entry(str)
|
|
1747
1982
|
raise DataFormatError, "invalid sequence set string" if str.empty?
|
|
1748
|
-
str.split(":", 2).map! {
|
|
1983
|
+
str.split(":", 2).map! { import_num _1 }
|
|
1749
1984
|
end
|
|
1750
1985
|
|
|
1751
|
-
|
|
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
|
|
1990
|
+
end
|
|
1991
|
+
|
|
1992
|
+
def normal_string?(str) normalized_entries? each_parsed_entry str end
|
|
1993
|
+
|
|
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
|
|
1752
2003
|
|
|
1753
|
-
|
|
1754
|
-
|
|
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
|
|
1755
2029
|
range.include?(min) || range.include?(max) || (min..max).cover?(range)
|
|
1756
2030
|
end
|
|
1757
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
|
+
|
|
1758
2134
|
def modifying!
|
|
1759
2135
|
if frozen?
|
|
1760
2136
|
raise FrozenError, "can't modify frozen #{self.class}: %p" % [self]
|
|
1761
2137
|
end
|
|
1762
2138
|
end
|
|
1763
2139
|
|
|
1764
|
-
def
|
|
1765
|
-
|
|
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
|
|
1766
2153
|
|
|
1767
2154
|
#
|
|
1768
|
-
# --|=====| |=====new
|
|
1769
|
-
# ?????????-|=====new
|
|
2155
|
+
# --|=====| |=====new run=======| append
|
|
2156
|
+
# ?????????-|=====new run=======|-|===lower===|-- insert
|
|
1770
2157
|
#
|
|
1771
|
-
# |=====new
|
|
2158
|
+
# |=====new run=======|
|
|
1772
2159
|
# ---------??=======lower=======??--------------- noop
|
|
1773
2160
|
#
|
|
1774
2161
|
# ---------??===lower==|--|==| join remaining
|
|
1775
2162
|
# ---------??===lower==|--|==|----|===upper===|-- join until upper
|
|
1776
2163
|
# ---------??===lower==|--|==|--|=====upper===|-- join to upper
|
|
1777
|
-
def
|
|
2164
|
+
def add_minmax(minmax)
|
|
1778
2165
|
modifying!
|
|
1779
|
-
min, max
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
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)
|
|
1784
2172
|
end
|
|
1785
2173
|
end
|
|
1786
2174
|
|
|
1787
|
-
def
|
|
1788
|
-
return if
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
lower_idx
|
|
1792
|
-
return if
|
|
1793
|
-
tmax_adj =
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
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
|
|
1797
2188
|
end
|
|
1798
|
-
|
|
2189
|
+
slice_runs! next_idx..upper_idx
|
|
1799
2190
|
end
|
|
1800
2191
|
|
|
1801
|
-
# |====
|
|
2192
|
+
# |====subtracted run=======|
|
|
1802
2193
|
# --|====| no more 1. noop
|
|
1803
2194
|
# --|====|---------------------------|====lower====|-- 2. noop
|
|
1804
2195
|
# -------|======lower================|---------------- 3. split
|
|
@@ -1811,61 +2202,59 @@ module Net
|
|
|
1811
2202
|
# -------??=====lower====|--|====| no more 6. delete rest
|
|
1812
2203
|
# -------??=====lower====|--|====|---|====upper====|-- 7. delete until
|
|
1813
2204
|
# -------??=====lower====|--|====|--|=====upper====|-- 8. delete and trim
|
|
1814
|
-
def
|
|
2205
|
+
def subtract_minmax(minmax)
|
|
1815
2206
|
modifying!
|
|
1816
|
-
min, max
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
elsif max <
|
|
1821
|
-
|
|
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
|
|
1822
2214
|
end
|
|
1823
2215
|
end
|
|
1824
2216
|
|
|
1825
|
-
def
|
|
1826
|
-
|
|
1827
|
-
|
|
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
|
|
1828
2221
|
end
|
|
1829
|
-
lower[0] = tmax + 1
|
|
1830
2222
|
end
|
|
1831
2223
|
|
|
1832
|
-
def
|
|
1833
|
-
if
|
|
1834
|
-
|
|
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
|
|
1835
2227
|
lower_idx += 1
|
|
1836
2228
|
end
|
|
1837
|
-
if tmax ==
|
|
1838
|
-
|
|
1839
|
-
elsif (
|
|
1840
|
-
upper_idx
|
|
1841
|
-
|
|
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
|
|
1842
2238
|
end
|
|
1843
|
-
tuples.slice!(lower_idx..upper_idx)
|
|
1844
|
-
end
|
|
1845
|
-
|
|
1846
|
-
def tuple_gte_with_index(num)
|
|
1847
|
-
idx = tuples.bsearch_index { _2 >= num } and [tuples[idx], idx]
|
|
1848
2239
|
end
|
|
1849
2240
|
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
def nz_number(num)
|
|
1856
|
-
String === num && !/\A[1-9]\d*\z/.match?(num) and
|
|
1857
|
-
raise DataFormatError, "%p is not a valid nz-number" % [num]
|
|
1858
|
-
NumValidator.ensure_nz_number Integer num
|
|
1859
|
-
rescue TypeError # To catch errors from Integer()
|
|
1860
|
-
raise DataFormatError, $!.message
|
|
1861
|
-
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
|
|
1862
2245
|
|
|
2246
|
+
######################################################################{{{2
|
|
1863
2247
|
# intentionally defined after the class implementation
|
|
1864
2248
|
|
|
2249
|
+
FULL_SET_DATA = [[1, STAR_INT].freeze].freeze
|
|
2250
|
+
private_constant :FULL_SET_DATA
|
|
2251
|
+
|
|
1865
2252
|
EMPTY = new.freeze
|
|
1866
2253
|
FULL = self["1:*"]
|
|
1867
2254
|
private_constant :EMPTY, :FULL
|
|
1868
2255
|
|
|
2256
|
+
# }}}
|
|
2257
|
+
# vim:foldmethod=marker
|
|
1869
2258
|
end
|
|
1870
2259
|
end
|
|
1871
2260
|
end
|