net-imap 0.6.6 → 0.6.8

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.
@@ -2,6 +2,8 @@
2
2
 
3
3
  require "set" unless defined?(::Set)
4
4
 
5
+ require_relative "utils"
6
+
5
7
  module Net
6
8
  class IMAP
7
9
 
@@ -256,6 +258,20 @@ module Net
256
258
  # - #disjoint?:
257
259
  # Returns whether +self+ and a given object have no common elements.
258
260
  #
261
+ # <i>Comparison to a number:</i>
262
+ # - #all_above?:
263
+ # Returns whether every number in +self+ is greater than a given number.
264
+ # - #all_below?:
265
+ # Returns whether every number in +self+ is less than a given number.
266
+ # - #any_above?:
267
+ # Returns whether +self+ contains any numbers greater than a given number.
268
+ # - #any_below?:
269
+ # Returns whether +self+ contains any numbers less than a given number.
270
+ # - #none_above?:
271
+ # Returns whether +self+ contains no numbers greater than a given number.
272
+ # - #none_below?:
273
+ # Returns whether +self+ contains no numbers less than a given number.
274
+ #
259
275
  # === Methods for Querying
260
276
  # These methods do not modify +self+.
261
277
  #
@@ -345,7 +361,7 @@ module Net
345
361
  # - #above: Return a copy of +self+ which only contains numbers above a
346
362
  # given number.
347
363
  # - #below: Return a copy of +self+ which only contains numbers below a
348
- # given value.
364
+ # given number.
349
365
  # - #limit: Returns a copy of +self+ which has replaced <tt>*</tt> with a
350
366
  # given maximum value and removed all members over that maximum.
351
367
  #
@@ -409,6 +425,8 @@ module Net
409
425
  # representation and returns +self+.
410
426
  #
411
427
  class SequenceSet
428
+ include Utils # :nodoc:
429
+
412
430
  # The largest possible non-zero unsigned 32-bit integer
413
431
  UINT32_MAX = 2**32 - 1
414
432
 
@@ -774,6 +792,225 @@ module Net
774
792
  empty? || import_runs(other).none? { intersect_run? _1 }
775
793
  end
776
794
 
