
/****************************************************************************/
/*                                                                          */
/*      NNstat -- Internet Statistics Collection Package                    */
/*                                                                          */
/*            Written by: Bob Braden & Annette DeSchon                      */
/*            USC Information Sciences Institute                            */
/*            Marina del Rey, California                                    */
/*                                                                          */
/*      Copyright (c) 1991 University of Southern California.               */
/*      All rights reserved.                                                */
/*                                                                          */
/*      Redistribution and use in source and binary forms are permitted     */
/*      provided that the above copyright notice and this paragraph are     */
/*      duplicated in all such forms and that any documentation,            */
/*      advertising materials, and other materials related to such          */
/*      distribution and use acknowledge that the software was              */
/*      developed by the University of Southern California, Information     */
/*      Sciences Institute.  The name of the University may not be used     */
/*      to endorse or promote products derived from this software           */
/*      without specific prior written permission.                          */
/*      THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR        */
/*      IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED      */
/*      WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR          */
/*      PURPOSE.                                                            */
/*                                                                          */
/****************************************************************************/

static char rcsid[]=
  "$Header: /tmp_mnt/r/jove_staff3/mogul/alpha/code/NNstat/RCS/sobjma2.c,v 1.5 1993/08/31 22:45:04 mogul Exp $";
 
/*
 *   Internet Statistics Facility -- Generic Operation Routines
 *
 *    This module defines a set of operations on a particular class
 *    of Statistical Objects.  In the spirit of object-oriented
 *    programming and information-hiding, these routines are
 *    the only ones which know about the internal structre of the
 *    Statistical Object (SOBJ) data structures.
 *
 */
 
/*  CHANGES:
 *    21Mar89 Merit: Fix bug in Delete_MA2()
 *      Rel 2.4:
 *    01Nov89 RTB: Change calling sequence for MA_list().
 *     Rel 3.0:
 *    ISI:  Add List routine.
 *    ISI:  Remove redundant NL when no bins.  
  *   ISI:  Read/List output to logfp, not stdout. 
  *   Aug93/DECWRL: Alpha/OSF port
 *
 */
 
/*
 *
 *          MA2 Object Class == matrix-all2
 *
 *    Objects of the MA2 class are frequency matrices for pairs of 32-bit ,
 *    values using dynamically-created bins.  Thus, every distinct value pair
 *    is counted; there are no defaults. 
 *
 *    MA2 objects are implemented using a chained hash table.  The bins are
 *    in a space which is allocated dynamically in pages of 4K bytes. 
 *    Unlike MA objects, MA2 objects do not keep a sorted list of bins.
 *    
 *
 *
 *    Operations on MA2 class objects:
 *
 *       SOBJ *Make_MA2( object-name, &sym-switch, 1/0, datalen1, datalen2 )
 *
 *       Write_MA2( (SOBJ *) SOBJ-ptr, &value1, len1, &value2, &len2)
 *
 *       Read_MA2( fd, (SOBJ *) SOBJ-ptr )
 *
 *       Delete_MA2( (SOBJ *) SOBJ-ptr )
 *
 *       Clear_MA2( (SOBJ *) SOBJ-ptr )
 *
 *       List_MA2( fd, OBJp )  [uses List_MA]
 *
 */
 
#include <stdio.h>
#include <sys/types.h>
#include "stat.h"
#include "sobj.h"


    /*     
     *  Define MA2 object data structures
     *
     */

#define MAX_PAGES 8   /* Max no. 256-entry (4K byte) bin pages */
#define HASHsize 255
    
#define BIN_pwr2  8   /* log2( number bins per extension page )*/
#define BIN_PAGE  (1<<BIN_pwr2)   /* Number bins per extension page */
#define BIN_MASK  BIN_PAGE -1
#define BIN_MAX   BIN_PAGE*MAX_PAGES

        /* Map bin index into bin address */    
#define BIN_ADDR(maap, i) (MAE *) ( \
             (char *) maap->maa_page[((i)>>BIN_pwr2)]+ \
             maap->maa_esize*((i)&BIN_MASK) ) 
                
        /* Hash Function ==  simple congruential hash */    
#define hashfunc( V, S)  V%S                                 

        /* Bin format */
    
#define MAE struct ma_bin_element
MAE {
    MAE  *mae_next ;    /* Hash chain field */
    MADE  mae_made ;    /* Rest... count, value concatentation, time */
        /* We chose an internal representation which is the SAME as the
         * network representation, to simplify READ operations.
         */
};

