commit: r315 - in trunk: daemon examples include libspread

[email protected]
Newsgroups gmane.network.spread.cvs
Message-ID <[email protected]>
Author: jonathan
Date: 2006-03-01 08:12:14 -0500 (Wed, 01 Mar 2006)
New Revision: 315

Modified:
   trunk/daemon/Changelog
   trunk/examples/simple_user.c
   trunk/examples/user.c
   trunk/include/sp.h
   trunk/include/sp_func.h
   trunk/libspread/fl.c
   trunk/libspread/sp.c
Log:
Include nice membership body parsing functions for applications to use. These
replace the old method of walking through the byte array in the membership body
to extract the vs_sets and gid. Deprecate the old functions SP_get_offset*
as they are extremely confusing to use. New functions SP_get_memb_info(), 
SP_get_vs_sets_info() and SP_get_vs_set_members() return strutures with the 
fields filled in.

Update flush library to use new api and update example programs.


Modified: trunk/daemon/Changelog
===================================================================
--- trunk/daemon/Changelog	2006-02-21 05:26:22 UTC (rev 314)
+++ trunk/daemon/Changelog	2006-03-01 13:12:14 UTC (rev 315)
@@ -1,3 +1,14 @@
+Wed Mar  1 08:05:32 2006  Jonathan Stanton  <[email protected]>
+
+	* libspread/sp.c, fl.c, include/sp.h, sp_func.h (SP_get_vs* memb_info()):
+	Add new membership parsing functions SP_get_memb_info(),
+	SP_get_vs_sets_info() and SP_get_vs_set_members() to analyze
+	the contents of the mesage body of membership messages. 
+	Deprecate old functions that just gave byte locations within 
+	body as they are too confusing. 
+	* examples/user.c, simple_user.c: Update example programs
+	to use new membership parsing functions.
+
 Wed Feb  8 03:44:23 2006  Jonathan Stanton  <[email protected]>
 
 	* arch.h: Add HAVE_STRERROR for Windows builds. This avoids

Modified: trunk/examples/simple_user.c
===================================================================
--- trunk/examples/simple_user.c	2006-02-21 05:26:22 UTC (rev 314)
+++ trunk/examples/simple_user.c	2006-03-01 13:12:14 UTC (rev 315)
@@ -102,9 +102,8 @@
 static	char		mess[102400];
 	char		sender[MAX_GROUP_NAME];
 	char		target_groups[100][MAX_GROUP_NAME];
-	group_id	*grp_id;
 	int		num_groups;
-	int		num_bytes;
+        membership_info memb_info;
 	int		service_type;
 	int16		mess_type;
 	int		endian_mismatch;
