GwySerializable

GwySerializable — Serialisable value-like object interface

Functions

Types and Values

Object Hierarchy

    GInterface
    ╰── GwySerializable

Prerequisites

GwySerializable requires GObject.

Known Implementations

GwySerializable is implemented by GwyContainer, GwySIUnit and GwyStringList.

Includes

#include <libgwyddion/gwyddion.h>

Description

GwySerializable is an abstract interface for data-like objects that can be serialised and deserialised. You can serialise any object implementing this interface with functions such as gwy_serialize_gio() and the restore (deserialise) it with gwy_deserialize_memory().

Gwyddion implements a simple serialisation model: only tree-like structures of objects, formed by ownership, can be serialised and restored. Any inter-object relations other than plain ownership must be stored in some weak form and there is no explicit support for this. Moreover, only object values are preserved, their identities are lost (in particular, signals, user data and similar attributes are not subject to serialisation and deserialisation). This, on the other hand, means that any saved object can be restored individually and independently and still be in a meaningful state.

Beside saving and restoration, all serialisable classes implement a copy-constructor that creates a duplicate of an existing object. This constructor is invoked with gwy_serializable_copy(). Furthermore, the value of one such object can be transferred to another object of the same class (or a superclass), similarly to overriden assignment operators in OO languages. This assignment is performed with gwy_serializable_assign().

Most classes that implement GwySerializable define convenience macros for the copy-constructor and assignment operator, called gwy_foo_copy() and gwy_foo_assign(), respectively, where foo is the lowercase class name.

Implementing GwySerializable

You can implement serialisation and deserialisation in your classes. Use the G_IMPLEMENT_ITERFACE macro to generate the boilerplate interface implementation code. The copy constructor and assignment operators are usually easy so we focus on serialisation and deserialisation (with public object data for simplicity). There are a number of helper functions, such as gwy_deserialize_filter() for getting only the interesting items in deserialisation or gwy_serialize_item_double() for handling the itemisation of a single value.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// Standard object structure definitions
typedef struct _MyObject      MyObject;
typedef struct _MyObjectClass MyObjectClass;

struct _MyObject {
    GObject g_object;
    gint32 data;
};

struct _MyObjectClass {
    GObjectClass g_object_class;
};

// Define the type
G_DEFINE_TYPE_WITH_CODE(MyObject, my_object, G_TYPE_OBJECT,
                        G_IMPLEMENT_INTERFACE(GWY_TYPE_SERIALIZABLE, serializable_init));

// Serialised data specification, used both in itemize() and construct()
static const GwySerializableItem default_items[] = {
    { .name = "data", .ctype = GWY_SERIALIZABLE_INT32, },
};

// Implement the interface
static void
serializable_init(GwySerializableInterface *iface)
{
    iface->n_items   = serializable_n_items;
    iface->itemize   = serializable_itemize;
    iface->construct = serializable_construct;
    iface->copy      = serializable_copy;
    iface->assign    = serializable_assign;
}

// Duplication is easy, create a new object and set data
static GwySerializable*
serializable_copy(GwySerializable *serializable)
{
    MyObject *myobject = GWY_MY_OBJECT(serializable);
    MyObject *copy = g_object_newv(MY_TYPE_OBJECT, 0, NULL);

    copy->data = myobject->data;
    return GWY_SERIALIZABLE(copy);
}

// Assigning is even easier, just set data
static void
serializable_assign(GwySerializable *destination,
                    GwySerializable *source)
{
    MyObject *myobject = GWY_MY_OBJECT(serializable);
    MyObject *src = GWY_MY_OBJECT(source);

    src->data = myobject->data;
}

// Our object needs at most one item and it does not contain any child objects.  Thus we can simply return
// a constant.
static gsize
serializable_n_items(G_GNUC_UNUSED GwySerializable *serializable)
{
    return 1;
}

