D2741855 broke my wangle. Reverting
[folly.git] / folly / json.cpp
index 28fe5f11e992806f3949d6b42a32d26c66894b74..63fa2ba08dc7236577d73de4e781f04be132ce3c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Facebook, Inc.
+ * Copyright 2015 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * limitations under the License.
  */
 
-#include "folly/json.h"
+#include <folly/json.h>
 #include <cassert>
 #include <boost/next_prior.hpp>
 #include <boost/algorithm/string.hpp>
 
-#include "folly/Range.h"
-#include "folly/Unicode.h"
-#include "folly/Conv.h"
+#include <folly/Conv.h>
+#include <folly/Range.h>
+#include <folly/String.h>
+#include <folly/Unicode.h>
 
 namespace folly {
 
@@ -127,11 +128,11 @@ struct Printer {
     switch (v.type()) {
     case dynamic::DOUBLE:
       if (!opts_.allow_nan_inf &&
-          (isnan(v.asDouble()) || isinf(v.asDouble()))) {
+          (std::isnan(v.asDouble()) || std::isinf(v.asDouble()))) {
         throw std::runtime_error("folly::toJson: JSON object value was a "
           "NaN or INF");
       }
-      toAppend(v.asDouble(), &out_);
+      toAppend(v.asDouble(), &out_, opts_.double_mode, opts_.double_num_digits);
       break;
     case dynamic::INT64: {
       auto intval = v.asInt();
@@ -216,7 +217,7 @@ private:
     indent();
     newline();
     (*this)(a[0]);
-    for (auto& val : makeRange(boost::next(a.begin()), a.end())) {
+    for (auto& val : range(boost::next(a.begin()), a.end())) {
       out_ += ',';
       newline();
       (*this)(val);
@@ -255,25 +256,25 @@ private:
   serialization_opts const& opts_;
 };
 
-//////////////////////////////////////////////////////////////////////
+  //////////////////////////////////////////////////////////////////////
 
-struct ParseError : std::runtime_error {
-  explicit ParseError(int line)
-    : std::runtime_error(to<std::string>("json parse error on line ", line))
-  {}
+  struct ParseError : std::runtime_error {
+    explicit ParseError(int line)
+      : std::runtime_error(to<std::string>("json parse error on line ", line))
+    {}
 
-  explicit ParseError(int line, std::string const& context,
-      std::string const& expected)
-    : std::runtime_error(to<std::string>("json parse error on line ", line,
-        !context.empty() ? to<std::string>(" near `", context, '\'')
-                        : "",
-        ": ", expected))
-  {}
+    explicit ParseError(int line, std::string const& context,
+        std::string const& expected)
+      : std::runtime_error(to<std::string>("json parse error on line ", line,
+          !context.empty() ? to<std::string>(" near `", context, '\'')
+                          : "",
+          ": ", expected))
+    {}
 
-  explicit ParseError(std::string const& what)
-    : std::runtime_error("json parse error: " + what)
-  {}
-};
+    explicit ParseError(std::string const& msg)
+      : std::runtime_error("json parse error: " + msg)
+    {}
+  };
 
 // Wraps our input buffer with some helper functions.
 struct Input {
@@ -323,20 +324,7 @@ struct Input {
   }
 
   void skipWhitespace() {
-    // Spaces other than ' ' characters are less common but should be
-    // checked.  This configuration where we loop on the ' '
-    // separately from oddspaces was empirically fastest.
-    auto oddspace = [] (char c) {
-      return c == '\n' || c == '\t' || c == '\r';
-    };
-
-  loop:
-    for (; !range_.empty() && range_.front() == ' '; range_.pop_front()) {
-    }
-    if (!range_.empty() && oddspace(range_.front())) {
-      range_.pop_front();
-      goto loop;
-    }
+    range_ = folly::skipWhitespace(range_);
     storeCurrent();
   }
 
@@ -482,8 +470,10 @@ dynamic parseArray(Input& in) {
 
 dynamic parseNumber(Input& in) {
   bool const negative = (*in == '-');
-  if (negative) {
-    if (in.consume("-Infinity")) {
+  if (negative && in.consume("-Infinity")) {
+    if (in.getOpts().parse_numbers_as_strings) {
+      return "-Infinity";
+    } else {
       return -std::numeric_limits<double>::infinity();
     }
   }
@@ -494,10 +484,28 @@ dynamic parseNumber(Input& in) {
   }
 
   auto const wasE = *in == 'e' || *in == 'E';
+
+  constexpr const char* maxInt = "9223372036854775807";
+  constexpr const char* minInt = "9223372036854775808";
+  constexpr auto maxIntLen = __builtin_strlen(maxInt);
+
+
+  if (*in != '.' && !wasE && in.getOpts().parse_numbers_as_strings) {
+    return integral;
+  }
+
   if (*in != '.' && !wasE) {
-    auto val = to<int64_t>(integral);
-    in.skipWhitespace();
-    return val;
+    if (LIKELY(!in.getOpts().double_fallback || integral.size() < maxIntLen) ||
+         (integral.size() == maxIntLen &&
+           (integral <= maxInt || (integral == minInt && negative)))) {
+      auto val = to<int64_t>(integral);
+      in.skipWhitespace();
+      return val;
+    } else {
+      auto val = to<double>(integral);
+      in.skipWhitespace();
+      return val;
+    }
   }
 
   auto end = !wasE ? (++in, in.skipDigits().end()) : in.begin();
@@ -509,8 +517,10 @@ dynamic parseNumber(Input& in) {
     auto expPart = in.skipDigits();
     end = expPart.end();
   }
-  auto fullNum = makeRange(integral.begin(), end);
-
+  auto fullNum = range(integral.begin(), end);
+  if (in.getOpts().parse_numbers_as_strings) {
+    return fullNum;
+  }
   auto val = to<double>(fullNum);
   return val;
 }
@@ -625,8 +635,12 @@ dynamic parseValue(Input& in) {
          in.consume("true") ? true :
          in.consume("false") ? false :
          in.consume("null") ? nullptr :
-         in.consume("Infinity") ? std::numeric_limits<double>::infinity() :
-         in.consume("NaN") ? std::numeric_limits<double>::quiet_NaN() :
+         in.consume("Infinity") ?
+          (in.getOpts().parse_numbers_as_strings ? (dynamic)"Infinity" :
+            (dynamic)std::numeric_limits<double>::infinity()) :
+         in.consume("NaN") ?
+           (in.getOpts().parse_numbers_as_strings ? (dynamic)"NaN" :
+             (dynamic)std::numeric_limits<double>::quiet_NaN()) :
          in.error("expected json value");
 }
 
@@ -716,6 +730,65 @@ void escapeString(StringPiece input,
   out.push_back('\"');
 }
 
+fbstring stripComments(StringPiece jsonC) {
+  fbstring result;
+  enum class State {
+    None,
+    InString,
+    InlineComment,
+    LineComment
+  } state = State::None;
+
+  for (size_t i = 0; i < jsonC.size(); ++i) {
+    auto s = jsonC.subpiece(i);
+    switch (state) {
+      case State::None:
+        if (s.startsWith("/*")) {
+          state = State::InlineComment;
+          ++i;
+          continue;
+        } else if (s.startsWith("//")) {
+          state = State::LineComment;
+          ++i;
+          continue;
+        } else if (s[0] == '\"') {
+          state = State::InString;
+        }
+        result.push_back(s[0]);
+        break;
+      case State::InString:
+        if (s[0] == '\\') {
+          if (UNLIKELY(s.size() == 1)) {
+            throw std::logic_error("Invalid JSONC: string is not terminated");
+          }
+          result.push_back(s[0]);
+          result.push_back(s[1]);
+          ++i;
+          continue;
+        } else if (s[0] == '\"') {
+          state = State::None;
+        }
+        result.push_back(s[0]);
+        break;
+      case State::InlineComment:
+        if (s.startsWith("*/")) {
+          state = State::None;
+          ++i;
+        }
+        break;
+      case State::LineComment:
+        if (s[0] == '\n') {
+          // skip the line break. It doesn't matter.
+          state = State::None;
+        }
+        break;
+      default:
+        throw std::logic_error("Unknown comment state");
+    }
+  }
+  return result;
+}
+
 }
 
 //////////////////////////////////////////////////////////////////////