#pragma once /* * This module is included as a part of libSprawl * * Copyright (C) 2013 Jaedyn K. Draper * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifdef _WIN32 #pragma warning( push ) #pragma warning( disable: 4250 ) #endif #ifndef SPRAWL_REPLICABLE_MAX_DEPTH # define SPRAWL_REPLICABLE_MAX_DEPTH 256 #endif #include "Serializer.hpp" #include "BinarySerializer.hpp" #include "JSONSerializer.hpp" #include <map> namespace sprawl { namespace serialization { class ReplicationKey { public: ReplicationKey() : m_size(0) , m_data() { // } ReplicationKey( ReplicationKey const& other ); uint32_t size() const { return m_size; } int32_t& operator[](const uint32_t index) { return m_data[index]; } int32_t const& operator[](const uint32_t index) const { return m_data[index]; } void push_back(const int32_t val) { if(m_size == SPRAWL_REPLICABLE_MAX_DEPTH) { SPRAWL_ABORT_MSG_F("Replicable key depth (%d) exceeded. Redefine SPRAWL_REPLICABLE_MAX_DEPTH", SPRAWL_REPLICABLE_MAX_DEPTH); } m_data[m_size++] = val; } void pop_back() { --m_size; } int32_t& front() { return m_data[0]; } int32_t& back() { return m_data[m_size - 1]; } int32_t const& front() const { return m_data[0]; } int32_t const& back() const { return m_data[m_size - 1]; } bool empty() const { return (m_size == 0); } bool operator==( ReplicationKey const& other ) const; bool operator<( ReplicationKey const& other ) const; void clear() { m_size = 0; } SPRAWL_WARN_UNUSED_RESULT ErrorState<void> Serialize(SerializerBase& s); protected: friend struct RKeyHash; uint32_t m_size; int32_t m_data[SPRAWL_REPLICABLE_MAX_DEPTH]; }; struct RKeyHash { std::size_t operator()(ReplicationKey const& key) const { return sprawl::murmur3::Hash(key.m_data, key.m_size * sizeof(int32_t)); } }; bool StartsWith(ReplicationKey const& x, ReplicationKey const& y); template<typename T> class ReplicableBase : virtual public SerializerBase { protected: typedef sprawl::memory::StlWrapper<std::pair<ReplicationKey const, sprawl::String>> ReplicationMapAllocator; typedef sprawl::memory::StlWrapper<std::pair<sprawl::String const, int32_t>> StringToKeyAllocator; typedef sprawl::memory::StlWrapper<std::pair<int32_t const, sprawl::String>> KeyToStringAllocator; typedef std::map<ReplicationKey, sprawl::String, std::less<ReplicationKey>, ReplicationMapAllocator> ReplicationMap; typedef std::unordered_map<sprawl::String, int32_t, std::hash<sprawl::String>, std::equal_to<sprawl::String>, StringToKeyAllocator> StringToKeyMap; typedef std::unordered_map<int32_t, sprawl::String, std::hash<int32_t>, std::equal_to<int32_t>, KeyToStringAllocator> KeyToStringMap; ReplicableBase() : m_data() , m_diffs() , m_depth_tracker() , m_current_key() , m_name_index() , m_highest_name(1) , m_current_map_key() , m_keyindex() , m_serializer(nullptr) , m_baseline(nullptr) , m_marked(false) { } virtual ~ReplicableBase() { if(m_serializer) delete m_serializer; if(m_baseline) delete m_baseline; } public: virtual uint32_t GetVersion() override { return m_serializer->GetVersion(); } virtual bool IsValid() override { return true; } virtual bool Error() override { return false; } virtual size_t Size() override { return 0; } virtual void SetVersion(uint32_t i) override { m_serializer->SetVersion(i); } virtual bool IsMongoStream() override { return m_serializer->IsMongoStream(); } virtual ErrorState<void> Reset() override { this->m_data.clear(); this->m_depth_tracker.clear(); this->m_diffs.clear(); this->m_removed.clear(); this->m_name_index.clear(); this->m_current_map_key.clear(); this->m_highest_name = 1; this->m_current_key.clear(); this->m_keyindex.clear(); return ErrorState<void>(); } virtual bool IsReplicable() override { return true; } void StartArray( sprawl::String const& name, uint32_t& size, bool b ) override { PushKey(name, true); if(IsLoading()) { //A little less pleasant than would be ideal... //Go through the list of unconsumed keys to determine the size of the array post-merge. std::set<ReplicationKey, std::less<ReplicationKey>, sprawl::memory::StlWrapper<ReplicationKey>>::iterator it; bool shrunk = false; for(size_t i=0; i<size; i++) { if(!m_serializer->IsBinary()) { m_current_key.push_back(m_name_index[name]); } else { m_current_key.push_back(-1); } m_current_key.push_back((short)(i+1)); it = m_removed.find(m_current_key); if(it != m_removed.end()) { if(!shrunk) { //Elements deleted, size reduced. size = i; shrunk = true; } if(shrunk) { m_removed.erase(it); } } m_current_key.pop_back(); m_current_key.pop_back(); } typename ReplicableBase<T>::ReplicationMap::iterator it2; for(;;) { if(!m_serializer->IsBinary()) { m_current_key.push_back(m_name_index[name]); } else { m_current_key.push_back(-1); } m_current_key.push_back((short)(size+1)); it2 = m_diffs.find(m_current_key); if(it2 == m_diffs.end()) { m_current_key.pop_back(); m_current_key.pop_back(); break; } else { if(shrunk) { m_diffs.erase(it2); } else { size++; } } m_current_key.pop_back(); m_current_key.pop_back(); } } // if(IsLoading()) else if(!m_marked) { m_baseline->StartArray(name, size, b); } } void EndArray() override { PopKey(); if(!m_marked) { m_baseline->EndArray(); } } virtual uint32_t StartObject( sprawl::String const& name, bool b) override { PushKey(name); if(!m_marked) { m_baseline->StartObject(name, b); } return 0; } virtual uint32_t StartMap( sprawl::String const& name, bool b) override { PushKey(name); m_current_map_key.push_back(m_current_key); if(IsLoading()) { std::set<sprawl::String, std::less<sprawl::String>, sprawl::memory::StlWrapper<sprawl::String>> unique_subkeys; for(auto& kvp : m_diffs) { size_t size = m_current_key.size(); if(kvp.first.size() != size + 2) continue; if(!StartsWith(kvp.first, m_current_key)) continue; if(kvp.first[size] == -1) continue; unique_subkeys.insert(m_keyindex[kvp.first[size]]); } return unique_subkeys.size(); } else if(!m_marked) { m_baseline->StartMap(name, b); } return 0; } void EndMap() { m_current_map_key.pop_back(); PopKey(); if(!m_marked) { m_baseline->EndMap(); } } void EndObject() override { PopKey(); if(!m_marked) { m_baseline->EndObject(); } } sprawl::String GetNextKey() override { //Get the next key that belongs to our current element. //TODO: Have GetMap() just return a list of keys to grab that the calling function can iterate so we can avoid these string compares. for(auto& kvp : m_diffs) { size_t size = m_current_key.size(); if(kvp.first.size() != size + 2) continue; if(!StartsWith(kvp.first, m_current_key)) continue; if(kvp.first[size] == -1) continue; return m_keyindex[kvp.first[size]]; } return ""; } StringSet GetDeletedKeys(sprawl::String const& name) override { StringSet deleted_keys; PushKey(name); //Go through our removed keys and figure out which ones belong to this map. for(auto it = m_removed.begin(); it != m_removed.end(); ) { auto delete_it = it++; size_t size = m_current_key.size(); if(delete_it->size() != size + 2) continue; if(!StartsWith(*delete_it, m_current_key)) continue; if((*delete_it)[size] == -1) continue; deleted_keys.insert(m_keyindex[(*delete_it)[size]]); m_removed.erase(delete_it); } DecrementCurrentKeyCounter(); PopKey(); return std::move(deleted_keys); } protected: template<typename T2> SPRAWL_WARN_UNUSED_RESULT ErrorState<void> serialize_impl( T2* var, sprawl::String const& name, bool PersistToDB) { m_serializer->Reset(); PushKey(name); if(IsLoading()) { typename T::serializer_type serializer; SPRAWL_RETHROW(serializer % m_current_key); //Do nothing if this element hasn't changed. auto it = m_diffs.find(m_current_key); if(it != m_diffs.end()) { SPRAWL_RETHROW(m_serializer->Data(it->second)); SPRAWL_RETHROW((*m_serializer) % sprawl::serialization::prepare_data(*var, name, PersistToDB)); m_diffs.erase(it); } } else { SPRAWL_RETHROW((*m_serializer) % sprawl::serialization::prepare_data(*var, name, PersistToDB)); m_data.insert(std::make_pair(m_current_key, m_serializer->Str())); if(!m_marked) { SPRAWL_RETHROW((*m_baseline) % sprawl::serialization::prepare_data(*var, name, PersistToDB)); } } PopKey(); return ErrorState<void>(); } public: virtual SPRAWL_WARN_UNUSED_RESULT ErrorState<void> serialize(int* var, const uint32_t /*bytes*/, sprawl::String const& name, bool PersistToDB) override { SPRAWL_RETHROW(serialize_impl(var, name, PersistToDB)); return ErrorState<void>(); } virtual SPRAWL_WARN_UNUSED_RESULT ErrorState<void> serialize(long int* var, const uint32_t /*bytes*/, sprawl::String const& name, bool PersistToDB) override { SPRAWL_RETHROW(serialize_impl(var, name, PersistToDB)); return ErrorState<void>(); } virtual SPRAWL_WARN_UNUSED_RESULT ErrorState<void> serialize(long long int* var, const uint32_t /*bytes*/, sprawl::String const& name, bool PersistToDB) override { SPRAWL_RETHROW(serialize_impl(var, name, PersistToDB)); return ErrorState<void>(); } virtual SPRAWL_WARN_UNUSED_RESULT ErrorState<void> serialize(short int* var, const uint32_t /*bytes*/, sprawl::String const& name, bool PersistToDB) override { SPRAWL_RETHROW(serialize_impl(var, name, PersistToDB)); return ErrorState<void>(); } virtual SPRAWL_WARN_UNUSED_RESULT ErrorState<void> serialize(char* var, const uint32_t /*bytes*/, sprawl::String const& name, bool PersistToDB) override { SPRAWL_RETHROW(serialize_impl(var, name, PersistToDB)); return ErrorState<void>(); } virtual SPRAWL_WARN_UNUSED_RESULT ErrorState<void> serialize(float* var, const uint32_t /*bytes*/, sprawl::String const& name, bool PersistToDB) override { SPRAWL_RETHROW(serialize_impl(var, name, PersistToDB)); return ErrorState<void>(); } virtual SPRAWL_WARN_UNUSED_RESULT ErrorState<void> serialize(double* var, const uint32_t /*bytes*/, sprawl::String const& name, bool PersistToDB) override { SPRAWL_RETHROW(serialize_impl(var, name, PersistToDB)); return ErrorState<void>(); } virtual SPRAWL_WARN_UNUSED_RESULT ErrorState<void> serialize(long double* var, const uint32_t /*bytes*/, sprawl::String const& name, bool PersistToDB) override { SPRAWL_RETHROW(serialize_impl(var, name, PersistToDB)); return ErrorState<void>(); } virtual SPRAWL_WARN_UNUSED_RESULT ErrorState<void> serialize(bool* var, const uint32_t /*bytes*/, sprawl::String const& name, bool PersistToDB) override { SPRAWL_RETHROW(serialize_impl(var, name, PersistToDB)); return ErrorState<void>(); } virtual SPRAWL_WARN_UNUSED_RESULT ErrorState<void> serialize(unsigned int* var, const uint32_t /*bytes*/, sprawl::String const& name, bool PersistToDB) override { SPRAWL_RETHROW(serialize_impl(var, name, PersistToDB)); return ErrorState<void>(); } virtual SPRAWL_WARN_UNUSED_RESULT ErrorState<void> serialize(unsigned long int* var, const uint32_t /*bytes*/, sprawl::String const& name, bool PersistToDB) override { SPRAWL_RETHROW(serialize_impl(var, name, PersistToDB)); return ErrorState<void>(); } virtual SPRAWL_WARN_UNUSED_RESULT ErrorState<void> serialize(unsigned long long int* var, const uint32_t /*bytes*/, sprawl::String const& name, bool PersistToDB) override { SPRAWL_RETHROW(serialize_impl(var, name, PersistToDB)); return ErrorState<void>(); } virtual SPRAWL_WARN_UNUSED_RESULT ErrorState<void> serialize(unsigned short int* var, const uint32_t /*bytes*/, sprawl::String const& name, bool PersistToDB) override { SPRAWL_RETHROW(serialize_impl(var, name, PersistToDB)); return ErrorState<void>(); } virtual SPRAWL_WARN_UNUSED_RESULT ErrorState<void> serialize(unsigned char* var, const uint32_t /*bytes*/, sprawl::String const& name, bool PersistToDB) override { SPRAWL_RETHROW(serialize_impl(var, name, PersistToDB)); return ErrorState<void>(); } virtual SPRAWL_WARN_UNUSED_RESULT ErrorState<void> serialize(std::string* var, const uint32_t /*bytes*/, sprawl::String const& name, bool PersistToDB) override { SPRAWL_RETHROW(serialize_impl(var, name, PersistToDB)); return ErrorState<void>(); } virtual SPRAWL_WARN_UNUSED_RESULT ErrorState<void> serialize(sprawl::String* var, const uint32_t /*bytes*/, sprawl::String const& name, bool PersistToDB) override { SPRAWL_RETHROW(serialize_impl(var, name, PersistToDB)); return ErrorState<void>(); } protected: virtual void PushKey(sprawl::String const& name, bool /*forArray*/ = false) { if(!m_serializer->IsBinary() || (!m_current_map_key.empty() && m_current_key == m_current_map_key.back())) { if( m_name_index.count(name) == 0 ) { m_name_index[name] = m_highest_name++; } m_current_key.push_back(m_name_index[name]); } else { m_current_key.push_back(-1); } int16_t& depth = m_depth_tracker[m_current_key]; depth++; m_current_key.push_back( depth ); } virtual void PopKey() { m_current_key.pop_back(); m_current_key.pop_back(); } void DecrementCurrentKeyCounter() { m_current_key.pop_back(); m_depth_tracker[m_current_key]--; m_current_key.push_back(-1); } typename ReplicableBase<T>::ReplicationMap m_data; typename ReplicableBase<T>::ReplicationMap m_diffs; std::set<ReplicationKey, std::less<ReplicationKey>, sprawl::memory::StlWrapper<ReplicationKey>> m_removed; std::unordered_map<ReplicationKey, int16_t, RKeyHash, std::equal_to<ReplicationKey>, sprawl::memory::StlWrapper<std::pair<ReplicationKey const, int16_t>>> m_depth_tracker; ReplicationKey m_current_key; StringToKeyMap m_name_index; int32_t m_highest_name; std::vector<ReplicationKey, sprawl::memory::StlWrapper<ReplicationKey>> m_current_map_key; KeyToStringMap m_keyindex; T* m_serializer; T* m_baseline; bool m_marked; }; template<typename T> class ReplicableSerializer : public ReplicableBase<T>, public Serializer { public: ReplicableSerializer() : ReplicableBase<T>() , Serializer() , m_marked_data() { this->m_serializer = new T(false); this->m_baseline = new T(); } using Serializer::operator%; using Serializer::IsLoading; using ReplicableBase<T>::serialize; using ReplicableBase<T>::IsBinary; using ReplicableBase<T>::IsMongoStream; using ReplicableBase<T>::IsReplicable; using ReplicableBase<T>::IsValid; using ReplicableBase<T>::GetVersion; using ReplicableBase<T>::SetVersion; using ReplicableBase<T>::Size; using ReplicableBase<T>::StartObject; using ReplicableBase<T>::EndObject; using ReplicableBase<T>::StartArray; using ReplicableBase<T>::EndArray; using ReplicableBase<T>::StartMap; using ReplicableBase<T>::EndMap; using ReplicableBase<T>::GetNextKey; using ReplicableBase<T>::GetDeletedKeys; virtual ErrorState<void> Reset() override { ReplicableBase<T>::Reset(); m_marked_data.clear(); this->m_marked = false; return ErrorState<void>(); } virtual void Mark() { //Save what we just serialized to reference the next time we serialize, and return the serializer to a blank state. this->m_marked_data = std::move( this->m_data ); this->m_depth_tracker.clear(); this->m_diffs.clear(); this->m_removed.clear(); this->m_keyindex.clear(); this->m_marked = true; } virtual void Discard() { this->m_depth_tracker.clear(); this->m_diffs.clear(); this->m_removed.clear(); this->m_keyindex.clear(); this->m_data.clear(); } const char* Data() override { return Str().c_str(); } sprawl::String Str() override { //Str is a combination of Mark() and diff(). //Doing this enables the replicable serializer to follow the exact same API as a regular serializer, so they can be changed out easily. //But it does sacrifice some control over when these two things happen - most people will probably not care. sprawl::String ret = diff(); Mark(); return std::move(ret); } SPRAWL_WARN_UNUSED_RESULT ErrorState<sprawl::String> diff() { return GetDiff( this->m_data, this->m_marked_data ); } SPRAWL_WARN_UNUSED_RESULT ErrorState<sprawl::String> rdiff() { return GetDiff( this->m_marked_data, this->m_data ); } sprawl::String getBaselineStr() { return this->m_baseline->Str(); } protected: SPRAWL_WARN_UNUSED_RESULT ErrorState<sprawl::String> GetDiff( const typename ReplicableBase<T>::ReplicationMap& data, const typename ReplicableBase<T>::ReplicationMap& markedData ) { //Meat and potatoes of this replication system: Go through all of our keys and determine if they've changed since the last time we serialized data. //Ignore what hasn't, return what has. std::unordered_set<int32_t> used_indices; this->m_diffs.clear(); this->m_removed.clear(); this->m_keyindex.clear(); //Adds and changes for( auto& kvp : data ) { auto it = markedData.find(kvp.first); if( it == markedData.end() || kvp.second != it->second ) { this->m_diffs.insert(kvp); for(size_t idx = 0; idx < kvp.first.size(); idx += 2) { if(kvp.first[idx] != -1) { used_indices.insert(kvp.first[idx]); } } } } //Removes get their own special treatment for( auto& kvp : markedData ) { auto it = data.find(kvp.first); if( it == data.end() ) { this->m_removed.insert(kvp.first); for(size_t idx = 0; idx < kvp.first.size(); idx += 2) { if(kvp.first[idx] != -1) { used_indices.insert(kvp.first[idx]); } } } } T serializer; for( auto& kvp : this->m_name_index ) { if(used_indices.count(kvp.second) == 0) continue; this->m_keyindex[kvp.second] = kvp.first; } SPRAWL_RETHROW(serializer % sprawl::serialization::prepare_data(this->m_keyindex, "keyindex")); SPRAWL_RETHROW(serializer % sprawl::serialization::prepare_data(this->m_diffs, "diffs")); SPRAWL_RETHROW(serializer % sprawl::serialization::prepare_data(this->m_removed, "removed")); return serializer.Str(); } typename ReplicableBase<T>::ReplicationMap m_marked_data; }; template<typename T> class ReplicableDeserializer : public ReplicableBase<T>, public Deserializer { public: ReplicableDeserializer(sprawl::String const& data) : ReplicableBase<T>() , Deserializer() , m_bIsValid(true) { this->m_serializer = new T("", 0, false); T serializer(data); SPRAWL_ACTION_ON_ERROR(serializer % sprawl::serialization::prepare_data(this->m_keyindex, "keyindex"), m_bIsValid = false); SPRAWL_ACTION_ON_ERROR(serializer % sprawl::serialization::prepare_data(this->m_diffs, "diffs"), m_bIsValid = false); SPRAWL_ACTION_ON_ERROR(serializer % sprawl::serialization::prepare_data(this->m_removed, "removed"), m_bIsValid = false); for(auto& kvp : this->m_keyindex) { this->m_name_index[kvp.second] = kvp.first; } } ReplicableDeserializer(const char* data, size_t length) : ReplicableBase<T>() , Deserializer() , m_bIsValid(true) { this->m_serializer = new T("", 0, false); T serializer(data, length); SPRAWL_ACTION_ON_ERROR(serializer % sprawl::serialization::prepare_data(this->m_keyindex, "keyindex"), m_bIsValid = false); SPRAWL_ACTION_ON_ERROR(serializer % sprawl::serialization::prepare_data(this->m_diffs, "diffs"), m_bIsValid = false); SPRAWL_ACTION_ON_ERROR(serializer % sprawl::serialization::prepare_data(this->m_removed, "removed"), m_bIsValid = false); for(auto& kvp : this->m_keyindex) { this->m_name_index[kvp.second] = kvp.first; } } using Deserializer::operator%; using Deserializer::IsLoading; using ReplicableBase<T>::serialize; using ReplicableBase<T>::IsBinary; using ReplicableBase<T>::IsMongoStream; using ReplicableBase<T>::IsReplicable; virtual bool IsValid() override { return m_bIsValid; } using ReplicableBase<T>::GetVersion; using ReplicableBase<T>::SetVersion; using ReplicableBase<T>::Size; using ReplicableBase<T>::Reset; using ReplicableBase<T>::StartObject; using ReplicableBase<T>::EndObject; using ReplicableBase<T>::StartArray; using ReplicableBase<T>::EndArray; using ReplicableBase<T>::StartMap; using ReplicableBase<T>::EndMap; using ReplicableBase<T>::GetNextKey; using ReplicableBase<T>::GetDeletedKeys; const char* Data() override { return Str().c_str(); } sprawl::String Str() override { return ""; } SPRAWL_WARN_UNUSED_RESULT ErrorState<void> Data(sprawl::String const& data) { this->m_diffs.clear(); this->m_data.clear(); this->m_depth_tracker.clear(); T serializer(data); SPRAWL_RETHROW(serializer % sprawl::serialization::prepare_data(this->m_diffs, "diffs")); return ErrorState<void>(); } SPRAWL_WARN_UNUSED_RESULT ErrorState<void> Data(const char* data, size_t length) { this->m_diffs.clear(); this->m_data.clear(); this->m_depth_tracker.clear(); T serializer(data, length); SPRAWL_RETHROW(serializer % sprawl::serialization::prepare_data(this->m_diffs, "diffs")); return ErrorState<void>(); } protected: virtual SPRAWL_WARN_UNUSED_RESULT ErrorState<SerializerBase*> GetAnother(sprawl::String const& data) override { return new T(data, false); } virtual SPRAWL_WARN_UNUSED_RESULT ErrorState<SerializerBase*> GetAnother() override { SPRAWL_UNIMPLEMENTED_BASE_CLASS_METHOD; return nullptr; } bool m_bIsValid; }; class JSONSerializer; class BinarySerializer; extern template class ReplicableSerializer<JSONSerializer>; extern template class ReplicableDeserializer<JSONDeserializer>; extern template class ReplicableSerializer<BinarySerializer>; extern template class ReplicableDeserializer<BinaryDeserializer>; } } #ifdef _WIN32 #pragma warning( pop ) #endif
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#5 | 16768 | ShadauxCat |
Improvements to error handling in builds with exceptions disabled: - In debug builds or with SPRAWL_ERRORSTATE_STRICT enabled, ErrorState will output a message to stderr and terminate if Get() is called when an error flag is set. (In release buils or with SPRAWL_ERRORSTATE_PERMISSIVE defined, Get() will return junk memory in this case.) - In debug builds or with SPRAWL_ERRORSTATE_STRICT enabled, ErrorState will output a message to stderr and terminate if its destructor is called without checking the errorstate if an error is present (equivalent to an exception terminating the application if no catch() block is present for it). - On linux builds and when running "Analyze" through visual studio, a warning will be issued if any function returning ErrorState has its return value ignored. (This only applies to builds with exceptions not enabled; when exceptions are enabled no warning is issued) - Many functions that could return ErrorState were having their return values silently ignored in internal sprawl code so the user would not find out about errors if exceptions are disabled; now anything in sprawl code that calls a function returning ErrorState will either handle the error, or (in most cases) surface it back up to the user. - As a positive side-effect of the warnings for ignoring ErrorState, several constructors that were capable of throwing exceptions are no longer capable of doing so. #review-16769 |
||
#4 | 16378 | ShadauxCat |
New exception framework, phase one. Added new class, ErrorState, for handling errors when exceptions are disabled. When exceptions are enabled, ErrorState<T> is an alias for T. When they're disabled, ErrorState<T> additionally encodes an error code, and will have junk data (and probably a crash) if an error is returned and not checked before the data is used. ErrorState<T> is implicitly convertible to and from T, so applications that don't care about errors can code like they don't exist... Phase two will involve overloading operators on ErrorState so that things that return ErrorState can still function as much as possible like they do when exceptions are enabled. #review-16379 |
||
#3 | 14783 | ShadauxCat |
Style corrections (placement of const) #review-14784 |
||
#2 | 14216 | ShadauxCat |
-Moved some global sprawl::Strings into local scope in json serialization test because of initialization order issues in the memory allocator on mac. This is a temporary fix, and a real fix will come by making the pool allocator work in explicitly-sized pieces and putting all the code for those pieces into a cpp file. -Fixed a large number of warnings on mac/linux that were exposed by fixes to csbuild -Fixed compile errors on mac due to malloc and alloca not being defined, fixed by #include <stdlib.h> in appropriate places -Fixed mac os x trying to link against pthread erroneously -Provided os x implementation of time library -Fixed compile errors on os x due to std::unordered_map whining about the difference between an allocator that allocates std::pair<key, value> and one that allocates std::pair<key const, value>, which, of course, is that the allocator will be no different at all. -Fixed an actual issue where one unordered_map was allocating only key_type instead of std::pair<key_type, value_type> -Fixed a memory leak where coroutine objects would never be cleaned up because either Yield() or reactivate_() will never return (and thus never clean up their stack memory and thus never release any dynamic memory held in stack objects) depending on the situation - if the function runs to completion, reactivate_() never returns after calling swapcontext(); meanwhile, if the function does not run to completion, Yield() never returns after calling Pause(). This behavior will need to be well-documented because it will affect client-side code as well. Stack memory within a coroutine should not rely on RAII behavior. -Fixed compile failure when creating a StlWrapper with a const value_type #review-14217 |
||
#1 | 11496 | ShadauxCat | Initial checkin: Current states for csbuild and libSprawl |