Browse Source

Add more functionality in String class

Panagiotis Christopoulos Charitos 6 years ago
parent
commit
03113813c2
4 changed files with 145 additions and 34 deletions
  1. 40 4
      src/anki/util/String.cpp
  2. 81 29
      src/anki/util/String.h
  3. 1 1
      tests/util/SparseArray.cpp
  4. 23 0
      tests/util/String.cpp

+ 40 - 4
src/anki/util/String.cpp

@@ -176,10 +176,10 @@ void String::create(Allocator alloc, Char c, PtrSize length)
 	m_data[length] = '\0';
 }
 
-void String::appendInternal(Allocator alloc, const Char* str, PtrSize strSize)
+void String::appendInternal(Allocator& alloc, const Char* str, PtrSize strLen)
 {
 	ANKI_ASSERT(str != nullptr);
-	ANKI_ASSERT(strSize > 1);
+	ANKI_ASSERT(strLen > 0);
 
 	auto size = m_data.getSize();
 
@@ -190,14 +190,16 @@ void String::appendInternal(Allocator alloc, const Char* str, PtrSize strSize)
 	}
 
 	DynamicArray<Char> newData;
-	newData.create(alloc, size + strSize - 1);
+	newData.create(alloc, size + strLen);
 
 	if(!m_data.isEmpty())
 	{
 		std::memcpy(&newData[0], &m_data[0], sizeof(Char) * size);
 	}
 
-	std::memcpy(&newData[size - 1], str, sizeof(Char) * strSize);
+	std::memcpy(&newData[size - 1], str, sizeof(Char) * strLen);
+
+	newData[newData.getSize() - 1] = '\0';
 
 	m_data.destroy(alloc);
 	m_data = std::move(newData);
@@ -235,4 +237,38 @@ void String::sprintf(Allocator alloc, CString fmt, ...)
 	}
 }
 
+String& String::replaceAll(Allocator alloc, CString from, CString to)
+{
+	String tmp = {alloc, toCString()};
+	const PtrSize fromLen = from.getLength();
+	const PtrSize toLen = to.getLength();
+
+	PtrSize pos = NPOS;
+	while((pos = tmp.find(from)) != NPOS)
+	{
+		String tmp2;
+		if(pos > 0)
+		{
+			tmp2.create(alloc, tmp.getBegin(), tmp.getBegin() + pos);
+		}
+
+		if(toLen > 0)
+		{
+			tmp2.append(alloc, to.getBegin(), to.getBegin() + toLen);
+		}
+
+		if(pos + fromLen < tmp.getLength())
+		{
+			tmp2.append(alloc, tmp.getBegin() + pos + fromLen, tmp.getEnd());
+		}
+
+		tmp.destroy(alloc);
+		tmp = std::move(tmp2);
+	}
+
+	destroy(alloc);
+	*this = std::move(tmp);
+	return *this;
+}
+
 } // end namespace anki

+ 81 - 29
src/anki/util/String.h

@@ -170,18 +170,28 @@ public:
 		return m_ptr;
 	}
 
-	const Char* begin() const
+	const Char* getBegin() const
 	{
 		checkInit();
 		return &m_ptr[0];
 	}
 
-	const Char* end() const
+	const Char* getEnd() const
 	{
 		checkInit();
 		return &m_ptr[getLength()];
 	}
 
+	const Char* begin() const
+	{
+		return getBegin();
+	}
+
+	const Char* end() const
+	{
+		return getEnd();
+	}
+
 	/// Return true if the string is not initialized.
 	Bool isEmpty() const
 	{
@@ -284,6 +294,11 @@ public:
 		*this = std::move(b);
 	}
 
+	String(Allocator alloc, CString str)
+	{
+		create(alloc, str);
+	}
+
 	/// Requires manual destruction.
 	~String()
 	{
@@ -391,36 +406,84 @@ public:
 		create(alloc, b.toCString());
 	}
 
+	/// Append another string to this one.
+	void append(Allocator alloc, const String& b)
+	{
+		if(!b.isEmpty())
+		{
+			appendInternal(alloc, &b.m_data[0], b.m_data.getSize() - 1);
+		}
+	}
+
+	/// Append a const string to this one.
+	void append(Allocator alloc, const CStringType& cstr)
+	{
+		if(!cstr.isEmpty())
+		{
+			appendInternal(alloc, cstr.cstr(), cstr.getLength());
+		}
+	}
+
+	/// Append using a range. Copies the range of [first, oneAfterLast)
+	void append(Allocator alloc, ConstIterator first, ConstIterator oneAfterLast)
+	{
+		const PtrSize len = oneAfterLast - first;
+		appendInternal(alloc, first, len);
+	}
+
+	/// Create formated string.
+	void sprintf(Allocator alloc, CString fmt, ...);
+
 	/// Destroy the string.
 	void destroy(Allocator alloc)
 	{
 		m_data.destroy(alloc);
 	}
 
