diff options
-rw-r--r-- | include/llvm/Support/Format.h | 61 | ||||
-rw-r--r-- | lib/Support/raw_ostream.cpp | 10 | ||||
-rw-r--r-- | unittests/Support/raw_ostream_test.cpp | 27 |
3 files changed, 59 insertions, 39 deletions
diff --git a/include/llvm/Support/Format.h b/include/llvm/Support/Format.h index 2ab097faf5..df03f66ddc 100644 --- a/include/llvm/Support/Format.h +++ b/include/llvm/Support/Format.h @@ -36,6 +36,10 @@ class format_object_base { protected: const char *Fmt; virtual void home(); // Out of line virtual method. + + /// snprint - Call snprintf() for this object, on the given buffer and size. + virtual int snprint(char *Buffer, unsigned BufferSize) const = 0; + public: format_object_base(const char *fmt) : Fmt(fmt) {} virtual ~format_object_base() {} @@ -43,7 +47,23 @@ public: /// 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; + unsigned print(char *Buffer, unsigned BufferSize) const { + assert(BufferSize && "Invalid buffer size!"); + + // Print the string, leaving room for the terminating null. + int N = snprint(Buffer, BufferSize); + + // VC++ and old GlibC return negative on overflow, just double the size. + if (N < 0) + return BufferSize*2; + + // Other impls yield number of bytes needed, not including the final '\0'. + if (unsigned(N) >= BufferSize) + return N+1; + + // Otherwise N is the length of output (not including the final '\0'). + return N; + } }; /// format_object1 - This is a templated helper class used by the format @@ -58,17 +78,8 @@ public: : 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; + virtual int snprint(char *Buffer, unsigned BufferSize) const { + return snprintf(Buffer, BufferSize, Fmt, Val); } }; @@ -85,17 +96,8 @@ public: : format_object_base(fmt), Val1(val1), Val2(val2) { } - /// 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, Val1, Val2); - 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; + virtual int snprint(char *Buffer, unsigned BufferSize) const { + return snprintf(Buffer, BufferSize, Fmt, Val1, Val2); } }; @@ -113,17 +115,8 @@ public: : format_object_base(fmt), Val1(val1), Val2(val2), Val3(val3) { } - /// 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, Val1, Val2, Val3); - 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; + virtual int snprint(char *Buffer, unsigned BufferSize) const { + return snprintf(Buffer, BufferSize, Fmt, Val1, Val2, Val3); } }; diff --git a/lib/Support/raw_ostream.cpp b/lib/Support/raw_ostream.cpp index 2181ccec0b..221859d533 100644 --- a/lib/Support/raw_ostream.cpp +++ b/lib/Support/raw_ostream.cpp @@ -253,12 +253,12 @@ 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. size_t NextBufferSize = 127; - if (OutBufEnd-OutBufCur > 3) { - size_t BufferBytesLeft = OutBufEnd-OutBufCur; + size_t BufferBytesLeft = OutBufEnd - OutBufCur; + if (BufferBytesLeft > 3) { size_t BytesUsed = Fmt.print(OutBufCur, BufferBytesLeft); // Common case is that we have plenty of space. - if (BytesUsed < BufferBytesLeft) { + if (BytesUsed <= BufferBytesLeft) { OutBufCur += BytesUsed; return *this; } @@ -277,11 +277,11 @@ raw_ostream &raw_ostream::operator<<(const format_object_base &Fmt) { V.resize(NextBufferSize); // Try formatting into the SmallVector. - size_t BytesUsed = Fmt.print(&V[0], NextBufferSize); + size_t BytesUsed = Fmt.print(V.data(), NextBufferSize); // If BytesUsed fit into the vector, we win. if (BytesUsed <= NextBufferSize) - return write(&V[0], BytesUsed); + return write(V.data(), BytesUsed); // Otherwise, try again with a new size. assert(BytesUsed > NextBufferSize && "Didn't grow buffer!?"); diff --git a/unittests/Support/raw_ostream_test.cpp b/unittests/Support/raw_ostream_test.cpp index 3e22a0569b..52639ba7a2 100644 --- a/unittests/Support/raw_ostream_test.cpp +++ b/unittests/Support/raw_ostream_test.cpp @@ -8,6 +8,8 @@ //===----------------------------------------------------------------------===// #include "gtest/gtest.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; @@ -20,6 +22,23 @@ template<typename T> std::string printToString(const T &Value) { return res; } +/// printToString - Print the given value to a stream which only has \arg +/// BytesLeftInBuffer bytes left in the buffer. This is useful for testing edge +/// cases in the buffer handling logic. +template<typename T> std::string printToString(const T &Value, + unsigned BytesLeftInBuffer) { + // FIXME: This is relying on internal knowledge of how raw_ostream works to + // get the buffer position right. + SmallString<256> SVec; + assert(BytesLeftInBuffer < 256 && "Invalid buffer count!"); + llvm::raw_svector_ostream OS(SVec); + unsigned StartIndex = 256 - BytesLeftInBuffer; + for (unsigned i = 0; i != StartIndex; ++i) + OS << '?'; + OS << Value; + return OS.str().substr(StartIndex); +} + template<typename T> std::string printToStringUnbuffered(const T &Value) { std::string res; llvm::raw_string_ostream OS(res); @@ -90,4 +109,12 @@ TEST(raw_ostreamTest, Types_Unbuffered) { EXPECT_EQ("-9223372036854775808", printToStringUnbuffered(INT64_MIN)); } +TEST(raw_ostreamTest, BufferEdge) { + EXPECT_EQ("1.20", printToString(format("%.2f", 1.2), 1)); + EXPECT_EQ("1.20", printToString(format("%.2f", 1.2), 2)); + EXPECT_EQ("1.20", printToString(format("%.2f", 1.2), 3)); + EXPECT_EQ("1.20", printToString(format("%.2f", 1.2), 4)); + EXPECT_EQ("1.20", printToString(format("%.2f", 1.2), 10)); +} + } |