7
const gchar *null_string = "";
10
nv_registry_get_handle(NVRegistry *self, const gchar *name)
14
p = g_hash_table_lookup(self->name_map, name);
16
return GPOINTER_TO_UINT(p);
21
nv_registry_alloc_handle(NVRegistry *self, const gchar *name)
27
p = g_hash_table_lookup(self->name_map, name);
29
return GPOINTER_TO_UINT(p);
34
msg_error("Name-value pairs cannot have a zero-length name",
35
evt_tag_str("value", name),
41
msg_error("Value names cannot be longer than 255 characters, this value will always expand to the emptry string",
42
evt_tag_str("value", name),
46
else if (self->names->len >= 65535)
48
msg_error("Hard wired limit of 65535 name-value pairs have been reached, all further name-value pair will expand to nothing",
49
evt_tag_str("value", name),
53
/* flags (2 bytes) || length (1 byte) || name (len bytes) || NUL */
54
/* memory layout: flags || length || name (NUL terminated) */
56
stored.name_len = len;
57
stored.name = g_strdup(name);
58
g_array_append_val(self->names, stored);
59
g_hash_table_insert(self->name_map, stored.name, GUINT_TO_POINTER(self->names->len));
60
return self->names->len;
64
* nv_registry_add_alias:
65
* @handle: a NV handle to be aliased
66
* @alias: must be a caller allocated string pointing to the alias of "handle"
69
nv_registry_add_alias(NVRegistry *self, NVHandle handle, const gchar *alias)
71
g_hash_table_insert(self->name_map, (gchar *) alias, GUINT_TO_POINTER((glong) handle));
75
nv_registry_set_handle_flags(NVRegistry *self, NVHandle handle, guint16 flags)
79
if (G_UNLIKELY(!handle))
82
stored = &g_array_index(self->names, NVHandleDesc, handle - 1);
83
stored->flags = flags;
87
nv_registry_new(const gchar **static_names)
89
NVRegistry *self = g_new0(NVRegistry, 1);
92
self->name_map = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
93
self->names = g_array_new(FALSE, FALSE, sizeof(NVHandleDesc));
94
for (i = 0; static_names[i]; i++)
96
nv_registry_alloc_handle(self, static_names[i]);
102
nv_registry_free(NVRegistry *self)
106
for (i = 0; i < self->names->len; i++)
107
g_free(g_array_index(self->names, NVHandleDesc, i).name);
108
g_array_free(self->names, TRUE);
109
g_hash_table_destroy(self->name_map);
113
/* clonable LogMessage support with shared data pointers */
115
#define NV_TABLE_DYNVALUE_HANDLE(x) ((x) >> 16)
116
#define NV_TABLE_DYNVALUE_OFS(x) ((x) & 0xFFFF)
119
static inline gchar *
120
nv_table_get_bottom(NVTable *self)
122
return nv_table_get_top(self) - (self->used << NV_TABLE_SCALE);
125
static inline gchar *
126
nv_table_get_ofs_table_top(NVTable *self)
128
return (gchar *) &self->data[self->num_static_entries * sizeof(self->static_entries[0]) + self->num_dyn_entries * sizeof(guint32)];
131
static inline NVEntry *
132
nv_table_get_entry_at_ofs(NVTable *self, guint16 ofs)
136
return (NVEntry *) (nv_table_get_top(self) - (ofs << NV_TABLE_SCALE));
139
static inline guint32 *
140
nv_table_get_dyn_entries(NVTable *self)
142
return (guint32 *) &self->static_entries[self->num_static_entries];
145
static inline gboolean
146
nv_table_alloc_check(NVTable *self, gsize alloc_size)
148
if (nv_table_get_bottom(self) - alloc_size < nv_table_get_ofs_table_top(self))
153
/* return the offset to a newly allocated payload string */
154
static inline NVEntry *
155
nv_table_alloc_value(NVTable *self, gsize alloc_size)
159
alloc_size = NV_TABLE_BOUND(alloc_size);
160
/* alloc error, NVTable should be realloced */
161
if (!nv_table_alloc_check(self, alloc_size))
163
self->used += alloc_size >> NV_TABLE_SCALE;
164
entry = (NVEntry *) (nv_table_get_top(self) - (self->used << NV_TABLE_SCALE));
165
entry->alloc_len = alloc_size >> NV_TABLE_SCALE;
166
entry->indirect = FALSE;
167
entry->referenced = FALSE;
171
/* we only support single indirection */
173
nv_table_resolve_indirect(NVTable *self, NVEntry *entry, gssize *length)
175
const gchar *referenced_value;
176
gssize referenced_length;
178
referenced_value = nv_table_get_value(self, entry->vindirect.handle, &referenced_length);
179
if (entry->vindirect.ofs > referenced_length)
182
/* here we assume that indirect references are only looked up with
183
* non-zero terminated strings properly handled, thus the caller has
184
* to supply a non-NULL value_len */
186
*length = MIN(entry->vindirect.ofs + entry->vindirect.len, referenced_length) - entry->vindirect.ofs;
187
return referenced_value + entry->vindirect.ofs;
190
static const inline gchar *
191
nv_table_resolve_entry(NVTable *self, NVEntry *entry, gssize *length)
193
if (!entry->indirect)
196
*length = entry->vdirect.value_len;
197
return entry->vdirect.data + entry->name_len + 1;
200
return nv_table_resolve_indirect(self, entry, length);
204
nv_table_get_entry_slow(NVTable *self, NVHandle handle, guint32 **dyn_slot)
208
guint32 *dyn_entries = nv_table_get_dyn_entries(self);
211
if (!self->num_dyn_entries)
217
/* open-coded binary search */
220
h = self->num_dyn_entries - 1;
225
mv = NV_TABLE_DYNVALUE_HANDLE(dyn_entries[m]);
228
*dyn_slot = &dyn_entries[m];
229
ofs = NV_TABLE_DYNVALUE_OFS(dyn_entries[m]);
232
else if (mv > handle)
242
return nv_table_get_entry_at_ofs(self, ofs);
246
nv_table_reserve_table_entry(NVTable *self, NVHandle handle, guint32 **dyn_slot)
248
if (G_UNLIKELY(!(*dyn_slot) && handle > self->num_static_entries))
250
/* this is a dynamic value */
251
guint32 *dyn_entries = nv_table_get_dyn_entries(self);;
253
gboolean found = FALSE;
255
if (!nv_table_alloc_check(self, sizeof(dyn_entries[0])))
259
h = self->num_dyn_entries - 1;
266
mv = NV_TABLE_DYNVALUE_HANDLE(dyn_entries[m]);
274
else if (mv > handle)
283
/* if we find the proper slot we set that, if we don't, we insert a new entry */
287
g_assert(ndx >= 0 && ndx <= self->num_dyn_entries);
288
if (ndx < self->num_dyn_entries)
290
memmove(&dyn_entries[ndx + 1], &dyn_entries[ndx], (self->num_dyn_entries - ndx) * sizeof(dyn_entries[0]));
293
*dyn_slot = &dyn_entries[ndx];
295
/* we set ofs to zero here, which means that the NVEntry won't
296
be found even if the slot is present in dyn_entries */
297
**dyn_slot = (handle << 16) + 0;
299
self->num_dyn_entries++;
305
nv_table_set_table_entry(NVTable *self, NVHandle handle, guint16 ofs, guint32 *dyn_slot)
307
if (G_LIKELY(handle <= self->num_static_entries))
309
/* this is a statically allocated value, simply store the offset */
310
self->static_entries[handle-1] = ofs;
314
/* this is a dynamic value */
315
*dyn_slot = (handle << 16) + ofs;
320
nv_table_make_direct(NVHandle handle, NVEntry *entry, gpointer user_data)
322
NVTable *self = (NVTable *) (((gpointer *) user_data)[0]);
323
NVHandle ref_handle = GPOINTER_TO_UINT(((gpointer *) user_data)[1]);
325
if (entry->indirect && entry->vindirect.handle == ref_handle)
330
value = nv_table_resolve_indirect(self, entry, &value_len);
331
if (!nv_table_add_value(self, handle, entry->vindirect.name, entry->name_len, value, value_len, NULL))
333
/* nvtable full, but we can't realloc it ourselves,
334
* propagate this back as a failure of
335
* nv_table_add_value() */
344
nv_table_add_value(NVTable *self, NVHandle handle, const gchar *name, gsize name_len, const gchar *value, gsize value_len, gboolean *new_entry)
352
entry = nv_table_get_entry(self, handle, &dyn_slot);
353
if (G_UNLIKELY(!entry && !new_entry && value_len == 0))
355
/* we don't store zero length matches unless the caller is
356
* interested in whether a new entry was created. It is used by
357
* the SDATA support code to decide whether a previously
358
* not-present SDATA was set */
361
if (G_UNLIKELY(entry && !entry->indirect && entry->referenced))
363
gpointer data[2] = { self, GUINT_TO_POINTER((glong) handle) };
365
if (nv_table_foreach_entry(self, nv_table_make_direct, data))
367
/* we had to stop iteration, which means that we were unable
368
* to allocate enough space for making indirect entries
373
if (G_UNLIKELY(entry && (((guint) entry->alloc_len << NV_TABLE_SCALE)) >= value_len + NV_ENTRY_DIRECT_HDR + name_len + 2))
376
/* this value already exists and the new value fits in the old space */
377
if (!entry->indirect)
379
dst = entry->vdirect.data + entry->name_len + 1;
381
entry->vdirect.value_len = value_len;
382
memcpy(dst, value, value_len);
387
/* this was an indirect entry, convert it */
389
entry->vdirect.value_len = value_len;
390
entry->name_len = name_len;
391
memcpy(entry->vdirect.data, name, name_len + 1);
392
memcpy(entry->vdirect.data + name_len + 1, value, value_len);
393
entry->vdirect.data[entry->name_len + 1 + value_len] = 0;
397
else if (!entry && new_entry)
400
/* check if there's enough free space: size of the struct plus the
401
* size needed for a dynamic table slot */
402
if (!nv_table_reserve_table_entry(self, handle, &dyn_slot))
404
entry = nv_table_alloc_value(self, NV_ENTRY_DIRECT_HDR + name_len + value_len + 2);
405
if (G_UNLIKELY(!entry))
410
ofs = (nv_table_get_top(self) - (gchar *) entry) >> NV_TABLE_SCALE;
411
entry->vdirect.value_len = value_len;
412
if (handle >= self->num_static_entries)
414
/* we only store the name for non-builtin values */
415
entry->name_len = name_len;
416
memcpy(entry->vdirect.data, name, name_len + 1);
420
memcpy(entry->vdirect.data + entry->name_len + 1, value, value_len);
421
entry->vdirect.data[entry->name_len + 1 + value_len] = 0;
423
nv_table_set_table_entry(self, handle, ofs, dyn_slot);
428
nv_table_add_value_indirect(NVTable *self, NVHandle handle, const gchar *name, gsize name_len, NVHandle ref_handle, guint8 type, guint16 rofs, guint16 rlen, gboolean *new_entry)
430
NVEntry *entry, *ref_entry;
436
ref_entry = nv_table_get_entry(self, ref_handle, &dyn_slot);
437
if (ref_entry && ref_entry->indirect)
439
const gchar *ref_value;
442
/* NOTE: uh-oh, the to-be-referenced value is already an indirect
443
* reference, this is not supported, copy the stuff */
445
ref_value = nv_table_resolve_indirect(self, ref_entry, &ref_length);
447
if (rofs > ref_length)
454
rlen = MIN(rofs + rlen, ref_length) - rofs;
456
return nv_table_add_value(self, handle, name, name_len, ref_value + rofs, rlen, new_entry);
459
entry = nv_table_get_entry(self, handle, &dyn_slot);
460
if (!entry && !new_entry && (rlen == 0 || !ref_entry))
462
/* we don't store zero length matches unless the caller is
463
* interested in whether a new entry was created. It is used by
464
* the SDATA support code to decide whether a previously
465
* not-present SDATA was set */
469
if (entry && !entry->indirect && entry->referenced)
471
gpointer data[2] = { self, GUINT_TO_POINTER((glong) handle) };
473
if (!nv_table_foreach_entry(self, nv_table_make_direct, data))
476
if (entry && (((guint) entry->alloc_len << NV_TABLE_SCALE) >= NV_ENTRY_INDIRECT_HDR + name_len + 1))
478
/* this value already exists and the new reference fits in the old space */
479
ref_entry->referenced = TRUE;
480
entry->vindirect.handle = ref_handle;
481
entry->vindirect.ofs = rofs;
482
entry->vindirect.len = rlen;
483
entry->vindirect.type = type;
484
if (!entry->indirect)
486
/* previously a non-indirect entry, convert it */
488
if (handle >= self->num_static_entries)
490
entry->name_len = name_len;
491
memcpy(entry->vindirect.name, name, name_len + 1);
500
else if (!entry && new_entry)
503
if (!nv_table_reserve_table_entry(self, handle, &dyn_slot))
505
entry = nv_table_alloc_value(self, NV_ENTRY_INDIRECT_HDR + name_len + 1);
511
ofs = (nv_table_get_top(self) - (gchar *) entry) >> NV_TABLE_SCALE;
512
entry->vindirect.handle = ref_handle;
513
entry->vindirect.ofs = rofs;
514
entry->vindirect.len = rlen;
515
entry->vindirect.type = type;
517
ref_entry->referenced = TRUE;
518
if (handle >= self->num_static_entries)
520
entry->name_len = name_len;
521
memcpy(entry->vindirect.name, name, name_len + 1);
526
nv_table_set_table_entry(self, handle, ofs, dyn_slot);
531
nv_table_call_foreach(NVHandle handle, NVEntry *entry, gpointer user_data)
533
NVTable *self = (NVTable *) ((gpointer *) user_data)[0];
534
NVRegistry *registry = (NVRegistry *) ((gpointer *) user_data)[1];
535
NVTableForeachFunc func = ((gpointer *) user_data)[2];
536
gpointer func_data = ((gpointer *) user_data)[3];
540
value = nv_table_resolve_entry(self, entry, &value_len);
541
return func(handle, nv_registry_get_handle_name(registry, handle, NULL), value, value_len, func_data);
545
nv_table_foreach(NVTable *self, NVRegistry *registry, NVTableForeachFunc func, gpointer user_data)
547
gpointer data[4] = { self, registry, func, user_data };
549
return nv_table_foreach_entry(self, nv_table_call_foreach, data);
553
nv_table_foreach_entry(NVTable *self, NVTableForeachEntryFunc func, gpointer user_data)
555
guint32 *dyn_entries;
559
for (i = 0; i < self->num_static_entries; i++)
561
entry = nv_table_get_entry_at_ofs(self, self->static_entries[i]);
565
if (func(i + 1, entry, user_data))
569
dyn_entries = nv_table_get_dyn_entries(self);
570
for (i = 0; i < self->num_dyn_entries; i++)
572
entry = nv_table_get_entry_at_ofs(self, NV_TABLE_DYNVALUE_OFS(dyn_entries[i]));
577
if (func(NV_TABLE_DYNVALUE_HANDLE(dyn_entries[i]), entry, user_data))
586
nv_table_new(gint num_static_entries, gint num_dyn_values, gint init_length)
591
alloc_length = NV_TABLE_BOUND(init_length) + NV_TABLE_BOUND(sizeof(NVTable) + num_static_entries * sizeof(self->static_entries[0]) + num_dyn_values * sizeof(guint32));
592
self = (NVTable *) g_malloc(alloc_length);
594
self->size = alloc_length >> NV_TABLE_SCALE;
596
self->num_dyn_entries = 0;
597
self->num_static_entries = NV_TABLE_BOUND_NUM_STATIC(num_static_entries);
599
memset(&self->static_entries[0], 0, self->num_static_entries * sizeof(self->static_entries[0]));
604
nv_table_realloc(NVTable *self)
606
gint old_size = self->size;
609
if (self->ref_cnt == 1)
611
self = g_realloc(self, old_size << (NV_TABLE_SCALE + 1));
614
memmove(NV_TABLE_ADDR(self, self->size - self->used),
615
NV_TABLE_ADDR(self, old_size - self->used),
616
self->used << NV_TABLE_SCALE);
620
new = g_malloc(old_size << (NV_TABLE_SCALE + 1));
622
/* we only copy the header in this case */
623
memcpy(new, self, sizeof(NVTable) + self->num_static_entries * sizeof(self->static_entries[0]) + self->num_dyn_entries * sizeof(guint32));
627
memmove(NV_TABLE_ADDR(new, new->size - new->used),
628
NV_TABLE_ADDR(self, old_size - self->used),
629
self->used << NV_TABLE_SCALE);
631
nv_table_unref(self);
638
nv_table_ref(NVTable *self)
645
nv_table_unref(NVTable *self)
647
if (--self->ref_cnt == 0)
655
* @self: payload to clone
656
* @additional_space: specifies how much additional space is needed in
657
* the newly allocated clone
661
nv_table_clone(NVTable *self, gint additional_space)
666
if (nv_table_get_bottom(self) - nv_table_get_ofs_table_top(self) < additional_space)
667
new_size = self->size;
669
new_size = self->size + (NV_TABLE_BOUND(additional_space) >> NV_TABLE_SCALE);
671
new = g_malloc(new_size << NV_TABLE_SCALE);
672
memcpy(new, self, sizeof(NVTable) + self->num_static_entries * sizeof(self->static_entries[0]) + self->num_dyn_entries * sizeof(guint32));
673
new->size = new_size;
676
memcpy(NV_TABLE_ADDR(new, new->size - new->used),
677
NV_TABLE_ADDR(self, self->size - self->used),
678
self->used << NV_TABLE_SCALE);