5e8791830d94457e03b7f62648dbb419059ecae9
[platform/upstream/libsolv.git] / src / strpool.c
1 /*
2  * Copyright (c) 2007, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 #include <string.h>
9 #include "util.h"
10 #include "strpool.h"
11
12 #define STRING_BLOCK      2047
13 #define STRINGSPACE_BLOCK 65535
14
15 void
16 stringpool_init(Stringpool *ss, const char *strs[])
17 {
18   unsigned totalsize = 0;
19   unsigned count;
20
21   memset(ss, 0, sizeof(*ss));
22   /* count number and total size of predefined strings */
23   for (count = 0; strs[count]; count++)
24     totalsize += strlen(strs[count]) + 1;
25
26   /* alloc appropriate space */
27   ss->stringspace = solv_extend_resize(0, totalsize, 1, STRINGSPACE_BLOCK);
28   ss->strings = solv_extend_resize(0, count, sizeof(Offset), STRING_BLOCK);
29
30   /* now copy predefined strings into allocated space */
31   ss->sstrings = 0;
32   for (count = 0; strs[count]; count++)
33     {
34       strcpy(ss->stringspace + ss->sstrings, strs[count]);
35       ss->strings[count] = ss->sstrings;
36       ss->sstrings += strlen(strs[count]) + 1;
37     }
38   ss->nstrings = count;
39 }
40
41 void
42 stringpool_free(Stringpool *ss)
43 {
44   solv_free(ss->strings);
45   solv_free(ss->stringspace);
46   solv_free(ss->stringhashtbl);
47 }
48
49 void
50 stringpool_freehash(Stringpool *ss)
51 {
52   ss->stringhashtbl = solv_free(ss->stringhashtbl);
53   ss->stringhashmask = 0;
54 }
55
56 void
57 stringpool_init_empty(Stringpool *ss)
58 {
59   const char *emptystrs[] = {
60     "<NULL>",
61     "",
62     0,
63   };
64   stringpool_init(ss, emptystrs);
65 }
66
67 void
68 stringpool_clone(Stringpool *ss, Stringpool *from)
69 {
70   memset(ss, 0, sizeof(*ss));
71   ss->strings = solv_extend_resize(0, from->nstrings, sizeof(Offset), STRING_BLOCK);
72   memcpy(ss->strings, from->strings, from->nstrings * sizeof(Offset));
73   ss->stringspace = solv_extend_resize(0, from->sstrings, 1, STRINGSPACE_BLOCK);
74   memcpy(ss->stringspace, from->stringspace, from->sstrings);
75   ss->nstrings = from->nstrings;
76   ss->sstrings = from->sstrings;
77 }
78
79 void
80 stringpool_resize_hash(Stringpool *ss, int numnew)
81 {
82   Hashval h, hh, hashmask;
83   Hashtable hashtbl;
84   int i;
85
86   if (numnew <= 0)
87     return;
88   hashmask = mkmask(ss->nstrings + numnew);
89   if (hashmask <= ss->stringhashmask)
90     return;     /* same as before */
91
92   /* realloc hash table */
93   ss->stringhashmask = hashmask;
94   solv_free(ss->stringhashtbl);
95   ss->stringhashtbl = hashtbl = (Hashtable)solv_calloc(hashmask + 1, sizeof(Id));
96   
97   /* rehash all strings into new hashtable */
98   for (i = 1; i < ss->nstrings; i++)
99     {
100       h = strhash(ss->stringspace + ss->strings[i]) & hashmask;
101       hh = HASHCHAIN_START;
102       while (hashtbl[h] != 0)
103         h = HASHCHAIN_NEXT(h, hh, hashmask);
104       hashtbl[h] = i;
105     }
106 }
107
108 Id
109 stringpool_strn2id(Stringpool *ss, const char *str, unsigned int len, int create)
110 {
111   Hashval h, hh, hashmask, oldhashmask;
112   Id id;
113   Hashtable hashtbl;
114
115   if (!str)
116     return STRID_NULL;
117   if (!len)
118     return STRID_EMPTY;
119
120   hashmask = oldhashmask = ss->stringhashmask;
121   /* expand hashtable if needed */
122   if ((Hashval)ss->nstrings * 2 > hashmask)
123     {
124       stringpool_resize_hash(ss, STRING_BLOCK);
125       hashmask = ss->stringhashmask;
126     }
127   hashtbl = ss->stringhashtbl;
128
129   /* compute hash and check for match */
130   h = strnhash(str, len) & hashmask;
131   hh = HASHCHAIN_START;
132   while ((id = hashtbl[h]) != 0)
133     {
134       if(!memcmp(ss->stringspace + ss->strings[id], str, len)
135          && ss->stringspace[ss->strings[id] + len] == 0)
136         break;
137       h = HASHCHAIN_NEXT(h, hh, hashmask);
138     }
139   if (id || !create)    /* exit here if string found */
140     return id;
141
142   /* this should be a test for a flag that tells us if the
143    * correct blocking is used, but adding a flag would break
144    * the ABI. So we use the existance of the hash area as
145    * indication instead */
146   if (!oldhashmask)
147     {
148       ss->stringspace = solv_extend_resize(ss->stringspace, ss->sstrings + len + 1, 1, STRINGSPACE_BLOCK);
149       ss->strings = solv_extend_resize(ss->strings, ss->nstrings + 1, sizeof(Offset), STRING_BLOCK);
150     }
151
152   /* generate next id and save in table */
153   id = ss->nstrings++;
154   hashtbl[h] = id;
155
156   ss->strings = solv_extend(ss->strings, id, 1, sizeof(Offset), STRING_BLOCK);
157   ss->strings[id] = ss->sstrings;       /* we will append to the end */
158
159   /* append string to stringspace */
160   ss->stringspace = solv_extend(ss->stringspace, ss->sstrings, len + 1, 1, STRINGSPACE_BLOCK);
161   memcpy(ss->stringspace + ss->sstrings, str, len);
162   ss->stringspace[ss->sstrings + len] = 0;
163   ss->sstrings += len + 1;
164   return id;
165 }
166
167 Id
168 stringpool_str2id(Stringpool *ss, const char *str, int create)
169 {
170   if (!str)
171     return STRID_NULL;
172   if (!*str)
173     return STRID_EMPTY;
174   return stringpool_strn2id(ss, str, (unsigned int)strlen(str), create);
175 }
176
177 void
178 stringpool_shrink(Stringpool *ss)
179 {
180   ss->stringspace = solv_extend_resize(ss->stringspace, ss->sstrings, 1, STRINGSPACE_BLOCK);
181   ss->strings = solv_extend_resize(ss->strings, ss->nstrings, sizeof(Offset), STRING_BLOCK);
182 }