// Fill the item list with actual object data.  This is a bit
// overcomplicated for such a simple object as MyObject, of course.
static gsize
serializable_itemize(GwySerializable *serializable,
                     GwySerializableItems *items)
{
    MyObject *myobject = GWY_MY_OBJECT(serializable);
    GwySerializableItem it[G_N_ELEMENTS(default_items)];
    gsize n_items = 0;

    memcpy(it, default_items, sizeof(default_items));

    // Do not store the data if it has the default value
    if (myobject->data) {
        g_return_val_if_fail(items->len - items->n, 0);
        items->items[items->n].value.v_int32 = myobject->data;
        items->n++;
        n_items++;
    }

    return n_items;
}

// Construct the object from item list.  The data item does not need to be present, in this case the default zero
// value is used.  Also, for more complicated objects the deserialisation can fail or resources need to be released;
// see GwySerializableInterface for the clean-up rules.
static gboolean
serializable_construct(GwySerializable *serializable,
                       GwySerializableItems *items,
                       GwyErrorList **error_list)
{
    GwySerializableItem it[G_N_ELEMENTS(default_items)];

    memcpy(it, default_items, sizeof(default_items));
    gwy_deserialize_filter_items(it, G_N_ELEMENTS(it), items, "MyObject", error_list);

    MyObject *myobject = GWY_MY_OBJECT(serializable);
    myobject->data = it[0].value.v_int32;

    return TRUE;
}

Functions

gwy_serializable_copy()

GwySerializable *
gwy_serializable_copy (GwySerializable *serializable);

Creates an object with identical value.

This is a copy-constructor creating a deep copy.

You can duplicate a NULL, too, but you are discouraged from doing it.

GwySerializable implementations are encouraged to provide my_object_copy() convenience wrappers with argument and return value of the specific type and the corresponding type checking.

Parameters

serializable

A serializable object.

 

Returns

The newly created object copy.

[transfer full]


gwy_serializable_assign()

void
gwy_serializable_assign (GwySerializable *destination,
                         GwySerializable *source);

Copies the value of an object to another object.

If destination is the same object as source the function is no-op.

What constitutes ‘object value’ depends on the class. The general rule is that destination will look as the result of gwy_serializable_copy(). However, it will keep its identity (address, connected signal handlers, references, object data, etc.). The operation may be more efficient than constructing a new object but it may be not.

GwySerializable implementations are encouraged to provide my_object_assign() convenience wrappers with arguments of the specific type and the corresponding type checking.

Parameters

destination

An object of the same type as source . More precisely, source may be subclass of destination (the extra information is lost then).

 

source

A serializable object.

 

gwy_serializable_n_items()

gsize
gwy_serializable_n_items (GwySerializable *serializable);

Provides an upper bound of the number of items the flattened representation of an object will take.

This function wraps the virtual table method n_items(). It increases the returned value by the number of item used to represent the object header (which is 1).

Parameters

serializable

A serializable object.

 

Returns

An upper bound of the number of items serializable will serialize to.


gwy_serializable_itemize()

void
gwy_serializable_itemize (GwySerializable *serializable,
                          GwySerializableItems *items);

Creates the flattened representation of a serializable object.

This function wraps the virtual table method itemize(). It deals with recording of the object header item. Method itemize() then only serialises the actual data of the object.

Parameters

serializable

A serializable object.

 

items

List of flattened object tree items to append the representation of serializable to.

 

gwy_serializable_done()

void
gwy_serializable_done (GwySerializable *serializable);

Frees temporary storage allocated by object itemization.

This function calls the virtual table method done(), if the class has any.

Parameters

serializable

A serializable object.

 

GWY_SERIALIZABLE_GET_INTERFACE()

#define GWY_SERIALIZABLE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), GWY_TYPE_SERIALIZABLE, GwySerializableInterface))

Types and Values

enum GwySerializableCType

Type of serialisable value.

The type is a single byte, i.e. a value smaller than 256. It is equal to the character used in GWY files to denote the corresponding type.

Signed and usinged types are not distinguished here as their byte order is handled the same way.

