![]() |
libfilezilla
|
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