Add reference counted strings

The last part of the reference counting saga.

Now that we have:

 - reference counter types
 - reference counted allocations

we can finally add reference counted strings using reference counted
allocations to avoid creating a new String type, and reimplementing
every single string-based API.
This commit is contained in:
Emmanuele Bassi 2018-06-12 13:46:28 +01:00
parent 4b33b03dd3
commit 00a723f597
12 changed files with 308 additions and 0 deletions

View File

@ -122,6 +122,7 @@
<xi:include href="xml/refcount.xml"/>
<xi:include href="xml/rcbox.xml"/>
<xi:include href="xml/arcbox.xml"/>
<xi:include href="xml/refstring.xml"/>
</chapter>
<chapter id="deprecated">

View File

@ -3490,3 +3490,11 @@ g_arc_box_acquire
g_arc_box_release
g_arc_box_release_full
</SECTION>
<SECTION>
<FILE>refstring</FILE>
g_ref_string_new
g_ref_string_new_intern
g_ref_string_acquire
g_ref_string_release
</SECTION>

View File

@ -153,6 +153,7 @@ libglib_2_0_la_SOURCES = \
grcbox.c \
grcboxprivate.h \
grefcount.c \
grefstring.c \
gregex.c \
gscanner.c \
gscripttable.h \
@ -291,6 +292,7 @@ glibsubinclude_HEADERS = \
grand.h \
grcbox.h \
grefcount.h \
grefstring.h \
gregex.h \
gscanner.h \
gsequence.h \

View File

@ -87,3 +87,4 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantDict, g_variant_dict_unref)
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GVariantDict, g_variant_dict_clear)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantType, g_variant_type_free)
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(GStrv, g_strfreev, NULL)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GRefString, g_ref_string_release)

View File

@ -71,6 +71,7 @@
#include <glib/grand.h>
#include <glib/grcbox.h>
#include <glib/grefcount.h>
#include <glib/grefstring.h>
#include <glib/gregex.h>
#include <glib/gscanner.h>
#include <glib/gsequence.h>

175
glib/grefstring.c Normal file
View File

@ -0,0 +1,175 @@
/* grefstring.c: Reference counted strings
*
* Copyright 2018 Emmanuele Bassi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
/**
* SECTION:refstring
* @Title: Reference counted strings
* @Short_description: Strings with reference counted memory management
*
* Reference counted strings are normal C strings that have been augmented
* with a reference counter to manage their resources. You allocate a new
* reference counted string and acquire and release references as needed,
* instead of copying the string among callers; when the last reference on
* the string is released, the resources allocated for it are freed.
*
* Since: 2.58
*/
#include "config.h"
#include "grefstring.h"
#include "ghash.h"
#include "gmessages.h"
#include "grcbox.h"
#include "gthread.h"
#include <string.h>
G_LOCK_DEFINE_STATIC (interned_ref_strings);
static GHashTable *interned_ref_strings;
/**
* g_ref_string_new:
* @str: (not nullable): a NUL-terminated string
*
* Creates a new reference counted string and copies the contents of @str
* into it.
*
* Returns: (not nullable): the newly created reference counted string
*
* Since: 2.58
*/
char *
g_ref_string_new (const char *str)
{
char *res;
gsize len;
g_return_val_if_fail (str != NULL && *str != '\0', NULL);
len = strlen (str);
res = (char *) g_arc_box_dup (sizeof (char) * len + 1, str);
res[len] = '\0';
return res;
}
/**
* g_ref_string_new_intern:
* @str: (not nullable): a NUL-terminated string
*
* Creates a new reference counted string and copies the content of @str
* into it.
*
* If you call this function multiple times with the same @str, or with
* the same contents of @str, it will return a new reference, instead of
* creating a new string.
*
* Returns: (not nullable): the newly created reference counted string, or
* a new reference to an existing string
*
* Since: 2.58
*/
char *
g_ref_string_new_intern (const char *str)
{
gsize len;
char *res;
g_return_val_if_fail (str != NULL && *str != '\0', NULL);
G_LOCK (interned_ref_strings);
if (G_UNLIKELY (interned_ref_strings == NULL))
interned_ref_strings = g_hash_table_new_full (g_str_hash, g_str_equal,
(GDestroyNotify) g_arc_box_release,
NULL);
res = g_hash_table_lookup (interned_ref_strings, str);
if (res != NULL)
{
G_UNLOCK (interned_ref_strings);
return g_arc_box_acquire (res);
}
len = strlen (str);
res = (char *) g_arc_box_dup (sizeof (char) * len + 1, str);
res[len] = '\0';
g_hash_table_add (interned_ref_strings, g_arc_box_acquire (res));
G_UNLOCK (interned_ref_strings);
return res;
}
/**
* g_ref_string_acquire:
* @str: a reference counted string
*
* Acquires a reference on a string.
*
* Returns: the given string, with its reference count increased
*
* Since: 2.58
*/
char *
g_ref_string_acquire (char *str)
{
g_return_val_if_fail (str != NULL && *str != '\0', NULL);
return g_arc_box_acquire (str);
}
static void
remove_if_interned (gpointer data)
{
char *str = data;
G_LOCK (interned_ref_strings);
if (G_LIKELY (interned_ref_strings != NULL))
{
if (g_hash_table_contains (interned_ref_strings, str))
g_hash_table_remove (interned_ref_strings, str);
if (g_hash_table_size (interned_ref_strings) == 0)
g_clear_pointer (&interned_ref_strings, g_hash_table_destroy);
}
G_UNLOCK (interned_ref_strings);
}
/**
* g_ref_string_release:
* @str: a reference counted string
*
* Releases a reference on a string; if it was the last reference, the
* resources allocated by the string are freed as well.
*
* Since: 2.58
*/
void
g_ref_string_release (char *str)
{
g_return_if_fail (str != NULL && *str != '\0');
g_arc_box_release_full (str, remove_if_interned);
}

