bindata 1.6.0 → 1.8.0
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.
Potentially problematic release.
This version of bindata might be problematic. Click here for more details.
- data/.gitignore +1 -0
- data/.travis.yml +5 -0
- data/ChangeLog.rdoc +9 -0
- data/README.md +8 -1
- data/doc/manual.md +27 -2
- data/examples/tcp_ip.rb +179 -0
- data/lib/bindata.rb +4 -2
- data/lib/bindata/base.rb +2 -2
- data/lib/bindata/buffer.rb +119 -0
- data/lib/bindata/dsl.rb +41 -0
- data/lib/bindata/io.rb +321 -190
- data/lib/bindata/primitive.rb +6 -0
- data/lib/bindata/record.rb +1 -37
- data/lib/bindata/registry.rb +27 -23
- data/lib/bindata/version.rb +1 -1
- data/lib/bindata/virtual.rb +25 -19
- data/test/buffer_test.rb +144 -0
- data/test/common.rb +1 -1
- data/test/io_test.rb +117 -51
- data/test/lazy_test.rb +12 -12
- data/test/primitive_test.rb +20 -0
- data/test/registry_test.rb +23 -0
- data/test/virtual_test.rb +38 -0
- metadata +9 -4
    
        data/lib/bindata/primitive.rb
    CHANGED
    
    
    
        data/lib/bindata/record.rb
    CHANGED
    
    | @@ -3,42 +3,6 @@ require 'bindata/sanitize' | |
| 3 3 | 
             
            require 'bindata/struct'
         | 
| 4 4 |  | 
| 5 5 | 
             
            module BinData
         | 
| 6 | 
            -
              # Extracts args for Records.
         | 
| 7 | 
            -
              #
         | 
| 8 | 
            -
              # Foo.new(:bar => "baz) is ambiguous as to whether :bar is a value or parameter.
         | 
| 9 | 
            -
              #
         | 
| 10 | 
            -
              # BaseArgExtractor always assumes :bar is parameter.  This extractor correctly
         | 
| 11 | 
            -
              # identifies it as value or parameter.
         | 
| 12 | 
            -
              class RecordArgExtractor
         | 
| 13 | 
            -
                class << self
         | 
| 14 | 
            -
                  def extract(the_class, the_args)
         | 
| 15 | 
            -
                    value, parameters, parent = BaseArgExtractor.extract(the_class, the_args)
         | 
| 16 | 
            -
             | 
| 17 | 
            -
                    if parameters_is_value?(the_class, value, parameters)
         | 
| 18 | 
            -
                      value = parameters
         | 
| 19 | 
            -
                      parameters = {}
         | 
| 20 | 
            -
                    end
         | 
| 21 | 
            -
             | 
| 22 | 
            -
                    [value, parameters, parent]
         | 
| 23 | 
            -
                  end
         | 
| 24 | 
            -
             | 
| 25 | 
            -
                  def parameters_is_value?(the_class, value, parameters)
         | 
| 26 | 
            -
                    if value.nil? and parameters.length > 0
         | 
| 27 | 
            -
                      field_names_in_parameters?(the_class, parameters)
         | 
| 28 | 
            -
                    else
         | 
| 29 | 
            -
                      false
         | 
| 30 | 
            -
                    end
         | 
| 31 | 
            -
                  end
         | 
| 32 | 
            -
             | 
| 33 | 
            -
                  def field_names_in_parameters?(the_class, parameters)
         | 
| 34 | 
            -
                    field_names = the_class.fields.field_names
         | 
| 35 | 
            -
                    param_keys = parameters.keys
         | 
| 36 | 
            -
             | 
| 37 | 
            -
                    (field_names & param_keys).length > 0
         | 
| 38 | 
            -
                  end
         | 
| 39 | 
            -
                end
         | 
| 40 | 
            -
              end
         | 
| 41 | 
            -
             | 
| 42 6 | 
             
              # A Record is a declarative wrapper around Struct.
         | 
| 43 7 | 
             
              #
         | 
| 44 8 | 
             
              #    require 'bindata'
         | 
| @@ -68,7 +32,7 @@ module BinData | |
| 68 32 | 
             
                class << self
         | 
| 69 33 |  | 
| 70 34 | 
             
                  def arg_extractor
         | 
| 71 | 
            -
                     | 
| 35 | 
            +
                    MultiFieldArgExtractor
         | 
| 72 36 | 
             
                  end
         | 
