Top Level Namespace

Defined Under Namespace

Modules: Enumerable, ExternalSortModule, GeoTreeModule, PS_Private Classes: BlockFile, DiskBlockFile, IllegalStateException, MyTestSuite, PSWriter

Constant Summary collapse

ZERO_CHAR =

A string containing a single zero, with ASCII 8-bit encoding (i.e., plain old bytes)

"\0".force_encoding("ASCII-8BIT")

Instance Method Summary collapse

Instance Method Details

#add_sp(s, indent = 0) ⇒ Object

Append a particular number of spaces to a string



77
78
79
# File 'lib/geotree/tools.rb', line 77

def add_sp(s, indent = 0)
  s << ' ' * indent
end

#assert!(cond, *msg) ⇒ Object

Assert that a value is true. Should be considered a very temporary, debug-only option; it is slow and generates a warning that it is being called.

Parameters:

  • cond

    condition

  • msg

    generates additional message using printf(), if these arguments exist



134
135
136
137
138
139
140
# File 'lib/geotree/tools.rb', line 134

def assert!(cond, *msg)
  one_time_alert("warning",0,"Checking assertion")
  if not cond
    str = (msg.size == 0) ? "assertion error" : sprintf(*msg)
    raise Exception, str
  end
end

#blockObject

Method that takes a code block as an argument to achieve the same functionality as Java/C++‘s

do {
  ...
  ...  possibly with 'break' to jump to the end ...
} while (false);


281
282
283
# File 'lib/geotree/tools.rb', line 281

def block
  yield
end

#bytes_to_str(byte_array) ⇒ Object

Construct a string from an array of bytes

Parameters:

  • byte_array

    array of bytes, or string (in which case it returns it unchanged)



624
625
626
627
628
# File 'lib/geotree/tools.rb', line 624

def bytes_to_str(byte_array)
  return byte_array if byte_array.is_a? String
  
  byte_array.pack('C*')
end

#capture_beginObject

Redirect standard output to an internal string



405
406
407
408
409
# File 'lib/geotree/tools.rb', line 405

def capture_begin
    raise IllegalStateException if $IODest
    $IODest = StringIO.new
    $OldStdOut, $stdout = $stdout, $IODest
end

#capture_endObject

Restore standard output; return captured text

Returns:

  • text that was redirected

Raises:



414
415
416
417
418
419
420
# File 'lib/geotree/tools.rb', line 414

def capture_end
  raise IllegalStateException if !$IODest
  $stdout = $OldStdOut  
  ret = $IODest.string
  $IODest = nil
  ret
end

#d(arg) ⇒ Object

Convert an object to a human-readable string, or <nil>; should be considered a debug-only feature



47
48
49
# File 'lib/geotree/tools.rb', line 47

def d(arg)
  arg.nil? ? "<nil>" : arg.inspect
end

#d2(arg, indent = 0) ⇒ Object

Convert an object to a human-readable string, by calling a type-appropriate function: da, dh, or just d.

Parameters:

  • arg

    object

  • indent (defaults to: 0)

    optional indentation for pretty printing; if result spans multiple lines, each line should be indented by this amount



57
58
59
60
61
62
# File 'lib/geotree/tools.rb', line 57

def d2(arg, indent = 0)
  return da(arg, indent) if arg.is_a? Array
  return dh(arg, indent) if arg.is_a? Hash
  return df(arg) if arg.class == FalseClass || arg.class == TrueClass
  return d(arg)  
end

#da(array, indent = 0) ⇒ Object

Pretty-print an array, one element to a line

Parameters:

  • indent (defaults to: 0)

    indentation of each line, in spaces



84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/geotree/tools.rb', line 84

def da(array, indent = 0)
  return d(array) if !array
  s = 'Array ['
  indent += 2
  array.each do |x| 
    s << "\n"
    add_sp(s,indent)
    s2 = d2(x, indent + 2)
    s << s2
  end
  s << " ]"
  s
end

#df(flag, label = nil) ⇒ Object

Generate debug description of a boolean value

Parameters:

  • flag

    value to interpret as a boolean; prints ‘T’ iff not nil

  • label (defaults to: nil)

    optional label