795
+ # :call-seq:
796
+ # all_above?(number) -> true or false
797
+ #
798
+ # Returns whether the set only contains numbers greater than +number+ (not
799
+ # inclusive of +number+).
800
+ #
801
+ # Net::IMAP::SequenceSet["5:8"].all_above?(9) => true
802
+ # Net::IMAP::SequenceSet["5:8"].all_above?(7) => false
803
+ # Net::IMAP::SequenceSet["5:8"].all_above?(5) => false
804
+ #
805
+ # <tt>"*"</tt> is evaluated as +UINT32_MAX+:
806
+ # Net::IMAP::SequenceSet["*"].all_above?(UINT32_MAX - 1) => true
807
+ # Net::IMAP::SequenceSet["*"].all_above?(UINT32_MAX) => false
808
+ #
809
+ # This is roughly equivalent to several other comparisons:
810
+ #
811
+ # # Given the following assumptions:
812
+ # set.empty? => false
813
+ # set.include_star? => false
814
+ # number => ^(1...UINT32_MAX)
815
+ #
816
+ # # Then these expressions will always return the same result:
817
+ # set.all_above?(number) => result
818
+ # set.none_below?(number + 1) => ^result
819
+ # !set.any_below?(number + 1) => ^result
820
+ #
821
+ # set == set.above(number) => ^result
822
+ # set.disjoint?(..number) => ^result
823
+ # number < set.min => ^result
824
+ #
825
+ # Related: {other comparison methods}[rdoc-ref:SequenceSet@Methods+for+Comparing],
826
+ # #above
827
+ def all_above?(number)
828
+ number = nz_number(number)
829
+ empty? || number < min(star: UINT32_MAX)
830
+ end
831
+
832
+ # :call-seq:
833
+ # all_below?(number) -> true or false
834
+ #
835
+ # Returns whether the set only contains numbers less than +number+ (not
836
+ # inclusive of +number+).
837
+ #
838
+ # Net::IMAP::SequenceSet["5:8"].all_below?(9) => true
839
+ # Net::IMAP::SequenceSet["5:8"].all_below?(8) => false
840
+ # Net::IMAP::SequenceSet["5:8"].all_below?(5) => false
841
+ #
842
+ # <tt>"*"</tt> is evaluated as +UINT32_MAX+:
843
+ # Net::IMAP::SequenceSet["*"].all_below?(UINT32_MAX) => false
844
+ #
845
+ # This is roughly equivalent to several other comparisons:
846
+ #
847
+ # # Given the following assumptions:
848
+ # set.empty? => false
849
+ # set.include_star? => false
850
+ # number => ^(2..UINT32_MAX)
851
+ #
852
+ # # Then these expressions will always return the same result:
853
+ # set.all_below?(number) => result
854
+ # set.none_above?(number - 1) => ^result
855
+ # !set.any_above?(number - 1) => ^result
856
+ #
857
+ # set == set.below(number) => ^result
858
+ # set.disjoint?(number..) => ^result
859
+ # set.max < number => ^result
860
+ #
861
+ # Related: {other comparison methods}[rdoc-ref:SequenceSet@Methods+for+Comparing],
862
+ # #below
863
+ def all_below?(number)
864
+ number = nz_number(number)
865
+ empty? || max(star: UINT32_MAX) < number
866
+ end
867
+
868
+ # :call-seq:
869
+ # any_above?(number) -> true or false
870
+ #
871
+ # Returns whether the set contains any numbers greater than +number+ (not
872
+ # inclusive of +number+).
873
+ #
874
+ # Net::IMAP::SequenceSet["5:8"].any_above?(1) => true
875
+ # Net::IMAP::SequenceSet["5:8"].any_above?(7) => true
876
+ # Net::IMAP::SequenceSet["5:8"].any_above?(8) => false
877
+ #
878
+ # <tt>"*"</tt> is evaluated as +UINT32_MAX+:
879
+ # Net::IMAP::SequenceSet["1:*"].any_above?(UINT32_MAX - 1) => true
880
+ # Net::IMAP::SequenceSet["1:*"].any_above?(UINT32_MAX) => false
881
+ #
882
+ # This is roughly equivalent to several other comparisons:
883
+ #
884
+ # # Given the following assumptions:
885
+ # set.empty? => false
886
+ # set.include_star? => false
887
+ # number => ^(1...UINT32_MAX)
888
+ #
889
+ # # Then these expressions will always return the same result:
890
+ # set.any_above?(number) => result
891
+ # !set.none_above?(number) => ^result
892
+ # !set.all_below?(number + 1) => ^result
893
+ #
894
+ # set.cover?(set.above(number)) => ^result
895
+ # set.intersect?(number + 1..) => ^result
896
+ # number < set.max => ^result
897
+ #
898
+ # Related: {other comparison methods}[rdoc-ref:SequenceSet@Methods+for+Comparing],
899
+ # #above
900
+ def any_above?(number)
901
+ number = nz_number(number)
902
+ valid? && number < max(star: UINT32_MAX)
903
+ end
904
+
905
+ # :call-seq:
906
+ # any_below?(number) -> true or false
907
+ #
908
+ # Returns whether the set contains any numbers less than +number+ (not
909
+ # inclusive of +number+).
910
+ #
911
+ # Net::IMAP::SequenceSet["5:8"].any_below? 5 => false
912
+ # Net::IMAP::SequenceSet["5:8"].any_below? 6 => true
913
+ # Net::IMAP::SequenceSet["5:8"].any_below? 9 => true
914
+ #
915
+ # <tt>"*"</tt> is evaluated as +UINT32_MAX+:
916
+ # Net::IMAP::SequenceSet["*"].any_below?(UINT32_MAX) => false
917
+ #
918
+ # This is roughly equivalent to several other comparisons:
919
+ #
920
+ # # Given the following assumptions:
921
+ # set.empty? => false
922
+ # set.include_star? => false
923
+ # number => ^(2..UINT32_MAX)
924
+ #
925
+ # # Then these expressions will always return the same result:
926
+ # set.any_below?(number) => result
927
+ # !set.none_below?(number) => ^result
928
+ # !set.all_above?(number - 1) => ^result
929
+ #
930
+ # set.cover?(set.below(number)) => ^result
931
+ # set.intersect?(...number) => ^result
932
+ # set.min < number => ^result
933
+ #
934
+ # Related: {other comparison methods}[rdoc-ref:SequenceSet@Methods+for+Comparing],
935
+ # #below
936
+ def any_below?(number)
937
+ number = nz_number(number)
938
+ valid? && min(star: UINT32_MAX) < number
939
+ end
940
+
941
+ # :call-seq:
942
+ # none_above?(number) -> true or false
943
+ #
944
+ # Returns whether the set contains no numbers greater than +number+ (not
945
+ # inclusive of +number+).
946
+ #
947
+ # Net::IMAP::SequenceSet["5:8"].none_above?(9) => true
948
+ # Net::IMAP::SequenceSet["5:8"].none_above?(8) => true
949
+ # Net::IMAP::SequenceSet["5:8"].none_above?(7) => false
950
+ #
951
+ # <tt>"*"</tt> is evaluated as +UINT32_MAX+:
952
+ # Net::IMAP::SequenceSet["*"].none_above?(UINT32_MAX - 1) => false
953
+ # Net::IMAP::SequenceSet["*"].none_above?(UINT32_MAX) => true
954
+ #
955
+ # This is roughly equivalent to several other comparisons:
956
+ #
957
+ # # Given the following assumptions:
958
+ # set.empty? => false
959
+ # set.include_star? => false
960
+ # number => ^(1...UINT32_MAX)
961
+ #
962
+ # # Then these expressions will always return the same result:
963
+ # set.none_above?(number) => result
964
+ # !set.any_above?(number) => ^result
965
+ # set.all_below?(number + 1) => ^result
966
+ #
967
+ # set.above(number).empty? => ^result
968
+ # set.disjoint?(number..) => ^result
969
+ # set.max <= number => ^result
970
+ #
971
+ # Related: {other comparison methods}[rdoc-ref:SequenceSet@Methods+for+Comparing],
972
+ # #above
973
+ def none_above?(number)
974
+ number = nz_number(number)
975
+ empty? || max(star: UINT32_MAX) <= number
976
+ end
977
+
978
+ # :call-seq:
979
+ # none_below?(number) -> true or false
980
+ #
981
+ # Returns whether the set contains no numbers less than +number+ (not
982
+ # inclusive of +number+).
983
+ #
984
+ # Net::IMAP::SequenceSet["5:8"].none_below?(4) => true
985
+ # Net::IMAP::SequenceSet["5:8"].none_below?(5) => true
986
+ # Net::IMAP::SequenceSet["5:8"].none_below?(6) => false
987
+ #
988
+ # <tt>"*"</tt> is evaluated as +UINT32_MAX+:
989
+ # Net::IMAP::SequenceSet["*"].none_below?(UINT32_MAX) => true
990
+ #
991
+ # This is roughly equivalent to several other comparisons:
992
+ #
993
+ # # Given the following assumptions:
994
+ # set.empty? => false
995
+ # set.include_star? => false
996
+ # number => ^(2..UINT32_MAX)
997
+ #
998
+ # # Then these expressions will always return the same result:
999
+ # set.none_below?(number) => result
1000
+ # !set.any_below?(number) => ^result
1001
+ # set.all_above?(number - 1) => ^result
1002
+ #
1003
+ # set.above(number).empty? => ^result
1004
+ # set.disjoint?(...number) => ^result
1005
+ # number <= set.min => ^result
1006
+ #
1007
+ # Related: {other comparison methods}[rdoc-ref:SequenceSet@Methods+for+Comparing],
1008
+ # #below
1009
+ def none_below?(number)
1010
+ number = nz_number(number)
1011
+ empty? || number <= min(star: UINT32_MAX)
1012
+ end
1013
+
777
1014
  # :call-seq:
778
1015
  # max(star: :*) => integer or star or nil
779
1016
  # max(count) => SequenceSet
@@ -786,8 +1023,11 @@ module Net
786
1023
  # is empty. (+star+ is ignored when +count+ is given.)
787
1024
  #
788
1025
  # Related: #min, #minmax, #slice
789
- def max(count = nil, star: :*)
790
- if count
1026
+ def max(count = (unset_count = true; nil), star: :*)
1027
+ if !unset_count
1028
+ count = implicit_int(count)
1029
+ raise ArgumentError, 'negative count' if count < 0
1030
+ return remain_frozen_empty if count == 0
791
1031
  if cardinality <= count
792
1032
  frozen? ? self : dup
793
1033
  else
@@ -810,8 +1050,10 @@ module Net
810
1050
  # is empty. (+star+ is ignored when +count+ is given.)
811
1051
  #
812
1052
  # Related: #max, #minmax, #slice
813
- def min(count = nil, star: :*)
814
- if count
1053
+ def min(count = (unset_count = true; nil), star: :*)
1054
+ if !unset_count
1055
+ count = implicit_int(count)
1056
+ raise ArgumentError, 'negative count' if count < 0
815
1057
  slice(0...count) || remain_frozen_empty
816
1058
  elsif (val = min_num)
