You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by fd...@apache.org on 2011/07/10 14:15:19 UTC

svn commit: r1144845 - in /couchdb/trunk: ./ src/couchdb/ src/couchdb/priv/ src/couchdb/priv/couch_ejson_compare/

Author: fdmanana
Date: Sun Jul 10 12:15:18 2011
New Revision: 1144845

URL: http://svn.apache.org/viewvc?rev=1144845&view=rev
Log:
Add NIF version for the less_json function

This is a NIF implementation of the existing less_json
function used for view collation. It speeds up the view
collation process. It fallbacks to the pure Erlang
implementation when running on OTP R13B04 or older.

This is part of COUCHDB-1186 and closes it.

Added:
    couchdb/trunk/src/couchdb/couch_ejson_compare.erl
    couchdb/trunk/src/couchdb/priv/couch_ejson_compare/
    couchdb/trunk/src/couchdb/priv/couch_ejson_compare/couch_ejson_compare.c
    couchdb/trunk/src/couchdb/priv/couch_ejson_compare/erl_nif_compat.h
Modified:
    couchdb/trunk/configure.ac
    couchdb/trunk/src/couchdb/Makefile.am
    couchdb/trunk/src/couchdb/couch_view.erl
    couchdb/trunk/src/couchdb/priv/Makefile.am

Modified: couchdb/trunk/configure.ac
URL: http://svn.apache.org/viewvc/couchdb/trunk/configure.ac?rev=1144845&r1=1144844&r2=1144845&view=diff
==============================================================================
--- couchdb/trunk/configure.ac (original)
+++ couchdb/trunk/configure.ac Sun Jul 10 12:15:18 2011
@@ -378,6 +378,7 @@ fi
 otp_release="`${ERL} -noshell -eval 'io:put_chars(erlang:system_info(otp_release)).' -s erlang halt`"
 AC_SUBST(otp_release)
 AM_CONDITIONAL([USE_OTP_NIFS], [test x$otp_release \> xR13B03])
+AM_CONDITIONAL([USE_EJSON_COMPARE_NIF], [test x$otp_release \> xR13B04])
 
 has_crypto=`${ERL} -eval "case application:load(crypto) of ok -> ok; _ -> exit(no_crypto) end." -noshell -s init stop`
 

Modified: couchdb/trunk/src/couchdb/Makefile.am
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/Makefile.am?rev=1144845&r1=1144844&r2=1144845&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/Makefile.am (original)
+++ couchdb/trunk/src/couchdb/Makefile.am Sun Jul 10 12:15:18 2011
@@ -42,6 +42,7 @@ source_files = \
     couch_db_update_notifier_sup.erl \
     couch_doc.erl \
     couch_drv.erl \
+    couch_ejson_compare.erl \
     couch_event_sup.erl \
     couch_external_manager.erl \
     couch_external_server.erl \
@@ -111,6 +112,7 @@ compiled_files = \
     couch_db_update_notifier_sup.beam \
     couch_doc.beam \
     couch_drv.beam \
+    couch_ejson_compare.beam \
     couch_event_sup.beam \
     couch_external_manager.beam \
     couch_external_server.beam \

