Commit 20338e7d authored by Morten Welinder's avatar Morten Welinder

GOString: move to goffice.

parent 12bd9a0b
......@@ -29,7 +29,6 @@
#include <sheet-object-graph.h>
#include <workbook-view.h>
#include <go-string.h>
#include <goffice/goffice.h>
#include <gsf/gsf-utils.h>
......
......@@ -66,7 +66,6 @@
#include <gnm-so-polygon.h>
#include <sheet-object-graph.h>
#include <sheet-object-image.h>
#include <go-string.h>
#include <goffice/goffice.h>
#include <gsf/gsf-input.h>
......
......@@ -64,7 +64,6 @@
#include <expr-name.h>
#include <mathfunc.h>
#include <go-string.h>
#include <go-data-slicer.h>
#include <gsf/gsf-utils.h>
#include <gsf/gsf-output.h>
......
......@@ -25,7 +25,6 @@
#include <parse-util.h>
#include <sheet.h>
#include <workbook.h>
#include <go-string.h>
#include <goffice/goffice.h>
#include <gsf/gsf-utils.h>
......
......@@ -30,7 +30,6 @@
#include <expr-impl.h>
#include <expr-name.h>
#include <parse-util.h>
#include <go-string.h>
#include <goffice/goffice.h>
#include <gsf/gsf-utils.h>
......
......@@ -34,7 +34,6 @@
#include <gnm-data-cache-source.h>
#include <gnm-sheet-slicer.h>
#include <go-string.h>
#include <go-data-cache.h>
#include <go-data-cache-field.h>
#include <go-data-slicer-field.h>
......
......@@ -26,7 +26,6 @@
#include "gnm-datetime.h"
#include "workbook.h"
#include "go-val.h"
#include "go-string.h"
#include "go-data-cache.h"
#include "go-data-cache-field.h"
......
......@@ -51,7 +51,6 @@
#include "go-val.h"
#include <go-string.h>
#include <goffice/goffice.h>
#include <gsf/gsf-output.h>
......
......@@ -34,7 +34,6 @@
#include <rangefunc.h>
#include <gnm-i18n.h>
#include <go-string.h>
#include <goffice/goffice.h>
#include <gnm-plugin.h>
......
......@@ -48,7 +48,6 @@
#include <gutils.h>
#include <xml-io.h>
#include <go-string.h>
#include <goffice/goffice.h>
#include <gsf/gsf-libxml.h>
......
......@@ -63,7 +63,6 @@
#include <gutils.h>
#include <xml-io.h>
#include <go-string.h>
#include <gsf/gsf-libxml.h>
#include <gsf/gsf-output.h>
......
......@@ -22,7 +22,6 @@
#include "gnm-py-interpreter.h"
#include "py-gnumeric.h"
#include <go-string.h>
#include <goffice/goffice.h>
#include <glib/gi18n-lib.h>
......
......@@ -32,7 +32,6 @@
#include "gutils.h"
#include "parse-util.h"
#include <go-string.h>
#include <goffice/goffice.h>
#include <gsf/gsf-output.h>
......
......@@ -58,8 +58,6 @@ libspreadsheet_la_LIBADD = \
libspreadsheet_la_SOURCES = \
go-val.h \
go-val.c \
go-string.h \
go-string.c \
\
goffice-data.h \
go-data-cache-source.c \
......
......@@ -26,7 +26,6 @@
#include "sheet-style.h"
#include "parse-util.h"
#include <go-string.h>
#include <goffice/goffice.h>
/**
......
......@@ -72,7 +72,6 @@
#include "data-shuffling.h"
#include "tools/tabulate.h"
#include <go-string.h>
#include <goffice/goffice.h>
#include <gsf/gsf-doc-meta-data.h>
#include <string.h>
......
......@@ -19,7 +19,6 @@
#include "value.h"
#include "parse-util.h"
#include <go-string.h>
#include <gsf/gsf-impl-utils.h>
#include <string.h>
......
......@@ -36,7 +36,6 @@
#include "workbook.h"
#include "workbook-control.h"
#include <go-string.h>
/**********************************************************************************
* UTILITY ROUTINES
......
......@@ -37,7 +37,6 @@
#include "gutils.h"
#include "sheet-view.h"
#include <go-string.h>
#include <goffice/goffice.h>
#include <string.h>
......
......@@ -53,7 +53,6 @@
#include <widgets/gnumeric-dashed-canvas-line.h>
#include <widgets/gnm-format-sel.h>
#include <go-string.h>
#include <goffice/goffice.h>
#include <goffice/cut-n-paste/foocanvas/foo-canvas-util.h>
#include <goffice/cut-n-paste/foocanvas/foo-canvas-rect-ellipse.h>
......
......@@ -42,7 +42,6 @@
#include <commands.h>
#include <widgets/gnumeric-expr-entry.h>
#include <go-string.h>
#include <glade/glade.h>
#include <gtk/gtk.h>
#include <string.h>
......
......@@ -36,7 +36,6 @@
#include <application.h>
#include <gnumeric-gconf.h>
#include <go-string.h>
#include <gsf/gsf-impl-utils.h>
#include <glade/glade.h>
#include <gtk/gtk.h>
......
......@@ -39,7 +39,6 @@
#include <wbc-gtk.h>
#include <go-string.h>
#include <gtk/gtk.h>
#define GOTO_KEY "goto-dialog"
......
......@@ -25,7 +25,6 @@
#include "gutils.h"
#include "sheet-style.h"
#include <go-string.h>
#include <goffice/goffice.h>
/**
......
......@@ -41,7 +41,6 @@
#include "parse-util.h"
#include "mathfunc.h"
#include <go-string.h>
#include <goffice/goffice.h>
#include <math.h>
#include <string.h>
......
......@@ -28,7 +28,6 @@
#include "number-match.h"
#include "func-builtin.h"
#include <go-string.h>
#include <goffice/goffice.h>
#include <glib.h>
#include <string.h>
......
......@@ -24,7 +24,6 @@
#include "go-data-cache-source.h"
#include "go-data-cache.h"
#include "go-string.h"
#include <gnumeric.h>
#include <ranges.h>
......
......@@ -26,7 +26,6 @@
#include <gnumeric.h>
#include "goffice-data.h"
#include "goffice-utils.h"
G_BEGIN_DECLS
......
......@@ -4,7 +4,6 @@
#include <glib.h>
#include <goffice/goffice.h>
#include "goffice-utils.h" /* merge into real file when go-string moves */
G_BEGIN_DECLS
......
......@@ -25,7 +25,6 @@
#include "go-data-cache-field-impl.h"
#include "go-data-cache.h"
#include <go-string.h>
#include <go-val.h>
#include <gsf/gsf-impl-utils.h>
......
......@@ -23,7 +23,6 @@
#include <go-data-cache.h>
#include <glib-object.h>
#include <go-string.h>
G_BEGIN_DECLS
......
......@@ -23,7 +23,6 @@
#include "goffice-data.h" /* remove after move to goffice */
#include <goffice/goffice.h>
#include <go-string.h>
#include <glib-object.h>
G_BEGIN_DECLS
......
......@@ -21,9 +21,9 @@
#ifndef GO_DATA_SLICER_IMPL_H
#define GO_DATA_SLICER_IMPL_H
#include <goffice/goffice.h>
#include <go-data-slicer.h>
#include <glib-object.h>
#include <goffice-utils.h>
G_BEGIN_DECLS
......
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* go-string.c : ref counted shared strings with richtext and phonetic support
*
* Copyright (C) 2008 Jody Goldberg (jody@gnome.org)
* Copyright (C) 2007-2008 Morten Welinder (terra@gnome.org)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include <gnumeric-config.h>
#include "gnumeric.h"
#include "go-string.h"
#include <goffice/goffice.h>
#include <gsf/gsf-utils.h>
#include <string.h>
typedef struct {
GOString base;
guint32 hash;
guint32 flags; /* len in bytes (not characters) */
guint32 ref_count;
} GOStringImpl;
typedef struct _GOStringRichImpl {
GOStringImpl base;
PangoAttrList *markup;
GOStringPhonetic *phonetic;
} GOStringRichImpl;
#define GO_STRING_HAS_CASEFOLD (1u << 31)
#define GO_STRING_HAS_COLLATE (1u << 30)
#define GO_STRING_IS_RICH (1u << 29)
#define GO_STRING_IS_SHARED (1u << 28) /* rich strings share this base */
#define GO_STRING_IS_DEPENDENT (1u << 27) /* a rich string that shares an underlying */
/* mask off just the len */
#define GO_STRING_LEN(s) (((GOStringImpl const *)(s))->flags & ((1u << 27) - 1u))
/* Collection of unique strings
* : GOStringImpl.base.hash -> GOStringImpl * */
static GHashTable *go_strings_base;
/* Collection of gslist keyed the basic string
* : str (directly on the pointer) -> GSList of GOStringRichImpl */
static GHashTable *go_strings_shared;
static inline GOStringImpl *
go_string_impl_new (char const *str, guint32 hash, guint32 flags, guint32 ref_count)
{
GOStringImpl *res = g_slice_new (GOStringImpl);
res->base.str = str;
res->hash = hash;
res->flags = flags;
res->ref_count = ref_count;
g_hash_table_replace (go_strings_base, res, res);
return res;
}
static void
go_string_phonetic_unref (GOStringPhonetic *phonetic)
{
/* TODO */
}
static GOString *
replace_rich_base_with_plain (GOStringRichImpl *rich)
{
GOStringImpl *res = go_string_impl_new (rich->base.base.str, rich->base.hash,
(rich->base.flags & (~GO_STRING_IS_RICH)) | GO_STRING_IS_SHARED, 2);
rich->base.flags |= GO_STRING_IS_DEPENDENT;
if ((rich->base.flags & GO_STRING_IS_SHARED)) {
GSList *shares = g_hash_table_lookup (go_strings_shared, res->base.str);
unsigned n = g_slist_length (shares);
g_assert (rich->base.ref_count > n);
rich->base.flags &= ~GO_STRING_IS_SHARED;
rich->base.ref_count -= n;
res->ref_count += n;
/* ignore result, assignment is just to make the compiler shutup */
shares = g_slist_insert (shares, res, 1);
} else
g_hash_table_insert (go_strings_shared, (gpointer) res->base.str,
g_slist_prepend (NULL, rich));
return &res->base;
}
/**
* go_string_new_len :
* @str : string (optionally %NULL)
* @len : guint32
*
* GOString duplicates @str if no string already exists.
*
* Returns: a reference to a #GOString containing @str, or %NULL if @str is NULL
**/
GOString *
go_string_new_len (char const *str, guint32 len)
{
GOStringImpl key, *res;
if (NULL == str)
return NULL;
key.base.str = str;
key.flags = len;
key.hash = g_str_hash (str);
res = g_hash_table_lookup (go_strings_base, &key);
if (NULL == res) {
/* Copy str */
res = go_string_impl_new (g_strndup (str, len),
key.hash, key.flags, 1);
return &res->base;
} else if (G_UNLIKELY (res->flags & GO_STRING_IS_RICH))
/* if rich was there first move it to the shared */
return replace_rich_base_with_plain ((GOStringRichImpl *)res);
else
return go_string_ref (&res->base);
}
/**
* go_string_new_nocopy :
* @str : string (optionally %NULL)
* @len : guint32
*
* GOString takes ownership of @str
*
* Returns: a reference to a #GOString containing @str
**/
GOString *
go_string_new_nocopy_len (char *str, guint32 len)
{
GOStringImpl key, *res;
if (NULL == str)
return NULL;
key.base.str = str;
key.flags = len;
key.hash = g_str_hash (str);
res = g_hash_table_lookup (go_strings_base, &key);
if (NULL == res) {
/* NO copy str */
res = go_string_impl_new (str, key.hash, key.flags, 1);
return &res->base;
}
if (str != res->base.str) g_free (str); /* Be extra careful */
/* if rich was there first move it to the shared */
if (G_UNLIKELY (res->flags & GO_STRING_IS_RICH))
return replace_rich_base_with_plain ((GOStringRichImpl *)res);
return go_string_ref (&res->base);
}
/**
* go_string_new :
* @str : string (optionally %NULL)
*
* GOString duplicates @str if no string already exists.
*
* Returns: a reference to a #GOString containing @str, or %NULL if @str is NULL
**/
GOString *
go_string_new (char const *str)
{
return str ? go_string_new_len (str, strlen (str)) : NULL;
}
/**
* go_string_new_nocopy :
* @str : string
*
* GOString takes ownership of @str
*
* Returns: a reference to a #GOString containing @str
**/
GOString *
go_string_new_nocopy (char *str)
{
return str ? go_string_new_nocopy_len (str, strlen (str)) : NULL;
}
/**
* go_string_new_rich :
* @str :
* @len : < 0 will call strlen
* @copy : %TRUE @str should be copied when adding to the string table.
* @markup : optionally %NULL list, GOString steals the ref
* @phonetic : optionally %NULL list of phonetic extensions, GOString steals the ref.
*
* Returns : a string.
**/
GOString *
go_string_new_rich (char const *str,
int len,
gboolean copy,
PangoAttrList *markup,
GOStringPhonetic *phonetic)
{
GOStringImpl *base;
GOStringRichImpl *rich;
if (NULL == str) {
if (NULL != markup) pango_attr_list_unref (markup);
if (NULL != phonetic) go_string_phonetic_unref (phonetic);
return NULL;
}
/* TODO : when we use a better representation for attributes (eg array
* of GOFont indicies) look into sharing rich strings */
rich = g_slice_new (GOStringRichImpl);
rich->base.base.str = str;
rich->base.hash = g_str_hash (str);
rich->base.flags = ((len > 0) ? (guint32) len : strlen (str)) | GO_STRING_IS_RICH;
rich->base.ref_count = 1;
rich->markup = markup;
rich->phonetic = phonetic;
base = g_hash_table_lookup (go_strings_base, rich);
if (NULL == base) {
if (copy) rich->base.base.str = g_strndup (str, len);
g_hash_table_insert (go_strings_base, rich, rich);
} else {
go_string_ref (&base->base);
if (str != rich->base.base.str) { /* watch for people doing something stupid */
if (!copy) g_free ((char *)str);
rich->base.base.str = base->base.str;
}
rich->base.flags |= GO_STRING_IS_DEPENDENT;
if ((base->flags & GO_STRING_IS_SHARED)) {
GSList *shares = g_hash_table_lookup (go_strings_shared, rich->base.base.str);
/* ignore result, assignment is just to make the compiler shutup */
shares = g_slist_insert (shares, rich, 1);
} else {
base->flags |= GO_STRING_IS_SHARED;
g_hash_table_insert (go_strings_shared, (gpointer) rich->base.base.str,
g_slist_prepend (NULL, rich));
}
}
return &rich->base.base;
}
GOString *
go_string_ref (GOString *gstr)
{
if (NULL != gstr)
((GOStringImpl *)gstr)->ref_count++;
return gstr;
}
void
go_string_unref (GOString *gstr)
{
GOStringImpl *impl = (GOStringImpl *)gstr;
if (NULL == gstr)
return;
g_return_if_fail (impl->ref_count > 0);
if ((--(impl->ref_count)) == 0) {
/* polite assertion failure */
g_return_if_fail (!(impl->flags & GO_STRING_IS_SHARED));
if ((impl->flags & GO_STRING_IS_RICH)) {
GOStringRichImpl *rich = (GOStringRichImpl *)gstr;
if (NULL != rich->markup) pango_attr_list_unref (rich->markup);
if (NULL != rich->phonetic) go_string_phonetic_unref (rich->phonetic);
}
if (G_UNLIKELY (impl->flags & GO_STRING_IS_DEPENDENT)) {
GOStringImpl *base = g_hash_table_lookup (go_strings_base, gstr);
GSList *shares = g_hash_table_lookup (go_strings_shared, gstr->str);
GSList *new_shares = g_slist_remove (shares, gstr);
if (new_shares != shares) {
if (new_shares == NULL) {
base->flags &= ~GO_STRING_IS_SHARED;
g_hash_table_remove (go_strings_shared, gstr->str);
} else
g_hash_table_replace (go_strings_shared, (gpointer)gstr->str, new_shares);
}
go_string_unref (&base->base);
} else {
g_hash_table_remove (go_strings_base, gstr);
g_free ((gpointer)gstr->str);
}
g_slice_free1 (sizeof (GOStringImpl), gstr);
}
}
unsigned int
go_string_get_ref_count (GOString const *gstr)
{
return gstr ? ((GOStringImpl const *)gstr)->ref_count : 0;
}
guint32
go_string_hash (gconstpointer gstr)
{
return gstr ? ((GOStringImpl const *)gstr)->hash : 0;
}
int
go_string_cmp (gconstpointer gstr_a, gconstpointer gstr_b)
{
return (gstr_a == gstr_b)
? 0
: strcmp (go_string_get_collation (gstr_a),
go_string_get_collation (gstr_b));
}
int
go_string_cmp_ignorecase (gconstpointer gstr_a, gconstpointer gstr_b)
{
return (gstr_a == gstr_b)
? 0
: strcmp (go_string_get_casefold (gstr_a),
go_string_get_casefold (gstr_b));
}
gboolean
go_string_equal (gconstpointer gstr_a, gconstpointer gstr_b)
{
GOString const *a = gstr_a;
GOString const *b = gstr_b;
return a == b || (NULL != a && NULL != b && a->str == b->str);
}
static void
go_string_impl_append_extra (GOStringImpl *gstri, char *extra, unsigned int offset)
{
guint32 len = strlen (extra);
gchar *res = g_realloc ((gpointer)gstri->base.str, offset + 4 + len + 1);
GSF_LE_SET_GUINT32(res + offset, len);
memcpy ((gpointer)(res + offset + 4), extra, len + 1);
g_free (extra);
if (res != gstri->base.str) {
/* update any shared strings */
if ((gstri->flags & GO_STRING_IS_SHARED)) {
GSList *shares = g_hash_table_lookup (go_strings_shared, gstri->base.str);
g_hash_table_remove (go_strings_shared, gstri->base.str);
g_hash_table_insert (go_strings_shared, res, shares);
for (; shares != NULL ; shares = shares->next)
((GOStringImpl *)(shares->data))->base.str = res;
}
((GOStringImpl *)gstri)->base.str = res;
}
}
char const *
go_string_get_collation (GOString const *gstr)
{
GOStringImpl *gstri = (GOStringImpl *)gstr;
unsigned int len;
if (NULL == gstr)
return "";
len = GO_STRING_LEN (gstri);
if (0 == (gstri->flags & GO_STRING_HAS_COLLATE)) {
gchar *collate = g_utf8_collate_key (gstri->base.str, len);
/* Keep it simple, drop the casefold to avoid issues with overlapping */
gstri->flags &= ~GO_STRING_HAS_CASEFOLD;
gstri->flags |= GO_STRING_HAS_COLLATE;
go_string_impl_append_extra (gstri, collate, len + 1);
}
return gstri->base.str + len + 1 + 4;
}
char const *
go_string_get_casefold (GOString const *gstr)
{
GOStringImpl *gstri = (GOStringImpl *)gstr;
unsigned int offset;
if (NULL == gstr)
return "";
offset = GO_STRING_LEN (gstri) + 1;
if (0 != (gstri->flags & GO_STRING_HAS_COLLATE))
offset += GSF_LE_GET_GUINT32(gstri->base.str + offset) + 4 + 1;
if (0 == (gstri->flags & GO_STRING_HAS_CASEFOLD)) {
gchar *casefold = g_utf8_casefold (gstri->base.str, GO_STRING_LEN (gstri));
gstri->flags |= GO_STRING_HAS_CASEFOLD;
go_string_impl_append_extra (gstri, casefold, offset);
}
return gstri->base.str + offset + 4;
}
static GOString *go_string_ERROR_val = NULL;
/**
* go_string_ERROR :
*
* A convenience for g_return_val to share one error string without adding a
* reference to functions that do not add references to the result
*
* Returns : A string saying 'ERROR' but does not add a ref to it.
**/
GOString *
go_string_ERROR (void)
{
if (NULL == go_string_ERROR_val)
go_string_ERROR_val = go_string_new ("<ERROR>");
return go_string_ERROR_val;
}
/*******************************************************************************/
/* Internal comparison routine that actually does a strcmp, rather than
* assuming that only str == str are equal */
static gboolean
go_string_equal_internal (gconstpointer gstr_a, gconstpointer gstr_b)
{
GOStringImpl const *a = gstr_a;
GOStringImpl const *b = gstr_b;
return a == b ||
((a->hash == b->hash) &&
(GO_STRING_LEN (a) == GO_STRING_LEN (b)) &&
0 == strcmp (a->base.str, b->base.str));
}
void
go_string_init (void)
{
go_strings_base = g_hash_table_new (go_string_hash, go_string_equal_internal);
go_strings_shared = g_hash_table_new (g_direct_hash, g_direct_equal);
}
static gboolean
cb_string_pool_leak (G_GNUC_UNUSED gpointer key,
gpointer value,
G_GNUC_UNUSED gpointer user)
{