Bug 1060419 - add PrintfTarget class to jsprf.h, r?froydnj draft
authorTom Tromey <tom@tromey.com>
Thu, 12 Jan 2017 15:35:55 -0700
changeset 486138 597beae78106c1611757d2410b2b38502027419f
parent 486137 4f52e98019be92c6047a4358d65e7e4072341341
child 486139 37f022114148d61e3a87124344d2a3722ee154b0
push id45909
push userbmo:ttromey@mozilla.com
push dateFri, 17 Feb 2017 16:00:11 +0000
reviewersfroydnj
bugs1060419
milestone54.0a1
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);