diff options
Diffstat (limited to 'source/core')
| -rw-r--r-- | source/core/slang-castable-list-impl.cpp | 178 | ||||
| -rw-r--r-- | source/core/slang-castable-list-impl.h | 83 | ||||
| -rw-r--r-- | source/core/slang-castable-list.h | 57 | ||||
| -rw-r--r-- | source/core/slang-destroyable.h | 9 | ||||
| -rw-r--r-- | source/core/slang-lazy-castable-list.cpp | 163 | ||||
| -rw-r--r-- | source/core/slang-lazy-castable-list.h | 66 |
6 files changed, 547 insertions, 9 deletions
diff --git a/source/core/slang-castable-list-impl.cpp b/source/core/slang-castable-list-impl.cpp new file mode 100644 index 000000000..ee10ea995 --- /dev/null +++ b/source/core/slang-castable-list-impl.cpp @@ -0,0 +1,178 @@ +// slang-castable-list-impl.cpp +#include "slang-castable-list-impl.h" + +namespace Slang { + + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! UnknownCastableAdapter !!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +void* UnknownCastableAdapter::castAs(const Guid& guid) +{ + if (auto intf = getInterface(guid)) + { + return intf; + } + if (auto obj = getObject(guid)) + { + return obj; + } + + if (m_found && guid == m_foundGuid) + { + return m_found; + } + + ComPtr<ISlangUnknown> cast; + if (SLANG_SUCCEEDED(m_contained->queryInterface(guid, (void**)cast.writeRef())) && cast) + { + // Save the interface in the cache + m_found = cast; + m_foundGuid = guid; + + return cast; + } + return nullptr; +} + +void* UnknownCastableAdapter::getInterface(const Guid& guid) +{ + if (guid == ISlangUnknown::getTypeGuid() || + guid == ICastable::getTypeGuid()) + { + return static_cast<ICastable*>(this); + } + return nullptr; +} + +void* UnknownCastableAdapter::getObject(const Guid& guid) +{ + SLANG_UNUSED(guid); + return nullptr; +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CastableList !!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +CastableList::~CastableList() +{ + for (auto castable : m_list) + { + castable->release(); + } +} + +void* CastableList::castAs(const Guid& guid) +{ + if (auto intf = getInterface(guid)) + { + return intf; + } + return getObject(guid); +} + +void* CastableList::getInterface(const Guid& guid) +{ + if (guid == ISlangUnknown::getTypeGuid() || + guid == ICastable::getTypeGuid() || + guid == ICastableList::getTypeGuid()) + { + return static_cast<ICastableList*>(this); + } + return nullptr; +} + +void* CastableList::getObject(const Guid& guid) +{ + SLANG_UNUSED(guid); + return nullptr; +} + +void* CastableList::find(const Guid& guid) +{ + for (ICastable* castable : m_list) + { + if (auto ptr = castable->castAs(guid)) + { + return ptr; + } + } + return nullptr; +} + +Index CastableList::indexOf(ICastable* castable) +{ + const Count count = m_list.getCount(); + for (Index i = 0; i < count; ++i) + { + ICastable* cur = m_list[i]; + if (cur == castable) + { + return i; + } + } + return -1; +} + +void CastableList::add(ICastable* castable) +{ + SLANG_ASSERT(castable); + castable->addRef(); + m_list.add(castable); +} + +void CastableList::removeAt(Index i) +{ + auto castable = m_list[i]; + m_list.removeAt(i); + castable->release(); +} + +void CastableList::clear() +{ + for (auto castable : m_list) + { + castable->release(); + } + m_list.clear(); +} + +void CastableList::addUnknown(ISlangUnknown* unk) +{ + // If it has ICastable interface we can just add as that + { + ComPtr<ICastable> castable; + if (SLANG_SUCCEEDED(unk->queryInterface(ICastable::getTypeGuid(), (void**)castable.writeRef())) && castable) + { + return add(castable); + } + } + + // Wrap it in an adapter + IUnknownCastableAdapter* adapter = new UnknownCastableAdapter(unk); + add(adapter); +} + +Index CastableList::indexOfUnknown(ISlangUnknown* unk) +{ + SLANG_ASSERT(unk); + // If it has a castable interface we can just look for that + { + ComPtr<ICastable> castable; + if (SLANG_SUCCEEDED(unk->queryInterface(ICastable::getTypeGuid(), (void**)castable.writeRef())) && castable) + { + return indexOf(castable); + } + } + + // It's not derived from ICastable, so can only be in list via an adapter + const Count count = m_list.getCount(); + for (Index i = 0; i < count; ++i) + { + auto adapter = as<IUnknownCastableAdapter>(m_list[i]); + if (adapter && adapter->getContained() == unk) + { + return i; + } + } + return -1; +} + +} // namespace Slang diff --git a/source/core/slang-castable-list-impl.h b/source/core/slang-castable-list-impl.h new file mode 100644 index 000000000..6c03855f2 --- /dev/null +++ b/source/core/slang-castable-list-impl.h @@ -0,0 +1,83 @@ +// slang-castable-list.h +#ifndef SLANG_CASTABLE_LIST_IMPL_H +#define SLANG_CASTABLE_LIST_IMPL_H + +#include "slang-castable-list.h" + +#include "../../slang-com-helper.h" +#include "../../slang-com-ptr.h" + +#include "../core/slang-com-object.h" + +namespace Slang +{ + +/* An adapter such that types which aren't derived from ICastable, can be used as such. + +With the following caveats. +* the interfaces/objects of the adapter are checked *first*, so IUnknown will always be for the adapter +* assumes when doing a queryInterface on the contained item, it will remain in scope when released (this is *not* strict COM) +*/ +class UnknownCastableAdapter : public ComBaseObject, public IUnknownCastableAdapter +{ +public: + SLANG_COM_BASE_IUNKNOWN_ALL + + // ICastable + SLANG_NO_THROW void* SLANG_MCALL castAs(const Guid& guid) SLANG_OVERRIDE; + + // IUnknownCastableAdapter + virtual SLANG_NO_THROW ISlangUnknown* SLANG_MCALL getContained() SLANG_OVERRIDE { return m_contained; } + + UnknownCastableAdapter(ISlangUnknown* unk): + m_contained(unk) + { + SLANG_ASSERT(unk); + } + +protected: + void* getInterface(const Guid& guid); + void* getObject(const Guid& guid); + + ComPtr<ISlangUnknown> m_contained; + + // We hold a cache for a single lookup to make things a little faster + void* m_found = nullptr; + Guid m_foundGuid; +}; + +/* Implementation of the ICastableList interface. +Is atomic reference counted*/ +class CastableList : public ComBaseObject, public ICastableList +{ +public: + SLANG_COM_BASE_IUNKNOWN_ALL + + // ICastable + SLANG_NO_THROW void* SLANG_MCALL castAs(const Guid& guid) SLANG_OVERRIDE; + + // ICastableList + virtual Count SLANG_MCALL getCount() SLANG_OVERRIDE { return m_list.getCount(); } + virtual ICastable* SLANG_MCALL getAt(Index i) SLANG_OVERRIDE { return m_list[i]; } + virtual void SLANG_MCALL add(ICastable* castable) SLANG_OVERRIDE; + virtual void SLANG_MCALL addUnknown(ISlangUnknown* unk) SLANG_OVERRIDE; + virtual void SLANG_MCALL removeAt(Index i) SLANG_OVERRIDE; + virtual void SLANG_MCALL clear() SLANG_OVERRIDE; + virtual Index SLANG_MCALL indexOf(ICastable* castable) SLANG_OVERRIDE; + virtual Index SLANG_MCALL indexOfUnknown(ISlangUnknown* unk) SLANG_OVERRIDE; + virtual void* SLANG_MCALL find(const Guid& guid) SLANG_OVERRIDE; + virtual ICastable*const* SLANG_MCALL getBuffer() SLANG_OVERRIDE { return m_list.getBuffer(); } + + /// Dtor + virtual ~CastableList(); + +protected: + void* getInterface(const Guid& guid); + void* getObject(const Guid& guid); + + List<ICastable*> m_list; +}; + +} // namespace Slang + +#endif diff --git a/source/core/slang-castable-list.h b/source/core/slang-castable-list.h new file mode 100644 index 000000000..942f53376 --- /dev/null +++ b/source/core/slang-castable-list.h @@ -0,0 +1,57 @@ +// slang-castable-list.h +#ifndef SLANG_CASTABLE_LIST_H +#define SLANG_CASTABLE_LIST_H + +#include "../core/slang-basic.h" + +#include "../core/slang-destroyable.h" + +namespace Slang +{ + +/* A useful interface for handling lists of castable interfaces. Cannot hold nullptr */ +class ICastableList : public ICastable +{ + SLANG_COM_INTERFACE(0x335f3d40, 0x934c, 0x40dc, { 0xb5, 0xe1, 0xf7, 0x6e, 0x40, 0x3, 0x62, 0x5 }) + + /// Get the count of all interfaces held in the list + virtual Count SLANG_MCALL getCount() = 0; + /// Get the interface at the specified index + virtual ICastable* SLANG_MCALL getAt(Index i) = 0; + /// Add an item to the list + virtual void SLANG_MCALL add(ICastable* unk) = 0; + /// Add IUnknown, will cast to ICastable and if that's not possible will wrap + virtual void SLANG_MCALL addUnknown(ISlangUnknown* unk) = 0; + /// Remove item at index, remaining items stay in the same order + virtual void SLANG_MCALL removeAt(Index i) = 0; + /// Clear the list + virtual void SLANG_MCALL clear() = 0; + /// Find the first index of castable, or -1 if not found + virtual Index SLANG_MCALL indexOf(ICastable* castable) = 0; + /// Find the index interface (handling wrapping if necessary) + virtual Index SLANG_MCALL indexOfUnknown(ISlangUnknown* unk) = 0; + /// Find the first item that casts to non null + virtual void* SLANG_MCALL find(const Guid& guid) = 0; + /// Access the internal buffer (any mutation can invalidate this value) + virtual ICastable*const* SLANG_MCALL getBuffer() = 0; +}; + +// Simply finding things in a ICastableList +template <typename T> +SLANG_FORCE_INLINE T* find(ICastableList* list) +{ + return reinterpret_cast<T*>(list->find(T::getTypeGuid())); +} + +/* Adapter interface to make a non castable types work as ICastable */ +class IUnknownCastableAdapter : public ICastable +{ + SLANG_COM_INTERFACE(0x8b4aad81, 0x4934, 0x4a67, { 0xb2, 0xe2, 0xe9, 0x17, 0xfc, 0x29, 0x12, 0x54 }); + + /// When using the adapter, this provides a way to directly get the internal no ICastable type + virtual SLANG_NO_THROW ISlangUnknown* SLANG_MCALL getContained() = 0; +}; + +} // namespace Slang + +#endif diff --git a/source/core/slang-destroyable.h b/source/core/slang-destroyable.h index bf373ba33..390b16e7c 100644 --- a/source/core/slang-destroyable.h +++ b/source/core/slang-destroyable.h @@ -9,15 +9,6 @@ namespace Slang { -/* Adapter interface to make a non castable type work as ICastable */ -class IUnknownCastableAdapter : public ICastable -{ - SLANG_COM_INTERFACE(0x8b4aad81, 0x4934, 0x4a67, { 0xb2, 0xe2, 0xe9, 0x17, 0xfc, 0x29, 0x12, 0x54 } ); - - /// When using the adapter, this provides a way to directly get the internal no ICastable type - virtual SLANG_NO_THROW ISlangUnknown* SLANG_MCALL getContained() = 0; -}; - /* An interface that allows for an object to implement 'destruction'. A destroyed interface/object should release any other contained references. Behavior of an interface that is IDestroyed should be defined on the interface. Typically diff --git a/source/core/slang-lazy-castable-list.cpp b/source/core/slang-lazy-castable-list.cpp new file mode 100644 index 000000000..d9f0090d5 --- /dev/null +++ b/source/core/slang-lazy-castable-list.cpp @@ -0,0 +1,163 @@ +// slang-lazy-castable-list.cpp +#include "slang-lazy-castable-list.h" + +#include "slang-castable-list-impl.h" + +namespace Slang { + +void LazyCastableList::removeAt(Index index) +{ + SLANG_ASSERT(index >= 0 && index < getCount()); + + switch (m_state) + { + case State::None: break; + case State::One: + { + m_state = State::None; + m_castable.setNull(); + break; + } + case State::List: + { + static_cast<ICastableList*>(m_castable.get())->removeAt(index); + break; + } + } +} + +void LazyCastableList::clear() +{ + if (m_state == State::List) + { + auto list = static_cast<ICastableList*>(m_castable.get()); + list->clear(); + } + else + { + m_state = State::None; + m_castable.setNull(); + } +} + +void LazyCastableList::clearAndDeallocate() +{ + m_state = State::None; + m_castable.setNull(); +} + +Count LazyCastableList::getCount() const +{ + switch (m_state) + { + case State::None: return 0; + case State::One: return 1; + default: + case State::List: return static_cast<ICastableList*>(m_castable.get())->getCount(); + } +} + +void LazyCastableList::add(ICastable* castable) +{ + SLANG_ASSERT(castable); + if (m_state == State::None) + { + m_castable = castable; + m_state = State::One; + } + else + { + requireList()->add(castable); + } +} + +ICastableList* LazyCastableList::requireList() +{ + switch (m_state) + { + case State::None: + { + m_castable = new CastableList; + m_state = State::List; + break; + } + case State::One: + { + // Turn into a list + auto list = new CastableList; + list->add(m_castable); + m_castable = list; + m_state = State::List; + break; + } + default: break; + } + SLANG_ASSERT(m_state == State::List); + return static_cast<ICastableList*>(m_castable.get()); +} + +ICastableList* LazyCastableList::getList() +{ + return (m_state == State::None) ? nullptr : requireList(); +} + +void* LazyCastableList::find(const Guid& guid) +{ + for (auto castable : getView()) + { + if (auto ptr = castable->castAs(guid)) + { + return ptr; + } + } + return nullptr; +} + +ConstArrayView<ICastable*> LazyCastableList::getView() const +{ + switch (m_state) + { + case State::None: return ConstArrayView<ICastable*>(); + case State::One: return ConstArrayView<ICastable*>((ICastable*const*)&m_castable, 1); + default: + case State::List: + { + auto list = static_cast<ICastableList*>(m_castable.get()); + return ConstArrayView<ICastable*>(list->getBuffer(), list->getCount()); + } + } +} + +Index LazyCastableList::indexOf(ICastable* castable) const +{ + return getView().indexOf(castable); +} + +Index LazyCastableList::indexOfUnknown(ISlangUnknown* unk) const +{ + // Try as a ICastable first + { + ComPtr<ICastable> castable; + if (SLANG_SUCCEEDED(unk->queryInterface(ICastable::getTypeGuid(), (void**)castable.writeRef())) && castable) + { + return indexOf(castable); + } + } + + // It's not derived from ICastable, so can only be in list via an adapter + const auto view = getView(); + + const Count count = view.getCount(); + for (Index i = 0; i < count; ++i) + { + auto adapter = as<IUnknownCastableAdapter>(view[i]); + if (adapter && adapter->getContained() == unk) + { + return i; + } + } + + return -1; +} + +} // namespace Slang diff --git a/source/core/slang-lazy-castable-list.h b/source/core/slang-lazy-castable-list.h new file mode 100644 index 000000000..4f73ce9ca --- /dev/null +++ b/source/core/slang-lazy-castable-list.h @@ -0,0 +1,66 @@ +// slang-lazy-castable-list.h +#ifndef SLANG_LAZY_CASTABLE_LIST_H +#define SLANG_LAZY_CASTABLE_LIST_H + +#include "slang-castable-list.h" + +#include "../../slang-com-ptr.h" + +namespace Slang +{ + +/* Sometimes the overhead around having a potential list of items that might often be +empty or only contain a single element is considerable. + +The `LazyCastableList` provides functionality around ICastableList to minimize allocation, or the +need to allocate an ICastableList. It does this by tracking state in m_state, and varying the +meaning of m_castable. + +* State::None - there is no list +* State::One - there is a single entry, that is held in m_castable +* State::List - m_castable is actually ICastableList, and holds the contents +*/ +class LazyCastableList +{ +public: + /// Add a castable to the lsit + void add(ICastable* castable); + /// Return the amount of items in the list + Count getCount() const; + /// Remove the item at the specified index + void removeAt(Index index); + /// Clear the list + void clear(); + /// Clear and deallocate the list + void clearAndDeallocate(); + /// Find the first item that castAs(guid) produces a result + void* find(const Guid& guid); + /// Get the contents of the list as a view + ConstArrayView<ICastable*> getView() const; + /// Get the index of castable in the list. Returns -1 if not found + Index indexOf(ICastable* castable) const; + /// Get the index of unk. Handles if the wrapping has been used. + Index indexOfUnknown(ISlangUnknown* unk) const; + + /// Will always return a valid ICastableList + ICastableList* requireList(); + /// Will return nullptr if the list is empty, else it returns a ICastableList holding the elements + ICastableList* getList(); + +protected: + enum class State + { + None, + One, + List, + }; + // A state is not *strictly* necessary, because we can always determine what m_castable is + // with a castAs. But doing so is not exactly fast, and using the state makes some code simpler + // additionally. + State m_state = State::None; + ComPtr<ICastable> m_castable; +}; + +} // namespace Slang + +#endif |
