#include "String.hpp" #include "../memory/PoolAllocator.hpp" #include <stdint.h> #ifdef _WIN32 # pragma warning(disable: 4351) #endif namespace sprawl { String::Holder::Holder() : m_staticData() , m_dynamicData(nullptr) , m_data(nullptr) , m_refCount(1) , m_length(0) , m_hash(0) , m_hashComputed(false) { // } String::Holder::Holder(const char* data) : m_staticData() , m_dynamicData(nullptr) , m_data(nullptr) , m_refCount(1) , m_length(strlen(data)) , m_hash(0) , m_hashComputed(false) { if( m_length < staticDataSize ) { memcpy(m_staticData, data, m_length); m_staticData[m_length] = '\0'; m_data = m_staticData; } else { m_dynamicData = new char[m_length+1]; memcpy(m_dynamicData, data, m_length); m_dynamicData[m_length] = '\0'; m_data = m_dynamicData; } } String::Holder::Holder(const char* data, size_t length) : m_staticData() , m_dynamicData(nullptr) , m_data(nullptr) , m_refCount(1) , m_length(length) , m_hash(0) , m_hashComputed(false) { if( m_length < staticDataSize ) { memcpy(m_staticData, data, m_length); m_staticData[m_length] = '\0'; m_data = m_staticData; } else { m_dynamicData = new char[m_length+1]; memcpy(m_dynamicData, data, m_length); m_dynamicData[m_length] = '\0'; m_data = m_dynamicData; } } String::Holder::Holder(StringLiteral const& literal) : m_staticData() , m_dynamicData(nullptr) , m_data(literal.m_ptr) , m_refCount(1) , m_length(literal.m_length) , m_hash(0) , m_hashComputed(false) { // } void String::Holder::IncRef() { ++m_refCount; } bool String::Holder::DecRef() { return (--m_refCount == 0); } String::Holder* String::Holder::CreateHolder() { typedef memory::PoolAllocator<sizeof(String::Holder)> holderAlloc; String::Holder* ret = (String::Holder*)holderAlloc::alloc(); return ret; } void String::Holder::FreeHolder(Holder* holder) { typedef memory::PoolAllocator<sizeof(String::Holder)> holderAlloc; holder->~Holder(); holderAlloc::free(holder); } String::Holder::~Holder() { if(m_dynamicData) { delete[] m_dynamicData; } } /*static*/ String::Holder String::ms_emptyHolder(StringLiteral("")); String::String() : m_holder(&ms_emptyHolder) { } String::String(const char* const data) : m_holder(Holder::CreateHolder()) { new (m_holder) Holder(data); } String::String(const char* const data, size_t length) : m_holder(Holder::CreateHolder()) { new (m_holder) Holder(data, length); } String::String(String const& other) : m_holder(other.m_holder) { if(m_holder != &ms_emptyHolder) { m_holder->IncRef(); } } String::String(String&& other) : m_holder(other.m_holder) { //Don't change ref count here, it's staying the same because it's being *moved* other.m_holder = &ms_emptyHolder; } String::String(std::string const& stlString) : m_holder(Holder::CreateHolder()) { new (m_holder) Holder(stlString.c_str(), stlString.length()); } String::String(StringLiteral const& stringLiteral) : m_holder(Holder::CreateHolder()) { new (m_holder) Holder(stringLiteral); } String::~String() { if(m_holder != &ms_emptyHolder && m_holder->DecRef()) { Holder::FreeHolder(m_holder); } } sprawl::String String::operator+(sprawl::String const& other) const { sprawl::String ret; ret.m_holder = Holder::CreateHolder(); new (ret.m_holder) Holder(); size_t fullLength = m_holder->m_length + other.m_holder->m_length; if(fullLength < Holder::staticDataSize) { memcpy(ret.m_holder->m_staticData, m_holder->m_data, m_holder->m_length); memcpy(ret.m_holder->m_staticData + m_holder->m_length, other.m_holder->m_data, other.m_holder->m_length); ret.m_holder->m_staticData[fullLength] = '\0'; ret.m_holder->m_data = ret.m_holder->m_staticData; } else { ret.m_holder->m_dynamicData = new char[fullLength+1]; memcpy(ret.m_holder->m_dynamicData, m_holder->m_data, m_holder->m_length); memcpy(ret.m_holder->m_dynamicData + m_holder->m_length, other.m_holder->m_data, other.m_holder->m_length); ret.m_holder->m_dynamicData[fullLength] = '\0'; ret.m_holder->m_data = ret.m_holder->m_dynamicData; } ret.m_holder->m_length = fullLength; return std::move(ret); } sprawl::String String::operator+(const char* other) const { sprawl::String ret; ret.m_holder = Holder::CreateHolder(); new (ret.m_holder) Holder(); size_t length = strlen(other); size_t fullLength = m_holder->m_length + length; if(fullLength < Holder::staticDataSize) { memcpy(ret.m_holder->m_staticData, m_holder->m_data, m_holder->m_length); memcpy(ret.m_holder->m_staticData + m_holder->m_length, other, length); ret.m_holder->m_staticData[fullLength] = '\0'; ret.m_holder->m_data = ret.m_holder->m_staticData; } else { ret.m_holder->m_dynamicData = new char[fullLength+1]; memcpy(ret.m_holder->m_dynamicData, m_holder->m_data, m_holder->m_length); memcpy(ret.m_holder->m_dynamicData + m_holder->m_length, other, length); ret.m_holder->m_dynamicData[fullLength] = '\0'; ret.m_holder->m_data = ret.m_holder->m_dynamicData; } ret.m_holder->m_length = fullLength; return std::move(ret); } sprawl::String& String::operator+=(sprawl::String const& other) { Holder* newHolder = Holder::CreateHolder(); new (newHolder) Holder(); size_t fullLength = m_holder->m_length + other.m_holder->m_length; if(fullLength < Holder::staticDataSize) { memcpy(newHolder->m_staticData, m_holder->m_data, m_holder->m_length); memcpy(newHolder->m_staticData + m_holder->m_length, other.m_holder->m_data, other.m_holder->m_length); newHolder->m_staticData[fullLength] = '\0'; newHolder->m_data = newHolder->m_staticData; } else { newHolder->m_dynamicData = new char[fullLength+1]; memcpy(newHolder->m_dynamicData, m_holder->m_data, m_holder->m_length); memcpy(newHolder->m_dynamicData + m_holder->m_length, other.m_holder->m_data, other.m_holder->m_length); newHolder->m_dynamicData[fullLength] = '\0'; newHolder->m_data = newHolder->m_dynamicData; } newHolder->m_length = fullLength; Holder::FreeHolder(m_holder); m_holder = newHolder; return *this; } sprawl::String& String::operator+=(const char* other) { Holder* newHolder = Holder::CreateHolder(); new (newHolder) Holder(); size_t length = strlen(other); size_t fullLength = m_holder->m_length + length; if(fullLength < Holder::staticDataSize) { memcpy(newHolder->m_staticData, m_holder->m_data, m_holder->m_length); memcpy(newHolder->m_staticData + m_holder->m_length, other, length); newHolder->m_staticData[fullLength] = '\0'; newHolder->m_data = newHolder->m_staticData; } else { newHolder->m_dynamicData = new char[fullLength+1]; memcpy(newHolder->m_dynamicData, m_holder->m_data, m_holder->m_length); memcpy(newHolder->m_dynamicData + m_holder->m_length, other, length); newHolder->m_dynamicData[fullLength] = '\0'; newHolder->m_data = newHolder->m_dynamicData; } newHolder->m_length = fullLength; Holder::FreeHolder(m_holder); m_holder = newHolder; return *this; } String& String::operator=(String const& other) { if(m_holder != &ms_emptyHolder && m_holder->DecRef()) { Holder::FreeHolder(m_holder); } m_holder = other.m_holder; if(m_holder != &ms_emptyHolder) { m_holder->IncRef(); } return *this; } String& String::operator=(String&& other) { if(m_holder != &ms_emptyHolder && m_holder->DecRef()) { Holder::FreeHolder(m_holder); } m_holder = other.m_holder; other.m_holder = &ms_emptyHolder; return *this; } bool String::operator<(String const& other) const { if(m_holder == other.m_holder) { return false; } if(m_holder == &ms_emptyHolder) { return false; } if(other.m_holder == &ms_emptyHolder) { return true; } size_t length = other.m_holder->m_length < m_holder->m_length ? other.m_holder->m_length : m_holder->m_length; const char* const left = m_holder->m_data; const char* const right = other.m_holder->m_data; for(size_t i = 0; i < length; ++i) { if(left[i] == right[i]) continue; return left[i] < right[i]; } return m_holder->m_length < other.m_holder->m_length; } String String::GetOwned() const { if (m_holder->m_data == m_holder->m_dynamicData || m_holder->m_data == m_holder->m_staticData) { return *this; } return String(m_holder->m_data, m_holder->m_length); } std::string String::toStdString() const { if(m_holder == &ms_emptyHolder) { static std::string emptyStr; return emptyStr; } return std::string(m_holder->m_data, m_holder->m_length); } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#5 | 16052 | ShadauxCat |
- Changed default block size for concurrent queue to a more reasonable value - Changed some memory orders to memory_order_seq_cst when they don't actually need to be that to get around a bug in visual studio 2013 - debug builds assert when memory_order_acq_rel is used for a compare_exchange_strong (this is a standard library bug and is fixed in VS2015) - Added Event API - events are an alternative to condition variables that do not require a mutex and are guaranteed not to miss any signals, even if the signal comes while the thread is not listening for it. Unlike condition variables, however, they do not support broadcasting (and in fact, in general, are not safe to use with multiple threads listening for the same event simultaneously - though notifying on the same event is fine) - Rewrote ThreadManager around ConcurrentQueue and Event API so it is now lock-free. Also improved some behaviors of the staged thread manager operation so it now supports tasks that can be run on multiple stages via a bitmask. - Fixed an issue where the Coroutine copy constructor was calling the std::function constructor instead and another where initializing with a stack might try to call the wrong constructor and vice-versa - Fixed Coroutine never calling munmap() on its stack in linux and causing a memory leak - Added default arguments to time functions - Attempted to fix some issues with BinaryTree. Fixed some but not all. It's currently not suitable for use, sadly. - Logging Improvements: - - Added thread ID to logging - - Fixed some issues with category handlers - - Added backtraces - - Added the following additional log macros: - - - LOG_IF - - - LOG_EVERY_N - - - LOG_FIRST_N - - - LOG_IF_EVERY_N - - - LOG_IF_FIRST_N - - - LOG_ASSERT - - Added the ability to set extra info callbacks to get data such as script backtraces - - Removed the thread-related handlers and replaced them with RunHandler_Threaded and RunHandler_ThreadManager, which will enable any passed-in handler to be run in a threaded fashion - Removed StaticPoolAllocator and renamed DynamicPoolAllocator to PoolAllocator; adjusted unit tests accordingly - PoolAllocator now allocates its pool with mmap and VirtualAlloc, rather than with malloc - Fixed a bug with Vector copy assignment operator - Improved performance of StringBuilder considerably for cases where there are no modifier strings - Removed Copy-On-Write behavior of JSONToken as it was broken; copies are now performed with explicit DeepCopy() and ShallowCopy() functions - Fixed some parser bugs with JSONToken - Added iteration to JSONToken to iterate its children - Fixed crash when reading a negative number of bytes from a file - Changed StringBuilder to favor speed instead of memory by default - Added some performance unit tests for JSON token #review-16053 |
||
#4 | 14816 | ShadauxCat |
Filled in some more filesystem functions, added appropriate unit tests. Only a few remain. #review-14817 |
||
#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 |