1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2012 Kay Sievers <kay@vrfy.org>
12 #include "alloc-util.h"
17 * Strbuf stores given strings in a single continuous allocated memory
18 * area. Identical strings are de-duplicated and return the same offset
19 * as the first string stored. If the tail of a string already exists
20 * in the buffer, the tail is returned.
22 * A trie (http://en.wikipedia.org/wiki/Trie) is used to maintain the
23 * information about the stored strings.
25 * Example of udev rules:
28 * read rules file: /usr/lib/udev/rules.d/99-systemd.rules
29 * rules contain 196608 bytes tokens (16384 * 12 bytes), 39742 bytes strings
30 * 23939 strings (207859 bytes), 20404 de-duplicated (171653 bytes), 3536 trie nodes used
34 struct strbuf
*strbuf_new(void) {
37 str
= new(struct strbuf
, 1);
40 *str
= (struct strbuf
) {
42 .root
= new0(struct strbuf_node
, 1),
46 if (!str
->buf
|| !str
->root
) {
55 static struct strbuf_node
* strbuf_node_cleanup(struct strbuf_node
*node
) {
58 for (i
= 0; i
< node
->children_count
; i
++)
59 strbuf_node_cleanup(node
->children
[i
].child
);
64 /* clean up trie data, leave only the string buffer */
65 void strbuf_complete(struct strbuf
*str
) {
69 str
->root
= strbuf_node_cleanup(str
->root
);
72 /* clean up everything */
73 void strbuf_cleanup(struct strbuf
*str
) {
79 static int strbuf_children_cmp(const struct strbuf_child_entry
*n1
,
80 const struct strbuf_child_entry
*n2
) {
84 static void bubbleinsert(struct strbuf_node
*node
,
86 struct strbuf_node
*node_child
) {
88 struct strbuf_child_entry
new = {
92 int left
= 0, right
= node
->children_count
;
94 while (right
> left
) {
95 int middle
= (right
+ left
) / 2 ;
96 if (strbuf_children_cmp(&node
->children
[middle
], &new) <= 0)
102 memmove(node
->children
+ left
+ 1, node
->children
+ left
,
103 sizeof(struct strbuf_child_entry
) * (node
->children_count
- left
));
104 node
->children
[left
] = new;
106 node
->children_count
++;
109 /* add string, return the index/offset into the buffer */
110 ssize_t
strbuf_add_string(struct strbuf
*str
, const char *s
, size_t len
) {
112 struct strbuf_node
*node
;
115 struct strbuf_child_entry
*child
;
116 struct strbuf_node
*node_child
;
122 /* search string; start from last character to find possibly matching tails */
132 for (depth
= 0; depth
<= len
; depth
++) {
133 struct strbuf_child_entry search
;
135 /* match against current node */
136 off
= node
->value_off
+ node
->value_len
- len
;
137 if (depth
== len
|| (node
->value_len
>= len
&& memcmp(str
->buf
+ off
, s
, len
) == 0)) {
138 str
->dedup_len
+= len
;
143 c
= s
[len
- 1 - depth
];
145 /* lookup child node */
147 child
= bsearch_safe(&search
, node
->children
, node
->children_count
,
148 sizeof(struct strbuf_child_entry
),
149 (__compar_fn_t
) strbuf_children_cmp
);
156 buf_new
= realloc(str
->buf
, str
->len
+ len
+1);
161 memcpy(str
->buf
+ off
, s
, len
);
163 str
->buf
[str
->len
++] = '\0';
166 node_child
= new(struct strbuf_node
, 1);
169 *node_child
= (struct strbuf_node
) {
174 /* extend array, add new entry, sort for bisection */
175 child
= reallocarray(node
->children
, node
->children_count
+ 1, sizeof(struct strbuf_child_entry
));
183 node
->children
= child
;
184 bubbleinsert(node
, c
, node_child
);