String.hpp #12

  • //
  • guest/
  • ShadauxCat/
  • Sprawl/
  • Mainline/
  • string/
  • String.hpp
  • View
  • Commits
  • Open Download .zip Download (8 KB)
#pragma once

#ifndef SPRAWL_STRING_NO_STL_COMPAT
#	include <string>
#	include "../hash/Murmur3.hpp"
#endif

#include "StringCommon.hpp"
#include "StringBuilder.hpp"
#include <stdint.h>
#include <unordered_map>

#include <atomic>

namespace sprawl
{
	class StringLiteral;

	class String
	{
	public:
		class Holder
		{
		protected:
			friend class String;

			Holder();
			Holder(const char* data);
			Holder(const char* data, size_t length);
			Holder(StringLiteral const& literal);

			void IncRef();
			bool DecRef();

			static Holder* CreateHolder();
			static void FreeHolder(Holder* holder);

			~Holder();

			inline size_t GetHash() const
			{
				if(m_hashComputed)
				{
					return m_hash;
				}

				m_hash = sprawl::murmur3::Hash( m_data, m_length );
				m_hashComputed = true;
				return m_hash;
			}

			static constexpr size_t staticDataSize = SPRAWL_STATIC_STRING_SIZE;

			char m_staticData[staticDataSize];
			char* m_dynamicData;
			const char* m_data;
			std::atomic<int> m_refCount;
			size_t m_length;
			mutable size_t m_hash;
			mutable bool m_hashComputed;
		private:
			Holder(Holder const& other);
			Holder& operator=(Holder const& other);
		};

		String();
		String(const char* const data);
		String(const char* const data, size_t length);

		String(String const& other);
		String(String&& other);

#ifndef SPRAWL_STRING_NO_STL_COMPAT
		String(std::string const& stlString);
#endif

		String(StringLiteral const& stringLiteral);

		~String();

		inline size_t GetHash() const
		{
			return m_holder->GetHash();
		}

#ifndef SPRAWL_STRING_NO_STL_COMPAT
		std::string toStdString() const;
#endif

		const char* c_str() const
		{
			return m_holder->m_data;
		}

		size_t length() const
		{
			return m_holder->m_length;
		}

		String& operator=(String const& other);
		String& operator=(String&& other);

		inline bool operator==(String const& other) const
		{
			return (m_holder == other.m_holder) || ((m_holder->m_length == other.m_holder->m_length) && (SPRAWL_MEMCMP(m_holder->m_data, other.m_holder->m_data, m_holder->m_length) == 0));
		}

		bool operator!=(String const& other) const
		{
			return !operator==(other);
		}

		sprawl::String operator+(sprawl::String const& other) const;

		sprawl::String operator+(const char* other) const;

		sprawl::String& operator+=(sprawl::String const& other);

		sprawl::String& operator+=(const char* other);

		bool empty() const
		{
			return m_holder->m_length == 0;
		}

		bool operator<(String const& other) const;

		char const& operator[](size_t index) const
		{
			return m_holder->m_data[index];
		}

		String GetOwned() const;

		template<typename... Params>
		String format(Params const& ...params)
		{
#if !SPRAWL_STRINGBUILDER_FAVOR_SPEED_OVER_MEMORY
			StringBuilder nullBuilder(0);

			ExecuteFormat(nullBuilder, params...);

			size_t const length = nullBuilder.Size();

			StringBuilder builder(length, false);
#else
			size_t const startingLength = m_holder->m_length * 2 + 1;

			StringBuilder builder(startingLength, true);
#endif

			ExecuteFormat(builder, params...);

			return builder.Str();
		}

	private:
		template<int idx, typename... Params>
		class FormatHelper;

		template<int idx>
		class FormatHelper<idx>
		{
		public:
			void Append(int pos, StringBuilder& builder, char* modifiers)
			{
				(void)(pos);
				(void)(modifiers);
				builder << "< ??? >";
			}
		};

		template<int idx, typename T>
		class FormatHelper<idx, T> : public FormatHelper<idx + 1>
		{
		public:
			typedef FormatHelper<idx + 1> Base;
			FormatHelper(T const& val)
				: Base()
				, m_value(val)
			{
				//
			}

