diff --git a/iores.cpp b/iores.cpp index 5ba42df..62319aa 100644 --- a/iores.cpp +++ b/iores.cpp @@ -34,6 +34,7 @@ #include "ioreth.hpp" #include "util.hpp" #include "rand.hpp" +#include "unit_int.hpp" class Options { @@ -140,16 +141,16 @@ class Options switch (c) { case 's': /* disk access range in blocks */ - accessRange_ = ::atol(optarg); + accessRange_ = fromUnitIntString(optarg); break; case 'b': /* blocksize */ - blockSize_ = ::atol(optarg); + blockSize_ = fromUnitIntString(optarg); break; case 'p': /* period */ period_ = ::atol(optarg); break; case 'c': /* count */ - count_ = ::atol(optarg); + count_ = fromUnitIntString(optarg); break; case 'w': /* write */ mode_ = WRITE_MODE; diff --git a/ioth.cpp b/ioth.cpp index 8bd3e38..9081b95 100644 --- a/ioth.cpp +++ b/ioth.cpp @@ -35,6 +35,7 @@ #include "ioreth.hpp" #include "util.hpp" #include "thread_pool.hpp" +#include "unit_int.hpp" /** * Parse commane-line arguments as options. @@ -127,16 +128,16 @@ class Options switch (c) { case 's': /* start offset in blocks */ - startBlockId_ = ::atol(optarg); + startBlockId_ = fromUnitIntString(optarg); break; case 'b': /* blocksize */ - blockSize_ = ::atol(optarg); + blockSize_ = fromUnitIntString(optarg); break; case 'p': /* period */ period_ = ::atol(optarg); break; case 'c': /* count */ - count_ = ::atol(optarg); + count_ = fromUnitIntString(optarg); break; case 'w': /* write */ mode_ = WRITE_MODE; diff --git a/unit_int.hpp b/unit_int.hpp new file mode 100644 index 0000000..e81798a --- /dev/null +++ b/unit_int.hpp @@ -0,0 +1,100 @@ +#pragma once + +#include +#include +#include +#include +#include + +/** + * Formst string with va_list. + */ +inline std::string formatStringV(const char *format, va_list ap) +{ + char *p = nullptr; + int ret = ::vasprintf(&p, format, ap); + if (ret < 0) throw std::runtime_error("vasprintf failed."); + try { + std::string s(p, ret); + ::free(p); + return s; + } catch (...) { + ::free(p); + throw; + } +} + +/** + * Create a std::string using printf() like formatting. + */ +inline std::string formatString(const char * format, ...) +{ + std::string s; + std::exception_ptr ep; + va_list args; + va_start(args, format); + try { + s = formatStringV(format, args); + } catch (...) { + ep = std::current_exception(); + } + va_end(args); + if (ep) std::rethrow_exception(ep); + return s; +} + +/** + * Convert size string with unit suffix to unsigned integer. + */ +inline uint64_t fromUnitIntString(const std::string &valStr) +{ + if (valStr.empty()) { + throw std::runtime_error("fromUnitIntString: invalid argument."); + } + char *endp; + uint64_t val = ::strtoll(valStr.c_str(), &endp, 10); + int shift = 0; + switch (*endp) { + case 'e': case 'E': shift = 60; break; + case 'p': case 'P': shift = 50; break; + case 't': case 'T': shift = 40; break; + case 'g': case 'G': shift = 30; break; + case 'm': case 'M': shift = 20; break; + case 'k': case 'K': shift = 10; break; + case '\0': break; + default: + throw std::runtime_error("fromUnitIntString: invalid suffix charactor."); + } + if (((val << shift) >> shift) != val) { + throw std::runtime_error("fromUnitIntString: overflow."); + } + return val << shift; +} + +/** + * Unit suffixes: + * k: 2^10 + * m: 2^20 + * g: 2^30 + * t: 2^40 + * p: 2^50 + * e: 2^60 + */ +inline std::string toUnitIntString(uint64_t val) +{ + uint64_t mask = (1ULL << 10) - 1; + const char units[] = " kmgtpezy"; + + size_t i = 0; + while (i < sizeof(units)) { + if ((val & ~mask) != val) { break; } + i++; + val >>= 10; + } + + if (0 < i && i < sizeof(units)) { + return formatString("%" PRIu64 "%c", val, units[i]); + } else { + return formatString("%" PRIu64 "", val); + } +}