summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/llvm/Support/Format.h61
-rw-r--r--lib/Support/raw_ostream.cpp10
-rw-r--r--unittests/Support/raw_ostream_test.cpp27
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));
+}
+
}