Bug 1060419 - add PrintfTarget class to jsprf.h, r=froydnj
authorTom Tromey <tom@tromey.com>
Thu, 12 Jan 2017 15:35:55 -0700
changeset 343527 4182949e0fbce045a1c877475da09e1c2cc18bec
parent 343526 a69d8969642d40db1312c0b4b9bde8a5d6422556
child 343528 83b8f4166e2f3d29b90312030fa13c5ccc3188db
push id31381
push userkwierso@gmail.com
push dateFri, 17 Feb 2017 20:45:51 +0000
treeherdermozilla-central@f302def88fe5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1060419
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1060419 - add PrintfTarget class to jsprf.h, r=froydnj MozReview-Commit-ID: 7ce959WmxoE
js/src/jsprf.cpp
js/src/jsprf.h
--- a/js/src/jsprf.cpp
+++ b/js/src/jsprf.cpp
@@ -34,19 +34,19 @@ using namespace js;
 #ifdef HAVE_VA_COPY
 #define VARARGS_ASSIGN(foo, bar)        VA_COPY(foo, bar)
 #elif defined(HAVE_VA_LIST_AS_ARRAY)
 #define VARARGS_ASSIGN(foo, bar)        foo[0] = bar[0]
 #else
 #define VARARGS_ASSIGN(foo, bar)        (foo) = (bar)
 #endif
 
-struct SprintfState
+struct SprintfState : public mozilla::PrintfTarget
 {
-    bool (*stuff)(SprintfState* ss, const char* sp, size_t len);
+    virtual bool append(const char* sp, size_t len);
 
     char* base;
     char* cur;
     size_t maxlen;
 };
 
 /*
  * Numbered Argument State
@@ -75,56 +75,50 @@ typedef mozilla::Vector<NumArgState, 20,
 #define TYPE_UNKNOWN    20
 
 #define FLAG_LEFT       0x1
 #define FLAG_SIGNED     0x2
 #define FLAG_SPACED     0x4
 #define FLAG_ZEROS      0x8
 #define FLAG_NEG        0x10
 
-inline bool
-generic_write(SprintfState* ss, const char* src, size_t srclen)
-{
-    return (*ss->stuff)(ss, src, srclen);
-}
-
 // Fill into the buffer using the data in src
-static bool
-fill2(SprintfState* ss, const char* src, int srclen, int width, int flags)
+bool
+mozilla::PrintfTarget::fill2(const char* src, int srclen, int width, int flags)
 {
     char space = ' ';
 
     width -= srclen;
     if (width > 0 && (flags & FLAG_LEFT) == 0) {    // Right adjusting
         if (flags & FLAG_ZEROS)
             space = '0';
         while (--width >= 0) {
-            if (!(*ss->stuff)(ss, &space, 1))
+            if (!emit(&space, 1))
                 return false;
         }
     }
 
     // Copy out the source data
-    if (!generic_write(ss, src, srclen))
+    if (!emit(src, srclen))
         return false;
 
     if (width > 0 && (flags & FLAG_LEFT) != 0) {    // Left adjusting
         while (--width >= 0) {
-            if (!(*ss->stuff)(ss, &space, 1))
+            if (!emit(&space, 1))
                 return false;
         }
     }
     return true;
 }
 
 /*
  * Fill a number. The order is: optional-sign zero-filling conversion-digits
  */
-static bool
-fill_n(SprintfState* ss, const char* src, int srclen, int width, int prec, int type, int flags)
+bool
+mozilla::PrintfTarget::fill_n(const char* src, int srclen, int width, int prec, int type, int flags)
 {
     int zerowidth = 0;
     int precwidth = 0;
     int signwidth = 0;
     int leftspaces = 0;
     int rightspaces = 0;
     int cvtwidth;
     char sign;
@@ -164,43 +158,44 @@ fill_n(SprintfState* ss, const char* src
         }
     } else {
         if (width > cvtwidth) {
             // Space filling on the left (i.e. right adjusting)
             leftspaces = width - cvtwidth;
         }
     }
     while (--leftspaces >= 0) {
-        if (!(*ss->stuff)(ss, " ", 1))
+        if (!emit(" ", 1))
             return false;
     }
     if (signwidth) {
-        if (!(*ss->stuff)(ss, &sign, 1))
+        if (!emit(&sign, 1))
             return false;
     }
     while (--precwidth >= 0) {
-        if (!(*ss->stuff)(ss, "0", 1))
+        if (!emit("0", 1))
             return false;
     }
     while (--zerowidth >= 0) {
-        if (!(*ss->stuff)(ss, "0", 1))
+        if (!emit("0", 1))
             return false;
     }
