chars 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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}.