|
Packit Service |
87a54e |
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
#include "nm-default.h"
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
#include "nm-ref-string.h"
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
/*****************************************************************************/
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
typedef struct {
|
|
Packit Service |
a1bd4f |
NMRefString r;
|
|
Packit Service |
a1bd4f |
volatile int ref_count;
|
|
Packit Service |
a1bd4f |
char str_data[];
|
|
Packit |
5756e2 |
} RefString;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
G_LOCK_DEFINE_STATIC(gl_lock);
|
|
Packit |
5756e2 |
static GHashTable *gl_hash;
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
/* the first field of NMRefString is a pointer to the NUL terminated string.
|
|
Packit |
5756e2 |
* This also allows to compare strings with nm_pstr_equal(), although, pointer
|
|
Packit |
5756e2 |
* equality might be better. */
|
|
Packit Service |
a1bd4f |
G_STATIC_ASSERT(G_STRUCT_OFFSET(NMRefString, str) == 0);
|
|
Packit Service |
a1bd4f |
G_STATIC_ASSERT(G_STRUCT_OFFSET(RefString, r) == 0);
|
|
Packit Service |
a1bd4f |
G_STATIC_ASSERT(G_STRUCT_OFFSET(RefString, r.str) == 0);
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
/*****************************************************************************/
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
static guint
|
|
Packit Service |
a1bd4f |
_ref_string_hash(gconstpointer ptr)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
const RefString *a = ptr;
|
|
Packit Service |
a1bd4f |
NMHashState h;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
nm_hash_init(&h, 1463435489u);
|
|
Packit Service |
a1bd4f |
nm_hash_update(&h, a->r.str, a->r.len);
|
|
Packit Service |
a1bd4f |
return nm_hash_complete(&h);
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
static gboolean
|
|
Packit Service |
a1bd4f |
_ref_string_equal(gconstpointer pa, gconstpointer pb)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
const RefString *a = pa;
|
|
Packit Service |
a1bd4f |
const RefString *b = pb;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
return a->r.len == b->r.len && memcmp(a->r.str, b->r.str, a->r.len) == 0;
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
/*****************************************************************************/
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
static void
|
|
Packit Service |
a1bd4f |
_ASSERT(const RefString *rstr0)
|
|
Packit |
5756e2 |
{
|
|
Packit |
5756e2 |
#if NM_MORE_ASSERTS
|
|
Packit Service |
a1bd4f |
int r;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
nm_assert(rstr0);
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
G_LOCK(gl_lock);
|
|
Packit Service |
a1bd4f |
r = g_atomic_int_get(&rstr0->ref_count);
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
nm_assert(r > 0);
|
|
Packit Service |
a1bd4f |
nm_assert(r < G_MAXINT);
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
nm_assert(rstr0 == g_hash_table_lookup(gl_hash, rstr0));
|
|
Packit Service |
a1bd4f |
G_UNLOCK(gl_lock);
|
|
Packit |
5756e2 |
#endif
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
/**
|
|
Packit |
5756e2 |
* nm_ref_string_new_len:
|
|
Packit |
5756e2 |
* @cstr: the string to intern. Must contain @len bytes.
|
|
Packit |
5756e2 |
* If @len is zero, @cstr may be %NULL. Note that it is
|
|
Packit |
5756e2 |
* acceptable that the string contains a NUL character
|
|
Packit |
5756e2 |
* within the first @len bytes. That is, the string is
|
|
Packit |
5756e2 |
* not treated as a NUL terminated string, but as binary.
|
|
Packit |
5756e2 |
* Also, contrary to strncpy(), this will read all the
|
|
Packit |
5756e2 |
* first @len bytes. It won't stop at the first NUL.
|
|
Packit |
5756e2 |
* @len: the length of the string (usually there is no NUL character
|
|
Packit |
5756e2 |
* within the first @len bytes, but that would be acceptable as well
|
|
Packit |
5756e2 |
* to add binary data).
|
|
Packit |
5756e2 |
*
|
|
Packit |
5756e2 |
* Note that the resulting NMRefString instance will always be NUL terminated
|
|
Packit |
5756e2 |
* (at position @len).
|
|
Packit |
5756e2 |
*
|
|
Packit |
5756e2 |
* Note that NMRefString are always interned/deduplicated. If such a string
|
|
Packit |
5756e2 |
* already exists, the existing instance will be referred and returned.
|
|
Packit |
5756e2 |
*
|
|
Packit |
5756e2 |
*
|
|
Packit |
5756e2 |
* Since all NMRefString are shared and interned, you may use
|
|
Packit |
5756e2 |
* pointer equality to compare them. Note that if a NMRefString contains
|
|
Packit |
5756e2 |
* a NUL character (meaning, if
|
|
Packit |
5756e2 |
*
|
|
Packit |
5756e2 |
* strlen (nm_ref_string_get_str (str)) != nm_ref_string_get_len (str)
|
|
Packit |
5756e2 |
*
|
|
Packit |
5756e2 |
* ), then pointer in-equality does not mean that the NUL terminated strings
|
|
Packit |
5756e2 |
* are also unequal. In other words, for strings that contain NUL characters,
|
|
Packit |
5756e2 |
*
|
|
Packit |
5756e2 |
* if (str1 != str2)
|
|
Packit |
5756e2 |
* assert (!nm_streq0 (nm_ref_string_get_str (str1), nm_ref_string_get_str (str2)));
|
|
Packit |
5756e2 |
*
|
|
Packit |
5756e2 |
* might not hold!
|
|
Packit |
5756e2 |
*
|
|
Packit |
5756e2 |
*
|
|
Packit |
5756e2 |
* NMRefString is thread-safe.
|
|
Packit |
5756e2 |
*
|
|
Packit |
5756e2 |
* Returns: (transfer full): the interned string. This is
|
|
Packit |
5756e2 |
* never %NULL, but note that %NULL is also a valid NMRefString.
|
|
Packit |
5756e2 |
* The result must be unrefed with nm_ref_string_unref().
|
|
Packit |
5756e2 |
*/
|
|
Packit |
5756e2 |
NMRefString *
|
|
Packit Service |
a1bd4f |
nm_ref_string_new_len(const char *cstr, gsize len)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
RefString *rstr0;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
G_LOCK(gl_lock);
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (G_UNLIKELY(!gl_hash)) {
|
|
Packit Service |
a1bd4f |
gl_hash = g_hash_table_new_full(_ref_string_hash, _ref_string_equal, g_free, NULL);
|
|
Packit Service |
a1bd4f |
rstr0 = NULL;
|
|
Packit Service |
a1bd4f |
} else {
|
|
Packit Service |
a1bd4f |
NMRefString rr_lookup = {
|
|
Packit Service |
a1bd4f |
.len = len,
|
|
Packit Service |
a1bd4f |
.str = cstr,
|
|
Packit Service |
a1bd4f |
};
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
rstr0 = g_hash_table_lookup(gl_hash, &rr_lookup);
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (rstr0) {
|
|
Packit Service |
a1bd4f |
nm_assert(({
|
|
Packit Service |
a1bd4f |
int r = g_atomic_int_get(&rstr0->ref_count);
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
(r >= 0 && r < G_MAXINT);
|
|
Packit Service |
a1bd4f |
}));
|
|
Packit Service |
a1bd4f |
g_atomic_int_inc(&rstr0->ref_count);
|
|
Packit Service |
a1bd4f |
} else {
|
|
Packit Service |
a1bd4f |
rstr0 = g_malloc(sizeof(RefString) + 1 + len);
|
|
Packit Service |
a1bd4f |
rstr0->ref_count = 1;
|
|
Packit Service |
a1bd4f |
*((gsize *) &rstr0->r.len) = len;
|
|
Packit Service |
a1bd4f |
*((const char **) &rstr0->r.str) = rstr0->str_data;
|
|
Packit Service |
a1bd4f |
if (len > 0)
|
|
Packit Service |
a1bd4f |
memcpy(rstr0->str_data, cstr, len);
|
|
Packit Service |
a1bd4f |
rstr0->str_data[len] = '\0';
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (!g_hash_table_add(gl_hash, rstr0))
|
|
Packit Service |
a1bd4f |
nm_assert_not_reached();
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
G_UNLOCK(gl_lock);
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
return &rstr0->r;
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
NMRefString *
|
|
Packit Service |
a1bd4f |
nm_ref_string_ref(NMRefString *rstr)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
RefString *const rstr0 = (RefString *) rstr;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
if (!rstr)
|
|
Packit Service |
a1bd4f |
return NULL;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
_ASSERT(rstr0);
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
g_atomic_int_inc(&rstr0->ref_count);
|
|
Packit Service |
a1bd4f |
return &rstr0->r;
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
void
|
|
Packit Service |
a1bd4f |
_nm_ref_string_unref_non_null(NMRefString *rstr)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
RefString *const rstr0 = (RefString *) rstr;
|
|
Packit Service |
8f75d2 |
int r;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
_ASSERT(rstr0);
|
|
Packit |
5756e2 |
|
|
Packit Service |
8f75d2 |
/* fast-path: first try to decrement the ref-count without bringing it
|
|
Packit Service |
8f75d2 |
* to zero. */
|
|
Packit Service |
8f75d2 |
r = rstr0->ref_count;
|
|
Packit Service |
8f75d2 |
if (G_LIKELY(r > 1 && g_atomic_int_compare_and_exchange(&rstr0->ref_count, r, r - 1)))
|
|
Packit Service |
a1bd4f |
return;
|
|
Packit |
5756e2 |
|
|
Packit Service |
8f75d2 |
/* We apparently are about to return the last reference. Take a lock. */
|
|
Packit Service |
8f75d2 |
|
|
Packit Service |
a1bd4f |
G_LOCK(gl_lock);
|
|
Packit |
5756e2 |
|
|
Packit Service |
8f75d2 |
nm_assert(g_hash_table_lookup(gl_hash, rstr0) == rstr0);
|
|
Packit |
5756e2 |
|
|
Packit Service |
8f75d2 |
if (G_LIKELY(g_atomic_int_dec_and_test(&rstr0->ref_count))) {
|
|
Packit Service |
a1bd4f |
if (!g_hash_table_remove(gl_hash, rstr0))
|
|
Packit Service |
a1bd4f |
nm_assert_not_reached();
|
|
Packit Service |
a1bd4f |
}
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
G_UNLOCK(gl_lock);
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
/*****************************************************************************/
|