• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <stdint.h>
19 #include <string.h>
20 
21 // Print a panic message then exit the program immediately.
g_critical(const char * fmt,...)22 void g_critical(const char* fmt, ...) {
23   va_list args;
24   fprintf(stderr, "CRITICAL: ");
25   va_start(args, fmt);
26   vfprintf(stderr, fmt, args);
27   va_end(args);
28 }
29 
g_panic(const char * fmt,...)30 void g_panic(const char* fmt, ...) {
31   va_list args;
32   fprintf(stderr, "PANIC: ");
33   va_start(args, fmt);
34   vfprintf(stderr, fmt, args);
35   va_end(args);
36   exit(1);
37 }
38 
39 // Heap allocation.
40 
g_malloc(size_t size)41 void* g_malloc(size_t size) {
42   if (size == 0)
43     return NULL;
44 
45   void* ptr = malloc(size);
46   if (ptr == NULL)
47     g_panic("Can't allocate %zu bytes!\n", size);
48 
49   return ptr;
50 }
51 
52 
g_malloc0(size_t size)53 void* g_malloc0(size_t size) {
54   void* ptr = g_malloc(size);
55   if (ptr == NULL)
56     return NULL;
57 
58   memset(ptr, 0, size);
59   return ptr;
60 }
61 
62 
g_realloc(void * ptr,size_t size)63 void* g_realloc(void* ptr, size_t size) {
64   if (size == 0) {
65     g_free(ptr);
66     return NULL;
67   }
68   void* new_ptr = realloc(ptr, size);
69   if (new_ptr == NULL)
70     g_panic("Can't reallocate pointer %p to %zu bytes!\n", ptr, size);
71 
72   return new_ptr;
73 }
74 
75 
g_free(void * ptr)76 void g_free(void* ptr) {
77   if (ptr)
78     free(ptr);
79 }
80 
81 // Strings.
82 
g_strdup(const char * str)83 char* g_strdup(const char* str) {
84   if (str == NULL)
85     return NULL;
86 
87   size_t len = strlen(str);
88   char* copy = g_malloc(len + 1);
89   memcpy(copy, str, len + 1);
90   return copy;
91 }
92 
93 
g_strndup(const char * str,size_t size)94 char* g_strndup(const char* str, size_t size) {
95   const char *end = memchr(str, 0, size);
96   char *copy;
97 
98   if (end)
99     size = end - str;
100 
101   copy = g_malloc(size + 1);
102   memcpy(copy, str, size);
103   copy[size] = 0;
104   return copy;
105 }
106 
107 
g_strdup_printf(const char * fmt,...)108 char* g_strdup_printf(const char* fmt, ...) {
109   char* result;
110   va_list args;
111   va_start(args, fmt);
112   g_vasprintf(&result, fmt, args);
113   va_end(args);
114   return result;
115 }
116 
117 
g_strdup_vprintf(const char * fmt,va_list args)118 char* g_strdup_vprintf(const char* fmt, va_list args) {
119   char* result;
120   g_vasprintf(&result, fmt, args);
121   return result;
122 }
123 
124 
g_vasprintf(char ** str,const char * fmt,va_list args)125 int g_vasprintf(char** str, const char* fmt, va_list args) {
126 #ifdef _WIN32
127   // On Win32, vsnprintf() is broken and always returns -1 in case of truncation,
128   // so make an educated guess, and try again if that fails with a larger pool.
129   size_t capacity = 128;
130   char* buffer = g_malloc(capacity);
131   for (;;) {
132     va_list args2;
133     va_copy(args2, args);
134     int len = vsnprintf(buffer, capacity, fmt, args2);
135     if (len >= 0) {
136       *str = buffer;
137       return len;
138     }
139     va_end(args2);
140 
141     capacity *= 2;
142     if (capacity > INT_MAX)
143       g_panic("Formatted string is too long!\n");
144 
145     buffer = g_realloc(buffer, capacity);
146   }
147 #else
148   // On other systems, vsnprintf() works properly and returns the size of the
149   // formatted string without truncation.
150   va_list args2;
151   va_copy(args2, args);
152   int len = vsnprintf(NULL, 0, fmt, args2);
153   va_end(args2);
154   if (len < 0)
155     g_panic("Can't format string!\n");
156 
157   char* result = g_malloc(len + 1);
158   va_copy(args2, args);
159   vsnprintf(result, (size_t)len, fmt, args2);
160   va_end(args2);
161 
162   *str = result;
163   return len;
164 #endif
165 }
166 
g_strsplit(const char * string,const char * delim,int max_tokens)167 char** g_strsplit(const char* string, const char* delim, int max_tokens) {
168   // Sanity checks.
169   if (!string || !delim || !*delim)
170     return NULL;
171 
172   if (max_tokens < 1)
173     max_tokens = INT_MAX;
174 
175   // Create empty list of results.
176   GSList* string_list = NULL;
177   guint n = 0;
178 
179   if (*string) {
180     // Input list is not empty, so try to split it.
181     const char* remainder = string;
182     const char* s = strstr(remainder, delim);
183     if (s) {
184       size_t delim_len = strlen(delim);
185       while (--max_tokens && s) {
186         size_t len = s - remainder;
187         string_list = g_slist_prepend(string_list, g_strndup(remainder, len));
188         n++;
189         remainder = s + delim_len;
190         s = strstr(remainder, delim);
191       }
192     }
193     n++;
194     string_list = g_slist_prepend(string_list, g_strdup(remainder));
195   }
196 
197   // Convert list into NULL-terminated vector.
198   char** result = g_new(char*, n + 1);
199   result[n--] = NULL;
200   GSList* slist = string_list;
201   while (slist) {
202     result[n--] = slist->data;
203     slist = slist->next;
204   }
205   g_slist_free(string_list);
206 
207   return result;
208 }
209 
g_strfreev(char ** strings)210 void g_strfreev(char** strings) {
211   guint n;
212   for (n = 0; strings[n]; ++n) {
213     g_free(strings[n]);
214   }
215   g_free(strings);
216 }
217 
g_str_equal(const void * v1,const void * v2)218 gboolean g_str_equal(const void* v1, const void* v2) {
219   return !strcmp((const char*)v1, (const char*)v2);
220 }
221 
g_str_hash(const void * str)222 guint g_str_hash(const void* str) {
223   const signed char* p = str;
224   guint hash = 5381U;
225 
226   for (; *p; ++p)
227     hash = (hash << 5) + hash + (guint)*p;
228 
229   return hash;
230 }
231 
232 // Single-linked list
233 
_g_slist_alloc(void)234 static GSList* _g_slist_alloc(void) {
235   return (GSList*) g_malloc(sizeof(GSList));
236 }
237 
g_slist_free(GSList * list)238 void g_slist_free(GSList* list) {
239   while (list) {
240     GSList* next = list->next;
241     g_free(list);
242     list = next;
243   }
244 }
245 
g_slist_last(GSList * list)246 GSList* g_slist_last(GSList* list) {
247   while (list && list->next)
248     list = list->next;
249   return list;
250 }
251 
g_slist_find(GSList * list,const void * data)252 GSList* g_slist_find(GSList* list, const void* data) {
253   while (list) {
254     if (list->data == data)
255       break;
256     list = list->next;
257   }
258   return list;
259 }
260 
g_slist_append(GSList * list,void * data)261 GSList* g_slist_append(GSList* list, void* data) {
262   GSList* new_list = _g_slist_alloc();
263   new_list->data = data;
264   new_list->next = NULL;
265 
266   if (!list)
267     return new_list;
268 
269   GSList* last = g_slist_last(list);
270   last->next = new_list;
271   return list;
272 }
273 
g_slist_prepend(GSList * list,void * data)274 GSList* g_slist_prepend(GSList* list, void* data) {
275   GSList* new_list = _g_slist_alloc();
276   new_list->data = data;
277   new_list->next = list;
278   return new_list;
279 }
280 
g_slist_remove(GSList * list,const void * data)281 GSList* g_slist_remove(GSList* list, const void* data) {
282   GSList** pnode = &list;
283   for (;;) {
284     GSList* node = *pnode;
285     if (!node)
286       break;
287     if (node->data == data) {
288       *pnode = node->next;
289       g_slist_free(node);
290       break;
291     }
292     pnode = &node->next;
293   }
294   return list;
295 }
296 
g_slist_foreach(GSList * list,GFunc func,void * user_data)297 void g_slist_foreach(GSList* list, GFunc func, void* user_data) {
298   while (list) {
299     GSList* next = list->next;
300     (*func)(list->data, user_data);
301     list = next;
302   }
303 }
304 
305 //
g_slist_sort_merge(GSList * l1,GSList * l2,GFunc compare_func,void * user_data)306 static GSList* g_slist_sort_merge(GSList* l1,
307                                   GSList* l2,
308                                   GFunc compare_func,
309                                   void* user_data) {
310   GSList* list = NULL;
311   GSList** tail = &list;
312 
313   while (l1 && l2) {
314     int cmp = ((GCompareDataFunc) compare_func)(l1->data, l2->data, user_data);
315     if (cmp <= 0) {
316       *tail = l1;
317       tail = &l1->next;
318       l1 = l1->next;
319     } else {
320       *tail = l2;
321       tail = &l2->next;
322       l2 = l2->next;
323     }
324   }
325   *tail = l1 ? l1 : l2;
326 
327   return list;
328 }
329 
g_slist_sort_real(GSList * list,GFunc compare_func,void * user_data)330 static GSList* g_slist_sort_real(GSList* list,
331                                  GFunc compare_func,
332                                  void* user_data) {
333 
334   if (!list)
335     return NULL;
336   if (!list->next)
337     return list;
338 
339   // Split list into two halves.
340   GSList* l1 = list;
341   GSList* l2 = list->next;
342 
343   while (l2->next && l2->next->next) {
344     l2 = l2->next->next;
345     l1 = l1->next;
346   }
347   l2 = l1->next;
348   l1->next = NULL;
349 
350   return g_slist_sort_merge(
351       g_slist_sort_real(list, compare_func, user_data),
352       g_slist_sort_real(l2, compare_func, user_data),
353       compare_func,
354       user_data);
355 }
356 
g_slist_sort(GSList * list,GCompareFunc compare_func)357 GSList* g_slist_sort(GSList* list, GCompareFunc compare_func) {
358   return g_slist_sort_real(list, (GFunc) compare_func, NULL);
359 }
360 
361 // Atomic operations
362 
363 #if !_WIN32
364 // Note: Windows implementation in glib-mini-win32.c
365 
g_atomic_int_inc(int volatile * atomic)366 void g_atomic_int_inc(int volatile* atomic) {
367   __sync_fetch_and_add(atomic, 1);
368 }
369 
g_atomic_int_dec_and_test(int volatile * atomic)370 gboolean g_atomic_int_dec_and_test(int volatile* atomic) {
371   return __sync_fetch_and_add(atomic, -1) == 1;
372 }
373 #endif  // !_WIN32
374 
375 // Hash Tables
376 
377 // This is a rather vanilla implementation, slightly simpler
378 // than the GLib one, since QEMU doesn't require the full features:
379 //
380 // - Uses a single dynamic array of (key,value,hash) tuples.
381 // - Array capacity is always 2^N
382 // - No use of modulo primes for simplicity, we don't expect
383 //   QEMU/QAPI to degenerate here.
384 // - Dumb container only: doesn't own and free keys and values.
385 // - No optimization for sets (i.e. when key == value for each entry).
386 // - No iterators.
387 //
388 typedef struct {
389   gpointer key;
390   gpointer value;
391   guint    hash;
392 } GHashEntry;
393 
394 #define HASH_UNUSED    0   // Must be 0, see _remove_all() below.
395 #define HASH_TOMBSTONE 1
396 #define HASH_IS_REAL(h)  ((h) >= 2)
397 
398 #define HASH_MIN_SHIFT  3
399 #define HASH_MIN_CAPACITY  (1 << HASH_MIN_SHIFT)
400 
401 #define HASH_LOAD_SCALE 1024
402 #define HASH_MIN_LOAD   256
403 #define HASH_MAX_LOAD   768
404 
405 struct _GHashTable {
406   int ref_count;
407   int num_items;
408   int num_used;  // count of items + tombstones
409   int capacity;
410   GHashEntry* entries;
411   GHashFunc hash_func;
412   GEqualFunc key_equal_func;
413 };
414 
415 // Default hash function for pointers.
_gpointer_hash(gconstpointer key)416 static guint _gpointer_hash(gconstpointer key) {
417     return (guint)(uintptr_t)key;
418 }
419 
420 // Compute the hash value of a given key.
421 static inline size_t
_g_hash_table_hash(GHashTable * table,gconstpointer key)422 _g_hash_table_hash(GHashTable* table, gconstpointer key) {
423   size_t hash = table->hash_func(key);
424   return HASH_IS_REAL(hash) ? hash : 2;
425 }
426 
427 
g_hash_table_new(GHashFunc hash_func,GEqualFunc key_equal_func)428 GHashTable* g_hash_table_new(GHashFunc hash_func,
429                              GEqualFunc key_equal_func) {
430   GHashTable* hash_table = g_new0(GHashTable, 1);
431 
432   hash_table->ref_count = 1;
433   hash_table->num_items = 0;
434   hash_table->capacity = HASH_MIN_CAPACITY;
435   hash_table->entries = g_new0(GHashEntry, hash_table->capacity);
436   hash_table->hash_func = hash_func ? hash_func : &_gpointer_hash;
437   hash_table->key_equal_func = key_equal_func;
438 
439   return hash_table;
440 }
441 
442 
_g_hash_table_remove_all(GHashTable * hash_table)443 static void _g_hash_table_remove_all(GHashTable* hash_table) {
444   // NOTE: This uses the fact that HASH_UNUSED is 0!
445   hash_table->num_items = 0;
446   memset(hash_table->entries,
447          0,
448          sizeof(hash_table->entries[0]) * hash_table->capacity);
449 }
450 
451 
g_hash_table_ref(GHashTable * hash_table)452 GHashTable* g_hash_table_ref(GHashTable* hash_table) {
453   if (!hash_table)
454     return NULL;
455 
456   g_atomic_int_inc(&hash_table->ref_count);
457   return hash_table;
458 }
459 
460 
g_hash_table_unref(GHashTable * hash_table)461 void g_hash_table_unref(GHashTable* hash_table) {
462   if (!hash_table)
463     return;
464 
465   if (!g_atomic_int_dec_and_test(&hash_table->ref_count))
466     return;
467 
468   _g_hash_table_remove_all(hash_table);
469 
470   g_free(hash_table->entries);
471   hash_table->capacity = 0;
472   hash_table->entries = NULL;
473 
474   g_free(hash_table);
475 }
476 
477 
g_hash_table_destroy(GHashTable * hash_table)478 void g_hash_table_destroy(GHashTable* hash_table) {
479   if (!hash_table)
480     return;
481 
482   _g_hash_table_remove_all(hash_table);
483   g_hash_table_unref(hash_table);
484 }
485 
486 
487 // Probe the hash table for |key|. If it is in the table, return the index
488 // to the corresponding entry. Otherwise, return the index of an unused
489 // or tombstone entry. Also sets |*key_hash| to the key hash value on
490 // return.
_g_hash_table_lookup_index(GHashTable * hash_table,gconstpointer key,guint * key_hash)491 static guint _g_hash_table_lookup_index(GHashTable* hash_table,
492                                         gconstpointer key,
493                                         guint* key_hash) {
494   guint hash = _g_hash_table_hash(hash_table, key);
495   *key_hash = hash;
496 
497   guint probe_mask = (hash_table->capacity - 1);
498   gint tombstone = -1;
499   guint probe_index = hash & probe_mask;
500   guint step = 0;
501 
502   GHashEntry* probe = &hash_table->entries[probe_index];
503   while (probe->hash != HASH_UNUSED) {
504     if (probe->hash == hash) {
505       if (hash_table->key_equal_func) {
506         if (hash_table->key_equal_func(probe->key, key))
507           return probe_index;
508       } else if (probe->key == key) {
509         return probe_index;
510       }
511     } else if (probe->hash == HASH_TOMBSTONE && tombstone < 0) {
512       tombstone = (int)probe_index;
513     }
514 
515     step++;
516     probe_index = (probe_index + step) & probe_mask;
517     probe = &hash_table->entries[probe_index];
518   }
519 
520   if (tombstone >= 0)
521     return (guint)tombstone;
522 
523   return probe_index;
524 }
525 
526 
g_hash_table_lookup(GHashTable * hash_table,const void * key)527 void* g_hash_table_lookup(GHashTable* hash_table,
528                           const void* key) {
529   guint key_hash = HASH_UNUSED;
530   guint probe_index = _g_hash_table_lookup_index(hash_table, key, &key_hash);
531   GHashEntry* entry = &hash_table->entries[probe_index];
532 
533   return HASH_IS_REAL(entry->hash) ? entry->value : NULL;
534 }
535 
536 
537 // Remove key/value pair at index position |i|.
_g_hash_table_remove_index(GHashTable * hash_table,int i)538 static void _g_hash_table_remove_index(GHashTable* hash_table,
539                                        int i) {
540   GHashEntry* entry = &hash_table->entries[i];
541   entry->hash = HASH_TOMBSTONE;
542   entry->key = NULL;
543   entry->value = NULL;
544 }
545 
546 
g_hash_table_remove(GHashTable * hash_table,const void * key)547 gboolean g_hash_table_remove(GHashTable* hash_table,
548                              const void* key) {
549   guint key_hash = HASH_UNUSED;
550   guint probe_index = _g_hash_table_lookup_index(hash_table, key, &key_hash);
551   GHashEntry* entry = &hash_table->entries[probe_index];
552   if (!HASH_IS_REAL(entry->hash))
553     return 0;
554 
555   _g_hash_table_remove_index(hash_table, probe_index);
556   hash_table->num_items--;
557   return 1;
558 }
559 
560 // Resize the hash table, this also gets rid of all tombstones.
_g_hash_table_resize(GHashTable * hash_table)561 static void _g_hash_table_resize(GHashTable* hash_table) {
562   guint old_capacity = hash_table->capacity;
563 
564   // Compute new capacity from new size
565   guint new_size = hash_table->num_items * 2;
566   guint new_capacity = HASH_MIN_CAPACITY;
567   while (new_capacity < new_size)
568     new_capacity <<= 1;
569 
570   GHashEntry* new_entries = g_new0(GHashEntry, new_capacity);
571   guint n;
572   for (n = 0; n < old_capacity; ++n) {
573     GHashEntry* old_entry = &hash_table->entries[n];
574     guint old_hash = old_entry->hash;
575 
576     if (!HASH_IS_REAL(old_hash))
577       continue;
578 
579     guint probe_mask = (new_capacity - 1);
580     guint probe_n = old_hash & probe_mask;
581     GHashEntry* probe = &new_entries[probe_n];
582     guint step = 0;
583     while (probe->hash != HASH_UNUSED) {
584       step++;
585       probe_n = (probe_n + step) & probe_mask;
586       probe = &new_entries[probe_n];
587     }
588     probe[0] = old_entry[0];
589   }
590 
591   g_free(hash_table->entries);
592   hash_table->entries = new_entries;
593   hash_table->capacity = new_capacity;
594   hash_table->num_used = hash_table->num_items;
595 }
596 
597 // Resize the hash table if needed.
_g_hash_table_maybe_resize(GHashTable * hash_table)598 static void _g_hash_table_maybe_resize(GHashTable* hash_table) {
599   guint count = hash_table->num_items;
600   guint capacity = hash_table->capacity;
601   // Computations explained.
602   //
603   // load < min_load i.e.
604   // => used / capacity < min_load
605   // => used < min_load * capacity
606   // => used * HASH_SCALE < HASH_MIN_LOAD * capacity
607   //
608   // load > max_load
609   // => used / capacity > max_load
610   // => used * HASH_SCALER > HASH_MAX_LOAD * capacity
611   uint64_t load = (uint64_t)count * HASH_LOAD_SCALE;
612   uint64_t min_load = (uint64_t)capacity * HASH_MIN_LOAD;
613   uint64_t max_load = (uint64_t)capacity * HASH_MAX_LOAD;
614   if (load < min_load || load > max_load) {
615     _g_hash_table_resize(hash_table);
616   }
617 }
618 
_g_hash_table_insert_index(GHashTable * hash_table,guint key_index,guint new_key_hash,gpointer new_key,gpointer new_value)619 static void _g_hash_table_insert_index(GHashTable* hash_table,
620                                        guint key_index,
621                                        guint new_key_hash,
622                                        gpointer new_key,
623                                        gpointer new_value) {
624   GHashEntry* entry = &hash_table->entries[key_index];
625   guint old_hash = entry->hash;
626 
627   entry->key = new_key;
628   entry->value = new_value;
629   entry->hash = new_key_hash;
630 
631   if (HASH_IS_REAL(old_hash)) {
632     // Simple replacement, exit immediately.
633     return;
634   }
635 
636   hash_table->num_items++;
637   if (old_hash == HASH_TOMBSTONE) {
638     // No need to resize when replacing a tombstone.
639     return;
640   }
641 
642   hash_table->num_used++;
643   _g_hash_table_maybe_resize(hash_table);
644 }
645 
g_hash_table_insert(GHashTable * hash_table,void * key,void * value)646 void g_hash_table_insert(GHashTable* hash_table,
647                          void* key,
648                          void* value) {
649   guint key_hash;
650   guint key_index =
651       _g_hash_table_lookup_index(hash_table, key, &key_hash);
652 
653   _g_hash_table_insert_index(hash_table, key_index, key_hash, key, value);
654 }
655 
656 
g_hash_table_foreach(GHashTable * hash_table,GHFunc func,gpointer user_data)657 void g_hash_table_foreach(GHashTable* hash_table,
658                           GHFunc func,
659                           gpointer user_data) {
660   guint n;
661   for (n = 0; n < hash_table->capacity; ++n) {
662     GHashEntry* entry = &hash_table->entries[n];
663     if (HASH_IS_REAL(entry->hash))
664       (*func)(entry->key, entry->value, user_data);
665   }
666 }
667 
668 
g_hash_table_find(GHashTable * hash_table,GHRFunc predicate,gpointer user_data)669 gpointer g_hash_table_find(GHashTable* hash_table,
670                            GHRFunc predicate,
671                            gpointer user_data) {
672   guint n;
673   for (n = 0; n < hash_table->capacity; ++n) {
674     GHashEntry* entry = &hash_table->entries[n];
675     if (HASH_IS_REAL(entry->hash) &&
676         (*predicate)(entry->key, entry->value, user_data)) {
677       return entry->value;
678     }
679   }
680   return NULL;
681 }
682 
683 
g_hash_table_size(GHashTable * hash_table)684 guint g_hash_table_size(GHashTable* hash_table) {
685   return hash_table->num_items;
686 }
687 
688 // Queues
689 
690 struct _GQueueNode {
691   void* data;
692   GQueueNode* next;
693   GQueueNode* prev;
694 };
695 
_g_queue_node_alloc(void)696 static inline GQueueNode* _g_queue_node_alloc(void) {
697   return g_new0(GQueueNode, 1);
698 }
699 
_g_queue_node_free(GQueueNode * node)700 static void inline _g_queue_node_free(GQueueNode* node) {
701   g_free(node);
702 }
703 
g_queue_new(void)704 GQueue* g_queue_new(void) {
705   GQueue* queue = g_new0(GQueue, 1);
706   return queue;
707 }
708 
g_queue_free(GQueue * queue)709 void g_queue_free(GQueue* queue) {
710   GQueueNode* node = queue->head;
711   while (node) {
712     GQueueNode* next = node->next;
713     _g_queue_node_free(node);
714     node = next;
715   }
716   queue->head = queue->tail = NULL;
717   queue->length = 0;
718   g_free(queue);
719 }
720 
g_queue_is_empty(GQueue * queue)721 gboolean g_queue_is_empty(GQueue* queue) {
722   return queue->head == NULL;
723 }
724 
g_queue_push_tail(GQueue * queue,void * data)725 void g_queue_push_tail(GQueue* queue, void* data) {
726   GQueueNode* node = _g_queue_node_alloc();
727   node->data = data;
728   node->next = NULL;
729   node->prev = queue->tail;
730   queue->tail = node;
731   queue->length++;
732 }
733 
g_queue_peek_head(GQueue * queue)734 void* g_queue_peek_head(GQueue* queue) {
735   return (queue->head) ? queue->head->data : NULL;
736 }
737 
g_queue_peek_tail(GQueue * queue)738 void* g_queue_peek_tail(GQueue* queue) {
739   return (queue->tail) ? queue->tail->data : NULL;
740 }
741 
g_queue_pop_head(GQueue * queue)742 void* g_queue_pop_head(GQueue* queue) {
743   GQueueNode* head = queue->head;
744   if (!head)
745     return NULL;
746 
747   void* result = head->data;
748 
749   if (head->next) {
750     queue->head = head->next;
751     head->next->prev = NULL;
752   } else {
753     queue->head = NULL;
754     queue->tail = NULL;
755   }
756   queue->length--;
757 
758   return result;
759 }
760