3 /* ddt_exchange - send/recv data described */
5 /* Authors: Olivier Aumage, Martin Quinson */
6 /* Copyright (C) 2003, 2004 the GRAS posse. */
8 /* This program is free software; you can redistribute it and/or modify it
9 under the terms of the license (GNU LGPL) which comes with this package. */
11 #include "gras/DataDesc/datadesc_private.h"
12 #include "gras/Transport/transport_interface.h" /* gras_trp_chunk_send/recv */
14 GRAS_LOG_NEW_DEFAULT_SUBCATEGORY(exchange,datadesc);
17 /* CRUDE HACK to turn all cycle detection of */
19 #define gras_dict_get_ext(a,b,c,d) mismatch_error
20 #define gras_dict_set_ext(a,b,c,d,e) no_error
23 const char *gras_datadesc_cat_names[9] = {
25 "scalar", "struct", "union", "ref", "array", "ignored",
28 static gras_datadesc_type_t *int_type = NULL;
29 static gras_datadesc_type_t *pointer_type = NULL;
30 static inline gras_error_t gras_dd_send_int(gras_socket_t *sock, int i);
31 static inline gras_error_t gras_dd_recv_int(gras_socket_t *sock, int r_arch, int *i);
33 static inline gras_error_t
34 gras_dd_alloc_ref(gras_dict_t *refs, long int size,
35 char **r_ref, long int r_len,
38 gras_dd_is_r_null(char **r_ptr, long int length);
41 gras_datadesc_send_rec(gras_socket_t *sock,
44 gras_datadesc_type_t *type,
47 gras_datadesc_recv_rec(gras_socket_t *sock,
50 gras_datadesc_type_t *type,
58 static inline gras_error_t
59 gras_dd_send_int(gras_socket_t *sock,int i) {
62 int_type = gras_datadesc_by_name("int");
63 gras_assert(int_type);
66 DEBUG1("send_int(%d)",i);
67 return gras_trp_chunk_send(sock, (char*)&i, int_type->size[GRAS_THISARCH]);
70 static inline gras_error_t
71 gras_dd_recv_int(gras_socket_t *sock, int r_arch, int *i) {
75 int_type = gras_datadesc_by_name("int");
76 gras_assert(int_type);
79 if (int_type->size[GRAS_THISARCH] >= int_type->size[r_arch]) {
80 TRY(gras_trp_chunk_recv(sock, (char*)i, int_type->size[r_arch]));
81 if (r_arch != GRAS_THISARCH)
82 TRY(gras_dd_convert_elm(int_type,1,r_arch, i,i));
85 ptr = malloc((size_t)int_type->size[r_arch]);
86 TRY(gras_trp_chunk_recv(sock, (char*)ptr, int_type->size[r_arch]));
87 if (r_arch != GRAS_THISARCH)
88 TRY(gras_dd_convert_elm(int_type,1,r_arch, ptr,i));
91 DEBUG1("recv_int(%d)",*i);
97 * Note: here we suppose that the remote NULL is a sequence
98 * of 'length' bytes set to 0.
99 * FIXME: Check in configure?
102 gras_dd_is_r_null(char **r_ptr, long int length) {
105 for (i=0; i<length; i++) {
106 if ( ((unsigned char*)r_ptr) [i]) {
114 static inline gras_error_t
115 gras_dd_alloc_ref(gras_dict_t *refs,
118 long int r_len, /* pointer_type->size[r_arch] */
122 gras_assert1(size>0,"Cannot allocate %ld bytes!", size);
123 if (! (l_data = malloc((size_t)size)) )
127 DEBUG2("l_data=%p, &l_data=%p",l_data,&l_data);
129 DEBUG3("alloc_ref: r_ref=%p; *r_ref=%p, r_len=%ld",
130 r_ref, r_ref?*r_ref:NULL, r_len);
132 if (r_ref && !gras_dd_is_r_null( r_ref, r_len)) {
133 gras_error_t errcode;
134 void *ptr = malloc(sizeof(void *));
138 memcpy(ptr,l_ref, sizeof(void *));
140 DEBUG2("Insert %p under %p",*(void**)ptr, *(void**)r_ref);
142 TRY(gras_dict_set_ext(refs,(const char *) r_ref, r_len, ptr, free));
149 * gras_datadesc_type_cmp:
151 * Compares two datadesc types with the same semantic than strcmp.
153 * This comparison does not take the set headers into account (name and ID),
154 * but only the payload (actual type description).
156 int gras_datadesc_type_cmp(const gras_datadesc_type_t *d1,
157 const gras_datadesc_type_t *d2) {
159 gras_dd_cat_field_t *field1,*field2;
160 gras_datadesc_type_t *field_desc_1,*field_desc_2;
162 if (d1 == d2) return 0; /* easy optimization */
165 DEBUG0("ddt_cmp: !d1 && d2 => 1");
169 DEBUG0("ddt_cmp: !d1 && !d2 => 0");
173 DEBUG0("ddt_cmp: d1 && !d2 => -1");
177 for (cpt=0; cpt<gras_arch_count; cpt++) {
178 if (d1->size[cpt] != d2->size[cpt]) {
179 DEBUG5("ddt_cmp: %s->size=%ld != %s->size=%ld (on %s)",
180 d1->name,d1->size[cpt],d2->name,d2->size[cpt],
181 gras_arches[cpt].name);
182 return d1->size[cpt] > d2->size[cpt] ? 1 : -1;
185 if (d1->alignment[cpt] != d2->alignment[cpt]) {
186 DEBUG5("ddt_cmp: %s->alignment=%ld != %s->alignment=%ld (on %s)",
187 d1->name,d1->alignment[cpt],d2->name,d2->alignment[cpt],
188 gras_arches[cpt].name);
189 return d1->alignment[cpt] > d2->alignment[cpt] ? 1 : -1;
192 if (d1->aligned_size[cpt] != d2->aligned_size[cpt]) {
193 DEBUG5("ddt_cmp: %s->aligned_size=%ld != %s->aligned_size=%ld (on %s)",
194 d1->name,d1->aligned_size[cpt],d2->name,d2->aligned_size[cpt],
195 gras_arches[cpt].name);
196 return d1->aligned_size[cpt] > d2->aligned_size[cpt] ? 1 : -1;
200 if (d1->category_code != d2->category_code) {
201 DEBUG4("ddt_cmp: %s->cat=%s != %s->cat=%s",
202 d1->name,gras_datadesc_cat_names[d1->category_code],
203 d2->name,gras_datadesc_cat_names[d2->category_code]);
204 return d1->category_code > d2->category_code ? 1 : -1;
207 if (d1->send != d2->send) {
208 DEBUG4("ddt_cmp: %s->send=%p != %s->send=%p",
209 d1->name,d1->send, d2->name,d2->send);
210 return d1->send > d2->send ? 1 : -1;
213 if (d1->recv != d2->recv) {
214 DEBUG4("ddt_cmp: %s->recv=%p != %s->recv=%p",
215 d1->name,d1->recv, d2->name,d2->recv);
216 return d1->recv > d2->recv ? 1 : -1;
219 switch (d1->category_code) {
220 case e_gras_datadesc_type_cat_scalar:
221 if (d1->category.scalar_data.encoding != d2->category.scalar_data.encoding)
222 return d1->category.scalar_data.encoding > d2->category.scalar_data.encoding ? 1 : -1 ;
225 case e_gras_datadesc_type_cat_struct:
226 if (gras_dynar_length(d1->category.struct_data.fields) !=
227 gras_dynar_length(d2->category.struct_data.fields)) {
228 DEBUG4("ddt_cmp: %s (having %d fields) != %s (having %d fields)",
229 d1->name, gras_dynar_length(d1->category.struct_data.fields),
230 d2->name, gras_dynar_length(d2->category.struct_data.fields));
232 return gras_dynar_length(d1->category.struct_data.fields) >
233 gras_dynar_length(d2->category.struct_data.fields) ?
236 gras_dynar_foreach(d1->category.struct_data.fields, cpt, field1) {
238 gras_dynar_get(d2->category.struct_data.fields, cpt, &field2);
239 field_desc_1 = field1->type;
240 field_desc_2 = field2->type;
241 ret = gras_datadesc_type_cmp(field_desc_1,field_desc_2);
243 DEBUG6("%s->field[%d]=%s != %s->field[%d]=%s",
244 d1->name,cpt,field1->name,
245 d2->name,cpt,field2->name);
252 case e_gras_datadesc_type_cat_union:
253 if (d1->category.union_data.selector != d2->category.union_data.selector)
254 return d1->category.union_data.selector > d2->category.union_data.selector ? 1 : -1;
256 if (gras_dynar_length(d1->category.union_data.fields) !=
257 gras_dynar_length(d2->category.union_data.fields))
258 return gras_dynar_length(d1->category.union_data.fields) >
259 gras_dynar_length(d2->category.union_data.fields) ?
262 gras_dynar_foreach(d1->category.union_data.fields, cpt, field1) {
264 gras_dynar_get(d2->category.union_data.fields, cpt, field2);
265 field_desc_1 = field1->type;
266 field_desc_2 = field2->type;
267 ret = gras_datadesc_type_cmp(field_desc_1,field_desc_2);
275 case e_gras_datadesc_type_cat_ref:
276 if (d1->category.ref_data.selector != d2->category.ref_data.selector)
277 return d1->category.ref_data.selector > d2->category.ref_data.selector ? 1 : -1;
279 if (d1->category.ref_data.type != d2->category.ref_data.type)
280 return d1->category.ref_data.type > d2->category.ref_data.type ? 1 : -1;
283 case e_gras_datadesc_type_cat_array:
284 if (d1->category.array_data.type != d2->category.array_data.type)
285 return d1->category.array_data.type > d2->category.array_data.type ? 1 : -1;
287 if (d1->category.array_data.fixed_size != d2->category.array_data.fixed_size)
288 return d1->category.array_data.fixed_size > d2->category.array_data.fixed_size ? 1 : -1;
290 if (d1->category.array_data.dynamic_size != d2->category.array_data.dynamic_size)
291 return d1->category.array_data.dynamic_size > d2->category.array_data.dynamic_size ? 1 : -1;
295 case e_gras_datadesc_type_cat_ignored:
296 /* That's ignored... */
298 /* two stupidly created ddt are equally stupid ;) */
308 * Copy the data pointed by src and described by type
309 * to a new location, and store a pointer to it in dst.
312 gras_error_t gras_datadesc_cpy(gras_datadesc_type_t *type,
319 gras_datadesc_send_rec(gras_socket_t *sock,
322 gras_datadesc_type_t *type,
325 gras_error_t errcode;
327 gras_datadesc_type_t *sub_type; /* type on which we recurse */
329 VERB2("Send a %s (%s)",
330 type->name, gras_datadesc_cat_names[type->category_code]);
333 type->send(state,data);
336 switch (type->category_code) {
337 case e_gras_datadesc_type_cat_scalar:
338 TRY(gras_trp_chunk_send(sock, data, type->size[GRAS_THISARCH]));
341 case e_gras_datadesc_type_cat_struct: {
342 gras_dd_cat_struct_t struct_data;
343 gras_dd_cat_field_t *field;
346 struct_data = type->category.struct_data;
347 gras_assert1(struct_data.closed,
348 "Please call gras_datadesc_declare_struct_close on %s before sending it",
350 VERB1(">> Send all fields of the structure %s",type->name);
351 gras_dynar_foreach(struct_data.fields, cpt, field) {
353 field_data += field->offset[GRAS_THISARCH];
355 sub_type = field->type;
358 field->pre(state,field_data);
360 VERB1("Send field %s",field->name);
361 TRY(gras_datadesc_send_rec(sock,state,refs,sub_type, field_data));
364 field->post(state,field_data);
366 VERB1("<< Sent all fields of the structure %s", type->name);
371 case e_gras_datadesc_type_cat_union: {
372 gras_dd_cat_union_t union_data;
373 gras_dd_cat_field_t *field=NULL;
376 union_data = type->category.union_data;
378 gras_assert1(union_data.closed,
379 "Please call gras_datadesc_declare_union_close on %s before sending it",
381 /* retrieve the field number */
382 field_num = union_data.selector(state, data);
384 gras_assert1(field_num > 0,
385 "union field selector of %s gave a negative value",
388 gras_assert3(field_num < gras_dynar_length(union_data.fields),
389 "union field selector of %s returned %d but there is only %d fields",
390 type->name, field_num, gras_dynar_length(union_data.fields));
392 /* Send the field number */
393 TRY(gras_dd_send_int(sock, field_num));
395 /* Send the content */
396 gras_dynar_get(union_data.fields, field_num, field);
397 sub_type = field->type;
400 field->pre(state,data);
402 TRY(gras_datadesc_send_rec(sock,state,refs, sub_type, data));
405 field->post(state,data);
410 case e_gras_datadesc_type_cat_ref: {
411 gras_dd_cat_ref_t ref_data;
413 void **ref=(void**)data;
418 ref_data = type->category.ref_data;
420 /* Detect the referenced type and send it to peer if needed */
421 sub_type = ref_data.type;
422 if (sub_type == NULL) {
423 sub_type = (*ref_data.selector)(state,data);
424 TRY(gras_dd_send_int(sock, sub_type->code));
427 /* Send the actual value of the pointer for cycle handling */
429 pointer_type = gras_datadesc_by_name("data pointer");
430 gras_assert(pointer_type);
433 TRY(gras_trp_chunk_send(sock, (char*)data,
434 pointer_type->size[GRAS_THISARCH]));
436 /* Send the pointed data only if not already sent */
437 if (*(void**)data == NULL) {
438 VERB0("Not sending NULL referenced data");
441 errcode = gras_dict_get_ext(refs,(char*)ref, sizeof(void*), &dummy);
442 if (errcode == mismatch_error) {
443 VERB1("Sending data referenced at %p", *ref);
444 TRY(gras_dict_set_ext(refs, (char*)ref, sizeof(void*), ref, NULL));
445 TRY(gras_datadesc_send_rec(sock,state,refs, sub_type, *ref));
447 } else if (errcode == no_error) {
448 VERB1("Not sending data referenced at %p (already done)", *ref);
456 case e_gras_datadesc_type_cat_array: {
457 gras_dd_cat_array_t array_data;
462 array_data = type->category.array_data;
464 /* determine and send the element count */
465 count = array_data.fixed_size;
467 count = array_data.dynamic_size(state,data);
468 gras_assert1(count >=0,
469 "Invalid (negative) array size for type %s",type->name);
470 TRY(gras_dd_send_int(sock, count));
473 /* send the content */
474 sub_type = array_data.type;
475 elm_size = sub_type->aligned_size[GRAS_THISARCH];
476 if (sub_type->category_code == e_gras_datadesc_type_cat_scalar) {
477 VERB1("Array of %ld scalars, send it in one shot",count);
478 TRY(gras_trp_chunk_send(sock, data,
479 sub_type->aligned_size[GRAS_THISARCH] * count));
481 for (cpt=0; cpt<count; cpt++) {
482 TRY(gras_datadesc_send_rec(sock,state,refs, sub_type, ptr));
490 gras_assert0(0, "Invalid type");
497 * gras_datadesc_send:
499 * Copy the data pointed by src and described by type to the socket
502 gras_error_t gras_datadesc_send(gras_socket_t *sock,
503 gras_datadesc_type_t *type,
506 gras_error_t errcode;
507 gras_cbps_t *state = NULL;
508 gras_dict_t *refs; /* all references already sent */
510 TRY(gras_dict_new(&refs));
511 TRY(gras_cbps_new(&state));
513 errcode = gras_datadesc_send_rec(sock,state,refs,type,(char*)src);
515 gras_dict_free(&refs);
516 gras_cbps_free(&state);
522 * gras_datadesc_recv_rec:
524 * Do the data reception job recursively.
526 * subsize used only to deal with vicious case of reference to dynamic array.
527 * This size is needed at the reference reception level (to allocate enough
528 * space) and at the array reception level (to fill enough room).
530 * Having this size passed as an argument of the recursive function is a crude
531 * hack, but I was told that working code is sometimes better than neat one ;)
534 gras_datadesc_recv_rec(gras_socket_t *sock,
537 gras_datadesc_type_t *type,
544 gras_error_t errcode;
546 gras_datadesc_type_t *sub_type;
548 VERB2("Recv a %s @%p", type->name, l_data);
551 switch (type->category_code) {
552 case e_gras_datadesc_type_cat_scalar:
553 if (type->size[GRAS_THISARCH] == type->size[r_arch]) {
554 TRY(gras_trp_chunk_recv(sock, (char*)l_data, type->size[r_arch]));
555 if (r_arch != GRAS_THISARCH)
556 TRY(gras_dd_convert_elm(type,1,r_arch, l_data,l_data));
559 ptr = malloc((size_t)type->size[r_arch]);
560 TRY(gras_trp_chunk_recv(sock, (char*)ptr, type->size[r_arch]));
561 if (r_arch != GRAS_THISARCH)
562 TRY(gras_dd_convert_elm(type,1,r_arch, ptr,l_data));
567 case e_gras_datadesc_type_cat_struct: {
568 gras_dd_cat_struct_t struct_data;
569 gras_dd_cat_field_t *field;
571 struct_data = type->category.struct_data;
573 gras_assert1(struct_data.closed,
574 "Please call gras_datadesc_declare_struct_close on %s before receiving it",
576 VERB1(">> Receive all fields of the structure %s",type->name);
577 gras_dynar_foreach(struct_data.fields, cpt, field) {
578 char *field_data = l_data + field->offset[GRAS_THISARCH];
580 sub_type = field->type;
582 TRY(gras_datadesc_recv_rec(sock,state,refs, sub_type,
586 VERB1("<< Received all fields of the structure %s", type->name);
591 case e_gras_datadesc_type_cat_union: {
592 gras_dd_cat_union_t union_data;
593 gras_dd_cat_field_t *field=NULL;
596 union_data = type->category.union_data;
598 gras_assert1(union_data.closed,
599 "Please call gras_datadesc_declare_union_close on %s before receiving it",
601 /* retrieve the field number */
602 TRY(gras_dd_recv_int(sock, r_arch, &field_num));
604 RAISE1(mismatch_error,
605 "Received union field for %s is negative", type->name);
606 if (field_num < gras_dynar_length(union_data.fields))
607 RAISE3(mismatch_error,
608 "Received union field for %s is %d but there is only %d fields",
609 type->name, field_num, gras_dynar_length(union_data.fields));
611 /* Recv the content */
612 gras_dynar_get(union_data.fields, field_num, field);
613 sub_type = field->type;
615 TRY(gras_datadesc_recv_rec(sock,state,refs, sub_type,
621 case e_gras_datadesc_type_cat_ref: {
624 gras_dd_cat_ref_t ref_data;
626 ref_data = type->category.ref_data;
628 /* Get the referenced type locally or from peer */
629 sub_type = ref_data.type;
630 if (sub_type == NULL) {
632 TRY(gras_dd_recv_int(sock, r_arch, &ref_code));
633 TRY(gras_datadesc_by_id(ref_code, &sub_type));
636 /* Get the actual value of the pointer for cycle handling */
638 pointer_type = gras_datadesc_by_name("data pointer");
639 gras_assert(pointer_type);
642 if (! (r_ref = malloc((size_t)pointer_type->size[r_arch])) )
644 TRY(gras_trp_chunk_recv(sock, (char*)r_ref,
645 pointer_type->size[r_arch]));
647 /* Receive the pointed data only if not already sent */
648 if (gras_dd_is_r_null(r_ref, pointer_type->size[r_arch])) {
649 VERB1("Not receiving data remotely referenced @%p since it's NULL",
651 *(void**)l_data = NULL;
655 errcode = gras_dict_get_ext(refs,
656 (char*)r_ref, pointer_type->size[r_arch],
660 if (errcode == mismatch_error) {
661 int subsubcount = -1;
662 void *l_referenced=NULL;
664 VERB2("Receiving a ref to '%s', remotely @%p",
665 sub_type->name, *(void**)r_ref);
666 if (sub_type->category_code == e_gras_datadesc_type_cat_array) {
667 /* Damn. Reference to a dynamic array. Allocating the size for it
668 is more complicated */
669 gras_dd_cat_array_t array_data = sub_type->category.array_data;
670 gras_datadesc_type_t *subsub_type;
672 subsubcount = array_data.fixed_size;
674 TRY(gras_dd_recv_int(sock, r_arch, &subsubcount));
676 subsub_type = array_data.type;
679 TRY(gras_dd_alloc_ref(refs,
680 subsub_type->size[GRAS_THISARCH] * subsubcount,
681 r_ref,pointer_type->size[r_arch],
682 (char**)&l_referenced));
684 TRY(gras_dd_alloc_ref(refs,sub_type->size[GRAS_THISARCH],
685 r_ref,pointer_type->size[r_arch],
686 (char**)&l_referenced));
689 TRY(gras_datadesc_recv_rec(sock,state,refs, sub_type,
690 r_arch,r_ref,pointer_type->size[r_arch],
691 (char*)l_referenced, subsubcount));
692 *(void**)l_data=l_referenced;
693 VERB3("'%s' remotely referenced at %p locally at %p",
694 sub_type->name, *(void**)r_ref, l_referenced);
696 } else if (errcode == no_error) {
697 VERB2("NOT receiving data remotely referenced @%p (already done, @%p here)",
698 *(void**)r_ref, *(void**)l_ref);
700 *(void**)l_data=*l_ref;
709 case e_gras_datadesc_type_cat_array: {
710 gras_dd_cat_array_t array_data;
715 array_data = type->category.array_data;
716 /* determine element count locally, or from caller, or from peer */
717 count = array_data.fixed_size;
721 TRY(gras_dd_recv_int(sock, r_arch, &count));
723 RAISE1(mismatch_error,
724 "Invalid (negative) array size for type %s",type->name);
726 /* receive the content */
727 sub_type = array_data.type;
728 if (sub_type->category_code == e_gras_datadesc_type_cat_scalar) {
729 VERB1("Array of %d scalars, get it in one shoot", count);
730 if (sub_type->aligned_size[GRAS_THISARCH] >=
731 sub_type->aligned_size[r_arch]) {
732 TRY(gras_trp_chunk_recv(sock, (char*)l_data,
733 sub_type->aligned_size[r_arch] * count));
734 if (r_arch != GRAS_THISARCH)
735 TRY(gras_dd_convert_elm(sub_type,count,r_arch, l_data,l_data));
737 ptr = malloc((size_t)sub_type->aligned_size[r_arch] * count);
738 TRY(gras_trp_chunk_recv(sock, (char*)ptr,
739 sub_type->size[r_arch] * count));
740 if (r_arch != GRAS_THISARCH)
741 TRY(gras_dd_convert_elm(sub_type,count,r_arch, ptr,l_data));
745 /* not scalar content, get it recursively (may contain pointers) */
746 elm_size = sub_type->aligned_size[GRAS_THISARCH];
747 VERB2("Receive a %d-long array of %s",count, sub_type->name);
750 for (cpt=0; cpt<count; cpt++) {
751 TRY(gras_datadesc_recv_rec(sock,state,refs, sub_type,
752 r_arch, NULL, 0, ptr,-1));
760 gras_assert0(0, "Invalid type");
764 type->recv(state,l_data);
770 * gras_datadesc_recv:
772 * Get an instance of the datatype described by @type from the @socket,
773 * and store a pointer to it in @dst
777 gras_datadesc_recv(gras_socket_t *sock,
778 gras_datadesc_type_t *type,
782 gras_error_t errcode;
783 gras_cbps_t *state = NULL; /* callback persistent state */
784 gras_dict_t *refs; /* all references already sent */
786 TRY(gras_dict_new(&refs));
787 TRY(gras_cbps_new(&state));
789 errcode = gras_datadesc_recv_rec(sock, state, refs, type,
793 gras_dict_free(&refs);
794 gras_cbps_free(&state);