Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 32 additions & 6 deletions lib/nice-ffi/struct.rb
Original file line number Diff line number Diff line change
Expand Up @@ -368,16 +368,29 @@ def initialize( val, options={} )


def init_from_hash( val ) # :nodoc:
members.each do |member|
self[ member ] = val[ member ]
clear
val.each do |sym, value|
raise NoMethodError unless self.members.member?( sym )
if self[ sym ].is_a?( FFI::Struct )
self[ sym ] = self[ sym ].class.new( value )
else
self[ sym ] = value
end
end
end
private :init_from_hash


def init_from_array( val ) # :nodoc:
unless val.length == members.length
raise IndexError, "expected #{members.length} items, got #{val.length}"
end
members.each_with_index do |member, i|
self[ member ] = val[ i ]
if self[ member ].is_a?( FFI::Struct )
self[ member ] = self[ member ].class.new( val[ i ] )
else
self[ member ] = val[ i ]
end
end
end
private :init_from_array
Expand All @@ -402,7 +415,13 @@ def init_from_bytes( val ) # :nodoc:
# # => [1,2,3,4]
#
def to_ary
members.collect{ |m| self[m] }
members.collect do |m|
if self[ m ].is_a?( FFI::Struct )
self[ m ].to_ary
else
self[ m ]
end
end
end


Expand All @@ -422,8 +441,15 @@ def to_bytes
# # => {:h=>4, :w=>3, :x=>1, :y=>2}
#
def to_hash
return {} if members.empty?
Hash[ *(members.collect{ |m| [m, self[m]] }.flatten!) ]
m_list = {}
members.collect do |m|
if self[ m ].is_a?( FFI::Struct )
m_list[ m ] = self[ m ].to_hash
else
m_list[ m ] = self[ m ]
end
end
return m_list
end


Expand Down
152 changes: 152 additions & 0 deletions spec/struct_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
require 'spec_helper.rb'

# Define some sample structs for test purposes
class Inner < NiceFFI::Struct
layout :one, :uint8,
:two, :uint32,
:three, :uint16
end
class Outer < NiceFFI::Struct
layout :header, :uint8,
:nested, Inner,
:footer, :uint16
end

describe NiceFFI::Struct do

it "generates accessors for nested structures" do
x = Outer.new ""
expect( x ).to respond_to :nested
expect( x.nested ).to be_a( Inner )
expect( x.nested ).to respond_to :one
end

describe "to_hash" do
before :all do
@x = Inner.new [ 3, 2, 1 ]
@h = @x.to_hash
end

it "generates the correct number of elements" do
expect( @h.length ).to eql @x.members.length
end

it "generates the correct sequence of keys" do
expect( @h.keys ).to match_array @x.members
end

it "generates the correct values" do
@h.each {|k,v|
expect( v ).to eql @x[k]
}
end

it "recursively encodes nested structures" do
o = Outer.new ""
h = o.to_hash
expect( h[:nested] ).to be_a( Hash )
end
end

describe "#init_from_hash" do
before :all do
@hash = { :one => 1, :two => 2, :three => 3 }
end

it "loads the correct values" do
x = Inner.new @hash

expect( x[:one] ).to eql 1
expect( x[:two] ).to eql 2
expect( x[:three] ).to eql 3
end

it "only alters the specified fields" do
x = Inner.new @hash.reject{|x| x == :two}

expect( x[:one] ).to eql 1
expect( x[:two] ).to eql 0
expect( x[:three] ).to eql 3
end

it "rejects unknown fields" do
expect {
h = @hash.merge( { :dummy => 0 } )
x = Inner.new h
}.to raise_error( NoMethodError )
end

it "can be used to re-constitute a nested structure" do
x = Outer.new [rand(250), [rand(250), rand(250), rand(250)], rand(250)]

y = Outer.new x.to_hash

expect( y[:header] ).to eql x[:header]
expect( y[:footer] ).to eql x[:footer]
expect( y[:nested][:one] ).to eql x[:nested][:one]
expect( y[:nested][:two] ).to eql x[:nested][:two]
expect( y[:nested][:three] ).to eql x[:nested][:three]
end
end

describe "to_ary" do
before :all do
@init_data = [ 1, 2, 3 ]
@x = Inner.new @init_data
@a = @x.to_ary
end

it "generates the correct number of elements" do
expect( @a.length ).to eql @x.members.length
end

it "generates the correct sequence of values" do
expect( @a ).to match_array @init_data
end

it "recursively encodes nested structures" do
o = Outer.new ""
a = o.to_ary
expect( a[1] ).to be_a( ::Array )
end
end

describe "#init_from_ary" do
before :all do
@init_data = [ 1, 2, 3 ]
end

it "loads the correct values" do
x = Inner.new @init_data

expect( x[:one] ).to eql 1
expect( x[:two] ).to eql 2
expect( x[:three] ).to eql 3
end

it "errors if array is too small" do
expect {
x = Inner.new @init_data[0...-1]
}.to raise_error( IndexError )
end

it "errors if array is too large" do
expect {
x = Inner.new @init_data + [5]
}.to raise_error( IndexError )
end

it "can be used to re-constitute a nested structure" do
x = Outer.new [rand(250), [rand(250), rand(250), rand(250)], rand(250)]

y = Outer.new x.to_ary

expect( y[:header] ).to eql x[:header]
expect( y[:footer] ).to eql x[:footer]
expect( y[:nested][:one] ).to eql x[:nested][:one]
expect( y[:nested][:two] ).to eql x[:nested][:two]
expect( y[:nested][:three] ).to eql x[:nested][:three]
end
end

end