gtk2/gtk/gtkistringprivate.h
Christian Hergert 5e341210a1 texthistory: add GtkTextHistory helper
The GtkTextHistory helper provides the fundamental undo/redo stack that
can be integrated with other text widgets. It allows coalescing related
actions to reduce both the number of undo actions to the user as well as
the memory overhead.

A new istring helper is used by GtkTextHistory to allow for "inline
strings" that gracefully grow to using allocations with g_realloc(). This
ensure that most undo operations require no additional allocations other
than the struct for the action itself.

A queue of undoable and redoable actions are maintained and the link for
the queue is embedded in the undo action union. This allows again, for
reducing the number of allocations involved for undo operations.
2019-11-05 09:34:29 -08:00

172 lines
3.5 KiB
C

#ifndef __GTK_ISTRING_PRIVATE_H__
#define __GTK_ISTRING_PRIVATE_H__
#include <glib.h>
#include <string.h>
typedef struct
{
guint n_bytes;
guint n_chars;
union {
char buf[24];
char *str;
} u;
} IString;
static inline gboolean
istring_is_inline (const IString *str)
{
return str->n_bytes <= (sizeof str->u.buf - 1);
}
static inline char *
istring_str (IString *str)
{
if (istring_is_inline (str))
return str->u.buf;
else
return str->u.str;
}
static inline void
istring_clear (IString *str)
{
if (istring_is_inline (str))
str->u.buf[0] = 0;
else
g_clear_pointer (&str->u.str, g_free);
str->n_bytes = 0;
str->n_chars = 0;
}
static inline void
istring_set (IString *str,
const char *text,
guint n_bytes,
guint n_chars)
{
if G_LIKELY (n_bytes <= (sizeof str->u.buf - 1))
{
memcpy (str->u.buf, text, n_bytes);
str->u.buf[n_bytes] = 0;
}
else
{
str->u.str = g_strndup (text, n_bytes);
}
str->n_bytes = n_bytes;
str->n_chars = n_chars;
}
static inline gboolean
istring_empty (IString *str)
{
return str->n_bytes == 0;
}
static inline gboolean
istring_ends_with_space (IString *str)
{
return g_ascii_isspace (istring_str (str)[str->n_bytes - 1]);
}
static inline gboolean
istring_starts_with_space (IString *str)
{
return g_unichar_isspace (g_utf8_get_char (istring_str (str)));
}
static inline gboolean
istring_contains_unichar (IString *str,
gunichar ch)
{
return g_utf8_strchr (istring_str (str), str->n_bytes, ch) != NULL;
}
static inline gboolean
istring_only_contains_space (IString *str)
{
const char *iter;
for (iter = istring_str (str); *iter; iter = g_utf8_next_char (iter))
{
if (!g_unichar_isspace (g_utf8_get_char (iter)))
return FALSE;
}
return TRUE;
}
static inline gboolean
istring_contains_space (IString *str)
{
const char *iter;
for (iter = istring_str (str); *iter; iter = g_utf8_next_char (iter))
{
if (g_unichar_isspace (g_utf8_get_char (iter)))
return TRUE;
}
return FALSE;
}
static inline void
istring_prepend (IString *str,
IString *other)
{
if G_LIKELY (str->n_bytes + other->n_bytes < sizeof str->u.buf - 1)
{
memmove (str->u.buf + other->n_bytes, str->u.buf, str->n_bytes);
memcpy (str->u.buf, other->u.buf, other->n_bytes);
str->n_bytes += other->n_bytes;
str->n_chars += other->n_chars;
str->u.buf[str->n_bytes] = 0;
}
else
{
gchar *old = NULL;
if (!istring_is_inline (str))
old = str->u.str;
str->u.str = g_strconcat (istring_str (str), istring_str (other), NULL);
str->n_bytes += other->n_bytes;
str->n_chars += other->n_chars;
g_free (old);
}
}
static inline void
istring_append (IString *str,
IString *other)
{
const gchar *text = istring_str (other);
guint n_bytes = other->n_bytes;
guint n_chars = other->n_chars;
if G_LIKELY (istring_is_inline (str))
{
if G_LIKELY (str->n_bytes + n_bytes <= (sizeof str->u.buf - 1))
memcpy (str->u.buf + str->n_bytes, text, n_bytes);
else
str->u.str = g_strconcat (str->u.buf, text, NULL);
}
else
{
str->u.str = g_realloc (str->u.str, str->n_bytes + n_bytes + 1);
memcpy (str->u.str + str->n_bytes, text, n_bytes);
}
str->n_bytes += n_bytes;
str->n_chars += n_chars;
istring_str (str)[str->n_bytes] = 0;
}
#endif /* __GTK_ISTRING_PRIVATE_H__ */