
/****************************************************************************/
/*                                                                          */
/*      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/code/NNstat/3.3beta/RCS/sobjfo.c,v 1.6 1993/09/22 22:46:20 mogul Exp $";
 
 /* CHANGES:
  *     Rel 3.0:
  *   ISI:  Fix bug -- handle nparms==0 without error.
  *   ISI:  Add List routine.  
  *   ISI:  Remove redundant NL when no bins.  
  *   Aug93/DECWRL: Alpha/OSF port.  XDR is a pointer type.
  */
  
/*
 *   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.
 *
 */
 
/*
 *
 *          FO Object Class ==  FREQ.ONLY 
 *
 *    Objects of the FO class are frequency distributions using preset
 *    mappings of values into bins; there is also a default bin for
 *    those values which do not map into any of the defined bins.
 *
 *    FO objects are implemented using a precomputed open hashing.  When
 *    the table is computed it tries to choose a hash table large enough
 *    to get unique hashes. 
 *
 *
 *    Operations on FO class objects:
 *
 *       SOBJ *Make_FO( objname, &parmlist[#bins], #bins, datalen)
 *
 *       Write_FO( (SOBJ *) SOBJ-ptr, &value , length)
 *
 *       Read_FO( fh, (SOBJ *) SOBJ-ptr )
 *
 *       Delete_FO( (SOBJ *) SOBJ-ptr )
 *
 *       Clear_FO( (SOBJ *) SOBJ-ptr )
 *
 *       List_FO( fh )
 *
 *    THIS ROUTINES ARE ALSO USED BY THE SETF (SET-FILTER) CLASS
 *
 */
 
#include <stdio.h>
#include <sys/types.h>
#include "stat.h"
#include "sobj.h"
    
SOBJ *Make_FO() ;
boolean   Write_FO(), Read_FO();
void  Clear_FO(), Delete_FO(), List_FO() ;

    /* Tranfer Vector 
     *   for external access to these routines
     */
GENERICOP FOop = { Make_FO, Write_FO, Read_FO, Clear_FO, Delete_FO, List_FO } ;

    /*     
     *  Define FO object data structures
     *
     */

    /* Parameters controlling size of hash table */
#define HASHmaxmult 2  /* min = 2*nbins */
#define HASHmaxtries 8 /* Try no more than 8 hash table sizes */
#define HASHincr  7    /* Add to hash table size for each try */


    /* One bin element */

#define FOE struct only_bin
FOE {
    FODE  foe_data;     /* Externally-seen data: (value, count) pair */
} ;

#define foe_value  foe_data.fod_value
#define foe_count  foe_data.fod_count

    /* Object area */
    
#define FOA struct stat_farea
FOA {
    SOBJ   foa_sobj ;         /* SOBJ = standard root of area */
    
    short  foa_esize ;        /* Size of element */
    short  foa_hashs ;        /* Size of hash table (#shorts) */
    short  foa_nbins ;        /* Size of bin area (#elements) */    
    char   foa_flags ;        /* Flags */
#define FOF_MULTI 0x80        /* Non-unique hash */
    char   foa_dum2 ;         /* (unused) */
    
    u_int32 foa_others;        /* Count of Writes matching no bin */
    FOE  **foa_hashp ;        /* Ptr to hash table */
    FOE   *foa_foep ;         /* Ptr to bin vector */
    /*----------------------------------------------------
                     Hash Table... 
         (a vector of pointers to bins)
    /*----------------------------------------------------
                    Vector of bins...
         (each entry is an FOE)
    /*----------------------------------------------------*/
} ;
    

struct FO_parms {
        u_int32 fop_value[LONGPERVAL];  /* Actually use (dleng+3)/4 words */
};
    
                
#define hashfunc( V, S)  V%S    /* Simple congruential hash */  


