Newer
Older
Import / applications / HighwayDash / ports / Framework / StringExperiments.cpp
//  BlockyFroggy
//  Copyright © 2017 John Ryland.
//  All rights reserved.
#include "StringExperiments.h"

#include "Utilities.h"
#include <vector>
#include <ctime>
#include <cstdlib>
#include <chrono>
#include <sstream>
#include <new>
#include <dlfcn.h>
#include <iostream>
#include <utility>


#if ENABLE_UNIT_TESTS


template <typename T>
T* arrayCopy(T* a_dst, const T* a_src, size_t a_elementCount)
{
  return (T*)std::memcpy((void*)a_dst, (void*)a_src, a_elementCount * sizeof(T));
}


char* strDup(const char* a_str, size_t a_len)
{
  return arrayCopy(new char[a_len], a_str, a_len);
}


class StringLiteral
{
public:
  //StringLiteral(const char * const a_str) : m_length(strlen(a_str)), m_chars(a_str) {}
  //StringLiteral(const char * a_str) = delete;
  
  template<int N> StringLiteral(const char (&a_str)[N]) : m_length(N), m_chars(a_str) {}
  template<int N> StringLiteral(char (&)[N]) = delete;
  size_t size() { return m_length; }
  operator const char*() { return m_chars; }
private:
  friend class String;
  size_t m_length = 0;
  const char* m_chars = nullptr;
};


#define BARK std::cout << __PRETTY_FUNCTION__ << std::endl

struct Dummy {};

template<typename T>
struct IsCharPtr {};

template<>
struct IsCharPtr<const char *> { typedef Dummy* Type; };

template<>
struct IsCharPtr<char *> { typedef Dummy* Type; };


class ReferenceCount
{
public:
  operator size_t() const { return (m_count == nullptr) ? 1 : (*m_count).load(); }

  void operator++()
  {
    if (m_count == nullptr)
    {
      m_count = new std::atomic<size_t>;
      *m_count = 2;
    }
    else
    {
      (*m_count)++;
    }
  }
  
  // returns true if need to delete the object
  bool operator--()
  {
    if (operator size_t() == 1) // We are the sole owner left
    {
      if (m_count)
        delete m_count;
      m_count = nullptr;
      return true;
    }
    else
    {
      (*m_count)--;
    }
    return false;
  }
  
  void reset()
  {
    m_count = nullptr;
  }

private:
  std::atomic<size_t>* m_count = nullptr; // a nullptr refCount is semantically equivolent to a refCount of one
};


class String
{
public:
  // Default construct an invalid string
  String()
  {
    init(StringInvalid, nullptr, 0);
  }
  
  // Construct from a string literal
  template<int N>
  explicit String(const char (&a_str)[N])
  {
    init(StringLiteral, a_str, N);
    BARK;
  }

  // Constructor from an explicit StringLiteral
  explicit String(const StringLiteral& a_other)
  {
    init(StringLiteral, a_other.m_chars, a_other.m_length);
    BARK;
  }
  
  // Construct from an array of chars
  template<int N>
  explicit String(char (&a_str)[N])
  {
    init(StringCopy, strDup(a_str, N), N);
    BARK;
  }

  // Construct from an array of chars
  template<typename T>
  explicit String(T a_str, typename IsCharPtr<T>::Type=0)
    : m_length(strlen(a_str))
  {
    init(StringCopy, strDup(a_str, m_length), m_length);
    BARK;
  }

  // Copy constructor
  explicit String(const String& a_other)
  {
    init(a_other.m_type, a_other.m_chars, a_other.m_length);
    a_other.addRef();
    m_refCount = a_other.m_refCount;
    BARK;
  }

  // Copy slice / substring constructor
  String(const String& a_other, size_t a_startOffset, size_t a_length)
  {
    init(a_other.m_type, a_other.m_chars + a_startOffset, a_length);
    a_other.addRef();
    m_refCount = a_other.m_refCount;
    BARK;
  }
  
  // Assignment operator
  String& operator=(const String& a_other)
  {
    if (&a_other != this) // Self assignment does nothing
    {
      a_other.addRef();
      removeRef();
      init(a_other.m_type, a_other.m_chars, a_other.m_length);
      m_refCount = a_other.m_refCount;
    }
    return *this;
  }

  ~String()
  {
    removeRef();
  }

  size_t size() { return m_length; }

  operator const char*() { return m_chars; }

