lenc 1.1.2 → 1.1.3

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
  SHA1:
3
- metadata.gz: d9ce6a4fc8b6c84fa002e82abda1e1f736c9e893
4
- data.tar.gz: b7a33e7faac481c0aa8525970c4fce8e3d8bc88e
3
+ metadata.gz: b4b56f1960462bf9fd9382ff2059182a881e51dd
4
+ data.tar.gz: 0cb704fcc7bda5e7a326202be609027d1cb7edfa
5
5
  SHA512:
6
- metadata.gz: 2110c704af356d26953cd3fdf7adb7c15bf045464e66d195724e3c45359eba9aa77f117e8ebbed9a9edd06658ec50a578187aca2c740715c1ecb68554729f62a
7
- data.tar.gz: ae79cf0f60550963f8eab2eb1db540682230ecebef1e3466441d3f60ee420788c4e6b4bbf05ba94511a35ee13f43438719dd2bef08bf9a21aacd16af05748094
6
+ metadata.gz: 41948757b4d963f3b7cc23f6c8292e8ce51b918b2f1860087cb34c3d95765f6b82900fee46c47adb399b01cb6050a09761c6d56caf3c921badbb542263f45a1a
7
+ data.tar.gz: da5a5af9488d3901d29b71c29556df4c343f0dfa82c882556dad404ef24148e593e046d022604abef1cb2b4859c832fa49b41695d1d20fbb4e49bcf7fb3ad038
@@ -18,4 +18,6 @@
18
18
  * 1.1.2 released
19
19
  * Now hosting on GitHub
20
20
  * Updated README file
