| /** @file |
| |
| Extendible |
| |
| @section license License |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| |
| @section details Details |
| |
| //////////////////////////////////////////// |
| Implements: |
| * Extendible<Derived_t> |
| * Schema |
| * fieldAdd |
| * fieldFind |
| */ |
| |
| #pragma once |
| #include <cstdint> |
| #include <typeinfo> |
| #include <typeindex> |
| #include <cstddef> |
| #include <cstring> |
| #include <sstream> |
| #include <functional> |
| #include <iomanip> |
| #include <ostream> |
| #include <typeindex> |
| #include <typeinfo> |
| #include <type_traits> |
| #include <unordered_map> |
| |
| #include "tscore/AtomicBit.h" |
| #include "tscore/ink_assert.h" |
| #include "tscore/ink_memory.h" |
| #include "tscore/ink_defs.h" |
| |
| ////////////////////////////////////////// |
| /// SUPPORT MACRO |
| #define DEF_EXT_NEW_DEL(cls) \ |
| void *operator new(size_t sz) { return ats_malloc(ext::sizeOf<cls>()); } \ |
| void *operator new(size_t sz, void *ptr) { return ptr; } \ |
| void operator delete(void *ptr) { free(ptr); } |
| |
| ////////////////////////////////////////// |
| /// HELPER CLASSES |
| |
| ///////////////////////// |
| // Has 'const_iterator' trait class |
| template <typename T> class has_const_iterator |
| { |
| template <typename U> static std::true_type check(typename U::const_iterator *); |
| template <typename U> static std::false_type check(...); |
| |
| public: |
| static constexpr decltype(check<T>(nullptr)) value{}; // value is 'constexpr true' if T defines 'const_iterator' |
| }; |
| ///////////////////////// |
| // Has 'super_type' trait class |
| template <typename T> class has_super_type |
| { |
| template <typename U> static std::true_type check(typename U::super_type *); |
| template <typename U> static std::false_type check(...); |
| |
| public: |
| static constexpr decltype(check<T>(nullptr)) value{}; // value is 'constexpr true' if T defines 'super_type' |
| }; |
| |
| ////////////////////////////////////////// |
| ////////////////////////////////////////// |
| ////////////////////////////////////////// |
| |
| // C API |
| |
| // context (internally FieldDesc*) |
| typedef void const *ExtFieldContext; |
| typedef void *DerivedPtr; |
| typedef void *FieldPtr; |
| |
| FieldPtr ExtFieldPtr(DerivedPtr derived, ExtFieldContext field_context, int *size = nullptr); |
| |
| ////////////////////////////////////////// |
| ////////////////////////////////////////// |
| ////////////////////////////////////////// |
| |
| namespace ext |
| { |
| //////////////////////////////////////////////////// |
| // Forward declarations |
| template <typename Derived_t, typename Field_t> class FieldId; // field handle (strongly type) |
| template <typename Derived_t> class Extendible; |
| |
| namespace details // internal stuff |
| { |
| using Offest_t = uint16_t; // used to store byte offsets to fields |
| struct FieldDesc; // keeps the field properties, and methods |
| class Schema; // container of fields and methods |
| |
| // forward declare friend functions used outside of details |
| template <typename T> uintptr_t initRecurseSuper(T &, uintptr_t); |
| template <typename T> FieldPtr FieldPtrGet(Extendible<T> const &, details::FieldDesc const &); |
| |
| ////////////////////////////////////////// |
| |
| bool & |
| areFieldsFinalized() |
| { |
| static bool finalized = false; |
| return finalized; |
| } |
| |
| ///////////////////////////////////////////////////////////////////// |
| /// ext::details::FieldDesc - type erased field descriptor, with type specific std::functions |
| struct FieldDesc { |
| Offest_t ext_loc_offset; ///< byte offset to Extendible._ext_loc |
| Offest_t field_offset; ///< byte offset from ext_loc to field |
| std::type_index field_type_idx; ///< data type index |
| uint16_t size; ///< byte size of field |
| uint8_t align; ///< alignment of field |
| uint8_t mask; ///< mask for packed bit operations |
| |
| // specialize the following |
| std::function<void(FieldPtr)> constructor; |
| std::function<void(FieldPtr)> destructor; |
| std::function<void(std::ostream &, void const *)> serializer; |
| |
| FieldDesc() : field_type_idx(typeid(std::nullptr_t)) {} |
| }; |
| |
| ///////////////////////////////////////////////////////////////////// |
| /// ext::details::Schema manages the a static layout of fields as data structures |
| class Schema |
| { |
| public: |
| std::unordered_map<std::string, FieldDesc> fields; ///< defined elements of the blob by name |
| size_t alloc_size = 0; ///< bytes to allocate for fields |
| uint8_t alloc_align = 1; ///< alignment of block |
| std::atomic<uint> cnt_constructed = {0}; ///< the number of Extendible<Derived> created. |
| std::atomic<uint> cnt_fld_constructed = {0}; ///< the number of Extendible<Derived> that constructed fields. |
| std::atomic<uint> cnt_destructed = {0}; ///< the number of Extendible<Derived> destroyed. |
| |
| public: |
| Schema() {} |
| ~Schema(); |
| |
| /// Testing methods |
| bool no_instances() const; ///< returns true if there are no instances of Extendible<Derived_t> |
| bool reset(); ///< clears all field definitions. |
| |
| size_t fullSize(size_t base_size) const; ///< returns sizeof memory allocated |
| void updateMemOffsets(); ///< updates memory offsets, alignment, and total allocation size |
| void callConstructor(uintptr_t ext_start_ptr); ///< calls constructor for each field |
| void callDestructor(uintptr_t ext_start_ptr); ///< call destructor for each field |
| |
| }; // end Schema struct |
| } // namespace details |
| |
| /// ext::Extendible allows code (and Plugins) to declare member-like variables during system init. |
| /* |
| * This class uses a special allocator (ext::create) to extend the memory allocated to store run-time static |
| * variables, which are registered by plugins during system init. The API is in a functional style to support |
| * multiple inheritance of Extendible classes. This is templated so static variables are instanced per Derived |
| * type, because we need to have different field schema per type. |
| * |
| * @tparam Derived_t - the class that you want to extend at runtime. |
| * |
| * @see test_Extendible.cc for examples |
| * |
| */ |
| template <typename Derived_t> class Extendible |
| { |
| public: |
| using short_ptr_t = uint16_t; |
| |
| // static |
| static details::Schema schema; ///< one schema instance per Derived_t to define contained fields |
| |
| // return the address offset of the ext_loc member variable |
| static constexpr size_t |
| getLocOffset() |
| { |
| return offsetof(Extendible<Derived_t>, ext_loc); |
| } |
| |
| // member variables |
| private: |
| short_ptr_t ext_loc = 0; ///< byte offset to extendible storage |
| |
| // lifetime management |
| /** don't allow copy construct, that doesn't allow atomicity */ |
| public: |
| Extendible(Extendible &) = delete; |
| |
| protected: |
| Extendible(); |
| // use ext::create() exclusively for allocation and initialization |
| |
| /** destruct all fields */ |
| ~Extendible(); |
| |
| private: |
| /** construct all fields */ |
| size_t initFields(uintptr_t start_ptr); ///< tell this extendible where it's memory offset start is. |
| |
| uintptr_t |
| getBegin() const |
| { |
| return uintptr_t(this) + ext_loc; |
| } |
| |
| template <typename T> friend T *create(); |
| template <typename T> friend uintptr_t details::initRecurseSuper(T &, uintptr_t); |
| template <typename T> friend FieldPtr details::FieldPtrGet(Extendible<T> const &, details::FieldDesc const &); |
| template <typename T> friend std::string viewFormat(T const &, uintptr_t, int); |
| }; |
| |
| // define the static schema per derived type |
| template <typename Derived_t> details::Schema Extendible<Derived_t>::schema; |
| |
| //#################################################### |
| //#################################################### |
| // UTILITY Functions |
| |
| ////////////////////////////////////////////////////// |
| /// HexToString function for serializing untyped C storage |
| // TODO: use ts::bwf::As_Hex() after PR goes through |
| inline void |
| hexToStream(std::ostream &os, void const *buf, uint16_t size) |
| { |
| static const char hexDigits[] = "0123456789abcdef"; |
| |
| const uint8_t *src = static_cast<const uint8_t *>(buf); |
| for (int i = 0; i < size; i++) { |
| os << hexDigits[src[i]]; |
| } |
| }; |
| |
| template <typename Field_t> |
| void |
| serializeField(std::ostream &os, Field_t const &f) |
| { |
| using namespace std; |
| // print containers as lists |
| if constexpr (has_const_iterator<Field_t>::value) { |
| os << "["; |
| for (auto const &a : f) { |
| serializeField(os, a); |
| os << ", "; |
| } |
| os << "]"; |
| return; |
| } else { |
| os << f; |
| } |
| } |
| //#################################################### |
| //#################################################### |
| //#################################################### |
| |
| ///////////////////////////////////////////////////////////////////// |
| // FieldId |
| /// a strongly typed pointer to FieldDesc |
| template <typename Derived_t, typename Field_t> class FieldId |
| { |
| public: |
| ext::details::FieldDesc const *desc = nullptr; |
| bool isValid() const; |
| FieldId(ext::details::FieldDesc const &); |
| FieldId() {} |
| FieldId(FieldId const &) = default; |
| }; |
| |
| namespace details |
| { |
| template <typename Derived_t> |
| FieldPtr |
| FieldPtrGet(Extendible<Derived_t> const &d, FieldDesc const &desc) |
| { |
| return FieldPtr(d.Extendible<Derived_t>::getBegin() + desc.field_offset); |
| } |
| |
| // overloadable function to construct and init an FieldId |
| ////////////////////////////////////////////////////// |
| /// Type Generic Template |
| |
| template <typename Derived_t, typename Field_t> |
| Field_t const & |
| fieldGet(void const *fld_ptr, FieldId<Derived_t, Field_t> const &field) |
| { |
| return *static_cast<Field_t const *>(fld_ptr); |
| } |
| |
| template <typename Derived_t, typename Field_t> |
| Field_t & |
| fieldSet(void *fld_ptr, FieldId<Derived_t, Field_t> const &field) |
| { |
| return *static_cast<Field_t *>(fld_ptr); |
| } |
| |
| template <typename Derived_t, typename Field_t> |
| void |
| makeFieldId(FieldId<Derived_t, Field_t> &id, FieldDesc &desc) |
| { |
| ink_assert(!areFieldsFinalized()); |
| |
| desc.field_type_idx = std::type_index(typeid(Field_t)); |
| desc.ext_loc_offset = Extendible<Derived_t>::getLocOffset(); |
| desc.field_offset = std::numeric_limits<decltype(desc.field_offset)>::max(); |
| desc.size = sizeof(Field_t); |
| desc.align = alignof(Field_t); |
| desc.mask = 0; |
| |
| id = FieldId<Derived_t, Field_t>(desc); |
| |
| // |
| desc.constructor = [](FieldPtr fld_ptr) { new (fld_ptr) Field_t(); }; |
| desc.destructor = [](FieldPtr fld_ptr) { static_cast<Field_t *>(fld_ptr)->~Field_t(); }; |
| desc.serializer = [id](std::ostream &os, void const *fld_ptr) { serializeField(os, fieldGet(fld_ptr, id)); }; |
| } |
| |
| ////////////////////////////////////////////////////// |
| /// C API specialization |
| // no type or constructor. Just a size. |
| |
| template <typename Derived_t> |
| void |
| makeFieldId(FieldDesc &desc, uint16_t size) |
| { |
| ink_assert(!areFieldsFinalized()); |
| desc.field_type_idx = typeid(void); |
| desc.ext_loc_offset = Extendible<Derived_t>::getLocOffset(); |
| desc.field_offset = std::numeric_limits<decltype(desc.field_offset)>::max(); |
| desc.size = size; |
| desc.align = 1; |
| desc.mask = 0; |
| |
| // |
| desc.constructor = nullptr; |
| desc.destructor = nullptr; |
| desc.serializer = [size](std::ostream &os, void const *fld_ptr) { hexToStream(os, fld_ptr, size); }; |
| } |
| |
| ////////////////////////////////////////////////////// |
| /// Bool specializations |
| |
| template <typename Derived_t> |
| bool |
| fieldGet(const void *fld_ptr, FieldId<Derived_t, bool> const &field) |
| { |
| return bool((*static_cast<const uint8_t *>(fld_ptr)) & field.desc->mask); |
| } |
| |
| template <typename Derived_t> |
| AtomicBit |
| fieldSet(FieldPtr fld_ptr, FieldId<Derived_t, bool> const &field) |
| { |
| return AtomicBit{static_cast<uint8_t *>(fld_ptr), field.desc->mask}; |
| } |
| |
| template <typename Derived_t> |
| void |
| makeFieldId(FieldId<Derived_t, bool> &id, FieldDesc &desc) |
| { |
| desc.field_type_idx = std::type_index(typeid(bool)); |
| desc.ext_loc_offset = Extendible<Derived_t>::getLocOffset(); |
| desc.field_offset = std::numeric_limits<decltype(desc.field_offset)>::max(); |
| desc.size = 0; |
| desc.align = 0; |
| desc.mask = 0; |
| |
| id = FieldId<Derived_t, bool>(desc); |
| |
| // |
| desc.constructor = nullptr; |
| desc.destructor = nullptr; |
| desc.serializer = [id](std::ostream &os, void const *fld_ptr) { serializeField(os, fieldGet(fld_ptr, id)); }; |
| } |
| |
| ////////////////////////////////////////////////////// |
| /// std::atomic<bool> specializations (same as bool) |
| |
| template <typename Derived_t> |
| inline bool |
| fieldGet(void const *fld_ptr, FieldId<Derived_t, std::atomic<bool>> const &field) |
| { |
| return bool(fld_ptr & field.mask); |
| } |
| |
| template <typename Derived_t> |
| inline AtomicBit |
| fieldSet(FieldPtr fld_ptr, FieldId<Derived_t, std::atomic<bool>> const &field) |
| { |
| return AtomicBit{fld_ptr, field.mask}; |
| } |
| |
| template <typename Derived_t> |
| void |
| makeFieldId(FieldId<Derived_t, std::atomic<bool>> &id, FieldDesc &desc) |
| { |
| desc.field_type_idx = std::type_index(typeid(std::atomic<bool>)); |
| desc.ext_loc_offset = Extendible<Derived_t>::getLocOffset(); |
| desc.field_offset = std::numeric_limits<decltype(desc.field_offset)>::max(); |
| desc.size = 0; |
| desc.align = 0; |
| desc.mask = 0; |
| |
| id = FieldId<Derived_t, std::atomic<bool>>(desc); |
| |
| // |
| desc.constructor = nullptr; |
| desc.destructor = nullptr; |
| desc.serializer = [id](std::ostream &os, void const *fld_ptr) { serializeField(os, fieldGet(fld_ptr, id)); }; |
| } |
| } // namespace details |
| |
| ////////////////////////////////////////////////////// |
| /// safely cast FieldDesc back to FieldId |
| |
| template <typename Derived_t, typename Field_t> |
| FieldId<Derived_t, Field_t>::FieldId(ext::details::FieldDesc const &fld_desc) : desc(&fld_desc) |
| { |
| const size_t loc = Extendible<Derived_t>::getLocOffset(); |
| ink_assert(loc == fld_desc.ext_loc_offset); |
| ink_assert(std::type_index(typeid(Field_t)) == fld_desc.field_type_idx); |
| } |
| |
| template <typename Derived_t, typename Field_t> |
| bool |
| FieldId<Derived_t, Field_t>::isValid() const |
| { |
| return desc != nullptr; |
| } |
| |
| //#################################################### |
| //#################################################### |
| //#################################################### |
| // Functional API for Extendible Field Access |
| // |
| |
| //////////////////////////////////////////////////// |
| // Schema Method Definitions |
| // |
| |
| /// Add a new Field to this record type |
| template <class Derived_t, class Field_t> |
| bool |
| fieldAdd(FieldId<Derived_t, Field_t> &field_id, char const *field_name) |
| { |
| using namespace ext::details; |
| Schema &schema = Extendible<Derived_t>::schema; |
| ink_release_assert(schema.no_instances()); // it's too late, we already started allocating. |
| ink_release_assert(!areFieldsFinalized()); // it's too late, Fields must be added during Plugin Init. |
| |
| auto field_iter = schema.fields.find(field_name); |
| if (field_iter != schema.fields.end()) { |
| return false; |
| } |
| |
| makeFieldId(field_id, schema.fields[field_name]); |
| schema.updateMemOffsets(); |
| return true; |
| } |
| |
| /// Add a new Field to Derived_t, this C function uses a fat API |
| template <class Derived_t> |
| ExtFieldContext |
| fieldAdd(char const *field_name, int size, void (*construct_fn)(void *), void (*destruct_fn)(void *)) |
| { |
| using namespace ext::details; |
| Schema &schema = Extendible<Derived_t>::schema; |
| ink_release_assert(schema.no_instances()); // it's too late, we already started allocating. |
| ink_release_assert(!areFieldsFinalized()); // it's too late, Fields must be added during Plugin Init. |
| ink_release_assert(size >= 0); // non-negative numbers please |
| |
| auto field_iter = schema.fields.find(field_name); |
| if (field_iter != schema.fields.end()) { |
| return nullptr; |
| } |
| if (size == 0) { |
| FieldId<Derived_t, bool> id; |
| makeFieldId(id, schema.fields[field_name]); |
| } else { |
| FieldDesc &desc = schema.fields[field_name]; |
| makeFieldId<Derived_t>(desc, size); |
| desc.constructor = construct_fn; |
| desc.destructor = destruct_fn; |
| } |
| schema.updateMemOffsets(); |
| return &schema.fields[field_name]; |
| } |
| |
| template <class Derived_t, class Field_t> |
| bool |
| fieldFind(FieldId<Derived_t, Field_t> &field_id, char const *field_name) |
| { |
| using namespace ext::details; |
| ink_release_assert(areFieldsFinalized()); |
| Schema const &schema = Extendible<Derived_t>::schema; |
| auto field_iter = schema.fields.find(field_name); |
| if (field_iter == schema.fields.end()) { |
| return false; // didn't find name |
| } |
| field_id = FieldId<Derived_t, Field_t>(field_iter->second); |
| return true; |
| } |
| |
| // each Derived_t will have a DerivedExtfieldFind |
| template <class Derived_t> |
| ExtFieldContext |
| fieldFind(char const *field_name) |
| { |
| using namespace ext::details; |
| ink_release_assert(areFieldsFinalized()); |
| Schema const &schema = Extendible<Derived_t>::schema; |
| auto field_iter = schema.fields.find(field_name); |
| if (field_iter == schema.fields.end()) { |
| return nullptr; // didn't find name |
| } |
| return &field_iter->second; |
| } |
| |
| //////////////////////////////////////////////////// |
| /// ext::get & ext::set accessor functions |
| template <typename T, typename Derived_t, typename Field_t> |
| inline decltype(auto) |
| get(T const &d, FieldId<Derived_t, Field_t> &field) |
| { |
| Extendible<Derived_t> const &ext = d; |
| FieldPtr fld_ptr = ext::details::FieldPtrGet(ext, *field.desc); |
| return ext::details::fieldGet(fld_ptr, field); |
| } |
| |
| template <typename T, typename Derived_t, typename Field_t> |
| inline decltype(auto) |
| set(T &d, FieldId<Derived_t, Field_t> &field) |
| { |
| Extendible<Derived_t> const &ext = d; |
| FieldPtr fld_ptr = ext::details::FieldPtrGet(ext, *field.desc); |
| return ext::details::fieldSet(fld_ptr, field); |
| } |
| |
| ///////////////////////// |
| // ext::sizeOf - returns the size of a class + all extensions. |
| // |
| |
| template <typename Derived_t> |
| inline size_t |
| sizeOf(size_t size = sizeof(Derived_t)) |
| { |
| // add size of super extendibles |
| if constexpr (has_super_type<Derived_t>::value) { |
| size = ext::sizeOf<typename Derived_t::super_type>(size); |
| } else { |
| static_assert(std::is_same<decltype(Derived_t::schema), decltype(Extendible<Derived_t>::schema)>::value, |
| "ambiguous schema, Derived_t is missing super_type"); |
| } |
| |
| // add size of this extendible |
| if constexpr (std::is_base_of<Extendible<Derived_t>, Derived_t>::value) { |
| size = Extendible<Derived_t>::schema.fullSize(size); |
| // assert that the schema is not ambiguous. |
| } |
| return size; |
| } |
| |
| ///////////////////////// |
| // Ext Alloc & Init |
| // |
| template <class Derived_t> Extendible<Derived_t>::Extendible() |
| { |
| ink_assert(ext::details::areFieldsFinalized()); |
| // don't call callConstructor until the derived class is fully constructed. |
| ++schema.cnt_constructed; |
| } |
| |
| template <class Derived_t> Extendible<Derived_t>::~Extendible() |
| { |
| // assert callConstructors was called. |
| ink_assert(ext_loc); |
| schema.callDestructor(uintptr_t(this) + ext_loc); |
| ++schema.cnt_destructed; |
| ink_assert(schema.cnt_destructed <= schema.cnt_fld_constructed); |
| } |
| |
| /// tell this extendible where it's memory offset start is. Added to support inheriting from extendible classes |
| template <class Derived_t> |
| uintptr_t |
| Extendible<Derived_t>::initFields(uintptr_t start_ptr) |
| { |
| ink_assert(ext_loc == 0); |
| start_ptr = ROUNDUP(start_ptr, schema.alloc_align); // pad the previous struct, so that our fields are memaligned correctly |
| ink_assert(start_ptr - uintptr_t(this) < UINT16_MAX); |
| ext_loc = uint16_t(start_ptr - uintptr_t(this)); // store the offset to be used by ext::get and ext::set |
| ink_assert(ext_loc > 0); |
| schema.callConstructor(start_ptr); // construct all fields |
| return start_ptr + schema.alloc_size; // return the end of the extendible data |
| } |
| |
| namespace details |
| { |
| /// recursively init all extendible structures, and construct fields |
| template <typename Derived_t> |
| uintptr_t |
| initRecurseSuper(Derived_t &devired, uintptr_t tail_ptr /*= 0*/) |
| { |
| // track a tail pointer, that starts after the class, and interate each extendible block |
| if constexpr (has_super_type<Derived_t>::value) { |
| // init super type, move tail pointer |
| tail_ptr = initRecurseSuper<typename Derived_t::super_type>(devired, tail_ptr); |
| } |
| if constexpr (std::is_base_of<Extendible<Derived_t>, Derived_t>::value) { |
| // set start for this extendible block after the previous extendible, and move tail pointer to after this block |
| tail_ptr = devired.Extendible<Derived_t>::initFields(tail_ptr); |
| } |
| return tail_ptr; |
| } |
| |
| } // namespace details |
| |
| // allocate and initialize an extendible data structure |
| template <typename Derived_t, typename... Args> |
| Derived_t * |
| create(Args &&... args) |
| { |
| // don't instantiate until all Fields are finalized. |
| ink_assert(ext::details::areFieldsFinalized()); |
| |
| // calculate the memory needed for the class and all Extendible blocks |
| const size_t type_size = ext::sizeOf<Derived_t>(); |
| |
| // alloc one block of memory |
| Derived_t *ptr = static_cast<Derived_t *>(ats_memalign(alignof(Derived_t), type_size)); |
| |
| // construct (recursively super-to-sub class) |
| new (ptr) Derived_t(std::forward(args)...); |
| |
| // define extendible blocks start offsets (recursively super-to-sub class) |
| details::initRecurseSuper(*ptr, uintptr_t(ptr) + sizeof(Derived_t)); |
| return ptr; |
| } |
| |
| ///////////////////////// |
| // ExtDebugFormat - print a ascii chart of memory layout of a class |
| // |
| // Example layout of C -> B -> A, where C and A are extendible. All contain 1 int. |
| // See test_Extendible.cc for class implementation. |
| // 1A | EXT | 2b | ##________##__ |
| // 1A | BASE | 2b | __##__________ |
| // 1B | BASE | 2b | ____##________ |
| // 1C | EXT | 2b | ______##____## |
| // 1C | BASE | 2b | ________##____ |
| |
| template <typename T> |
| std::string |
| viewFormat(T const &t, uintptr_t _base_addr = 0, int _full_size = ext::sizeOf<T>()) |
| { |
| using namespace std; |
| stringstream ss; |
| if (_base_addr == 0) { |
| _base_addr = uintptr_t(&t); |
| } |
| int super_size = 0; |
| if constexpr (has_super_type<T>::value) { |
| ss << viewFormat<typename T::super_type>(t, _base_addr, _full_size); |
| super_size += sizeof(typename T::super_type); |
| } |
| |
| if constexpr (is_base_of<Extendible<T>, T>::value) { |
| Extendible<T> const *e = &t; |
| int ptr_start = uintptr_t(e) + Extendible<T>::getLocOffset() - _base_addr; |
| int ptr_end = ptr_start + sizeof(typename Extendible<T>::short_ptr_t); |
| int ext_start = e->getBegin() - _base_addr; |
| int ext_end = Extendible<T>::schema.fullSize(ext_start); |
| |
| ink_assert(ptr_end <= ext_start); |
| ink_assert(ext_end <= _full_size); |
| |
| ss << endl << setw(30) << typeid(T).name() << " | EXT | " << setw(5) << ext_end - ext_start << "b |"; |
| ss << string(ptr_start, '_').c_str(); |
| ss << string(ptr_end - ptr_start, '#').c_str(); |
| ss << string(ext_start - ptr_end, '_').c_str(); |
| ss << string(ext_end - ext_start, '#').c_str(); |
| ss << string(_full_size - ext_end, '_').c_str(); |
| |
| super_size += sizeof(Extendible<T>); |
| } |
| |
| int super_start = uintptr_t(&t) - _base_addr; |
| int member_start = super_start + super_size; |
| int member_end = super_start + sizeof(T); |
| |
| ink_assert(member_start <= member_end); |
| ink_assert(member_end <= _full_size); |
| |
| ss << endl << setw(30) << typeid(T).name() << " | BASE | " << setw(5) << sizeof(T) - super_size << "b |"; |
| ss << string(super_start, '_').c_str(); |
| ss << string(member_start - super_start, '_').c_str(); |
| ss << string(member_end - member_start, '#').c_str(); |
| ss << string(_full_size - member_end, '_').c_str(); |
| |
| return ss.str(); |
| } |
| |
| namespace details |
| { |
| std::string |
| ltrim(std::string const &str, const std::string &chars = "\t\n\v\f\r ") |
| { |
| std::string r(str); |
| r.erase(0, str.find_first_not_of(chars)); |
| return r; |
| } |
| } // namespace details |
| |
| template <typename T> |
| void |
| serialize(std::ostream &os, T const &t) |
| { |
| using namespace std; |
| size_t indent = os.width(); |
| os << endl << setw(indent) << "" << details::ltrim(typeid(T).name(), " 0123456789") << ": {" << endl; |
| indent += 2; |
| if constexpr (is_base_of<Extendible<T>, T>::value) { |
| if constexpr (has_super_type<T>::value) { |
| serialize<typename T::super_type>(os, t); |
| } |
| auto const &schema = T::schema; |
| size_t name_width = 0; |
| for (const auto &kv : schema.fields) { |
| name_width = max(name_width, kv.first.length()); |
| } |
| // TODO: clang-5 didn't like the use of a range based for here, change later |
| for (auto it = schema.fields.begin(); it != schema.fields.end(); ++it) { |
| auto &fname = it->first; |
| auto &field = it->second; |
| ink_assert(field.serializer); |
| os << setw(indent) << "" << setw(name_width) << right << fname << ": "; |
| field.serializer(os, details::FieldPtrGet(t, field)); |
| os << "," << endl; |
| } |
| } |
| indent -= 2; |
| os << setw(indent + 1) << "}"; |
| } |
| |
| template <typename T> |
| std::string |
| toString(T const &t) |
| { |
| std::stringstream ss; |
| ss.width(0); |
| serialize(ss, t); |
| return ss.str(); |
| } |
| } // namespace ext |
| |
| // C API |
| // |
| |
| FieldPtr |
| ExtFieldPtr(DerivedPtr derived, ExtFieldContext field_context, int *size /*= nullptr*/) |
| { |
| using namespace ext; |
| using namespace ext::details; |
| ink_assert(field_context); |
| ink_assert(derived); |
| FieldDesc const &desc = *static_cast<FieldDesc const *>(field_context); |
| if (size) { |
| *size = desc.size; |
| } |
| |
| Offest_t const *loc = (Offest_t const *)(uintptr_t(derived) + desc.ext_loc_offset); |
| return FieldPtr(uintptr_t(derived) + (*loc) + desc.field_offset); |
| } |