#include "printf.h" #include #include #include #include std::string mini_sprintf_format(bool left_align, bool sign, bool space_on_left, bool padding_by_zero, int width, int precision, int length, char specifier, const MlValue &value) { std::string s; std::ostringstream stream_str; // PERF string append should be faster bool is_positive = false; if (specifier == 's') { return value.as_string(); } if (specifier == 'c') { stream_str << (char) value.as_int(); return stream_str.str(); } if (specifier == 'i' || specifier == 'd') { if (value.is_number()) { auto ival = value.as_int(); is_positive = ival >= 0; s = std::to_string(ival); } else if (value.is_string()) { // print ascii code of character if (!value.as_string().empty()) { stream_str << (int) value.as_string()[0]; return stream_str.str(); } else { // TODO handle empty string - error? } } } else if (specifier == 'f' || specifier == 'e') { double dval = value.as_float(); is_positive = dval >= 0; if (specifier == 'f') stream_str << std::fixed; else stream_str << std::scientific; if (precision >= 0) stream_str << std::setprecision(precision); stream_str << dval; s = stream_str.str(); } if (width > -1 && s.size() < width) { int new_width = width - (sign && is_positive ? 1 : 0); // spce for + sign if (s.size() < new_width) { char padd = padding_by_zero ? '0' : ' '; if (left_align) { s.insert(0, new_width - s.size(), padd); } else { s.append(new_width - s.size(), ' '); // cannot append 0 because its different number then } } } if (sign && is_positive) { s.insert(0, 1, '+'); } if (space_on_left) { s.insert(0, 1, ' '); } return s; } std::string mini_sprintf(const std::string &format_str, const std::vector ¶meters) { int arg_position = 0; char c, c1; std::string buf; std::string output_str; std::string::const_iterator si; for (si = format_str.begin(); si != format_str.end(); ++si) { c = *si; // formatting directives if (c == '%') { if (++si >= format_str.end()) { return output_str; // end of string, invalid % on last pos } c1 = *si; switch (c1) { case '%': output_str += c1; break; case '-': case '+': case ' ': case '#': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': case 'i': case 'd': case 'f': case 'c': case 's': bool left_align = false; bool sign = false; bool space_on_left = false; bool padding_by_zero = false; int width = -1; int precision = -1; int length = -1; // http://www.cplusplus.com/reference/cstdio/printf/ // https://www.menie.org/georges/embedded/small_printf_source_code.html // // %[flags][width][.precision][length]specifier // flags: - + \_ # 0 while ((*si == '-' || *si == '+' || *si == ' ' || *si == '#' || *si == '0') && si < format_str.end()) { switch (*si) { case '-': left_align = true; break; case '+': sign = true; break; case ' ': space_on_left = true; break; case '#': break; case '0': padding_by_zero = true; left_align = true; break; } si++; } // width if (si >= format_str.end()) return output_str; // invalid end of string while (*si >= '0' && *si <= '9' && si < format_str.end()) { if (width == -1) { width = 0; } width = width * 10 + (*si - '0'); si++; } // precision if (si >= format_str.end()) return output_str; // invalid end of string if (*si == '.') { precision = 0; if (++si >= format_str.end()) return output_str; // invalid end of string while (*si >= '0' && *si <= '9' && si < format_str.end()) { precision = precision * 10 + (*si - '0'); si++; } } // length // specifier if (si >= format_str.end()) return output_str; // invalid end of string if (*si == 'i' || *si == 'd' || *si == 'f' || *si == 's' || *si == 'c') { // TODO more specifiers std::string s = mini_sprintf_format(left_align, sign, space_on_left, padding_by_zero, width, precision, length, *si, parameters[arg_position]); arg_position++; output_str += s; } else { output_str += "UNKNOWN FORMAT SPECIFIER"; } } // escaping sequences } else if (c == '\\') { if (++si >= format_str.end()) { return output_str; // end of string, invalid % on last pos } c1 = *si; switch (c1) { case 't': output_str += '\t'; break; case 'r': output_str += '\r'; break; case 'n': output_str += '\n'; break; case 'x': // hex ie \x1b if (si + 2 >= format_str.end()) return output_str; // end of string, invalid hex constant buf.clear(); buf.push_back(*(si + 1)); buf.push_back(*(si + 2)); output_str.push_back((char) std::strtol(&buf[0], nullptr, 16)); si += 2; break; case '0': // octal ie "\033" if (si + 2 >= format_str.end()) return output_str; // end of string, invalid octal constant buf.clear(); buf.push_back(*(si + 1)); buf.push_back(*(si + 2)); output_str.push_back((char) std::strtol(&buf[0], nullptr, 8)); // TODO maybe octal constant has 3 bytes // if (si + 2 >= format_str.end()) return output_str; // end of string, invalid octal or hex constant // buf.push_back(*(si + 3)); // si += 3; si += 2; break; default: output_str += c1; } // normal characters } else { output_str += c; } } return output_str; }