21
-
21
+
22
+ 2013-04-15
23
+ * No longer updating CHANGELOG; see git commit comments instead.
@@ -0,0 +1,628 @@
1
+ require 'set'
2
+ require 'fileutils'
3
+
4
+ # Various utility and debug convenience functions.
5
+ #
6
+
7
+ # Convenience method to perform 'require_relative' on a set of files
8
+ #
9
+ # @param fileListStr space-delimited file/path items, without .rb extensions
10
+ # @param subdir optional path to files relative to this file
11
+ #
12
+ def req(fileListStr,subdir = nil)
13
+ fileListStr.split(' ').each do |x|
14
+ if subdir
15
+ x = File.join(subdir,x)
16
+ end
17
+ x += '.rb'
18
+ require_relative(x)
19
+ end
20
+ end
21
+
22
+ # Shorthand for printf(...)
23
+ # @param args passed to printf
24
+ def pr(*args)
25
+ printf(*args)
26
+ end
27
+
28
+ # Convert an object to a human-readable string,
29
+ # or <nil>; should be considered a debug-only feature
30
+ #
31
+ def d(arg)
32
+ arg.nil? ? "<nil>" : arg.inspect
33
+ end
34
+
35
+ # Convert an object to a human-readable string,
36
+ # by calling a type-appropriate function: da, dh, or just d.
37
+ # @param arg object
38
+ # @param indent optional indentation for pretty printing; if result
39
+ # spans multiple lines, each line should be indented by this amount
40
+ #
41
+ def d2(arg, indent = 0)
42
+ return da(arg, indent) if arg.is_a? Array
43
+ return dh(arg, indent) if arg.is_a? Hash
44
+ return df(arg) if arg.class == FalseClass || arg.class == TrueClass
45
+ return d(arg)
46
+ end
47
+
48
+ # Convert an object to a human-readable string, prefixed with its type
49
+ #
50
+ def dt(arg)
51
+ if arg.nil?
52
+ return "<nil>"
53
+ end
54
+ s = arg.class.to_s
55
+ s << ':'
56
+ s << arg.inspect
57
+ s
58
+ end
59
+
60
+ # Append a particular number of spaces to a string
61
+ def add_sp(s, indent = 0)
62
+ s << ' ' * indent
63
+ end
64
+
65
+ # Pretty-print an array,
66
+ # one element to a line
67
+ # @param indent indentation of each line, in spaces
68
+ def da(array, indent = 0)
69
+ return d(array) if !array
70
+ s = 'Array ['
71
+ indent += 2
72
+ array.each do |x|
73
+ s << "\n"
74
+ add_sp(s,indent)
75
+ s2 = d2(x, indent + 2)
76
+ s << s2
77
+ end
78
+ s << " ]"
79
+ s
80
+ end
81
+
82
+ # Pretty-print a hash,
83
+ # one element to a line
84
+ # @param indent indentation of each line, in spaces
85
+ def dh(hash, indent = 0)
86
+ return d(hash) if !hash
87
+ s = 'Hash {'
88
+ indent += 2
89
+ hash.each_pair do |key,val|
90
+ s2 = d(key)
91
+ s3 = d2(val, indent + 4)
92
+ s << "\n "
93
+ add_sp(s,indent)
94
+ s << s2.chomp << " => " << s3.chomp
95
+ end
96
+ s << " }"
97
+ s
98
+ end
99
+
100
+ # Generate debug description of a boolean value
101
+ # @param flag value to interpret as a boolean; prints 'T' iff not nil
102
+ # @param label optional label
103
+ def df(flag, label=nil)
104
+ s = ''
105
+ if label
106
+ s << label << ':'
107
+ end
108
+ s << (flag ? "T" : "F")
109
+ s << ' '
110
+ s
111
+ end
112
+
113
+ # Assert that a value is true. Should be considered a
114
+ # very temporary, debug-only option; it is slow and
115
+ # generates a warning that it is being called.
116
+ # @param cond condition
117
+ # @param msg generates additional message using printf(), if these arguments exist
118
+ def assert!(cond, *msg)
119
+ one_time_alert("warning",0,"Checking assertion")
120
+ if not cond
121
+ str = (msg.size == 0) ? "assertion error" : sprintf(*msg)
122
+ raise Exception, str
123
+ end
124
+ end
125
+
126
+ # Extensions to the Enumerable module
127
+ #
128
+ module Enumerable
129
+ # Calculate a value for each item, and return the item with the
130
+ # highest value, its index, and the value.
131
+ # @yieldparam function to calculate value of an object, given that object as a parameter
132
+ # @return the triple [object, index, value] reflecting the maximum value, or
133
+ # nil if there were no items
134
+ def max_with_index
135
+
136
+ best = nil
137
+
138
+ each_with_index do |obj,ind|
139
+ sc = yield(obj)
140
+ if !best || best[2] < sc
141
+ best = [obj,ind,sc]
142
+ end
143
+ end
144
+ best
145
+ end
146
+ end
147
+
148
+ # Get a nice, concise description of the file and line
149
+ # of some caller within the stack.
150
+ #
151
+ # @param nSkip the number of items deep in the call stack to look
152
+ #
153
+ def get_caller_location(nSkip = 2)
154
+
155
+ filename = nil
156
+ linenumber = nil
157
+
158
+ if nSkip >= 0 && nSkip < caller.size
159
+ fi = caller[nSkip]
160
+
161
+ i = fi.index(':')
162
+ j = nil
163
+ if i
164
+ j = fi.index(':',i+1)
165
+ end
166
+ if j
167
+ pth = fi[0,i].split('/')
168
+ if pth.size
169
+ filename = pth[-1]
170
+ end
171
+ linenumber = fi[i+1,j-i-1]
172
+ end
173
+ end
174
+ if filename && linenumber
175
+ loc = filename + " ("+linenumber+")"
176
+ else
177
+ loc = "(UNKNOWN LOCATION)"
178
+ end
179
+ loc
180
+ end
181
+
182
+ # Set of alert strings that have already been reported
183
+ # (to avoid printing anything on subsequent invocations)
184
+ #
185
+ $AlertStrings = Set.new
186
+
187
+ # Print a message if it hasn't yet been printed,
188
+ # which includes the caller's location
189
+ #
190
+ # @param typeString e.g., "warning", "unimplemented"
191
+ # @param nSkip the number of levels deep that the caller is in the stack
192
+ # @param args if present, calls sprintf(...) with these to append to the message
193
+ #
194
+ def one_time_alert(typeString, nSkip, *args)
195
+ loc = get_caller_location(nSkip + 2)
196
+ s = "*** "+typeString+" " + loc
197
+ if args && args.size
198
+ s2 = sprintf(args[0], *args[1..-1])
199
+ msg = s + ": " + s2
200
+ else
201
+ msg = s
202
+ end
203
+
204
+ if $AlertStrings.add?(msg)
205
+ puts msg
206
+ end
207
+ end
208
+
209
+ # Print a 'warning' alert, one time only
210
+ # @param args if present, calls printf() with these
211
+ def warn(*args)
212
+ one_time_alert("warning",0, *args)
213
+ end
214
+
215
+ # Convenience method for setting 'db' true within methods,
216
+ # and to print a one-time warning if so.
217
+ # @param val value to set db to; it is convenient to disable
218
+ # debug printing quickly by adding a zero, e.g., 'warndb 0'
219
+ #
220
+ def warndb(val = true)
221
+ if !val || val == 0
222
+ return false
223
+ end
224
+ one_time_alert("warning",1, "Debug printing enabled")
225
+ true
226
+ end
227
+
228
+ # Print an 'unimplemented' alert, one time only
229
+ # @param args if present, calls printf() with these
230
+ def unimp(*args)
231
+ one_time_alert("unimplemented", 0, *args)
232
+ end
233
+
234
+ # Write a string to a text file
235
+ #
236
+ def write_text_file(path, contents)
237
+ File.open(path, "wb") {|f| f.write(contents) }
238
+ end
239
+
240
+ # Read a file's contents, return as a string
241
+ #
242
+ def read_text_file(path)
243
+ contents = nil
244
+ File.open(path,"rb") {|f| contents = f.read }
245
+ contents
246
+ end
247
+
248
+ # Method that takes a code block as an argument to
249
+ # achieve the same functionality as Java/C++'s
250
+ # do {
251
+ # ...
252
+ # ... possibly with 'break' to jump to the end ...
253
+ # } while (false);
254
+ #
255
+ def block
256
+ yield
257
+ end
258
+
259
+ # Exception class for objects in illegal states
260
+ #
261
+ class IllegalStateException < Exception
262
+ end
263
+
264
+ def to_hex(value, num_digits=4)
265
+ s = sprintf("%x", value)
266
+ s.rjust(num_digits,'0')
267
+ end
268
+
269
+ def hex_dump(byte_array_or_string, title=nil, offset=0, length= -1, bytes_per_row=16, with_text=true)
270
+ ss = hex_dump_to_string(byte_array_or_string, title, offset, length, bytes_per_row, with_text)
271
+ puts ss
272
+ end
273
+
274
+ def hex_dump_to_string(byte_array_or_string, title=nil, offset=0, length= -1, bytes_per_row=16, with_text=true)
275
+
276
+ byte_array = byte_array_or_string
277
+ if byte_array.is_a? String
278
+ byte_array = byte_array.bytes.to_a
279
+ end
280
+
281
+ ss = ''
282
+
283
+ if title
284
+ ss << title << ":\n"
285
+ end
286
+
287
+ if length < 0
288
+ length = byte_array.size - offset
289
+ end
290
+
291
+ length = [length, byte_array.size - offset].min
292
+
293
+ max_addr = offset + length - 1
294
+ num_digits = 4
295
+ while (1 << (4 * num_digits)) <= max_addr
296
+ num_digits += 1
297
+ end
298
+
299
+ while true
300
+ ss << to_hex(offset, num_digits)
301
+ ss << ': '
302
+
303
+ chunk = [length, bytes_per_row].min
304
+ bytes_per_row.times do |i|
305
+ if i % 4 == 0
306
+ ss << ' '
307
+ end
308
+
309
+ if i < chunk
310
+ v = byte_array[offset + i]
311
+ ss << ((v != 0) ? to_hex(v,2) : '..')
312
+ ss << ' '
313
+ else
314
+ ss << ' '
315
+ end
316
+
317
+ end
318
+
319
+
320
+ if with_text
321
+ ss << ' |'
322
+ bytes_per_row.times do |i|
323
+ if i < chunk
324
+ v = byte_array[offset + i]
325
+ ss << ((v >= 32 && v < 127) ? v : '_')
326
+ end
327
+ end
328
+ ss << '|'
329
+ end
330
+ ss << "\n"
331
+
332
+ length -= chunk
333
+ offset += chunk
334
+ break if length <= 0
335
+ end
336
+
337
+ ss << "\n"
338
+ ss
339
+ end
340
+
341
+ $prevTime = nil
342
+
343
+ # Calculate time elapsed, in seconds, from last call to this function;
344
+ # if it's never been called, returns zero
345
+ def elapsed
346
+ curr = Time.now.to_f
347
+ elap = 0
348
+ if $prevTime
349
+ elap = curr - $prevTime
350
+ end
351
+ $prevTime = curr
352
+ elap
353
+ end
354
+
355
+ # Delete a file or directory, if it exists.
356
+ # Caution! If directory, deletes all files and subdirectories.
357
+ def remove_file_or_dir(pth)
358
+ if File.directory?(pth)
359
+ FileUtils.remove_dir(pth)
360
+ elsif File.file?(pth)
361
+ FileUtils.remove_file(pth)
362
+ end
363
+ end
364
+
365
+ require 'stringio'
366
+
367
+ $IODest = nil
368
+ $OldStdOut = nil
369
+
370
+ def capture_begin
371
+ raise IllegalStateException if $IODest
372
+ $IODest = StringIO.new
373
+ $OldStdOut, $stdout = $stdout, $IODest
374
+ end
375
+
376
+ def capture_end
377
+ raise IllegalStateException if !$IODest
378
+ $stdout = $OldStdOut
379
+ ret = $IODest.string
380
+ $IODest = nil
381
+ ret
382
+ end
383
+
384
+ def match_expected_output(str = nil)
385
+
386
+ if !str
387
+ str = capture_end
388
+ end
389
+
390
+ cl_method = caller[0][/`.*'/][1..-2]
391
+ if (cl_method.start_with?("test_"))
392
+ cl_method = cl_method[5..-1]
393
+ end
394
+ path = "_output_" + cl_method + ".txt"
395
+ # path = File.absolute_path(path)
396
+
397
+ if !File.file?(path)
398
+ printf("no such file #{path} exists, writing it...\n")
399
+ writeTextFile(path,str)
400
+ else
401
+ exp_cont = read_text_file(path)
402
+ if str != exp_cont
403
+ d1 = str
404
+ d2 = exp_cont
405
+ # d1 = hex_dump_to_string(str,"Output")
406
+ # d2 = hex_dump_to_string(exp_cont,"Expected")
407
+
408
+ raise IllegalStateException,"output did not match expected:\n#{d1}#{d2}"
409
+ end
410
+ end
411
+ end
412
+
413
+ # Convenience method to detect if a script is being run
414
+ # e.g. as a 'main' method (for debug purposes only).
415
+ # If so, it changes the current directory to the
416
+ # directory containing the script (if such a directory exists).
417
+ #
418
+ # @param file pass __FILE__ in here
419
+ # @return true if so
420
+ #
421
+ def main?(file)
422
+
423
+ scr = $0
424
+
425
+ # The test/unit framework seems to be adding a suffix ": xxx#xxx.."
426
+ # to the .rb filename, so adjust in this case
427
+ i = scr.index(".rb: ")
428
+ if i
429
+ scr = scr[0...i+3]
430
+ end
431
+
432
+ if (ret = (file == scr))
433
+ dr = File.dirname(file)
434
+ if File.directory?(dr)
435
+ Dir.chdir(dr)
436
+ end
437
+ end
438
+ ret
439
+ end
440
+
441
+ if defined? Test::Unit
442
+
443
+ # A simple extension to Ruby's Test::Unit class that provides
444
+ # suite-level setup/teardown methods.
445
+ #
446
+ # If test suite functionality is desired within a script,
447
+ # then require 'test/unit' before requiring 'tools.rb'.
448
+ # This will cause the following class, MyTestSuite, to be defined.
449
+ #
450
+ # The user's test script can define subclasses of this,
451
+ # and declare test methods with the name 'test_xxxx', where
452
+ # xxxx is lexicographically between 01 and zz.
453
+ #
454
+ # There are two levels of setup/teardown called : suite level, and
455
+ # method level. For example, if the user's test class performs two tests:
456
+ #
457
+ # def test_b ... end
458
+ # def test_c ... end
459
+ #
460
+ # Then the test framework will make these calls:
461
+ #
462
+ # suite_setup
463
+ #
464
+ # method_setup
465
+ # test_b
466
+ # method_teardown
467
+ #
468
+ # method_setup
469
+ # test_c
470
+ # method_teardown
471
+ #
472
+ # suite_teardown
473
+ #
474
+ # Notes
475
+ # -----
476
+ # 1) The usual setup / teardown methods should NOT be overridden; instead,
477
+ # use the method_xxx alternatives.
478
+ #
479
+ # 2) The base class implementations of method_/suite_xxx do nothing.
480
+ #
481
+ # 3) The number of test cases reported may be higher than you expect, since
482
+ # there are additional test methods defined by the TestSuite class to
483
+ # implement the suite setup / teardown functionality.
484
+ #
485
+ # 4) Avoid naming test methods that fall outside of test_01 ... test_zz.
486
+ #
487
+ class MyTestSuite < Test::Unit::TestCase
488
+
489
+ # This is named to be the FIRST test called. It
490
+ # will do suite-level setup, and nothing else.
491
+ def test_00_setup
492
+ @@suiteSetup = true
493
+ suite_setup()
494
+ end
495
+
496
+ # This is named to be the LAST test called. It
497
+ # will do suite-level teardown, and nothing else.
498
+ def test_zzzzzz_teardown
499
+ suite_teardown()
500
+ @@suiteSetup = false
501
+ end
502
+
503
+ # True if called within suite-level setup/teardown window
504
+ def _suite_active?
505
+ !(@__name__ == "test_00_setup" || @__name__ == "test_zzzzzz_teardown")
506
+ end
507
+
508
+ def setup
509
+ if _suite_active?
510
+ # If only a specific test was requested, the
511
+ # suite setup may not have run... if not, do it now.
512
+ if !defined? @@suiteSetup
513
+ suite_setup
514
+ end
515
+ return
516
+ end
517
+ method_setup
518
+ end
519
+
520
+ def teardown
521
+ if _suite_active?
522
+ if !defined? @@suiteSetup
523
+ suite_teardown
524
+ end
525
+ return
526
+ end
527
+ method_teardown
528
+ end
529
+
530
+ def suite_setup
531
+ end
532
+
533
+ def suite_teardown
534
+ end
535
+
536
+ def method_setup
537
+ end
538
+
539
+ def method_teardown
540
+ end
541
+ end
542
+ end
543
+
544
+ # Construct a string from an array of bytes
545
+ # @param byte_array array of bytes, or string (in which case it
546
+ # returns it unchanged)
547
+ #
548
+ def bytes_to_str(byte_array)
549
+ return byte_array if byte_array.is_a? String
550
+
551
+ byte_array.pack('C*')
552
+ end
553
+
554
+ # Construct an array of bytes from a string
555
+ # @param str string, or array of bytes (in which case it
556
+ # returns it unchanged)
557
+ #
558
+ def str_to_bytes(str)
559
+ return str if str.is_a? Array
560
+ str.bytes
561
+ end
562
+
563
+ # Get directory entries, excluding '.' and '..'
564
+ #
565
+ def dir_entries(path)
566
+ ents = Dir.entries(path)
567
+ ents.reject!{|entry| entry == '.' || entry == '..'}
568
+ end
569
+
570
+ def int_to_bytes(x)
571
+ [(x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff]
572
+ end
573
+
574
+ def short_to_bytes(x)
575
+ [(x >> 8) & 0xff, x & 0xff]
576
+ end
577
+
578
+ # Decode a short from an array of bytes (big-endian).
579
+ # @param ba array of bytes
580
+ # @param offset offset of first (most significant) byte
581
+ #
582
+ def short_from_bytes(ba, offset=0)
583
+ (ba[offset] << 8) | ba[offset + 1]
584
+ end
585
+
586
+ # Decode an int from an array of bytes (big-endian).
587
+ # @param ba array of bytes
588
+ # @param offset offset of first (most significant) byte
589
+ #
590
+ def int_from_bytes(ba, offset=0)
591
+ (((((ba[offset] << 8) | ba[offset + 1]) << 8) | \
592
+ ba[offset + 2]) << 8) | ba[offset + 3]
593
+ end
594
+
595
+ # Transform string to 8-bit ASCII (i.e., just treat each byte as-is)
596
+ #
597
+ def to_ascii8(str)
598
+ str.force_encoding("ASCII-8BIT")
599
+ end
600
+
601
+ # Verify that a string is encoded as ASCII-8BIT
602
+ def simple_str(s)
603
+ if s.encoding.name != 'ASCII-8BIT' && s.encoding.name != 'UTF-8'
604
+ pr("string [%s]\n encoding is %s,\n expected ASCII-8BIT\n",s,s.encoding.name)
605
+ assert!(false)
606
+ end
607
+ end
608
+
609
+ # Truncate or pad string so it has a particular size
610
+ #
611
+ # @param s input string
612
+ # @param size
613
+ # @param pad padding character to use if string needs to grow
614
+ # @return modified string
615
+ #
616
+ def str_sized(s, size, pad="\0")
617
+ s[0...size].ljust(size,pad)
618
+ end
619
+
620
+ # Determine if running on the Windows operating system.
621
+ # Note: there is some debate about the best way to do this.
622
+ #
623
+ def windows?
624
+ if !defined? $__windows__
625
+ $__windows__ = (RUBY_PLATFORM =~ /mswin/)
626
+ end
627
+ $__windows__
628
+ end
@@ -1,8 +1,26 @@
1
1
  require 'set'
2
2
  require 'fileutils'
3
3
 
4
+ ###############################################################
5
+ #
4
6
  # Various utility and debug convenience functions.
5
7
  #
8
+ ###############################################################
9
+
10
+ # Exception class for objects in illegal states
11
+ #
12
+ class IllegalStateException < Exception
13
+ end
14
+
15
+ # A string containing a single zero, with ASCII 8-bit encoding (i.e., plain old bytes)
16
+ ZERO_CHAR = "\0".force_encoding("ASCII-8BIT")
17
+
18
+ # Construct a string of zeros
19
+ # @param count number of zeros
20
+ #
21
+ def zero_bytes(count)
22
+ ZERO_CHAR * count
23
+ end
6
24
 
7
25
  # Convenience method to perform 'require_relative' on a set of files
8
26
  #
@@ -20,20 +38,16 @@ def req(fileListStr,subdir = nil)
20
38
  end
21
39
 
22
40
  # Shorthand for printf(...)
23
- # @param args passed to printf
24
- def pr(*args)
25
- printf(*args)
26
- end
27
-
41
+ #
42
+ alias :pr :printf
28
43
 
29
44
  # Convert an object to a human-readable string,
30
- # or <nil>
45
+ # or <nil>; should be considered a debug-only feature
31
46
  #
32
47
  def d(arg)
33
48
  arg.nil? ? "<nil>" : arg.inspect
34
49
  end
35
50
 
36
-
37
51
  # Convert an object to a human-readable string,
38
52
  # by calling a type-appropriate function: da, dh, or just d.
39
53
  # @param arg object
@@ -112,8 +126,6 @@ def df(flag, label=nil)
112
126
  s
113
127
  end
114
128
 
115
-
116
-
117
129
  # Assert that a value is true. Should be considered a
118
130
  # very temporary, debug-only option; it is slow and
119
131
  # generates a warning that it is being called.
@@ -127,63 +139,37 @@ def assert!(cond, *msg)
127
139
  end
128
140
  end
129
141
 
142
+ # Abort with message about unimplemented code
143
+ #
144
+ def unimp!(msg = nil)
145
+ msg2 = "Unimplemented code"
146
+ if msg
147
+ msg2 << ": " << msg
148
+ end
149
+ raise Exception, msg2
150
+ end
130
151
 
131
- ## Set test directory. If nil, sets to home directory + "__test__"
132
- #def setTestDir(d = nil)
133
- # $testDir = d || File.join(Dir.home,"__test__")
134
- #end
135
-
136
- ## Get a path within the test directory;
137
- ## create test directory if it doesn't exist.
138
- ##
139
- ## relPath : if nil, returns the test directory; else
140
- ## returns the test directory joined to this one
141
- ##
142
- #def withinTestDir(relPath = nil)
143
- # if !$testDir
144
- # raise IllegalStateException, "No test directory has been defined"
145
- # end
146
- # if !File.directory?($testDir)
147
- # Dir::mkdir($testDir)
148
- # end
149
- # relPath ? File.join($testDir,relPath) : $testDir
150
- #end
151
-
152
- ## Convert a .dot file (string) to a PDF file "__mygraph__nnn.pdf"
153
- ## in the test directory.
154
- ##
155
- ## It does this by making a system call to the 'dot' utility.
156
- ##
157
- #def dotToPDF(dotFile, name = "")
158
- # gr = dotFile
159
- # dotPath = withinTestDir(".__mygraph__.dot")
160
- # write_text_file(dotPath,gr)
161
- # destName = withinTestDir( "__mygraph__"+name+".pdf")
162
- # system("dot -Tpdf "+dotPath+" -o "+destName)
163
- #end
164
-
165
- ## Extensions to the Enumerable module
166
- ##
167
- #module Enumerable
168
- # # Calculate a value for each item, and return the item with the
169
- # # highest value, its index, and the value.
170
- # # @yieldparam function to calculate value of an object, given that object as a parameter
171
- # # @return the triple [object, index, value] reflecting the maximum value, or
172
- # # nil if there were no items
173
- # def max_with_index
174
- #
175
- # best = nil
176
- #
177
- # each_with_index do |obj,ind|
178
- # sc = yield(obj)
179
- # if !best || best[2] < sc
180
- # best = [obj,ind,sc]
181
- # end
182
- # end
183
- # best
184
- # end
185
- #end
186
-
152
+ # Extensions to the Enumerable module
153
+ #
154
+ module Enumerable
155
+ # Calculate a value for each item, and return the item with the
156
+ # highest value, its index, and the value.
157
+ # @yieldparam function to calculate value of an object, given that object as a parameter
158
+ # @return the triple [object, index, value] reflecting the maximum value, or
159
+ # nil if there were no items
160
+ def max_with_index
161
+
162
+ best = nil
163
+
164
+ each_with_index do |obj,ind|
165
+ sc = yield(obj)
166
+ if !best || best[2] < sc
167
+ best = [obj,ind,sc]
168
+ end
169
+ end
170
+ best
171
+ end
172
+ end
187
173
 
188
174
  # Get a nice, concise description of the file and line
189
175
  # of some caller within the stack.
@@ -252,6 +238,19 @@ def warn(*args)
252
238
  one_time_alert("warning",0, *args)
253
239
  end
254
240
 
241
+ # Convenience method for setting 'db' true within methods,
242
+ # and to print a one-time warning if so.
243
+ # @param val value to set db to; it is convenient to disable
244
+ # debug printing quickly by adding a zero, e.g., 'warndb 0'
245
+ #
246
+ def warndb(val = true)
247
+ if !val || val == 0
248
+ return false
249
+ end
250
+ one_time_alert("warning",1,"Debug printing enabled")
251
+ true
252
+ end
253
+
255
254
  # Print an 'unimplemented' alert, one time only
256
255
  # @param args if present, calls printf() with these
257
256
  def unimp(*args)
@@ -283,22 +282,30 @@ def block
283
282
  yield
284
283
  end
285
284
 
286
- # Exception class for objects in illegal states
285
+ # Construct hex representation of value
286
+ # @param value integer value
287
+ # @param num_digits number of hex digits
287
288
  #
288
- class IllegalStateException < Exception
289
- end
290
-
291
-
292
289
  def to_hex(value, num_digits=4)
293
290
  s = sprintf("%x", value)
294
291
  s.rjust(num_digits,'0')
295
292
  end
296
293
 
294
+ # Hex dump a string or byte array
295
+ # @param byte_array_or_string
296
+ # @param title
297
+ # @param offset offset to first value within array
298
+ # @param length number of values to dump
299
+ # @param bytes_per_row
300
+ # @param with_text if true, displays ASCII values to right of hex dump
301
+ #
297
302
  def hex_dump(byte_array_or_string, title=nil, offset=0, length= -1, bytes_per_row=16, with_text=true)
298
303
  ss = hex_dump_to_string(byte_array_or_string, title, offset, length, bytes_per_row, with_text)
299
304
  puts ss
300
305
  end
301
306
 
307
+ # Hex dump a string or byte array to a string; see hex_dump for parameter descriptions
308
+ #
302
309
  def hex_dump_to_string(byte_array_or_string, title=nil, offset=0, length= -1, bytes_per_row=16, with_text=true)
303
310
 
304
311
  byte_array = byte_array_or_string
@@ -341,10 +348,8 @@ def hex_dump_to_string(byte_array_or_string, title=nil, offset=0, length= -1, by
341
348
  else
342
349
  ss << ' '
343
350
  end
344
-
345
351
  end
346
352
 
347
-
348
353
  if with_text
349
354
  ss << ' |'
350
355
  bytes_per_row.times do |i|
@@ -361,8 +366,6 @@ def hex_dump_to_string(byte_array_or_string, title=nil, offset=0, length= -1, by
361
366
  offset += chunk
362
367
  break if length <= 0
363
368
  end
364
-
365
- ss << "\n"
366
369
  ss
367
370
  end
368
371
 
@@ -370,6 +373,7 @@ $prevTime = nil
370
373
 
371
374
  # Calculate time elapsed, in seconds, from last call to this function;
372
375
  # if it's never been called, returns zero
376
+ #
373
377
  def elapsed
374
378
  curr = Time.now.to_f
375
379
  elap = 0
@@ -380,74 +384,9 @@ def elapsed
380
384
  elap
381
385
  end
382
386
 
383
- # Construct a string from an array of bytes
384
- # @param byte_array array of bytes, or string (in which case it
385
- # returns it unchanged)
386
- #
387
- def bytes_to_str(byte_array)
388
- return byte_array if byte_array.is_a? String
389
-
390
- byte_array.pack('C*')
391
- end
392
-
393
- # Construct an array of bytes from a string
394
- # @param str string, or array of bytes (in which case it
395
- # returns it unchanged)
396
- #
397
- def str_to_bytes(str)
398
- return str if str.is_a? Array
399
- str.bytes
400
- end
401
-
402
- # Get directory entries, excluding '.' and '..'
403
- #
404
- def dir_entries(path)
405
- ents = Dir.entries(path)
406
- ents.reject!{|entry| entry == '.' || entry == '..'}
407
- end
408
-
409
- # Convenience method for setting 'db' true within methods,
410
- # and to print a one-time warning if so.
411
- # @param val value to set db to; it is convenient to disable
412
- # debug printing quickly by adding a zero, e.g., 'warndb 0'
413
- #
414
- def warndb(val = true)
415
- if !val || val == 0
416
- return false
417
- end
418
- one_time_alert("warning",1, "Debug printing enabled")
419
- true
420
- end
421
-
422
-
423
- def int_to_bytes(x)
424
- [(x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff]
425
- end
426
-
427
- def short_to_bytes(x)
428
- [(x >> 8) & 0xff, x & 0xff]
429
- end
430
-
431
- # Decode a short from an array of bytes (big-endian).
432
- # @param ba array of bytes
433
- # @param offset offset of first (most significant) byte
434
- #
435
- def short_from_bytes(ba, offset=0)
436
- (ba[offset] << 8) | ba[offset + 1]
437
- end
438
-
439
- # Decode an int from an array of bytes (big-endian).
440
- # @param ba array of bytes
441
- # @param offset offset of first (most significant) byte
442
- #
443
- def int_from_bytes(ba, offset=0)
444
- (((((ba[offset] << 8) | ba[offset + 1]) << 8) | \
445
- ba[offset + 2]) << 8) | ba[offset + 3]
446
- end
447
-
448
-
449
387
  # Delete a file or directory, if it exists.
450
388
  # Caution! If directory, deletes all files and subdirectories.
389
+ #
451
390
  def remove_file_or_dir(pth)
452
391
  if File.directory?(pth)
453
392
  FileUtils.remove_dir(pth)
@@ -456,52 +395,96 @@ def remove_file_or_dir(pth)
456
395
  end
457
396
  end
458
397
 
459
- # Transform string to 8-bit ASCII (i.e., just treat each byte as-is)
398
+ require 'stringio'
399
+
400
+ $IODest = nil
401
+ $OldStdOut = nil
402
+
403
+ # Redirect standard output to an internal string
460
404
  #
461
- def to_ascii8(str)
462
- str.force_encoding("ASCII-8BIT")
405
+ def capture_begin
406
+ raise IllegalStateException if $IODest
407
+ $IODest = StringIO.new
408
+ $OldStdOut, $stdout = $stdout, $IODest
463
409
  end
464
410
 
465
- # Verify that a string is encoded as ASCII-8BIT
466
- def simple_str(s)
467
- if s.encoding.name != 'ASCII-8BIT' && s.encoding.name != 'UTF-8'
468
- pr("string [%s]\n encoding is %s,\n expected ASCII-8BIT\n",s,s.encoding.name)
469
- assert!(false)
470
- end
411
+ # Restore standard output; return captured text
412
+ # @return text that was redirected
413
+ #
414
+ def capture_end
415
+ raise IllegalStateException if !$IODest
416
+ $stdout = $OldStdOut
417
+ ret = $IODest.string
418
+ $IODest = nil
419
+ ret
471
420
  end
472
421
 
473
- # Truncate or pad string so it has a particular size
422
+ # Compare a string with disk file; abort if different. Disk filename is derived
423
+ # from caller function name; e.g., test_xxx produces filename _output_xxx
474
424
  #
475
- # @param s input string
476
- # @param size
477
- # @param pad padding character to use if string needs to grow
478
- # @return modified string
425
+ # @param str if not nil, string to compare; if nil, calls capture_end to get string
479
426
  #
480
- def str_sized(s, size, pad="\0")
481
- s[0...size].ljust(size,pad)
482
- end
427
+ def match_expected_output(str = nil)
483
428
 
484
- # Determine if running on the Windows operating system.
485
- # Note: there is some debate about the best way to do this.
486
- #
487
- def windows?
488
- if !defined? $__windows__
489
- $__windows__ = (RUBY_PLATFORM =~ /mswin/)
429
+ if !str
430
+ str = capture_end
490
431
  end
491
- $__windows__
492
- end
493
432
 
494
- # I didn't end up needing this:
495
- #
496
- #module Kernel
497
- # def suppress_warnings
498
- # original_verbosity = $VERBOSE
499
- # $VERBOSE = nil
500
- # result = yield
501
- # $VERBOSE = original_verbosity
502
- # return result
503
- # end
504
- #end
433
+ cl_method = caller[0][/`.*'/][1..-2]
434
+ if (cl_method.start_with?("test_"))
435
+ cl_method = cl_method[5..-1]
436
+ end
437
+ path = "_output_" + cl_method + ".txt"
438
+
439
+ if !File.file?(path)
440
+ printf("no such file #{path} exists, writing it...\n")
441
+ write_text_file(path,str)
442
+ else
443
+ exp_cont = read_text_file(path)
444
+ if str != exp_cont
445
+ d1 = str
446
+ d2 = exp_cont
447
+
448
+ # Find location where they differ
449
+ lines1 = d1.split("\n")
450
+ lines2 = d2.split("\n")
451
+ j = [lines1.size, lines2.size].max
452
+
453
+ s = "???"
454
+ found_diff = false
455
+ hist = []
456
+
457
+ found_count = 0
458
+ j.times do |i|
459
+ found_diff ||= (i >= lines1.size || i >= lines2.size || lines1[i] != lines2[i])
460
+ s = sprintf("%3d:",i)
461
+ if !found_diff
462
+ hist << "#{s} #{lines1[i]}\n #{lines2[i]}\n"
463
+ else
464
+ if found_count < 3
465
+ if i < lines1.size
466
+ s << " #{lines1[i]}\n"
467
+ else
468
+ s << " ---END---\n"
469
+ end
470
+ if i < lines2.size
471
+ s << " #{lines2[i]}\n"
472
+ else
473
+ s << " ---END---\n"
474
+ end
475
+ hist << s
476
+ end
477
+ found_count += 1
478
+ end
479
+ while hist.size > 6
480
+ hist.shift
481
+ end
482
+ end
483
+ dash = "-" * 95 + "\n"
484
+ raise IllegalStateException,"output did not match expected:\n#{dash}#{hist.join('')}#{dash}"
485
+ end
486
+ end
487
+ end
505
488
 
506
489
  # Convenience method to detect if a script is being run
507
490
  # e.g. as a 'main' method (for debug purposes only).
@@ -634,3 +617,136 @@ if defined? Test::Unit
634
617
  end
635
618
  end
636
619
 
620
+ # Construct a string from an array of bytes
621
+ # @param byte_array array of bytes, or string (in which case it
622
+ # returns it unchanged)
623
+ #
624
+ def bytes_to_str(byte_array)
625
+ return byte_array if byte_array.is_a? String
626
+
627
+ byte_array.pack('C*')
628
+ end
629
+
630
+ # Construct an array of bytes from a string
631
+ # @param str string, or array of bytes (in which case it
632
+ # returns it unchanged)
633
+ #
634
+ def str_to_bytes(str)
635
+ return str if str.is_a? Array
636
+ str.bytes
637
+ end
638
+
639
+ # Get directory entries, excluding '.' and '..'
640
+ #
641
+ def dir_entries(path)
642
+ ents = Dir.entries(path)
643
+ ents.reject!{|entry| entry == '.' || entry == '..'}
644
+ end
645
+
646
+ def int_to_bytes(x)
647
+ [(x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff]
648
+ end
649
+
650
+ def short_to_bytes(x)
651
+ [(x >> 8) & 0xff, x & 0xff]
652
+ end
653
+
654
+ # Decode a short from an array of bytes (big-endian).
655
+ # @param ba array of bytes
656
+ # @param offset offset of first (most significant) byte
657
+ #
658
+ def short_from_bytes(ba, offset=0)
659
+ (ba[offset] << 8) | ba[offset + 1]
660
+ end
661
+
662
+ # Decode an int from an array of bytes (big-endian).
663
+ # @param ba array of bytes
664
+ # @param offset offset of first (most significant) byte
665
+ #
666
+ def int_from_bytes(ba, offset=0)
667
+ (((((ba[offset] << 8) | ba[offset + 1]) << 8) | \
668
+ ba[offset + 2]) << 8) | ba[offset + 3]
669
+ end
670
+
671
+ # Transform string to 8-bit ASCII (i.e., just treat each byte as-is)
672
+ #
673
+ def to_ascii8(str)
674
+ str.force_encoding("ASCII-8BIT")
675
+ end
676
+
677
+ # Verify that a string is encoded as ASCII-8BIT
678
+ def simple_str(s)
679
+ if s.encoding.name != 'ASCII-8BIT' && s.encoding.name != 'UTF-8'
680
+ pr("string [%s]\n encoding is %s,\n expected ASCII-8BIT\n",s,s.encoding.name)
681
+ assert!(false)
682
+ end
683
+ end
684
+
685
+ # Truncate or pad string so it has a particular size
686
+ #
687
+ # @param s input string
688
+ # @param size
689
+ # @param pad padding character to use if string needs to grow
690
+ # @return modified string
691
+ #
692
+ def str_sized(s, size, pad="\0")
693
+ s[0...size].ljust(size,pad)
694
+ end
695
+
696
+ # Determine if running on the Windows operating system.
697
+ # Note: there is some debate about the best way to do this.
698
+ #
699
+ def windows?
700
+ if !defined? $__windows__
701
+ $__windows__ = (RUBY_PLATFORM =~ /mswin/)
702
+ end
703
+ $__windows__
704
+ end
705
+
706
+ # Mark all constants ending with '_' as private constants
707
+ #
708
+ # @param entity the class to examine
709
+ # @param add_non_suffix_versions if true, for each constant ABC_ found, also
710
+ # defines a constant ABC with the same value that is also private
711
+ #
712
+ def privatize(entity, add_non_suffix_versions = false)
713
+
714
+ db = false
715
+
716
+ # First command defines constants ABC = n for each constant ABC_ = n;
717
+ # Second declares both versions to be private
718
+
719
+ cmd1 = nil
720
+ cmd2 = nil
721
+
722
+ entity.constants.each do |c|
723
+ nm = c.to_s
724
+
725
+ if nm.end_with?('_')
726
+ nm_small = nm[0..-2]
727
+
728
+ if !cmd2
729
+ if add_non_suffix_versions
730
+ cmd1 = ''
731
+ end
732
+ cmd2 = 'private_constant '
733
+ else
734
+ cmd2 << ','
735
+ end
736
+
737
+
738
+ !cmd1 || cmd1 << entity.to_s << '::' << nm_small << '=' << entity.const_get(c).to_s << "\n"
739
+ !cmd1 || cmd2 << ':' << nm_small << ','
740
+ cmd2 << ':' << nm
741
+ end
742
+ end
743
+
744
+ if cmd2
745
+ if cmd1
746
+ !db || pr("about to eval:\n%s\n",cmd1)
747
+ eval(cmd1)
748
+ end
749
+ !db || pr("about to eval:\n%s\n",cmd2)
750
+ eval(cmd2)
751
+ end
752
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lenc
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 1.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Sember
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-04-09 00:00:00.000000000 Z
11
+ date: 2013-04-15 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: "Encrypts a set of local files, and copies the encrypted versions to
14
14
  a repository, \nwhich may be located within a free cloud service (Dropbox, Google
@@ -22,6 +22,7 @@ extensions: []
22
22
  extra_rdoc_files: []
23
23
  files:
24
24
  - lib/lenc.rb
25
+ - lib/lenc/_OLD_tools.rb
25
26
  - lib/lenc/aes.rb
26
27
  - lib/lenc/config_file.rb
27
28
  - lib/lenc/lencrypt.rb