1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#ifndef __NM_REF_STRING_H__
#define __NM_REF_STRING_H__
/*****************************************************************************/
typedef struct _NMRefString {
const gsize len;
union {
struct {
volatile int _ref_count;
const char str[];
};
struct {
/* This union field is only used during lookup by external string.
* In that case, len will be set to G_MAXSIZE, and the actual len/str values
* are set in _priv_lookup. */
gsize l_len;
const char *l_str;
} _priv_lookup;
};
} NMRefString;
/*****************************************************************************/
void _nm_assert_nm_ref_string(NMRefString *rstr);
static inline void
nm_assert_nm_ref_string(NMRefString *rstr)
{
#if NM_MORE_ASSERTS
_nm_assert_nm_ref_string(rstr);
#endif
}
/*****************************************************************************/
NMRefString *nm_ref_string_new_len(const char *cstr, gsize len);
static inline NMRefString *
nm_ref_string_new(const char *cstr)
{
return cstr ? nm_ref_string_new_len(cstr, strlen(cstr)) : NULL;
}
/*****************************************************************************/
static inline NMRefString *
nm_ref_string_ref(NMRefString *rstr)
{
if (rstr) {
nm_assert_nm_ref_string(rstr);
g_atomic_int_inc(&rstr->_ref_count);
}
return rstr;
}
void _nm_ref_string_unref_slow_path(NMRefString *rstr);
static inline void
nm_ref_string_unref(NMRefString *rstr)
{
int r;
if (!rstr)
return;
nm_assert_nm_ref_string(rstr);
/* fast-path: first try to decrement the ref-count without bringing it
* to zero. */
r = rstr->_ref_count;
if (G_LIKELY(r > 1 && g_atomic_int_compare_and_exchange(&rstr->_ref_count, r, r - 1)))
return;
_nm_ref_string_unref_slow_path(rstr);
}
NM_AUTO_DEFINE_FCN_VOID(NMRefString *, _nm_auto_ref_string, nm_ref_string_unref);
#define nm_auto_ref_string nm_auto(_nm_auto_ref_string)
/*****************************************************************************/
static inline const char *
nm_ref_string_get_str(NMRefString *rstr)
{
return rstr ? rstr->str : NULL;
}
static inline gsize
nm_ref_string_get_len(NMRefString *rstr)
{
return rstr ? rstr->len : 0u;
}
static inline gboolean
nm_ref_string_equal(NMRefString *a, NMRefString *b)
{
return a == b;
}
static inline int
nm_ref_string_cmp(NMRefString *a, NMRefString *b)
{
NM_CMP_SELF(a, b);
/* It would be cheaper to first compare by length. But this
* way we get a nicer, ASCIIbethical sort order. */
NM_CMP_DIRECT_MEMCMP(a->str, b->str, NM_MIN(a->len, b->len));
NM_CMP_DIRECT(a->len, b->len);
return nm_assert_unreachable_val(0);
}
#define NM_CMP_DIRECT_REF_STRING(a, b) NM_CMP_RETURN_DIRECT(nm_ref_string_cmp((a), (b)))
static inline gboolean
nm_ref_string_equal_str(NMRefString *rstr, const char *str)
{
if (!str)
return (!!rstr);
if (!rstr)
return FALSE;
return rstr->len == strlen(str) && (rstr->str == str || memcmp(rstr->str, str, rstr->len) == 0);
}
static inline gboolean
NM_IS_REF_STRING(NMRefString *rstr)
{
if (rstr)
nm_assert_nm_ref_string(rstr);
/* Technically, %NULL is also a valid NMRefString (according to nm_ref_string_new(),
* nm_ref_string_get_str() and nm_ref_string_unref()). However, NM_IS_REF_STRING()
* does not think so. If callers want to allow %NULL, they need to check
* separately. */
return !!rstr;
}
static inline NMRefString *
NM_REF_STRING_UPCAST(const char *str)
{
NMRefString *rstr;
if (!str)
return NULL;
rstr = (gpointer) (((char *) str) - G_STRUCT_OFFSET(NMRefString, str));
nm_assert_nm_ref_string(rstr);
return rstr;
}
static inline NMRefString *
nm_ref_string_ref_upcast(const char *str)
{
return nm_ref_string_ref(NM_REF_STRING_UPCAST(str));
}
static inline void
nm_ref_string_unref_upcast(const char *str)
{
nm_ref_string_unref(NM_REF_STRING_UPCAST(str));
}
static inline gboolean
nm_ref_string_reset_str(NMRefString **ptr, const char *str)
{
nm_auto_ref_string NMRefString *rstr = NULL;
gsize l;
nm_assert(ptr);
if (!str)
return nm_clear_pointer(ptr, nm_ref_string_unref);
l = strlen(str);
if ((*ptr) && (*ptr)->len == l && ((*ptr)->str == str || memcmp((*ptr)->str, str, l) == 0))
return FALSE;
rstr = *ptr;
*ptr = nm_ref_string_new_len(str, l);
return TRUE;
}
#endif /* __NM_REF_STRING_H__ */
|