source: libcfa/src/iostream.cfa@ ecfa58be

Last change on this file since ecfa58be was ae0c1c3, checked in by Andrew Beach <ajbeach@…>, 5 months ago

Rewrote the iostream traits to have a single assertion each, a table containing function pointers. This is just an experiment right now. It seems that it does cause significant speed up of assertion resolution, but for some reason also seems to add a flat overhead that mostly eats up that saving.

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