couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rnew...@apache.org
Subject [06/12] Replace ejson with jiffy
Date Mon, 23 Dec 2013 23:31:15 GMT
http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/c_src/double-conversion/strtod.cc
----------------------------------------------------------------------
diff --git a/src/jiffy/c_src/double-conversion/strtod.cc b/src/jiffy/c_src/double-conversion/strtod.cc
new file mode 100644
index 0000000..9758989
--- /dev/null
+++ b/src/jiffy/c_src/double-conversion/strtod.cc
@@ -0,0 +1,554 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <stdarg.h>
+#include <limits.h>
+
+#include "strtod.h"
+#include "bignum.h"
+#include "cached-powers.h"
+#include "ieee.h"
+
+namespace double_conversion {
+
+// 2^53 = 9007199254740992.
+// Any integer with at most 15 decimal digits will hence fit into a double
+// (which has a 53bit significand) without loss of precision.
+static const int kMaxExactDoubleIntegerDecimalDigits = 15;
+// 2^64 = 18446744073709551616 > 10^19
+static const int kMaxUint64DecimalDigits = 19;
+
+// Max double: 1.7976931348623157 x 10^308
+// Min non-zero double: 4.9406564584124654 x 10^-324
+// Any x >= 10^309 is interpreted as +infinity.
+// Any x <= 10^-324 is interpreted as 0.
+// Note that 2.5e-324 (despite being smaller than the min double) will be read
+// as non-zero (equal to the min non-zero double).
+static const int kMaxDecimalPower = 309;
+static const int kMinDecimalPower = -324;
+
+// 2^64 = 18446744073709551616
+static const uint64_t kMaxUint64 = UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF);
+
+
+static const double exact_powers_of_ten[] = {
+  1.0,  // 10^0
+  10.0,
+  100.0,
+  1000.0,
+  10000.0,
+  100000.0,
+  1000000.0,
+  10000000.0,
+  100000000.0,
+  1000000000.0,
+  10000000000.0,  // 10^10
+  100000000000.0,
+  1000000000000.0,
+  10000000000000.0,
+  100000000000000.0,
+  1000000000000000.0,
+  10000000000000000.0,
+  100000000000000000.0,
+  1000000000000000000.0,
+  10000000000000000000.0,
+  100000000000000000000.0,  // 10^20
+  1000000000000000000000.0,
+  // 10^22 = 0x21e19e0c9bab2400000 = 0x878678326eac9 * 2^22
+  10000000000000000000000.0
+};
+static const int kExactPowersOfTenSize = ARRAY_SIZE(exact_powers_of_ten);
+
+// Maximum number of significant digits in the decimal representation.
+// In fact the value is 772 (see conversions.cc), but to give us some margin
+// we round up to 780.
+static const int kMaxSignificantDecimalDigits = 780;
+
+static Vector<const char> TrimLeadingZeros(Vector<const char> buffer) {
+  for (int i = 0; i < buffer.length(); i++) {
+    if (buffer[i] != '0') {
+      return buffer.SubVector(i, buffer.length());
+    }
+  }
+  return Vector<const char>(buffer.start(), 0);
+}
+
+
+static Vector<const char> TrimTrailingZeros(Vector<const char> buffer) {
+  for (int i = buffer.length() - 1; i >= 0; --i) {
+    if (buffer[i] != '0') {
+      return buffer.SubVector(0, i + 1);
+    }
+  }
+  return Vector<const char>(buffer.start(), 0);
+}
+
+
+static void CutToMaxSignificantDigits(Vector<const char> buffer,
+                                       int exponent,
+                                       char* significant_buffer,
+                                       int* significant_exponent) {
+  for (int i = 0; i < kMaxSignificantDecimalDigits - 1; ++i) {
+    significant_buffer[i] = buffer[i];
+  }
+  // The input buffer has been trimmed. Therefore the last digit must be
+  // different from '0'.
+  ASSERT(buffer[buffer.length() - 1] != '0');
+  // Set the last digit to be non-zero. This is sufficient to guarantee
+  // correct rounding.
+  significant_buffer[kMaxSignificantDecimalDigits - 1] = '1';
+  *significant_exponent =
+      exponent + (buffer.length() - kMaxSignificantDecimalDigits);
+}
+
+
+// Trims the buffer and cuts it to at most kMaxSignificantDecimalDigits.
+// If possible the input-buffer is reused, but if the buffer needs to be
+// modified (due to cutting), then the input needs to be copied into the
+// buffer_copy_space.
+static void TrimAndCut(Vector<const char> buffer, int exponent,
+                       char* buffer_copy_space, int space_size,
+                       Vector<const char>* trimmed, int* updated_exponent) {
+  Vector<const char> left_trimmed = TrimLeadingZeros(buffer);
+  Vector<const char> right_trimmed = TrimTrailingZeros(left_trimmed);
+  exponent += left_trimmed.length() - right_trimmed.length();
+  if (right_trimmed.length() > kMaxSignificantDecimalDigits) {
+    ASSERT(space_size >= kMaxSignificantDecimalDigits);
+    CutToMaxSignificantDigits(right_trimmed, exponent,
+                              buffer_copy_space, updated_exponent);
+    *trimmed = Vector<const char>(buffer_copy_space,
+                                 kMaxSignificantDecimalDigits);
+  } else {
+    *trimmed = right_trimmed;
+    *updated_exponent = exponent;
+  }
+}
+
+
+// Reads digits from the buffer and converts them to a uint64.
+// Reads in as many digits as fit into a uint64.
+// When the string starts with "1844674407370955161" no further digit is read.
+// Since 2^64 = 18446744073709551616 it would still be possible read another
+// digit if it was less or equal than 6, but this would complicate the code.
+static uint64_t ReadUint64(Vector<const char> buffer,
+                           int* number_of_read_digits) {
+  uint64_t result = 0;
+  int i = 0;
+  while (i < buffer.length() && result <= (kMaxUint64 / 10 - 1)) {
+    int digit = buffer[i++] - '0';
+    ASSERT(0 <= digit && digit <= 9);
+    result = 10 * result + digit;
+  }
+  *number_of_read_digits = i;
+  return result;
+}
+
+
+// Reads a DiyFp from the buffer.
+// The returned DiyFp is not necessarily normalized.
+// If remaining_decimals is zero then the returned DiyFp is accurate.
+// Otherwise it has been rounded and has error of at most 1/2 ulp.
+static void ReadDiyFp(Vector<const char> buffer,
+                      DiyFp* result,
+                      int* remaining_decimals) {
+  int read_digits;
+  uint64_t significand = ReadUint64(buffer, &read_digits);
+  if (buffer.length() == read_digits) {
+    *result = DiyFp(significand, 0);
+    *remaining_decimals = 0;
+  } else {
+    // Round the significand.
+    if (buffer[read_digits] >= '5') {
+      significand++;
+    }
+    // Compute the binary exponent.
+    int exponent = 0;
+    *result = DiyFp(significand, exponent);
+    *remaining_decimals = buffer.length() - read_digits;
+  }
+}
+
+
+static bool DoubleStrtod(Vector<const char> trimmed,
+                         int exponent,
+                         double* result) {
+#if !defined(DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS)
+  // On x86 the floating-point stack can be 64 or 80 bits wide. If it is
+  // 80 bits wide (as is the case on Linux) then double-rounding occurs and the
+  // result is not accurate.
+  // We know that Windows32 uses 64 bits and is therefore accurate.
+  // Note that the ARM simulator is compiled for 32bits. It therefore exhibits
+  // the same problem.
+  return false;
+#endif
+  if (trimmed.length() <= kMaxExactDoubleIntegerDecimalDigits) {
+    int read_digits;
+    // The trimmed input fits into a double.
+    // If the 10^exponent (resp. 10^-exponent) fits into a double too then we
+    // can compute the result-double simply by multiplying (resp. dividing) the
+    // two numbers.
+    // This is possible because IEEE guarantees that floating-point operations
+    // return the best possible approximation.
+    if (exponent < 0 && -exponent < kExactPowersOfTenSize) {
+      // 10^-exponent fits into a double.
+      *result = static_cast<double>(ReadUint64(trimmed, &read_digits));
+      ASSERT(read_digits == trimmed.length());
+      *result /= exact_powers_of_ten[-exponent];
+      return true;
+    }
+    if (0 <= exponent && exponent < kExactPowersOfTenSize) {
+      // 10^exponent fits into a double.
+      *result = static_cast<double>(ReadUint64(trimmed, &read_digits));
+      ASSERT(read_digits == trimmed.length());
+      *result *= exact_powers_of_ten[exponent];
+      return true;
+    }
+    int remaining_digits =
+        kMaxExactDoubleIntegerDecimalDigits - trimmed.length();
+    if ((0 <= exponent) &&
+        (exponent - remaining_digits < kExactPowersOfTenSize)) {
+      // The trimmed string was short and we can multiply it with
+      // 10^remaining_digits. As a result the remaining exponent now fits
+      // into a double too.
+      *result = static_cast<double>(ReadUint64(trimmed, &read_digits));
+      ASSERT(read_digits == trimmed.length());
+      *result *= exact_powers_of_ten[remaining_digits];
+      *result *= exact_powers_of_ten[exponent - remaining_digits];
+      return true;
+    }
+  }
+  return false;
+}
+
+
+// Returns 10^exponent as an exact DiyFp.
+// The given exponent must be in the range [1; kDecimalExponentDistance[.
+static DiyFp AdjustmentPowerOfTen(int exponent) {
+  ASSERT(0 < exponent);
+  ASSERT(exponent < PowersOfTenCache::kDecimalExponentDistance);
+  // Simply hardcode the remaining powers for the given decimal exponent
+  // distance.
+  ASSERT(PowersOfTenCache::kDecimalExponentDistance == 8);
+  switch (exponent) {
+    case 1: return DiyFp(UINT64_2PART_C(0xa0000000, 00000000), -60);
+    case 2: return DiyFp(UINT64_2PART_C(0xc8000000, 00000000), -57);
+    case 3: return DiyFp(UINT64_2PART_C(0xfa000000, 00000000), -54);
+    case 4: return DiyFp(UINT64_2PART_C(0x9c400000, 00000000), -50);
+    case 5: return DiyFp(UINT64_2PART_C(0xc3500000, 00000000), -47);
+    case 6: return DiyFp(UINT64_2PART_C(0xf4240000, 00000000), -44);
+    case 7: return DiyFp(UINT64_2PART_C(0x98968000, 00000000), -40);
+    default:
+      UNREACHABLE();
+      return DiyFp(0, 0);
+  }
+}
+
+
+// If the function returns true then the result is the correct double.
+// Otherwise it is either the correct double or the double that is just below
+// the correct double.
+static bool DiyFpStrtod(Vector<const char> buffer,
+                        int exponent,
+                        double* result) {
+  DiyFp input;
+  int remaining_decimals;
+  ReadDiyFp(buffer, &input, &remaining_decimals);
+  // Since we may have dropped some digits the input is not accurate.
+  // If remaining_decimals is different than 0 than the error is at most
+  // .5 ulp (unit in the last place).
+  // We don't want to deal with fractions and therefore keep a common
+  // denominator.
+  const int kDenominatorLog = 3;
+  const int kDenominator = 1 << kDenominatorLog;
+  // Move the remaining decimals into the exponent.
+  exponent += remaining_decimals;
+  int error = (remaining_decimals == 0 ? 0 : kDenominator / 2);
+
+  int old_e = input.e();
+  input.Normalize();
+  error <<= old_e - input.e();
+
+  ASSERT(exponent <= PowersOfTenCache::kMaxDecimalExponent);
+  if (exponent < PowersOfTenCache::kMinDecimalExponent) {
+    *result = 0.0;
+    return true;
+  }
+  DiyFp cached_power;
+  int cached_decimal_exponent;
+  PowersOfTenCache::GetCachedPowerForDecimalExponent(exponent,
+                                                     &cached_power,
+                                                     &cached_decimal_exponent);
+
+  if (cached_decimal_exponent != exponent) {
+    int adjustment_exponent = exponent - cached_decimal_exponent;
+    DiyFp adjustment_power = AdjustmentPowerOfTen(adjustment_exponent);
+    input.Multiply(adjustment_power);
+    if (kMaxUint64DecimalDigits - buffer.length() >= adjustment_exponent) {
+      // The product of input with the adjustment power fits into a 64 bit
+      // integer.
+      ASSERT(DiyFp::kSignificandSize == 64);
+    } else {
+      // The adjustment power is exact. There is hence only an error of 0.5.
+      error += kDenominator / 2;
+    }
+  }
+
+  input.Multiply(cached_power);
+  // The error introduced by a multiplication of a*b equals
+  //   error_a + error_b + error_a*error_b/2^64 + 0.5
+  // Substituting a with 'input' and b with 'cached_power' we have
+  //   error_b = 0.5  (all cached powers have an error of less than 0.5 ulp),
+  //   error_ab = 0 or 1 / kDenominator > error_a*error_b/ 2^64
+  int error_b = kDenominator / 2;
+  int error_ab = (error == 0 ? 0 : 1);  // We round up to 1.
+  int fixed_error = kDenominator / 2;
+  error += error_b + error_ab + fixed_error;
+
+  old_e = input.e();
+  input.Normalize();
+  error <<= old_e - input.e();
+
+  // See if the double's significand changes if we add/subtract the error.
+  int order_of_magnitude = DiyFp::kSignificandSize + input.e();
+  int effective_significand_size =
+      Double::SignificandSizeForOrderOfMagnitude(order_of_magnitude);
+  int precision_digits_count =
+      DiyFp::kSignificandSize - effective_significand_size;
+  if (precision_digits_count + kDenominatorLog >= DiyFp::kSignificandSize) {
+    // This can only happen for very small denormals. In this case the
+    // half-way multiplied by the denominator exceeds the range of an uint64.
+    // Simply shift everything to the right.
+    int shift_amount = (precision_digits_count + kDenominatorLog) -
+        DiyFp::kSignificandSize + 1;
+    input.set_f(input.f() >> shift_amount);
+    input.set_e(input.e() + shift_amount);
+    // We add 1 for the lost precision of error, and kDenominator for
+    // the lost precision of input.f().
+    error = (error >> shift_amount) + 1 + kDenominator;
+    precision_digits_count -= shift_amount;
+  }
+  // We use uint64_ts now. This only works if the DiyFp uses uint64_ts too.
+  ASSERT(DiyFp::kSignificandSize == 64);
+  ASSERT(precision_digits_count < 64);
+  uint64_t one64 = 1;
+  uint64_t precision_bits_mask = (one64 << precision_digits_count) - 1;
+  uint64_t precision_bits = input.f() & precision_bits_mask;
+  uint64_t half_way = one64 << (precision_digits_count - 1);
+  precision_bits *= kDenominator;
+  half_way *= kDenominator;
+  DiyFp rounded_input(input.f() >> precision_digits_count,
+                      input.e() + precision_digits_count);
+  if (precision_bits >= half_way + error) {
+    rounded_input.set_f(rounded_input.f() + 1);
+  }
+  // If the last_bits are too close to the half-way case than we are too
+  // inaccurate and round down. In this case we return false so that we can
+  // fall back to a more precise algorithm.
+
+  *result = Double(rounded_input).value();
+  if (half_way - error < precision_bits && precision_bits < half_way + error) {
+    // Too imprecise. The caller will have to fall back to a slower version.
+    // However the returned number is guaranteed to be either the correct
+    // double, or the next-lower double.
+    return false;
+  } else {
+    return true;
+  }
+}
+
+
+// Returns
+//   - -1 if buffer*10^exponent < diy_fp.
+//   -  0 if buffer*10^exponent == diy_fp.
+//   - +1 if buffer*10^exponent > diy_fp.
+// Preconditions:
+//   buffer.length() + exponent <= kMaxDecimalPower + 1
+//   buffer.length() + exponent > kMinDecimalPower
+//   buffer.length() <= kMaxDecimalSignificantDigits
+static int CompareBufferWithDiyFp(Vector<const char> buffer,
+                                  int exponent,
+                                  DiyFp diy_fp) {
+  ASSERT(buffer.length() + exponent <= kMaxDecimalPower + 1);
+  ASSERT(buffer.length() + exponent > kMinDecimalPower);
+  ASSERT(buffer.length() <= kMaxSignificantDecimalDigits);
+  // Make sure that the Bignum will be able to hold all our numbers.
+  // Our Bignum implementation has a separate field for exponents. Shifts will
+  // consume at most one bigit (< 64 bits).
+  // ln(10) == 3.3219...
+  ASSERT(((kMaxDecimalPower + 1) * 333 / 100) < Bignum::kMaxSignificantBits);
+  Bignum buffer_bignum;
+  Bignum diy_fp_bignum;
+  buffer_bignum.AssignDecimalString(buffer);
+  diy_fp_bignum.AssignUInt64(diy_fp.f());
+  if (exponent >= 0) {
+    buffer_bignum.MultiplyByPowerOfTen(exponent);
+  } else {
+    diy_fp_bignum.MultiplyByPowerOfTen(-exponent);
+  }
+  if (diy_fp.e() > 0) {
+    diy_fp_bignum.ShiftLeft(diy_fp.e());
+  } else {
+    buffer_bignum.ShiftLeft(-diy_fp.e());
+  }
+  return Bignum::Compare(buffer_bignum, diy_fp_bignum);
+}
+
+
+// Returns true if the guess is the correct double.
+// Returns false, when guess is either correct or the next-lower double.
+static bool ComputeGuess(Vector<const char> trimmed, int exponent,
+                         double* guess) {
+  if (trimmed.length() == 0) {
+    *guess = 0.0;
+    return true;
+  }
+  if (exponent + trimmed.length() - 1 >= kMaxDecimalPower) {
+    *guess = Double::Infinity();
+    return true;
+  }
+  if (exponent + trimmed.length() <= kMinDecimalPower) {
+    *guess = 0.0;
+    return true;
+  }
+
+  if (DoubleStrtod(trimmed, exponent, guess) ||
+      DiyFpStrtod(trimmed, exponent, guess)) {
+    return true;
+  }
+  if (*guess == Double::Infinity()) {
+    return true;
+  }
+  return false;
+}
+
+double Strtod(Vector<const char> buffer, int exponent) {
+  char copy_buffer[kMaxSignificantDecimalDigits];
+  Vector<const char> trimmed;
+  int updated_exponent;
+  TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits,
+             &trimmed, &updated_exponent);
+  exponent = updated_exponent;
+
+  double guess;
+  bool is_correct = ComputeGuess(trimmed, exponent, &guess);
+  if (is_correct) return guess;
+
+  DiyFp upper_boundary = Double(guess).UpperBoundary();
+  int comparison = CompareBufferWithDiyFp(trimmed, exponent, upper_boundary);
+  if (comparison < 0) {
+    return guess;
+  } else if (comparison > 0) {
+    return Double(guess).NextDouble();
+  } else if ((Double(guess).Significand() & 1) == 0) {
+    // Round towards even.
+    return guess;
+  } else {
+    return Double(guess).NextDouble();
+  }
+}
+
+float Strtof(Vector<const char> buffer, int exponent) {
+  char copy_buffer[kMaxSignificantDecimalDigits];
+  Vector<const char> trimmed;
+  int updated_exponent;
+  TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits,
+             &trimmed, &updated_exponent);
+  exponent = updated_exponent;
+
+  double double_guess;
+  bool is_correct = ComputeGuess(trimmed, exponent, &double_guess);
+
+  float float_guess = static_cast<float>(double_guess);
+  if (float_guess == double_guess) {
+    // This shortcut triggers for integer values.
+    return float_guess;
+  }
+
+  // We must catch double-rounding. Say the double has been rounded up, and is
+  // now a boundary of a float, and rounds up again. This is why we have to
+  // look at previous too.
+  // Example (in decimal numbers):
+  //    input: 12349
+  //    high-precision (4 digits): 1235
+  //    low-precision (3 digits):
+  //       when read from input: 123
+  //       when rounded from high precision: 124.
+  // To do this we simply look at the neigbors of the correct result and see
+  // if they would round to the same float. If the guess is not correct we have
+  // to look at four values (since two different doubles could be the correct
+  // double).
+
+  double double_next = Double(double_guess).NextDouble();
+  double double_previous = Double(double_guess).PreviousDouble();
+
+  float f1 = static_cast<float>(double_previous);
+  float f2 = float_guess;
+  float f3 = static_cast<float>(double_next);
+  float f4;
+  if (is_correct) {
+    f4 = f3;
+  } else {
+    double double_next2 = Double(double_next).NextDouble();
+    f4 = static_cast<float>(double_next2);
+  }
+  ASSERT(f1 <= f2 && f2 <= f3 && f3 <= f4);
+
+  // If the guess doesn't lie near a single-precision boundary we can simply
+  // return its float-value.
+  if (f1 == f4) {
+    return float_guess;
+  }
+
+  ASSERT((f1 != f2 && f2 == f3 && f3 == f4) ||
+         (f1 == f2 && f2 != f3 && f3 == f4) ||
+         (f1 == f2 && f2 == f3 && f3 != f4));
+
+  // guess and next are the two possible canditates (in the same way that
+  // double_guess was the lower candidate for a double-precision guess).
+  float guess = f1;
+  float next = f4;
+  DiyFp upper_boundary;
+  if (guess == 0.0f) {
+    float min_float = 1e-45f;
+    upper_boundary = Double(static_cast<double>(min_float) / 2).AsDiyFp();
+  } else {
+    upper_boundary = Single(guess).UpperBoundary();
+  }
+  int comparison = CompareBufferWithDiyFp(trimmed, exponent, upper_boundary);
+  if (comparison < 0) {
+    return guess;
+  } else if (comparison > 0) {
+    return next;
+  } else if ((Single(guess).Significand() & 1) == 0) {
+    // Round towards even.
+    return guess;
+  } else {
+    return next;
+  }
+}
+
+}  // namespace double_conversion

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/c_src/double-conversion/strtod.h
----------------------------------------------------------------------
diff --git a/src/jiffy/c_src/double-conversion/strtod.h b/src/jiffy/c_src/double-conversion/strtod.h
new file mode 100644
index 0000000..ed0293b
--- /dev/null
+++ b/src/jiffy/c_src/double-conversion/strtod.h
@@ -0,0 +1,45 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef DOUBLE_CONVERSION_STRTOD_H_
+#define DOUBLE_CONVERSION_STRTOD_H_
+
+#include "utils.h"
+
+namespace double_conversion {
+
+// The buffer must only contain digits in the range [0-9]. It must not
+// contain a dot or a sign. It must not start with '0', and must not be empty.
+double Strtod(Vector<const char> buffer, int exponent);
+
+// The buffer must only contain digits in the range [0-9]. It must not
+// contain a dot or a sign. It must not start with '0', and must not be empty.
+float Strtof(Vector<const char> buffer, int exponent);
+
+}  // namespace double_conversion
+
+#endif  // DOUBLE_CONVERSION_STRTOD_H_

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/c_src/double-conversion/utils.h
----------------------------------------------------------------------
diff --git a/src/jiffy/c_src/double-conversion/utils.h b/src/jiffy/c_src/double-conversion/utils.h
new file mode 100644
index 0000000..9ebb9ee
--- /dev/null
+++ b/src/jiffy/c_src/double-conversion/utils.h
@@ -0,0 +1,326 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef DOUBLE_CONVERSION_UTILS_H_
+#define DOUBLE_CONVERSION_UTILS_H_
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <assert.h>
+#ifndef ASSERT
+#define ASSERT(condition)      (assert(condition))
+#endif
+#ifndef UNIMPLEMENTED
+#define UNIMPLEMENTED() (abort())
+#endif
+#ifndef UNREACHABLE
+#define UNREACHABLE()   (abort())
+#endif
+
+// Double operations detection based on target architecture.
+// Linux uses a 80bit wide floating point stack on x86. This induces double
+// rounding, which in turn leads to wrong results.
+// An easy way to test if the floating-point operations are correct is to
+// evaluate: 89255.0/1e22. If the floating-point stack is 64 bits wide then
+// the result is equal to 89255e-22.
+// The best way to test this, is to create a division-function and to compare
+// the output of the division with the expected result. (Inlining must be
+// disabled.)
+// On Linux,x86 89255e-22 != Div_double(89255.0/1e22)
+#if defined(_M_X64) || defined(__x86_64__) || \
+    defined(__ARMEL__) || defined(__avr32__) || \
+    defined(__hppa__) || defined(__ia64__) || \
+    defined(__mips__) || defined(__powerpc__) || \
+    defined(__sparc__) || defined(__sparc) || defined(__s390__) || \
+    defined(__SH4__) || defined(__alpha__) || \
+    defined(_MIPS_ARCH_MIPS32R2)
+#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1
+#elif defined(_M_IX86) || defined(__i386__) || defined(__i386)
+#if defined(_WIN32)
+// Windows uses a 64bit wide floating point stack.
+#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1
+#else
+#undef DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS
+#endif  // _WIN32
+#else
+#error Target architecture was not detected as supported by Double-Conversion.
+#endif
+
+
+#if defined(_WIN32) && !defined(__MINGW32__)
+
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef short int16_t;  // NOLINT
+typedef unsigned short uint16_t;  // NOLINT
+typedef int int32_t;
+typedef unsigned int uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+// intptr_t and friends are defined in crtdefs.h through stdio.h.
+
+#else
+
+#include <stdint.h>
+
+#endif
+
+// The following macro works on both 32 and 64-bit platforms.
+// Usage: instead of writing 0x1234567890123456
+//      write UINT64_2PART_C(0x12345678,90123456);
+#define UINT64_2PART_C(a, b) (((static_cast<uint64_t>(a) << 32) + 0x##b##u))
+
+
+// The expression ARRAY_SIZE(a) is a compile-time constant of type
+// size_t which represents the number of elements of the given
+// array. You should only use ARRAY_SIZE on statically allocated
+// arrays.
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a)                                   \
+  ((sizeof(a) / sizeof(*(a))) /                         \
+  static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
+#endif
+
+// A macro to disallow the evil copy constructor and operator= functions
+// This should be used in the private: declarations for a class
+#ifndef DISALLOW_COPY_AND_ASSIGN
+#define DISALLOW_COPY_AND_ASSIGN(TypeName)      \
+  TypeName(const TypeName&);                    \
+  void operator=(const TypeName&)
+#endif
+
+// A macro to disallow all the implicit constructors, namely the
+// default constructor, copy constructor and operator= functions.
+//
+// This should be used in the private: declarations for a class
+// that wants to prevent anyone from instantiating it. This is
+// especially useful for classes containing only static methods.
+#ifndef DISALLOW_IMPLICIT_CONSTRUCTORS
+#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+  TypeName();                                    \
+  DISALLOW_COPY_AND_ASSIGN(TypeName)
+#endif
+
+namespace double_conversion {
+
+static const int kCharSize = sizeof(char);
+
+// Returns the maximum of the two parameters.
+template <typename T>
+static T Max(T a, T b) {
+  return a < b ? b : a;
+}
+
+
+// Returns the minimum of the two parameters.
+template <typename T>
+static T Min(T a, T b) {
+  return a < b ? a : b;
+}
+
+
+inline int StrLength(const char* string) {
+  size_t length = strlen(string);
+  ASSERT(length == static_cast<size_t>(static_cast<int>(length)));
+  return static_cast<int>(length);
+}
+
+// This is a simplified version of V8's Vector class.
+template <typename T>
+class Vector {
+ public:
+  Vector() : start_(NULL), length_(0) {}
+  Vector(T* data, int length) : start_(data), length_(length) {
+    ASSERT(length == 0 || (length > 0 && data != NULL));
+  }
+
+  // Returns a vector using the same backing storage as this one,
+  // spanning from and including 'from', to but not including 'to'.
+  Vector<T> SubVector(int from, int to) {
+    ASSERT(to <= length_);
+    ASSERT(from < to);
+    ASSERT(0 <= from);
+    return Vector<T>(start() + from, to - from);
+  }
+
+  // Returns the length of the vector.
+  int length() const { return length_; }
+
+  // Returns whether or not the vector is empty.
+  bool is_empty() const { return length_ == 0; }
+
+  // Returns the pointer to the start of the data in the vector.
+  T* start() const { return start_; }
+
+  // Access individual vector elements - checks bounds in debug mode.
+  T& operator[](int index) const {
+    ASSERT(0 <= index && index < length_);
+    return start_[index];
+  }
+
+  T& first() { return start_[0]; }
+
+  T& last() { return start_[length_ - 1]; }
+
+ private:
+  T* start_;
+  int length_;
+};
+
+
+// Helper class for building result strings in a character buffer. The
+// purpose of the class is to use safe operations that checks the
+// buffer bounds on all operations in debug mode.
+class StringBuilder {
+ public:
+  StringBuilder(char* buffer, int size)
+      : buffer_(buffer, size), position_(0) { }
+
+  ~StringBuilder() { if (!is_finalized()) Finalize(); }
+
+  int size() const { return buffer_.length(); }
+
+  // Get the current position in the builder.
+  int position() const {
+    ASSERT(!is_finalized());
+    return position_;
+  }
+
+  // Reset the position.
+  void Reset() { position_ = 0; }
+
+  // Add a single character to the builder. It is not allowed to add
+  // 0-characters; use the Finalize() method to terminate the string
+  // instead.
+  void AddCharacter(char c) {
+    ASSERT(c != '\0');
+    ASSERT(!is_finalized() && position_ < buffer_.length());
+    buffer_[position_++] = c;
+  }
+
+  // Add an entire string to the builder. Uses strlen() internally to
+  // compute the length of the input string.
+  void AddString(const char* s) {
+    AddSubstring(s, StrLength(s));
+  }
+
+  // Add the first 'n' characters of the given string 's' to the
+  // builder. The input string must have enough characters.
+  void AddSubstring(const char* s, int n) {
+    ASSERT(!is_finalized() && position_ + n < buffer_.length());
+    ASSERT(static_cast<size_t>(n) <= strlen(s));
+    memmove(&buffer_[position_], s, n * kCharSize);
+    position_ += n;
+  }
+
+
+  // Add character padding to the builder. If count is non-positive,
+  // nothing is added to the builder.
+  void AddPadding(char c, int count) {
+    for (int i = 0; i < count; i++) {
+      AddCharacter(c);
+    }
+  }
+
+  // Finalize the string by 0-terminating it and returning the buffer.
+  char* Finalize() {
+    ASSERT(!is_finalized() && position_ < buffer_.length());
+    buffer_[position_] = '\0';
+    // Make sure nobody managed to add a 0-character to the
+    // buffer while building the string.
+    ASSERT(strlen(buffer_.start()) == static_cast<size_t>(position_));
+    position_ = -1;
+    ASSERT(is_finalized());
+    return buffer_.start();
+  }
+
+ private:
+  Vector<char> buffer_;
+  int position_;
+
+  bool is_finalized() const { return position_ < 0; }
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder);
+};
+
+// The type-based aliasing rule allows the compiler to assume that pointers of
+// different types (for some definition of different) never alias each other.
+// Thus the following code does not work:
+//
+// float f = foo();
+// int fbits = *(int*)(&f);
+//
+// The compiler 'knows' that the int pointer can't refer to f since the types
+// don't match, so the compiler may cache f in a register, leaving random data
+// in fbits.  Using C++ style casts makes no difference, however a pointer to
+// char data is assumed to alias any other pointer.  This is the 'memcpy
+// exception'.
+//
+// Bit_cast uses the memcpy exception to move the bits from a variable of one
+// type of a variable of another type.  Of course the end result is likely to
+// be implementation dependent.  Most compilers (gcc-4.2 and MSVC 2005)
+// will completely optimize BitCast away.
+//
+// There is an additional use for BitCast.
+// Recent gccs will warn when they see casts that may result in breakage due to
+// the type-based aliasing rule.  If you have checked that there is no breakage
+// you can use BitCast to cast one pointer type to another.  This confuses gcc
+// enough that it can no longer see that you have cast one pointer type to
+// another thus avoiding the warning.
+//
+// Adding an unused attribute for this static check. Apparently GCC 4.8
+// now throws an error for unused typedefs which triggers -Werror.
+//
+// PJD: 4-24-2013
+//
+
+#if defined(__GNUC__)
+#define UNUSED __attribute__((unused))
+#else
+#define UNUSED
+#endif
+
+template <class Dest, class Source>
+inline Dest BitCast(const Source& source) {
+  // Compile time assertion: sizeof(Dest) == sizeof(Source)
+  // A compile error here means your Dest and Source have different sizes.
+  UNUSED typedef char VerifySizesAreEqual[sizeof(Dest) == sizeof(Source) ? 1 : -1];
+
+  Dest dest;
+  memmove(&dest, &source, sizeof(dest));
+  return dest;
+}
+
+template <class Dest, class Source>
+inline Dest BitCast(Source* source) {
+  return BitCast<Dest>(reinterpret_cast<uintptr_t>(source));
+}
+
+}  // namespace double_conversion
+
+#endif  // DOUBLE_CONVERSION_UTILS_H_

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/c_src/doubles.cc
----------------------------------------------------------------------
diff --git a/src/jiffy/c_src/doubles.cc b/src/jiffy/c_src/doubles.cc
new file mode 100644
index 0000000..0cedfd3
--- /dev/null
+++ b/src/jiffy/c_src/doubles.cc
@@ -0,0 +1,33 @@
+#include "double-conversion/double-conversion.h"
+
+#define BEGIN_C extern "C" {
+#define END_C }
+
+
+namespace dc = double_conversion;
+
+
+BEGIN_C
+
+int
+double_to_shortest(char* buf, size_t size, size_t* len, double val)
+{
+    int flags = dc::DoubleToStringConverter::UNIQUE_ZERO |
+                dc::DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN |
+                dc::DoubleToStringConverter::EMIT_TRAILING_DECIMAL_POINT |
+                dc::DoubleToStringConverter::EMIT_TRAILING_ZERO_AFTER_POINT;
+
+    dc::StringBuilder builder(buf, size);
+    dc::DoubleToStringConverter conv(flags, NULL, NULL, 'e', -6, 21, 6, 0);
+
+    if(!conv.ToShortest(val, &builder)) {
+        return 0;
+    }
+
+    *len = (size_t) builder.position();
+    builder.Finalize();
+
+    return 1;
+}
+
+END_C

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/c_src/encoder.c
----------------------------------------------------------------------
diff --git a/src/jiffy/c_src/encoder.c b/src/jiffy/c_src/encoder.c
new file mode 100644
index 0000000..c23b309
--- /dev/null
+++ b/src/jiffy/c_src/encoder.c
@@ -0,0 +1,709 @@
+// This file is part of Jiffy released under the MIT license.
+// See the LICENSE file for more information.
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "erl_nif.h"
+#include "jiffy.h"
+
+#define BIN_INC_SIZE 2048
+
+#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
+
+#define MAYBE_PRETTY(e)             \
+do {                                \
+    if(e->pretty) {                 \
+        if(!enc_shift(e))           \
+            return 0;               \
+    }                               \
+} while(0)
+
+#if WINDOWS || WIN32
+#define inline __inline
+#define snprintf  _snprintf
+#endif
+
+typedef struct {
+    ErlNifEnv*      env;
+    jiffy_st*       atoms;
+    int             uescape;
+    int             pretty;
+
+    int             shiftcnt;
+    int             count;
+
+    int             iolen;
+    ERL_NIF_TERM    iolist;
+    ErlNifBinary*   curr;
+
+
+    char*           p;
+    unsigned char*  u;
+    size_t          i;
+} Encoder;
+
+
+// String constants for pretty printing.
+// Every string starts with its length.
+#define NUM_SHIFTS 8
+static char* shifts[NUM_SHIFTS] = {
+    "\x01\n",
+    "\x03\n  ",
+    "\x05\n    ",
+    "\x07\n      ",
+    "\x09\n        ",
+    "\x0b\n          ",
+    "\x0d\n            ",
+    "\x0f\n              "
+};
+
+
+int
+enc_init(Encoder* e, ErlNifEnv* env, ERL_NIF_TERM opts, ErlNifBinary* bin)
+{
+    ERL_NIF_TERM val;
+
+    e->env = env;
+    e->atoms = enif_priv_data(env);
+    e->uescape = 0;
+    e->pretty = 0;
+    e->shiftcnt = 0;
+    e->count = 0;
+
+    if(!enif_is_list(env, opts)) {
+        return 0;
+    }
+
+    while(enif_get_list_cell(env, opts, &val, &opts)) {
+        if(enif_compare(val, e->atoms->atom_uescape) == 0) {
+            e->uescape = 1;
+        } else if(enif_compare(val, e->atoms->atom_pretty) == 0) {
+            e->pretty = 1;
+        } else if(enif_compare(val, e->atoms->atom_force_utf8) == 0) {
+            // Ignore, handled in Erlang
+        } else {
+            return 0;
+        }
+    }
+
+    e->iolen = 0;
+    e->iolist = enif_make_list(env, 0);
+    e->curr = bin;
+    if(!enif_alloc_binary(BIN_INC_SIZE, e->curr)) {
+        return 0;
+    }
+
+    memset(e->curr->data, 0, e->curr->size);
+
+    e->p = (char*) e->curr->data;
+    e->u = (unsigned char*) e->curr->data;
+    e->i = 0;
+
+    return 1;
+}
+
+void
+enc_destroy(Encoder* e)
+{
+    if(e->curr != NULL) {
+        enif_release_binary(e->curr);
+    }
+}
+
+ERL_NIF_TERM
+enc_error(Encoder* e, const char* msg)
+{
+    //assert(0 && msg);
+    return make_error(e->atoms, e->env, msg);
+}
+
+static inline int
+enc_ensure(Encoder* e, size_t req)
+{
+    size_t need = e->curr->size;
+    while(req >= (need - e->i)) need <<= 1;
+
+    if(need != e->curr->size) {
+        if(!enif_realloc_binary(e->curr, need)) {
+            return 0;
+        }
+        e->p = (char*) e->curr->data;
+        e->u = (unsigned char*) e->curr->data;
+    }
+
+    return 1;
+}
+
+int
+enc_result(Encoder* e, ERL_NIF_TERM* value)
+{
+    if(e->i != e->curr->size) {
+        if(!enif_realloc_binary(e->curr, e->i)) {
+            return 0;
+        }
+    }
+
+    *value = enif_make_binary(e->env, e->curr);
+    e->curr = NULL;
+    return 1;
+}
+
+int
+enc_done(Encoder* e, ERL_NIF_TERM* value)
+{
+    ERL_NIF_TERM last;
+
+    if(e->iolen == 0) {
+        return enc_result(e, value);
+    }
+
+    if(e->i > 0 ) {
+        if(!enc_result(e, &last)) {
+            return 0;
+        }
+
+        e->iolist = enif_make_list_cell(e->env, last, e->iolist);
+        e->iolen++;
+    }
+
+    *value = e->iolist;
+    return 1;
+}
+
+static inline int
+enc_unknown(Encoder* e, ERL_NIF_TERM value)
+{
+    ErlNifBinary* bin = e->curr;
+    ERL_NIF_TERM curr;
+
+    if(e->i > 0) {
+        if(!enc_result(e, &curr)) {
+            return 0;
+        }
+
+        e->iolist = enif_make_list_cell(e->env, curr, e->iolist);
+        e->iolen++;
+    }
+
+    e->iolist = enif_make_list_cell(e->env, value, e->iolist);
+    e->iolen++;
+
+    // Reinitialize our binary for the next buffer.
+    e->curr = bin;
+    if(!enif_alloc_binary(BIN_INC_SIZE, e->curr)) {
+        return 0;
+    }
+
+    memset(e->curr->data, 0, e->curr->size);
+
+    e->p = (char*) e->curr->data;
+    e->u = (unsigned char*) e->curr->data;
+    e->i = 0;
+
+    return 1;
+}
+
+static inline int
+enc_literal(Encoder* e, const char* literal, size_t len)
+{
+    if(!enc_ensure(e, len)) {
+        return 0;
+    }
+
+    memcpy(&(e->p[e->i]), literal, len);
+    e->i += len;
+    e->count++;
+    return 1;
+}
+
+static inline int
+enc_string(Encoder* e, ERL_NIF_TERM val)
+{
+    ErlNifBinary bin;
+    char atom[512];
+
+    unsigned char* data;
+    size_t size;
+
+    int esc_extra = 0;
+    int ulen;
+    int uval;
+    int i;
+
+    if(enif_is_binary(e->env, val)) {
+        if(!enif_inspect_binary(e->env, val, &bin)) {
+            return 0;
+        }
+        data = bin.data;
+        size = bin.size;
+    } else if(enif_is_atom(e->env, val)) {
+        if(!enif_get_atom(e->env, val, atom, 512, ERL_NIF_LATIN1)) {
+            return 0;
+        }
+        data = (unsigned char*) atom;
+        size = strlen(atom);
+    } else {
+        return 0;
+    }
+
+    i = 0;
+    while(i < size) {
+        switch((char) data[i]) {
+            case '\"':
+            case '\\':
+            case '\b':
+            case '\f':
+            case '\n':
+            case '\r':
+            case '\t':
+                esc_extra += 1;
+                i++;
+                continue;
+            default:
+                if(data[i] < 0x20) {
+                    esc_extra += 5;
+                    i++;
+                    continue;
+                } else if(data[i] < 0x80) {
+                    i++;
+                    continue;
+                }
+                ulen = utf8_validate(&(data[i]), size - i);
+                if(ulen < 0) {
+                    return 0;
+                }
+                if(e->uescape) {
+                    uval = utf8_to_unicode(&(data[i]), ulen);
+                    if(uval < 0) {
+                        return 0;
+                    }
+                    esc_extra += utf8_esc_len(uval);
+                    if(ulen < 0) {
+                        return 0;
+                    }
+                }
+                i += ulen;
+        }
+    }
+
+    if(!enc_ensure(e, size + esc_extra + 2)) {
+        return 0;
+    }
+
+    e->p[e->i++] = '\"';
+
+    i = 0;
+    while(i < size) {
+        switch((char) data[i]) {
+            case '\"':
+            case '\\':
+                e->p[e->i++] = '\\';
+                e->u[e->i++] = data[i];
+                i++;
+                continue;
+            case '\b':
+                e->p[e->i++] = '\\';
+                e->p[e->i++] = 'b';
+                i++;
+                continue;
+            case '\f':
+                e->p[e->i++] = '\\';
+                e->p[e->i++] = 'f';
+                i++;
+                continue;
+            case '\n':
+                e->p[e->i++] = '\\';
+                e->p[e->i++] = 'n';
+                i++;
+                continue;
+            case '\r':
+                e->p[e->i++] = '\\';
+                e->p[e->i++] = 'r';
+                i++;
+                continue;
+            case '\t':
+                e->p[e->i++] = '\\';
+                e->p[e->i++] = 't';
+                i++;
+                continue;
+            default:
+                if(data[i] < 0x20) {
+                    ulen = unicode_uescape(data[i], &(e->p[e->i]));
+                    if(ulen < 0) {
+                        return 0;
+                    }
+                    e->i += ulen;
+                    i++;
+                } else if((data[i] & 0x80) && e->uescape) {
+                    uval = utf8_to_unicode(&(data[i]), size-i);
+                    if(uval < 0) {
+                        return 0;
+                    }
+
+                    ulen = unicode_uescape(uval, &(e->p[e->i]));
+                    if(ulen < 0) {
+                        return 0;
+                    }
+                    e->i += ulen;
+
+                    ulen = utf8_len(uval);
+                    if(ulen < 0) {
+                        return 0;
+                    }
+                    i += ulen;
+                } else {
+                    e->u[e->i++] = data[i++];
+                }
+        }
+    }
+
+    e->p[e->i++] = '\"';
+    e->count++;
+
+    return 1;
+}
+
+static inline int
+enc_long(Encoder* e, ErlNifSInt64 val)
+{
+    if(!enc_ensure(e, 32)) {
+        return 0;
+    }
+
+#if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_))
+    snprintf(&(e->p[e->i]), 32, "%ld", val);
+#elif SIZEOF_LONG == 8
+    snprintf(&(e->p[e->i]), 32, "%ld", val);
+#else
+    snprintf(&(e->p[e->i]), 32, "%lld", val);
+#endif
+
+    e->i += strlen(&(e->p[e->i]));
+    e->count++;
+
+    return 1;
+}
+
+static inline int
+enc_double(Encoder* e, double val)
+{
+    char* start;
+    size_t len;
+
+    if(!enc_ensure(e, 32)) {
+        return 0;
+    }
+
+    start = &(e->p[e->i]);
+
+    if(!double_to_shortest(start, e->curr->size, &len, val)) {
+        return 0;
+    }
+
+    e->i += len;
+    e->count++;
+    return 1;
+}
+
+static inline int
+enc_char(Encoder* e, char c)
+{
+    if(!enc_ensure(e, 1)) {
+        return 0;
+    }
+
+    e->p[e->i++] = c;
+    return 1;
+}
+
+static int
+enc_shift(Encoder* e) {
+    int i;
+    char* shift;
+    assert(e->shiftcnt >= 0 && "Invalid shift count.");
+    shift = shifts[MIN(e->shiftcnt, NUM_SHIFTS-1)];
+
+    if(!enc_literal(e, shift + 1, *shift))
+        return 0;
+
+    // Finish the rest of this shift it's it bigger than
+    // our largest predefined constant.
+    for(i = NUM_SHIFTS - 1; i < e->shiftcnt; i++) {
+        if(!enc_literal(e, "  ", 2))
+            return 0;
+    }
+
+    return 1;
+}
+
+static inline int
+enc_start_object(Encoder* e)
+{
+    e->count++;
+    e->shiftcnt++;
+    if(!enc_char(e, '{'))
+        return 0;
+    MAYBE_PRETTY(e);
+    return 1;
+}
+
+static inline int
+enc_end_object(Encoder* e)
+{
+    e->shiftcnt--;
+    MAYBE_PRETTY(e);
+    return enc_char(e, '}');
+}
+
+static inline int
+enc_start_array(Encoder* e)
+{
+    e->count++;
+    e->shiftcnt++;
+    if(!enc_char(e, '['))
+        return 0;
+    MAYBE_PRETTY(e);
+    return 1;
+}
+
+static inline int
+enc_end_array(Encoder* e)
+{
+    e->shiftcnt--;
+    MAYBE_PRETTY(e);
+    return enc_char(e, ']');
+}
+
+static inline int
+enc_colon(Encoder* e)
+{
+    if(e->pretty)
+        return enc_literal(e, " : ", 3);
+    return enc_char(e, ':');
+}
+
+static inline int
+enc_comma(Encoder* e)
+{
+    if(!enc_char(e, ','))
+        return 0;
+    MAYBE_PRETTY(e);
+    return 1;
+}
+
+ERL_NIF_TERM
+encode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+    Encoder enc;
+    Encoder* e = &enc;
+
+    ErlNifBinary bin;
+    ERL_NIF_TERM ret;
+
+    ERL_NIF_TERM stack;
+    ERL_NIF_TERM curr;
+    ERL_NIF_TERM item;
+    const ERL_NIF_TERM* tuple;
+    int arity;
+    ErlNifSInt64 lval;
+    double dval;
+
+    if(argc != 2) {
+        return enif_make_badarg(env);
+    }
+
+    if(!enc_init(e, env, argv[1], &bin)) {
+        return enif_make_badarg(env);
+    }
+
+    stack = enif_make_list(env, 1, argv[0]);
+
+    while(!enif_is_empty_list(env, stack)) {
+        if(!enif_get_list_cell(env, stack, &curr, &stack)) {
+            ret = enc_error(e, "internal_error");
+            goto done;
+        }
+        if(enif_is_identical(curr, e->atoms->ref_object)) {
+            if(!enif_get_list_cell(env, stack, &curr, &stack)) {
+                ret = enc_error(e, "internal_error");
+                goto done;
+            }
+            if(enif_is_empty_list(env, curr)) {
+                if(!enc_end_object(e)) {
+                    ret = enc_error(e, "internal_error");
+                    goto done;
+                }
+                continue;
+            }
+            if(!enif_get_list_cell(env, curr, &item, &curr)) {
+                ret = enc_error(e, "internal_error");
+                goto done;
+            }
+            if(!enif_get_tuple(env, item, &arity, &tuple)) {
+                ret = enc_error(e, "invalid_object_pair");
+                goto done;
+            }
+            if(arity != 2) {
+                ret = enc_error(e, "invalid_object_pair");
+                goto done;
+            }
+            if(!enc_comma(e)) {
+                ret = enc_error(e, "internal_error");
+                goto done;
+            }
+            if(!enc_string(e, tuple[0])) {
+                ret = enc_error(e, "invalid_object_key");
+                goto done;
+            }
+            if(!enc_colon(e)) {
+                ret = enc_error(e, "internal_error");
+                goto done;
+            }
+            stack = enif_make_list_cell(env, curr, stack);
+            stack = enif_make_list_cell(env, e->atoms->ref_object, stack);
+            stack = enif_make_list_cell(env, tuple[1], stack);
+        } else if(enif_is_identical(curr, e->atoms->ref_array)) {
+            if(!enif_get_list_cell(env, stack, &curr, &stack)) {
+                ret = enc_error(e, "internal_error");
+                goto done;
+            }
+            if(enif_is_empty_list(env, curr)) {
+                if(!enc_end_array(e)) {
+                    ret = enc_error(e, "internal_error");
+                    goto done;
+                }
+                continue;
+            }
+            if(!enc_comma(e)) {
+                ret = enc_error(e, "internal_error");
+                goto done;
+            }
+            if(!enif_get_list_cell(env, curr, &item, &curr)) {
+                ret = enc_error(e, "internal_error");
+                goto done;
+            }
+            stack = enif_make_list_cell(env, curr, stack);
+            stack = enif_make_list_cell(env, e->atoms->ref_array, stack);
+            stack = enif_make_list_cell(env, item, stack);
+        } else if(enif_compare(curr, e->atoms->atom_null) == 0) {
+            if(!enc_literal(e, "null", 4)) {
+                ret = enc_error(e, "null");
+                goto done;
+            }
+        } else if(enif_compare(curr, e->atoms->atom_true) == 0) {
+            if(!enc_literal(e, "true", 4)) {
+                ret = enc_error(e, "true");
+                goto done;
+            }
+        } else if(enif_compare(curr, e->atoms->atom_false) == 0) {
+            if(!enc_literal(e, "false", 5)) {
+                ret = enc_error(e, "false");
+                goto done;
+            }
+        } else if(enif_is_binary(env, curr)) {
+            if(!enc_string(e, curr)) {
+                ret = enc_error(e, "invalid_string");
+                goto done;
+            }
+        } else if(enif_is_atom(env, curr)) {
+            if(!enc_string(e, curr)) {
+                ret = enc_error(e, "invalid_string");
+                goto done;
+            }
+        } else if(enif_get_int64(env, curr, &lval)) {
+            if(!enc_long(e, lval)) {
+                ret = enc_error(e, "internal_error");
+                goto done;
+            }
+        } else if(enif_get_double(env, curr, &dval)) {
+            if(!enc_double(e, dval)) {
+                ret = enc_error(e, "internal_error");
+                goto done;
+            }
+        } else if(enif_get_tuple(env, curr, &arity, &tuple)) {
+            if(arity != 1) {
+                ret = enc_error(e, "invalid_ejson");
+                goto done;
+            }
+            if(!enif_is_list(env, tuple[0])) {
+                ret = enc_error(e, "invalid_object");
+                goto done;
+            }
+            if(!enc_start_object(e)) {
+                ret = enc_error(e, "internal_error");
+                goto done;
+            }
+            if(enif_is_empty_list(env, tuple[0])) {
+                if(!enc_end_object(e)) {
+                    ret = enc_error(e, "internal_error");
+                    goto done;
+                }
+                continue;
+            }
+            if(!enif_get_list_cell(env, tuple[0], &item, &curr)) {
+                ret = enc_error(e, "internal_error");
+                goto done;
+            }
+            if(!enif_get_tuple(env, item, &arity, &tuple)) {
+                ret = enc_error(e, "invalid_object_member");
+                goto done;
+            }
+            if(arity != 2) {
+                ret = enc_error(e, "invalid_object_member_arity");
+                goto done;
+            }
+            if(!enc_string(e, tuple[0])) {
+                ret = enc_error(e, "invalid_object_member_key");
+                goto done;
+            }
+            if(!enc_colon(e)) {
+                ret = enc_error(e, "internal_error");
+                goto done;
+            }
+            stack = enif_make_list_cell(env, curr, stack);
+            stack = enif_make_list_cell(env, e->atoms->ref_object, stack);
+            stack = enif_make_list_cell(env, tuple[1], stack);
+        } else if(enif_is_list(env, curr)) {
+            if(!enc_start_array(e)) {
+                ret = enc_error(e, "internal_error");
+                goto done;
+            }
+            if(enif_is_empty_list(env, curr)) {
+                if(!enc_end_array(e)) {
+                    ret = enc_error(e, "internal_error");
+                    goto done;
+                }
+                continue;
+            }
+            if(!enif_get_list_cell(env, curr, &item, &curr)) {
+                ret = enc_error(e, "internal_error");
+                goto done;
+            }
+            stack = enif_make_list_cell(env, curr, stack);
+            stack = enif_make_list_cell(env, e->atoms->ref_array, stack);
+            stack = enif_make_list_cell(env, item, stack);
+        } else {
+            if(!enc_unknown(e, curr)) {
+                ret = enc_error(e, "internal_error");
+                goto done;
+            }
+        }
+    }
+
+    if(!enc_done(e, &item)) {
+        ret = enc_error(e, "internal_error");
+        goto done;
+    }
+
+    if(e->iolen == 0) {
+        ret = item;
+    } else {
+        ret = enif_make_tuple2(env, e->atoms->atom_partial, item);
+    }
+
+done:
+    enc_destroy(e);
+    return ret;
+}

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/c_src/jiffy.c
----------------------------------------------------------------------
diff --git a/src/jiffy/c_src/jiffy.c b/src/jiffy/c_src/jiffy.c
new file mode 100644
index 0000000..964222c
--- /dev/null
+++ b/src/jiffy/c_src/jiffy.c
@@ -0,0 +1,61 @@
+// This file is part of Jiffy released under the MIT license.
+// See the LICENSE file for more information.
+
+#include "jiffy.h"
+
+static int
+load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
+{
+    jiffy_st* st = enif_alloc(sizeof(jiffy_st));
+    if(st == NULL) {
+        return 1;
+    }
+
+    st->atom_ok = make_atom(env, "ok");
+    st->atom_error = make_atom(env, "error");
+    st->atom_null = make_atom(env, "null");
+    st->atom_true = make_atom(env, "true");
+    st->atom_false = make_atom(env, "false");
+    st->atom_bignum = make_atom(env, "bignum");
+    st->atom_bignum_e = make_atom(env, "bignum_e");
+    st->atom_bigdbl = make_atom(env, "bigdbl");
+    st->atom_partial = make_atom(env, "partial");
+    st->atom_uescape = make_atom(env, "uescape");
+    st->atom_pretty = make_atom(env, "pretty");
+    st->atom_force_utf8 = make_atom(env, "force_utf8");
+
+    // Markers used in encoding
+    st->ref_object = make_atom(env, "$object_ref$");
+    st->ref_array = make_atom(env, "$array_ref$");
+
+    *priv = (void*) st;
+
+    return 0;
+}
+
+static int
+reload(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
+{
+    return 0;
+}
+
+static int
+upgrade(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM info)
+{
+    return load(env, priv, info);
+}
+
+static void
+unload(ErlNifEnv* env, void* priv)
+{
+    enif_free(priv);
+    return;
+}
+
+static ErlNifFunc funcs[] =
+{
+    {"nif_decode", 1, decode},
+    {"nif_encode", 2, encode}
+};
+
+ERL_NIF_INIT(jiffy, funcs, &load, &reload, &upgrade, &unload);

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/c_src/jiffy.h
----------------------------------------------------------------------
diff --git a/src/jiffy/c_src/jiffy.h b/src/jiffy/c_src/jiffy.h
new file mode 100644
index 0000000..3dda545
--- /dev/null
+++ b/src/jiffy/c_src/jiffy.h
@@ -0,0 +1,45 @@
+// This file is part of Jiffy released under the MIT license.
+// See the LICENSE file for more information.
+
+#ifndef JIFFY_H
+#define JIFFY_H
+
+#include "erl_nif.h"
+
+typedef struct {
+    ERL_NIF_TERM    atom_ok;
+    ERL_NIF_TERM    atom_error;
+    ERL_NIF_TERM    atom_null;
+    ERL_NIF_TERM    atom_true;
+    ERL_NIF_TERM    atom_false;
+    ERL_NIF_TERM    atom_bignum;
+    ERL_NIF_TERM    atom_bignum_e;
+    ERL_NIF_TERM    atom_bigdbl;
+    ERL_NIF_TERM    atom_partial;
+    ERL_NIF_TERM    atom_uescape;
+    ERL_NIF_TERM    atom_pretty;
+    ERL_NIF_TERM    atom_force_utf8;
+
+    ERL_NIF_TERM    ref_object;
+    ERL_NIF_TERM    ref_array;
+} jiffy_st;
+
+ERL_NIF_TERM make_atom(ErlNifEnv* env, const char* name);
+ERL_NIF_TERM make_ok(jiffy_st* st, ErlNifEnv* env, ERL_NIF_TERM data);
+ERL_NIF_TERM make_error(jiffy_st* st, ErlNifEnv* env, const char* error);
+
+ERL_NIF_TERM decode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+ERL_NIF_TERM encode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+
+int int_from_hex(const unsigned char* p);
+int int_to_hex(int val, char* p);
+int utf8_len(int c);
+int utf8_esc_len(int c);
+int utf8_validate(unsigned char* data, size_t size);
+int utf8_to_unicode(unsigned char* buf, size_t size);
+int unicode_to_utf8(int c, unsigned char* buf);
+int unicode_from_pair(int hi, int lo);
+int unicode_uescape(int c, char* buf);
+int double_to_shortest(char *buf, size_t size, size_t* len, double val);
+
+#endif // Included JIFFY_H

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/c_src/utf8.c
----------------------------------------------------------------------
diff --git a/src/jiffy/c_src/utf8.c b/src/jiffy/c_src/utf8.c
new file mode 100644
index 0000000..3ac65cb
--- /dev/null
+++ b/src/jiffy/c_src/utf8.c
@@ -0,0 +1,249 @@
+// This file is part of Jiffy released under the MIT license.
+// See the LICENSE file for more information.
+#include "jiffy.h"
+#include <stdio.h>
+
+static const char hexvals[256] = {
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+     0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1,
+    -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+static const char hexdigits[16] = {
+    '0', '1', '2', '3',
+    '4', '5', '6', '7',
+    '8', '9', 'A', 'B',
+    'C', 'D', 'E', 'F'
+};
+
+int
+int_from_hex(const unsigned char* p)
+{
+    unsigned char* h = (unsigned char*) p;
+    int ret;
+
+    if(hexvals[*(h+0)] < 0) return -1;
+    if(hexvals[*(h+1)] < 0) return -1;
+    if(hexvals[*(h+2)] < 0) return -1;
+    if(hexvals[*(h+3)] < 0) return -1;
+
+    ret = (hexvals[*(h+0)] << 12)
+        + (hexvals[*(h+1)] << 8)
+        + (hexvals[*(h+2)] << 4)
+        + (hexvals[*(h+3)] << 0);
+
+    return ret;
+}
+
+int
+int_to_hex(int val, char* p)
+{
+    if(val < 0 || val > 65535)
+        return -1;
+
+    p[0] = hexdigits[(val >> 12) & 0xF];
+    p[1] = hexdigits[(val >> 8) & 0xF];
+    p[2] = hexdigits[(val >> 4) & 0xF];
+    p[3] = hexdigits[val & 0xF];
+
+    return 1;
+}
+
+int
+utf8_len(int c)
+{
+    if(c < 128) {
+        return 1;
+    } else if(c < 0x800) {
+        return 2;
+    } else if(c < 0x10000) {
+        if(c < 0xD800 || (c > 0xDFFF && c < 0xFFFE)) {
+            return 3;
+        } else {
+            return -1;
+        }
+    } else if(c <= 0x10FFFF) {
+        return 4;
+    } else {
+        return -1;
+    }
+}
+
+int
+utf8_esc_len(int c)
+{
+    if(c < 0x10000) {
+        return 6;
+    } else if(c <= 0x10FFFF) {
+        return 12;
+    } else {
+        return -1;
+    }
+}
+
+int
+utf8_validate(unsigned char* data, size_t size)
+{
+    int ulen = -1;
+    int ui;
+
+    if((data[0] & 0x80) == 0x00) {
+        ulen = 1;
+    } if((data[0] & 0xE0) == 0xC0) {
+        ulen = 2;
+    } else if((data[0] & 0xF0) == 0xE0) {
+        ulen = 3;
+    } else if((data[0] & 0xF8) == 0xF0) {
+        ulen = 4;
+    }
+    if(ulen < 0 || ulen > size) {
+        return -1;
+    }
+
+    // Check each continuation byte.
+    for(ui = 1; ui < ulen; ui++) {
+        if((data[ui] & 0xC0) != 0x80) return -1;
+    }
+
+    // Wikipedia says I have to check that a UTF-8 encoding
+    // uses as few bits as possible. This means that we
+    // can't do things like encode 't' in three bytes.
+    // To check this all we need to ensure is that for each
+    // of the following bit patterns that there is at least
+    // one 1 bit in any of the x's
+    //  1: 0yyyyyyy
+    //  2: 110xxxxy 10yyyyyy
+    //  3: 1110xxxx 10xyyyyy 10yyyyyy
+    //  4: 11110xxx 10xxyyyy 10yyyyyy 10yyyyyy
+
+    // ulen == 1 passes by definition
+    if(ulen == 2) {
+        if((data[0] & 0x1E) == 0)
+            return -1;
+    } else if(ulen == 3) {
+        if((data[0] & 0x0F) + (data[1] & 0x20) == 0)
+            return -1;
+    } else if(ulen == 4) {
+        if((data[0] & 0x07) + (data[1] & 0x30) == 0)
+            return -1;
+    }
+
+    // Lastly we need to check some miscellaneous ranges for
+    // some of the larger code point values.
+    if(ulen >= 3) {
+        ui = utf8_to_unicode(data, ulen);
+        if(ui < 0) {
+            return -1;
+        } else if(ui >= 0xD800 && ui <= 0xDFFF) {
+            return -1;
+        } else if(ui == 0xFFFE || ui == 0xFFFF) {
+            return -1;
+        } else if(ui > 0x10FFFF) {
+            return -1;
+        }
+    }
+
+    return ulen;
+}
+
+int
+utf8_to_unicode(unsigned char* buf, size_t size)
+{
+    int ret;
+    if((buf[0] & 0x80) == 0x00) {
+        // 0xxxxxxx
+        ret = (int) buf[0];
+    } else if((buf[0] & 0xE0) == 0xC0 && size >= 2) {
+        // 110xxxxy 10yyyyyy
+        ret = ((buf[0] & 0x1F) << 6)
+            | ((buf[1] & 0x3F));
+    } else if((buf[0] & 0xF0) == 0xE0 && size >= 3) {
+        // 1110xxxx 10xyyyyy 10yyyyyy
+        ret = ((buf[0] & 0x0F) << 12)
+            | ((buf[1] & 0x3F) << 6)
+            | ((buf[2] & 0x3F));
+        if(ret >= 0xD800 && ret <= 0xDFFF) {
+            ret = -1;
+        }
+    } else if((buf[0] & 0xF8) == 0xF0 && size >= 4) {
+        // 11110xxx 10xxyyyy 10yyyyyy 10yyyyyy
+        ret = ((buf[0] & 0x07) << 18)
+            | ((buf[1] & 0x3F) << 12)
+            | ((buf[2] & 0x3F) << 6)
+            | ((buf[3] & 0x3F));
+    } else {
+        ret = -1;
+    }
+    return ret;
+}
+
+int
+unicode_to_utf8(int c, unsigned char* buf)
+{
+    if(c < 0x80) {
+        buf[0] = (unsigned char) c;
+        return 1;
+    } else if(c < 0x800) {
+        buf[0] = (unsigned char) 0xC0 + (c >> 6);
+        buf[1] = (unsigned char) 0x80 + (c & 0x3F);
+        return 2;
+    } else if(c < 0x10000) {
+        if(c < 0xD800 || (c > 0xDFFF && c < 0xFFFE)) {
+            buf[0] = (unsigned char) 0xE0 + (c >> 12);
+            buf[1] = (unsigned char) 0x80 + ((c >> 6) & 0x3F);
+            buf[2] = (unsigned char) 0x80 + (c & 0x3F);
+            return 3;
+        } else {
+            return -1;
+        }
+    } else if(c < 0x10FFFF) {
+        buf[0] = (unsigned char) 0xF0 + (c >> 18);
+        buf[1] = (unsigned char) 0x80 + ((c >> 12) & 0x3F);
+        buf[2] = (unsigned char) 0x80 + ((c >> 6) & 0x3F);
+        buf[3] = (unsigned char) 0x80 + (c & 0x3F);
+        return 4;
+    }
+    return -1;
+}
+
+int
+unicode_from_pair(int hi, int lo)
+{
+    if(hi < 0xD800 || hi >= 0xDC00) return -1;
+    if(lo < 0xDC00 || lo > 0xDFFF) return -1;
+    return ((hi & 0x3FF) << 10) + (lo & 0x3FF) + 0x10000;
+}
+
+int
+unicode_uescape(int val, char* p)
+{
+    int n;
+    if(val < 0x10000) {
+        p[0] = '\\';
+        p[1] = 'u';
+        if(int_to_hex(val, p+2) < 0) {
+            return -1;
+        }
+        return 6;
+    } else if (val <= 0x10FFFF) {
+        n = val - 0x10000;
+        p[0] = '\\';
+        p[1] = 'u';
+        if(int_to_hex((0xD800 | ((n >> 10) & 0x03FF)), p+2) < 0) {
+            return -1;
+        }
+        p[6] = '\\';
+        p[7] = 'u';
+        if(int_to_hex((0xDC00 | (n & 0x03FF)), p+8) < 0) {
+            return -1;
+        }
+        return 12;
+    }
+    return -1;
+}
+

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/c_src/util.c
----------------------------------------------------------------------
diff --git a/src/jiffy/c_src/util.c b/src/jiffy/c_src/util.c
new file mode 100644
index 0000000..f1be3ec
--- /dev/null
+++ b/src/jiffy/c_src/util.c
@@ -0,0 +1,26 @@
+// This file is part of Jiffy released under the MIT license.
+// See the LICENSE file for more information.
+
+#include "jiffy.h"
+
+ERL_NIF_TERM
+make_atom(ErlNifEnv* env, const char* name)
+{
+    ERL_NIF_TERM ret;
+    if(enif_make_existing_atom(env, name, &ret, ERL_NIF_LATIN1)) {
+        return ret;
+    }
+    return enif_make_atom(env, name);
+}
+
+ERL_NIF_TERM
+make_ok(jiffy_st* st, ErlNifEnv* env, ERL_NIF_TERM value)
+{
+    return enif_make_tuple2(env, st->atom_ok, value);
+}
+
+ERL_NIF_TERM
+make_error(jiffy_st* st, ErlNifEnv* env, const char* error)
+{
+    return enif_make_tuple2(env, st->atom_error, make_atom(env, error));
+}

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/rebar.config
----------------------------------------------------------------------
diff --git a/src/jiffy/rebar.config b/src/jiffy/rebar.config
new file mode 100644
index 0000000..f992d04
--- /dev/null
+++ b/src/jiffy/rebar.config
@@ -0,0 +1,34 @@
+{port_specs, [
+    {"priv/jiffy.so", [
+        "c_src/*.c",
+        "c_src/*.cc",
+        "c_src/double-conversion/*.cc"
+    ]}
+]}.
+
+{port_env, [
+    {".*", "CXXFLAGS", "$CXXFLAGS -g -Wall -Werror -O3"},
+
+    {"(linux|solaris|freebsd|netbsd|openbsd|dragonfly|darwin)",
+        "LDFLAGS", "$LDFLAGS -lstdc++"},
+
+    %% OS X Leopard flags for 64-bit
+    {"darwin9.*-64$", "CXXFLAGS", "-m64"},
+    {"darwin9.*-64$", "LDFLAGS", "-arch x86_64"},
+
+    %% OS X Snow Leopard flags for 32-bit
+    {"darwin10.*-32$", "CXXFLAGS", "-m32"},
+    {"darwin10.*-32$", "LDFLAGS", "-arch i386"},
+
+    %% This will merge into basho/rebar/rebar.config eventually
+    {"win32", "CFLAGS", "/Wall /DWIN32 /D_WINDOWS /D_WIN32 /DWINDOWS"},
+    {"win32", "CXXFLAGS", "-g -Wall -O3"}
+]}.
+
+
+{eunit_opts, [
+    verbose,
+    {report, {
+        eunit_surefire, [{dir,"."}]
+    }}
+]}.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/rebar.config.script
----------------------------------------------------------------------
diff --git a/src/jiffy/rebar.config.script b/src/jiffy/rebar.config.script
new file mode 100644
index 0000000..58d4ff7
--- /dev/null
+++ b/src/jiffy/rebar.config.script
@@ -0,0 +1,33 @@
+% This file is part of Jiffy released under the MIT license.
+% See the LICENSE file for more information.
+
+% Only include PropEr as a dependency when the JIFFY_DEV
+% environment variable is defined. This allows downstream
+% applications to avoid requiring PropEr.
+%
+% This script is based on the example provided with Rebar.
+
+ErlOpts = {erl_opts, [
+    {d, 'JIFFY_DEV'}
+]},
+
+Proper = [
+    {proper, ".*", {git, "git://github.com/manopapad/proper.git", "master"}}
+],
+
+ConfigPath = filename:dirname(SCRIPT),
+DevMarker = filename:join([ConfigPath, ".jiffy.dev"]),
+
+case filelib:is_file(DevMarker) of
+    true ->
+        % Don't override existing dependencies
+        NewConfig = case lists:keyfind(deps, 1, CONFIG) of
+            false ->
+                CONFIG ++ [{deps, Proper}];
+            {deps, DepsList} ->
+                lists:keyreplace(deps, 1, CONFIG, {deps, DepsList ++ Proper})
+        end,
+        NewConfig ++ [ErlOpts];
+    false ->
+        CONFIG
+end.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/src/jiffy.app.src
----------------------------------------------------------------------
diff --git a/src/jiffy/src/jiffy.app.src b/src/jiffy/src/jiffy.app.src
new file mode 100644
index 0000000..4ea304a
--- /dev/null
+++ b/src/jiffy/src/jiffy.app.src
@@ -0,0 +1,6 @@
+{application, jiffy, [
+    {description, "JSON Decoder/Encoder."},
+    {vsn, git},
+    {registered, []},
+    {applications, [kernel]}
+]}.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/src/jiffy.erl
----------------------------------------------------------------------
diff --git a/src/jiffy/src/jiffy.erl b/src/jiffy/src/jiffy.erl
new file mode 100644
index 0000000..c4b3d69
--- /dev/null
+++ b/src/jiffy/src/jiffy.erl
@@ -0,0 +1,107 @@
+% This file is part of Jiffy released under the MIT license.
+% See the LICENSE file for more information.
+
+-module(jiffy).
+-export([decode/1, encode/1, encode/2]).
+-define(NOT_LOADED, not_loaded(?LINE)).
+
+-on_load(init/0).
+
+decode(Data) when is_binary(Data) ->
+    case nif_decode(Data) of
+        {error, _} = Error ->
+            throw(Error);
+        {partial, EJson} ->
+            finish_decode(EJson);
+        EJson ->
+            EJson
+    end;
+decode(Data) when is_list(Data) ->
+    decode(iolist_to_binary(Data)).
+
+
+encode(Data) ->
+    encode(Data, []).
+
+
+encode(Data, Options) ->
+    ForceUTF8 = lists:member(force_utf8, Options),
+    case nif_encode(Data, Options) of
+        {error, invalid_string} when ForceUTF8 == true ->
+            FixedData = jiffy_utf8:fix(Data),
+            encode(FixedData, Options -- [force_utf8]);
+        {error, _} = Error ->
+            throw(Error);
+        {partial, IOData} ->
+            finish_encode(IOData, []);
+        IOData ->
+            IOData
+    end.
+
+
+finish_decode({bignum, Value}) ->
+    list_to_integer(binary_to_list(Value));
+finish_decode({bignum_e, Value}) ->
+    {IVal, EVal} = case string:to_integer(binary_to_list(Value)) of
+        {I, [$e | ExpStr]} ->
+            {E, []} = string:to_integer(ExpStr),
+            {I, E};
+        {I, [$E | ExpStr]} ->
+            {E, []} = string:to_integer(ExpStr),
+            {I, E}
+    end,
+    IVal * math:pow(10, EVal);
+finish_decode({bigdbl, Value}) ->
+    list_to_float(binary_to_list(Value));
+finish_decode({Pairs}) when is_list(Pairs) ->
+    finish_decode_obj(Pairs, []);
+finish_decode(Vals) when is_list(Vals) ->
+    finish_decode_arr(Vals, []);
+finish_decode(Val) ->
+    Val.
+
+finish_decode_obj([], Acc) ->
+    {lists:reverse(Acc)};
+finish_decode_obj([{K, V} | Pairs], Acc) ->
+    finish_decode_obj(Pairs, [{K, finish_decode(V)} | Acc]).
+
+finish_decode_arr([], Acc) ->
+    lists:reverse(Acc);
+finish_decode_arr([V | Vals], Acc) ->
+    finish_decode_arr(Vals, [finish_decode(V) | Acc]).
+
+
+finish_encode([], Acc) ->
+    %% No reverse! The NIF returned us
+    %% the pieces in reverse order.
+    Acc;
+finish_encode([<<_/binary>>=B | Rest], Acc) ->
+    finish_encode(Rest, [B | Acc]);
+finish_encode([Val | Rest], Acc) when is_integer(Val) ->
+    Bin = list_to_binary(integer_to_list(Val)),
+    finish_encode(Rest, [Bin | Acc]);
+finish_encode(_, _) ->
+    throw({error, invalid_ejson}).
+
+
+init() ->
+    PrivDir = case code:priv_dir(?MODULE) of
+        {error, _} ->
+            EbinDir = filename:dirname(code:which(?MODULE)),
+            AppPath = filename:dirname(EbinDir),
+            filename:join(AppPath, "priv");
+        Path ->
+            Path
+    end,
+    erlang:load_nif(filename:join(PrivDir, "jiffy"), 0).
+
+
+not_loaded(Line) ->
+    erlang:nif_error({not_loaded, [{module, ?MODULE}, {line, Line}]}).
+
+nif_decode(_Data) ->
+    ?NOT_LOADED.
+
+nif_encode(_Data, _Options) ->
+    ?NOT_LOADED.
+

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/src/jiffy_utf8.erl
----------------------------------------------------------------------
diff --git a/src/jiffy/src/jiffy_utf8.erl b/src/jiffy/src/jiffy_utf8.erl
new file mode 100644
index 0000000..ee937fe
--- /dev/null
+++ b/src/jiffy/src/jiffy_utf8.erl
@@ -0,0 +1,104 @@
+% This file is part of Jiffy released under the MIT license.
+% See the LICENSE file for more information.
+
+-module(jiffy_utf8).
+-export([fix/1]).
+
+
+fix({Props}) ->
+    fix_props(Props, []);
+fix(Values) when is_list(Values) ->
+    fix_array(Values, []);
+fix(Bin) when is_binary(Bin) ->
+    fix_bin(Bin);
+fix(Val) ->
+    Val.
+
+
+fix_props([], Acc) ->
+    {lists:reverse(Acc)};
+fix_props([{K0, V0} | Rest], Acc) ->
+    K = fix(K0),
+    V = fix(V0),
+    fix_props(Rest, [{K, V} | Acc]).
+
+
+fix_array([], Acc) ->
+    lists:reverse(Acc);
+fix_array([Val | Rest], Acc0) ->
+    Acc = [fix(Val) | Acc0],
+    fix_array(Rest, Acc).
+
+
+fix_bin(Bin) ->
+    Dec0 = loose_decode(Bin, 0, []),
+    Dec1 = try_combining(Dec0, []),
+    Dec2 = replace_garbage(Dec1, []),
+    list_to_binary(xmerl_ucs:to_utf8(Dec2)).
+
+
+loose_decode(Bin, O, Acc) ->
+    case Bin of
+        <<_:O/binary>> ->
+            lists:reverse(Acc);
+        <<_:O/binary, 0:1/integer, V:7/integer, _/binary>> ->
+            loose_decode(Bin, O+1, [V | Acc]);
+        <<_:O/binary, 6:3/integer, V0:5/integer,
+                2:2/integer, V1:6/integer, _/binary>> ->
+            B = <<0:5/integer, V0:5/integer, V1:6/integer>>,
+            <<V:16/integer>> = B,
+            loose_decode(Bin, O+2, [V | Acc]);
+        <<_:O/binary, 14:4/integer, V0:4/integer,
+                2:2/integer, V1:6/integer,
+                2:2/integer, V2:6/integer, _/binary>> ->
+            B = <<V0:4/integer, V1:6/integer, V2:6/integer>>,
+            <<V:16/integer>> = B,
+            loose_decode(Bin, O+3, [V | Acc]);
+        <<_:O/binary, 30:5/integer, V0:3/integer,
+                2:2/integer, V1:6/integer,
+                2:2/integer, V2:6/integer,
+                2:2/integer, V3:6/integer, _/binary>> ->
+            B = <<0:11/integer, V0:3/integer, V1:6/integer,
+                    V2:6/integer, V3:6/integer>>,
+            <<V:32/integer>> = B,
+            loose_decode(Bin, O+4, [V | Acc]);
+        <<_:O/binary, _:8/integer, R/binary>> ->
+            % Broken lead or continuation byte. Discard first
+            % byte and all broken continuations. Replace the
+            % whole mess with a replacment code point.
+            T = 1 + count_continuation_bytes(R, 0),
+            loose_decode(Bin, O+T, [16#FFFD | Acc])
+    end.
+
+
+count_continuation_bytes(R, O) ->
+    case R of
+        <<_:O/binary, 2:2/integer, _:6/integer, _/binary>> ->
+            count_continuation_bytes(R, O+1);
+        _ ->
+            O
+    end.
+
+
+try_combining([], Acc) ->
+    lists:reverse(Acc);
+try_combining([H, L | Rest], Acc) when H >= 16#D800, H =< 16#DFFF,
+                                        L >= 16#D800, L =< 16#DFFF ->
+    Bin = <<H:16/big-unsigned-integer, L:16/big-unsigned-integer>>,
+    try
+        [C] = xmerl_ucs:from_utf16be(Bin),
+        try_combining(Rest, [C | Acc])
+    catch _:_ ->
+        try_combining(Rest, [L, H | Acc])
+    end;
+try_combining([C | Rest], Acc) ->
+    try_combining(Rest, [C | Acc]).
+
+
+replace_garbage([], Acc) ->
+    lists:reverse(Acc);
+replace_garbage([C | Rest], Acc) ->
+    case xmerl_ucs:is_unicode(C) of
+        true -> replace_garbage(Rest, [C | Acc]);
+        false -> replace_garbage(Rest, [16#FFFD | Acc])
+    end.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/test/001-yajl-tests.t
----------------------------------------------------------------------
diff --git a/src/jiffy/test/001-yajl-tests.t b/src/jiffy/test/001-yajl-tests.t
new file mode 100755
index 0000000..b89fbee
--- /dev/null
+++ b/src/jiffy/test/001-yajl-tests.t
@@ -0,0 +1,30 @@
+#! /usr/bin/env escript
+% This file is part of Jiffy released under the MIT license.
+% See the LICENSE file for more information.
+
+main([]) ->
+    code:add_pathz("test"),
+    code:add_pathz("ebin"),
+
+    Cases = read_cases(),
+
+    etap:plan(length(Cases)),
+    lists:foreach(fun(Case) -> test(Case) end, Cases),
+    etap:end_tests().
+
+test({Name, Json, {error, _}=Erl}) ->
+    etap:is((catch jiffy:decode(Json)), Erl, Name);
+test({Name, Json, Erl}) ->
+    etap:is(jiffy:decode(Json), Erl, Name).
+
+read_cases() ->
+    CasesPath = filename:join(["test", "cases", "*.json"]),
+    FileNames = lists:sort(filelib:wildcard(CasesPath)),
+    lists:map(fun(F) -> make_pair(F) end, FileNames).
+
+make_pair(FileName) ->
+    {ok, Json} = file:read_file(FileName),
+    {BaseName, _} = lists:splitwith(fun(C) -> C /= $. end, FileName),
+    ErlFname = BaseName ++ ".eterm",
+    {ok, [Term]} = file:consult(ErlFname),
+    {BaseName, Json, Term}.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/test/002-literals.t
----------------------------------------------------------------------
diff --git a/src/jiffy/test/002-literals.t b/src/jiffy/test/002-literals.t
new file mode 100755
index 0000000..8df7255
--- /dev/null
+++ b/src/jiffy/test/002-literals.t
@@ -0,0 +1,21 @@
+#! /usr/bin/env escript
+% This file is part of Jiffy released under the MIT license.
+% See the LICENSE file for more information.
+
+main([]) ->
+    code:add_pathz("ebin"),
+    code:add_pathz("test"),
+
+    etap:plan(6),
+    etap:is(jiffy:decode(<<"true">>), true, "DEC: true -> true"),
+    etap:is(jiffy:encode(true), <<"true">>, "ENC: true -> true"),
+
+    etap:is(jiffy:decode(<<"false">>), false, "DEC: false -> false"),
+    etap:is(jiffy:encode(false), <<"false">>, "ENC: false -> false"),
+
+    etap:is(jiffy:decode(<<"null">>), null, "DEC: null -> null"),
+    etap:is(jiffy:encode(null), <<"null">>, "ENC: null -> null"),
+
+    etap:end_tests().
+
+

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/test/003-numbers.t
----------------------------------------------------------------------
diff --git a/src/jiffy/test/003-numbers.t b/src/jiffy/test/003-numbers.t
new file mode 100755
index 0000000..2ac2ab4
--- /dev/null
+++ b/src/jiffy/test/003-numbers.t
@@ -0,0 +1,118 @@
+#! /usr/bin/env escript
+% This file is part of Jiffy released under the MIT license.
+% See the LICENSE file for more information.
+
+main([]) ->
+    code:add_pathz("ebin"),
+    code:add_pathz("test"),
+
+    etap:plan(59 + 2 * length(double_conversion_tests())),
+    util:test_good(good()),
+    util:test_errors(errors()),
+    run_double_conversion_tests(),
+    etap:end_tests().
+
+
+run_double_conversion_tests() ->
+    lists:foreach(fun(Double) ->
+        Descr = io_lib:format("~f", [Double]),
+        etap:is(jiffy:decode(jiffy:encode(Double)), Double, Descr),
+        NegDouble = -1.0 * Double,
+        NegDescr = io_lib:format("~f", [NegDouble]),
+        etap:is(jiffy:decode(jiffy:encode(NegDouble)), NegDouble, NegDescr)
+    end, double_conversion_tests()).
+
+good() ->
+    [
+        {<<"0">>, 0},
+        {<<"-0">>, 0, <<"0">>},
+        {<<"1">>, 1},
+        {<<"12">>, 12},
+        {<<"-3">>, -3},
+        {<<"1234567890123456789012345">>, 1234567890123456789012345},
+        {<<"1310050760199">>, 1310050760199},
+        {
+            <<"1234567890123456789012345.0">>,
+            1.23456789012345678e24,
+            <<"1.2345678901234568e+24">>
+        },
+        {
+            <<"1234567890123456789012345.0E3">>,
+            1.2345678901234569e27,
+            <<"1.2345678901234569e+27">>
+        },
+        {
+            <<"1234567890123456789012345012">>,
+            1234567890123456789012345012,
+            <<"1234567890123456789012345012">>
+        },
+        {<<"1.0">>, 1.0},
+        {
+            <<"0.000000000000000000000000000000000001">>,
+            1.0E-36,
+            <<"1e-36">>
+        },
+        {<<"0.75">>, 0.75},
+        {<<"2.0123456789">>, 2.0123456789, <<"2.0123456789">>},
+        {<<"2.4234324E24">>, 2.4234324E24, <<"2.4234324e+24">>},
+        {<<"-3.1416">>, -3.1416, <<"-3.1416">>},
+        {<<"1E4">>, 10000.0, <<"10000.0">>},
+        {<<"1.0E+01">>, 10.0, <<"10.0">>},
+        {<<"1e1">>, 10.0, <<"10.0">>},
+        {<<"3.0E2">>, 300.0, <<"300.0">>},
+        {<<"0E3">>, 0.0, <<"0.0">>},
+        {<<"1.5E3">>, 1500.0, <<"1500.0">>},
+        {<<"2.5E-1">>, 0.25, <<"0.25">>},
+        {<<"-0.325E+2">>, -32.5, <<"-32.5">>}
+    ].
+
+
+errors() ->
+    [
+        <<"02">>,
+        <<"-01">>,
+        <<"+12">>,
+        <<"-">>,
+        <<"1.">>,
+        <<".1">>,
+        <<"1.-1">>,
+        <<"1E">>,
+        <<"1-E2">>,
+        <<"2E +3">>,
+        <<"1EA">>
+    ].
+
+
+double_conversion_tests() ->
+    [
+        0.0,
+        0.00000001,
+        0.000000012,
+        0.0000000123,
+        0.0000001,
+        0.00000012,
+        0.000000123,
+        0.000001,
+        0.00001,
+        0.01,
+        0.0123,
+        0.1,
+        0.3,
+        1.0,
+        1.0e20,
+        1.0e21,
+        9.0,
+        10.0,
+        90.0,
+        90.12,
+        10000.0,
+        12345.0,
+        12345.0e23,
+        100000.0,
+        100000000000000000000.0,
+        111111111111111111111.0,
+        1111111111111111111111.0,
+        11111111111111111111111.0
+    ].
+
+

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/test/004-strings.t
----------------------------------------------------------------------
diff --git a/src/jiffy/test/004-strings.t b/src/jiffy/test/004-strings.t
new file mode 100755
index 0000000..d5e5161
--- /dev/null
+++ b/src/jiffy/test/004-strings.t
@@ -0,0 +1,131 @@
+#! /usr/bin/env escript
+% This file is part of Jiffy released under the MIT license.
+% See the LICENSE file for more information.
+
+main([]) ->
+    code:add_pathz("ebin"),
+    code:add_pathz("test"),
+
+    etap:plan(119),
+    util:test_good(good()),
+    util:test_good(uescaped(), [uescape]),
+    util:test_errors(errors()),
+
+    test_utf8(utf8_cases()),
+
+    etap:end_tests().
+
+good() ->
+    [
+        {<<"\"\"">>, <<"">>},
+        {<<"\"/\"">>, <<"/">>},
+        {<<"\"0\"">>, <<"0">>},
+        {<<"\"foo\"">>, <<"foo">>},
+        {<<"\"\\\"foobar\\\"\"">>, <<"\"foobar\"">>},
+        {<<"\"\\n\\n\\n\"">>, <<"\n\n\n">>},
+        {<<"\"\\\" \\b\\f\\r\\n\\t\\\"\"">>, <<"\" \b\f\r\n\t\"">>},
+        {<<"\"foo\\u0005bar\"">>, <<"foo", 5, "bar">>},
+        {
+            <<"\"\\uD834\\uDD1E\"">>,
+            <<240, 157, 132, 158>>,
+            <<34, 240, 157, 132, 158, 34>>
+        }
+    ].
+
+uescaped() ->
+    [
+        {
+            <<"\"\\u8CA8\\u5481\\u3002\\u0091\\u0091\"">>,
+            <<232,178,168,229,146,129,227,128,130,194,145,194,145>>
+        },
+        {
+            <<"\"\\uD834\\uDD1E\"">>,
+            <<240, 157, 132, 158>>
+        },
+        {
+            <<"\"\\uD83D\\uDE0A\"">>,
+            <<240, 159, 152, 138>>
+        }
+    ].
+
+errors() ->
+    [
+        "\"",
+        <<"\"foo">>,
+        <<"\"", 0, "\"">>,
+        <<"\"\\g\"">>,
+        <<"\"\\uFFFF\"">>,
+        <<"\"\\uFFFE\"">>,
+        <<"\"\\uD834foo\\uDD1E\"">>,
+        % CouchDB-345
+        <<34,78,69,73,77,69,78,32,70,216,82,82,32,70,65,69,78,33,34>>
+    ].
+
+test_utf8([]) ->
+    ok;
+test_utf8([{Case, Fixed} | Rest]) ->
+    etap:fun_is(
+        fun({error, invalid_string}) -> true; (Else) -> Else end,
+        (catch jiffy:encode(Case)),
+        lists:flatten(io_lib:format("Invalid utf-8: ~p", [Case]))
+    ),
+    etap:fun_is(
+        fun(Fixed) -> true; (Else) -> Else end,
+        jiffy:encode(Case, [force_utf8]),
+        lists:flatten(io_lib:format("Fixed correctly: ~p", [Fixed]))
+    ),
+    Case2 = <<34, Case/binary, 34>>,
+    etap:fun_is(
+        fun({error, {_, invalid_string}}) -> true; (Else) -> Else end,
+        (catch jiffy:decode(Case2)),
+        lists:flatten(io_lib:format("Invalid utf-8: ~p", [Case2]))
+    ),
+    test_utf8(Rest).
+
+utf8_cases() ->
+    [
+        % Stray continuation byte
+        {<<16#C2, 16#81, 16#80>>, <<16#C2, 16#81, 16#EF, 16#BF, 16#BD>>},
+        {<<"foo", 16#80, "bar">>, <<"foo", 16#EF, 16#BF, 16#BD, "bar">>},
+
+        % Invalid Unicode code points
+        {<<239, 191, 190>>, <<16#EF, 16#BF, 16#BD>>},
+        {<<237, 160, 129>>, <<16#EF, 16#BF, 16#BD>>},
+
+        % Not enough extension bytes
+        {<<16#C0>>, <<16#EF, 16#BF, 16#BD>>},
+
+        {<<16#E0>>, <<16#EF, 16#BF, 16#BD>>},
+        {<<16#E0, 16#80>>, <<16#EF, 16#BF, 16#BD>>},
+
+        {<<16#F0>>, <<16#EF, 16#BF, 16#BD>>},
+        {<<16#F0, 16#80>>, <<16#EF, 16#BF, 16#BD>>},
+        {<<16#F0, 16#80, 16#80>>, <<16#EF, 16#BF, 16#BD>>},
+
+        {<<16#F8>>, <<16#EF, 16#BF, 16#BD>>},
+        {<<16#F8, 16#80>>, <<16#EF, 16#BF, 16#BD>>},
+        {<<16#F8, 16#80, 16#80>>, <<16#EF, 16#BF, 16#BD>>},
+        {<<16#F8, 16#80, 16#80, 16#80>>, <<16#EF, 16#BF, 16#BD>>},
+
+        {<<16#FC>>, <<16#EF, 16#BF, 16#BD>>},
+        {<<16#FC, 16#80>>, <<16#EF, 16#BF, 16#BD>>},
+        {<<16#FC, 16#80, 16#80>>, <<16#EF, 16#BF, 16#BD>>},
+        {<<16#FC, 16#80, 16#80, 16#80>>, <<16#EF, 16#BF, 16#BD>>},
+        {<<16#FC, 16#80, 16#80, 16#80, 16#80>>, <<16#EF, 16#BF, 16#BD>>},
+
+        % No data in high bits.
+        {<<16#C0, 16#80>>, <<"\"\\u0000\"">>},
+        {<<16#C1, 16#80>>, <<"\"\\u0000\"">>},
+
+        {<<16#E0, 16#80, 16#80>>, <<"\"\\u0000\"">>},
+        {<<16#E0, 16#90, 16#80>>, <<"\"\\u0000\"">>},
+
+        {<<16#F0, 16#80, 16#80, 16#80>>, <<"\"\\u0000\"">>},
+        {<<16#F0, 16#88, 16#80, 16#80>>, <<"\"\\u0000\"">>},
+
+        {<<16#F8, 16#80, 16#80, 16#80, 16#80>>, <<"\"\\u0000\"">>},
+        {<<16#F8, 16#84, 16#80, 16#80, 16#80>>, <<"\"\\u0000\"">>},
+
+        {<<16#FC, 16#80, 16#80, 16#80, 16#80, 16#80>>, <<"\"\\u0000\"">>},
+        {<<16#FC, 16#82, 16#80, 16#80, 16#80, 16#80>>, <<"\"\\u0000\"">>}
+    ].

http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/test/005-arrays.t
----------------------------------------------------------------------
diff --git a/src/jiffy/test/005-arrays.t b/src/jiffy/test/005-arrays.t
new file mode 100755
index 0000000..1c43c0e
--- /dev/null
+++ b/src/jiffy/test/005-arrays.t
@@ -0,0 +1,36 @@
+#! /usr/bin/env escript
+% This file is part of Jiffy released under the MIT license.
+% See the LICENSE file for more information.
+
+main([]) ->
+    code:add_pathz("ebin"),
+    code:add_pathz("test"),
+
+    etap:plan(18),
+    util:test_good(good()),
+    util:test_errors(errors()),
+    etap:end_tests().
+
+good() ->
+    [
+        {<<"[]">>, []},
+        {<<"[\t[\n]\r]">>, [[]], <<"[[]]">>},
+        {<<"[\t123, \r true\n]">>, [123, true], <<"[123,true]">>},
+        {<<"[1,\"foo\"]">>, [1, <<"foo">>]},
+        {<<"[11993444355.0,1]">>, [11993444355.0,1]},
+        {
+            <<"[\"\\u00A1\",\"\\u00FC\"]">>,
+            [<<194, 161>>, <<195, 188>>],
+            <<"[\"", 194, 161, "\",\"", 195, 188, "\"]">>
+        }
+    ].
+
+errors() ->
+    [
+        <<"[">>,
+        <<"]">>,
+        <<"[,]">>,
+        <<"[123">>,
+        <<"[123,]">>,
+        <<"[32 true]">>
+    ].


Mime
View raw message