COMMON: Add replace functions to Common and String.

COMMON: Add replacement to common/algorithm.h

COMMON: Intermediate commit to show doubts.

 COMMON: Basic String::replace() methods implemented.

COMMON: Fix typo in the algorithm.h documentation.

COMMON: Fix documentation of String::replace()

COMMON: Fix formatting issues in method signatures.

COMMON: Add assert and reformat loops in str and algorithm.

COMMON: Fix typo in comment.

COMMON: Fix style in string test cases.

COMMON: Add Doxygen documentation to algorithm and String.

COMMON: Add Doxygen documentation to algorithm and String.

COMMON: Add Doxygen documentation to algorithm.

COMMON: Fix style in algorithm comments.

COMMON: Add Doxygen comments to String.

COMMON: Add Doxygen comments to algorithm test function.

COMMON: Add String support for substring replace.

COMMON: Fix string replace to comply with STL

COMMON: Fix documentation on string replace

COMMON: Fix style in string replace

COMMON: Fix unwanted reference problem in String::replace().

COMMON: Fix indentation in comments for replace

COMMON: Fix indentation in replace

COMMON: Fix comments in String::replace to match implementation.

COMMON: Remove assert to allow for not-null-terminated character arrays

COMMON: Add new test for String::replace

COMMON: Fix broken comments on String::replace

COMMON: Fix sharing bug on ensureCapacity

COMMON: Remove superfluous call to makeUnique()
This commit is contained in:
Borja Lorente Escobar 2016-03-02 17:07:50 +01:00 committed by Borja Lorente
parent f397a73138
commit a19b50ddf2
5 changed files with 259 additions and 27 deletions

View File

@ -270,5 +270,26 @@ T gcd(T a, T b) {
#pragma warning(pop)
#endif
/**
* Replacement algorithm for iterables.
*
* Replaces all occurrences of "original" in [begin, end) with occurrences of "replaced".
*
* @param[in, out] begin: First element to be examined.
* @param[in] end: Last element in the seubsection. Not examined.
* @param[in] original: Elements to be replaced.
* @param[in] replaced: Element to replace occurrences of "original".
*
* @note Usage examples and unit tests may be found in "test/common/algorithm.h"
*/
template<class It, class Dat>
void replace(It begin, It end, const Dat &original, const Dat &replaced) {
for (; begin != end; ++begin) {
if (*begin == original) {
*begin = replaced;
}
}
}
} // End of namespace Common
#endif

View File