119
120
121
122
123
124
125
126
127
# File 'lib/geotree/tools.rb', line 119

def df(flag, label=nil)
  s = ''
  if label
    s << label << ':'
  end
  s << (flag ? "T" : "F")
  s << ' '
  s
end

#dh(hash, indent = 0) ⇒ Object

Pretty-print a hash, one element to a line

Parameters:

  • indent (defaults to: 0)

    indentation of each line, in spaces



101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/geotree/tools.rb', line 101

def dh(hash, indent = 0)
  return d(hash) if !hash
  s = 'Hash {'
  indent += 2
  hash.each_pair do |key,val| 
    s2 = d(key)
    s3 = d2(val, indent + 4)
    s << "\n " 
    add_sp(s,indent)
    s << s2.chomp << " => " << s3.chomp
  end
  s << " }"
  s
end

#dir_entries(path) ⇒ Object

Get directory entries, excluding ‘.’ and ‘..’



641
642
643
644
# File 'lib/geotree/tools.rb', line 641

def dir_entries(path)
  ents = Dir.entries(path)
  ents.reject!{|entry| entry == '.' || entry == '..'}
end

#dt(arg) ⇒ Object

Convert an object to a human-readable string, prefixed with its type



66
67
68
69
70
71
72
73
74
# File 'lib/geotree/tools.rb', line 66

def dt(arg)
  if arg.nil?
    return "<nil>"
  end
  s = arg.class.to_s
  s << ':'
  s << arg.inspect
  s
end

#elapsedObject

Calculate time elapsed, in seconds, from last call to this function; if it’s never been called, returns zero



377
378
379
380
381
382
383
384
385
# File 'lib/geotree/tools.rb', line 377

def elapsed 
  curr = Time.now.to_f
  elap = 0
  if $prevTime
    elap = curr - $prevTime
  end
  $prevTime = curr
  elap
end

#get_caller_location(nSkip = 2) ⇒ Object

Get a nice, concise description of the file and line of some caller within the stack.

Parameters:

  • nSkip (defaults to: 2)

    the number of items deep in the call stack to look



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/geotree/tools.rb', line 179

def get_caller_location(nSkip = 2) 
  
  filename = nil
  linenumber = nil
  
  if nSkip >= 0 && nSkip < caller.size
    fi = caller[nSkip]   
    
    i = fi.index(':')
    j = nil
    if i
      j = fi.index(':',i+1)
    end
    if j
      pth = fi[0,i].split('/')
      if pth.size
        filename = pth[-1]
      end
      linenumber = fi[i+1,j-i-1]  
    end
  end
  if filename && linenumber
    loc = filename + " ("+linenumber+")"
  else 
    loc = "(UNKNOWN LOCATION)"
  end
  loc
end

#hex_dump(byte_array_or_string, title = nil, offset = 0, length = -1,, bytes_per_row = 16, with_text = true) ⇒ Object

Hex dump a string or byte array

Parameters:

  • byte_array_or_string
  • title (defaults to: nil)
  • offset (defaults to: 0)

    offset to first value within array

  • length (defaults to: -1,)

    number of values to dump

  • bytes_per_row (defaults to: 16)
  • with_text (defaults to: true)

    if true, displays ASCII values to right of hex dump



302
303
304
305
# File 'lib/geotree/tools.rb', line 302

def hex_dump(byte_array_or_string, title=nil, offset=0, length= -1, bytes_per_row=16, with_text=true) 
  ss = hex_dump_to_string(byte_array_or_string, title, offset, length, bytes_per_row, with_text)
  puts ss
end

#hex_dump_to_string(byte_array_or_string, title = nil, offset = 0, length = -1,, bytes_per_row = 16, with_text = true) ⇒ Object

Hex dump a string or byte array to a string; see hex_dump for parameter descriptions



309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
# File 'lib/geotree/tools.rb', line 309

