diff options
-rw-r--r-- | lib/efl/eina/eina_hash.rb | 81 | ||||
-rw-r--r-- | spec/eina/eina_hash_spec.rb | 186 |
2 files changed, 267 insertions, 0 deletions
diff --git a/lib/efl/eina/eina_hash.rb b/lib/efl/eina/eina_hash.rb new file mode 100644 index 0000000..76cd5d2 --- /dev/null +++ b/lib/efl/eina/eina_hash.rb @@ -0,0 +1,81 @@ +#! /usr/bin/env ruby +# -*- coding: UTF-8 -*- +# +require 'efl/eina' +require 'efl/ffi/eina/eina_hash' +# +class Hash + def self.from_eina_hash o + if o.is_a? Efl::Eina::EinaHash + o.to_h + elsif o.is_a? ::FFI::Pointer + Efl::Eina::EinaHash.new(o).to_h + else + raise ArgumentError.new " wrong argument #{o.class.name}" + end + end +end +module Efl + module Eina + # + class EinaHash + include Efl::Helper + include Enumerable + @func_prefixes = [ 'eina_hash_' ].freeze + def initialize o=nil, &block + cstr = ( block_given? ? block : Proc.new { Efl::FFI.eina_hash_string_djb2_new ::FFI::Pointer::NULL } ) + @ptr = ( + case o + when ::FFI::Pointer + ( o==::FFI::Pointer::NULL ? cstr.call : o ) + when NilClass + cstr.call + when self.class + o.ptr + when Hash + ptr = cstr.call + o.each do |k,v| Efl::FFI.eina_hash_add ptr, k, v end + ptr + else + raise ArgumentError.new "#{ptr.class} valid argument" + end + ) + end + def free + return if @ptr==::FFI::Pointer::NULL + @ptr = Efl::FFI.eina_hash_free @ptr + end + def each data=::FFI::Pointer::NULL, &block + return if not block_given? + Efl::FFI::eina_hash_foreach @ptr, Proc.new{|h,k,v,d| block.call(k,v) }, data + end + def to_h + rh = {} + each { |k,v| rh[k]=v; true } + rh + end + def to_h_conv &block + rh = {} + if block_given? + each { |k,v| rh[block.call(k)]=v; true } + else + each { |k,v| rh[k.read_string]=v; true } + end + rh + end + # for fun and tests + def add k, v + Efl::FFI.eina_hash_add @ptr, k, v + v + end + alias :[]= :add + def find k + r = Efl::FFI.eina_hash_find @ptr, k + return ( r==::FFI::Pointer::NULL ? nil : r ) + end + alias :[] :find + end + end +end +# +# EOF diff --git a/spec/eina/eina_hash_spec.rb b/spec/eina/eina_hash_spec.rb new file mode 100644 index 0000000..3cd5504 --- /dev/null +++ b/spec/eina/eina_hash_spec.rb @@ -0,0 +1,186 @@ +#! /usr/bin/env ruby +# -*- coding: UTF-8 -*- +# +require 'efl/eina/eina_hash' +# +describe Efl::Eina::EinaHash do + # + before(:all) { + EinaHash = Efl::Eina::EinaHash + Efl::Eina.init.should eql 1 + @d0 = ::FFI::MemoryPointer.from_string "D0" + @d1 = ::FFI::MemoryPointer.from_string "D1" + @d2 = ::FFI::MemoryPointer.from_string "D2" + @d3 = ::FFI::MemoryPointer.from_string "D3" + } + after(:all) { + Efl::Eina.shutdown.should eql 0 + } + # + it "should append prepend and fetch" do + h = EinaHash.new + h.add 'k2', @d2 + h.add 'k1', @d1 + h['k3']=@d3 + h['k0']=@d0 + h['k0'].read_string.should eql "D0" + h['k1'].read_string.should eql "D1" + h['k2'].read_string.should eql "D2" + h['k3'].read_string.should eql "D3" + cpt=0 + h.each { |k,v| + cpt+=1 + v.read_string.empty?.should be_false + } + cpt.should eql 4 + h.free + end + # + it "should be able to convert into ruby Hash from NULL pointer" do + h = Hash.from_eina_hash ::FFI::Pointer::NULL + h.empty?.should be_true + h.is_a?(Hash).should be_true + end + # + it "should be able to convert into ruby Hash from empty EinaHash" do + h = Hash.from_eina_hash EinaHash.new + h.empty?.should be_true + h.is_a?(Hash).should be_true + end + # + it "should be able to convert into ruby Hash from empty EinaHash pointer" do + h = Hash.from_eina_hash EinaHash.new.ptr + h.empty?.should be_true + h.is_a?(Hash).should be_true + end + # + it "should be able to convert into ruby Hash from non empty EinaHash" do + h = EinaHash.new + d0 = ::FFI::MemoryPointer.from_string "D0" + d1 = ::FFI::MemoryPointer.from_string "D1" + d2 = ::FFI::MemoryPointer.from_string "D2" + d3 = ::FFI::MemoryPointer.from_string "D3" + h.add 'k2', d2 + h.add 'k1', d1 + h['k3']=d3 + h["k0"]=d0 + h["k0"].read_string.should eql "D0" + h['k1'].read_string.should eql "D1" + h['k2'].read_string.should eql "D2" + h['k3'].read_string.should eql "D3" + cpt=0 + h.each { |k,v| + cpt+=1 + v.read_string.empty?.should be_false + true + } + cpt.should eql 4 + rh = Hash.from_eina_hash h + rh.length.should eql 4 + rh2 = {} + rh.each { |k,v| + rh2[k.read_string]=v.read_string + true + } + rh2['k0'].should eql 'D0' + rh2['k1'].should eql 'D1' + rh2['k2'].should eql 'D2' + rh2['k3'].should eql 'D3' + h.free + end + # + it "should be able to convert into ruby Hash from non empty EinaHash pointer" do + h = EinaHash.new + d0 = ::FFI::MemoryPointer.from_string "D0" + d1 = ::FFI::MemoryPointer.from_string "D1" + d2 = ::FFI::MemoryPointer.from_string "D2" + d3 = ::FFI::MemoryPointer.from_string "D3" + h.add 'k2', d2 + h.add 'k1', d1 + h['k3']=d3 + h['k0']=d0 + h['k0'].read_string.should eql "D0" + h['k1'].read_string.should eql "D1" + h['k2'].read_string.should eql "D2" + h['k3'].read_string.should eql "D3" + rh = Hash.from_eina_hash h.ptr + rh.length.should eql 4 + h.free + end + # + it "should be able to convert into ruby Hash from non empty EinaHash pointer, with key from string" do + h = EinaHash.new + d0 = ::FFI::MemoryPointer.from_string "D0" + d1 = ::FFI::MemoryPointer.from_string "D1" + d2 = ::FFI::MemoryPointer.from_string "D2" + d3 = ::FFI::MemoryPointer.from_string "D3" + h.add 'k2', d2 + h.add 'k1', d1 + h['k3']=d3 + h['k0']=d0 + h['k0'].read_string.should eql "D0" + h['k1'].read_string.should eql "D1" + h['k2'].read_string.should eql "D2" + h['k3'].read_string.should eql "D3" + rh = h.to_h_conv + rh.length.should eql 4 + rh['k0'].read_string.should eql "D0" + rh['k1'].read_string.should eql "D1" + rh['k2'].read_string.should eql "D2" + rh['k3'].read_string.should eql "D3" + h.free + end + # + it "should be able to convert into ruby Hash from non empty EinaHash pointer, with key from string block" do + h = EinaHash.new + d0 = ::FFI::MemoryPointer.from_string "D0" + d1 = ::FFI::MemoryPointer.from_string "D1" + d2 = ::FFI::MemoryPointer.from_string "D2" + d3 = ::FFI::MemoryPointer.from_string "D3" + h.add 'k2', d2 + h.add 'k1', d1 + h['k3']=d3 + h['k0']=d0 + h['k0'].read_string.should eql "D0" + h['k1'].read_string.should eql "D1" + h['k2'].read_string.should eql "D2" + h['k3'].read_string.should eql "D3" + cpt=0 + rh = h.to_h_conv { |k| cpt+=1; k.read_string } + cpt.should eql 4 + rh.length.should eql 4 + rh['k0'].read_string.should eql "D0" + rh['k1'].read_string.should eql "D1" + rh['k2'].read_string.should eql "D2" + rh['k3'].read_string.should eql "D3" + h.free + end + # + it "should be able to build from ruby Hash" do + rh = {} + k0 = ::FFI::MemoryPointer.from_string "0" + k1 = ::FFI::MemoryPointer.from_string "1" + k2 = ::FFI::MemoryPointer.from_string "2" + k3 = ::FFI::MemoryPointer.from_string "3" + d0 = ::FFI::MemoryPointer.from_string "D0" + d1 = ::FFI::MemoryPointer.from_string "D1" + d2 = ::FFI::MemoryPointer.from_string "D2" + d3 = ::FFI::MemoryPointer.from_string "D3" + rh[k0]=d0 + rh[k1]=d1 + rh[k2]=d2 + rh[k3]=d3 + h = EinaHash.new rh + h[k0].read_string.should eql "D0" + h[k1].read_string.should eql "D1" + h[k2].read_string.should eql "D2" + h[k3].read_string.should eql "D3" + h.free + end + # + it "alternate constructor should work" do + cstr_cnt = 0 + h = EinaHash.new { cstr_cnt+=1; Efl::FFI.eina_hash_string_superfast_new ::FFI::Pointer::NULL } + cstr_cnt.should eql 1 + end +end |