Refactored InfoSink. I have replaced most instances of sprintf with std::ostringstream to make it safer. I have made sure that everything still compiles and passes conformance tests.
Review URL: http://codereview.appspot.com/1391041

git-svn-id: https://angleproject.googlecode.com/svn/trunk@322 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/compiler/Common.h b/src/compiler/Common.h
index 79c5871..1c1e5c6 100644
--- a/src/compiler/Common.h
+++ b/src/compiler/Common.h
@@ -7,16 +7,16 @@
 #ifndef _COMMON_INCLUDED_
 #define _COMMON_INCLUDED_
 
-#include <assert.h>
-#include <stdio.h>
-
 #include <map>
+#include <sstream>
 #include <string>
 #include <vector>
 
 #include "compiler/PoolAlloc.h"
 
 typedef int TSourceLoc;
+const unsigned int SourceLocLineMask = 0xffff;
+const unsigned int SourceLocStringShift = 16;
 
 //
 // Put POOL_ALLOCATOR_NEW_DELETE in base classes to make them use this scheme.
@@ -35,7 +35,8 @@
 // Pool version of string.
 //
 typedef pool_allocator<char> TStringAllocator;
-typedef std::basic_string <char, std::char_traits<char>, TStringAllocator > TString;
+typedef std::basic_string <char, std::char_traits<char>, TStringAllocator> TString;
+typedef std::basic_ostringstream<char, std::char_traits<char>, TStringAllocator> TStringStream;
 inline TString* NewPoolTString(const char* s)
 {
 	void* memory = GlobalPoolAllocator.allocate(sizeof(TString));
@@ -43,6 +44,13 @@
 }
 
 //
+// Persistent string memory.  Should only be used for strings that survive
+// across compiles/links.
+//
+#define TPersistString std::string
+#define TPersistStringStream std::ostringstream
+
+//
 // Pool allocator versions of vectors, lists, and maps
 //
 template <class T> class TVector : public std::vector<T, pool_allocator<T> > {
@@ -63,49 +71,6 @@
     TMap(const tAllocator& a) : std::map<K, D, CMP, tAllocator>(std::map<K, D, CMP, tAllocator>::key_compare(), a) {}
 };
 
-//
-// Persistent string memory.  Should only be used for strings that survive
-// across compiles/links.
-//
-typedef std::basic_string<char> TPersistString;
-
-//
-// Create a TString object from an integer.
-//
-inline const TString String(const int i, const int base = 10)
-{
-    char text[16];     // 32 bit ints are at most 10 digits in base 10
-    
-    #ifdef _WIN32
-        _itoa(i, text, base);
-    #else
-        // we assume base 10 for all cases
-        sprintf(text, "%d", i);
-    #endif
-
-    return text;
-}
-
-const unsigned int SourceLocLineMask = 0xffff;
-const unsigned int SourceLocStringShift = 16;
-
-__inline TPersistString FormatSourceLoc(const TSourceLoc loc)
-{
-    char locText[64];
-
-    int string = loc >> SourceLocStringShift;
-    int line = loc & SourceLocLineMask;
-
-    if (line)
-        sprintf(locText, "%d:%d", string, line);
-    else
-        sprintf(locText, "%d:? ", string);
-
-    return TPersistString(locText);
-}
-
-
 typedef TMap<TString, TString> TPragmaTable;
-typedef TMap<TString, TString>::tAllocator TPragmaTableAllocator;
 
 #endif // _COMMON_INCLUDED_
diff --git a/src/compiler/ConstantUnion.h b/src/compiler/ConstantUnion.h
index 1fdb61d..cf531ea 100644
--- a/src/compiler/ConstantUnion.h
+++ b/src/compiler/ConstantUnion.h
@@ -7,6 +7,7 @@
 #ifndef _CONSTANT_UNION_INCLUDED_
 #define _CONSTANT_UNION_INCLUDED_
 
