glib-mini.c revision ad5a0ac8a11793472af654411a522270a4f1e5a1
1// Copyright 2014 The Android Open Source Project
2//
3// This software is licensed under the terms of the GNU General Public
4// License version 2, as published by the Free Software Foundation, and
5// may be copied, distributed, and modified under those terms.
6//
7// This program is distributed in the hope that it will be useful,
8// but WITHOUT ANY WARRANTY; without even the implied warranty of
9// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10// GNU General Public License for more details.
11
12#include <glib.h>
13
14#include <limits.h>
15#include <stdarg.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19
20// Print a panic message then exit the program immediately.
21void g_panic(const char* fmt, ...) {
22  va_list args;
23  fprintf(stderr, "MiniGLib:PANIC: ");
24  va_start(args, fmt);
25  vfprintf(stderr, fmt, args);
26  va_end(args);
27  exit(1);
28}
29
30
31// Heap allocation.
32
33void* g_malloc(size_t size) {
34  if (size == 0)
35    return NULL;
36
37  void* ptr = malloc(size);
38  if (ptr == NULL)
39    g_panic("Can't allocate %zu bytes!\n", size);
40
41  return ptr;
42}
43
44
45void* g_malloc0(size_t size) {
46  void* ptr = g_malloc(size);
47  if (ptr == NULL)
48    return NULL;
49
50  memset(ptr, 0, size);
51  return ptr;
52}
53
54
55void* g_realloc(void* ptr, size_t size) {
56  if (size == 0) {
57    g_free(ptr);
58    return NULL;
59  }
60  void* new_ptr = realloc(ptr, size);
61  if (new_ptr == NULL)
62    g_panic("Can't reallocate pointer %p to %zu bytes!\n", ptr, size);
63
64  return new_ptr;
65}
66
67
68void g_free(void* ptr) {
69  if (ptr)
70    free(ptr);
71}
72
73// Strings.
74
75char* g_strdup(const char* str) {
76  if (str == NULL)
77    return NULL;
78
79  size_t len = strlen(str);
80  char* copy = g_malloc(len + 1);
81  memcpy(copy, str, len + 1);
82  return copy;
83}
84
85
86char* g_strndup(const char* str, size_t size) {
87  const char *end = memchr(str, 0, size);
88  char *copy;
89
90  if (end)
91    size = end - str;
92
93  copy = g_malloc(size + 1);
94  memcpy(copy, str, size);
95  copy[size] = 0;
96  return copy;
97}
98
99
100char* g_strdup_printf(const char* fmt, ...) {
101  char* result;
102  va_list args;
103  va_start(args, fmt);
104  g_vasprintf(&result, fmt, args);
105  va_end(args);
106  return result;
107}
108
109
110char* g_strdup_vprintf(const char* fmt, va_list args) {
111  char* result;
112  g_vasprintf(&result, fmt, args);
113  return result;
114}
115
116
117int g_vasprintf(char** str, const char* fmt, va_list args) {
118#ifdef _WIN32
119  // On Win32, vsnprintf() is broken and always returns -1 in case of truncation,
120  // so make an educated guess, and try again if that fails with a larger pool.
121  size_t capacity = 128;
122  char* buffer = g_malloc(capacity);
123  for (;;) {
124    va_list args2;
125    va_copy(args2, args);
126    int len = vsnprintf(buffer, capacity, fmt, args2);
127    if (len >= 0) {
128      *str = buffer;
129      return len;
130    }
131    va_end(args2);
132
133    capacity *= 2;
134    if (capacity > INT_MAX)
135      g_panic("Formatted string is too long!\n");
136
137    buffer = g_realloc(buffer, capacity);
138  }
139#else
140  // On other systems, vsnprintf() works properly and returns the size of the
141  // formatted string without truncation.
142  va_list args2;
143  va_copy(args2, args);
144  int len = vsnprintf(NULL, 0, fmt, args2);
145  va_end(args2);
146  if (len < 0)
147    g_panic("Can't format string!\n");
148
149  char* result = g_malloc(len + 1);
150  va_copy(args2, args);
151  vsnprintf(result, (size_t)len, fmt, args2);
152  va_end(args2);
153
154  *str = result;
155  return len;
156#endif
157}
158
159gboolean g_str_equal(const void* v1, const void* v2) {
160  return !strcmp((const char*)v1, (const char*)v2);
161}
162
163guint g_str_hash(const void* str) {
164  const signed char* p = str;
165  guint hash = 5381U;
166
167  for (; *p; ++p)
168    hash = (hash << 5) + hash + (guint)*p;
169
170  return hash;
171}
172
173// Single-linked list
174
175static GSList* _g_slist_alloc(void) {
176  return (GSList*) g_malloc(sizeof(GSList));
177}
178
179void g_slist_free(GSList* list) {
180  while (list) {
181    GSList* next = list->next;
182    g_free(list);
183    list = next;
184  }
185}
186
187GSList* g_slist_last(GSList* list) {
188  while (list && list->next)
189    list = list->next;
190  return list;
191}
192
193GSList* g_slist_find(GSList* list, const void* data) {
194  while (list) {
195    if (list->data == data)
196      break;
197    list = list->next;
198  }
199  return list;
200}
201
202GSList* g_slist_append(GSList* list, void* data) {
203  GSList* new_list = _g_slist_alloc();
204  new_list->data = data;
205  new_list->next = NULL;
206
207  if (!list)
208    return new_list;
209
210  GSList* last = g_slist_last(list);
211  last->next = new_list;
212  return list;
213}
214
215GSList* g_slist_prepend(GSList* list, void* data) {
216  GSList* new_list = _g_slist_alloc();
217  new_list->data = data;
218  new_list->next = list;
219  return new_list;
220}
221
222GSList* g_slist_remove(GSList* list, const void* data) {
223  GSList** pnode = &list;
224  for (;;) {
225    GSList* node = *pnode;
226    if (!node)
227      break;
228    if (node->data == data) {
229      *pnode = node->next;
230      g_slist_free(node);
231      break;
232    }
233    pnode = &node->next;
234  }
235  return list;
236}
237
238void g_slist_foreach(GSList* list, GFunc func, void* user_data) {
239  while (list) {
240    GSList* next = list->next;
241    (*func)(list->data, user_data);
242    list = next;
243  }
244}
245
246//
247static GSList* g_slist_sort_merge(GSList* l1,
248                                  GSList* l2,
249                                  GFunc compare_func,
250                                  void* user_data) {
251  GSList* list = NULL;
252  GSList** tail = &list;
253
254  while (l1 && l2) {
255    int cmp = ((GCompareDataFunc) compare_func)(l1->data, l2->data, user_data);
256    if (cmp <= 0) {
257      *tail = l1;
258      tail = &l1->next;
259      l1 = l1->next;
260    } else {
261      *tail = l2;
262      tail = &l2->next;
263      l2 = l2->next;
264    }
265  }
266  *tail = l1 ? l1 : l2;
267
268  return list;
269}
270
271static GSList* g_slist_sort_real(GSList* list,
272                                 GFunc compare_func,
273                                 void* user_data) {
274
275  if (!list)
276    return NULL;
277  if (!list->next)
278    return list;
279
280  // Split list into two halves.
281  GSList* l1 = list;
282  GSList* l2 = list->next;
283
284  while (l2->next && l2->next->next) {
285    l2 = l2->next->next;
286    l1 = l1->next;
287  }
288  l2 = l1->next;
289  l1->next = NULL;
290
291  return g_slist_sort_merge(
292      g_slist_sort_real(list, compare_func, user_data),
293      g_slist_sort_real(l2, compare_func, user_data),
294      compare_func,
295      user_data);
296}
297
298GSList* g_slist_sort(GSList* list, GCompareFunc compare_func) {
299  return g_slist_sort_real(list, (GFunc) compare_func, NULL);
300}
301
302// Atomic operations
303
304#if !_WIN32
305// Note: Windows implementation in glib-mini-win32.c
306
307void g_atomic_int_inc(int volatile* atomic) {
308  __sync_fetch_and_add(atomic, 1);
309}
310
311gboolean g_atomic_int_dec_and_test(int volatile* atomic) {
312  return __sync_fetch_and_add(atomic, -1) == 1;
313}
314#endif  // !_WIN32
315
316// Hash Tables
317
318// This is a rather vanilla implementation, slightly simpler
319// than the GLib one, since QEMU doesn't require the full features:
320//
321// - Uses a single dynamic array of (key,value,hash) tuples.
322// - Array capacity is always 2^N
323// - No use of modulo primes for simplicity, we don't expect
324//   QEMU/QAPI to degenerate here.
325// - Dumb container only: doesn't own and free keys and values.
326// - No optimization for sets (i.e. when key == value for each entry).
327// - No iterators.
328//
329typedef struct {
330  gpointer key;
331  gpointer value;
332  guint    hash;
333} GHashEntry;
334
335#define HASH_UNUSED    0   // Must be 0, see _remove_all() below.
336#define HASH_TOMBSTONE 1
337#define HASH_IS_REAL(h)  ((h) >= 2)
338
339#define HASH_MIN_SHIFT  3
340#define HASH_MIN_CAPACITY  (1 << HASH_MIN_SHIFT)
341
342struct _GHashTable {
343  int ref_count;
344  int num_items;
345  int num_used;  // count of items + tombstones
346  int shift;
347  int capacity;
348  GHashEntry* entries;
349  GHashFunc hash_func;
350  GEqualFunc key_equal_func;
351};
352
353// Compute the hash value of a given key.
354static inline size_t
355_g_hash_table_hash(GHashTable* table, gconstpointer key) {
356  size_t hash = table->hash_func(key);
357  return HASH_IS_REAL(hash) ? hash : 2;
358}
359
360
361GHashTable* g_hash_table_new(GHashFunc hash_func,
362                             GEqualFunc key_equal_func) {
363  GHashTable* hash_table = g_new0(GHashTable, 1);
364
365  hash_table->ref_count = 1;
366  hash_table->num_items = 0;
367  hash_table->capacity = HASH_MIN_CAPACITY;
368  hash_table->entries = g_new0(GHashEntry, hash_table->capacity);
369  hash_table->hash_func = hash_func;
370  hash_table->key_equal_func = key_equal_func;
371
372  return hash_table;
373}
374
375
376static void _g_hash_table_remove_all(GHashTable* hash_table) {
377  // NOTE: This uses the fact that HASH_UNUSED is 0!
378  hash_table->num_items = 0;
379  memset(hash_table->entries,
380         0,
381         sizeof(hash_table->entries[0]) * hash_table->capacity);
382}
383
384
385GHashTable* g_hash_table_ref(GHashTable* hash_table) {
386  if (!hash_table)
387    return NULL;
388
389  g_atomic_int_inc(&hash_table->ref_count);
390  return hash_table;
391}
392
393
394void g_hash_table_unref(GHashTable* hash_table) {
395  if (!hash_table)
396    return;
397
398  if (!g_atomic_int_dec_and_test(&hash_table->ref_count))
399    return;
400
401  _g_hash_table_remove_all(hash_table);
402
403  g_free(hash_table->entries);
404  hash_table->capacity = 0;
405  hash_table->entries = NULL;
406
407  g_free(hash_table);
408}
409
410
411void g_hash_table_destroy(GHashTable* hash_table) {
412  if (!hash_table)
413    return;
414
415  _g_hash_table_remove_all(hash_table);
416  g_hash_table_unref(hash_table);
417}
418
419
420// Probe the hash table for |key|. If it is in the table, return the index
421// to the corresponding entry. Otherwise, return the index of an unused
422// or tombstone entry. Also sets |*key_hash| to the key hash value on
423// return.
424static guint _g_hash_table_lookup_index(GHashTable* hash_table,
425                                        gconstpointer key,
426                                        guint* key_hash) {
427  guint hash = _g_hash_table_hash(hash_table, key);
428  *key_hash = hash;
429
430  guint probe_mask = (hash_table->capacity - 1);
431  gint tombstone = -1;
432  guint probe_index = hash & probe_mask;
433  guint step = 0;
434
435  GHashEntry* probe = &hash_table->entries[probe_index];
436  while (probe->hash != HASH_UNUSED) {
437    if (probe->hash == hash) {
438      if (hash_table->key_equal_func) {
439        if (hash_table->key_equal_func(probe->key, key))
440          return probe_index;
441      } else if (probe->key == key) {
442        return probe_index;
443      }
444    } else if (probe->hash == HASH_TOMBSTONE && tombstone < 0) {
445      tombstone = (int)probe_index;
446    }
447
448    step++;
449    probe_index = (probe_index + step) & probe_mask;
450    probe = &hash_table->entries[probe_index];
451  }
452
453  if (tombstone >= 0)
454    return (guint)tombstone;
455
456  return probe_index;
457}
458
459
460void* g_hash_table_lookup(GHashTable* hash_table,
461                          const void* key) {
462  guint key_hash = HASH_UNUSED;
463  guint probe_index = _g_hash_table_lookup_index(hash_table, key, &key_hash);
464  GHashEntry* entry = &hash_table->entries[probe_index];
465
466  return HASH_IS_REAL(entry->hash) ? entry->value : NULL;
467}
468
469
470// Remove key/value pair at index position |i|.
471static void _g_hash_table_remove_index(GHashTable* hash_table,
472                                       int i) {
473  GHashEntry* entry = &hash_table->entries[i];
474  entry->hash = HASH_TOMBSTONE;
475  entry->key = NULL;
476  entry->value = NULL;
477}
478
479
480gboolean g_hash_table_remove(GHashTable* hash_table,
481                             const void* key) {
482  guint key_hash = HASH_UNUSED;
483  guint probe_index = _g_hash_table_lookup_index(hash_table, key, &key_hash);
484  GHashEntry* entry = &hash_table->entries[probe_index];
485  if (!HASH_IS_REAL(entry->hash))
486    return 0;
487
488  _g_hash_table_remove_index(hash_table, probe_index);
489  hash_table->num_items--;
490  return 1;
491}
492
493// Resize the hash table, this also gets rid of all tombstones.
494static void _g_hash_table_resize(GHashTable* hash_table) {
495  guint old_capacity = hash_table->capacity;
496
497  // Compute new shift from new size
498  guint new_size = hash_table->num_items * 2;
499  guint new_capacity = HASH_MIN_CAPACITY;
500  while (new_capacity < new_size)
501    new_capacity <<= 1;
502
503  GHashEntry* new_entries = g_new0(GHashEntry, new_capacity);
504  guint n;
505  for (n = 0; n < old_capacity; ++n) {
506    GHashEntry* old_entry = &hash_table->entries[n];
507    guint old_hash = old_entry->hash;
508
509    if (!HASH_IS_REAL(old_hash))
510      continue;
511
512    guint probe_mask = (new_capacity - 1);
513    guint probe_n = old_hash & probe_mask;
514    GHashEntry* probe = &new_entries[probe_n];
515    guint step = 0;
516    while (probe->hash != HASH_UNUSED) {
517      step++;
518      probe_n = (probe_n + step) & probe_mask;
519      probe = &new_entries[probe_n];
520    }
521    probe[0] = old_entry[0];
522  }
523
524  g_free(hash_table->entries);
525  hash_table->entries = new_entries;
526  hash_table->capacity = new_capacity;
527  hash_table->num_used = hash_table->num_items;
528}
529
530// Resize the hash table if needed.
531static void _g_hash_table_maybe_resize(GHashTable* hash_table) {
532  guint used = hash_table->num_used;
533  guint count = hash_table->num_items;
534  guint capacity = hash_table->capacity;
535  if ((capacity > count * 4 && capacity > HASH_MIN_CAPACITY) ||
536      capacity < used + (used / 16)) {
537    _g_hash_table_resize(hash_table);
538  }
539}
540
541static void _g_hash_table_insert_index(GHashTable* hash_table,
542                                       guint key_index,
543                                       guint new_key_hash,
544                                       gpointer new_key,
545                                       gpointer new_value) {
546  GHashEntry* entry = &hash_table->entries[key_index];
547  guint old_hash = entry->hash;
548
549  entry->key = new_key;
550  entry->value = new_value;
551  entry->hash = new_key_hash;
552
553  if (HASH_IS_REAL(old_hash)) {
554    // Simple replacement, exit immediately.
555    return;
556  }
557
558  hash_table->num_items++;
559  if (old_hash == HASH_TOMBSTONE) {
560    // No need to resize when replacing a tombstone.
561    return;
562  }
563
564  hash_table->num_used++;
565  _g_hash_table_maybe_resize(hash_table);
566}
567
568void g_hash_table_insert(GHashTable* hash_table,
569                         void* key,
570                         void* value) {
571  guint key_hash;
572  guint key_index =
573      _g_hash_table_lookup_index(hash_table, key, &key_hash);
574
575  _g_hash_table_insert_index(hash_table, key_index, key_hash, key, value);
576}
577
578
579void g_hash_table_foreach(GHashTable* hash_table,
580                          GHFunc func,
581                          gpointer user_data) {
582  guint n;
583  for (n = 0; n < hash_table->capacity; ++n) {
584    GHashEntry* entry = &hash_table->entries[n];
585    if (HASH_IS_REAL(entry->hash))
586      (*func)(entry->key, entry->value, user_data);
587  }
588}
589
590
591gpointer g_hash_table_find(GHashTable* hash_table,
592                           GHRFunc predicate,
593                           gpointer user_data) {
594  guint n;
595  for (n = 0; n < hash_table->capacity; ++n) {
596    GHashEntry* entry = &hash_table->entries[n];
597    if (HASH_IS_REAL(entry->hash) &&
598        (*predicate)(entry->key, entry->value, user_data)) {
599      return entry->value;
600    }
601  }
602  return NULL;
603}
604
605
606guint g_hash_table_size(GHashTable* hash_table) {
607  return hash_table->num_items;
608}
609