-    if (!(*ss->stuff)(ss, src, uint32_t(srclen)))
+    if (!emit(src, uint32_t(srclen)))
         return false;
     while (--rightspaces >= 0) {
-        if (!(*ss->stuff)(ss, " ", 1))
+        if (!emit(" ", 1))
             return false;
     }
     return true;
 }
 
 /* Convert a long into its printable form. */
-static bool cvt_l(SprintfState* ss, long num, int width, int prec, int radix,
-                  int type, int flags, const char* hexp)
+bool
+mozilla::PrintfTarget::cvt_l(long num, int width, int prec, int radix,
+                             int type, int flags, const char* hexp)
 {
     char cvtbuf[100];
     char* cvt;
     int digits;
 
     // according to the man page this needs to happen
     if ((prec == 0) && (num == 0))
         return true;
@@ -218,22 +213,23 @@ static bool cvt_l(SprintfState* ss, long
     }
     if (digits == 0) {
         *--cvt = '0';
         digits++;
     }
 
     // Now that we have the number converted without its sign, deal with
     // the sign and zero padding.
-    return fill_n(ss, cvt, digits, width, prec, type, flags);
+    return fill_n(cvt, digits, width, prec, type, flags);
 }
 
 /* Convert a 64-bit integer into its printable form. */
-static bool cvt_ll(SprintfState* ss, int64_t num, int width, int prec, int radix,
-                   int type, int flags, const char* hexp)
+bool
+mozilla::PrintfTarget::cvt_ll(int64_t num, int width, int prec, int radix,
+                              int type, int flags, const char* hexp)
 {
     // According to the man page, this needs to happen.
     if (prec == 0 && num == 0)
         return true;
 
     // Converting decimal is a little tricky. In the unsigned case we
     // need to stop when we hit 10 digits. In the signed case, we can
     // stop when the number is zero.
@@ -251,24 +247,25 @@ static bool cvt_ll(SprintfState* ss, int
     }
     if (digits == 0) {
         *--cvt = '0';
         digits++;
     }
 
     // Now that we have the number converted without its sign, deal with
     // the sign and zero padding.
-    return fill_n(ss, cvt, digits, width, prec, type, flags);
+    return fill_n(cvt, digits, width, prec, type, flags);
 }
 
 /*
  * Convert a double precision floating point number into its printable
  * form.
  */
-static bool cvt_f(SprintfState* ss, double d, const char* fmt0, const char* fmt1)
+bool
+mozilla::PrintfTarget::cvt_f(double d, const char* fmt0, const char* fmt1)
 {
     char fin[20];
     char fout[300];
     int amount = fmt1 - fmt0;
 
     MOZ_ASSERT((amount > 0) && (amount < (int)sizeof(fin)));
     if (amount >= (int)sizeof(fin)) {
         // Totally bogus % command to sprintf. Just ignore it
@@ -284,43 +281,39 @@ static bool cvt_f(SprintfState* ss, doub
         while (*p) {
             MOZ_ASSERT(*p != 'L');
             p++;
         }
     }
 #endif
     SprintfLiteral(fout, fin, d);
 
-    return (*ss->stuff)(ss, fout, strlen(fout));
+    return emit(fout, strlen(fout));
 }
 
-static inline const char* generic_null_str(const char*) { return "(null)"; }
-
-static inline size_t generic_strlen(const char* s) { return strlen(s); }
-
 /*
  * Convert a string into its printable form.  "width" is the output
  * width. "prec" is the maximum number of characters of "s" to output,
  * where -1 means until NUL.
  */
-static bool
-cvt_s(SprintfState* ss, const char* s, int width, int prec, int flags)
+bool
+mozilla::PrintfTarget::cvt_s(const char* s, int width, int prec, int flags)
 {
     if (prec == 0)
         return true;
     if (!s)
-        s = generic_null_str(s);
+        s = "(null)";
 
     // Limit string length by precision value
-    int slen = int(generic_strlen(s));
+    int slen = int(strlen(s));
     if (0 < prec && prec < slen)
         slen = prec;
 
     // and away we go
-    return fill2(ss, s, slen, width, flags);
+    return fill2(s, slen, width, flags);
 }
 
 /*
  * BuildArgArray stands for Numbered Argument list Sprintf
  * for example,
  *      fmp = "%4$i, %2$d, %3s, %1d";
  * the number must start from 1, and no gap among them
  */
@@ -525,21 +518,18 @@ BuildArgArray(const char* fmt, va_list a
         }
 
         cn++;
     }
 
     return true;
 }
 