38
glib/grefstring.h Normal file
View File

@ -0,0 +1,38 @@
/* grefstring.h: Reference counted strings
*
* Copyright 2018 Emmanuele Bassi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "gmem.h"
#include "gmacros.h"
G_BEGIN_DECLS
GLIB_AVAILABLE_IN_2_58
char * g_ref_string_new (const char *str);
GLIB_AVAILABLE_IN_2_58
char * g_ref_string_new_intern (const char *str);
GLIB_AVAILABLE_IN_2_58
char * g_ref_string_acquire (char *str);
GLIB_AVAILABLE_IN_2_58
void g_ref_string_release (char *str);
typedef char GRefString;
G_END_DECLS

View File

@ -78,6 +78,7 @@ glib_sub_headers = files(
'grand.h',
'grcbox.h',
'grefcount.h',
'grefstring.h',
'gregex.h',
'gscanner.h',
'gsequence.h',
@ -164,6 +165,7 @@ glib_sources = files(
'grand.c',
'grcbox.c',
'grefcount.c',
'grefstring.c',
'gregex.c',
'gscanner.c',
'gsequence.c',

View File

@ -91,6 +91,7 @@ test_programs = \
rand \
rcbox \
rec-mutex \
refstring \
regex \
rwlock \
scannerapi \

View File

@ -422,6 +422,13 @@ test_strv (void)
g_assert (val != NULL);
}
static void
test_refstring (void)
{
g_autoptr(GRefString) str = g_ref_string_new ("hello, world");
g_assert_nonnull (str);
}
static void
mark_freed (gpointer ptr)
{
@ -539,6 +546,7 @@ main (int argc, gchar *argv[])
g_test_add_func ("/autoptr/g_variant_dict", test_g_variant_dict);
g_test_add_func ("/autoptr/g_variant_type", test_g_variant_type);
g_test_add_func ("/autoptr/strv", test_strv);
g_test_add_func ("/autoptr/refstring", test_refstring);
g_test_add_func ("/autoptr/autolist", test_autolist);
g_test_add_func ("/autoptr/autoslist", test_autoslist);

View File

@ -50,6 +50,7 @@ glib_tests = [
'rec-mutex',
'refcount',
'refcount-macro',
'refstring',
'regex',
'rwlock',
'scannerapi',

70
glib/tests/refstring.c Normal file
View File

@ -0,0 +1,70 @@
/* refstring.c: Reference counted strings
*
* Copyright 2018 Emmanuele Bassi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include <glib.h>
#include <string.h>
/* test_refstring_base: Test the base API of GRefString */
static void
test_refstring_base (void)
{
char *s = g_ref_string_new ("hello, world");
g_test_message ("s = '%s' (%p)", s, s);
g_assert_cmpint (strcmp (s, "hello, world"), ==, 0);
g_assert_cmpint (strlen (s), ==, strlen ("hello, world"));
g_ref_string_release (s);
}
/* test_refstring_intern: Test the interning API of GRefString */
static void
test_refstring_intern (void)
{
char *s = g_ref_string_new_intern ("hello, world");
char *p;
g_test_message ("s = '%s' (%p)", s, s);
g_assert_cmpstr (s, ==, "hello, world");
p = g_ref_string_new_intern ("hello, world");
g_test_message ("p = s = '%s' (%p)", p, p);
g_assert_true (s == p);
g_ref_string_release (p);
p = g_ref_string_new_intern ("goodbye, world");
g_test_message ("p = '%s' (%p)", p, p);
g_assert_false (s == p);
g_ref_string_release (p);
g_ref_string_release (s);
}
int
main (int argc,
char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/refstring/base", test_refstring_base);
g_test_add_func ("/refstring/intern", test_refstring_intern);
return g_test_run ();
}