mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-10 11:26:16 +01: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:
parent
4b33b03dd3
commit
00a723f597
@ -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">
|
||||
|
@ -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>
|
||||
|
@ -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 \
|
||||
|
@ -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)
|
||||
|
@ -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
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',
|
||||
'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',
|
||||
|
@ -91,6 +91,7 @@ test_programs = \
|
||||
rand \
|
||||
rcbox \
|
||||
rec-mutex \
|
||||
refstring \
|
||||
regex \
|
||||
rwlock \
|
||||
scannerapi \
|
||||
|
@ -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);
|
||||
|
||||
|
@ -50,6 +50,7 @@ glib_tests = [
|
||||
'rec-mutex',
|
||||
'refcount',
|
||||
'refcount-macro',
|
||||
'refstring',
|
||||
'regex',
|
||||
'rwlock',
|
||||
'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 ();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user