| 73 37 |  | 
| 74 38 | 
             
                  def sanitize_parameters!(params) #:nodoc:
         | 
    
        data/lib/bindata/registry.rb
    CHANGED
    
    | @@ -18,28 +18,29 @@ module BinData | |
| 18 18 | 
             
                def register(name, class_to_register)
         | 
| 19 19 | 
             
                  return if class_to_register.nil?
         | 
| 20 20 |  | 
| 21 | 
            -
                  formatted_name =  | 
| 21 | 
            +
                  formatted_name = underscore_name(name)
         | 
| 22 22 | 
             
                  warn_if_name_is_already_registered(formatted_name, class_to_register)
         | 
| 23 23 |  | 
| 24 24 | 
             
                  @registry[formatted_name] = class_to_register
         | 
| 25 25 | 
             
                end
         | 
| 26 26 |  | 
| 27 27 | 
             
                def unregister(name)
         | 
| 28 | 
            -
                   | 
| 29 | 
            -
                  @registry.delete(formatted_name)
         | 
| 28 | 
            +
                  @registry.delete(underscore_name(name))
         | 
| 30 29 | 
             
                end
         | 
| 31 30 |  | 
| 32 31 | 
             
                def lookup(name, endian = nil)
         | 
| 33 | 
            -
                  key =  | 
| 34 | 
            -
                  try_registering_key(key) unless @registry.has_key?(key)
         | 
| 35 | 
            -
             | 
| 32 | 
            +
                  key = normalize_name(name, endian)
         | 
| 36 33 | 
             
                  @registry[key] || raise(UnRegisteredTypeError, name.to_s)
         | 
| 37 34 | 
             
                end
         | 
| 38 35 |  | 
| 39 36 | 
             
                def normalize_name(name, endian = nil)
         | 
| 40 | 
            -
                   | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 37 | 
            +
                  name = underscore_name(name)
         | 
| 38 | 
            +
                  return name if is_registered?(name)
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  name = name_with_endian(name, endian)
         | 
| 41 | 
            +
                  return name if is_registered?(name)
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  name
         | 
| 43 44 | 
             
                end
         | 
| 44 45 |  | 
| 45 46 | 
             
                # Convert CamelCase +name+ to underscore style.
         | 
| @@ -54,25 +55,28 @@ module BinData | |
| 54 55 | 
             
                #---------------
         | 
| 55 56 | 
             
                private
         | 
| 56 57 |  | 
| 57 | 
            -
                def  | 
| 58 | 
            -
                  name  | 
| 58 | 
            +
                def name_with_endian(name, endian)
         | 
| 59 | 
            +
                  return name if endian.nil?
         | 
| 59 60 |  | 
| 60 | 
            -
                   | 
| 61 | 
            -
                  if  | 
| 62 | 
            -
                     | 
| 63 | 
            -
             | 
| 64 | 
            -
                     | 
| 65 | 
            -
                      result = name + ((endian == :little) ? "_le" : "_be")
         | 
| 66 | 
            -
                    end
         | 
| 61 | 
            +
                  suffix = (endian == :little) ? "le" : "be"
         | 
| 62 | 
            +
                  if /^u?int\d+$/ =~ name
         | 
| 63 | 
            +
                    name + suffix
         | 
| 64 | 
            +
                  else
         | 
| 65 | 
            +
                    name + "_" + suffix
         | 
| 67 66 | 
             
                  end
         | 
| 68 | 
            -
                  result
         | 
| 69 67 | 
             
                end
         | 
| 70 68 |  | 
| 71 | 
            -
                def  | 
| 72 | 
            -
                   | 
| 73 | 
            -
             | 
| 69 | 
            +
                def is_registered?(name)
         | 
| 70 | 
            +
                  register_dynamic_class(name) unless @registry.has_key?(name)
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                  @registry.has_key?(name)
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                def register_dynamic_class(name)
         | 
| 76 | 
            +
                  if /^u?int\d+(le|be)$/ =~ name or /^bit\d+(le)?$/ =~ name
         | 
| 77 | 
            +
                    class_name = name.gsub(/(?:^|_)(.)/) { $1.upcase }
         | 
| 74 78 | 
             
                    begin
         | 
| 75 | 
            -
                       | 
| 79 | 
            +
                      BinData::const_get(class_name)
         | 
| 76 80 | 
             
                    rescue NameError
         | 
| 77 81 | 
             
                    end
         | 
| 78 82 | 
             
                  end
         | 
    
        data/lib/bindata/version.rb
    CHANGED
    
    
    
        data/lib/bindata/virtual.rb
    CHANGED
    
    | @@ -1,4 +1,4 @@ | |
