mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager
synced 2024-07-23 19:24:38 +00:00
glib-aux: add NMPrioq priority queue (heap)
Taken from systemd's "Prioq". Differences from Prioq: - It is glib-ized, so certain operations cannot fail since g_malloc() never fails. - Unlike Prioq, this structure is stack allocated. I think that makes sense, because we basically always want to embed the data structure in another object. There is never a need for passing this around as a pointer. And if you really want, you can box it yourself. - The queue either accepts a GCompareFunc or a GComareDataFunc. This is for convenience. The prioq_ensure_allocated() and prioq_ensure_put() consequently are dropped, as they would be cumbersome with this pattern and don't seem useful.
This commit is contained in:
parent
5f3259b620
commit
996b679bd0
|
@ -454,6 +454,8 @@ src_libnm_glib_aux_libnm_glib_aux_la_SOURCES = \
|
||||||
src/libnm-glib-aux/nm-logging-syslog.h \
|
src/libnm-glib-aux/nm-logging-syslog.h \
|
||||||
src/libnm-glib-aux/nm-macros-internal.h \
|
src/libnm-glib-aux/nm-macros-internal.h \
|
||||||
src/libnm-glib-aux/nm-obj.h \
|
src/libnm-glib-aux/nm-obj.h \
|
||||||
|
src/libnm-glib-aux/nm-prioq.c \
|
||||||
|
src/libnm-glib-aux/nm-prioq.h \
|
||||||
src/libnm-glib-aux/nm-random-utils.c \
|
src/libnm-glib-aux/nm-random-utils.c \
|
||||||
src/libnm-glib-aux/nm-random-utils.h \
|
src/libnm-glib-aux/nm-random-utils.h \
|
||||||
src/libnm-glib-aux/nm-ref-string.c \
|
src/libnm-glib-aux/nm-ref-string.c \
|
||||||
|
|
|
@ -13,6 +13,7 @@ libnm_glib_aux = static_library(
|
||||||
'nm-json-aux.c',
|
'nm-json-aux.c',
|
||||||
'nm-keyfile-aux.c',
|
'nm-keyfile-aux.c',
|
||||||
'nm-logging-base.c',
|
'nm-logging-base.c',
|
||||||
|
'nm-prioq.c',
|
||||||
'nm-random-utils.c',
|
'nm-random-utils.c',
|
||||||
'nm-ref-string.c',
|
'nm-ref-string.c',
|
||||||
'nm-secret-utils.c',
|
'nm-secret-utils.c',
|
||||||
|
|
323
src/libnm-glib-aux/nm-prioq.c
Normal file
323
src/libnm-glib-aux/nm-prioq.c
Normal file
|
@ -0,0 +1,323 @@
|
||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Taken from systemd's Prioq.
|
||||||
|
*
|
||||||
|
* Priority Queue
|
||||||
|
* The prioq object implements a priority queue. That is, it orders objects by
|
||||||
|
* their priority and allows O(1) access to the object with the highest
|
||||||
|
* priority. Insertion and removal are Θ(log n). Optionally, the caller can
|
||||||
|
* provide a pointer to an index which will be kept up-to-date by the prioq.
|
||||||
|
*
|
||||||
|
* The underlying algorithm used in this implementation is a Heap.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libnm-glib-aux/nm-default-glib-i18n-lib.h"
|
||||||
|
|
||||||
|
#include "nm-prioq.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
struct _NMPrioqItem {
|
||||||
|
void *data;
|
||||||
|
unsigned *idx;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
void
|
||||||
|
nm_prioq_init(NMPrioq *q, GCompareFunc compare_func)
|
||||||
|
{
|
||||||
|
nm_assert(q);
|
||||||
|
nm_assert(compare_func);
|
||||||
|
|
||||||
|
*q = (NMPrioq){
|
||||||
|
._priv =
|
||||||
|
{
|
||||||
|
.compare_func = compare_func,
|
||||||
|
.compare_data = NULL,
|
||||||
|
.compare_with_data = FALSE,
|
||||||
|
.items = NULL,
|
||||||
|
.n_items = 0,
|
||||||
|
.n_allocated = 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nm_prioq_init_with_data(NMPrioq *q, GCompareDataFunc compare_func, gpointer compare_data)
|
||||||
|
{
|
||||||
|
nm_assert(q);
|
||||||
|
nm_assert(compare_func);
|
||||||
|
|
||||||
|
*q = (NMPrioq){
|
||||||
|
._priv =
|
||||||
|
{
|
||||||
|
.compare_data_func = compare_func,
|
||||||
|
.compare_data = compare_data,
|
||||||
|
.compare_with_data = TRUE,
|
||||||
|
.items = NULL,
|
||||||
|
.n_items = 0,
|
||||||
|
.n_allocated = 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nm_prioq_destroy(NMPrioq *q)
|
||||||
|
{
|
||||||
|
if (!q || !q->_priv.compare_func)
|
||||||
|
return;
|
||||||
|
|
||||||
|
free(q->_priv.items);
|
||||||
|
q->_priv.compare_func = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static int
|
||||||
|
compare(NMPrioq *q, unsigned a, unsigned b)
|
||||||
|
{
|
||||||
|
nm_assert(q);
|
||||||
|
nm_assert(q->_priv.compare_func);
|
||||||
|
nm_assert(a != b);
|
||||||
|
nm_assert(a < q->_priv.n_items);
|
||||||
|
nm_assert(b < q->_priv.n_items);
|
||||||
|
|
||||||
|
if (q->_priv.compare_with_data) {
|
||||||
|
return q->_priv.compare_data_func(q->_priv.items[a].data,
|
||||||
|
q->_priv.items[b].data,
|
||||||
|
q->_priv.compare_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return q->_priv.compare_func(q->_priv.items[a].data, q->_priv.items[b].data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
swap(NMPrioq *q, unsigned j, unsigned k)
|
||||||
|
{
|
||||||
|
nm_assert(q);
|
||||||
|
nm_assert(j < q->_priv.n_items);
|
||||||
|
nm_assert(k < q->_priv.n_items);
|
||||||
|
|
||||||
|
nm_assert(!q->_priv.items[j].idx || *(q->_priv.items[j].idx) == j);
|
||||||
|
nm_assert(!q->_priv.items[k].idx || *(q->_priv.items[k].idx) == k);
|
||||||
|
|
||||||
|
NM_SWAP(&q->_priv.items[j].data, &q->_priv.items[k].data);
|
||||||
|
NM_SWAP(&q->_priv.items[j].idx, &q->_priv.items[k].idx);
|
||||||
|
|
||||||
|
if (q->_priv.items[j].idx)
|
||||||
|
*q->_priv.items[j].idx = j;
|
||||||
|
|
||||||
|
if (q->_priv.items[k].idx)
|
||||||
|
*q->_priv.items[k].idx = k;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned
|
||||||
|
shuffle_up(NMPrioq *q, unsigned idx)
|
||||||
|
{
|
||||||
|
nm_assert(q);
|
||||||
|
nm_assert(idx < q->_priv.n_items);
|
||||||
|
|
||||||
|
while (idx > 0) {
|
||||||
|
unsigned k;
|
||||||
|
|
||||||
|
k = (idx - 1) / 2;
|
||||||
|
|
||||||
|
if (compare(q, k, idx) <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
swap(q, idx, k);
|
||||||
|
idx = k;
|
||||||
|
}
|
||||||
|
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned
|
||||||
|
shuffle_down(NMPrioq *q, unsigned idx)
|
||||||
|
{
|
||||||
|
nm_assert(q);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
unsigned j;
|
||||||
|
unsigned k;
|
||||||
|
unsigned s;
|
||||||
|
|
||||||
|
k = (idx + 1) * 2; /* right child */
|
||||||
|
j = k - 1; /* left child */
|
||||||
|
|
||||||
|
if (j >= q->_priv.n_items)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (compare(q, j, idx) < 0) {
|
||||||
|
/* So our left child is smaller than we are, let's
|
||||||
|
* remember this fact */
|
||||||
|
s = j;
|
||||||
|
} else
|
||||||
|
s = idx;
|
||||||
|
|
||||||
|
if ((k < q->_priv.n_items) && compare(q, k, s) < 0) {
|
||||||
|
/* So our right child is smaller than we are, let's
|
||||||
|
* remember this fact */
|
||||||
|
s = k;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* s now points to the smallest of the three items */
|
||||||
|
|
||||||
|
if (s == idx)
|
||||||
|
/* No swap necessary, we're done */
|
||||||
|
break;
|
||||||
|
|
||||||
|
swap(q, idx, s);
|
||||||
|
idx = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nm_prioq_put(NMPrioq *q, void *data, unsigned *idx)
|
||||||
|
{
|
||||||
|
unsigned k;
|
||||||
|
|
||||||
|
nm_assert(q);
|
||||||
|
|
||||||
|
if (q->_priv.n_items >= q->_priv.n_allocated) {
|
||||||
|
q->_priv.n_allocated = NM_MAX((q->_priv.n_items + 1u) * 2u, 16u);
|
||||||
|
q->_priv.items = g_renew(struct _NMPrioqItem, q->_priv.items, q->_priv.n_allocated);
|
||||||
|
}
|
||||||
|
|
||||||
|
k = q->_priv.n_items++;
|
||||||
|
|
||||||
|
q->_priv.items[k] = (struct _NMPrioqItem){
|
||||||
|
.data = data,
|
||||||
|
.idx = idx,
|
||||||
|
};
|
||||||
|
if (idx)
|
||||||
|
*idx = k;
|
||||||
|
|
||||||
|
shuffle_up(q, k);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
remove_item(NMPrioq *q, struct _NMPrioqItem *i)
|
||||||
|
{
|
||||||
|
struct _NMPrioqItem *l;
|
||||||
|
unsigned k;
|
||||||
|
|
||||||
|
nm_assert(q);
|
||||||
|
nm_assert(i);
|
||||||
|
nm_assert(q->_priv.n_items > 0);
|
||||||
|
nm_assert(i >= q->_priv.items);
|
||||||
|
nm_assert(i < &q->_priv.items[q->_priv.n_items]);
|
||||||
|
|
||||||
|
l = &q->_priv.items[q->_priv.n_items - 1u];
|
||||||
|
|
||||||
|
if (i == l) {
|
||||||
|
/* Last entry, let's just remove it */
|
||||||
|
q->_priv.n_items--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Not last entry, let's replace the last entry with
|
||||||
|
* this one, and reshuffle */
|
||||||
|
k = i - q->_priv.items;
|
||||||
|
|
||||||
|
*i = *l;
|
||||||
|
if (i->idx)
|
||||||
|
*i->idx = k;
|
||||||
|
q->_priv.n_items--;
|
||||||
|
|
||||||
|
k = shuffle_down(q, k);
|
||||||
|
shuffle_up(q, k);
|
||||||
|
}
|
||||||
|
|
||||||
|
_nm_pure static struct _NMPrioqItem *
|
||||||
|
find_item(NMPrioq *q, void *data, unsigned *idx)
|
||||||
|
{
|
||||||
|
struct _NMPrioqItem *i;
|
||||||
|
|
||||||
|
nm_assert(q);
|
||||||
|
|
||||||
|
if (q->_priv.n_items <= 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (idx) {
|
||||||
|
if (*idx == NM_PRIOQ_IDX_NULL || *idx >= q->_priv.n_items)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
i = &q->_priv.items[*idx];
|
||||||
|
if (i->data == data)
|
||||||
|
return i;
|
||||||
|
} else {
|
||||||
|
for (i = q->_priv.items; i < &q->_priv.items[q->_priv.n_items]; i++) {
|
||||||
|
if (i->data == data)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
nm_prioq_remove(NMPrioq *q, void *data, unsigned *idx)
|
||||||
|
{
|
||||||
|
struct _NMPrioqItem *i;
|
||||||
|
|
||||||
|
nm_assert(q);
|
||||||
|
|
||||||
|
i = find_item(q, data, idx);
|
||||||
|
if (!i)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
remove_item(q, i);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
nm_prioq_reshuffle(NMPrioq *q, void *data, unsigned *idx)
|
||||||
|
{
|
||||||
|
struct _NMPrioqItem *i;
|
||||||
|
unsigned k;
|
||||||
|
|
||||||
|
nm_assert(q);
|
||||||
|
|
||||||
|
i = find_item(q, data, idx);
|
||||||
|
if (!i)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
k = i - q->_priv.items;
|
||||||
|
k = shuffle_down(q, k);
|
||||||
|
shuffle_up(q, k);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
nm_prioq_peek_by_index(NMPrioq *q, unsigned idx)
|
||||||
|
{
|
||||||
|
nm_assert(q);
|
||||||
|
|
||||||
|
if (idx >= q->_priv.n_items)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return q->_priv.items[idx].data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
nm_prioq_pop(NMPrioq *q)
|
||||||
|
{
|
||||||
|
void *data;
|
||||||
|
|
||||||
|
nm_assert(q);
|
||||||
|
|
||||||
|
if (q->_priv.n_items <= 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
data = q->_priv.items[0].data;
|
||||||
|
remove_item(q, &q->_priv.items[0]);
|
||||||
|
return data;
|
||||||
|
}
|
71
src/libnm-glib-aux/nm-prioq.h
Normal file
71
src/libnm-glib-aux/nm-prioq.h
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#ifndef __NM_PRIOQ_H__
|
||||||
|
#define __NM_PRIOQ_H__
|
||||||
|
|
||||||
|
#define NM_PRIOQ_IDX_NULL G_MAXUINT
|
||||||
|
|
||||||
|
struct _NMPrioqItem;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
struct {
|
||||||
|
union {
|
||||||
|
GCompareDataFunc compare_data_func;
|
||||||
|
GCompareFunc compare_func;
|
||||||
|
};
|
||||||
|
|
||||||
|
gpointer compare_data;
|
||||||
|
|
||||||
|
struct _NMPrioqItem *items;
|
||||||
|
|
||||||
|
unsigned n_items;
|
||||||
|
unsigned n_allocated;
|
||||||
|
|
||||||
|
bool compare_with_data;
|
||||||
|
} _priv;
|
||||||
|
} NMPrioq;
|
||||||
|
|
||||||
|
#define NM_PRIOQ_ZERO \
|
||||||
|
{ \
|
||||||
|
._priv = { \
|
||||||
|
.compare_func = NULL, \
|
||||||
|
}, \
|
||||||
|
}
|
||||||
|
|
||||||
|
void nm_prioq_init(NMPrioq *q, GCompareFunc compare_func);
|
||||||
|
void nm_prioq_init_with_data(NMPrioq *q, GCompareDataFunc compare_func, gpointer compare_data);
|
||||||
|
|
||||||
|
void nm_prioq_destroy(NMPrioq *q);
|
||||||
|
|
||||||
|
#define nm_auto_prioq nm_auto(nm_prioq_destroy)
|
||||||
|
|
||||||
|
void nm_prioq_put(NMPrioq *q, void *data, unsigned *idx);
|
||||||
|
gboolean nm_prioq_remove(NMPrioq *q, void *data, unsigned *idx);
|
||||||
|
gboolean nm_prioq_reshuffle(NMPrioq *q, void *data, unsigned *idx);
|
||||||
|
|
||||||
|
void *nm_prioq_peek_by_index(NMPrioq *q, unsigned idx) _nm_pure;
|
||||||
|
|
||||||
|
static inline void *
|
||||||
|
nm_prioq_peek(NMPrioq *q)
|
||||||
|
{
|
||||||
|
return nm_prioq_peek_by_index(q, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *nm_prioq_pop(NMPrioq *q);
|
||||||
|
|
||||||
|
#define NM_PRIOQ_FOREACH_ITEM(q, p) for (unsigned _i = 0; (p = nm_prioq_peek_by_index(q, _i)); _i++)
|
||||||
|
|
||||||
|
_nm_pure static inline unsigned
|
||||||
|
nm_prioq_size(NMPrioq *q)
|
||||||
|
{
|
||||||
|
nm_assert(q);
|
||||||
|
return q->_priv.n_items;
|
||||||
|
}
|
||||||
|
|
||||||
|
_nm_pure static inline gboolean
|
||||||
|
nm_prioq_isempty(NMPrioq *q)
|
||||||
|
{
|
||||||
|
return nm_prioq_size(q) <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* __NM_PRIOQ_H__ */
|
|
@ -11,6 +11,7 @@
|
||||||
#include "libnm-glib-aux/nm-time-utils.h"
|
#include "libnm-glib-aux/nm-time-utils.h"
|
||||||
#include "libnm-glib-aux/nm-ref-string.h"
|
#include "libnm-glib-aux/nm-ref-string.h"
|
||||||
#include "libnm-glib-aux/nm-io-utils.h"
|
#include "libnm-glib-aux/nm-io-utils.h"
|
||||||
|
#include "libnm-glib-aux/nm-prioq.h"
|
||||||
|
|
||||||
#include "libnm-glib-aux/nm-test-utils.h"
|
#include "libnm-glib-aux/nm-test-utils.h"
|
||||||
|
|
||||||
|
@ -2260,6 +2261,79 @@ test_garray(void)
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static int
|
||||||
|
_prioq_cmp(gconstpointer a, gconstpointer b)
|
||||||
|
{
|
||||||
|
NM_CMP_DIRECT(GPOINTER_TO_UINT(a), GPOINTER_TO_UINT(b));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
_prioq_cmp_with_data(gconstpointer a, gconstpointer b, gpointer user_data)
|
||||||
|
{
|
||||||
|
return _prioq_cmp(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_nm_prioq(void)
|
||||||
|
{
|
||||||
|
nm_auto_prioq NMPrioq q = NM_PRIOQ_ZERO;
|
||||||
|
gpointer data[200];
|
||||||
|
gpointer data_pop[200];
|
||||||
|
guint data_idx[G_N_ELEMENTS(data)];
|
||||||
|
guint i;
|
||||||
|
guint n;
|
||||||
|
gpointer p;
|
||||||
|
|
||||||
|
if (nmtst_get_rand_one_case_in(10))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (nmtst_get_rand_bool())
|
||||||
|
nm_prioq_init(&q, _prioq_cmp);
|
||||||
|
else
|
||||||
|
nm_prioq_init_with_data(&q, _prioq_cmp_with_data, NULL);
|
||||||
|
|
||||||
|
g_assert(nm_prioq_size(&q) == 0);
|
||||||
|
|
||||||
|
if (nmtst_get_rand_one_case_in(10))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < G_N_ELEMENTS(data); i++) {
|
||||||
|
data[i] = GUINT_TO_POINTER((nmtst_get_rand_uint32() % G_N_ELEMENTS(data)) + 1u);
|
||||||
|
data_idx[i] = NM_PRIOQ_IDX_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
nm_prioq_put(&q, data[0], NULL);
|
||||||
|
g_assert(nm_prioq_size(&q) == 1);
|
||||||
|
|
||||||
|
p = nm_prioq_pop(&q);
|
||||||
|
g_assert(p == data[0]);
|
||||||
|
g_assert(nm_prioq_size(&q) == 0);
|
||||||
|
|
||||||
|
g_assert(!nm_prioq_pop(&q));
|
||||||
|
|
||||||
|
n = nmtst_get_rand_uint32() % G_N_ELEMENTS(data);
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
nm_prioq_put(&q, data[i], &data_idx[i]);
|
||||||
|
|
||||||
|
g_assert_cmpint(nm_prioq_size(&q), ==, n);
|
||||||
|
|
||||||
|
if (nmtst_get_rand_one_case_in(10))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
data_pop[i] = nm_prioq_pop(&q);
|
||||||
|
g_assert(data_pop[i]);
|
||||||
|
if (i > 0)
|
||||||
|
g_assert(_prioq_cmp(data_pop[i - 1], data_pop[i]) <= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert(!nm_prioq_pop(&q));
|
||||||
|
g_assert(nm_prioq_size(&q) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
NMTST_DEFINE();
|
NMTST_DEFINE();
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -2306,6 +2380,7 @@ main(int argc, char **argv)
|
||||||
g_test_add_func("/general/test_hostname_is_valid", test_hostname_is_valid);
|
g_test_add_func("/general/test_hostname_is_valid", test_hostname_is_valid);
|
||||||
g_test_add_func("/general/test_inet_utils", test_inet_utils);
|
g_test_add_func("/general/test_inet_utils", test_inet_utils);
|
||||||
g_test_add_func("/general/test_garray", test_garray);
|
g_test_add_func("/general/test_garray", test_garray);
|
||||||
|
g_test_add_func("/general/test_nm_prioq", test_nm_prioq);
|
||||||
|
|
||||||
return g_test_run();
|
return g_test_run();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue