prop_string.c revision 1.14 1 /* $NetBSD: prop_string.c,v 1.14 2020/06/06 21:25:59 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2006, 2020 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include "prop_object_impl.h"
33 #include <prop/prop_string.h>
34
35 #include <sys/rbtree.h>
36 #if defined(_KERNEL) || defined(_STANDALONE)
37 #include <sys/stdarg.h>
38 #else
39 #include <stdarg.h>
40 #endif /* _KERNEL || _STANDALONE */
41
42 struct _prop_string {
43 struct _prop_object ps_obj;
44 union {
45 char * psu_mutable;
46 const char * psu_immutable;
47 } ps_un;
48 #define ps_mutable ps_un.psu_mutable
49 #define ps_immutable ps_un.psu_immutable
50 size_t ps_size; /* not including \0 */
51 struct rb_node ps_link;
52 int ps_flags;
53 };
54
55 #define PS_F_NOCOPY 0x01
56 #define PS_F_MUTABLE 0x02
57
58 _PROP_POOL_INIT(_prop_string_pool, sizeof(struct _prop_string), "propstng")
59
60 _PROP_MALLOC_DEFINE(M_PROP_STRING, "prop string",
61 "property string container object")
62
63 static _prop_object_free_rv_t
64 _prop_string_free(prop_stack_t, prop_object_t *);
65 static bool _prop_string_externalize(
66 struct _prop_object_externalize_context *,
67 void *);
68 static _prop_object_equals_rv_t
69 _prop_string_equals(prop_object_t, prop_object_t,
70 void **, void **,
71 prop_object_t *, prop_object_t *);
72
73 static const struct _prop_object_type _prop_object_type_string = {
74 .pot_type = PROP_TYPE_STRING,
75 .pot_free = _prop_string_free,
76 .pot_extern = _prop_string_externalize,
77 .pot_equals = _prop_string_equals,
78 };
79
80 #define prop_object_is_string(x) \
81 ((x) != NULL && (x)->ps_obj.po_type == &_prop_object_type_string)
82 #define prop_string_contents(x) ((x)->ps_immutable ? (x)->ps_immutable : "")
83
84 /*
85 * In order to reduce memory usage, all immutable string objects are
86 * de-duplicated.
87 */
88
89 static int
90 /*ARGSUSED*/
91 _prop_string_rb_compare_nodes(void *ctx _PROP_ARG_UNUSED,
92 const void *n1, const void *n2)
93 {
94 const struct _prop_string * const ps1 = n1;
95 const struct _prop_string * const ps2 = n2;
96
97 _PROP_ASSERT(ps1->ps_immutable != NULL);
98 _PROP_ASSERT(ps2->ps_immutable != NULL);
99
100 return strcmp(ps1->ps_immutable, ps2->ps_immutable);
101 }
102
103 static int
104 /*ARGSUSED*/
105 _prop_string_rb_compare_key(void *ctx _PROP_ARG_UNUSED,
106 const void *n, const void *v)
107 {
108 const struct _prop_string * const ps = n;
109 const char * const cp = v;
110
111 _PROP_ASSERT(ps->ps_immutable != NULL);
112
113 return strcmp(ps->ps_immutable, cp);
114 }
115
116 static const rb_tree_ops_t _prop_string_rb_tree_ops = {
117 .rbto_compare_nodes = _prop_string_rb_compare_nodes,
118 .rbto_compare_key = _prop_string_rb_compare_key,
119 .rbto_node_offset = offsetof(struct _prop_string, ps_link),
120 .rbto_context = NULL
121 };
122
123 static struct rb_tree _prop_string_tree;
124
125 _PROP_ONCE_DECL(_prop_string_init_once)
126 _PROP_MUTEX_DECL_STATIC(_prop_string_tree_mutex)
127
128 static int
129 _prop_string_init(void)
130 {
131
132 _PROP_MUTEX_INIT(_prop_string_tree_mutex);
133 rb_tree_init(&_prop_string_tree,
134 &_prop_string_rb_tree_ops);
135
136 return 0;
137 }
138
139 /* ARGSUSED */
140 static _prop_object_free_rv_t
141 _prop_string_free(prop_stack_t stack, prop_object_t *obj)
142 {
143 prop_string_t ps = *obj;
144
145 if ((ps->ps_flags & PS_F_MUTABLE) == 0) {
146 _PROP_MUTEX_LOCK(_prop_string_tree_mutex);
147 /*
148 * Double-check the retain count now that we've
149 * acqured the tree lock; holding this lock prevents
150 * new retains from coming in by finding it in the
151 * tree.
152 */
153 if (_PROP_ATOMIC_LOAD(&ps->ps_obj.po_refcnt) == 0)
154 rb_tree_remove_node(&_prop_string_tree, ps);
155 else
156 ps = NULL;
157 _PROP_MUTEX_UNLOCK(_prop_string_tree_mutex);
158
159 if (ps == NULL)
160 return (_PROP_OBJECT_FREE_DONE);
161 }
162
163 if ((ps->ps_flags & PS_F_NOCOPY) == 0 && ps->ps_mutable != NULL)
164 _PROP_FREE(ps->ps_mutable, M_PROP_STRING);
165 _PROP_POOL_PUT(_prop_string_pool, ps);
166
167 return (_PROP_OBJECT_FREE_DONE);
168 }
169
170 static bool
171 _prop_string_externalize(struct _prop_object_externalize_context *ctx,
172 void *v)
173 {
174 prop_string_t ps = v;
175
176 if (ps->ps_size == 0)
177 return (_prop_object_externalize_empty_tag(ctx, "string"));
178
179 if (_prop_object_externalize_start_tag(ctx, "string") == false ||
180 _prop_object_externalize_append_encoded_cstring(ctx,
181 ps->ps_immutable) == false ||
182 _prop_object_externalize_end_tag(ctx, "string") == false)
183 return (false);
184
185 return (true);
186 }
187
188 /* ARGSUSED */
189 static _prop_object_equals_rv_t
190 _prop_string_equals(prop_object_t v1, prop_object_t v2,
191 void **stored_pointer1, void **stored_pointer2,
192 prop_object_t *next_obj1, prop_object_t *next_obj2)
193 {
194 prop_string_t str1 = v1;
195 prop_string_t str2 = v2;
196
197 if (str1 == str2)
198 return (_PROP_OBJECT_EQUALS_TRUE);
199 if (str1->ps_size != str2->ps_size)
200 return (_PROP_OBJECT_EQUALS_FALSE);
201 if (strcmp(prop_string_contents(str1), prop_string_contents(str2)))
202 return (_PROP_OBJECT_EQUALS_FALSE);
203 else
204 return (_PROP_OBJECT_EQUALS_TRUE);
205 }
206
207 static prop_string_t
208 _prop_string_alloc(int const flags)
209 {
210 prop_string_t ps;
211
212 ps = _PROP_POOL_GET(_prop_string_pool);
213 if (ps != NULL) {
214 _prop_object_init(&ps->ps_obj, &_prop_object_type_string);
215
216 ps->ps_mutable = NULL;
217 ps->ps_size = 0;
218 ps->ps_flags = flags;
219 }
220
221 return (ps);
222 }
223
224 static prop_string_t
225 _prop_string_instantiate(int const flags, const char * const str,
226 size_t const len)
227 {
228 prop_string_t ps;
229
230 _PROP_ONCE_RUN(_prop_string_init_once, _prop_string_init);
231
232 ps = _prop_string_alloc(flags);
233 if (ps != NULL) {
234 ps->ps_immutable = str;
235 ps->ps_size = len;
236
237 if ((flags & PS_F_MUTABLE) == 0) {
238 prop_string_t ops;
239
240 _PROP_MUTEX_LOCK(_prop_string_tree_mutex);
241 ops = rb_tree_insert_node(&_prop_string_tree, ps);
242 if (ops != ps) {
243 /*
244 * Equivalent string object already exist;
245 * free the new one and return a reference
246 * to the existing object.
247 */
248 prop_object_retain(ops);
249 _PROP_MUTEX_UNLOCK(_prop_string_tree_mutex);
250 _PROP_POOL_PUT(_prop_string_pool, ps);
251 ps = ops;
252 } else {
253 _PROP_MUTEX_UNLOCK(_prop_string_tree_mutex);
254 }
255 }
256 }
257
258 return (ps);
259 }
260
261 _PROP_DEPRECATED(prop_string_create,
262 "this program uses prop_string_create(); all functions "
263 "supporting mutable prop_strings are deprecated.")
264 prop_string_t
265 prop_string_create(void)
266 {
267
268 return (_prop_string_alloc(PS_F_MUTABLE));
269 }
270
271 _PROP_DEPRECATED(prop_string_create_cstring,
272 "this program uses prop_string_create_cstring(); all functions "
273 "supporting mutable prop_strings are deprecated.")
274 prop_string_t
275 prop_string_create_cstring(const char *str)
276 {
277 prop_string_t ps;
278 char *cp;
279 size_t len;
280
281 _PROP_ASSERT(str != NULL);
282
283 ps = _prop_string_alloc(PS_F_MUTABLE);
284 if (ps != NULL) {
285 len = strlen(str);
286 cp = _PROP_MALLOC(len + 1, M_PROP_STRING);
287 if (cp == NULL) {
288 prop_object_release(ps);
289 return (NULL);
290 }
291 strcpy(cp, str);
292 ps->ps_mutable = cp;
293 ps->ps_size = len;
294 }
295 return (ps);
296 }
297
298 _PROP_DEPRECATED(prop_string_create_cstring_nocopy,
299 "this program uses prop_string_create_cstring_nocopy(), "
300 "which is deprecated; use prop_string_create_nocopy() instead.")
301 prop_string_t
302 prop_string_create_cstring_nocopy(const char *str)
303 {
304 return prop_string_create_nocopy(str);
305 }
306
307 /*
308 * prop_string_create_format --
309 * Create a string object using the provided format string.
310 */
311 prop_string_t __printflike(1, 2)
312 prop_string_create_format(const char *fmt, ...)
313 {
314 prop_string_t ps;
315 char *str = NULL;
316 int len;
317 va_list ap;
318
319 _PROP_ASSERT(fmt != NULL);
320
321 va_start(ap, fmt);
322 len = vsnprintf(NULL, 0, fmt, ap);
323 va_end(ap);
324
325 if (len < 0)
326 return (NULL);
327
328 str = _PROP_MALLOC(len + 1, M_PROP_STRING);
329 if (str == NULL)
330 return (NULL);
331
332 va_start(ap, fmt);
333 vsnprintf(str, len + 1, fmt, ap);
334 va_end(ap);
335
336 ps = _prop_string_instantiate(0, str, len);
337 if (ps == NULL)
338 _PROP_FREE(str, M_PROP_STRING);
339
340 return (ps);
341 }
342
343 /*
344 * prop_string_create_copy --
345 * Create a string object by coping the provided constant string.
346 */
347 prop_string_t
348 prop_string_create_copy(const char *str)
349 {
350 return prop_string_create_format("%s", str);
351 }
352
353 /*
354 * prop_string_create_nocopy --
355 * Create a string object using the provided external constant
356 * string.
357 */
358 prop_string_t
359 prop_string_create_nocopy(const char *str)
360 {
361
362 _PROP_ASSERT(str != NULL);
363
364 return _prop_string_instantiate(PS_F_NOCOPY, str, strlen(str));
365 }
366
367 /*
368 * prop_string_copy --
369 * Copy a string. This reduces to a retain in the common case.
370 * Deprecated mutable string objects must be copied.
371 */
372 prop_string_t
373 prop_string_copy(prop_string_t ops)
374 {
375 prop_string_t ps;
376 char *cp;
377
378 if (! prop_object_is_string(ops))
379 return (NULL);
380
381 if ((ops->ps_flags & PS_F_MUTABLE) == 0) {
382 prop_object_retain(ops);
383 return (ops);
384 }
385
386 cp = _PROP_MALLOC(ops->ps_size + 1, M_PROP_STRING);
387 if (cp == NULL)
388 return NULL;
389
390 strcpy(cp, prop_string_contents(ops));
391
392 ps = _prop_string_instantiate(PS_F_MUTABLE, cp, ops->ps_size);
393 if (ps == NULL)
394 _PROP_FREE(cp, M_PROP_STRING);
395
396 return (ps);
397 }
398
399 _PROP_DEPRECATED(prop_string_copy_mutable,
400 "this program uses prop_string_copy_mutable(); all functions "
401 "supporting mutable prop_strings are deprecated.")
402 prop_string_t
403 prop_string_copy_mutable(prop_string_t ops)
404 {
405 prop_string_t ps;
406 char *cp;
407
408 if (! prop_object_is_string(ops))
409 return (NULL);
410
411 cp = _PROP_MALLOC(ops->ps_size + 1, M_PROP_STRING);
412 if (cp == NULL)
413 return NULL;
414
415 strcpy(cp, prop_string_contents(ops));
416
417 ps = _prop_string_instantiate(PS_F_MUTABLE, cp, ops->ps_size);
418 if (ps == NULL)
419 _PROP_FREE(cp, M_PROP_STRING);
420
421 return (ps);
422 }
423
424 /*
425 * prop_string_size --
426 * Return the size of the string, not including the terminating NUL.
427 */
428 size_t
429 prop_string_size(prop_string_t ps)
430 {
431
432 if (! prop_object_is_string(ps))
433 return (0);
434
435 return (ps->ps_size);
436 }
437
438 /*
439 * prop_string_value --
440 * Returns a pointer to the string object's value. This pointer
441 * remains valid only as long as the string object.
442 */
443 const char *
444 prop_string_value(prop_string_t ps)
445 {
446
447 if (! prop_object_is_string(ps))
448 return (NULL);
449
450 if ((ps->ps_flags & PS_F_MUTABLE) == 0)
451 return (ps->ps_immutable);
452
453 return (prop_string_contents(ps));
454 }
455
456 /*
457 * prop_string_copy_value --
458 * Copy the string object's value into the supplied buffer.
459 */
460 bool
461 prop_string_copy_value(prop_string_t ps, void *buf, size_t buflen)
462 {
463
464 if (! prop_object_is_string(ps))
465 return (false);
466
467 if (buf == NULL || buflen < ps->ps_size + 1)
468 return (false);
469
470 strcpy(buf, prop_string_contents(ps));
471
472 return (true);
473 }
474
475 _PROP_DEPRECATED(prop_string_mutable,
476 "this program uses prop_string_mutable(); all functions "
477 "supporting mutable prop_strings are deprecated.")
478 bool
479 prop_string_mutable(prop_string_t ps)
480 {
481
482 if (! prop_object_is_string(ps))
483 return (false);
484
485 return ((ps->ps_flags & PS_F_MUTABLE) != 0);
486 }
487
488 _PROP_DEPRECATED(prop_string_cstring,
489 "this program uses prop_string_cstring(), "
490 "which is deprecated; use prop_string_copy_value() instead.")
491 char *
492 prop_string_cstring(prop_string_t ps)
493 {
494 char *cp;
495
496 if (! prop_object_is_string(ps))
497 return (NULL);
498
499 cp = _PROP_MALLOC(ps->ps_size + 1, M_TEMP);
500 if (cp != NULL)
501 strcpy(cp, prop_string_contents(ps));
502
503 return (cp);
504 }
505
506 _PROP_DEPRECATED(prop_string_cstring_nocopy,
507 "this program uses prop_string_cstring_nocopy(), "
508 "which is deprecated; use prop_string_value() instead.")
509 const char *
510 prop_string_cstring_nocopy(prop_string_t ps)
511 {
512
513 if (! prop_object_is_string(ps))
514 return (NULL);
515
516 return (prop_string_contents(ps));
517 }
518
519 _PROP_DEPRECATED(prop_string_append,
520 "this program uses prop_string_append(); all functions "
521 "supporting mutable prop_strings are deprecated.")
522 bool
523 prop_string_append(prop_string_t dst, prop_string_t src)
524 {
525 char *ocp, *cp;
526 size_t len;
527
528 if (! (prop_object_is_string(dst) &&
529 prop_object_is_string(src)))
530 return (false);
531
532 if ((dst->ps_flags & PS_F_MUTABLE) == 0)
533 return (false);
534
535 len = dst->ps_size + src->ps_size;
536 cp = _PROP_MALLOC(len + 1, M_PROP_STRING);
537 if (cp == NULL)
538 return (false);
539 snprintf(cp, len + 1, "%s%s", prop_string_contents(dst),
540 prop_string_contents(src));
541 ocp = dst->ps_mutable;
542 dst->ps_mutable = cp;
543 dst->ps_size = len;
544 if (ocp != NULL)
545 _PROP_FREE(ocp, M_PROP_STRING);
546
547 return (true);
548 }
549
550 _PROP_DEPRECATED(prop_string_append_cstring,
551 "this program uses prop_string_append_cstring(); all functions "
552 "supporting mutable prop_strings are deprecated.")
553 bool
554 prop_string_append_cstring(prop_string_t dst, const char *src)
555 {
556 char *ocp, *cp;
557 size_t len;
558
559 if (! prop_object_is_string(dst))
560 return (false);
561
562 _PROP_ASSERT(src != NULL);
563
564 if ((dst->ps_flags & PS_F_MUTABLE) == 0)
565 return (false);
566
567 len = dst->ps_size + strlen(src);
568 cp = _PROP_MALLOC(len + 1, M_PROP_STRING);
569 if (cp == NULL)
570 return (false);
571 snprintf(cp, len + 1, "%s%s", prop_string_contents(dst), src);
572 ocp = dst->ps_mutable;
573 dst->ps_mutable = cp;
574 dst->ps_size = len;
575 if (ocp != NULL)
576 _PROP_FREE(ocp, M_PROP_STRING);
577
578 return (true);
579 }
580
581 /*
582 * prop_string_equals --
583 * Return true if two strings are equivalent.
584 */
585 bool
586 prop_string_equals(prop_string_t str1, prop_string_t str2)
587 {
588 if (!prop_object_is_string(str1) || !prop_object_is_string(str2))
589 return (false);
590
591 return prop_object_equals(str1, str2);
592 }
593
594 /*
595 * prop_string_equals_string --
596 * Return true if the string object is equivalent to the specified
597 * C string.
598 */
599 bool
600 prop_string_equals_string(prop_string_t ps, const char *cp)
601 {
602
603 if (! prop_object_is_string(ps))
604 return (false);
605
606 return (strcmp(prop_string_contents(ps), cp) == 0);
607 }
608
609 _PROP_DEPRECATED(prop_string_equals_cstring,
610 "this program uses prop_string_equals_cstring(), "
611 "which is deprecated; prop_string_equals_string() instead.")
612 bool
613 prop_string_equals_cstring(prop_string_t ps, const char *cp)
614 {
615 return prop_string_equals_string(ps, cp);
616 }
617
618 /*
619 * prop_string_compare --
620 * Compare two string objects, using strcmp() semantics.
621 */
622 int
623 prop_string_compare(prop_string_t ps1, prop_string_t ps2)
624 {
625 if (!prop_object_is_string(ps1) || !prop_object_is_string(ps2))
626 return (-666); /* arbitrary */
627
628 return (strcmp(prop_string_contents(ps1),
629 prop_string_contents(ps2)));
630 }
631
632 /*
633 * prop_string_compare_string --
634 * Compare a string object to the specified C string, using
635 * strcmp() semantics.
636 */
637 int
638 prop_string_compare_string(prop_string_t ps, const char *cp)
639 {
640 if (!prop_object_is_string(ps))
641 return (-666); /* arbitrary */
642
643 return (strcmp(prop_string_contents(ps), cp));
644 }
645
646 /*
647 * _prop_string_internalize --
648 * Parse a <string>...</string> and return the object created from the
649 * external representation.
650 */
651 /* ARGSUSED */
652 bool
653 _prop_string_internalize(prop_stack_t stack, prop_object_t *obj,
654 struct _prop_object_internalize_context *ctx)
655 {
656 prop_string_t string;
657 char *str;
658 size_t len, alen;
659
660 if (ctx->poic_is_empty_element) {
661 *obj = prop_string_create();
662 return (true);
663 }
664
665 /* No attributes recognized here. */
666 if (ctx->poic_tagattr != NULL)
667 return (true);
668
669 /* Compute the length of the result. */
670 if (_prop_object_internalize_decode_string(ctx, NULL, 0, &len,
671 NULL) == false)
672 return (true);
673
674 str = _PROP_MALLOC(len + 1, M_PROP_STRING);
675 if (str == NULL)
676 return (true);
677
678 if (_prop_object_internalize_decode_string(ctx, str, len, &alen,
679 &ctx->poic_cp) == false ||
680 alen != len) {
681 _PROP_FREE(str, M_PROP_STRING);
682 return (true);
683 }
684 str[len] = '\0';
685
686 if (_prop_object_internalize_find_tag(ctx, "string",
687 _PROP_TAG_TYPE_END) == false) {
688 _PROP_FREE(str, M_PROP_STRING);
689 return (true);
690 }
691
692 string = _prop_string_instantiate(0, str, len);
693 if (string == NULL)
694 _PROP_FREE(str, M_PROP_STRING);
695
696 *obj = string;
697 return (true);
698 }
699