net-imap 0.5.1 → 0.5.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -56,18 +56,20 @@ module Net
56
56
  # set = Net::IMAP::SequenceSet[1, 2, [3..7, 5], 6..10, 2048, 1024]
57
57
  # set.valid_string #=> "1:10,55,1024:2048"
58
58
  #
59
- # == Normalized form
59
+ # == Ordered and Normalized sets
60
60
  #
61
- # When a sequence set is created with a single String value, that #string
62
- # representation is preserved. SequenceSet's internal representation
63
- # implicitly sorts all entries, de-duplicates numbers, and coalesces
64
- # adjacent or overlapping ranges. Most enumeration methods and offset-based
65
- # methods use this normalized representation. Most modification methods
66
- # will convert #string to its normalized form.
61
+ # Sometimes the order of the set's members is significant, such as with the
62
+ # +ESORT+, <tt>CONTEXT=SORT</tt>, and +UIDPLUS+ extensions. So, when a
63
+ # sequence set is created by the parser or with a single string value, that
64
+ # #string representation is preserved.
67
65
  #
68
- # In some cases the order of the string representation is significant, such
69
- # as the +ESORT+, <tt>CONTEXT=SORT</tt>, and +UIDPLUS+ extensions. Use
70
- # #entries or #each_entry to enumerate the set in its original order. To
66
+ # Internally, SequenceSet stores a normalized representation which sorts all
67
+ # entries, de-duplicates numbers, and coalesces adjacent or overlapping
68
+ # ranges. Most methods use this normalized representation to achieve
69
+ # <tt>O(lg n)</tt> porformance. Use #entries or #each_entry to enumerate
70
+ # the set in its original order.
71
+ #
72
+ # Most modification methods convert #string to its normalized form. To
71
73
  # preserve #string order while modifying a set, use #append, #string=, or
72
74
  # #replace.
73
75
  #
@@ -160,7 +162,7 @@ module Net
160
162
  # - #===:
161
163
  # Returns whether a given object is fully contained within +self+, or
162
164
  # +nil+ if the object cannot be converted to a compatible type.
163
- # - #cover? (aliased as #===):
165
+ # - #cover?:
164
166
  # Returns whether a given object is fully contained within +self+.
165
167
  # - #intersect? (aliased as #overlap?):
166
168
  # Returns whether +self+ and a given object have any common elements.
@@ -181,30 +183,41 @@ module Net
181
183
  # - #max: Returns the maximum number in the set.
182
184
  # - #minmax: Returns the minimum and maximum numbers in the set.
183
185
  #
184
- # <i>Accessing value by offset:</i>
186
+ # <i>Accessing value by offset in sorted set:</i>
185
187
  # - #[] (aliased as #slice): Returns the number or consecutive subset at a
186
- # given offset or range of offsets.
187
- # - #at: Returns the number at a given offset.
188
- # - #find_index: Returns the given number's offset in the set
188
+ # given offset or range of offsets in the sorted set.
189
+ # - #at: Returns the number at a given offset in the sorted set.
190
+ # - #find_index: Returns the given number's offset in the sorted set.
191
+ #
192
+ # <i>Accessing value by offset in ordered entries</i>
193
+ # - #ordered_at: Returns the number at a given offset in the ordered entries.
194
+ # - #find_ordered_index: Returns the index of the given number's first
195
+ # occurrence in entries.
189
196
  #
190
197
  # <i>Set cardinality:</i>
191
198
  # - #count (aliased as #size): Returns the count of numbers in the set.
199
+ # Duplicated numbers are not counted.
192
200
  # - #empty?: Returns whether the set has no members. \IMAP syntax does not
193
201
  # allow empty sequence sets.
194
202
  # - #valid?: Returns whether the set has any members.
195
203
  # - #full?: Returns whether the set contains every possible value, including
196
204
  # <tt>*</tt>.
197
205
  #
