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