#define mae_value  mae_made.mad_value  /* leng1+leng2 padded to fullword */
#define mae_count  mae_made.mad_count
#define mae_time   mae_made.mad_time


    /* Object Area format */
#define MAA struct stat_xarea
MAA {
    SOBJ   maa_sobj ;         /* SOBJ = standard root of area */
    
    short  maa_symswitch;     /* Symmetry switch */
    short  maa_esize ;        /* Size of element */
    short  maa_hashs ;        /* Size of hash table (#shorts) */
    short  maa_maxchain ;     /* Max chain length  */
    
    int    maa_currbin ;      /* Index of first available bin */
    MAE  **maa_hashp ;        /* Ptr to hash table */
    MAE   *maa_page[MAX_PAGES] ;  /* Ptrs to bin segments */
    /*----------------------------------------------------
                     Hash Table... 
          (a vector of pointers: the heads of hash chains)
    /*----------------------------------------------------*/
} ;


SOBJ *Make_MA2() ;
boolean  Write_MA2(), Read_MA2();
void  Clear_MA2(), Delete_MA2();
extern void List_MA();

    /* Transfer Vector 
     *   for external access to these routines
     */
GENERICOP MA2op = { Make_MA2, Write_MA2, Read_MA2, Clear_MA2, Delete_MA2, List_MA};


SOBJ *Make_MA2(objname, parmp, nparms, datalen1, datalen2)
    char *objname;
    int  *parmp;
    int   nparms, datalen1, datalen2;
    {
    register MAA *maap ;
    register int i ;
    int hsize = HASHsize ;
    int vsize = ALIGN(datalen1+datalen2);

    if (datalen1 > MAX_DLENG || datalen2 > MAX_DLENG){
        SOBJ_error = "MA: Field size too big";
        return(NULL);
    }
    if (datalen2 == 0) {                           /*FIX4Jan88 */
        SOBJ_error = "MA: Missing second field";   /*FIX4Jan88 */
        return(NULL);                              /*FIX4Jan88 */
    }
    
    i = (sizeof(MAE *))*hsize + sizeof ( MAA )  ;
    if ((maap = (MAA *) malloc(i)) == NULL) {
        SOBJ_error = "MA: Memory overflow" ;
        return(NULL) ;
    }
    bzero(maap, i) ;  /* Clear entire area... */
        
    maap->maa_esize = PTRALIGN(sizeof(MAE) - 2*MAX_DLENG + vsize);
    maap->maa_hashs = hsize ;
    maap->maa_currbin = 0 ; 
    maap->maa_symswitch = (nparms && *parmp && (datalen1 == datalen2)); 
        
    maap->maa_hashp =  (MAE **) ((char *) maap + sizeof(MAA) ) ;
            /* Start of hash table */
                        
    return(&maap->maa_sobj) ;  /* SUCCESS!! */
}   /* Make_MA2() */  
     
     
boolean Write_MA2( maap, Valuep1, L1, Valuep2, L2)
    register MAA *maap ;
    char *Valuep1, *Valuep2 ;
    int L1, L2; 
    {
    register MAE *maep, **thp ;
    register int L = L1+L2;
    MAE **hp;
    int n;
    u_int32 HValue;      
    union v_union Values;
    
    ZERO_UNION(&Values); ZERO_UN2(&Values);
    if (maap->maa_symswitch && CLC(Valuep1, Valuep2, L1) > 0) {
            /* Note that symswitch can only be set
               if L1 == L2 */
        bcopy(Valuep2, Values.v_byte, L1);
        bcopy(Valuep1, &Values.v_byte[L1], L1);
    }
    else {
        bcopy(Valuep1, Values.v_byte, L1);
        bcopy(Valuep2, &Values.v_byte[L1], L2);
    }
    

    HValue = SUM_UN2(&Values) + SUM_UNION(&Values);

    hp = thp = &maap->maa_hashp[ hashfunc(HValue, maap->maa_hashs) ];
    
    n = 0;  
    while (maep = *thp) {
        if ((WORD1(maep->mae_value) == WORD1(&Values))  
            && (L <= sizeof(int32) ||
              ((WORD2(maep->mae_value) == WORD2(&Values))
               && (L <= 2*sizeof(int32) ||
                 ((WORD3(maep->mae_value) == WORD3(&Values))
                  && (L <= 3*sizeof(int32) ||
                    ((WORD4(maep->mae_value) == WORD4(&Values))
            )))))))                                  /* Lisp, anyone? */
            {
                /* Found bin, so increment count */
            maep->mae_count++ ;
            maep->mae_time = CurrTime ;
            return(0) ;
         }
        thp = &maep->mae_next ;
        n++;
    }
    
        /* No such bin... Must create new one and
         *   attach it to end of hash list...
         */      
    if (maap->maa_currbin >= BIN_MAX ) {
            /* We have run out of new bins. Count as orphan. */
        ((SOBJ *) maap)->sob_orphans++ ;
        return(0) ;
    }       
        if ((maep = BIN_ADDR( maap, maap->maa_currbin)) == NULL) {
            /*  must obtain new page */
        maep  = (MAE *) malloc(maap->maa_esize * BIN_PAGE) ;
        if (maep == NULL) {
            /* Cannot get new bin page. Count as orphan. */
            ((SOBJ *) maap)->sob_orphans++ ;
            return(0) ;
        }
        maap->maa_page[(maap->maa_currbin >> BIN_pwr2)] = maep ;
    }
                
        /*
         *   Fill in new bin 
         */
    WORD1(maep->mae_value) = WORD1(&Values);  
    if (L > sizeof(int32)) {
        WORD2(maep->mae_value) = WORD2(&Values);
        if (L > 2*sizeof(int32)) {
            WORD3(maep->mae_value) = WORD3(&Values); 
            if (L > 3*sizeof(int32)) {
                WORD4(maep->mae_value) = WORD4(&Values);
            }
        }
    }
    maep->mae_count = 1 ;
    maep->mae_time = CurrTime ;
    maap->maa_currbin++ ;

        /* Prepend new bin to hash chain  */
    maep->mae_next = *hp;  
    *hp = maep ;
        /* Measure the health of the hash chains... 
         *  record maximum length of any chain
         */
    if (++n > maap->maa_maxchain) maap->maa_maxchain = n;
    return(0) ; 
} /* Write_MA2() */
    
        
void Delete_MA2( maap )
    MAA *maap ;
    {
    Clear_MA2(maap) ;
    free(maap) ;
} /* Delete_MA2() */