SOBJ *Make_FO(objname, parmp, nparms, datalen)
    char *objname;
    struct FO_parms *parmp ;
    int datalen, nparms;
    {
    register FOA *foap ;
    register FOE **hp, *foep ;
    u_char *pp, *cp;    
    int i, ix, nhash ;
    int tries_left = HASHmaxtries ;    
       /* Try up to 'tries_left' sizes, to get unique hashing... */
       
    int esize = sizeof (FOE)  - MAX_DLENG + ALIGN(datalen);
    u_int32 HValue;      
    union v_union Value;
    int offset = PARMALIGN(datalen);
    int vsize = ALIGN(datalen);
    int nbins = nparms/(vsize>>2);
         
    ZERO_UNION(&Value);
    nhash = (nparms)? HASHmaxmult*nbins : 1;  
    while( tries_left-- ) {

        i = ALIGN(nhash*sizeof(FOE *) + sizeof(FOA)) + nbins*esize  ;
        if ((foap = (FOA *) malloc(i)) == NULL) {
            SOBJ_error = "Memory overflow!" ;
            return(NULL) ;
        }
        bzero(foap, i) ;  /* Clear entire area... */
        
        hp = (FOE **) ((char *) foap + sizeof(FOA) ) ;
            /* hp = start of hash table */          
        foap->foa_foep = foep = (FOE *) PTRALIGN( &hp[nhash] ) ;
            /* align start of bins to fullword boundary */
            
            /* Hash all the values now */
            
        pp = ( u_char *) parmp + offset;
        for (i = 0; i < nbins ; i++)  {
            bcopy(pp, Value.v_byte, datalen);               
            HValue = SUM_UNION(&Value);
            ix = hashfunc( HValue, nhash ) ;
            while (hp[ix]) {
                    /* Bin already in use... */
                    /* Could check for duplicates... */
                if (tries_left)  goto Tryagain ;
                ix++ ;
                ix %= nhash ;
                foap->foa_flags |= FOF_MULTI ;
            }
            hp[ix] = foep ;
            pp += vsize;
            foep = (FOE *) ((char *) foep + esize);
        }
            /* If reach here, have hashed all OK. */
        
        foap->foa_esize = esize;
        foap->foa_hashs = nhash;
        foap->foa_nbins = nbins ;
        foap->foa_hashp = hp;
                
            /* Now fill bin vector with values*/
        pp = ( u_char *) parmp + offset;
        cp = foap->foa_foep->foe_value; 
        for (i=0; i<nbins; i++) {
            bcopy( pp, cp, datalen) ;
            pp += vsize;
            cp += esize;
        }           
        return(&foap->foa_sobj) ;  /* SUCCESS!! */
        
Tryagain:   
    free(foap) ;                                
    nhash += HASHincr;
    }  /* End of loop to find perfect hash */ 
    /* (Never get here... ) */
}    
     
     
boolean Search_FO( foap, Valuep, L)
    register FOA *foap ;
    char *Valuep;
    register int L;
    {
    register FOE *foep, **hp ;
    union v_union  Value;
    extern Bcopy();
             
    ZERO_UNION(&Value);
    Bcopy(Valuep, Value.v_byte, L);                
    hp = &foap->foa_hashp[ hashfunc((SUM_UNION(&Value)), foap->foa_hashs) ];
    
    while (foep = *hp) {
        if (WORD1(&Value) == WORD1(foep->foe_value) &&
              (L <= sizeof(int32) || WORD2(&Value) == WORD2(foep->foe_value))){
                /* Found bin, so increment its count */
            foep->foe_count++;
            return(WRRET_TRUE);
        }
                /* Wrong bin...  */
        if ((foap->foa_flags & FOF_MULTI) == 0)  break;
        if (++hp >= &foap->foa_hashp[foap->foa_hashs]) hp = foap->foa_hashp;
    }
        /* No match.  Increment "other" */
    foap->foa_others++;
    return(WRRET_FALSE);
}
     
     
boolean Write_FO( foap, Valuep, L)
    register FOA *foap ;
    char *Valuep;
    register int L;
    {
    Search_FO(foap, Valuep, L);
    return(WRRET_OK);
}
    
        
void Delete_FO( foap )
    FOA *foap ;
    {
    free(foap) ;
}


void Clear_FO( foap )
    register FOA *foap ;
    {
    register FOE *foep = foap->foa_foep;
    register int left = foap->foa_nbins;
    
    while (left-- > 0) { 
        foep->foe_count = 0; 
        foep = (FOE *) ((char *) foep + foap->foa_esize);
    };
    foap->foa_others = 0;
}


boolean Read_FO(fh, foap)
    XDR *fh;
    register FOA *foap ;
    {
    FOE  *foep = foap->foa_foep;
    int   nbins = foap->foa_nbins;
    OBJ   xobj;
            
    if (!PutReadResp(fh, FOr_SIZE(nbins, foap->foa_sobj.sob_dleng),
                  foap)) return(FALSE); 
    
    SetupXRep(&xobj, foap);
    if (!ReadFO(fh, &foep, &nbins, &foap->foa_others, &xobj, 
                               foap->foa_esize, ISTTY(fh), NULL)){
        return(FALSE);
    }
    return(TRUE);       
} /* Read_FO() */