Added: couchdb/trunk/src/couchdb/couch_ejson_compare.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_ejson_compare.erl?rev=1144845&view=auto
==============================================================================
--- couchdb/trunk/src/couchdb/couch_ejson_compare.erl (added)
+++ couchdb/trunk/src/couchdb/couch_ejson_compare.erl Sun Jul 10 12:15:18 2011
@@ -0,0 +1,102 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_ejson_compare).
+
+-export([less/2]).
+
+-on_load(init/0).
+
+
+init() ->
+    LibDir = case couch_config:get("couchdb", "util_driver_dir") of
+    undefined ->
+        filename:join(couch_util:priv_dir(), "lib");
+    LibDir0 ->
+        LibDir0
+    end,
+    NumScheds = erlang:system_info(schedulers),
+    (catch erlang:load_nif(filename:join([LibDir, ?MODULE]), NumScheds)),
+    case erlang:system_info(otp_release) of
+    "R13B03" -> true;
+    _ -> ok
+    end.
+
+
+less(A, B) ->
+    try
+        less_nif(A, B)
+    catch
+    error:badarg ->
+        % Maybe the EJSON structure is too deep, fallback to Erlang land.
+        less_erl(A, B)
+    end.
+
+
+less_nif(A, B) ->
+    less_erl(A, B).
+
+
+less_erl(A,A)                                 -> 0;
+
+less_erl(A,B) when is_atom(A), is_atom(B)     -> atom_sort(A) - atom_sort(B);
+less_erl(A,_) when is_atom(A)                 -> -1;
+less_erl(_,B) when is_atom(B)                 -> 1;
+
+less_erl(A,B) when is_number(A), is_number(B) -> A - B;
+less_erl(A,_) when is_number(A)               -> -1;
+less_erl(_,B) when is_number(B)               -> 1;
+
+less_erl(A,B) when is_binary(A), is_binary(B) -> couch_util:collate(A,B);
+less_erl(A,_) when is_binary(A)               -> -1;
+less_erl(_,B) when is_binary(B)               -> 1;
+
+less_erl(A,B) when is_list(A), is_list(B)     -> less_list(A,B);
+less_erl(A,_) when is_list(A)                 -> -1;
+less_erl(_,B) when is_list(B)                 -> 1;
+
+less_erl({A},{B}) when is_list(A), is_list(B) -> less_props(A,B);
+less_erl({A},_) when is_list(A)               -> -1;
+less_erl(_,{B}) when is_list(B)               -> 1.
+
+atom_sort(null) -> 1;
+atom_sort(false) -> 2;
+atom_sort(true) -> 3.
+
+less_props([], [_|_]) ->
+    -1;
+less_props(_, []) ->
+    1;
+less_props([{AKey, AValue}|RestA], [{BKey, BValue}|RestB]) ->
+    case couch_util:collate(AKey, BKey) of
+    0 ->
+        case less_erl(AValue, BValue) of
+        0 ->
+            less_props(RestA, RestB);
+        Result ->
+            Result
+        end;
+    Result ->
+        Result
+    end.
+
+less_list([], [_|_]) ->
+    -1;
+less_list(_, []) ->
+    1;
+less_list([A|RestA], [B|RestB]) ->
+    case less_erl(A,B) of
+    0 ->
+        less_list(RestA, RestB);
+    Result ->
+        Result
+    end.

Modified: couchdb/trunk/src/couchdb/couch_view.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_view.erl?rev=1144845&r1=1144844&r2=1144845&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_view.erl (original)
+++ couchdb/trunk/src/couchdb/couch_view.erl Sun Jul 10 12:15:18 2011
@@ -399,7 +399,7 @@ nuke_dir(RootDelDir, Dir) ->
 
 % keys come back in the language of btree - tuples.
 less_json_ids({JsonA, IdA}, {JsonB, IdB}) ->
-    case less_json0(JsonA, JsonB) of
+    case couch_ejson_compare:less(JsonA, JsonB) of
     0 ->
         IdA < IdB;
     Result ->