206
+ # <i>Denormalized properties:</i>
207
+ # - #has_duplicates?: Returns whether the ordered entries repeat any
208
+ # numbers.
209
+ # - #count_duplicates: Returns the count of repeated numbers in the ordered
210
+ # entries.
211
+ # - #count_with_duplicates: Returns the count of numbers in the ordered
212
+ # entries, including any repeated numbers.
213
+ #
198
214
  # === Methods for Iterating
199
215
  #
216
+ # <i>Normalized (sorted and coalesced):</i>
200
217
  # - #each_element: Yields each number and range in the set, sorted and
201
218
  # coalesced, and returns +self+.
202
219
  # - #elements (aliased as #to_a): Returns an Array of every number and range
203
220
  # in the set, sorted and coalesced.
204
- # - #each_entry: Yields each number and range in the set, unsorted and
205
- # without deduplicating numbers or coalescing ranges, and returns +self+.
206
- # - #entries: Returns an Array of every number and range in the set,
207
- # unsorted and without deduplicating numbers or coalescing ranges.
208
221
  # - #each_range:
209
222
  # Yields each element in the set as a Range and returns +self+.
210
223
  # - #ranges: Returns an Array of every element in the set, converting
@@ -214,6 +227,14 @@ module Net
214
227
  # ranges into all of their contained numbers.
215
228
  # - #to_set: Returns a Set containing all of the #numbers in the set.
216
229
  #
230
+ # <i>Order preserving:</i>
231
+ # - #each_entry: Yields each number and range in the set, unsorted and
232
+ # without deduplicating numbers or coalescing ranges, and returns +self+.
233
+ # - #entries: Returns an Array of every number and range in the set,
234
+ # unsorted and without deduplicating numbers or coalescing ranges.
235
+ # - #each_ordered_number: Yields each number in the ordered entries and
236
+ # returns +self+.
237
+ #
217
238
  # === Methods for \Set Operations
218
239
  # These methods do not modify +self+.
219
240
  #
@@ -233,19 +254,29 @@ module Net
233
254
  # === Methods for Assigning
234
255
  # These methods add or replace elements in +self+.
235
256
  #
257
+ # <i>Normalized (sorted and coalesced):</i>
258
+ #
259
+ # These methods always update #string to be fully sorted and coalesced.
260
+ #
236
261
  # - #add (aliased as #<<): Adds a given object to the set; returns +self+.
237
262
  # - #add?: If the given object is not an element in the set, adds it and
238
263
  # returns +self+; otherwise, returns +nil+.
239
264
  # - #merge: Merges multiple elements into the set; returns +self+.
265
+ # - #complement!: Replaces the contents of the set with its own #complement.
266
+ #
267
+ # <i>Order preserving:</i>
268
+ #
269
+ # These methods _may_ cause #string to not be sorted or coalesced.
270
+ #
240
271
  # - #append: Adds a given object to the set, appending it to the existing
241
272
  # string, and returns +self+.
242
273
  # - #string=: Assigns a new #string value and replaces #elements to match.
243
274
  # - #replace: Replaces the contents of the set with the contents
244
275
  # of a given object.
245
- # - #complement!: Replaces the contents of the set with its own #complement.
246
276
  #
247
277
  # === Methods for Deleting
248
- # These methods remove elements from +self+.
278
+ # These methods remove elements from +self+, and update #string to be fully
279
+ # sorted and coalesced.
249
280
  #
250
281
  # - #clear: Removes all elements in the set; returns +self+.
251
282
  # - #delete: Removes a given object from the set; returns +self+.
@@ -681,8 +712,9 @@ module Net
681
712
  modifying!
682
713
  tuple = input_to_tuple object
683
714
  entry = tuple_to_str tuple
715
+ string unless empty? # write @string before tuple_add
684
716
  tuple_add tuple
685
- @string = -(string ? "#{@string},#{entry}" : entry)
717
+ @string = -(@string ? "#{@string},#{entry}" : entry)
686
718
  self
687
719
  end
688
720
 
