1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef COMPONENTS_ZUCCHINI_IO_UTILS_H_
6 #define COMPONENTS_ZUCCHINI_IO_UTILS_H_
7 
8 #include <stdint.h>
9 
10 #include <cctype>
11 #include <istream>
12 #include <ostream>
13 #include <sstream>
14 #include <string>
15 
16 #include "base/macros.h"
17 
18 namespace zucchini {
19 
20 // An std::ostream wrapper that that limits number of std::endl lines to output,
21 // useful for preventing excessive debug message output. Usage requires some
22 // work by the caller. Sample:
23 //   static LimitedOutputStream los(std::cerr, 10);
24 //   if (!los.full()) {
25 //     ...  // Prepare message. Block may be skipped so don't do other work!
26 //     los << message;
27 //     los << std::endl;  // Important!
28 //   }
29 class LimitedOutputStream : public std::ostream {
30  private:
31   class StreamBuf : public std::stringbuf {
32    public:
33     StreamBuf(std::ostream& os, int limit);
34     ~StreamBuf() override;
35 
36     int sync() override;
full()37     bool full() const { return counter_ >= limit_; }
38 
39    private:
40     std::ostream& os_;
41     const int limit_;
42     int counter_ = 0;
43   };
44 
45  public:
46   LimitedOutputStream(std::ostream& os, int limit);
full()47   bool full() const { return buf_.full(); }
48 
49  private:
50   StreamBuf buf_;
51 
52   DISALLOW_COPY_AND_ASSIGN(LimitedOutputStream);
53 };
54 
55 // A class to render hexadecimal numbers for std::ostream with 0-padding. This
56 // is more concise and flexible than stateful STL manipulator alternatives; so:
57 //   std::ios old_fmt(nullptr);
58 //   old_fmt.copyfmt(std::cout);
59 //   std::cout << std::uppercase << std::hex;
60 //   std::cout << std::setfill('0') << std::setw(8) << int_data << std::endl;
61 //   std::cout.copyfmt(old_fmt);
62 // can be expressed as:
63 //   std::cout << AxHex<8>(int_data) << std::endl;
64 template <int N, typename T = uint32_t>
65 struct AsHex {
AsHexAsHex66   explicit AsHex(T value_in) : value(value_in) {}
67   T value;
68 };
69 
70 template <int N, typename T>
71 std::ostream& operator<<(std::ostream& os, const AsHex<N, T>& as_hex) {
72   char buf[N + 1];
73   buf[N] = '\0';
74   T value = as_hex.value;
75   for (int i = N - 1; i >= 0; --i, value >>= 4)
76     buf[i] = "0123456789ABCDEF"[static_cast<int>(value & 0x0F)];
77   if (value)
78     os << "...";  // To indicate data truncation, or negative values.
79   os << buf;
80   return os;
81 }
82 
83 // An output manipulator to simplify printing list separators. Sample usage:
84 //   PrefixSep sep(",");
85 //   for (int i : {3, 1, 4, 1, 5, 9})
86 //      std::cout << sep << i;
87 //   std::cout << std::endl;  // Outputs "3,1,4,1,5,9\n".
88 class PrefixSep {
89  public:
PrefixSep(const std::string & sep_str)90   explicit PrefixSep(const std::string& sep_str) : sep_str_(sep_str) {}
91 
92   friend std::ostream& operator<<(std::ostream& ostr, PrefixSep& obj);
93 
94  private:
95   std::string sep_str_;
96   bool first_ = true;
97 
98   DISALLOW_COPY_AND_ASSIGN(PrefixSep);
99 };
100 
101 // An input manipulator that dictates the expected next character in
102 // |std::istream|, and invalidates the stream if expectation is not met.
103 class EatChar {
104  public:
EatChar(char ch)105   explicit EatChar(char ch) : ch_(ch) {}
106 
107   friend inline std::istream& operator>>(std::istream& istr,
108                                          const EatChar& obj) {
109     if (!istr.fail() && istr.get() != obj.ch_)
110       istr.setstate(std::ios_base::failbit);
111     return istr;
112   }
113 
114  private:
115   char ch_;
116 
117   DISALLOW_COPY_AND_ASSIGN(EatChar);
118 };
119 
120 // An input manipulator that reads an unsigned integer from |std::istream|,
121 // and invalidates the stream on failure. Intolerant of leading white spaces,
122 template <typename T>
123 class StrictUInt {
124  public:
StrictUInt(T & var)125   explicit StrictUInt(T& var) : var_(var) {}
126   StrictUInt(const StrictUInt&) = default;
127 
128   friend std::istream& operator>>(std::istream& istr, StrictUInt<T> obj) {
129     if (!istr.fail() && !::isdigit(istr.peek())) {
130       istr.setstate(std::ios_base::failbit);
131       return istr;
132     }
133     return istr >> obj.var_;
134   }
135 
136  private:
137   T& var_;
138 };
139 
140 // Stub out uint8_t: istream treats it as char, and value won't be read as int!
141 template <>
142 struct StrictUInt<uint8_t> {};
143 
144 }  // namespace zucchini
145 
146 #endif  // COMPONENTS_ZUCCHINI_IO_UTILS_H_
147