817
1059
  val != STAR_INT ? val : star
@@ -1122,7 +1364,7 @@ module Net
1122
1364
  #
1123
1365
  # Related: #delete, #delete?, #slice!, #subtract, #difference
1124
1366
  def delete_at(index)
1125
- slice! Integer(index.to_int)
1367
+ slice! implicit_int index
1126
1368
  end
1127
1369
 
1128
1370
  # :call-seq:
@@ -1138,9 +1380,10 @@ module Net
1138
1380
  # #string will be regenerated after deletion.
1139
1381
  #
1140
1382
  # Related: #slice, #delete_at, #delete, #delete?, #subtract, #difference
1141
- def slice!(index, length = nil)
1383
+ def slice!(index, length = (length_unset = true; nil))
1142
1384
  modifying! # short-circuit before slice
1143
- deleted = slice(index, length) and subtract deleted
1385
+ deleted = length_unset ? slice(index) : slice(index, length)
1386
+ subtract deleted if deleted
1144
1387
  deleted
1145
1388
  end
1146
1389
 
@@ -1453,7 +1696,7 @@ module Net
1453
1696
  number = import_num number
1454
1697
  each_minmax_with_index(minmaxes) do |min, max, idx_min|
1455
1698
  number < min and return nil
1456
- number <= max and return export_num(idx_min + (number - min))
1699
+ number <= max and return idx_min + (number - min)
1457
1700
  end
1458
1701
  nil
1459
1702
  end
@@ -1466,7 +1709,7 @@ module Net
1466
1709
  number = import_num number
1467
1710
  each_minmax_with_index(each_entry_minmax) do |min, max, idx_min|
1468
1711
  if min <= number && number <= max
1469
- return export_num(idx_min + (number - min))
1712
+ return idx_min + (number - min)
1470
1713
  end
1471
1714
  end
1472
1715
  nil
@@ -1482,7 +1725,7 @@ module Net
1482
1725
  #
1483
1726
  # Related: #[], #slice, #ordered_at
1484
1727
  def at(index)
1485
- seek_number_in_minmaxes(minmaxes, index)
1728
+ export_num sorted_set_num_at implicit_int index
1486
1729
  end
1487
1730
 
1488
1731
  # :call-seq: ordered_at(index) -> integer or nil
@@ -1495,22 +1738,55 @@ module Net
1495
1738
  #
