Skip to content
This repository has been archived by the owner on Jan 1, 2023. It is now read-only.

Commit

Permalink
Merge 79862 from mainline.
Browse files Browse the repository at this point in the history
Fix off-by-one in llvm::Format::print.
 - This also shortens the Format.h implementation, and uses the print buffer
  fully (it was wasting a character).

 - This manifested as llvm-test failures, because one side effect was that
  raw_ostream would write garbage '\x00' values into the output stream if it
  happened that the string was at the end of the buffer. This meant that grep
  would report 'Binary file matches', which meant the silly pattern matching
  llvm-test eventually does would fail. Cute. :)



git-svn-id: http://llvm.org/svn/llvm-project/llvm/branches/release_26@80563 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
Tanya Lattner committed Aug 31, 2009
1 parent a8ac6ef commit bc1143d
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 39 deletions.
61 changes: 27 additions & 34 deletions include/llvm/Support/Format.h
Expand Up @@ -36,14 +36,34 @@ 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() {}

/// 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
Expand All @@ -58,17 +78,8 @@ class format_object1 : public format_object_base {
: 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);
}
};

Expand All @@ -85,17 +96,8 @@ class format_object2 : public format_object_base {
: 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);
}
};

Expand All @@ -113,17 +115,8 @@ class format_object3 : public format_object_base {
: 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);
}
};

Expand Down
10 changes: 5 additions & 5 deletions lib/Support/raw_ostream.cpp
Expand Up @@ -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;
}
Expand All @@ -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!?");
Expand Down
27 changes: 27 additions & 0 deletions unittests/Support/raw_ostream_test.cpp
Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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));
}

}

0 comments on commit bc1143d

Please sign in to comment.