| /* | |
| * Licensed to the Apache Software Foundation (ASF) under one or more | |
| * contributor license agreements. See the NOTICE file distributed with | |
| * this work for additional information regarding copyright ownership. | |
| * The ASF licenses this file to You under the Apache License, Version 2.0 | |
| * (the "License"); you may not use this file except in compliance with | |
| * the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| #include <log4cxx/logstring.h> | |
| #include <log4cxx/helpers/transcoder.h> | |
| #include <log4cxx/helpers/pool.h> | |
| #include <stdlib.h> | |
| #include <log4cxx/helpers/exception.h> | |
| #include <log4cxx/private/bytebuffer_priv.h> | |
| #include <log4cxx/helpers/charsetdecoder.h> | |
| #include <log4cxx/helpers/charsetencoder.h> | |
| #include <log4cxx/helpers/stringhelper.h> | |
| #include <log4cxx/helpers/loglog.h> | |
| #include <vector> | |
| #include <cstring> | |
| #if !defined(LOG4CXX) | |
| #define LOG4CXX 1 | |
| #endif | |
| #include <log4cxx/private/log4cxx_private.h> | |
| #if LOG4CXX_CFSTRING_API | |
| #include <CoreFoundation/CFString.h> | |
| #endif | |
| using namespace LOG4CXX_NS; | |
| using namespace LOG4CXX_NS::helpers; | |
| void Transcoder::decodeUTF8(const std::string& src, LogString& dst) | |
| { | |
| std::string::const_iterator iter = src.begin(); | |
| while (iter != src.end()) | |
| { | |
| auto sv = getCodePoint(src, iter); | |
| encode(sv, dst); | |
| } | |
| } | |
| void Transcoder::encodeUTF8(const LogString& src, std::string& dst) | |
| { | |
| #if LOG4CXX_LOGCHAR_IS_UTF8 | |
| dst.append(src); | |
| #else | |
| LogString::const_iterator iter = src.begin(); | |
| while (iter != src.end()) | |
| { | |
| unsigned int sv = getCodePoint(src, iter); | |
| encode(sv, dst); | |
| } | |
| #endif | |
| } | |
| void Transcoder::encodeUTF8(unsigned int sv, ByteBuffer& dst) | |
| { | |
| dst.increment_position(CharsetEncoder::putUTF8CodePoint(sv, dst.current())); | |
| } | |
| void Transcoder::encodeUTF16BE(unsigned int sv, ByteBuffer& dst) | |
| { | |
| dst.increment_position(CharsetEncoder::putUTF16BECodePoint(sv, dst.current())); | |
| } | |
| void Transcoder::encodeUTF16LE(unsigned int sv, ByteBuffer& dst) | |
| { | |
| dst.increment_position(CharsetEncoder::putUTF16LECodePoint(sv, dst.current())); | |
| } | |
| unsigned int Transcoder::decode(const std::string& src, | |
| std::string::const_iterator& iter) | |
| { | |
| auto offset = iter - src.begin(); | |
| auto remaining = src.size() - offset; | |
| ByteBufferPriv buf(const_cast<char*>(src.data() + offset), remaining); | |
| auto result = CharsetDecoder::getUTF8CodePoint(buf); | |
| iter += remaining - buf.remaining(); | |
| return result; | |
| } | |
| template <typename T> | |
| unsigned int decodeCodePoint(const typename std::basic_string<T>& str, typename std::basic_string<T>::const_iterator& nextCodePoint) | |
| { | |
| auto lastCodePoint = nextCodePoint; | |
| auto ch = Transcoder::decode(str, nextCodePoint); | |
| if (nextCodePoint == lastCodePoint) // failed to decode input? | |
| { | |
| // Skip the undecodable run and keep escaping the remaining input | |
| // instead of discarding it; the run collapses to one replacement. | |
| for (++nextCodePoint; nextCodePoint != str.end(); ++nextCodePoint) | |
| { | |
| auto probe = nextCodePoint; | |
| Transcoder::decode(str, probe); | |
| if (probe != nextCodePoint) // next unit starts a decodable sequence | |
| break; | |
| } | |
| ch = 0xFFFD; // The Unicode replacement character | |
| } | |
| else if ( (0xD800 <= ch && ch <= 0xDFFF) // UTF-16 surrogate-range | |
| || 0xFFFF == ch || 0x10FFFF < ch) | |
| { | |
| ch = 0xFFFD; // The Unicode replacement character | |
| } | |
| return ch; | |
| } | |
| unsigned int Transcoder::getCodePoint(const std::string& str, std::string::const_iterator& nextCodePoint) | |
| { | |
| return decodeCodePoint<char>(str, nextCodePoint); | |
| } | |
| void Transcoder::encode(unsigned int sv, std::string& dst) | |
| { | |
| char tmp[8]; | |
| size_t bytes = CharsetEncoder::putUTF8CodePoint(sv, tmp); | |
| dst.append(tmp, bytes); | |
| } | |
| /// Does \c str contain the Unicode replacement character | |
| bool Transcoder::hasReplacementCharacter(const std::string& str) | |
| { | |
| return str.npos != str.find("\xEF\xBF\xBD"); | |
| } | |
| void Transcoder::decode(const std::string& src, LogString& dst) | |
| { | |
| #if LOG4CXX_CHARSET_UTF8 && LOG4CXX_LOGCHAR_IS_UTF8 | |
| dst.append(src); | |
| #else | |
| static CharsetDecoderPtr decoder(CharsetDecoder::getDefaultDecoder()); | |
| dst.reserve(dst.size() + src.size()); | |
| std::string::const_iterator iter = src.begin(); | |
| #if !LOG4CXX_CHARSET_EBCDIC | |
| for (; | |
| iter != src.end() && ((unsigned char) *iter) < 0x80; | |
| iter++) | |
| { | |
| dst.append(1, *iter); | |
| } | |
| #endif | |
| if (iter != src.end()) | |
| { | |
| size_t offset = iter - src.begin(); | |
| ByteBuffer buf(const_cast<char*>(src.data() + offset), src.size() - offset); | |
| while (buf.remaining() > 0) | |
| { | |
| log4cxx_status_t stat = decoder->decode(buf, dst); | |
| if (CharsetDecoder::isError(stat)) | |
| { | |
| encode(LOSSCHAR, dst); | |
| buf.increment_position(1); | |
| } | |
| } | |
| decoder->decode(buf, dst); | |
| } | |
| #endif | |
| } | |
| void Transcoder::encode(const LogString& src, std::string& dst) | |
| { | |
| #if LOG4CXX_CHARSET_UTF8 && LOG4CXX_LOGCHAR_IS_UTF8 | |
| dst.append(src); | |
| #else | |
| static CharsetEncoderPtr encoder(CharsetEncoder::getDefaultEncoder()); | |
| dst.reserve(dst.size() + src.size()); | |
| LogString::const_iterator iter = src.begin(); | |
| #if !LOG4CXX_CHARSET_EBCDIC | |
| for (; | |
| iter != src.end() && ((unsigned int) *iter) < 0x80; | |
| iter++) | |
| { | |
| dst.append(1, static_cast<char>(*iter)); | |
| } | |
| #endif | |
| if (iter != src.end()) | |
| { | |
| static const int BUFSIZE = 256; | |
| char buf[BUFSIZE]; | |
| ByteBuffer out(buf, BUFSIZE); | |
| while (iter != src.end()) | |
| { | |
| log4cxx_status_t stat = encoder->encode(src, iter, out); | |
| out.flip(); | |
| dst.append(out.data(), out.limit()); | |
| out.clear(); | |
| if (CharsetEncoder::isError(stat)) | |
| { | |
| encode(LOSSCHAR, dst); | |
| iter++; | |
| } | |
| } | |
| encoder->encode(src, iter, out); | |
| } | |
| #endif | |
| } | |
| template<class String, class Iterator> | |
| static unsigned int decodeUTF16(const String& in, Iterator& iter) | |
| { | |
| unsigned int ch1 = *iter; | |
| // | |
| // if not surrogate pair | |
| // | |
| if (ch1 < 0xD800 || ch1 > 0xDFFF) | |
| { | |
| // | |
| // then advance iterator and return wchar_t value | |
| // | |
| if (ch1 != 0xFFFF) | |
| { | |
| iter++; | |
| } | |
| return ch1; | |
| } | |
| else if (ch1 < 0xDC00) | |
| { | |
| // | |
| // started with high-surrogate value | |
| // if there is an additional wchar_t | |
| Iterator iter2 = iter + 1; | |
| if (iter2 != in.end()) | |
| { | |
| unsigned int ch2 = *iter2; | |
| // | |
| // if it is a matching low surrogate then | |
| // advance the iterator and return the scalar value | |
| if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) | |
| { | |
| iter += 2; | |
| return (ch1 - 0xD800) * 0x400 + (ch2 - 0xDC00) + 0x10000; | |
| } | |
| } | |
| } | |
| // | |
| // unrecognized value, do not advance iterator | |
| // | |
| return 0xFFFF; | |
| } | |
| template<class String> | |
| static void encodeUTF16(unsigned int sv, String& dst) | |
| { | |
| if (sv < 0x10000) | |
| { | |
| dst.append(1, sv); | |
| } | |
| else | |
| { | |
| unsigned char u = (unsigned char) (sv >> 16); | |
| unsigned char w = (unsigned char) (u - 1); | |
| unsigned short hs = (0xD800 + ((w & 0xF) << 6) + ((sv & 0xFFFF) >> 10)); | |
| unsigned short ls = (0xDC00 + (sv & 0x3FF)); | |
| dst.append(1, hs); | |
| dst.append(1, ls); | |
| } | |
| } | |
| #if LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR || defined(WIN32) || defined(_WIN32) | |
| void Transcoder::decode(const std::wstring& src, LogString& dst) | |
| { | |
| #if LOG4CXX_LOGCHAR_IS_WCHAR | |
| dst.append(src); | |
| #else | |
| std::wstring::const_iterator i = src.begin(); | |
| while (i != src.end()) | |
| { | |
| auto cp = getCodePoint(src, i); | |
| encode(cp, dst); | |
| } | |
| #endif | |
| } | |
| void Transcoder::encode(const LogString& src, std::wstring& dst) | |
| { | |
| #if LOG4CXX_LOGCHAR_IS_WCHAR | |
| dst.append(src); | |
| #else | |
| for (LogString::const_iterator i = src.begin(); i != src.end();) | |
| { | |
| unsigned int cp = getCodePoint(src, i); | |
| encode(cp, dst); | |
| } | |
| #endif | |
| } | |
| unsigned int Transcoder::decode(const std::wstring& in, | |
| std::wstring::const_iterator& iter) | |
| { | |
| #if defined(__STDC_ISO_10646__) | |
| return *(iter++); | |
| #else | |
| return decodeUTF16(in, iter); | |
| #endif | |
| } | |
| unsigned int Transcoder::getCodePoint(const std::wstring& str, std::wstring::const_iterator& nextCodePoint) | |
| { | |
| return decodeCodePoint<wchar_t>(str, nextCodePoint); | |
| } | |
| void Transcoder::encode(unsigned int sv, std::wstring& dst) | |
| { | |
| #if defined(__STDC_ISO_10646__) | |
| dst.append(1, sv); | |
| #else | |
| if (sizeof(wchar_t) == 4) | |
| { | |
| dst.append(1, sv); | |
| } | |
| else | |
| { | |
| encodeUTF16(sv, dst); | |
| } | |
| #endif | |
| } | |
| /// Does \c str contain the Unicode replacement character | |
| bool Transcoder::hasReplacementCharacter(const std::wstring& str) | |
| { | |
| return str.npos != str.find(LOSSCHAR); | |
| } | |
| #endif | |
| #if LOG4CXX_CFSTRING_API | |
| void Transcoder::decode(const CFStringRef& src, LogString& dst) | |
| { | |
| auto length = CFStringGetLength(src); | |
| #if defined(_DEBUG) | |
| if (LogLog::isDebugEnabled()) | |
| { | |
| LogString msg(LOG4CXX_STR("Transcoder::decodeCFString")); | |
| msg += LOG4CXX_STR(" length "); | |
| StringHelper::toString((size_t)length, msg); | |
| LogLog::debug(msg); | |
| } | |
| #endif | |
| if (length > 0) | |
| { | |
| std::vector<unsigned short> tmp(length); | |
| CFStringGetCharacters(src, CFRangeMake(0, length), &tmp[0]); | |
| for (auto i = tmp.begin(); i != tmp.end(); ) | |
| { | |
| auto start = i; | |
| unsigned int cp = decodeUTF16(tmp, i); | |
| if (cp != 0xFFFF) | |
| { | |
| encode(cp, dst); | |
| } | |
| else | |
| { | |
| encode(LOSSCHAR, dst); | |
| if (i == start) | |
| { | |
| i++; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| CFStringRef Transcoder::encode(const LogString& src) | |
| { | |
| std::basic_string<unsigned short> tmp; | |
| for (auto ch : src) | |
| encodeUTF16(ch, tmp); | |
| return CFStringCreateWithCharacters(kCFAllocatorDefault, tmp.data(), tmp.size()); | |
| } | |
| #endif // #if LOG4CXX_CFSTRING_API | |
| logchar Transcoder::decode(char val) | |
| { | |
| #if LOG4CXX_CHARSET_EBCDIC | |
| LogString dst; | |
| Transcoder::decode(std::string(1, val), dst); | |
| return dst[0]; | |
| #else | |
| return val; | |
| #endif | |
| } | |
| LogString Transcoder::decode(const char* val) | |
| { | |
| #if LOG4CXX_LOGCHAR_IS_UTF8 && !LOG4CXX_CHARSET_EBCDIC | |
| return val; | |
| #else | |
| LogString dst; | |
| Transcoder::decode(val, dst); | |
| return dst; | |
| #endif | |
| } | |
| std::string Transcoder::encodeCharsetName(const LogString& val) | |
| { | |
| char asciiTable[] = { ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', | |
| '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', | |
| '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', | |
| 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', | |
| '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', | |
| 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~' | |
| }; | |
| std::string out; | |
| for (auto& item : val) | |
| { | |
| if (item >= 0x20 && item < 0x7F) | |
| { | |
| out.append(1, asciiTable[item - 0x20]); | |
| } | |
| else | |
| { | |
| out.append(1, '?'); | |
| } | |
| } | |
| return out; | |
| } |