libfilezilla
format.hpp
Go to the documentation of this file.
00001 #ifndef LIBFILEZILLA_FORMAT_HEADER
00002 #define LIBFILEZILLA_FORMAT_HEADER
00003 
00004 #include "string.hpp"
00005 
00006 #include <cstdlib>
00007 
00008 #include <assert.h>
00009 
00014 namespace fz {
00015 
00017 namespace detail {
00018 
00019 // Get flags
00020 enum : char {
00021     pad_0 = 1,
00022     pad_blank = 2,
00023     with_width = 4,
00024     left_align = 8,
00025     always_sign = 16
00026 };
00027 
00028 // Converts integral type to desired string type...
00029 // ... basic case: simple unsigned value
00030 template<typename String, bool Unsigned, typename Arg>
00031 typename std::enable_if_t<std::is_integral<std::decay_t<Arg>>::value && !std::is_enum<std::decay_t<Arg>>::value, String> integral_to_string(char flags, int width, Arg && arg)
00032 {
00033     std::decay_t<Arg> v = arg;
00034 
00035     char lead{};
00036 
00037     assert(!Unsigned || !std::is_signed<std::decay_t<Arg>>::value || arg >= 0);
00038 
00039     if (std::is_signed<std::decay_t<Arg>>::value && !(arg >= 0)) {
00040         lead = '-';
00041     }
00042     else if (std::is_signed<std::decay_t<Arg>>::value && flags & always_sign) {
00043         lead = '+';
00044     }
00045     else if (flags & pad_blank && arg >= 0) {
00046         lead = ' ';
00047     }
00048 
00049     // max decimal digits in b-bit integer is floor((b-1) * log_10(2)) + 1 < b * 0.5 + 1
00050     typename String::value_type buf[sizeof(v) * 4 + 1];
00051     auto *const end = buf + sizeof(v) * 4 + 1;
00052     auto *p = end;
00053 
00054     do {
00055         int const mod = std::abs(static_cast<int>(v % 10));
00056         *(--p) = '0' + mod;
00057         v /= 10;
00058     } while (v);
00059 
00060     if (flags & with_width) {
00061         if (lead && width > 0) {
00062             --width;
00063         }
00064 
00065         String ret;
00066 
00067         if (flags & pad_0) {
00068             if (lead) {
00069                 ret += lead;
00070             }
00071             if (end - p < width) {
00072                 ret.append(width - (end - p), '0');
00073             }
00074             ret.append(p, end);
00075         }
00076         else {
00077             if (end - p < width && !(flags & left_align)) {
00078                 ret.append(width - (end - p), ' ');
00079             }
00080             if (lead) {
00081                 ret += lead;
00082             }
00083             ret.append(p, end);
00084             if (end - p < width && flags & left_align) {
00085                 ret.append(width - (end - p), ' ');
00086             }
00087         }
00088 
00089         return ret;
00090     }
00091     else {
00092         if (lead) {
00093             *(--p) = lead;
00094         }
00095         return String(p, end);
00096     }
00097 }
00098 
00099 // ... for strongly typed enums
00100 template<typename String, bool Unsigned, typename Arg>
00101 typename std::enable_if_t<std::is_enum<std::decay_t<Arg>>::value, String> integral_to_string(char flags, int width, Arg && arg)
00102 {
00103     return integral_to_string<String, Unsigned>(flags, width, static_cast<std::underlying_type_t<std::decay_t<Arg>>>(arg));
00104 }
00105 
00106 // ... assert otherwise
00107 template<typename String, bool Unsigned, typename Arg>
00108 typename std::enable_if_t<!std::is_integral<std::decay_t<Arg>>::value && !std::is_enum<std::decay_t<Arg>>::value, String> integral_to_string(char, int, Arg &&)
00109 {
00110     assert(0);
00111     return String();
00112 }
00113 
00114 
00115 // Converts argument to string...
00116 // ... if toString(arg) is valid expression
00117 template<typename String, typename Arg>
00118 auto arg_to_string(Arg&& arg) -> decltype(toString<String>(std::forward<Arg>(arg)))
00119 {
00120     return toString<String>(std::forward<Arg>(arg));
00121 }
00122 
00123 // ... assert otherwise
00124 template<typename String>
00125 String arg_to_string(...)
00126 {
00127     assert(0);
00128     return String();
00129 }
00130 
00131 
00132 // Converts integral type to hex string with desired string type...
00133 // ... basic case: simple unsigned value
00134 template<typename String, bool Lowercase, typename Arg>
00135 typename std::enable_if_t<std::is_integral<std::decay_t<Arg>>::value && !std::is_enum<std::decay_t<Arg>>::value, String> integral_to_hex_string(Arg && arg)
00136 {
00137     std::decay_t<Arg> v = arg;
00138     typename String::value_type buf[sizeof(v) * 2];
00139     auto *const end = buf + sizeof(v) * 2;
00140     auto *p = end;
00141 
00142     do {
00143         *(--p) = fz::int_to_hex_char<typename String::value_type, Lowercase>(v & 0xf);
00144         v >>= 4;
00145     } while (v);
00146 
00147     return String(p, end);
00148 }
00149 
00150 // ... for enums
00151 template<typename String, bool Lowercase, typename Arg>
00152 typename std::enable_if_t<std::is_enum<std::decay_t<Arg>>::value, String> integral_to_hex_string(Arg && arg)
00153 {
00154     return integral_to_hex_string<String, Lowercase>(static_cast<std::underlying_type_t<std::decay_t<Arg>>>(arg));
00155 }
00156 
00157 // ... assert otherwise
00158 template<typename String, bool Lowercase, typename Arg>
00159 typename std::enable_if_t<!std::is_integral<std::decay_t<Arg>>::value && !std::is_enum<std::decay_t<Arg>>::value, String> integral_to_hex_string(Arg &&)
00160 {
00161     assert(0);
00162     return String();
00163 }
00164 
00165 
00166 // Converts to pointer to hex string
00167 template<typename String, typename Arg>
00168 typename std::enable_if_t<std::is_pointer<std::decay_t<Arg>>::value, String> pointer_to_string(Arg&& arg)
00169 {
00170     return String({'0', 'x'}) + integral_to_hex_string<String, true>(reinterpret_cast<uintptr_t>(arg));
00171 }
00172 
00173 
00174 template<typename String, typename Arg>
00175 typename std::enable_if_t<!std::is_pointer<std::decay_t<Arg>>::value, String> pointer_to_string(Arg&&)
00176 {
00177     assert(0);
00178     return String();
00179 }
00180 
00181 
00182 template<typename String, typename... Args>
00183 String extract_arg(char, size_t, typename String::value_type, size_t)
00184 {
00185     return String();
00186 }
00187 
00188 template<typename String>
00189 void pad_arg(String& s, char flags, size_t width)
00190 {
00191     if (flags & with_width && s.size() < width) {
00192         if (flags & left_align) {
00193             s += String(width - s.size(), ' ');
00194         }
00195         else {
00196             s = String(width - s.size(), (flags & pad_0) ? '0' : ' ') + s;
00197         }
00198     }
00199 }
00200 
00201 template<typename String, typename Arg, typename... Args>
00202 String extract_arg(char flags, size_t width, typename String::value_type type, size_t arg_n, Arg&& arg, Args&&...args)
00203 {
00204     String ret;
00205 
00206     if (!arg_n) {
00207         if (type == 's') {
00208             ret = arg_to_string<String>(std::forward<Arg>(arg));
00209             pad_arg(ret, flags, width);
00210         }
00211         else if (type == 'd' || type == 'i') {
00212             ret = integral_to_string<String, false>(flags, width, std::forward<Arg>(arg));
00213         }
00214         else if (type == 'u') {
00215             ret = integral_to_string<String, true>(flags, width, std::forward<Arg>(arg));
00216         }
00217         else if (type == 'x') {
00218             ret = integral_to_hex_string<String, true>(std::forward<Arg>(arg));
00219             pad_arg(ret, flags, width);
00220         }
00221         else if (type == 'X') {
00222             ret = integral_to_hex_string<String, false>(std::forward<Arg>(arg));
00223             pad_arg(ret, flags, width);
00224         }
00225         else if (type == 'p') {
00226             ret = pointer_to_string<String>(std::forward<Arg>(arg));
00227             pad_arg(ret, flags, width);
00228         }
00229         else {
00230             assert(0);
00231         }
00232     }
00233     else {
00234         ret = extract_arg<String>(flags, width, type, arg_n - 1, std::forward<Args>(args)...);
00235     }
00236 
00237     return ret;
00238 }
00239 
00240 template<typename String, typename... Args>
00241 void process_arg(String const& fmt, typename String::size_type & pos, String& ret, size_t& arg_n, Args&&... args)
00242 {
00243     ++pos;
00244 
00245     // Get literal percent out of the way
00246     if (fmt[pos] == '%') {
00247         ret += '%';
00248         ++pos;
00249         return;
00250     }
00251 
00252 parse_start:
00253     char flags{};
00254     while (true) {
00255         if (fmt[pos] == '0') {
00256             flags |= pad_0;
00257         }
00258         else if (fmt[pos] == ' ') {
00259             flags |= pad_blank;
00260         }
00261         else if (fmt[pos] == '-') {
00262             flags &= ~pad_0;
00263             flags |= left_align;
00264         }
00265         else if (fmt[pos] == '+') {
00266             flags &= ~pad_blank;
00267             flags |= always_sign;
00268         }
00269         else {
00270             break;
00271         }
00272         ++pos;
00273     }
00274 
00275     // Field width
00276     size_t width{};
00277     while (fmt[pos] >= '0' && fmt[pos] <= '9') {
00278         flags |= with_width;
00279         width *= 10;
00280         width += fmt[pos] - '0';
00281         ++pos;
00282     }
00283     if (width > 10000) {
00284         assert(0);
00285         width = 10000;
00286     }
00287 
00288     if (fmt[pos] == '$') {
00289         // Positional argument, start over
00290         arg_n = width - 1;
00291         ++pos;
00292         goto parse_start;
00293     }
00294 
00295     // Ignore length modifier
00296     while (true) {
00297         auto c = fmt[pos];
00298         if (c == 'h' || c == 'l' || c == 'L' || c == 'j' || c == 'z' || c == 't') {
00299             ++pos;
00300         }
00301         else {
00302             break;
00303         }
00304     }
00305 
00306     assert(arg_n < sizeof...(args));
00307     if (arg_n >= sizeof...(args)) {
00308         ++pos;
00309         return;
00310     }
00311 
00312     auto const type = fmt[pos++];
00313 
00314     ret += extract_arg<String>(flags, width, type, arg_n++, std::forward<Args>(args)...);
00315 
00316     // Now we're ready to print!
00317 }
00318 
00319 }
00321 
00343 template<typename String, typename... Args>
00344 String sprintf(String const& fmt, Args&&... args)
00345 {
00346     String ret;
00347 
00348     // Find % characters
00349     typename String::size_type start = 0, pos;
00350     size_t arg_n{};
00351     while ((pos = fmt.find('%', start)) != String::npos) {
00352         
00353         // Copy segment preceeding the %
00354         ret += fmt.substr(start, pos - start);
00355 
00356         detail::process_arg(fmt, pos, ret, arg_n, std::forward<Args>(args)...);
00357 
00358         start = pos;
00359     }
00360 
00361     // Copy remainder of string
00362     ret += fmt.substr(start);
00363 
00364     return ret;
00365 }
00366 
00367 template<typename... Args>
00368 std::string sprintf(char const* fmt, Args&&... args)
00369 {
00370     return sprintf(std::string(fmt), std::forward<Args>(args)...);
00371 }
00372 
00373 template<typename... Args>
00374 std::wstring sprintf(wchar_t const* fmt, Args&&... args)
00375 {
00376     return sprintf(std::wstring(fmt), std::forward<Args>(args)...);
00377 }
00378 
00379 }
00380 
00381 #endif
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines