String Container as Class Template
Although the string container is implemented as a class template, explicit template instantiation is not required when creating string objects due to implicit instantiation mechanisms.
String Class Instantiation
When working with string objects, it's essential to understand that you're manipulating contiguous storage space. This space contains metadata for data size, storage capacity, and the address of the first element.
Both const and non-const objects share identical constructor and destructor implementations since the initialization list is considered the point of object instantiation.
#pragma once
#include <cstring>
#include <iostream>
namespace custom {
class TextBuffer {
private:
size_t data_size;
size_t buffer_capacity;
char* buffer_data;
public:
// Constructor
TextBuffer(const char* input_str = "")
: data_size(std::strlen(input_str))
, buffer_capacity(data_size)
, buffer_data(new char[buffer_capacity + 1]) {
std::memcpy(buffer_data, input_str, data_size + 1);
buffer_data[data_size] = '\0';
}
// Destructor
~TextBuffer() {
delete[] buffer_data;
buffer_data = nullptr;
data_size = buffer_capacity = 0;
}
};
}
The expression std::memcpy(buffer_data, input_str, data_size + 1) uses data_size + 1 because std::strlen counts characters excluding the null terminator, making the actual data size one byte larger.
Data Access and Traversal
Const objects cannot use methods like push_back, append, or += for data insertion.
Data Insertion Methods
push_back
void append_char(char character) {
if (data_size == buffer_capacity) {
expand_buffer(buffer_capacity == 0 ? 4 : (buffer_capacity * 2));
}
buffer_data[data_size] = character;
data_size++;
buffer_data[data_size] = '\0';
}
The expand_buffer method increases the string's storage capacity when needed. Capacity reduction is generally avoided due to performance overhead, though some compilers may implement it after clear operations.
void expand_buffer(size_t new_capacity) {
if (new_capacity > buffer_capacity) {
char* new_buffer = new char[new_capacity + 1];
std::memcpy(new_buffer, buffer_data, data_size + 1);
delete[] buffer_data;
buffer_data = new_buffer;
buffer_capacity = new_capacity;
}
}
append
void append_string(const char* source_str) {
size_t source_length = std::strlen(source_str);
if (data_size + source_length > buffer_capacity) {
expand_buffer(data_size + source_length);
}
std::memcpy(buffer_data + data_size, source_str, source_length + 1);
data_size += source_length;
}
+= Operator Overloads
TextBuffer& operator+=(char character) {
append_char(character);
return *this;
}
TextBuffer& operator+=(const char* source_str) {
append_string(source_str);
return *this;
}
TextBuffer& operator+=(const TextBuffer& other) {
append_string(other.buffer_data);
return *this;
}
Traversal Implementation
public:
typedef char* iterator;
typedef const char* const_iterator;
char& operator[](size_t index) {
if (index < data_size) {
return buffer_data[index];
}
throw std::out_of_range("Index out of range");
}
const char& operator[](size_t index) const {
if (index < data_size) {
return buffer_data[index];
}
throw std::out_of_range("Index out of range");
}
iterator begin() { return buffer_data; }
iterator end() { return buffer_data + data_size; }
const_iterator begin() const { return buffer_data; }
const_iterator end() const { return buffer_data + data_size; }
Insert and Erase Operations
Insert Implementation
TextBuffer& insert_chars(size_t position, size_t count, char character) {
assert(position <= data_size);
if (data_size + count > buffer_capacity) {
expand_buffer(data_size + count);
}
for (size_t i = data_size; i >= position; i--) {
buffer_data[i + count] = buffer_data[i];
}
for (size_t i = 0; i < count; i++) {
buffer_data[position + i] = character;
}
data_size += count;
return *this;
}
Erase Implementation
TextBuffer& remove_chars(size_t position, size_t count = npos) {
assert(position <= data_size);
if (count == npos || position + count >= data_size) {
buffer_data[position] = '\0';
data_size = position;
} else {
size_t remaining = data_size - (position + count);
std::memmove(buffer_data + position,
buffer_data + position + count,
remaining);
data_size -= count;
buffer_data[data_size] = '\0';
}
return *this;
}
Find Operatiosn
size_t find_char(char target, size_t start_pos = 0) const {
assert(start_pos <= data_size);
for (size_t i = start_pos; i < data_size; i++) {
if (buffer_data[i] == target) {
return i;
}
}
return npos;
}
size_t find_string(const char* target_str, size_t start_pos = 0) const {
assert(start_pos <= data_size);
const char* result = std::strstr(buffer_data + start_pos, target_str);
return result ? result - buffer_data : npos;
}
Substring Extraction
char* extract_substring(size_t start_pos = 0, size_t length = npos) const {
assert(start_pos <= data_size);
if (length == npos || start_pos + length > data_size) {
length = data_size - start_pos;
}
char* substring = new char[length + 1];
std::memcpy(substring, buffer_data + start_pos, length);
substring[length] = '\0';
return substring;
}
Comparison Operators
bool operator<(const TextBuffer& other) const {
int result = std::memcmp(buffer_data, other.buffer_data,
std::min(data_size, other.data_size));
return result == 0 ? data_size < other.data_size : result < 0;
}
bool operator==(const TextBuffer& other) const {
return data_size == other.data_size &&
std::memcmp(buffer_data, other.buffer_data, data_size) == 0;
}
bool operator<=(const TextBuffer& other) const {
return *this == other || *this < other;
}
bool operator>(const TextBuffer& other) const {
return !(*this <= other);
}
bool operator>=(const TextBuffer& other) const {
return !(*this < other);
}
Stream Operators
void clear_data() {
buffer_data[0] = '\0';
data_size = 0;
}
std::ostream& operator<<(std::ostream& os, const TextBuffer& buffer) {
for (size_t i = 0; i < buffer.data_size; i++) {
os << buffer.buffer_data[i];
}
return os;
}
std::istream& operator>>(std::istream& is, TextBuffer& buffer) {
buffer.clear_data();
char temp_buffer[128];
char ch;
// Skip whitespace
while (is.get(ch) && std::isspace(ch)) {}
if (!is.eof()) {
is.putback(ch);
}
size_t index = 0;
while (is.get(ch) && !std::isspace(ch)) {
temp_buffer[index++] = ch;
if (index == 127) {
temp_buffer[index] = '\0';
buffer += temp_buffer;
index = 0;
}
}
if (index > 0) {
temp_buffer[index] = '\0';
buffer += temp_buffer;
}
return is;
}
Copy Control Operations
TextBuffer(const TextBuffer& other) {
buffer_data = new char[other.buffer_capacity + 1];
std::memcpy(buffer_data, other.buffer_data, other.data_size + 1);
data_size = other.data_size;
buffer_capacity = other.buffer_capacity;
}
TextBuffer& operator=(TextBuffer other) {
swap_data(*this, other);
return *this;
}
void swap_data(TextBuffer& first, TextBuffer& second) {
std::swap(first.buffer_data, second.buffer_data);
std::swap(first.data_size, second.data_size);
std::swap(first.buffer_capacity, second.buffer_capacity);
}