Members

GWY_SERIALIZABLE_HEADER

Reserved type value which does not appear in files and denotes object header items internally. The serialiser/deserialised sets size to the object data size and array_size to the number of items.

 

GWY_SERIALIZABLE_CONSUMED

Reserved type value which does not appear in files and denotes already consumed items internally.

 

GWY_SERIALIZABLE_INT8

Denotes a character (8bit integer).

 

GWY_SERIALIZABLE_INT8_ARRAY

Denotes a character (8bit integer) array.

 

GWY_SERIALIZABLE_BOOLEAN

Denotes a one-byte boolean.

 

GWY_SERIALIZABLE_INT16

Denotes a 16bit integer.

 

GWY_SERIALIZABLE_INT16_ARRAY

Denotes an array of 16bit integers.

 

GWY_SERIALIZABLE_INT32

Denotes a 32bit integer.

 

GWY_SERIALIZABLE_INT32_ARRAY

Denotes an array of 32bit integers.

 

GWY_SERIALIZABLE_INT64

Denotes a 64bit integer.

 

GWY_SERIALIZABLE_INT64_ARRAY

Denotes an array of 64bit integers.

 

GWY_SERIALIZABLE_DOUBLE

Denotes a IEEE double.

 

GWY_SERIALIZABLE_DOUBLE_ARRAY

Denotes an array of IEEE doubles.

 

GWY_SERIALIZABLE_STRING

Denotes a UTF-8-encoded C string. If you need a raw sequence of bytes, use GWY_SERIALIZABLE_INT8_ARRAY instead.

 

GWY_SERIALIZABLE_STRING_ARRAY

Denotes an array of UTF-8 encoded C strings.

 

GWY_SERIALIZABLE_OBJECT

Denotes an object.

 

GWY_SERIALIZABLE_OBJECT_ARRAY

Denotes an array of objects.

 

GWY_SERIALIZABLE_BOXED

Denotes a serialisable boxed type.

 

enum GwySerializableArrayFlags

Members

GWY_SERIALIZABLE_BIG_DATA

   

GWY_SERIALIZABLE_TRANSFORMED

   

GwySerializableValue

Representation of individual values of object serialisable data.

See GwySerializableCType for corresponding type specifiers.

Member v_size has no use in implementations (it corresponds to GWY_SERIALIZABLE_HEADER), it is used only in object header items in the flattened object tree.

All string values must be valid UTF-8. If you need raw byte sequences use arrays of guint8.

Signed and unsigned integer members are provided for convenience, the serialisation does not distinguish between signed and unsigned integers.

Members

gboolean v_boolean;

The value as a gboolean.

 

gint8 v_int8;

The value as a gint8.

 

guint8 v_uint8;

The value as a guint8.

 

gint16 v_int16;

The value as a gint16.

 

guint16 v_uint16;

The value as a guint16.

 

gint32 v_int32;

The value as a gint32.

 

guint32 v_uint32;

The value as a guint32.

 

gint64 v_int64;

The value as a gint64.

 

guint64 v_uint64;

The value as a guint64.

 

gdouble v_double;

The value as a gdouble.

 

gchar *v_string;

The value as a gchar pointer.

 

guchar *v_ustring;

The value as a guchar pointer.

 

GObject *v_object;

The value as an object.

 

gpointer v_boxed;

The value as a boxed type.

 

gsize v_size;

The value as a gsize.

 

GType v_type;

The value as a GType.

 

gint8 *v_int8_array;

The value as an array of gint8s.

 

guint8 *v_uint8_array;

The value as an array of guint8s.

 

gint16 *v_int16_array;

The value as an array of gint16s.

 

guint16 *v_uint16_array;

The value as an array of guint16s.

 

gint32 *v_int32_array;

The value as an array of gint32s.

 

guint32 *v_uint32_array;

The value as an array of guint32s.

 

gint64 *v_int64_array;

The value as an array of gint64s.

 

guint64 *v_uint64_array;

