| // Copyright 2014 The Android Open Source Project |
| // |
| // This software is licensed under the terms of the GNU General Public |
| // License version 2, as published by the Free Software Foundation, and |
| // may be copied, distributed, and modified under those terms. |
| // |
| // This program is distributed in the hope that it will be useful, |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| // GNU General Public License for more details. |
| |
| #include <glib.h> |
| |
| #include <limits.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdint.h> |
| #include <string.h> |
| |
| // Print a panic message then exit the program immediately. |
| void g_critical(const char* fmt, ...) { |
| va_list args; |
| fprintf(stderr, "CRITICAL: "); |
| va_start(args, fmt); |
| vfprintf(stderr, fmt, args); |
| va_end(args); |
| } |
| |
| void g_panic(const char* fmt, ...) { |
| va_list args; |
| fprintf(stderr, "PANIC: "); |
| va_start(args, fmt); |
| vfprintf(stderr, fmt, args); |
| va_end(args); |
| exit(1); |
| } |
| |
| // Heap allocation. |
| |
| void* g_malloc(size_t size) { |
| if (size == 0) |
| return NULL; |
| |
| void* ptr = malloc(size); |
| if (ptr == NULL) |
| g_panic("Can't allocate %zu bytes!\n", size); |
| |
| return ptr; |
| } |
| |
| |
| void* g_malloc0(size_t size) { |
| void* ptr = g_malloc(size); |
| if (ptr == NULL) |
| return NULL; |
| |
| memset(ptr, 0, size); |
| return ptr; |
| } |
| |
| |
| void* g_realloc(void* ptr, size_t size) { |
| if (size == 0) { |
| g_free(ptr); |
| return NULL; |
| } |
| void* new_ptr = realloc(ptr, size); |
| if (new_ptr == NULL) |
| g_panic("Can't reallocate pointer %p to %zu bytes!\n", ptr, size); |
| |
| return new_ptr; |
| } |
| |
| |
| void g_free(void* ptr) { |
| if (ptr) |
| free(ptr); |
| } |
| |
| // Strings. |
| |
| char* g_strdup(const char* str) { |
| if (str == NULL) |
| return NULL; |
| |
| size_t len = strlen(str); |
| char* copy = g_malloc(len + 1); |
| memcpy(copy, str, len + 1); |
| return copy; |
| } |
| |
| |
| char* g_strndup(const char* str, size_t size) { |
| const char *end = memchr(str, 0, size); |
| char *copy; |
| |
| if (end) |
| size = end - str; |
| |
| copy = g_malloc(size + 1); |
| memcpy(copy, str, size); |
| copy[size] = 0; |
| return copy; |
| } |
| |
| |
| char* g_strdup_printf(const char* fmt, ...) { |
| char* result; |
| va_list args; |
| va_start(args, fmt); |
| g_vasprintf(&result, fmt, args); |
| va_end(args); |
| return result; |
| } |
| |
| |
| char* g_strdup_vprintf(const char* fmt, va_list args) { |
| char* result; |
| g_vasprintf(&result, fmt, args); |
| return result; |
| } |
| |
| |
| int g_vasprintf(char** str, const char* fmt, va_list args) { |
| #ifdef _WIN32 |
| // On Win32, vsnprintf() is broken and always returns -1 in case of truncation, |
| // so make an educated guess, and try again if that fails with a larger pool. |
| size_t capacity = 128; |
| char* buffer = g_malloc(capacity); |
| for (;;) { |
| va_list args2; |
| va_copy(args2, args); |
| int len = vsnprintf(buffer, capacity, fmt, args2); |
| if (len >= 0) { |
| *str = buffer; |
| return len; |
| } |
| va_end(args2); |
| |
| capacity *= 2; |
| if (capacity > INT_MAX) |
| g_panic("Formatted string is too long!\n"); |
| |
| buffer = g_realloc(buffer, capacity); |
| } |
| #else |
| // On other systems, vsnprintf() works properly and returns the size of the |
| // formatted string without truncation. |
| va_list args2; |
| va_copy(args2, args); |
| int len = vsnprintf(NULL, 0, fmt, args2); |
| va_end(args2); |
| if (len < 0) |
| g_panic("Can't format string!\n"); |
| |
| char* result = g_malloc(len + 1); |
| va_copy(args2, args); |
| vsnprintf(result, (size_t)len, fmt, args2); |
| va_end(args2); |
| |
| *str = result; |
| return len; |
| #endif |
| } |
| |
| char** g_strsplit(const char* string, const char* delim, int max_tokens) { |
| // Sanity checks. |
| if (!string || !delim || !*delim) |
| return NULL; |
| |
| if (max_tokens < 1) |
| max_tokens = INT_MAX; |
| |
| // Create empty list of results. |
| GSList* string_list = NULL; |
| guint n = 0; |
| |
| if (*string) { |
| // Input list is not empty, so try to split it. |
| const char* remainder = string; |
| const char* s = strstr(remainder, delim); |
| if (s) { |
| size_t delim_len = strlen(delim); |
| while (--max_tokens && s) { |
| size_t len = s - remainder; |
| string_list = g_slist_prepend(string_list, g_strndup(remainder, len)); |
| n++; |
| remainder = s + delim_len; |
| s = strstr(remainder, delim); |
| } |
| } |
| n++; |
| string_list = g_slist_prepend(string_list, g_strdup(remainder)); |
| } |
| |
| // Convert list into NULL-terminated vector. |
| char** result = g_new(char*, n + 1); |
| result[n--] = NULL; |
| GSList* slist = string_list; |
| while (slist) { |
| result[n--] = slist->data; |
| slist = slist->next; |
| } |
| g_slist_free(string_list); |
| |
| return result; |
| } |
| |
| void g_strfreev(char** strings) { |
| guint n; |
| for (n = 0; strings[n]; ++n) { |
| g_free(strings[n]); |
| } |
| g_free(strings); |
| } |
| |
| gboolean g_str_equal(const void* v1, const void* v2) { |
| return !strcmp((const char*)v1, (const char*)v2); |
| } |
| |
| guint g_str_hash(const void* str) { |
| const signed char* p = str; |
| guint hash = 5381U; |
| |
| for (; *p; ++p) |
| hash = (hash << 5) + hash + (guint)*p; |
| |
| return hash; |
| } |
| |
| // Single-linked list |
| |
| static GSList* _g_slist_alloc(void) { |
| return (GSList*) g_malloc(sizeof(GSList)); |
| } |
| |
| void g_slist_free(GSList* list) { |
| while (list) { |
| GSList* next = list->next; |
| g_free(list); |
| list = next; |
| } |
| } |
| |
| GSList* g_slist_last(GSList* list) { |
| while (list && list->next) |
| list = list->next; |
| return list; |
| } |
| |
| GSList* g_slist_find(GSList* list, const void* data) { |
| while (list) { |
| if (list->data == data) |
| break; |
| list = list->next; |
| } |
| return list; |
| } |
| |
| GSList* g_slist_append(GSList* list, void* data) { |
| GSList* new_list = _g_slist_alloc(); |
| new_list->data = data; |
| new_list->next = NULL; |
| |
| if (!list) |
| return new_list; |
| |
| GSList* last = g_slist_last(list); |
| last->next = new_list; |
| return list; |
| } |
| |
| GSList* g_slist_prepend(GSList* list, void* data) { |
| GSList* new_list = _g_slist_alloc(); |
| new_list->data = data; |
| new_list->next = list; |
| return new_list; |
| } |
| |
| GSList* g_slist_remove(GSList* list, const void* data) { |
| GSList** pnode = &list; |
| for (;;) { |
| GSList* node = *pnode; |
| if (!node) |
| break; |
| if (node->data == data) { |
| *pnode = node->next; |
| g_slist_free(node); |
| break; |
| } |
| pnode = &node->next; |
| } |
| return list; |
| } |
| |
| void g_slist_foreach(GSList* list, GFunc func, void* user_data) { |
| while (list) { |
| GSList* next = list->next; |
| (*func)(list->data, user_data); |
| list = next; |
| } |
| } |
| |
| // |
| static GSList* g_slist_sort_merge(GSList* l1, |
| GSList* l2, |
| GFunc compare_func, |
| void* user_data) { |
| GSList* list = NULL; |
| GSList** tail = &list; |
| |
| while (l1 && l2) { |
| int cmp = ((GCompareDataFunc) compare_func)(l1->data, l2->data, user_data); |
| if (cmp <= 0) { |
| *tail = l1; |
| tail = &l1->next; |
| l1 = l1->next; |
| } else { |
| *tail = l2; |
| tail = &l2->next; |
| l2 = l2->next; |
| } |
| } |
| *tail = l1 ? l1 : l2; |
| |
| return list; |
| } |
| |
| static GSList* g_slist_sort_real(GSList* list, |
| GFunc compare_func, |
| void* user_data) { |
| |
| if (!list) |
| return NULL; |
| if (!list->next) |
| return list; |
| |
| // Split list into two halves. |
| GSList* l1 = list; |
| GSList* l2 = list->next; |
| |
| while (l2->next && l2->next->next) { |
| l2 = l2->next->next; |
| l1 = l1->next; |
| } |
| l2 = l1->next; |
| l1->next = NULL; |
| |
| return g_slist_sort_merge( |
| g_slist_sort_real(list, compare_func, user_data), |
| g_slist_sort_real(l2, compare_func, user_data), |
| compare_func, |
| user_data); |
| } |
| |
| GSList* g_slist_sort(GSList* list, GCompareFunc compare_func) { |
| return g_slist_sort_real(list, (GFunc) compare_func, NULL); |
| } |
| |
| // Atomic operations |
| |
| #if !_WIN32 |
| // Note: Windows implementation in glib-mini-win32.c |
| |
| void g_atomic_int_inc(int volatile* atomic) { |
| __sync_fetch_and_add(atomic, 1); |
| } |
| |
| gboolean g_atomic_int_dec_and_test(int volatile* atomic) { |
| return __sync_fetch_and_add(atomic, -1) == 1; |
| } |
| #endif // !_WIN32 |
| |
| // Hash Tables |
| |
| // This is a rather vanilla implementation, slightly simpler |
| // than the GLib one, since QEMU doesn't require the full features: |
| // |
| // - Uses a single dynamic array of (key,value,hash) tuples. |
| // - Array capacity is always 2^N |
| // - No use of modulo primes for simplicity, we don't expect |
| // QEMU/QAPI to degenerate here. |
| // - Dumb container only: doesn't own and free keys and values. |
| // - No optimization for sets (i.e. when key == value for each entry). |
| // - No iterators. |
| // |
| typedef struct { |
| gpointer key; |
| gpointer value; |
| guint hash; |
| } GHashEntry; |
| |
| #define HASH_UNUSED 0 // Must be 0, see _remove_all() below. |
| #define HASH_TOMBSTONE 1 |
| #define HASH_IS_REAL(h) ((h) >= 2) |
| |
| #define HASH_MIN_SHIFT 3 |
| #define HASH_MIN_CAPACITY (1 << HASH_MIN_SHIFT) |
| |
| #define HASH_LOAD_SCALE 1024 |
| #define HASH_MIN_LOAD 256 |
| #define HASH_MAX_LOAD 768 |
| |
| struct _GHashTable { |
| int ref_count; |
| int num_items; |
| int num_used; // count of items + tombstones |
| int capacity; |
| GHashEntry* entries; |
| GHashFunc hash_func; |
| GEqualFunc key_equal_func; |
| }; |
| |
| // Default hash function for pointers. |
| static guint _gpointer_hash(gconstpointer key) { |
| return (guint)(uintptr_t)key; |
| } |
| |
| // Compute the hash value of a given key. |
| static inline size_t |
| _g_hash_table_hash(GHashTable* table, gconstpointer key) { |
| size_t hash = table->hash_func(key); |
| return HASH_IS_REAL(hash) ? hash : 2; |
| } |
| |
| |
| GHashTable* g_hash_table_new(GHashFunc hash_func, |
| GEqualFunc key_equal_func) { |
| GHashTable* hash_table = g_new0(GHashTable, 1); |
| |
| hash_table->ref_count = 1; |
| hash_table->num_items = 0; |
| hash_table->capacity = HASH_MIN_CAPACITY; |
| hash_table->entries = g_new0(GHashEntry, hash_table->capacity); |
| hash_table->hash_func = hash_func ? hash_func : &_gpointer_hash; |
| hash_table->key_equal_func = key_equal_func; |
| |
| return hash_table; |
| } |
| |
| |
| static void _g_hash_table_remove_all(GHashTable* hash_table) { |
| // NOTE: This uses the fact that HASH_UNUSED is 0! |
| hash_table->num_items = 0; |
| memset(hash_table->entries, |
| 0, |
| sizeof(hash_table->entries[0]) * hash_table->capacity); |
| } |
| |
| |
| GHashTable* g_hash_table_ref(GHashTable* hash_table) { |
| if (!hash_table) |
| return NULL; |
| |
| g_atomic_int_inc(&hash_table->ref_count); |
| return hash_table; |
| } |
| |
| |
| void g_hash_table_unref(GHashTable* hash_table) { |
| if (!hash_table) |
| return; |
| |
| if (!g_atomic_int_dec_and_test(&hash_table->ref_count)) |
| return; |
| |
| _g_hash_table_remove_all(hash_table); |
| |
| g_free(hash_table->entries); |
| hash_table->capacity = 0; |
| hash_table->entries = NULL; |
| |
| g_free(hash_table); |
| } |
| |
| |
| void g_hash_table_destroy(GHashTable* hash_table) { |
| if (!hash_table) |
| return; |
| |
| _g_hash_table_remove_all(hash_table); |
| g_hash_table_unref(hash_table); |
| } |
| |
| |
| // Probe the hash table for |key|. If it is in the table, return the index |
| // to the corresponding entry. Otherwise, return the index of an unused |
| // or tombstone entry. Also sets |*key_hash| to the key hash value on |
| // return. |
| static guint _g_hash_table_lookup_index(GHashTable* hash_table, |
| gconstpointer key, |
| guint* key_hash) { |
| guint hash = _g_hash_table_hash(hash_table, key); |
| *key_hash = hash; |
| |
| guint probe_mask = (hash_table->capacity - 1); |
| gint tombstone = -1; |
| guint probe_index = hash & probe_mask; |
| guint step = 0; |
| |
| GHashEntry* probe = &hash_table->entries[probe_index]; |
| while (probe->hash != HASH_UNUSED) { |
| if (probe->hash == hash) { |
| if (hash_table->key_equal_func) { |
| if (hash_table->key_equal_func(probe->key, key)) |
| return probe_index; |
| } else if (probe->key == key) { |
| return probe_index; |
| } |
| } else if (probe->hash == HASH_TOMBSTONE && tombstone < 0) { |
| tombstone = (int)probe_index; |
| } |
| |
| step++; |
| probe_index = (probe_index + step) & probe_mask; |
| probe = &hash_table->entries[probe_index]; |
| } |
| |
| if (tombstone >= 0) |
| return (guint)tombstone; |
| |
| return probe_index; |
| } |
| |
| |
| void* g_hash_table_lookup(GHashTable* hash_table, |
| const void* key) { |
| guint key_hash = HASH_UNUSED; |
| guint probe_index = _g_hash_table_lookup_index(hash_table, key, &key_hash); |
| GHashEntry* entry = &hash_table->entries[probe_index]; |
| |
| return HASH_IS_REAL(entry->hash) ? entry->value : NULL; |
| } |
| |
| |
| // Remove key/value pair at index position |i|. |
| static void _g_hash_table_remove_index(GHashTable* hash_table, |
| int i) { |
| GHashEntry* entry = &hash_table->entries[i]; |
| entry->hash = HASH_TOMBSTONE; |
| entry->key = NULL; |
| entry->value = NULL; |
| } |
| |
| |
| gboolean g_hash_table_remove(GHashTable* hash_table, |
| const void* key) { |
| guint key_hash = HASH_UNUSED; |
| guint probe_index = _g_hash_table_lookup_index(hash_table, key, &key_hash); |
| GHashEntry* entry = &hash_table->entries[probe_index]; |
| if (!HASH_IS_REAL(entry->hash)) |
| return 0; |
| |
| _g_hash_table_remove_index(hash_table, probe_index); |
| hash_table->num_items--; |
| return 1; |
| } |
| |
| // Resize the hash table, this also gets rid of all tombstones. |
| static void _g_hash_table_resize(GHashTable* hash_table) { |
| guint old_capacity = hash_table->capacity; |
| |
| // Compute new capacity from new size |
| guint new_size = hash_table->num_items * 2; |
| guint new_capacity = HASH_MIN_CAPACITY; |
| while (new_capacity < new_size) |
| new_capacity <<= 1; |
| |
| GHashEntry* new_entries = g_new0(GHashEntry, new_capacity); |
| guint n; |
| for (n = 0; n < old_capacity; ++n) { |
| GHashEntry* old_entry = &hash_table->entries[n]; |
| guint old_hash = old_entry->hash; |
| |
| if (!HASH_IS_REAL(old_hash)) |
| continue; |
| |
| guint probe_mask = (new_capacity - 1); |
| guint probe_n = old_hash & probe_mask; |
| GHashEntry* probe = &new_entries[probe_n]; |
| guint step = 0; |
| while (probe->hash != HASH_UNUSED) { |
| step++; |
| probe_n = (probe_n + step) & probe_mask; |
| probe = &new_entries[probe_n]; |
| } |
| probe[0] = old_entry[0]; |
| } |
| |
| g_free(hash_table->entries); |
| hash_table->entries = new_entries; |
| hash_table->capacity = new_capacity; |
| hash_table->num_used = hash_table->num_items; |
| } |
| |
| // Resize the hash table if needed. |
| static void _g_hash_table_maybe_resize(GHashTable* hash_table) { |
| guint count = hash_table->num_items; |
| guint capacity = hash_table->capacity; |
| // Computations explained. |
| // |
| // load < min_load i.e. |
| // => used / capacity < min_load |
| // => used < min_load * capacity |
| // => used * HASH_SCALE < HASH_MIN_LOAD * capacity |
| // |
| // load > max_load |
| // => used / capacity > max_load |
| // => used * HASH_SCALER > HASH_MAX_LOAD * capacity |
| uint64_t load = (uint64_t)count * HASH_LOAD_SCALE; |
| uint64_t min_load = (uint64_t)capacity * HASH_MIN_LOAD; |
| uint64_t max_load = (uint64_t)capacity * HASH_MAX_LOAD; |
| if (load < min_load || load > max_load) { |
| _g_hash_table_resize(hash_table); |
| } |
| } |
| |
| static void _g_hash_table_insert_index(GHashTable* hash_table, |
| guint key_index, |
| guint new_key_hash, |
| gpointer new_key, |
| gpointer new_value) { |
| GHashEntry* entry = &hash_table->entries[key_index]; |
| guint old_hash = entry->hash; |
| |
| entry->key = new_key; |
| entry->value = new_value; |
| entry->hash = new_key_hash; |
| |
| if (HASH_IS_REAL(old_hash)) { |
| // Simple replacement, exit immediately. |
| return; |
| } |
| |
| hash_table->num_items++; |
| if (old_hash == HASH_TOMBSTONE) { |
| // No need to resize when replacing a tombstone. |
| return; |
| } |
| |
| hash_table->num_used++; |
| _g_hash_table_maybe_resize(hash_table); |
| } |
| |
| void g_hash_table_insert(GHashTable* hash_table, |
| void* key, |
| void* value) { |
| guint key_hash; |
| guint key_index = |
| _g_hash_table_lookup_index(hash_table, key, &key_hash); |
| |
| _g_hash_table_insert_index(hash_table, key_index, key_hash, key, value); |
| } |
| |
| |
| void g_hash_table_foreach(GHashTable* hash_table, |
| GHFunc func, |
| gpointer user_data) { |
| guint n; |
| for (n = 0; n < hash_table->capacity; ++n) { |
| GHashEntry* entry = &hash_table->entries[n]; |
| if (HASH_IS_REAL(entry->hash)) |
| (*func)(entry->key, entry->value, user_data); |
| } |
| } |
| |
| |
| gpointer g_hash_table_find(GHashTable* hash_table, |
| GHRFunc predicate, |
| gpointer user_data) { |
| guint n; |
| for (n = 0; n < hash_table->capacity; ++n) { |
| GHashEntry* entry = &hash_table->entries[n]; |
| if (HASH_IS_REAL(entry->hash) && |
| (*predicate)(entry->key, entry->value, user_data)) { |
| return entry->value; |
| } |
| } |
| return NULL; |
| } |
| |
| |
| guint g_hash_table_size(GHashTable* hash_table) { |
| return hash_table->num_items; |
| } |
| |
| // Queues |
| |
| struct _GQueueNode { |
| void* data; |
| GQueueNode* next; |
| GQueueNode* prev; |
| }; |
| |
| static inline GQueueNode* _g_queue_node_alloc(void) { |
| return g_new0(GQueueNode, 1); |
| } |
| |
| static void inline _g_queue_node_free(GQueueNode* node) { |
| g_free(node); |
| } |
| |
| GQueue* g_queue_new(void) { |
| GQueue* queue = g_new0(GQueue, 1); |
| return queue; |
| } |
| |
| void g_queue_free(GQueue* queue) { |
| GQueueNode* node = queue->head; |
| while (node) { |
| GQueueNode* next = node->next; |
| _g_queue_node_free(node); |
| node = next; |
| } |
| queue->head = queue->tail = NULL; |
| queue->length = 0; |
| g_free(queue); |
| } |
| |
| gboolean g_queue_is_empty(GQueue* queue) { |
| return queue->head == NULL; |
| } |
| |
| void g_queue_push_tail(GQueue* queue, void* data) { |
| GQueueNode* node = _g_queue_node_alloc(); |
| node->data = data; |
| node->next = NULL; |
| node->prev = queue->tail; |
| queue->tail = node; |
| queue->length++; |
| } |
| |
| void* g_queue_peek_head(GQueue* queue) { |
| return (queue->head) ? queue->head->data : NULL; |
| } |
| |
| void* g_queue_peek_tail(GQueue* queue) { |
| return (queue->tail) ? queue->tail->data : NULL; |
| } |
| |
| void* g_queue_pop_head(GQueue* queue) { |
| GQueueNode* head = queue->head; |
| if (!head) |
| return NULL; |
| |
| void* result = head->data; |
| |
| if (head->next) { |
| queue->head = head->next; |
| head->next->prev = NULL; |
| } else { |
| queue->head = NULL; |
| queue->tail = NULL; |
| } |
| queue->length--; |
| |
| return result; |
| } |