chars 0.2.4 → 0.3.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6257e8d2fa2f62cc364fc2e0ba901d777963476968264dd7747bc4407e5ff54c
4
- data.tar.gz: 16f5ecc2f44e8265869aac4946a64cacda2ae8dcdae3b669081a48d34f034f4e
3
+ metadata.gz: 744e39b682dc29231aafe96056415cc38c65e032df5c2a3aff6a5a846b38f67b
4
+ data.tar.gz: 47e309f42894f6d94ec2d15568d5d33df8a97600c1c227eb2366eb8fb8d56eb4
5
5
  SHA512:
6
- metadata.gz: ebaf16c18de6b000b6219bf4afe7142926e24293b6b1e62214f937c51c5bdf7f5ffa7f46eecb151e1da044f7b49e1479b7d2cc947a6e20fa2a586f28b4a8a525
7
- data.tar.gz: 4c9c98dcbab77b201b86fc32a628575e4b0ebc03608c50c7591b51a70821127778ced5122f57de846c7c863440fa0e9555fe7bfd2940a913e494a929c74a513e
6
+ metadata.gz: 20755db9457fda76ceed274d18919a43dbf3c850d0aaad6a38f077b0e89fcf15d7c0a79b3ac95fe158d5abef8db4e68bd0ccc6971f8989e0b4f7c918b9a282b8
7
+ data.tar.gz: 54bc4adfd93752f68454c475b57a1705428231f8decab3be8b8710d6c344c60715af7c466b21b7c398e7b2cc30801ff5fe77a4924e1ac34b4fa724c3f1a3adec
data/ChangeLog.md CHANGED
@@ -1,3 +1,15 @@
1
+ ### 0.3.0 / 2021-10-23
2
+
3
+ * Added {Chars::WHITESPACE}.
4
+ * Added {Chars::DIGITS}.
5
+ * Added {Chars::CharSet#each_substring_with_index}.
6
+ * Added {Chars::CharSet#substrings_with_indexes}.
7
+ * Added {Chars::CharSet#each_substring}.
8
+ * Added {Chars::CharSet#substrings}.
9
+ * Added {Chars::CharSet#each_string_of_length}.
10
+ * Added {Chars::CharSet#strings_of_length}.
11
+ * Added {Chars::StringEnumerator}.
12
+
1
13
  ### 0.2.4 / 2021-10-22
2
14
 
3
15
  * Require [ruby] >= 2.0.0.
data/README.md CHANGED
@@ -48,14 +48,6 @@ Determine whether a String belongs to a character set:
48
48
  # => true
49
49
  ```
50
50
 
51
- Find all sub-strings that belong to a character set within a String:
52
-
53
- ```ruby
54
- ls = File.read('/bin/ls')
55
- Chars.printable.strings_in(ls)
56
- # => ["/lib64/ld-linux-x86-64.so.2", "KIq/", "5J~!", "%L~!", ...]
57
- ```
58
-
59
51
  Return a random character from the set of all characters:
60
52
 
61
53
  ```ruby
@@ -85,6 +77,14 @@ Chars.all.random_string(10)
85
77
  # => "\xc2h\xad\xccm7\x1e6J\x13"
86
78
  ```
87
79
 
80
+ Generate a secure password:
81
+
82
+ ```ruby
83
+ require 'securerandom'
84
+ Chars.visible.random_string(10..14, random: SecureRandom)
85
+ # => ".*$X=D*XK2h8gC"
86
+ ```
87
+
88
88
  Return a random String with a random length between 5 and 10, from the
89
89
  set of space characters:
90
90
 
@@ -93,6 +93,57 @@ Chars.space.random_string(5..10)
93
93
  # => "\r\v\n\t\n\f"
94
94
  ```
95
95
 
96
+ Find all sub-strings that belong to a character set within a String:
97
+
98
+ ```ruby
99
+ ls = File.binread('/bin/ls')
100
+ Chars.printable.substrings(ls)
101
+ # =>
102
+ # ["/lib64/ld-linux-x86-64.so.2",
103
+ # "_ITM_deregisterTMCloneTable",
104
+ # "__gmon_start__",
105
+ # "_ITM_registerTMCloneTable",
106
+ # ...
107
+ # ]
108
+ ```
109
+
110
+ Find all sub-strings that belong to a character set within a String, with
111
+ indexes:
112
+
113
+ ```ruby
114
+ ls = File.binread('/bin/ls')
115
+ Chars.printable.substrings_with_indexes(ls)
116
+ # =>
117
+ # [["/lib64/ld-linux-x86-64.so.2", 792],
118
+ # ["_ITM_deregisterTMCloneTable", 4009],
119
+ # ["__gmon_start__", 4037],
120
+ # ["_ITM_registerTMCloneTable", 4052],
121
+ # ...
122
+ # ]
123
+ ```
124
+
125
+ Enumerate over all strings from a character set of a given length:
126
+
127
+ ```ruby
128
+ passwords = Chars.visible.strings_of_length(6)
129
+ passwords.each { |password| puts password }
130
+ # AAAAAA
131
+ # AAAAAB
132
+ # AAAAAC
133
+ # ...
134
+ ```
135
+
136
+ Enumerate over all strings from a character set of lengths between 4 and 8:
137
+
138
+ ```ruby
139
+ passwords = Chars.visible.strings_of_length(4..8)
140
+ passwords.each { |password| puts password }
141
+ # AAAA
142
+ # AAAB
143
+ # AAAC
144
+ # ...
145
+ ```
146
+
96
147
  ## Requirements
97
148
 
98
149
  * [ruby](https://www.ruby-lang.org/) >= 2.0.0
@@ -104,13 +155,13 @@ Chars.space.random_string(5..10)
104
155
  ### gemspec
105
156
 
106
157
  ```ruby
107
- gem.add_dependency 'chars', '~> 0.2'
158
+ gem.add_dependency 'chars', '~> 0.3'
108
159
  ```
109
160
 
110
161
  ### Gemfile
111
162
 
112
163
  ```ruby
113
- gem 'chars', '~> 0.2'
164
+ gem 'chars', '~> 0.3'
114
165
  ```
115
166
 
116
167
  ## Crystal
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__),'..','lib'))
3
+ $LOAD_PATH.unshift(File.expand_path(File.join(__dir__,'..','lib')))
4
4
 
5
5
  require 'chars'
6
6
  require 'benchmark'
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.expand_path(File.join(__dir__,'..','lib')))
4
+
5
+ require 'chars'
6
+ require 'benchmark'
7
+
8
+ CHARSET = Chars::ALPHA_NUMERIC
9
+ STRING = File.binread('/usr/bin/openssl')
10
+
11
+ Benchmark.bm(41) do |b|
12
+ b.report('Chars::CharSet#each_substring_with_index') do
13
+ CHARSET.each_substring_with_index(STRING) { |string,index| }
14
+ end
15
+
16
+ b.report('Chars::CharSet#each_substring') do
17
+ CHARSET.each_substring(STRING) { |string| }
18
+ end
19
+
20
+ (5..20).step(5) do |n|
21
+ b.report("Chars::CharSet#each_substring (length=#{n})") do
22
+ CHARSET.strings_in(STRING, :length => n) { |offset,string| }
23
+ end
24
+ end
25
+ end
@@ -1,3 +1,5 @@
1
+ require 'chars/string_enumerator'
2
+
1
3
  require 'set'
2
4
 
3
5
  module Chars
@@ -172,21 +174,30 @@ module Chars
172
174
  #
173
175
  # Returns a random byte from the {CharSet}.
174
176
  #
177
+ # @param [Random, SecureRandom] random
178
+ # The random number generator to use.
179
+ #
175
180
  # @return [Integer]
176
181
  # A random byte value.
177
182
  #
178
- def random_byte
179
- self.entries[rand(self.length)]
183
+ def random_byte(random: Random)
184
+ self.entries[random.rand(self.length)]
180
185
  end
181
186
 
182
187
  #
183
188
  # Returns a random character from the {CharSet}.
184
189
  #
190
+ # @param [Hash{Symbol => Object}] kwargs
191
+ # Additional keyword arguments.
192
+ #
193
+ # @option kwargs [Random, SecureRandom] :random
194
+ # The random number generator to use.
195
+ #
185
196
  # @return [String]
186
197
  # A random char value.
187
198
  #
188
- def random_char
189
- @chars[random_byte]
199
+ def random_char(**kwargs)
200
+ @chars[random_byte(**kwargs)]
190
201
  end
191
202
 
192
203
  #
@@ -195,6 +206,12 @@ module Chars
195
206
  # @param [Integer] n
196
207
  # Specifies how many times to pass a random byte to the block.
197
208
  #
209
+ # @param [Hash{Symbol => Object}] kwargs
210
+ # Additional keyword arguments.
211
+ #
212
+ # @option kwargs [Random, SecureRandom] :random
213
+ # The random number generator to use.
214
+ #
198
215
  # @yield [byte]
199
216
  # The block will receive the random bytes.
200
217
  #
@@ -204,10 +221,12 @@ module Chars
204
221
  # @return [Enumerator]
205
222
  # If no block is given, an enumerator object will be returned.
206
223
  #
207
- def each_random_byte(n,&block)
208
- return enum_for(__method__,n) unless block_given?
224
+ def each_random_byte(n,**kwargs,&block)
225
+ return enum_for(__method__,n,**kwargs) unless block_given?
209
226
 
210
- n.times { yield random_byte }
227
+ n.times do
228
+ yield random_byte(**kwargs)
229
+ end
211
230
  return nil
212
231
  end
213
232
 
@@ -217,6 +236,12 @@ module Chars
217
236
  # @param [Integer] n
218
237
  # Specifies how many times to pass a random character to the block.
219
238
  #
239
+ # @param [Hash{Symbol => Object}] kwargs
240
+ # Additional keyword arguments.
241
+ #
242
+ # @option kwargs [Random, SecureRandom] :random
243
+ # The random number generator to use.
244
+ #
220
245
  # @yield [char]
221
246
  # The block will receive the random characters.
222
247
  #
@@ -226,10 +251,12 @@ module Chars
226
251
  # @return [Enumerator]
227
252
  # If no block is given, an enumerator object will be returned.
228
253
  #
229
- def each_random_char(n,&block)
230
- return enum_for(__method__,n) unless block_given?
254
+ def each_random_char(n,**kwargs,&block)
255
+ return enum_for(__method__,n,**kwargs) unless block_given?
231
256
 
232
- each_random_byte(n) { |byte| yield @chars[byte] }
257
+ each_random_byte(n,**kwargs) do |byte|
258
+ yield @chars[byte]
259
+ end
233
260
  end
234
261
 
235
262
  #
@@ -238,17 +265,24 @@ module Chars
238
265
  # @param [Integer, Array, Range] length
239
266
  # The length of the Array of random bytes.
240
267
  #
268
+ # @param [Random, SecureRandom] random
269
+ # The random number generator to use.
270
+ #
241
271
  # @return [Array<Integer>]
242
272
  # The randomly selected bytes.
243
273
  #
244
- def random_bytes(length)
274
+ def random_bytes(length, random: Random)
245
275
  case length
246
276
  when Array
247
- Array.new(length.sample) { random_byte }
277
+ Array.new(length.sample(random: random)) do
278
+ random_byte(random: random)
279
+ end
248
280
  when Range
249
- Array.new(rand(length)) { random_byte }
281
+ Array.new(random.rand(length)) do
282
+ random_byte(random: random)
283
+ end
250
284
  else
251
- Array.new(length) { random_byte }
285
+ Array.new(length) { random_byte(random: random) }
252
286
  end
253
287
  end
254
288
 
@@ -258,17 +292,20 @@ module Chars
258
292
  # @param [Integer, Array, Range] length
259
293
  # The length of the Array of random non-repeating bytes.
260
294
  #
295
+ # @param [Random, SecureRandom] random
296
+ # The random number generator to use.
297
+ #
261
298
  # @return [Array<Integer>]
262
299
  # The randomly selected non-repeating bytes.
263
300
  #
264
- def random_distinct_bytes(length)
265
- shuffled_bytes = bytes.shuffle
301
+ def random_distinct_bytes(length, random: Random)
302
+ shuffled_bytes = bytes.shuffle(random: random)
266
303
 
267
304
  case length
268
305
  when Array
269
- shuffled_bytes[0,length.sample]
306
+ shuffled_bytes[0,length.sample(random: random)]
270
307
  when Range
271
- shuffled_bytes[0,rand(length)]
308
+ shuffled_bytes[0,random.rand(length)]
272
309
  else
273
310
  shuffled_bytes[0,length]
274
311
  end
@@ -280,11 +317,17 @@ module Chars
280
317
  # @param [Integer, Array, Range] length
281
318
  # The length of the Array of random characters.
282
319
  #
320
+ # @param [Hash{Symbol => Object}] kwargs
321
+ # Additional keyword arguments.
322
+ #
323
+ # @option kwargs [Random, SecureRandom] :random
324
+ # The random number generator to use.
325
+ #
283
326
  # @return [Array<String>]
284
327
  # The randomly selected characters.
285
328
  #
286
- def random_chars(length)
287
- random_bytes(length).map { |byte| @chars[byte] }
329
+ def random_chars(length,**kwargs)
330
+ random_bytes(length,**kwargs).map { |byte| @chars[byte] }
288
331
  end
289
332
 
290
333
  #
@@ -294,13 +337,19 @@ module Chars
294
337
  # @param [Integer, Array, Range] length
295
338
  # The length of the String of random characters.
296
339
  #
340
+ # @param [Hash{Symbol => Object}] kwargs
341
+ # Additional keyword arguments.
342
+ #
343
+ # @option kwargs [Random, SecureRandom] :random
344
+ # The random number generator to use.
345
+ #
297
346
  # @return [String]
298
347
  # The String of randomly selected characters.
299
348
  #
300
349
  # @see random_chars
301
350
  #
302
- def random_string(length)
303
- random_chars(length).join
351
+ def random_string(length,**kwargs)
352
+ random_chars(length,**kwargs).join
304
353
  end
305
354
 
306
355
  #
@@ -310,11 +359,17 @@ module Chars
310
359
  # @param [Integer, Array, Range] length
311
360
  # The length of the Array of random non-repeating characters.
312
361
  #
362
+ # @param [Hash{Symbol => Object}] kwargs
363
+ # Additional keyword arguments.
364
+ #
365
+ # @option kwargs [Random, SecureRandom] :random
366
+ # The random number generator to use.
367
+ #
313
368
  # @return [Array<Integer>]
314
369
  # The randomly selected non-repeating characters.
315
370
  #
316
- def random_distinct_chars(length)
317
- random_distinct_bytes(length).map { |byte| @chars[byte] }
371
+ def random_distinct_chars(length,**kwargs)
372
+ random_distinct_bytes(length,**kwargs).map { |byte| @chars[byte] }
318
373
  end
319
374
 
320
375
  #
@@ -324,13 +379,161 @@ module Chars
324
379
  # @param [Integer, Array, Range] length
325
380
  # The length of the String of random non-repeating characters.
326
381
  #
382
+ # @param [Hash{Symbol => Object}] kwargs
383
+ # Additional keyword arguments.
384
+ #
385
+ # @option kwargs [Random, SecureRandom] :random
386
+ # The random number generator to use.
387
+ #
327
388
  # @return [String]
328
389
  # The String of randomly selected non-repeating characters.
329
390
  #
330
391
  # @see random_distinct_chars
331
392
  #
332
- def random_distinct_string(length)
333
- random_distinct_chars(length).join
393
+ def random_distinct_string(length,**kwargs)
394
+ random_distinct_chars(length,**kwargs).join
395
+ end
396
+
397
+ #
398
+ # Enumerates over all substrings and their indices within the given string,
399
+ # of minimum length and that are made up of characters from the {CharSet}.
400
+ #
401
+ # @param [String] data
402
+ # The data to find sub-strings within.
403
+ #
404
+ # @param [Integer] min_length
405
+ # The minimum length of sub-strings found within the given data.
406
+ #
407
+ # @yield [match, index]
408
+ # The given block will be passed every matched sub-string and it's index.
409
+ #
410
+ # @yield [String] match
411
+ # A sub-string containing the characters from the {CharSet}.
412
+ #
413
+ # @yield [Integer] index
414
+ # The index the sub-string was found at.
415
+ #
416
+ # @return [Enumerator]
417
+ # If no block is given, an Enumerator object will be returned.
418
+ #
419
+ # @since 0.3.0
420
+ #
421
+ def each_substring_with_index(data, min_length: 4)
422
+ unless block_given?
423
+ return enum_for(__method__,data, min_length: min_length)
424
+ end
425
+
426
+ return if data.size < min_length
427
+
428
+ index = 0
429
+
430
+ match_start = nil
431
+ match_end = nil
432
+
433
+ while index < data.size
434
+ unless match_start
435
+ if self.include_char?(data[index])
436
+ match_start = index
437
+ end
438
+ else
439
+ unless self.include_char?(data[index])
440
+ match_end = index
441
+ match_length = (match_end - match_start)
442
+
443
+ if match_length >= min_length
444
+ match = data[match_start,match_length]
445
+
446
+ yield match, match_start
447
+ end
448
+
449
+ match_start = match_end = nil
450
+ end
451
+ end
452
+
453
+ index += 1
454
+ end
455
+
456
+ # yield the remaining match
457
+ if match_start
458
+ yield data[match_start, data.size - match_start], match_start
459
+ end
460
+ end
461
+
462
+ #
463
+ # Returns an Array of all substrings and their indices within the given
464
+ # string, of minimum length and that are made up of characters from the
465
+ # {CharSet}.
466
+ #
467
+ # @param [String] data
468
+ # The data to find sub-strings within.
469
+ #
470
+ # @param [Hash{Symbol => Object}] kwargs
471
+ # Keyword arguments for {#each_substring_with_index}.
472
+ #
473
+ # @option kwargs [Integer] :min_length
474
+ # The minimum length of sub-strings found within the given data.
475
+ #
476
+ # @return [Array<(String, Integer)>]
477
+ # Tthe array of substrings and their indices within the given `data`.
478
+ #
479
+ # @see #each_substring_with_index
480
+ #
481
+ # @since 0.3.0
482
+ #
483
+ def substrings_with_indexes(data,**kwargs)
484
+ each_substring_with_index(data,**kwargs).to_a
485
+ end
486
+
487
+ #
488
+ # Enumerates over all substrings within the given string, of minimum length
489
+ # and that are made up of characters from the {CharSet}.
490
+ #
491
+ # @param [String] data
492
+ # The data to find sub-strings within.
493
+ #
494
+ # @param [Hash{Symbol => Object}] kwargs
495
+ # Keyword arguments for {#each_substring_with_index}.
496
+ #
497
+ # @option kwargs [Integer] :min_length
498
+ # The minimum length of sub-strings found within the given data.
499
+ #
500
+ # @return [Enumerator]
501
+ # If no block is given, an Enumerator object will be returned.
502
+ #
503
+ # @see #each_substring_with_index
504
+ #
505
+ # @since 0.3.0
506
+ #
507
+ def each_substring(data,**kwargs)
508
+ return enum_for(__method__,data,**kwargs) unless block_given?
509
+
510
+ each_substring_with_index(data,**kwargs) do |substring,index|
511
+ yield substring
512
+ end
513
+ end
514
+
515
+ #
516
+ # Returns an Array of all substrings within the given string,
517
+ # of minimum length and that are made up of characters from the {CharSet}.
518
+ #
519
+ # @param [String] data
520
+ # The data to find sub-strings within.
521
+ #
522
+ # @param [Hash{Symbol => Object}] kwargs
523
+ # Keyword arguments for {#each_substring_with_index}.
524
+ #
525
+ # @option kwargs [Integer] :min_length
526
+ # The minimum length of sub-strings found within the given data.
527
+ #
528
+ # @see #each_substring
529
+ #
530
+ # @return [Array<String>]
531
+ # Tthe array of substrings within the given `data`.
532
+ #
533
+ # @since 0.3.0
534
+ #
535
+ def substrings(data,**kwargs)
536
+ each_substring(data,**kwargs).to_a
334
537
  end
335
538
 
336
539
  #
@@ -364,49 +567,75 @@ module Chars
364
567
  # @return [Array, Hash]
365
568
  # If no block is given, an Array or Hash of sub-strings is returned.
366
569
  #
570
+ # @deprecated
571
+ # Use {#each_substring_with_index}, {#substrings_with_index},
572
+ # {#each_substring}, or {#substrings} instead.
573
+ #
367
574
  def strings_in(data,options={},&block)
575
+ kwargs = {min_length: options.fetch(:length,4)}
576
+
368
577
  unless block
369
578
  if options[:offsets]
370
- found = {}
371
- block = lambda { |offset,substring| found[offset] = substring }
579
+ return Hash[substrings_with_indexes(data,**kwargs)]
372
580
  else
373
- found = []
374
- block = lambda { |substring| found << substring }
581
+ return substrings(data,**kwargs)
375
582
  end
376
-
377
- strings_in(data,options,&block)
378
- return found
379
583
  end
380
584
 
381
- min_length = options.fetch(:length,4)
382
- return if data.length < min_length
383
-
384
- index = 0
385
-
386
- while index <= (data.length - min_length)
387
- if self === data[index,min_length]
388
- sub_index = (index + min_length)
389
-
390
- while self.include_char?(data[sub_index,1])
391
- sub_index += 1
392
- end
393
-
394
- match = data[index...sub_index]
585
+ case block.arity
586
+ when 2
587
+ each_substring_with_index(data,**kwargs,&block)
588
+ else
589
+ each_substring(data,**kwargs,&block)
590
+ end
591
+ end
395
592
 
396
- case block.arity
397
- when 2
398
- yield match, index
399
- else
400
- yield match
401
- end
593
+ #
594
+ # Enumerates through every possible string belonging to the {CharSet} and
595
+ # of the given length.
596
+ #
597
+ # @param [Range, Array, Integer] length
598
+ # The desired length(s) of each string.
599
+ #
600
+ # @yield [string]
601
+ # The given block will be passed each sequential string.
602
+ #
603
+ # @yieldparam [String] string
604
+ # A string belonging to {#char_set} and `length` long.
605
+ #
606
+ # @return [Enumerator]
607
+ # If no block is given, an Enumerator will be returned.
608
+ #
609
+ # @since 0.3.0
610
+ #
611
+ def each_string_of_length(length,&block)
612
+ return enum_for(__method__,length) unless block
402
613
 
403
- index = sub_index
404
- else
405
- index += 1
614
+ case length
615
+ when Range, Array
616
+ length.each do |len|
617
+ StringEnumerator.new(self,len).each(&block)
406
618
  end
619
+ else
620
+ StringEnumerator.new(self,length).each(&block)
407
621
  end
408
622
  end
409
623
 
624
+ #
625
+ # Returns an Enumerator that enumerates through every possible string
626
+ # belonging to the {CharSEt} and of the given length.
627
+ #
628
+ # @param [Range, Array, Integer] length
629
+ # The desired length(s) of each string.
630
+ #
631
+ # @return [Enumerator]
632
+ #
633
+ # @see #each_string
634
+ #
635
+ def strings_of_length(length)
636
+ each_string_of_length(length)
637
+ end
638
+
410
639
  #
411
640
  # Creates a new CharSet object by unioning the {CharSet} with another
412
641
  # {CharSet}.