@@ -838,8 +870,8 @@ module Net
838
870
  # <tt>*</tt> translates to an endless range. Use #limit to translate both
839
871
  # cases to a maximum value.
840
872
  #
841
- # If the original input was unordered or contains overlapping ranges, the
842
- # returned ranges will be ordered and coalesced.
873
+ # The returned elements will be sorted and coalesced, even when the input
874
+ # #string is not. <tt>*</tt> will sort last. See #normalize.
843
875
  #
844
876
  # Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].elements
845
877
  # #=> [2, 5..9, 11..12, :*]
@@ -857,7 +889,7 @@ module Net
857
889
  # translates to <tt>:*..</tt>. Use #limit to set <tt>*</tt> to a maximum
858
890
  # value.
859
891
  #
860
- # The returned ranges will be ordered and coalesced, even when the input
892
+ # The returned ranges will be sorted and coalesced, even when the input
861
893
  # #string is not. <tt>*</tt> will sort last. See #normalize.
862
894
  #
863
895
  # Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].ranges
@@ -906,9 +938,7 @@ module Net
906
938
  # Related: #entries, #each_element
907
939
  def each_entry(&block) # :yields: integer or range or :*
908
940
  return to_enum(__method__) unless block_given?
909
- return each_element(&block) unless @string
910
- @string.split(",").each do yield tuple_to_entry str_to_tuple _1 end
911
- self
941
+ each_entry_tuple do yield tuple_to_entry _1 end
912
942
  end
913
943
 
914
944
  # Yields each number or range (or <tt>:*</tt>) in #elements to the block
@@ -926,6 +956,16 @@ module Net
926
956
 
927
957
  private
928
958
 
959
+ def each_entry_tuple(&block)
960
+ return to_enum(__method__) unless block_given?
961
+ if @string
962
+ @string.split(",") do block.call str_to_tuple _1 end
963
+ else
964
+ @tuples.each(&block)
965
+ end
966
+ self
967
+ end
968
+
929
969
  def tuple_to_entry((min, max))
930
970
  if min == STAR_INT then :*
931
971
  elsif max == STAR_INT then min..
@@ -957,19 +997,36 @@ module Net
957
997
  # Returns an enumerator when called without a block (even if the set
958
998
  # contains <tt>*</tt>).
959
999
  #
960
- # Related: #numbers
1000
+ # Related: #numbers, #each_ordered_number
961
1001
  def each_number(&block) # :yields: integer
962
1002
  return to_enum(__method__) unless block_given?
963
1003
  raise RangeError, '%s contains "*"' % [self.class] if include_star?
964
- each_element do |elem|
965
- case elem
966
- when Range then elem.each(&block)
967
- when Integer then block.(elem)
968
- end
969
- end
1004
+ @tuples.each do each_number_in_tuple _1, _2, &block end
970
1005
  self
971
1006
  end
972
1007
 
1008
+ # Yields each number in #entries to the block and returns self.
1009
+ # If the set contains a <tt>*</tt>, RangeError will be raised.
1010
+ #
1011
+ # Returns an enumerator when called without a block (even if the set
1012
+ # contains <tt>*</tt>).
1013
+ #
1014
+ # Related: #entries, #each_number
1015
+ def each_ordered_number(&block)
1016
+ return to_enum(__method__) unless block_given?
1017
+ raise RangeError, '%s contains "*"' % [self.class] if include_star?
1018
+ each_entry_tuple do each_number_in_tuple _1, _2, &block end
1019
+ end
1020
+
1021
+ private def each_number_in_tuple(min, max, &block)
1022
+ if min == STAR_INT then yield :*
1023
+ elsif min == max then yield min
1024
+ elsif max != STAR_INT then (min..max).each(&block)
1025
+ else
1026
+ raise RangeError, "#{SequenceSet} cannot enumerate range with '*'"
1027
+ end
1028
+ end
1029
+
973
1030
  # Returns a Set with all of the #numbers in the sequence set.
974
1031
  #
