source: libcfa/src/iostream.cfa@ f45772e

Last change on this file since f45772e was 0860d9c, checked in by Michael Brooks <mlbrooks@…>, 2 years ago

Fix read-to-variable-length-string cases when internal buffer fills.

Also fix read-to-cstring ability to give no-exception cases when an entire buffer fills.

The added test cases run, and fail, when run against prior libcfa.
Doing so illustrates a CFA-string-level bug.
Doing so illustrates a C-string-level changed semantics.

At the CFA-string level, the bug was, when reading strings of just the right length,
what should be two reads ("abc" then "def") gets mashed into one ("abcdef").
These cases are clearly bugs because a test program that just echoes chuncks of delimeted input would do so inaccurately.
They're just hard to drive because the relevant chunk lengths are implementation-dependent, and sometimes big.

At the C-string level, the semantic change concerns when to throw the cstring_length exception.
By this change, make the original semantics,
"An exception means the maximum number of characters was read," into
"An exception means that if the buffer were larger, then more characters would have been read."

The added test cases cover the respective stop conditions for manipulator state "%s", include, exclude, getline, and getline/delimiter.

  • Property mode set to 100644
File size: 38.8 KB
RevLine 
[2fa0237]1
[86bd7c1f]2//
3// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
4//
5// The contents of this file are covered under the licence agreement in the
6// file "LICENCE" distributed with Cforall.
7//
[b117e0c]8// iostream.cfa --
[86bd7c1f]9//
[90c3b1c]10// Author : Peter A. Buhr
[86bd7c1f]11// Created On : Wed May 27 17:56:53 2015
[e24f13a]12// Last Modified By : Peter A. Buhr
[0f107e4]13// Last Modified On : Sat Sep 2 14:42:01 2023
14// Update Count : 1561
[86bd7c1f]15//
[51b73452]16
[58b6d1b]17#include "iostream.hfa"
[d3b7937]18
[51b73452]19#include <stdio.h>
[53a6c2a]20#include <stdbool.h> // true/false
[e59e663]21#include <stdint.h> // UINT64_MAX
[6026628]22#include <float.h> // DBL_DIG, LDBL_DIG
23#include <complex.h> // creal, cimag
[0860d9c]24#include <ctype.h> // isspace
[aa25216]25//#include <stdio.h>
26
[6026628]27extern "C" {
[44574f2]28extern size_t strlen (const char *__s) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1)));
[3c573e9]29extern int strcmp (const char *__s1, const char *__s2) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1, 2)));
30extern char *strcpy (char *__restrict __dest, const char *__restrict __src) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
31extern void *memcpy (void *__restrict __dest, const void *__restrict __src, size_t __n) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
[0860d9c]32extern char *strchr(const char *str, int ch);
[3c573e9]33} // extern "C"
[51b73452]34
[7cfef0d]35#include "math.hfa" // isfinite, floor, ceiling_div
36#include "bitmanip.hfa" // high1
[7fd71c7]37
[cce4648]38#pragma GCC visibility push(default)
[61c7239]39
[94d2544]40
[8d321f9]41// *********************************** ostream ***********************************
[61c7239]42
43
[85d8153]44forall( ostype & | basic_ostream( ostype ) ) {
[93c2e0a]45 ostype & ?|?( ostype & os, bool b ) {
[6c5d92f]46 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
[3ce0d440]47 fmt( os, "%s", b ? "true" : "false" );
48 return os;
49 } // ?|?
[94d2544]50 OSTYPE_VOID_IMPL( bool )
[51b73452]51
[200fcb3]52 ostype & ?|?( ostype & os, char c ) {
53 fmt( os, "%c", c );
[6c5d92f]54 if ( c == '\n' ) setNL$( os, true );
[f5d9c37]55 return nosep( os );
[200fcb3]56 } // ?|?
[94d2544]57 OSTYPE_VOID_IMPL( char )
[3ce0d440]58
[200fcb3]59 ostype & ?|?( ostype & os, signed char sc ) {
[6c5d92f]60 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
[0c51f9ad]61 fmt( os, "%'hhd", sc );
[3ce0d440]62 return os;
63 } // ?|?
[94d2544]64 OSTYPE_VOID_IMPL( signed char )
[3ce0d440]65
[200fcb3]66 ostype & ?|?( ostype & os, unsigned char usc ) {
[6c5d92f]67 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
[0c51f9ad]68 fmt( os, "%'hhu", usc );
[3ce0d440]69 return os;
70 } // ?|?
[94d2544]71 OSTYPE_VOID_IMPL( unsigned char )
[3ce0d440]72
73 ostype & ?|?( ostype & os, short int si ) {
[6c5d92f]74 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
[0c51f9ad]75 fmt( os, "%'hd", si );
[3ce0d440]76 return os;
77 } // ?|?
[94d2544]78 OSTYPE_VOID_IMPL( short int )
[3ce0d440]79
80 ostype & ?|?( ostype & os, unsigned short int usi ) {
[6c5d92f]81 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
[0c51f9ad]82 fmt( os, "%'hu", usi );
[3ce0d440]83 return os;
84 } // ?|?
[94d2544]85 OSTYPE_VOID_IMPL( unsigned short int )
[3ce0d440]86
87 ostype & ?|?( ostype & os, int i ) {
[6c5d92f]88 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
[0c51f9ad]89 fmt( os, "%'d", i );
[3ce0d440]90 return os;
91 } // ?|?
[94d2544]92 OSTYPE_VOID_IMPL( int )
[3ce0d440]93
94 ostype & ?|?( ostype & os, unsigned int ui ) {
[6c5d92f]95 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
[0c51f9ad]96 fmt( os, "%'u", ui );
[3ce0d440]97 return os;
98 } // ?|?
[94d2544]99 OSTYPE_VOID_IMPL( unsigned int )
[3ce0d440]100
101 ostype & ?|?( ostype & os, long int li ) {
[6c5d92f]102 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
[0c51f9ad]103 fmt( os, "%'ld", li );
[3ce0d440]104 return os;
105 } // ?|?
[94d2544]106 OSTYPE_VOID_IMPL( long int )
[3ce0d440]107
108 ostype & ?|?( ostype & os, unsigned long int uli ) {
[6c5d92f]109 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
[0c51f9ad]110 fmt( os, "%'lu", uli );
[3ce0d440]111 return os;
112 } // ?|?
[94d2544]113 OSTYPE_VOID_IMPL( unsigned long int )
[3ce0d440]114
115 ostype & ?|?( ostype & os, long long int lli ) {
[6c5d92f]116 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
[0c51f9ad]117 fmt( os, "%'lld", lli );
[3ce0d440]118 return os;
119 } // ?|?
[94d2544]120 OSTYPE_VOID_IMPL( long long int )
[3ce0d440]121
122 ostype & ?|?( ostype & os, unsigned long long int ulli ) {
[6c5d92f]123 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
[0c51f9ad]124 fmt( os, "%'llu", ulli );
[3ce0d440]125 return os;
126 } // ?|?
[94d2544]127 OSTYPE_VOID_IMPL( unsigned long long int )
[3ce0d440]128
[ef3ac46]129 #if defined( __SIZEOF_INT128__ )
[bd5b443]130 // UINT64_MAX 18_446_744_073_709_551_615_ULL
131 #define P10_UINT64 10_000_000_000_000_000_000_ULL // 19 zeroes
132
[ffa48a8]133 static inline void base10_128( ostype & os, unsigned int128 val ) {
[ef3ac46]134 #if defined(__GNUC__) && __GNUC_PREREQ(7,0) // gcc version >= 7
[2c60c644]135 if ( val > P10_UINT64 ) {
[ef3ac46]136 #else
[ffa48a8]137 if ( (uint64_t)(val >> 64) != 0 || (uint64_t)val > P10_UINT64 ) { // patch gcc 5 & 6 -O3 bug
[ef3ac46]138 #endif // __GNUC_PREREQ(7,0)
[bd5b443]139 base10_128( os, val / P10_UINT64 ); // recursive
140 fmt( os, "%.19lu", (uint64_t)(val % P10_UINT64) );
141 } else {
142 fmt( os, "%lu", (uint64_t)val );
143 } // if
144 } // base10_128
145
[ffa48a8]146 static inline void base10_128( ostype & os, int128 val ) {
[bd5b443]147 if ( val < 0 ) {
148 fmt( os, "-" ); // leading negative sign
149 val = -val;
150 } // if
151 base10_128( os, (unsigned int128)val ); // print zero/positive value
152 } // base10_128
153
154 ostype & ?|?( ostype & os, int128 llli ) {
[6c5d92f]155 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
[bd5b443]156 base10_128( os, llli );
157 return os;
158 } // ?|?
[94d2544]159 OSTYPE_VOID_IMPL( int128 )
[bd5b443]160
161 ostype & ?|?( ostype & os, unsigned int128 ullli ) {
[6c5d92f]162 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
[bd5b443]163 base10_128( os, ullli );
164 return os;
165 } // ?|?
[94d2544]166 OSTYPE_VOID_IMPL( unsigned int128 )
[ef3ac46]167 #endif // __SIZEOF_INT128__
[bd5b443]168
[94d2544]169 #define PRINT_WITH_DP( os, format, val, ... ) \
[e63326b]170 { \
171 enum { size = 48 }; \
172 char buf[size]; \
173 int len = snprintf( buf, size, format, ##__VA_ARGS__, val ); \
174 fmt( os, "%s", buf ); \
[fd4c009]175 if ( isfinite( val ) ) { /* if number, print decimal point when no fraction or exponent */ \
[f6a4917]176 for ( i; 0 ~ @ ) { \
[e63326b]177 if ( i == len ) { fmt( os, "." ); break; } \
[09a767e]178 if ( buf[i] == '.' || buf[i] == 'e' || buf[i] == 'E' || \
179 buf[i] == 'p' || buf[i] == 'P' ) break; /* decimal point or scientific ? */ \
[e63326b]180 } /* for */ \
181 } /* if */ \
182 }
[b2ac656]183
[3ce0d440]184 ostype & ?|?( ostype & os, float f ) {
[6c5d92f]185 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
[94d2544]186 PRINT_WITH_DP( os, "%'g", f );
[3ce0d440]187 return os;
188 } // ?|?
[94d2544]189 OSTYPE_VOID_IMPL( float )
[3ce0d440]190
191 ostype & ?|?( ostype & os, double d ) {
[6c5d92f]192 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
[94d2544]193 PRINT_WITH_DP( os, "%'.*lg", d, DBL_DIG );
[3ce0d440]194 return os;
195 } // ?|?
[94d2544]196 OSTYPE_VOID_IMPL( double )
[3ce0d440]197
198 ostype & ?|?( ostype & os, long double ld ) {
[6c5d92f]199 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
[94d2544]200 PRINT_WITH_DP( os, "%'.*Lg", ld, LDBL_DIG );
[3ce0d440]201 return os;
202 } // ?|?
[94d2544]203 OSTYPE_VOID_IMPL( long double )
[3ce0d440]204
205 ostype & ?|?( ostype & os, float _Complex fc ) {
[6c5d92f]206 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
[b2ac656]207// os | crealf( fc ) | nonl;
[94d2544]208 PRINT_WITH_DP( os, "%'g", crealf( fc ) );
209 PRINT_WITH_DP( os, "%'+g", cimagf( fc ) );
[3c5dee4]210 fmt( os, "i" );
[3ce0d440]211 return os;
212 } // ?|?
[94d2544]213 OSTYPE_VOID_IMPL( float _Complex )
[3ce0d440]214
215 ostype & ?|?( ostype & os, double _Complex dc ) {
[6c5d92f]216 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
[b2ac656]217// os | creal( dc ) | nonl;
[94d2544]218 PRINT_WITH_DP( os, "%'.*lg", creal( dc ), DBL_DIG );
219 PRINT_WITH_DP( os, "%'+.*lg", cimag( dc ), DBL_DIG );
[3c5dee4]220 fmt( os, "i" );
[3ce0d440]221 return os;
222 } // ?|?
[94d2544]223 OSTYPE_VOID_IMPL( double _Complex )
[3ce0d440]224
225 ostype & ?|?( ostype & os, long double _Complex ldc ) {
[6c5d92f]226 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
[b2ac656]227// os | creall( ldc ) || nonl;
[94d2544]228 PRINT_WITH_DP( os, "%'.*Lg", creall( ldc ), LDBL_DIG );
229 PRINT_WITH_DP( os, "%'+.*Lg", cimagl( ldc ), LDBL_DIG );
[3c5dee4]230 fmt( os, "i" );
[3ce0d440]231 return os;
232 } // ?|?
[94d2544]233 OSTYPE_VOID_IMPL( long double _Complex )
[3ce0d440]234
[e474cf09]235 ostype & ?|?( ostype & os, const char s[] ) {
[3ce0d440]236 enum { Open = 1, Close, OpenClose };
[28c2933d]237 static const unsigned char mask[256] @= { // 256 covers all Latin-1 characters
[3ce0d440]238 // opening delimiters, no space after
239 ['('] : Open, ['['] : Open, ['{'] : Open,
[ad72c8b]240 ['='] : Open, ['$'] : Open, [(unsigned char)'£'] : Open, [(unsigned char)'¥'] : Open,
241 [(unsigned char)'¡'] : Open, [(unsigned char)'¿'] : Open, [(unsigned char)'«'] : Open,
[3ce0d440]242 // closing delimiters, no space before
243 [','] : Close, ['.'] : Close, [';'] : Close, ['!'] : Close, ['?'] : Close,
[ad72c8b]244 ['%'] : Close, [(unsigned char)'¢'] : Close, [(unsigned char)'»'] : Close,
[3ce0d440]245 [')'] : Close, [']'] : Close, ['}'] : Close,
246 // opening-closing delimiters, no space before or after
247 ['\''] : OpenClose, ['`'] : OpenClose, ['"'] : OpenClose, [':'] : OpenClose,
248 [' '] : OpenClose, ['\f'] : OpenClose, ['\n'] : OpenClose, ['\r'] : OpenClose, ['\t'] : OpenClose, ['\v'] : OpenClose, // isspace
249 }; // mask
250
[6a33e40]251 if ( s == 0p ) { fmt( os, "%s", "0p" ); return os; } // null pointer
[f5d9c37]252 if ( s[0] == '\0' ) { nosep( os ); return os; } // null string => no leading/trailing separator
[3ce0d440]253
254 // first character IS NOT spacing or closing punctuation => add left separator
[e474cf09]255 unsigned char ch = s[0]; // must make unsigned
[6c5d92f]256 if ( sepPrt$( os ) && mask[ ch ] != Close && mask[ ch ] != OpenClose ) {
257 fmt( os, "%s", sepGetCur$( os ) );
[3ce0d440]258 } // if
259
260 // if string starts line, must reset to determine open state because separator is off
[6c5d92f]261 sepReset$( os ); // reset separator
[3ce0d440]262
263 // last character IS spacing or opening punctuation => turn off separator for next item
[85d8153]264 int len = strlen( s );
[e474cf09]265 ch = s[len - 1]; // must make unsigned
[85d8153]266 fmt( os, "%s", s ); // fmt resets seperator, but reset it again
[6c5d92f]267 if ( sepPrt$( os ) && mask[ ch ] != Open && mask[ ch ] != OpenClose ) {
[f5d9c37]268 sep( os );
[3ce0d440]269 } else {
[f5d9c37]270 nosep( os );
[3ce0d440]271 } // if
[6c5d92f]272 if ( ch == '\n' ) setNL$( os, true ); // check *AFTER* sepPrt$ call above as it resets NL flag
[85d8153]273 return os;
274// return write( os, s, len );
[3ce0d440]275 } // ?|?
[94d2544]276 OSTYPE_VOID_IMPL( const char * )
[3ce0d440]277
[e7a8f65]278// ostype & ?|?( ostype & os, const char16_t s[] ) {
[6c5d92f]279// if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
[e474cf09]280// fmt( os, "%ls", s );
[3ce0d440]281// return os;
282// } // ?|?
[d3b7937]283
[3ce0d440]284// #if ! ( __ARM_ARCH_ISA_ARM == 1 && __ARM_32BIT_STATE == 1 ) // char32_t == wchar_t => ambiguous
[e7a8f65]285// ostype & ?|?( ostype & os, const char32_t s[] ) {
[6c5d92f]286// if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
[e474cf09]287// fmt( os, "%ls", s );
[3ce0d440]288// return os;
289// } // ?|?
290// #endif // ! ( __ARM_ARCH_ISA_ARM == 1 && __ARM_32BIT_STATE == 1 )
[d3b7937]291
[e7a8f65]292// ostype & ?|?( ostype & os, const wchar_t s[] ) {
[6c5d92f]293// if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
[e474cf09]294// fmt( os, "%ls", s );
[3ce0d440]295// return os;
296// } // ?|?
297
298 ostype & ?|?( ostype & os, const void * p ) {
[6c5d92f]299 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
[3ce0d440]300 fmt( os, "%p", p );
301 return os;
302 } // ?|?
[94d2544]303 OSTYPE_VOID_IMPL( const void * )
[3ce0d440]304
305 // manipulators
306 ostype & ?|?( ostype & os, ostype & (* manip)( ostype & ) ) {
[e474cf09]307 return manip( os );
[200fcb3]308 } // ?|?
309 void ?|?( ostype & os, ostype & (* manip)( ostype & ) ) {
[e474cf09]310 manip( os );
[6c5d92f]311 if ( getPrt$( os ) ) ends( os ); // something printed ?
312 setPrt$( os, false ); // turn off
[3ce0d440]313 } // ?|?
314
[200fcb3]315 ostype & nl( ostype & os ) {
[9d362a0]316 (ostype &)(os | '\n');
[6c5d92f]317 setPrt$( os, false ); // turn off
318 setNL$( os, true );
[f5d9c37]319 return nosep( os ); // prepare for next line
[200fcb3]320 } // nl
321
322 ostype & nonl( ostype & os ) {
[6c5d92f]323 setPrt$( os, false ); // turn off
[3ce0d440]324 return os;
[200fcb3]325 } // nonl
[3ce0d440]326
[f5d9c37]327 ostype & nlOn( ostype & os ) {
328 nlOn( os ); // call void returning
[3ce0d440]329 return os;
[f5d9c37]330 } // nlOn
[3ce0d440]331
[f5d9c37]332 ostype & nlOff( ostype & os ) {
333 nlOff( os ); // call void returning
[3ce0d440]334 return os;
[f5d9c37]335 } // nlOff
[6de9f4a]336
[f5d9c37]337 ostype & sepVal( ostype & os ) {
338 return (ostype &)(os | sepGet( os ));
339 } // sepVal
340
341 ostype & sepTupleVal( ostype & os ) {
342 return os | sepGetTuple( os );
343 } // sepTupleVal
344
345 ostype & sep( ostype & os ) {
346 sep( os ); // call void returning
[3ce0d440]347 return os;
[f5d9c37]348 } // sep
[b6dc097]349
[f5d9c37]350 ostype & nosep( ostype & os ) {
351 nosep( os ); // call void returning
[3ce0d440]352 return os;
[f5d9c37]353 } // nosep
[cf16f94]354
[f5d9c37]355 ostype & sepOn( ostype & os ) {
356 sepOn( os ); // call void returning
[200fcb3]357 return os;
[f5d9c37]358 } // sepOn
[200fcb3]359
[f5d9c37]360 ostype & sepOff( ostype & os ) {
361 sepOff( os ); // call void returning
[200fcb3]362 return os;
[f5d9c37]363 } // sepOff
[85d8153]364} // distribution
[e474cf09]365
[c443d1d]366// tuples
[fd54fef]367forall( ostype &, T, Params... | writeable( T, ostype ) | { ostype & ?|?( ostype &, Params ); } ) {
[200fcb3]368 ostype & ?|?( ostype & os, T arg, Params rest ) {
[9d362a0]369 (ostype &)(os | arg); // print first argument
[6c5d92f]370 sepSetCur$( os, sepGetTuple( os ) ); // switch to tuple separator
[9d362a0]371 (ostype &)(os | rest); // print remaining arguments
[6c5d92f]372 sepSetCur$( os, sepGet( os ) ); // switch to regular separator
[200fcb3]373 return os;
374 } // ?|?
375 void ?|?( ostype & os, T arg, Params rest ) {
[65240bb]376 // (ostype &)(?|?( os, arg, rest )); ends( os );
[9d362a0]377 (ostype &)(os | arg); // print first argument
[6c5d92f]378 sepSetCur$( os, sepGetTuple( os ) ); // switch to tuple separator
[9d362a0]379 (ostype &)(os | rest); // print remaining arguments
[6c5d92f]380 sepSetCur$( os, sepGet( os ) ); // switch to regular separator
[65240bb]381 ends( os );
[200fcb3]382 } // ?|?
383} // distribution
[c443d1d]384
[61c7239]385// writes the range [begin, end) to the given stream
[fd54fef]386forall( ostype &, elt_type | writeable( elt_type, ostype ), iterator_type | iterator( iterator_type, elt_type ) ) {
[61c7239]387 void write( iterator_type begin, iterator_type end, ostype & os ) {
388 void print( elt_type i ) { os | i; }
389 for_each( begin, end, print );
390 } // ?|?
391
392 void write_reverse( iterator_type begin, iterator_type end, ostype & os ) {
393 void print( elt_type i ) { os | i; }
394 for_each_reverse( begin, end, print );
395 } // ?|?
396} // distribution
397
[8d321f9]398// *********************************** manipulators ***********************************
[3c573e9]399
[8d321f9]400// *********************************** integral ***********************************
[3c573e9]401
402static const char * shortbin[] = { "0", "1", "10", "11", "100", "101", "110", "111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111" };
403static const char * longbin[] = { "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111" };
404
405// Default prefix for non-decimal prints is 0b, 0, 0x.
[94d2544]406#define INTEGRAL_FMT_IMPL( T, IFMTNP, IFMTP ) \
[85d8153]407forall( ostype & | basic_ostream( ostype ) ) { \
[3c573e9]408 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \
[6c5d92f]409 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); \
[3c573e9]410\
411 if ( f.base == 'b' || f.base == 'B' ) { /* bespoke binary format */ \
[7fd71c7]412 int bits = high1( f.val ); /* position of most significant bit */ \
413 if ( bits == 0 ) bits = 1; /* 0 value => force one bit to print */ \
[c7978c0]414 int spaces; \
[3c573e9]415 if ( ! f.flags.left ) { /* right justified ? */ \
416 /* Note, base prefix then zero padding or spacing then prefix. */ \
[c7978c0]417 if ( f.flags.pc ) { \
418 spaces = f.wd - f.pc; \
419 if ( ! f.flags.nobsdp ) { spaces -= 2; } /* base prefix takes space */ \
420 if ( spaces > 0 ) fmt( os, "%*s", spaces, " " ); /* space pad */ \
[3c573e9]421 if ( ! f.flags.nobsdp ) { fmt( os, "0%c", f.base ); } \
[c7978c0]422 spaces = f.pc - bits; \
[3c573e9]423 if ( spaces > 0 ) fmt( os, "%0*d", spaces, 0 ); /* zero pad */ \
424 } else { \
[c7978c0]425 spaces = f.wd - bits; \
426 if ( ! f.flags.nobsdp ) { spaces -= 2; } /* base prefix takes space */ \
427 if ( f.flags.pad0 ) { \
428 if ( ! f.flags.nobsdp ) { fmt( os, "0%c", f.base ); } \
429 if ( spaces > 0 ) fmt( os, "%0*d", spaces, 0 ); /* zero pad */ \
430 } else { \
431 if ( spaces > 0 ) fmt( os, "%*s", spaces, " " ); /* space pad */ \
432 if ( ! f.flags.nobsdp ) { fmt( os, "0%c", f.base ); } \
433 } /* if */ \
434 } /* if */ \
435 } else { \
436 if ( ! f.flags.nobsdp ) fmt( os, "0%c", f.base ); \
437 if ( f.flags.pc ) { \
438 spaces = f.pc - bits; \
439 if ( spaces > 0 ) fmt( os, "%0*d", spaces, 0 ); /* zero pad */ \
440 spaces = f.wd - f.pc; \
441 } else { /* pad0 flag ignored with left flag */ \
442 spaces = f.wd - bits; \
[3c573e9]443 } /* if */ \
[c7978c0]444 if ( ! f.flags.nobsdp ) { spaces -= 2; } /* base prefix takes space */ \
[3c573e9]445 } /* if */ \
[fd4d3017]446 int shift = floor( bits - 1, 4 ); \
[3c573e9]447 typeof( f.val ) temp = f.val; \
448 fmt( os, "%s", shortbin[(temp >> shift) & 0xf] ); \
449 for () { \
450 shift -= 4; \
451 if ( shift < 0 ) break; \
452 temp = f.val; \
453 fmt( os, "%s", longbin[(temp >> shift) & 0xf] ); \
454 } /* for */ \
455 if ( f.flags.left && spaces > 0 ) fmt( os, "%*s", spaces, " " ); \
456 return os; \
[2c60c644]457 } /* if */ \
[3c573e9]458\
459 char fmtstr[sizeof(IFMTP)]; /* sizeof includes '\0' */ \
460 if ( ! f.flags.pc ) memcpy( &fmtstr, IFMTNP, sizeof(IFMTNP) ); \
461 else memcpy( &fmtstr, IFMTP, sizeof(IFMTP) ); \
[0c51f9ad]462 int star = 5; /* position before first '*' */ \
[3c573e9]463\
464 /* Insert flags into spaces before '*', from right to left. */ \
465 if ( ! f.flags.nobsdp ) { fmtstr[star] = '#'; star -= 1; } \
466 if ( f.flags.left ) { fmtstr[star] = '-'; star -= 1; } \
[2c60c644]467 if ( f.flags.sign ) { fmtstr[star] = '+'; star -= 1; } \
[3c573e9]468 if ( f.flags.pad0 && ! f.flags.pc ) { fmtstr[star] = '0'; star -= 1; } \
[0c51f9ad]469 fmtstr[star] = '\''; star -= 1; /* locale */ \
[3c573e9]470 fmtstr[star] = '%'; \
[09a767e]471\
472 /* Special case printing 0 in hexadecimal as printf does not put the base. */ \
473 if ( (f.base == 'x' | f.base == 'X') && ! f.flags.nobsdp && f.val == 0 ) { \
474 fmt( os, f.base == 'x' ? "0x" : "0X" ); \
475 f.wd -= 2; \
476 if ( f.wd < 0 ) f.wd = 1; \
477 } /* if */ \
[3c573e9]478\
479 if ( ! f.flags.pc ) { /* no precision */ \
480 fmtstr[sizeof(IFMTNP)-2] = f.base; /* sizeof includes '\0' */ \
[2c60c644]481 /* printf( "%s %c\n", &fmtstr[star], f.base ); */ \
[3c573e9]482 fmt( os, &fmtstr[star], f.wd, f.val ); \
483 } else { /* precision */ \
484 fmtstr[sizeof(IFMTP)-2] = f.base; /* sizeof includes '\0' */ \
[2c60c644]485 /* printf( "%s %c\n", &fmtstr[star], f.base ); */ \
[3c573e9]486 fmt( os, &fmtstr[star], f.wd, f.pc, f.val ); \
487 } /* if */ \
488 return os; \
489 } /* ?|? */ \
[94d2544]490 OSTYPE_VOID_IMPL( _Ostream_Manip(T) ) \
[3c573e9]491} // distribution
492
[94d2544]493INTEGRAL_FMT_IMPL( signed char, " *hh ", " *.*hh " )
494INTEGRAL_FMT_IMPL( unsigned char, " *hh ", " *.*hh " )
495INTEGRAL_FMT_IMPL( signed short int, " *h ", " *.*h " )
496INTEGRAL_FMT_IMPL( unsigned short int, " *h ", " *.*h " )
497INTEGRAL_FMT_IMPL( signed int, " * ", " *.* " )
498INTEGRAL_FMT_IMPL( unsigned int, " * ", " *.* " )
499INTEGRAL_FMT_IMPL( signed long int, " *l ", " *.*l " )
500INTEGRAL_FMT_IMPL( unsigned long int, " *l ", " *.*l " )
501INTEGRAL_FMT_IMPL( signed long long int, " *ll ", " *.*ll " )
502INTEGRAL_FMT_IMPL( unsigned long long int, " *ll ", " *.*ll " )
[bd5b443]503
[2c60c644]504
505#if defined( __SIZEOF_INT128__ )
506// Default prefix for non-decimal prints is 0b, 0, 0x.
[85d8153]507forall( ostype & | basic_ostream( ostype ) )
[2c60c644]508static inline void base_128( ostype & os, unsigned int128 val, unsigned int128 power, _Ostream_Manip(uint64_t) & f, unsigned int maxdig, unsigned int bits, unsigned int cnt = 0 ) {
509 int wd = 1; // f.wd is never 0 because 0 implies left-pad
510 if ( val > power ) { // subdivide value into printable 64-bit values
511 base_128( os, val / power, power, f, maxdig, bits, cnt + 1 ); // recursive
512 f.val = val % power;
[ac9ba12]513 if ( cnt == 1 && f.flags.left ) { wd = f.wd; f.wd = maxdig; } // copy f.wd and reset for printing middle chunk
[7d9bbef]514 // printf( "R val:%#lx(%lu) wd:%u pc:%u base:%c neg:%d pc:%d left:%d nobsdp:%d sign:%d pad0:%d\n",
515 // f.val, f.val, f.wd, f.pc, f.base, f.flags.neg, f.flags.pc, f.flags.left, f.flags.nobsdp, f.flags.sign, f.flags.pad0 );
[2c60c644]516 (ostype &)(os | f);
517 if ( cnt == 1 ) {
[ac9ba12]518 if ( f.flags.left ) { wd -= maxdig; f.wd = wd < 0 ? 1 : wd; } // update and restore f.wd for printing end chunk
[f5d9c37]519 nosep( os ); // no seperator between chunks
[2c60c644]520 } // if
[ac9ba12]521 } else { // print start chunk
[2c60c644]522 f.val = val;
523 // f.pc is unsigned => use wd
524 if ( f.flags.pc && f.pc > maxdig * cnt ) { wd = f.pc - maxdig * cnt; f.pc = wd < 0 ? 0 : wd; }
525 else { f.flags.pc = false; f.pc = 0; }
526
527 if ( ! f.flags.left ) { // right justify
528 wd = f.wd - maxdig * cnt;
529 f.wd = wd < 0 ? 1 : wd;
530 wd = maxdig;
531 } else { // left justify
532 if ( cnt != 0 ) { // value >= 2^64 ?
533 unsigned int dig, bs = 0;
[ac9ba12]534 // compute size of prefix digits and base
[2c60c644]535 if ( f.base == 'd' || f.base == 'u' ) { // no base prefix
536 dig = ceil( log10( f.val ) ); // use floating-point
537 if ( f.base == 'd' && (f.flags.neg || f.flags.sign) ) bs = 1; // sign ?
538 } else {
[53fd995]539 dig = ceiling_div( high1( f.val ), bits );
[2c60c644]540 if ( ! f.flags.nobsdp ) { // base prefix ?
541 if ( f.base == 'o' ) {
542 // 0 prefix for octal is not added for precision with leading zero
543 if ( f.pc <= dig ) bs = 1; // 1 character prefix
544 } else bs = 2; // 2 character prefix
545 } // if
546 } // if
547 wd = f.wd - (f.pc > dig ? f.pc : dig) - bs; // precision > leading digits ?
548 if ( wd < 0 ) wd = 1;
549 f.wd = 1;
550 } // if
551 // all manipulators handled implicitly for value < 2^64
552 } // if
553 // prior checks ensure wd not negative
554
555 if ( f.flags.neg ) f.val = -f.val;
[7d9bbef]556 // printf( "L val:%#lx(%lu) wd:%u pc:%u base:%c neg:%d pc:%d left:%d nobsdp:%d sign:%d pad0:%d\n",
557 // f.val, f.val, f.wd, f.pc, f.base, f.flags.neg, f.flags.pc, f.flags.left, f.flags.nobsdp, f.flags.sign, f.flags.pad0 );
[2c60c644]558 (ostype &)(os | f);
559
[ac9ba12]560 // remaining middle and end chunks are padded with 0s on the left
[2c60c644]561 if ( ! f.flags.left ) { f.flags.pad0 = true; f.flags.pc = false; } // left pad with 0s
562 else { f.pc = maxdig; f.flags.pc = true; } // left pad with precision
[ac9ba12]563
[f5d9c37]564 if ( cnt != 0 ) nosep( os ); // no seperator between chunks
[ac9ba12]565 f.wd = wd; // reset f.wd for next chunk
566 f.flags.sign = false; // no leading +/- sign
567 f.flags.nobsdp = true; // no leading base prefix
[2c60c644]568 } // if
569} // base_128
570
[94d2544]571#define INTEGRAL_FMT_IMPL128( T ) \
[85d8153]572forall( ostype & | basic_ostream( ostype ) ) { \
[2c60c644]573 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \
574 _Ostream_Manip(uint64_t) fmt; \
575 fmt.[wd, pc, base, all] = f.[wd, pc, base, all]; \
576 if ( f.base == 'b' | f.base == 'B' ) { \
577 base_128( os, f.val, (unsigned int128)1 << 64, fmt, 64, 1 ); \
578 } else if ( f.base == 'o' ) { \
579 base_128( os, f.val, (unsigned int128)1 << 63, fmt, 21, 3 ); \
580 } else if ( f.base == 'd' || f.base == 'u' ) { \
581 if ( f.base == 'd' && f.val < 0 ) { f.val = -f.val; fmt.flags.neg = true; } \
582 base_128( os, f.val, (unsigned int128)10_000_000_000_000_000_000UL, fmt, 19, 0 ); \
583 } else { \
584 base_128( os, f.val, (unsigned int128)1 << 64, fmt, 16, 4 ); \
585 } /* if */ \
586 return os; \
587 } /* ?|? */ \
[94d2544]588 OSTYPE_VOID_IMPL( _Ostream_Manip(T) ) \
[2c60c644]589} // distribution
590
[94d2544]591INTEGRAL_FMT_IMPL128( int128 )
592INTEGRAL_FMT_IMPL128( unsigned int128 )
[2c60c644]593#endif // __SIZEOF_INT128__
[bd5b443]594
[8d321f9]595// *********************************** floating point ***********************************
[3c573e9]596
[fd4c009]597static const char *suffixes[] = {
598 "y", "z", "a", "f", "p", "n", "u", "m", "",
599 "K", "M", "G", "T", "P", "E", "Z", "Y"
600};
601#define SUFFIXES_START (-24) /* Smallest power for which there is a suffix defined. */
602#define SUFFIXES_END (SUFFIXES_START + (int)((sizeof(suffixes) / sizeof(char *) - 1) * 3))
603
[94d2544]604#define PRINT_WITH_DP2( os, format, ... ) \
[3c573e9]605 { \
[fd4c009]606 if ( ! f.flags.eng ) { \
607 len = snprintf( buf, size, format, ##__VA_ARGS__ ); \
[bcbc7e4]608 if ( isfinite( f.val ) && ! f.flags.nobsdp ) { /* if number, print decimal point when no fraction or exponent */ \
[09a767e]609 for ( i = 0; i < len && buf[i] != '.' && buf[i] != 'e' && buf[i] != 'E' && \
[bcbc7e4]610 buf[i] != 'p' && buf[i] != 'P'; i += 1 ); /* decimal point or scientific ? */ \
[fd4c009]611 if ( i == len ) { \
612 if ( ! f.flags.left ) { \
613 buf[i] = '.'; buf[i + 1] = '\0'; \
614 if ( buf[0] == ' ' ) bufbeg = 1; /* decimal point within width */ \
615 } else { \
616 for ( i = 0; i < len && buf[i] != ' '; i += 1 ); /* trailing blank ? */ \
617 buf[i] = '.'; \
618 if ( i == len ) buf[i + 1] = '\0'; \
619 } /* if */ \
[3c573e9]620 } /* if */ \
621 } /* if */ \
[fd4c009]622 } else { \
623 int exp10, len2; \
624 eng( f.val, f.pc, exp10 ); /* changes arguments */ \
[b1e614f4]625 /* printf( "%g %d %d %d %s\n", f.val, f.wd, f.pc, exp10, format ); */ \
[fd4c009]626 if ( ! f.flags.left && f.wd > 1 ) { \
[b1e614f4]627 /* Exponent size: 'e', optional minus sign, number of digits: log10(0) => undefined */ \
628 f.wd -= 1 + (exp10 < 0 ? 1 : 0) + lrint( floor( exp10 == 0 ? 0 : log10( abs( exp10 ) ) ) ) + 1; \
[fd4c009]629 if ( f.wd < 1 ) f.wd = 1; \
630 } /* if */ \
631 len = snprintf( buf, size, format, ##__VA_ARGS__ ); \
632 if ( f.flags.left ) { \
633 for ( len -= 1; len > 0 && buf[len] == ' '; len -= 1 ); \
634 len += 1; \
635 } /* if */ \
636 if ( ! f.flags.nobsdp || (exp10 < SUFFIXES_START) || (exp10 > SUFFIXES_END) ) { \
[fb907d3]637 len2 = snprintf( &buf[len], size - len, "e%d", (int)exp10 /* ambiguity with function exp10 */ ); \
[fd4c009]638 } else { \
639 len2 = snprintf( &buf[len], size - len, "%s", suffixes[(exp10 - SUFFIXES_START) / 3] ); \
640 } /* if */ \
641 if ( f.flags.left && len + len2 < f.wd ) buf[len + len2] = ' '; \
[3c573e9]642 } /* if */ \
643 fmt( os, "%s", &buf[bufbeg] ); \
644 }
645
[94d2544]646#define FLOATING_POINT_FMT_IMPL( T, DFMTNP, DFMTP ) \
[85d8153]647forall( ostype & | basic_ostream( ostype ) ) { \
[fd4c009]648 static void eng( T &value, int & pc, int & exp10 ) { \
649 exp10 = lrint( floor( log10( abs( value ) ) ) ); /* round to desired precision */ \
650 if ( exp10 < 0 ) exp10 -= 2; \
651 exp10 = floor( exp10, 3 ); \
652 value *= pow( 10.0, -exp10 ); \
653 if ( pc <= 3 ) pc = 3; \
654 } /* eng */ \
655\
[3c573e9]656 ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \
[fd4c009]657 enum { size = 48 }; \
658 char buf[size]; \
659 int bufbeg = 0, i, len; \
660\
[6c5d92f]661 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) ); \
[fd4c009]662 char fmtstr[sizeof(DFMTP) + 8]; /* sizeof includes '\0' */ \
[3c573e9]663 if ( ! f.flags.pc ) memcpy( &fmtstr, DFMTNP, sizeof(DFMTNP) ); \
664 else memcpy( &fmtstr, DFMTP, sizeof(DFMTP) ); \
[b5f17e14]665 int star = 5; /* position before first '*' */ \
[3c573e9]666\
667 /* Insert flags into spaces before '*', from right to left. */ \
668 if ( f.flags.left ) { fmtstr[star] = '-'; star -= 1; } \
669 if ( f.flags.sign ) { fmtstr[star] = '+'; star -= 1; } \
670 if ( f.flags.pad0 ) { fmtstr[star] = '0'; star -= 1; } \
[b5f17e14]671 fmtstr[star] = '\''; star -= 1; /* locale */ \
[3c573e9]672 fmtstr[star] = '%'; \
673\
674 if ( ! f.flags.pc ) { /* no precision */ \
675 fmtstr[sizeof(DFMTNP)-2] = f.base; /* sizeof includes '\0' */ \
[b1e614f4]676 /* printf( "%g %d %s\n", f.val, f.wd, &fmtstr[star] ); */ \
[94d2544]677 PRINT_WITH_DP2( os, &fmtstr[star], f.wd, f.val ) \
[3c573e9]678 } else { /* precision */ \
679 fmtstr[sizeof(DFMTP)-2] = f.base; /* sizeof includes '\0' */ \
680 /* printf( "%g %d %d %s\n", f.val, f.wd, f.pc, &fmtstr[star] ); */ \
[94d2544]681 PRINT_WITH_DP2( os, &fmtstr[star], f.wd, f.pc, f.val ) \
[3c573e9]682 } /* if */ \
683 return os; \
684 } /* ?|? */ \
[e3fea42]685\
[94d2544]686 OSTYPE_VOID_IMPL( _Ostream_Manip(T) ) \
[3c573e9]687} // distribution
688
[94d2544]689FLOATING_POINT_FMT_IMPL( double, " * ", " *.* " )
690FLOATING_POINT_FMT_IMPL( long double, " *L ", " *.*L " )
[3c573e9]691
[8d321f9]692// *********************************** character ***********************************
[3c573e9]693
[85d8153]694forall( ostype & | basic_ostream( ostype ) ) {
[3c573e9]695 ostype & ?|?( ostype & os, _Ostream_Manip(char) f ) {
[4f37255]696 if ( f.base != 'c' ) { // bespoke binary/octal/hex format
[3c573e9]697 _Ostream_Manip(unsigned char) fmtuc @= { f.val, f.wd, f.pc, f.base, {'\0'} };
698 fmtuc.flags.pc = f.flags.pc;
699 fmtuc.flags.nobsdp = f.flags.nobsdp;
700// os | fmtuc | nonl;
701 (ostype &)(os | fmtuc);
702 return os;
703 } // if
704
[6c5d92f]705 if ( sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
[3c573e9]706
707 #define CFMTNP "% * "
[4f37255]708 char fmtstr[sizeof(CFMTNP)]; // sizeof includes '\0'
[3c573e9]709 memcpy( &fmtstr, CFMTNP, sizeof(CFMTNP) );
[4f37255]710 int star = 1; // position before first '*'
[3c573e9]711
712 // Insert flags into spaces before '*', from right to left.
713 if ( f.flags.left ) { fmtstr[star] = '-'; star -= 1; }
714 fmtstr[star] = '%';
715
[4f37255]716 fmtstr[sizeof(CFMTNP)-2] = f.base; // sizeof includes '\0'
[3c573e9]717 // printf( "%d %s\n", f.wd, &fmtstr[star] );
718 fmt( os, &fmtstr[star], f.wd, f.val );
719 return os;
720 } // ?|?
[94d2544]721 OSTYPE_VOID_IMPL( _Ostream_Manip(char) )
[3c573e9]722} // distribution
723
[8d321f9]724// *********************************** C string ***********************************
[3c573e9]725
[85d8153]726forall( ostype & | basic_ostream( ostype ) ) {
[3c573e9]727 ostype & ?|?( ostype & os, _Ostream_Manip(const char *) f ) {
728 if ( ! f.val ) return os; // null pointer ?
729
730 if ( f.base != 's' ) { // bespoke binary/octal/hex format
731 _Ostream_Manip(unsigned char) fmtuc @= { 0, f.wd, f.pc, f.base, {'\0'} };
732 fmtuc.flags.pc = f.flags.pc;
733 fmtuc.flags.nobsdp = f.flags.nobsdp;
[ebf8ca5]734 for ( i; 0 ~ @ : @; f.val[i] != '\0' ) {
[3c573e9]735 fmtuc.val = f.val[i];
736// os | fmtuc | nonl;
737 (ostype &)(os | fmtuc);
738 } // for
739 return os;
740 } // if
741
[f5d9c37]742 if ( f.val[0] != '\0' && // null string => no leading separator
743 sepPrt$( os ) ) fmt( os, "%s", sepGetCur$( os ) );
[3c573e9]744
745 #define SFMTNP "% * "
746 #define SFMTP "% *.* "
747 char fmtstr[sizeof(SFMTP)]; // sizeof includes '\0'
748 if ( ! f.flags.pc ) memcpy( &fmtstr, SFMTNP, sizeof(SFMTNP) );
749 else memcpy( &fmtstr, SFMTP, sizeof(SFMTP) );
750 int star = 1; // position before first '*'
751
752 // Insert flags into spaces before '*', from right to left.
753 if ( f.flags.left ) { fmtstr[star] = '-'; star -= 1; }
754 fmtstr[star] = '%';
755
756 if ( ! f.flags.pc ) { // no precision
757 // printf( "%d %s\n", f.wd, &fmtstr[star] );
758 fmtstr[sizeof(SFMTNP)-2] = f.base; // sizeof includes '\0'
759 fmt( os, &fmtstr[star], f.wd, f.val );
760 } else { // precision
761 fmtstr[sizeof(SFMTP)-2] = f.base; // sizeof includes '\0'
762 // printf( "%d %d %s\n", f.wd, f.pc, &fmtstr[star] );
763 fmt( os, &fmtstr[star], f.wd, f.pc, f.val );
764 } // if
[f5d9c37]765 if ( f.val[0] == '\0' ) { nosep( os ); } // null string => no trailing separator
[3c573e9]766 return os;
767 } // ?|?
[94d2544]768 OSTYPE_VOID_IMPL( _Ostream_Manip(const char *) )
[3c573e9]769} // distribution
770
771
[8d321f9]772// *********************************** istream ***********************************
[3c573e9]773
[e56cfdb0]774
[ef3ac46]775forall( istype & | basic_istream( istype ) ) {
[93c2e0a]776 istype & ?|?( istype & is, bool & b ) {
[3ce0d440]777 char val[6];
778 fmt( is, "%5s", val );
779 if ( strcmp( val, "true" ) == 0 ) b = true;
780 else if ( strcmp( val, "false" ) == 0 ) b = false;
781 else {
[93c2e0a]782 fprintf( stderr, "invalid Boolean constant\n" );
[4f37255]783 abort(); // cannot use abort stream
[3ce0d440]784 } // if
785 return is;
786 } // ?|?
[c635047]787 ISTYPE_VOID_IMPL( bool & )
[3ce0d440]788
789 istype & ?|?( istype & is, char & c ) {
[0efb269]790 char temp;
791 for () {
[4f37255]792 fmt( is, "%c", &temp ); // must pass pointer through varg to fmt
[0efb269]793 // do not overwrite parameter with newline unless appropriate
[c8371b5]794 if ( temp != '\n' || getANL$( is ) ) { c = temp; break; }
[0efb269]795 if ( eof( is ) ) break;
796 } // for
[3ce0d440]797 return is;
798 } // ?|?
[c635047]799 ISTYPE_VOID_IMPL( char & )
[3ce0d440]800
801 istype & ?|?( istype & is, signed char & sc ) {
[3c573e9]802 fmt( is, "%hhi", &sc );
[3ce0d440]803 return is;
804 } // ?|?
[c635047]805 ISTYPE_VOID_IMPL( signed char & )
[3ce0d440]806
807 istype & ?|?( istype & is, unsigned char & usc ) {
[3c573e9]808 fmt( is, "%hhi", &usc );
[3ce0d440]809 return is;
810 } // ?|?
[c635047]811 ISTYPE_VOID_IMPL( unsigned char & )
[3ce0d440]812
813 istype & ?|?( istype & is, short int & si ) {
[3c573e9]814 fmt( is, "%hi", &si );
[3ce0d440]815 return is;
816 } // ?|?
[c635047]817 ISTYPE_VOID_IMPL( short int & )
[3ce0d440]818
819 istype & ?|?( istype & is, unsigned short int & usi ) {
[3c573e9]820 fmt( is, "%hi", &usi );
[3ce0d440]821 return is;
822 } // ?|?
[c635047]823 ISTYPE_VOID_IMPL( unsigned short int & )
[3ce0d440]824
825 istype & ?|?( istype & is, int & i ) {
[3c573e9]826 fmt( is, "%i", &i );
[3ce0d440]827 return is;
828 } // ?|?
[c635047]829 ISTYPE_VOID_IMPL( int & )
[3ce0d440]830
831 istype & ?|?( istype & is, unsigned int & ui ) {
[3c573e9]832 fmt( is, "%i", &ui );
[3ce0d440]833 return is;
834 } // ?|?
[c635047]835 ISTYPE_VOID_IMPL( unsigned int & )
[3ce0d440]836
837 istype & ?|?( istype & is, long int & li ) {
[3c573e9]838 fmt( is, "%li", &li );
[3ce0d440]839 return is;
840 } // ?|?
[c635047]841 ISTYPE_VOID_IMPL( long int & )
[3ce0d440]842
843 istype & ?|?( istype & is, unsigned long int & ulli ) {
[3c573e9]844 fmt( is, "%li", &ulli );
[3ce0d440]845 return is;
846 } // ?|?
[c635047]847 ISTYPE_VOID_IMPL( unsigned long int & )
[3ce0d440]848
849 istype & ?|?( istype & is, long long int & lli ) {
[3c573e9]850 fmt( is, "%lli", &lli );
[3ce0d440]851 return is;
852 } // ?|?
[c635047]853 ISTYPE_VOID_IMPL( long long int & )
[3ce0d440]854
855 istype & ?|?( istype & is, unsigned long long int & ulli ) {
[3c573e9]856 fmt( is, "%lli", &ulli );
[3ce0d440]857 return is;
858 } // ?|?
[c635047]859 ISTYPE_VOID_IMPL( unsigned long long int & )
[3ce0d440]860
[ef3ac46]861 #if defined( __SIZEOF_INT128__ )
[e474cf09]862 istype & ?|?( istype & is, int128 & llli ) {
863 return (istype &)(is | (unsigned int128 &)llli);
864 } // ?|?
[c635047]865 ISTYPE_VOID_IMPL( int128 & )
[fe68bdf]866
[e474cf09]867 istype & ?|?( istype & is, unsigned int128 & ullli ) {
[fe68bdf]868 char s[40];
869 bool sign = false;
870
871 if ( fmt( is, " %[-]", s ) == 1 ) sign = true; // skip whitespace, negative sign ?
872 // If the input is too large, the value returned is undefined. If there is no input, no value is returned
873 if ( fmt( is, "%39[0-9]%*[0-9]", s ) == 1 ) { // take first 39 characters, ignore remaining
[e474cf09]874 ullli = 0;
[ebf8ca5]875 for ( i; 0 ~ @ : @; s[i] != '\0' ) {
[e474cf09]876 ullli = ullli * 10 + s[i] - '0';
[fe68bdf]877 } // for
[e474cf09]878 if ( sign ) ullli = -ullli;
[fe68bdf]879 } else if ( sign ) ungetc( is, '-' ); // return minus when no digits
880 return is;
881 } // ?|?
[c635047]882 ISTYPE_VOID_IMPL( unsigned int128 & )
[ef3ac46]883 #endif // __SIZEOF_INT128__
[3ce0d440]884
885 istype & ?|?( istype & is, float & f ) {
886 fmt( is, "%f", &f );
887 return is;
888 } // ?|?
[c635047]889 ISTYPE_VOID_IMPL( float & )
[3ce0d440]890
891 istype & ?|?( istype & is, double & d ) {
892 fmt( is, "%lf", &d );
893 return is;
894 } // ?|?
[c635047]895 ISTYPE_VOID_IMPL( double & )
[3ce0d440]896
897 istype & ?|?( istype & is, long double & ld ) {
898 fmt( is, "%Lf", &ld );
899 return is;
900 } // ?|?
[c635047]901 ISTYPE_VOID_IMPL( long double & )
[3ce0d440]902
903 istype & ?|?( istype & is, float _Complex & fc ) {
904 float re, im;
[3c573e9]905 fmt( is, "%f%fi", &re, &im );
[3ce0d440]906 fc = re + im * _Complex_I;
907 return is;
908 } // ?|?
[c635047]909 ISTYPE_VOID_IMPL( float _Complex & )
[3ce0d440]910
911 istype & ?|?( istype & is, double _Complex & dc ) {
912 double re, im;
913 fmt( is, "%lf%lfi", &re, &im );
914 dc = re + im * _Complex_I;
915 return is;
916 } // ?|?
[c635047]917 ISTYPE_VOID_IMPL( double _Complex & )
[3ce0d440]918
919 istype & ?|?( istype & is, long double _Complex & ldc ) {
920 long double re, im;
921 fmt( is, "%Lf%Lfi", &re, &im );
922 ldc = re + im * _Complex_I;
923 return is;
924 } // ?|?
[c635047]925 ISTYPE_VOID_IMPL( long double _Complex & )
[3ce0d440]926
[0f107e4]927 istype & ?|?( istype & is, const char fmt[] ) {
928 fmt( is, fmt, "" );
929 return is;
930 } // ?|?
931 ISTYPE_VOID_IMPL( const char * )
[04396aa]932
[3ce0d440]933 // manipulators
934 istype & ?|?( istype & is, istype & (* manip)( istype & ) ) {
935 return manip( is );
936 } // ?|?
[ff7f6d07]937
[e474cf09]938 void ?|?( istype & is, istype & (* manip)( istype & ) ) {
939 manip( is ); ends( is );
940 } // ?|?
[3ce0d440]941
[200fcb3]942 istype & nl( istype & is ) {
[3c5dee4]943 fmt( is, "%*[^\n]" ); // ignore characters to newline
[e7a8f65]944 if ( ! eof( is ) && getANL$( is ) ) fmt( is, "%*c" ); // read newline
[3ce0d440]945 return is;
[200fcb3]946 } // nl
[0efb269]947
948 istype & nlOn( istype & is ) {
949 nlOn( is ); // call void returning
950 return is;
951 } // nlOn
952
953 istype & nlOff( istype & is ) {
954 nlOff( is ); // call void returning
955 return is;
956 } // nlOff
[ef3ac46]957} // distribution
[44574f2]958
[8d321f9]959// *********************************** manipulators ***********************************
[3c573e9]960
[ef3ac46]961forall( istype & | basic_istream( istype ) ) {
[0f107e4]962 istype & ?|?( istype & is, _Istream_Cskip f ) {
963 // printf( "skip %s %d\n", f.scanset, f.wd );
[bf1cbde]964 if ( f.scanset ) {
965 int nscanset = strlen(f.scanset);
966 char fmtstr[ sizeof("%*[]") + nscanset ];
967 int pos = 0;
968 fmtstr[pos] = '%'; pos += 1;
969 fmtstr[pos] = '*'; pos += 1;
970 fmtstr[pos] = '['; pos += 1;
971 strcpy( &fmtstr[pos], f.scanset ); pos += nscanset;
972 fmtstr[pos] = ']'; pos += 1;
973 fmtstr[pos] = '\0';
974 fmt( is, fmtstr, (void*)0 ); // last arg is dummy: suppress gcc warning
975 }
[0f107e4]976 else for ( f.wd ) fmt( is, "%*c" );
977 return is;
978 }
979 ISTYPE_VOID_IMPL( _Istream_Cskip )
[e7a8f65]980
[0f107e4]981 istype & ?|?( istype & is, _Istream_Cstr f ) {
[38de914]982 const char * scanset = f.scanset;
[0f107e4]983 if ( f.flags.delimiter ) scanset = f.delimiter; // getline ?
[5ad2c6c7]984
[e474cf09]985 size_t len = 0;
[aa25216]986 if ( scanset ) len = strlen( scanset );
[e474cf09]987 char fmtstr[len + 16];
988 int start = 1;
989 fmtstr[0] = '%';
990 if ( f.flags.ignore ) { fmtstr[1] = '*'; start += 1; }
[0f107e4]991 // no maximum width necessary because text ignored => width is read width
[2fa0237]992 if ( f.wd != -1 ) {
993 // wd is buffer bytes available (for input chars + null terminator)
994 // rwd is count of input chars
[0860d9c]995 int rwd;
996 if (f.flags.rwd) {
997 verify (f.wd >= 0);
998 rwd = f.wd;
999 } else {
1000 verify (f.wd >= 1);
1001 rwd = f.wd - 1;
1002 } // if
[2fa0237]1003 start += sprintf( &fmtstr[start], "%d", rwd );
1004 }
[2f34fde]1005
[aa25216]1006 if ( ! scanset ) {
[2f34fde]1007 // %s, %*s, %ws, %*ws
[e474cf09]1008 fmtstr[start] = 's'; fmtstr[start + 1] = '\0';
1009 // printf( "cstr %s\n", fmtstr );
[2f34fde]1010 } else {
1011 // incl %[xxx], %*[xxx], %w[xxx], %*w[xxx]
1012 // excl %[^xxx], %*[^xxx], %w[^xxx], %*w[^xxx]
1013 fmtstr[start] = '['; start += 1;
1014 if ( f.flags.inex ) { fmtstr[start] = '^'; start += 1; }
[aa25216]1015 strcpy( &fmtstr[start], scanset ); // copy includes '\0'
[2f34fde]1016 len += start;
1017 fmtstr[len] = ']'; fmtstr[len + 1] = '\0';
1018 // printf( "incl/excl %s\n", fmtstr );
[e474cf09]1019 } // if
[2f34fde]1020
[2fa0237]1021 int check = f.wd - 2;
[8cffa4f]1022 if (! f.flags.ignore ) {
1023 f.s[0] = '\0';
1024 if ( ! f.flags.rwd ) f.s[check] = '\0'; // insert sentinel
1025 }
[686912c]1026 len = fmt( is, fmtstr, f.s );
1027 //fprintf( stderr, "KK %s %zd %d %c %s\n", fmtstr, len, check, f.s[check], f.s );
[aa25216]1028
[0860d9c]1029 if ( ! f.flags.ignore && ! f.flags.rwd && f.s[check] != '\0' ) { // sentinel overwritten ?
1030 // buffer filled, but would we have kept going?
1031 if ( ! eof( is ) ) {
1032 char peek;
1033 fmt( is, "%c", &peek );
1034 ungetc( is, peek );
1035 bool hasMore;
1036 if (f.flags.delimiter) { // getline
1037 hasMore = (peek != f.delimiter[0]);
1038 } else if (f.scanset) { // incl/excl
1039 bool peekMatch = strchr(f.scanset, peek) != 0p;
1040 hasMore = f.flags.inex ? (!peekMatch) : (peekMatch);
1041 } else { // %s
1042 hasMore = !isspace(peek);
1043 }
1044 if (hasMore) throw (cstring_length){ &cstring_length_vt };
1045 } // if
1046 } // if
[38de914]1047
[0f107e4]1048 if ( f.flags.delimiter ) { // getline ?
[686912c]1049 if ( len == 0 ) f.s[0] = '\0'; // empty read => argument unchanged => set empty
[0f107e4]1050 if ( ! eof( is ) ) { // ignore delimiter, may not be present because of width
1051 char delimiter;
1052 fmt( is, "%c", &delimiter );
1053 if ( delimiter != f.delimiter[0] ) ungetc( is, delimiter );
1054 } // if
[686912c]1055 } //if
[61c7239]1056 return is;
[e474cf09]1057 } // ?|?
[c635047]1058 ISTYPE_VOID_IMPL( _Istream_Cstr )
[e474cf09]1059
1060 istype & ?|?( istype & is, _Istream_Char f ) {
[e7a8f65]1061 fmt( is, "%*c" ); // argument variable unused
[e474cf09]1062 return is;
1063 } // ?|?
[c635047]1064 ISTYPE_VOID_IMPL( _Istream_Char )
[e474cf09]1065} // distribution
[86a8be5]1066
[94d2544]1067#define INPUT_FMT_IMPL( T, CODE ) \
[ef3ac46]1068forall( istype & | basic_istream( istype ) ) { \
[e474cf09]1069 istype & ?|?( istype & is, _Istream_Manip(T) f ) { \
1070 enum { size = 16 }; \
1071 char fmtstr[size]; \
1072 if ( f.wd == -1 ) { \
1073 snprintf( fmtstr, size, "%%%s%s", f.ignore ? "*" : "", CODE ); \
1074 } else { \
1075 snprintf( fmtstr, size, "%%%s%d%s", f.ignore ? "*" : "", f.wd, CODE ); \
1076 } /* if */ \
1077 /* printf( "%d %s %p\n", f.wd, fmtstr, &f.val ); */ \
1078 fmt( is, fmtstr, &f.val ); \
1079 return is; \
1080 } /* ?|? */ \
[c635047]1081 ISTYPE_VOID_IMPL( _Istream_Manip(T) ) \
[e474cf09]1082} // distribution
[3c573e9]1083
[94d2544]1084INPUT_FMT_IMPL( signed char, "hhi" )
1085INPUT_FMT_IMPL( unsigned char, "hhi" )
1086INPUT_FMT_IMPL( signed short int, "hi" )
1087INPUT_FMT_IMPL( unsigned short int, "hi" )
1088INPUT_FMT_IMPL( signed int, "i" )
1089INPUT_FMT_IMPL( unsigned int, "i" )
1090INPUT_FMT_IMPL( signed long int, "li" )
1091INPUT_FMT_IMPL( unsigned long int, "li" )
1092INPUT_FMT_IMPL( signed long long int, "lli" )
1093INPUT_FMT_IMPL( unsigned long long int, "lli" )
1094
1095INPUT_FMT_IMPL( float, "f" )
1096INPUT_FMT_IMPL( double, "lf" )
1097INPUT_FMT_IMPL( long double, "Lf" )
[3c573e9]1098
[ef3ac46]1099forall( istype & | basic_istream( istype ) ) {
[e474cf09]1100 istype & ?|?( istype & is, _Istream_Manip(float _Complex) fc ) {
1101 float re, im;
1102 _Istream_Manip(float) fmtuc @= { re, fc.wd, fc.ignore };
1103 is | fmtuc;
1104 &fmtuc.val = &im;
1105 is | fmtuc;
1106 if ( ! fc.ignore ) fc.val = re + im * _Complex_I; // re/im are uninitialized for ignore
1107 return is;
1108 } // ?|?
[c635047]1109 ISTYPE_VOID_IMPL( _Istream_Manip(float _Complex) )
[e474cf09]1110
1111 istype & ?|?( istype & is, _Istream_Manip(double _Complex) dc ) {
1112 double re, im;
1113 _Istream_Manip(double) fmtuc @= { re, dc.wd, dc.ignore };
1114 is | fmtuc;
1115 &fmtuc.val = &im;
1116 is | fmtuc;
1117 if ( ! dc.ignore ) dc.val = re + im * _Complex_I; // re/im are uninitialized for ignore
1118 return is;
1119 } // ?|?
[c635047]1120 ISTYPE_VOID_IMPL( _Istream_Manip(double _Complex) )
[e474cf09]1121
1122 istype & ?|?( istype & is, _Istream_Manip(long double _Complex) ldc ) {
1123 long double re, im;
1124 _Istream_Manip(long double) fmtuc @= { re, ldc.wd, ldc.ignore };
1125 is | fmtuc;
1126 &fmtuc.val = &im;
1127 is | fmtuc;
1128 if ( ! ldc.ignore ) ldc.val = re + im * _Complex_I; // re/im are uninitialized for ignore
1129 return is;
1130 } // ?|?
[c635047]1131 ISTYPE_VOID_IMPL( _Istream_Manip(long double _Complex) )
[e474cf09]1132} // distribution
[3c573e9]1133
[2f34fde]1134
[86bd7c1f]1135// Local Variables: //
1136// tab-width: 4 //
[084520f]1137// compile-command: "cfa iostream.cfa" //
[86bd7c1f]1138// End: //
Note: See TracBrowser for help on using the repository browser.