void
List_FO(fh, OBJp)
XDR *fh;
OBJ *OBJp;
{
   FODE  bins[MAX_FONO];  /* Place to decode data from bins */
   FODE *binp = bins;
   int   count = MAX_FONO; 
   int  defaultno; 
   
   if (!ReadFO(fh, &binp, &count, &defaultno, OBJp, sizeof(FODE), 1, NULL))
      printf(" BAD ReadFO! ");
} /* List_FO() */

        
u_int32 BinCount_FO(foap)
    register FOA *foap;
    {
    return(foap->foa_sobj.sob_totalno - foap->foa_others);
}


#define OUTSIZE 256
    /*
     *  ReadFO() --  Main I/O routine for reading FO (freq-only) and 
     *               FOB (freq-only-bytes) objects.
     *
     */
boolean ReadFO(xdrs, binpp, maxnop, defaultp, objp, binsize, islog, totbytp)
    register XDR *xdrs;
    FODE **binpp;
    int   *maxnop, *defaultp;
    OBJ   *objp;
    int    binsize, islog;
    struct FOparmB  *totbytp;  /* (NULL ptr if FO object) */
{
    register int i; 
    int   k = *maxnop;
    FODE *foep, fode;
    char  outp[OUTSIZE];
    int   outl = 0;
    EnumHandle enumh = ExistEnumObj(objp->obj_name);
    char  bytbuf[18];
    extern int aligncnt;

    if (islog) {
        outl = OUTSIZE;
        MakeCols(NULL);
    }
    if (xdrs) {
       if (!xdr_u_int(xdrs, defaultp))
           return(FALSE);
       if (totbytp) {
          if (!xdr_opaque(xdrs, totbytp, sizeof(struct FOparmB)))
              return(FALSE);
       }
       if (!xdr_int(xdrs, &k))
          return(FALSE);     
       }
    if (islog) {    
#ifndef PRINT_NETLOAD
        if (totbytp) {
            print_dbl(bytbuf, totbytp->totalB.H, totbytp->totalB.L);
            sprintf(outp, "  Total Bytes= %sB  #bins= %d\n", bytbuf, k);
        }
        else 
            sprintf(outp, "  #bins= %d\n", k);
#else 
        if (totbytp) {
            print_dbl(bytbuf, totbytp->totalB.H, totbytp->totalB.L);
            sprintf(outp, "  Total Bytes= %sB  #bins= %d ", bytbuf, k);
        }
        else 
            sprintf(outp, "  #bins= %d ", k);
        if (totbytp) {
            print_netload_percent(endof(outp),
                totbytp->totalB.H, totbytp->totalB.L, objp->obj_state.totalno,
                objp->obj_state.currtime - objp->obj_state.cleartime);
        }
        strcat(outp, "\n");
#endif
        log(outp, logfp);
        sprintf(outp, "[default]= %d ", *defaultp);
        if (totbytp) {
            print_dbl(bytbuf, totbytp->defaultB.H, totbytp->defaultB.L);
            sprintf(endof(outp), "&%sB ", bytbuf);
        }
        if ((i = Align(outp)) != 0)
           sprintf(endof(outp), "%*c", i, ' ');
        print_percent(endof(outp), objp->obj_state.totalno, *defaultp);
#ifdef PRINT_NETLOAD
        if (totbytp) {
            print_netload_percent(endof(outp),
                totbytp->defaultB.H, totbytp->defaultB.L, *defaultp,
                objp->obj_state.currtime - objp->obj_state.cleartime);
        }
#endif
        strcat(outp, "\n");
        log(outp, logfp);
    }
    
    if (*binpp && k>*maxnop) return(FALSE);
        /* decode: caller gave us a buffer, but it was too small. */
    if ((*maxnop = k) == 0) return(TRUE);
    
    aligncnt = 0;
    foep = (binsize)? *binpp : &fode;
    for (i=0;i<k;i++) {
        if (!FAO_list(xdrs, outp, outl, foep, &(objp->obj_state), enumh, 
                                                      (totbytp)?FOBOBJ:FOOBJ))
            return(FALSE);
        if (islog)  log(outp, logfp);
        foep = (FODE *) ((char *) foep + binsize);
    }
    if (islog) log("\n", logfp);
    return(TRUE);
} /* ReadFO */
