From f43d32d6d2cc7ecd04f4f06f20d5a6fc2c87c9ae Mon Sep 17 00:00:00 2001 From: Aki Date: Tue, 12 Mar 2024 01:08:39 +0100 Subject: Another reorganization change that diverts me from crying unable to get rid off singleton madness --- FoundationEx/CMakeLists.txt | 17 +- FoundationEx/Dictionary.h | 100 ----- FoundationEx/Dictionary.inl.h | 246 ------------ FoundationEx/List.h | 106 ------ FoundationEx/List.inl.h | 432 --------------------- FoundationEx/Reader.cpp | 119 ------ FoundationEx/Reader.h | 70 ---- FoundationEx/Text.cpp | 678 --------------------------------- FoundationEx/Text.h | 209 ----------- FoundationEx/Utils.cpp | 38 -- FoundationEx/Utils.h | 18 - FoundationEx/include/Dictionary.h | 100 +++++ FoundationEx/include/Dictionary.inl.h | 246 ++++++++++++ FoundationEx/include/List.h | 106 ++++++ FoundationEx/include/List.inl.h | 432 +++++++++++++++++++++ FoundationEx/include/Reader.h | 70 ++++ FoundationEx/include/Text.h | 211 +++++++++++ FoundationEx/include/Utils.h | 18 + FoundationEx/src/Reader.cpp | 119 ++++++ FoundationEx/src/Text.cpp | 686 ++++++++++++++++++++++++++++++++++ FoundationEx/src/Utils.cpp | 38 ++ FoundationEx/test/Text.cpp | 47 +++ 22 files changed, 2079 insertions(+), 2027 deletions(-) delete mode 100644 FoundationEx/Dictionary.h delete mode 100644 FoundationEx/Dictionary.inl.h delete mode 100644 FoundationEx/List.h delete mode 100644 FoundationEx/List.inl.h delete mode 100644 FoundationEx/Reader.cpp delete mode 100644 FoundationEx/Reader.h delete mode 100644 FoundationEx/Text.cpp delete mode 100644 FoundationEx/Text.h delete mode 100644 FoundationEx/Utils.cpp delete mode 100644 FoundationEx/Utils.h create mode 100644 FoundationEx/include/Dictionary.h create mode 100644 FoundationEx/include/Dictionary.inl.h create mode 100644 FoundationEx/include/List.h create mode 100644 FoundationEx/include/List.inl.h create mode 100644 FoundationEx/include/Reader.h create mode 100644 FoundationEx/include/Text.h create mode 100644 FoundationEx/include/Utils.h create mode 100644 FoundationEx/src/Reader.cpp create mode 100644 FoundationEx/src/Text.cpp create mode 100644 FoundationEx/src/Utils.cpp create mode 100644 FoundationEx/test/Text.cpp diff --git a/FoundationEx/CMakeLists.txt b/FoundationEx/CMakeLists.txt index 71dd25d..045b44e 100644 --- a/FoundationEx/CMakeLists.txt +++ b/FoundationEx/CMakeLists.txt @@ -1,12 +1,7 @@ project(FoundationEx) -add_library( - FoundationEx - STATIC - Reader.cpp - Text.cpp - Utils.cpp - ) -target_include_directories( - FoundationEx - PUBLIC . - ) +add_library(${PROJECT_NAME} STATIC src/Reader.cpp src/Text.cpp src/Utils.cpp) +target_include_directories(${PROJECT_NAME} PUBLIC include) +add_executable(${PROJECT_NAME}_test test/Text.cpp) +target_link_libraries(${PROJECT_NAME}_test PRIVATE ${PROJECT_NAME} GTest::gtest_main) +generate_emulator(${PROJECT_NAME}_test) +gtest_discover_tests(${PROJECT_NAME}_test DISCOVERY_TIMEOUT 60) diff --git a/FoundationEx/Dictionary.h b/FoundationEx/Dictionary.h deleted file mode 100644 index bcb30ed..0000000 --- a/FoundationEx/Dictionary.h +++ /dev/null @@ -1,100 +0,0 @@ -/* Starshatter: The Open Source Project - Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors - Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors - Copyright (c) 1997-2006, Destroyer Studios LLC. - - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Declaration of the Dictionary class -*/ - -#ifndef Dictionary_h -#define Dictionary_h - -#include "Text.h" - -// +-------------------------------------------------------------------+ - -template class Dictionary; -template class DictionaryIter; -template class DictionaryCell; - -// +-------------------------------------------------------------------+ - -template class Dictionary -{ -public: - Dictionary(); - ~Dictionary(); - - T& operator[](const Text& key); - - void insert(const Text& key, const T& val); - void remove(const Text& key); - - void clear(); - - int size() const { return items; } - int isEmpty() const { return !items; } - - int contains(const Text& key) const; - T find(const Text& key, T defval) const; - -private: - void init(); - - int items; - - typedef DictionaryCell* PTR; - PTR table[256]; - - friend class DictionaryIter; -}; - -// +-------------------------------------------------------------------+ - -template class DictionaryIter -{ -public: - DictionaryIter(Dictionary& l); - ~DictionaryIter(); - - int operator++(); // prefix - - void reset(); - void forth(); - - Text key() const; - T value() const; - - void attach(Dictionary& l); - Dictionary& container(); - -private: - Dictionary* dict; - DictionaryCell* here; - int chain; -}; - -// +-------------------------------------------------------------------+ - -template class DictionaryCell -{ -public: - DictionaryCell(const Text& k) : key(k), value( ), next(0) { } - DictionaryCell(const Text& k, const T& v) : key(k), value(v), next(0) { } - ~DictionaryCell() { } - - Text key; - T value; - DictionaryCell* next; -}; - -// +-------------------------------------------------------------------+ - -#include "Dictionary.inl.h" -#endif // Dictionary_h - diff --git a/FoundationEx/Dictionary.inl.h b/FoundationEx/Dictionary.inl.h deleted file mode 100644 index cd58679..0000000 --- a/FoundationEx/Dictionary.inl.h +++ /dev/null @@ -1,246 +0,0 @@ -/* Starshatter: The Open Source Project - Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors - Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors - Copyright (c) 1997-2006, Destroyer Studios LLC. - - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Implementation of the Dictionary class -*/ - -#ifndef NDEBUG -#define DICT_CHECK(a, b) if ((a) == 0) throw b; -#else -#define DICT_CHECK(a, b) -#endif - -const int CHAINS = 256; - -// +-------------------------------------------------------------------+ - -template Dictionary::Dictionary() - : items(0) -{ init(); } - -template Dictionary::~Dictionary() -{ clear(); } - -// +-------------------------------------------------------------------+ - -template -void Dictionary::init() -{ - items = 0; - memset(table, 0, CHAINS*sizeof(PTR)); -} - -template -void Dictionary::clear() -{ - for (int i = 0; i < CHAINS; i++) { - DictionaryCell* link = table[i]; - - while (link) { - DictionaryCell* n = link->next; - delete link; - link = n; - } - } - - init(); -} - -// +-------------------------------------------------------------------+ - -template -T& Dictionary::operator[](const Text& key) -{ - int idx = key.hash() % CHAINS; - DictionaryCell* cell = table[idx]; - - if (cell == 0) { // empty chain - items++; - cell = new DictionaryCell(key); - table[idx] = cell; - return cell->value; - } - else { // search for key - while (cell->next && cell->key != key) - cell = cell->next; - - if (cell->key != key) { // not found in chain - items++; - cell->next = new DictionaryCell(key); - return cell->next->value; - } - else { // found: return it! - return cell->value; - } - } -} - -// +-------------------------------------------------------------------+ - -template -void Dictionary::insert(const Text& key, const T& val) -{ - T& value = operator[](key); - value = val; -} - -// +-------------------------------------------------------------------+ - -template -void Dictionary::remove(const Text& key) -{ - int idx = key.hash() % CHAINS; - DictionaryCell* cell = table[idx]; - - if (cell == 0) { // empty chain - return; - } - else { // search for key - while (cell->next && cell->key != key) - cell = cell->next; - - if (cell->key != key) { // not found in chain - return; - } - else { // found: remove it! - if (table[idx] == cell) { - table[idx] = cell->next; - delete cell; - } - else { - DictionaryCell* p = table[idx]; - while (p->next != cell) - p = p->next; - p->next = cell->next; - delete cell; - } - } - } -} - -// +-------------------------------------------------------------------+ - -template -int Dictionary::contains(const Text& key) const -{ - int idx = key.hash() % CHAINS; - DictionaryCell* cell = table[idx]; - - if (cell != 0) { - while (cell->next && cell->key != key) - cell = cell->next; - - if (cell->key == key) - return 1; - } - - return 0; -} - -// +-------------------------------------------------------------------+ - -template -T Dictionary::find(const Text& key, T defval) const -{ - int idx = key.hash() % CHAINS; - DictionaryCell* cell = table[idx]; - - if (cell != 0) { - while (cell->next && cell->key != key) - cell = cell->next; - - if (cell->key == key) - return cell->value; - } - - return defval; -} - -// +-------------------------------------------------------------------+ - -template DictionaryIter::DictionaryIter(Dictionary& d) - : dict(&d), chain(0), here(0) -{ } - -template DictionaryIter::~DictionaryIter() -{ } - -// +-------------------------------------------------------------------+ - -template -void DictionaryIter::reset() -{ - chain = 0; - here = 0; -} - -// +-------------------------------------------------------------------+ - -template -Text DictionaryIter::key() const -{ - return here->key; -} - -template -T DictionaryIter::value() const -{ - return here->value; -} - -// +-------------------------------------------------------------------+ - -template -int DictionaryIter::operator++() -{ - forth(); - int more = chain < CHAINS; - return more; -} - -// +-------------------------------------------------------------------+ - -template -void DictionaryIter::forth() -{ - if (here) { - here = here->next; - if (!here) // off the end of this chain - chain++; - } - - if (!here) { - while (!dict->table[chain] && chain < CHAINS) - chain++; - - if (chain < CHAINS) - here = dict->table[chain]; - else - here = 0; - } -} - -// +-------------------------------------------------------------------+ - -template -void DictionaryIter::attach(Dictionary& d) -{ - dict = &d; - reset(); -} - -// +-------------------------------------------------------------------+ - -template -Dictionary& DictionaryIter::container() -{ - return *dict; -} - diff --git a/FoundationEx/List.h b/FoundationEx/List.h deleted file mode 100644 index 54dfcb1..0000000 --- a/FoundationEx/List.h +++ /dev/null @@ -1,106 +0,0 @@ -/* Starshatter: The Open Source Project - Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors - Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors - Copyright (c) 1997-2006, Destroyer Studios LLC. - - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Declaration of the List class template -*/ - -#ifndef List_h -#define List_h - -// +-------------------------------------------------------------------+ - -template class List; -template class ListIter; - -// +-------------------------------------------------------------------+ - -template class List -{ -public: - List() : items(0), extent(0), array(0) { } - List(const List& l); - ~List() { delete [] array; } - - T*& operator[](int i); - T* operator[](int i) const; - T*& at(int i); - T* at(int i) const; - - void append(List& list); - void append(const T* val); - void insert(const T* val, int index=0); - void insertSort(const T* val); - - T* first() const { return operator[](0); } - T* last() const { return operator[](items-1); } - T* remove(const T* val); - T* removeIndex(int index); - - void clear(); - void destroy(); - - int size() const { return items; } - bool isEmpty() const { return !items; } - - bool contains(const T* val) const; - int count(const T* val) const; - int index(const T* val) const; - T* find(const T* val) const; - - void sort(); - void shuffle(); - -private: - typedef T* PTR; - void qsort(T** a, int lo, int hi); - void resize(int newsize); - bool check(int& index) const; - void swap(T** a, int i, int j); - - int items; - int extent; - PTR* array; - - friend class ListIter; -}; - -// +-------------------------------------------------------------------+ - -template class ListIter -{ -public: - ListIter() : list(0), step(-1) { } - ListIter(const ListIter& i) : list(i.list), step(i.step) { } - ListIter(List& l) : list(&l), step(-1) { } - - int operator++() { return next() != 0; } - int operator--() { return prev() != 0; } - T* operator->() { return value(); } - T& operator* () { return *value(); } - - void reset() { step = -1; } - T* next(); - T* prev(); - T* value(); - T* removeItem(); - - void attach(List& l); - List& container(); - int size(); - int index() { return step; } - -private: - List* list; - int step; -}; - -#include "List.inl.h" -#endif // List_h - diff --git a/FoundationEx/List.inl.h b/FoundationEx/List.inl.h deleted file mode 100644 index 9ccb796..0000000 --- a/FoundationEx/List.inl.h +++ /dev/null @@ -1,432 +0,0 @@ -/* Starshatter: The Open Source Project - Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors - Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors - Copyright (c) 1997-2006, Destroyer Studios LLC. - - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Implementation of the List class template -*/ - -#include - -#include "Utils.h" - - -template -List::List(const List& l) - : items(l.items), extent(l.extent) -{ - array = new PTR[extent]; - for (int i = 0; i < extent; i++) - array[i] = l.array[i]; -} - -template -void List::clear() -{ - delete [] array; - items = 0; - extent = 0; - array = 0; -} - -template -void List::destroy() -{ - if (items) { - items = 0; // prevent dangerous re-entrancy - - for (int i = 0; i < extent; i++) - delete array[i]; - - delete [] array; - items = 0; - extent = 0; - array = 0; - } -} - -// +-------------------------------------------------------------------+ - -template -bool List::check(int& index) const -{ - if (index < 0) { - Print("Bounds error in List(%08p) T=%s index=%d min=0\n", this, T::TYPENAME(), index); - index = 0; - } - - else if (index >= items) { - Print("Bounds error in List(%08p) T=%s index=%d max=%d\n", this, T::TYPENAME(), index, items-1); - index = items-1; - } - - return (index >= 0 && index < items); -} - -// +-------------------------------------------------------------------+ - -template -T*& List::operator[](int index) -{ - if (check(index)) - return array[index]; - - if (!array || !extent) - resize(1); - - return array[0]; -} - -template -T* List::operator[](int index) const -{ - if (check(index)) - return array[index]; - return 0; -} - -template -T*& List::at(int index) -{ - if (check(index)) - return array[index]; - - if (!array || !extent) - resize(1); - - return array[0]; -} - -template -T* List::at(int index) const -{ - if (check(index)) - return array[index]; - return 0; -} - -// +-------------------------------------------------------------------+ - -template -void List::resize(int newsize) -{ - if (newsize > extent) { - extent = 16 * (newsize/16 + 1); - T** v = new PTR[extent]; - int i; - for (i = 0; i < items; i++) - v[i] = array[i]; - - for (; i < extent; i++) - v[i] = 0; - - delete [] array; - array = v; - } -} - -// +-------------------------------------------------------------------+ - -template -void List::append(const T* item) -{ - if (item) { - if (items+1 > extent) resize(items+1); - array[items++] = (T*)item; - } -} - -template -void List::append(List& list) -{ - if (&list != this && list.items > 0) { - int need = items + list.items; - if (need > extent) resize(need); - - for (int i = 0; i < list.items; i++) - array[items++] = list.array[i]; - } -} - -// +-------------------------------------------------------------------+ - -template -void List::insert(const T* item, int index) -{ - if (item && index >= 0 && index <= items) { - if (items+1 > extent) resize(items+1); - - // slide right: - for (int i = items; i > index; i--) - array[i] = array[i-1]; - - array[index] = (T*)item; - items++; - } -} - -// +-------------------------------------------------------------------+ - -template -void List::insertSort(const T* item) -{ - if (item) { - int i; - for (i = 0; i < items; i++) { - if (*item < *array[i]) - break; - } - - insert(item, i); - } -} - -// +-------------------------------------------------------------------+ - -template -T* List::remove(const T* val) -{ - if (items == 0 || val == 0) - return 0; - - for (int i = 0; i < items; i++) { - if (array[i] == val) { - return removeIndex(i); - } - } - - return 0; -} - -// +-------------------------------------------------------------------+ - -template -T* List::removeIndex(int index) -{ - if (!check(index)) - return 0; - - T* tmp = array[index]; - array[index] = 0; - - // slide left: - for (int i = index; i < items-1; i++) - array[i] = array[i+1]; - - // blank out the hole we just created: - array[items-1] = 0; - - items--; - return tmp; -} - -// +-------------------------------------------------------------------+ - -template -bool List::contains(const T* val) const -{ - if (val) { - if (index(val) != -1) - return true; - } - - return false; -} - -// +-------------------------------------------------------------------+ - -template -int List::count(const T* val) const -{ - int c = 0; - - if (val) { - for (int i = 0; i < items; i++) { - if (array[i] && ((*array[i])==(*val))) - c++; - } - } - - return c; -} - -// +-------------------------------------------------------------------+ - -template -int List::index(const T* val) const -{ - if (val) { - for (int i = 0; i < items; i++) { - if (array[i] && ((*array[i])==(*val))) - return i; - } - } - - return -1; -} - -// +-------------------------------------------------------------------+ - -template -T* List::find(const T* val) const -{ - if (val) { - for (int i = 0; i < items; i++) { - if (array[i] && ((*array[i])==(*val))) - return array[i]; - } - } - - return 0; -} - -// +-------------------------------------------------------------------+ - -template -void List::swap(T** a, int i, int j) -{ - if (i >= 0 && i < items && j >= 0 && j < items && i != j) { - T* t = a[i]; - a[i] = a[j]; - a[j] = t; - } -} - -template -void List::qsort(T** a, int lo0, int hi0) -{ - int lo = lo0; - int hi = hi0; - - // zero or one element list, nothing to do: - if (lo >= hi) { - return; - } - - // two element list, swap if needed: - else if (lo == hi-1) { - if (*a[hi] < *a[lo]) { - swap(a, lo, hi); - } - return; - } - - // pick a pivot, and move it out of the way: - int mid = (lo+hi)/2; - T* pivot = a[mid]; - a[mid] = a[hi]; - a[hi] = pivot; - - while (lo < hi) { - while ((*a[lo] <= *pivot) && lo < hi) lo++; - while ((*pivot <= *a[hi]) && lo < hi) hi--; - - if (lo < hi) { - swap(a, lo, hi); - } - } - - // Put the pivot into its final location: - a[hi0] = a[hi]; - a[hi] = pivot; - - qsort(a, lo0, lo-1); - qsort(a, hi+1, hi0); -} - -template -void List::sort() -{ - if (items < 2) - return; - - qsort(array, 0, items-1); -} - -template -void List::shuffle() -{ - if (items < 3) - return; - - for (int s = 0; s < 5; s++) { - for (int i = 0; i < items; i++) { - int j = (rand()>>4) % items; - swap(array, i, j); - } - } -} - -// +===================================================================+ - -template -T* ListIter::value() -{ - if (list && step >= 0 && step < list->items) - return list->array[step]; - - return 0; -} - -// +-------------------------------------------------------------------+ - -template -T* ListIter::removeItem() -{ - if (list && step >= 0 && step < list->items) - return list->removeIndex(step--); - - return 0; -} - -// +-------------------------------------------------------------------+ - -template -T* ListIter::next() -{ - if (list && step >= -1 && step < list->items-1) - return list->array[++step]; - - return 0; -} - -template -T* ListIter::prev() -{ - if (list && step > 0 && step < list->items) - return list->array[--step]; - - return 0; -} - -// +-------------------------------------------------------------------+ - -template -void ListIter::attach(List& l) -{ - list = &l; - step = -1; -} - -// +-------------------------------------------------------------------+ - -template -int ListIter::size() -{ - if (!list) return 0; - return list->items; -} - -// +-------------------------------------------------------------------+ - -template -List& ListIter::container() -{ - return *list; -} - diff --git a/FoundationEx/Reader.cpp b/FoundationEx/Reader.cpp deleted file mode 100644 index b39e9bc..0000000 --- a/FoundationEx/Reader.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* Starshatter: The Open Source Project - Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors - Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors - Copyright (c) 1997-2006, Destroyer Studios LLC. - - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Implementation of the Reader class -*/ - -#include "Reader.h" - -#include -#include -#include - -#include "Utils.h" - - -Text -ConsoleReader::more() -{ - // loop until the user types something - do { - printPrimaryPrompt(); - fillInputBuffer(); - } while (! *p); - - return Text(p); -} - - -void -ConsoleReader::printPrimaryPrompt() -{ - std::printf("- "); -} - - -void -ConsoleReader::fillInputBuffer() -{ - std::fgets(buffer, 980, stdin); - p = buffer; - while (std::isspace(*p)) p++; -} - - -FileReader::FileReader(const char* fname) : - filename(fname), - done(0) -{ -} - - -Text -FileReader::more() -{ - if (done) return Text(); - - std::fstream fin(filename, std::fstream::in); - - if (!fin) { - Print("ERROR(Parse): Could not open file '%s'\n", filename.data()); - return Text(); - } - - Text result; - char buf[1000], newline; - - while (fin.get(buf, 1000)) { - result.append(buf); - fin.get(newline); - result.append(newline); - } - - done = 1; - return result; -} - - -BlockReader::BlockReader(const char* block) : - data((char*) block), - done(0), - length(0) -{ -} - - -BlockReader::BlockReader(const char* block, int len) : - data((char*) block), - done(0), - length(len) -{ -} - - -Text -BlockReader::more() -{ - if (done) return Text(); - - if (length) { - Text result(data, length); - done = 1; - return result; - } - else if (data) { - Text result(data); - done = 1; - return result; - } - - done = 1; - return Text(); -} diff --git a/FoundationEx/Reader.h b/FoundationEx/Reader.h deleted file mode 100644 index ed6aebc..0000000 --- a/FoundationEx/Reader.h +++ /dev/null @@ -1,70 +0,0 @@ -/* Starshatter: The Open Source Project - Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors - Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors - Copyright (c) 1997-2006, Destroyer Studios LLC. - - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Declaration of the Reader class -*/ - -#ifndef Reader_h -#define Reader_h - -#include "Text.h" - - -class Reader -{ -public: - Reader() { } - virtual ~Reader() { } - - virtual Text more() = 0; -}; - - -class ConsoleReader : public Reader -{ -public: - virtual Text more() override; - - void printPrimaryPrompt(); - void fillInputBuffer(); - -private: - char buffer[1000]; - char* p; -}; - - -class FileReader : public Reader -{ -public: - FileReader(const char* fname); - virtual Text more() override; - -private: - Text filename; - int done; -}; - - -class BlockReader : public Reader -{ -public: - BlockReader(const char* block); - BlockReader(const char* block, int len); - virtual Text more(); - -private: - char* data; - int done; - int length; -}; - - -#endif // Reader_h diff --git a/FoundationEx/Text.cpp b/FoundationEx/Text.cpp deleted file mode 100644 index 442a048..0000000 --- a/FoundationEx/Text.cpp +++ /dev/null @@ -1,678 +0,0 @@ -/* Starshatter: The Open Source Project - Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors - Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors - Copyright (c) 1997-2006, Destroyer Studios LLC. - - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Implementation of the Text class -*/ - -#include "Text.h" - -#include -#include -#include - -// +-------------------------------------------------------------------+ -// SPECIAL TEXT REP FOR NULL STRINGS -// This is used only by the default constructor for the Text object, -// to prevent extra rep allocation when constructing empty strings. - -TextRep TextRep::nullrep; - -TextRep::TextRep() - : ref(1234567), data(0), length(0), hash(0), sensitive(true) -{ - data = new char[4]; - - if (data) - std::memset(data, 0, 4); -} - -// +-------------------------------------------------------------------+ - - -TextRep::TextRep(const char* s) - : ref(1), length(0), sensitive(true) -{ - if (s) length = std::strlen(s); - - data = new char[length+1]; - - if (data) { - if (s) std::strcpy(data, s); - else data[length] = '\0'; - - dohash(); - } -} - -TextRep::TextRep(const char* s, int len) - : ref(1), length(len), sensitive(true) -{ - if (length < 0) length = 0; - - data = new char[length+1]; - - if (data) { - std::memcpy(data, s, length); - data[length] = '\0'; - dohash(); - } -} - -TextRep::TextRep(char c, int len) - : ref(1), length(len), sensitive(true) -{ - if (length < 0) length = 0; - - data = new char[length+1]; - - if (data) { - std::memset(data, c, length); - data[length] = '\0'; - dohash(); - } -} - -TextRep::TextRep(const TextRep* rep) - : ref(1) -{ - length = rep->length; - - data = new char[length+1]; - - hash = rep->hash; - sensitive = rep->sensitive; - - if (data) - std::strcpy(data, rep->data); -} - -TextRep::~TextRep() -{ - delete[] data; -} - -void -TextRep::addref() -{ - ref++; -} - -long -TextRep::deref() -{ - long r = --ref; - return r; -} - -inline static void mash(unsigned& hash, unsigned chars) -{ - hash = (chars ^ ((hash << 5) | (hash >> (8*sizeof(unsigned) - 5)))); -} - -void -TextRep::dohash() -{ - unsigned hv = (unsigned)length; // Mix in the string length. - unsigned i = length*sizeof(char)/sizeof(unsigned); - const unsigned* p = (const unsigned*)data; - - while (i--) - mash(hv, *p++); // XOR in the characters. - - // XOR in any remaining characters: - i = length*sizeof(char)%sizeof(unsigned); - if (i) { - unsigned h = 0; - const char* c = (const char*)p; - while (i--) - h = ((h << 8*sizeof(char)) | *c++); - mash(hv, h); - } - - hash = hv; -} - -// +-------------------------------------------------------------------+ - -Text::Text() -{ - rep = &TextRep::nullrep; - rep->addref(); - sym = rep->data; -} - -Text::Text(char c) -{ - char buf[2]; buf[0] = c; buf[1] = '\0'; - - rep = new TextRep(buf); - - if (!rep) { - rep = &TextRep::nullrep; - rep->addref(); - } - - sym = rep->data; -} - -Text::Text(const char* s) -{ - rep = new TextRep(s); - - if (!rep) { - rep = &TextRep::nullrep; - rep->addref(); - } - - sym = rep->data; -} - -Text::Text(const char* s, int len) -{ - rep = new TextRep(s, len); - - if (!rep) { - rep = &TextRep::nullrep; - rep->addref(); - } - - sym = rep->data; -} - -Text::Text(char c, int len) -{ - rep = new TextRep(c, len); - - if (!rep) { - rep = &TextRep::nullrep; - rep->addref(); - } - - sym = rep->data; -} - -Text::Text(const Text& s) -{ - rep = s.rep; - rep->addref(); - sym = rep->data; -} - -Text::~Text() -{ - if (rep->deref() == 0) delete rep; - - rep = &TextRep::nullrep; - sym = rep->data; -} - -Text& -Text::operator=(const char* s) -{ - if (rep->deref() == 0) delete rep; - rep = new TextRep(s); - - if (!rep) - rep = &TextRep::nullrep; - sym = rep->data; - return *this; -} - -Text& -Text::operator=(const Text& s) -{ - s.rep->addref(); - if (rep->deref() == 0) delete rep; - rep = s.rep; - sym = rep->data; - return *this; -} - -Text -Text::operator+(char c) -{ - char* buf = new char[rep->length + 2]; - - if (buf) { - std::strcpy(buf, sym); - buf[rep->length] = c; - buf[rep->length+1] = '\0'; - Text retval(buf); - delete [] buf; - return retval; - } - - else { - return *this; - } -} - -Text -Text::operator+(const char* s) -{ - char* buf = new char[std::strlen(s) + rep->length + 1]; - - if (buf) { - std::strcpy(buf, sym); - std::strcat(buf, s); - Text retval(buf); - delete [] buf; - return retval; - } - - else { - return *this; - } -} - -Text -Text::operator+(const Text& s) -{ - char* buf = new char[s.rep->length + rep->length + 1]; - - if (buf) { - std::strcpy(buf, sym); - std::strcat(buf, s.sym); - Text retval(buf); - delete [] buf; - return retval; - } - - else { - return *this; - } -} - -bool -Text::isSensitive() const -{ - return rep->sensitive; -} - -void -Text::setSensitive(bool s) -{ - rep->sensitive = s; -} - -Text& -Text::append(char c) -{ - char* buf = new char[rep->length + 2]; - - if (buf) { - std::strcpy(buf, sym); - buf[rep->length] = c; - buf[rep->length+1] = '\0'; - if (rep->deref() == 0) delete rep; - - rep = new TextRep(buf); - - if (!rep) - rep = &TextRep::nullrep; - - sym = rep->data; - delete [] buf; - } - - return *this; -} - -Text& -Text::append(const char* s) -{ - char* buf = new char[std::strlen(s) + rep->length + 1]; - - if (buf) { - std::strcpy(buf, sym); - std::strcat(buf, s); - if (rep->deref() == 0) delete rep; - - rep = new TextRep(buf); - - if (!rep) - rep = &TextRep::nullrep; - - sym = rep->data; - delete [] buf; - } - - return *this; -} - -Text& -Text::append(const Text& s) -{ - char* buf = new char[s.rep->length + rep->length + 1]; - - if (buf) { - int lenA = rep->length; - int lenB = s.rep->length; - - std::memcpy(buf, sym, lenA); - std::memcpy(buf + lenA, s.sym, lenB); - buf[lenA + lenB] = 0; - - if (rep->deref() == 0) delete rep; - - rep = new TextRep(buf); - - if (!rep) - rep = &TextRep::nullrep; - - sym = rep->data; - delete [] buf; - } - - return *this; -} - -void -Text::clone() -{ - if (rep->ref > 1) { - rep->deref(); - - TextRep* t = new TextRep(rep); - - rep = t; - - if (!rep) - rep = &TextRep::nullrep; - - sym = rep->data; - } -} - -char -Text::operator[](int index) const -{ - if (index < (int) rep->length) - return sym[index]; - else - throw "BOUNDS ERROR"; - - return '\0'; -} - -char -Text::operator()(int index) const -{ - return sym[index]; -} - -char& -Text::operator[](int index) -{ - if (index < (int) rep->length) { - clone(); - return (char&) sym[index]; - } - else - throw "BOUNDS ERROR"; - - return (char&) sym[0]; -} - -char& -Text::operator()(int index) -{ - clone(); - return (char&) sym[index]; -} - -Text -Text::operator()(int start, int len) const -{ - if (start > rep->length || len <= 0) - return Text(); - - if (start + len > rep->length) - len = rep->length - start; - - char* buf = new char[len+1]; - - if (buf) { - std::strncpy(buf, sym+start, len); - buf[len] = '\0'; - - Text retval(buf); - delete [] buf; - return retval; - } - - return Text(); -} - -bool -Text::contains(char c) const -{ - if (rep->length > 0) { - if (!rep->sensitive) { - char alt = c; - if (std::islower(alt)) alt = std::toupper(alt); - else if (std::isupper(alt)) alt = std::tolower(alt); - - if (std::strchr(rep->data, alt) != 0) - return true; - } - - if (std::strchr(rep->data, c) != 0) - return true; - } - - return false; -} - -bool -Text::contains(const char* pattern) const -{ - if (rep->length > 0 && pattern && *pattern) { - if (rep->sensitive) { - if (std::strstr(rep->data, pattern) != 0) - return true; - } - else { - Text smash1(*this); - smash1.toLower(); - Text smash2(pattern); - smash2.toLower(); - - if (std::strstr(smash1.data(), smash2.data()) != 0) - return true; - } - } - - return false; -} - -bool -Text::containsAnyOf(const char* charSet) const -{ - if (rep->length > 0 && charSet && *charSet) { - if (rep->sensitive) { - if (strpbrk(rep->data, charSet) != 0) - return true; - } - else { - Text smash1(*this); - smash1.toLower(); - Text smash2(charSet); - smash2.toLower(); - - if (strpbrk(smash1.data(), smash2.data()) != 0) - return true; - } - } - - return false; -} - -int -Text::indexOf(char c) const -{ - if (rep->length > 0) { - if (!rep->sensitive) { - char alt = c; - if (std::islower(alt)) alt = std::toupper(alt); - else if (std::isupper(alt)) alt = std::tolower(alt); - - const char* p = std::strchr(rep->data, alt); - - if (p) - return (p - rep->data); - } - - const char* p = std::strchr(rep->data, c); - - if (p) - return (p - rep->data); - } - - return -1; -} - -int -Text::indexOf(const char* pattern) const -{ - if (rep->length > 0 && pattern && *pattern) { - if (rep->sensitive) { - const char* p = std::strstr(rep->data, pattern); - if (p) return (p - rep->data); - } - else { - Text smash1(*this); - smash1.toLower(); - Text smash2(pattern); - smash2.toLower(); - - const char* p = std::strstr(smash1.data(), smash2.data()); - if (p) return (p - smash1.data()); - } - } - - return -1; -} - -void -Text::toLower() -{ - clone(); - size_t n = rep->length; - char* p = (char*) sym; - while (n--) { - *p = std::tolower((unsigned char)*p); - p++; - } - - rep->dohash(); -} - -void -Text::toUpper() -{ - clone(); - size_t n = rep->length; - char* p = (char*) sym; - while ( n-- ) { - *p = std::toupper((unsigned char)*p); - p++; - } - - rep->dohash(); -} - -Text -Text::substring(int start, int length) -{ - Text result; - - if (start >= 0 && start < (int) rep->length && length > 0) { - if (start + length > (int) rep->length) - length = (int) rep->length - start; - - const char* s = sym + start; - - result.rep = new TextRep(s, length); - - if (!result.rep) - result.rep = &TextRep::nullrep; - - result.sym = result.rep->data; - } - - return result; -} - -Text -Text::trim() -{ - Text result; - - if (rep->length) { - const char* p = sym; - const char* q = sym + rep->length-1; - - while (p && *p && isspace(*p)) p++; - while (q>p && *q && isspace(*q)) q--; - - result = substring(p-sym, q-p+1); - } - - return result; -} - -Text -Text::replace(const char* pattern, const char* substitution) -{ - Text result; - - if (rep->length && pattern && *pattern) { - const char* pos = rep->data; - const char* bound = rep->data + rep->length; - int skip = std::strlen(pattern); - do { - const char* p = std::strstr(pos, pattern); - if (p) { - int len = p - pos; - result.append(Text(pos, len)); - result.append(substitution); - pos = p + skip; - } - else if (pos < bound) { - result.append(pos); - break; - } - } - while (pos < bound); - } - - return result; -} - -Text -Text::concat(const char* tail) const -{ - Text result(*this); - result.append(tail); - return result; -} - -Text -Text::format(const char* fmt, ...) -{ - char buf[2048]; - std::va_list args; - va_start(args, fmt); - int len = std::vsnprintf(buf, 2048, fmt, args); - Text result(buf, len); - va_end(args); - return result; -} diff --git a/FoundationEx/Text.h b/FoundationEx/Text.h deleted file mode 100644 index 37e5842..0000000 --- a/FoundationEx/Text.h +++ /dev/null @@ -1,209 +0,0 @@ -/* Starshatter: The Open Source Project - Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors - Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors - Copyright (c) 1997-2006, Destroyer Studios LLC. - - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - Declaration of the Text class -*/ - -#ifndef Text_h -#define Text_h - -#include -#include -#include - -#ifndef _WIN32 -#include -#endif - -// +-------------------------------------------------------------------+ - -class TextRep -{ - friend class Text; - -public: - TextRep(); - ~TextRep(); - -private: - TextRep(const char* s); - TextRep(const char* s, int len); - TextRep(char c, int len); - TextRep(const TextRep* rep); - - void addref(); - long deref(); - - void dohash(); - - char* data; - std::atomic ref; - int length; - unsigned hash; - bool sensitive; - - static TextRep nullrep; -}; - -// +-------------------------------------------------------------------+ - -class Text -{ -public: - static const char* TYPENAME() { return "Text"; } - - Text(); - Text(char c); - Text(const char* s); - Text(const char* s, int len); - Text(char c, int len); - Text(const Text& s); - ~Text(); - - friend std::ostream& operator<<(std::ostream& out, const Text& text); - - // case sensitivity - bool isSensitive() const; - void setSensitive(bool s); - - // comparison - int compare(const char* s) const; - int compare(const Text& s) const; - - // assignment - Text& operator=(const char* s); - Text& operator=(const Text& s); - - // catenation - Text& append(char c); - Text& append(const char* s); - Text& append(const Text& s); - - Text operator+(char c); - Text operator+(const char* s); - Text operator+(const Text& s); - - Text& operator+=(char c) { return append(c); } - Text& operator+=(const char* s) { return append(s); } - Text& operator+=(const Text& s) { return append(s); } - - // indexing - char operator[](int index) const; - char operator()(int index) const; - char& operator[](int index); - char& operator()(int index); - - Text operator()(int start, int len) const; - - // access - int length() const { return rep->length; } - unsigned hash() const { return rep->hash; } - - const char* data() const { return sym; } - operator const char* () const { return sym; } - - bool contains(char c) const; - bool contains(const char* s) const; - - bool containsAnyOf(const char* charSet) const; - - int indexOf(char c) const; - int indexOf(const char* s) const; - - // mutation - void toLower(); - void toUpper(); - - // substring - Text substring(int start, int length); - Text trim(); - Text replace(const char* pattern, const char* substitution); - Text concat(const char* tail) const; - static Text format(const char* fmt, ...); - -private: - void clone(); - - const char* sym; - TextRep* rep; -}; - -// +-------------------------------------------------------------------+ - -inline int Text::compare(const char* s) const -{ - if (rep->sensitive) - return strcmp(sym, s); - else -#ifdef _WIN32 - return _stricmp(sym, s); -#else - return strcasecmp(sym, s); -#endif -} - -inline int Text::compare(const Text& s) const -{ - if (rep->sensitive && s.rep->sensitive) - return strcmp(sym, s.sym); - else -#ifdef _WIN32 - return _stricmp(sym, s.sym); -#else - return strcasecmp(sym, s.sym); -#endif -} - -// +-------------------------------------------------------------------+ - -inline std::ostream& operator<<(std::ostream& out, const Text& text) { - out << text.data(); return out; } -inline int operator==(const Text& l, const Text& r) { - return (l.length() == r.length()) && (l.compare(r) == 0); } -inline int operator!=(const Text& l, const Text& r) { return l.compare(r) != 0; } -inline int operator< (const Text& l, const Text& r) { return l.compare(r) < 0; } -inline int operator<=(const Text& l, const Text& r) { return l.compare(r) <= 0; } -inline int operator> (const Text& l, const Text& r) { return l.compare(r) > 0; } -inline int operator>=(const Text& l, const Text& r) { return l.compare(r) >= 0; } - -inline int operator==(const char* l, const Text& r) { return r.compare(l) == 0; } -inline int operator!=(const char* l, const Text& r) { return r.compare(l) != 0; } -inline int operator< (const char* l, const Text& r) { return r.compare(l) < 0; } -inline int operator<=(const char* l, const Text& r) { return r.compare(l) <= 0; } -inline int operator> (const char* l, const Text& r) { return r.compare(l) > 0; } -inline int operator>=(const char* l, const Text& r) { return r.compare(l) >= 0; } - -inline int operator==( char* l, const Text& r) { return r.compare(l) == 0; } -inline int operator!=( char* l, const Text& r) { return r.compare(l) != 0; } -inline int operator< ( char* l, const Text& r) { return r.compare(l) < 0; } -inline int operator<=( char* l, const Text& r) { return r.compare(l) <= 0; } -inline int operator> ( char* l, const Text& r) { return r.compare(l) > 0; } -inline int operator>=( char* l, const Text& r) { return r.compare(l) >= 0; } - -inline int operator==(const Text& l, const char* r) { return l.compare(r) == 0; } -inline int operator!=(const Text& l, const char* r) { return l.compare(r) != 0; } -inline int operator< (const Text& l, const char* r) { return l.compare(r) < 0; } -inline int operator<=(const Text& l, const char* r) { return l.compare(r) <= 0; } -inline int operator> (const Text& l, const char* r) { return l.compare(r) > 0; } -inline int operator>=(const Text& l, const char* r) { return l.compare(r) >= 0; } - -inline int operator==(const Text& l, char* r) { return l.compare(r) == 0; } -inline int operator!=(const Text& l, char* r) { return l.compare(r) != 0; } -inline int operator< (const Text& l, char* r) { return l.compare(r) < 0; } -inline int operator<=(const Text& l, char* r) { return l.compare(r) <= 0; } -inline int operator> (const Text& l, char* r) { return l.compare(r) > 0; } -inline int operator>=(const Text& l, char* r) { return l.compare(r) >= 0; } - -inline Text operator+(const char* l, const Text& r) { return Text(l) + r; } -inline Text operator+( char* l, const Text& r) { return Text(l) + r; } - -// +-------------------------------------------------------------------+ - -#endif // Text_h diff --git a/FoundationEx/Utils.cpp b/FoundationEx/Utils.cpp deleted file mode 100644 index e3b1861..0000000 --- a/FoundationEx/Utils.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* Starshatter: The Open Source Project - Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors - Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors - Copyright (c) 1997-2006, Destroyer Studios LLC. -*/ - -#include "Utils.h" - -#include -#include - - -FILE* ErrLog = nullptr; - - -void AssignErrLog(FILE * out) -{ - ErrLog = out; -} - - -int CloseErrLog() -{ - if (ErrLog) - return fclose(ErrLog); - return 0; -} - - -void Print(const char* fmt, ...) -{ - if (!ErrLog) return; - va_list args; - va_start(args, fmt); - vfprintf(ErrLog, fmt, args); - fflush(ErrLog); - va_end(args); -} diff --git a/FoundationEx/Utils.h b/FoundationEx/Utils.h deleted file mode 100644 index 731bc01..0000000 --- a/FoundationEx/Utils.h +++ /dev/null @@ -1,18 +0,0 @@ -/* Starshatter: The Open Source Project - Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors - Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors - Copyright (c) 1997-2006, Destroyer Studios LLC. -*/ - -#ifndef Utils_h -#define Utils_h - -#include - - -void AssignErrLog(FILE * out); -int CloseErrLog(); -void Print(const char* fmt, ...); - - -#endif // Utils_h diff --git a/FoundationEx/include/Dictionary.h b/FoundationEx/include/Dictionary.h new file mode 100644 index 0000000..bcb30ed --- /dev/null +++ b/FoundationEx/include/Dictionary.h @@ -0,0 +1,100 @@ +/* Starshatter: The Open Source Project + Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors + Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors + Copyright (c) 1997-2006, Destroyer Studios LLC. + + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Declaration of the Dictionary class +*/ + +#ifndef Dictionary_h +#define Dictionary_h + +#include "Text.h" + +// +-------------------------------------------------------------------+ + +template class Dictionary; +template class DictionaryIter; +template class DictionaryCell; + +// +-------------------------------------------------------------------+ + +template class Dictionary +{ +public: + Dictionary(); + ~Dictionary(); + + T& operator[](const Text& key); + + void insert(const Text& key, const T& val); + void remove(const Text& key); + + void clear(); + + int size() const { return items; } + int isEmpty() const { return !items; } + + int contains(const Text& key) const; + T find(const Text& key, T defval) const; + +private: + void init(); + + int items; + + typedef DictionaryCell* PTR; + PTR table[256]; + + friend class DictionaryIter; +}; + +// +-------------------------------------------------------------------+ + +template class DictionaryIter +{ +public: + DictionaryIter(Dictionary& l); + ~DictionaryIter(); + + int operator++(); // prefix + + void reset(); + void forth(); + + Text key() const; + T value() const; + + void attach(Dictionary& l); + Dictionary& container(); + +private: + Dictionary* dict; + DictionaryCell* here; + int chain; +}; + +// +-------------------------------------------------------------------+ + +template class DictionaryCell +{ +public: + DictionaryCell(const Text& k) : key(k), value( ), next(0) { } + DictionaryCell(const Text& k, const T& v) : key(k), value(v), next(0) { } + ~DictionaryCell() { } + + Text key; + T value; + DictionaryCell* next; +}; + +// +-------------------------------------------------------------------+ + +#include "Dictionary.inl.h" +#endif // Dictionary_h + diff --git a/FoundationEx/include/Dictionary.inl.h b/FoundationEx/include/Dictionary.inl.h new file mode 100644 index 0000000..cd58679 --- /dev/null +++ b/FoundationEx/include/Dictionary.inl.h @@ -0,0 +1,246 @@ +/* Starshatter: The Open Source Project + Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors + Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors + Copyright (c) 1997-2006, Destroyer Studios LLC. + + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Implementation of the Dictionary class +*/ + +#ifndef NDEBUG +#define DICT_CHECK(a, b) if ((a) == 0) throw b; +#else +#define DICT_CHECK(a, b) +#endif + +const int CHAINS = 256; + +// +-------------------------------------------------------------------+ + +template Dictionary::Dictionary() + : items(0) +{ init(); } + +template Dictionary::~Dictionary() +{ clear(); } + +// +-------------------------------------------------------------------+ + +template +void Dictionary::init() +{ + items = 0; + memset(table, 0, CHAINS*sizeof(PTR)); +} + +template +void Dictionary::clear() +{ + for (int i = 0; i < CHAINS; i++) { + DictionaryCell* link = table[i]; + + while (link) { + DictionaryCell* n = link->next; + delete link; + link = n; + } + } + + init(); +} + +// +-------------------------------------------------------------------+ + +template +T& Dictionary::operator[](const Text& key) +{ + int idx = key.hash() % CHAINS; + DictionaryCell* cell = table[idx]; + + if (cell == 0) { // empty chain + items++; + cell = new DictionaryCell(key); + table[idx] = cell; + return cell->value; + } + else { // search for key + while (cell->next && cell->key != key) + cell = cell->next; + + if (cell->key != key) { // not found in chain + items++; + cell->next = new DictionaryCell(key); + return cell->next->value; + } + else { // found: return it! + return cell->value; + } + } +} + +// +-------------------------------------------------------------------+ + +template +void Dictionary::insert(const Text& key, const T& val) +{ + T& value = operator[](key); + value = val; +} + +// +-------------------------------------------------------------------+ + +template +void Dictionary::remove(const Text& key) +{ + int idx = key.hash() % CHAINS; + DictionaryCell* cell = table[idx]; + + if (cell == 0) { // empty chain + return; + } + else { // search for key + while (cell->next && cell->key != key) + cell = cell->next; + + if (cell->key != key) { // not found in chain + return; + } + else { // found: remove it! + if (table[idx] == cell) { + table[idx] = cell->next; + delete cell; + } + else { + DictionaryCell* p = table[idx]; + while (p->next != cell) + p = p->next; + p->next = cell->next; + delete cell; + } + } + } +} + +// +-------------------------------------------------------------------+ + +template +int Dictionary::contains(const Text& key) const +{ + int idx = key.hash() % CHAINS; + DictionaryCell* cell = table[idx]; + + if (cell != 0) { + while (cell->next && cell->key != key) + cell = cell->next; + + if (cell->key == key) + return 1; + } + + return 0; +} + +// +-------------------------------------------------------------------+ + +template +T Dictionary::find(const Text& key, T defval) const +{ + int idx = key.hash() % CHAINS; + DictionaryCell* cell = table[idx]; + + if (cell != 0) { + while (cell->next && cell->key != key) + cell = cell->next; + + if (cell->key == key) + return cell->value; + } + + return defval; +} + +// +-------------------------------------------------------------------+ + +template DictionaryIter::DictionaryIter(Dictionary& d) + : dict(&d), chain(0), here(0) +{ } + +template DictionaryIter::~DictionaryIter() +{ } + +// +-------------------------------------------------------------------+ + +template +void DictionaryIter::reset() +{ + chain = 0; + here = 0; +} + +// +-------------------------------------------------------------------+ + +template +Text DictionaryIter::key() const +{ + return here->key; +} + +template +T DictionaryIter::value() const +{ + return here->value; +} + +// +-------------------------------------------------------------------+ + +template +int DictionaryIter::operator++() +{ + forth(); + int more = chain < CHAINS; + return more; +} + +// +-------------------------------------------------------------------+ + +template +void DictionaryIter::forth() +{ + if (here) { + here = here->next; + if (!here) // off the end of this chain + chain++; + } + + if (!here) { + while (!dict->table[chain] && chain < CHAINS) + chain++; + + if (chain < CHAINS) + here = dict->table[chain]; + else + here = 0; + } +} + +// +-------------------------------------------------------------------+ + +template +void DictionaryIter::attach(Dictionary& d) +{ + dict = &d; + reset(); +} + +// +-------------------------------------------------------------------+ + +template +Dictionary& DictionaryIter::container() +{ + return *dict; +} + diff --git a/FoundationEx/include/List.h b/FoundationEx/include/List.h new file mode 100644 index 0000000..54dfcb1 --- /dev/null +++ b/FoundationEx/include/List.h @@ -0,0 +1,106 @@ +/* Starshatter: The Open Source Project + Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors + Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors + Copyright (c) 1997-2006, Destroyer Studios LLC. + + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Declaration of the List class template +*/ + +#ifndef List_h +#define List_h + +// +-------------------------------------------------------------------+ + +template class List; +template class ListIter; + +// +-------------------------------------------------------------------+ + +template class List +{ +public: + List() : items(0), extent(0), array(0) { } + List(const List& l); + ~List() { delete [] array; } + + T*& operator[](int i); + T* operator[](int i) const; + T*& at(int i); + T* at(int i) const; + + void append(List& list); + void append(const T* val); + void insert(const T* val, int index=0); + void insertSort(const T* val); + + T* first() const { return operator[](0); } + T* last() const { return operator[](items-1); } + T* remove(const T* val); + T* removeIndex(int index); + + void clear(); + void destroy(); + + int size() const { return items; } + bool isEmpty() const { return !items; } + + bool contains(const T* val) const; + int count(const T* val) const; + int index(const T* val) const; + T* find(const T* val) const; + + void sort(); + void shuffle(); + +private: + typedef T* PTR; + void qsort(T** a, int lo, int hi); + void resize(int newsize); + bool check(int& index) const; + void swap(T** a, int i, int j); + + int items; + int extent; + PTR* array; + + friend class ListIter; +}; + +// +-------------------------------------------------------------------+ + +template class ListIter +{ +public: + ListIter() : list(0), step(-1) { } + ListIter(const ListIter& i) : list(i.list), step(i.step) { } + ListIter(List& l) : list(&l), step(-1) { } + + int operator++() { return next() != 0; } + int operator--() { return prev() != 0; } + T* operator->() { return value(); } + T& operator* () { return *value(); } + + void reset() { step = -1; } + T* next(); + T* prev(); + T* value(); + T* removeItem(); + + void attach(List& l); + List& container(); + int size(); + int index() { return step; } + +private: + List* list; + int step; +}; + +#include "List.inl.h" +#endif // List_h + diff --git a/FoundationEx/include/List.inl.h b/FoundationEx/include/List.inl.h new file mode 100644 index 0000000..9ccb796 --- /dev/null +++ b/FoundationEx/include/List.inl.h @@ -0,0 +1,432 @@ +/* Starshatter: The Open Source Project + Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors + Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors + Copyright (c) 1997-2006, Destroyer Studios LLC. + + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Implementation of the List class template +*/ + +#include + +#include "Utils.h" + + +template +List::List(const List& l) + : items(l.items), extent(l.extent) +{ + array = new PTR[extent]; + for (int i = 0; i < extent; i++) + array[i] = l.array[i]; +} + +template +void List::clear() +{ + delete [] array; + items = 0; + extent = 0; + array = 0; +} + +template +void List::destroy() +{ + if (items) { + items = 0; // prevent dangerous re-entrancy + + for (int i = 0; i < extent; i++) + delete array[i]; + + delete [] array; + items = 0; + extent = 0; + array = 0; + } +} + +// +-------------------------------------------------------------------+ + +template +bool List::check(int& index) const +{ + if (index < 0) { + Print("Bounds error in List(%08p) T=%s index=%d min=0\n", this, T::TYPENAME(), index); + index = 0; + } + + else if (index >= items) { + Print("Bounds error in List(%08p) T=%s index=%d max=%d\n", this, T::TYPENAME(), index, items-1); + index = items-1; + } + + return (index >= 0 && index < items); +} + +// +-------------------------------------------------------------------+ + +template +T*& List::operator[](int index) +{ + if (check(index)) + return array[index]; + + if (!array || !extent) + resize(1); + + return array[0]; +} + +template +T* List::operator[](int index) const +{ + if (check(index)) + return array[index]; + return 0; +} + +template +T*& List::at(int index) +{ + if (check(index)) + return array[index]; + + if (!array || !extent) + resize(1); + + return array[0]; +} + +template +T* List::at(int index) const +{ + if (check(index)) + return array[index]; + return 0; +} + +// +-------------------------------------------------------------------+ + +template +void List::resize(int newsize) +{ + if (newsize > extent) { + extent = 16 * (newsize/16 + 1); + T** v = new PTR[extent]; + int i; + for (i = 0; i < items; i++) + v[i] = array[i]; + + for (; i < extent; i++) + v[i] = 0; + + delete [] array; + array = v; + } +} + +// +-------------------------------------------------------------------+ + +template +void List::append(const T* item) +{ + if (item) { + if (items+1 > extent) resize(items+1); + array[items++] = (T*)item; + } +} + +template +void List::append(List& list) +{ + if (&list != this && list.items > 0) { + int need = items + list.items; + if (need > extent) resize(need); + + for (int i = 0; i < list.items; i++) + array[items++] = list.array[i]; + } +} + +// +-------------------------------------------------------------------+ + +template +void List::insert(const T* item, int index) +{ + if (item && index >= 0 && index <= items) { + if (items+1 > extent) resize(items+1); + + // slide right: + for (int i = items; i > index; i--) + array[i] = array[i-1]; + + array[index] = (T*)item; + items++; + } +} + +// +-------------------------------------------------------------------+ + +template +void List::insertSort(const T* item) +{ + if (item) { + int i; + for (i = 0; i < items; i++) { + if (*item < *array[i]) + break; + } + + insert(item, i); + } +} + +// +-------------------------------------------------------------------+ + +template +T* List::remove(const T* val) +{ + if (items == 0 || val == 0) + return 0; + + for (int i = 0; i < items; i++) { + if (array[i] == val) { + return removeIndex(i); + } + } + + return 0; +} + +// +-------------------------------------------------------------------+ + +template +T* List::removeIndex(int index) +{ + if (!check(index)) + return 0; + + T* tmp = array[index]; + array[index] = 0; + + // slide left: + for (int i = index; i < items-1; i++) + array[i] = array[i+1]; + + // blank out the hole we just created: + array[items-1] = 0; + + items--; + return tmp; +} + +// +-------------------------------------------------------------------+ + +template +bool List::contains(const T* val) const +{ + if (val) { + if (index(val) != -1) + return true; + } + + return false; +} + +// +-------------------------------------------------------------------+ + +template +int List::count(const T* val) const +{ + int c = 0; + + if (val) { + for (int i = 0; i < items; i++) { + if (array[i] && ((*array[i])==(*val))) + c++; + } + } + + return c; +} + +// +-------------------------------------------------------------------+ + +template +int List::index(const T* val) const +{ + if (val) { + for (int i = 0; i < items; i++) { + if (array[i] && ((*array[i])==(*val))) + return i; + } + } + + return -1; +} + +// +-------------------------------------------------------------------+ + +template +T* List::find(const T* val) const +{ + if (val) { + for (int i = 0; i < items; i++) { + if (array[i] && ((*array[i])==(*val))) + return array[i]; + } + } + + return 0; +} + +// +-------------------------------------------------------------------+ + +template +void List::swap(T** a, int i, int j) +{ + if (i >= 0 && i < items && j >= 0 && j < items && i != j) { + T* t = a[i]; + a[i] = a[j]; + a[j] = t; + } +} + +template +void List::qsort(T** a, int lo0, int hi0) +{ + int lo = lo0; + int hi = hi0; + + // zero or one element list, nothing to do: + if (lo >= hi) { + return; + } + + // two element list, swap if needed: + else if (lo == hi-1) { + if (*a[hi] < *a[lo]) { + swap(a, lo, hi); + } + return; + } + + // pick a pivot, and move it out of the way: + int mid = (lo+hi)/2; + T* pivot = a[mid]; + a[mid] = a[hi]; + a[hi] = pivot; + + while (lo < hi) { + while ((*a[lo] <= *pivot) && lo < hi) lo++; + while ((*pivot <= *a[hi]) && lo < hi) hi--; + + if (lo < hi) { + swap(a, lo, hi); + } + } + + // Put the pivot into its final location: + a[hi0] = a[hi]; + a[hi] = pivot; + + qsort(a, lo0, lo-1); + qsort(a, hi+1, hi0); +} + +template +void List::sort() +{ + if (items < 2) + return; + + qsort(array, 0, items-1); +} + +template +void List::shuffle() +{ + if (items < 3) + return; + + for (int s = 0; s < 5; s++) { + for (int i = 0; i < items; i++) { + int j = (rand()>>4) % items; + swap(array, i, j); + } + } +} + +// +===================================================================+ + +template +T* ListIter::value() +{ + if (list && step >= 0 && step < list->items) + return list->array[step]; + + return 0; +} + +// +-------------------------------------------------------------------+ + +template +T* ListIter::removeItem() +{ + if (list && step >= 0 && step < list->items) + return list->removeIndex(step--); + + return 0; +} + +// +-------------------------------------------------------------------+ + +template +T* ListIter::next() +{ + if (list && step >= -1 && step < list->items-1) + return list->array[++step]; + + return 0; +} + +template +T* ListIter::prev() +{ + if (list && step > 0 && step < list->items) + return list->array[--step]; + + return 0; +} + +// +-------------------------------------------------------------------+ + +template +void ListIter::attach(List& l) +{ + list = &l; + step = -1; +} + +// +-------------------------------------------------------------------+ + +template +int ListIter::size() +{ + if (!list) return 0; + return list->items; +} + +// +-------------------------------------------------------------------+ + +template +List& ListIter::container() +{ + return *list; +} + diff --git a/FoundationEx/include/Reader.h b/FoundationEx/include/Reader.h new file mode 100644 index 0000000..ed6aebc --- /dev/null +++ b/FoundationEx/include/Reader.h @@ -0,0 +1,70 @@ +/* Starshatter: The Open Source Project + Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors + Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors + Copyright (c) 1997-2006, Destroyer Studios LLC. + + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Declaration of the Reader class +*/ + +#ifndef Reader_h +#define Reader_h + +#include "Text.h" + + +class Reader +{ +public: + Reader() { } + virtual ~Reader() { } + + virtual Text more() = 0; +}; + + +class ConsoleReader : public Reader +{ +public: + virtual Text more() override; + + void printPrimaryPrompt(); + void fillInputBuffer(); + +private: + char buffer[1000]; + char* p; +}; + + +class FileReader : public Reader +{ +public: + FileReader(const char* fname); + virtual Text more() override; + +private: + Text filename; + int done; +}; + + +class BlockReader : public Reader +{ +public: + BlockReader(const char* block); + BlockReader(const char* block, int len); + virtual Text more(); + +private: + char* data; + int done; + int length; +}; + + +#endif // Reader_h diff --git a/FoundationEx/include/Text.h b/FoundationEx/include/Text.h new file mode 100644 index 0000000..4cf5cc4 --- /dev/null +++ b/FoundationEx/include/Text.h @@ -0,0 +1,211 @@ +/* Starshatter: The Open Source Project + Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors + Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors + Copyright (c) 1997-2006, Destroyer Studios LLC. + + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Declaration of the Text class +*/ + +#ifndef Text_h +#define Text_h + +#include +#include +#include + +#ifndef _WIN32 +#include +#endif + +// +-------------------------------------------------------------------+ + +class TextRep +{ + friend class Text; + +public: + TextRep(); + ~TextRep(); + +private: + TextRep(const char* s); + TextRep(const char* s, int len); + TextRep(char c, int len); + TextRep(const TextRep* rep); + + void addref(); + long deref(); + + void dohash(); + + char* data; + std::atomic ref; + int length; + unsigned hash; + bool sensitive; + + static TextRep nullrep; +}; + +// +-------------------------------------------------------------------+ + +class Text +{ +public: + static const char* TYPENAME() { return "Text"; } + + Text(); + Text(char c); + Text(const char* s); + Text(const char* s, int len); + Text(char c, int len); + Text(const Text& s); + ~Text(); + + friend std::ostream& operator<<(std::ostream& out, const Text& text); + + // case sensitivity + bool isSensitive() const; + void setSensitive(bool s); + + // comparison + int compare(const char* s) const; + int compare(const Text& s) const; + + // assignment + Text& operator=(const char* s); + Text& operator=(const Text& s); + + // catenation + Text& append(char c); + Text& append(const char* s); + Text& append(const Text& s); + + Text operator+(char c); + Text operator+(const char* s); + Text operator+(const Text& s); + + Text& operator+=(char c) { return append(c); } + Text& operator+=(const char* s) { return append(s); } + Text& operator+=(const Text& s) { return append(s); } + + // indexing + char operator[](int index) const; + char operator()(int index) const; + char& operator[](int index); + char& operator()(int index); + + Text operator()(int start, int len) const; + + // access + int length() const { return rep->length; } + unsigned hash() const { return rep->hash; } + + const char* data() const { return sym; } + operator const char* () const { return sym; } + + bool empty() const; + + bool contains(char c) const; + bool contains(const char* s) const; + + bool containsAnyOf(const char* charSet) const; + + int indexOf(char c) const; + int indexOf(const char* s) const; + + // mutation + void toLower(); + void toUpper(); + + // substring + Text substring(int start, int length); + Text trim(); + Text replace(const char* pattern, const char* substitution); + Text concat(const char* tail) const; + static Text format(const char* fmt, ...); + +private: + void clone(); + + const char* sym; + TextRep* rep; +}; + +// +-------------------------------------------------------------------+ + +inline int Text::compare(const char* s) const +{ + if (rep->sensitive) + return strcmp(sym, s); + else +#ifdef _WIN32 + return _stricmp(sym, s); +#else + return strcasecmp(sym, s); +#endif +} + +inline int Text::compare(const Text& s) const +{ + if (rep->sensitive && s.rep->sensitive) + return strcmp(sym, s.sym); + else +#ifdef _WIN32 + return _stricmp(sym, s.sym); +#else + return strcasecmp(sym, s.sym); +#endif +} + +// +-------------------------------------------------------------------+ + +inline std::ostream& operator<<(std::ostream& out, const Text& text) { + out << text.data(); return out; } +inline int operator==(const Text& l, const Text& r) { + return (l.length() == r.length()) && (l.compare(r) == 0); } +inline int operator!=(const Text& l, const Text& r) { return l.compare(r) != 0; } +inline int operator< (const Text& l, const Text& r) { return l.compare(r) < 0; } +inline int operator<=(const Text& l, const Text& r) { return l.compare(r) <= 0; } +inline int operator> (const Text& l, const Text& r) { return l.compare(r) > 0; } +inline int operator>=(const Text& l, const Text& r) { return l.compare(r) >= 0; } + +inline int operator==(const char* l, const Text& r) { return r.compare(l) == 0; } +inline int operator!=(const char* l, const Text& r) { return r.compare(l) != 0; } +inline int operator< (const char* l, const Text& r) { return r.compare(l) < 0; } +inline int operator<=(const char* l, const Text& r) { return r.compare(l) <= 0; } +inline int operator> (const char* l, const Text& r) { return r.compare(l) > 0; } +inline int operator>=(const char* l, const Text& r) { return r.compare(l) >= 0; } + +inline int operator==( char* l, const Text& r) { return r.compare(l) == 0; } +inline int operator!=( char* l, const Text& r) { return r.compare(l) != 0; } +inline int operator< ( char* l, const Text& r) { return r.compare(l) < 0; } +inline int operator<=( char* l, const Text& r) { return r.compare(l) <= 0; } +inline int operator> ( char* l, const Text& r) { return r.compare(l) > 0; } +inline int operator>=( char* l, const Text& r) { return r.compare(l) >= 0; } + +inline int operator==(const Text& l, const char* r) { return l.compare(r) == 0; } +inline int operator!=(const Text& l, const char* r) { return l.compare(r) != 0; } +inline int operator< (const Text& l, const char* r) { return l.compare(r) < 0; } +inline int operator<=(const Text& l, const char* r) { return l.compare(r) <= 0; } +inline int operator> (const Text& l, const char* r) { return l.compare(r) > 0; } +inline int operator>=(const Text& l, const char* r) { return l.compare(r) >= 0; } + +inline int operator==(const Text& l, char* r) { return l.compare(r) == 0; } +inline int operator!=(const Text& l, char* r) { return l.compare(r) != 0; } +inline int operator< (const Text& l, char* r) { return l.compare(r) < 0; } +inline int operator<=(const Text& l, char* r) { return l.compare(r) <= 0; } +inline int operator> (const Text& l, char* r) { return l.compare(r) > 0; } +inline int operator>=(const Text& l, char* r) { return l.compare(r) >= 0; } + +inline Text operator+(const char* l, const Text& r) { return Text(l) + r; } +inline Text operator+( char* l, const Text& r) { return Text(l) + r; } + +// +-------------------------------------------------------------------+ + +#endif // Text_h diff --git a/FoundationEx/include/Utils.h b/FoundationEx/include/Utils.h new file mode 100644 index 0000000..731bc01 --- /dev/null +++ b/FoundationEx/include/Utils.h @@ -0,0 +1,18 @@ +/* Starshatter: The Open Source Project + Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors + Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors + Copyright (c) 1997-2006, Destroyer Studios LLC. +*/ + +#ifndef Utils_h +#define Utils_h + +#include + + +void AssignErrLog(FILE * out); +int CloseErrLog(); +void Print(const char* fmt, ...); + + +#endif // Utils_h diff --git a/FoundationEx/src/Reader.cpp b/FoundationEx/src/Reader.cpp new file mode 100644 index 0000000..0ca49aa --- /dev/null +++ b/FoundationEx/src/Reader.cpp @@ -0,0 +1,119 @@ +/* Starshatter: The Open Source Project + Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors + Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors + Copyright (c) 1997-2006, Destroyer Studios LLC. + + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Implementation of the Reader class +*/ + +#include + +#include +#include +#include + +#include + + +Text +ConsoleReader::more() +{ + // loop until the user types something + do { + printPrimaryPrompt(); + fillInputBuffer(); + } while (! *p); + + return Text(p); +} + + +void +ConsoleReader::printPrimaryPrompt() +{ + std::printf("- "); +} + + +void +ConsoleReader::fillInputBuffer() +{ + std::fgets(buffer, 980, stdin); + p = buffer; + while (std::isspace(*p)) p++; +} + + +FileReader::FileReader(const char* fname) : + filename(fname), + done(0) +{ +} + + +Text +FileReader::more() +{ + if (done) return Text(); + + std::fstream fin(filename, std::fstream::in); + + if (!fin) { + Print("ERROR(Parse): Could not open file '%s'\n", filename.data()); + return Text(); + } + + Text result; + char buf[1000], newline; + + while (fin.get(buf, 1000)) { + result.append(buf); + fin.get(newline); + result.append(newline); + } + + done = 1; + return result; +} + + +BlockReader::BlockReader(const char* block) : + data((char*) block), + done(0), + length(0) +{ +} + + +BlockReader::BlockReader(const char* block, int len) : + data((char*) block), + done(0), + length(len) +{ +} + + +Text +BlockReader::more() +{ + if (done) return Text(); + + if (length) { + Text result(data, length); + done = 1; + return result; + } + else if (data) { + Text result(data); + done = 1; + return result; + } + + done = 1; + return Text(); +} diff --git a/FoundationEx/src/Text.cpp b/FoundationEx/src/Text.cpp new file mode 100644 index 0000000..1f46c2e --- /dev/null +++ b/FoundationEx/src/Text.cpp @@ -0,0 +1,686 @@ +/* Starshatter: The Open Source Project + Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors + Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors + Copyright (c) 1997-2006, Destroyer Studios LLC. + + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Implementation of the Text class +*/ + +#include + +#include +#include +#include + +// +-------------------------------------------------------------------+ +// SPECIAL TEXT REP FOR NULL STRINGS +// This is used only by the default constructor for the Text object, +// to prevent extra rep allocation when constructing empty strings. + +TextRep TextRep::nullrep; + +TextRep::TextRep() + : ref(1234567), data(0), length(0), hash(0), sensitive(true) +{ + data = new char[4]; + + if (data) + std::memset(data, 0, 4); +} + +// +-------------------------------------------------------------------+ + + +TextRep::TextRep(const char* s) + : ref(1), length(0), sensitive(true) +{ + if (s) length = std::strlen(s); + + data = new char[length+1]; + + if (data) { + if (s) std::strcpy(data, s); + else data[length] = '\0'; + + dohash(); + } +} + +TextRep::TextRep(const char* s, int len) + : ref(1), length(len), sensitive(true) +{ + if (length < 0) length = 0; + + data = new char[length+1]; + + if (data) { + std::memcpy(data, s, length); + data[length] = '\0'; + dohash(); + } +} + +TextRep::TextRep(char c, int len) + : ref(1), length(len), sensitive(true) +{ + if (length < 0) length = 0; + + data = new char[length+1]; + + if (data) { + std::memset(data, c, length); + data[length] = '\0'; + dohash(); + } +} + +TextRep::TextRep(const TextRep* rep) + : ref(1) +{ + length = rep->length; + + data = new char[length+1]; + + hash = rep->hash; + sensitive = rep->sensitive; + + if (data) + std::strcpy(data, rep->data); +} + +TextRep::~TextRep() +{ + delete[] data; +} + +void +TextRep::addref() +{ + ref++; +} + +long +TextRep::deref() +{ + long r = --ref; + return r; +} + +inline static void mash(unsigned& hash, unsigned chars) +{ + hash = (chars ^ ((hash << 5) | (hash >> (8*sizeof(unsigned) - 5)))); +} + +void +TextRep::dohash() +{ + unsigned hv = (unsigned)length; // Mix in the string length. + unsigned i = length*sizeof(char)/sizeof(unsigned); + const unsigned* p = (const unsigned*)data; + + while (i--) + mash(hv, *p++); // XOR in the characters. + + // XOR in any remaining characters: + i = length*sizeof(char)%sizeof(unsigned); + if (i) { + unsigned h = 0; + const char* c = (const char*)p; + while (i--) + h = ((h << 8*sizeof(char)) | *c++); + mash(hv, h); + } + + hash = hv; +} + +// +-------------------------------------------------------------------+ + +Text::Text() +{ + rep = &TextRep::nullrep; + rep->addref(); + sym = rep->data; +} + +Text::Text(char c) +{ + char buf[2]; buf[0] = c; buf[1] = '\0'; + + rep = new TextRep(buf); + + if (!rep) { + rep = &TextRep::nullrep; + rep->addref(); + } + + sym = rep->data; +} + +Text::Text(const char* s) +{ + rep = new TextRep(s); + + if (!rep) { + rep = &TextRep::nullrep; + rep->addref(); + } + + sym = rep->data; +} + +Text::Text(const char* s, int len) +{ + rep = new TextRep(s, len); + + if (!rep) { + rep = &TextRep::nullrep; + rep->addref(); + } + + sym = rep->data; +} + +Text::Text(char c, int len) +{ + rep = new TextRep(c, len); + + if (!rep) { + rep = &TextRep::nullrep; + rep->addref(); + } + + sym = rep->data; +} + +Text::Text(const Text& s) +{ + rep = s.rep; + rep->addref(); + sym = rep->data; +} + +Text::~Text() +{ + if (rep->deref() == 0) delete rep; + + rep = &TextRep::nullrep; + sym = rep->data; +} + +Text& +Text::operator=(const char* s) +{ + if (rep->deref() == 0) delete rep; + rep = new TextRep(s); + + if (!rep) + rep = &TextRep::nullrep; + sym = rep->data; + return *this; +} + +Text& +Text::operator=(const Text& s) +{ + s.rep->addref(); + if (rep->deref() == 0) delete rep; + rep = s.rep; + sym = rep->data; + return *this; +} + +Text +Text::operator+(char c) +{ + char* buf = new char[rep->length + 2]; + + if (buf) { + std::strcpy(buf, sym); + buf[rep->length] = c; + buf[rep->length+1] = '\0'; + Text retval(buf); + delete [] buf; + return retval; + } + + else { + return *this; + } +} + +Text +Text::operator+(const char* s) +{ + char* buf = new char[std::strlen(s) + rep->length + 1]; + + if (buf) { + std::strcpy(buf, sym); + std::strcat(buf, s); + Text retval(buf); + delete [] buf; + return retval; + } + + else { + return *this; + } +} + +Text +Text::operator+(const Text& s) +{ + char* buf = new char[s.rep->length + rep->length + 1]; + + if (buf) { + std::strcpy(buf, sym); + std::strcat(buf, s.sym); + Text retval(buf); + delete [] buf; + return retval; + } + + else { + return *this; + } +} + +bool +Text::isSensitive() const +{ + return rep->sensitive; +} + +void +Text::setSensitive(bool s) +{ + rep->sensitive = s; +} + +Text& +Text::append(char c) +{ + char* buf = new char[rep->length + 2]; + + if (buf) { + std::strcpy(buf, sym); + buf[rep->length] = c; + buf[rep->length+1] = '\0'; + if (rep->deref() == 0) delete rep; + + rep = new TextRep(buf); + + if (!rep) + rep = &TextRep::nullrep; + + sym = rep->data; + delete [] buf; + } + + return *this; +} + +Text& +Text::append(const char* s) +{ + char* buf = new char[std::strlen(s) + rep->length + 1]; + + if (buf) { + std::strcpy(buf, sym); + std::strcat(buf, s); + if (rep->deref() == 0) delete rep; + + rep = new TextRep(buf); + + if (!rep) + rep = &TextRep::nullrep; + + sym = rep->data; + delete [] buf; + } + + return *this; +} + +Text& +Text::append(const Text& s) +{ + char* buf = new char[s.rep->length + rep->length + 1]; + + if (buf) { + int lenA = rep->length; + int lenB = s.rep->length; + + std::memcpy(buf, sym, lenA); + std::memcpy(buf + lenA, s.sym, lenB); + buf[lenA + lenB] = 0; + + if (rep->deref() == 0) delete rep; + + rep = new TextRep(buf); + + if (!rep) + rep = &TextRep::nullrep; + + sym = rep->data; + delete [] buf; + } + + return *this; +} + +void +Text::clone() +{ + if (rep->ref > 1) { + rep->deref(); + + TextRep* t = new TextRep(rep); + + rep = t; + + if (!rep) + rep = &TextRep::nullrep; + + sym = rep->data; + } +} + +char +Text::operator[](int index) const +{ + if (index < (int) rep->length) + return sym[index]; + else + throw "BOUNDS ERROR"; + + return '\0'; +} + +char +Text::operator()(int index) const +{ + return sym[index]; +} + +char& +Text::operator[](int index) +{ + if (index < (int) rep->length) { + clone(); + return (char&) sym[index]; + } + else + throw "BOUNDS ERROR"; + + return (char&) sym[0]; +} + +char& +Text::operator()(int index) +{ + clone(); + return (char&) sym[index]; +} + +Text +Text::operator()(int start, int len) const +{ + if (start > rep->length || len <= 0) + return Text(); + + if (start + len > rep->length) + len = rep->length - start; + + char* buf = new char[len+1]; + + if (buf) { + std::strncpy(buf, sym+start, len); + buf[len] = '\0'; + + Text retval(buf); + delete [] buf; + return retval; + } + + return Text(); +} + + +bool +Text::empty() const +{ + return !rep || rep->length < 1; +} + + +bool +Text::contains(char c) const +{ + if (rep->length > 0) { + if (!rep->sensitive) { + char alt = c; + if (std::islower(alt)) alt = std::toupper(alt); + else if (std::isupper(alt)) alt = std::tolower(alt); + + if (std::strchr(rep->data, alt) != 0) + return true; + } + + if (std::strchr(rep->data, c) != 0) + return true; + } + + return false; +} + +bool +Text::contains(const char* pattern) const +{ + if (rep->length > 0 && pattern && *pattern) { + if (rep->sensitive) { + if (std::strstr(rep->data, pattern) != 0) + return true; + } + else { + Text smash1(*this); + smash1.toLower(); + Text smash2(pattern); + smash2.toLower(); + + if (std::strstr(smash1.data(), smash2.data()) != 0) + return true; + } + } + + return false; +} + +bool +Text::containsAnyOf(const char* charSet) const +{ + if (rep->length > 0 && charSet && *charSet) { + if (rep->sensitive) { + if (strpbrk(rep->data, charSet) != 0) + return true; + } + else { + Text smash1(*this); + smash1.toLower(); + Text smash2(charSet); + smash2.toLower(); + + if (strpbrk(smash1.data(), smash2.data()) != 0) + return true; + } + } + + return false; +} + +int +Text::indexOf(char c) const +{ + if (rep->length > 0) { + if (!rep->sensitive) { + char alt = c; + if (std::islower(alt)) alt = std::toupper(alt); + else if (std::isupper(alt)) alt = std::tolower(alt); + + const char* p = std::strchr(rep->data, alt); + + if (p) + return (p - rep->data); + } + + const char* p = std::strchr(rep->data, c); + + if (p) + return (p - rep->data); + } + + return -1; +} + +int +Text::indexOf(const char* pattern) const +{ + if (rep->length > 0 && pattern && *pattern) { + if (rep->sensitive) { + const char* p = std::strstr(rep->data, pattern); + if (p) return (p - rep->data); + } + else { + Text smash1(*this); + smash1.toLower(); + Text smash2(pattern); + smash2.toLower(); + + const char* p = std::strstr(smash1.data(), smash2.data()); + if (p) return (p - smash1.data()); + } + } + + return -1; +} + +void +Text::toLower() +{ + clone(); + size_t n = rep->length; + char* p = (char*) sym; + while (n--) { + *p = std::tolower((unsigned char)*p); + p++; + } + + rep->dohash(); +} + +void +Text::toUpper() +{ + clone(); + size_t n = rep->length; + char* p = (char*) sym; + while ( n-- ) { + *p = std::toupper((unsigned char)*p); + p++; + } + + rep->dohash(); +} + +Text +Text::substring(int start, int length) +{ + Text result; + + if (start >= 0 && start < (int) rep->length && length > 0) { + if (start + length > (int) rep->length) + length = (int) rep->length - start; + + const char* s = sym + start; + + result.rep = new TextRep(s, length); + + if (!result.rep) + result.rep = &TextRep::nullrep; + + result.sym = result.rep->data; + } + + return result; +} + +Text +Text::trim() +{ + Text result; + + if (rep->length) { + const char* p = sym; + const char* q = sym + rep->length-1; + + while (p && *p && isspace(*p)) p++; + while (q>p && *q && isspace(*q)) q--; + + result = substring(p-sym, q-p+1); + } + + return result; +} + +Text +Text::replace(const char* pattern, const char* substitution) +{ + Text result; + + if (rep->length && pattern && *pattern) { + const char* pos = rep->data; + const char* bound = rep->data + rep->length; + int skip = std::strlen(pattern); + do { + const char* p = std::strstr(pos, pattern); + if (p) { + int len = p - pos; + result.append(Text(pos, len)); + result.append(substitution); + pos = p + skip; + } + else if (pos < bound) { + result.append(pos); + break; + } + } + while (pos < bound); + } + + return result; +} + +Text +Text::concat(const char* tail) const +{ + Text result(*this); + result.append(tail); + return result; +} + +Text +Text::format(const char* fmt, ...) +{ + char buf[2048]; + std::va_list args; + va_start(args, fmt); + int len = std::vsnprintf(buf, 2048, fmt, args); + Text result(buf, len); + va_end(args); + return result; +} diff --git a/FoundationEx/src/Utils.cpp b/FoundationEx/src/Utils.cpp new file mode 100644 index 0000000..827ecba --- /dev/null +++ b/FoundationEx/src/Utils.cpp @@ -0,0 +1,38 @@ +/* Starshatter: The Open Source Project + Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors + Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors + Copyright (c) 1997-2006, Destroyer Studios LLC. +*/ + +#include + +#include +#include + + +FILE* ErrLog = nullptr; + + +void AssignErrLog(FILE * out) +{ + ErrLog = out; +} + + +int CloseErrLog() +{ + if (ErrLog) + return fclose(ErrLog); + return 0; +} + + +void Print(const char* fmt, ...) +{ + if (!ErrLog) return; + va_list args; + va_start(args, fmt); + vfprintf(ErrLog, fmt, args); + fflush(ErrLog); + va_end(args); +} diff --git a/FoundationEx/test/Text.cpp b/FoundationEx/test/Text.cpp new file mode 100644 index 0000000..7400eb1 --- /dev/null +++ b/FoundationEx/test/Text.cpp @@ -0,0 +1,47 @@ +#include + +#include + +#include + + +TEST(FoundationEx, DefaultConstructedTextIsEmpty) +{ + Text t; + ASSERT_EQ(0, t.length()); + ASSERT_TRUE(t.empty()); +} + + +TEST(FoundationEx, TextCanBeInitializedWithLiteral) +{ + Text t {"Hello, there"}; + ASSERT_EQ("Hello, there", t); + ASSERT_EQ(12, t.length()); +} + + +TEST(FoundationEx, TextCanBeCopied) +{ + Text a {"Hello, there"}; + Text b(a); + ASSERT_EQ(a, b); +} + + +TEST(FoundationEx, ConcatenateTextWithLiteralWithoutSideEffects) +{ + const Text a {"Hello"}; + const auto b = a + ", there"; + ASSERT_EQ("Hello", a); + ASSERT_EQ("Hello, there", b); +} + + +TEST(FoundationEx, ReplaceInTextWithoutSideEffects) +{ + Text a {"Hello, all"}; + const auto b = a.replace("Hello", "Goodbye"); // Test::replace should be const + ASSERT_EQ("Hello, all", a); + ASSERT_EQ("Goodbye, all", b); +} -- cgit v1.1