From 097af7fc8f8688cc21453a5561347f14ca7c5771 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sat, 23 Aug 2008 19:23:10 +0000 Subject: [PATCH] add a simple mechanism for formatted output. This gives raw_ostream's all the power and risk of fprintf format strings. Use them like this: OS << format("%10.4f", 42.0) << "\n" << format("%x", 42) << '\n'; git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@55246 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/Support/raw_ostream.h | 71 +++++++++++++++++++++++++++--- lib/Support/raw_ostream.cpp | 50 +++++++++++++++++++++ 2 files changed, 116 insertions(+), 5 deletions(-) diff --git a/include/llvm/Support/raw_ostream.h b/include/llvm/Support/raw_ostream.h index 34e40da5174..58d5be27417 100644 --- a/include/llvm/Support/raw_ostream.h +++ b/include/llvm/Support/raw_ostream.h @@ -21,7 +21,8 @@ #include namespace llvm { - + class format_object_base; + /// raw_ostream - This class implements an extremely fast bulk output stream /// that can *only* output to a stream. It does not support seeking, reopening, /// rewinding, line buffered disciplines etc. It is a simple buffer that outputs @@ -92,11 +93,8 @@ public: } raw_ostream &operator<<(unsigned long N); - raw_ostream &operator<<(long N); - raw_ostream &operator<<(unsigned long long N); - raw_ostream &operator<<(long long N); raw_ostream &operator<<(unsigned int N) { @@ -111,9 +109,11 @@ public: return this->operator<<(ftostr(N)); } - raw_ostream &write(const char *Ptr, unsigned Size); + // Formatted output, see the format() function below. + raw_ostream &operator<<(const format_object_base &Fmt); + //===--------------------------------------------------------------------===// // Subclass Interface //===--------------------------------------------------------------------===// @@ -137,6 +137,63 @@ private: virtual void handle(); }; +//===----------------------------------------------------------------------===// +// Formatted Output +//===----------------------------------------------------------------------===// + +/// format_object_base - This is a helper class used for handling formatted +/// output. It is the abstract base class of a templated derived class. +class format_object_base { +protected: + const char *Fmt; + virtual void home(); // Out of line virtual method. +public: + format_object_base(const char *fmt) : Fmt(fmt) {} + virtual ~format_object_base() {} + + /// print - Format the object into the specified buffer. On success, this + /// returns the length of the formatted string. If the buffer is too small, + /// this returns a length to retry with, which will be larger than BufferSize. + virtual unsigned print(char *Buffer, unsigned BufferSize) const = 0; +}; + +/// format_object - This is a templated helper class used by the format function +/// that captures the object to be formated and the format string. When +/// actually printed, this synthesizes the string into a temporary buffer +/// provided and returns whether or not it is big enough. +template + class format_object : public format_object_base { + T Val; +public: + format_object(const char *fmt, const T &val) + : format_object_base(fmt), Val(val) { + } + + /// print - Format the object into the specified buffer. On success, this + /// returns the length of the formatted string. If the buffer is too small, + /// this returns a length to retry with, which will be larger than BufferSize. + virtual unsigned print(char *Buffer, unsigned BufferSize) const { + int N = snprintf(Buffer, BufferSize-1, Fmt, Val); + if (N < 0) // VC++ and old GlibC return negative on overflow. + return BufferSize*2; + if (unsigned(N) >= BufferSize-1)// Other impls yield number of bytes needed. + return N+1; + // If N is positive and <= BufferSize-1, then the string fit, yay. + return N; + } +}; + +/// format - This is a helper function that is used to produce formatted output. +/// This is typically used like: OS << format("%0.4f", myfloat) << '\n'; +template +inline format_object format(const char *Fmt, const T &Val) { + return format_object(Fmt, Val); +} + +//===----------------------------------------------------------------------===// +// File Output Streams +//===----------------------------------------------------------------------===// + /// raw_fd_ostream - A raw_ostream that writes to a file descriptor. /// class raw_fd_ostream : public raw_ostream { @@ -187,6 +244,10 @@ raw_ostream &outs(); raw_ostream &errs(); +//===----------------------------------------------------------------------===// +// Bridge Output Streams +//===----------------------------------------------------------------------===// + /// raw_os_ostream - A raw_ostream that writes to an std::ostream. This is a /// simple adaptor class. class raw_os_ostream : public raw_ostream { diff --git a/lib/Support/raw_ostream.cpp b/lib/Support/raw_ostream.cpp index d776fa583f1..104dc771498 100644 --- a/lib/Support/raw_ostream.cpp +++ b/lib/Support/raw_ostream.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/raw_ostream.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/Config/config.h" #include @@ -134,6 +135,55 @@ raw_ostream &raw_ostream::write(const char *Ptr, unsigned Size) { return *this; } +// Formatted output. +raw_ostream &raw_ostream::operator<<(const format_object_base &Fmt) { + // If we have more than a few bytes left in our output buffer, try formatting + // directly onto its end. + unsigned NextBufferSize = 127; + if (OutBufEnd-OutBufCur > 3) { + unsigned BufferBytesLeft = OutBufEnd-OutBufCur; + unsigned BytesUsed = Fmt.print(OutBufCur, BufferBytesLeft); + + // Common case is that we have plenty of space. + if (BytesUsed < BufferBytesLeft) { + OutBufCur += BytesUsed; + return *this; + } + + // Otherwise, we overflowed and the return value tells us the size to try + // again with. + NextBufferSize = BytesUsed; + } + + // If we got here, we didn't have enough space in the output buffer for the + // string. Try printing into a SmallVector that is resized to have enough + // space. Iterate until we win. + SmallVector V; + + while (1) { + V.resize(NextBufferSize); + + // Try formatting into the SmallVector. + unsigned BytesUsed = Fmt.print(&V[0], NextBufferSize); + + // If BytesUsed fit into the vector, we win. + if (BytesUsed < NextBufferSize) + return write(&V[0], BytesUsed); + + // Otherwise, try again with a new size. + assert(BytesUsed > NextBufferSize && "Didn't grow buffer!?"); + NextBufferSize = BytesUsed; + } +} + +//===----------------------------------------------------------------------===// +// Formatted Output +//===----------------------------------------------------------------------===// + +// Out of line virtual method. +void format_object_base::home() { +} + //===----------------------------------------------------------------------===// // raw_fd_ostream //===----------------------------------------------------------------------===//