mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-09-27 17:52:58 +02:00
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:
@@ -122,6 +122,7 @@
|
|||||||
<xi:include href="xml/refcount.xml"/>
|
<xi:include href="xml/refcount.xml"/>
|
||||||
<xi:include href="xml/rcbox.xml"/>
|
<xi:include href="xml/rcbox.xml"/>
|
||||||
<xi:include href="xml/arcbox.xml"/>
|
<xi:include href="xml/arcbox.xml"/>
|
||||||
|
<xi:include href="xml/refstring.xml"/>
|
||||||
</chapter>
|
</chapter>
|
||||||
|
|
||||||
<chapter id="deprecated">
|
<chapter id="deprecated">
|
||||||
|
@@ -3490,3 +3490,11 @@ g_arc_box_acquire
|
|||||||
g_arc_box_release
|
g_arc_box_release
|
||||||
g_arc_box_release_full
|
g_arc_box_release_full
|
||||||
</SECTION>
|
</SECTION>
|
||||||
|
|
||||||
|
<SECTION>
|
||||||
|
<FILE>refstring</FILE>
|
||||||
|
g_ref_string_new
|
||||||
|
g_ref_string_new_intern
|
||||||
|
g_ref_string_acquire
|
||||||
|
g_ref_string_release
|
||||||
|
</SECTION>
|
||||||
|
@@ -153,6 +153,7 @@ libglib_2_0_la_SOURCES = \
|
|||||||
grcbox.c \
|
grcbox.c \
|
||||||
grcboxprivate.h \
|
grcboxprivate.h \
|
||||||
grefcount.c \
|
grefcount.c \
|
||||||
|
grefstring.c \
|
||||||
gregex.c \
|
gregex.c \
|
||||||
gscanner.c \
|
gscanner.c \
|
||||||
gscripttable.h \
|
gscripttable.h \
|
||||||
@@ -291,6 +292,7 @@ glibsubinclude_HEADERS = \
|
|||||||
grand.h \
|
grand.h \
|
||||||
grcbox.h \
|
grcbox.h \
|
||||||
grefcount.h \
|
grefcount.h \
|
||||||
|
grefstring.h \
|
||||||
gregex.h \
|
gregex.h \
|
||||||
gscanner.h \
|
gscanner.h \
|
||||||
gsequence.h \
|
gsequence.h \
|
||||||
|
@@ -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_AUTO_CLEANUP_CLEAR_FUNC(GVariantDict, g_variant_dict_clear)
|
||||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantType, g_variant_type_free)
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantType, g_variant_type_free)
|
||||||
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(GStrv, g_strfreev, NULL)
|
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(GStrv, g_strfreev, NULL)
|
||||||
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GRefString, g_ref_string_release)
|
||||||
|
@@ -71,6 +71,7 @@
|
|||||||
#include <glib/grand.h>
|
#include <glib/grand.h>
|
||||||
#include <glib/grcbox.h>
|
#include <glib/grcbox.h>
|
||||||
#include <glib/grefcount.h>
|
#include <glib/grefcount.h>
|
||||||
|
#include <glib/grefstring.h>
|
||||||
#include <glib/gregex.h>
|
#include <glib/gregex.h>
|
||||||
#include <glib/gscanner.h>
|
#include <glib/gscanner.h>
|
||||||
#include <glib/gsequence.h>
|
#include <glib/gsequence.h>
|
||||||
|
175
glib/grefstring.c
Normal file
175
glib/grefstring.c
Normal 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
38
glib/grefstring.h
Normal 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
|
@@ -78,6 +78,7 @@ glib_sub_headers = files(
|
|||||||
'grand.h',
|
'grand.h',
|
||||||
'grcbox.h',
|
'grcbox.h',
|
||||||
'grefcount.h',
|
'grefcount.h',
|
||||||
|
'grefstring.h',
|
||||||
'gregex.h',
|
'gregex.h',
|
||||||
'gscanner.h',
|
'gscanner.h',
|
||||||
'gsequence.h',
|
'gsequence.h',
|
||||||
@@ -164,6 +165,7 @@ glib_sources = files(
|
|||||||
'grand.c',
|
'grand.c',
|
||||||
'grcbox.c',
|
'grcbox.c',
|
||||||
'grefcount.c',
|
'grefcount.c',
|
||||||
|
'grefstring.c',
|
||||||
'gregex.c',
|
'gregex.c',
|
||||||
'gscanner.c',
|
'gscanner.c',
|
||||||
'gsequence.c',
|
'gsequence.c',
|
||||||
|
@@ -91,6 +91,7 @@ test_programs = \
|
|||||||
rand \
|
rand \
|
||||||
rcbox \
|
rcbox \
|
||||||
rec-mutex \
|
rec-mutex \
|
||||||
|
refstring \
|
||||||
regex \
|
regex \
|
||||||
rwlock \
|
rwlock \
|
||||||
scannerapi \
|
scannerapi \
|
||||||
|
@@ -422,6 +422,13 @@ test_strv (void)
|
|||||||
g_assert (val != NULL);
|
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
|
static void
|
||||||
mark_freed (gpointer ptr)
|
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_dict", test_g_variant_dict);
|
||||||
g_test_add_func ("/autoptr/g_variant_type", test_g_variant_type);
|
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/strv", test_strv);
|
||||||
|
g_test_add_func ("/autoptr/refstring", test_refstring);
|
||||||
g_test_add_func ("/autoptr/autolist", test_autolist);
|
g_test_add_func ("/autoptr/autolist", test_autolist);
|
||||||
g_test_add_func ("/autoptr/autoslist", test_autoslist);
|
g_test_add_func ("/autoptr/autoslist", test_autoslist);
|
||||||
|
|
||||||
|
@@ -50,6 +50,7 @@ glib_tests = [
|
|||||||
'rec-mutex',
|
'rec-mutex',
|
||||||
'refcount',
|
'refcount',
|
||||||
'refcount-macro',
|
'refcount-macro',
|
||||||
|
'refstring',
|
||||||
'regex',
|
'regex',
|
||||||
'rwlock',
|
'rwlock',
|
||||||
'scannerapi',
|
'scannerapi',
|
||||||
|
70
glib/tests/refstring.c
Normal file
70
glib/tests/refstring.c
Normal 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 ();
|
||||||
|
}
|
||||||
|
|
Reference in New Issue
Block a user