-	Iterator begin()
+	Iterator getBegin()
 	{
 		checkInit();
 		return &m_data[0];
 	}
 
-	ConstIterator begin() const
+	ConstIterator getBegin() const
 	{
 		checkInit();
 		return &m_data[0];
 	}
 
-	Iterator end()
+	Iterator getEnd()
 	{
 		checkInit();
 		return &m_data[m_data.getSize() - 1];
 	}
 
-	ConstIterator end() const
+	ConstIterator getEnd() const
 	{
 		checkInit();
 		return &m_data[m_data.getSize() - 1];
 	}
 
+	Iterator begin()
+	{
+		return getBegin();
+	}
+
+	ConstIterator begin() const
+	{
+		return getBegin();
+	}
+
+	Iterator end()
+	{
+		return getEnd();
+	}
+
+	ConstIterator end() const
+	{
+		return getEnd();
+	}
+
 	/// Return the string's length. It doesn't count the terminating character.
 	PtrSize getLength() const
 	{
@@ -437,27 +500,6 @@ public:
 		return CStringType(&m_data[0]);
 	}
 
-	/// Append another string to this one.
-	void append(Allocator alloc, const String& b)
-	{
-		if(!b.isEmpty())
-		{
-			appendInternal(alloc, &b.m_data[0], b.m_data.getSize());
-		}
-	}
-
-	/// Append a const string to this one.
-	void append(Allocator alloc, const CStringType& cstr)
-	{
-		if(!cstr.isEmpty())
-		{
-			appendInternal(alloc, &cstr[0], cstr.getLength() + 1);
-		}
-	}
-
-	/// Create formated string.
-	void sprintf(Allocator alloc, CString fmt, ...);
-
 	/// Return true if it's empty.
 	Bool isEmpty() const
 	{
@@ -549,6 +591,9 @@ public:
 		return anki::computeHash(&m_data[0], m_data.getSize());
 	}
 
+	/// Replace all occurrences of "from" with "to".
+	String& replaceAll(Allocator alloc, CString from, CString to);
+
 protected:
 	DynamicArray<Char> m_data;
 
@@ -558,7 +603,7 @@ protected:
 	}
 
 	/// Append to this string.
-	void appendInternal(Allocator alloc, const Char* str, PtrSize strSize);
+	void appendInternal(Allocator& alloc, const Char* str, PtrSize strLen);
 
 	void move(String& b)
 	{
@@ -573,7 +618,7 @@ inline void String::toString(Allocator alloc, TNumber number)
 	destroy(alloc);
 
 	Array<Char, 512> buff;
-	I ret = std::snprintf(&buff[0], buff.size(), detail::toStringFormat<TNumber>(), number);
+	const I ret = std::snprintf(&buff[0], buff.size(), detail::toStringFormat<TNumber>(), number);
 
 	if(ret < 0 || ret > static_cast<I>(buff.getSize()))
 	{
@@ -709,6 +754,13 @@ public:
 		Base::toString(m_alloc, number);
 	}
 
+	/// Replace all occurrences of "from" with "to".
+	StringAuto& replaceAll(CString from, CString to)
+	{
+		Base::replaceAll(m_alloc, from, to);
+		return *this;
+	}
+
 private:
 	GenericMemoryPoolAllocator<Char> m_alloc;
 

+ 1 - 1
tests/util/SparseArray.cpp

@@ -324,7 +324,7 @@ ANKI_TEST(Util, SparseArrayBench)
 	StlMap stdMap(10, std::hash<int>(), std::equal_to<int>(), allocStl);
 
 	using AkMap = SparseArray<int, U32>;
-	AkMap akMap(256, log2(256.0f), 0.90f);
+	AkMap akMap(256, U32(log2(256.0f)), 0.90f);
 
 	HighRezTimer timer;
 

+ 23 - 0
tests/util/String.cpp

@@ -207,6 +207,29 @@ ANKI_TEST(Util, String)
 		ANKI_TEST_EXPECT_EQ(f, 123456789.145);
 		a.destroy(alloc);
 	}
+
+	// replaceAll
+	{
+		String a = {alloc, "foo"};
+		a.replaceAll(alloc, "foo", "bar");
+		ANKI_TEST_EXPECT_EQ(a, "bar");
+		a.destroy(alloc);
+
+		a.create(alloc, "lafooha");
+		a.replaceAll(alloc, "foo", "bar");
+		ANKI_TEST_EXPECT_EQ(a, "labarha");
+		a.destroy(alloc);
+
+		a.create(alloc, "jjhfalkakljla");
+		a.replaceAll(alloc, "a", "b");
+		ANKI_TEST_EXPECT_EQ(a, "jjhfblkbkljlb");
+		a.destroy(alloc);
+
+		a.create(alloc, "%foo%ajlkadsf%foo%");
+		a.replaceAll(alloc, "%foo%", "");
+		ANKI_TEST_EXPECT_EQ(a, "ajlkadsf");
+		a.destroy(alloc);
+	}
 }
 
 } // end namespace anki