diff --git a/layersvt/api_dump.h b/layersvt/api_dump.h index 6e13b6de33..f27b825a9c 100644 --- a/layersvt/api_dump.h +++ b/layersvt/api_dump.h @@ -45,7 +45,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -241,9 +243,96 @@ class ConditionalFrameOutput { } }; +#ifdef __ANDROID__ +template > +class AndroidLogcatBuf final : public std::basic_streambuf { + public: + class LogWriter { + public: + virtual void write(const std::basic_string &data) = 0; + virtual ~LogWriter() {} + }; + + AndroidLogcatBuf(std::unique_ptr log_writer, size_t bufsize = 0x200) + : buffer_(std::make_unique(bufsize)), bufsize_(bufsize), log_writer_(std::move(log_writer)) { + // We use the last position of buffer_ to store the overflow character. We use 0 as a sentinel element to indicate if this + // overflow slot is in use. + this->setp(buffer_.get(), buffer_.get() + bufsize_ - 1); + } + ~AndroidLogcatBuf() = default; + + private: + using int_type = typename traits::int_type; + int_type overflow(int_type c) override { + // Conform with the C++ standard. Return not eof when called with eof: + // https://en.cppreference.com/w/cpp/io/basic_stringbuf/overflow. + if (c == traits::eof()) { + return traits::not_eof(c); + } + buffer_[bufsize_ - 1] = traits::to_char_type(c); + flushPending(); + buffer_[bufsize_ - 1] = traits::to_char_type(0); + return traits::not_eof(c); + } + + int sync() override { + flushPending(); + if (!pending_content_.empty()) { + // Also write data after the last new line. + log_writer_->write(pending_content_); + pending_content_.clear(); + } + return 0; + } + + // Flush pending_content_ + buffer_ up to the last new line to the log writer, and always move whatever is left in buffer_ to + // pending_content_. + void flushPending() { + auto len = this->pptr() - this->pbase(); + if (this->pptr() == buffer_.get() + bufsize_ - 1 && buffer_[bufsize_ - 1] != traits::to_char_type(0)) { + len++; + } + constexpr size_t npos = std::basic_string_view::npos; + std::basic_string_view buf_content(this->pbase(), len); + std::size_t last_new_line_pos = buf_content.find_last_of(traits::to_char_type('\n')); + if (last_new_line_pos == npos) { + pending_content_.append(buf_content); + this->setp(buffer_.get(), buffer_.get() + bufsize_ - 1); + return; + } + std::basic_string_view before_new_line = buf_content.substr(0, last_new_line_pos); + std::basic_string to_print = std::move(pending_content_); + to_print.append(before_new_line); + if (!to_print.empty()) { + log_writer_->write(to_print); + } + std::basic_string_view after_new_line = buf_content.substr(last_new_line_pos + 1); + pending_content_ = std::basic_string(after_new_line); + this->setp(buffer_.get(), buffer_.get() + bufsize_ - 1); + } + + std::unique_ptr buffer_; + size_t bufsize_; + std::basic_string pending_content_; + std::unique_ptr log_writer_; +}; + +class AndroidLogcatWriter final : public AndroidLogcatBuf<>::LogWriter { + public: + AndroidLogcatWriter() = default; + void write(const std::string &content) override { + __android_log_print(ANDROID_LOG_INFO, "api_dump", "%s", content.c_str()); + } +}; +#endif + class ApiDumpSettings { public: ApiDumpSettings() : output_stream(std::cout.rdbuf()) { +#ifdef __ANDROID__ + android_logcat_buf = std::make_unique>(std::make_unique()); + output_stream.rdbuf(android_logcat_buf.get()); +#endif std::string filename_string = ""; // If the layer settings file has a flag indicating to output to a file, // do so, to the appropriate filename. @@ -673,6 +762,9 @@ class ApiDumpSettings { // Since basically every function in this struct is const, we have to work around that. mutable std::ostream output_stream; std::ofstream output_file_stream; +#ifdef __ANDROID__ + std::unique_ptr> android_logcat_buf = nullptr; +#endif ApiDumpFormat output_format; bool show_params; bool show_address;