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