/*! \file lib/ogsf/gsds.c \brief OGSF library - dataset loading and management (lower level functions) GRASS OpenGL gsurf OGSF Library The idea here is to treat datasets as separate objects, which SHOULD: - allow easier reuse of data for different attributes. - allow a mechanism for changing data and have changes reflected in each attribute using that data. - allow a mechanism to automatically update data when the data source is changed. - allow easier weaning from GRASS. - allow easier use of shared memory between processes. These structures are defined in gstypes.h: typedef struct{ float *fb; int *ib; short *sb; char *cb; struct BM *bm; } typbuff; How about adding a transform func here, so GET_MAPATT would do an on-the-fly transformation? Or even a transform func LIST! typedef struct{ int data_id; int dims[MAXDIMS]; int ndims; int numbytes; char unique_name[80]; typbuff databuff; int changed; int need_reload; } dataset; (C) 1999-2008 by the GRASS Development Team This program is free software under the GNU General Public License (>=v2). Read the file COPYING that comes with GRASS for details. \author Bill Brown UI GMS Lab \author Doxygenized by Martin Landa (May 2008) */ #include #include #include #include #include #define LUCKY 33 #define BLOC 20 #define MAX_DS 100 static int init_gsds(void); static int check_numsets(void); static dataset *get_dataset(int); static int get_type(dataset *); static dataset *Data[MAX_DS]; static dataset Ds[MAX_DS]; /* trying to avoid allocation */ static int Numsets = 0; static int Cur_id = LUCKY; static int Cur_max; static size_t Tot_mem = 0; /*! \brief Initialize gsds */ static int init_gsds(void) { int i; for (i = 0; i < MAX_DS; i++) { /* avoiding dynamic allocation */ Data[i] = &(Ds[i]); } Cur_max = MAX_DS; return (1); } /*! \brief Check numsets \return 0 numset < cur_max */ static int check_numsets(void) { if (Numsets < Cur_max) { return (0); } G_fatal_error(_("Maximum number of datasets exceeded")); /* This return statement keeps compilers happy, it is never executed */ return (0); } /*! \brief Get dataset \param id data id \return pointer to dataset struct \return NULL dataset not found */ static dataset *get_dataset(int id) { int i; for (i = 0; i < Numsets; i++) { if (Data[i]->data_id == id) { return (Data[i]); } } return (NULL); } /*! \brief Get type \param ds pointer to dataset struct \return type code \return -1 unsupported type */ static int get_type(dataset * ds) { if (ds) { if (ds->databuff.bm) { return (ATTY_MASK); } if (ds->databuff.cb) { return (ATTY_CHAR); } if (ds->databuff.sb) { return (ATTY_SHORT); } if (ds->databuff.ib) { return (ATTY_INT); } if (ds->databuff.fb) { return (ATTY_FLOAT); } } return (-1); } /*! \brief Get handle to gsds. Successive calls will continue search until "begin" is set (problem here is, unique_name no longer uniquely identifies dataset, since changes may be made; but unique_name should still be useful for reloading dataset) changes & types are set to actual for dataset if found. \param name \param changes,types acceptable changes & types, flags may be or'd not changed is assumed to always be acceptable \param begin flag to indicate search from beginning \return data id \return -1 not found */ int gsds_findh(const char *name, IFLAG * changes, IFLAG * types, int begin) { static int i; int start; start = begin ? 0 : i + 1; for (i = start; i < Numsets; i++) { if (!strcmp(Data[i]->unique_name, name)) { if ((Data[i]->changed & *changes) || !(Data[i]->changed)) { if (get_type(Data[i]) & *types) { *changes = Data[i]->changed; *types = get_type(Data[i]); return (Data[i]->data_id); } } } } return (-1); } /*! \brief Get handle to gsds \param name raster map name \return -1 on failure \return data id */ int gsds_newh(const char *name) { dataset *new; static int first = 1; int i; if (first) { if (0 > init_gsds()) { return (-1); } first = 0; } else if (0 > check_numsets()) { return (-1); } if (!name) { return (-1); } new = Data[Numsets]; if (new) { Numsets++; new->data_id = Cur_id++; for (i = 0; i < MAXDIMS; i++) { new->dims[i] = 0; } new->unique_name = G_store(name); new->databuff.fb = NULL; new->databuff.ib = NULL; new->databuff.sb = NULL; new->databuff.cb = NULL; new->databuff.bm = NULL; new->databuff.nm = NULL; new->databuff.k = 0.0; new->changed = 0; new->ndims = 0; new->need_reload = 1; return (new->data_id); } return (-1); } /*! \brief Get data buffer Doesn't prevent writing a buff thats's been gotten with change_flag == 0 (could return a copy, but willing to trust calling func for now) \param id dataset id \param change_flag set changed flag \return pointer to typbuff struct \return NULL on failure */ typbuff *gsds_get_typbuff(int id, IFLAG change_flag) { dataset *ds; if ((ds = get_dataset(id))) { ds->changed = ds->changed | change_flag; ds->need_reload = 0; return (&(ds->databuff)); } return (NULL); } /*! \brief Get name \param id \return name \return NULL on failure */ char *gsds_get_name(int id) { int i; dataset *fds; static char retstr[GPATH_MAX]; for (i = 0; i < Numsets; i++) { if (Data[i]->data_id == id) { fds = Data[i]; strcpy(retstr, fds->unique_name); return (retstr); } } return (NULL); } /*! \brief Free allocated dataset \param id \return 0 not found \return 1 found */ int gsds_free_datah(int id) { int i, j, found = 0; dataset *fds; G_debug(3, "gsds_free_datah"); for (i = 0; i < Numsets; i++) { if (Data[i]->data_id == id) { found = 1; fds = Data[i]; free_data_buffs(fds, ATTY_ANY); G_free((void *)fds->unique_name); fds->unique_name = NULL; fds->data_id = 0; for (j = i; j < (Numsets - 1); j++) { Data[j] = Data[j + 1]; } Data[j] = fds; } } if (found) { --Numsets; } return (found); } /*! \brief Free allocated buffer \param id dataset id \param typ data type \return 0 not found \return 1 found */ int gsds_free_data_buff(int id, int typ) { int i, found = 0; dataset *fds; for (i = 0; i < Numsets; i++) { if (Data[i]->data_id == id) { found = 1; fds = Data[i]; free_data_buffs(fds, typ); } } return (found); } /*! \brief Free data buffer \param ds pointer to dataset struct \param typ data type \return freed size */ size_t free_data_buffs(dataset * ds, int typ) { int i; size_t siz, nsiz = 1, freed = 0; for (i = 0; i < ds->ndims; i++) { nsiz *= ds->dims[i]; } if (typ & ATTY_NULL) { if (ds->databuff.nm) { siz = BM_get_map_size(ds->databuff.nm); BM_destroy(ds->databuff.nm); ds->databuff.nm = NULL; freed += siz; } } if (typ & ATTY_MASK) { if (ds->databuff.bm) { siz = BM_get_map_size(ds->databuff.bm); BM_destroy(ds->databuff.bm); ds->databuff.bm = NULL; freed += siz; } } if (typ & ATTY_CHAR) { if (ds->databuff.cb) { siz = nsiz * sizeof(char); free(ds->databuff.cb); ds->databuff.cb = NULL; freed += siz; } } if (typ & ATTY_SHORT) { if (ds->databuff.sb) { siz = nsiz * sizeof(short); free(ds->databuff.sb); ds->databuff.sb = NULL; freed += siz; } } if (typ & ATTY_INT) { if (ds->databuff.ib) { siz = nsiz * sizeof(int); free(ds->databuff.ib); ds->databuff.ib = NULL; freed += siz; } } if (typ & ATTY_FLOAT) { if (ds->databuff.fb) { siz = nsiz * sizeof(float); free(ds->databuff.fb); ds->databuff.fb = NULL; freed += siz; } } Tot_mem -= freed; ds->numbytes -= freed; if (freed) { G_debug(5, "free_data_buffs(): freed data from id no. %d", ds->data_id); G_debug(5, "free_data_buffs(): %.3f Kbytes freed, current total = %.3f", freed / 1000., Tot_mem / 1000.); } return (freed); } /*! \brief Allocates correct buffer according to type, keeps track of total mem \todo add ATTY_CONST \param id dataset id \param dims array of dimensions \param ndims number of dimensions \param type data type \return amount of allocated memory */ size_t gsds_alloc_typbuff(int id, int *dims, int ndims, int type) { dataset *ds; int i; size_t siz = 1; if ((ds = get_dataset(id))) { /* free_data_buffs(ds); careful here - allowing > 1 type to coexist (for float -> color conv.) now also use this to allocate a null mask (then if not used, use gsds_free_data_buff(id, ATTY_NULL)) */ for (i = 0; i < ndims; i++) { ds->dims[i] = dims[i]; siz *= dims[i]; } switch (type) { case ATTY_NULL: if (ndims != 2) { /* higher dimension bitmaps not supported */ return 0; } if (NULL == (ds->databuff.nm = BM_create(dims[1], dims[0]))) { return 0; } siz = BM_get_map_size(ds->databuff.nm); break; case ATTY_MASK: if (ndims != 2) { /* higher dimension bitmaps not supported */ return (-1); } if (NULL == (ds->databuff.bm = BM_create(dims[1], dims[0]))) { return 0; } siz = BM_get_map_size(ds->databuff.bm); break; case ATTY_CHAR: siz *= sizeof(char); if (siz) { if (NULL == (ds->databuff.cb = (unsigned char *)G_malloc(siz))) { return 0; } } else { return 0; } break; case ATTY_SHORT: siz *= sizeof(short); if (siz) { if (NULL == (ds->databuff.sb = (short *)G_malloc(siz))) { return 0; } } else { return 0; } break; case ATTY_INT: siz *= sizeof(int); if (siz) { if (NULL == (ds->databuff.ib = (int *)G_malloc(siz))) { return 0; } } else { return 0; } break; case ATTY_FLOAT: siz *= sizeof(float); if (siz) { if (NULL == (ds->databuff.fb = (float *)G_malloc(siz))) { return 0; } } else { return 0; } break; default: return 0; } ds->changed = 0; /* starting with clean slate */ ds->need_reload = 1; ds->numbytes += siz; ds->ndims = ndims; Tot_mem += siz; G_debug(5, "gsds_alloc_typbuff(): %f Kbytes allocated, current total = %f", siz / 1000., Tot_mem / 1000.); return (siz); } return 0; } /*! \brief ADD \param id \return -1 on error \return */ int gsds_get_changed(int id) { dataset *ds; if ((ds = get_dataset(id))) { return ((int)ds->changed); } return (-1); } /*! \brief ADD \param id \param reason \return -1 on error \return */ int gsds_set_changed(int id, IFLAG reason) { dataset *ds; if ((ds = get_dataset(id))) { ds->changed = reason; } return (-1); } /*! \brief ADD \param id \return */ int gsds_get_type(int id) { dataset *ds; ds = get_dataset(id); return (get_type(ds)); }