void ssl_scache_shmht_status(server_rec *s, pool *p, void (*func)(char *, void *), void *arg)
{
    SSLModConfigRec *mc = myModConfig();
    void *vpKey;
    void *vpData;
    int nKey;
    int nData;
    int nElem;
    int nSize;
    int nAverage;

    nElem = 0;
    nSize = 0;
    ssl_mutex_on(s);
    if (table_first(mc->tSessionCacheDataTable,
                    &vpKey, &nKey, &vpData, &nData) == TABLE_ERROR_NONE) {
        do {
            if (vpKey == NULL || vpData == NULL)
                continue;
            nElem += 1;
            nSize += nData;
        } while (table_next(mc->tSessionCacheDataTable,
                            &vpKey, &nKey, &vpData, &nData) == TABLE_ERROR_NONE);
    }
    ssl_mutex_off(s);
    if (nSize > 0 && nElem > 0)
        nAverage = nSize / nElem;
    else
        nAverage = 0;
    func(ap_psprintf(p, "cache type: <b>SHMHT</b>, maximum size: <b>%d</b> bytes<br>", mc->nSessionCacheDataSize), arg);
    func(ap_psprintf(p, "current sessions: <b>%d</b>, current size: <b>%d</b> bytes<br>", nElem, nSize), arg);
    func(ap_psprintf(p, "average session size: <b>%d</b> bytes<br>", nAverage), arg);
    return;
}
Пример #2
0
/*
 * dump the table to stdout
 */
static	void	dump_table(table_t *tab_p)
{
  int	ret, entry_c;
  long	*data_p, *key_p;
  
  for (ret = table_first(tab_p, (void **)&key_p, NULL, (void **)&data_p, NULL),
	 entry_c = 0;
       ret == TABLE_ERROR_NONE;
       ret = table_next(tab_p, (void **)&key_p, NULL, (void **)&data_p, NULL),
	 entry_c++) {
    (void)printf("%d: key %ld, data %ld\n", entry_c, *key_p, *data_p);
  }
  
  if (ret != TABLE_ERROR_NOT_FOUND) {
    (void)fprintf(stderr, "ERROR: first or next key in table: %s\n",
		  table_strerror(ret));
  }
}
Пример #3
0
/*
 * compare the keys in two tables.  returns 1 if equal else 0
 */
static	int	test_eq(table_t *tab1_p, table_t *tab2_p, const int verb_b)
{
  int	ret, eq = 1, key_size, data1_size, data2_size;
  void	*key_p, *data1_p, *data2_p;
  
  /* test the table entries */
  for (ret = table_first(tab1_p, (void **)&key_p, &key_size,
			 (void **)&data1_p, &data1_size);
       ret == TABLE_ERROR_NONE;
       ret = table_next(tab1_p, (void **)&key_p, &key_size,
			(void **)&data1_p, &data1_size)) {
    ret = table_retrieve(tab2_p, key_p, key_size,
			 (void **)&data2_p, &data2_size);
    if (ret != TABLE_ERROR_NONE) {
      (void)fprintf(stderr, "could not find key of %d bytes: %s\n",
		    key_size, table_strerror(ret));
      eq = 0;
    }
    else if (data1_size == data2_size
	     && memcmp(data1_p, data2_p, data1_size) == 0) {
      if (verb_b) {
	(void)printf("key of %d bytes, data of %d bytes\n",
		     key_size, data1_size);
	fflush(stdout);
      }
    }
    else {
      (void)fprintf(stderr,
		    "ERROR: key of %d bytes: data (size %d) != other "
		    "(size %d)\n",
		    key_size, data1_size, data2_size);
      eq = 0;
    }
  }
  
  if (ret != TABLE_ERROR_NOT_FOUND) {
    eq = 0;
  }
  return eq;
}
Пример #4
0
/*
 * Carry out aggregation on a complete data set held in a table
 * This is an alternative entry point to the class that does not need
 * the setting up of a session.
 * The table should identify keys, time, sequence and duration in the 
 * standard way as defined by FHA spec.
 * Returns a TABLE of results compliant to the FHA spec, _time will be
 * set to the last time of the dataset, _seq to 0. _dur is not set
 * The result is independent of dataset's memory allocation, sothe caller
 * needs to run table_destroy() to free its memory.
 * Returns NULL if there is an error, if dataset is NULL or if there
 * was insufficent data.
 */
