1require 'dl'
2
3module DL
4  class Stack
5    def self.[](*types)
6      new(types)
7    end
8
9    def initialize(types)
10      parse_types(types)
11    end
12
13    def size()
14      @size
15    end
16
17    def types()
18      @types
19    end
20
21    def pack(ary)
22      case SIZEOF_VOIDP
23      when SIZEOF_LONG
24        ary.pack(@template).unpack('l!*')
25      when SIZEOF_LONG_LONG
26        ary.pack(@template).unpack('q*')
27      else
28        raise(RuntimeError, "sizeof(void*)?")
29      end
30    end
31
32    def unpack(ary)
33      case SIZEOF_VOIDP
34      when SIZEOF_LONG
35        ary.pack('l!*').unpack(@template)
36      when SIZEOF_LONG_LONG
37        ary.pack('q*').unpack(@template)
38      else
39        raise(RuntimeError, "sizeof(void*)?")
40      end
41    end
42
43    private
44
45    def align(addr, align)
46      d = addr % align
47      if( d == 0 )
48        addr
49      else
50        addr + (align - d)
51      end
52    end
53
54    ALIGN_MAP = {
55      TYPE_VOIDP => ALIGN_VOIDP,
56      TYPE_CHAR  => ALIGN_VOIDP,
57      TYPE_SHORT => ALIGN_VOIDP,
58      TYPE_INT   => ALIGN_VOIDP,
59      TYPE_LONG  => ALIGN_VOIDP,
60      TYPE_FLOAT => ALIGN_FLOAT,
61      TYPE_DOUBLE => ALIGN_DOUBLE,
62    }
63
64    PACK_MAP = {
65      TYPE_VOIDP => ((SIZEOF_VOIDP == SIZEOF_LONG_LONG)? "q" : "l!"),
66      TYPE_CHAR  => "c",
67      TYPE_SHORT => "s!",
68      TYPE_INT   => "i!",
69      TYPE_LONG  => "l!",
70      TYPE_FLOAT => "f",
71      TYPE_DOUBLE => "d",
72    }
73
74    SIZE_MAP = {
75      TYPE_VOIDP => SIZEOF_VOIDP,
76      TYPE_CHAR  => SIZEOF_CHAR,
77      TYPE_SHORT => SIZEOF_SHORT,
78      TYPE_INT   => SIZEOF_INT,
79      TYPE_LONG  => SIZEOF_LONG,
80      TYPE_FLOAT => SIZEOF_FLOAT,
81      TYPE_DOUBLE => SIZEOF_DOUBLE,
82    }
83    if defined?(TYPE_LONG_LONG)
84      ALIGN_MAP[TYPE_LONG_LONG] = ALIGN_LONG_LONG
85      PACK_MAP[TYPE_LONG_LONG] = "q"
86      SIZE_MAP[TYPE_LONG_LONG] = SIZEOF_LONG_LONG
87    end
88
89    def parse_types(types)
90      @types = types
91      @template = ""
92      addr      = 0
93      types.each{|t|
94        addr = add_padding(addr, ALIGN_MAP[t])
95        @template << PACK_MAP[t]
96        addr += SIZE_MAP[t]
97      }
98      addr = add_padding(addr, ALIGN_MAP[SIZEOF_VOIDP])
99      if( addr % SIZEOF_VOIDP == 0 )
100        @size = addr / SIZEOF_VOIDP
101      else
102        @size = (addr / SIZEOF_VOIDP) + 1
103      end
104    end
105
106    def add_padding(addr, align)
107      orig_addr = addr
108      addr = align(orig_addr, align)
109      d = addr - orig_addr
110      if( d > 0 )
111        @template << "x#{d}"
112      end
113      addr
114    end
115  end
116end
117