			void Append(int pos, StringBuilder& builder, char* modifiers)
			{
				if(pos == idx)
				{
					builder.AppendElementToBuffer(m_value, modifiers);
				}
				else
				{
					Base::Append(pos, builder, modifiers);
				}
			}

		private:
			T const& m_value;
		};

		template<int idx, typename T, typename... Params>
		class FormatHelper<idx, T, Params...> : public FormatHelper<idx + 1, Params...>
		{
		public:
			typedef FormatHelper<idx + 1, Params...> Base;
			FormatHelper(T const& val, Params const& ...values)
				: Base(values...)
				, m_value(val)
			{
				//
			}

			void Append(int pos, StringBuilder& builder, char* modifiers)
			{
				if(pos == idx)
				{
					builder.AppendElementToBuffer(m_value, modifiers);
				}
				else
				{
					Base::Append(pos, builder, modifiers);
				}
			}

		private:
			T const& m_value;
		};

		template<typename... Params>
		void ExecuteFormat(	StringBuilder& builder, Params const& ...params)
		{
			FormatHelper<0, Params...> helper(params...);

			int curIdx = -1;
			size_t lastIdx = 0;

			bool inBracket = false;

			char modifiers[10];
			size_t modifierPos = 0;
			bool inModifiers = false;

			size_t const formatLength = m_holder->m_length;
			char const* const data = m_holder->m_data;

			for(size_t i = 0; i < formatLength; ++i)
			{
				const char c = data[i];
				if(c == '{')
				{
					if(inBracket)
					{
						builder << '{';
					}
					inBracket = !inBracket;
					continue;
				}

				if(inBracket)
				{
					if(c == '}')
					{
						modifiers[modifierPos] = '\0';

						if(curIdx == -1)
						{
							helper.Append(lastIdx, builder, modifiers);
							++lastIdx;
						}
						else
						{
							helper.Append(curIdx, builder, modifiers);
							lastIdx = curIdx + 1;
						}
						modifiers[0] = '\0';
						modifierPos = 0;
						curIdx = -1;
						inBracket = false;
						inModifiers = false;
					}
					else if(c == ':' && !inModifiers)
					{
						inModifiers = true;
					}
					else if(inModifiers)
					{
						modifiers[modifierPos++] = c;
					}
					else if(isdigit(c))
					{
						if(curIdx == -1)
						{
							curIdx = c - '0';
						}
						else
						{
							curIdx *= 10;
							curIdx += c - '0';
						}
					}
					else
					{
						builder << '{';
						if(curIdx != -1)
						{
							builder << curIdx;
						}
						builder << c;
						inBracket = false;
					}
					continue;
				}

				builder << c;
			}
		}

	private:
		Holder* m_holder;
		static Holder ms_emptyHolder;
	};

	class StringLiteral
	{
	public:
		template<size_t N>
		StringLiteral(const char (&ptr)[N])
			: m_ptr(ptr)
			, m_length(N-1)
		{
			//
		}

		explicit StringLiteral(const char* ptr, size_t length)
			: m_ptr(ptr)
			, m_length(length)
		{
			//
		}

		const char* GetPtr() const { return m_ptr; }
		size_t GetLength() const { return m_length; }
		bool operator==(StringLiteral const& other) const { return m_ptr == other.m_ptr; }
		bool operator!=(StringLiteral const& other) const { return m_ptr != other.m_ptr; }

	protected:
		friend class String::Holder;
		char const* m_ptr;
		size_t m_length;
	};
	typedef StringLiteral StringRef;

	template<typename... Params>
	sprawl::String Format(const char* const text, Params&&... params)
	{
		return sprawl::String(sprawl::StringLiteral(text, strlen(text))).format(std::forward<Params>(params)...);
	}
}

#ifndef SPRAWL_STRING_NO_STL_COMPAT
namespace std
{
	template<>
	struct hash<sprawl::String>
	{
		typedef sprawl::String argument_type;
		typedef std::size_t value_type;

		inline value_type operator()(argument_type const& str) const
		{
			return str.GetHash();
		}
	};

	template<>
	struct hash<sprawl::StringLiteral>
	{
		typedef sprawl::StringLiteral argument_type;
		typedef std::size_t value_type;

		inline value_type operator()(argument_type const& str) const
		{
			return sprawl::murmur3::HashPointer(intptr_t(str.GetPtr()));
		}
	};
}
#endif

