diff options
Diffstat (limited to 'lib/base/namespace.cpp')
-rw-r--r-- | lib/base/namespace.cpp | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/lib/base/namespace.cpp b/lib/base/namespace.cpp new file mode 100644 index 0000000..4c5f4f6 --- /dev/null +++ b/lib/base/namespace.cpp @@ -0,0 +1,189 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "base/namespace.hpp" +#include "base/objectlock.hpp" +#include "base/debug.hpp" +#include "base/primitivetype.hpp" +#include "base/debuginfo.hpp" +#include "base/exception.hpp" +#include <sstream> + +using namespace icinga; + +template class std::map<icinga::String, std::shared_ptr<icinga::NamespaceValue> >; + +REGISTER_PRIMITIVE_TYPE(Namespace, Object, Namespace::GetPrototype()); + +/** + * Creates a new namespace. + * + * @param constValues If true, all values inserted into the namespace are treated as constants and can't be updated. + */ +Namespace::Namespace(bool constValues) + : m_ConstValues(constValues), m_Frozen(false) +{ } + +Value Namespace::Get(const String& field) const +{ + Value value; + if (!Get(field, &value)) + BOOST_THROW_EXCEPTION(ScriptError("Namespace does not contain field '" + field + "'")); + return value; +} + +bool Namespace::Get(const String& field, Value *value) const +{ + auto lock(ReadLockUnlessFrozen()); + + auto nsVal = m_Data.find(field); + + if (nsVal == m_Data.end()) { + return false; + } + + *value = nsVal->second.Val; + return true; +} + +void Namespace::Set(const String& field, const Value& value, bool isConst, const DebugInfo& debugInfo) +{ + ObjectLock olock(this); + + if (m_Frozen) { + BOOST_THROW_EXCEPTION(ScriptError("Namespace is read-only and must not be modified.", debugInfo)); + } + + std::unique_lock<decltype(m_DataMutex)> dlock (m_DataMutex); + + auto nsVal = m_Data.find(field); + + if (nsVal == m_Data.end()) { + m_Data[field] = NamespaceValue{value, isConst || m_ConstValues}; + } else { + if (nsVal->second.Const) { + BOOST_THROW_EXCEPTION(ScriptError("Constant must not be modified.", debugInfo)); + } + + nsVal->second.Val = value; + } +} + +/** + * Returns the number of elements in the namespace. + * + * @returns Number of elements. + */ +size_t Namespace::GetLength() const +{ + auto lock(ReadLockUnlessFrozen()); + + return m_Data.size(); +} + +bool Namespace::Contains(const String& field) const +{ + auto lock (ReadLockUnlessFrozen()); + + return m_Data.find(field) != m_Data.end(); +} + +void Namespace::Remove(const String& field) +{ + ObjectLock olock(this); + + if (m_Frozen) { + BOOST_THROW_EXCEPTION(ScriptError("Namespace is read-only and must not be modified.")); + } + + std::unique_lock<decltype(m_DataMutex)> dlock (m_DataMutex); + + auto it = m_Data.find(field); + + if (it == m_Data.end()) { + return; + } + + if (it->second.Const) { + BOOST_THROW_EXCEPTION(ScriptError("Constants must not be removed.")); + } + + m_Data.erase(it); +} + +/** + * Freeze the namespace, preventing further updates. + * + * This only prevents inserting, replacing or deleting values from the namespace. This operation has no effect on + * objects referenced by the values, these remain mutable if they were before. + */ +void Namespace::Freeze() { + ObjectLock olock(this); + + m_Frozen = true; +} + +std::shared_lock<std::shared_timed_mutex> Namespace::ReadLockUnlessFrozen() const +{ + if (m_Frozen.load(std::memory_order_relaxed)) { + return std::shared_lock<std::shared_timed_mutex>(); + } else { + return std::shared_lock<std::shared_timed_mutex>(m_DataMutex); + } +} + +Value Namespace::GetFieldByName(const String& field, bool, const DebugInfo& debugInfo) const +{ + auto lock (ReadLockUnlessFrozen()); + + auto nsVal = m_Data.find(field); + + if (nsVal != m_Data.end()) + return nsVal->second.Val; + else + return GetPrototypeField(const_cast<Namespace *>(this), field, false, debugInfo); /* Ignore indexer not found errors similar to the Dictionary class. */ +} + +void Namespace::SetFieldByName(const String& field, const Value& value, bool overrideFrozen, const DebugInfo& debugInfo) +{ + // The override frozen parameter is mandated by the interface but ignored here. If the namespace is frozen, this + // disables locking for read operations, so it must not be modified again to ensure the consistency of the internal + // data structures. + (void) overrideFrozen; + + Set(field, value, false, debugInfo); +} + +bool Namespace::HasOwnField(const String& field) const +{ + return Contains(field); +} + +bool Namespace::GetOwnField(const String& field, Value *result) const +{ + return Get(field, result); +} + +Namespace::Iterator Namespace::Begin() +{ + ASSERT(OwnsLock()); + + return m_Data.begin(); +} + +Namespace::Iterator Namespace::End() +{ + ASSERT(OwnsLock()); + + return m_Data.end(); +} + +Namespace::Iterator icinga::begin(const Namespace::Ptr& x) +{ + return x->Begin(); +} + +Namespace::Iterator icinga::end(const Namespace::Ptr& x) +{ + return x->End(); +} + |