  char at(size_t a_index) const { return (a_index < m_length) ? m_chars[a_index] : 0; }
  char operator [](size_t a_index) const { return at(a_index); }

  const char* cbegin() const { return m_chars; }
  const char* cend() const { return m_chars + m_length; }

  const char* begin() const { return m_chars; }
  const char* end() const { return m_chars + m_length; }

  char* begin() { modify(); return m_chars; }
  char* end() { modify(); return m_chars + m_length; }

  const String &asConst() const noexcept { return *this; }

private:
  enum Type
  {
    StringInvalid,
    StringLiteral,
    StringCopy,
    //StringLiteralSlice,
    //StringCopySlice,
    StringShort1,
    StringShort2,
    StringShort3,
    StringShort4,
    StringShort5,
    StringShort6,
    StringShort7,
    StringShort8,
  };

  void init(Type a_type, const char* a_chars, size_t a_length)
  {
    m_type = a_type;
    m_chars = const_cast<char*>(a_chars);
    m_length = a_length;
  }
 
  void addRef() const
  {
    if (m_type == StringCopy)// || m_type == StringCopySlice)
    {
      ++m_refCount;
    }
  }
  
  void removeRef()
  {
    if (m_type == StringCopy)// || m_type == StringCopySlice)
    {
      if (--m_refCount)
      {
        delete[] m_chars;
        m_chars = nullptr;
      }
    }
  }
  
  void modify()
  {
    if ((m_refCount >= 2) || (m_type == StringLiteral))// || (m_type == StringLiteralSlice))
    {
      deepCopy(); // This will always be the non-deleting case when it removes the ref
    }
  }
  
  void deepCopy()
  {
    char* newChars = strDup(m_chars, m_length);
    removeRef();
    init(StringCopy, newChars, m_length);
    m_refCount.reset(); // detach from the original ref count
  }

  Type m_type = StringInvalid;
  
  //! @todo short string optimization
//  union {
//    struct {
      size_t m_length = 0;
       char* m_chars = nullptr;
//    } m_longString;
//    char m_shortString[8];
//  } m_sso;

  mutable ReferenceCount m_refCount;
};


const char* b = "b";
const char* b2 = "f";
const char* f() { return b2; }

//const char* const blah = "bbb";


DECLARE_UNIT_TEST(StringExperiment_Tests)
{
  const char a[] = "a";
  //char buffer[10] = "buffer";
  //char* c = buffer;

  //StringLiteral bb(blah);       // Foo::Foo(const char (&)[N]) [N = 2]

  StringLiteral l("literal");   // Foo::Foo(const char (&)[N]) [N = 2]
  StringLiteral aa(a);          // Foo::Foo(const char (&)[N]) [N = 2]
  //StringLiteral a2(aa);
  //StringLiteral b3(b);
  //StringLiteral bb2(b2);
  //StringLiteral c3(c);
  String s;
{
}{  String ls("literal");  // Foo::Foo(const char (&)[N]) [N = 2]
  s = ls;
}{  String aas(a);         // Foo::Foo(const char (&)[N]) [N = 2]
  s = aas;
}{
  String aas(a);
  String a2s(aas);
  s = a2s;
}{  String a3s(aa);
  s = a3s;
  String slice(String("This is a long\n string"), 5, 10);
  
  // non-const vers called
  for (const char& ch : slice)
  {
    putchar(ch);
  }
  
  // non-const vers called
  for (char ch : slice)
  {
    putchar(ch);
  }

  // non-const vers called
  for (char& ch : slice)
  {
    putchar(ch);
  }
  
  // const vers called
  for (const char& ch : slice.asConst())
  {
    putchar(ch);
  }
  
  // const vers called
  for (char ch : slice.asConst())
  {
    putchar(ch);
  }
  
  // for (char& ch : slice.asConst())  // can't compile - invalid


  /*
}{  String bbs(b);      // Foo::Foo(T, typename IsCharPtr<T>::Type) [T = const char *]
  s = bbs;
}{  String ccs(c);      // Foo::Foo(T, typename IsCharPtr<T>::Type) [T = char *]
  s = ccs;
}{  String ees(buffer); // Foo::Foo(char (&)[N]) [N = 10]
  s = ees;
}{  String ffs(f());    // Foo::Foo(T, typename IsCharPtr<T>::Type) [T = const char *]
  s = ffs;
  */
}

}


#endif