diff --git a/src/support/tests/check_trivstring.cpp b/src/support/tests/check_trivstring.cpp new file mode 100644 index 0000000000..e6a23d5603 --- /dev/null +++ b/src/support/tests/check_trivstring.cpp @@ -0,0 +1,48 @@ +#include + +#include "../trivstring.h" + +#include + + +using namespace lyx; + +using namespace std; + +void test_trivstring() +{ + string const input[] = { + "", + "a", + "42", + "max sso", // max. string with sso on 64 bit + "something which does not fit into sso" + }; + size_t const n = sizeof(input) / sizeof(input[0]); + for (size_t i = 0; i < n; ++i) { + // construction from std::string + trivstring const a(input[i]); + // construction from trivstring + trivstring const b(a); + // assignment from trivstring + trivstring const c = a; + // assignment from std::string + trivstring const d = input[i]; + // assignment from trivstring + string const e = a.str(); + // assignment from trivstring via C string + string const f = a.c_str(); + cout << a.length() << endl; + cout << a.str() << endl; + cout << b.str() << endl; + cout << c.str() << endl; + cout << d.str() << endl; + cout << e << endl; + cout << f << endl; + } +} + +int main() +{ + test_trivstring(); +} diff --git a/src/support/tests/regfiles/trivstring b/src/support/tests/regfiles/trivstring new file mode 100644 index 0000000000..e23bd5526c --- /dev/null +++ b/src/support/tests/regfiles/trivstring @@ -0,0 +1,35 @@ +0 + + + + + + +1 +a +a +a +a +a +a +2 +42 +42 +42 +42 +42 +42 +7 +max sso +max sso +max sso +max sso +max sso +max sso +37 +something which does not fit into sso +something which does not fit into sso +something which does not fit into sso +something which does not fit into sso +something which does not fit into sso +something which does not fit into sso diff --git a/src/support/tests/test_trivstring b/src/support/tests/test_trivstring new file mode 100755 index 0000000000..6e57bb1e62 --- /dev/null +++ b/src/support/tests/test_trivstring @@ -0,0 +1,7 @@ +#!/bin/sh + +regfile=`cat ${srcdir}/tests/regfiles/trivstring` +output=`./check_trivstring` + +test "$regfile" = "$output" +exit $? diff --git a/src/support/trivstring.cpp b/src/support/trivstring.cpp new file mode 100644 index 0000000000..71085ee04e --- /dev/null +++ b/src/support/trivstring.cpp @@ -0,0 +1,180 @@ +/** + * \file trivstring.cpp + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Georg Baum + * + * Full author contact details are available in file CREDITS. + */ + +#include + +#include "support/trivstring.h" +#include "support/docstring.h" + +#include + +using namespace std; + +namespace lyx { + +template trivial_string::trivial_string(trivial_string const &); +template trivial_string::trivial_string(trivial_string const &); +template +trivial_string::trivial_string(trivial_string const & that) : size_(that.size_) +{ + if (use_sso()) + copy(that.data_sso(), that.data_sso() + size_ + 1, data_sso()); + else if (size_ > 0) { + data_ = new Char[size_ + 1]; + copy(that.data_, that.data_ + size_ + 1, data_); + } + // else Happens only for really big Char types +} + + +template trivial_string::trivial_string(string const &); +template trivial_string::trivial_string(docstring const &); +template +trivial_string::trivial_string( + basic_string, allocator > const & that) + : size_(that.length()) +{ + if (use_sso()) { + copy(that.begin(), that.end(), data_sso()); + data_sso()[size_] = '\0'; + } else if (size_ > 0) { + data_ = new Char[size_ + 1]; + copy(that.begin(), that.end(), data_); + data_[size_] = '\0'; + } + // else Happens only for really big Char types +} + + +template trivial_string & +trivial_string::operator=(trivial_string const &); +template trivial_string & +trivial_string::operator=(trivial_string const &); +template +trivial_string & trivial_string::operator=(trivial_string const & that) +{ + if (&that == this) + return *this; + if (!use_sso()) + delete[] data_; + size_ = that.size_; + if (use_sso()) + copy(that.data_sso(), that.data_sso() + size_ + 1, data_sso()); + else if (size_ > 0) { + data_ = new Char[size_ + 1]; + copy(that.data_, that.data_ + size_ + 1, data_); + } else { + // Happens only for really big Char types + data_ = 0; + } + return *this; +} + + +template trivial_string & +trivial_string::operator=(string const &); +template trivial_string & +trivial_string::operator=(docstring const &); +template +trivial_string & +trivial_string::operator=(basic_string, allocator > const & that) +{ + if (!use_sso()) + delete[] data_; + size_ = that.size(); + if (use_sso()) { + copy(that.begin(), that.end(), data_sso()); + data_sso()[size_] = '\0'; + } else if (size_ > 0) { + data_ = new Char[size_ + 1]; + copy(that.begin(), that.end(), data_); + } else { + // Happens only for really big Char types + data_ = 0; + } + return *this; +} + + +template void +trivial_string::swap(trivial_string &); +template void +trivial_string::swap(trivial_string &); +template +void trivial_string::swap(trivial_string & that) +{ + size_t const sizetmp = that.size_; + that.size_ = size_; + size_ = sizetmp; + Char * const datatmp = that.data_; + that.data_ = data_; + data_ = datatmp; +} + + +template +int trivial_string::compare(trivial_string const & other) const +{ + size_t const lsize = this->length(); + size_t const rsize = other.length(); + size_t const len = min(lsize, rsize); + int r = char_traits::compare(c_str(), other.c_str(), len); + if (r == 0) { + if (lsize > rsize) + r = 1; + else if (lsize < rsize) + r = -1; + } + return r; +} + + +template string trivial_string::str() const; +template docstring trivial_string::str() const; +template +basic_string, allocator > +trivial_string::str() const +{ + if (use_sso()) + return basic_string, allocator >( + data_sso(), size_); + if (size_ > 0) + return basic_string, allocator >( + data_, size_); + // Happens only for really big Char types + return basic_string, allocator >(); +} + + +template char const * trivial_string::c_str() const; +template char_type const * trivial_string::c_str() const; +template Char const * trivial_string::c_str() const +{ + if (use_sso()) + return data_sso(); + if (size_ > 0) + return data_; + // Happens only for really big Char types + static const Char empty_char = '\0'; + return &empty_char; +} + + +template bool operator<(trivial_string const &, + trivial_string const &); +template bool operator<(trivial_string const &, + trivial_string const &); +template +bool operator<(trivial_string const & lhs, trivial_string const &rhs) +{ + return lhs.compare(rhs) < 0; +} + +} // namespace lyx diff --git a/src/support/trivstring.h b/src/support/trivstring.h new file mode 100644 index 0000000000..4123d32ed1 --- /dev/null +++ b/src/support/trivstring.h @@ -0,0 +1,87 @@ +// -*- C++ -*- +/** + * \file trivstring.h + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Georg Baum + * + * Full author contact details are available in file CREDITS. + */ + +#ifndef LYX_TRIVSTRING_H +#define LYX_TRIVSTRING_H + +#include "support/strfwd.h" + +#include + +namespace lyx { + +/** + * Trivial string class with almost no features. + * The public interface is a subset of the std::basic_string interface. + * The only important feature is that any read-only access does not need + * synchronization between multiple threads, i.e. it is thread-safe without + * locking. + * Therefore you can safely use a const trivial_string object in multiple + * threads at the same time. This is not the case for std::basic_string in some + * STL implementations (e. g. GNU libcstd++, see bug 9336 and + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=21334. + * This class should not be used for anything else than providing thread-safety. + * It should be removed as soon as LyX requires C++11, and all supported STL + * implementations provide a C++11 conformant std::basic_string. + */ +template class trivial_string +{ +public: + /// Construct an empty string + trivial_string() : size_(0), data_(0) {} + /// Construct a string from a copy of \p that + trivial_string(trivial_string const & that); + /// Construct a string from a copy of \p that + trivial_string(std::basic_string, std::allocator > const & that); + /// + ~trivial_string() { if (!use_sso()) delete[] data_; } + /// Assign a copy of \p that + trivial_string & operator=(trivial_string const & that); + /// Assign a copy of \p that + trivial_string & operator=(std::basic_string, std::allocator > const & that); + /// Exchange contents with contents of \p that + void swap(trivial_string & that); + /// The length of the string, excluding the final 0 character + size_t length() const { return size_; } + /// Is this string empty? + bool empty() const { return size_ == 0; } + /// Is this string ordered before, at the same position or after \p other? + int compare(trivial_string const & other) const; + /// Create a copy as std::basic_string + std::basic_string, std::allocator > str() const; + /// Return a C-compatible string, terminated by a 0 character. + /// This is never a copy and only valid for the life time of the trivial_string instance. + Char const * c_str() const; +private: + /** + * Whether short string optimization is used. + * Short string optimization is a technique where no additional memory + * needs to be allocated to store the string contents. + * Instead, the memory which would be used to store the pointer to the + * character buffer is reinterpreted to be a Char * buffer. + * On most 64 bit systems and with Char == char this allows to store + * strings of up to 7 characters without allocating additional memory. + */ + bool use_sso() const { return (size_ + 1) * sizeof(Char) <= sizeof(Char *); } + /// The character storage if sso is used + Char * data_sso() { return reinterpret_cast(&data_); } + /// The character storage if sso is used + Char const * data_sso() const { return reinterpret_cast(&data_); } + /// The length of the string, excluding the final 0 character + size_t size_; + /// The character storage + Char * data_; +}; +template bool operator<(trivial_string const & lhs, trivial_string const &rhs); + + +} // namespace lyx +#endif