class Object
Constants
- ZERO_CHAR
A string containing a single zero, with ASCII 8-bit encoding (i.e., plain old bytes)
Public Instance Methods
Append a particular number of spaces to a string
# File lib/tokn/tools.rb, line 77 def add_sp(s, indent = 0) s << ' ' * indent end
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. @param cond condition @param msg generates additional message using printf(), if these arguments exist
# File lib/tokn/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
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);
# File lib/tokn/tools.rb, line 281 def block yield end
Construct a string from an array of bytes @param byte_array array of bytes, or string (in which case it
returns it unchanged)
# File lib/tokn/tools.rb, line 624 def bytes_to_str(byte_array) return byte_array if byte_array.is_a? String byte_array.pack('C*') end
Redirect standard output to an internal string
# File lib/tokn/tools.rb, line 405 def capture_begin raise IllegalStateException if $IODest $IODest = StringIO.new $OldStdOut, $stdout = $stdout, $IODest end
Restore standard output; return captured text @return text that was redirected
# File lib/tokn/tools.rb, line 414 def capture_end raise IllegalStateException if !$IODest $stdout = $OldStdOut ret = $IODest.string $IODest = nil ret end
Convert an object to a human-readable string, or <nil>; should be considered a debug-only feature
# File lib/tokn/tools.rb, line 47 def d(arg) arg.nil? ? "<nil>" : arg.inspect end
Convert an object to a human-readable string, by calling a type-appropriate function: da, dh, or just d. @param arg object @param indent optional indentation for pretty printing; if result
spans multiple lines, each line should be indented by this amount
# File lib/tokn/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
Pretty-print an array, one element to a line @param indent indentation of each line, in spaces
# File lib/tokn/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
Generate debug description of a boolean value @param flag value to interpret as a boolean; prints 'T' iff not nil @param label optional label
# File lib/tokn/tools.rb, line 119 def df(flag, label=nil) s = '' if label s << label << ':' end s << (flag ? "T" : "F") s << ' ' s end
Pretty-print a hash, one element to a line @param indent indentation of each line, in spaces
# File lib/tokn/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
Get directory entries, excluding '.' and '..'
# File lib/tokn/tools.rb, line 641 def dir_entries(path) ents = Dir.entries(path) ents.reject!{|entry| entry == '.' || entry == '..'} end
Convert a .dot file (string) to a PDF file “__mygraph__nnn.pdf”
It does this by making a system call to the 'dot' utility.
# File lib/tokn/tokn_const.rb, line 40 def dotToPDF(dotFile, name = "", test_dir = nil) gr = dotFile raise ArgumentError if !test_dir dotPath = File.join(test_dir,".__mygraph__.dot") write_text_file(dotPath,gr) destName = File.join(test_dir,"__mygraph__"+name+".pdf") system("dot -Tpdf "+dotPath+" -o "+destName) end
Convert an object to a human-readable string, prefixed with its type
# File lib/tokn/tools.rb, line 66 def dt(arg) if arg.nil? return "<nil>" end s = arg.class.to_s s << ':' s << arg.inspect s end
Calculate time elapsed, in seconds, from last call to this function; if it's never been called, returns zero
# File lib/tokn/tools.rb, line 377 def elapsed curr = Time.now.to_f elap = 0 if $prevTime elap = curr - $prevTime end $prevTime = curr elap end
Get a nice, concise description of the file and line of some caller within the stack.
@param nSkip the number of items deep in the call stack to look
# File lib/tokn/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 a string or byte array @param byte_array_or_string @param title @param offset offset to first value within array @param length number of values to dump @param bytes_per_row @param with_text if true, displays ASCII values to right of hex dump
# File lib/tokn/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 a string or byte array to a string; see hex_dump
for parameter descriptions
# File lib/tokn/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
Decode an int from an array of bytes (big-endian). @param ba array of bytes @param offset offset of first (most significant) byte
# File lib/tokn/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
# File lib/tokn/tools.rb, line 646 def int_to_bytes(x) [(x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff] end
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).
@param file pass __FILE__ in here @return true if so
# File lib/tokn/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
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
@param str if not nil, string to compare; if nil, calls capture_end
to get string
# File lib/tokn/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
Print a message if it hasn't yet been printed, which includes the caller's location
@param typeString e.g., “warning”, “unimplemented” @param nSkip the number of levels deep that the caller is in the stack @param args if present, calls sprintf(…) with these to append to the message
# File lib/tokn/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
Mark all constants ending with '_' as private constants
@param entity the class to examine @param add_non_suffix_versions if true, for each constant ABC_ found, also
defines a constant ABC with the same value that is also private
# File lib/tokn/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 a file's contents, return as a string
# File lib/tokn/tools.rb, line 268 def read_text_file(path) contents = nil File.open(path,"rb") {|f| contents = f.read } contents end
Delete a file or directory, if it exists. Caution! If directory, deletes all files and subdirectories.
# File lib/tokn/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
Convenience method to perform 'require_relative' on a set of files
@param fileListStr space-delimited file/path items, without .rb extensions @param subdir optional path to files relative to this file
# File lib/tokn/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
Decode a short from an array of bytes (big-endian). @param ba array of bytes @param offset offset of first (most significant) byte
# File lib/tokn/tools.rb, line 658 def short_from_bytes(ba, offset=0) (ba[offset] << 8) | ba[offset + 1] end
# File lib/tokn/tools.rb, line 650 def short_to_bytes(x) [(x >> 8) & 0xff, x & 0xff] end
Verify that a string is encoded as ASCII-8BIT
# File lib/tokn/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
Truncate or pad string so it has a particular size
@param s input string @param size @param pad padding character to use if string needs to grow @return modified string
# File lib/tokn/tools.rb, line 692 def str_sized(s, size, pad="\0") s[0...size].ljust(size,pad) end
Construct an array of bytes from a string
@param str string, or array of bytes (in which case it
returns it unchanged)
# File lib/tokn/tools.rb, line 634 def str_to_bytes(str) return str if str.is_a? Array str.bytes end
Transform string to 8-bit ASCII (i.e., just treat each byte as-is)
# File lib/tokn/tools.rb, line 673 def to_ascii8(str) str.force_encoding("ASCII-8BIT") end
Construct hex representation of value @param value integer value @param num_digits number of hex digits
# File lib/tokn/tools.rb, line 289 def to_hex(value, num_digits=4) s = sprintf("%x", value) s.rjust(num_digits,'0') end
Print an 'unimplemented' alert, one time only @param args if present, calls printf() with these
# File lib/tokn/tools.rb, line 256 def unimp(*args) one_time_alert("unimplemented", 0, *args) end
Abort with message about unimplemented code
# File lib/tokn/tools.rb, line 144 def unimp!(msg = nil) msg2 = "Unimplemented code" if msg msg2 << ": " << msg end raise Exception, msg2 end
Print a 'warning' alert, one time only @param args if present, calls printf() with these
# File lib/tokn/tools.rb, line 237 def warn(*args) one_time_alert("warning",0, *args) end
Convenience method for setting 'db' true within methods, and to print a one-time warning if so. @param val value to set db to; it is convenient to disable
debug printing quickly by adding a zero, e.g., 'warndb 0'
# File lib/tokn/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
Determine if running on the Windows operating system. Note: there is some debate about the best way to do this.
# File lib/tokn/tools.rb, line 699 def windows? if !defined? $__windows__ $__windows__ = (RUBY_PLATFORM =~ /mswin/) end $__windows__ end
Write a string to a text file
# File lib/tokn/tools.rb, line 262 def write_text_file(path, contents) File.open(path, "wb") {|f| f.write(contents) } end
Construct a string of zeros @param count number of zeros
# File lib/tokn/tools.rb, line 21 def zero_bytes(count) ZERO_CHAR * count end