Merge commit 'origin/covariance'
[pspp-builds.git] / src / libpspp / intern.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2010 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/hash-functions.h"
26 #include "libpspp/hmap.h"
27
28 #include "gl/xalloc.h"
29
30 /* A single interned string. */
31 struct interned_string
32   {
33     struct hmap_node node;      /* Node in hash table. */
34     size_t ref_cnt;             /* Reference count. */
35     char string[1];             /* Null-terminated string. */
36   };
37
38 /* All interned strings. */
39 static struct hmap interns = HMAP_INITIALIZER (interns);
40
41 /* Searches the table of interned string for  */
42 static struct interned_string *
43 intern_lookup__ (const char *s, size_t length, unsigned int hash)
44 {
45   struct interned_string *is;
46
47   HMAP_FOR_EACH_WITH_HASH (is, struct interned_string, node, hash, &interns)
48     if (!memcmp (s, is->string, length + 1))
49       return is;
50
51   return NULL;
52 }
53
54 /* Returns an interned version of string S.  Pass the returned string to
55    intern_unref() to release it. */
56 const char *
57 intern_new (const char *s)
58 {
59   size_t length = strlen (s);
60   unsigned int hash = hash_bytes (s, length, 0);
61   struct interned_string *is;
62
63   is = intern_lookup__ (s, length, hash);
64   if (is != NULL)
65     is->ref_cnt++;
66   else
67     {
68       is = xmalloc (length + sizeof *is);
69       hmap_insert (&interns, &is->node, hash);
70       is->ref_cnt = 1;
71       memcpy (is->string, s, length + 1);
72     }
73   return is->string;
74 }
75
76 static struct interned_string *
77 interned_string_from_string (const char *s)
78 {
79   const size_t ofs = offsetof (struct interned_string, string);
80   struct interned_string *is = (struct interned_string *) (s - ofs);
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 }