First version of trivstring class (bug #9336)

As discused on the list. This is not used yet, but it is intended to provide
thread-safe read-access without the need for synchronization if the used STL
implementation does not provide it for std::basic_string. This is the case for
all implementations using copy-on-write.
This commit is contained in:
Georg Baum 2014-12-07 10:50:18 +01:00
parent 2614d5a386
commit 46f7b578b2
5 changed files with 357 additions and 0 deletions

View File

@ -0,0 +1,48 @@
#include <config.h>
#include "../trivstring.h"
#include <iostream>
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();
}

View File

@ -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

View File

@ -0,0 +1,7 @@
#!/bin/sh
regfile=`cat ${srcdir}/tests/regfiles/trivstring`
output=`./check_trivstring`
test "$regfile" = "$output"
exit $?

180
src/support/trivstring.cpp Normal file
View File

@ -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 <config.h>
#include "support/trivstring.h"
#include "support/docstring.h"
#include <algorithm>
using namespace std;
namespace lyx {
template trivial_string<char>::trivial_string(trivial_string const &);
template trivial_string<char_type>::trivial_string(trivial_string const &);
template<typename Char>
trivial_string<Char>::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<char>::trivial_string(string const &);
template trivial_string<char_type>::trivial_string(docstring const &);
template<typename Char>
trivial_string<Char>::trivial_string(
basic_string<Char, char_traits<Char>, allocator<Char> > 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<char> &
trivial_string<char>::operator=(trivial_string const &);
template trivial_string<char_type> &
trivial_string<char_type>::operator=(trivial_string const &);
template<typename Char>
trivial_string<Char> & trivial_string<Char>::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<char> &
trivial_string<char>::operator=(string const &);
template trivial_string<char_type> &
trivial_string<char_type>::operator=(docstring const &);
template<typename Char>
trivial_string<Char> &
trivial_string<Char>::operator=(basic_string<Char, char_traits<Char>, allocator<Char> > 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<char>::swap(trivial_string<char> &);
template void
trivial_string<char_type>::swap(trivial_string<char_type> &);
template<typename Char>
void trivial_string<Char>::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<typename Char>
int trivial_string<Char>::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<Char>::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<char>::str() const;
template docstring trivial_string<char_type>::str() const;
template<typename Char>
basic_string<Char, char_traits<Char>, allocator<Char> >
trivial_string<Char>::str() const
{
if (use_sso())
return basic_string<Char, char_traits<Char>, allocator<Char> >(
data_sso(), size_);
if (size_ > 0)
return basic_string<Char, char_traits<Char>, allocator<Char> >(
data_, size_);
// Happens only for really big Char types
return basic_string<Char, char_traits<Char>, allocator<Char> >();
}
template char const * trivial_string<char>::c_str() const;
template char_type const * trivial_string<char_type>::c_str() const;
template<typename Char> Char const * trivial_string<Char>::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<char> const &,
trivial_string<char> const &);
template bool operator<(trivial_string<char_type> const &,
trivial_string<char_type> const &);
template <typename Char>
bool operator<(trivial_string<Char> const & lhs, trivial_string<Char> const &rhs)
{
return lhs.compare(rhs) < 0;
}
} // namespace lyx

87
src/support/trivstring.h Normal file
View File

@ -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 <cstdlib>
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 <typename Char> 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<Char, std::char_traits<Char>, std::allocator<Char> > 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<Char, std::char_traits<Char>, std::allocator<Char> > 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<Char, std::char_traits<Char>, std::allocator<Char> > 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<Char * >(&data_); }
/// The character storage if sso is used
Char const * data_sso() const { return reinterpret_cast<Char const *>(&data_); }
/// The length of the string, excluding the final 0 character
size_t size_;
/// The character storage
Char * data_;
};
template <typename Char> bool operator<(trivial_string<Char> const & lhs, trivial_string<Char> const &rhs);
} // namespace lyx
#endif