You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by rn...@apache.org on 2013/12/24 00:31:15 UTC
[06/12] Replace ejson with jiffy
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]">>
+ ].