975
1032
  # If the set contains a <tt>*</tt>, RangeError will be raised.
@@ -981,8 +1038,10 @@ module Net
981
1038
 
982
1039
  # Returns the count of #numbers in the set.
983
1040
  #
984
- # If <tt>*</tt> and <tt>2**32 - 1</tt> (the maximum 32-bit unsigned
985
- # integer value) are both in the set, they will only be counted once.
1041
+ # <tt>*</tt> will be counted as <tt>2**32 - 1</tt> (the maximum 32-bit
1042
+ # unsigned integer value).
1043
+ #
1044
+ # Related: #count_with_duplicates
986
1045
  def count
987
1046
  @tuples.sum(@tuples.count) { _2 - _1 } +
988
1047
  (include_star? && include?(UINT32_MAX) ? -1 : 0)
@@ -990,33 +1049,87 @@ module Net
990
1049
 
991
1050
  alias size count
992
1051
 
993
- # Returns the index of +number+ in the set, or +nil+ if +number+ isn't in
994
- # the set.
1052
+ # Returns the count of numbers in the ordered #entries, including any
1053
+ # repeated numbers.
1054
+ #
1055
+ # <tt>*</tt> will be counted as <tt>2**32 - 1</tt> (the maximum 32-bit
1056
+ # unsigned integer value).
1057
+ #
1058
+ # When #string is normalized, this behaves the same as #count.
995
1059
  #
996
- # Related: #[]
1060
+ # Related: #entries, #count_duplicates, #has_duplicates?
1061
+ def count_with_duplicates
1062
+ return count unless @string
1063
+ each_entry_tuple.sum {|min, max|
1064
+ max - min + ((max == STAR_INT && min != STAR_INT) ? 0 : 1)
1065
+ }
1066
+ end
1067
+
1068
+ # Returns the count of repeated numbers in the ordered #entries, the
1069
+ # difference between #count_with_duplicates and #count.
1070
+ #
1071
+ # When #string is normalized, this is zero.
1072
+ #
1073
+ # Related: #entries, #count_with_duplicates, #has_duplicates?
1074
+ def count_duplicates
1075
+ return 0 unless @string
1076
+ count_with_duplicates - count
1077
+ end
1078
+
1079
+ # :call-seq: has_duplicates? -> true | false
1080
+ #
1081
+ # Returns whether or not the ordered #entries repeat any numbers.
1082
+ #
1083
+ # Always returns +false+ when #string is normalized.
1084
+ #
1085
+ # Related: #entries, #count_with_duplicates, #count_duplicates?
1086
+ def has_duplicates?
1087
+ return false unless @string
1088
+ count_with_duplicates != count
1089
+ end
1090
+
1091
+ # Returns the (sorted and deduplicated) index of +number+ in the set, or
1092
+ # +nil+ if +number+ isn't in the set.
1093
+ #
1094
+ # Related: #[], #at, #find_ordered_index
997
1095
  def find_index(number)
998
1096
  number = to_tuple_int number
999
- each_tuple_with_index do |min, max, idx_min|
1097
+ each_tuple_with_index(@tuples) do |min, max, idx_min|
1000
1098
  number < min and return nil
1001
1099
  number <= max and return from_tuple_int(idx_min + (number - min))
1002
1100
  end
1003
1101
  nil
1004
1102
  end
1005
1103
 
1104
+ # Returns the first index of +number+ in the ordered #entries, or
1105
+ # +nil+ if +number+ isn't in the set.
1106
+ #
1107
+ # Related: #find_index
1108
+ def find_ordered_index(number)
1109
+ number = to_tuple_int number
1110
+ each_tuple_with_index(each_entry_tuple) do |min, max, idx_min|
1111
+ if min <= number && number <= max
1112
+ return from_tuple_int(idx_min + (number - min))
1113
+ end
1114
+ end
1115
+ nil
1116
+ end
1117
+
1006
1118
  private
1007
1119
 
1008
- def each_tuple_with_index
1120
+ def each_tuple_with_index(tuples)
1009
1121
  idx_min = 0
