ggformat.h (6061B)
1 /* 2 * ggformat v1.0 3 * 4 * Copyright (c) 2017 Michael Savage <mike@mikejsavage.co.uk> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #pragma once 20 21 #include <stdio.h> 22 #include <stdlib.h> 23 24 /* 25 * prototypes of the functions you should be calling 26 */ 27 28 /* 29 * `ggformat` writes at most `len` bytes to `buf`, and that always includes a 30 * null terminator. It returns the number of bytes that would have been written 31 * if `buf` were large enough, not including the null terminator, and can be 32 * larger than `len` (just like sprintf). 33 */ 34 template< typename... Rest > 35 size_t ggformat( char * buf, size_t len, const char * fmt, const Rest & ... rest ); 36 37 /* 38 * `ggprint_to_file` does what you would expect, and `ggprint` writes to 39 * standard output. Both return `true` on success, or `false` if the write 40 * fails. 41 */ 42 template< typename... Rest > 43 bool ggprint_to_file( FILE * file, const char * fmt, const Rest & ... rest ); 44 45 template< typename... Rest > 46 bool ggprint( const char * fmt, const Rest & ... rest ); 47 48 /* 49 * structures and functions used for formatting specific data types 50 */ 51 52 struct FormatOpts { 53 enum NumberFormat { DECIMAL, HEX, BINARY }; 54 55 int width = -1; 56 int precision = -1; 57 bool plus_sign = false; 58 bool left_align = false; 59 bool zero_pad = false; 60 NumberFormat number_format = DECIMAL; 61 }; 62 63 struct FormatBuffer { 64 FormatBuffer( char * b, size_t c ) { 65 buf = b; 66 capacity = c; 67 len = 0; 68 } 69 70 char * buf; 71 size_t capacity; 72 size_t len; 73 }; 74 75 /* 76 * format implementations for primitive types 77 */ 78 79 void format( FormatBuffer * fb, signed char x, const FormatOpts & opts ); 80 void format( FormatBuffer * fb, short x, const FormatOpts & opts ); 81 void format( FormatBuffer * fb, int x, const FormatOpts & opts ); 82 void format( FormatBuffer * fb, long x, const FormatOpts & opts ); 83 void format( FormatBuffer * fb, long long x, const FormatOpts & opts ); 84 void format( FormatBuffer * fb, unsigned char x, const FormatOpts & opts ); 85 void format( FormatBuffer * fb, unsigned short x, const FormatOpts & opts ); 86 void format( FormatBuffer * fb, unsigned int x, const FormatOpts & opts ); 87 void format( FormatBuffer * fb, unsigned long x, const FormatOpts & opts ); 88 void format( FormatBuffer * fb, unsigned long long x, const FormatOpts & opts ); 89 void format( FormatBuffer * fb, double x, const FormatOpts & opts ); 90 void format( FormatBuffer * fb, bool x, const FormatOpts & opts ); 91 void format( FormatBuffer * fb, char x, const FormatOpts & opts ); 92 void format( FormatBuffer * fb, const char * x, const FormatOpts & opts = FormatOpts() ); 93 94 /* 95 * nasty implementation details that have to go in the header 96 */ 97 98 #define GGFORMAT_ASSERT( p ) \ 99 do { \ 100 if( !( p ) ) { \ 101 fprintf( stderr, "assertion failed: %s\n", #p ); \ 102 abort(); \ 103 } \ 104 } while( 0 ) 105 106 #if defined( _MSC_VER ) 107 # define GGFORMAT_COMPILER_MSVC 1 108 #elif defined( __clang__ ) 109 # define GGFORMAT_COMPILER_CLANG 1 110 #elif defined( __GNUC__ ) 111 # define GGFORMAT_COMPILER_GCC 1 112 #else 113 # error new compiler 114 #endif 115 116 // this is optional but helps compile times 117 #if GGFORMAT_COMPILER_MSVC 118 # define GGFORMAT_DISABLE_OPTIMISATIONS() __pragma( optimize( "", off ) ) 119 # define GGFORMAT_ENABLE_OPTIMISATIONS() __pragma( optimize( "", on ) ) 120 #elif GGFORMAT_COMPILER_GCC 121 # define GGFORMAT_DISABLE_OPTIMISATIONS() \ 122 _Pragma( "GCC push_options" ) \ 123 _Pragma( "GCC optimize (\"O0\")" ) 124 # define GGFORMAT_ENABLE_OPTIMISATIONS() _Pragma( "GCC pop_options" ) 125 #elif GGFORMAT_COMPILER_CLANG 126 # define GGFORMAT_DISABLE_OPTIMISATIONS() _Pragma( "clang optimize off" ) 127 # define GGFORMAT_ENABLE_OPTIMISATIONS() _Pragma( "clang optimize on" ) 128 #else 129 # error new compiler 130 #endif 131 132 FormatOpts parse_formatopts( const char * fmt, size_t len ); 133 void ggformat_impl( FormatBuffer * fb, const char * fmt ); 134 bool ggformat_find( const char * str, size_t * start, size_t * one_past_end ); 135 void ggformat_literals( FormatBuffer * fb, const char * literals, size_t len ); 136 137 GGFORMAT_DISABLE_OPTIMISATIONS(); 138 139 template< typename T, typename... Rest > 140 void ggformat_impl( FormatBuffer * fb, const char * fmt, const T & first, const Rest & ... rest ) { 141 size_t start, one_past_end; 142 bool has_fmt = ggformat_find( fmt, &start, &one_past_end ); 143 GGFORMAT_ASSERT( has_fmt ); 144 145 ggformat_literals( fb, fmt, start ); 146 147 FormatOpts opts = parse_formatopts( fmt + start + 1, one_past_end - start - 1 ); 148 format( fb, first, opts ); 149 150 ggformat_impl( fb, fmt + one_past_end + 1, rest... ); 151 } 152 153 template< typename... Rest > 154 size_t ggformat( char * buf, size_t len, const char * fmt, const Rest & ... rest ) { 155 FormatBuffer fb( buf, len ); 156 ggformat_impl( &fb, fmt, rest... ); 157 return fb.len; 158 } 159 160 template< typename... Rest > 161 bool ggprint_to_file( FILE * file, const char * fmt, const Rest & ... rest ) { 162 char buf[ 4096 ]; 163 FormatBuffer fb( buf, sizeof( buf ) ); 164 ggformat_impl( &fb, fmt, rest... ); 165 166 if( fb.len < fb.capacity ) { 167 size_t written = fwrite( buf, 1, fb.len, file ); 168 return written == fb.len; 169 } 170 171 char * large_buf = ( char * ) malloc( fb.len + 1 ); 172 GGFORMAT_ASSERT( large_buf != NULL ); 173 FormatBuffer new_fb( large_buf, fb.len + 1 ); 174 ggformat_impl( &new_fb, fmt, rest... ); 175 size_t written = fwrite( large_buf, 1, fb.len, file ); 176 free( large_buf ); 177 178 return written == fb.len; 179 } 180 181 template< typename... Rest > 182 bool ggprint( const char * fmt, const Rest & ... rest ) { 183 return ggprint_to_file( stdout, fmt, rest... ); 184 } 185 186 GGFORMAT_ENABLE_OPTIMISATIONS();