def hex_dump_to_string(byte_array_or_string, title=nil, offset=0, length= -1, bytes_per_row=16, with_text=true)
  
  byte_array = byte_array_or_string
  if byte_array.is_a? String
    byte_array = byte_array.bytes.to_a
  end
  
  ss = ''
  
  if title 
    ss << title << ":\n"
  end
  
  if length < 0 
    length = byte_array.size - offset
  end
    
  length = [length, byte_array.size - offset].min
  
  max_addr = offset + length - 1
  num_digits = 4
  while (1 << (4 * num_digits)) <= max_addr
    num_digits += 1
  end
  
  while true
    ss << to_hex(offset, num_digits)
    ss << ': '
    
    chunk = [length, bytes_per_row].min
    bytes_per_row.times do |i|
      if i % 4 == 0 
        ss << '  '
      end
      
      if i < chunk 
        v = byte_array[offset + i]
        ss << ((v != 0) ? to_hex(v,2) : '..')
        ss << ' '
      else
        ss << '   '
      end
    end
        
    if with_text 
      ss << '  |'
      bytes_per_row.times do |i|
        if i < chunk 
          v = byte_array[offset + i]
          ss << ((v >= 32 && v < 127) ? v : '_')
        end
      end
      ss << '|'
    end
    ss << "\n"
    
    length -= chunk
    offset += chunk
    break if length <= 0
  end
  ss
end

#int_from_bytes(ba, offset = 0) ⇒ Object

Decode an int from an array of bytes (big-endian).

Parameters:

  • ba

    array of bytes

  • offset (defaults to: 0)

    offset of first (most significant) byte



666
667
668
669
# File 'lib/geotree/tools.rb', line 666

def int_from_bytes(ba, offset=0) 
  (((((ba[offset] << 8) | ba[offset + 1]) << 8) | \
      ba[offset + 2]) << 8) | ba[offset + 3]
end

#int_to_bytes(x) ⇒ Object



646
647
648
# File 'lib/geotree/tools.rb', line 646

