JSON Voorhees
Killer JSON for C++
Loading...
Searching...
No Matches
algorithm.hpp
Go to the documentation of this file.
1/** \file jsonv/algorithm.hpp
2 * A collection of algorithms a la `<algorithm>`.
3 *
4 * Copyright (c) 2014-2018 by Travis Gockel. All rights reserved.
5 *
6 * This program is free software: you can redistribute it and/or modify it under the terms of the Apache License
7 * as published by the Apache Software Foundation, either version 2 of the License, or (at your option) any later
8 * version.
9 *
10 * \author Travis Gockel (travis@gockelhut.com)
11**/
12#pragma once
13
14#include <jsonv/config.hpp>
15#include <jsonv/value.hpp>
16#include <jsonv/path.hpp>
17
18#include <cmath>
19#include <cstdint>
20#include <functional>
21#include <limits>
22
23namespace jsonv
24{
25
26class path;
27
28/** \addtogroup Algorithm
29 * \{
30 * A collection of useful free functions a la \c <algorithm>.
31**/
32
33/** Traits describing how to perform various aspects of comparison. This implementation for comparison is strict and is
34 * ultimately the one used by \c value::compare.
35 *
36 * \see compare
37**/
39{
40 /** Compare two kinds \a a and \a b. This should return 0 if the types are the same or if they are directly
41 * comparable (such as \c kind::integer and \c kind::decimal) -- if you return 0 for non-comparable types, you risk
42 * getting a \c kind_error thrown.
43 **/
45 static int compare_kinds(kind a, kind b)
46 {
47 int va = kindval(a);
48 int vb = kindval(b);
49 return va == vb ? 0 : va < vb ? -1 : 1;
50 }
51
52 /** Compare two boolean values. **/
54 static int compare_booleans(bool a, bool b)
55 {
56 return a == b ? 0
57 : a ? 1
58 : -1;
59 }
60
61 /** Compare two integer values. **/
63 static int compare_integers(std::int64_t a, std::int64_t b)
64 {
65 return a == b ? 0
66 : a < b ? -1
67 : 1;
68 }
69
70 /** Compare the integer \a a with the decimal \a b by their exact numeric values. \a a is never converted to
71 * \c double, which would round an integer past 2^53 onto a neighbor: \c 9007199254740993 is greater than the
72 * decimal \c 9007199254740992.0, not equal to it. An integer equals a decimal only when the decimal holds exactly
73 * that integer, so both signed zeros equal \c 0. Infinities follow numeric order and NaN is greater than every
74 * integer, as in \c compare_decimals. \c compare calls this for both operand orders, reversing the sign of the
75 * result when the decimal is on the left.
76 **/
78 static int compare_integer_decimal(std::int64_t a, double b)
79 {
80 // Every integer lies in [-2^63, 2^63), and both bounds are exact doubles. Settling any b outside them first
81 // keeps the cast below defined -- double(INT64_MAX) rounds up to 2^63, so it cannot serve as the upper bound.
82 constexpr double integer_min = static_cast<double>(std::numeric_limits<std::int64_t>::min());
83 constexpr double integer_end = -integer_min;
84 if (std::isnan(b) || b >= integer_end)
85 return -1;
86 if (b < integer_min)
87 return 1;
88
89 const double whole = std::trunc(b);
90 const auto b_whole = static_cast<std::int64_t>(whole);
91 if (a != b_whole)
92 return a < b_whole ? -1 : 1;
93
94 // Same integral part, so the fractional part decides.
95 return b == whole ? 0
96 : b > whole ? -1
97 : 1;
98 }
99
100 /** Compare two decimal values exactly, without an epsilon tolerance. Signed zeros compare equal and
101 * infinities follow numeric order. All NaNs compare equal to each other and greater than every non-NaN
102 * number, regardless of sign or payload. This defines a strict weak ordering for decimals.
103 **/
105 static int compare_decimals(double a, double b)
106 {
107 if (std::isnan(a))
108 return std::isnan(b) ? 0 : 1;
109 if (std::isnan(b))
110 return -1;
111 return a == b ? 0
112 : a < b ? -1
113 : 1;
114 }
115
116 /** Compare two string values. **/
118 static int compare_strings(const std::string& a, const std::string& b)
119 {
120 return a.compare(b);
121 }
122
123 /** Compare two strings used for the keys of objects. **/
125 static int compare_object_keys(const std::string& a, const std::string& b)
126 {
127 return a.compare(b);
128 }
129
130 /** Compare two objects \e before comparing the values. The \c compare function will only check the contents of an
131 * object if this function returns 0.
132 **/
134 static int compare_objects_meta(const value&, const value&)
135 {
136 return 0;
137 }
138
139private:
141 static int kindval(kind k)
142 {
143 switch (k)
144 {
145 case jsonv::kind::null:
146 return 0;
147 case jsonv::kind::boolean:
148 return 1;
149 case jsonv::kind::integer:
150 case jsonv::kind::decimal:
151 return 2;
152 case jsonv::kind::string:
153 return 3;
154 case jsonv::kind::array:
155 return 4;
156 case jsonv::kind::object:
157 return 5;
158 default:
159 return -1;
160 }
161 }
162};
163
164/** Compare the values \a a and \a b using the comparison \a traits.
165 *
166 * \tparam TCompareTraits A type which should be compatible with the public signatures on the \c compare_traits class.
167**/
168template <typename TCompareTraits>
170int compare(const value& a, const value& b, const TCompareTraits& traits)
171{
172 if (&a == &b)
173 return 0;
174
175 if (int kindcmp = traits.compare_kinds(a.kind(), b.kind()))
176 return kindcmp;
177
178 switch (a.kind())
179 {
180 case jsonv::kind::null:
181 return 0;
182 case jsonv::kind::boolean:
183 return traits.compare_booleans(a.as_boolean(), b.as_boolean());
184 case jsonv::kind::integer:
185 // b might be a decimal, but the integer is never converted to one (see compare_traits::compare_integer_decimal)
186 if (b.kind() == jsonv::kind::integer)
187 return traits.compare_integers(a.as_integer(), b.as_integer());
188 else
189 return traits.compare_integer_decimal(a.as_integer(), b.as_decimal());
190 case jsonv::kind::decimal:
191 if (b.kind() == jsonv::kind::integer)
192 {
193 // Reverse the sign rather than negate: custom traits may return INT_MIN, which has no negation.
194 int cmp = traits.compare_integer_decimal(b.as_integer(), a.as_decimal());
195 return cmp < 0 ? 1 : cmp > 0 ? -1 : 0;
196 }
197 else
198 return traits.compare_decimals(a.as_decimal(), b.as_decimal());
199 case jsonv::kind::string:
200 return traits.compare_strings(a.as_string(), b.as_string());
201 case jsonv::kind::array:
202 {
203 auto aiter = a.begin_array();
204 auto biter = b.begin_array();
205 for ( ; aiter != a.end_array() && biter != b.end_array(); ++aiter, ++biter)
206 if (int cmp = compare(*aiter, *biter, traits))
207 return cmp;
208 return aiter == a.end_array() ? biter == b.end_array() ? 0 : -1
209 : 1;
210 }
211 case jsonv::kind::object:
212 {
213 if (int objmetacmp = traits.compare_objects_meta(a, b))
214 return objmetacmp;
215
216 auto aiter = a.begin_object();
217 auto biter = b.begin_object();
218 for ( ; aiter != a.end_object() && biter != b.end_object(); ++aiter, ++biter)
219 {
220 if (int cmp = traits.compare_object_keys(aiter->first, biter->first))
221 return cmp;
222 if (int cmp = compare(aiter->second, biter->second, traits))
223 return cmp;
224 }
225 return aiter == a.end_object() ? biter == b.end_object() ? 0 : -1
226 : 1;
227 }
228 default:
229 return -1;
230 }
231}
232
233/// Compare the values \a a and \a b with strict comparison traits.
234///
235/// Decimal comparison follows the exact ordering defined by \c compare_traits::compare_decimals. An integer and a
236/// decimal compare by exact numeric value, as defined by \c compare_traits::compare_integer_decimal.
237///
238/// \see value::compare
239/// \see compare_icase
241
242/// Compare the values \a a and \a b, but use case-insensitive matching on \c kind::string values. This does \e not use
243/// case-insensitive matching on the keys of objects!
244///
245/// \see compare
247
248/// The results of the \c diff operation.
250{
251 /// Elements that were the same between the two halves of the diff.
253
254 /// Elements that were unique to the left hand side of the diff.
256
257 /// Elements that were unique to the right hand side of the diff.
259};
260
261/** Find the differences and similarities between the structures of \a left and \a right. If \a left and \a right have
262 * a different \c kind (and the kind difference is not \c kind::integer and \c kind::decimal), \a left and \a right
263 * will be placed directly in the result. If they have the same \c kind and it is scalar, the values get a direct
264 * comparison. If they are the same, the result is moved to \c diff_result::same. If they are different, \a left and
265 * \a right are moved to \c diff_result::left and \c diff_result::right, respectively. For \c kind::array and
266 * \c kind::object, the \c value elements are compared recursively.
267**/
269
270/** Run a function over the values in the \a input. The behavior of this function is different, depending on the \c kind
271 * of \a input. For scalar kinds (\c kind::integer, \c kind::null, etc), \a func is called once with the value. If
272 * \a input is \c kind::array, \c func is called for every value in the array and the output will be an array with each
273 * element transformed by \a func. If \a input is \c kind::object, the result will be an object with each key
274 * transformed by \a func.
275 *
276 * \param func The function to apply to the element or elements of \a input.
277 * \param input The value to transform.
278**/
279JSONV_NODISCARD JSONV_PUBLIC value map(const std::function<value (const value&)>& func,
280 const value& input
281 );
282
283/** Run a function over the values in the \a input. The behavior of this function is different, depending on the \c kind
284 * of \a input. For scalar kinds (\c kind::integer, \c kind::null, etc), \a func is called once with the value. If
285 * \a input is \c kind::array, \c func is called for every value in the array and the output will be an array with each
286 * element transformed by \a func. If \a input is \c kind::object, the result will be an object with each key
287 * transformed by \a func.
288 *
289 * \param func The function to apply to the element or elements of \a input.
290 * \param input The value to transform.
291 *
292 * \note
293 * This version of \c map provides only a basic exception-safety guarantee. If an exception is thrown while
294 * transforming a non-scalar \c kind, there is no rollback action, so \a input is left in a usable, but
295 * \e unpredictable state. If you need a strong exception guarantee, use the version of \c map that takes a constant
296 * reference to a \c value.
297**/
298JSONV_NODISCARD JSONV_PUBLIC value map(const std::function<value (value)>& func,
299 value&& input
300 );
301
302/** Recursively walk the provided \a tree and call \a func for each item in the tree.
303 *
304 * \param tree The JSON value to traverse.
305 * \param func The function to call for each element in the tree.
306 * \param base_path The path to prepend to each output path to \a func. This can be useful if beginning traversal from
307 * inside of some JSON structure.
308 * \param leafs_only If true, call \a func only when the current path is a "leaf" value (\c string, \c integer,
309 * \c decimal, \c boolean, or \c null \e or an empty \c array or \c object); if false, call \a func
310 * for all entries in the tree.
311**/
312JSONV_PUBLIC void traverse(const value& tree,
313 const std::function<void (const path&, const value&)>& func,
314 const path& base_path,
315 bool leafs_only = false
316 );
317
318/** Recursively walk the provided \a tree and call \a func for each item in the tree.
319 *
320 * \param tree The JSON value to traverse.
321 * \param func The function to call for each element in the tree.
322 * \param leafs_only If true, call \a func only when the current path is a "leaf" value (\c string, \c integer,
323 * \c decimal, \c boolean, or \c null \e or an empty \c array or \c object); if false, call \a func
324 * for all entries in the tree.
325**/
326JSONV_PUBLIC void traverse(const value& tree,
327 const std::function<void (const path&, const value&)>& func,
328 bool leafs_only = false
329 );
330
331/** This class is used in \c merge_explicit for defining what the function should do in the cases of conflicts. **/
333{
334public:
335 virtual ~merge_rules() noexcept;
336
337 /** Called when merging a \c kind::object and the two objects share a key. The implementation can either throw or
338 * merge the keys.
339 *
340 * \param current_path is the merge \c path with the key that conflicted appended.
341 * \param a is the left-hand \c value to merge.
342 * \param b is the right-hand \c value to merge.
343 **/
345 virtual value resolve_same_key(path&& current_path, value&& a, value&& b) const = 0;
346
347 /** Called when \a a and \a b have \c kind values which are incompatible for merging. The implementation can either
348 * throw or coerce a merge.
349 *
350 * \param current_path \c path with the conflicting \c kind values.
351 * \param a is the left-hand \c value to merge.
352 * \param b is the right-hand \c value to merge.
353 **/
355 virtual value resolve_type_conflict(path&& current_path, value&& a, value&& b) const = 0;
356};
357
358/// An implementation of \c merge_rules that allows you to bind whatever functions you want to resolve conflicts.
360 public merge_rules
361{
362public:
363 using same_key_function = std::function<value (path&&, value&&, value&&)>;
364
365 using type_conflict_function = std::function<value (path&&, value&&, value&&)>;
366
367public:
368 dynamic_merge_rules(same_key_function same_key,
369 type_conflict_function type_conflict
370 );
371
372 virtual ~dynamic_merge_rules() noexcept;
373
374 same_key_function same_key;
375
376 type_conflict_function type_conflict;
377
378 /// \see merge_rules::resolve_same_key
380 virtual value resolve_same_key(path&& current_path, value&& a, value&& b) const override;
381
382 /// \see merge_rules::resolve_type_conflict
384 virtual value resolve_type_conflict(path&& current_path, value&& a, value&& b) const override;
385};
386
387/// These rules throw an exception on all conflicts.
389 public merge_rules
390{
391public:
392 /// \throws std::logic_error
394 virtual value resolve_same_key(path&& current_path, value&& a, value&& b) const override;
395
396 /// \throws kind_error
398 virtual value resolve_type_conflict(path&& current_path, value&& a, value&& b) const override;
399};
400
401/// These rules will recursively merge everything they can and coerce all values.
403 public merge_rules
404{
405public:
406 /// Recursively calls \c merge_explicit with the two values.
408 virtual value resolve_same_key(path&& current_path, value&& a, value&& b) const override;
409
410 /// Calls \c coerce_merge to combine the values.
412 virtual value resolve_type_conflict(path&& current_path, value&& a, value&& b) const override;
413};
414
415/// \{
416
417/// Merges \c values into a single \c value by the given \a rules (see \c merge_rules). The \a current_path is the
418/// \c path into the \c value being merged, which can give more useful error information when merging recursively.
419///
420/// Two values \a a and \a b are merged by a few simple rules:
421///
422/// - If \a a.kind() != \a b.kind() and they are not \c kind::integer and \c kind::decimal, call
423/// \c merge_rules::resolve_type_conflict and return the result.
424/// - Otherwise, branch based on the (shared) type:
425/// - \c kind::object - Return a new object with all the values from \a a and \a b for the keys which are unique per
426/// object. For the keys which are shared, the value is the result of \c merge_rules::resolve_same_key.
427/// - \c kind::array - Return a new array with the values of \a b appended to \a a.
428/// - \c kind::string - Return a new string with \a b appended to \a a.
429/// - \c kind::boolean - Return `a.as_boolean() || b.as_boolean()`
430/// - \c kind::integer - If \a b is \c kind::integer, return `a + b` as an integer; otherwise, return it as a
431/// decimal.
432/// - \c kind::decimal - Return `a + b` as a decimal.
433///
434/// Three or more values are merged two at a time from left to right, a single value is returned as it is, and no values
435/// at all make an empty object. This is how \c merge takes any number of values.
437 path current_path,
438 value a,
439 value b
440 );
441
443
445
446template <typename... TValue>
448value merge_explicit(const merge_rules& rules, path current_path, value a, value b, value c, TValue&&... rest)
449{
450 value ab = merge_explicit(rules, current_path, std::move(a), std::move(b));
451 return merge_explicit(rules,
452 std::move(current_path),
453 std::move(ab),
454 std::move(c),
455 std::forward<TValue>(rest)...
456 );
457}
458/// \}
459
460/** Merges all the provided \a values into a single \c value. If there are any key or type conflicts, an exception will
461 * be thrown.
462**/
463template <typename... TValue>
465value merge(TValue&&... values)
466{
468 path(),
469 std::forward<TValue>(values)...
470 );
471}
472
473/** Merges all the provided \a values into a single \c value. If there are any keys which are shared, their values are
474 * also merged.
475**/
476template <typename... TValue>
478value merge_recursive(TValue&&... values)
479{
481 path(),
482 std::forward<TValue>(values)...
483 );
484}
485
486/** Error thrown when an unrepresentable value is encountered in a JSON AST.
487 *
488 * \see validate
489**/
491 public std::runtime_error
492{
493public:
494 /** Special code for describing the error encountered. **/
495 enum class code
496 {
497 /** Encountered a number which is NaN or Infinity. **/
498 non_finite_number
499 };
500
501public:
502 explicit validation_error(code code_, jsonv::path path_, jsonv::value value_);
503
504 virtual ~validation_error() noexcept;
505
506 /** Get the error code. **/
508 code error_code() const;
509
510 /** Get the path in the AST the error was found. **/
512 const jsonv::path& path() const;
513
514 /** Get the value that caused the error. **/
516 const jsonv::value& value() const;
517
518private:
519 code _code;
520 jsonv::path _path;
521 jsonv::value _value;
522};
523
524JSONV_PUBLIC std::ostream& operator<<(std::ostream& os, const validation_error::code& code);
525
526/** Check that the provided \a val is perfectly representable as a JSON string. The JSON specification does not have
527 * support for things like non-finite floating-point numbers (\c NaN and \c infinity). This means \c value defined with
528 * these values will get serialized as \c null. This constitutes a loss of information, but not acting this way would
529 * lead to the encoder outputting invalid JSON text, which is completely unacceptable. Use this funciton to check that
530 * there will be no information loss when encoding.
531 *
532 * \throws validation_error if \a val contains an unrepresentable value.
533**/
534JSONV_PUBLIC void validate(const value& val);
535
536/** \} **/
537
538}
An implementation of merge_rules that allows you to bind whatever functions you want to resolve confl...
virtual value resolve_same_key(path &&current_path, value &&a, value &&b) const override
virtual value resolve_type_conflict(path &&current_path, value &&a, value &&b) const override
This class is used in merge_explicit for defining what the function should do in the cases of conflic...
virtual value resolve_same_key(path &&current_path, value &&a, value &&b) const =0
Called when merging a kind::object and the two objects share a key.
virtual value resolve_type_conflict(path &&current_path, value &&a, value &&b) const =0
Called when a and b have kind values which are incompatible for merging.
Represents an exact path in some JSON structure.
Definition path.hpp:87
These rules will recursively merge everything they can and coerce all values.
virtual value resolve_type_conflict(path &&current_path, value &&a, value &&b) const override
Calls coerce_merge to combine the values.
virtual value resolve_same_key(path &&current_path, value &&a, value &&b) const override
Recursively calls merge_explicit with the two values.
These rules throw an exception on all conflicts.
virtual value resolve_same_key(path &&current_path, value &&a, value &&b) const override
virtual value resolve_type_conflict(path &&current_path, value &&a, value &&b) const override
Error thrown when an unrepresentable value is encountered in a JSON AST.
code
Special code for describing the error encountered.
Represents a single JSON value, which can be any one of a potential kind, each behaving slightly diff...
Definition value.hpp:113
double as_decimal() const
Get this value as a decimal.
const std::string & as_string() const
Get this value as a string.
object_iterator end_object()
Get an iterator to the one past the end of this object.
jsonv::kind kind() const
Get this value's kind.
Definition value.hpp:585
bool as_boolean() const
Get this value as a boolean.
object_iterator begin_object()
Get an iterator to the first key-value pair in this object.
int64_t as_integer() const
Get this value as an integer.
array_iterator begin_array()
Get an iterator to the beginning of this array.
array_iterator end_array()
Get an iterator to the end of this array.
Copyright (c) 2014-2020 by Travis Gockel.
value same
Elements that were the same between the two halves of the diff.
value right
Elements that were unique to the right hand side of the diff.
value left
Elements that were unique to the left hand side of the diff.
int compare(const value &a, const value &b, const TCompareTraits &traits)
Compare the values a and b using the comparison traits.
JSONV_PUBLIC diff_result diff(value left, value right)
Find the differences and similarities between the structures of left and right.
JSONV_PUBLIC value merge_explicit(const merge_rules &rules, path current_path, value a, value b)
Merges values into a single value by the given rules (see merge_rules).
JSONV_PUBLIC value map(const std::function< value(const value &)> &func, const value &input)
Run a function over the values in the input.
JSONV_PUBLIC int compare_icase(const value &a, const value &b)
Compare the values a and b, but use case-insensitive matching on kind::string values.
value merge(TValue &&... values)
Merges all the provided values into a single value.
value merge_recursive(TValue &&... values)
Merges all the provided values into a single value.
JSONV_PUBLIC void traverse(const value &tree, const std::function< void(const path &, const value &)> &func, const path &base_path, bool leafs_only=false)
Recursively walk the provided tree and call func for each item in the tree.
The results of the diff operation.
#define JSONV_NODISCARD
Warn if the caller discards the result of this function.
Definition config.hpp:132
#define JSONV_PUBLIC
This function or class is part of the public API for JSON Voorhees.
Definition config.hpp:113
kind
Describes the kind of data a value holds.
Definition kind.hpp:30
STL namespace.
Support for JSONPath.
Traits describing how to perform various aspects of comparison.
Definition algorithm.hpp:39
static int compare_strings(const std::string &a, const std::string &b)
Compare two string values.
static int compare_integers(std::int64_t a, std::int64_t b)
Compare two integer values.
Definition algorithm.hpp:63
static int compare_integer_decimal(std::int64_t a, double b)
Compare the integer a with the decimal b by their exact numeric values.
Definition algorithm.hpp:78
static int compare_booleans(bool a, bool b)
Compare two boolean values.
Definition algorithm.hpp:54
static int compare_objects_meta(const value &, const value &)
Compare two objects before comparing the values.
static int compare_decimals(double a, double b)
Compare two decimal values exactly, without an epsilon tolerance.
static int compare_kinds(kind a, kind b)
Compare two kinds a and b.
Definition algorithm.hpp:45
static int compare_object_keys(const std::string &a, const std::string &b)
Compare two strings used for the keys of objects.
Copyright (c) 2012-2020 by Travis Gockel.