]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/strbuf.c
resolved: don't check conflicts for DNS-SD enumeration RRs
[thirdparty/systemd.git] / src / basic / strbuf.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
955bd501
KS
2/***
3 This file is part of systemd.
4
1298001e 5 Copyright 2012 Kay Sievers <kay@vrfy.org>
955bd501
KS
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
11c3a366 21#include <errno.h>
955bd501
KS
22#include <stdlib.h>
23#include <string.h>
24
b5efdb8a 25#include "alloc-util.h"
955bd501
KS
26#include "strbuf.h"
27
4693cfb3 28/*
ab06eef8 29 * Strbuf stores given strings in a single continuous allocated memory
f1c0ece1
KS
30 * area. Identical strings are de-duplicated and return the same offset
31 * as the first string stored. If the tail of a string already exists
32 * in the buffer, the tail is returned.
4693cfb3 33 *
f1c0ece1
KS
34 * A trie (http://en.wikipedia.org/wiki/Trie) is used to maintain the
35 * information about the stored strings.
4693cfb3
KS
36 *
37 * Example of udev rules:
38 * $ ./udevadm test .
39 * ...
40 * read rules file: /usr/lib/udev/rules.d/99-systemd.rules
41 * rules contain 196608 bytes tokens (16384 * 12 bytes), 39742 bytes strings
42 * 23939 strings (207859 bytes), 20404 de-duplicated (171653 bytes), 3536 trie nodes used
43 * ...
44 */
45
955bd501
KS
46struct strbuf *strbuf_new(void) {
47 struct strbuf *str;
48
49 str = new0(struct strbuf, 1);
50 if (!str)
51 return NULL;
52
53 str->buf = new0(char, 1);
54 if (!str->buf)
55 goto err;
56 str->len = 1;
57
58 str->root = new0(struct strbuf_node, 1);
59 if (!str->root)
60 goto err;
61 str->nodes_count = 1;
62 return str;
63err:
64 free(str->buf);
65 free(str->root);
6b430fdb 66 return mfree(str);
955bd501
KS
67}
68
69static void strbuf_node_cleanup(struct strbuf_node *node) {
70 size_t i;
71
72 for (i = 0; i < node->children_count; i++)
73 strbuf_node_cleanup(node->children[i].child);
74 free(node->children);
75 free(node);
76}
77
4693cfb3 78/* clean up trie data, leave only the string buffer */
955bd501
KS
79void strbuf_complete(struct strbuf *str) {
80 if (!str)
81 return;
82 if (str->root)
83 strbuf_node_cleanup(str->root);
84 str->root = NULL;
85}
86
4693cfb3 87/* clean up everything */
955bd501
KS
88void strbuf_cleanup(struct strbuf *str) {
89 if (!str)
90 return;
91 if (str->root)
92 strbuf_node_cleanup(str->root);
93 free(str->buf);
94 free(str);
95}
96
3c8bed4e
ZJS
97static int strbuf_children_cmp(const struct strbuf_child_entry *n1,
98 const struct strbuf_child_entry *n2) {
955bd501
KS
99 return n1->c - n2->c;
100}
101
3c8bed4e
ZJS
102static void bubbleinsert(struct strbuf_node *node,
103 uint8_t c,
104 struct strbuf_node *node_child) {
105
106 struct strbuf_child_entry new = {
107 .c = c,
108 .child = node_child,
109 };
110 int left = 0, right = node->children_count;
111
112 while (right > left) {
113 int middle = (right + left) / 2 ;
114 if (strbuf_children_cmp(&node->children[middle], &new) <= 0)
115 left = middle + 1;
116 else
117 right = middle;
118 }
119
120 memmove(node->children + left + 1, node->children + left,
121 sizeof(struct strbuf_child_entry) * (node->children_count - left));
122 node->children[left] = new;
123
313cefa1 124 node->children_count++;
3c8bed4e
ZJS
125}
126
4693cfb3 127/* add string, return the index/offset into the buffer */
955bd501
KS
128ssize_t strbuf_add_string(struct strbuf *str, const char *s, size_t len) {
129 uint8_t c;
130 struct strbuf_node *node;
131 size_t depth;
132 char *buf_new;
133 struct strbuf_child_entry *child;
134 struct strbuf_node *node_child;
135 ssize_t off;
136
137 if (!str->root)
138 return -EINVAL;
139
140 /* search string; start from last character to find possibly matching tails */
141 if (len == 0)
142 return 0;
143 str->in_count++;
144 str->in_len += len;
145
146 node = str->root;
147 c = s[len-1];
148 for (depth = 0; depth <= len; depth++) {
149 struct strbuf_child_entry search;
150
151 /* match against current node */
152 off = node->value_off + node->value_len - len;
153 if (depth == len || (node->value_len >= len && memcmp(str->buf + off, s, len) == 0)) {
154 str->dedup_len += len;
155 str->dedup_count++;
156 return off;
157 }
158
c45606eb
LR
159 c = s[len - 1 - depth];
160
82501b3f
ZJS
161 /* bsearch is not allowed on a NULL sequence */
162 if (node->children_count == 0)
163 break;
164
955bd501 165 /* lookup child node */
955bd501 166 search.c = c;
3c8bed4e
ZJS
167 child = bsearch(&search, node->children, node->children_count,
168 sizeof(struct strbuf_child_entry),
169 (__compar_fn_t) strbuf_children_cmp);
955bd501
KS
170 if (!child)
171 break;
172 node = child->child;
173 }
174
175 /* add new string */
176 buf_new = realloc(str->buf, str->len + len+1);
177 if (!buf_new)
178 return -ENOMEM;
179 str->buf = buf_new;
180 off = str->len;
181 memcpy(str->buf + off, s, len);
182 str->len += len;
183 str->buf[str->len++] = '\0';
184
185 /* new node */
186 node_child = new0(struct strbuf_node, 1);
187 if (!node_child)
188 return -ENOMEM;
955bd501
KS
189 node_child->value_off = off;
190 node_child->value_len = len;
191
192 /* extend array, add new entry, sort for bisection */
193 child = realloc(node->children, (node->children_count + 1) * sizeof(struct strbuf_child_entry));
a9c307e5
ZJS
194 if (!child) {
195 free(node_child);
955bd501 196 return -ENOMEM;
a9c307e5
ZJS
197 }
198
199 str->nodes_count++;
200
955bd501 201 node->children = child;
3c8bed4e 202 bubbleinsert(node, c, node_child);
955bd501
KS
203
204 return off;
205}