Coverage Report

Created: 2026-10-13 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pidgin/libpurple/stringref.c
Line
Count
Source
1
/**
2
 * @file stringref.c Reference-counted immutable strings
3
 * @ingroup core
4
 */
5
6
/* purple
7
 *
8
 * Purple is the legal property of its developers, whose names are too numerous
9
 * to list here.  Please refer to the COPYRIGHT file distributed with this
10
 * source distribution.
11
 *
12
 * This program is free software; you can redistribute it and/or modify
13
 * it under the terms of the GNU General Public License as published by
14
 * the Free Software Foundation; either version 2 of the License, or
15
 * (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU General Public License
23
 * along with this program; if not, write to the Free Software
24
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
25
 *
26
 */
27
28
#include "internal.h"
29
30
#include <string.h>
31
#include <stdarg.h>
32
33
#include "debug.h"
34
#include "eventloop.h"
35
#include "stringref.h"
36
37
/**
38
 * The internal representation of a stringref.
39
 *
40
 * @note For this structure to be useful, the string contained within
41
 * it must be immutable -- for this reason, do _not_ access it
42
 * directly!
43
 */
44
struct _PurpleStringref {
45
  guint32 ref;  /**< The reference count of this string.
46
           *   Note that reference counts are only
47
           *   31 bits, and the high-order bit
48
           *   indicates whether this string is up
49
           *   for GC at the next idle handler...
50
           *   But you aren't going to touch this
51
           *   anyway, right? */
52
  char value[1];  /**< The string contained in this ref.
53
           *   Notice that it is simply "hanging
54
           *   off the end" of the ref ... this
55
           *   is to save an allocation. */
56
};
57
58
0
#define REFCOUNT(x) ((x) & 0x7fffffff)
59
60
static GList *gclist = NULL;
61
62
static void stringref_free(PurpleStringref *stringref);
63
static gboolean gs_idle_cb(gpointer data);
64
65
PurpleStringref *purple_stringref_new(const char *value)
66
0
{
67
0
  PurpleStringref *newref;
68
0
  size_t len;
69
70
0
  if (value == NULL)
71
0
    return NULL;
72
73
0
  len = strlen(value);
74
75
0
  newref = g_malloc(sizeof(PurpleStringref) + len);
76
  /* g_strlcpy() takes the size of the buffer, including the NUL.
77
     strlen() returns the length of the string, without the NUL. */
78
0
  g_strlcpy(newref->value, value, len + 1);
79
0
  newref->ref = 1;
80
81
0
  return newref;
82
0
}
83
84
PurpleStringref *purple_stringref_new_noref(const char *value)
85
0
{
86
0
  PurpleStringref *newref;
87
88
0
  if (value == NULL)
89
0
    return NULL;
90
91
0
  newref = g_malloc(sizeof(PurpleStringref) + strlen(value));
92
0
  strcpy(newref->value, value);
93
0
  newref->ref = 0x80000000;
94
95
0
  if (gclist == NULL)
96
0
    purple_timeout_add(0, gs_idle_cb, NULL);
97
0
  gclist = g_list_prepend(gclist, newref);
98
99
0
  return newref;
100
0
}
101
102
PurpleStringref *purple_stringref_printf(const char *format, ...)
103
0
{
104
0
  PurpleStringref *newref;
105
0
  va_list ap;
106
107
0
  if (format == NULL)
108
0
    return NULL;
109
110
0
  va_start(ap, format);
111
0
  newref = g_malloc(sizeof(PurpleStringref) + g_printf_string_upper_bound(format, ap));
112
0
  vsprintf(newref->value, format, ap);
113
0
  va_end(ap);
114
0
  newref->ref = 1;
115
116
0
  return newref;
117
0
}
118
119
PurpleStringref *purple_stringref_ref(PurpleStringref *stringref)
120
0
{
121
0
  if (stringref == NULL)
122
0
    return NULL;
123
0
  stringref->ref++;
124
0
  return stringref;
125
0
}
126
127
void purple_stringref_unref(PurpleStringref *stringref)
128
0
{
129
0
  if (stringref == NULL)
130
0
    return;
131
0
  if (REFCOUNT(--(stringref->ref)) == 0) {
132
0
    if (stringref->ref & 0x80000000)
133
0
      gclist = g_list_remove(gclist, stringref);
134
0
    stringref_free(stringref);
135
0
  }
136
0
}
137
138
const char *purple_stringref_value(const PurpleStringref *stringref)
139
0
{
140
0
  return (stringref == NULL ? NULL : stringref->value);
141
0
}
142
143
int purple_stringref_cmp(const PurpleStringref *s1, const PurpleStringref *s2)
144
0
{
145
0
  return (s1 == s2 ? 0 : strcmp(purple_stringref_value(s1), purple_stringref_value(s2)));
146
0
}
147
148
size_t purple_stringref_len(const PurpleStringref *stringref)
149
0
{
150
0
  return strlen(purple_stringref_value(stringref));
151
0
}
152
153
static void stringref_free(PurpleStringref *stringref)
154
0
{
155
#ifdef DEBUG
156
  if (REFCOUNT(stringref->ref) != 0) {
157
    purple_debug(PURPLE_DEBUG_ERROR, "stringref", "Free of nonzero (%d) ref stringref!\n", REFCOUNT(stringref->ref));
158
    return;
159
  }
160
#endif /* DEBUG */
161
0
  g_free(stringref);
162
0
}
163
164
static gboolean gs_idle_cb(gpointer data)
165
0
{
166
0
  PurpleStringref *ref;
167
0
  GList *del;
168
169
0
  while (gclist != NULL) {
170
0
    ref = gclist->data;
171
0
    if (REFCOUNT(ref->ref) == 0) {
172
0
      stringref_free(ref);
173
0
    }
174
0
    del = gclist;
175
0
    gclist = gclist->next;
176
0
    g_list_free_1(del);
177
0
  }
178
179
0
  return FALSE;
180
0
}