#ifndef _WIN32
#ifndef SPRAWL_NO_FORMAT_LITERAL
namespace sprawl
{
	namespace detail
	{
		class FormatHelper
		{
		public:
			template<size_t N>
			FormatHelper(const char (&ptr)[N])
				: m_str(StringLiteral(ptr))
			{
				//
			}

			explicit FormatHelper(const char* ptr, size_t length)
			: m_str(StringLiteral(ptr, length))
			{
				//
			}

			template<typename... Params>
			sprawl::String operator()(Params&&... params)
			{
				return m_str.format(std::forward<Params>(params)...);
			}

		private:
			String m_str;
		};
	}
}

inline sprawl::detail::FormatHelper operator "" _format(const char *ptr, size_t length)
{
	return sprawl::detail::FormatHelper(ptr, length);
}

#endif
#endif
# Change User Description Committed
#12 19906 ShadauxCat - Added tag, compile time string type
- Since tag requires visual studio 2015, removed compatibility code for earlier versions of visual studio
- Improved compiler detection
- Added endianness detection
- Added template if/else helper
- Fixed bug with murmur3 64 bit
- Added seed argument for murmur3

#review-19907
#11 14833 ShadauxCat First checkin of logging module.

Also fixes the following issues:

-Added UpperBound() and LowerBound() to BinaryTree and created appropriate unit tests
-Added Sync() to ThreadManager to force it to run all tasks to completion and not return until it has no tasks left
-Fixed a bug in String::format() where a non-numeric value inside {} would be treated as an empty {}; it now simply prints whatever the value was. (i.e., "{blah}".format(foo) simply returns "{blah}")
-Added Reset() to sprawl::StringBuilder
-Disabled the switch-enum warning flag in gcc because it's stupid and ridiculous that a default case doesn't shut it up
-Made sprawl::Mutex movable. This may turn out to be a bad idea but it enabled keeping them in a map.
-Fixed a name collission between HashMap and BinaryTree; both defined sprawl::collections::detail::UnderlyingType and ::MethodType. Prefixed the ones in BinaryTree with "Tree". This isn't the best solution, but it works for now.

#review-14834
#10 14816 ShadauxCat Filled in some more filesystem functions, added appropriate unit tests.
Only a few remain.

#review-14817
#9 14783 ShadauxCat Style corrections (placement of const)

#review-14784
#8 14771 ShadauxCat Sadface.
User-defined literals not supported in visual studio 2013. Disabled for win32.

#review-14772
#7 14769 ShadauxCat Added string literal to allow format strings to be used in a way that's closer to python:

"{} {} {}"_format(foo, bar, baz);

.format() would be great, but not possible; _format() is almost as good.

#review-14770
#6 14761 ShadauxCat First drop of code for sprawl::filesystem and sprawl::path.
Library will continue to grow.

Also fixed a warning on linux.

#review-14762
#5 14146 ShadauxCat Moving a gtest-specific function out of String.hpp

#review-14147
#4 14144 ShadauxCat Switching unit tests to gtest.
100 is a decent number of tests to start with, but it needs to be more like 400 to test the current codebase.

#review-14145
#3 14015 ShadauxCat -Made reference counts in strings atomic
-Fixed an issue where items that were implicitly convertible to a hash map's value could not be inserted without explicit construction. (i.e., inserting a String object as map.insert("myString") would fail; it had to be map.insert(sprawl::String("myString")))
-Added template aliases for HashMap to simplify construction in simple single key/single value case
-Deprecated MSVC11 support.
#2 12508 ShadauxCat -Added threading library.
Currently only functional for Linux; Windows will fail to link. (I will fix this soon.)
-Fixed missing move and copy constructors in List and ForwardList
-Fixed broken move constructor in HashMap
-Fixed missing const get() in HashMap
-Fixed broken operator-> in ListIterator
-Added sprawl::noncopyable
-Added sketch headers for filesystem library
-Made StringLiteral hashable, added special hashes for pointers and integers in murmur3
-Fixed compiler warning in async_network
-Updated memory allocators to use new threading library for mutexes
-Added accessibility to sprawl::StringLiteral to be able toa ccess its pointer and length and perform pointer comparisons

#review-12504
#1 11496 ShadauxCat Initial checkin: Current states for csbuild and libSprawl