+#include <assert.h>
 
 class ConstantUnion {
 public:
diff --git a/src/compiler/InfoSink.cpp b/src/compiler/InfoSink.cpp
index 6690d88..317a88f 100644
--- a/src/compiler/InfoSink.cpp
+++ b/src/compiler/InfoSink.cpp
@@ -6,50 +6,54 @@
 
 #include "compiler/InfoSink.h"
 
-#ifdef _WIN32
-    #include <windows.h>
-#endif
-
-void TInfoSinkBase::append(const char *s)           
-{
-    if (outputStream & EString) {
-        checkMem(strlen(s)); 
-        sink.append(s); 
+void TInfoSinkBase::prefix(TPrefixType message) {
+    switch(message) {
+        case EPrefixNone:
+            break;
+        case EPrefixWarning:
+            sink.append("WARNING: ");
+            break;
+        case EPrefixError:
+            sink.append("ERROR: ");
+            break;
+        case EPrefixInternalError:
+            sink.append("INTERNAL ERROR: ");
+            break;
+        case EPrefixUnimplemented:
+            sink.append("UNIMPLEMENTED: ");
+            break;
+        case EPrefixNote:
+            sink.append("NOTE: ");
+            break;
+        default:
+            sink.append("UNKOWN ERROR: ");
+            break;
     }
-
-    if (outputStream & EStdOut)
-        fprintf(stdout, "%s", s);
 }
 
-void TInfoSinkBase::append(int count, char c)       
-{ 
-    if (outputStream & EString) {
-        checkMem(count);         
-        sink.append(count, c); 
-    }
+void TInfoSinkBase::location(TSourceLoc loc) {
+    int string = loc >> SourceLocStringShift;
+    int line = loc & SourceLocLineMask;
 
-    if (outputStream & EStdOut)
-        fprintf(stdout, "%c", c);
+    TPersistStringStream stream;
+    if (line)
+        stream << string << ":" << line;
+    else
+        stream << string << ":? ";
+    stream << ": ";
+
+    sink.append(stream.str());
 }
 
-void TInfoSinkBase::append(const TPersistString& t) 
-{ 
-    if (outputStream & EString) {
-        checkMem(t.size());  
-        sink.append(t); 
-    }
-
-    if (outputStream & EStdOut)
-        fprintf(stdout, "%s", t.c_str());
+void TInfoSinkBase::message(TPrefixType message, const char* s) {
+    prefix(message);
+    sink.append(s);
+    sink.append("\n");
 }
 
-void TInfoSinkBase::append(const TString& t)
-{ 
-    if (outputStream & EString) {
-        checkMem(t.size());  
-        sink.append(t.c_str()); 
-    }
-
-    if (outputStream & EStdOut)
-        fprintf(stdout, "%s", t.c_str());
+void TInfoSinkBase::message(TPrefixType message, const char* s, TSourceLoc loc) {
+    prefix(message);
+    location(loc);
+    sink.append(s);
+    sink.append("\n");
 }
diff --git a/src/compiler/InfoSink.h b/src/compiler/InfoSink.h
index 6ddf6b0..5c25c03 100644
--- a/src/compiler/InfoSink.h
+++ b/src/compiler/InfoSink.h
@@ -8,7 +8,6 @@
 #define _INFOSINK_INCLUDED_
 
 #include <math.h>
-
 #include "compiler/Common.h"
 
 // Returns the fractional part of the given floating-point number.
@@ -30,11 +29,6 @@
     EPrefixNote
 };
 
-enum TOutputStream {
-    ENull = 0,
-    EStdOut = 0x01,
-    EString = 0x02,
-};
 //
 // Encapsulate info logs for all objects that have them.
 //
@@ -43,72 +37,68 @@
 //
 class TInfoSinkBase {
 public:
-    TInfoSinkBase() : outputStream(EString) {}
-    void erase() { sink.erase(); }
-    TInfoSinkBase& operator<<(const TPersistString& t) { append(t); return *this; }
-    TInfoSinkBase& operator<<(char c)                  { append(1, c); return *this; }
-    TInfoSinkBase& operator<<(const char* s)           { append(s); return *this; }
-    TInfoSinkBase& operator<<(int n)                   { append(String(n)); return *this; }
-    TInfoSinkBase& operator<<(const unsigned int n)    { append(String(n)); return *this; }
-    TInfoSinkBase& operator<<(float n) {
-        char buf[40];
-        // Make sure that at least one decimal point is written. If a number
-        // does not have a fractional part, %g does not written the decimal
-        // portion which gets interpreted as integer by the compiler.
-        const char* format = fractionalPart(n) == 0.0f ? "%.1f" : "%.8g";
-        sprintf(buf, format, n);
-        append(buf); 
+    TInfoSinkBase() {}
+
+    template <typename T>
+    TInfoSinkBase& operator<<(const T& t) {
+        TPersistStringStream stream;
+        stream << t;
+        sink.append(stream.str());
         return *this;
     }
-    TInfoSinkBase& operator+(const TPersistString& t)  { append(t); return *this; }
-    TInfoSinkBase& operator+(const TString& t)         { append(t); return *this; }
-    TInfoSinkBase& operator<<(const TString& t)        { append(t); return *this; }
-    TInfoSinkBase& operator+(const char* s)            { append(s); return *this; }
-    const char* c_str() const { return sink.c_str(); }
-    void prefix(TPrefixType message) {
-        switch(message) {
-        case EPrefixNone:                                      break;
-        case EPrefixWarning:       append("WARNING: ");        break;
-        case EPrefixError:         append("ERROR: ");          break;
-        case EPrefixInternalError: append("INTERNAL ERROR: "); break;
-        case EPrefixUnimplemented: append("UNIMPLEMENTED: ");  break;
-        case EPrefixNote:          append("NOTE: ");           break;
-        default:                   append("UNKOWN ERROR: ");   break;
+    // Override << operator for specific types. It is faster to append strings
+    // and characters directly to the sink.
+    TInfoSinkBase& operator<<(char c) {
+        sink.append(1, c);
+        return *this;
+    }
+    TInfoSinkBase& operator<<(const char* str) {
+        sink.append(str);
+        return *this;
+    }
+    TInfoSinkBase& operator<<(const TPersistString& str) {
+        sink.append(str);
+        return *this;
+    }
+    TInfoSinkBase& operator<<(const TString& str) {
+        sink.append(str.c_str());
+        return *this;
+    }
+    // Make sure floats are written with correct precision.
+    TInfoSinkBase& operator<<(float f) {
+        // Make sure that at least one decimal point is written. If a number
+        // does not have a fractional part, the default precision format does
+        // not write the decimal portion which gets interpreted as integer by
+        // the compiler.
+        TPersistStringStream stream;
+        if (fractionalPart(f) == 0.0f) {
+            stream.precision(2);
+            stream << std::showpoint << f;
+        } else {
+            stream << f;
         }
+        sink.append(stream.str());
+        return *this;
     }
-    void location(TSourceLoc loc) {
-        append(FormatSourceLoc(loc).c_str());
-        append(": ");
-    }
-    void message(TPrefixType message, const char* s) {
-        prefix(message);
-        append(s);
-        append("\n");
-    }
-    void message(TPrefixType message, const char* s, TSourceLoc loc) {
-        prefix(message);
-        location(loc);
-        append(s);
-        append("\n");
-    }
-    
-    void setOutputStream(int output = 4)
-    {
-        outputStream = output;
+    // Write boolean values as their names instead of integral value.
+    TInfoSinkBase& operator<<(bool b) {
+        const char* str = b ? "true" : "false";
+        sink.append(str);
+        return *this;
     }
 
-protected:
-    void append(const char *s); 
+    void erase() { sink.clear(); }
 
-    void append(int count, char c);
-    void append(const TPersistString& t);
-    void append(const TString& t);
+    const TPersistString& str() const { return sink; }
+    const char* c_str() const { return sink.c_str(); }
 
-    void checkMem(size_t growth) { if (sink.capacity() < sink.size() + growth + 2)  
-                                       sink.reserve(sink.capacity() +  sink.capacity() / 2); }
-    void appendToStream(const char* s);
+    void prefix(TPrefixType message);
+    void location(TSourceLoc loc);
+    void message(TPrefixType message, const char* s);
+    void message(TPrefixType message, const char* s, TSourceLoc loc);
+
+private:
     TPersistString sink;
-    int outputStream;
 };
 
 class TInfoSink {
diff --git a/src/compiler/Initialize.cpp b/src/compiler/Initialize.cpp
index ab61528..ebd23fb 100644
--- a/src/compiler/Initialize.cpp
+++ b/src/compiler/Initialize.cpp
@@ -477,37 +477,21 @@
 
 void TBuiltIns::initialize(const TBuiltInResource &resources)
 {
-    TString builtIns;
+    TStringStream builtIns;
 
     // Implementation dependent constants
-    char builtInConstant[80];
+    builtIns << "const int gl_MaxVertexAttribs = " << resources.maxVertexAttribs << ";";
+    builtIns << "const int gl_MaxVertexUniformVectors = " << resources.maxVertexUniformVectors << ";";
 
-    sprintf(builtInConstant, "const int  gl_MaxVertexAttribs = %d;", resources.maxVertexAttribs);
-    builtIns.append(TString(builtInConstant));
+    builtIns << "const int gl_MaxVaryingVectors = " << resources.maxVaryingVectors << ";";
+    builtIns << "const int gl_MaxVertexTextureImageUnits = " << resources.maxVertexTextureImageUnits << ";";
+    builtIns << "const int gl_MaxCombinedTextureImageUnits = " << resources.maxCombinedTextureImageUnits << ";";
+    builtIns << "const int gl_MaxTextureImageUnits = " << resources.maxTextureImageUnits << ";";
+    builtIns << "const int gl_MaxFragmentUniformVectors = " << resources.maxFragmentUniformVectors << ";";
+    builtIns << "const int gl_MaxDrawBuffers = " << resources.maxDrawBuffers << ";";
 
-    sprintf(builtInConstant, "const int  gl_MaxVertexUniformVectors = %d;", resources.maxVertexUniformVectors);
-    builtIns.append(TString(builtInConstant));       
-
-    sprintf(builtInConstant, "const int  gl_MaxVaryingVectors = %d;", resources.maxVaryingVectors);
-    builtIns.append(TString(builtInConstant));        
-
-    sprintf(builtInConstant, "const int  gl_MaxVertexTextureImageUnits = %d;", resources.maxVertexTextureImageUnits);
-    builtIns.append(TString(builtInConstant));        
-
-    sprintf(builtInConstant, "const int  gl_MaxCombinedTextureImageUnits = %d;", resources.maxCombinedTextureImageUnits);
-    builtIns.append(TString(builtInConstant));        
-
-    sprintf(builtInConstant, "const int  gl_MaxTextureImageUnits = %d;", resources.maxTextureImageUnits);
-    builtIns.append(TString(builtInConstant));
-
-    sprintf(builtInConstant, "const int  gl_MaxFragmentUniformVectors = %d;", resources.maxFragmentUniformVectors);
-    builtIns.append(TString(builtInConstant));
-
-    sprintf(builtInConstant, "const int  gl_MaxDrawBuffers = %d;", resources.maxDrawBuffers);
-    builtIns.append(TString(builtInConstant));
-
-    builtInStrings[EShLangFragment].push_back(builtIns);
-    builtInStrings[EShLangVertex].push_back(builtIns);
+    builtInStrings[EShLangFragment].push_back(builtIns.str());
+    builtInStrings[EShLangVertex].push_back(builtIns.str());
 }
 
 void IdentifyBuiltIns(EShLanguage language, TSymbolTable& symbolTable)
diff --git a/src/compiler/OutputGLSL.cpp b/src/compiler/OutputGLSL.cpp
index 66ff473..4209b40 100644
--- a/src/compiler/OutputGLSL.cpp
+++ b/src/compiler/OutputGLSL.cpp
@@ -183,12 +183,7 @@
             {
                 case EbtFloat: out << pConstUnion->getFConst(); break;
                 case EbtInt: out << pConstUnion->getIConst(); break;
-                case EbtBool:
-                    if (pConstUnion->getBConst())
-                        out << "true";
-                    else
-                        out << "false";
-                    break;
+                case EbtBool: out << pConstUnion->getBConst(); break;
                 default: UNREACHABLE();
             }
             if (i != size - 1) out << ", ";
diff --git a/src/compiler/OutputHLSL.cpp b/src/compiler/OutputHLSL.cpp
index 66d0e83..774d596 100644
--- a/src/compiler/OutputHLSL.cpp
+++ b/src/compiler/OutputHLSL.cpp
@@ -2085,16 +2085,7 @@
             {
               case EbtFloat: out << constUnion->getFConst(); break;
               case EbtInt:   out << constUnion->getIConst(); break;
-              case EbtBool:
-                if (constUnion->getBConst())
-                {
-                    out << "true";
-                }
-                else
-                {
-                    out << "false";
-                }
-                break;
+              case EbtBool:  out << constUnion->getBConst(); break;
               default: UNREACHABLE();
             }
 
diff --git a/src/compiler/SymbolTable.h b/src/compiler/SymbolTable.h
index fa6e64d..87c6e8a 100644
--- a/src/compiler/SymbolTable.h
+++ b/src/compiler/SymbolTable.h
@@ -30,6 +30,7 @@
 //   are tracked in the intermediate representation, not the symbol table.
 //
 
+#include <assert.h>
 #include "compiler/Common.h"
 #include "compiler/intermediate.h"
 #include "compiler/InfoSink.h"
diff --git a/src/compiler/Types.h b/src/compiler/Types.h
index fca0ac1..813250a 100644
--- a/src/compiler/Types.h
+++ b/src/compiler/Types.h
@@ -7,6 +7,7 @@
 #ifndef _TYPES_INCLUDED
 #define _TYPES_INCLUDED
 
+#include <assert.h>
 #include "compiler/Common.h"
 #include "compiler/BaseTypes.h"
 
diff --git a/src/compiler/intermOut.cpp b/src/compiler/intermOut.cpp
index 70ead52..db042dd 100644
--- a/src/compiler/intermOut.cpp
+++ b/src/compiler/intermOut.cpp
@@ -37,22 +37,20 @@
 
 TString TType::getCompleteString() const
 {
-    char buf[100];
-    char *p = &buf[0];
+    TStringStream stream;
 
     if (qualifier != EvqTemporary && qualifier != EvqGlobal)
-        p += sprintf(p, "%s %s ", getQualifierString(), getPrecisionString());
+        stream << getQualifierString() << " " << getPrecisionString() << " ";
     if (array)
-        p += sprintf(p, "array of ");
+        stream << "array of ";
     if (matrix)
-        p += sprintf(p, "%dX%d matrix of ", size, size);
+        stream << size << "X" << size << " matrix of ";
     else if (size > 1)
-        p += sprintf(p, "%d-component vector of ", size);
+        stream << size << "-component vector of ";
 
-    sprintf(p, "%s", getBasicString());
-
-    return TString(buf);
-}   
+    stream << getBasicString();
+    return stream.str();
+}
 
 //
 // Helper functions for printing, not part of traversing.
@@ -62,7 +60,7 @@
 {
     int i;
 
-    infoSink.debug << FormatSourceLoc(node->getLine());
+    infoSink.debug.location(node->getLine());
 
     for (i = 0; i < depth; ++i)
         infoSink.debug << "  ";
@@ -81,12 +79,8 @@
 {
     OutputTreeText(infoSink, node, depth);
 
-    char buf[100];
-    sprintf(buf, "'%s' (%s)\n",
-        node->getSymbol().c_str(),
-        node->getCompleteString().c_str());
-
-    infoSink.debug << buf;
+    infoSink.debug << "'" << node->getSymbol() << "' ";
+    infoSink.debug << "(" << node->getCompleteString() << ")\n";
 }
 
 bool TOutputTraverser::visitBinary(Visit visit, TIntermBinary* node)
@@ -317,7 +311,6 @@
 
     int size = node->getType().getObjectSize();
 
-    char buf[300];
     for (int i = 0; i < size; i++) {
         OutputTreeText(out, node, depth);
         switch (node->getUnionArrayPointer()[i].getType()) {
@@ -331,12 +324,12 @@
                 out.debug << "\n";
                 break;
             case EbtFloat:
-                sprintf(buf, "%f (%s)", node->getUnionArrayPointer()[i].getFConst(), "const float");
-                out.debug << buf << "\n";
+                out.debug << node->getUnionArrayPointer()[i].getFConst();
+                out.debug << " (const float)\n";
                 break;
             case EbtInt:
-                sprintf(buf, "%d (%s)", node->getUnionArrayPointer()[i].getIConst(), "const int");
-                out.debug << buf << "\n";
+                out.debug << node->getUnionArrayPointer()[i].getIConst();
+                out.debug << " (const int)\n";
                 break;
             default:
                 out.info.message(EPrefixInternalError, "Unknown constant", node->getLine());
diff --git a/src/compiler/parseConst.cpp b/src/compiler/parseConst.cpp
index 4b130e9..833d429 100644
--- a/src/compiler/parseConst.cpp
+++ b/src/compiler/parseConst.cpp
@@ -63,9 +63,10 @@
     TQualifier qualifier = node->getType().getQualifier();
     
     if (qualifier != EvqConst) {
-        char buf[200];
-        sprintf(buf, "'constructor' : assigning non-constant to %s", type.getCompleteString().c_str());
-        infoSink.info.message(EPrefixError, buf, node->getLine());
+        TString buf;
+        buf.append("'constructor' : assigning non-constant to ");
+        buf.append(type.getCompleteString());
+        infoSink.info.message(EPrefixError, buf.c_str(), node->getLine());
         error = true;
         return false;  
     }
@@ -77,9 +78,10 @@
 
 bool TConstTraverser::visitUnary(Visit visit, TIntermUnary* node)
 {
-    char buf[200];
-    sprintf(buf, "'constructor' : assigning non-constant to '%s'", type.getCompleteString().c_str());
-    infoSink.info.message(EPrefixError, buf, node->getLine());
+    TString buf;
+    buf.append("'constructor' : assigning non-constant to ");
+    buf.append(type.getCompleteString());
+    infoSink.info.message(EPrefixError, buf.c_str(), node->getLine());
     error = true;
     return false;  
 }
@@ -87,9 +89,10 @@
 bool TConstTraverser::visitAggregate(Visit visit, TIntermAggregate* node)
 {
     if (!node->isConstructor() && node->getOp() != EOpComma) {
-        char buf[200];
-        sprintf(buf, "'constructor' : assigning non-constant to '%s'", type.getCompleteString().c_str());
-        infoSink.info.message(EPrefixError, buf, node->getLine());
+        TString buf;
+        buf.append("'constructor' : assigning non-constant to ");
+        buf.append(type.getCompleteString());
+        infoSink.info.message(EPrefixError, buf.c_str(), node->getLine());
         error = true;
         return false;  
     }