BEAST/BSE - Better Audio System and Sound Engine
0.8.2
|
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__ */