1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2010, 2011 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "libpspp/intern.h"
24 #include "libpspp/assertion.h"
25 #include "libpspp/cast.h"
26 #include "libpspp/hash-functions.h"
27 #include "libpspp/hmap.h"
29 #include "gl/xalloc.h"
31 /* A single interned string. */
32 struct interned_string
34 struct hmap_node node; /* Node in hash table. */
35 size_t ref_cnt; /* Reference count. */
36 size_t length; /* strlen(string). */
37 char string[1]; /* Null-terminated string. */
40 /* All interned strings. */
41 static struct hmap interns = HMAP_INITIALIZER (interns);
43 /* Searches the table of interned strings for one equal to S, which has length
44 LENGTH and hash value HASH. */
45 static struct interned_string *
46 intern_lookup__ (const char *s, size_t length, unsigned int hash)
48 struct interned_string *is;
50 HMAP_FOR_EACH_WITH_HASH (is, struct interned_string, node, hash, &interns)
51 if (is->length == length && !memcmp (s, is->string, length))
57 /* Returns an interned version of string S. Pass the returned string to
58 intern_unref() to release it. */
60 intern_new (const char *s)
62 size_t length = strlen (s);
63 unsigned int hash = hash_bytes (s, length, 0);
64 struct interned_string *is;
66 is = intern_lookup__ (s, length, hash);
71 is = xmalloc (length + sizeof *is);
72 hmap_insert (&interns, &is->node, hash);
75 memcpy (is->string, s, length + 1);
81 intern_new_if_nonnull (const char *s)
83 return s ? intern_new (s) : NULL;
87 static struct interned_string *
88 interned_string_from_string (const char *s_)
90 char (*s)[1] = (char (*)[1]) s_;
91 struct interned_string *is = UP_CAST (s, struct interned_string, string);
92 assert (is->ref_cnt > 0);
96 /* Increases the reference count on S, which must be an interned string
97 returned by intern_new(). */
99 intern_ref (const char *s)
101 struct interned_string *is = interned_string_from_string (s);
107 intern_ref_if_nonnull (const char *s)
109 return s ? intern_ref (s) : NULL;
112 /* Decreases the reference count on S, which must be an interned string
113 returned by intern_new(). If the reference count reaches 0, frees the
116 intern_unref (const char *s)
120 struct interned_string *is = interned_string_from_string (s);
121 if (--is->ref_cnt == 0)
123 hmap_delete (&interns, &is->node);
129 /* Given null-terminated string S, returns true if S is an interned string
130 returned by intern_string_new(), false otherwise.
132 This is appropriate for use in debug assertions, e.g.:
133 assert (is_interned_string (s));
136 is_interned_string (const char *s)
138 size_t length = strlen (s);
139 unsigned int hash = hash_bytes (s, length, 0);
140 return intern_lookup__ (s, length, hash) != NULL;
143 /* Returns the length of S, which must be an interned string returned by
146 intern_strlen (const char *s)
148 return interned_string_from_string (s)->length;