summaryrefslogtreecommitdiff
path: root/src/couch/src/couch_ejson_compare.erl
blob: 8681296f159de30d15ffc3ead1edf3213f4a9c8e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
% 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, less_json_ids/2, less_json/2]).

% For testing
-export([less_nif/2, less_erl/2, compare_strings_nif/2]).


-on_load(init/0).


init() ->
    NumScheds = erlang:system_info(schedulers),
    Dir = code:priv_dir(couch),
    ok = erlang:load_nif(filename:join(Dir, ?MODULE), NumScheds).

% partitioned row comparison
less({p, PA, A}, {p, PB, B}) ->
    less([PA, A], [PB, B]);

less(A, B) ->
    try
        less_nif(A, B)
    catch
    error:max_depth_error ->
        % The EJSON structure is too deep, fallback to Erlang land.
        less_erl(A, B)
    end.

less_json_ids({JsonA, IdA}, {JsonB, IdB}) ->
    case less(JsonA, JsonB) of
    0 ->
        IdA < IdB;
    Result ->
        Result < 0
    end.

less_json(A,B) ->
    less(A, B) < 0.


less_nif(A, B) ->
    erlang:nif_error(less_nif_load_error, [A, B]).


compare_strings_nif(A, B) ->
    erlang:nif_error(compare_string_nif, [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) -> compare_strings_nif(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([], []) ->
    0;
less_props([], [_|_]) ->
    -1;
less_props(_, []) ->
    1;
less_props([{AKey, AValue}|RestA], [{BKey, BValue}|RestB]) ->
    case compare_strings_nif(AKey, BKey) of
    0 ->
        case less_erl(AValue, BValue) of
        0 ->
            less_props(RestA, RestB);
        Result ->
            Result
        end;
    Result ->
        Result
    end.

less_list([], []) ->
    0;
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.