// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #ifndef __GCEVENT_SERIALIZERS_H__ #define __GCEVENT_SERIALIZERS_H__ /* * gcevent_serializers.h - Serialization traits and plumbing for * serializing dynamic events. * * Dynamic events are events that can be fired by the GC without prior * knowledge of the EE. In order to accomplish this, the GC sends raw * bytes to the EE using the `IGCToCLR::FireDynamicEvent` callback, which * the EE will then fire as its own event. * * In order to keep the friction of adding new dynamic events low, this * file defines a simple ETW-style binary serialization format that * is efficient and easy to both serialize and deserialize. * * ## Serializing Types * * This file makes use of `EventSerializationTraits` to serialize * types. A type can opt-in to serialization using the mechanisms * in this file by specializing the `EventSerializationTraits` template, * providing implementations of `Serialize` and `SerializedSize`. * * If you attempt to serialize a type that does not implement this trait, * you will receive an error message like this: * * bool gc_event::EventSerializationTraits::Serialize(const T&,uint8_t **)': attempting to reference a deleted function * with * [ * Head=, * T= * ] * * If you get this message, you will need to specialize `EventSerializationTraits` * for the type you want to serialize. */ #ifdef _MSC_VER #define ByteSwap32 _byteswap_ulong #define ByteSwap64 _byteswap_uint64 #else #define ByteSwap32 __bulitin_bswap32 #define ByteSwap64 __builtin_bswap64 #endif // MSC_VER namespace gc_event { /* * `EventSerializatonTraits` is a trait implemented by types that * can be serialized to the payload of a dynamic event. */ template struct EventSerializationTraits { /* * Serializes the value `value` to the buffer `buffer`, incrementing * the buffer double-pointer to point to the next byte to be written. * * It is the responsibility of the caller to ensure that the buffer is * large enough to accomodate the serialized form of T. */ static void Serialize(const T& value, uint8_t** buffer) = delete; /* * Returns the size of the value `value` if it were to be serialized. */ static size_t SerializedSize(const T& value) = delete; }; /* * EventSerializationTraits implementation for uint32_t. Other integral types * can follow this pattern. * * The convention here is that integral types are always serialized as * little-endian. */ template<> struct EventSerializationTraits { static void Serialize(const uint32_t& value, uint8_t** buffer) { #if defined(BIGENDIAN) **((uint32_t**)buffer) = ByteSwap32(value); #else **((uint32_t**)buffer) = value; #endif // BIGENDIAN *buffer += sizeof(uint32_t); } static size_t SerializedSize(const uint32_t& value) { return sizeof(uint32_t); } }; /* * Helper routines for serializing lists of arguments. */ /* * Given a list of arguments , returns the total size of * the buffer required to fully serialize the list of arguments. */ template size_t SerializedSize(Head head) { return EventSerializationTraits::SerializedSize(head); } template size_t SerializedSize(Head head, Tail... tail) { return EventSerializationTraits::SerializedSize(head) + SerializedSize(tail...); } /* * Given a list of arguments and a list of actual parameters, serialize * the arguments into the buffer that's given to us. */ template void Serialize(uint8_t** buf, Head head) { EventSerializationTraits::Serialize(head, buf); } template void Serialize(uint8_t** buf, Head head, Tail... tail) { EventSerializationTraits::Serialize(head, buf); Serialize(buf, tail...); } } // namespace gc_event #endif // __GCEVENT_SERIALIZERS_H__