text: // Copyright (c) 2011-2016 The Skytells developers
  // Distributed under the MIT/X11 software license, see the accompanying
  // @license MIT : http://www.opensource.org/licenses/mit-license.php.
  // @package Oxgram Cryptocurrency by Skytells, Inc.
  
  #pragma once
  
  #include "StringView.h"
  #include <limits>
  #include <string.h>
  
  namespace Common {
  
  // 'StringBuffer' is a string of fixed maximum size.
  template<size_t MAXIMUM_SIZE_VALUE> 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<Size stringSize> 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<size_t MAXIMUM_SIZE> const typename StringBuffer<MAXIMUM_SIZE>::Size StringBuffer<MAXIMUM_SIZE>::INVALID = std::numeric_limits<typename StringBuffer<MAXIMUM_SIZE>::Size>::max();
  
  }
  