/* Chrysalide - Outil d'analyse de fichiers binaires
 * objhole.h - définitions internes pour ll'utilisation d'un espace inutilisé dans la structure GObject
 *
 * Copyright (C) 2024 Cyrille Bagard
 *
 *  This file is part of Chrysalide.
 *
 *  Chrysalide is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Chrysalide 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 Chrysalide.  If not, see <http://www.gnu.org/licenses/>.
 */


#ifndef _GLIBEXT_OBJHOLE_INT_H
#define _GLIBEXT_OBJHOLE_INT_H


#include "objhole.h"


#include "../common/cpp.h"



/**
 * Une structure GObject a la définition suivante :
 *
 *    struct  _GObject
 *    {
 *        GTypeInstance  g_type_instance;
 *        volatile guint ref_count;
 *        GData          *qdata;
 *    };
 *
 * Chaque objet GLib alloué comporte ainsi 4 octets inutilisés :
 *
 *    (gdb) pt /o GObject
 *    type = struct _GObject {
 *    /      0      |       8 /    GTypeInstance g_type_instance;
 *    /      8      |       4 /    guint ref_count;
 *    / XXX  4-byte hole      /
 *    /     16      |       8 /    GData *qdata;
 *
 *                                 / total size (bytes):   24 /
 *    }
 *
 * La situation n'a pas échappé aux développeurs GLib, avec la définition réelle
 * de la structure  (cf. https://github.com/GNOME/glib/blob/main/gobject/gobject.c) :
 *
 *    #if SIZEOF_INT == 4 && GLIB_SIZEOF_VOID_P == 8
 *    #define HAVE_OPTIONAL_FLAGS
 *    #endif
 *
 *    typedef struct
 *    {
 *      GTypeInstance  g_type_instance;
 *      guint          ref_count;
 *    #ifdef HAVE_OPTIONAL_FLAGS
 *      guint          optional_flags;
 *    #endif
 *      GData         *qdata;
 *    } GObjectReal;
 *
 *    G_STATIC_ASSERT(sizeof(GObject) == sizeof(GObjectReal));
 *    G_STATIC_ASSERT(G_STRUCT_OFFSET(GObject, ref_count) == G_STRUCT_OFFSET(GObjectReal, ref_count));
 *    G_STATIC_ASSERT(G_STRUCT_OFFSET(GObject, qdata) == G_STRUCT_OFFSET(GObjectReal, qdata));
 *
 * L'espace entre les deux derniers champs ne peut donc être pleinement exploité deux fois.
 */

/**
 * Les bits effectivements utilisés par GLib s'identifie ainsi (02/12/24) :
 *
 *    $ wget -qO /dev/stdout https://raw.githubusercontent.com/GNOME/glib/refs/heads/main/gobject/gobject.c \
 *        | grep ' *    #define OPTIONAL_FLAG_'
 *    #define OPTIONAL_FLAG_IN_CONSTRUCTION    (1 << 0)
 *    #define OPTIONAL_FLAG_HAS_SIGNAL_HANDLER (1 << 1)
 *    #define OPTIONAL_FLAG_HAS_NOTIFY_HANDLER (1 << 2)
 *    #define OPTIONAL_FLAG_LOCK               (1 << 3)
 *    #define OPTIONAL_FLAG_EVER_HAD_WEAK_REF  (1 << 4)
 *
 * Les n premiers bits doivent ainsi être préservés, même s'il est possible de
 * partager le bit de verrouilage OPTIONAL_FLAG_LOCK.
 */


#ifndef HAVE_OPTIONAL_FLAGS_IN_GOBJECT

#   if SIZEOF_INT == 4 && GLIB_SIZEOF_VOID_P >= 8
#       define HAVE_OPTIONAL_FLAGS_IN_GOBJECT 1
#   else
#       define HAVE_OPTIONAL_FLAGS_IN_GOBJECT 0
#   endif

#endif


/* Nouvelle version dense des objets (instance) */
typedef struct _GThickObject
{
    /**
     * (cf. structure GObjectReal officielle).
     */

    GTypeInstance g_type_instance;          /* Type d'objet                */
    guint ref_count;                        /* Décompte des références     */

#ifdef HAVE_OPTIONAL_FLAGS_IN_GOBJECT
    guint extra;                            /* Zone partagée avec GLib     */
#endif

    GData *qdata;                           /* Données complémentaires ?   */

#ifndef HAVE_OPTIONAL_FLAGS_IN_GOBJECT
    guint extra;                            /* Zone supplémentaire propre  */
#endif

} GThickObject;

/* Nouvelle version dense des objets (classe) */
struct _GThickObjectClass
{
    GObjectClass parent;                    /* A laisser en premier        */

};


/**
 * Définition du périmètre et des moyens d'accès.
 */

/* GLib 2.83.0 - cfa36f5e9 */
#define GOBJECT_RESERVED_EXTRA_BITS 5

#define GET_GOBJECT_EXTRA(obj, tp)                              \
    ({                                                          \
        BUILD_BUG_ON(sizeof(tp) > sizeof(guint));               \
        tp ___result;                                           \
        guint __val;                                            \
        __val = g_thick_object_get_extra(G_THICK_OBJECT(obj));  \
        ___result = *(tp *)(guint []){ __val };                 \
        ___result;                                              \
    })

#define SET_GOBJECT_EXTRA(obj, tp, data)                        \
    ({                                                          \
        BUILD_BUG_ON(sizeof(tp) > sizeof(guint));               \
        BUILD_BUG_ON(sizeof(data) > sizeof(guint *));           \
        guint __val;                                            \
        __val = *(guint *)data;                                 \
        g_thick_object_set_extra(G_THICK_OBJECT(obj), __val);   \
    })



#endif  /* _GLIBEXT_OBJHOLE_INT_H */