1496
1739
  # Related: #[], #slice, #ordered_at
1497
1740
  def ordered_at(index)
1498
- seek_number_in_minmaxes(each_entry_minmax, index)
1741
+ export_num ordered_list_num_at implicit_int index
1499
1742
  end
1500
1743
 
1501
1744
  # :call-seq:
1502
1745
  # seqset[index] -> integer or :* or nil
1503
1746
  # slice(index) -> integer or :* or nil
1504
- # seqset[start, length] -> sequence set or nil
1505
- # slice(start, length) -> sequence set or nil
1747
+ # seqset[index, length] -> sequence set or nil
1748
+ # slice(index, length) -> sequence set or nil
1506
1749
  # seqset[range] -> sequence set or nil
1507
1750
  # slice(range) -> sequence set or nil
1508
1751
  #
1509
1752
  # Returns a number or a subset from the _sorted_ set, without modifying
1510
1753
  # the set.
1511
1754
  #
1755
+ # With a single +index+ argument, returns an integer or +:*+ or nil:
1756
+ # set = Net::IMAP::SequenceSet["10:15,20:23,26"]
1757
+ # set[0] #=> 10
1758
+ # set[-1] #=> 26
1759
+ #
1760
+ # With +index+ and +length+ arguments, returns a new SequenceSet or nil:
1761
+ # set = Net::IMAP::SequenceSet["10:15,20:23,26"]
1762
+ # set[1, 2] #=> Net::IMAP::SequenceSet["11:12"]
1763
+ # set[-2, 2] #=> Net::IMAP::SequenceSet["23,26"]
1764
+ #
1765
+ # With a single +range+ argument, returns a new SequenceSet or nil:
1766
+ # set = Net::IMAP::SequenceSet["10:15,20:23,26"]
1767
+ # set[0...2] #=> Net::IMAP::SequenceSet["10:11"]
1768
+ # set[0..2] #=> Net::IMAP::SequenceSet["11:12"]
1769
+ # set[0..-2] #=> Net::IMAP::SequenceSet["10:15,20:23"]
1770
+ # set[-6..6] #=> Net::IMAP::SequenceSet["15,20"]
1771
+ #
1772
+ # Note that the result is based on the sorted and de-duplicated set, not
1773
+ # on the ordered #entries in #string.
1774
+ #
1775
+ # set = Net::IMAP::SequenceSet["12,20:23,11:16,21"]
1776
+ # set[0] #=> 11
1777
+ # set[-1] #=> 23
1778
+ #
1779
+ # This behaves like <tt>Array#slice</tt> on a virtual array of all of the
1780
+ # monotonically sorted #numbers in +self+:
1781
+ # # WARNING: For illustration only. Do NOT do this with large sets.
1782
+ # sliced_array = seqset.numbers[*args] and
1783
+ # sliced_set = Net::IMAP::SequenceSet(sliced_array)
1784
+ # set.slice(*args) == sliced_set #=> true
1785
+ #
1786
+ # ==== Number lookup by index
1787
+ #
1512
1788
  # When an Integer argument +index+ is given, the number at offset +index+
1513
- # in the sorted set is returned:
1789
+ # in the sorted set is returned.
1514
1790
  #
1515
1791
  # set = Net::IMAP::SequenceSet["10:15,20:23,26"]
1516
1792
  # set[0] #=> 10
@@ -1521,24 +1797,86 @@ module Net
1521
1797
  # set = Net::IMAP::SequenceSet["10:15,20:23,26"]
1522
1798
  # set[-1] #=> 26
1523
1799
  # set[-3] #=> 22
1524
- # set[-6] #=> 15
1525
- #
1526
- # If +index+ is out of range, +nil+ is returned.
1800
+ # set[-11] #=> 10
1527
1801
  #
1802
+ # The range for +index+ is <tt>-cardinality...cardinality</tt>.
1803
+ # If +index+ is out of range, returns +nil+.
1528
1804
  # set = Net::IMAP::SequenceSet["10:15,20:23,26"]
