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