@@ -136,11 +135,14 @@
 
 	}else if( Is_membership_mess( service_type ) ){
 		/* A membership notification */
+                ret = SP_get_memb_info( mess, service_type, &memb_info );
+                if (ret < 0) {
+                        printf("BUG: membership message does not have valid body\n");
+                        SP_error( ret );
+                        exit( 1 );
+                }
 		if     ( Is_reg_memb_mess( service_type ) )
 		{
-			num_bytes = 0;
-			grp_id = (group_id *)&mess[num_bytes];
-			num_bytes += sizeof( group_id );
 			printf("received REGULAR membership ");
 			if( Is_caused_join_mess( service_type ) ) printf("caused by JOIN ");
 			if( Is_caused_leave_mess( service_type ) ) printf("caused by LEAVE ");
@@ -149,7 +151,7 @@
 				sender, num_groups );
 			for( i=0; i < num_groups; i++ )
 				printf("\t%s\n", &target_groups[i][0] );
-			printf("grp id is %d %d %d\n",grp_id->id[0], grp_id->id[1], grp_id->id[2] );
+			printf("grp id is %d %d %d\n",memb_info.gid.id[0], memb_info.gid.id[1], memb_info.gid.id[2] );
 		}else if( Is_transition_mess(   service_type ) ) {
 			printf("received TRANSITIONAL membership for group %s\n", sender );
 		}else if( Is_caused_leave_mess( service_type ) ){

Modified: trunk/examples/user.c
===================================================================
--- trunk/examples/user.c	2006-02-21 05:26:22 UTC (rev 314)
+++ trunk/examples/user.c	2006-03-01 13:12:14 UTC (rev 315)
@@ -80,6 +80,8 @@
 static  int     To_exit = 0;
 
 #define MAX_MESSLEN     102400
+#define MAX_VSSETS      10
+#define MAX_MEMBERS     100
 
 #ifdef	_REENTRANT
 
@@ -383,15 +385,13 @@
 
 static	char		 mess[MAX_MESSLEN];
 	char		 sender[MAX_GROUP_NAME];
-	char		 target_groups[100][MAX_GROUP_NAME];
-	group_id	*grp_id_ptr;
-        int32u          *num_vs_sets_ptr;
-        int32u           local_vs_set_offset;
-	char  		*vs_set_size_ptr;
-        int32u           vs_set_size;
-	char		*vs_set_members;
+	char		 target_groups[MAX_MEMBERS][MAX_GROUP_NAME];
+        membership_info  memb_info;
+        vs_set_info      vssets[MAX_VSSETS];
+        unsigned int     my_vsset_index;
+        int              num_vs_sets;
+        char             members[MAX_MEMBERS][MAX_GROUP_NAME];
 	int		 num_groups;
-	int		 num_bytes;
 	int		 service_type;
 	int16		 mess_type;
 	int		 endian_mismatch;
@@ -408,7 +408,7 @@
                 if ( (ret == GROUPS_TOO_SHORT) || (ret == BUFFER_TOO_SHORT) ) {
                         service_type = DROP_RECV;
                         printf("\n========Buffers or Groups too Short=======\n");
-                        ret = SP_receive( Mbox, &service_type, sender, 100, &num_groups, target_groups, 
+                        ret = SP_receive( Mbox, &service_type, sender, MAX_MEMBERS, &num_groups, target_groups, 
                                           &mess_type, &endian_mismatch, sizeof(mess), mess );
                 }
         }
@@ -435,47 +435,48 @@
 			sender, mess_type, endian_mismatch, num_groups, ret, mess );
 	}else if( Is_membership_mess( service_type ) )
         {
+                ret = SP_get_memb_info( mess, service_type, &memb_info );
+                if (ret < 0) {
+                        printf("BUG: membership message does not have valid body\n");
+                        SP_error( ret );
+                        exit( 1 );
+                }
 		if     ( Is_reg_memb_mess( service_type ) )
 		{
-			num_bytes           = SP_get_gid_offset_memb_mess();
-			grp_id_ptr          = (group_id *)&mess[num_bytes];
-			num_bytes           = SP_get_num_vs_sets_offset_memb_mess();
-			num_vs_sets_ptr     = (int32u *)&mess[num_bytes];
-                        local_vs_set_offset = SP_get_local_vs_set_offset_memb_mess(mess);
-			num_bytes           = SP_get_first_vs_set_offset_memb_mess();
-                        num_bytes          += SP_get_vs_set_size_offset_vs_set();
-                        vs_set_size_ptr     = &mess[num_bytes];
-                        num_bytes          += SP_get_vs_set_members_offset_vs_set();
-			vs_set_members      = &mess[num_bytes];
-
 			printf("Received REGULAR membership for group %s with %d members, where I am member %d:\n",
 				sender, num_groups, mess_type );
 			for( i=0; i < num_groups; i++ )
 				printf("\t%s\n", &target_groups[i][0] );
-			printf("grp id is %d %d %d\n",grp_id_ptr->id[0], grp_id_ptr->id[1], grp_id_ptr->id[2] );
+			printf("grp id is %d %d %d\n",memb_info.gid.id[0], memb_info.gid.id[1], memb_info.gid.id[2] );
 
 			if( Is_caused_join_mess( service_type ) )
 			{
-				printf("Due to the JOIN of %s\n", vs_set_members );
+				printf("Due to the JOIN of %s\n", memb_info.changed_member );
 			}else if( Is_caused_leave_mess( service_type ) ){
-				printf("Due to the LEAVE of %s\n", vs_set_members);
+				printf("Due to the LEAVE of %s\n", memb_info.changed_member );
 			}else if( Is_caused_disconnect_mess( service_type ) ){
-				printf("Due to the DISCONNECT of %s\n", vs_set_members );
+				printf("Due to the DISCONNECT of %s\n", memb_info.changed_member );
 			}else if( Is_caused_network_mess( service_type ) ){
-				printf("Due to NETWORK change with %u VS sets\n", *num_vs_sets_ptr);
-                                for( i = 0; i < *num_vs_sets_ptr; ++i )
+				printf("Due to NETWORK change with %u VS sets\n", memb_info.num_vs_sets);
+                                num_vs_sets = SP_get_vs_sets_info( mess, &vssets[0], MAX_VSSETS, &my_vsset_index );
+                                if (num_vs_sets < 0) {
+                                        printf("BUG: membership message has more then %d vs sets. Recompile with larger MAX_VSSETS\n", MAX_VSSETS);
+                                        SP_error( num_vs_sets );
+                                        exit( 1 );
+                                }
+                                for( i = 0; i < num_vs_sets; i++ )
                                 {
-                                        if( 0 != i )
-                                        {
-                                                vs_set_size_ptr = vs_set_members;
-                                                vs_set_members += sizeof(int32u);
+                                        printf("%s VS set %d has %u members:\n",
+                                               (i  == my_vsset_index) ?
+                                               ("LOCAL") : ("OTHER"), i, vssets[i].num_members );
+                                        ret = SP_get_vs_set_members(mess, &vssets[i], members, MAX_MEMBERS);
+                                        if (ret < 0) {
+                                                printf("VS Set has more then %d members. Recompile with larger MAX_MEMBERS\n", MAX_MEMBERS);
+                                                SP_error( ret );
+                                                exit( 1 );
                                         }
-                                        memcpy( &vs_set_size, vs_set_size_ptr, sizeof(int32u) );
-                                        printf("%s VS set %d has %d members:\n",
-                                               (vs_set_size_ptr - mess == local_vs_set_offset) ?
-                                               ("LOCAL") : ("OTHER"), i, vs_set_size );
-                                        for( j = 0; j < vs_set_size; j++, vs_set_members += MAX_GROUP_NAME )
-                                                printf("\t%s\n", vs_set_members );
+                                        for( j = 0; j < vssets[i].num_members; j++ )
+                                                printf("\t%s\n", members[j] );
                                 }
 			}
 		}else if( Is_transition_mess(   service_type ) ) {

Modified: trunk/include/sp.h
===================================================================
--- trunk/include/sp.h	2006-02-21 05:26:22 UTC (rev 314)
+++ trunk/include/sp.h	2006-03-01 13:12:14 UTC (rev 315)
@@ -148,7 +148,18 @@
 	int32		id[3];
 } group_id;
 
+typedef struct dummy_vs_set_info {
+        unsigned int num_members;
+        unsigned int members_offset;  /* offset from beginning of msg body*/
+} vs_set_info;
 
+typedef struct dummy_membership_info {
+        group_id     gid;
+        char         changed_member[MAX_GROUP_NAME]; /* filled in on CAUSED_BY_JOIN, CAUSED_BY_LEAVE or CAUSED_BY_DISCONNECT only */
+        unsigned int num_vs_sets;
+        vs_set_info  my_vs_set;
+} membership_info;
+
 #include "sp_events.h"
 #include "sp_func.h"
 #ifdef __cplusplus

Modified: trunk/include/sp_func.h
===================================================================
--- trunk/include/sp_func.h	2006-02-21 05:26:22 UTC (rev 314)
+++ trunk/include/sp_func.h	2006-03-01 13:12:14 UTC (rev 315)
@@ -93,21 +93,31 @@
 			 int16 *mess_type, int *endian_mismatch,
 			 scatter *scat_mess );
 
-/* FIXME: We could make something much friendlier here. */
-/* returns offset in memb. message of gid (group id), num_vs_sets,
- * offset to local vs_set, and first vs_set */
-int     SP_get_gid_offset_memb_mess(void);
-int     SP_get_num_vs_sets_offset_memb_mess(void);
-int     SP_get_first_vs_set_offset_memb_mess(void);
-/* returns offset into the buffer for a vs_set of the number of
- * members in the vs_set. */
-int     SP_get_vs_set_size_offset_vs_set(void);
-int     SP_get_vs_set_members_offset_vs_set(void);
-/* returns value from regular membership message */
-int     SP_get_offset_to_local_vs_set_offset(void);
-int     SP_get_local_vs_set_offset_memb_mess( char *reg_memb_mess );  
-int     SP_get_local_vs_set_offset_memb_scat( const scatter *reg_memb_scat );
+/* get membership info from a message */
+int     SP_get_memb_info( const char *memb_mess, 
+                          const service service_type,
+                          membership_info *memb_info);
+int     SP_get_vs_sets_info( const char *memb_mess,
+                             vs_set_info *vs_sets, 
+                             int num_vs_sets,
+                             unsigned int *my_vs_set_index);
+int     SP_get_vs_set_members( const char *memb_mess, 
+                               const vs_set_info *vs_set, 
+                               char member_names[][MAX_GROUP_NAME],
+                               int member_names_len);
 
+int     SP_scat_get_memb_info( const scatter *memb_mess_scat, 
+                               const service service_type,
+                               membership_info *memb_info);
+int     SP_scat_get_vs_sets_info( const scatter *memb_mess_scat, 
+                                  vs_set_info *vs_sets, 
+                                  int num_vs_sets,
+                                  unsigned int *my_vs_set_index);
+int     SP_scat_get_vs_set_members( const scatter *memb_mess_scat, 
+                                    const vs_set_info *vs_set, 
+                                    char member_names[][MAX_GROUP_NAME],
+                                    int member_names_len);
+
 int	SP_poll( mailbox mbox );
 
 int	SP_equal_group_ids( group_id g1, group_id g2 );

Modified: trunk/libspread/fl.c
===================================================================
--- trunk/libspread/fl.c	2006-02-21 05:26:22 UTC (rev 314)
+++ trunk/libspread/fl.c	2006-03-01 13:12:14 UTC (rev 315)
@@ -1432,13 +1432,12 @@
 	}
       }
     } else if (Is_reg_memb_mess(*m->serv_type)) {
+      membership_info m_info;
       /* if its a regular membership message read the group id out of the msg */
       assert(m->ret >= sizeof(group_id) + sizeof(int) + MAX_GROUP_NAME);    /* ensure received */
-      err = scatp_begin(&pos, scat);
-      assert(err == 0);
-      err = scatp_jforward(&pos, SP_get_gid_offset_memb_mess());
-      assert(err == SP_get_gid_offset_memb_mess());
-      err = scatp_cpy1((char*) &m->dst_gid, &pos, sizeof(group_id));       /* copy out new gid */
+      err = SP_scat_get_memb_info( scat, *m->serv_type, &m_info);
+
+      memcpy(&m->dst_gid, &m_info.gid, sizeof(group_id) );
       assert(err == sizeof(group_id));                             /* already endian corrected */
       DEBUG(std_stkfprintf(stderr, 0, "Recvd a SP reg memb mess: New SP gid is %d %d %d\n", 
 			   m->dst_gid.id[0], m->dst_gid.id[1], m->dst_gid.id[2]));
@@ -1978,38 +1977,22 @@
 
 static char*
 determine_leavers(stdhash *leavers, view *last_sp_view, gc_recv_mess *m) {
-  char     delta[MAX_GROUP_NAME], *delta_ptr = delta, *ret = 0;
+  char     *ret = 0;
   int      i;
-  scatter *scat;
-  scatp    pos;
   long     err;
-  stduint32   offset;
   stduint32   num_membs;
+  membership_info       m_info;
+  char    (*members)[MAX_GROUP_NAME];
 
   DEBUG(std_stkfprintf(stderr, 1, "determine_leavers: old gid %d %d %d -> new gid %d %d %d\n", 
 		       last_sp_view->gid.id[0], last_sp_view->gid.id[1], 
 		       last_sp_view->gid.id[2], m->dst_gid.id[0], m->dst_gid.id[1],
 		       m->dst_gid.id[2]));
 
-  get_scat_info(m, &i, &scat);                                   /* i is a dummy variable here */
+  err = SP_scat_get_memb_info( m->scat_mess, *m->serv_type, &m_info);
+  assert(err == 1);
+  num_membs = m_info.my_vs_set.num_members;
 
-  err = scatp_set(&pos, scat, SP_get_offset_to_local_vs_set_offset(), SEEK_SET);
-  assert(err == 0);
-
-  err = scatp_cpy1((char*) &offset, &pos, sizeof(stduint32));
-  assert(err == sizeof(stduint32));
-
-  offset += SP_get_first_vs_set_offset_memb_mess();                     /* get local VS offset */
-
-  err = scatp_set(&pos, scat, offset, SEEK_SET);
-  assert(err == offset);
-
-  err = scatp_cpy1((char*) &num_membs, &pos, sizeof(stduint32));         /* read out num membs */
-  assert(err == sizeof(stduint32));
-
-  err = scatp_jforward(&pos, sizeof(stduint32));                          /* advance to vs_set */
-  assert(err == sizeof(stduint32));
-
   if (!Is_caused_network_mess(*m->serv_type)) {               /* join, leave, or disconnection */
     assert(num_membs == 1);
 
@@ -2018,13 +2001,11 @@
     stdhash_construct(leavers, sizeof(char*), 0, 
 		      group_name_ptr_cmp, group_name_ptr_hashcode, 0);
     
-    err = scatp_cpy1(delta, &pos, MAX_GROUP_NAME);      /* read out joiner/leaver/disconnector */
-    assert(err == MAX_GROUP_NAME);
-    delta[MAX_GROUP_NAME - 1] = 0;                                  /* ensure null termination */
 
     if ((ret = (char*) malloc(MAX_GROUP_NAME)) == 0)
       stderr_abort("(%s, %d): malloc(%d)\n", __FILE__, __LINE__, MAX_GROUP_NAME);
-    strncpy(ret, delta, MAX_GROUP_NAME);                    /* strndup delta member for return */
+    memcpy( ret, m_info.changed_member, MAX_GROUP_NAME); /* read out joiner/leaver/disconnector */
+    ret[MAX_GROUP_NAME - 1] = 0;                                  /* ensure null termination */
 
     DEBUG(std_stkfprintf(stderr, 0, "delta memb: '%s'\n", delta));
     if (!Is_caused_join_mess(*m->serv_type)) {                   /* insert leaver into leavers */
@@ -2037,13 +2018,14 @@
     stdhash_copy_construct(leavers, &last_sp_view->orig_membs);     /* copy last SP membership */
 
     DEBUG(std_stkfprintf(stderr, 0, "Num_membs %u\n", num_membs));
+    if ((members = (char (*)[MAX_GROUP_NAME])malloc(num_membs * sizeof( *members ) ) ) == 0)
+      stderr_abort("(%s, %d): malloc(%d)\n", __FILE__, __LINE__, num_membs * MAX_GROUP_NAME);
+    err = SP_scat_get_vs_set_members( m->scat_mess, &m_info.my_vs_set, members, num_membs);
+    assert(err == num_membs);
     for (i = 0; i < num_membs; ++i) {        /* remove members that came with me: leaves who left */
-      err = scatp_adv_cpy1(&delta_ptr, &pos, MAX_GROUP_NAME, 0, 1);
-      assert(err == MAX_GROUP_NAME);
-
-      delta[MAX_GROUP_NAME - 1] = 0;                                /* ensure null termination */
+      members[i][MAX_GROUP_NAME -1] = 0;
       err = stdhash_size(leavers);
-      stdhash_erase_key(leavers, &delta_ptr);
+      stdhash_erase_key(leavers, &(members[i][0]));
       assert(err == stdhash_size(leavers) + 1);
     }
   }

Modified: trunk/libspread/sp.c
===================================================================
--- trunk/libspread/sp.c	2006-02-21 05:26:22 UTC (rev 314)
+++ trunk/libspread/sp.c	2006-03-01 13:12:14 UTC (rev 315)
@@ -67,9 +67,23 @@
 #include "sess_types.h"
 #include "scatter.h"
 #include "alarm.h"
-#include "sp_func.h"
 #include "acm.h"
 
+/* SP functions need these types, but internal headers do not */
+typedef struct dummy_vs_set_info {
+        unsigned int num_members;
+        unsigned int members_offset;  /* offset from beginning of msg body*/
+} vs_set_info;
+
+typedef struct dummy_membership_info {
+        group_id     gid;
+        char         changed_member[MAX_GROUP_NAME]; /* filled in on CAUSED_BY_JOIN, CAUSED_BY_LEAVE or CAUSED_BY_DISCONNECT only */
+        unsigned int num_vs_sets;
+        vs_set_info  my_vs_set;
+} membership_info;
+
+#include "sp_func.h"
+
 enum sp_sess_state {
     SESS_UNUSED,
     SESS_ACTIVE,
@@ -1764,6 +1778,9 @@
 	else return( 0 );
 }
 
+/* get membership info from a message */
+
+/* internal only for now -- maybe static? */
 int     SP_get_gid_offset_memb_mess(void) 
 {
         return 0;
@@ -1794,6 +1811,199 @@
         return sizeof(int32u);
 }
 
+/* public functions */
+int     SP_get_memb_info( const char *memb_mess, 
+                          const service service_type,
+                          membership_info *memb_info) 
+{
+        int32u my_vs_offset;
+
+        if ( !Is_membership_mess( service_type ) )
+                return(ILLEGAL_MESSAGE);
+
+        memcpy( &memb_info->gid, &memb_mess[SP_get_gid_offset_memb_mess()], sizeof(group_id));
+        
+        if ( Is_reg_memb_mess( service_type ) )
+        {
+                if( Is_caused_join_mess( service_type ) || 
+                    Is_caused_leave_mess( service_type ) ||
+                    Is_caused_disconnect_mess( service_type ) )
+                {
+                        memcpy(memb_info->changed_member, &memb_mess[SP_get_first_vs_set_offset_memb_mess()+SP_get_vs_set_members_offset_vs_set()], MAX_GROUP_NAME);
+                } else if( Is_caused_network_mess( service_type ) )
+                {
+                        memset(memb_info->changed_member, 0, MAX_GROUP_NAME);
+                }
+                memb_info->num_vs_sets = *((int32u *)&memb_mess[SP_get_num_vs_sets_offset_memb_mess()]);
+                my_vs_offset = *((int32u *)&memb_mess[SP_get_offset_to_local_vs_set_offset()]);
+                my_vs_offset += SP_get_first_vs_set_offset_memb_mess();
+                memb_info->my_vs_set.num_members = *((int32u *)&memb_mess[my_vs_offset + SP_get_vs_set_size_offset_vs_set()]);
+                memb_info->my_vs_set.members_offset  = my_vs_offset + SP_get_vs_set_members_offset_vs_set();
+        } else {
+                /* Is transitional */
+                memb_info->num_vs_sets = 0;
+                memb_info->my_vs_set.num_members = 0;
+                memb_info->my_vs_set.members_offset  = 0;
+        }
+        return( 1 );
+}
+
+
+int     SP_get_vs_sets_info( const char *memb_mess, 
+                             vs_set_info *vs_sets, 
+                             int num_vs_sets,
+                             unsigned int *my_vs_set_index)
+{
+        unsigned int actual_num_vs_sets;
+        unsigned int memb_offset, local_vs_set_offset;
+        int i;
+
+        actual_num_vs_sets = *((int32u *)&memb_mess[SP_get_num_vs_sets_offset_memb_mess()]);
+
+        if (num_vs_sets < actual_num_vs_sets)
+                return(BUFFER_TOO_SHORT);
+
+        local_vs_set_offset = *((int32u *)&memb_mess[SP_get_offset_to_local_vs_set_offset()]);
+        local_vs_set_offset += SP_get_first_vs_set_offset_memb_mess();
+        memb_offset = SP_get_first_vs_set_offset_memb_mess();
+
+        for ( i=0; i < num_vs_sets; i++, vs_sets++ )
+        {
+                if (memb_offset == local_vs_set_offset)
+                        *my_vs_set_index = i;
+                vs_sets->num_members = *(int32u *)&memb_mess[memb_offset];
+                memb_offset += SP_get_vs_set_members_offset_vs_set();
+                vs_sets->members_offset = memb_offset;
+                memb_offset += (vs_sets->num_members * MAX_GROUP_NAME) ;
+        }
+        return ( actual_num_vs_sets );
+}
+
+int     SP_get_vs_set_members( const char *memb_mess, 
+                               const vs_set_info *vs_set, 
+                               char member_names[][MAX_GROUP_NAME],
+                               int member_names_len)
+{
+        int i;
+        const char *members_ptr;
+
+        if (member_names_len < vs_set->num_members)
+                return(BUFFER_TOO_SHORT);
+
+        members_ptr = &memb_mess[vs_set->members_offset];
+        for ( i=0; i < vs_set->num_members; i++, members_ptr += MAX_GROUP_NAME )
+        {
+                memcpy(&member_names[i][0], members_ptr, MAX_GROUP_NAME);
+        }
+        return( vs_set->num_members );
+}
+
+static  void    scat_read(void *dest_ptr, const scatter *msg, int start_byte, int copy_size)
+{
+        int scat, scat_index, bytes_index, bytes_to_copy;
+
+        scat = 0;
+        scat_index = 0;
+        while(start_byte > msg->elements[scat].len) 
+        {
+                scat++;
+                start_byte -= msg->elements[scat].len;
+        }
+        scat_index = start_byte;
+        for( bytes_index = 0; bytes_index < copy_size ; bytes_index += bytes_to_copy )
+        {
+                bytes_to_copy = copy_size - bytes_index;
+                if( bytes_to_copy > msg->elements[scat].len - scat_index )
+                        bytes_to_copy = msg->elements[scat].len - scat_index;
+                memcpy( dest_ptr, &(msg->elements[scat].buf[scat_index]), bytes_to_copy );
+                if( bytes_to_copy == msg->elements[scat].len - scat_index )
+                {
+                        scat_index = 0;
+                        ++scat;
+                } else {
+                        scat_index = bytes_to_copy;
+                }
+        }
+}
+
+int     SP_scat_get_memb_info( const scatter *memb_mess_scat, 
+                               const service service_type,
+                               membership_info *memb_info)
+{
+        int32u my_vs_offset;
+
+        if ( !Is_membership_mess( service_type ) )
+                return(ILLEGAL_MESSAGE);
+
+        scat_read( &memb_info->gid, memb_mess_scat, 0, sizeof( group_id ));
+        
+        if ( Is_reg_memb_mess( service_type ) )
+        {
+                if( Is_caused_join_mess( service_type ) || 
+                    Is_caused_leave_mess( service_type ) ||
+                    Is_caused_disconnect_mess( service_type ) )
+                {
+                        scat_read(memb_info->changed_member, memb_mess_scat, SP_get_first_vs_set_offset_memb_mess()+SP_get_vs_set_members_offset_vs_set(), MAX_GROUP_NAME );
+                } else if( Is_caused_network_mess( service_type ) )
+                {
+                        memset(memb_info->changed_member, 0, MAX_GROUP_NAME);
+                }
+                scat_read( &(memb_info->num_vs_sets), memb_mess_scat, SP_get_num_vs_sets_offset_memb_mess(), sizeof( int32u ) );
+                scat_read( &my_vs_offset, memb_mess_scat, SP_get_offset_to_local_vs_set_offset(), sizeof( int32u ));
+                my_vs_offset += SP_get_first_vs_set_offset_memb_mess();
+                scat_read( &(memb_info->my_vs_set.num_members), memb_mess_scat,  my_vs_offset + SP_get_vs_set_size_offset_vs_set(), sizeof( int32u ) );
+                memb_info->my_vs_set.members_offset  = my_vs_offset + SP_get_vs_set_members_offset_vs_set();
+        } else {
+                /* Is transitional */
+                memb_info->num_vs_sets = 0;
+                memb_info->my_vs_set.num_members = 0;
+                memb_info->my_vs_set.members_offset  = 0;
+        }
+        return( 1 );
+}
+int     SP_scat_get_vs_sets_info( const scatter *memb_mess_scat, 
+                                  vs_set_info *vs_sets, 
+                                  int num_vs_sets,
+                                  unsigned int *my_vs_set_index)
+{
+        unsigned int actual_num_vs_sets;
+        unsigned int memb_offset, local_vs_set_offset;
+        int i;
+
+        scat_read( &actual_num_vs_sets, memb_mess_scat, SP_get_num_vs_sets_offset_memb_mess(), sizeof( int32u ) );
+
+        if (num_vs_sets < actual_num_vs_sets)
+                return(BUFFER_TOO_SHORT);
+
+        scat_read( &local_vs_set_offset, memb_mess_scat, SP_get_offset_to_local_vs_set_offset(), sizeof( int32u ) );
+        local_vs_set_offset += SP_get_first_vs_set_offset_memb_mess();
+        memb_offset = SP_get_first_vs_set_offset_memb_mess();
+
+        for ( i=0; i < num_vs_sets; i++, vs_sets++ )
+        {
+                if (memb_offset == local_vs_set_offset)
+                        *my_vs_set_index = i;
+
+                scat_read(&(vs_sets->num_members), memb_mess_scat, memb_offset, sizeof( int32u ) );
+                memb_offset += SP_get_vs_set_members_offset_vs_set();
+                vs_sets->members_offset = memb_offset;
+                memb_offset += (vs_sets->num_members * MAX_GROUP_NAME) ;
+        }
+        return ( actual_num_vs_sets );
+}
+int     SP_scat_get_vs_set_members( const scatter *memb_mess_scat, 
+                                    const vs_set_info *vs_set, 
+                                    char member_names[][MAX_GROUP_NAME],
+                                    int member_names_len)
+{
+        if (member_names_len < vs_set->num_members)
+                return(BUFFER_TOO_SHORT);
+
+        scat_read( member_names, memb_mess_scat, vs_set->members_offset, vs_set->num_members * MAX_GROUP_NAME);
+        return( vs_set->num_members );
+}
+
+
 int     SP_get_local_vs_set_offset_memb_mess( char *reg_memb_mess )
 {
         int32u offset;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.