1 /*
2  * Copyright © 2011 Mozilla Foundation
3  *
4  * This program is made available under an ISC-style license.  See the
5  * accompanying file LICENSE for details.
6  */
7 
8 #include "cubeb_strings.h"
9 
10 #include <assert.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #define CUBEB_STRINGS_INLINE_COUNT 4
15 
16 struct cubeb_strings {
17   uint32_t size;
18   uint32_t count;
19   char ** data;
20   char * small_store[CUBEB_STRINGS_INLINE_COUNT];
21 };
22 
23 int
cubeb_strings_init(cubeb_strings ** strings)24 cubeb_strings_init(cubeb_strings ** strings)
25 {
26   cubeb_strings* strs = NULL;
27 
28   if (!strings) {
29     return CUBEB_ERROR;
30   }
31 
32   strs = calloc(1, sizeof(cubeb_strings));
33   assert(strs);
34 
35   if (!strs) {
36     return CUBEB_ERROR;
37   }
38 
39   strs->size = sizeof(strs->small_store) / sizeof(strs->small_store[0]);
40   strs->count = 0;
41   strs->data = strs->small_store;
42 
43   *strings = strs;
44 
45   return CUBEB_OK;
46 }
47 
48 void
cubeb_strings_destroy(cubeb_strings * strings)49 cubeb_strings_destroy(cubeb_strings * strings)
50 {
51   char ** sp = NULL;
52   char ** se = NULL;
53 
54   if (!strings) {
55     return;
56   }
57 
58   sp = strings->data;
59   se = sp + strings->count;
60 
61   for ( ;  sp != se; sp++) {
62     if (*sp) {
63       free(*sp);
64     }
65   }
66 
67   if (strings->data != strings->small_store) {
68     free(strings->data);
69   }
70 
71   free(strings);
72 }
73 
74 /** Look for string in string storage.
75     @param strings Opaque pointer to interned string storage.
76     @param s String to look up.
77     @retval Read-only string or NULL if not found. */
78 static char const *
cubeb_strings_lookup(cubeb_strings * strings,char const * s)79 cubeb_strings_lookup(cubeb_strings * strings, char const * s)
80 {
81   char ** sp = NULL;
82   char ** se = NULL;
83 
84   if (!strings || !s) {
85     return NULL;
86   }
87 
88   sp = strings->data;
89   se = sp + strings->count;
90 
91   for ( ; sp != se; sp++) {
92     if (*sp && strcmp(*sp, s) == 0) {
93       return *sp;
94     }
95   }
96 
97   return NULL;
98 }
99 
100 static char const *
cubeb_strings_push(cubeb_strings * strings,char const * s)101 cubeb_strings_push(cubeb_strings * strings, char const * s)
102 {
103   char * is = NULL;
104 
105   if (strings->count == strings->size) {
106     char ** new_data;
107     uint32_t value_size = sizeof(char const *);
108     uint32_t new_size = strings->size * 2;
109     if (!new_size || value_size > (uint32_t)-1 / new_size) {
110       // overflow
111       return NULL;
112     }
113 
114     if (strings->small_store == strings->data) {
115       // First time heap allocation.
116       new_data = malloc(new_size * value_size);
117       if (new_data) {
118         memcpy(new_data, strings->small_store, sizeof(strings->small_store));
119       }
120     } else {
121       new_data = realloc(strings->data, new_size * value_size);
122     }
123 
124     if (!new_data) {
125       // out of memory
126       return NULL;
127     }
128 
129     strings->size = new_size;
130     strings->data = new_data;
131   }
132 
133   is = strdup(s);
134   strings->data[strings->count++] = is;
135 
136   return is;
137 }
138 
139 char const *
cubeb_strings_intern(cubeb_strings * strings,char const * s)140 cubeb_strings_intern(cubeb_strings * strings, char const * s)
141 {
142   char const * is = NULL;
143 
144   if (!strings || !s) {
145     return NULL;
146   }
147 
148   is = cubeb_strings_lookup(strings, s);
149   if (is) {
150     return is;
151   }
152 
153   return cubeb_strings_push(strings, s);
154 }
155 
156