From c273a0c151671bc141caa3e6714917381b728393 Mon Sep 17 00:00:00 2001 From: Oleg Andreev Date: Mon, 2 Nov 2015 12:49:42 +0300 Subject: [PATCH 1/3] Place small string memory buffer more efficiently, added tests. --- include/TINYSTL/string.h | 207 +++++++++++++++++++++++++-------------- test/string_assign.cpp | 100 +++++++++++++++++++ test/string_resize.cpp | 48 +++++++++ 3 files changed, 280 insertions(+), 75 deletions(-) create mode 100644 test/string_assign.cpp create mode 100644 test/string_resize.cpp diff --git a/include/TINYSTL/string.h b/include/TINYSTL/string.h index 677cff9..9546f6f 100644 --- a/include/TINYSTL/string.h +++ b/include/TINYSTL/string.h @@ -45,147 +45,206 @@ namespace tinystl { const char* c_str() const; size_t size() const; + size_t capacity() const; - void reserve(size_t size); + void reserve(size_t capacity); void resize(size_t size); + void assign(const char* first, const char* last); + void append(const char* first, const char* last); void swap(string& other); + typedef char* iterator; + iterator begin(); + iterator end(); + + typedef const char* const_iterator; + const_iterator begin() const; + const_iterator end() const; + private: typedef char* pointer; - pointer m_first; - pointer m_last; - pointer m_capacity; - - static const size_t c_nbuffer = 12; - char m_buffer[12]; + static const size_t c_nbuffer = 16; + static const size_t c_longflag = ((size_t)1) << (sizeof(size_t) * 8 - 1); + size_t m_size; + union { + struct { + pointer m_first; + pointer m_capacity; + }; + char m_buffer[c_nbuffer]; + }; }; inline string::string() - : m_first(m_buffer) - , m_last(m_buffer) - , m_capacity(m_buffer + c_nbuffer) + : m_size(0) { - resize(0); + m_buffer[0] = 0; } inline string::string(const string& other) - : m_first(m_buffer) - , m_last(m_buffer) - , m_capacity(m_buffer + c_nbuffer) + : m_size(0) { - reserve(other.size()); - append(other.m_first, other.m_last); + assign(other.begin(), other.end()); } inline string::string(const char* sz) - : m_first(m_buffer) - , m_last(m_buffer) - , m_capacity(m_buffer + c_nbuffer) + : m_size(0) { size_t len = 0; for (const char* it = sz; *it; ++it) ++len; - reserve(len); - append(sz, sz + len); + assign(sz, sz + len); } inline string::string(const char* sz, size_t len) - : m_first(m_buffer) - , m_last(m_buffer) - , m_capacity(m_buffer + c_nbuffer) + : m_size(0) { - reserve(len); append(sz, sz + len); } inline string::~string() { - if (m_first != m_buffer) + if (m_size & c_longflag) TINYSTL_ALLOCATOR::static_deallocate(m_first, m_capacity - m_first); } inline string& string::operator=(const string& other) { - string(other).swap(*this); + if (this != &other) + assign(other.begin(), other.end()); return *this; } inline const char* string::c_str() const { - return m_first; + if (m_size & c_longflag) + return m_first; + else + return m_buffer; } - inline size_t string::size() const - { - return (size_t)(m_last - m_first); + inline size_t string::size() const { + return m_size & ~c_longflag; } - inline void string::reserve(size_t capacity) { - if (m_first + capacity + 1 <= m_capacity) - return; + inline size_t string::capacity() const { + if (m_size & c_longflag) + return m_capacity - m_first - 1; + else + return c_nbuffer - 1; + } - const size_t size = (size_t)(m_last - m_first); + inline void string::reserve(size_t cap) { + if (cap <= capacity()) + return; - pointer newfirst = (pointer)TINYSTL_ALLOCATOR::static_allocate(capacity + 1); - for (pointer it = m_first, newit = newfirst, end = m_last; it != end; ++it, ++newit) - *newit = *it; - if (m_first != m_buffer) + pointer newfirst = (pointer)TINYSTL_ALLOCATOR::static_allocate(cap + 1); + if (m_size & c_longflag) { + for (pointer it = m_first, newit = newfirst, e = m_first + (m_size & ~c_longflag); it != e; ++it, ++newit) + *newit = *it; TINYSTL_ALLOCATOR::static_deallocate(m_first, m_capacity - m_first); - + } else { + for (pointer it = m_buffer, newit = newfirst, e = m_buffer + m_size; it != e; ++it, ++newit) + *newit = *it; + m_size |= c_longflag; + } m_first = newfirst; - m_last = newfirst + size; - m_capacity = m_first + capacity; + m_capacity = m_first + cap + 1; } inline void string::resize(size_t size) { reserve(size); - for (pointer it = m_last, end = m_first + size + 1; it < end; ++it) + pointer newend = begin() + size; + for (pointer it = end(); it != newend; ++it) *it = 0; + *newend = 0; + m_size = size; + } - m_last += size; + inline void string::assign(const char* first, const char* last) { + size_t newsize = last - first; + reserve(newsize); + + char* newit = begin(); + for (const char* it = first; it != last; ++it, ++newit) + *newit = *it; + *newit = 0; + m_size = newsize | (m_size & c_longflag); } inline void string::append(const char* first, const char* last) { - const size_t newsize = (size_t)((m_last - m_first) + (last - first) + 1); - if (m_first + newsize > m_capacity) + const size_t newsize = (size_t)(size() + (last - first)); + if (newsize > capacity()) reserve((newsize * 3) / 2); - for (; first != last; ++m_last, ++first) - *m_last = *first; - *m_last = 0; + char* newit = end(); + for (const char* it = first; it != last; ++it, ++newit) + *newit = *it; + *newit = 0; + m_size = newsize | (m_size & c_longflag); } inline void string::swap(string& other) { - const pointer tfirst = m_first, tlast = m_last, tcapacity = m_capacity; - m_first = other.m_first, m_last = other.m_last, m_capacity = other.m_capacity; - other.m_first = tfirst, other.m_last = tlast, other.m_capacity = tcapacity; - + size_t tsize = m_size; + pointer tfirst, tcapacity; char tbuffer[c_nbuffer]; - if (m_first == other.m_buffer) - for (pointer it = other.m_buffer, end = m_last, out = tbuffer; it != end; ++it, ++out) - *out = *it; + if (tsize & c_longflag) { + tfirst = m_first; + tcapacity = m_capacity; + } else { + for (pointer it = m_buffer, newit = tbuffer, e = m_buffer + tsize; it != e; ++it, ++newit) + *newit = *it; + } - if (other.m_first == m_buffer) { - other.m_last = other.m_last - other.m_first + other.m_buffer; - other.m_first = other.m_buffer; - other.m_capacity = other.m_buffer + c_nbuffer; + m_size = other.m_size; + if (other.m_size & c_longflag) { + m_first = other.m_first; + m_capacity = other.m_capacity; + } else { + for (pointer it = other.m_buffer, newit = m_buffer, e = other.m_buffer + m_size; it != e; ++it, ++newit) + *newit = *it; + m_buffer[m_size] = 0; + } - for (pointer it = other.m_first, end = other.m_last, in = m_buffer; it != end; ++it, ++in) - *it = *in; - *other.m_last = 0; + other.m_size = tsize; + if (tsize & c_longflag) { + other.m_first = tfirst; + other.m_capacity = tcapacity; + } else { + for (pointer it = tbuffer, newit = other.m_buffer, e = tbuffer + tsize; it != e; ++it, ++newit) + *newit = *it; + other.m_buffer[tsize] = 0; } + } - if (m_first == other.m_buffer) { - m_last = m_last - m_first + m_buffer; - m_first = m_buffer; - m_capacity = m_buffer + c_nbuffer; + inline string::iterator string::begin() { + if (m_size & c_longflag) + return m_first; + else + return m_buffer; + } - for (pointer it = m_first, end = m_last, in = tbuffer; it != end; ++it, ++in) - *it = *in; - *m_last = 0; - } + inline string::iterator string::end() { + if (m_size & c_longflag) + return m_first + (m_size & ~c_longflag); + else + return m_buffer + m_size; + } + + inline string::const_iterator string::begin() const { + if (m_size & c_longflag) + return m_first; + else + return m_buffer; + } + + inline string::const_iterator string::end() const { + if (m_size & c_longflag) + return m_first + (m_size & ~c_longflag); + else + return m_buffer + m_size; } inline bool operator==(const string& lhs, const string& rhs) { @@ -195,10 +254,8 @@ namespace tinystl { if (lsize != rsize) return false; - pointer lit = lhs.c_str(), rit = rhs.c_str(); - pointer lend = lit + lsize; - while (lit != lend) - if (*lit++ != *rit++) + for (pointer lit = lhs.c_str(), rit = rhs.c_str(), lend = lit + lsize; lit != lend; ++lit, ++rit) + if (*lit != *rit) return false; return true; diff --git a/test/string_assign.cpp b/test/string_assign.cpp new file mode 100644 index 0000000..c5287ed --- /dev/null +++ b/test/string_assign.cpp @@ -0,0 +1,100 @@ +/*- + * Copyright 2012-2015 Matthew Endsley + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +TEST(test_empty) { + tinystl::string s; + CHECK(s.size() == 0); + CHECK(s.capacity() == 15); + CHECK(s.begin() == s.end()); + CHECK(strlen(s.c_str()) == 0); + CHECK(s == ""); +} + +TEST(test_small) { + tinystl::string s1(""); + CHECK(s1.size() == 0); + CHECK(s1.capacity() == 15); + CHECK(s1.begin() == s1.end()); + CHECK(strlen(s1.c_str()) == 0); + CHECK(s1 == ""); + + tinystl::string s2("hello"); + CHECK(s2.size() == 5); + CHECK(s2.capacity() == 15); + CHECK(s2.begin() + 5 == s2.end()); + CHECK(strlen(s2.c_str()) == 5); + CHECK(s2 == "hello"); + + tinystl::string s3("exactly 15 char"); + CHECK(s3.size() == 15); + CHECK(s3.capacity() == 15); + CHECK(s3.begin() + 15 == s3.end()); + CHECK(strlen(s3.c_str()) == 15); + CHECK(s3 == "exactly 15 char"); +} + +TEST(test_long) { + const char* origin = "very long string larger than large string limit"; + size_t len = strlen(origin); + tinystl::string s(origin); + CHECK(s.size() == len); + CHECK(s.capacity() == len); + CHECK(s.begin() + len == s.end()); + CHECK(strlen(s.c_str()) == len); + CHECK(s == origin); +} + +TEST(test_assign) { + tinystl::string s; + const char* originsmall = "small"; + size_t lensmall = strlen(originsmall); + s = originsmall; + CHECK(s.size() == lensmall); + CHECK(s.capacity() == 15); + CHECK(s.begin() + lensmall == s.end()); + CHECK(strlen(s.c_str()) == lensmall); + CHECK(s == originsmall); + + const char* originlong = "long long long long long long long long long long long long"; + size_t lenlong = strlen(originlong); + s = originlong; + CHECK(s.size() == lenlong); + CHECK(s.capacity() == lenlong); + CHECK(s.begin() + lenlong == s.end()); + CHECK(strlen(s.c_str()) == lenlong); + CHECK(s == originlong); + + s = originsmall; + CHECK(s.size() == lensmall); + CHECK(s.capacity() == lenlong); + CHECK(s.begin() + lensmall == s.end()); + CHECK(strlen(s.c_str()) == lensmall); + CHECK(s == originsmall); +} diff --git a/test/string_resize.cpp b/test/string_resize.cpp new file mode 100644 index 0000000..bb9fc66 --- /dev/null +++ b/test/string_resize.cpp @@ -0,0 +1,48 @@ +/*- + * Copyright 2012-2015 Matthew Endsley + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +TEST(test_reserve) { + tinystl::string s; + s.reserve(0); + CHECK(s.capacity() == 15); + s.reserve(10); + s = "short"; + CHECK(s.capacity() == 15); + CHECK(s == "short"); + s.reserve(15); + CHECK(s.capacity() == 15); + CHECK(s == "short"); + s.reserve(100); + CHECK(s.capacity() == 100); + CHECK(s == "short"); + s.reserve(101); + CHECK(s.capacity() == 101); + CHECK(s == "short"); +} From b2ec80e427120fee56c625a04dcbe56f79c9abe3 Mon Sep 17 00:00:00 2001 From: Oleg Andreev Date: Mon, 2 Nov 2015 15:24:56 +0300 Subject: [PATCH 2/3] Added a bunch of useful operators and methods. --- include/TINYSTL/string.h | 132 +++++++++++++++++++++++++++++++++++---- test/string_assign.cpp | 51 ++++++++++----- test/string_compare.cpp | 39 ++++++++++++ test/string_modify.cpp | 39 ++++++++++++ test/string_resize.cpp | 13 ++++ 5 files changed, 247 insertions(+), 27 deletions(-) create mode 100644 test/string_compare.cpp create mode 100644 test/string_modify.cpp diff --git a/include/TINYSTL/string.h b/include/TINYSTL/string.h index 9546f6f..bbc2260 100644 --- a/include/TINYSTL/string.h +++ b/include/TINYSTL/string.h @@ -42,16 +42,30 @@ namespace tinystl { ~string(); string& operator=(const string& other); + string& operator=(char ch); + string& operator=(const char* sz); + + string& operator+=(const string& other); + string& operator+=(char ch); + string& operator+=(const char* sz); const char* c_str() const; + bool empty() const; size_t size() const; size_t capacity() const; void reserve(size_t capacity); - void resize(size_t size); + void resize(size_t newsize); + void resize(size_t newsize, char ch); + + void clear(); + void assign(char ch); + void assign(const char* sz); void assign(const char* first, const char* last); + void push_back(char ch); + void append(const char* sz); void append(const char* first, const char* last); void swap(string& other); @@ -93,11 +107,7 @@ namespace tinystl { inline string::string(const char* sz) : m_size(0) { - size_t len = 0; - for (const char* it = sz; *it; ++it) - ++len; - - assign(sz, sz + len); + assign(sz); } inline string::string(const char* sz, size_t len) @@ -117,6 +127,31 @@ namespace tinystl { return *this; } + inline string& string::operator=(char ch) { + assign(ch); + return *this; + } + + inline string& string::operator=(const char* sz) { + assign(sz); + return *this; + } + + inline string& string::operator+=(const string& other) { + append(other.begin(), other.end()); + return *this; + } + + inline string& string::operator+=(char ch) { + push_back(ch); + return *this; + } + + inline string& string::operator+=(const char* sz) { + append(sz); + return *this; + } + inline const char* string::c_str() const { if (m_size & c_longflag) return m_first; @@ -124,6 +159,10 @@ namespace tinystl { return m_buffer; } + inline bool string::empty() const { + return size() == 0; + } + inline size_t string::size() const { return m_size & ~c_longflag; } @@ -153,13 +192,36 @@ namespace tinystl { m_capacity = m_first + cap + 1; } - inline void string::resize(size_t size) { - reserve(size); - pointer newend = begin() + size; - for (pointer it = end(); it != newend; ++it) - *it = 0; - *newend = 0; - m_size = size; + inline void string::resize(size_t newsize) { + resize(newsize, 0); + } + + inline void string::resize(size_t newsize, char ch) { + if (size() < newsize) { + reserve(newsize); + for (pointer it = end(), newend = begin() + newsize; it != newend; ++it) + *it = ch; + } + m_size = newsize | (m_size & c_longflag); + *end() = 0; + } + + inline void string::clear() { + resize(0); + } + + inline void string::assign(char ch) { + m_size = 1 | (m_size & c_longflag); + *begin() = ch; + *end() = 0; + } + + inline void string::assign(const char* sz) { + size_t len = 0; + for (const char *it = sz; *it; ++it) + ++len; + + assign(sz, sz + len); } inline void string::assign(const char* first, const char* last) { @@ -173,6 +235,24 @@ namespace tinystl { m_size = newsize | (m_size & c_longflag); } + inline void string::push_back(char ch) { + if (size() + 1 < capacity()) { + char* e = end(); + *e = ch; + *(e + 1) = 0; + ++m_size; + } else { + append(&ch, &ch + 1); + } + } + + inline void string::append(const char *sz) { + size_t len = 0; + for (const char *it = sz; *it; ++it) + ++len; + append(sz, sz + len); + } + inline void string::append(const char* first, const char* last) { const size_t newsize = (size_t)(size() + (last - first)); if (newsize > capacity()) @@ -261,6 +341,32 @@ namespace tinystl { return true; } + inline bool operator==(const string& lhs, const char* rhs) { + typedef const char* pointer; + + const size_t lsize = lhs.size(); + for (pointer lit = lhs.c_str(), rit = rhs, lend = lit + lsize; lit != lend; ++lit, ++rit) + if (*lit != *rit) + return false; + return *(rhs + lsize) == 0; + } + + inline bool operator==(const char* lhs, const string& rhs) { + return rhs == lhs; + } + + inline bool operator!=(const string& lhs, const string& rhs) { + return !(lhs == rhs); + } + + inline bool operator!=(const string& lhs, const char* rhs) { + return !(lhs == rhs); + } + + inline bool operator!=(const char* lhs, const string& rhs) { + return !(lhs == rhs); + } + static inline size_t hash(const string& value) { return hash_string(value.c_str(), value.size()); } diff --git a/test/string_assign.cpp b/test/string_assign.cpp index c5287ed..13ca126 100644 --- a/test/string_assign.cpp +++ b/test/string_assign.cpp @@ -30,7 +30,7 @@ TEST(test_empty) { tinystl::string s; - CHECK(s.size() == 0); + CHECK(s.empty()); CHECK(s.capacity() == 15); CHECK(s.begin() == s.end()); CHECK(strlen(s.c_str()) == 0); @@ -39,7 +39,7 @@ TEST(test_empty) { TEST(test_small) { tinystl::string s1(""); - CHECK(s1.size() == 0); + CHECK(s1.empty()); CHECK(s1.capacity() == 15); CHECK(s1.begin() == s1.end()); CHECK(strlen(s1.c_str()) == 0); @@ -73,14 +73,14 @@ TEST(test_long) { TEST(test_assign) { tinystl::string s; - const char* originsmall = "small"; - size_t lensmall = strlen(originsmall); - s = originsmall; - CHECK(s.size() == lensmall); + const char* originshort = "short"; + size_t lenshort = strlen(originshort); + s = originshort; + CHECK(s.size() == lenshort); CHECK(s.capacity() == 15); - CHECK(s.begin() + lensmall == s.end()); - CHECK(strlen(s.c_str()) == lensmall); - CHECK(s == originsmall); + CHECK(s.begin() + lenshort == s.end()); + CHECK(strlen(s.c_str()) == lenshort); + CHECK(s == originshort); const char* originlong = "long long long long long long long long long long long long"; size_t lenlong = strlen(originlong); @@ -91,10 +91,33 @@ TEST(test_assign) { CHECK(strlen(s.c_str()) == lenlong); CHECK(s == originlong); - s = originsmall; - CHECK(s.size() == lensmall); + s = originshort; + CHECK(s.size() == lenshort); CHECK(s.capacity() == lenlong); - CHECK(s.begin() + lensmall == s.end()); - CHECK(strlen(s.c_str()) == lensmall); - CHECK(s == originsmall); + CHECK(s.begin() + lenshort == s.end()); + CHECK(strlen(s.c_str()) == lenshort); + CHECK(s == originshort); } + +TEST(test_swap) { + tinystl::string ss1("short"); + tinystl::string ss2("another"); + tinystl::string sl1("long string for testing purposes"); + tinystl::string sl2("another long string for testing purposes"); + + ss1.swap(ss2); + CHECK(ss1 == "another"); + CHECK(ss2 == "short"); + + sl1.swap(sl2); + CHECK(sl1 == "another long string for testing purposes"); + CHECK(sl2 == "long string for testing purposes"); + + ss1.swap(sl2); + CHECK(ss1 == "long string for testing purposes"); + CHECK(sl2 == "another"); + + sl1.swap(ss2); + CHECK(sl1 == "short"); + CHECK(ss2 == "another long string for testing purposes"); +} \ No newline at end of file diff --git a/test/string_compare.cpp b/test/string_compare.cpp new file mode 100644 index 0000000..c321aef --- /dev/null +++ b/test/string_compare.cpp @@ -0,0 +1,39 @@ +/*- + * Copyright 2012-2015 Matthew Endsley + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +TEST(test_equal) { + tinystl::string s("hello"); + CHECK(s == tinystl::string("hello")); + CHECK(s == "hello"); + CHECK("hello" == s); + CHECK(s != tinystl::string("hello world")); + CHECK(s != "hello world"); + CHECK("hello world" != s); +} \ No newline at end of file diff --git a/test/string_modify.cpp b/test/string_modify.cpp new file mode 100644 index 0000000..bd41d90 --- /dev/null +++ b/test/string_modify.cpp @@ -0,0 +1,39 @@ +/*- + * Copyright 2012-2015 Matthew Endsley + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +TEST(test_append) { + tinystl::string s; + s += "hello"; + s += ' '; + s += "world"; + CHECK(s == "hello world"); + s += " and this is a very long string"; + CHECK(s == "hello world and this is a very long string"); +} \ No newline at end of file diff --git a/test/string_resize.cpp b/test/string_resize.cpp index bb9fc66..8a1344f 100644 --- a/test/string_resize.cpp +++ b/test/string_resize.cpp @@ -46,3 +46,16 @@ TEST(test_reserve) { CHECK(s.capacity() == 101); CHECK(s == "short"); } + +TEST(test_resize) { + tinystl::string s; + s.resize(1, ' '); + CHECK(s == " "); + s.resize(16, '+'); + CHECK(s == " +++++++++++++++"); + s.clear(); + s.resize(16, '@'); + CHECK(s == "@@@@@@@@@@@@@@@@"); + s.resize(12, '-'); + CHECK(s == "@@@@@@@@@@@@"); +} \ No newline at end of file From a67d7af72b37ce43aa147ec68f43b52684bb5ac7 Mon Sep 17 00:00:00 2001 From: Oleg Andreev Date: Tue, 3 Nov 2015 12:38:29 +0300 Subject: [PATCH 3/3] Fixed a bug in reserve(), added another bunch of methods and operators. --- include/TINYSTL/string.h | 306 ++++++++++++++++++++++++++++----------- test/string_compare.cpp | 35 ++++- test/string_modify.cpp | 34 ++++- 3 files changed, 291 insertions(+), 84 deletions(-) diff --git a/include/TINYSTL/string.h b/include/TINYSTL/string.h index bbc2260..2382be6 100644 --- a/include/TINYSTL/string.h +++ b/include/TINYSTL/string.h @@ -38,6 +38,7 @@ namespace tinystl { string(); string(const string& other); string(const char* sz); + string(const char* first, const char* last); string(const char* sz, size_t len); ~string(); @@ -54,30 +55,43 @@ namespace tinystl { size_t size() const; size_t capacity() const; + typedef char* iterator; + iterator begin(); + iterator end(); + + typedef const char* const_iterator; + const_iterator begin() const; + const_iterator end() const; + + char operator[](size_t pos) const; + char& operator[](size_t pos); + + int compare(const string& other) const; + int compare(const char* sz) const; + void reserve(size_t capacity); - void resize(size_t newsize); - void resize(size_t newsize, char ch); + void resize(size_t n); + void resize(size_t n, char ch); void clear(); void assign(char ch); void assign(const char* sz); void assign(const char* first, const char* last); + void assign(const string& other); void push_back(char ch); void append(const char* sz); void append(const char* first, const char* last); + void append(const string& other); + void insert(iterator where, char ch); + void insert(iterator where, const char* sz); + void insert(iterator where, const char* first, const char* last); + void insert(iterator where, const string& other); + void erase(iterator first, iterator last); void swap(string& other); - typedef char* iterator; - iterator begin(); - iterator end(); - - typedef const char* const_iterator; - const_iterator begin() const; - const_iterator end() const; - private: typedef char* pointer; static const size_t c_nbuffer = 16; @@ -101,7 +115,7 @@ namespace tinystl { inline string::string(const string& other) : m_size(0) { - assign(other.begin(), other.end()); + assign(other); } inline string::string(const char* sz) @@ -110,6 +124,12 @@ namespace tinystl { assign(sz); } + inline string::string(const char* first, const char* last) + : m_size(0) + { + assign(first, last); + } + inline string::string(const char* sz, size_t len) : m_size(0) { @@ -123,7 +143,7 @@ namespace tinystl { inline string& string::operator=(const string& other) { if (this != &other) - assign(other.begin(), other.end()); + assign(other); return *this; } @@ -138,7 +158,7 @@ namespace tinystl { } inline string& string::operator+=(const string& other) { - append(other.begin(), other.end()); + append(other); return *this; } @@ -174,36 +194,80 @@ namespace tinystl { return c_nbuffer - 1; } + inline string::iterator string::begin() { + if (m_size & c_longflag) + return m_first; + else + return m_buffer; + } + + inline string::iterator string::end() { + if (m_size & c_longflag) + return m_first + (m_size & ~c_longflag); + else + return m_buffer + m_size; + } + + inline string::const_iterator string::begin() const { + if (m_size & c_longflag) + return m_first; + else + return m_buffer; + } + + inline string::const_iterator string::end() const { + if (m_size & c_longflag) + return m_first + (m_size & ~c_longflag); + else + return m_buffer + m_size; + } + + inline char string::operator[](size_t pos) const { + return begin()[pos]; + } + + inline char& string::operator[](size_t pos) { + return begin()[pos]; + } + + inline int string::compare(const string& other) const { + return compare(other.c_str()); + } + + inline int string::compare(const char* sz) const { + const char* it = c_str(); + for (; *it && *sz && (*it == *sz); ++it, ++sz); + return *it - *sz; + } + inline void string::reserve(size_t cap) { if (cap <= capacity()) return; pointer newfirst = (pointer)TINYSTL_ALLOCATOR::static_allocate(cap + 1); - if (m_size & c_longflag) { - for (pointer it = m_first, newit = newfirst, e = m_first + (m_size & ~c_longflag); it != e; ++it, ++newit) - *newit = *it; + for (pointer it = begin(), newit = newfirst, e = end() + 1; it != e; ++it, ++newit) + *newit = *it; + if (m_size & c_longflag) TINYSTL_ALLOCATOR::static_deallocate(m_first, m_capacity - m_first); - } else { - for (pointer it = m_buffer, newit = newfirst, e = m_buffer + m_size; it != e; ++it, ++newit) - *newit = *it; + else m_size |= c_longflag; - } m_first = newfirst; m_capacity = m_first + cap + 1; } - inline void string::resize(size_t newsize) { - resize(newsize, 0); + inline void string::resize(size_t n) { + resize(n, 0); } - inline void string::resize(size_t newsize, char ch) { - if (size() < newsize) { - reserve(newsize); - for (pointer it = end(), newend = begin() + newsize; it != newend; ++it) + inline void string::resize(size_t n, char ch) { + if (size() < n) { + reserve(n); + for (pointer it = end(), e = begin() + n; it != e; ++it) *it = ch; } - m_size = newsize | (m_size & c_longflag); - *end() = 0; + pointer it = begin() + n; + *it = 0; + m_size = n | (m_size & c_longflag); } inline void string::clear() { @@ -211,9 +275,10 @@ namespace tinystl { } inline void string::assign(char ch) { + pointer it = begin(); + *it = ch; + *(it + 1) = 0; m_size = 1 | (m_size & c_longflag); - *begin() = ch; - *end() = 0; } inline void string::assign(const char* sz) { @@ -228,18 +293,22 @@ namespace tinystl { size_t newsize = last - first; reserve(newsize); - char* newit = begin(); + pointer newit = begin(); for (const char* it = first; it != last; ++it, ++newit) *newit = *it; *newit = 0; m_size = newsize | (m_size & c_longflag); } + inline void string::assign(const string& other) { + assign(other.begin(), other.end()); + } + inline void string::push_back(char ch) { - if (size() + 1 < capacity()) { - char* e = end(); - *e = ch; - *(e + 1) = 0; + if (size() != capacity()) { + pointer it = end(); + *it = ch; + *(it + 1) = 0; ++m_size; } else { append(&ch, &ch + 1); @@ -258,15 +327,65 @@ namespace tinystl { if (newsize > capacity()) reserve((newsize * 3) / 2); - char* newit = end(); + pointer newit = end(); + for (const char* it = first; it != last; ++it, ++newit) + *newit = *it; + *newit = 0; + m_size = newsize | (m_size & c_longflag); + } + + inline void string::append(const string& other) { + append(other.begin(), other.end()); + } + + inline void string::insert(iterator where, char ch) { + insert(where, &ch, &ch + 1); + } + + inline void string::insert(iterator where, const char* sz) { + size_t len = 0; + for (const char *it = sz; *it; ++it) + ++len; + insert(where, sz, sz + len); + } + + inline void string::insert(iterator where, const char* first, const char* last) { + if (first == last) + return; + + const size_t w = where - begin(); + const size_t newsize = (size_t)(size() + (last - first)); + if (newsize > capacity()) + reserve((newsize * 3) / 2); + + pointer newit = begin() + newsize; + for (pointer it = end(), e = begin() + w; it >= e; --it, --newit) + *newit = *it; + + newit = begin() + w; for (const char* it = first; it != last; ++it, ++newit) *newit = *it; + m_size = newsize | (m_size & c_longflag); + } + + inline void string::insert(iterator where, const string& other) { + insert(where, other.begin(), other.end()); + } + + inline void string::erase(iterator first, iterator last) { + if (first == last) + return; + + const size_t newsize = (size_t)(size() + (first - last)); + pointer newit = first; + for (pointer it = last, e = end(); it != e; ++it, ++newit) + *newit = *it; *newit = 0; m_size = newsize | (m_size & c_longflag); } inline void string::swap(string& other) { - size_t tsize = m_size; + const size_t tsize = m_size; pointer tfirst, tcapacity; char tbuffer[c_nbuffer]; @@ -274,7 +393,7 @@ namespace tinystl { tfirst = m_first; tcapacity = m_capacity; } else { - for (pointer it = m_buffer, newit = tbuffer, e = m_buffer + tsize; it != e; ++it, ++newit) + for (pointer it = m_buffer, newit = tbuffer, e = m_buffer + tsize + 1; it != e; ++it, ++newit) *newit = *it; } @@ -283,9 +402,8 @@ namespace tinystl { m_first = other.m_first; m_capacity = other.m_capacity; } else { - for (pointer it = other.m_buffer, newit = m_buffer, e = other.m_buffer + m_size; it != e; ++it, ++newit) + for (pointer it = other.m_buffer, newit = m_buffer, e = other.m_buffer + m_size + 1; it != e; ++it, ++newit) *newit = *it; - m_buffer[m_size] = 0; } other.m_size = tsize; @@ -293,66 +411,44 @@ namespace tinystl { other.m_first = tfirst; other.m_capacity = tcapacity; } else { - for (pointer it = tbuffer, newit = other.m_buffer, e = tbuffer + tsize; it != e; ++it, ++newit) + for (pointer it = tbuffer, newit = other.m_buffer, e = tbuffer + tsize + 1; it != e; ++it, ++newit) *newit = *it; - other.m_buffer[tsize] = 0; } } - inline string::iterator string::begin() { - if (m_size & c_longflag) - return m_first; - else - return m_buffer; - } - - inline string::iterator string::end() { - if (m_size & c_longflag) - return m_first + (m_size & ~c_longflag); - else - return m_buffer + m_size; + inline string operator+(const string& lhs, const string& rhs) { + string ret; + ret.reserve(lhs.size() + rhs.size()); + ret += lhs; + ret += rhs; + return ret; } - inline string::const_iterator string::begin() const { - if (m_size & c_longflag) - return m_first; - else - return m_buffer; + inline string operator+(const string& lhs, const char* rhs) { + string ret = lhs; + ret += rhs; + return ret; } - inline string::const_iterator string::end() const { - if (m_size & c_longflag) - return m_first + (m_size & ~c_longflag); - else - return m_buffer + m_size; + inline string operator+(const char* lhs, const string& rhs) { + string ret = lhs; + ret += rhs; + return ret; } inline bool operator==(const string& lhs, const string& rhs) { - typedef const char* pointer; - - const size_t lsize = lhs.size(), rsize = rhs.size(); - if (lsize != rsize) + if (lhs.size() != rhs.size()) return false; - for (pointer lit = lhs.c_str(), rit = rhs.c_str(), lend = lit + lsize; lit != lend; ++lit, ++rit) - if (*lit != *rit) - return false; - - return true; + return lhs.compare(rhs) == 0; } inline bool operator==(const string& lhs, const char* rhs) { - typedef const char* pointer; - - const size_t lsize = lhs.size(); - for (pointer lit = lhs.c_str(), rit = rhs, lend = lit + lsize; lit != lend; ++lit, ++rit) - if (*lit != *rit) - return false; - return *(rhs + lsize) == 0; + return lhs.compare(rhs) == 0; } inline bool operator==(const char* lhs, const string& rhs) { - return rhs == lhs; + return rhs.compare(lhs) == 0; } inline bool operator!=(const string& lhs, const string& rhs) { @@ -367,6 +463,54 @@ namespace tinystl { return !(lhs == rhs); } + inline bool operator<(const string& lhs, const string& rhs) { + return lhs.compare(rhs) < 0; + } + + inline bool operator<(const string& lhs, const char* rhs) { + return lhs.compare(rhs) < 0; + } + + inline bool operator<(const char* lhs, const string& rhs) { + return rhs.compare(lhs) > 0; + } + + inline bool operator>(const string& lhs, const string& rhs) { + return rhs < lhs; + } + + inline bool operator>(const string& lhs, const char* rhs) { + return rhs < lhs; + } + + inline bool operator>(const char* lhs, const string& rhs) { + return rhs < lhs; + } + + inline bool operator<=(const string& lhs, const string& rhs) { + return !(rhs < lhs); + } + + inline bool operator<=(const string& lhs, const char* rhs) { + return !(rhs < lhs); + } + + inline bool operator<=(const char* lhs, const string& rhs) { + return !(rhs < lhs); + } + + inline bool operator>=(const string& lhs, const string& rhs) { + return !(lhs < rhs); + } + + inline bool operator>=(const string& lhs, const char* rhs) { + return !(lhs < rhs); + } + + inline bool operator>=(const char* lhs, const string& rhs) { + return !(lhs < rhs); + } + static inline size_t hash(const string& value) { return hash_string(value.c_str(), value.size()); } diff --git a/test/string_compare.cpp b/test/string_compare.cpp index c321aef..891fe1a 100644 --- a/test/string_compare.cpp +++ b/test/string_compare.cpp @@ -26,8 +26,6 @@ #include #include -#include - TEST(test_equal) { tinystl::string s("hello"); CHECK(s == tinystl::string("hello")); @@ -36,4 +34,37 @@ TEST(test_equal) { CHECK(s != tinystl::string("hello world")); CHECK(s != "hello world"); CHECK("hello world" != s); +} + +TEST(test_ltgt) { + tinystl::string s("hello"); + CHECK(!(s < "hello")); + CHECK(s < "helloo"); + CHECK(s < "hello0"); + CHECK(s > "he1"); + CHECK(s > "hell"); + CHECK(s > "a"); + CHECK(s < "z"); + CHECK(s > "aaaaaaaa"); + CHECK(s < "zzzzzzzz"); + CHECK(s > "hella"); + CHECK(s < "hellz"); + CHECK(s < "hellz"); +} + +TEST(test_lege) { + tinystl::string s("hello"); + CHECK(s <= "hello"); + CHECK(s >= "hello"); + CHECK(s <= "helloo"); + CHECK(s <= "hello0"); + CHECK(s >= "he1"); + CHECK(s >= "hell"); + CHECK(s >= "a"); + CHECK(s <= "z"); + CHECK(s >= "aaaaaaaa"); + CHECK(s <= "zzzzzzzz"); + CHECK(s >= "hella"); + CHECK(s <= "hellz"); + CHECK(s <= "hellz"); } \ No newline at end of file diff --git a/test/string_modify.cpp b/test/string_modify.cpp index bd41d90..4057345 100644 --- a/test/string_modify.cpp +++ b/test/string_modify.cpp @@ -26,7 +26,6 @@ #include #include -#include TEST(test_append) { tinystl::string s; @@ -36,4 +35,37 @@ TEST(test_append) { CHECK(s == "hello world"); s += " and this is a very long string"; CHECK(s == "hello world and this is a very long string"); +} + +TEST(test_add) { + CHECK(tinystl::string("hello") + tinystl::string(" world") == "hello world"); + CHECK(tinystl::string("hello") + " world" == "hello world"); + CHECK(tinystl::string("hello") + " " + "world" == "hello world"); + CHECK("hello" + tinystl::string(" ") + "world" == "hello world"); +} + +TEST(test_insert) { + tinystl::string s("world"); + s.insert(s.end(), '!'); + CHECK(s == "world!"); + s.insert(s.begin(), "hello"); + CHECK(s == "helloworld!"); + s.insert(s.begin() + 5, " "); + CHECK(s == "hello world!"); + s.insert(s.end() - 1, ", prepend a huge string to check"); + CHECK(s == "hello world, prepend a huge string to check!"); +} + +TEST(test_erase) { + tinystl::string s("hello"); + s.erase(s.begin(), s.end()); + CHECK(s.empty()); + s = "hello"; + s.erase(s.end() - 1, s.end()); + CHECK(s == "hell"); + s = "hello world and this is a very long string"; + s.erase(s.begin(), s.begin() + 4); + CHECK(s == "o world and this is a very long string"); + s.erase(s.begin(), s.end()); + CHECK(s.empty()); } \ No newline at end of file