|  | /* | 
|  | * Copyright (C) 2012 The Android Open Source Project | 
|  | * | 
|  | * Licensed 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. | 
|  | */ | 
|  |  | 
|  | #ifndef ART_LIBARTBASE_BASE_INDENTER_H_ | 
|  | #define ART_LIBARTBASE_BASE_INDENTER_H_ | 
|  |  | 
|  | #include <ostream> | 
|  | #include <streambuf> | 
|  |  | 
|  | #include <android-base/logging.h> | 
|  |  | 
|  | #include "macros.h" | 
|  |  | 
|  | namespace art { | 
|  |  | 
|  | constexpr char kIndentChar =' '; | 
|  | constexpr size_t kIndentBy1Count = 2; | 
|  |  | 
|  | class Indenter : public std::streambuf { | 
|  | public: | 
|  | Indenter(std::streambuf* out, char text, size_t count) | 
|  | : indent_next_(true), out_sbuf_(out), | 
|  | text_{text, text, text, text, text, text, text, text}, | 
|  | count_(count) {} | 
|  |  | 
|  | private: | 
|  | std::streamsize xsputn(const char* s, std::streamsize n) override { | 
|  | std::streamsize result = n;  // Aborts on failure. | 
|  | const char* eol = static_cast<const char*>(memchr(s, '\n', n)); | 
|  | while (eol != nullptr) { | 
|  | size_t to_write = eol + 1 - s; | 
|  | Write(s, to_write); | 
|  | s += to_write; | 
|  | n -= to_write; | 
|  | indent_next_ = true; | 
|  | eol = static_cast<const char*>(memchr(s, '\n', n)); | 
|  | } | 
|  | if (n != 0u) { | 
|  | Write(s, n); | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | int_type overflow(int_type c) override { | 
|  | if (UNLIKELY(c == std::char_traits<char>::eof())) { | 
|  | out_sbuf_->pubsync(); | 
|  | return c; | 
|  | } | 
|  | char data[1] = { static_cast<char>(c) }; | 
|  | Write(data, 1u); | 
|  | indent_next_ = (c == '\n'); | 
|  | return c; | 
|  | } | 
|  |  | 
|  | int sync() override { | 
|  | return out_sbuf_->pubsync(); | 
|  | } | 
|  |  | 
|  | void Write(const char* s, std::streamsize n) { | 
|  | if (indent_next_) { | 
|  | size_t remaining = count_; | 
|  | while (remaining != 0u) { | 
|  | size_t to_write = std::min(remaining, sizeof(text_)); | 
|  | RawWrite(text_, to_write); | 
|  | remaining -= to_write; | 
|  | } | 
|  | indent_next_ = false; | 
|  | } | 
|  | RawWrite(s, n); | 
|  | } | 
|  |  | 
|  | void RawWrite(const char* s, std::streamsize n) { | 
|  | size_t written = out_sbuf_->sputn(s, n); | 
|  | s += written; | 
|  | n -= written; | 
|  | while (n != 0u) { | 
|  | out_sbuf_->pubsync(); | 
|  | written = out_sbuf_->sputn(s, n); | 
|  | CHECK_NE(written, 0u) << "Error writing to buffer. Disk full?"; | 
|  | s += written; | 
|  | n -= written; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool indent_next_; | 
|  |  | 
|  | // Buffer to write output to. | 
|  | std::streambuf* const out_sbuf_; | 
|  |  | 
|  | // Text output as indent. | 
|  | const char text_[8]; | 
|  |  | 
|  | // Number of times text is output. | 
|  | size_t count_; | 
|  |  | 
|  | friend class VariableIndentationOutputStream; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(Indenter); | 
|  | }; | 
|  |  | 
|  | class VariableIndentationOutputStream { | 
|  | public: | 
|  | explicit VariableIndentationOutputStream(std::ostream* os, char text = kIndentChar) | 
|  | : indenter_(os->rdbuf(), text, 0u), | 
|  | indented_os_(&indenter_) { | 
|  | } | 
|  |  | 
|  | std::ostream& Stream() { | 
|  | return indented_os_; | 
|  | } | 
|  |  | 
|  | size_t GetIndentation() const { | 
|  | return indenter_.count_; | 
|  | } | 
|  |  | 
|  | void IncreaseIndentation(size_t adjustment) { | 
|  | indenter_.count_ += adjustment; | 
|  | } | 
|  |  | 
|  | void DecreaseIndentation(size_t adjustment) { | 
|  | DCHECK_GE(indenter_.count_, adjustment); | 
|  | indenter_.count_ -= adjustment; | 
|  | } | 
|  |  | 
|  | private: | 
|  | Indenter indenter_; | 
|  | std::ostream indented_os_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(VariableIndentationOutputStream); | 
|  | }; | 
|  |  | 
|  | class ScopedIndentation { | 
|  | public: | 
|  | explicit ScopedIndentation(VariableIndentationOutputStream* vios, | 
|  | size_t adjustment = kIndentBy1Count) | 
|  | : vios_(vios), | 
|  | adjustment_(adjustment) { | 
|  | vios_->IncreaseIndentation(adjustment_); | 
|  | } | 
|  |  | 
|  | ~ScopedIndentation() { | 
|  | vios_->DecreaseIndentation(adjustment_); | 
|  | } | 
|  |  | 
|  | private: | 
|  | VariableIndentationOutputStream* const vios_; | 
|  | const size_t adjustment_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(ScopedIndentation); | 
|  | }; | 
|  |  | 
|  | }  // namespace art | 
|  |  | 
|  | #endif  // ART_LIBARTBASE_BASE_INDENTER_H_ |