1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef ART_LIBARTBASE_BASE_INDENTER_H_
18 #define ART_LIBARTBASE_BASE_INDENTER_H_
19 
20 #include <ostream>
21 #include <streambuf>
22 
23 #include <android-base/logging.h>
24 
25 #include "macros.h"
26 
27 namespace art {
28 
29 constexpr char kIndentChar =' ';
30 constexpr size_t kIndentBy1Count = 2;
31 
32 class Indenter : public std::streambuf {
33  public:
Indenter(std::streambuf * out,char text,size_t count)34   Indenter(std::streambuf* out, char text, size_t count)
35       : indent_next_(true), out_sbuf_(out),
36         text_{text, text, text, text, text, text, text, text},
37         count_(count) {}
38 
39  private:
xsputn(const char * s,std::streamsize n)40   std::streamsize xsputn(const char* s, std::streamsize n) override {
41     std::streamsize result = n;  // Aborts on failure.
42     const char* eol = static_cast<const char*>(memchr(s, '\n', n));
43     while (eol != nullptr) {
44       size_t to_write = eol + 1 - s;
45       Write(s, to_write);
46       s += to_write;
47       n -= to_write;
48       indent_next_ = true;
49       eol = static_cast<const char*>(memchr(s, '\n', n));
50     }
51     if (n != 0u) {
52       Write(s, n);
53     }
54     return result;
55   }
56 
overflow(int_type c)57   int_type overflow(int_type c) override {
58     if (UNLIKELY(c == std::char_traits<char>::eof())) {
59       out_sbuf_->pubsync();
60       return c;
61     }
62     char data[1] = { static_cast<char>(c) };
63     Write(data, 1u);
64     indent_next_ = (c == '\n');
65     return c;
66   }
67 
sync()68   int sync() override {
69     return out_sbuf_->pubsync();
70   }
71 
Write(const char * s,std::streamsize n)72   void Write(const char* s, std::streamsize n) {
73     if (indent_next_) {
74       size_t remaining = count_;
75       while (remaining != 0u) {
76         size_t to_write = std::min(remaining, sizeof(text_));
77         RawWrite(text_, to_write);
78         remaining -= to_write;
79       }
80       indent_next_ = false;
81     }
82     RawWrite(s, n);
83   }
84 
RawWrite(const char * s,std::streamsize n)85   void RawWrite(const char* s, std::streamsize n) {
86     size_t written = out_sbuf_->sputn(s, n);
87     s += written;
88     n -= written;
89     while (n != 0u) {
90       out_sbuf_->pubsync();
91       written = out_sbuf_->sputn(s, n);
92       CHECK_NE(written, 0u) << "Error writing to buffer. Disk full?";
93       s += written;
94       n -= written;
95     }
96   }
97 
98   bool indent_next_;
99 
100   // Buffer to write output to.
101   std::streambuf* const out_sbuf_;
102 
103   // Text output as indent.
104   const char text_[8];
105 
106   // Number of times text is output.
107   size_t count_;
108 
109   friend class VariableIndentationOutputStream;
110 
111   DISALLOW_COPY_AND_ASSIGN(Indenter);
112 };
113 
114 class VariableIndentationOutputStream {
115  public:
116   explicit VariableIndentationOutputStream(std::ostream* os, char text = kIndentChar)
117       : indenter_(os->rdbuf(), text, 0u),
118         indented_os_(&indenter_) {
119   }
120 
Stream()121   std::ostream& Stream() {
122     return indented_os_;
123   }
124 
GetIndentation()125   size_t GetIndentation() const {
126     return indenter_.count_;
127   }
128 
IncreaseIndentation(size_t adjustment)129   void IncreaseIndentation(size_t adjustment) {
130     indenter_.count_ += adjustment;
131   }
132 
DecreaseIndentation(size_t adjustment)133   void DecreaseIndentation(size_t adjustment) {
134     DCHECK_GE(indenter_.count_, adjustment);
135     indenter_.count_ -= adjustment;
136   }
137 
138  private:
139   Indenter indenter_;
140   std::ostream indented_os_;
141 
142   DISALLOW_COPY_AND_ASSIGN(VariableIndentationOutputStream);
143 };
144 
145 class ScopedIndentation {
146  public:
147   explicit ScopedIndentation(VariableIndentationOutputStream* vios,
148                              size_t adjustment = kIndentBy1Count)
vios_(vios)149       : vios_(vios),
150         adjustment_(adjustment) {
151     vios_->IncreaseIndentation(adjustment_);
152   }
153 
~ScopedIndentation()154   ~ScopedIndentation() {
155     vios_->DecreaseIndentation(adjustment_);
156   }
157 
158  private:
159   VariableIndentationOutputStream* const vios_;
160   const size_t adjustment_;
161 
162   DISALLOW_COPY_AND_ASSIGN(ScopedIndentation);
163 };
164 
165 }  // namespace art
166 
167 #endif  // ART_LIBARTBASE_BASE_INDENTER_H_
168