@@ -407,59 +407,4 @@ less_json_ids({JsonA, IdA}, {JsonB, IdB}
     end.
 
 less_json(A,B) ->
-    less_json0(A,B) < 0.
-
-less_json0(A,A)                                 -> 0;
-
-less_json0(A,B) when is_atom(A), is_atom(B)     -> atom_sort(A) - atom_sort(B);
-less_json0(A,_) when is_atom(A)                 -> -1;
-less_json0(_,B) when is_atom(B)                 -> 1;
-
-less_json0(A,B) when is_number(A), is_number(B) -> A - B;
-less_json0(A,_) when is_number(A)               -> -1;
-less_json0(_,B) when is_number(B)               -> 1;
-
-less_json0(A,B) when is_binary(A), is_binary(B) -> couch_util:collate(A,B);
-less_json0(A,_) when is_binary(A)               -> -1;
-less_json0(_,B) when is_binary(B)               -> 1;
-
-less_json0(A,B) when is_list(A), is_list(B)     -> less_list(A,B);
-less_json0(A,_) when is_list(A)                 -> -1;
-less_json0(_,B) when is_list(B)                 -> 1;
-
-less_json0({A},{B}) when is_list(A), is_list(B) -> less_props(A,B);
-less_json0({A},_) when is_list(A)               -> -1;
-less_json0(_,{B}) when is_list(B)               -> 1.
-
-atom_sort(null) -> 1;
-atom_sort(false) -> 2;
-atom_sort(true) -> 3.
-
-less_props([], [_|_]) ->
-    -1;
-less_props(_, []) ->
-    1;
-less_props([{AKey, AValue}|RestA], [{BKey, BValue}|RestB]) ->
-    case couch_util:collate(AKey, BKey) of
-    0 ->
-        case less_json0(AValue, BValue) of
-        0 ->
-            less_props(RestA, RestB);
-        Result ->
-            Result
-        end;
-    Result ->
-        Result
-    end.
-
-less_list([], [_|_]) ->
-    -1;
-less_list(_, []) ->
-    1;
-less_list([A|RestA], [B|RestB]) ->
-    case less_json0(A,B) of
-    0 ->
-        less_list(RestA, RestB);
-    Result ->
-        Result
-    end.
+    couch_ejson_compare:less(A, B) < 0.

Modified: couchdb/trunk/src/couchdb/priv/Makefile.am
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/priv/Makefile.am?rev=1144845&r1=1144844&r2=1144845&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/priv/Makefile.am (original)
+++ couchdb/trunk/src/couchdb/priv/Makefile.am Sun Jul 10 12:15:18 2011
@@ -16,11 +16,22 @@ couchprivlibdir = $(couchlibdir)/priv/li
 
 EXTRA_DIST = \
 	spawnkillable/couchspawnkillable.sh \
-	stat_descriptions.cfg.in
+	stat_descriptions.cfg.in \
+	couch_ejson_compare/erl_nif_compat.h
 
 CLEANFILES = stat_descriptions.cfg
 
 couchprivlib_LTLIBRARIES = couch_icu_driver.la
+if USE_EJSON_COMPARE_NIF
+couchprivlib_LTLIBRARIES += couch_ejson_compare.la
+couch_ejson_compare_la_SOURCES = couch_ejson_compare/couch_ejson_compare.c
+couch_ejson_compare_la_CFLAGS = -D_BSD_SOURCE $(ICU_CFLAGS) $(ERLANG_FLAGS)
+couch_ejson_compare_la_LDFLAGS = -module -avoid-version
+couch_ejson_compare_la_LIBADD = $(ICU_LIBS)
+if WINDOWS
+couch_ejson_compare_la_LDFLAGS += -no-undefined
+endif
+endif
 couch_icu_driver_la_SOURCES = icu_driver/couch_icu_driver.c
 couch_icu_driver_la_LDFLAGS = -module -avoid-version
 couch_icu_driver_la_CFLAGS = $(ICU_CFLAGS) $(ERLANG_FLAGS)
@@ -71,6 +82,11 @@ install-data-hook:
 	    cd "$(DESTDIR)$(couchprivlibdir)" && \
 	        $(LN_S) couch_icu_driver couch_icu_driver.so; \
 	fi
+	if test -f "$(DESTDIR)$(couchprivlibdir)/couch_ejson_compare_nif"; then \
+	    rm -f "$(DESTDIR)$(couchprivlibdir)/couch_ejson_compare_nif.so"; \
+	    cd "$(DESTDIR)$(couchprivlibdir)" && \
+	        $(LN_S) couch_ejson_compare_nif couch_ejson_compare_nif.so; \
+	fi
 if WINDOWS
 	$(INSTALL) $(ICU_BIN)/icuuc42.dll $(bindir)
 	$(INSTALL) $(ICU_BIN)/icudt42.dll $(bindir)

Added: couchdb/trunk/src/couchdb/priv/couch_ejson_compare/couch_ejson_compare.c
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/priv/couch_ejson_compare/couch_ejson_compare.c?rev=1144845&view=auto
==============================================================================
--- couchdb/trunk/src/couchdb/priv/couch_ejson_compare/couch_ejson_compare.c (added)
+++ couchdb/trunk/src/couchdb/priv/couch_ejson_compare/couch_ejson_compare.c Sun Jul 10 12:15:18 2011
@@ -0,0 +1,456 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include "erl_nif_compat.h"
+#include "unicode/ucol.h"
+#include "unicode/ucasemap.h"
+
+#define MAX_DEPTH 10
+
+static ERL_NIF_TERM ATOM_TRUE;
+static ERL_NIF_TERM ATOM_FALSE;
+static ERL_NIF_TERM ATOM_NULL;
+
+typedef struct {
+    ErlNifEnv* env;
+    int error;
+    UCollator* coll;
+} ctx_t;
+
+static UCollator** collators = NULL;
+static int collStackTop = 0;
+static int numCollators = 0;
+static ErlNifMutex* collMutex = NULL;
+
+static ERL_NIF_TERM less_json_nif(ErlNifEnv*, int, const ERL_NIF_TERM []);
+static int on_load(ErlNifEnv*, void**, ERL_NIF_TERM);
+static void on_unload(ErlNifEnv*, void*);
+static __inline int less_json(int, ctx_t*, ERL_NIF_TERM, ERL_NIF_TERM);
+static __inline int atom_sort_order(ErlNifEnv*, ERL_NIF_TERM);
+static __inline int compare_strings(ctx_t*, ErlNifBinary, ErlNifBinary);
+static __inline int compare_lists(int, ctx_t*, ERL_NIF_TERM, ERL_NIF_TERM);
+static __inline int compare_props(int, ctx_t*, ERL_NIF_TERM, ERL_NIF_TERM);
+static __inline int term_is_number(ErlNifEnv*, ERL_NIF_TERM);
+static __inline void reserve_coll(ctx_t*);
+static __inline void release_coll(ctx_t*);
+
+
+void
+reserve_coll(ctx_t *ctx)
+{
+    if (ctx->coll == NULL) {
+        enif_mutex_lock(collMutex);
+        assert(collStackTop < numCollators);
+        ctx->coll = collators[collStackTop];
+        collStackTop += 1;
+        enif_mutex_unlock(collMutex);
+    }
+}
+
+
+void
+release_coll(ctx_t *ctx)
+{
+    if (ctx->coll != NULL) {
+        enif_mutex_lock(collMutex);
+        collStackTop -= 1;
+        assert(collStackTop >= 0);
+        enif_mutex_unlock(collMutex);
+    }
+}
+
+
+
+ERL_NIF_TERM
+less_json_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+    ctx_t ctx;
+    int result;
+
+    ctx.env = env;
+    ctx.error = 0;
+    ctx.coll = NULL;
+
+    result = less_json(1, &ctx, argv[0], argv[1]);
+    release_coll(&ctx);
+
+    /*
+     * There are 2 possible failure reasons:
+     *
+     * 1) We got an invalid EJSON operand;
+     * 2) The EJSON structures are too deep - to avoid allocating too
+     *    many C stack frames (because less_json is a recursive function),
+     *    and running out of memory, we throw a badarg exception to Erlang
+     *    and do the comparison in Erlang land. In practice, views keys are
+     *    EJSON structures with very little nesting.
+     */
+    return ctx.error ? enif_make_badarg(env) : enif_make_int(env, result);
+}
+
+
+int
+less_json(int depth, ctx_t* ctx, ERL_NIF_TERM a, ERL_NIF_TERM b)
+{
+    int aIsAtom, bIsAtom;
+    int aIsBin, bIsBin;
+    int aIsNumber, bIsNumber;
+    int aIsList, bIsList;
+    int aArity, bArity;
+    const ERL_NIF_TERM *aProps, *bProps;
+
+    /*
+     * Avoid too much recursion. Normally there isn't more than a few levels
+     * of recursion, as in practice view keys do not go beyond 1 to 3 levels
+     * of nesting. In case of too much recursion, signal it to the Erlang land
+     * via an exception and do the EJSON comparison in Erlang land.
+     */
+    if (depth > MAX_DEPTH) {
+        ctx->error = 1;
+        return 0;
+    }
+
+    aIsAtom = enif_is_atom(ctx->env, a);
+    bIsAtom = enif_is_atom(ctx->env, b);
+
+    if (aIsAtom) {
+        if (bIsAtom) {
+            int aSortOrd, bSortOrd;
+
+            if ((aSortOrd = atom_sort_order(ctx->env, a)) == -1) {
+                ctx->error = 1;
+                return 0;
+            }
+
+            if ((bSortOrd = atom_sort_order(ctx->env, b)) == -1) {
+                ctx->error = 1;
+                return 0;
+            }
+
+            return aSortOrd - bSortOrd;
+        }
+
+        return -1;
+    }
+
+    if (bIsAtom) {
+        return 1;
+    }
+
+    aIsNumber = term_is_number(ctx->env, a);
+    bIsNumber = term_is_number(ctx->env, b);
+
+    if (aIsNumber) {
+        if (bIsNumber) {
+            return enif_compare_compat(ctx->env, a, b);
+        }
+
+        return -1;
+    }
+
+    if (bIsNumber) {
+        return 1;
+    }
+
+    aIsBin = enif_is_binary(ctx->env, a);
+    bIsBin = enif_is_binary(ctx->env, b);
+
+    if (aIsBin) {
+        if (bIsBin) {
+            ErlNifBinary binA, binB;
+
+            enif_inspect_binary(ctx->env, a, &binA);
+            enif_inspect_binary(ctx->env, b, &binB);
+
+            return compare_strings(ctx, binA, binB);
+        }
+
+        return -1;
+    }
+
+    if (bIsBin) {
+        return 1;
+    }
+
+    aIsList = enif_is_list(ctx->env, a);
+    bIsList = enif_is_list(ctx->env, b);
+
+    if (aIsList) {
+        if (bIsList) {
+            return compare_lists(depth, ctx, a, b);
+        }
+
+        return -1;
+    }
+
+    if (bIsList) {
+        return 1;
+    }
+
+    if (!enif_get_tuple(ctx->env, a, &aArity, &aProps)) {
+        ctx->error = 1;
+        return 0;
+    }
+    if ((aArity != 1) || !enif_is_list(ctx->env, aProps[0])) {
+        ctx->error = 1;
+        return 0;
+    }
+
+    if (!enif_get_tuple(ctx->env, b, &bArity, &bProps)) {
+        ctx->error = 1;
+        return 0;
+    }
+    if ((bArity != 1) || !enif_is_list(ctx->env, bProps[0])) {
+        ctx->error = 1;
+        return 0;
+    }
+
+    return compare_props(depth, ctx, aProps[0], bProps[0]);
+}
+
+
+int
+atom_sort_order(ErlNifEnv* env, ERL_NIF_TERM a)
+{
+    if (enif_compare_compat(env, a, ATOM_NULL) == 0) {
+        return 1;
+    } else if (enif_compare_compat(env, a, ATOM_FALSE) == 0) {
+        return 2;
+    } else if (enif_compare_compat(env, a, ATOM_TRUE) == 0) {
+        return 3;
+    }
+
+    return -1;
+}
+
+
+int
+term_is_number(ErlNifEnv* env, ERL_NIF_TERM t)
+{
+    /* Determination by exclusion of parts. To be used only inside less_json! */
+    return !enif_is_binary(env, t) && !enif_is_list(env, t) &&
+        !enif_is_tuple(env, t);
+}
+
+
+int
+compare_lists(int depth, ctx_t* ctx, ERL_NIF_TERM a, ERL_NIF_TERM b)
+{
+    ERL_NIF_TERM headA, tailA;
+    ERL_NIF_TERM headB, tailB;
+    int aIsEmpty, bIsEmpty;
+    int result;
+
+    while (1) {
+        aIsEmpty = !enif_get_list_cell(ctx->env, a, &headA, &tailA);
+        bIsEmpty = !enif_get_list_cell(ctx->env, b, &headB, &tailB);
+
+        if (aIsEmpty) {
+            if (bIsEmpty) {
+                return 0;
+            }
+            return -1;
+        }
+
+        if (bIsEmpty) {
+            return 1;
+        }
+
+        result = less_json(depth + 1, ctx, headA, headB);
+
+        if (ctx->error || result != 0) {
+            return result;
+        }
+
+        a = tailA;
+        b = tailB;
+    }
+
+    return result;
+}
+
+
+int
+compare_props(int depth, ctx_t* ctx, ERL_NIF_TERM a, ERL_NIF_TERM b)
+{
+    ERL_NIF_TERM headA, tailA;
+    ERL_NIF_TERM headB, tailB;
+    int aArity, bArity;
+    const ERL_NIF_TERM *aKV, *bKV;
+    ErlNifBinary keyA, keyB;
+    int aIsEmpty, bIsEmpty;
+    int keyCompResult, valueCompResult;
+
+    while (1) {
+        aIsEmpty = !enif_get_list_cell(ctx->env, a, &headA, &tailA);
+        bIsEmpty = !enif_get_list_cell(ctx->env, b, &headB, &tailB);
+
+        if (aIsEmpty) {
+            if (bIsEmpty) {
+                return 0;
+            }
+            return -1;
+        }
+
+        if (bIsEmpty) {
+            return 1;
+        }
+
+        if (!enif_get_tuple(ctx->env, headA, &aArity, &aKV)) {
+            ctx->error = 1;
+            return 0;
+        }
+        if ((aArity != 2) || !enif_inspect_binary(ctx->env, aKV[0], &keyA)) {
+            ctx->error = 1;
+            return 0;
+        }
+
+        if (!enif_get_tuple(ctx->env, headB, &bArity, &bKV)) {
+            ctx->error = 1;
+            return 0;
+        }
+        if ((bArity != 2) || !enif_inspect_binary(ctx->env, bKV[0], &keyB)) {
+            ctx->error = 1;
+            return 0;
+        }
+
+        keyCompResult = compare_strings(ctx, keyA, keyB);
+
+        if (ctx->error || keyCompResult != 0) {
+            return keyCompResult;
+        }
+
+        valueCompResult = less_json(depth + 1, ctx, aKV[1], bKV[1]);
+
+        if (ctx->error || valueCompResult != 0) {
+            return valueCompResult;
+        }
+
+        a = tailA;
+        b = tailB;
+    }
+
+    return 0;
+}
+
+
+int
+compare_strings(ctx_t* ctx, ErlNifBinary a, ErlNifBinary b)
+{
+    UErrorCode status = U_ZERO_ERROR;
+    UCharIterator iterA, iterB;
+    int result;
+
+    uiter_setUTF8(&iterA, (const char *) a.data, (uint32_t) a.size);
+    uiter_setUTF8(&iterB, (const char *) b.data, (uint32_t) b.size);
+
+    reserve_coll(ctx);
+    result = ucol_strcollIter(ctx->coll, &iterA, &iterB, &status);
+
+    if (U_FAILURE(status)) {
+        ctx->error = 1;
+        return 0;
+    }
+
+    /* ucol_strcollIter returns 0, -1 or 1
+     * (see type UCollationResult in unicode/ucol.h) */
+
+    return result;
+}
+
+
+int
+on_load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
+{
+    UErrorCode status = U_ZERO_ERROR;
+    int i, j;
+
+    if (!enif_get_int(env, info, &numCollators)) {
+        return 1;
+    }
+
+    if (numCollators < 1) {
+        return 2;
+    }
+
+    collMutex = enif_mutex_create("coll_mutex");
+
+    if (collMutex == NULL) {
+        return 3;
+    }
+
+    collators = enif_alloc(sizeof(UCollator*) * numCollators);
+
+    if (collators == NULL) {
+        enif_mutex_destroy(collMutex);
+        return 4;
+    }
+
+    for (i = 0; i < numCollators; i++) {
+        collators[i] = ucol_open("", &status);
+
+        if (U_FAILURE(status)) {
+            for (j = 0; j < i; j++) {
+                ucol_close(collators[j]);
+            }
+
+            enif_free(collators);
+            enif_mutex_destroy(collMutex);
+
+            return 5;
+        }
+    }
+
+    ATOM_TRUE = enif_make_atom(env, "true");
+    ATOM_FALSE = enif_make_atom(env, "false");
+    ATOM_NULL = enif_make_atom(env, "null");
+
+    return 0;
+}
+
+
+void
+on_unload(ErlNifEnv* env, void* priv_data)
+{
+    if (collators != NULL) {
+        int i;
+
+        for (i = 0; i < numCollators; i++) {
+            ucol_close(collators[i]);
+        }
+
+        enif_free(collators);
+    }
+
+    if (collMutex != NULL) {
+        enif_mutex_destroy(collMutex);
+    }
+}
+
+
+static ErlNifFunc nif_functions[] = {
+    {"less_nif", 2, less_json_nif}
+};
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ERL_NIF_INIT(couch_ejson_compare, nif_functions, &on_load, NULL, NULL, &on_unload);
+
+#ifdef __cplusplus
+}
+#endif