1010
- @tuples.each do |min, max|
1011
- yield min, max, idx_min, (idx_max = idx_min + (max - min))
1122
+ tuples.each do |min, max|
1123
+ idx_max = idx_min + (max - min)
1124
+ yield min, max, idx_min, idx_max
1012
1125
  idx_min = idx_max + 1
1013
1126
  end
1014
1127
  idx_min
1015
1128
  end
1016
1129
 
1017
- def reverse_each_tuple_with_index
1130
+ def reverse_each_tuple_with_index(tuples)
1018
1131
  idx_max = -1
1019
- @tuples.reverse_each do |min, max|
1132
+ tuples.reverse_each do |min, max|
1020
1133
  yield min, max, (idx_min = idx_max - (max - min)), idx_max
1021
1134
  idx_max = idx_min - 1
1022
1135
  end
@@ -1027,18 +1140,38 @@ module Net
1027
1140
 
1028
1141
  # :call-seq: at(index) -> integer or nil
1029
1142
  #
1030
- # Returns a number from +self+, without modifying the set. Behaves the
1031
- # same as #[], except that #at only allows a single integer argument.
1143
+ # Returns the number at the given +index+ in the sorted set, without
1144
+ # modifying the set.
1032
1145
  #
1033
- # Related: #[], #slice
1146
+ # +index+ is interpreted the same as in #[], except that #at only allows a
1147
+ # single integer argument.
1148
+ #
1149
+ # Related: #[], #slice, #ordered_at
1034
1150
  def at(index)
1151
+ lookup_number_by_tuple_index(tuples, index)
1152
+ end
1153
+
1154
+ # :call-seq: ordered_at(index) -> integer or nil
1155
+ #
1156
+ # Returns the number at the given +index+ in the ordered #entries, without
1157
+ # modifying the set.
1158
+ #
1159
+ # +index+ is interpreted the same as in #at (and #[]), except that
1160
+ # #ordered_at applies to the ordered #entries, not the sorted set.
1161
+ #
1162
+ # Related: #[], #slice, #ordered_at
1163
+ def ordered_at(index)
1164
+ lookup_number_by_tuple_index(each_entry_tuple, index)
1165
+ end
1166
+
1167
+ private def lookup_number_by_tuple_index(tuples, index)
1035
1168
  index = Integer(index.to_int)
1036
1169
  if index.negative?
1037
- reverse_each_tuple_with_index do |min, max, idx_min, idx_max|
1170
+ reverse_each_tuple_with_index(tuples) do |min, max, idx_min, idx_max|
1038
1171
  idx_min <= index and return from_tuple_int(min + (index - idx_min))
1039
1172
  end
1040
1173
  else
1041
- each_tuple_with_index do |min, _, idx_min, idx_max|
1174
+ each_tuple_with_index(tuples) do |min, _, idx_min, idx_max|
1042
1175
  index <= idx_max and return from_tuple_int(min + (index - idx_min))
1043
1176
  end
1044
1177
  end
@@ -1053,17 +1186,18 @@ module Net
1053
1186
  # seqset[range] -> sequence set or nil
1054
1187
  # slice(range) -> sequence set or nil
1055
1188
  #
1056
- # Returns a number or a subset from +self+, without modifying the set.
1189
+ # Returns a number or a subset from the _sorted_ set, without modifying
1190
+ # the set.
1057
1191
  #
1058
1192
  # When an Integer argument +index+ is given, the number at offset +index+
1059
- # is returned:
1193
+ # in the sorted set is returned:
1060
1194
  #
1061
1195
  # set = Net::IMAP::SequenceSet["10:15,20:23,26"]
1062
1196
  # set[0] #=> 10
1063
1197
  # set[5] #=> 15
1064
1198
  # set[10] #=> 26
1065
1199
  #
