]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/strbuf.c
c2342e67a22bdc4dd430c5d36e8958fc49cbddc6
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "alloc-util.h"
10 * Strbuf stores given strings in a single continuous allocated memory
11 * area. Identical strings are de-duplicated and return the same offset
12 * as the first string stored. If the tail of a string already exists
13 * in the buffer, the tail is returned.
15 * A trie (http://en.wikipedia.org/wiki/Trie) is used to maintain the
16 * information about the stored strings.
18 * Example of udev rules:
21 * read rules file: /usr/lib/udev/rules.d/99-systemd.rules
22 * rules contain 196608 bytes tokens (16384 * 12 bytes), 39742 bytes strings
23 * 23939 strings (207859 bytes), 20404 de-duplicated (171653 bytes), 3536 trie nodes used
27 struct strbuf
* strbuf_new(void) {
28 _cleanup_(strbuf_freep
) struct strbuf
*str
= NULL
;
30 str
= new(struct strbuf
, 1);
34 *str
= (struct strbuf
) {
36 .root
= new0(struct strbuf_node
, 1),
40 if (!str
->buf
|| !str
->root
)
46 static struct strbuf_node
* strbuf_node_cleanup(struct strbuf_node
*node
) {
49 FOREACH_ARRAY(child
, node
->children
, node
->children_count
)
50 strbuf_node_cleanup(child
->child
);
56 /* clean up trie data, leave only the string buffer */
57 void strbuf_complete(struct strbuf
*str
) {
58 if (!str
|| !str
->root
)
61 str
->root
= strbuf_node_cleanup(str
->root
);
64 /* clean up everything */
65 struct strbuf
* strbuf_free(struct strbuf
*str
) {
74 static int strbuf_children_cmp(const struct strbuf_child_entry
*n1
, const struct strbuf_child_entry
*n2
) {
78 return CMP(n1
->c
, n2
->c
);
81 static void bubbleinsert(struct strbuf_node
*node
,
83 struct strbuf_node
*node_child
) {
85 struct strbuf_child_entry
new = {
89 int left
= 0, right
= node
->children_count
;
91 while (right
> left
) {
92 int middle
= (right
+ left
) / 2 ;
93 if (strbuf_children_cmp(&node
->children
[middle
], &new) <= 0)
99 memmove(node
->children
+ left
+ 1, node
->children
+ left
,
100 sizeof(struct strbuf_child_entry
) * (node
->children_count
- left
));
101 node
->children
[left
] = new;
103 node
->children_count
++;
106 /* add string, return the index/offset into the buffer */
107 ssize_t
strbuf_add_string_full(struct strbuf
*str
, const char *s
, size_t len
) {
112 assert(s
|| len
== 0);
120 /* search string; start from last character to find possibly matching tails */
129 struct strbuf_node
*node
= str
->root
;
130 for (size_t depth
= 0; depth
<= len
; depth
++) {
131 /* match against current node */
132 off
= node
->value_off
+ node
->value_len
- len
;
133 if (depth
== len
|| (node
->value_len
>= len
&& memcmp(str
->buf
+ off
, s
, len
) == 0)) {
134 str
->dedup_len
+= len
;
139 c
= s
[len
- 1 - depth
];
141 /* lookup child node */
142 struct strbuf_child_entry
*child
, search
= { .c
= c
};
143 child
= typesafe_bsearch(&search
, node
->children
, node
->children_count
, strbuf_children_cmp
);
150 if (!GREEDY_REALLOC(str
->buf
, str
->len
+ len
+ 1))
153 memcpy(str
->buf
+ off
, s
, len
);
155 str
->buf
[str
->len
++] = '\0';
158 _cleanup_free_
struct strbuf_node
*node_child
= NULL
;
160 node_child
= new(struct strbuf_node
, 1);
163 *node_child
= (struct strbuf_node
) {
168 /* extend array, add new entry, sort for bisection */
169 if (!GREEDY_REALLOC(node
->children
, node
->children_count
+ 1))
174 bubbleinsert(node
, c
, TAKE_PTR(node_child
));