diff --git a/docs/reference/glib/glib-docs.xml b/docs/reference/glib/glib-docs.xml
index 9d253ee5f..e0e8ba233 100644
--- a/docs/reference/glib/glib-docs.xml
+++ b/docs/reference/glib/glib-docs.xml
@@ -122,6 +122,7 @@
+
diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt
index 2a4d1f281..dd851bc9e 100644
--- a/docs/reference/glib/glib-sections.txt
+++ b/docs/reference/glib/glib-sections.txt
@@ -3490,3 +3490,11 @@ g_arc_box_acquire
g_arc_box_release
g_arc_box_release_full
+
+
+refstring
+g_ref_string_new
+g_ref_string_new_intern
+g_ref_string_acquire
+g_ref_string_release
+
diff --git a/glib/Makefile.am b/glib/Makefile.am
index 352ecb0ec..c7adc1f0e 100644
--- a/glib/Makefile.am
+++ b/glib/Makefile.am
@@ -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 \
diff --git a/glib/glib-autocleanups.h b/glib/glib-autocleanups.h
index 9f86bd99c..ce1690b78 100644
--- a/glib/glib-autocleanups.h
+++ b/glib/glib-autocleanups.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)
diff --git a/glib/glib.h b/glib/glib.h
index 309366281..94a11fb62 100644
--- a/glib/glib.h
+++ b/glib/glib.h
@@ -71,6 +71,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/glib/grefstring.c b/glib/grefstring.c
new file mode 100644
index 000000000..cc62c8011
--- /dev/null
+++ b/glib/grefstring.c
@@ -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 .
+ */
+
+/**
+ * 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
+
+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);
+}
diff --git a/glib/grefstring.h b/glib/grefstring.h
new file mode 100644
index 000000000..f2156fef4
--- /dev/null
+++ b/glib/grefstring.h
@@ -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 .
+ */
+
+#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
diff --git a/glib/meson.build b/glib/meson.build
index 1a3f44797..c05c69406 100644
--- a/glib/meson.build
+++ b/glib/meson.build
@@ -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',
diff --git a/glib/tests/Makefile.am b/glib/tests/Makefile.am
index 0d524d57f..71ac15517 100644
--- a/glib/tests/Makefile.am
+++ b/glib/tests/Makefile.am
@@ -91,6 +91,7 @@ test_programs = \
rand \
rcbox \
rec-mutex \
+ refstring \
regex \
rwlock \
scannerapi \
diff --git a/glib/tests/autoptr.c b/glib/tests/autoptr.c
index c8c130d5c..5b3bce71c 100644
--- a/glib/tests/autoptr.c
+++ b/glib/tests/autoptr.c
@@ -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);
diff --git a/glib/tests/meson.build b/glib/tests/meson.build
index 3c20bad39..7c49d579c 100644
--- a/glib/tests/meson.build
+++ b/glib/tests/meson.build
@@ -50,6 +50,7 @@ glib_tests = [
'rec-mutex',
'refcount',
'refcount-macro',
+ 'refstring',
'regex',
'rwlock',
'scannerapi',
diff --git a/glib/tests/refstring.c b/glib/tests/refstring.c
new file mode 100644
index 000000000..0181be945
--- /dev/null
+++ b/glib/tests/refstring.c
@@ -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 .
+ */
+
+#include
+#include
+
+/* 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 ();
+}
+