def int_to_bytes(x)
  [(x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff]
end

#main?(file) ⇒ Boolean

Convenience method to detect if a script is being run e.g. as a ‘main’ method (for debug purposes only). If so, it changes the current directory to the directory containing the script (if such a directory exists).

Parameters:

  • file

    pass __FILE__ in here

Returns:

  • (Boolean)

    true if so



497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
# File 'lib/geotree/tools.rb', line 497

def main?(file)
  
  scr = $0

  # The test/unit framework seems to be adding a suffix ": xxx#xxx.."
  # to the .rb filename, so adjust in this case
  i = scr.index(".rb: ")
  if i
    scr = scr[0...i+3]
  end

  if (ret = (file == scr))
    dr = File.dirname(file)
    if File.directory?(dr)
      Dir.chdir(dr)
    end
  end
  ret
end

#match_expected_output(str = nil) ⇒ Object

Compare a string with disk file; abort if different. Disk filename is derived from caller function name; e.g., test_xxx produces filename _output_xxx

Parameters:

  • str (defaults to: nil)

    if not nil, string to compare; if nil, calls capture_end to get string



427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
# File 'lib/geotree/tools.rb', line 427

def match_expected_output(str = nil)

  if !str
    str = capture_end
  end

  cl_method = caller[0][/`.*'/][1..-2]
  if (cl_method.start_with?("test_"))
    cl_method = cl_method[5..-1]
  end
  path = "_output_" + cl_method + ".txt"

  if !File.file?(path)
    printf("no such file #{path} exists, writing it...\n")
    write_text_file(path,str)
  else
    exp_cont = read_text_file(path)
    if str != exp_cont
      d1 = str
      d2 = exp_cont

      # Find location where they differ
      lines1 = d1.split("\n")
      lines2 = d2.split("\n")
      j = [lines1.size, lines2.size].max

      s = "???"
      found_diff = false
      hist = []

      found_count = 0
      j.times do |i|
        found_diff ||= (i >= lines1.size || i >= lines2.size || lines1[i] != lines2[i])
        s = sprintf("%3d:",i)
        if !found_diff
          hist << "#{s}  #{lines1[i]}\n      #{lines2[i]}\n"
        else
          if found_count < 3
            if i < lines1.size
              s << "  #{lines1[i]}\n"
            else
              s << "  ---END---\n"
            end
            if i < lines2.size
              s << "      #{lines2[i]}\n"
            else
              s << "      ---END---\n"
            end
            hist << s
          end
          found_count += 1
        end
        while hist.size > 6
          hist.shift
        end
      end
      dash = "-" * 95 + "\n"
      raise IllegalStateException,"output did not match expected:\n#{dash}#{hist.join('')}#{dash}"
    end
  end
end

#one_time_alert(typeString, nSkip, *args) ⇒ Object

Print a message if it hasn’t yet been printed, which includes the caller’s location

Parameters:

  • typeString

    e.g., “warning”, “unimplemented”

  • nSkip

    the number of levels deep that the caller is in the stack

  • args

    if present, calls sprintf(…) with these to append to the message



220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/geotree/tools.rb', line 220

def one_time_alert(typeString, nSkip, *args) 
  loc = get_caller_location(nSkip + 2)
  s = "*** "+typeString+" " + loc
  if args && args.size
    s2 = sprintf(args[0], *args[1..-1])
    msg = s + ": " + s2
  else 
    msg = s
  end

  if $AlertStrings.add?(msg)
    puts msg
  end
end

#privatize(entity, add_non_suffix_versions = false) ⇒ Object

Mark all constants ending with ‘_’ as private constants

Parameters:

  • entity

    the class to examine

  • add_non_suffix_versions (defaults to: false)

    if true, for each constant ABC_ found, also defines a constant ABC with the same value that is also private



712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
# File 'lib/geotree/tools.rb', line 712

def privatize(entity, add_non_suffix_versions = false)
  
  db = false
  
  # First command defines constants ABC = n for each constant ABC_ = n;
  # Second declares both versions to be private
  
  cmd1 = nil
  cmd2 = nil
  
  entity.constants.each do |c|
    nm = c.to_s
    
    if nm.end_with?('_')
      nm_small = nm[0..-2]
      
      if !cmd2
        if add_non_suffix_versions
          cmd1 = ''
        end
        cmd2 = 'private_constant '
      else
        cmd2 << ','
      end 
      
      
      !cmd1 || cmd1 << entity.to_s << '::' << nm_small << '=' << entity.const_get(c).to_s << "\n"
      !cmd1 || cmd2 << ':' << nm_small << ','
      cmd2 << ':' << nm
    end
  end
  
  if cmd2
     if cmd1
       !db || pr("about to eval:\n%s\n",cmd1)
       eval(cmd1)
     end
     !db || pr("about to eval:\n%s\n",cmd2)
     eval(cmd2)
  end
end

#read_text_file(path) ⇒ Object

Read a file’s contents, return as a string



268
269
270
271
272
# File 'lib/geotree/tools.rb', line 268

def read_text_file(path)
  contents = nil
  File.open(path,"rb") {|f| contents = f.read }
  contents
end

#remove_file_or_dir(pth) ⇒ Object

Delete a file or directory, if it exists. Caution! If directory, deletes all files and subdirectories.



390
391
392
393
394
395
396
# File 'lib/geotree/tools.rb', line 390

def remove_file_or_dir(pth)
  if File.directory?(pth)
    FileUtils.remove_dir(pth)
  elsif File.file?(pth)
    FileUtils.remove_file(pth)
  end
end

#req(fileListStr, subdir = nil) ⇒ Object

Convenience method to perform ‘require_relative’ on a set of files

Parameters:

  • fileListStr

    space-delimited file/path items, without .rb extensions

  • subdir (defaults to: nil)

    optional path to files relative to this file



30
31
32
33
34
35
36
37
38
# File 'lib/geotree/tools.rb', line 30

def req(fileListStr,subdir = nil)
  fileListStr.split(' ').each do |x|
    if subdir
      x = File.join(subdir,x)
    end
    x += '.rb'
    require_relative(x)
  end
end

#short_from_bytes(ba, offset = 0) ⇒ Object

Decode a short from an array of bytes (big-endian).

Parameters:

  • ba

    array of bytes

  • offset (defaults to: 0)

    offset of first (most significant) byte



658
659
660
# File 'lib/geotree/tools.rb', line 658

def short_from_bytes(ba, offset=0) 
  (ba[offset] << 8) | ba[offset + 1] 
end

#short_to_bytes(x) ⇒ Object



650
651
652
# File 'lib/geotree/tools.rb', line 650

def short_to_bytes(x) 
  [(x >> 8) & 0xff, x & 0xff]
end

#simple_str(s) ⇒ Object

Verify that a string is encoded as ASCII-8BIT



678
679
680
681
682
683
# File 'lib/geotree/tools.rb', line 678

def simple_str(s)
  if s.encoding.name != 'ASCII-8BIT' && s.encoding.name != 'UTF-8'
    pr("string [%s]\n encoding is %s,\n expected ASCII-8BIT\n",s,s.encoding.name)
    assert!(false)
  end
end

#str_sized(s, size, pad = "\0") ⇒ Object

Truncate or pad string so it has a particular size

Parameters:

  • s

    input string

  • size
  • pad (defaults to: "\0")

    padding character to use if string needs to grow

Returns:

  • modified string



692
693
694
# File 'lib/geotree/tools.rb', line 692

def str_sized(s, size, pad="\0")
  s[0...size].ljust(size,pad)
end

#str_to_bytes(str) ⇒ Object

Construct an array of bytes from a string

Parameters:

  • str

    string, or array of bytes (in which case it returns it unchanged)



634
635
636
637
# File 'lib/geotree/tools.rb', line 634

def str_to_bytes(str)
  return str if str.is_a? Array
  str.bytes
end

#to_ascii8(str) ⇒ Object

Transform string to 8-bit ASCII (i.e., just treat each byte as-is)



673
674
675
# File 'lib/geotree/tools.rb', line 673

def to_ascii8(str)
  str.force_encoding("ASCII-8BIT")
end

#to_hex(value, num_digits = 4) ⇒ Object

Construct hex representation of value

Parameters:

  • value

    integer value

  • num_digits (defaults to: 4)

    number of hex digits



289
290
291
292
# File 'lib/geotree/tools.rb', line 289

def to_hex(value, num_digits=4) 
  s = sprintf("%x", value)
  s.rjust(num_digits,'0')
end

#unimp(*args) ⇒ Object

Print an ‘unimplemented’ alert, one time only

Parameters:

  • args

    if present, calls printf() with these



256
257
258
# File 'lib/geotree/tools.rb', line 256

def unimp(*args)
  one_time_alert("unimplemented", 0, *args)
end

#unimp!(msg = nil) ⇒ Object

Abort with message about unimplemented code

Raises:

  • (Exception)


144
145
146
147
148
149
150
# File 'lib/geotree/tools.rb', line 144

def unimp!(msg = nil)
  msg2 = "Unimplemented code"
  if msg
    msg2 << ": " << msg
  end
  raise Exception, msg2
end

#warn(*args) ⇒ Object

Print a ‘warning’ alert, one time only

Parameters:

  • args

    if present, calls printf() with these



237
238
239
# File 'lib/geotree/tools.rb', line 237

def warn(*args) 
  one_time_alert("warning",0, *args)
end

#warndb(val = true) ⇒ Object

Convenience method for setting ‘db’ true within methods, and to print a one-time warning if so.

Parameters:

  • val (defaults to: true)

    value to set db to; it is convenient to disable debug printing quickly by adding a zero, e.g., ‘warndb 0’



246
247
248
249
250
251
252
# File 'lib/geotree/tools.rb', line 246

def warndb(val = true)
  if !val || val == 0
    return false
  end
  one_time_alert("warning",1,"Debug printing enabled")
  true
end

#windows?Boolean

Determine if running on the Windows operating system. Note: there is some debate about the best way to do this.

Returns:

  • (Boolean)


699
700
701
702
703
704
# File 'lib/geotree/tools.rb', line 699

def windows?
  if !defined? $__windows__
    $__windows__ = (RUBY_PLATFORM =~ /mswin/)
  end
  $__windows__
end

#write_text_file(path, contents) ⇒ Object

Write a string to a text file



262
263
264
# File 'lib/geotree/tools.rb', line 262

def write_text_file(path, contents)
    File.open(path, "wb") {|f| f.write(contents) }
end

#zero_bytes(count) ⇒ Object

Construct a string of zeros

Parameters:

  • count

    number of zeros



21
22
23
# File 'lib/geotree/tools.rb', line 21

def zero_bytes(count)
  ZERO_CHAR * count
end