xref: /netbsd-src/external/bsd/kyua-cli/dist/utils/units.cpp (revision f39f9c9b2b3d39fa4e71f38ebea4c5d12192a641)
16b3a42afSjmmv // Copyright 2012 Google Inc.
26b3a42afSjmmv // All rights reserved.
36b3a42afSjmmv //
46b3a42afSjmmv // Redistribution and use in source and binary forms, with or without
56b3a42afSjmmv // modification, are permitted provided that the following conditions are
66b3a42afSjmmv // met:
76b3a42afSjmmv //
86b3a42afSjmmv // * Redistributions of source code must retain the above copyright
96b3a42afSjmmv //   notice, this list of conditions and the following disclaimer.
106b3a42afSjmmv // * Redistributions in binary form must reproduce the above copyright
116b3a42afSjmmv //   notice, this list of conditions and the following disclaimer in the
126b3a42afSjmmv //   documentation and/or other materials provided with the distribution.
136b3a42afSjmmv // * Neither the name of Google Inc. nor the names of its contributors
146b3a42afSjmmv //   may be used to endorse or promote products derived from this software
156b3a42afSjmmv //   without specific prior written permission.
166b3a42afSjmmv //
176b3a42afSjmmv // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
186b3a42afSjmmv // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
196b3a42afSjmmv // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
206b3a42afSjmmv // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
216b3a42afSjmmv // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
226b3a42afSjmmv // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
236b3a42afSjmmv // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
246b3a42afSjmmv // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
256b3a42afSjmmv // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
266b3a42afSjmmv // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
276b3a42afSjmmv // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
286b3a42afSjmmv 
296b3a42afSjmmv #include "utils/units.hpp"
306b3a42afSjmmv 
316b3a42afSjmmv extern "C" {
326b3a42afSjmmv #include <stdint.h>
336b3a42afSjmmv }
346b3a42afSjmmv 
356b3a42afSjmmv #include <stdexcept>
366b3a42afSjmmv 
376b3a42afSjmmv #include "utils/format/macros.hpp"
386b3a42afSjmmv #include "utils/text/exceptions.hpp"
396b3a42afSjmmv #include "utils/text/operations.ipp"
406b3a42afSjmmv 
416b3a42afSjmmv namespace units = utils::units;
426b3a42afSjmmv 
436b3a42afSjmmv 
446b3a42afSjmmv /// Constructs a zero bytes quantity.
bytes(void)456b3a42afSjmmv units::bytes::bytes(void) :
466b3a42afSjmmv     _count(0)
476b3a42afSjmmv {
486b3a42afSjmmv }
496b3a42afSjmmv 
506b3a42afSjmmv 
516b3a42afSjmmv /// Constructs an arbitrary bytes quantity.
526b3a42afSjmmv ///
536b3a42afSjmmv /// \param count_ The amount of bytes in the quantity.
bytes(const uint64_t count_)546b3a42afSjmmv units::bytes::bytes(const uint64_t count_) :
556b3a42afSjmmv     _count(count_)
566b3a42afSjmmv {
576b3a42afSjmmv }
586b3a42afSjmmv 
596b3a42afSjmmv 
606b3a42afSjmmv /// Parses a string into a bytes quantity.
616b3a42afSjmmv ///
626b3a42afSjmmv /// \param in_str The user-provided string to be converted.
636b3a42afSjmmv ///
646b3a42afSjmmv /// \return The converted bytes quantity.
656b3a42afSjmmv ///
666b3a42afSjmmv /// \throw std::runtime_error If the input string is empty or invalid.
676b3a42afSjmmv units::bytes
parse(const std::string & in_str)686b3a42afSjmmv units::bytes::parse(const std::string& in_str)
696b3a42afSjmmv {
706b3a42afSjmmv     if (in_str.empty())
716b3a42afSjmmv         throw std::runtime_error("Bytes quantity cannot be empty");
726b3a42afSjmmv 
736b3a42afSjmmv     uint64_t multiplier;
746b3a42afSjmmv     std::string str = in_str;
756b3a42afSjmmv     {
766b3a42afSjmmv         const char unit = str[str.length() - 1];
776b3a42afSjmmv         switch (unit) {
786b3a42afSjmmv         case 'T': case 't': multiplier = TB; break;
796b3a42afSjmmv         case 'G': case 'g': multiplier = GB; break;
806b3a42afSjmmv         case 'M': case 'm': multiplier = MB; break;
816b3a42afSjmmv         case 'K': case 'k': multiplier = KB; break;
826b3a42afSjmmv         default: multiplier = 1;
836b3a42afSjmmv         }
846b3a42afSjmmv         if (multiplier != 1)
856b3a42afSjmmv             str.erase(str.length() - 1);
866b3a42afSjmmv     }
876b3a42afSjmmv 
886b3a42afSjmmv     if (str.empty())
896b3a42afSjmmv         throw std::runtime_error("Bytes quantity cannot be empty");
906b3a42afSjmmv     if (str[0] == '.' || str[str.length() - 1] == '.') {
916b3a42afSjmmv         // The standard parser for float values accepts things like ".3" and
926b3a42afSjmmv         // "3.", which means that we would interpret ".3K" and "3.K" as valid
936b3a42afSjmmv         // quantities.  I think this is ugly and should not be allowed, so
946b3a42afSjmmv         // special-case this condition and just error out.
956b3a42afSjmmv         throw std::runtime_error(F("Invalid bytes quantity '%s'") % in_str);
966b3a42afSjmmv     }
976b3a42afSjmmv 
986b3a42afSjmmv     double count;
996b3a42afSjmmv     try {
1006b3a42afSjmmv         count = text::to_type< double >(str);
1016b3a42afSjmmv     } catch (const text::value_error& e) {
1026b3a42afSjmmv         throw std::runtime_error(F("Invalid bytes quantity '%s'") % in_str);
1036b3a42afSjmmv     }
1046b3a42afSjmmv 
1056b3a42afSjmmv     return bytes(uint64_t(count * multiplier));
1066b3a42afSjmmv }
1076b3a42afSjmmv 
1086b3a42afSjmmv 
1096b3a42afSjmmv /// Formats a bytes quantity for user consumption.
1106b3a42afSjmmv ///
1116b3a42afSjmmv /// \return A textual representation of the bytes quantiy.
1126b3a42afSjmmv std::string
format(void) const1136b3a42afSjmmv units::bytes::format(void) const
1146b3a42afSjmmv {
1156b3a42afSjmmv     if (_count >= TB) {
1166b3a42afSjmmv         return F("%.2sT") % (static_cast< float >(_count) / TB);
1176b3a42afSjmmv     } else if (_count >= GB) {
1186b3a42afSjmmv         return F("%.2sG") % (static_cast< float >(_count) / GB);
1196b3a42afSjmmv     } else if (_count >= MB) {
1206b3a42afSjmmv         return F("%.2sM") % (static_cast< float >(_count) / MB);
1216b3a42afSjmmv     } else if (_count >= KB) {
1226b3a42afSjmmv         return F("%.2sK") % (static_cast< float >(_count) / KB);
1236b3a42afSjmmv     } else {
1246b3a42afSjmmv         return F("%s") % _count;
1256b3a42afSjmmv     }
1266b3a42afSjmmv }
1276b3a42afSjmmv 
1286b3a42afSjmmv 
1296b3a42afSjmmv /// Implicit conversion to an integral representation.
operator uint64_t(void) const1306b3a42afSjmmv units::bytes::operator uint64_t(void) const
1316b3a42afSjmmv {
1326b3a42afSjmmv     return _count;
1336b3a42afSjmmv }
1346b3a42afSjmmv 
1356b3a42afSjmmv 
1366b3a42afSjmmv /// Extracts a bytes quantity from a stream.
1376b3a42afSjmmv ///
1386b3a42afSjmmv /// \param input The stream from which to read a single word representing the
1396b3a42afSjmmv ///     bytes quantity.
1406b3a42afSjmmv /// \param rhs The variable into which to store the parsed value.
1416b3a42afSjmmv ///
1426b3a42afSjmmv /// \return The input stream.
1436b3a42afSjmmv ///
1446b3a42afSjmmv /// \post The bad bit of input is set to 1 if the parsing failed.
1456b3a42afSjmmv std::istream&
operator >>(std::istream & input,bytes & rhs)146*f39f9c9bSjmmv units::operator>>(std::istream& input, bytes& rhs)
1476b3a42afSjmmv {
1486b3a42afSjmmv     std::string word;
1496b3a42afSjmmv     input >> word;
1506b3a42afSjmmv     if (input.good() || input.eof()) {
1516b3a42afSjmmv         try {
152*f39f9c9bSjmmv             rhs = bytes::parse(word);
1536b3a42afSjmmv         } catch (const std::runtime_error& e) {
1546b3a42afSjmmv             input.setstate(std::ios::badbit);
1556b3a42afSjmmv         }
1566b3a42afSjmmv     }
1576b3a42afSjmmv     return input;
1586b3a42afSjmmv }
1596b3a42afSjmmv 
1606b3a42afSjmmv 
1616b3a42afSjmmv /// Injects a bytes quantity into a stream.
1626b3a42afSjmmv ///
1636b3a42afSjmmv /// \param output The stream into which to inject the bytes quantity as a
1646b3a42afSjmmv ///     user-readable string.
1656b3a42afSjmmv /// \param rhs The bytes quantity to format.
1666b3a42afSjmmv ///
1676b3a42afSjmmv /// \return The output stream.
1686b3a42afSjmmv std::ostream&
operator <<(std::ostream & output,const bytes & rhs)169*f39f9c9bSjmmv units::operator<<(std::ostream& output, const bytes& rhs)
1706b3a42afSjmmv {
1716b3a42afSjmmv     return (output << rhs.format());
1726b3a42afSjmmv }
173