258e2829af132f8548b59a00577a29c623fcbabc
[pspp-builds.git] / src / libpspp / intern.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2010, 2011 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include "libpspp/intern.h"
20
21 #include <assert.h>
22 #include <string.h>
23
24 #include "libpspp/assertion.h"
25 #include "libpspp/cast.h"
26 #include "libpspp/hash-functions.h"
27 #include "libpspp/hmap.h"
28
29 #include "gl/xalloc.h"
30
31 /* A single interned string. */
32 struct interned_string
33   {
34     struct hmap_node node;      /* Node in hash table. */
35     size_t ref_cnt;             /* Reference count. */
36     char string[1];             /* Null-terminated string. */
37   };
38
39 /* All interned strings. */
40 static struct hmap interns = HMAP_INITIALIZER (interns);
41
42 /* Searches the table of interned string for  */
43 static struct interned_string *
44 intern_lookup__ (const char *s, size_t length, unsigned int hash)
45 {
46   struct interned_string *is;
47
48   HMAP_FOR_EACH_WITH_HASH (is, struct interned_string, node, hash, &interns)
49     if (!memcmp (s, is->string, length + 1))
50       return is;
51
52   return NULL;
53 }
54
55 /* Returns an interned version of string S.  Pass the returned string to
56    intern_unref() to release it. */
57 const char *
58 intern_new (const char *s)
59 {
60   size_t length = strlen (s);
61   unsigned int hash = hash_bytes (s, length, 0);
62   struct interned_string *is;
63
64   is = intern_lookup__ (s, length, hash);
65   if (is != NULL)
66     is->ref_cnt++;
67   else
68     {
69       is = xmalloc (length + sizeof *is);
70       hmap_insert (&interns, &is->node, hash);
71       is->ref_cnt = 1;
72       memcpy (is->string, s, length + 1);
73     }
74   return is->string;
75 }
76
77 static struct interned_string *
78 interned_string_from_string (const char *s)
79 {
80   struct interned_string *is = UP_CAST (s, struct interned_string, string);
81   assert (is->ref_cnt > 0);
82   return is;
83 }
84
85 /* Increases the reference count on S, which must be an interned string
86    returned by intern_new(). */
87 const char *
88 intern_ref (const char *s)
89 {
90   struct interned_string *is = interned_string_from_string (s);
91   is->ref_cnt++;
92   return s;
93 }
94
95 /* Decreases the reference count on S, which must be an interned string
96    returned by intern_new().  If the reference count reaches 0, frees the
97    interned string. */
98 void
99 intern_unref (const char *s)
100 {
101   struct interned_string *is = interned_string_from_string (s);
102   if (--is->ref_cnt == 0)
103     {
104       hmap_delete (&interns, &is->node);
105       free (is);
106     }
107 }
108
109 /* Given null-terminated string S, returns true if S is an interned string
110    returned by intern_string_new(), false otherwise.
111
112    This is appropriate for use in debug assertions, e.g.:
113        assert (is_interned_string (s));
114 */
115 bool
116 is_interned_string (const char *s)
117 {
118   size_t length = strlen (s);
119   unsigned int hash = hash_bytes (s, length, 0);
120   return intern_lookup__ (s, length, hash) != NULL;
121 }