ReflectedMap.h

// ReflectedMap.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2022

@import "IObject.h"
@import "ReflectedType.h"

namespace ceda
{
template <typename K, typename V> class xmap;

///////////////////////////////////////////////////////////////////////////////////////////////////
// xmap<Key,Value> functions

/*
For various types of Key and Payload, xmap<Key,Payload> is implemented as a B+Tree in 
application DLLs.  It is necessary to provide information to the reflection system to 
allow clients to access an xmap in a type invariant manner.

For example, PrintReflectedVariable makes use of the abstract iterator to iterate through
each (Key,Payload) pair of ay given xmap.
*/

/*
Abstract bidirectional iterator for an xmap<Key,Payload>

The following shows an example of forwards iteration through all the (Key,Payload)
elements of a given xmap 'xm'.

    void f(ptr<IXMap> xm)
    {
        ptr<IBiDirIterator> i = xm->GetIterator();
        ssize_t payloadOffset = xm->GetPayloadOffset();
        while(const void* key = i->GetElement())
        {
            const void* payload = (const octet_t*)key + payloadOffset;
            // todo use key,payload
            
            i->Next();
        }
        i->Close();     // iterator must be closed when no longer required
    }
*/

$interface+ IBiDirIterator
{
    // An iterator must always be closed when it is no longer required
    void Close();
    
    // Returns the address of the current element pointed to by this iterator, or else
    // returns nullptr if the iterator is positioned one before the first or one after the
    // last element.
    const void* GetElement();
    
    // Used to step the iterator forwards or backwards
    void Next();
    void Prev();
};

// Provides some useful functions for dealing with serialisable elements of some type
// todo: There is no need for 'self' so it would be more efficient to simply use a set 
// of function pointers
$interface+ IKeyType
{
    // Retrieve the size of a buffer that should be allocated for a variable
    //ssize_t size() const;
    
    // Write the value of the variable at 'addr' to the given archive
    void SerialiseVariable(Archive& ar, const void* addr) const;

    // Read the value of the variable from the given archive
    void DeserialiseVariable(InputArchive& ar, void* addr) const;
    
    void SkipVariable(InputArchive& ar) const;
    // Construct a variable at 'addr'
    //void Construct(void* addr) const;
    
    // Destruct a variable at 'addr'
    //void Destruct(void* addr) const;
};

$interface+ IXMap
{
    // A bit weird to think these as methods!
    void Construct();
    void CopyConstruct(const void* rhs);

    void CopyAssign(const void* rhs);
    
    ssize_t GetKeySize() const;
    
    // Look up the map with the given key.  
    // Returns address of payload if found otherwise nullptr
    const void* Get(const void* key) const;
    
    // Performs a look up on the xmap.  Reads the key from the archive, and uses
    // that key to index into the xmap.  Sets 'payload' to the address of the payload or
    // else nullptr if the key was not found
    void Search(InputArchive& ar, const void*& payload) const;

    // Must be called with an exclusive lock
    // Searches the map for the given key.
    // If no entry is found then inserts an entry using a default constructed payload value.
    // It is permissible to subsequently modify the payload in the same transaction without
    // needing to mark any persistable objects as dirty - because the B+Tree leaf 
    // node containing the payload is marked as dirty by this function.
    // Returns a pointer to the payload.  Never returns nullptr.
    void* Insert(const void* key);

    // Must be called with an exclusive lock
    // Reads a key value from the given archive, then searches the map for that key.
    // If no entry is found then inserts an entry using a default constructed payload value.
    // It is permissible to subsequently modify the payload in the same transaction without
    // needing to mark any persistable objects as dirty - because the B+Tree leaf 
    // node containing the payload is marked as dirty by this function.
    // Returns a pointer to the payload.  Never returns nullptr.
    void DeserialiseAndInsert(InputArchive& ar, void*& payload);
    
    // Total number of elements in the map
    ssize_t size() const;
    
    // Get the offset to the payload from the pointer to the key in any given 
    // (Key,Payload) pair
    ssize_t GetPayloadOffset() const;
    
    // Retrieves an iterator that can be used to iterate through all (Key,Payload) pairs
    // of the map
    // The returned iterator must be closed after it is no longer required.
    ptr<IBiDirIterator> GetIterator() const;

    ptr<IKeyType> GetKeyType() const;
};

///////////////////////////////////////////////////////////////////////////////////////////////////
// ReflectedMap

struct ReflectedMap
{
    inline ptr<IXMap> make_ptr_IXMap(void* self) const
    {
        cxAssert(self);
        cxAssert(IXMapFnTable);
        return make_ptr<IXMap>(self,IXMapFnTable); 
    }
    inline ptr<const IXMap> make_ptr_IXMap(const void* self) const
    {
        cxAssert(self);
        cxAssert(IXMapFnTable);
        return make_ptr<IXMap>(self,IXMapFnTable); 
    }
    
    // Conceptually like
    //      const xmap<K,V>& m = *data;
    //      Key k;
    //      ar >> k;
    //      return m.HasKey(k) ? &m[k] : nullptr;
    inline const void* Search(const void* data, InputArchive& ar) const
    {
        cxAssert(data);
        cxAssert(IXMapFnTable);
        cxAssert(IXMapFnTable->Search);
        const void* p;
        (IXMapFnTable->Search)(data, ar, p);
        return p;
    }

    // Conceptually like
    //      xmap<K,V>& m = *data;
    //      Key k;
    //      ar >> k;
    //      return &m[k];

    inline void* DeserialiseAndInsert(void* data, InputArchive& ar) const
    {
        cxAssert(data);
        cxAssert(IXMapFnTable);
        cxAssert(IXMapFnTable->DeserialiseAndInsert);
        void* p;
        (IXMapFnTable->DeserialiseAndInsert)(data, ar, p);
        cxAssert(p);
        return p;
    }

    const TypeOps& ops;
    ReflectedType key;
    ReflectedType value;
    const IXMap::FnTable* IXMapFnTable = nullptr;
};

@api xostream& operator<<(xostream& os, const ReflectedMap& r);

template<typename T> struct is_map { constexpr static bool value = false; };
template<typename K, typename V> struct is_map<xmap<K,V>> { constexpr static bool value = true; };

template<typename K, typename V>
inline const ReflectedMap& GetReflectedMap()
{
    static ReflectedMap s =
    {
        GetTypeOpsX<xmap<K,V>>(),
        GetReflectedType<K>(),
        GetReflectedType<V>(),
        &IXMap::Stubs<xmap<K,V>>::GetTable()
    };
    return s;
}

template<typename K, typename V>
struct GetReflectedType_class<xmap<K,V>> 
{
    static inline ReflectedType get()
    {
        ReflectedType r;
        r.tag = EReflectedTypeTag::Map;
        r.Map = &GetReflectedMap<K,V>();
        return r;
    }
};
} // namespace ceda