TABLE cascade_aggregate(enum cascade_fn func, 	/* aggregation function */
			TABLE dataset		/* multi-sample, multi-key
						 * dataset in a table */ )
{
     TREE *inforow, *keyvals, *databykey, *colnames;
     char *keycol, *colname, *tmpstr, *type;
     int duration, haskey=0;
     TABLE itab, result;
     TABSET tset;
     ITREE *col;
     double val, tmpval1, tmpval2;
     time_t t1, t2, tdiff;

     /* assert special cases */
     if ( ! dataset ) {
          elog_printf(DIAG, "no dataset given to aggregate");
          return NULL;
     }
     if (table_nrows(dataset) == 0) {
          elog_printf(DIAG, "no rows to aggregate in dataset");
     }
     if ( ! table_hascol(dataset, "_time")) {
          tmpstr = table_outheader(dataset);
          elog_printf(ERROR, "attempting to aggregate a table without _time "
		      "column (columns: %s)", tmpstr);
	  nfree(tmpstr);
          return NULL;
     }

     /* find any keys that might exist */
     inforow = table_getinforow(dataset, "key");
     if (inforow) {
          keycol = tree_search(inforow, "1", 2);
	  if (keycol) {
	       keyvals = table_uniqcolvals(dataset, keycol, NULL);
	       if (keyvals) {
		    /* separate the combined data set into ones of
		     * separate keys */
		    haskey++;
		    databykey = tree_create();
		    tset = tableset_create(dataset);
		    tree_traverse(keyvals) {
		         /* select out the data */
		         tableset_reset(tset);
			 tableset_where(tset, keycol, eq, 
					tree_getkey(keyvals));
			 itab = tableset_into(tset);
		         tree_add(databykey, tree_getkey(keyvals), itab);
		    }
		    tableset_destroy(tset);
	       }
	       tree_destroy(keyvals);
	  }
	  tree_destroy(inforow);
     }

     /* if there were no keys found, pretend that we have a single one */
     if ( ! haskey ) {
          databykey = tree_create();
	  tree_add(databykey, "nokey", dataset);
     }

     /* find the time span and duration of the dataset */
     table_first(dataset);
     if (table_hascol(dataset, "_dur"))
          duration = strtol(table_getcurrentcell(dataset, "_dur"), NULL, 10);
     else
          duration = 0;
     t1 = strtol(table_getcurrentcell(dataset, "_time"), NULL, 10);
     table_last(dataset);
     t2 = strtol(table_getcurrentcell(dataset, "_time"), NULL, 10);
     tdiff = t2-t1+duration;

     /* go over the keyed table and apply our operators to each column 
      * in turn */
     result = table_create_fromdonor(dataset);
     table_addcol(result, "_seq", NULL);	/* make col before make row */
     table_addcol(result, "_time", NULL);
     table_addcol(result, "_dur", NULL);
     tree_traverse(databykey) {
          table_addemptyrow(result);
          itab = tree_get(databykey);
	  colnames = table_getheader(itab);
	  tree_traverse(colnames) {
	       colname = tree_getkey(colnames);
	       if ( ! table_hascol(result, colname)) {
		     tmpstr = xnstrdup(colname);
		     table_addcol(result, tmpstr, NULL);
		     table_freeondestroy(result, tmpstr);
	       }
	       col = table_getcol(itab, colname);
	       type = table_getinfocell(itab, "type", colname);
	       if (type && strcmp(type, "str") == 0) {
		    /* string value: report the last one */
		    itree_last(col);
		    table_replacecurrentcell(result, colname, itree_get(col));
	       } else if (strcmp(colname, "_dur") == 0) {
		    /* _dur: use the last value, can treat as string */
		    itree_last(col);
		    table_replacecurrentcell(result, "_dur", itree_get(col));
	       } else if (strcmp(colname, "_seq") == 0) {
		    /* _seq: only one result is produced so must be 0 */
		    table_replacecurrentcell(result, "_seq", "0");
	       } else if (strcmp(colname, "_time") == 0) {
		    /* _time: use the last value, can treat as string */
		    itree_last(col);
		    table_replacecurrentcell(result, "_time", itree_get(col));
	       } else {
		    /* numeric value: treat as a float and report it */
		    switch (func) {
		    case CASCADE_AVG:
		         /* average of column's values */
		         val = 0.0;
			 itree_traverse(col)
			      val += atof( itree_get(col) );
			 val = val / itree_n(col);
			 break;
		    case CASCADE_MIN:
		         /* minimum of column's values */
		         val = DBL_MAX;
			 itree_traverse(col) {
			      tmpval1 = atof( itree_get(col) );
			      if (tmpval1 < val)
				   val = tmpval1;
			 }
			 break;
		    case CASCADE_MAX:
		         /* maximum of column's values */
		         val = DBL_MIN;
			 itree_traverse(col) {
			      tmpval1 = atof( itree_get(col) );
			      if (tmpval1 > val)
				   val = tmpval1;
			 }
			 break;
		    case CASCADE_SUM:
		         /* sum of column's values */
		         val = 0.0;
			 itree_traverse(col)
			      val += atof( itree_get(col) );
			 break;
		    case CASCADE_LAST:
		         /* last value */
		         itree_last(col);
			 val = atof( itree_get(col) );
			 break;
		    case CASCADE_FIRST:
		         /* last value */
		         itree_first(col);
			 val = atof( itree_get(col) );
			 break;
		    case CASCADE_DIFF:
		         /* the difference in values of first and last */
		         itree_first(col);
			 tmpval1 = atof( itree_get(col) );
			 itree_last(col);
			 tmpval2 = atof( itree_get(col) );
			 val = tmpval2 - tmpval1;
			 break;
		    case CASCADE_RATE:
		         /* difference in values (as CASCADE_DIFF) then 
			  * divided by the number of seconds in the set  */
		         itree_first(col);
			 tmpval1 = atof( itree_get(col) );
			 itree_last(col);
			 tmpval2 = atof( itree_get(col) );
			 val = tmpval2 - tmpval1;
			 val = val / tdiff;
			 break;
		    }
		    /* save the floating point value */
		    table_replacecurrentcell_alloc(result, colname, 
						   util_ftoa(val));
	       }
	       itree_destroy(col);
	  }
	  /* make sure that there are values for the special columns */
	  if ( ! table_hascol(dataset, "_time"))
	       table_replacecurrentcell(result, "_time", 
					util_decdatetime(time(NULL)));
	  if ( ! table_hascol(dataset, "_seq"))
	       table_replacecurrentcell(result, "_seq", "0");
	  if ( ! table_hascol(dataset, "_dur"))
	       table_replacecurrentcell(result, "_dur", "0");
     }

     /* clear up */
     if (haskey) {
          tree_traverse(databykey) {
	       itab = tree_get(databykey);
	       table_destroy(itab);
	  }
     }
     tree_destroy(databykey);

     return result;
}
Пример #5
0
/*
 * try ITERN random program iterations.
 */
