// Copyright (c) 2011-2016 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #pragma once #include "StringView.h" #include #include namespace Common { // 'StringBuffer' is a string of fixed maximum size. template class StringBuffer { public: typedef char Object; typedef size_t Size; const static Size MAXIMUM_SIZE = MAXIMUM_SIZE_VALUE; const static Size INVALID; static_assert(MAXIMUM_SIZE != 0, "StringBuffer's size must not be zero"); // Default constructor. // After construction, 'StringBuffer' is empty, that is 'size' == 0 StringBuffer() : size(0) { } // Direct constructor. // Copies string from 'stringData' to 'StringBuffer'. // The behavior is undefined unless ('stringData' != 'nullptr' || 'stringSize' == 0) && 'stringSize' <= 'MAXIMUM_SIZE'. StringBuffer(const Object* stringData, Size stringSize) : size(stringSize) { assert(stringData != nullptr || size == 0); assert(size <= MAXIMUM_SIZE); memcpy(data, stringData, size); } // Constructor from C array. // Copies string from 'stringData' to 'StringBuffer'. // The behavior is undefined unless ('stringData' != 'nullptr' || 'stringSize' == 0) && 'stringSize' <= 'MAXIMUM_SIZE'. Input state can be malformed using poiner conversions. template explicit StringBuffer(const Object(&stringData)[stringSize]) : size(stringSize - 1) { assert(stringData != nullptr || size == 0); assert(size <= MAXIMUM_SIZE); memcpy(data, stringData, size); } // Constructor from StringView // Copies string from 'stringView' to 'StringBuffer'. // The behavior is undefined unless 'stringView.size()' <= 'MAXIMUM_SIZE'. explicit StringBuffer(StringView stringView) : size(stringView.getSize()) { assert(size <= MAXIMUM_SIZE); memcpy(data, stringView.getData(), size); } // Copy constructor. // Copies string from 'other' to 'StringBuffer'. StringBuffer(const StringBuffer& other) : size(other.size) { memcpy(data, other.data, size); } // Destructor. // No special action is performed. ~StringBuffer() { } // Copy assignment operator. StringBuffer& operator=(const StringBuffer& other) { size = other.size; memcpy(data, other.data, size); return *this; } // StringView assignment operator. // Copies string from 'stringView' to 'StringBuffer'. // The behavior is undefined unless 'stringView.size()' <= 'MAXIMUM_SIZE'. StringBuffer& operator=(StringView stringView) { assert(stringView.getSize() <= MAXIMUM_SIZE); memcpy(data, stringView.getData(), stringView.getSize()); size = stringView.getSize(); return *this; } operator StringView() const { return StringView(data, size); } explicit operator std::string() const { return std::string(data, size); } Object* getData() { return data; } const Object* getData() const { return data; } Size getSize() const { return size; } // Return false if 'StringView' is not EMPTY. bool isEmpty() const { return size == 0; } // Get 'StringBuffer' element by index. // The behavior is undefined unless 'index' < 'size'. Object& operator[](Size index) { assert(index < size); return *(data + index); } // Get 'StringBuffer' element by index. // The behavior is undefined unless 'index' < 'size'. const Object& operator[](Size index) const { assert(index < size); return *(data + index); } // Get first element. // The behavior is undefined unless 'size' > 0 Object& first() { assert(size > 0); return *data; } // Get first element. // The behavior is undefined unless 'size' > 0 const Object& first() const { assert(size > 0); return *data; } // Get last element. // The behavior is undefined unless 'size' > 0 Object& last() { assert(size > 0); return *(data + (size - 1)); } // Get last element. // The behavior is undefined unless 'size' > 0 const Object& last() const { assert(size > 0); return *(data + (size - 1)); } // Return a pointer to the first element. Object* begin() { return data; } // Return a pointer to the first element. const Object* begin() const { return data; } // Return a pointer after the last element. Object* end() { return data + size; } // Return a pointer after the last element. const Object* end() const { return data + size; } // Compare elements of two strings, return false if there is a difference. bool operator==(StringView other) const { if (size == other.getSize()) { for (Size i = 0;; ++i) { if (i == size) { return true; } if (!(*(data + i) == *(other.getData() + i))) { break; } } } return false; } // Compare elements two strings, return false if there is no difference. bool operator!=(StringView other) const { return !(*this == other); } // Compare two strings character-wise. bool operator<(StringView other) const { Size count = other.getSize() < size ? other.getSize() : size; for (Size i = 0; i < count; ++i) { Object char1 = *(data + i); Object char2 = *(other.getData() + i); if (char1 < char2) { return true; } if (char2 < char1) { return false; } } return size < other.getSize(); } // Compare two strings character-wise. bool operator<=(StringView other) const { return !(other < *this); } // Compare two strings character-wise. bool operator>(StringView other) const { return other < *this; } // Compare two strings character-wise. bool operator>=(StringView other) const { return !(*this < other); } // Return false if 'StringView' does not contain 'object' at the beginning. bool beginsWith(const Object& object) const { if (size == 0) { return false; } return *data == object; } // Return false if 'StringView' does not contain 'other' at the beginning. bool beginsWith(StringView other) const { if (size >= other.getSize()) { for (Size i = 0;; ++i) { if (i == other.getSize()) { return true; } if (!(*(data + i) == *(other.getData() + i))) { break; } } } return false; } // Return false if 'StringView' does not contain 'object'. bool contains(const Object& object) const { for (Size i = 0; i < size; ++i) { if (*(data + i) == object) { return true; } } return false; } // Return false if 'StringView' does not contain 'other'. bool contains(StringView other) const { if (size >= other.getSize()) { Size i = size - other.getSize(); for (Size j = 0; !(i < j); ++j) { for (Size k = 0;; ++k) { if (k == other.getSize()) { return true; } if (!(*(data + j + k) == *(other.getData() + k))) { break; } } } } return false; } // Return false if 'StringView' does not contain 'object' at the end. bool endsWith(const Object& object) const { if (size == 0) { return false; } return *(data + (size - 1)) == object; } // Return false if 'StringView' does not contain 'other' at the end. bool endsWith(StringView other) const { if (size >= other.getSize()) { Size i = size - other.getSize(); for (Size j = 0;; ++j) { if (j == other.getSize()) { return true; } if (!(*(data + i + j) == *(other.getData() + j))) { break; } } } return false; } // Looks for the first occurence of 'object' in 'StringView', // returns index or INVALID if there are no occurences. Size find(const Object& object) const { for (Size i = 0; i < size; ++i) { if (*(data + i) == object) { return i; } } return INVALID; } // Looks for the first occurence of 'other' in 'StringView', // returns index or INVALID if there are no occurences. Size find(StringView other) const { if (size >= other.getSize()) { Size i = size - other.getSize(); for (Size j = 0; !(i < j); ++j) { for (Size k = 0;; ++k) { if (k == other.getSize()) { return j; } if (!(*(data + j + k) == *(other.getData() + k))) { break; } } } } return INVALID; } // Looks for the last occurence of 'object' in 'StringView', // returns index or INVALID if there are no occurences. Size findLast(const Object& object) const { for (Size i = 0; i < size; ++i) { if (*(data + (size - 1 - i)) == object) { return size - 1 - i; } } return INVALID; } // Looks for the first occurence of 'other' in 'StringView', // returns index or INVALID if there are no occurences. Size findLast(StringView other) const { if (size >= other.getSize()) { Size i = size - other.getSize(); for (Size j = 0; !(i < j); ++j) { for (Size k = 0;; ++k) { if (k == other.getSize()) { return i - j; } if (!(*(data + (i - j + k)) == *(other.getData() + k))) { break; } } } } return INVALID; } // Returns substring of 'headSize' first elements. // The behavior is undefined unless 'headSize' <= 'size'. StringView head(Size headSize) const { assert(headSize <= size); return StringView(data, headSize); } // Returns substring of 'tailSize' last elements. // The behavior is undefined unless 'tailSize' <= 'size'. StringView tail(Size tailSize) const { assert(tailSize <= size); return StringView(data + (size - tailSize), tailSize); } // Returns 'StringView' without 'headSize' first elements. // The behavior is undefined unless 'headSize' <= 'size'. StringView unhead(Size headSize) const { assert(headSize <= size); return StringView(data + headSize, size - headSize); } // Returns 'StringView' without 'tailSize' last elements. // The behavior is undefined unless 'tailSize' <= 'size'. StringView untail(Size tailSize) const { assert(tailSize <= size); return StringView(data, size - tailSize); } // Returns substring starting at 'startIndex' and contaning 'endIndex' - 'startIndex' elements. // The behavior is undefined unless 'startIndex' <= 'endIndex' and 'endIndex' <= 'size'. StringView range(Size startIndex, Size endIndex) const { assert(startIndex <= endIndex && endIndex <= size); return StringView(data + startIndex, endIndex - startIndex); } // Returns substring starting at 'startIndex' and contaning 'sliceSize' elements. // The behavior is undefined unless 'startIndex' <= 'size' and 'sliceSize' <= 'size' - 'startIndex'. StringView slice(Size startIndex, Size sliceSize) const { assert(startIndex <= size && sliceSize <= size - startIndex); return StringView(data + startIndex, sliceSize); } // Appends 'object' to 'StringBuffer'. // The behavior is undefined unless 1 <= 'MAXIMUM_SIZE' - 'size'. StringBuffer& append(Object object) { assert(1 <= MAXIMUM_SIZE - size); data[size] = object; ++size; return *this; } // Appends 'stringView' to 'StringBuffer'. // The behavior is undefined unless 'stringView.size()' <= 'MAXIMUM_SIZE' - 'size'. StringBuffer& append(StringView stringView) { assert(stringView.getSize() <= MAXIMUM_SIZE - size); if (stringView.getSize() != 0) { memcpy(data + size, stringView.getData(), stringView.getSize()); size += stringView.getSize(); } return *this; } // Sets 'StringBuffer' to empty state, that is 'size' == 0 StringBuffer& clear() { size = 0; return *this; } // Removes substring starting at 'startIndex' and contaning 'cutSize' elements. // The behavior is undefined unless 'startIndex' <= 'size' and 'cutSize' <= 'size' - 'startIndex'. StringBuffer& cut(Size startIndex, Size cutSize) { assert(startIndex <= size && cutSize <= size - startIndex); if (cutSize != 0) { memcpy(data + startIndex, data + startIndex + cutSize, size - startIndex - cutSize); size -= cutSize; } return *this; } // Copy 'object' to each element of 'StringBuffer'. StringBuffer& fill(Object object) { if (size > 0) { memset(data, object, size); } return *this; } // Inserts 'object' into 'StringBuffer' at 'index'. // The behavior is undefined unless 'index' <= 'size' and 1 <= 'MAXIMUM_SIZE' - 'size'. StringBuffer& insert(Size index, Object object) { assert(index <= size); assert(1 <= MAXIMUM_SIZE - size); memmove(data + index + 1, data + index, size - index); data[index] = object; ++size; return *this; } // Inserts 'stringView' into 'StringBuffer' at 'index'. // The behavior is undefined unless 'index' <= 'size' and 'stringView.size()' <= 'MAXIMUM_SIZE' - 'size'. StringBuffer& insert(Size index, StringView stringView) { assert(index <= size); assert(stringView.getSize() <= MAXIMUM_SIZE - size); if (stringView.getSize() != 0) { memmove(data + index + stringView.getSize(), data + index, size - index); memcpy(data + index, stringView.getData(), stringView.getSize()); size += stringView.getSize(); } return *this; } // Overwrites 'StringBuffer' starting at 'index' with 'stringView', possibly expanding 'StringBuffer'. // The behavior is undefined unless 'index' <= 'size' and 'stringView.size()' <= 'MAXIMUM_SIZE' - 'index'. StringBuffer& overwrite(Size index, StringView stringView) { assert(index <= size); assert(stringView.getSize() <= MAXIMUM_SIZE - index); memcpy(data + index, stringView.getData(), stringView.getSize()); if (size < index + stringView.getSize()) { size = index + stringView.getSize(); } return *this; } // Sets 'size' to 'bufferSize', assigning value '\0' to newly inserted elements. // The behavior is undefined unless 'bufferSize' <= 'MAXIMUM_SIZE'. StringBuffer& resize(Size bufferSize) { assert(bufferSize <= MAXIMUM_SIZE); if (bufferSize > size) { memset(data + size, 0, bufferSize - size); } size = bufferSize; return *this; } // Reverse 'StringBuffer' elements. StringBuffer& reverse() { for (Size i = 0; i < size / 2; ++i) { Object object = *(data + i); *(data + i) = *(data + (size - 1 - i)); *(data + (size - 1 - i)) = object; } return *this; } // Sets 'size' to 'bufferSize'. // The behavior is undefined unless 'bufferSize' <= 'size'. StringBuffer& shrink(Size bufferSize) { assert(bufferSize <= size); size = bufferSize; return *this; } protected: Object data[MAXIMUM_SIZE]; Size size; }; template const typename StringBuffer::Size StringBuffer::INVALID = std::numeric_limits::Size>::max(); }