// 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