1529
1805
  # set[11] #=> nil
1530
1806
  # set[-12] #=> nil
1531
1807
  #
1532
- # The result is based on the sorted and de-duplicated set, not on the
1533
- # ordered #entries in #string.
1808
+ # With a single Integer argument, this behaves identically to #at.
1534
1809
  #
1535
- # set = Net::IMAP::SequenceSet["12,20:23,11:16,21"]
1536
- # set[0] #=> 11
1537
- # set[-1] #=> 23
1810
+ # ==== Subset slice by index and length
1811
+ #
1812
+ # When two Integer arguments, +index+ and +length+ are given, returns a
1813
+ # new SequenceSet containing the +length+ successive numbers beginning at
1814
+ # offset +index+.:
1815
+ # set = Net::IMAP::SequenceSet["10:15,20:23,26"]
1816
+ # set[0, 2] #=> Net::IMAP::SequenceSet["10:11"]
1817
+ # set[1, 2] #=> Net::IMAP::SequenceSet["11:12"]
1818
+ #
1819
+ # If <tt>index + length</tt> is greater than #cardinality, returns all
1820
+ # elements from +index+ to the end:
1821
+ # set = Net::IMAP::SequenceSet["10:15,20:23,26"]
1822
+ # set[0, 15] #=> Net::IMAP::SequenceSet["10:15,20:23,26"]
1823
+ # set[5, 10] #=> Net::IMAP::SequenceSet["15,20:23,26"]
1824
+ # set[10, 5] #=> Net::IMAP::SequenceSet["26"]
1825
+ #
1826
+ # If +index+ is equal to #cardinality, returns a new empty SequenceSet.
1827
+ # set = Net::IMAP::SequenceSet["10:15,20:23,26"]
1828
+ # set[11, 5] #=> Net::IMAP::SequenceSet.empty
1829
+ #
1830
+ # If +length+ is negative, returns +nil+.
1831
+ # set = Net::IMAP::SequenceSet[1..10]
1832
+ # set[5, -1] #=> nil
1833
+ #
1834
+ # If +index+ is out of range (absolute value greater than #cardinality),
1835
+ # returns +nil+.
1836
+ #
1837
+ # If +index+ is in range and +length+ is zero, returns a new empty
1838
+ # SequenceSet.
1839
+ #
1840
+ # ==== Subset slice by index range
1841
+ #
1842
+ # When a single Range argument +range+ is given, returns a new SequenceSet
1843
+ # containing the successive numbers at the offsets indicated by +range+.
1844
+ #
1845
+ # set = Net::IMAP::SequenceSet["10:15,20:23,26"]
1846
+ # set[0...2] #=> Net::IMAP::SequenceSet["10:11"]
1847
+ # set[2..4] #=> Net::IMAP::SequenceSet["12:14"]
1848
+ #
1849
+ # set[-2..-1] #=> Net::IMAP::SequenceSet["23,26"]
1850
+ # set[-4...-2] #=> Net::IMAP::SequenceSet["21:22"]
1851
+ #
1852
+ # set[4..-4] #=> Net::IMAP::SequenceSet["14:15,20:21"]
1853
+ # set[-6...6] #=> Net::IMAP::SequenceSet["15"]
1538
1854
  #