| 1 | 
            -
            require "bindata/ | 
| 1 | 
            +
            require "bindata/base"
         | 
| 2 2 |  | 
| 3 3 | 
             
            module BinData
         | 
| 4 4 | 
             
              # A virtual field is one that is neither read, written nor occupies space.
         | 
| @@ -17,31 +17,37 @@ module BinData | |
| 17 17 | 
             
              #   obj.a #=> "abcde"
         | 
| 18 18 | 
             
              #   obj.c.offset #=> 10
         | 
| 19 19 | 
             
              #
         | 
| 20 | 
            -
               | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 20 | 
            +
              #   obj = A.read("abcdeABCDE") #=> BinData::ValidityError: assertion failed for obj.c
         | 
| 21 | 
            +
              #
         | 
| 22 | 
            +
              # == Parameters
         | 
| 23 | 
            +
              #
         | 
| 24 | 
            +
              # Parameters may be provided at initialisation to control the behaviour of
         | 
| 25 | 
            +
              # an object.  These params include those for BinData::Base as well as:
         | 
| 26 | 
            +
              #
         | 
| 27 | 
            +
              # [<tt>:assert</tt>]    Raise an error when reading or assigning if the value
         | 
| 28 | 
            +
              #                       of this evaluated parameter is false.
         | 
| 29 | 
            +
              #
         | 
| 30 | 
            +
              class Virtual < BinData::Base
         | 
| 23 31 |  | 
| 24 | 
            -
                 | 
| 25 | 
            -
                  def sanitize_parameters!(params) #:nodoc:
         | 
| 26 | 
            -
                    if params.has_parameter?(:asserted_value)
         | 
| 27 | 
            -
                      fail ":asserted_value can not be used on virtual field"
         | 
| 28 | 
            -
                    end
         | 
| 29 | 
            -
                  end
         | 
| 30 | 
            -
                end
         | 
| 32 | 
            +
                optional_parameter :assert
         | 
| 31 33 |  | 
| 32 | 
            -
                 | 
| 33 | 
            -
                 | 
| 34 | 
            +
                def clear?; true; end
         | 
| 35 | 
            +
                def snapshot; nil; end
         | 
| 36 | 
            +
                def do_num_bytes; 0; end
         | 
| 37 | 
            +
                def do_write(io); end
         | 
| 34 38 |  | 
| 35 | 
            -
                def  | 
| 36 | 
            -
                   | 
| 39 | 
            +
                def assign(val)
         | 
| 40 | 
            +
                  assert!
         | 
| 37 41 | 
             
                end
         | 
| 38 42 |  | 
| 39 | 
            -
                def  | 
| 40 | 
            -
                   | 
| 43 | 
            +
                def do_read(io)
         | 
| 44 | 
            +
                  assert!
         | 
| 41 45 | 
             
                end
         | 
| 42 46 |  | 
| 43 | 
            -
                def  | 
| 44 | 
            -
                   | 
| 47 | 
            +
                def assert!
         | 
| 48 | 
            +
                  if has_parameter?(:assert) and not eval_parameter(:assert)
         | 
| 49 | 
            +
                    raise ValidityError, "assertion failed for #{debug_name}"
         | 
| 50 | 
            +
                  end
         | 
| 45 51 | 
             
                end
         | 
| 46 52 | 
             
              end
         | 
| 47 53 | 
             
            end
         | 
    
        data/test/buffer_test.rb
    ADDED
    
    | @@ -0,0 +1,144 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require File.expand_path(File.join(File.dirname(__FILE__), "common"))
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            describe BinData::Buffer, "when instantiating" do
         | 
| 6 | 
            +
              describe "with no mandatory parameters supplied" do
         | 
| 7 | 
            +
                it "raises an error" do
         | 
| 8 | 
            +
                  args = {}
         | 
| 9 | 
            +
                  lambda { BinData::Buffer.new(args) }.must_raise ArgumentError
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              describe "with some but not all mandatory parameters supplied" do
         | 
| 14 | 
            +
                it "raises an error" do
         | 
| 15 | 
            +
                  args = {:length => 3}
         | 
| 16 | 
            +
                  lambda { BinData::Buffer.new(args) }.must_raise ArgumentError
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              it "fails if a given type is unknown" do
         | 
| 21 | 
            +
                args = {:type => :does_not_exist, :length => 3}
         | 
| 22 | 
            +
                lambda { BinData::Buffer.new(args) }.must_raise BinData::UnRegisteredTypeError
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
              it "accepts BinData::Base as :type" do
         | 
