1/*	$NetBSD: string.c,v 1.2 2017/01/28 21:31:45 christos Exp $	*/
2
3/*
4 * Copyright (c) 2010 Kungliga Tekniska H��gskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
7 *
8 * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
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 *
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 *
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 *
21 * 3. Neither the name of the Institute nor the names of its contributors
22 *    may be used to endorse or promote products derived from this software
23 *    without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 */
37
38#include "baselocl.h"
39#include <string.h>
40
41static void
42string_dealloc(void *ptr)
43{
44    heim_string_t s = ptr;
45    heim_string_free_f_t *deallocp;
46    heim_string_free_f_t dealloc;
47
48    if (*(const char *)ptr != '\0')
49	return;
50
51    /* Possible string ref */
52    deallocp = _heim_get_isaextra(s, 0);
53    dealloc = *deallocp;
54    if (dealloc != NULL) {
55	char **strp = _heim_get_isaextra(s, 1);
56	dealloc(*strp);
57    }
58}
59
60static int
61string_cmp(void *a, void *b)
62{
63    if (*(char *)a == '\0') {
64	char **strp = _heim_get_isaextra(a, 1);
65
66	if (*strp != NULL)
67	    a = *strp; /* a is a string ref */
68    }
69    if (*(char *)b == '\0') {
70	char **strp = _heim_get_isaextra(b, 1);
71
72	if (*strp != NULL)
73	    b = *strp; /* b is a string ref */
74    }
75    return strcmp(a, b);
76}
77
78static unsigned long
79string_hash(void *ptr)
80{
81    const char *s = ptr;
82    unsigned long n;
83
84    for (n = 0; *s; ++s)
85	n += *s;
86    return n;
87}
88
89struct heim_type_data _heim_string_object = {
90    HEIM_TID_STRING,
91    "string-object",
92    NULL,
93    string_dealloc,
94    NULL,
95    string_cmp,
96    string_hash,
97    NULL
98};
99
100/**
101 * Create a string object
102 *
103 * @param string the string to create, must be an utf8 string
104 *
105 * @return string object
106 */
107
108heim_string_t
109heim_string_create(const char *string)
110{
111    return heim_string_create_with_bytes(string, strlen(string));
112}
113
114/**
115 * Create a string object without copying the source.
116 *
117 * @param string the string to referenced, must be UTF-8
118 * @param dealloc the function to use to release the referece to the string
119 *
120 * @return string object
121 */
122
123heim_string_t
124heim_string_ref_create(const char *string, heim_string_free_f_t dealloc)
125{
126    heim_string_t s;
127    heim_string_free_f_t *deallocp;
128
129    s = _heim_alloc_object(&_heim_string_object, 1);
130    if (s) {
131	const char **strp;
132
133	((char *)s)[0] = '\0';
134	deallocp = _heim_get_isaextra(s, 0);
135	*deallocp = dealloc;
136	strp = _heim_get_isaextra(s, 1);
137	*strp = string;
138    }
139    return s;
140}
141
142/**
143 * Create a string object
144 *
145 * @param string the string to create, must be an utf8 string
146 * @param len the length of the string
147 *
148 * @return string object
149 */
150
151heim_string_t
152heim_string_create_with_bytes(const void *data, size_t len)
153{
154    heim_string_t s;
155
156    s = _heim_alloc_object(&_heim_string_object, len + 1);
157    if (s) {
158	memcpy(s, data, len);
159	((char *)s)[len] = '\0';
160    }
161    return s;
162}
163
164/**
165 * Create a string object using a format string
166 *
167 * @param fmt format string
168 * @param ...
169 *
170 * @return string object
171 */
172
173heim_string_t
174heim_string_create_with_format(const char *fmt, ...)
175{
176    heim_string_t s;
177    char *str = NULL;
178    va_list ap;
179    int ret;
180
181    va_start(ap, fmt);
182    ret = vasprintf(&str, fmt, ap);
183    va_end(ap);
184    if (ret < 0 || str == NULL)
185	return NULL;
186
187    s = heim_string_ref_create(str, string_dealloc);
188    if (s == NULL)
189	free(str);
190    return s;
191}
192
193/**
194 * Return the type ID of string objects
195 *
196 * @return type id of string objects
197 */
198
199heim_tid_t
200heim_string_get_type_id(void)
201{
202    return HEIM_TID_STRING;
203}
204
205/**
206 * Get the string value of the content.
207 *
208 * @param string the string object to get the value from
209 *
210 * @return a utf8 string
211 */
212
213const char *
214heim_string_get_utf8(heim_string_t string)
215{
216    if (*(const char *)string == '\0') {
217	const char **strp;
218
219	/* String ref */
220	strp = _heim_get_isaextra(string, 1);
221	if (*strp != NULL)
222	    return *strp;
223    }
224    return (const char *)string;
225}
226
227/*
228 *
229 */
230
231static void
232init_string(void *ptr)
233{
234    heim_dict_t *dict = ptr;
235    *dict = heim_dict_create(101);
236    heim_assert(*dict != NULL, "__heim_string_constant");
237}
238
239heim_string_t
240__heim_string_constant(const char *_str)
241{
242    static HEIMDAL_MUTEX mutex = HEIMDAL_MUTEX_INITIALIZER;
243    static heim_base_once_t once;
244    static heim_dict_t dict = NULL;
245    heim_string_t s, s2;
246
247    heim_base_once_f(&once, &dict, init_string);
248    s = heim_string_create(_str);
249
250    HEIMDAL_MUTEX_lock(&mutex);
251    s2 = heim_dict_get_value(dict, s);
252    if (s2) {
253	heim_release(s);
254	s = s2;
255    } else {
256	_heim_make_permanent(s);
257	heim_dict_set_value(dict, s, s);
258    }
259    HEIMDAL_MUTEX_unlock(&mutex);
260
261    return s;
262}
263