1539
- # Related: #at
1540
- def [](index, length = nil)
1541
- if length then slice_length(index, length)
1855
+ # An end-less range slices until the last number, and a begin-less range
1856
+ # slices from the first number.
1857
+ # set = Net::IMAP::SequenceSet["10:15,20:23,26"]
1858
+ # set[..3] #=> Net::IMAP::SequenceSet["10:13"]
1859
+ # set[..-3] #=> Net::IMAP::SequenceSet["10:15,20:22"]
1860
+ # set[-2..] #=> Net::IMAP::SequenceSet["23,26"]
1861
+ # set[2..] #=> Net::IMAP::SequenceSet["12:15,20:23,26"]
1862
+ #
1863
+ # When +range.begin+ points to a smaller index than +range.end+, a new
1864
+ # empty SequenceSet is returned.
1865
+ #
1866
+ # set = Net::IMAP::SequenceSet[1..10]
1867
+ # set[5.. 4] #=> SequenceSet.empty
1868
+ # set[-4..-5] #=> SequenceSet.empty
1869
+ # set[5..-6] #=> SequenceSet.empty
1870
+ #
1871
+ # If +range.begin+ is out of range (absolute value greater than
1872
+ # #cardinality), returns +nil+.
1873
+ #
1874
+ # This behaves similarly to a slice with +range.begin+ as +index+ and
1875
+ # +range.size+ as +length+, when that both sides of the range are either
1876
+ # negative or non-negative. Note that the minimum +range.size+ is zero,
1877
+ # so this can't return +nil+ for a negative range length.
1878
+ def [](index, length = (length_unset = true; nil))
1879
+ if !length_unset then slice_length(index, length)
1542
1880
  elsif index.is_a?(Range) then slice_range(index)
1543
1881
  else at(index)
1544
1882
  end
@@ -1560,7 +1898,9 @@ module Net
1560
1898
  # Net::IMAP::SequenceSet["5,10:22,50"] & (21..) # to_s => "21:22,50"
1561
1899
  # Net::IMAP::SequenceSet["5,10:22,50"] - (..20) # to_s => "21:22,50"
1562
1900
  #
1563
- # Related: #above, #-, #&
1901
+ # Related: #below,
1902
+ # {other set operations}[rdoc-ref:SequenceSet@Methods+for+Set+Operations],
1903
+ # #all_above?, #any_above?, #none_above?
1564
1904
  def above(num)
1565
1905
  NumValidator.valid_nz_number?(num) or
1566
1906
  raise ArgumentError, "not a valid sequence set number"
@@ -1591,7 +1931,9 @@ module Net
1591
1931
  # Net::IMAP::SequenceSet["5,10:22,*"].below(30) # to_s => "5,10:22"
1592
1932
  # Net::IMAP::SequenceSet["5,10:22,*"].limit(max: 29) # to_s => "5,10:22,29"
1593
1933
  #
1594
- # Related: #above, #-, #&, #limit
1934
+ # Related: #above,
1935
+ # {other set operations}[rdoc-ref:SequenceSet@Methods+for+Set+Operations],
1936
+ # #all_below?, #any_below?, #none_below?
1595
1937
  def below(num)
1596
1938
  NumValidator.valid_nz_number?(num) or
1597
1939
  raise ArgumentError, "not a valid sequence set number"
@@ -1869,8 +2211,9 @@ module Net
1869
2211
  def remain_frozen_empty; frozen? ? SequenceSet.empty : SequenceSet.new end
1870
2212
 
1871
2213
  # frozen clones are shallow copied
1872
- def initialize_clone(other)
1873
- @set_data = other.dup_set_data unless other.frozen?
2214
+ def initialize_clone(other, freeze: nil)
2215
+ @set_data = other.dup_set_data unless other.frozen? && freeze != false
2216
+ freeze_set_data if freeze
1874
2217
  super
1875
2218
  end
1876
2219
 
@@ -1922,7 +2265,7 @@ module Net
1922
2265
  def number_input?(input)
1923
2266
  case input
1924
2267
  when *STARS, Integer then true
1925
- when String then !input.include?(/[:,]/)
2268
+ when String then !input.match?(/[:,]/)
1926
2269
  end
1927
2270
  end
1928
2271
 
@@ -2039,15 +2382,22 @@ module Net
2039
2382
  ######################################################################{{{2
2040
2383
  # Number indexing methods
2041
2384
 
2385
+ def sorted_set_num_at(index)
2386
+ seek_number_in_minmaxes(minmaxes, index)
2387
+ end
2388
+
2389
+ def ordered_list_num_at(index)
2390
+ seek_number_in_minmaxes(each_entry_minmax, index)
2391
+ end
2392
+
2042
2393
  def seek_number_in_minmaxes(minmaxes, index)
