Super Simple Strings
Another bit of code I once whipped up for an assignment, it was used in my animated console game Texty.
The most notible thing is that it allocates memory in blocks instead of just allocting the required amount, hopefully this speeds up things by reducing the amount of times we must allocate memory/resize the strings, and have all our memory allocated in nice even blocks defaulted to 64 bytes.
String.h
#ifndef STRING_H_
#define STRING_H_
/*
Class: String
Namespace: myString
Purpose: Wrapper of cstring to act like std::string
-automatically allocates memory in blocks of ALLOC_SIZE when needed
*/
const int MAX_INPUT = 255;
const int ALLOC_SIZE = 64;
#include
namespace myString
{
class String;
const String operator+(const String& lhs, const String& rhs);
bool operator==(const String& lhs, const String& rhs);
bool operator==(const String& lhs, const char* rhs);
void operator+=(String& lhs, const String& rhs);
class String{
public:
String();
String(char* str);
String(const String& str);
virtual ~String();
int Length() const; //
char CharAt(int index) const; //
void Clear(){ for (int i = _strlen(data) - 1; i >= 0; --i)data[i] = 0; } //clears string but keeps memory
void RemoveLast(){ if (_strlen(data)) data[_strlen(data) - 1] = 0; }
String& Formatted();
bool EqualTo(const String& str) const; //
bool EqualTo(const char* str) const; //
void Append(const String& str); //
void Append(const char c);
void Append(const char* str); //
void Prepend(const String& str); //
void Prepend(const char* str); //
int Find(const String& str) const; //Finds the str, within our string
int Find(const char* str) const; //could be intFind(const String& str, int index = 0) const to save a function!
int Find(int index, const String& str) const; //Finds the str, within our string starting from index
int Find(int index, const char* str) const; //
void Replace(const String& find, const String& replace); //Replaces all occurences of find with replace
void Replace(const char* find, const char* replace); //
char* CStr() const;
String& ToLower(); //converts string to lower case
String& ToUpper(); //converts string to upper case
virtual void WriteToConsole(); //writes string to console with a newline via cout || may override in game
virtual bool ReadFromConsole(); //Reads a line from console (cin.getline) || may override in game
String& operator=(String rhs); //
virtual void operator=(const char* str); //
friend void _swap(String& first, String& second){
std::swap(first.data, second.data);
std::swap(first.memAllocated, second.memAllocated);
}
protected:
char* data; //this is our string
int memAllocated; //how much mem we have allocated
protected:
/*Utilites*/
bool _alloc(int min); //allocates memory and then copies data to it
void _strcpy(const char* src); //copies src to string member, calls _alloc if needed
void _strcpy(const char* src, char* dest); //copies one src to dest, no tests - unsafe!
int _strlen(const char* str) const; //return length of str up to null terminate
char _toLower(char c) const; //returns character in lower case
char _toUpper(char c) const; //to lower
int _countstr(const char* str) const; //counts str in data, returns how many str is in data
bool _isint(char c) const;
};
}
#endif
String.cpp
#include "String.h"
#include
#include
namespace myString
{
const String operator+(const String& lhs, const String& rhs)
{
String ret = lhs;
ret.Append(rhs);
return String(ret.CStr());
}
bool operator==(const String& lhs, const String& rhs)
{
return lhs.EqualTo(rhs);
}
bool operator==(const String& lhs, const char* rhs)
{
return lhs.EqualTo(rhs);
}
void operator+=(String& lhs, const String& rhs)
{
lhs.Append(rhs);
}
String::String()
:data(0),
memAllocated(0)
{
}
String::String(char* str)
: data(0),
memAllocated(0)
{
_strcpy(str);
}
String::String(const String& str)
{
_strcpy(str.CStr());
}
String::~String()
{
delete[]data;
data = NULL;
}
String& String::operator=(String str){
_swap(*this, str);
return *this;
}
void String::operator=(const char* str)
{
if (!*str){
delete[]data;
data = NULL;
}
_strcpy(str);
}
int String::Length() const
{
return _strlen(data); //return length
}
char String::CharAt(int index) const
{
return (index >= 0 && index < _strlen(data)) ? data[index] : 0; //if valid index return char at that index, else return null
}
String& String::Formatted() //makes new words upper case
{
if (*data){
char* cp = (char*)data;
*cp++ = _toUpper(*cp);
while (*cp){
if (*cp++ == 32)
*cp = _toUpper(*cp);
}
}
return *this;
}
bool String::EqualTo(const String& str) const
{
return EqualTo(str.CStr()); //^^
}
bool String::EqualTo(const char* str) const
{
if (!str || !*str || !data || !*data) return 0; //if either is null or something, then return (if test null do length)
char* cp = (char*)str, *cp2 = (char*)data; //set charpointer to start of checkstring, set charpointer2 to start of our data
while (*cp && *cp2 && *cp++ == *cp2++); //while neither pointer is null and they are equal, increment them
return !*cp&&!*cp2; //if both are null(former loop ended) then they are equal and it returns true
}
void String::Append(const String& str)
{
Append(str.CStr());
}
void String::Append(const char c) //append just a char (same as below function read there)
{
if (!c) return;
if (!data){
_alloc(2);
}
int toAlloc = _strlen(data) + 2;
if (toAlloc > memAllocated)
_alloc(toAlloc);
char* cp = (char*)(data + _strlen(data));
*cp++ = c;
*cp = 0;
}
void String::Append(const char* str) //append char*
{
if (!str || !*str) return; //if nothing to append, return
if (!data){ _strcpy(str); return; } //if we dont have a string, just copy it then returrn
int toAlloc = _strlen(str) + _strlen(data) + 1; //see how much memory we need
if (toAlloc > memAllocated) //if we need more memory
_alloc(toAlloc); //call our alloc with minimum memory we need
char* cp = (char*)(data + _strlen(data)); //set charpointer to end of data
while (*str){ //whilst appendstring isnt null
*cp++ = *str++; //copy appendstring to datapointer
}
*cp = 0; //insert null string
}
void String::Prepend(const String& str)
{
Prepend(str.CStr()); //^^
}
void String::Prepend(const char* str)
{
if (!str || !*str) return; //simple check
int offset = _strlen(str), datal = _strlen(data); //set offset prependstring length, datalength to data length
int toAlloc = offset + datal + 1; //get minimum memsize we need
if (toAlloc > memAllocated) //if we need more memory
_alloc(toAlloc); //call our alloc with minmimum memory we need
char* c1 = (char*)(data + datal), *cp = (char*)str; //set char1 to last position of data, charpointer to first pos in prependstring
*(c1 + offset) = 0; //insert null terminating char at prependlen + datalen
--c1; //so we rewrite the exisitin terminatin null in data
while (datal--) //while datalength is positive
*(c1 + offset) = *c1--; //copy last char in (old)data to new last position
c1 = data; //set char1 to start of data
while (*str) //whilst prependstring isnt null
*c1++ = *str++; //copy prependstring to start of data
}
char* String::CStr() const //returns pointer to our data
{
if (!data || !*data) return 0;
return data;
}
String& String::ToLower() //converts sting to lower - actually changes i dont think possible to not change
{
if (data && *data){ //if we have a string and its not null
char* cp = (char*)data; //set charpointer to data
while (*cp){ //while data pointer isnt null
if (*cp >= 65 && *cp <= 90) //if it is upper case
*cp += 32; //make it lower case
*cp++; //increment data pointer
}
}
return *this; //return this instance so we can do string.ToLower().Equals() etc
}
String& String::ToUpper() //converts sting to upper - actually changes i dont think possible to not change
{
if (data && *data){ //if we have a string and its not null
char* cp = (char*)data; //set charpointer to data
while (*cp){ //while data pointer is not null
if (*cp >= 97 && *cp <= 122) //if char isupper case
*cp -= 32; //make it lower case
*cp++; //increment data pointer
}
}
return *this; //return this instance so we can do string.ToUpper().Equals() etc
}
int String::Find(const String& str) const
{
return Find(str.CStr()); //^^
}
int String::Find(const char* str) const
{
if (!data || !*data || !str || !*str) return -1; //bunch of checks
char *cp = (char*)data, *c1, *c2; //set charpointer to data
int ret = 0; //set return to first position
while (*cp){ //while data pointer isnt null
c1 = cp; //set char1 to CURRENT data poistion
c2 = (char*)str; //set char2 to FIRST char in find string
while (*c1 && *c2 && (_toLower(*c1) == _toLower(*c2)))//while neither is null and they match
++c1, ++c2; //increment til they are null
if (!*c2) //if findstring pointer is null(ie ended)
return ret; //return current position
++cp; //no match, increment data pointer and current position
++ret;
}
return -1; //no match
}
int String::Find(int index, const String& str) const
{
return Find(index, str.CStr()); //^^
}
int String::Find(int index, const char* str) const //find string starting from index
{
if (index >= _strlen(data) || !data || !str || !*data || !*str || index < 0) return -1; //bunch of checks (in order of likely to least likely)
char* cp = (char*)(data + index), *c1, *c2; //set charpointer to data+index
int ret = index; //set our return to index
while (*cp){ //while data isnt null
c1 = cp; //set char1 to CURRENT position on data
c2 = (char*)str; //set char2 to START of find string
while (*c1 && *c2 && (_toLower(*c1) == _toLower(*c2))) //whilst neither is null and they are equal
++c1, ++c2; //increment til we end
if (!*c2) //if pointer to find string is null (ie ended) then return our current location
return ret;
++cp; //no match, increment our data pointer
++ret; //increment our current position
}
return -1; //nothing found
}
void String::Replace(const String& find, const String& replace)
{
Replace(find.CStr(), replace.CStr()); //^^
}
void String::Replace(const char* find, const char* replace) //extra new :<
{
if (!data || !*data || !find || !*find || !replace) return;
int count = _countstr(find);
int toAlloc = _strlen(data) - (_strlen(find)*count) + (strlen(replace)*count) + 1;
char* tmp = new char[toAlloc];
char* cpt = (char*)tmp, *cf, *cr = (char*)replace;
char* cps = (char*)data, *cs = (char*)data;
while (*cps){
cf = (char*)find;
while (*cs && *cf && *cs == *cf){
*cs++;
*cf++;
}
if (!*cf){
cr = (char*)replace;
cps = cs;
while (*cr)
*cpt++ = *cr++;
}
else{
*cpt++ = *cps++;
cs = cps;
}
}
*cpt = 0;
_strcpy(tmp);
delete[]tmp;
}
void String::WriteToConsole()
{
if (!data || !*data) return; //if nothing to write return
std::cout << data << '\n'; //print to screeen
}
bool String::ReadFromConsole() //somewhat safe
{
char tmp[MAX_INPUT] = { 0 }; //get tmp variable of size maxinput
std::cin.getline(tmp, MAX_INPUT - 1, '\n'); //read in a line
if (!std::cin){ //if cin failed then clear flags and return false
std::cin.clear();
std::cin.ignore(1000, '\n');
return false;
}
_strcpy(tmp); //copy our tmp to our data - if tmp is null or something, _strcpy will just return
return true; //success input
}
bool String::_alloc(int min) //handles all the memory allocation for class
{
int memsz = (min / ALLOC_SIZE + 1)*ALLOC_SIZE; //calc memsize to nearest ALLOC_SIZE (64 i think by default)
if (memsz < memAllocated) return true; //dont need to alloc anything
try{ //put in try incase bad alloc
char* tmp = new char[memsz]; //allocate more than we need to avoid constant new/delete
memAllocated = memsz; //set our memallocated to what we just allocated
char* cp = (char*)data, *cp2 = (char*)tmp; //few charpointers
if (cp && data){ //some checks
while (*cp) //while data is not null
*cp2++ = *cp++; //copy char from data to our new memory address and increment the indexes
delete[]data; //delete data (free our old memory up)
}
*cp2 = 0; //insert a terminating null onto our new memory
data = tmp; //set our data member to the new memory address
}
catch (std::bad_alloc&){
return false; //bad alloc return false
}
return true; //successful return true
}
int String::_strlen(const char* str) const //counts string length up to null char
{
int count = 0;
if (!str || !*str) //if str and str is not null
return 0;
while (*str++) count++; //while char at some spot in str is not null count it
return count; //return count
}
void String::_strcpy(const char* src) //string copy
{
if (!src || !*src) return; //if nothing to copy, then return without doing anything
int toAlloc = _strlen(src) + 1;
if (toAlloc > memAllocated) //if we need more memory allocated then
_alloc(toAlloc); //call our alloc function with the minimum size we need
char* cp = (char*)data; //set charpointer to data start pos
while (*src){ //while src string isnt null
*cp++ = *src++; //copy src string to our data string
}
*cp = 0; //insert null terminating
}
void String::_strcpy(const char* src, char* dest) //unsafe non class specific version
{ //unsafe as doesnt check memory sizes
if (!src || !*src || !dest) return; //if nothing to copy or we dont have a dest then return
char* cp = (char*)src;
while (*src){ //whilst src string isnt null
*dest++ = *src++; //copy src to dest
}
*dest = 0; //insert null terminating
}
char String::_toLower(char c) const //to lower
{
return ((c >= 65 && c <= 90) ? (c + 32) : (c)); //if its upper return c+32, else return c (no change)
}
char String::_toUpper(char c) const //to lower
{
return ((c >= 97 && c <= 122) ? (c - 32) : (c)); //if its upper return c+32, else return c (no change)
}
int String::_countstr(const char* str) const //count all str in our string
{
if (!data || !*data || !*str) //if no memory or either string is null then return
return -1;
char *cp = (char*)data, *c1, *c2; //set charpointer to data
int count = 0;
while (*cp){ //whilst data isnt null
c1 = cp; //(char1) = CURRENT position in data
c2 = (char*)str; //(char2) = FIRST position in find string
while (*c1 && *c2 && (_toLower(*c1) == _toLower(*c2))) //while neither points to null and they are equal (not case sensitive)
++c1, ++c2; //increment them
if (!*c2) //if our pointer to FIND STRING is null (ie its finished) then count that we found a match
++count;
++cp; //increment our data pointer
}
return count; //return count
}
bool String::_isint(char c) const //returns true if char is an int
{
return (c >= 48 && c <= 57);
}
}