1 
2 /**
3  *    Copyright (C) 2018-present MongoDB, Inc.
4  *
5  *    This program is free software: you can redistribute it and/or modify
6  *    it under the terms of the Server Side Public License, version 1,
7  *    as published by MongoDB, Inc.
8  *
9  *    This program is distributed in the hope that it will be useful,
10  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *    Server Side Public License for more details.
13  *
14  *    You should have received a copy of the Server Side Public License
15  *    along with this program. If not, see
16  *    <http://www.mongodb.com/licensing/server-side-public-license>.
17  *
18  *    As a special exception, the copyright holders give permission to link the
19  *    code of portions of this program with the OpenSSL library under certain
20  *    conditions as described in each individual source file and distribute
21  *    linked combinations including the program with the OpenSSL library. You
22  *    must comply with the Server Side Public License in all respects for
23  *    all of the code used other than as permitted herein. If you modify file(s)
24  *    with this exception, you may extend this exception to your version of the
25  *    file(s), but you are not obligated to do so. If you do not wish to do so,
26  *    delete this exception statement from your version. If you delete this
27  *    exception statement from all source files in the program, then also delete
28  *    it in the license file.
29  */
30 
31 #pragma once
32 
33 #include <boost/container/flat_set.hpp>
34 #include <initializer_list>
35 #include <map>
36 #include <set>
37 #include <vector>
38 
39 #include "mongo/base/disallow_copying.h"
40 #include "mongo/base/string_data_comparator_interface.h"
41 #include "mongo/stdx/unordered_map.h"
42 #include "mongo/stdx/unordered_set.h"
43 #include "mongo/util/assert_util.h"
44 
45 namespace mongo {
46 
47 class BSONElement;
48 class BSONObj;
49 
50 /**
51  * Base class for the BSONObj and BSONElement comparator interfaces.
52  */
53 template <typename T>
54 class BSONComparatorInterfaceBase {
55     MONGO_DISALLOW_COPYING(BSONComparatorInterfaceBase);
56 
57 public:
58     BSONComparatorInterfaceBase(BSONComparatorInterfaceBase&& other) = default;
59     BSONComparatorInterfaceBase& operator=(BSONComparatorInterfaceBase&& other) = default;
60 
61     /**
62      * Set of rules used in the comparison of BSON Objects and Elements.
63      */
64     enum ComparisonRules {
65         // Set this bit to consider the field name in element comparisons.
66         // if (kConsiderFieldName = 0) --> 'a: 1' == 'b: 1'
67         // if (kConsiderFieldName = 1) --> 'a: 1' != 'b: 1'
68         kConsiderFieldName = 1 << 0,
69 
70         // Set this bit to ignore the element order in BSON Object comparisons. This field will
71         // remain set/unset for nested objects.
72         //
73         // e.g. if kIgnoreFieldOrder == 1, then the following objects are considered equal:
74         //
75         // obj1: {
76         //     a: {
77         //         b: 1,
78         //         c: 1
79         //     },
80         //     d: 1
81         // }
82         //
83         // obj2: {
84         //     d: 1,
85         //     a: {
86         //         c: 1,
87         //         b: 1,
88         //     },
89         // }
90         kIgnoreFieldOrder = 1 << 1,
91     };
92     using ComparisonRulesSet = uint32_t;
93 
94     /**
95      * A deferred comparison between two objects of type T, which can be converted into a boolean
96      * via the evaluate() method.
97      */
98     struct DeferredComparison {
99         enum class Type {
100             kLT,
101             kLTE,
102             kEQ,
103             kGT,
104             kGTE,
105             kNE,
106         };
107 
DeferredComparisonDeferredComparison108         DeferredComparison(Type type, const T& lhs, const T& rhs)
109             : type(type), lhs(lhs), rhs(rhs) {}
110 
111         Type type;
112         const T& lhs;
113         const T& rhs;
114     };
115 
116     /**
117      * Functor compatible for use with ordered STL containers.
118      */
119     class LessThan {
120     public:
LessThan(const BSONComparatorInterfaceBase * comparator)121         explicit LessThan(const BSONComparatorInterfaceBase* comparator)
122             : _comparator(comparator) {}
123 
operator()124         bool operator()(const T& lhs, const T& rhs) const {
125             return _comparator->compare(lhs, rhs) < 0;
126         }
127 
128     private:
129         const BSONComparatorInterfaceBase* _comparator;
130     };
131 
132     /**
133      * Functor compatible for use with unordered STL containers.
134      */
135     class EqualTo {
136     public:
EqualTo(const BSONComparatorInterfaceBase * comparator)137         explicit EqualTo(const BSONComparatorInterfaceBase* comparator) : _comparator(comparator) {}
138 
operator()139         bool operator()(const T& lhs, const T& rhs) const {
140             return _comparator->compare(lhs, rhs) == 0;
141         }
142 
143     private:
144         const BSONComparatorInterfaceBase* _comparator;
145     };
146 
147     /**
148      * Function object for hashing with respect to this comparator. Compatible with the hash concept
149      * from std::hash.
150      */
151     class Hasher {
152     public:
Hasher(const BSONComparatorInterfaceBase * comparator)153         explicit Hasher(const BSONComparatorInterfaceBase* comparator) : _comparator(comparator) {}
154 
operator()155         size_t operator()(const T& toHash) const {
156             return _comparator->hash(toHash);
157         }
158 
159     private:
160         const BSONComparatorInterfaceBase* _comparator;
161     };
162 
163     using Set = std::set<T, LessThan>;
164 
165     using FlatSet = boost::container::flat_set<T, LessThan>;
166 
167     using UnorderedSet = stdx::unordered_set<T, Hasher, EqualTo>;
168 
169     template <typename ValueType>
170     using Map = std::map<T, ValueType, LessThan>;
171 
172     template <typename ValueType>
173     using UnorderedMap = stdx::unordered_map<T, ValueType, Hasher, EqualTo>;
174 
175     virtual ~BSONComparatorInterfaceBase() = default;
176 
177     /**
178      * Compares two BSONObj/BSONElement objects. Returns <0, 0, >0 if 'lhs' < 'rhs', 'lhs' == 'rhs',
179      * or 'lhs' > 'rhs' respectively.
180      */
181     virtual int compare(const T& lhs, const T& rhs) const = 0;
182 
183     /**
184      * Produces a hash for a BSONObj or BSONElement in such a way that respects this comparator.
185      *
186      * The hash function is subject to change. Do not use in cases where hashes need to be
187      * consistent across versions.
188      */
hash(const T & toHash)189     size_t hash(const T& toHash) const {
190         size_t seed = 0;
191         hash_combine(seed, toHash);
192         return seed;
193     }
194 
195     /**
196      * Produces a hash for a BSONObj or BSONElement in such a way that respects this comparator. The
197      * resulting hash is combined with 'seed', allowing the caller to create a composite hash out of
198      * several BSONObj/BSONElement objects.
199      *
200      * The hash function is subject to change. Do not use in cases where hashes need to be
201      * consistent across versions.
202      */
203     virtual void hash_combine(size_t& seed, const T& toHash) const = 0;
204 
205     /**
206      * Evaluates a deferred comparison object generated by invocation of one of the BSONObj operator
207      * overloads for relops.
208      */
evaluate(DeferredComparison deferredComparison)209     bool evaluate(DeferredComparison deferredComparison) const {
210         int cmp = compare(deferredComparison.lhs, deferredComparison.rhs);
211         switch (deferredComparison.type) {
212             case DeferredComparison::Type::kLT:
213                 return cmp < 0;
214             case DeferredComparison::Type::kLTE:
215                 return cmp <= 0;
216             case DeferredComparison::Type::kEQ:
217                 return cmp == 0;
218             case DeferredComparison::Type::kGT:
219                 return cmp > 0;
220             case DeferredComparison::Type::kGTE:
221                 return cmp >= 0;
222             case DeferredComparison::Type::kNE:
223                 return cmp != 0;
224         }
225 
226         MONGO_UNREACHABLE;
227     }
228 
229     /**
230      * Returns a function object which computes whether one BSONObj is less than another under this
231      * comparator. This comparator must outlive the returned function object.
232      */
makeLessThan()233     LessThan makeLessThan() const {
234         return LessThan(this);
235     }
236 
237     /**
238      * Returns a function object which computes whether one BSONObj is equal to another under this
239      * comparator. This comparator must outlive the returned function object.
240      */
makeEqualTo()241     EqualTo makeEqualTo() const {
242         return EqualTo(this);
243     }
244 
245 protected:
246     constexpr BSONComparatorInterfaceBase() = default;
247 
248     Set makeSet(std::initializer_list<T> init = {}) const {
249         return Set(init, LessThan(this));
250     }
251 
makeFlatSet(const std::vector<T> & elements)252     FlatSet makeFlatSet(const std::vector<T>& elements) const {
253         return FlatSet(elements.begin(), elements.end(), LessThan(this));
254     }
255 
256     UnorderedSet makeUnorderedSet(std::initializer_list<T> init = {}) const {
257         return UnorderedSet(init, 0, Hasher(this), EqualTo(this));
258     }
259 
260     template <typename ValueType>
261     Map<ValueType> makeMap(std::initializer_list<std::pair<const T, ValueType>> init = {}) const {
262         return Map<ValueType>(init, LessThan(this));
263     }
264 
265     template <typename ValueType>
266     UnorderedMap<ValueType> makeUnorderedMap(
267         std::initializer_list<std::pair<const T, ValueType>> init = {}) const {
268         return UnorderedMap<ValueType>(init, 0, Hasher(this), EqualTo(this));
269     }
270 
271     /**
272      * Hashes 'objToHash', respecting the equivalence classes given by 'stringComparator'.
273      *
274      * Helper intended for use by subclasses.
275      */
276     static void hashCombineBSONObj(size_t& seed,
277                                    const BSONObj& objToHash,
278                                    ComparisonRulesSet rules,
279                                    const StringData::ComparatorInterface* stringComparator);
280 
281     /**
282      * Hashes 'elemToHash', respecting the equivalence classes given by 'stringComparator'.
283      *
284      * Helper intended for use by subclasses.
285      */
286     static void hashCombineBSONElement(size_t& seed,
287                                        BSONElement elemToHash,
288                                        ComparisonRulesSet rules,
289                                        const StringData::ComparatorInterface* stringComparator);
290 };
291 
292 }  // namespace mongo
293