static	void	stress(table_t *tab_p, const int iter_n, const int mmaping_b)
{
  void		*data, *key;
  int		which = 0, mode, weight_total;
  int		iter_c, pnt_c, free_c, ret, ksize, dsize;
  entry_t	*grid, *free_p, *grid_p, *last_p;
  int		linear_b = 0, linear_eof_b = 0;
  
  (void)printf("Performing stress tests with %d iterations:\n", iter_n);
  (void)fflush(stdout);
  
  grid = malloc(sizeof(entry_t) * MAX_ENTRIES);
  if (grid == NULL) {
    (void)printf("problems allocating space for %d entries.\n",
		 MAX_ENTRIES);
    exit(1);
  }
  
  /* initialize free list */
  free_p = grid;
  for (grid_p = grid; grid_p < grid + MAX_ENTRIES; grid_p++) {
    grid_p->en_free_b = 1;
    grid_p->en_key = NULL;
    grid_p->en_key_size = 0;
    grid_p->en_data = NULL;
    grid_p->en_data_size = 0;
    grid_p->en_next_p = grid_p + 1;
  }
  /* redo the last next pointer */
  (grid_p - 1)->en_next_p = NULL;
  free_c = MAX_ENTRIES;
  
#if 0
  /* load the list */
  if (mmaping_b) {
    for (ret = table_first(tab_p, (void **)&key_p, NULL, (void **)&data_p,
			   NULL);
	 ret == TABLE_ERROR_NONE;
	 ret = table_next(tab_p, (void **)&key_p, NULL, (void **)&data_p,
			  NULL)) {
    }
  }
#endif
  
  /* total the weights */
  weight_total = 0;
  for (mode = 0; mode < MODE_MAX; mode++) {
    weight_total += mode_weights[mode];
  }
  
  for (iter_c = 0; iter_c < iter_n;) {
    int		weight;
    
    /* decide what to do */
    weight = RANDOM_VALUE(weight_total) + 1;
    for (mode = 0; mode < MODE_MAX; mode++) {
      weight -= mode_weights[mode];
      if (weight <= 0) {
	break;
      }
    }
    
    /* out of bounds */
    if (mode >= MODE_MAX) {
      continue;
    }
    
    switch (mode) {
      
    case MODE_CLEAR:
      if (mmaping_b || large_b) {
	continue;
      }
      
      call_c++;
      table_clear(tab_p);
      
      /* re-init free list */
      free_p = grid;
      for (grid_p = grid; grid_p < grid + MAX_ENTRIES; grid_p++) {
	if (! grid_p->en_free_b) {
	  if (grid_p->en_key != NULL) {
	    free(grid_p->en_key);
	  }
	  if (grid_p->en_data != NULL) {
	    free(grid_p->en_data);
	  }
	}
	grid_p->en_free_b = 1;
	grid_p->en_next_p = grid_p + 1;
      }
      /* redo the last next pointer */
      (grid_p - 1)->en_next_p = NULL;
      free_c = MAX_ENTRIES;
      linear_b = 0;
      linear_eof_b = 0;
      iter_c++;
      if (verbose_b) {
	(void)printf("table cleared.\n");
	fflush(stdout);
      }
      break;
      
    case MODE_INSERT:
      if (mmaping_b) {
	continue;
      }
      if (free_c > 0) {
	which = RANDOM_VALUE(free_c);
	last_p = NULL;
	grid_p = free_p;
	for (pnt_c = 0; pnt_c < which && grid_p != NULL; pnt_c++) {
	  last_p = grid_p;
	  grid_p = grid_p->en_next_p;
	}
	if (grid_p == NULL) {
	  (void)printf("reached end of free list prematurely\n");
	  exit(1);
	}
	
	do {
	  key = random_block(&ksize);
	} while (key == NULL);
	data = random_block(&dsize);
	
	call_c++;
	ret = table_insert(tab_p, key, ksize, data, dsize, NULL, 0);
	if (ret == TABLE_ERROR_NONE) {
	  if (verbose_b) {
	    (void)printf("stored in pos %d: %d, %d bytes of key, data\n",
			 grid_p - grid, ksize, dsize);
	    fflush(stdout);
	  }
	  
	  grid_p->en_free_b = 0;
	  grid_p->en_key = key;
	  grid_p->en_key_size = ksize;
	  grid_p->en_data = data;
	  grid_p->en_data_size = dsize;
	  
	  /* shift free list */
	  if (last_p == NULL) {
	    free_p = grid_p->en_next_p;
	  }
	  else {
	    last_p->en_next_p = grid_p->en_next_p;
	  }
	  grid_p->en_next_p = NULL;
	  free_c--;
	  iter_c++;
	}
	else {
	  for (grid_p = grid; grid_p < grid + MAX_ENTRIES; grid_p++) {
	    if (grid_p->en_free_b) {
	      continue;
	    }
	    if (grid_p->en_key_size == ksize
		&& memcmp(grid_p->en_key, key, ksize) == 0) {
	      break;
	    }
	  }
	  
	  /* if we did not store it then error */
	  if (grid_p >= grid + MAX_ENTRIES) {
	    (void)fprintf(stderr, "ERROR storing #%d: %s\n",
			  which, table_strerror(ret));
	  }
	  if (key != NULL) {
	    free(key);
	  }
	  if (data != NULL) {
	    free(data);
	  }
	}
      }
      break;
      
    case MODE_OVERWRITE:
      if (mmaping_b) {
	continue;
      }
      if (free_c < MAX_ENTRIES) {
	which = RANDOM_VALUE(MAX_ENTRIES);
	
	if (grid[which].en_free_b) {
	  continue;
	}
	
	data = random_block(&dsize);
	
	call_c++;
	ret = table_insert(tab_p, grid[which].en_key, grid[which].en_key_size,
			   data, dsize, NULL, 1);
	if (ret == TABLE_ERROR_NONE) {
	  if (verbose_b) {
	    (void)printf("overwrite pos %d with data of %d bytes\n",
			 which, dsize);
	    fflush(stdout);
	  }
	  grid[which].en_free_b = 0;
	  if (grid[which].en_data != NULL) {
	    free(grid[which].en_data);
	  }
	  grid[which].en_data = data;
	  grid[which].en_data_size = dsize;
	  grid[which].en_next_p = NULL;
	  free_c--;
	  iter_c++;
	}
	else {
	  (void)fprintf(stderr, "ERROR overwriting #%d: %s\n",
			which, table_strerror(ret));
	  free(data);
	}
      }
      break;
      
    case MODE_RETRIEVE:
      if (free_c < MAX_ENTRIES) {
	which = RANDOM_VALUE(MAX_ENTRIES);
	
	if (grid[which].en_free_b) {
	  continue;
	}
	
	call_c++;
	ret = table_retrieve(tab_p, grid[which].en_key, grid[which].en_key_size,
			     (void **)&data, &dsize);
	if (ret == TABLE_ERROR_NONE) {
	  if (grid[which].en_data_size == dsize
	      && memcmp(grid[which].en_data, data, dsize) == 0) {
	    if (verbose_b) {
	      (void)printf("retrieved key #%d, got data of %d bytes\n",
			   which, dsize);
	      fflush(stdout);
	    }
	  }
	  else {
	    (void)fprintf(stderr,
			  "ERROR: retrieve key #%d: data (%d bytes) didn't "
			  "match table (%d bytes)\n",
			  which, grid[which].en_data_size, dsize);
	  }
	  iter_c++;
	}
	else {
	  (void)fprintf(stderr, "error retrieving key #%d: %s\n",
			which, table_strerror(ret));
	}
      }
      break;
      
    case MODE_DELETE:
      if (mmaping_b) {
	continue;
      }
      if (free_c >= MAX_ENTRIES) {
	continue;
      }
      
      which = RANDOM_VALUE(MAX_ENTRIES);
      
      if (grid[which].en_free_b) {
	continue;
      }
      
      call_c++;
      ret = table_delete(tab_p, grid[which].en_key, grid[which].en_key_size,
			 (void **)&data, &dsize);
      if (ret == TABLE_ERROR_NONE) {
	if (grid[which].en_data_size == dsize
	    && memcmp(grid[which].en_data, data, dsize) == 0) {
	  if (verbose_b) {
	    (void)printf("deleted key #%d, got data of %d bytes\n",
			 which, dsize);
	    fflush(stdout);
	  }
	}
	else {
	  (void)fprintf(stderr,
			"ERROR deleting key #%d: data didn't match table\n",
			which);
	}
	grid[which].en_free_b = 1;
	if (grid[which].en_key != NULL) {
	  free(grid[which].en_key);
	}
	if (grid[which].en_data != NULL) {
	  free(grid[which].en_data);
	}
	grid[which].en_next_p = free_p;
	free_p = grid + which;
	free_c++;
	if (free_c == MAX_ENTRIES) {
	  linear_b = 0;
	  linear_eof_b = 0;
	}
	iter_c++;
	if (data != NULL) {
	  free(data);
	}
      }
      else {
	(void)fprintf(stderr, "ERROR deleting key %d: %s\n",
		      which, table_strerror(ret));
      }
      break;
      
    case MODE_DELETE_FIRST:
      /*
       * We have a problem here.  This is the only action routine
       * which modifies the table and is not key based.  We don't have
       * a way of looking up the key in our local data structure.
       */
      break;
      
    case MODE_FIRST:
      call_c++;
      ret = table_first(tab_p, (void **)&key, &ksize, (void **)&data, &dsize);
      if (ret == TABLE_ERROR_NONE) {
	linear_b = 1;
	linear_eof_b = 0;
	if (verbose_b) {
	  (void)printf("first entry has key, data of %d, %d bytes\n",
		       ksize, dsize);
	  fflush(stdout);
	}
	iter_c++;
      }
      else if (free_c == MAX_ENTRIES) {
	if (verbose_b) {
	  (void)printf("no first in table\n");
	  fflush(stdout);
	}
      }
      else {
	(void)fprintf(stderr, "ERROR: first in table: %s\n",
		      table_strerror(ret));
      }
      break;
      
    case MODE_NEXT:
      call_c++;
      ret = table_next(tab_p, (void **)&key, &ksize, (void **)&data, &dsize);
      if (ret == TABLE_ERROR_NONE) {
	if (verbose_b) {
	  (void)printf("next entry has key, data of %d, %d\n",
		       ksize, dsize);
	  fflush(stdout);
	}
	iter_c++;
      }
      else if (ret == TABLE_ERROR_LINEAR && (! linear_b)) {
	if (verbose_b) {
	  (void)printf("no first command run yet\n");
	  fflush(stdout);
	}
      }
      else if (ret == TABLE_ERROR_NOT_FOUND) {
	if (verbose_b) {
	  (void)printf("reached EOF with next in table: %s\n",
		       table_strerror(ret));
	  fflush(stdout);
	}
	linear_b = 0;
	linear_eof_b = 1;
      }
      else {
	(void)fprintf(stderr, "ERROR: table_next reports: %s\n",
		      table_strerror(ret));
	linear_b = 0;
	linear_eof_b = 0;
      }
      break;
      
    case MODE_THIS:
      call_c++;
      ret = table_this(tab_p, (void **)&key, &ksize, (void **)&data, &dsize);
      if (ret == TABLE_ERROR_NONE) {
	if (verbose_b) {
	  (void)printf("this entry has key,data of %d, %d bytes\n",
		       ksize, dsize);
	  fflush(stdout);
	}
	iter_c++;
      }
      else if (ret == TABLE_ERROR_LINEAR && (! linear_b)) {
	if (verbose_b) {
	  (void)printf("no first command run yet\n");
	  fflush(stdout);
	}
      }
      else if (ret == TABLE_ERROR_NOT_FOUND || linear_eof_b) {
	if (verbose_b) {
	  (void)printf("table linear already reached EOF\n");
	  fflush(stdout);
	}
      }
      else {
	(void)fprintf(stderr, "ERROR: this table: %s\n", table_strerror(ret));
	linear_b = 0;
	linear_eof_b = 0;
      }
      break;
      
    case MODE_INFO:
      {
	int	buckets, entries;
	
	call_c++;
	ret = table_info(tab_p, &buckets, &entries);
	if (ret == TABLE_ERROR_NONE) {
	  if (verbose_b) {
	    (void)printf("table has %d buckets, %d entries\n",
			 buckets, entries);
	    fflush(stdout);
	  }
	  iter_c++;
	}
	else {
	  (void)fprintf(stderr, "ERROR: table info: %s\n",
			table_strerror(ret));
	}
      }
    break;
    
    case MODE_ADJUST:
      {
	int	buckets, entries;
	
	if (mmaping_b || auto_adjust_b || large_b) {
	  continue;
	}
	
	call_c++;
	ret = table_info(tab_p, &buckets, &entries);
	if (ret == TABLE_ERROR_NONE) {
	  if (entries == 0) {
	    if (verbose_b) {
	      (void)printf("cannot adjusted table, %d entries\n", entries);
	      fflush(stdout);
	    }
	  }
	  else if (buckets == entries) {
	    if (verbose_b) {
	      (void)printf("no need to adjust table, %d buckets and entries\n",
			   buckets);
	      fflush(stdout);
	    }
	  }
	  else {
	    ret = table_adjust(tab_p, entries);
	    if (ret == TABLE_ERROR_NONE) {
	      (void)printf("adjusted table from %d to %d buckets\n",
			   buckets, entries);
	      iter_c++;
	    }
	    else {
	      (void)printf("ERROR: table adjust to %d buckets: %s\n",
			   entries, table_strerror(ret));
	    }
	  }
	}
	else {
	  (void)fprintf(stderr, "ERROR: table info: %s\n",
			table_strerror(ret));
	}
      }
      break;
      
    default:
      (void)printf("unknown mode %d\n", which);
      break;
    }
  }
  
  /* run through the grid and free the entries */
  for (grid_p = grid; grid_p < grid + MAX_ENTRIES; grid_p++) {
    if (! grid_p->en_free_b) {
      if (grid_p->en_key != NULL) {
	free(grid_p->en_key);
      }
      if (grid_p->en_data != NULL) {
	free(grid_p->en_data);
      }
    }
  }
  
  /* free used pointers */
  free(grid);
}