]> git.lyx.org Git - lyx.git/blobdiff - src/support/unicode.cpp
Update shortcuts in fr.po
[lyx.git] / src / support / unicode.cpp
index 95415a5538e45ad11a4ed4695c5b277ae79024c3..b5839827403ba688987636a2ebc523d5932a7902 100644 (file)
@@ -14,7 +14,8 @@
 
 #include "support/unicode.h"
 #include "support/debug.h"
-#include "support/mutex.h"
+
+#include <QThreadStorage>
 
 #include <iconv.h>
 
@@ -48,102 +49,73 @@ namespace lyx {
        char const * ucs4_codeset = "UCS-4LE";
 #endif
 
-static const iconv_t invalid_cd = (iconv_t)(-1);
-
 
-struct IconvProcessor::Impl
-{
-       Impl(string const & to, string const & from)
-               : cd(invalid_cd), tocode_(to), fromcode_(from)
-       {}
-
-       ~Impl()
-       {
-               if (cd != invalid_cd && iconv_close(cd) == -1)
-                               LYXERR0("Error returned from iconv_close(" << errno << ")");
+struct IconvProcessor::Handler {
+       // assumes cd is valid
+       Handler(iconv_t const cd) : cd(cd) {}
+       ~Handler() {
+               if (iconv_close(cd) == -1)
+                       LYXERR0("Error returned from iconv_close(" << errno << ')');
        }
-
-       iconv_t cd;
-       string tocode_;
-       string fromcode_;
-
-       Mutex mutex_; // iconv() is not thread save, see #7240
+       iconv_t const cd;
 };
 
 
-IconvProcessor::IconvProcessor(char const * tocode, char const * fromcode)
-       : pimpl_(new IconvProcessor::Impl(tocode, fromcode))
-{
-}
-
-
-IconvProcessor::IconvProcessor(IconvProcessor const & other)
-       : pimpl_(new IconvProcessor::Impl(other.pimpl_->tocode_, other.pimpl_->fromcode_))
-{
-}
-
-
-IconvProcessor::~IconvProcessor()
-{
-       delete pimpl_;
-}
+IconvProcessor::IconvProcessor(string tocode, string fromcode)
+       : tocode_(move(tocode)), fromcode_(move(fromcode))
+{}
 
 
-void IconvProcessor::operator=(IconvProcessor const & other)
-{
-       if (&other != this)
-               pimpl_ = new Impl(other.pimpl_->tocode_, other.pimpl_->fromcode_);
-}
+// for gcc 4.6
+IconvProcessor::IconvProcessor(IconvProcessor && other)
+       : tocode_(move(other.tocode_)), fromcode_(move(other.fromcode_)),
+         h_(move(other.h_))
+{}
 
 
 bool IconvProcessor::init()
 {
-       if (pimpl_->cd != invalid_cd)
+       if (h_)
                return true;
-
-       pimpl_->cd = iconv_open(pimpl_->tocode_.c_str(), pimpl_->fromcode_.c_str());
-       if (pimpl_->cd != invalid_cd)
+       iconv_t cd = iconv_open(tocode_.c_str(), fromcode_.c_str());
+       if (cd != (iconv_t)(-1)) {
+               h_ = make_unique<Handler>(cd);
                return true;
-
+       }
        lyxerr << "Error returned from iconv_open" << endl;
        switch (errno) {
-               case EINVAL:
-                       lyxerr << "EINVAL The conversion from " << pimpl_->fromcode_
-                               << " to " << pimpl_->tocode_
-                               << " is not supported by the implementation."
-                               << endl;
-                       break;
-               default:
-                       lyxerr << "\tSome other error: " << errno << endl;
-                       break;
+       case EINVAL:
+               lyxerr << "EINVAL The conversion from " << fromcode_ << " to "
+                      << tocode_ << " is not supported by the implementation."
+                      << endl;
+               break;
+       default:
+               lyxerr << "\tSome other error: " << errno << endl;
+               break;
        }
        return false;
 }
 
 
 int IconvProcessor::convert(char const * buf, size_t buflen,
-               char * outbuf, size_t maxoutsize)
+                            char * outbuf, size_t maxoutsize)
 {
-       Mutex::Locker lock(&pimpl_->mutex_);
-
        if (buflen == 0)
                return 0;
 
-       if (pimpl_->cd == invalid_cd) {
-               if (!init())
-                       return -1;
-       }
+       if (!h_ && !init())
+               return -1;
 
        char ICONV_CONST * inbuf = const_cast<char ICONV_CONST *>(buf);
        size_t inbytesleft = buflen;
        size_t outbytesleft = maxoutsize;
 
-       int res = iconv(pimpl_->cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
+       int res = iconv(h_->cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
 
        // flush out remaining data. This is needed because iconv sometimes
        // holds back chars in the stream, waiting for a combination character
        // (see e.g. http://sources.redhat.com/bugzilla/show_bug.cgi?id=1124)
-       iconv(pimpl_->cd, NULL, NULL, &outbuf, &outbytesleft);
+       iconv(h_->cd, NULL, NULL, &outbuf, &outbytesleft);
 
        //lyxerr << dec;
        //lyxerr << "Inbytesleft: " << inbytesleft << endl;
@@ -160,25 +132,13 @@ int IconvProcessor::convert(char const * buf, size_t buflen,
                        lyxerr << "E2BIG  There is not sufficient room at *outbuf." << endl;
                        break;
                case EILSEQ:
-                       lyxerr << "EILSEQ An invalid multibyte sequence"
-                               << " has been encountered in the input.\n"
-                               << "When converting from " << pimpl_->fromcode_
-                               << " to " << pimpl_->tocode_ << ".\n";
-                       lyxerr << "Input:" << hex;
-                       for (size_t i = 0; i < buflen; ++i) {
-                               // char may be signed, avoid output of
-                               // something like 0xffffffc2
-                               boost::uint32_t const b =
-                                       *reinterpret_cast<unsigned char const *>(buf + i);
-                               lyxerr << " 0x" << (unsigned int)b;
-                       }
-                       lyxerr << dec << endl;
-                       break;
                case EINVAL:
-                       lyxerr << "EINVAL An incomplete multibyte sequence"
-                               << " has been encountered in the input.\n"
-                               << "When converting from " << pimpl_->fromcode_
-                               << " to " << pimpl_->tocode_ << ".\n";
+                       lyxerr << (errno == EINVAL
+                                  ? "EINVAL An incomplete "
+                                  : "EILSEQ An invalid ")
+                               << "multibyte sequence has been encountered in the input.\n"
+                               << "When converting from " << fromcode_
+                               << " to " << tocode_ << ".\n";
                        lyxerr << "Input:" << hex;
                        for (size_t i = 0; i < buflen; ++i) {
                                // char may be signed, avoid output of
@@ -194,27 +154,11 @@ int IconvProcessor::convert(char const * buf, size_t buflen,
                        break;
        }
        // We got an error so we close down the conversion engine
-       if (iconv_close(pimpl_->cd) == -1) {
-               lyxerr << "Error returned from iconv_close("
-                       << errno << ")" << endl;
-       }
-       pimpl_->cd = invalid_cd;
+       h_.reset();
        return -1;
 }
 
 
-std::string IconvProcessor::from() const
-{
-       return pimpl_->fromcode_;
-}
-
-
-std::string IconvProcessor::to() const
-{
-       return pimpl_->tocode_;
-}
-
-
 namespace {
 
 
@@ -228,7 +172,10 @@ iconv_convert(IconvProcessor & processor, InType const * buf, size_t buflen)
        char const * inbuf = reinterpret_cast<char const *>(buf);
        size_t inbytesleft = buflen * sizeof(InType);
 
-       static std::vector<char> outbuf(32768);
+       static QThreadStorage<std::vector<char> *> static_outbuf;
+       if (!static_outbuf.hasLocalData())
+               static_outbuf.setLocalData(new std::vector<char>(32768));
+       std::vector<char> & outbuf = *static_outbuf.localData();
        // The number of UCS4 code points in buf is at most inbytesleft.
        // The output encoding will use at most
        // max_encoded_bytes(pimpl_->tocode_) per UCS4 code point.
@@ -246,7 +193,16 @@ iconv_convert(IconvProcessor & processor, InType const * buf, size_t buflen)
        return vector<RetType>(tmp, tmp + bytes / sizeof(RetType));
 }
 
-} // anon namespace
+} // namespace
+
+
+IconvProcessor & utf8ToUcs4()
+{
+       static QThreadStorage<IconvProcessor *> processor;
+       if (!processor.hasLocalData())
+               processor.setLocalData(new IconvProcessor(ucs4_codeset, "UTF-8"));
+       return *processor.localData();
+}
 
 
 vector<char_type> utf8_to_ucs4(vector<char> const & utf8str)
@@ -261,32 +217,60 @@ vector<char_type> utf8_to_ucs4(vector<char> const & utf8str)
 vector<char_type>
 utf8_to_ucs4(char const * utf8str, size_t ls)
 {
-       static IconvProcessor processor(ucs4_codeset, "UTF-8");
-       return iconv_convert<char_type>(processor, utf8str, ls);
+       return iconv_convert<char_type>(utf8ToUcs4(), utf8str, ls);
 }
 
 
 vector<char_type>
 utf16_to_ucs4(unsigned short const * s, size_t ls)
 {
-       static IconvProcessor processor(ucs4_codeset, utf16_codeset);
-       return iconv_convert<char_type>(processor, s, ls);
+       static QThreadStorage<IconvProcessor *> processor;
+       if (!processor.hasLocalData())
+               processor.setLocalData(new IconvProcessor(ucs4_codeset, utf16_codeset));
+       return iconv_convert<char_type>(*processor.localData(), s, ls);
 }
 
 
 vector<unsigned short>
 ucs4_to_utf16(char_type const * s, size_t ls)
 {
-       static IconvProcessor processor(utf16_codeset, ucs4_codeset);
-       return iconv_convert<unsigned short>(processor, s, ls);
+       static QThreadStorage<IconvProcessor *> processor;
+       if (!processor.hasLocalData())
+               processor.setLocalData(new IconvProcessor(utf16_codeset, ucs4_codeset));
+       return iconv_convert<unsigned short>(*processor.localData(), s, ls);
+}
+
+
+IconvProcessor & ucs4ToUtf8()
+{
+       static QThreadStorage<IconvProcessor *> processor;
+       if (!processor.hasLocalData())
+               processor.setLocalData(new IconvProcessor("UTF-8", ucs4_codeset));
+       return *processor.localData();
 }
 
+namespace {
+
+IconvProcessor & getProc(map<string, IconvProcessor> & processors,
+                         string const & encoding, bool to)
+{
+       string const & fromcode = to ? ucs4_codeset : encoding;
+       string const & tocode = to ? encoding : ucs4_codeset;
+       map<string, IconvProcessor>::iterator const it = processors.find(encoding);
+       if (it == processors.end()) {
+               IconvProcessor p(fromcode, tocode);
+               return processors.insert(make_pair(encoding, move(p))).first->second;
+       } else
+               return it->second;
+}
+
+} // namespace
+
 
 vector<char>
 ucs4_to_utf8(char_type c)
 {
-       static IconvProcessor processor("UTF-8", ucs4_codeset);
-       return iconv_convert<char>(processor, &c, 1);
+       return iconv_convert<char>(ucs4ToUtf8(), &c, 1);
 }
 
 
@@ -303,46 +287,49 @@ ucs4_to_utf8(vector<char_type> const & ucs4str)
 vector<char>
 ucs4_to_utf8(char_type const * ucs4str, size_t ls)
 {
-       static IconvProcessor processor("UTF-8", ucs4_codeset);
-       return iconv_convert<char>(processor, ucs4str, ls);
+       return iconv_convert<char>(ucs4ToUtf8(), ucs4str, ls);
 }
 
 
 vector<char_type>
 eightbit_to_ucs4(char const * s, size_t ls, string const & encoding)
 {
-       static map<string, IconvProcessor> processors;
-       if (processors.find(encoding) == processors.end()) {
-               IconvProcessor processor(ucs4_codeset, encoding.c_str());
-               processors.insert(make_pair(encoding, processor));
-       }
-       return iconv_convert<char_type>(processors[encoding], s, ls);
+       static QThreadStorage<map<string, IconvProcessor> *> static_processors;
+       if (!static_processors.hasLocalData())
+               static_processors.setLocalData(new map<string, IconvProcessor>);
+       map<string, IconvProcessor> & processors = *static_processors.localData();
+       IconvProcessor & processor = getProc(processors, encoding, true);
+       return iconv_convert<char_type>(processor, s, ls);
 }
 
 
+namespace {
+
+map<string, IconvProcessor> & ucs4To8bitProcessors()
+{
+       static QThreadStorage<map<string, IconvProcessor> *> processors;
+       if (!processors.hasLocalData())
+               processors.setLocalData(new map<string, IconvProcessor>);
+       return *processors.localData();
+}
+
+} // namespace
+
 vector<char>
 ucs4_to_eightbit(char_type const * ucs4str, size_t ls, string const & encoding)
 {
-       static map<string, IconvProcessor> processors;
-       if (processors.find(encoding) == processors.end()) {
-               IconvProcessor processor(encoding.c_str(), ucs4_codeset);
-               processors.insert(make_pair(encoding, processor));
-       }
-       return iconv_convert<char>(processors[encoding], ucs4str, ls);
+       map<string, IconvProcessor> & processors(ucs4To8bitProcessors());
+       IconvProcessor & processor = getProc(processors, encoding, false);
+       return iconv_convert<char>(processor, ucs4str, ls);
 }
 
 
 char ucs4_to_eightbit(char_type ucs4, string const & encoding)
 {
-       static map<string, IconvProcessor> processors;
-       map<string, IconvProcessor>::iterator it = processors.find(encoding);
-       if (it == processors.end()) {
-               IconvProcessor processor(encoding.c_str(), ucs4_codeset);
-               it = processors.insert(make_pair(encoding, processor)).first;
-       }
-
+       map<string, IconvProcessor> & processors(ucs4To8bitProcessors());
+       IconvProcessor & processor = getProc(processors, encoding, false);
        char out;
-       int const bytes = it->second.convert((char *)(&ucs4), 4, &out, 1);
+       int const bytes = processor.convert((char *)(&ucs4), 4, &out, 1);
        if (bytes > 0)
                return out;
        return 0;
@@ -352,15 +339,13 @@ char ucs4_to_eightbit(char_type ucs4, string const & encoding)
 void ucs4_to_multibytes(char_type ucs4, vector<char> & out,
        string const & encoding)
 {
-       static map<string, IconvProcessor> processors;
-       map<string, IconvProcessor>::iterator it = processors.find(encoding);
-       if (it == processors.end()) {
-               IconvProcessor processor(encoding.c_str(), ucs4_codeset);
-               it = processors.insert(make_pair(encoding, processor)).first;
-       }
-
+       static QThreadStorage<map<string, IconvProcessor> *> static_processors;
+       if (!static_processors.hasLocalData())
+               static_processors.setLocalData(new map<string, IconvProcessor>);
+       map<string, IconvProcessor> & processors = *static_processors.localData();
+       IconvProcessor & processor = getProc(processors, encoding, false);
        out.resize(4);
-       int bytes = it->second.convert((char *)(&ucs4), 4, &out[0], 4);
+       int bytes = processor.convert((char *)(&ucs4), 4, &out[0], 4);
        if (bytes > 0)
                out.resize(bytes);
        else