summaryrefslogtreecommitdiff
path: root/ext/dl/lib/dl/struct.rb
blob: eb727762aea3769c89b76c6bb5b4f4d49e49b4ae (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# -*- ruby -*-

require 'dl'
require 'dl/import'

module DL
  module Importable
    module Internal
      def define_struct(contents)
	init_types()
	Struct.new(@types, contents)
      end
      alias struct define_struct

      def define_union(contents)
	init_types()
	Union.new(@types, contents)
      end
      alias union define_union

      class Memory
	def initialize(ptr, names, ty, len, enc, dec)
	  @ptr = ptr
	  @names = names
	  @ty    = ty
	  @len   = len
	  @enc   = enc
	  @dec   = dec

	  # define methods
	  @names.each{|name|
	    instance_eval [
	      "def #{name}",
	      "  v = @ptr[\"#{name}\"]",
	      "  v = @dec[\"#{name}\"].call(v,@len[\"#{name}\"]) if @dec[\"#{name}\"]",
	      "  return v",
	      "end",
	      "def #{name}=(v)",
	      "  v = @enc[\"#{name}\"].call(v,@len[\"#{name}\"]) if @enc[\"#{name}\"]",
	      "  @ptr[\"#{name}\"] = v",
	      "  return v",
	      "end",
	    ].join("\n")
	  }
	end
      end

      class Struct
	def initialize(types, contents)
	  @names = []
	  @ty   = {}
	  @len  = {}
	  @enc  = {}
	  @dec  = {}
	  @size = 0
	  @tys  = ""
	  @types = types
	  parse(contents)
	end

	def new(size = nil)
	  if( !size )
	    size = @size
	  end
	  ptr = DL::malloc(size)
	  ptr.struct!(@tys, *@names)
	  mem = Memory.new(ptr, @names, @ty, @len, @enc, @dec)
	  return mem
	end

	def parse(contents)
	  contents.each{|elem|
	    name,ty,num,enc,dec = parse_elem(elem)
	    @names.push(name)
	    @ty[name]  = ty
	    @len[name] = num
	    @enc[name] = enc
	    @dec[name] = dec
	    if( num )
	      @tys += "#{ty}#{num}"
	    else
	      @tys += ty
	    end
	  }
	  @size = DL.sizeof(@tys)
	end
	
	def parse_elem(elem)
	  elem.strip!
	  case elem
	  when /^([\w\d_\*]+)([\*\s]+)([\w\d_]+)$/
	    ty = ($1 + $2).strip
	    name = $3
	    num = nil;
	  when /^([\w\d_\*]+)([\*\s]+)([\w\d_]+)\[(\d+)\]$/
	    ty = ($1 + $2).strip
	    name = $3
	    num = $4.to_i
	  else
	    raise(RuntimeError, "invalid element: #{elem}")
	  end
	  ty,_,_,enc,dec = @types.encode_type(ty)
	  return [name,ty,num,enc,dec]
	end
      end  # class Struct
      
      class Union < Struct
	def new
	  ptr = DL::malloc(@size)
	  ptr.union!(@tys, *@names)
	  mem = Memory.new(ptr, @names, @ty, @len, @enc, @dec)
	  return mem
	end
      end
    end  # module Internal
  end  # module Importable
end  # module DL