The value as an array of guint64s.

 

gdouble *v_double_array;

The value as an array of gdoubles.

 

gchar **v_string_array;

The value as an array of gchar pointers.

 

guchar **v_ustring_array;

The value as an array of guchar pointers.

 

GObject **v_object_array;

The value as an array of objects.

 

GwySerializableAux


GwySerializableItem

typedef struct {
    GwySerializableValue value;
    const gchar *name;
    gsize array_size;
    GwySerializableAux aux;
    guchar ctype;
    guint8 array_flags;
} GwySerializableItem;

Information about one object component that is subject to serialisation or deserialisation.

Members

GwySerializableValue value;

Item value, the interpreration depends on the ctype member.

 

const gchar *name;

Component name.

 

gsize array_size;

Array size of the component if of an array type. For boxed types that do not carry type information, deserialisation sets is to the GType of the boxed type. Unused otherwise.

 

GwySerializableAux aux;

   

guchar ctype;

Item type, one of the GwySerializableCType enum.

 

guint8 array_flags;

   

GwySerializableItems

typedef struct {
    gsize len;
    gsize n;
    GwySerializableItem *items;
} GwySerializableItems;

Flattened tree structure of object data used during serialisation and deserialisation.

Members

gsize len;

Allocated number of items.

 

gsize n;

Number of items present.

 

GwySerializableItem *items;

Array of items of total size len with n positions occupied.

 

GwySerializable

typedef struct _GwySerializable GwySerializable;

Formal type of serialisable objects.


struct GwySerializableInterface

struct GwySerializableInterface {
    gsize            (*n_items)  (GwySerializable *serializable);
    gsize            (*itemize)  (GwySerializable *serializable,
                                  GwySerializableItems *items);
    void             (*done)     (GwySerializable *serializable);

    gboolean         (*construct)(GwySerializable *serializable,
                                  GwySerializableItems *items,
                                  GwyErrorList **error_list);

    GwySerializable* (*copy)     (GwySerializable *serializable);
    void             (*assign)   (GwySerializable *destination,
                                  GwySerializable *source);
};

Interface implemented by serialisable objects.

The object class must implement all the methods, except GwySerializableInterface.done() which is optional.

Members

n_items()

Returns the number of items the object will flatten to, including items of all contained objects (but excluding the object header items of self). It should use gwy_serializable_n_items() to calculate the numbers of items the contained objects will flatten to.

This method may return a reasonable upper estimate instead of the exact number; for instance, if objects of some class are known to need 5 to 7 items, n_items() can simply return 7.

 

itemize()

Appends flattened representation of the object data to the items list. It should use gwy_serializable_itemize() to add the data of contained objects.

The number of items corresponding to this object is returned, not including items of contained objects: every contained object counts only as one item.

 

done()

Frees all temporary data created by GwySerializableInterface.itemize(). This method is optional but if it is defined it is guaranteed to be called after each GwySerializableInterface.itemize().

 

construct()

Deserialises an object from array of flattened data items. The first argument is the object of the final class (i.e. it may be a derived class), constructed with the default parameterless constructor.

All strings, objects and arrays in the item list are newly allocated. The method can (and, generally, should) take ownership by filling corresponding item values with NULL, setting their array_size to zero and setting the type to GWY_SERIALIZABLE_CONSUMED. The caller takes care of freeing them if they are not consumed by construct().

This method must make any assumptions about the contents of items. Commonly, however, only items from a small predefined set are expected and then gwy_deserialize_filter_items() can be used to simplify the processing of the item list.

It is possible to chain this method to the parent class and pass it the the item list or a part of it, if you are careful.

 

copy()

Creates a new object with all data identical to this object. This method is expected to create a deep copy. Classes may provide other methods for shallow copies.

 

assign()

Makes all data of this object identical to the data of another object of the same class. Implementations may assume that the is-a relation is satisfied for the source object. This method is expected to perform a deep copy. Classes may provide other methods for shallow copies.