commit 5955fd4cfeadce7ffa93592a2da5285bab7a2f1b
parent 5592e74188ce7249f5e49690e63016670fd0b576
Author: Michael Savage <mikejsavage@gmail.com>
Date: Wed, 23 Aug 2017 23:00:14 +0100
Forgot the header!
Diffstat:
ggformat.h | | | 173 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 173 insertions(+), 0 deletions(-)
diff --git a/ggformat.h b/ggformat.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2017 Michael Savage <mike@mikejsavage.co.uk>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#pragma once
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/*
+ * prototypes of the functions you should be calling
+ */
+
+template< typename... Rest >
+size_t ggformat( char * buf, size_t len, const char * fmt, Rest... rest );
+
+template< typename... Rest >
+bool ggprint_to_file( FILE * file, const char * fmt, Rest... rest );
+
+template< typename... Rest >
+bool ggprint( const char * fmt, Rest... rest );
+
+/*
+ * structures and functions used for formatting specific data types
+ */
+
+struct FormatOpts {
+ enum NumberFormat { DECIMAL, HEX, BINARY };
+
+ int width = -1;
+ int precision = -1;
+ bool plus_sign = false;
+ bool left_align = false;
+ bool zero_pad = false;
+ NumberFormat number_format = DECIMAL;
+};
+
+struct FormatBuffer {
+ FormatBuffer( char * b, size_t c ) {
+ buf = b;
+ capacity = c;
+ len = 0;
+ }
+
+ char * buf;
+ size_t capacity;
+ size_t len;
+};
+
+/*
+ * format implementations for primitive types
+ */
+
+void format( FormatBuffer * fb, signed char x, const FormatOpts & opts );
+void format( FormatBuffer * fb, short x, const FormatOpts & opts );
+void format( FormatBuffer * fb, int x, const FormatOpts & opts );
+void format( FormatBuffer * fb, long x, const FormatOpts & opts );
+void format( FormatBuffer * fb, long long x, const FormatOpts & opts );
+void format( FormatBuffer * fb, unsigned char x, const FormatOpts & opts );
+void format( FormatBuffer * fb, unsigned short x, const FormatOpts & opts );
+void format( FormatBuffer * fb, unsigned int x, const FormatOpts & opts );
+void format( FormatBuffer * fb, unsigned long x, const FormatOpts & opts );
+void format( FormatBuffer * fb, unsigned long long x, const FormatOpts & opts );
+void format( FormatBuffer * fb, double x, const FormatOpts & opts );
+void format( FormatBuffer * fb, bool x, const FormatOpts & opts );
+void format( FormatBuffer * fb, char x, const FormatOpts & opts );
+void format( FormatBuffer * fb, const char * x, const FormatOpts & opts = FormatOpts() );
+
+/*
+ * nasty implementation details that have to go in the header
+ */
+
+#define GGFORMAT_ASSERT( p ) \
+ do { \
+ if( !( p ) ) { \
+ fprintf( stderr, "assertion failed: %s\n", #p ); \
+ abort(); \
+ } \
+ } while( 0 )
+
+#if defined( _MSC_VER )
+# define COMPILER_MSVC 1
+#elif defined( __clang__ )
+# define COMPILER_CLANG 1
+#elif defined( __GNUC__ )
+# define COMPILER_GCC 1
+#else
+# error new compiler
+#endif
+
+// this is optional but helps compile times
+#if COMPILER_MSVC
+# define PRAGMA_DISABLE_OPTIMISATIONS() __pragma( optimize( "", off ) )
+# define PRAGMA_ENABLE_OPTIMISATIONS() __pragma( optimize( "", on ) )
+#elif COMPILER_GCC
+# define PRAGMA_DISABLE_OPTIMISATIONS() \
+ _Pragma( "GCC push_options" ) \
+ _Pragma( "GCC optimize (\"O0\")" )
+# define PRAGMA_ENABLE_OPTIMISATIONS() _Pragma( "GCC pop_options" )
+#elif COMPILER_CLANG
+# define PRAGMA_DISABLE_OPTIMISATIONS() _Pragma( "clang optimize off" )
+# define PRAGMA_ENABLE_OPTIMISATIONS() _Pragma( "clang optimize on" )
+#else
+# error new compiler
+#endif
+
+FormatOpts parse_formatopts( const char * fmt, size_t len );
+void ggformat_impl( FormatBuffer * fb, const char * fmt );
+bool ggformat_find( const char * str, size_t * start, size_t * one_past_end );
+void ggformat_literals( FormatBuffer * fb, const char * literals, size_t len );
+
+PRAGMA_DISABLE_OPTIMISATIONS();
+
+template< typename T, typename... Rest >
+void ggformat_impl( FormatBuffer * fb, const char * fmt, const T & first, Rest... rest ) {
+ size_t start, one_past_end;
+ bool has_fmt = ggformat_find( fmt, &start, &one_past_end );
+ GGFORMAT_ASSERT( has_fmt );
+
+ ggformat_literals( fb, fmt, start );
+
+ FormatOpts opts = parse_formatopts( fmt + start + 1, one_past_end - start - 1 );
+ format( fb, first, opts );
+
+ ggformat_impl( fb, fmt + one_past_end + 1, rest... );
+}
+
+template< typename... Rest >
+size_t ggformat( char * buf, size_t len, const char * fmt, Rest... rest ) {
+ FormatBuffer fb( buf, len );
+ ggformat_impl( &fb, fmt, rest... );
+ return fb.len;
+}
+
+template< typename... Rest >
+bool ggprint_to_file( FILE * file, const char * fmt, Rest... rest ) {
+ char buf[ 4096 ];
+ FormatBuffer fb( buf, sizeof( buf ) );
+ ggformat_impl( &fb, fmt, rest... );
+
+ if( fb.len < fb.capacity ) {
+ size_t written = fwrite( buf, 1, fb.len, file );
+ return written == fb.len;
+ }
+
+ char * large_buf = ( char * ) malloc( fb.len + 1 );
+ GGFORMAT_ASSERT( large_buf != NULL );
+ FormatBuffer new_fb( large_buf, fb.len + 1 );
+ ggformat_impl( &new_fb, fmt, rest... );
+ size_t written = fwrite( large_buf, 1, fb.len, file );
+ free( large_buf );
+
+ return written == fb.len;
+}
+
+template< typename... Rest >
+bool ggprint( const char * fmt, Rest... rest ) {
+ return ggprint_to_file( stdout, fmt, rest... );
+}
+
+PRAGMA_ENABLE_OPTIMISATIONS();