FastArduino v1.10
C++ library to build fast but small Arduino/AVR projects
Loading...
Searching...
No Matches
ios.h
Go to the documentation of this file.
1// Copyright 2016-2023 Jean-Francois Poilpret
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
16
21#ifndef IOS_H
22#define IOS_H
23
24#include <math.h>
25#include <stddef.h>
26#include <stdlib.h>
27#include <string.h>
28#include "streambuf.h"
29#include "flash.h"
30
31namespace streams
32{
38 {
39 public:
56 using iostate = uint8_t;
57
64 static constexpr iostate eofbit = 0x01;
69 static constexpr iostate failbit = 0x02;
74 static constexpr iostate badbit = 0x04;
76 static constexpr iostate goodbit = 0;
77
82 {
83 return state_;
84 }
85
90 void setstate(iostate state)
91 {
92 clear(rdstate() | state);
93 }
94
100 void clear(iostate state = goodbit)
101 {
102 state_ = state;
103 }
104
114 bool good() const
115 {
116 return rdstate() == goodbit;
117 }
118
125 bool eof() const
126 {
127 return rdstate() & eofbit;
128 }
129
140 bool fail() const
141 {
142 return rdstate() & (failbit | badbit);
143 }
144
152 bool bad() const
153 {
154 return rdstate() & badbit;
155 }
156
163 bool operator!() const
164 {
165 return fail();
166 }
167
173 explicit operator bool() const
174 {
175 return !fail();
176 }
177
209 using fmtflags = uint16_t;
210
212 static constexpr fmtflags dec = 0x0001;
214 static constexpr fmtflags bin = 0x0002;
216 static constexpr fmtflags oct = 0x0004;
218 static constexpr fmtflags hex = 0x0008;
227 static constexpr fmtflags basefield = dec | bin | oct | hex;
228
233 static constexpr fmtflags left = 0x0010;
238 static constexpr fmtflags right = 0x0020;
245 static constexpr fmtflags adjustfield = left | right;
246
248 static constexpr fmtflags scientific = 0x0040;
250 static constexpr fmtflags fixed = 0x0080;
257 static constexpr fmtflags floatfield = scientific | fixed;
258
260 static constexpr fmtflags boolalpha = 0x0200;
268 static constexpr fmtflags showbase = 0x0400;
270 static constexpr fmtflags showpos = 0x1000;
272 static constexpr fmtflags skipws = 0x2000;
274 static constexpr fmtflags unitbuf = 0x4000;
281 static constexpr fmtflags uppercase = 0x8000U;
282
288 {
289 flags_ = flags;
290 }
291
297 {
298 return flags_;
299 }
300
313 {
314 flags_ |= flags;
315 }
316
336 {
337 flags_ = (flags_ & ~mask) | (flags & mask);
338 }
339
347 {
348 flags_ &= ~flags;
349 }
350
358 char fill() const
359 {
360 return fill_;
361 }
362
369 void fill(char fill)
370 {
371 fill_ = fill;
372 }
373
390 void width(uint8_t width)
391 {
392 width_ = width;
393 }
394
400 uint8_t width() const
401 {
402 return width_;
403 }
404
411 void precision(uint8_t precision)
412 {
413 precision_ = (precision < MAX_PRECISION ? precision : MAX_PRECISION);
414 }
415
421 uint8_t precision() const
422 {
423 return precision_;
424 }
425
435 {
436 flags_ = rhs.flags_;
437 width_ = rhs.width_;
438 precision_ = rhs.precision_;
439 fill_ = rhs.fill_;
440 return *this;
441 }
442
447 static constexpr uint8_t MAX_PRECISION = 16;
448
449 protected:
451 ios_base() = default;
452 ios_base(const ios_base&) = delete;
453 ios_base& operator=(const ios_base&) = delete;
454
455 static constexpr uint8_t DOUBLE_BUFFER_SIZE = MAX_PRECISION + 7 + 1;
456
457 void init()
458 {
459 width_ = 0;
460 precision_ = 6;
461 flags_ = skipws | dec;
462 fill_ = ' ';
463 }
464 // Conversions from string
465 void convert(const char* token, bool& value)
466 {
467 if (flags() & boolalpha)
468 value = (strcmp(token, "true") == 0);
469 else
470 value = (atol(token) != 0);
471 }
472 void convert(const char* token, double& value)
473 {
474 char* endptr;
475 double val = strtod(token, &endptr);
476 if (endptr != token)
477 value = val;
478 else
480 }
481 const char* binary_token(const char* token) const
482 {
483 if ((base() == 2) && ((strncmp(token, "0b0", 3) == 0) || (strncmp(token, "0b1", 3) == 0))) return token + 2;
484 return token;
485 }
486 bool convert(const char* token, long& value)
487 {
488 char* endptr;
489 long val = strtol(binary_token(token), &endptr, base());
490 if (endptr != token)
491 value = val;
492 else
494 return (endptr != token);
495 }
496 bool convert(const char* token, unsigned long& value)
497 {
498 char* endptr;
499 unsigned long val = strtoul(binary_token(token), &endptr, base());
500 if (endptr != token)
501 value = val;
502 else
504 return (endptr != token);
505 }
506 void convert(const char* token, int& value)
507 {
508 long val;
509 if (convert(token, val)) value = (int) val;
510 }
511 void convert(const char* token, unsigned int& value)
512 {
513 unsigned long val;
514 if (convert(token, val)) value = (unsigned int) val;
515 }
516
517 // Conversions to string
518 void convert(ostreambuf& out, int value) const
519 {
520 // Allocate sufficient size for bin representation
521 char buffer[sizeof(int) * 8 + 1];
522 format_number(out, itoa(value, buffer, base()));
523 }
524 void convert(ostreambuf& out, unsigned int value) const
525 {
526 // Allocate sufficient size for bin representation
527 char buffer[sizeof(unsigned int) * 8 + 1];
528 format_number(out, utoa(value, buffer, base()));
529 }
530 void convert(ostreambuf& out, long value) const
531 {
532 // Allocate sufficient size for bin representation
533 char buffer[sizeof(long) * 8 + 1];
534 format_number(out, ltoa(value, buffer, base()));
535 }
536 void convert(ostreambuf& out, unsigned long value) const
537 {
538 // Allocate sufficient size for bin representation
539 char buffer[sizeof(unsigned long) * 8 + 1];
540 format_number(out, ultoa(value, buffer, base()));
541 }
542 static size_t double_digits(double value)
543 {
544 int digits = int(log10(fabs(value)));
545 if (digits < 0)
546 return 1;
547 else
548 return size_t(digits + 1);
549 }
550 bool is_too_large(double value) const
551 {
552 // Number of chars = sign + digits before DP + DP + precision + \0
553 return (1 + double_digits(value) + 1 + precision() + 1) > DOUBLE_BUFFER_SIZE;
554 }
555 void convert(ostreambuf& out, double value) const
556 {
557 // Allocate sufficient size for fixed/scientific representation with precision max = 16
558 // Need 1 more for sign, 1 for DP, 1 for first digit, 4 for e+00
559 char buffer[DOUBLE_BUFFER_SIZE];
560 // If value is too large, force scientific anyway
561 if ((flags() & scientific) || is_too_large(value))
562 {
563 const uint8_t DTOSTRE_MAX_PRECISION = 7;
564 dtostre(value, buffer, precision(), 0);
565 if (precision() > DTOSTRE_MAX_PRECISION)
566 {
567 // If precision() > 7, then it is limited to 7 by dtostre(), add 0 manually then
568 // Size of the exponent part, always e+00 or e-00
569 const uint8_t EXPONENT_SIZE = 1 + 1 + 2;
570 // Find exponent marker 'e' and move exponent part right (including null)
571 char* exponent = strchr(buffer, 'e');
572 uint8_t added_zeros = precision() - DTOSTRE_MAX_PRECISION;
573 memmove(exponent + added_zeros, exponent, EXPONENT_SIZE + 1);
574 // Add '0' in the added space before exponent
575 memset(exponent, '0', added_zeros);
576 }
577 }
578 else if (flags() & fixed)
579 dtostrf(value, 0, precision(), buffer);
580 else
581 {
582 // In this mode any trailing 0 after DP is discarded
583 // DP is also discarded if there are only 0 after it
584 dtostrf(value, 0, precision(), buffer);
585 char* dp = strchr(buffer, '.');
586 if (dp)
587 {
588 // Number has a decimal point, remove all trailing zeros
589 char* reverse = buffer + strlen(buffer) - 1;
590 while ((reverse != dp) && (*reverse == '0')) *reverse-- = 0;
591 // If there is no significant digit after DP, remove DP
592 if (reverse == dp) *reverse = 0;
593 }
594 }
595 upper(buffer, true);
596 justify(out, buffer, add_sign(buffer, true), nullptr);
597 }
598 void convert(ostreambuf& out, char value) const
599 {
600 char buffer[1 + 1];
601 buffer[0] = value;
602 buffer[1] = 0;
603 justify(out, buffer, false, nullptr);
604 }
605 void convert(ostreambuf& out, bool value) const
606 {
607 if (flags() & boolalpha)
608 justify(out, (value ? F("true") : F("false")));
609 else
610 convert(out, (value ? 1 : 0));
611 }
612
613 void upper(char* input, bool is_float = false) const
614 {
615 if ((flags() & uppercase) && ((flags() & hex) || is_float)) strupr(input);
616 }
617
618 const char* prefix_base() const
619 {
620 if (flags() & showbase)
621 {
622 if (flags() & bin)
623 return "0b";
624 if (flags() & oct)
625 return "0";
626 if (flags() & hex)
627 return "0x";
628 }
629 return nullptr;
630 }
631
632 bool add_sign(const char* input, bool is_float = false) const
633 {
634 return (flags() & showpos) && ((flags() & dec) || is_float) && (input[0] != '+') && (input[0] != '-');
635 }
636
637 void format_number(ostreambuf& out, char* input) const
638 {
639 upper(input);
640 justify(out, input, add_sign(input), prefix_base());
641 }
642
643 void output_number(ostreambuf& out, const char* input, bool add_sign, const char* prefix) const
644 {
645 if (add_sign) out.put_('+', false);
646 if (prefix) out.sputn(prefix);
647 out.sputn(input);
648 }
649
650 void output_filler(ostreambuf& out, char filler, uint8_t size) const
651 {
652 while (size--) out.put_(filler, false);
653 }
654
655 void justify(ostreambuf& out, const char* input, bool add_sign, const char* prefix) const
656 {
657 // Handle case where padding must be added
658 // Speed optimization: handle padding situation ONLY if width is != 0
659 if (width())
660 {
661 size_t len = strlen(input) + (prefix ? strlen(prefix) : 0) + (add_sign ? 1 : 0);
662 if (len < width())
663 {
664 uint8_t add = width() - len;
665 if (flags() & left)
666 {
667 output_number(out, input, add_sign, prefix);
668 output_filler(out, fill(), add);
669 out.on_put();
670 }
671 else
672 {
673 output_filler(out, fill(), add);
674 output_number(out, input, add_sign, prefix);
675 }
676 return;
677 }
678 }
679 // Handle case where no padding is needed
680 output_number(out, input, add_sign, prefix);
681 }
682
683 void justify(ostreambuf& out, const flash::FlashStorage* input) const
684 {
685 size_t len = strlen_P((const char*) input);
686 if (len < width())
687 {
688 uint8_t add = width() - len;
689 if (flags() & left)
690 {
691 out.sputn(input);
692 output_filler(out, fill(), add);
693 out.on_put();
694 }
695 else
696 {
697 output_filler(out, fill(), add);
698 out.sputn(input);
699 }
700 }
701 else
702 out.sputn(input);
703 }
704
705 int base() const
706 {
707 if (flags() & bin) return 2;
708 if (flags() & oct) return 8;
709 if (flags() & hex) return 16;
710 return 10;
711 }
713
714 private:
715 iostate state_ = 0;
716 fmtflags flags_ = skipws | dec;
717 uint8_t width_ = 0;
718 uint8_t precision_ = 6;
719 char fill_ = ' ';
720 };
721
723 using ios = ios_base;
724
729 template<typename FSTREAM> inline void skipws(FSTREAM& stream)
730 {
731 stream.setf(ios::skipws);
732 }
733
738 template<typename FSTREAM> inline void noskipws(FSTREAM& stream)
739 {
740 stream.unsetf(ios::skipws);
741 }
742
747 template<typename FSTREAM> inline void bin(FSTREAM& stream)
748 {
749 stream.setf(ios::bin, ios::basefield);
750 }
751
756 template<typename FSTREAM> inline void oct(FSTREAM& stream)
757 {
758 stream.setf(ios::oct, ios::basefield);
759 }
760
765 template<typename FSTREAM> inline void dec(FSTREAM& stream)
766 {
767 stream.setf(ios::dec, ios::basefield);
768 }
769
774 template<typename FSTREAM> inline void hex(FSTREAM& stream)
775 {
776 stream.setf(ios::hex, ios::basefield);
777 }
778
788 template<typename FSTREAM> inline void boolalpha(FSTREAM& stream)
789 {
790 stream.setf(ios::boolalpha);
791 }
792
801 template<typename FSTREAM> inline void noboolalpha(FSTREAM& stream)
802 {
803 stream.unsetf(ios::boolalpha);
804 }
805
821 template<typename FSTREAM> inline void showbase(FSTREAM& stream)
822 {
823 stream.setf(ios::showbase);
824 }
825
836 template<typename FSTREAM> inline void noshowbase(FSTREAM& stream)
837 {
838 stream.unsetf(ios::showbase);
839 }
840
849 template<typename FSTREAM> inline void showpos(FSTREAM& stream)
850 {
851 stream.setf(ios::showpos);
852 }
853
862 template<typename FSTREAM> inline void noshowpos(FSTREAM& stream)
863 {
864 stream.unsetf(ios::showpos);
865 }
866
877 template<typename FSTREAM> inline void uppercase(FSTREAM& stream)
878 {
879 stream.setf(ios::uppercase);
880 }
881
892 template<typename FSTREAM> inline void nouppercase(FSTREAM& stream)
893 {
894 stream.unsetf(ios::uppercase);
895 }
896
906 template<typename FSTREAM> inline void unitbuf(FSTREAM& stream)
907 {
908 stream.setf(ios::unitbuf);
909 }
910
922 template<typename FSTREAM> inline void nounitbuf(FSTREAM& stream)
923 {
924 stream.unsetf(ios::unitbuf);
925 }
926
935 template<typename FSTREAM> inline void left(FSTREAM& stream)
936 {
937 stream.setf(ios::left, ios::adjustfield);
938 }
939
948 template<typename FSTREAM> inline void right(FSTREAM& stream)
949 {
950 stream.setf(ios::right, ios::adjustfield);
951 }
952
966 template<typename FSTREAM> inline void defaultfloat(FSTREAM& stream)
967 {
968 stream.unsetf(ios::floatfield);
969 }
970
982 template<typename FSTREAM> inline void fixed(FSTREAM& stream)
983 {
984 stream.setf(ios::fixed, ios::floatfield);
985 }
986
1000 template<typename FSTREAM> inline void scientific(FSTREAM& stream)
1001 {
1002 stream.setf(ios::scientific, ios::floatfield);
1003 }
1004}
1005
1006#endif /* IOS_H */
Base class for formatted streams.
Definition: ios.h:38
fmtflags flags() const
Return the format flags currently selected in this stream.
Definition: ios.h:296
bool good() const
Definition: ios.h:114
void precision(uint8_t precision)
Set precision (number of digits after decimal point) used for displaying floating values.
Definition: ios.h:411
uint8_t precision() const
Get the current precision (default = 6) used for formatted floating values output.
Definition: ios.h:421
static constexpr fmtflags fixed
Write floating point values in scientific notation.
Definition: ios.h:250
static constexpr iostate eofbit
This bit is set if the stream has unexpectedly reached its end during an extraction.
Definition: ios.h:64
uint16_t fmtflags
Bitmask type to represent stream format flags.
Definition: ios.h:209
static constexpr fmtflags uppercase
Write uppercase letters instead of lowercase in certain insertion operations.
Definition: ios.h:281
static constexpr fmtflags basefield
Bitmask constant used with setf(fmtflags, fmtflags) when changing the output base format.
Definition: ios.h:227
static constexpr fmtflags dec
Read or write integral values using decimal (0..9) base format.
Definition: ios.h:212
bool operator!() const
Return true if an error has occurred on the associated stream, since last time state was reset (clear...
Definition: ios.h:163
static constexpr fmtflags left
Pad all output to width() characters, with fill() character appended at the end so that the output ap...
Definition: ios.h:233
bool fail() const
Return true if an error has occurred on the associated stream, since last time state was reset (clear...
Definition: ios.h:140
void setf(fmtflags flags, fmtflags mask)
Set this stream's format flags whose bits are set in both flags and mask, and clears the format flags...
Definition: ios.h:335
iostate rdstate() const
Return the current stream error state.
Definition: ios.h:81
static constexpr fmtflags showpos
Write non-negative numerical values preceded by +.
Definition: ios.h:270
static constexpr fmtflags oct
Read or write integral values using octal (0..7) base format.
Definition: ios.h:216
uint8_t iostate
Bitmask type to represent stream state flags.
Definition: ios.h:56
void clear(iostate state=goodbit)
Set the stream error state flags by assigning them the value of state.
Definition: ios.h:100
void width(uint8_t width)
Set minimum width used for displaying values.
Definition: ios.h:390
void setf(fmtflags flags)
Set this stream's format flags whose bits are set in flags, leaving unchanged the rest.
Definition: ios.h:312
static constexpr fmtflags right
Pad all output to width() characters, with fill() character added at the beginning so that the output...
Definition: ios.h:238
static constexpr fmtflags skipws
Skip leading spaces on certain extraction (read) operations.
Definition: ios.h:272
static constexpr fmtflags floatfield
Bitmask constant used with setf(fmtflags, fmtflags) when changing the floating point output represent...
Definition: ios.h:257
static constexpr fmtflags scientific
Write floating point values in fixed-point notation.
Definition: ios.h:248
void fill(char fill)
Set fill as new fill character for this stream.
Definition: ios.h:369
uint8_t width() const
Get the current minimum width value (default = 0) used for formatted output.
Definition: ios.h:400
void unsetf(fmtflags flags)
Clear this stream's format flags whose bits are set in flags.
Definition: ios.h:346
static constexpr fmtflags bin
Read or write integral values using binary (0,1) base format.
Definition: ios.h:214
char fill() const
Return the fill character.
Definition: ios.h:358
ios_base & copyfmt(const ios_base &rhs)
Copy formatting information from rhs to this stream.
Definition: ios.h:434
static constexpr iostate badbit
This bit is set when an irrecoverable stream error has occurred, e.g.
Definition: ios.h:74
void setstate(iostate state)
Set the stream error flags state in addition to currently set flags.
Definition: ios.h:90
static constexpr fmtflags hex
Read or write integral values using hexadecimal (0..9,A..F) base format.
Definition: ios.h:218
static constexpr iostate failbit
This bit is set when an input or operation failed due to a formatting error during extraction.
Definition: ios.h:69
static constexpr fmtflags boolalpha
Read or write bool values as alphabetic string (true or false).
Definition: ios.h:260
bool bad() const
Return true if a non-recoverable error has occurred on the associated stream.
Definition: ios.h:152
bool eof() const
Return true if the associated stream has reached end-of-file.
Definition: ios.h:125
void flags(fmtflags flags)
Set new format flags for this stream.
Definition: ios.h:287
static constexpr fmtflags unitbuf
Flush output after each insertion operation.
Definition: ios.h:274
static constexpr fmtflags adjustfield
Bitmask constant used with setf(fmtflags, fmtflags) when changing the output adjustment.
Definition: ios.h:245
static constexpr iostate goodbit
No error; always 0.
Definition: ios.h:76
static constexpr fmtflags showbase
Write integral values prefixed by their base:
Definition: ios.h:268
static constexpr uint8_t MAX_PRECISION
The maximum allowed precision.
Definition: ios.h:447
Flash memory utilities.
#define F(ptr)
Force string constant to be stored as flash storage.
Definition: flash.h:150
Defines C++-like streams API, based on circular buffers for input or output.
Definition: empty_streams.h:34
void noboolalpha(FSTREAM &stream)
Clear the ios::boolalpha format flag for stream.
Definition: ios.h:801
void nounitbuf(FSTREAM &stream)
Clear the ios::unitbuf format flag for stream.
Definition: ios.h:922
void showbase(FSTREAM &stream)
Set the ios::showbase format flag for stream.
Definition: ios.h:821
void nouppercase(FSTREAM &stream)
Clear the ios::uppercase format flag for stream.
Definition: ios.h:892
void noskipws(FSTREAM &stream)
Manipulator for an input stream, which will deactivate whitespace discarding before formatted input o...
Definition: ios.h:738
void right(FSTREAM &stream)
Set the ios::adjustfield format flag for stream to ios::right, thus adjusting next output to the righ...
Definition: ios.h:948
void skipws(FSTREAM &stream)
Manipulator for an input stream, which will activate whitespace discarding before formatted input ope...
Definition: ios.h:729
void bin(FSTREAM &stream)
Manipulator for an output or input stream, which will set the base, used to represent (output) or int...
Definition: ios.h:747
void boolalpha(FSTREAM &stream)
Set the ios::boolalpha format flag for stream.
Definition: ios.h:788
void showpos(FSTREAM &stream)
Set the ios::showpos format flag for stream.
Definition: ios.h:849
void hex(FSTREAM &stream)
Manipulator for an output or input stream, which will set the base, used to represent (output) or int...
Definition: ios.h:774
void scientific(FSTREAM &stream)
Set the ios::floatfield format flag for stream to ios::scientific.
Definition: ios.h:1000
void oct(FSTREAM &stream)
Manipulator for an output or input stream, which will set the base, used to represent (output) or int...
Definition: ios.h:756
void defaultfloat(FSTREAM &stream)
Set the ios::floatfield format flag for stream to ios::defaultfloat.
Definition: ios.h:966
void uppercase(FSTREAM &stream)
Set the ios::uppercase format flag for stream.
Definition: ios.h:877
void fixed(FSTREAM &stream)
Set the ios::floatfield format flag for stream to ios::fixed.
Definition: ios.h:982
void noshowpos(FSTREAM &stream)
Clear the ios::showpos format flag for stream.
Definition: ios.h:862
void dec(FSTREAM &stream)
Manipulator for an output or input stream, which will set the base, used to represent (output) or int...
Definition: ios.h:765
void unitbuf(FSTREAM &stream)
Set the ios::unitbuf format flag for stream.
Definition: ios.h:906
void noshowbase(FSTREAM &stream)
Clear the ios::showbase format flag for stream.
Definition: ios.h:836
void left(FSTREAM &stream)
Set the ios::adjustfield format flag for stream to ios::left, thus adjusting next output to the left.
Definition: ios.h:935
C++-like std::iostream facilities.