@ -75,7 +75,7 @@ void String::initWithCStr(const char *str, uint32 len) {
}
String::String(const String &str)
: _size(str._size) {
: _size(str._size) {
if (str.isStorageIntern()) {
// String in internal storage: just copy it
memcpy(_storage, str._storage, _builtinCapacity);
@ -91,7 +91,7 @@ String::String(const String &str)
}
String::String(char c)
: _size(0), _str(_storage) {
: _size(0), _str(_storage) {
_storage[0] = c;
_storage[1] = 0;
@ -132,24 +132,19 @@ void String::ensureCapacity(uint32 new_size, bool keep_old) {
if (!isShared && new_size < curCapacity)
return;
if (isShared && new_size < _builtinCapacity) {
// We share the storage, but there is enough internal storage: Use that.
newStorage = _storage;
newCapacity = _builtinCapacity;
} else {
// We need to allocate storage on the heap!
// We need to allocate storage on the heap!
// Compute a suitable new capacity limit
// If the current capacity is sufficient we use the same capacity
if (new_size < curCapacity)
newCapacity = curCapacity;
else
newCapacity = MAX(curCapacity * 2, computeCapacity(new_size+1));
// Compute a suitable new capacity limit
// If the current capacity is sufficient we use the same capacity
if (new_size < curCapacity)
newCapacity = curCapacity;
else
newCapacity = MAX(curCapacity * 2, computeCapacity(new_size+1));
// Allocate new storage
newStorage = new char[newCapacity];
assert(newStorage);
}
// Allocate new storage
newStorage = new char[newCapacity];
assert(newStorage);
// Copy old data if needed, elsewise reset the new storage.
if (keep_old) {
@ -444,6 +439,58 @@ uint String::hash() const {
return hashit(c_str());
}
void String::replace(uint32 pos, uint32 count, const String &str) {
replace(pos, count, str, 0, str._size);
}
void String::replace(uint32 pos, uint32 count, const char *str) {
replace(pos, count, str, 0, strlen(str));
}
void String::replace(iterator begin, iterator end, const String &str) {
replace(begin - _str, end - begin, str._str, 0, str._size);
}
void String::replace(iterator begin, iterator end, const char *str) {
replace(begin - _str, end - begin, str, 0, strlen(str));
}
void String::replace(uint32 posOri, uint32 countOri, const String &str,
uint32 posDest, uint32 countDest) {
replace(posOri, countOri, str._str, posDest, countDest);
}
void String::replace(uint32 posOri, uint32 countOri, const char *str,
uint32 posDest, uint32 countDest) {
ensureCapacity(_size + countDest - countOri, true);
// Prepare string for the replaced text.
if (countOri < countDest) {
uint32 offset = countDest - countOri; ///< Offset to copy the characters
uint32 newSize = _size + offset;
_size = newSize;
// Push the old characters to the end of the string
for (uint32 i = _size; i >= posOri + countDest; i--)
_str[i] = _str[i - offset];
} else if (countOri > countDest){
uint32 offset = countOri - countDest; ///< Number of positions that we have to pull back
// Pull the remainder string back
for (uint32 i = posOri + countDest; i < _size; i++)
_str[i] = _str[i + offset];
_size -= offset;
}
// Copy the replaced part of the string
for (uint32 i = 0; i < countDest; i++)
_str[posOri + i] = str[posDest + i];
}
// static
String String::format(const char *fmt, ...) {
String output;

View File

@ -46,6 +46,17 @@ namespace Common {
class String {
public:
static const uint32 npos = 0xFFFFFFFF;
typedef char value_type;
/**
* Unsigned version of the underlying type. This can be used to cast
* individual string characters to bigger integer types without sign
* extension happening.
*/
typedef unsigned char unsigned_type;
typedef char * iterator;
typedef const char * const_iterator;
protected:
/**
* The size of the internal storage. Increasing this means less heap
@ -222,6 +233,38 @@ public:
void trim();
uint hash() const;
/**@{
* Functions to replace some amount of chars with chars from some other string.
*
* @note The implementation follows that of the STL's std::string:
* http://www.cplusplus.com/reference/string/string/replace/
*
* @param pos Starting position for the replace in the original string.
* @param count Number of chars to replace from the original string.
* @param str Source of the new chars.
* @param posOri Same as pos
* @param countOri Same as count
* @param posDest Initial position to read str from.
* @param countDest Number of chars to read from str. npos by default.
*/
// Replace 'count' bytes, starting from 'pos' with str.
void replace(uint32 pos, uint32 count, const String &str);
// The same as above, but accepts a C-like array of characters.
void replace(uint32 pos, uint32 count, const char *str);
// Replace the characters in [begin, end) with str._str.
void replace(iterator begin, iterator end, const String &str);
// Replace the characters in [begin, end) with str.
void replace(iterator begin, iterator end, const char *str);
// Replace _str[posOri, posOri + countOri) with
// str._str[posDest, posDest + countDest)
void replace(uint32 posOri, uint32 countOri, const String &str,
uint32 posDest, uint32 countDest);
// Replace _str[posOri, posOri + countOri) with
// str[posDest, posDest + countDest)
void replace(uint32 posOri, uint32 countOri, const char *str,
uint32 posDest, uint32 countDest);
/**@}*/
/**
* Print formatted data into a String object. Similar to sprintf,
@ -238,15 +281,6 @@ public:
static String vformat(const char *fmt, va_list args);
public:
typedef char value_type;
/**
* Unsigned version of the underlying type. This can be used to cast
* individual string characters to bigger integer types without sign
* extension happening.
*/
typedef unsigned char unsigned_type;
typedef char * iterator;
typedef const char * const_iterator;
iterator begin() {
// Since the user could potentially

View File

@ -25,6 +25,34 @@ class AlgorithmTestSuite : public CxxTest::TestSuite {
return true;
}
/**
* Auxiliary function to check the equality of two generic collections (A and B), from one_first to one_last.
*
* @note: It assumes that other has at least (one_last - one-first) lenght, starting from other_first.
*
* @param one_first: The first element of the first collection to be compared.
* @param one_last: The last element of the first collection to be compared.
* @param other_first: The first element of the collection to be compared.
* @return true if, for each index i in [one_first, one_last), A[i] == B[i], false otherwise.
*/
template<typename It>
bool checkEqual(It one_first, It one_last, It other_first) {
if (one_first == one_last)
return true;
// Check whether two containers have the same items in the same order,
// starting from some iterators one_first and other_first
//
// It iterates through the containers, comparing the elements one by one.
// If it finds a discrepancy, it returns false. Otherwise, it returns true.
for (; one_first != one_last; ++one_first, ++other_first)
if (*one_first != *other_first)
return false;
return true;
}
struct Item {
int value;
Item(int v) : value(v) {}
@ -97,4 +125,32 @@ public:
Common::sort(list.begin(), list.end());
TS_ASSERT_EQUALS(checkSort(list.begin(), list.end(), Common::Less<Item>()), true);
}
void test_string_replace() {
Common::String original = "Hello World";
Common::String expected = "Hells Wsrld";
Common::replace(original.begin(), original.end(), 'o', 's');
TS_ASSERT_EQUALS(original, expected);
}
void test_container_replace() {
Common::List<int> original;
Common::List<int> expected;
for (int i = 0; i < 6; ++i) {
original.push_back(i);
if (i == 3) {
expected.push_back(5);
} else {
expected.push_back(i);
}
}
Common::replace(original.begin(), original.end(), 3, 5);
TS_ASSERT_EQUALS(checkEqual(original.begin(), original.end(), expected.begin()), true);
}
};

View File

@ -419,4 +419,78 @@ class StringTestSuite : public CxxTest::TestSuite
TS_ASSERT_EQUALS(scumm_strnicmp("abCd", "ABCde", 4), 0);
TS_ASSERT_LESS_THAN(scumm_strnicmp("abCd", "ABCde", 5), 0);
}
void test_replace() {
// Tests created with the results of the STL std::string class
// --------------------------
// Tests without displacement
// --------------------------
Common::String testString = Common::String("This is the original string.");
// Positions and sizes as parameters, string as replacement
testString.replace(12, 8, Common::String("newnewne"));
TS_ASSERT_EQUALS(testString, Common::String("This is the newnewne string."));
// The same but with char*
testString.replace(0, 4, "That");
TS_ASSERT_EQUALS(testString, Common::String("That is the newnewne string."));
// Using iterators (also a terribly useless program as a test).
testString.replace(testString.begin(), testString.end(), "That is the supernew string.");
TS_ASSERT_EQUALS(testString, Common::String("That is the supernew string."));
// With sub strings of character arrays.
testString.replace(21, 6, "That phrase is new.", 5, 6);
TS_ASSERT_EQUALS(testString, Common::String("That is the supernew phrase."));
// Now with substrings.
testString.replace(12, 2, Common::String("That hy is new."), 5, 2);
TS_ASSERT_EQUALS(testString, Common::String("That is the hypernew phrase."));
// --------------------------
// Tests with displacement
// --------------------------
testString = Common::String("Hello World");
// Positions and sizes as parameters, string as replacement
testString.replace(6, 5, Common::String("friends"));
TS_ASSERT_EQUALS(testString, Common::String("Hello friends"));
// The same but with char*
testString.replace(0, 5, "Good");
TS_ASSERT_EQUALS(testString, Common::String("Good friends"));
// Using iterators (also a terribly useless program as a test)
testString.replace(testString.begin() + 4, testString.begin() + 5, " coffee ");
TS_ASSERT_EQUALS(testString, Common::String("Good coffee friends"));
// With sub strings of character arrays
testString.replace(4, 0, "Lorem ipsum expresso dolor sit amet", 11, 9);
TS_ASSERT_EQUALS(testString, Common::String("Good expresso coffee friends"));
// Now with substrings
testString.replace(5, 9, Common::String("Displaced ristretto string"), 10, 10);
TS_ASSERT_EQUALS(testString, Common::String("Good ristretto coffee friends"));
// -----------------------
// Deep copy compliance
// -----------------------
// Makes a deep copy without changing the length of the original
Common::String s1 = "TestTestTestTestTestTestTestTestTestTestTest";
Common::String s2(s1);
TS_ASSERT_EQUALS(s1, "TestTestTestTestTestTestTestTestTestTestTest");
TS_ASSERT_EQUALS(s2, "TestTestTestTestTestTestTestTestTestTestTest");
s1.replace(0, 4, "TEST");
TS_ASSERT_EQUALS(s1, "TESTTestTestTestTestTestTestTestTestTestTest");
TS_ASSERT_EQUALS(s2, "TestTestTestTestTestTestTestTestTestTestTest");
// Makes a deep copy when we shorten the string
Common::String s3 = "TestTestTestTestTestTestTestTestTestTestTest";
Common::String s4(s3);
s3.replace(0, 32, "");
TS_ASSERT_EQUALS(s3, "TestTestTest");
TS_ASSERT_EQUALS(s4, "TestTestTestTestTestTestTestTestTestTestTest");
}
};