-/*
- * The workhorse sprintf code.
- */
-static bool
-dosprintf(SprintfState* ss, const char* fmt, va_list ap)
+bool
+mozilla::PrintfTarget::vprint(const char* fmt, va_list ap)
 {
     char c;
     int flags, width, prec, radix, type;
     union {
         char ch;
         int i;
         long l;
         long long ll;
@@ -562,30 +552,30 @@ dosprintf(SprintfState* ss, const char* 
     NumArgStateVector nas;
     if (!BuildArgArray(fmt, ap, nas)) {
         // the fmt contains error Numbered Argument format, jliu@netscape.com
         MOZ_CRASH("Bad format string");
     }
 
     while ((c = *fmt++) != 0) {
         if (c != '%') {
-            if (!(*ss->stuff)(ss, fmt - 1, 1))
+            if (!emit(fmt - 1, 1))
                 return false;
 
             continue;
         }
         fmt0 = fmt - 1;
 
         // Gobble up the % format string. Hopefully we have handled all
         // of the strange cases!
         flags = 0;
         c = *fmt++;
         if (c == '%') {
             // quoting a % with %%
-            if (!(*ss->stuff)(ss, fmt - 1, 1))
+            if (!emit(fmt - 1, 1))
                 return false;
 
             continue;
         }
 
         if (!nas.empty()) {
             // the fmt contains the Numbered Arguments feature
             i = 0;
@@ -725,17 +715,17 @@ dosprintf(SprintfState* ss, const char* 
                 if (u.l < 0) {
                     u.l = -u.l;
                     flags |= FLAG_NEG;
                 }
                 goto do_long;
               case TYPE_ULONG:
                 u.l = (long)va_arg(ap, unsigned long);
               do_long:
-                if (!cvt_l(ss, u.l, width, prec, radix, type, flags, hexp))
+                if (!cvt_l(u.l, width, prec, radix, type, flags, hexp))
                     return false;
 
                 break;
 
               case TYPE_LONGLONG:
                 u.ll = va_arg(ap, long long);
                 if (u.ll < 0) {
                     u.ll = -u.ll;
@@ -743,61 +733,61 @@ dosprintf(SprintfState* ss, const char* 
                 }
                 goto do_longlong;
               case TYPE_POINTER:
                 u.ll = (uintptr_t)va_arg(ap, void*);
                 goto do_longlong;
               case TYPE_ULONGLONG:
                 u.ll = va_arg(ap, unsigned long long);
               do_longlong:
-                if (!cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp))
+                if (!cvt_ll(u.ll, width, prec, radix, type, flags, hexp))
                     return false;
 
                 break;
             }
             break;
 
           case 'e':
           case 'E':
           case 'f':
           case 'g':
             u.d = va_arg(ap, double);
             if (!nas.empty()) {
                 i = fmt - dolPt;
                 if (i < int(sizeof(pattern))) {
                     pattern[0] = '%';
-                    js_memcpy(&pattern[1], dolPt, size_t(i));
-                    if (!cvt_f(ss, u.d, pattern, &pattern[i + 1]))
+                    memcpy(&pattern[1], dolPt, size_t(i));
+                    if (!cvt_f(u.d, pattern, &pattern[i + 1]))
                         return false;
                 }
             } else {
-                if (!cvt_f(ss, u.d, fmt0, fmt))
+                if (!cvt_f(u.d, fmt0, fmt))
                     return false;
             }
 
             break;
 
           case 'c':
             if ((flags & FLAG_LEFT) == 0) {
                 while (width-- > 1) {
-                    if (!(*ss->stuff)(ss, " ", 1))
+                    if (!emit(" ", 1))
                         return false;
                 }
             }
             switch (type) {
               case TYPE_SHORT:
               case TYPE_INTN:
                 u.ch = va_arg(ap, int);
-                if (!(*ss->stuff)(ss, &u.ch, 1))
+                if (!emit(&u.ch, 1))
                     return false;
                 break;
             }
             if (flags & FLAG_LEFT) {
                 while (width-- > 1) {
-                    if (!(*ss->stuff)(ss, " ", 1))
+                    if (!emit(" ", 1))
                         return false;
                 }
             }
             break;
 
           case 'p':
             type = TYPE_POINTER;
             radix = 16;
@@ -810,79 +800,90 @@ dosprintf(SprintfState* ss, const char* 
           case 'G':
             // XXX not supported I suppose
             MOZ_ASSERT(0);
             break;
 #endif
 
           case 's':
             u.s = va_arg(ap, const char*);
-            if (!cvt_s(ss, u.s, width, prec, flags))
+            if (!cvt_s(u.s, width, prec, flags))
                 return false;
             break;
 
           case 'n':
             u.ip = va_arg(ap, int*);
             if (u.ip) {
-                *u.ip = ss->cur - ss->base;
+                *u.ip = mEmitted;
             }
             break;
 
           default:
             // Not a % token after all... skip it
 #if 0
             MOZ_ASSERT(0);
 #endif
-            if (!(*ss->stuff)(ss, "%", 1))
+            if (!emit("%", 1))
                 return false;
-            if (!(*ss->stuff)(ss, fmt - 1, 1))
+            if (!emit(fmt - 1, 1))
                 return false;
         }
     }
 
     // Stuff trailing NUL
-    if (!(*ss->stuff)(ss, "\0", 1))
+    if (!emit("\0", 1))
         return false;
 
     return true;
 }
 
 /************************************************************************/
 
+bool
+mozilla::PrintfTarget::print(const char* format, ...)
+{
+    va_list ap;
+
+    va_start(ap, format);
+    bool result = vprint(format, ap);
+    va_end(ap);
+    return result;
+}
+
 /*
  * Stuff routine that automatically grows the js_malloc'd output buffer
  * before it overflows.
  */
-static bool
-GrowStuff(SprintfState* ss, const char* sp, size_t len)
+bool
+SprintfState::append(const char* sp, size_t len)
 {
     ptrdiff_t off;
     char* newbase;
     size_t newlen;
 
-    off = ss->cur - ss->base;
-    if (off + len >= ss->maxlen) {
+    off = cur - base;
+    if (off + len >= maxlen) {
         /* Grow the buffer */
-        newlen = ss->maxlen + ((len > 32) ? len : 32);
-        newbase = static_cast<char*>(js_realloc(ss->base, newlen));
+        newlen = maxlen + ((len > 32) ? len : 32);
+        newbase = static_cast<char*>(js_realloc(base, newlen));
         if (!newbase) {
             /* Ran out of memory */
             return false;
         }
-        ss->base = newbase;
-        ss->maxlen = newlen;
-        ss->cur = ss->base + off;
+        base = newbase;
+        maxlen = newlen;
+        cur = base + off;
     }
 
     /* Copy data */
     while (len) {
         --len;
-        *ss->cur++ = *sp++;
+        *cur++ = *sp++;
     }
-    MOZ_ASSERT(size_t(ss->cur - ss->base) <= ss->maxlen);
+    MOZ_ASSERT(size_t(cur - base) <= maxlen);
     return true;
 }
 
 /*
  * sprintf into a js_malloc'd buffer
  */
 char*
 mozilla::Smprintf(const char* fmt, ...)
@@ -905,21 +906,20 @@ mozilla::SmprintfFree(char* mem)
     js_free(mem);
 }
 
 char*
 mozilla::Vsmprintf(const char* fmt, va_list ap)
 {
     SprintfState ss;
 
-    ss.stuff = GrowStuff;
     ss.base = 0;
     ss.cur = 0;
     ss.maxlen = 0;
-    if (!dosprintf(&ss, fmt, ap)) {
+    if (!ss.vprint(fmt, ap)) {
         js_free(ss.base);
         return 0;
     }
     return ss.base;
 }
 
 char*
 mozilla::SmprintfAppend(char* last, const char* fmt, ...)
@@ -933,28 +933,27 @@ mozilla::SmprintfAppend(char* last, cons
     return rv;
 }
 
 char*
 mozilla::VsmprintfAppend(char* last, const char* fmt, va_list ap)
 {
     SprintfState ss;
 
-    ss.stuff = GrowStuff;
     if (last) {
         size_t lastlen = strlen(last);
         ss.base = last;
         ss.cur = last + lastlen;
         ss.maxlen = lastlen;
     } else {
         ss.base = 0;
         ss.cur = 0;
         ss.maxlen = 0;
     }
-    if (!dosprintf(&ss, fmt, ap)) {
+    if (!ss.vprint(fmt, ap)) {
         js_free(ss.base);
         return 0;
     }
     return ss.base;
 }
 
 #undef TYPE_SHORT
 #undef TYPE_USHORT
--- a/js/src/jsprf.h
+++ b/js/src/jsprf.h
@@ -22,16 +22,18 @@
 **           You should use PRI*SIZE macros instead
 **      %s - string
 **      %c - character
 **      %p - pointer (deals with machine dependent pointer size)
 **      %f - float
 **      %g - float
 */
 
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/SizePrintfMacros.h"
 #include "mozilla/Types.h"
 
 #include <stdarg.h>
 
 #include "jstypes.h"
 
@@ -61,16 +63,58 @@ extern MFBT_API char* SmprintfAppend(cha
     MOZ_FORMAT_PRINTF(2, 3);
 
 /*
 ** va_list forms of the above.
 */
 extern MFBT_API char* Vsmprintf(const char* fmt, va_list ap);
 extern MFBT_API char* VsmprintfAppend(char* last, const char* fmt, va_list ap);
 
+/*
+ * This class may be subclassed to provide a way to get the output of
+ * a printf-like call, as the output is generated.
+ */
+class PrintfTarget
+{
+public:
+    /* The Printf-like interface.  */
+    bool MFBT_API print(const char* format, ...) MOZ_FORMAT_PRINTF(2, 3);
+
+    /* The Vprintf-like interface.  */
+    bool MFBT_API vprint(const char* format, va_list);
+
+protected:
+    MFBT_API PrintfTarget() : mEmitted(0) { }
+    virtual ~PrintfTarget() { }
+
+    /* Subclasses override this.  It is called when more output is
+       available.  It may be called with len==0.  This should return
+       true on success, or false on failure.  */
+    virtual bool append(const char* sp, size_t len) = 0;
+
+private:
+
+    /* Number of bytes emitted so far.  */
+    size_t mEmitted;
+
+    /* The implementation calls this to emit bytes and update
+       mEmitted.  */
+    bool emit(const char* sp, size_t len) {
+        mEmitted += len;
+        return append(sp, len);
+    }
+
+    bool fill2(const char* src, int srclen, int width, int flags);
+    bool fill_n(const char* src, int srclen, int width, int prec, int type, int flags);
+    bool cvt_l(long num, int width, int prec, int radix, int type, int flags, const char* hxp);
+    bool cvt_ll(int64_t num, int width, int prec, int radix, int type, int flags, const char* hexp);
+    bool cvt_f(double d, const char* fmt0, const char* fmt1);
+    bool cvt_s(const char* s, int width, int prec, int flags);
+};
+
 } // namespace mozilla
 
 /* Wrappers for mozilla::Smprintf and friends that are used throughout
    JS.  */
 
 extern JS_PUBLIC_API(char*) JS_smprintf(const char* fmt, ...)
     MOZ_FORMAT_PRINTF(1, 2);