output.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. #include "output.h"
  2. #include <util/string/cast.h>
  3. #include "format.h"
  4. #include <util/memory/tempbuf.h>
  5. #include <util/generic/singleton.h>
  6. #include <util/generic/yexception.h>
  7. #include <util/charset/utf8.h>
  8. #include <util/charset/wide.h>
  9. #if defined(_android_)
  10. #include <util/system/dynlib.h>
  11. #include <util/system/guard.h>
  12. #include <util/system/mutex.h>
  13. #include <android/log.h>
  14. #endif
  15. #include <cerrno>
  16. #include <cstdio>
  17. #include <filesystem>
  18. #include <string_view>
  19. #include <optional>
  20. #if defined(_win_)
  21. #include <io.h>
  22. #endif
  23. constexpr size_t MAX_UTF8_BYTES = 4; // UTF-8-encoded code point takes between 1 and 4 bytes
  24. IOutputStream::IOutputStream() noexcept = default;
  25. IOutputStream::~IOutputStream() = default;
  26. void IOutputStream::DoFlush() {
  27. /*
  28. * do nothing
  29. */
  30. }
  31. void IOutputStream::DoFinish() {
  32. Flush();
  33. }
  34. void IOutputStream::DoWriteV(const TPart* parts, size_t count) {
  35. for (size_t i = 0; i < count; ++i) {
  36. const TPart& part = parts[i];
  37. DoWrite(part.buf, part.len);
  38. }
  39. }
  40. void IOutputStream::DoWriteC(char ch) {
  41. DoWrite(&ch, 1);
  42. }
  43. template <>
  44. void Out<wchar16>(IOutputStream& o, wchar16 ch) {
  45. const wchar32 w32ch = ReadSymbol(&ch, &ch + 1);
  46. size_t length;
  47. unsigned char buffer[MAX_UTF8_BYTES];
  48. WriteUTF8Char(w32ch, length, buffer);
  49. o.Write(buffer, length);
  50. }
  51. template <>
  52. void Out<wchar32>(IOutputStream& o, wchar32 ch) {
  53. size_t length;
  54. unsigned char buffer[MAX_UTF8_BYTES];
  55. WriteUTF8Char(ch, length, buffer);
  56. o.Write(buffer, length);
  57. }
  58. static void WriteString(IOutputStream& o, const wchar16* w, size_t n) {
  59. const size_t buflen = (n * MAX_UTF8_BYTES); // * 4 because the conversion functions can convert unicode character into maximum 4 bytes of UTF8
  60. TTempBuf buffer(buflen + 1);
  61. char* const data = buffer.Data();
  62. size_t written = 0;
  63. WideToUTF8(w, n, data, written);
  64. data[written] = 0;
  65. o.Write(data, written);
  66. }
  67. static void WriteString(IOutputStream& o, const wchar32* w, size_t n) {
  68. const size_t buflen = (n * MAX_UTF8_BYTES); // * 4 because the conversion functions can convert unicode character into maximum 4 bytes of UTF8
  69. TTempBuf buffer(buflen + 1);
  70. char* const data = buffer.Data();
  71. size_t written = 0;
  72. WideToUTF8(w, n, data, written);
  73. data[written] = 0;
  74. o.Write(data, written);
  75. }
  76. template <>
  77. void Out<TString>(IOutputStream& o, const TString& p) {
  78. o.Write(p.data(), p.size());
  79. }
  80. template <>
  81. void Out<std::string>(IOutputStream& o, const std::string& p) {
  82. o.Write(p.data(), p.length());
  83. }
  84. template <>
  85. void Out<std::string_view>(IOutputStream& o, const std::string_view& p) {
  86. o.Write(p.data(), p.length());
  87. }
  88. template <>
  89. void Out<std::u16string_view>(IOutputStream& o, const std::u16string_view& p) {
  90. WriteString(o, p.data(), p.length());
  91. }
  92. template <>
  93. void Out<std::u32string_view>(IOutputStream& o, const std::u32string_view& p) {
  94. WriteString(o, p.data(), p.length());
  95. }
  96. #ifndef USE_STL_SYSTEM
  97. // FIXME thegeorg@: remove #ifndef upon raising minimal macOS version to 10.15 in https://st.yandex-team.ru/DTCC-836
  98. template <>
  99. void Out<std::filesystem::path>(IOutputStream& o, const std::filesystem::path& p) {
  100. o.Write(p.string());
  101. }
  102. #endif
  103. template <>
  104. void Out<TStringBuf>(IOutputStream& o, const TStringBuf& p) {
  105. o.Write(p.data(), p.length());
  106. }
  107. template <>
  108. void Out<TWtringBuf>(IOutputStream& o, const TWtringBuf& p) {
  109. WriteString(o, p.data(), p.length());
  110. }
  111. template <>
  112. void Out<TUtf32StringBuf>(IOutputStream& o, const TUtf32StringBuf& p) {
  113. WriteString(o, p.data(), p.length());
  114. }
  115. template <>
  116. void Out<const wchar16*>(IOutputStream& o, const wchar16* w) {
  117. if (w) {
  118. WriteString(o, w, std::char_traits<wchar16>::length(w));
  119. } else {
  120. o.Write("(null)");
  121. }
  122. }
  123. template <>
  124. void Out<const wchar32*>(IOutputStream& o, const wchar32* w) {
  125. if (w) {
  126. WriteString(o, w, std::char_traits<wchar32>::length(w));
  127. } else {
  128. o.Write("(null)");
  129. }
  130. }
  131. template <>
  132. void Out<TUtf16String>(IOutputStream& o, const TUtf16String& w) {
  133. WriteString(o, w.c_str(), w.size());
  134. }
  135. template <>
  136. void Out<TUtf32String>(IOutputStream& o, const TUtf32String& w) {
  137. WriteString(o, w.c_str(), w.size());
  138. }
  139. #define DEF_CONV_DEFAULT(type) \
  140. template <> \
  141. void Out<type>(IOutputStream & o, type p) { \
  142. o << ToString(p); \
  143. }
  144. #define DEF_CONV_CHR(type) \
  145. template <> \
  146. void Out<type>(IOutputStream & o, type p) { \
  147. o.Write((char)p); \
  148. }
  149. #define DEF_CONV_NUM(type, len) \
  150. template <> \
  151. void Out<type>(IOutputStream & o, type p) { \
  152. char buf[len]; \
  153. o.Write(buf, ToString(p, buf, sizeof(buf))); \
  154. } \
  155. \
  156. template <> \
  157. void Out<volatile type>(IOutputStream & o, volatile type p) { \
  158. Out<type>(o, p); \
  159. }
  160. DEF_CONV_NUM(bool, 64)
  161. DEF_CONV_CHR(char)
  162. DEF_CONV_CHR(signed char)
  163. DEF_CONV_CHR(unsigned char)
  164. DEF_CONV_NUM(signed short, 64)
  165. DEF_CONV_NUM(signed int, 64)
  166. DEF_CONV_NUM(signed long int, 64)
  167. DEF_CONV_NUM(signed long long int, 64)
  168. DEF_CONV_NUM(unsigned short, 64)
  169. DEF_CONV_NUM(unsigned int, 64)
  170. DEF_CONV_NUM(unsigned long int, 64)
  171. DEF_CONV_NUM(unsigned long long int, 64)
  172. DEF_CONV_NUM(float, 512)
  173. DEF_CONV_NUM(double, 512)
  174. DEF_CONV_NUM(long double, 512)
  175. #if !defined(_YNDX_LIBCXX_ENABLE_VECTOR_BOOL_COMPRESSION) || (_YNDX_LIBCXX_ENABLE_VECTOR_BOOL_COMPRESSION == 1)
  176. // TODO: acknowledge std::bitset::reference for both libc++ and libstdc++
  177. template <>
  178. void Out<typename std::vector<bool>::reference>(IOutputStream& o, const std::vector<bool>::reference& bit) {
  179. return Out<bool>(o, static_cast<bool>(bit));
  180. }
  181. #endif
  182. #ifndef TSTRING_IS_STD_STRING
  183. template <>
  184. void Out<TBasicCharRef<TString>>(IOutputStream& o, const TBasicCharRef<TString>& c) {
  185. o << static_cast<char>(c);
  186. }
  187. template <>
  188. void Out<TBasicCharRef<TUtf16String>>(IOutputStream& o, const TBasicCharRef<TUtf16String>& c) {
  189. o << static_cast<wchar16>(c);
  190. }
  191. template <>
  192. void Out<TBasicCharRef<TUtf32String>>(IOutputStream& o, const TBasicCharRef<TUtf32String>& c) {
  193. o << static_cast<wchar32>(c);
  194. }
  195. #endif
  196. template <>
  197. void Out<const void*>(IOutputStream& o, const void* t) {
  198. o << Hex(size_t(t));
  199. }
  200. template <>
  201. void Out<void*>(IOutputStream& o, void* t) {
  202. Out<const void*>(o, t);
  203. }
  204. using TNullPtr = decltype(nullptr);
  205. template <>
  206. void Out<TNullPtr>(IOutputStream& o, TTypeTraits<TNullPtr>::TFuncParam) {
  207. o << TStringBuf("nullptr");
  208. }
  209. #define DEF_OPTIONAL(TYPE) \
  210. template <> \
  211. void Out<std::optional<TYPE>>(IOutputStream & o, const std::optional<TYPE>& value) { \
  212. if (value) { \
  213. o << *value; \
  214. } else { \
  215. o << "(NULL)"; \
  216. } \
  217. }
  218. DEF_OPTIONAL(ui32);
  219. DEF_OPTIONAL(i64);
  220. DEF_OPTIONAL(ui64);
  221. DEF_OPTIONAL(std::string);
  222. DEF_OPTIONAL(TString);
  223. DEF_OPTIONAL(TStringBuf);
  224. #if defined(_android_)
  225. namespace {
  226. class TAndroidStdIOStreams {
  227. public:
  228. TAndroidStdIOStreams()
  229. : LogLibrary("liblog.so")
  230. , LogFuncPtr((TLogFuncPtr)LogLibrary.Sym("__android_log_write"))
  231. , Out(LogFuncPtr)
  232. , Err(LogFuncPtr)
  233. {
  234. }
  235. public:
  236. using TLogFuncPtr = void (*)(int, const char*, const char*);
  237. class TAndroidStdOutput: public IOutputStream {
  238. public:
  239. inline TAndroidStdOutput(TLogFuncPtr logFuncPtr) noexcept
  240. : Buffer()
  241. , LogFuncPtr(logFuncPtr)
  242. {
  243. }
  244. virtual ~TAndroidStdOutput() {
  245. }
  246. private:
  247. virtual void DoWrite(const void* buf, size_t len) override {
  248. with_lock (BufferMutex) {
  249. Buffer.Write(buf, len);
  250. }
  251. }
  252. virtual void DoFlush() override {
  253. with_lock (BufferMutex) {
  254. LogFuncPtr(ANDROID_LOG_DEBUG, GetTag(), Buffer.Data());
  255. Buffer.Clear();
  256. }
  257. }
  258. virtual const char* GetTag() const = 0;
  259. private:
  260. TMutex BufferMutex;
  261. TStringStream Buffer;
  262. TLogFuncPtr LogFuncPtr;
  263. };
  264. class TStdErr: public TAndroidStdOutput {
  265. public:
  266. TStdErr(TLogFuncPtr logFuncPtr)
  267. : TAndroidStdOutput(logFuncPtr)
  268. {
  269. }
  270. virtual ~TStdErr() {
  271. }
  272. private:
  273. virtual const char* GetTag() const override {
  274. return "stderr";
  275. }
  276. };
  277. class TStdOut: public TAndroidStdOutput {
  278. public:
  279. TStdOut(TLogFuncPtr logFuncPtr)
  280. : TAndroidStdOutput(logFuncPtr)
  281. {
  282. }
  283. virtual ~TStdOut() {
  284. }
  285. private:
  286. virtual const char* GetTag() const override {
  287. return "stdout";
  288. }
  289. };
  290. static bool Enabled;
  291. TDynamicLibrary LogLibrary; // field order is important, see constructor
  292. TLogFuncPtr LogFuncPtr;
  293. TStdOut Out;
  294. TStdErr Err;
  295. static inline TAndroidStdIOStreams& Instance() {
  296. return *SingletonWithPriority<TAndroidStdIOStreams, 4>();
  297. }
  298. };
  299. bool TAndroidStdIOStreams::Enabled = false;
  300. }
  301. #endif // _android_
  302. namespace {
  303. class TStdOutput: public IOutputStream {
  304. public:
  305. inline TStdOutput(FILE* f) noexcept
  306. : F_(f)
  307. {
  308. }
  309. ~TStdOutput() override = default;
  310. private:
  311. void DoWrite(const void* buf, size_t len) override {
  312. if (len != fwrite(buf, 1, len, F_)) {
  313. #if defined(_win_)
  314. // On Windows, if 'F_' is console -- 'fwrite' returns count of written characters.
  315. // If, for example, console output codepage is UTF-8, then returned value is
  316. // not equal to 'len'. So, we ignore some 'errno' values...
  317. if ((errno == 0 || errno == EINVAL || errno == EILSEQ) && _isatty(fileno(F_))) {
  318. return;
  319. }
  320. #endif
  321. ythrow TSystemError() << "write failed";
  322. }
  323. }
  324. void DoFlush() override {
  325. if (fflush(F_) != 0) {
  326. ythrow TSystemError() << "fflush failed";
  327. }
  328. }
  329. private:
  330. FILE* F_;
  331. };
  332. struct TStdIOStreams {
  333. struct TStdErr: public TStdOutput {
  334. inline TStdErr()
  335. : TStdOutput(stderr)
  336. {
  337. }
  338. ~TStdErr() override = default;
  339. };
  340. struct TStdOut: public TStdOutput {
  341. inline TStdOut()
  342. : TStdOutput(stdout)
  343. {
  344. }
  345. ~TStdOut() override = default;
  346. };
  347. TStdOut Out;
  348. TStdErr Err;
  349. static inline TStdIOStreams& Instance() {
  350. return *SingletonWithPriority<TStdIOStreams, 4>();
  351. }
  352. };
  353. }
  354. IOutputStream& NPrivate::StdErrStream() noexcept {
  355. #if defined(_android_)
  356. if (TAndroidStdIOStreams::Enabled) {
  357. return TAndroidStdIOStreams::Instance().Err;
  358. }
  359. #endif
  360. return TStdIOStreams::Instance().Err;
  361. }
  362. IOutputStream& NPrivate::StdOutStream() noexcept {
  363. #if defined(_android_)
  364. if (TAndroidStdIOStreams::Enabled) {
  365. return TAndroidStdIOStreams::Instance().Out;
  366. }
  367. #endif
  368. return TStdIOStreams::Instance().Out;
  369. }
  370. void RedirectStdioToAndroidLog(bool redirect) {
  371. #if defined(_android_)
  372. TAndroidStdIOStreams::Enabled = redirect;
  373. #else
  374. Y_UNUSED(redirect);
  375. #endif
  376. }