Added: couchdb/trunk/src/couchdb/priv/couch_ejson_compare/erl_nif_compat.h
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/priv/couch_ejson_compare/erl_nif_compat.h?rev=1144845&view=auto
==============================================================================
--- couchdb/trunk/src/couchdb/priv/couch_ejson_compare/erl_nif_compat.h (added)
+++ couchdb/trunk/src/couchdb/priv/couch_ejson_compare/erl_nif_compat.h Sun Jul 10 12:15:18 2011
@@ -0,0 +1,128 @@
+/* Copyright (c) 2010-2011 Basho Technologies, Inc.
+ * With some minor modifications for Apache CouchDB.
+ *
+ * This file is provided to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License.  You may obtain
+ * a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+*/
+
+#ifndef ERL_NIF_COMPAT_H_
+#define ERL_NIF_COMPAT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "erl_nif.h"
+
+
+#if ERL_NIF_MAJOR_VERSION == 0 && ERL_NIF_MINOR_VERSION == 1
+#define OTP_R13B03
+#elif ERL_NIF_MAJOR_VERSION == 1 && ERL_NIF_MINOR_VERSION == 0
+#define OTP_R13B04
+#elif ERL_NIF_MAJOR_VERSION == 2 && ERL_NIF_MINOR_VERSION == 0
+#define OTP_R14A
+#define OTP_R14B
+#define OTP_R14B01
+#elif ERL_NIF_MAJOR_VERSION == 2 && ERL_NIF_MINOR_VERSION == 1
+#define OTP_R14B02
+#elif ERL_NIF_MAJOR_VERSION == 2 && ERL_NIF_MINOR_VERSION == 2
+#define OTP_R14B03
+#endif
+
+
+#ifdef OTP_R13B03
+
+#define enif_open_resource_type_compat enif_open_resource_type
+#define enif_alloc_resource_compat enif_alloc_resource
+#define enif_release_resource_compat enif_release_resource
+#define enif_alloc_binary_compat enif_alloc_binary
+#define enif_alloc_compat enif_alloc
+#define enif_release_binary_compat enif_release_binary
+#define enif_free_compat enif_free
+#define enif_get_atom_compat enif_get_atom
+#define enif_priv_data_compat enif_get_data
+#define enif_make_uint_compat enif_make_ulong
+
+#define enif_make_string_compat(E, B, Enc) \
+    enif_make_string(E, B)
+
+#define enif_compare_compat enif_compare
+
+#endif /* R13B03 */
+
+
+#ifdef OTP_R13B04
+
+#define enif_open_resource_type_compat enif_open_resource_type
+#define enif_alloc_resource_compat enif_alloc_resource
+#define enif_release_resource_compat enif_release_resource
+#define enif_alloc_binary_compat enif_alloc_binary
+#define enif_realloc_binary_compat enif_realloc_binary
+#define enif_release_binary_compat enif_release_binary
+#define enif_alloc_compat enif_alloc
+#define enif_free_compat enif_free
+#define enif_get_atom_compat enif_get_atom
+#define enif_priv_data_compat enif_priv_data
+#define enif_make_string_compat enif_make_string
+#define enif_make_uint_compat enif_make_uint
+#define enif_compare_compat enif_compare
+
+#endif /* R13B04 */
+
+
+/* OTP R14 and future releases */
+#if !defined(OTP_R13B03) && !defined(OTP_R13B04)
+
+#define enif_open_resource_type_compat(E, N, D, F, T) \
+    enif_open_resource_type(E, NULL, N, D, F, T)
+
+#define enif_alloc_resource_compat(E, T, S) \
+    enif_alloc_resource(T, S)
+
+#define enif_release_resource_compat(E, H) \
+    enif_release_resource(H)
+
+#define enif_alloc_binary_compat(E, S, B) \
+    enif_alloc_binary(S, B)
+
+#define enif_realloc_binary_compat(E, S, B) \
+    enif_realloc_binary(S, B)
+
+#define enif_release_binary_compat(E, B) \
+    enif_release_binary(B)
+
+#define enif_alloc_compat(E, S) \
+    enif_alloc(S)
+
+#define enif_free_compat(E, P) \
+    enif_free(P)
+
+#define enif_get_atom_compat(E, T, B, S) \
+    enif_get_atom(E, T, B, S, ERL_NIF_LATIN1)
+
+#define enif_priv_data_compat enif_priv_data
+#define enif_make_string_compat enif_make_string
+#define enif_make_uint_compat enif_make_uint
+
+#define enif_compare_compat(E, A, B) \
+    enif_compare(A, B)
+
+#endif  /* R14 and future releases */
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* ERL_NIF_COMPAT_H_ */