/* Chrysalide - Outil d'analyse de fichiers binaires * type.h - prototypes pour la manipulation des types en tout genre * * Copyright (C) 2010-2018 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 . */ #include "type.h" #include #include #include #include "type-int.h" #include "storage/serialize.h" #include "../common/extstr.h" #include "../common/leb128.h" /* Initialise la classe des types quelconques. */ static void g_data_type_class_init(GDataTypeClass *); /* Initialise l'instance d'un type quelconque. */ static void g_data_type_init(GDataType *); /* Procède à l'initialisation de l'interface de sérialisation. */ static void g_data_type_serializable_interface_init(GSerializableObjectIface *); /* Supprime toutes les références externes. */ static void g_data_type_dispose(GDataType *); /* Procède à la libération totale de la mémoire. */ static void g_data_type_finalize(GDataType *); /* Charge un objet depuis une mémoire tampon. */ static bool _g_data_type_load(GDataType *, GObjectStorage *, packed_buffer_t *); /* Charge un objet depuis une mémoire tampon. */ static bool g_data_type_load(GDataType *, GObjectStorage *, packed_buffer_t *); /* Sauvegarde un objet dans une mémoire tampon. */ static bool _g_data_type_store(const GDataType *, GObjectStorage *, packed_buffer_t *); /* Sauvegarde un objet dans une mémoire tampon. */ static bool g_data_type_store(const GDataType *, GObjectStorage *, packed_buffer_t *); /* Indique le type défini pour un type quelconque. */ G_DEFINE_TYPE_WITH_CODE(GDataType, g_data_type, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE(G_TYPE_SERIALIZABLE_OBJECT, g_data_type_serializable_interface_init)); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe des types quelconques. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_data_type_class_init(GDataTypeClass *klass) { GObjectClass *object; /* Autre version de la classe */ object = G_OBJECT_CLASS(klass); object->dispose = (GObjectFinalizeFunc/* ! */)g_data_type_dispose; object->finalize = (GObjectFinalizeFunc)g_data_type_finalize; klass->load = (type_load_fc)_g_data_type_load; klass->store = (type_store_fc)_g_data_type_store; } /****************************************************************************** * * * Paramètres : type = instance à initialiser. * * * * Description : Initialise l'instance d'un type quelconque. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_data_type_init(GDataType *type) { type_extra_data_t *extra; /* Données insérées à modifier */ extra = GET_DATA_TYPE_EXTRA(type); INIT_GOBJECT_EXTRA_LOCK(extra); g_data_type_set_qualifiers(type, TQF_NONE); type->namespace = NULL; } /****************************************************************************** * * * Paramètres : iface = interface GLib à initialiser. * * * * Description : Procède à l'initialisation de l'interface de sérialisation. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_data_type_serializable_interface_init(GSerializableObjectIface *iface) { iface->load = (load_serializable_object_cb)g_data_type_load; iface->store = (store_serializable_object_cb)g_data_type_store; } /****************************************************************************** * * * Paramètres : type = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_data_type_dispose(GDataType *type) { g_clear_object(&type->namespace); G_OBJECT_CLASS(g_data_type_parent_class)->dispose(G_OBJECT(type)); } /****************************************************************************** * * * Paramètres : type = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_data_type_finalize(GDataType *type) { G_OBJECT_CLASS(g_data_type_parent_class)->finalize(G_OBJECT(type)); } /****************************************************************************** * * * Paramètres : type = type de données à constuire. * * storage = conservateur de données à manipuler ou NULL. * * pbuf = zone tampon à remplir. * * * * Description : Charge un objet depuis une mémoire tampon. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool _g_data_type_load(GDataType *type, GObjectStorage *storage, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ uleb128_t value; /* Valeur ULEB128 à charger */ char ns_sep[2]; /* Séparateur d'éléments */ GDataType *namespace; /* Espace de noms / classe */ result = unpack_uleb128(&value, pbuf); if (!result) goto exit; g_data_type_set_qualifiers(type, value); result = unpack_uleb128(&value, pbuf); if (!result) goto exit; g_data_type_set_flags(type, value); result = extract_packed_buffer(pbuf, ns_sep, 2 * sizeof(char), false); if (!result) goto exit; if (ns_sep[0] != '\0') { namespace = G_DATA_TYPE(g_object_storage_unpack_object(storage, "types", pbuf)); result = (namespace != NULL); if (!result) goto exit; result = g_data_type_set_namespace(type, namespace, ns_sep); assert(result); g_object_unref(G_OBJECT(namespace)); } exit: return result; } /****************************************************************************** * * * Paramètres : type = type de données à constuire. * * storage = conservateur de données à manipuler ou NULL. * * pbuf = zone tampon à remplir. * * * * Description : Charge un objet depuis une mémoire tampon. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool g_data_type_load(GDataType *type, GObjectStorage *storage, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ GDataTypeClass *class; /* Classe du type */ class = G_DATA_TYPE_GET_CLASS(type); result = class->load(type, storage, pbuf); return result; } /****************************************************************************** * * * Paramètres : type = type de données à consulter. * * storage = conservateur de données à manipuler ou NULL. * * pbuf = zone tampon à remplir. * * * * Description : Sauvegarde un objet dans une mémoire tampon. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool _g_data_type_store(const GDataType *type, GObjectStorage *storage, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ type_extra_data_t *extra; /* Données insérées à modifier */ result = pack_uleb128((uleb128_t []){ g_data_type_get_qualifiers(type) }, pbuf); if (!result) goto exit; result = pack_uleb128((uleb128_t []){ g_data_type_get_flags(type) }, pbuf); if (!result) goto exit; extra = GET_DATA_TYPE_EXTRA(type); LOCK_GOBJECT_EXTRA(extra); result = extend_packed_buffer(pbuf, extra->ns_sep, 2 * sizeof(char), false); if (!result) goto unlocking_exit; if (extra->ns_sep[0] != '\0') { assert(type->namespace != NULL); result = g_object_storage_pack_object(storage, "types", G_SERIALIZABLE_OBJECT(type->namespace), pbuf); if (!result) goto unlocking_exit; } unlocking_exit: UNLOCK_GOBJECT_EXTRA(extra); exit: return result; } /****************************************************************************** * * * Paramètres : type = type de données à consulter. * * storage = conservateur de données à manipuler ou NULL. * * pbuf = zone tampon à remplir. * * * * Description : Sauvegarde un objet dans une mémoire tampon. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool g_data_type_store(const GDataType *type, GObjectStorage *storage, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ GDataTypeClass *class; /* Classe du type */ class = G_DATA_TYPE_GET_CLASS(type); result = class->store(type, storage, pbuf); return result; } /****************************************************************************** * * * Paramètres : type = type à consulter. * * * * Description : Calcule une empreinte pour un type de données. * * * * Retour : Valeur arbitraire sur 32 bits, idéalement unique par type. * * * * Remarques : - * * * ******************************************************************************/ guint g_data_type_hash(const GDataType *type) { guint result; /* Empreinte à renvoyer */ GDataTypeClass *class; /* Classe du type */ class = G_DATA_TYPE_GET_CLASS(type); result = class->hash(type); result ^= g_int_hash((gint []){ g_data_type_get_qualifiers(type) }); if (type->namespace != NULL) result ^= g_data_type_hash(type->namespace); return result; } /****************************************************************************** * * * Paramètres : type = type à dupliquer. * * * * Description : Crée un copie d'un type existant. * * * * Retour : Nouvelle instance de type identique à celle fournie. * * * * Remarques : - * * * ******************************************************************************/ GDataType *g_data_type_dup(const GDataType *type) { GDataType *result; /* Copie à retourner */ GDataTypeClass *class; /* Classe du type */ type_extra_data_t *extra; /* Données insérées à modifier */ GDataType *ns; /* Eventuel espace de noms */ bool status; /* Bilan d'un rattachement */ class = G_DATA_TYPE_GET_CLASS(type); result = class->dup(type); g_data_type_set_qualifiers(result, g_data_type_get_qualifiers(type)); g_data_type_set_flags(result, g_data_type_get_flags(type)); extra = GET_DATA_TYPE_EXTRA(type); LOCK_GOBJECT_EXTRA(extra); if (extra->ns_sep[0] != '\0') { assert(type->namespace != NULL); ns = g_data_type_dup(type->namespace); status = g_data_type_set_namespace(result, ns, extra->ns_sep); assert(status); g_object_unref(G_OBJECT(ns)); if (!status) g_clear_object(&result); } UNLOCK_GOBJECT_EXTRA(extra); return result; } /****************************************************************************** * * * Paramètres : type = type à convertir. * * include = doit-on inclure les espaces de noms ? * * * * Description : Décrit le type fourni sous forme de caractères. * * * * Retour : Chaîne à libérer de la mémoire après usage. * * * * Remarques : - * * * ******************************************************************************/ char *g_data_type_to_string(const GDataType *type, bool include) { char *result; /* Chaîne à retourner */ GDataTypeClass *class; /* Classe du type */ type_extra_data_t *extra; /* Données insérées à modifier */ char *namespace; /* Groupe d'appartenance */ TypeQualifier qualifiers; /* Qualificatifs du type */ class = G_DATA_TYPE_GET_CLASS(type); result = class->to_string(type, include); if (result == NULL) result = strdup(""); if (include) { extra = GET_DATA_TYPE_EXTRA(type); LOCK_GOBJECT_EXTRA(extra); if (type->namespace != NULL && g_data_type_handle_namespaces(type)) { namespace = g_data_type_to_string(type->namespace, true); result = strprep(result, extra->ns_sep); result = strprep(result, namespace); free(namespace); } UNLOCK_GOBJECT_EXTRA(extra); } qualifiers = g_data_type_get_qualifiers(type); if (qualifiers & TQF_RESTRICT) result = strprep(result, "restrict "); if (qualifiers & TQF_VOLATILE) result = strprep(result, "volatile "); if (qualifiers & TQF_CONST) result = strprep(result, "const "); return result; } /****************************************************************************** * * * Paramètres : type = instance à mettre à jour. * * qualifiers = nouveaux qualificatifs pour le type. * * * * Description : Définit l'ensemble des qualificatifs d'une instance de type. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_data_type_set_qualifiers(GDataType *type, TypeQualifier qualifiers) { type_extra_data_t *extra; /* Données insérées à modifier */ extra = GET_DATA_TYPE_EXTRA(type); LOCK_GOBJECT_EXTRA(extra); extra->qualifiers = qualifiers; UNLOCK_GOBJECT_EXTRA(extra); } /****************************************************************************** * * * Paramètres : type = instance à mettre à jour. * * qualifier = nouveau qualificatif pour le type. * * * * Description : Ajoute un qualificatif à une instance de type. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_data_type_add_qualifier(GDataType *type, TypeQualifier qualifier) { type_extra_data_t *extra; /* Données insérées à modifier */ extra = GET_DATA_TYPE_EXTRA(type); LOCK_GOBJECT_EXTRA(extra); extra->qualifiers |= qualifier; UNLOCK_GOBJECT_EXTRA(extra); } /****************************************************************************** * * * Paramètres : type = instance à consulter. * * * * Description : Fournit les qualificatifs associés à une instance de type. * * * * Retour : Qualificatifs éventuels. * * * * Remarques : - * * * ******************************************************************************/ TypeQualifier g_data_type_get_qualifiers(const GDataType *type) { TypeQualifier result; /* Qualificatifs à renvoyer */ type_extra_data_t *extra; /* Données insérées à modifier */ extra = GET_DATA_TYPE_EXTRA(type); LOCK_GOBJECT_EXTRA(extra); result = extra->qualifiers; UNLOCK_GOBJECT_EXTRA(extra); return result; } /****************************************************************************** * * * Paramètres : type = type à mettre à jour. * * namespace = instance d'appartenance. * * sep = séparateur à utiliser entre les éléments. * * * * Description : Définit le groupe d'appartenance d'un type donné. * * * * Retour : true si la définition est effective, false en cas de rejet. * * * * Remarques : - * * * ******************************************************************************/ bool g_data_type_set_namespace(GDataType *type, GDataType *namespace, const char *sep) { bool result; /* Bilan à retourner */ type_extra_data_t *extra; /* Données insérées à modifier */ result = ((namespace == NULL && sep == NULL) || (namespace != NULL && sep != NULL && sep[0] != '\0')); if (result) { extra = GET_DATA_TYPE_EXTRA(type); LOCK_GOBJECT_EXTRA(extra); if (sep == NULL) { extra->ns_sep[0] = '\0'; extra->ns_sep[1] = '\0'; } else { extra->ns_sep[0] = sep[0]; extra->ns_sep[1] = sep[1]; } if (type->namespace != NULL) g_object_unref(G_OBJECT(type->namespace)); type->namespace = namespace; g_object_ref(G_OBJECT(namespace)); UNLOCK_GOBJECT_EXTRA(extra); } return result; } /****************************************************************************** * * * Paramètres : type = type à consulter. * * * * Description : Fournit le groupe d'appartenance d'un type donné. * * * * Retour : Eventuelle instance d'appartenance ou NULL. * * * * Remarques : - * * * ******************************************************************************/ GDataType *g_data_type_get_namespace(const GDataType *type) { GDataType *result; /* Espace à renvoyer */ type_extra_data_t *extra; /* Données insérées à modifier */ extra = GET_DATA_TYPE_EXTRA(type); LOCK_GOBJECT_EXTRA(extra); result = type->namespace; if (result != NULL) g_object_ref(G_OBJECT(result)); UNLOCK_GOBJECT_EXTRA(extra); return result; } /****************************************************************************** * * * Paramètres : type = type à consulter. * * * * Description : Fournit la chaîne de séparation entre deux entités. * * * * Retour : Eventuelle chaîne de séparation ou NULL. * * * * Remarques : - * * * ******************************************************************************/ char *g_data_type_get_namespace_separator(const GDataType *type) { char *result; /* Séparateur à retourner */ type_extra_data_t *extra; /* Données insérées à modifier */ extra = GET_DATA_TYPE_EXTRA(type); LOCK_GOBJECT_EXTRA(extra); if (extra->ns_sep[0] == '\0') result = NULL; else if (extra->ns_sep[1] == '\0') result = strndup(extra->ns_sep, 1); else result = strndup(extra->ns_sep, 2); UNLOCK_GOBJECT_EXTRA(extra); return result; } /****************************************************************************** * * * Paramètres : type = type à consulter. * * * * Description : Indique si le type assure une gestion des espaces de noms. * * * * Retour : Bilan de la consultation. * * * * Remarques : - * * * ******************************************************************************/ bool g_data_type_handle_namespaces(const GDataType *type) { bool result; /* Bilan à retourner */ GDataTypeClass *class; /* Classe du type */ class = G_DATA_TYPE_GET_CLASS(type); if (class->handle_ns != NULL) result = class->handle_ns(type); else result = true; return result; } /****************************************************************************** * * * Paramètres : type = instance à mettre à jour. * * flags = nouvelles propriétés pour le type. * * * * Description : Définit l'ensemble des fanions d'une instance de type. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_data_type_set_flags(GDataType *type, TypeFlag flags) { type_extra_data_t *extra; /* Données insérées à modifier */ extra = GET_DATA_TYPE_EXTRA(type); LOCK_GOBJECT_EXTRA(extra); extra->flags = flags; UNLOCK_GOBJECT_EXTRA(extra); } /****************************************************************************** * * * Paramètres : type = instance à mettre à jour. * * flag = nouvelle propriété pour le type. * * * * Description : Ajoute un fanion à une instance de type. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_data_type_add_flag(GDataType *type, TypeFlag flag) { type_extra_data_t *extra; /* Données insérées à modifier */ extra = GET_DATA_TYPE_EXTRA(type); LOCK_GOBJECT_EXTRA(extra); extra->flags |= flag; UNLOCK_GOBJECT_EXTRA(extra); } /****************************************************************************** * * * Paramètres : type = instance à consulter. * * * * Description : Fournit les fanions associés à une instance de type. * * * * Retour : Qualificatifs éventuels. * * * * Remarques : - * * * ******************************************************************************/ TypeFlag g_data_type_get_flags(const GDataType *type) { TypeFlag result; /* Propriétés à renvoyer */ type_extra_data_t *extra; /* Données insérées à modifier */ extra = GET_DATA_TYPE_EXTRA(type); LOCK_GOBJECT_EXTRA(extra); result = extra->flags; UNLOCK_GOBJECT_EXTRA(extra); return result; } /****************************************************************************** * * * Paramètres : type = type à consulter. * * * * Description : Indique si le type est un pointeur. * * * * Retour : Bilan de la consultation. * * * * Remarques : - * * * ******************************************************************************/ bool g_data_type_is_pointer(const GDataType *type) { bool result; /* Bilan à retourner */ GDataTypeClass *class; /* Classe du type */ class = G_DATA_TYPE_GET_CLASS(type); if (class->is_pointer != NULL) result = class->is_pointer(type); else result = false; return result; } /****************************************************************************** * * * Paramètres : type = type à consulter. * * * * Description : Indique si le type est une référence. * * * * Retour : Bilan de la consultation. * * * * Remarques : - * * * ******************************************************************************/ bool g_data_type_is_reference(const GDataType *type) { bool result; /* Bilan à retourner */ GDataTypeClass *class; /* Classe du type */ class = G_DATA_TYPE_GET_CLASS(type); if (class->is_reference != NULL) result = class->is_reference(type); else result = false; return result; }