| 26 | 
            +
                obj = BinData::Int8.new(:initial_value => 5)
         | 
| 27 | 
            +
                array = BinData::Buffer.new(:type => obj, :length => 3)
         | 
| 28 | 
            +
                array.must_equal 5
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
            end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            describe BinData::Buffer, "subclassed with a single type" do
         | 
| 33 | 
            +
              class IntBuffer < BinData::Buffer
         | 
| 34 | 
            +
                endian :big
         | 
| 35 | 
            +
                default_parameter :length => 5
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                uint16
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
              it "behaves as type" do
         | 
| 41 | 
            +
                obj = IntBuffer.new(3)
         | 
| 42 | 
            +
                obj.must_equal 3
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              it "reads data" do
         | 
| 46 | 
            +
                obj = IntBuffer.read "\001\002\003\004\005"
         | 
| 47 | 
            +
                obj.must_equal 0x0102
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
              it "writes data" do
         | 
| 51 | 
            +
                obj = IntBuffer.new(3)
         | 
| 52 | 
            +
                obj.to_binary_s.must_equal "\000\003\000\000\000"
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              it "has total num_bytes" do
         | 
| 56 | 
            +
                obj = IntBuffer.new
         | 
| 57 | 
            +
                obj.num_bytes.must_equal 5
         | 
| 58 | 
            +
              end
         | 
| 59 | 
            +
            end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
            describe BinData::Buffer, "subclassed with multiple types" do
         | 
| 62 | 
            +
              class TupleBuffer < BinData::Buffer
         | 
| 63 | 
            +
                endian :big
         | 
| 64 | 
            +
                default_parameter :length => 5
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                uint16 :a
         | 
| 67 | 
            +
                uint16 :b
         | 
| 68 | 
            +
              end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
              it "behaves as type" do
         | 
| 71 | 
            +
                obj = TupleBuffer.new(:a => 1, :b => 2)
         | 
| 72 | 
            +
                obj.a.must_equal 1
         | 
| 73 | 
            +
                obj.b.must_equal 2
         | 
| 74 | 
            +
              end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
              it "has total num_bytes" do
         | 
| 77 | 
            +
                obj = TupleBuffer.new
         | 
| 78 | 
            +
                obj.num_bytes.must_equal 5
         | 
| 79 | 
            +
              end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
              it "reads data" do
         | 
| 82 | 
            +
                obj = TupleBuffer.read "\001\002\003\004\005"
         | 
| 83 | 
            +
                obj.a.must_equal 0x0102
         | 
| 84 | 
            +
                obj.b.must_equal 0x0304
         | 
| 85 | 
            +
              end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
              it "writes data" do
         | 
| 88 | 
            +
                obj = TupleBuffer.new(:a => 1, :b => 2)
         | 
| 89 | 
            +
                obj.to_binary_s.must_equal "\000\001\000\002\000"
         | 
| 90 | 
            +
              end
         | 
| 91 | 
            +
            end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
            describe BinData::Buffer, "inside a Record" do
         | 
| 94 | 
            +
              class BufferRecord < BinData::Record
         | 
| 95 | 
            +
                endian :little
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                uint16 :buffer_length, :value => lambda { 2 * list.length + 1 }
         | 
| 98 | 
            +
                buffer :list, :length => :buffer_length do
         | 
| 99 | 
            +
                  array :type => :int16, :read_until => :eof
         | 
| 100 | 
            +
                end
         | 
| 101 | 
            +
                string :footer, :length => 2, :asserted_value => "ZZ"
         | 
| 102 | 
            +
              end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
              it "reads" do
         | 
| 105 | 
            +
                obj = BufferRecord.read "\007\000\004\000\005\000\006\000\000ZZ"
         | 
| 106 | 
            +
                obj.list.must_equal [4, 5, 6]
         | 
| 107 | 
            +
              end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
              it "writes" do
         | 
| 110 | 
            +
                obj = BufferRecord.new(:list => [1, 2, 3, 4, 5])
         | 
| 111 | 
            +
                obj.to_binary_s.must_equal "\013\000\001\000\002\000\003\000\004\000\005\000\000ZZ"
         | 
| 112 | 
            +
              end
         | 
| 113 | 
            +
            end
         | 
| 114 | 
            +
             | 
| 115 | 
            +
            describe BinData::Buffer, "nested buffers" do
         | 
| 116 | 
            +
              class NestedBufferRecord < BinData::Record
         | 
| 117 | 
            +
                buffer :a, :length => 10 do
         | 
| 118 | 
            +
                  buffer :aa, :length => 5 do
         | 
| 119 | 
            +
                    string :read_length => 5
         | 
| 120 | 
            +
                  end
         | 
| 121 | 
            +
                  buffer :bb, :length => 20 do
         | 
| 122 | 
            +
                    string :read_length => 5
         | 
| 123 | 
            +
                  end
         | 
| 124 | 
            +
                end
         | 
| 125 | 
            +
                string :b, :read_length => 5
         | 
| 126 | 
            +
              end
         | 
| 127 | 
            +
             | 
| 128 | 
            +
              it "restricts large nested buffer" do
         | 
| 129 | 
            +
                obj = NestedBufferRecord.read "abcdefghijklmnopqrst"
         | 
| 130 | 
            +
                obj.a.aa.must_equal "abcde"
         | 
| 131 | 
            +
                obj.a.bb.must_equal "fghij"
         | 
| 132 | 
            +
                obj.b.must_equal "klmno"
         | 
| 133 | 
            +
              end
         | 
| 134 | 
            +
             | 
| 135 | 
            +
              it "restricts oversize writes" do
         | 
| 136 | 
            +
                obj = NestedBufferRecord.new
         | 
| 137 | 
            +
                obj.a.aa = "abcdefghij"
         | 
| 138 | 
            +
                obj.a.bb = "ABCDEFGHIJ"
         | 
| 139 | 
            +
                obj.b = "12345"
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                obj.to_binary_s.must_equal "abcdeABCDE12345"
         | 
| 142 | 
            +
              end
         | 
| 143 | 
            +
            end
         | 
| 144 | 
            +
             | 
    
        data/test/common.rb
    CHANGED
    
    
    
        data/test/io_test.rb
    CHANGED
    
    | @@ -2,63 +2,43 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            require File.expand_path(File.join(File.dirname(__FILE__), "common"))
         | 
| 4 4 |  | 
| 5 | 
            -
            describe BinData::IO, "reading from non seekable stream" do
         | 
| 5 | 
            +
            describe BinData::IO::Read, "reading from non seekable stream" do
         | 
| 6 6 | 
             
              before do
         | 
| 7 7 | 
             
                @rd, @wr = IO::pipe
         | 
| 8 | 
            -
                 | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
                else
         | 
| 13 | 
            -
                  # child
         | 
| 14 | 
            -
                  begin
         | 
| 15 | 
            -
                    @rd.close
         | 
| 16 | 
            -
                    @wr.write "a" * 5000
         | 
| 17 | 
            -
                    @wr.write "b" * 5000
         | 
| 18 | 
            -
                    @wr.close
         | 
| 19 | 
            -
                  rescue Exception
         | 
| 20 | 
            -
                    # ignore it
         | 
| 21 | 
            -
                  ensure
         | 
| 22 | 
            -
                    exit!
         | 
| 23 | 
            -
                  end
         | 
| 24 | 
            -
                end
         | 
| 8 | 
            +
                @io = BinData::IO::Read.new(@rd)
         | 
| 9 | 
            +
                @wr.write "a" * 2000
         | 
| 10 | 
            +
                @wr.write "b" * 2000
         | 
| 11 | 
            +
                @wr.close
         | 
| 25 12 | 
             
              end
         | 
| 26 13 |  | 
| 27 14 | 
             
              after do
         | 
| 28 15 | 
             
                @rd.close
         | 
| 29 | 
            -
                Process.wait
         | 
| 30 16 | 
             
              end
         | 
| 31 17 |  | 
| 32 | 
            -
              it " | 
| 18 | 
            +
              it "has correct offset" do
         | 
| 33 19 | 
             
                @io.readbytes(10)
         | 
| 34 | 
            -
                @io.offset.must_equal  | 
| 20 | 
            +
                @io.offset.must_equal 10
         | 
| 35 21 | 
             
              end
         | 
| 36 22 |  | 
| 37 23 | 
             
              it "seeks correctly" do
         | 