1066
- # If +index+ is negative, it counts relative to the end of +self+:
1200
+ # If +index+ is negative, it counts relative to the end of the sorted set:
1067
1201
  # set = Net::IMAP::SequenceSet["10:15,20:23,26"]
1068
1202
  # set[-1] #=> 26
1069
1203
  # set[-3] #=> 22
@@ -1075,13 +1209,14 @@ module Net
1075
1209
  # set[11] #=> nil
1076
1210
  # set[-12] #=> nil
1077
1211
  #
1078
- # The result is based on the normalized set—sorted and de-duplicatednot
1079
- # on the assigned value of #string.
1212
+ # The result is based on the sorted and de-duplicated set, not on the
1213
+ # ordered #entries in #string.
1080
1214
  #
1081
1215
  # set = Net::IMAP::SequenceSet["12,20:23,11:16,21"]
1082
1216
  # set[0] #=> 11
1083
1217
  # set[-1] #=> 23
1084
1218
  #
1219
+ # Related: #at
1085
1220
  def [](index, length = nil)
1086
1221
  if length then slice_length(index, length)
1087
1222
  elsif index.is_a?(Range) then slice_range(index)
@@ -1337,8 +1472,8 @@ module Net
1337
1472
  modifying!
1338
1473
  min, max = tuple
1339
1474
  lower, lower_idx = tuple_gte_with_index(min - 1)
1340
- if lower.nil? then tuples << tuple
1341
- elsif (max + 1) < lower.first then tuples.insert(lower_idx, tuple)
1475
+ if lower.nil? then tuples << [min, max]
1476
+ elsif (max + 1) < lower.first then tuples.insert(lower_idx, [min, max])
1342
1477
  else tuple_coalesce(lower, lower_idx, min, max)
1343
1478
  end
1344
1479
  end
@@ -4,7 +4,7 @@ module Net
4
4
  class IMAP
5
5
  module StringPrep
6
6
 
7
- # Defined in RFC3491[https://tools.ietf.org/html/rfc3491], the +nameprep+
7
+ # Defined in RFC3491[https://www.rfc-editor.org/rfc/rfc3491], the +nameprep+
8
8
  # profile of "Stringprep" is:
9
9
  # >>>
10
10
  # used by the IDNA protocol for preparing domain names; it is not
@@ -4,11 +4,11 @@ module Net
4
4
  class IMAP
5
5
  module StringPrep
6
6
 
7
- # Defined in RFC-4505[https://tools.ietf.org/html/rfc4505] §3, The +trace+
7
+ # Defined in RFC-4505[https://www.rfc-editor.org/rfc/rfc4505] §3, The +trace+
8
8
  # profile of \StringPrep is used by the +ANONYMOUS+ \SASL mechanism.
9
9
  module Trace
10
10
 
11
- # Defined in RFC-4505[https://tools.ietf.org/html/rfc4505] §3.
11
+ # Defined in RFC-4505[https://www.rfc-editor.org/rfc/rfc4505] §3.
12
12
  STRINGPREP_PROFILE = "trace"
13
13
 
14
14
  # >>>
@@ -23,7 +23,7 @@ module Net
23
23
  # No Unicode normalization is required by this profile.
24
24
  NORMALIZATION = nil
25
25
 
26
- # From RFC-4505[https://tools.ietf.org/html/rfc4505] §3, The "trace"
26
+ # From RFC-4505[https://www.rfc-editor.org/rfc/rfc4505] §3, The "trace"
27
27
  # Profile of "Stringprep":
28
28
  # >>>
29
29
  # Characters from the following tables of [StringPrep] are prohibited:
@@ -47,7 +47,7 @@ module Net
47
47
 
48
48
  module_function
49
49
 
50
- # From RFC-4505[https://tools.ietf.org/html/rfc4505] §3, The "trace"
50
+ # From RFC-4505[https://www.rfc-editor.org/rfc/rfc4505] §3, The "trace"
51
51
  # Profile of "Stringprep":
52
52
  # >>>
53
53
  # The character repertoire of this profile is Unicode 3.2 [Unicode].