coerce String <=> Integer when using Structs
[lwes-ruby.git] / ext / lwes / numeric.c
blob7616ad9014f550691f213d49b035dde7e77845c3
1 #include "lwes_ruby.h"
2 #include <arpa/inet.h>
4 static ID
5 sym_int16, sym_uint16,
6 sym_int32, sym_uint32,
7 sym_int64, sym_uint64,
8 sym_ip_addr;
9 static ID id_to_i;
11 void lwesrb_dump_type(LWES_BYTE type, LWES_BYTE_P buf, size_t *off)
13 if (marshall_BYTE(type, buf, MAX_MSG_SIZE, off) > 0)
14 return;
15 rb_raise(rb_eRuntimeError, "failed to dump type=%02x", (unsigned)type);
18 static int dump_uint16(VALUE val, LWES_BYTE_P buf, size_t *off)
20 int32_t tmp = NUM2INT(val);
22 if (tmp < 0)
23 rb_raise(rb_eRangeError, ":uint16 negative: %d", tmp);
24 if (tmp > UINT16_MAX)
25 rb_raise(rb_eRangeError, ":uint16 too large: %d", tmp);
27 lwesrb_dump_type(LWES_U_INT_16_TOKEN, buf, off);
28 return marshall_U_INT_16((LWES_U_INT_16)tmp, buf, MAX_MSG_SIZE, off);
31 static int dump_int16(VALUE val, LWES_BYTE_P buf, size_t *off)
33 int32_t tmp = NUM2INT(val);
35 if (tmp > INT16_MAX)
36 rb_raise(rb_eRangeError, ":int16 too large: %i", tmp);
37 if (tmp < INT16_MIN)
38 rb_raise(rb_eRangeError, ":int16 too small: %i", tmp);
40 lwesrb_dump_type(LWES_INT_16_TOKEN, buf, off);
41 return marshall_INT_16((LWES_INT_16)tmp, buf, MAX_MSG_SIZE, off);
44 static int dump_uint32(VALUE val, LWES_BYTE_P buf, size_t *off)
46 LONG_LONG tmp = NUM2LL(val);
48 if (tmp < 0)
49 rb_raise(rb_eRangeError, ":uint32 negative: %lli", tmp);
50 if (tmp > UINT32_MAX)
51 rb_raise(rb_eRangeError, ":uint32 too large: %lli", tmp);
53 lwesrb_dump_type(LWES_U_INT_32_TOKEN, buf, off);
54 return marshall_U_INT_32((LWES_U_INT_32)tmp, buf, MAX_MSG_SIZE, off);
57 static int dump_int32(VALUE val, LWES_BYTE_P buf, size_t *off)
59 LONG_LONG tmp = NUM2LL(val);
61 if (tmp > INT32_MAX)
62 rb_raise(rb_eRangeError, ":int32 too large: %lli", tmp);
63 if (tmp < INT32_MIN)
64 rb_raise(rb_eRangeError, ":int32 too small: %lli", tmp);
66 lwesrb_dump_type(LWES_INT_32_TOKEN, buf, off);
67 return marshall_INT_32((LWES_INT_32)tmp, buf, MAX_MSG_SIZE, off);
70 static int dump_uint64(VALUE val, LWES_BYTE_P buf, size_t *off)
72 unsigned LONG_LONG tmp = NUM2ULL(val); /* can raise RangeError */
73 ID type = TYPE(val);
75 if ((type == T_FIXNUM && FIX2LONG(val) < 0) ||
76 (type == T_BIGNUM && RTEST(rb_funcall(val, '<', 1, INT2FIX(0))))) {
77 volatile VALUE raise_inspect;
79 rb_raise(rb_eRangeError, ":uint64 negative: %s",
80 RAISE_INSPECT(val));
83 lwesrb_dump_type(LWES_U_INT_64_TOKEN, buf, off);
84 return marshall_U_INT_64((LWES_U_INT_64)tmp, buf, MAX_MSG_SIZE, off);
87 static int dump_int64(VALUE val, LWES_BYTE_P buf, size_t *off)
89 LONG_LONG tmp = NUM2LL(val); /* can raise RangeError */
91 lwesrb_dump_type(LWES_INT_64_TOKEN, buf, off);
92 return marshall_INT_64((LWES_INT_64)tmp, buf, MAX_MSG_SIZE, off);
95 static int dump_ip_addr(VALUE val, LWES_BYTE_P buf, size_t *off)
97 LWES_IP_ADDR addr;
98 volatile VALUE raise_inspect;
100 switch (TYPE(val)) {
101 case T_STRING:
102 addr.s_addr = inet_addr(RSTRING_PTR(val));
103 break;
104 case T_FIXNUM:
105 case T_BIGNUM:
106 addr.s_addr = htonl(NUM2UINT(val));
107 break;
108 default:
109 rb_raise(rb_eTypeError,
110 ":ip_addr address must be String or Integer: %s",
111 RAISE_INSPECT(val));
113 lwesrb_dump_type(LWES_IP_ADDR_TOKEN, buf, off);
114 return marshall_IP_ADDR(addr, buf, MAX_MSG_SIZE, off);
117 /* simple type => function dispatch map */
118 static struct _type_fn_map {
119 ID type;
120 int (*fn)(VALUE, LWES_BYTE_P, size_t *);
121 } type_fn_map[] = {
122 #define SYMFN(T) { (ID)&sym_##T, dump_##T }
123 SYMFN(uint16),
124 SYMFN(int16),
125 SYMFN(uint32),
126 SYMFN(int32),
127 SYMFN(uint64),
128 SYMFN(int64),
129 SYMFN(ip_addr),
130 #undef SYMFN
133 /* used for Struct serialization where types are known ahead of time */
134 static int dump_num(
135 LWES_TYPE type,
136 VALUE val,
137 LWES_BYTE_P buf,
138 size_t *off)
140 if (type != LWES_TYPE_IP_ADDR && TYPE(val) == T_STRING)
141 val = rb_funcall(val, id_to_i, 0, 0);
143 switch (type) {
144 case LWES_TYPE_U_INT_16: return dump_uint16(val, buf, off);
145 case LWES_TYPE_INT_16: return dump_int16(val, buf, off);
146 case LWES_TYPE_U_INT_32: return dump_uint32(val, buf, off);
147 case LWES_TYPE_INT_32: return dump_int32(val, buf, off);
148 case LWES_TYPE_U_INT_64: return dump_uint64(val, buf, off);
149 case LWES_TYPE_INT_64: return dump_int64(val, buf, off);
150 case LWES_TYPE_IP_ADDR: return dump_ip_addr(val, buf, off);
151 default:
152 rb_raise(rb_eRuntimeError,
153 "unknown LWES attribute type: 0x%02x", type);
155 assert("you should never get here (dump_num)");
156 return -1;
159 void lwesrb_dump_num(LWES_BYTE type, VALUE val, LWES_BYTE_P buf, size_t *off)
161 if (dump_num(type, val, buf, off) > 0)
162 return;
163 rb_raise(rb_eRuntimeError,
164 "dumping numeric type 0x%02x, type failed", type);
168 * used for Hash serialization
169 * array contains two elements:
170 * [ symbolic_type, number ]
171 * returns the return value of the underlying lwes_event_set_* call
173 void lwesrb_dump_num_ary(VALUE array, LWES_BYTE_P buf, size_t *off)
175 volatile VALUE raise_inspect;
176 int i, rv;
177 struct _type_fn_map *head;
178 VALUE *ary;
179 ID type;
181 assert(TYPE(array) == T_ARRAY && "need array here");
183 if (RARRAY_LEN(array) != 2)
184 rb_raise(rb_eArgError, "expected a two element array");
186 ary = RARRAY_PTR(array);
187 type = ary[0];
189 i = sizeof(type_fn_map) / sizeof(type_fn_map[0]);
190 for (head = type_fn_map; --i >= 0; head++) {
191 if (head->type != type)
192 continue;
194 rv = head->fn(ary[1], buf, off);
195 if (rv > 0)
196 return;
198 rb_raise(rb_eRuntimeError,
199 "dumping numeric type %s, type failed",
200 RAISE_INSPECT(type));
203 rb_raise(rb_eArgError, "unknown type: %s", RAISE_INSPECT(type));
206 void lwesrb_init_numeric(void)
208 int i;
210 LWESRB_MKSYM(int16);
211 LWESRB_MKSYM(uint16);
212 LWESRB_MKSYM(int32);
213 LWESRB_MKSYM(uint32);
214 LWESRB_MKSYM(int64);
215 LWESRB_MKSYM(uint64);
216 LWESRB_MKSYM(ip_addr);
217 id_to_i = rb_intern("to_i");
220 * we needed to have constants for compilation, so we set the
221 * address of the IDs and then dereference + reassign them
222 * at initialization time:
224 i = sizeof(type_fn_map) / sizeof(type_fn_map[0]);
225 while (--i >= 0)
226 type_fn_map[i].type = *(ID *)type_fn_map[i].type;