// Copyright 2022 Jeisson Hidalgo ECCI-UCR CC-BY-4.0 #include #include #include #include #include #include "String.hpp" namespace ecci { size_t String::instanceCount = 0; String::String(const char* text) : length(std::strlen(text)) , text{ reinterpret_cast( std::malloc(this->capacity() * sizeof(char))) } { assert(text); if (this->text == nullptr) { throw std::runtime_error("no enough memory to create string from char*"); } std::strncpy(this->text, text, this->capacity()); #ifdef VERBOSE std::cerr << "::String(" << this->text << ")" << std::endl; #endif // VERBOSE #ifdef TESTING ++String::instanceCount; #endif // TESTING } String::String(char ch) : length(1) , text(reinterpret_cast( std::malloc(this->capacity() * sizeof(char)))) { if (this->text == nullptr) { throw std::runtime_error("no enough memory to create string from char"); } this->text[0] = ch; this->text[1] = '\0'; #ifdef VERBOSE std::cerr << "::StringCh(" << this->text << ")" << std::endl; #endif // VERBOSE #ifdef TESTING ++String::instanceCount; #endif // TESTING } String::String(const size_t length) : length(0) , text(reinterpret_cast(std::malloc((length + 1) * sizeof(char)))) { if (this->text == nullptr) { throw std::runtime_error("no enough memory to create string with capacity"); } *this->text = '\0'; #ifdef VERBOSE std::cerr << "::StringSz(" << length << ")" << std::endl; #endif // VERBOSE #ifdef TESTING ++String::instanceCount; #endif // TESTING } String::String(const String& other) : length(other.length) , text(reinterpret_cast( std::malloc(other.capacity() * sizeof(char)))) { if (this->text == nullptr) { throw std::runtime_error("no enough memory to create string as a copy"); } std::strncpy(this->text, other.text, other.capacity()); #ifdef VERBOSE std::cerr << "::StringCp(" << this->text << ")" << std::endl; #endif // VERBOSE #ifdef TESTING ++String::instanceCount; #endif // TESTING } String::String(String&& other) : length(other.length) , text(other.text) { other.length = 0; other.text = nullptr; #ifdef VERBOSE std::cerr << "::StringMv(" << this->text << ")" << std::endl; #endif // VERBOSE #ifdef TESTING ++String::instanceCount; #endif // TESTING } String::~String() { #ifdef VERBOSE std::cerr << "::~String(" << (this->text ? this->text : "null") << ")" << std::endl; #endif // VERBOSE free(this->text); // NOTE: Do not decrease instance count } String& String::operator=(const String& other) { if (this != &other) { if (this->capacity() != other.capacity()) { this->length = other.length; free(this->text); this->text = reinterpret_cast( std::malloc(other.capacity() * sizeof(char))); if (this->text == nullptr) { throw std::runtime_error("no enough memory to assign string to other"); } } std::strncpy(this->text, other.text, other.capacity()); } #ifdef VERBOSE std::cerr << "::operator=(" << this->text << ")" << std::endl; #endif // VERBOSE return *this; } String& String::operator=(String&& other) { if (this != &other) { std::swap(this->length, other.length); std::swap(this->text, other.text); } #ifdef VERBOSE std::cerr << "::operator=Mv(" << this->text << ")" << std::endl; #endif // VERBOSE return *this; } char& String::at(size_t index) { if (index < this->length) { return this->text[index]; } else { throw std::runtime_error("index out of bounds"); } } const char& String::at(size_t index) const { if (index < this->length) { return this->text[index]; } else { throw std::runtime_error("index out of bounds"); } } String operator+(const String& left, const String& right) { const size_t result_length = left.length + right.length; String result(result_length); std::strncpy(result.text, left.text, left.length); std::strncpy(result.text + left.length, right.text, right.capacity()); result.length = result_length; return result; } String& String::operator+=(const String& right) { const size_t myNewLength = this->length + right.length; char* myNewText = reinterpret_cast( std::realloc(this->text, (myNewLength + 1) * sizeof(char))); if (myNewText == nullptr) { throw std::runtime_error("no enough memory to concatenate with +="); } this->text = myNewText; std::strncpy(this->text + this->length, right.text, right.capacity()); this->length = myNewLength; return *this; } std::ostream& operator<<(std::ostream& output, const String& text) { #ifdef VERBOSE return output << '[' << text.length << "]{" << text.text << '}'; #else return output << text.text; #endif // VERBOSE } std::istream& operator>>(std::istream& input, String& text) { size_t buffer_capacity = 100; char* buffer = reinterpret_cast( ::malloc(buffer_capacity * sizeof(char))); size_t buffer_length = 0; // Ignore whitespace before the token while (input.get(buffer[buffer_length]) && ::isspace(buffer[buffer_length])) { } // Avoid increase length if EOF was read if (input) { ++buffer_length; } // Read token characters while (input.get(buffer[buffer_length]) && !::isspace(buffer[buffer_length])) { ++buffer_length; if (buffer_length == buffer_capacity) { buffer_capacity *= 10; buffer = reinterpret_cast( ::realloc(buffer, buffer_capacity * sizeof(char))); assert(buffer); } } buffer[buffer_length] = '\0'; free(text.text); // Remove extra bytes from buffer to match capacity to length + 1 text.length = buffer_length; text.text = reinterpret_cast( ::realloc(buffer, text.capacity() * sizeof(char))); return input; } } // namespace ecci