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 +4 -4
- data/CHANGELOG.txt +3 -1
- data/lib/lenc/_OLD_tools.rb +628 -0
- data/lib/lenc/tools.rb +292 -176
- metadata +3 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: b4b56f1960462bf9fd9382ff2059182a881e51dd
         | 
| 4 | 
            +
              data.tar.gz: 0cb704fcc7bda5e7a326202be609027d1cb7edfa
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 41948757b4d963f3b7cc23f6c8292e8ce51b918b2f1860087cb34c3d95765f6b82900fee46c47adb399b01cb6050a09761c6d56caf3c921badbb542263f45a1a
         | 
| 7 | 
            +
              data.tar.gz: da5a5af9488d3901d29b71c29556df4c343f0dfa82c882556dad404ef24148e593e046d022604abef1cb2b4859c832fa49b41695d1d20fbb4e49bcf7fb3ad038
         | 
    
        data/CHANGELOG.txt
    CHANGED
    
    
| @@ -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
         | 
    
        data/lib/lenc/tools.rb
    CHANGED
    
    | @@ -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 | 
            -
            # | 
| 24 | 
            -
             | 
| 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 | 
            -
             | 
| 132 | 
            -
            # | 
| 133 | 
            -
             | 
| 134 | 
            -
            # | 
| 135 | 
            -
             
         | 
| 136 | 
            -
             | 
| 137 | 
            -
             | 
| 138 | 
            -
             | 
| 139 | 
            -
             | 
| 140 | 
            -
             | 
| 141 | 
            -
             | 
| 142 | 
            -
             | 
| 143 | 
            -
             | 
| 144 | 
            -
             | 
| 145 | 
            -
             | 
| 146 | 
            -
             | 
| 147 | 
            -
             | 
| 148 | 
            -
             | 
| 149 | 
            -
             | 
| 150 | 
            -
             | 
| 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 | 
            -
            #  | 
| 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 | 
            -
             | 
| 398 | 
            +
            require 'stringio'
         | 
| 399 | 
            +
             | 
| 400 | 
            +
            $IODest = nil
         | 
| 401 | 
            +
            $OldStdOut = nil
         | 
| 402 | 
            +
             | 
| 403 | 
            +
            # Redirect standard output to an internal string
         | 
| 460 404 | 
             
            #
         | 
| 461 | 
            -
            def  | 
| 462 | 
            -
             | 
| 405 | 
            +
            def capture_begin
         | 
| 406 | 
            +
                raise IllegalStateException if $IODest
         | 
| 407 | 
            +
                $IODest = StringIO.new
         | 
| 408 | 
            +
                $OldStdOut, $stdout = $stdout, $IODest
         | 
| 463 409 | 
             
            end
         | 
| 464 410 |  | 
| 465 | 
            -
            #  | 
| 466 | 
            -
             | 
| 467 | 
            -
             | 
| 468 | 
            -
             | 
| 469 | 
            -
             | 
| 470 | 
            -
               | 
| 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 | 
            -
            #  | 
| 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  | 
| 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  | 
| 481 | 
            -
              s[0...size].ljust(size,pad)
         | 
| 482 | 
            -
            end
         | 
| 427 | 
            +
            def match_expected_output(str = nil)
         | 
| 483 428 |  | 
| 484 | 
            -
             | 
| 485 | 
            -
             | 
| 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 | 
            -
             | 
| 495 | 
            -
             | 
| 496 | 
            -
             | 
| 497 | 
            -
             | 
| 498 | 
            -
             | 
| 499 | 
            -
             | 
| 500 | 
            -
             | 
| 501 | 
            -
             | 
| 502 | 
            -
             | 
| 503 | 
            -
             | 
| 504 | 
            -
             | 
| 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. | 
| 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- | 
| 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
         |