9 #include "shotgun/lib/shotgun.h"
10 #include "shotgun/lib/cpu.h"
11 #include "shotgun/lib/string.h"
12 #include "shotgun/lib/bytearray.h"
13 #include "shotgun/lib/symbol.h"
14 #include "shotgun/lib/tuple.h"
15 #include "shotgun/lib/bignum.h"
16 #include "shotgun/lib/float.h"
17 #include "shotgun/lib/sha1.h"
18 #include "shotgun/lib/sendsite.h"
20 #include "shotgun/lib/primitive_util.h"
22 struct marshal_state
{
28 static int _find_object(OBJECT obj
, struct marshal_state
*ms
) {
31 for(i
= 0; i
< ptr_array_length(ms
->objects
); i
++) {
32 if(obj
== (OBJECT
)ptr_array_get_index(ms
->objects
, i
)) {
40 static void _add_object(OBJECT obj
, struct marshal_state
*ms
) {
41 ptr_array_append(ms
->objects
, (xpointer
)obj
);
44 static OBJECT
unmarshal(STATE
, struct marshal_state
*ms
);
45 static void marshal(STATE
, OBJECT obj
, bstring buf
, struct marshal_state
*ms
);
47 static void int2be(unsigned int i
, unsigned char bytes
[4]) {
48 bytes
[0] = ( i
>> 24 ) & 0xff;
49 bytes
[1] = ( i
>> 16 ) & 0xff;
50 bytes
[2] = ( i
>> 8 ) & 0xff;
54 #define append_c(ch) bconchar(buf, ch)
55 static void _append_sz(bstring buf
, unsigned int i
) {
56 unsigned char bytes
[4];
58 bcatblk(buf
, bytes
, 4);
60 #define append_sz(sz) _append_sz(buf, (unsigned int)sz)
61 #define append_str(str, sz) bcatblk(buf, str, sz)
63 static int read_int(uint8_t *str
) {
64 return (int)( (str
[0] << 24)
70 static OBJECT
_nth_object(STATE
, struct marshal_state
*ms
) {
74 ref
= read_int(ms
->buf
+ 1);
75 return (OBJECT
)ptr_array_get_index(ms
->objects
, ref
);
78 static OBJECT
unmarshal_int(STATE
, struct marshal_state
*ms
) {
81 i
= read_int(ms
->buf
+ 2);
82 if(ms
->buf
[1] == 'n') {
88 static void marshal_str(STATE
, OBJECT obj
, bstring buf
) {
90 i
= N2I(string_get_bytes(obj
));
93 append_str(rbx_string_as_cstr(state
, obj
), i
);
96 static OBJECT
unmarshal_str(STATE
, struct marshal_state
*ms
) {
98 sz
= read_int(ms
->buf
+ 1);
101 return string_new2(state
, (char *) ms
->buf
+ 5, sz
);
104 static void marshal_sym(STATE
, OBJECT obj
, bstring buf
) {
107 str
= symtbl_find_string(state
, state
->global
->symbols
, obj
);
108 i
= N2I(string_get_bytes(str
));
111 append_str(rbx_string_as_cstr(state
, str
), i
);
114 static OBJECT
unmarshal_sym(STATE
, struct marshal_state
*ms
) {
117 sz
= read_int(ms
->buf
+ 1);
121 return symtbl_lookup_str_with_size(state
, state
->global
->symbols
,
122 (char *) ms
->buf
+ 5, sz
);
125 static void marshal_sendsite(STATE
, OBJECT obj
, bstring buf
) {
128 str
= symtbl_find_string(state
, state
->global
->symbols
, SENDSITE(obj
)->name
);
129 i
= N2I(string_get_bytes(str
));
132 append_str(rbx_string_as_cstr(state
, str
), i
);
135 static OBJECT
unmarshal_sendsite(STATE
, struct marshal_state
*ms
) {
139 sz
= read_int(ms
->buf
+ 1);
143 sym
= symtbl_lookup_str_with_size(state
, state
->global
->symbols
,
144 (char *) ms
->buf
+ 5, sz
);
146 return send_site_create(state
, sym
);
149 static void marshal_fields_as(STATE
, OBJECT obj
, bstring buf
, char type
, struct marshal_state
*ms
) {
151 sz
= NUM_FIELDS(obj
);
154 for(i
= 0; i
< sz
; i
++) {
155 marshal(state
, NTH_FIELD(obj
, i
), buf
, ms
);
159 static OBJECT
unmarshal_into_fields(STATE
, int sz
, OBJECT tup
, struct marshal_state
*ms
) {
163 for(i
= 0; i
< sz
; i
++) {
164 uint8_t *old
= ms
->buf
;
166 o
= unmarshal(state
, ms
);
167 j
= ms
->consumed
- cur
;
170 SET_FIELD(tup
, i
, o
);
176 static int unmarshal_num_fields(struct marshal_state
*ms
) {
178 i
= read_int(ms
->buf
+ 1);
186 static void marshal_ary(STATE
, OBJECT obj
, bstring buf
, struct marshal_state
*ms
) {
188 sz
= N2I(array_get_total(obj
));
191 for(i
= 0; i
< sz
; i
++) {
192 marshal(state
, array_get(state
, obj
, i
), buf
, ms
);
196 static OBJECT
unmarshal_ary(STATE
, struct marshal_state
*ms
) {
199 int sz
= unmarshal_num_fields(ms
);
200 OBJECT ary
= array_new(state
, sz
);
203 for(i
= 0; i
< sz
; i
++) {
204 uint8_t *old
= ms
->buf
;
206 o
= unmarshal(state
, ms
);
207 j
= ms
->consumed
- cur
;
210 array_set(state
, ary
, i
, o
);
216 static void marshal_tup(STATE
, OBJECT obj
, bstring buf
, struct marshal_state
*ms
) {
217 return marshal_fields_as(state
, obj
, buf
, 'p', ms
);
220 static OBJECT
unmarshal_tup(STATE
, struct marshal_state
*ms
) {
223 sz
= unmarshal_num_fields(ms
);
224 tup
= tuple_new(state
, sz
);
225 unmarshal_into_fields(state
, sz
, tup
, ms
);
229 static void marshal_bignum(STATE
, OBJECT obj
, bstring buf
) {
232 bignum_into_string(state
, obj
, 10, buffer
, 1024);
236 bcatblk(buf
, buffer
, i
);
237 append_c(0); /* zero byte */
240 static void marshal_fixnum(STATE
, OBJECT obj
, bstring buf
) {
244 i
= snprintf(buffer
, 1023, "%ld", (long int)N2I(obj
));
248 bcatblk(buf
, buffer
, i
);
252 static OBJECT
unmarshal_bignum(STATE
, struct marshal_state
*ms
) {
254 sz
= read_int(ms
->buf
+ 1);
257 ms
->consumed
++; /* zero byte */
258 return bignum_from_string(state
, (char *) ms
->buf
+ 5, 10);
261 static void marshal_floatpoint(STATE
, OBJECT obj
, bstring buf
) {
265 float_into_string(state
, obj
, buffer
, 26);
269 bcatblk(buf
, buffer
, i
);
270 append_c(0); /* zero byte */
273 static OBJECT
unmarshal_floatpoint(STATE
, struct marshal_state
*ms
) {
275 sz
= read_int(ms
->buf
+ 1);
278 ms
->consumed
++; /* zero byte */
279 return float_from_string(state
, (char *) ms
->buf
+ 5);
282 static void marshal_bytes(STATE
, OBJECT obj
, bstring buf
) {
284 i
= SIZE_OF_BODY(obj
);
287 append_str(bytearray_byte_address(state
, obj
), i
);
290 static OBJECT
unmarshal_bytes(STATE
, struct marshal_state
*ms
) {
293 sz
= read_int(ms
->buf
+ 1);
297 obj
= bytearray_new(state
, sz
);
299 memcpy(bytearray_byte_address(state
, obj
), ms
->buf
+ 5, sz
);
301 // printf("Unmarshaled bytes: %p / %d / %d\n", bytearray_byte_address(state, obj), sz, bytearray_bytes(state, obj));
306 static void marshal_iseq(STATE
, OBJECT obj
, bstring buf
) {
311 i
= SIZE_OF_BODY(obj
);
313 append_str(bytearray_byte_address(state
, obj
), i
);
316 static OBJECT
unmarshal_iseq(STATE
, struct marshal_state
*ms
) {
322 sz
= read_int(ms
->buf
+ 2);
327 obj
= iseq_new(state
, sz
);
329 memcpy(bytearray_byte_address(state
, obj
), ms
->buf
+ 6, sz
);
331 /* We only support iseq's stored big endian currently. */
332 sassert(endian
== 'b');
337 static OBJECT
unmarshal_cmethod(STATE
, struct marshal_state
*ms
) {
340 sz
= unmarshal_num_fields(ms
);
341 cm
= cmethod_allocate(state
);
342 unmarshal_into_fields(state
, sz
, cm
, ms
);
345 prim
= cmethod_get_primitive(cm
);
347 int idx
= calc_primitive_index(state
, symbol_to_string(state
, prim
));
349 cmethod_set_primitive(cm
, I2N(idx
));
350 } else if(NIL_P(prim
)) {
351 cmethod_set_primitive(cm
, I2N(-1));
356 static void marshal_cmethod2(STATE
, OBJECT obj
, bstring buf
, struct marshal_state
*ms
) {
360 /* rather than a size, we use a version id */
363 for(i
= 0; i
< 16; i
++) {
364 marshal(state
, NTH_FIELD(obj
, i
), buf
, ms
);
369 static OBJECT
unmarshal_cmethod2(STATE
, struct marshal_state
*ms
) {
371 OBJECT cm
, prim
, o
, l
;
373 ver
= unmarshal_num_fields(ms
);
374 cm
= cmethod_allocate(state
);
376 unmarshal_into_fields(state
, 16, cm
, ms
);
379 prim
= cmethod_get_primitive(cm
);
381 int idx
= calc_primitive_index(state
, symbol_to_string(state
, prim
));
383 cmethod_set_primitive(cm
, I2N(idx
));
384 } else if(NIL_P(prim
)) {
385 cmethod_set_primitive(cm
, I2N(-1));
388 /* Set the compiled method field on each SendSite */
389 l
= cmethod_get_literals(cm
);
391 int sz
= tuple_fields(state
, l
);
392 for(i
= 0; i
< sz
; i
++) {
393 o
= tuple_at(state
, l
, i
);
395 send_site_set_sender(state
, o
, cm
);
403 static OBJECT
unmarshal(STATE
, struct marshal_state
*ms
) {
404 uint8_t tag
= *ms
->buf
;
407 // printf("%c\n", tag);
410 o
= unmarshal_int(state
, ms
);
413 o
= unmarshal_str(state
, ms
);
417 o
= unmarshal_sendsite(state
, ms
);
420 o
= unmarshal_sym(state
, ms
);
423 o
= unmarshal_tup(state
, ms
);
427 o
= unmarshal_ary(state
, ms
);
431 o
= unmarshal_bytes(state
, ms
);
435 o
= unmarshal_iseq(state
, ms
);
439 o
= unmarshal_cmethod(state
, ms
);
443 o
= unmarshal_cmethod2(state
, ms
);
447 o
= unmarshal_bignum(state
, ms
);
451 o
= unmarshal_floatpoint(state
, ms
);
455 o
= _nth_object(state
, ms
);
471 printf("Unknown marshal type '0x%x' at %d!\n", tag
, ms
->consumed
);
477 static void marshal(STATE
, OBJECT obj
, bstring buf
, struct marshal_state
*ms
) {
482 marshal_fixnum(state
, obj
, buf
);
483 } else if(SYMBOL_P(obj
)) {
484 marshal_sym(state
, obj
, buf
);
485 } else if(obj
== Qnil
) {
487 } else if(obj
== Qtrue
) {
489 } else if(obj
== Qfalse
) {
491 } else if(REFERENCE_P(obj
)) {
492 if((ref
= _find_object(obj
, ms
)) > 0) {
496 _add_object(obj
, ms
);
497 kls
= object_class(state
, obj
);
498 if(kls
== state
->global
->string
) {
499 marshal_str(state
, obj
, buf
);
500 } else if(kls
== state
->global
->tuple
) {
501 marshal_tup(state
, obj
, buf
, ms
);
502 } else if(kls
== state
->global
->array
) {
503 marshal_ary(state
, obj
, buf
, ms
);
504 } else if(kls
== state
->global
->cmethod
) {
505 marshal_cmethod2(state
, obj
, buf
, ms
);
506 } else if(kls
== state
->global
->bytearray
) {
507 marshal_bytes(state
, obj
, buf
);
508 } else if(kls
== state
->global
->iseq
) {
509 marshal_iseq(state
, obj
, buf
);
510 } else if(kls
== BASIC_CLASS(bignum
)) {
511 marshal_bignum(state
, obj
, buf
);
512 } else if(kls
== BASIC_CLASS(floatpoint
)) {
513 marshal_floatpoint(state
, obj
, buf
);
514 } else if(SENDSITE_P(obj
)) {
515 marshal_sendsite(state
, obj
, buf
);
517 printf("Unable to marshal class %p = %s!\n", (void *)kls
, rbs_inspect(state
, kls
));
523 OBJECT
cpu_marshal(STATE
, OBJECT obj
, int version
) {
527 buf
= cpu_marshal_to_bstring(state
, obj
, version
);
528 ret
= string_newfrombstr(state
, buf
);
533 bstring
cpu_marshal_to_bstring(STATE
, OBJECT obj
, int version
) {
535 struct marshal_state ms
;
536 unsigned char cur_digest
[20];
539 ms
.objects
= ptr_array_new(8);
541 stream
= cstr2bstr("");
542 marshal(state
, obj
, stream
, &ms
);
543 sha1_hash_string((unsigned char*)bdata(stream
), blength(stream
), cur_digest
);
545 buf
= cstr2bstr("RBIX");
547 _append_sz(buf
, version
);
548 bcatblk(buf
, (void*)cur_digest
, 20);
549 bconcat(buf
, stream
);
552 ptr_array_free(ms
.objects
);
556 OBJECT
cpu_marshal_to_file(STATE
, OBJECT obj
, char *path
, int version
) {
559 struct marshal_state ms
;
560 unsigned char cur_digest
[20];
561 unsigned char bytes
[4];
563 f
= fopen(path
, "wb");
569 ms
.objects
= ptr_array_new(8);
573 marshal(state
, obj
, buf
, &ms
);
574 sha1_hash_string((unsigned char*)bdata(buf
), blength(buf
), cur_digest
);
576 /* TODO do error chceking here */
577 fwrite("RBIX", 1, 4, f
);
579 int2be(version
, bytes
);
580 fwrite(bytes
, 1, 4, f
);
582 fwrite(cur_digest
, 1, 20, f
);
584 fwrite(bdatae(buf
,""), 1, blength(buf
), f
);
588 ptr_array_free(ms
.objects
);
592 OBJECT
cpu_unmarshal(STATE
, uint8_t *str
, int len
, int version
) {
593 struct marshal_state ms
;
597 unsigned char cur_digest
[20];
599 if(!memcmp(str
, "RBIS", 4)) {
601 } else if(!memcmp(str
, "RBIX", 4)) {
602 in_version
= read_int(str
+ 4);
603 if(in_version
< version
) {
604 /* file is out of date. */
611 sha1_hash_string((unsigned char*)(str
+ offset
), len
- offset
, cur_digest
);
613 /* Check if the calculate one is the one in the stream. */
614 if(memcmp(str
+ offset
- 20, cur_digest
, 20)) {
618 printf("Invalid compiled file.\n");
622 ms
.objects
= ptr_array_new(8);
623 ms
.buf
= str
+ offset
;
625 ret
= unmarshal(state
, &ms
);
626 ptr_array_free(ms
.objects
);
630 OBJECT
cpu_unmarshal_file(STATE
, const char *path
, int version
) {
636 fd
= open(path
, O_RDONLY
);
641 if (fstat(fd
, &st
) || !st
.st_size
) {
646 map
= mmap(NULL
, (size_t) st
.st_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
649 obj
= cpu_unmarshal(state
, map
, (int)st
.st_size
, version
);
650 munmap(map
, st
.st_size
);