mirror of
				https://github.com/fmtlib/fmt.git
				synced 2025-10-29 05:01:46 +01:00 
			
		
		
		
	More tests.
This commit is contained in:
		
							
								
								
									
										81
									
								
								format.cc
									
									
									
									
									
								
							
							
						
						
									
										81
									
								
								format.cc
									
									
									
									
									
								
							| @@ -12,11 +12,13 @@ | ||||
|  | ||||
| using std::size_t; | ||||
|  | ||||
| // Throws Exception(message) if s contains '}' and FormatError reporting | ||||
| // unmatched '{' otherwise. The idea is that unmatched '{' should override | ||||
| // other errors. | ||||
| namespace { | ||||
|  | ||||
| // Throws Exception(message) if format contains '}', otherwise throws | ||||
| // FormatError reporting unmatched '{'. The idea is that unmatched '{' | ||||
| // should override other errors. | ||||
| template <typename Exception> | ||||
| static void Throw(const char *s, const char *message) { | ||||
| void Throw(const char *s, const char *message) { | ||||
|   while (*s && *s != '}') | ||||
|     ++s; | ||||
|   if (!*s) | ||||
| @@ -24,6 +26,35 @@ static void Throw(const char *s, const char *message) { | ||||
|   throw Exception(message); | ||||
| } | ||||
|  | ||||
| // Parses an unsigned integer advancing s to the end of the parsed input. | ||||
| // This function assumes that the first character of s is a digit. | ||||
| unsigned ParseUInt(const char *&s) { | ||||
|   assert('0' <= *s && *s <= '9'); | ||||
|   unsigned value = 0; | ||||
|   do { | ||||
|     unsigned new_value = value * 10 + (*s++ - '0'); | ||||
|     if (new_value < value)  // Check if value wrapped around. | ||||
|       Throw<fmt::FormatError>(s, "number is too big in format"); | ||||
|     value = new_value; | ||||
|   } while ('0' <= *s && *s <= '9'); | ||||
|   return value; | ||||
| } | ||||
|  | ||||
| // Flags. | ||||
| enum { | ||||
|   PLUS_FLAG = 1, | ||||
|   ZERO_FLAG = 2 | ||||
| }; | ||||
|  | ||||
| void CheckFlags(unsigned flags) { | ||||
|   if (flags == 0) return; | ||||
|   if ((flags & PLUS_FLAG) != 0) | ||||
|     throw fmt::FormatError("format specifier '+' used with non-numeric type"); | ||||
|   if ((flags & ZERO_FLAG) != 0) | ||||
|     throw fmt::FormatError("format specifier '0' used with non-numeric type"); | ||||
| } | ||||
| } | ||||
|  | ||||
| template <typename T> | ||||
| void fmt::Formatter::FormatArg( | ||||
|     const char *format, const T &arg, int width, int precision) { | ||||
| @@ -58,17 +89,9 @@ void fmt::Formatter::Format() { | ||||
|     buffer_.insert(buffer_.end(), start, s - 1); | ||||
|  | ||||
|     // Parse argument index. | ||||
|     unsigned arg_index = 0; | ||||
|     if ('0' <= *s && *s <= '9') { | ||||
|       do { | ||||
|         unsigned index = arg_index * 10 + (*s++ - '0'); | ||||
|         if (index < arg_index)  // Check if index wrapped around. | ||||
|           Throw<FormatError>(s, "argument index is too big"); // TODO: test | ||||
|         arg_index = index; | ||||
|       } while ('0' <= *s && *s <= '9'); | ||||
|     } else { | ||||
|       Throw<FormatError>(s, "missing argument index in format string"); | ||||
|     } | ||||
|     if (*s < '0' || *s > '9') | ||||
|       Throw<fmt::FormatError>(s, "missing argument index in format string"); | ||||
|     unsigned arg_index = ParseUInt(s); | ||||
|     if (arg_index >= args_.size()) | ||||
|       Throw<std::out_of_range>(s, "argument index is out of range in format"); | ||||
|  | ||||
| @@ -76,24 +99,27 @@ void fmt::Formatter::Format() { | ||||
|     char *arg_format_ptr = arg_format; | ||||
|     *arg_format_ptr++ = '%'; | ||||
|  | ||||
|     char type = 0; | ||||
|     unsigned flags = 0; | ||||
|     int width = -1; | ||||
|     int precision = -1; | ||||
|     char type = 0; | ||||
|     if (*s == ':') { | ||||
|       ++s; | ||||
|       if (*s == '+') | ||||
|       if (*s == '+') { | ||||
|         flags |= PLUS_FLAG; | ||||
|         *arg_format_ptr++ = *s++; | ||||
|       if (*s == '0') | ||||
|       } | ||||
|       if (*s == '0') { | ||||
|         flags |= ZERO_FLAG; | ||||
|         *arg_format_ptr++ = *s++; | ||||
|       } | ||||
|  | ||||
|       // Parse width. | ||||
|       if ('0' <= *s && *s <= '9') { | ||||
|         *arg_format_ptr++ = '*'; | ||||
|         width = 0; | ||||
|         do { | ||||
|           // TODO: check overflow | ||||
|           width = width * 10 + (*s++ - '0'); | ||||
|         } while ('0' <= *s && *s <= '9'); | ||||
|         unsigned number = ParseUInt(s); | ||||
|         if (number > INT_MAX) ; // TODO: error | ||||
|         width = number; | ||||
|       } | ||||
|  | ||||
|       // Parse precision. | ||||
| @@ -118,13 +144,14 @@ void fmt::Formatter::Format() { | ||||
|     } | ||||
|  | ||||
|     if (*s++ != '}') | ||||
|       throw FormatError("single '{' encountered in format string"); | ||||
|       throw FormatError("unmatched '{' in format"); | ||||
|     start = s; | ||||
|  | ||||
|     // Format argument. | ||||
|     Arg &arg = args_[arg_index]; | ||||
|     switch (arg.type) { | ||||
|     case CHAR: | ||||
|       CheckFlags(flags); | ||||
|       if (width == -1 && precision == -1) { | ||||
|         buffer_.push_back(arg.int_value); | ||||
|         break; | ||||
| @@ -161,12 +188,13 @@ void fmt::Formatter::Format() { | ||||
|       FormatArg(arg_format, arg.double_value, width, precision); | ||||
|       break; | ||||
|     case LONG_DOUBLE: | ||||
|       *arg_format_ptr++ = 'l'; | ||||
|       *arg_format_ptr++ = 'L'; | ||||
|       *arg_format_ptr++ = 'g'; | ||||
|       *arg_format_ptr = '\0'; | ||||
|       FormatArg(arg_format, arg.long_double_value, width, precision); | ||||
|       break; | ||||
|     case STRING: | ||||
|       CheckFlags(flags); | ||||
|       if (width == -1 && precision == -1) { | ||||
|         const char *str = arg.string_value; | ||||
|         buffer_.insert(buffer_.end(), str, str + std::strlen(str)); | ||||
| @@ -177,17 +205,20 @@ void fmt::Formatter::Format() { | ||||
|       FormatArg(arg_format, arg.string_value, width, precision); | ||||
|       break; | ||||
|     case WSTRING: | ||||
|       CheckFlags(flags); | ||||
|       *arg_format_ptr++ = 'l'; | ||||
|       *arg_format_ptr++ = 's'; | ||||
|       *arg_format_ptr = '\0'; | ||||
|       FormatArg(arg_format, arg.wstring_value, width, precision); | ||||
|       break; | ||||
|     case POINTER: | ||||
|       CheckFlags(flags); | ||||
|       *arg_format_ptr++ = 'p'; | ||||
|       *arg_format_ptr = '\0'; | ||||
|       FormatArg(arg_format, arg.pointer_value, width, precision); | ||||
|       break; | ||||
|     case OTHER: | ||||
|       CheckFlags(flags); | ||||
|       (this->*arg.format)(arg.other_value); | ||||
|       break; | ||||
|     default: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user