| 38 | 
            -
                @io.seekbytes( | 
| 24 | 
            +
                @io.seekbytes(1999)
         | 
| 39 25 | 
             
                @io.readbytes(5).must_equal "abbbb"
         | 
| 40 26 | 
             
              end
         | 
| 41 27 |  | 
| 42 | 
            -
              it " | 
| 43 | 
            -
                 | 
| 28 | 
            +
              it "#num_bytes_remaining raises IOError" do
         | 
| 29 | 
            +
                lambda {
         | 
| 30 | 
            +
                  @io.num_bytes_remaining
         | 
| 31 | 
            +
                }.must_raise IOError
         | 
| 44 32 | 
             
              end
         | 
| 45 33 | 
             
            end
         | 
| 46 34 |  | 
| 47 | 
            -
            describe BinData::IO, "when reading" do
         | 
| 35 | 
            +
            describe BinData::IO::Read, "when reading" do
         | 
| 48 36 | 
             
              let(:stream) { StringIO.new "abcdefghij" }
         | 
| 49 | 
            -
              let(:io) { BinData::IO.new(stream) }
         | 
| 50 | 
            -
             | 
| 51 | 
            -
              it "wraps strings in StringIO" do
         | 
| 52 | 
            -
                io.raw_io.class.must_equal StringIO
         | 
| 53 | 
            -
              end
         | 
| 37 | 
            +
              let(:io) { BinData::IO::Read.new(stream) }
         | 
| 54 38 |  | 
| 55 | 
            -
              it " | 
| 56 | 
            -
                io.raw_io.must_equal stream
         | 
| 57 | 
            -
              end
         | 
| 58 | 
            -
             | 
| 59 | 
            -
              it "raises error when io is BinData::IO" do
         | 
| 39 | 
            +
              it "raises error when io is BinData::IO::Read" do
         | 
| 60 40 | 
             
                lambda {
         | 
| 61 | 
            -
                  BinData::IO.new(BinData::IO.new(""))
         | 
| 41 | 
            +
                  BinData::IO::Read.new(BinData::IO::Read.new(""))
         | 
| 62 42 | 
             
                }.must_raise ArgumentError
         | 
| 63 43 | 
             
              end
         | 
| 64 44 |  | 
| @@ -100,17 +80,69 @@ describe BinData::IO, "when reading" do | |
| 100 80 | 
             
              end
         | 
| 101 81 | 
             
            end
         | 
| 102 82 |  | 
| 103 | 
            -
            describe BinData::IO, " | 
| 104 | 
            -
              let(:stream) { StringIO.new }
         | 
| 105 | 
            -
              let(:io) { BinData::IO.new(stream) }
         | 
| 83 | 
            +
            describe BinData::IO::Read, "#with_buffer" do
         | 
| 84 | 
            +
              let(:stream) { StringIO.new "abcdefghijklmnopqrst" }
         | 
| 85 | 
            +
              let(:io) { BinData::IO::Read.new(stream) }
         | 
| 86 | 
            +
             | 
| 87 | 
            +
              it "consumes entire buffer on short reads" do
         | 
| 88 | 
            +
                io.with_buffer(10) do
         | 
| 89 | 
            +
                  io.readbytes(4).must_equal "abcd"
         | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
                io.offset.must_equal(10)
         | 
| 92 | 
            +
              end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
              it "consumes entire buffer on read_all_bytes" do
         | 
| 95 | 
            +
                io.with_buffer(10) do
         | 
| 96 | 
            +
                  io.read_all_bytes.must_equal "abcdefghij"
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
                io.offset.must_equal(10)
         | 
| 99 | 
            +
              end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
              it "restricts large reads" do
         | 
| 102 | 
            +
                io.with_buffer(10) do
         | 
| 103 | 
            +
                  lambda {
         | 
| 104 | 
            +
                    io.readbytes(15)
         | 
| 105 | 
            +
                  }.must_raise IOError
         | 
| 106 | 
            +
                end
         | 
| 107 | 
            +
              end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
              it "is nestable" do
         | 
| 110 | 
            +
                io.with_buffer(10) do
         | 
| 111 | 
            +
                  io.readbytes(2).must_equal "ab"
         | 
| 112 | 
            +
                  io.with_buffer(5) do
         | 
| 113 | 
            +
                    io.read_all_bytes.must_equal "cdefg"
         | 
| 114 | 
            +
                  end
         | 
| 115 | 
            +
                  io.offset.must_equal(2 + 5)
         | 
| 116 | 
            +
                end
         | 
| 117 | 
            +
                io.offset.must_equal(10)
         | 
| 118 | 
            +
              end
         | 
| 119 | 
            +
             | 
| 120 | 
            +
              it "restricts large nested buffers" do
         | 
| 121 | 
            +
                io.with_buffer(10) do
         | 
| 122 | 
            +
                  io.readbytes(2).must_equal "ab"
         | 
| 123 | 
            +
                  io.with_buffer(20) do
         | 
| 124 | 
            +
                    io.read_all_bytes.must_equal "cdefghij"
         | 
| 125 | 
            +
                    io.offset.must_equal(10)
         | 
| 126 | 
            +
                  end
         | 
| 127 | 
            +
                end
         | 
| 128 | 
            +
                io.offset.must_equal(10)
         | 
| 129 | 
            +
              end
         | 
| 106 130 |  | 
| 107 | 
            -
              it " | 
| 108 | 
            -
                io. | 
| 131 | 
            +
              it "restricts large seeks" do
         | 
| 132 | 
            +
                io.with_buffer(10) do
         | 
| 133 | 
            +
                  io.seekbytes(15)
         | 
| 134 | 
            +
                end
         | 
| 135 | 
            +
                io.offset.must_equal(10)
         | 
| 109 136 | 
             
              end
         | 
| 137 | 
            +
            end
         | 
| 138 | 
            +
             | 
| 139 | 
            +
            describe BinData::IO::Write, "when writing" do
         | 
| 140 | 
            +
              let(:stream) { StringIO.new }
         | 
| 141 | 
            +
              let(:io) { BinData::IO::Write.new(stream) }
         | 
| 110 142 |  | 
| 111 143 | 
             
              it "raises error when io is BinData::IO" do
         | 
| 112 144 | 
             
                lambda {
         | 
| 113 | 
            -
                  BinData::IO.new(BinData::IO.new(""))
         | 
| 145 | 
            +
                  BinData::IO::Write.new(BinData::IO::Write.new(""))
         | 
| 114 146 | 
             
                }.must_raise ArgumentError
         | 
| 115 147 | 
             
              end
         | 
| 116 148 |  | 
| @@ -128,11 +160,43 @@ describe BinData::IO, "when writing" do | |
| 128 160 | 
             
              end
         | 
| 129 161 | 
             
            end
         | 
| 130 162 |  | 
| 131 | 
            -
            describe BinData::IO, " | 
| 163 | 
            +
            describe BinData::IO::Write, "#with_buffer" do
         | 
| 164 | 
            +
              let(:stream) { StringIO.new }
         | 
| 165 | 
            +
              let(:io) { BinData::IO::Write.new(stream) }
         | 
| 166 | 
            +
             | 
| 167 | 
            +
              it "pads entire buffer on short reads" do
         | 
| 168 | 
            +
                io.with_buffer(10) do
         | 
| 169 | 
            +
                  io.writebytes "abcde"
         | 
| 170 | 
            +
                end
         | 
| 171 | 
            +
             | 
| 172 | 
            +
                stream.value.must_equal "abcde\0\0\0\0\0"
         | 
| 173 | 
            +
              end
         | 
| 174 | 
            +
             | 
| 175 | 
            +
              it "discards excess on large writes" do
         | 
| 176 | 
            +
                io.with_buffer(5) do
         | 
| 177 | 
            +
                  io.writebytes "abcdefghij"
         | 
| 178 | 
            +
                end
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                stream.value.must_equal "abcde"
         | 
| 181 | 
            +
              end
         | 
| 182 | 
            +
             | 
| 183 | 
            +
              it "is nestable" do
         | 
| 184 | 
            +
                io.with_buffer(10) do
         | 
| 185 | 
            +
                  io.with_buffer(5) do
         | 
| 186 | 
            +
                    io.writebytes "abc"
         | 
| 187 | 
            +
                  end
         | 
| 188 | 
            +
                  io.writebytes "de"
         | 
| 189 | 
            +
                end
         | 
| 190 | 
            +
             | 
| 191 | 
            +
                stream.value.must_equal "abc\0\0de\0\0\0"
         | 
| 192 | 
            +
              end
         | 
| 193 | 
            +
            end
         | 
| 194 | 
            +
             | 
| 195 | 
            +
            describe BinData::IO::Read, "reading bits in big endian" do
         | 
| 132 196 | 
             
              let(:b1) { 0b1111_1010 }
         | 
| 133 197 | 
             
              let(:b2) { 0b1100_1110 }
         | 
| 134 198 | 
             
              let(:b3) { 0b0110_1010 }
         | 
| 135 | 
            -
              let(:io) { BinData::IO.new([b1, b2, b3].pack("CCC")) }
         | 
| 199 | 
            +
              let(:io) { BinData::IO::Read.new([b1, b2, b3].pack("CCC")) }
         | 
| 136 200 |  | 
| 137 201 | 
             
              it "reads a bitfield less than 1 byte" do
         | 
| 138 202 | 
             
                io.readbits(3, :big).must_equal 0b111
         | 
| @@ -174,11 +238,11 @@ describe BinData::IO, "reading bits in big endian" do | |
| 174 238 | 
             
              end
         | 
| 175 239 | 
             
            end
         | 
| 176 240 |  | 
| 177 | 
            -
            describe BinData::IO, "reading bits in little endian" do
         | 
| 241 | 
            +
            describe BinData::IO::Read, "reading bits in little endian" do
         | 
| 178 242 | 
             
              let(:b1) { 0b1111_1010 }
         | 
| 179 243 | 
             
              let(:b2) { 0b1100_1110 }
         | 
| 180 244 | 
             
              let(:b3) { 0b0110_1010 }
         | 
| 181 | 
            -
              let(:io) { BinData::IO.new([b1, b2, b3].pack("CCC")) }
         | 
| 245 | 
            +
              let(:io) { BinData::IO::Read.new([b1, b2, b3].pack("CCC")) }
         | 
| 182 246 |  | 
| 183 247 | 
             
              it "reads a bitfield less than 1 byte" do
         | 
| 184 248 | 
             
                io.readbits(3, :little).must_equal 0b010
         | 
| @@ -223,7 +287,7 @@ end | |
| 223 287 | 
             
            class BitWriterHelper
         | 
| 224 288 | 
             
              def initialize
         | 
| 225 289 | 
             
                @stringio = BinData::IO.create_string_io
         | 
| 226 | 
            -
                @io = BinData::IO.new(@stringio)
         | 
| 290 | 
            +
                @io = BinData::IO::Write.new(@stringio)
         | 
| 227 291 | 
             
              end
         | 
| 228 292 |  | 
| 229 293 | 
             
              def writebits(val, nbits, endian)
         | 
| @@ -241,7 +305,7 @@ class BitWriterHelper | |
| 241 305 | 
             
              end
         | 
| 242 306 | 
             
            end
         | 
| 243 307 |  | 
| 244 | 
            -
            describe BinData::IO, "writing bits in big endian" do
         | 
| 308 | 
            +
            describe BinData::IO::Write, "writing bits in big endian" do
         | 
| 245 309 | 
             
              let(:io) { BitWriterHelper.new }
         | 
| 246 310 |  | 
| 247 311 | 
             
              it "writes a bitfield less than 1 byte" do
         | 
| @@ -286,7 +350,7 @@ describe BinData::IO, "writing bits in big endian" do | |
| 286 350 | 
             
              end
         | 
| 287 351 | 
             
            end
         | 
| 288 352 |  | 
| 289 | 
            -
            describe BinData::IO, "writing bits in little endian" do
         | 
| 353 | 
            +
            describe BinData::IO::Write, "writing bits in little endian" do
         | 
| 290 354 | 
             
              let(:io) { BitWriterHelper.new }
         | 
| 291 355 |  | 
| 292 356 | 
             
              it "writes a bitfield less than 1 byte" do
         | 
| @@ -331,17 +395,19 @@ describe BinData::IO, "writing bits in little endian" do | |
| 331 395 | 
             
              end
         | 
| 332 396 | 
             
            end
         | 
| 333 397 |  | 
| 334 | 
            -
            describe BinData::IO, "with changing endian" do
         | 
| 398 | 
            +
            describe BinData::IO::Read, "with changing endian" do
         | 
| 335 399 | 
             
              it "does not mix different endianess when reading" do
         | 
| 336 400 | 
             
                b1 = 0b0110_1010
         | 
| 337 401 | 
             
                b2 = 0b1110_0010
         | 
| 338 402 | 
             
                str = [b1, b2].pack("CC")
         | 
| 339 | 
            -
                io = BinData::IO.new(str)
         | 
| 403 | 
            +
                io = BinData::IO::Read.new(str)
         | 
| 340 404 |  | 
| 341 405 | 
             
                io.readbits(3, :big).must_equal 0b011
         | 
| 342 406 | 
             
                io.readbits(4, :little).must_equal 0b0010
         | 
| 343 407 | 
             
              end
         | 
| 408 | 
            +
            end
         | 
| 344 409 |  | 
| 410 | 
            +
            describe BinData::IO::Write, "with changing endian" do
         | 
| 345 411 | 
             
              it "does not mix different endianess when writing" do
         | 
| 346 412 | 
             
                io = BitWriterHelper.new
         | 
| 347 413 | 
             
                io.writebits(0b110, 3, :big)
         |