]> git.lyx.org Git - lyx.git/commitdiff
First version of trivstring class (bug #9336)
authorGeorg Baum <baum@lyx.org>
Sun, 7 Dec 2014 09:50:18 +0000 (10:50 +0100)
committerGeorg Baum <baum@lyx.org>
Sun, 7 Dec 2014 12:14:17 +0000 (13:14 +0100)
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.

src/support/tests/check_trivstring.cpp [new file with mode: 0644]
src/support/tests/regfiles/trivstring [new file with mode: 0644]
src/support/tests/test_trivstring [new file with mode: 0755]
src/support/trivstring.cpp [new file with mode: 0644]
src/support/trivstring.h [new file with mode: 0644]

diff --git a/src/support/tests/check_trivstring.cpp b/src/support/tests/check_trivstring.cpp
new file mode 100644 (file)
index 0000000..e6a23d5
--- /dev/null
@@ -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();
+}
diff --git a/src/support/tests/regfiles/trivstring b/src/support/tests/regfiles/trivstring
new file mode 100644 (file)
index 0000000..e23bd55
--- /dev/null
@@ -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 (executable)
index 0000000..6e57bb1
--- /dev/null
@@ -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 (file)
index 0000000..71085ee
--- /dev/null
@@ -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
diff --git a/src/support/trivstring.h b/src/support/trivstring.h
new file mode 100644 (file)
index 0000000..4123d32
--- /dev/null
@@ -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