void Clear_MA2( maap )
    register MAA *maap ;
    {       
    register int i ;
    register MAE *maep ;
    
    bzero(maap->maa_hashp, (sizeof(MAE *))*maap->maa_hashs);
    maap->maa_currbin = maap->maa_maxchain = 0;  /*FIX4Jan88*/
    
    for (i = 0;i<MAX_PAGES;i++) {
        if (maep = maap->maa_page[i]) {
            free(maep) ;
            maap->maa_page[i] = NULL ;
        }
        else break ;
    }
} /* Clear_MA2() */
    
#define OUTSIZE 256 

boolean  Read_MA2(fd, maap)
    XDR *fd;
    register MAA  *maap ;
    {
    register MAE *maep ;
    register int limit;
    int i, left ;
    char outp[OUTSIZE];
    int  outl = (ISTTY(fd))?OUTSIZE:0;
    MADE made;
    OBJ  xobj;
    EnumHandle enumh = 0; 
    extern MakeCols();
        
    bzero(made.mad_value, 2*MAX_DLENG);
    if (outl)
        enumh = ExistEnumObj(maap->maa_sobj.sob_name);
        
    if (!PutReadResp(fd, MAr_SIZE(maap->maa_currbin, 
                maap->maa_sobj.sob_dleng+maap->maa_sobj.sob_dleng2), maap)) 
        return(FALSE);
    
    if (ISTTY(fd)) {
        MakeCols(NULL);  /* Initialize formatting */
        fprintf(logfp, "  #bins= %d  Maxchain = %d\n", 
            maap->maa_currbin, maap->maa_maxchain) ;
    }       
    else if (!xdr_int(fd, &maap->maa_currbin) ) 
        return(FALSE) ;
            /* XDR count field for element array that follows
             */  
        
    i = 0;
    if ((left = maap->maa_currbin) == 0) return(TRUE);
    SetupXRep(&xobj, maap);
    
    while (left > 0) {
        maep = maap->maa_page[i++];
        limit = (left < BIN_PAGE) ? left : BIN_PAGE;
        left -= limit;
        while (limit-- > 0) {           
            if (!MA_list(fd, outp, outl, &maep->mae_made, &xobj.obj_state, enumh)) 
                {
                return(FALSE);
            }
            if (outl) log(outp, logfp);
            maep = (MAE *) ((char *) maep + maap->maa_esize);
        } 
                    
    } ;
    if (outl) log("\n", logfp);
    return(TRUE);   
}   /* Read_MA2() */ 


    