2043
- index = Integer(index.to_int)
2044
2394
  if index.negative?
2045
2395
  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))
2396
+ idx_min <= index and return min + (index - idx_min)
2047
2397
  end
2048
2398
  else
2049
2399
  each_minmax_with_index(minmaxes) do |min, _, idx_min, idx_max|
2050
- index <= idx_max and return export_num(min + (index - idx_min))
2400
+ index <= idx_max and return min + (index - idx_min)
2051
2401
  end
2052
2402
  end
2053
2403
  nil
@@ -2073,30 +2423,41 @@ module Net
2073
2423
  end
2074
2424
 
2075
2425
  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)
2426
+ start = implicit_int(start)
2427
+ length = implicit_int(length)
2428
+ return nil if length.negative?
2429
+ stop = start + length unless start.negative? && start.abs <= length
2430
+ slice_range(start...stop)
2081
2431
  end
2082
2432
 
2083
2433
  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)
2434
+ first = implicit_int(range.begin || 0)
2435
+ rend = implicit_int(range.end) unless range.end.nil?
2436
+ excl = range.exclude_end?
2437
+ last = !(excl && rend == 0) && # (i...0)
2438
+ (excl && rend&.pred || rend || -1) # (i...j) vs (i..j) vs (i...)
2439
+ if !last || first.negative? == last.negative? && last < first
2440
+ remain_frozen_empty if valid_slice_start?(first)
2441
+ elsif (min = sorted_set_num_at(first))
2442
+ max = sorted_set_num_at(last) || (last.negative? ? 0 : STAR_INT)
2443
+ if min <= max then intersection export_minmax_entry [min, max]
2097
2444
  else remain_frozen_empty
2098
2445
  end
2446
+ elsif first.positive?
2447
+ remain_frozen_empty if valid_slice_start?(first)
2448
+ end
2449
+ end
2450
+
2451
+ # By short-circuiting, this is a small performance improvement over
2452
+ # `offset.abs <= cardinality`. But, slice_range should get a bigger
2453
+ # performance boost by combining this scan with the start offset scan.
2454
+ def valid_slice_start?(offset)
2455
+ offset = offset.abs
2456
+ minmaxes.each do |min, max|
2457
+ offset -= (max - min).succ
2458
+ return true if offset.negative?
2099
2459
  end
2460
+ !offset.positive?
2100
2461
  end
2101
2462
 
2102
2463
  ######################################################################{{{2
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+
4
+ # :enddoc:
5
+
6
+ module Net
7
+ class IMAP < Protocol
8
+
9
+ # INTERNAL utility methods. API is unstable and subject to change.
10
+ module Utils
11
+ module_function
12
+
13
+ # Something like this exists in ruby's C API. Why not in ruby's ruby?
14
+ def implicit_int(input)
15
+ Integer.try_convert(input) or
16
+ raise TypeError, case input
17
+ when nil
18
+ "no implicit conversion from %p to Integer" % [input]
19
+ when true, false
20
+ "no implicit conversion of %p into Integer" % [input]
21
+ else
22
+ "no implicit conversion of %s into Integer" % [input.class.name]
23
+ end
24
+ end
25
+
26
+ if defined?(Ractor.shareable_proc)
27
+ def shareable
28
+ case obj = yield
29
+ when Proc
30
+ Ractor.shareable_proc(&obj)
31
+ else
32
+ Ractor.make_shareable obj
33
+ end
34
+ end
35
+
36
+ # simplecov:disable
37
+ elsif defined?(Ractor.make_shareable)
38
+ def shareable(&b)
39
+ obj = nil.instance_eval(&b).freeze
40
+ Ractor.make_shareable obj
41
+ end
42
+ else
43
+ def shareable(&b) nil.instance_eval(&b).freeze end
44
+ end
45
+ # simplecov:enable
46
+
47
+ end
48
+ end
49
+ end