BEAST/BSE - Better Audio System and Sound Engine  0.8.2
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
gbsearcharray.hh
Go to the documentation of this file.
00001  // CC0 Public Domain: http://creativecommons.org/publicdomain/zero/1.0/
00002 #ifndef __G_BSEARCH_ARRAY_H__
00003 #define __G_BSEARCH_ARRAY_H__
00004 
00005 #include <sfi/glib-extra.hh>
00006 #include <string.h>
00007 
00008 
00009 G_BEGIN_DECLS   /* c++ guards */
00010 
00011 /* this implementation is intended to be usable in third-party code
00012  * simply by pasting the contents of this file. as such, the
00013  * implementation needs to be self-contained within this file.
00014  */
00015 
00016 /* convenience macro to avoid signed overflow for value comparisions */
00017 #define G_BSEARCH_ARRAY_CMP(v1,v2) ((v1) > (v2) ? +1 : (v1) == (v2) ? 0 : -1)
00018 
00019 
00020 /* --- typedefs --- */
00021 typedef gint  (*GBSearchCompareFunc) (gconstpointer bsearch_node1, /* key */
00022                                       gconstpointer bsearch_node2);
00023 typedef enum
00024 {
00025   G_BSEARCH_ARRAY_ALIGN_POWER2  = 1 << 0, /* align memory to power2 sizes */
00026   G_BSEARCH_ARRAY_AUTO_SHRINK  = 1 << 1   /* shrink array upon removal */
00027 } GBSearchArrayFlags;
00028 
00029 
00030 /* --- structures --- */
00031 typedef struct
00032 {
00033   guint               sizeof_node;
00034   GBSearchCompareFunc cmp_nodes;
00035   guint               flags;
00036 } GBSearchConfig;
00037 typedef union
00038 {
00039   guint    n_nodes;
00040   /*< private >*/
00041   gpointer alignment_dummy1;
00042   glong    alignment_dummy2;
00043   gdouble  alignment_dummy3;
00044 } GBSearchArray;
00045 
00046 
00047 /* --- public API --- */
00048 static inline GBSearchArray*  g_bsearch_array_create      (const GBSearchConfig *bconfig);
00049 static inline gpointer        g_bsearch_array_get_nth     (GBSearchArray        *barray,
00050                                                            const GBSearchConfig *bconfig,
00051                                                            guint                 nth);
00052 static inline guint           g_bsearch_array_get_index   (GBSearchArray        *barray,
00053                                                            const GBSearchConfig *bconfig,
00054                                                            gconstpointer         node_in_array);
00055 static inline GBSearchArray*  g_bsearch_array_remove      (GBSearchArray        *barray,
00056                                                            const GBSearchConfig *bconfig,
00057                                                            guint                 index);
00058 static inline GBSearchArray*  g_bsearch_array_remove_node (GBSearchArray        *barray,
00059                                                            const GBSearchConfig *bconfig,
00060                                                            gconstpointer         node_in_array);
00061 /* provide uninitialized space at index for node insertion */
00062 static inline GBSearchArray*  g_bsearch_array_grow        (GBSearchArray        *barray,
00063                                                            const GBSearchConfig *bconfig,
00064                                                            guint                 index);
00065 /* insert key_node into array if it does not exist, otherwise do nothing */
00066 static inline GBSearchArray*  g_bsearch_array_insert      (GBSearchArray        *barray,
00067                                                            const GBSearchConfig *bconfig,
00068                                                            gconstpointer         key_node);
00069 /* insert key_node into array if it does not exist,
00070  * otherwise replace the existing node's contents with key_node
00071  */
00072 static inline GBSearchArray*  g_bsearch_array_replace     (GBSearchArray        *barray,
00073                                                            const GBSearchConfig *bconfig,
00074                                                            gconstpointer         key_node);
00075 static inline void            g_bsearch_array_free        (GBSearchArray        *barray,
00076                                                            const GBSearchConfig *bconfig);
00077 #define g_bsearch_array_get_n_nodes(barray)     (((GBSearchArray*) (barray))->n_nodes)
00078 
00079 /* g_bsearch_array_lookup():
00080  * return NULL or exact match node
00081  */
00082 #define g_bsearch_array_lookup(barray, bconfig, key_node)       \
00083     g_bsearch_array_lookup_fuzzy ((barray), (bconfig), (key_node), 0)
00084 
00085 /* g_bsearch_array_lookup_sibling():
00086  * return NULL for barray->n_nodes==0, otherwise return the
00087  * exact match node, or, if there's no such node, return the
00088  * node last visited, which is pretty close to an exact match
00089  * (will be one off into either direction).
00090  */
00091 #define g_bsearch_array_lookup_sibling(barray, bconfig, key_node)       \
00092     g_bsearch_array_lookup_fuzzy ((barray), (bconfig), (key_node), 1)
00093 
00094 /* g_bsearch_array_lookup_insertion():
00095  * return NULL for barray->n_nodes==0 or exact match, otherwise
00096  * return the node where key_node should be inserted (may be one
00097  * after end, i.e. g_bsearch_array_get_index(result) <= barray->n_nodes).
00098  */
00099 #define g_bsearch_array_lookup_insertion(barray, bconfig, key_node)     \
00100     g_bsearch_array_lookup_fuzzy ((barray), (bconfig), (key_node), 2)
00101 
00102 
00103 /* --- implementation --- */
00104 /* helper macro to cut down realloc()s */
00105 #ifdef  DISABLE_MEM_POOLS
00106 #define G_BSEARCH_UPPER_POWER2(n)       (n)
00107 #else   /* !DISABLE_MEM_POOLS */
00108 #define G_BSEARCH_UPPER_POWER2(n)       ((n) ? 1 << g_bit_storage ((n) - 1) : 0)
00109 #endif  /* !DISABLE_MEM_POOLS */
00110 #define G_BSEARCH_ARRAY_NODES(barray)    (((guint8*) (barray)) + sizeof (GBSearchArray))
00111 static inline GBSearchArray*
00112 g_bsearch_array_create (const GBSearchConfig *bconfig)
00113 {
00114   GBSearchArray *barray;
00115   guint size;
00116 
00117   g_return_val_if_fail (bconfig != NULL, NULL);
00118 
00119   size = sizeof (GBSearchArray) + bconfig->sizeof_node;
00120   if (bconfig->flags & G_BSEARCH_ARRAY_ALIGN_POWER2)
00121     size = G_BSEARCH_UPPER_POWER2 (size);
00122   barray = (GBSearchArray *) g_realloc (NULL, size);
00123   memset (barray, 0, sizeof (GBSearchArray));
00124 
00125   return barray;
00126 }
00127 static inline gpointer
00128 g_bsearch_array_lookup_fuzzy (GBSearchArray             *barray,
00129                               const GBSearchConfig      *bconfig,
00130                               gconstpointer              key_node,
00131                               const guint                sibling_or_after);
00132 static inline gpointer
00133 g_bsearch_array_lookup_fuzzy (GBSearchArray        *barray,
00134                               const GBSearchConfig *bconfig,
00135                               gconstpointer         key_node,
00136                               const guint           sibling_or_after)
00137 {
00138   GBSearchCompareFunc cmp_nodes = bconfig->cmp_nodes;
00139   guint8 *check = NULL, *nodes = G_BSEARCH_ARRAY_NODES (barray);
00140   guint n_nodes = barray->n_nodes, offs = 0;
00141   guint sizeof_node = bconfig->sizeof_node;
00142   gint cmp = 0;
00143 
00144   while (offs < n_nodes)
00145     {
00146       guint i = (offs + n_nodes) >> 1;
00147 
00148       check = nodes + i * sizeof_node;
00149       cmp = cmp_nodes (key_node, check);
00150       if (cmp == 0)
00151         return sibling_or_after > 1 ? NULL : check;
00152       else if (cmp < 0)
00153         n_nodes = i;
00154       else /* (cmp > 0) */
00155         offs = i + 1;
00156     }
00157 
00158   /* check is last mismatch, cmp > 0 indicates greater key */
00159   return G_LIKELY (!sibling_or_after) ? NULL : (sibling_or_after > 1 && cmp > 0) ? check + sizeof_node : check;
00160 }
00161 static inline gpointer
00162 g_bsearch_array_get_nth (GBSearchArray *barray, const GBSearchConfig *bconfig, guint nth)
00163 {
00164   return (G_LIKELY (nth < barray->n_nodes) ?
00165           G_BSEARCH_ARRAY_NODES (barray) + nth * bconfig->sizeof_node :
00166           NULL);
00167 }
00168 static inline guint
00169 g_bsearch_array_get_index (GBSearchArray        *barray,
00170                            const GBSearchConfig *bconfig,
00171                            gconstpointer         node_in_array)
00172 {
00173   guint distance = ((guint8*) node_in_array) - G_BSEARCH_ARRAY_NODES (barray);
00174 
00175   g_return_val_if_fail (node_in_array != NULL, barray->n_nodes);
00176 
00177   distance /= bconfig->sizeof_node;
00178 
00179   return MIN (distance, barray->n_nodes + 1); /* may return one after end */
00180 }
00181 static inline GBSearchArray*
00182 g_bsearch_array_grow (GBSearchArray        *barray,
00183                       const GBSearchConfig *bconfig,
00184                       guint                 index)
00185 {
00186   guint old_size = barray->n_nodes * bconfig->sizeof_node;
00187   guint new_size = old_size + bconfig->sizeof_node;
00188   guint8 *node;
00189 
00190   g_return_val_if_fail (index <= barray->n_nodes, NULL);
00191 
00192   if (G_UNLIKELY (bconfig->flags & G_BSEARCH_ARRAY_ALIGN_POWER2))
00193     {
00194       new_size = G_BSEARCH_UPPER_POWER2 (sizeof (GBSearchArray) + new_size);
00195       old_size = G_BSEARCH_UPPER_POWER2 (sizeof (GBSearchArray) + old_size);
00196       if (old_size != new_size)
00197         barray = (GBSearchArray *) g_realloc (barray, new_size);
00198     }
00199   else
00200     barray = (GBSearchArray *) g_realloc (barray, sizeof (GBSearchArray) + new_size);
00201   node = G_BSEARCH_ARRAY_NODES (barray) + index * bconfig->sizeof_node;
00202   g_memmove (node + bconfig->sizeof_node, node, (barray->n_nodes - index) * bconfig->sizeof_node);
00203   barray->n_nodes += 1;
00204   return barray;
00205 }
00206 static inline GBSearchArray*
00207 g_bsearch_array_insert (GBSearchArray        *barray,
00208                         const GBSearchConfig *bconfig,
00209                         gconstpointer         key_node)
00210 {
00211   guint8 *node;
00212 
00213   if (G_UNLIKELY (!barray->n_nodes))
00214     {
00215       barray = g_bsearch_array_grow (barray, bconfig, 0);
00216       node = G_BSEARCH_ARRAY_NODES (barray);
00217     }
00218   else
00219     {
00220       node = (guint8 *) g_bsearch_array_lookup_insertion (barray, bconfig, key_node);
00221       if (G_LIKELY (node))
00222         {
00223           guint index = g_bsearch_array_get_index (barray, bconfig, node);
00224 
00225           /* grow and insert */
00226           barray = g_bsearch_array_grow (barray, bconfig, index);
00227           node = G_BSEARCH_ARRAY_NODES (barray) + index * bconfig->sizeof_node;
00228         }
00229       else /* no insertion needed, node already there */
00230         return barray;
00231     }
00232   memcpy (node, key_node, bconfig->sizeof_node);
00233   return barray;
00234 }
00235 static inline GBSearchArray*
00236 g_bsearch_array_replace (GBSearchArray        *barray,
00237                          const GBSearchConfig *bconfig,
00238                          gconstpointer         key_node)
00239 {
00240   guint8 *node = (guint8 *) g_bsearch_array_lookup (barray, bconfig, key_node);
00241   if (G_LIKELY (node))  /* expected path */
00242     memcpy (node, key_node, bconfig->sizeof_node);
00243   else                  /* revert to insertion */
00244     barray = g_bsearch_array_insert (barray, bconfig, key_node);
00245   return barray;
00246 }
00247 static inline GBSearchArray*
00248 g_bsearch_array_remove (GBSearchArray        *barray,
00249                         const GBSearchConfig *bconfig,
00250                         guint                 index)
00251 {
00252   guint8 *node;
00253 
00254   g_return_val_if_fail (index < barray->n_nodes, NULL);
00255 
00256   barray->n_nodes -= 1;
00257   node = G_BSEARCH_ARRAY_NODES (barray) + index * bconfig->sizeof_node;
00258   g_memmove (node, node + bconfig->sizeof_node, (barray->n_nodes - index) * bconfig->sizeof_node);
00259   if (G_UNLIKELY (bconfig->flags & G_BSEARCH_ARRAY_AUTO_SHRINK))
00260     {
00261       guint new_size = barray->n_nodes * bconfig->sizeof_node;
00262       guint old_size = new_size + bconfig->sizeof_node;
00263 
00264       if (G_UNLIKELY (bconfig->flags & G_BSEARCH_ARRAY_ALIGN_POWER2))
00265         {
00266           new_size = G_BSEARCH_UPPER_POWER2 (sizeof (GBSearchArray) + new_size);
00267           old_size = G_BSEARCH_UPPER_POWER2 (sizeof (GBSearchArray) + old_size);
00268           if (old_size != new_size)
00269             barray = (GBSearchArray *) g_realloc (barray, new_size);
00270         }
00271       else
00272         barray = (GBSearchArray *) g_realloc (barray, sizeof (GBSearchArray) + new_size);
00273     }
00274   return barray;
00275 }
00276 static inline GBSearchArray*
00277 g_bsearch_array_remove_node (GBSearchArray        *barray,
00278                              const GBSearchConfig *bconfig,
00279                              gconstpointer         node_in_array)
00280 {
00281   return g_bsearch_array_remove (barray, bconfig, g_bsearch_array_get_index (barray, bconfig, node_in_array));
00282 }
00283 static inline void
00284 g_bsearch_array_free (GBSearchArray        *barray,
00285                       const GBSearchConfig *bconfig)
00286 {
00287   g_return_if_fail (barray != NULL);
00288 
00289   g_free (barray);
00290 }
00291 
00292 G_END_DECLS     /* c++ guards */
00293 
00294 #endif  /* !__G_BSEARCH_ARRAY_H__ */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines