1*b0d29bc4SBrooks Davis // Copyright 2012 The Kyua Authors.
2*b0d29bc4SBrooks Davis // All rights reserved.
3*b0d29bc4SBrooks Davis //
4*b0d29bc4SBrooks Davis // Redistribution and use in source and binary forms, with or without
5*b0d29bc4SBrooks Davis // modification, are permitted provided that the following conditions are
6*b0d29bc4SBrooks Davis // met:
7*b0d29bc4SBrooks Davis //
8*b0d29bc4SBrooks Davis // * Redistributions of source code must retain the above copyright
9*b0d29bc4SBrooks Davis // notice, this list of conditions and the following disclaimer.
10*b0d29bc4SBrooks Davis // * Redistributions in binary form must reproduce the above copyright
11*b0d29bc4SBrooks Davis // notice, this list of conditions and the following disclaimer in the
12*b0d29bc4SBrooks Davis // documentation and/or other materials provided with the distribution.
13*b0d29bc4SBrooks Davis // * Neither the name of Google Inc. nor the names of its contributors
14*b0d29bc4SBrooks Davis // may be used to endorse or promote products derived from this software
15*b0d29bc4SBrooks Davis // without specific prior written permission.
16*b0d29bc4SBrooks Davis //
17*b0d29bc4SBrooks Davis // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18*b0d29bc4SBrooks Davis // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19*b0d29bc4SBrooks Davis // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20*b0d29bc4SBrooks Davis // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21*b0d29bc4SBrooks Davis // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22*b0d29bc4SBrooks Davis // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23*b0d29bc4SBrooks Davis // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24*b0d29bc4SBrooks Davis // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25*b0d29bc4SBrooks Davis // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26*b0d29bc4SBrooks Davis // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27*b0d29bc4SBrooks Davis // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*b0d29bc4SBrooks Davis
29*b0d29bc4SBrooks Davis #include "utils/units.hpp"
30*b0d29bc4SBrooks Davis
31*b0d29bc4SBrooks Davis extern "C" {
32*b0d29bc4SBrooks Davis #include <stdint.h>
33*b0d29bc4SBrooks Davis }
34*b0d29bc4SBrooks Davis
35*b0d29bc4SBrooks Davis #include <stdexcept>
36*b0d29bc4SBrooks Davis
37*b0d29bc4SBrooks Davis #include "utils/format/macros.hpp"
38*b0d29bc4SBrooks Davis #include "utils/text/exceptions.hpp"
39*b0d29bc4SBrooks Davis #include "utils/text/operations.ipp"
40*b0d29bc4SBrooks Davis
41*b0d29bc4SBrooks Davis namespace units = utils::units;
42*b0d29bc4SBrooks Davis
43*b0d29bc4SBrooks Davis
44*b0d29bc4SBrooks Davis /// Constructs a zero bytes quantity.
bytes(void)45*b0d29bc4SBrooks Davis units::bytes::bytes(void) :
46*b0d29bc4SBrooks Davis _count(0)
47*b0d29bc4SBrooks Davis {
48*b0d29bc4SBrooks Davis }
49*b0d29bc4SBrooks Davis
50*b0d29bc4SBrooks Davis
51*b0d29bc4SBrooks Davis /// Constructs an arbitrary bytes quantity.
52*b0d29bc4SBrooks Davis ///
53*b0d29bc4SBrooks Davis /// \param count_ The amount of bytes in the quantity.
bytes(const uint64_t count_)54*b0d29bc4SBrooks Davis units::bytes::bytes(const uint64_t count_) :
55*b0d29bc4SBrooks Davis _count(count_)
56*b0d29bc4SBrooks Davis {
57*b0d29bc4SBrooks Davis }
58*b0d29bc4SBrooks Davis
59*b0d29bc4SBrooks Davis
60*b0d29bc4SBrooks Davis /// Parses a string into a bytes quantity.
61*b0d29bc4SBrooks Davis ///
62*b0d29bc4SBrooks Davis /// \param in_str The user-provided string to be converted.
63*b0d29bc4SBrooks Davis ///
64*b0d29bc4SBrooks Davis /// \return The converted bytes quantity.
65*b0d29bc4SBrooks Davis ///
66*b0d29bc4SBrooks Davis /// \throw std::runtime_error If the input string is empty or invalid.
67*b0d29bc4SBrooks Davis units::bytes
parse(const std::string & in_str)68*b0d29bc4SBrooks Davis units::bytes::parse(const std::string& in_str)
69*b0d29bc4SBrooks Davis {
70*b0d29bc4SBrooks Davis if (in_str.empty())
71*b0d29bc4SBrooks Davis throw std::runtime_error("Bytes quantity cannot be empty");
72*b0d29bc4SBrooks Davis
73*b0d29bc4SBrooks Davis uint64_t multiplier;
74*b0d29bc4SBrooks Davis std::string str = in_str;
75*b0d29bc4SBrooks Davis {
76*b0d29bc4SBrooks Davis const char unit = str[str.length() - 1];
77*b0d29bc4SBrooks Davis switch (unit) {
78*b0d29bc4SBrooks Davis case 'T': case 't': multiplier = TB; break;
79*b0d29bc4SBrooks Davis case 'G': case 'g': multiplier = GB; break;
80*b0d29bc4SBrooks Davis case 'M': case 'm': multiplier = MB; break;
81*b0d29bc4SBrooks Davis case 'K': case 'k': multiplier = KB; break;
82*b0d29bc4SBrooks Davis default: multiplier = 1;
83*b0d29bc4SBrooks Davis }
84*b0d29bc4SBrooks Davis if (multiplier != 1)
85*b0d29bc4SBrooks Davis str.erase(str.length() - 1);
86*b0d29bc4SBrooks Davis }
87*b0d29bc4SBrooks Davis
88*b0d29bc4SBrooks Davis if (str.empty())
89*b0d29bc4SBrooks Davis throw std::runtime_error("Bytes quantity cannot be empty");
90*b0d29bc4SBrooks Davis if (str[0] == '.' || str[str.length() - 1] == '.') {
91*b0d29bc4SBrooks Davis // The standard parser for float values accepts things like ".3" and
92*b0d29bc4SBrooks Davis // "3.", which means that we would interpret ".3K" and "3.K" as valid
93*b0d29bc4SBrooks Davis // quantities. I think this is ugly and should not be allowed, so
94*b0d29bc4SBrooks Davis // special-case this condition and just error out.
95*b0d29bc4SBrooks Davis throw std::runtime_error(F("Invalid bytes quantity '%s'") % in_str);
96*b0d29bc4SBrooks Davis }
97*b0d29bc4SBrooks Davis
98*b0d29bc4SBrooks Davis double count;
99*b0d29bc4SBrooks Davis try {
100*b0d29bc4SBrooks Davis count = text::to_type< double >(str);
101*b0d29bc4SBrooks Davis } catch (const text::value_error& e) {
102*b0d29bc4SBrooks Davis throw std::runtime_error(F("Invalid bytes quantity '%s'") % in_str);
103*b0d29bc4SBrooks Davis }
104*b0d29bc4SBrooks Davis
105*b0d29bc4SBrooks Davis return bytes(uint64_t(count * multiplier));
106*b0d29bc4SBrooks Davis }
107*b0d29bc4SBrooks Davis
108*b0d29bc4SBrooks Davis
109*b0d29bc4SBrooks Davis /// Formats a bytes quantity for user consumption.
110*b0d29bc4SBrooks Davis ///
111*b0d29bc4SBrooks Davis /// \return A textual representation of the bytes quantiy.
112*b0d29bc4SBrooks Davis std::string
format(void) const113*b0d29bc4SBrooks Davis units::bytes::format(void) const
114*b0d29bc4SBrooks Davis {
115*b0d29bc4SBrooks Davis if (_count >= TB) {
116*b0d29bc4SBrooks Davis return F("%.2sT") % (static_cast< float >(_count) / TB);
117*b0d29bc4SBrooks Davis } else if (_count >= GB) {
118*b0d29bc4SBrooks Davis return F("%.2sG") % (static_cast< float >(_count) / GB);
119*b0d29bc4SBrooks Davis } else if (_count >= MB) {
120*b0d29bc4SBrooks Davis return F("%.2sM") % (static_cast< float >(_count) / MB);
121*b0d29bc4SBrooks Davis } else if (_count >= KB) {
122*b0d29bc4SBrooks Davis return F("%.2sK") % (static_cast< float >(_count) / KB);
123*b0d29bc4SBrooks Davis } else {
124*b0d29bc4SBrooks Davis return F("%s") % _count;
125*b0d29bc4SBrooks Davis }
126*b0d29bc4SBrooks Davis }
127*b0d29bc4SBrooks Davis
128*b0d29bc4SBrooks Davis
129*b0d29bc4SBrooks Davis /// Implicit conversion to an integral representation.
operator uint64_t(void) const130*b0d29bc4SBrooks Davis units::bytes::operator uint64_t(void) const
131*b0d29bc4SBrooks Davis {
132*b0d29bc4SBrooks Davis return _count;
133*b0d29bc4SBrooks Davis }
134*b0d29bc4SBrooks Davis
135*b0d29bc4SBrooks Davis
136*b0d29bc4SBrooks Davis /// Extracts a bytes quantity from a stream.
137*b0d29bc4SBrooks Davis ///
138*b0d29bc4SBrooks Davis /// \param input The stream from which to read a single word representing the
139*b0d29bc4SBrooks Davis /// bytes quantity.
140*b0d29bc4SBrooks Davis /// \param rhs The variable into which to store the parsed value.
141*b0d29bc4SBrooks Davis ///
142*b0d29bc4SBrooks Davis /// \return The input stream.
143*b0d29bc4SBrooks Davis ///
144*b0d29bc4SBrooks Davis /// \post The bad bit of input is set to 1 if the parsing failed.
145*b0d29bc4SBrooks Davis std::istream&
operator >>(std::istream & input,bytes & rhs)146*b0d29bc4SBrooks Davis units::operator>>(std::istream& input, bytes& rhs)
147*b0d29bc4SBrooks Davis {
148*b0d29bc4SBrooks Davis std::string word;
149*b0d29bc4SBrooks Davis input >> word;
150*b0d29bc4SBrooks Davis if (input.good() || input.eof()) {
151*b0d29bc4SBrooks Davis try {
152*b0d29bc4SBrooks Davis rhs = bytes::parse(word);
153*b0d29bc4SBrooks Davis } catch (const std::runtime_error& e) {
154*b0d29bc4SBrooks Davis input.setstate(std::ios::badbit);
155*b0d29bc4SBrooks Davis }
156*b0d29bc4SBrooks Davis }
157*b0d29bc4SBrooks Davis return input;
158*b0d29bc4SBrooks Davis }
159*b0d29bc4SBrooks Davis
160*b0d29bc4SBrooks Davis
161*b0d29bc4SBrooks Davis /// Injects a bytes quantity into a stream.
162*b0d29bc4SBrooks Davis ///
163*b0d29bc4SBrooks Davis /// \param output The stream into which to inject the bytes quantity as a
164*b0d29bc4SBrooks Davis /// user-readable string.
165*b0d29bc4SBrooks Davis /// \param rhs The bytes quantity to format.
166*b0d29bc4SBrooks Davis ///
167*b0d29bc4SBrooks Davis /// \return The output stream.
168*b0d29bc4SBrooks Davis std::ostream&
operator <<(std::ostream & output,const bytes & rhs)169*b0d29bc4SBrooks Davis units::operator<<(std::ostream& output, const bytes& rhs)
170*b0d29bc4SBrooks Davis {
171*b0d29bc4SBrooks Davis return (output << rhs.format());
172*b0d29bc4SBrooks Davis }
173