From babdced52fef50c37350469d46b25ea225363d84 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Fri, 24 May 2024 20:34:04 +0200 Subject: [PATCH 01/28] Replace GCC-specific attribute with [[nodiscard]] attribute (#6899) --- include/engine/guidance/collapse_turns.hpp | 8 +-- .../engine/guidance/collapsing_utility.hpp | 4 +- include/engine/guidance/lane_processing.hpp | 11 ++-- include/engine/guidance/post_processing.hpp | 20 +++---- .../engine/guidance/verbosity_reduction.hpp | 4 +- .../intersection/coordinate_extractor.hpp | 59 ++++++++----------- include/guidance/motorway_handler.hpp | 15 ++--- include/guidance/parsing_toolkit.hpp | 9 +-- include/guidance/turn_analysis.hpp | 9 +-- include/guidance/turn_handler.hpp | 30 ++++------ include/guidance/turn_instruction.hpp | 4 +- include/guidance/turn_lane_augmentation.hpp | 6 +- include/guidance/turn_lane_data.hpp | 5 +- include/guidance/turn_lane_handler.hpp | 28 ++++----- include/guidance/turn_lane_matcher.hpp | 13 ++-- include/util/alias.hpp | 4 +- include/util/attributes.hpp | 13 ---- include/util/guidance/name_announcements.hpp | 6 +- src/benchmarks/alias.cpp | 1 + src/engine/guidance/collapse_turns.cpp | 6 +- 20 files changed, 98 insertions(+), 157 deletions(-) delete mode 100644 include/util/attributes.hpp diff --git a/include/engine/guidance/collapse_turns.hpp b/include/engine/guidance/collapse_turns.hpp index 6d0b1fe7d..bfd1140f2 100644 --- a/include/engine/guidance/collapse_turns.hpp +++ b/include/engine/guidance/collapse_turns.hpp @@ -1,7 +1,6 @@ #ifndef OSRM_ENGINE_GUIDANCE_COLLAPSE_HPP #include "engine/guidance/route_step.hpp" -#include "util/attributes.hpp" #include #include @@ -12,16 +11,15 @@ namespace osrm::engine::guidance // Multiple possible reasons can result in unnecessary/confusing instructions // Collapsing such turns into a single turn instruction, we give a clearer // set of instructions that is not cluttered by unnecessary turns/name changes. -OSRM_ATTR_WARN_UNUSED -std::vector collapseTurnInstructions(std::vector steps); +[[nodiscard]] std::vector collapseTurnInstructions(std::vector steps); // Multiple possible reasons can result in unnecessary/confusing instructions // A prime example would be a segregated intersection. Turning around at this // intersection would result in two instructions to turn left. // Collapsing such turns into a single turn instruction, we give a clearer // set of instructions that is not cluttered by unnecessary turns/name changes. -OSRM_ATTR_WARN_UNUSED -std::vector collapseSegregatedTurnInstructions(std::vector steps); +[[nodiscard]] std::vector +collapseSegregatedTurnInstructions(std::vector steps); // A combined turn is a set of two instructions that actually form a single turn, as far as we // perceive it. A u-turn consisting of two left turns is one such example. But there are also lots diff --git a/include/engine/guidance/collapsing_utility.hpp b/include/engine/guidance/collapsing_utility.hpp index c0a012198..9177ef8d4 100644 --- a/include/engine/guidance/collapsing_utility.hpp +++ b/include/engine/guidance/collapsing_utility.hpp @@ -3,7 +3,6 @@ #include "guidance/turn_instruction.hpp" #include "engine/guidance/route_step.hpp" -#include "util/attributes.hpp" #include "util/bearing.hpp" #include "util/guidance/name_announcements.hpp" @@ -166,8 +165,7 @@ inline bool areSameSide(const RouteStep &lhs, const RouteStep &rhs) } // do this after invalidating any steps to compress the step array again -OSRM_ATTR_WARN_UNUSED -inline std::vector removeNoTurnInstructions(std::vector steps) +[[nodiscard]] inline std::vector removeNoTurnInstructions(std::vector steps) { // finally clean up the post-processed instructions. // Remove all invalid instructions from the set of instructions. diff --git a/include/engine/guidance/lane_processing.hpp b/include/engine/guidance/lane_processing.hpp index f42567279..0448151cb 100644 --- a/include/engine/guidance/lane_processing.hpp +++ b/include/engine/guidance/lane_processing.hpp @@ -1,10 +1,9 @@ #ifndef OSRM_ENGINE_GUIDANCE_LANE_PROCESSING_HPP_ #define OSRM_ENGINE_GUIDANCE_LANE_PROCESSING_HPP_ -#include - #include "engine/guidance/route_step.hpp" -#include "util/attributes.hpp" + +#include namespace osrm::engine::guidance { @@ -14,9 +13,9 @@ namespace osrm::engine::guidance // we anticipate lane changes emitting only matching lanes early on. // the second parameter describes the duration that we feel two segments need to be apart to count // as separate maneuvers. -OSRM_ATTR_WARN_UNUSED -std::vector anticipateLaneChange(std::vector steps, - const double min_distance_needed_for_lane_change = 200); +[[nodiscard]] std::vector +anticipateLaneChange(std::vector steps, + const double min_distance_needed_for_lane_change = 200); } // namespace osrm::engine::guidance diff --git a/include/engine/guidance/post_processing.hpp b/include/engine/guidance/post_processing.hpp index b489de07a..22ed7aae2 100644 --- a/include/engine/guidance/post_processing.hpp +++ b/include/engine/guidance/post_processing.hpp @@ -5,7 +5,6 @@ #include "engine/guidance/leg_geometry.hpp" #include "engine/guidance/route_step.hpp" #include "engine/phantom_node.hpp" -#include "util/attributes.hpp" #include @@ -13,8 +12,7 @@ namespace osrm::engine::guidance { // passed as none-reference to modify in-place and move out again -OSRM_ATTR_WARN_UNUSED -std::vector handleRoundabouts(std::vector steps); +[[nodiscard]] std::vector handleRoundabouts(std::vector steps); // trim initial/final segment of very short length. // This function uses in/out parameter passing to modify both steps and geometry in place. @@ -24,23 +22,21 @@ std::vector handleRoundabouts(std::vector steps); void trimShortSegments(std::vector &steps, LegGeometry &geometry); // assign relative locations to depart/arrive instructions -OSRM_ATTR_WARN_UNUSED -std::vector assignRelativeLocations(std::vector steps, - const LegGeometry &geometry, - const PhantomNode &source_node, - const PhantomNode &target_node); +[[nodiscard]] std::vector assignRelativeLocations(std::vector steps, + const LegGeometry &geometry, + const PhantomNode &source_node, + const PhantomNode &target_node); // collapse suppressed instructions remaining into intersections array -OSRM_ATTR_WARN_UNUSED -std::vector buildIntersections(std::vector steps); +[[nodiscard]] std::vector buildIntersections(std::vector steps); // postProcess will break the connection between the leg geometry // for which a segment is supposed to represent exactly the coordinates // between routing maneuvers and the route steps itself. // If required, we can get both in sync again using this function. // Move in LegGeometry for modification in place. -OSRM_ATTR_WARN_UNUSED -LegGeometry resyncGeometry(LegGeometry leg_geometry, const std::vector &steps); +[[nodiscard]] LegGeometry resyncGeometry(LegGeometry leg_geometry, + const std::vector &steps); /** * Apply maneuver override relations to the selected route. diff --git a/include/engine/guidance/verbosity_reduction.hpp b/include/engine/guidance/verbosity_reduction.hpp index 3ad6d26be..ae5dca97c 100644 --- a/include/engine/guidance/verbosity_reduction.hpp +++ b/include/engine/guidance/verbosity_reduction.hpp @@ -2,7 +2,6 @@ #define OSRM_ENGINE_GUIDANCE_VERBOSITY_REDUCTION_HPP_ #include "engine/guidance/route_step.hpp" -#include "util/attributes.hpp" #include @@ -13,8 +12,7 @@ namespace osrm::engine::guidance // to announce them. All these that are not collapsed into a single turn (think segregated // intersection) have to be checked for the length they are active in. If they are active for a // short distance only, we don't announce them -OSRM_ATTR_WARN_UNUSED -std::vector suppressShortNameSegments(std::vector steps); +[[nodiscard]] std::vector suppressShortNameSegments(std::vector steps); } // namespace osrm::engine::guidance diff --git a/include/extractor/intersection/coordinate_extractor.hpp b/include/extractor/intersection/coordinate_extractor.hpp index 376ea10d5..8e9a527b6 100644 --- a/include/extractor/intersection/coordinate_extractor.hpp +++ b/include/extractor/intersection/coordinate_extractor.hpp @@ -1,16 +1,15 @@ #ifndef OSRM_EXTRACTOR_INTERSECTION_COORDINATE_EXTRACTOR_HPP_ #define OSRM_EXTRACTOR_INTERSECTION_COORDINATE_EXTRACTOR_HPP_ -#include -#include - #include "extractor/compressed_edge_container.hpp" #include "extractor/query_node.hpp" -#include "util/attributes.hpp" #include "util/coordinate.hpp" #include "util/node_based_graph.hpp" +#include +#include + namespace osrm::extractor::intersection { @@ -27,17 +26,16 @@ class CoordinateExtractor * Note: The segment between intersection and turn coordinate can be zero, if the OSM modelling * is unfortunate. See https://github.com/Project-OSRM/osrm-backend/issues/3470 */ - OSRM_ATTR_WARN_UNUSED - util::Coordinate GetCoordinateAlongRoad(const NodeID intersection_node, - const EdgeID turn_edge, - const bool traversed_in_reverse, - const NodeID to_node, - const std::uint8_t number_of_in_lanes) const; + [[nodiscard]] util::Coordinate + GetCoordinateAlongRoad(const NodeID intersection_node, + const EdgeID turn_edge, + const bool traversed_in_reverse, + const NodeID to_node, + const std::uint8_t number_of_in_lanes) const; // Given a set of precomputed coordinates, select the representative coordinate along the road // that best describes the turn - OSRM_ATTR_WARN_UNUSED - util::Coordinate + [[nodiscard]] util::Coordinate ExtractRepresentativeCoordinate(const NodeID intersection_node, const EdgeID turn_edge, const bool traversed_in_reverse, @@ -47,7 +45,7 @@ class CoordinateExtractor // instead of finding only a single coordinate, we can also list all coordinates along a // road. - OSRM_ATTR_WARN_UNUSED std::vector + [[nodiscard]] std::vector GetCoordinatesAlongRoad(const NodeID intersection_node, const EdgeID turn_edge, const bool traversed_in_reverse, @@ -55,20 +53,18 @@ class CoordinateExtractor // wrapper in case of normal forward edges (traversed_in_reverse = false, to_node = // node_based_graph.GetTarget(turn_edge) - OSRM_ATTR_WARN_UNUSED - std::vector GetForwardCoordinatesAlongRoad(const NodeID from, - const EdgeID turn_edge) const; + [[nodiscard]] std::vector + GetForwardCoordinatesAlongRoad(const NodeID from, const EdgeID turn_edge) const; // a less precise way to compute coordinates along a route. Due to the heavy interaction of // graph traversal and turn instructions, we often don't care for high precision. We only want // to check for available connections in order, or find (with room for error) the straightmost // turn. This function will offer a bit more error potential but allow for much higher // performance - OSRM_ATTR_WARN_UNUSED - util::Coordinate GetCoordinateCloseToTurn(const NodeID from_node, - const EdgeID turn_edge, - const bool traversed_in_reverse, - const NodeID to_node) const; + [[nodiscard]] util::Coordinate GetCoordinateCloseToTurn(const NodeID from_node, + const EdgeID turn_edge, + const bool traversed_in_reverse, + const NodeID to_node) const; /* When extracting the coordinates, we first extract all coordinates. We don't care about most * of them, though. @@ -90,22 +86,19 @@ class CoordinateExtractor * The optional length cache needs to store the accumulated distance up to the respective * coordinate index [0,d(0,1),...] */ - OSRM_ATTR_WARN_UNUSED - std::vector + [[nodiscard]] std::vector TrimCoordinatesToLength(std::vector coordinates, const double desired_length, const std::vector &length_cache = {}) const; - OSRM_ATTR_WARN_UNUSED - std::vector PrepareLengthCache(const std::vector &coordinates, - const double limit) const; + [[nodiscard]] std::vector + PrepareLengthCache(const std::vector &coordinates, const double limit) const; /* when looking at a set of coordinates, this function allows trimming the vector to a smaller, * only containing coordinates up to a given distance along the path. The last coordinate might * be interpolated */ - OSRM_ATTR_WARN_UNUSED - std::vector + [[nodiscard]] std::vector TrimCoordinatesByLengthFront(std::vector coordinates, const double desired_length) const; @@ -130,10 +123,9 @@ class CoordinateExtractor * * for fixpoint `b`, vector_base `d` and vector_head `e` */ - OSRM_ATTR_WARN_UNUSED - util::Coordinate GetCorrectedCoordinate(const util::Coordinate fixpoint, - const util::Coordinate vector_base, - const util::Coordinate vector_head) const; + [[nodiscard]] util::Coordinate GetCorrectedCoordinate(const util::Coordinate fixpoint, + const util::Coordinate vector_base, + const util::Coordinate vector_head) const; /* generate a uniform vector of coordinates in same range distances * @@ -143,8 +135,7 @@ class CoordinateExtractor * Into: * x -- x -- x -- x -- x - x */ - OSRM_ATTR_WARN_UNUSED - std::vector + [[nodiscard]] std::vector SampleCoordinates(const std::vector &coordinates, const double length, const double rate) const; diff --git a/include/guidance/motorway_handler.hpp b/include/guidance/motorway_handler.hpp index 6b390d6a3..8f399156d 100644 --- a/include/guidance/motorway_handler.hpp +++ b/include/guidance/motorway_handler.hpp @@ -7,7 +7,6 @@ #include "guidance/intersection_handler.hpp" #include "guidance/is_through_street.hpp" -#include "util/attributes.hpp" #include "util/node_based_graph.hpp" #include @@ -42,18 +41,14 @@ class MotorwayHandler final : public IntersectionHandler Intersection intersection) const override final; private: - OSRM_ATTR_WARN_UNUSED - Intersection handleSliproads(const NodeID intersection_node_id, - Intersection intersection) const; + [[nodiscard]] Intersection handleSliproads(const NodeID intersection_node_id, + Intersection intersection) const; - OSRM_ATTR_WARN_UNUSED - Intersection fromMotorway(const EdgeID via_edge, Intersection intersection) const; + [[nodiscard]] Intersection fromMotorway(const EdgeID via_edge, Intersection intersection) const; - OSRM_ATTR_WARN_UNUSED - Intersection fromRamp(const EdgeID via_edge, Intersection intersection) const; + [[nodiscard]] Intersection fromRamp(const EdgeID via_edge, Intersection intersection) const; - OSRM_ATTR_WARN_UNUSED - Intersection fallback(Intersection intersection) const; + [[nodiscard]] Intersection fallback(Intersection intersection) const; }; } // namespace osrm::guidance diff --git a/include/guidance/parsing_toolkit.hpp b/include/guidance/parsing_toolkit.hpp index f402430bd..56cbb5fae 100644 --- a/include/guidance/parsing_toolkit.hpp +++ b/include/guidance/parsing_toolkit.hpp @@ -7,8 +7,6 @@ #include #include -#include "util/attributes.hpp" - namespace osrm::extractor::guidance { @@ -21,8 +19,7 @@ namespace osrm::extractor::guidance // will be corrected to left|throught, since the final lane is not drivable. // This is in contrast to a situation with lanes:psv:forward=0 (or not set) where left|through| // represents left|through|through -OSRM_ATTR_WARN_UNUSED -inline std::string +[[nodiscard]] inline std::string trimLaneString(std::string lane_string, std::int32_t count_left, std::int32_t count_right) { if (count_left) @@ -68,8 +65,8 @@ trimLaneString(std::string lane_string, std::int32_t count_left, std::int32_t co // turn:lanes=left|through|through|right // vehicle:lanes=yes|yes|no|yes // bicycle:lanes=yes|no|designated|yes -OSRM_ATTR_WARN_UNUSED -inline std::string applyAccessTokens(std::string lane_string, const std::string &access_tokens) +[[nodiscard]] inline std::string applyAccessTokens(std::string lane_string, + const std::string &access_tokens) { using tokenizer = boost::tokenizer>; boost::char_separator sep("|", "", boost::keep_empty_tokens); diff --git a/include/guidance/turn_analysis.hpp b/include/guidance/turn_analysis.hpp index c2e6f947c..d17ea0c03 100644 --- a/include/guidance/turn_analysis.hpp +++ b/include/guidance/turn_analysis.hpp @@ -17,7 +17,6 @@ #include "guidance/turn_classification.hpp" #include "guidance/turn_handler.hpp" -#include "util/attributes.hpp" #include "util/node_based_graph.hpp" #include @@ -47,13 +46,11 @@ class TurnAnalysis /* Full Analysis Process for a single node/edge combination. Use with caution, as the process is * relatively expensive */ - OSRM_ATTR_WARN_UNUSED - Intersection operator()(const NodeID node_prior_to_intersection, - const EdgeID entering_via_edge) const; + [[nodiscard]] Intersection operator()(const NodeID node_prior_to_intersection, + const EdgeID entering_via_edge) const; // Select turn types based on the intersection shape - OSRM_ATTR_WARN_UNUSED - Intersection + [[nodiscard]] Intersection AssignTurnTypes(const NodeID from_node, const EdgeID via_eid, const extractor::intersection::IntersectionView &intersection) const; diff --git a/include/guidance/turn_handler.hpp b/include/guidance/turn_handler.hpp index 6a5210992..88b9daae0 100644 --- a/include/guidance/turn_handler.hpp +++ b/include/guidance/turn_handler.hpp @@ -8,7 +8,6 @@ #include "guidance/intersection_handler.hpp" #include "guidance/is_through_street.hpp" -#include "util/attributes.hpp" #include "util/node_based_graph.hpp" #include @@ -76,20 +75,19 @@ class TurnHandler final : public IntersectionHandler bool isCompatibleByRoadClass(const Intersection &intersection, const Fork fork) const; // Dead end. - OSRM_ATTR_WARN_UNUSED - Intersection handleOneWayTurn(Intersection intersection) const; + [[nodiscard]] Intersection handleOneWayTurn(Intersection intersection) const; // Mode Changes, new names... - OSRM_ATTR_WARN_UNUSED - Intersection handleTwoWayTurn(const EdgeID via_edge, Intersection intersection) const; + [[nodiscard]] Intersection handleTwoWayTurn(const EdgeID via_edge, + Intersection intersection) const; // Forks, T intersections and similar - OSRM_ATTR_WARN_UNUSED - Intersection handleThreeWayTurn(const EdgeID via_edge, Intersection intersection) const; + [[nodiscard]] Intersection handleThreeWayTurn(const EdgeID via_edge, + Intersection intersection) const; // Handling of turns larger then degree three - OSRM_ATTR_WARN_UNUSED - Intersection handleComplexTurn(const EdgeID via_edge, Intersection intersection) const; + [[nodiscard]] Intersection handleComplexTurn(const EdgeID via_edge, + Intersection intersection) const; void handleDistinctConflict(const EdgeID via_edge, ConnectedRoad &left, ConnectedRoad &right) const; @@ -97,15 +95,13 @@ class TurnHandler final : public IntersectionHandler // Classification std::optional findFork(const EdgeID via_edge, Intersection &intersection) const; - OSRM_ATTR_WARN_UNUSED - Intersection assignLeftTurns(const EdgeID via_edge, - Intersection intersection, - const std::size_t starting_at) const; + [[nodiscard]] Intersection assignLeftTurns(const EdgeID via_edge, + Intersection intersection, + const std::size_t starting_at) const; - OSRM_ATTR_WARN_UNUSED - Intersection assignRightTurns(const EdgeID via_edge, - Intersection intersection, - const std::size_t up_to) const; + [[nodiscard]] Intersection assignRightTurns(const EdgeID via_edge, + Intersection intersection, + const std::size_t up_to) const; }; } // namespace osrm::guidance diff --git a/include/guidance/turn_instruction.hpp b/include/guidance/turn_instruction.hpp index ce7bb7bc0..83132ac24 100644 --- a/include/guidance/turn_instruction.hpp +++ b/include/guidance/turn_instruction.hpp @@ -2,7 +2,6 @@ #define OSRM_GUIDANCE_TURN_INSTRUCTION_HPP_ #include "guidance/roundabout_type.hpp" -#include "util/attributes.hpp" #include "util/typedefs.hpp" #include @@ -243,8 +242,7 @@ inline guidance::DirectionModifier::Enum getTurnDirection(const double angle) } // swaps left <-> right modifier types -OSRM_ATTR_WARN_UNUSED -inline guidance::DirectionModifier::Enum +[[nodiscard]] inline guidance::DirectionModifier::Enum mirrorDirectionModifier(const guidance::DirectionModifier::Enum modifier) { const constexpr guidance::DirectionModifier::Enum results[] = { diff --git a/include/guidance/turn_lane_augmentation.hpp b/include/guidance/turn_lane_augmentation.hpp index 53ecd9d7f..81ec3abe9 100644 --- a/include/guidance/turn_lane_augmentation.hpp +++ b/include/guidance/turn_lane_augmentation.hpp @@ -3,14 +3,12 @@ #include "guidance/intersection.hpp" #include "guidance/turn_lane_data.hpp" -#include "util/attributes.hpp" namespace osrm::guidance::lanes { -OSRM_ATTR_WARN_UNUSED -LaneDataVector handleNoneValueAtSimpleTurn(LaneDataVector lane_data, - const Intersection &intersection); +[[nodiscard]] LaneDataVector handleNoneValueAtSimpleTurn(LaneDataVector lane_data, + const Intersection &intersection); } // namespace osrm::guidance::lanes diff --git a/include/guidance/turn_lane_data.hpp b/include/guidance/turn_lane_data.hpp index 384172306..53635d014 100644 --- a/include/guidance/turn_lane_data.hpp +++ b/include/guidance/turn_lane_data.hpp @@ -2,7 +2,6 @@ #define OSRM_GUIDANCE_TURN_LANE_DATA_HPP_ #include "extractor/turn_lane_types.hpp" -#include "util/attributes.hpp" #include "util/typedefs.hpp" #include @@ -23,8 +22,8 @@ struct TurnLaneData using LaneDataVector = std::vector; // convertes a string given in the OSM format into a TurnLaneData vector -OSRM_ATTR_WARN_UNUSED -LaneDataVector laneDataFromDescription(const extractor::TurnLaneDescription &turn_lane_description); +[[nodiscard]] LaneDataVector +laneDataFromDescription(const extractor::TurnLaneDescription &turn_lane_description); // Locate A Tag in a lane data vector (if multiple tags are set, the first one found is returned) LaneDataVector::const_iterator findTag(const extractor::TurnLaneType::Mask tag, diff --git a/include/guidance/turn_lane_handler.hpp b/include/guidance/turn_lane_handler.hpp index de39a22cf..9994867a3 100644 --- a/include/guidance/turn_lane_handler.hpp +++ b/include/guidance/turn_lane_handler.hpp @@ -9,7 +9,6 @@ #include "guidance/turn_analysis.hpp" #include "guidance/turn_lane_data.hpp" -#include "util/attributes.hpp" #include "util/guidance/turn_lanes.hpp" #include "util/node_based_graph.hpp" #include "util/typedefs.hpp" @@ -68,8 +67,8 @@ class TurnLaneHandler ~TurnLaneHandler(); - OSRM_ATTR_WARN_UNUSED - Intersection assignTurnLanes(const NodeID at, const EdgeID via_edge, Intersection intersection); + [[nodiscard]] Intersection + assignTurnLanes(const NodeID at, const EdgeID via_edge, Intersection intersection); private: mutable std::atomic count_handled; @@ -108,24 +107,23 @@ class TurnLaneHandler const Intersection &intersection) const; // in case of a simple intersection, assign the lane entries - OSRM_ATTR_WARN_UNUSED - Intersection simpleMatchTuplesToTurns(Intersection intersection, - const LaneDataVector &lane_data, - const LaneDescriptionID lane_string_id); + [[nodiscard]] Intersection simpleMatchTuplesToTurns(Intersection intersection, + const LaneDataVector &lane_data, + const LaneDescriptionID lane_string_id); // partition lane data into lane data relevant at current turn and at next turn - OSRM_ATTR_WARN_UNUSED - std::pair partitionLaneData( - const NodeID at, LaneDataVector turn_lane_data, const Intersection &intersection) const; + [[nodiscard]] std::pair + partitionLaneData(const NodeID at, + LaneDataVector turn_lane_data, + const Intersection &intersection) const; // Sliproad turns have a separated lane to the right/left of other depicted lanes. These lanes // are not necessarily separated clearly from the rest of the way. As a result, we combine both // lane entries for our output, while performing the matching with the separated lanes only. - OSRM_ATTR_WARN_UNUSED - Intersection handleSliproadTurn(Intersection intersection, - const LaneDescriptionID lane_description_id, - LaneDataVector lane_data, - const Intersection &previous_intersection); + [[nodiscard]] Intersection handleSliproadTurn(Intersection intersection, + const LaneDescriptionID lane_description_id, + LaneDataVector lane_data, + const Intersection &previous_intersection); // get the lane data for an intersection void extractLaneData(const EdgeID via_edge, diff --git a/include/guidance/turn_lane_matcher.hpp b/include/guidance/turn_lane_matcher.hpp index b336dde8d..df1965535 100644 --- a/include/guidance/turn_lane_matcher.hpp +++ b/include/guidance/turn_lane_matcher.hpp @@ -5,7 +5,6 @@ #include "guidance/turn_instruction.hpp" #include "guidance/turn_lane_data.hpp" -#include "util/attributes.hpp" #include "util/guidance/turn_lanes.hpp" #include "util/node_based_graph.hpp" @@ -34,12 +33,12 @@ findBestMatchForReverse(const extractor::TurnLaneType::Mask leftmost_tag, bool canMatchTrivially(const Intersection &intersection, const LaneDataVector &lane_data); // perform a trivial match on the turn lanes -OSRM_ATTR_WARN_UNUSED -Intersection triviallyMatchLanesToTurns(Intersection intersection, - const LaneDataVector &lane_data, - const util::NodeBasedDynamicGraph &node_based_graph, - const LaneDescriptionID lane_string_id, - util::guidance::LaneDataIdMap &lane_data_to_id); +[[nodiscard]] Intersection +triviallyMatchLanesToTurns(Intersection intersection, + const LaneDataVector &lane_data, + const util::NodeBasedDynamicGraph &node_based_graph, + const LaneDescriptionID lane_string_id, + util::guidance::LaneDataIdMap &lane_data_to_id); } // namespace osrm::guidance::lanes diff --git a/include/util/alias.hpp b/include/util/alias.hpp index f335f4742..da6ab0e17 100644 --- a/include/util/alias.hpp +++ b/include/util/alias.hpp @@ -28,9 +28,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef OSRM_UTIL_ALIAS_HPP #define OSRM_UTIL_ALIAS_HPP -#include +#include #include -#include +#include #include namespace osrm diff --git a/include/util/attributes.hpp b/include/util/attributes.hpp deleted file mode 100644 index 667d933bf..000000000 --- a/include/util/attributes.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef OSRM_ATTRIBUTES_HPP_ -#define OSRM_ATTRIBUTES_HPP_ - -// OSRM_ATTR_WARN_UNUSED - caller has to use function's return value -// https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html - -#if defined(__GNUC__) && (__GNUC__ >= 4) -#define OSRM_ATTR_WARN_UNUSED __attribute__((warn_unused_result)) -#else -#define OSRM_ATTR_WARN_UNUSED -#endif - -#endif diff --git a/include/util/guidance/name_announcements.hpp b/include/util/guidance/name_announcements.hpp index 52f435750..7cdd3a76d 100644 --- a/include/util/guidance/name_announcements.hpp +++ b/include/util/guidance/name_announcements.hpp @@ -6,18 +6,16 @@ #include "extractor/name_table.hpp" #include "extractor/suffix_table.hpp" -#include "util/attributes.hpp" #include "util/typedefs.hpp" +#include + #include #include #include #include #include -#include -#include - namespace osrm::util::guidance { // Name Change Logic diff --git a/src/benchmarks/alias.cpp b/src/benchmarks/alias.cpp index f2eab6af8..e847d3e0e 100644 --- a/src/benchmarks/alias.cpp +++ b/src/benchmarks/alias.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include diff --git a/src/engine/guidance/collapse_turns.cpp b/src/engine/guidance/collapse_turns.cpp index 7f40c7c51..535a7539b 100644 --- a/src/engine/guidance/collapse_turns.cpp +++ b/src/engine/guidance/collapse_turns.cpp @@ -433,8 +433,7 @@ void suppressStep(RouteStep &step_at_turn_location, RouteStep &step_after_turn_l } // OTHER IMPLEMENTATIONS -OSRM_ATTR_WARN_UNUSED -RouteSteps collapseTurnInstructions(RouteSteps steps) +[[nodiscard]] RouteSteps collapseTurnInstructions(RouteSteps steps) { // make sure we can safely iterate over all steps (has depart/arrive with TurnType::NoTurn) BOOST_ASSERT(!hasTurnType(steps.front()) && !hasTurnType(steps.back())); @@ -589,8 +588,7 @@ RouteSteps collapseTurnInstructions(RouteSteps steps) } // OTHER IMPLEMENTATIONS -OSRM_ATTR_WARN_UNUSED -RouteSteps collapseSegregatedTurnInstructions(RouteSteps steps) +[[nodiscard]] RouteSteps collapseSegregatedTurnInstructions(RouteSteps steps) { // make sure we can safely iterate over all steps (has depart/arrive with TurnType::NoTurn) BOOST_ASSERT(!hasTurnType(steps.front()) && !hasTurnType(steps.back())); From 8b48e2ccc644914d7cdfba83d82995d30470cad4 Mon Sep 17 00:00:00 2001 From: Siarhei Fedartsou Date: Fri, 24 May 2024 20:39:45 +0200 Subject: [PATCH 02/28] Bump mapbox/variant to version 1.2.0 (#6898) --- CHANGELOG.md | 1 + scripts/update_dependencies.sh | 4 +- third_party/variant/.mason | 1 + third_party/variant/.travis.yml | 189 +- third_party/variant/CHANGELOG.md | 420 + third_party/variant/Jamroot | 114 +- third_party/variant/Makefile | 112 +- third_party/variant/README.md | 48 +- .../variant/include/mapbox/variant.hpp | 552 +- .../variant/include/mapbox/variant_cast.hpp | 85 + .../include/mapbox/variant_visitor.hpp | 19 +- third_party/variant/package.json | 10 + .../test/compilation_failure/get_type.cpp | 2 +- .../mutating_visitor_on_const.cpp | 2 +- third_party/variant/test/include/catch.hpp | 17943 ++++++++-------- .../variant/test/lambda_overload_test.cpp | 155 + .../variant/test/recursive_wrapper_test.cpp | 9 +- .../variant/test/t/binary_visitor_impl.hpp | 4 +- third_party/variant/test/t/issue21.cpp | 4 +- third_party/variant/test/t/nothrow_move.cpp | 66 + third_party/variant/test/t/optional.cpp | 3 +- .../variant/test/t/recursive_wrapper.cpp | 31 +- third_party/variant/test/t/sizeof.cpp | 3 +- third_party/variant/test/t/variant.cpp | 84 +- .../variant/test/t/variant_alternative.cpp | 31 + .../variant/test/t/visitor_result_type.cpp | 52 + 26 files changed, 10327 insertions(+), 9617 deletions(-) create mode 160000 third_party/variant/.mason create mode 100644 third_party/variant/CHANGELOG.md create mode 100644 third_party/variant/include/mapbox/variant_cast.hpp create mode 100644 third_party/variant/package.json create mode 100644 third_party/variant/test/t/nothrow_move.cpp create mode 100644 third_party/variant/test/t/variant_alternative.cpp create mode 100644 third_party/variant/test/t/visitor_result_type.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index ecc3876d2..9a30ef80d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - NodeJS: - CHANGED: Use node-api instead of NAN. [#6452](https://github.com/Project-OSRM/osrm-backend/pull/6452) - Misc: + - CHANGED: Bump mapbox/variant to version 1.2.0 [#6898](https://github.com/Project-OSRM/osrm-backend/pull/6898) - CHANGED: Avoid copy of std::function-based callback in path unpacking [#6895](https://github.com/Project-OSRM/osrm-backend/pull/6895) - CHANGED: Replace boost::hash by std::hash [#6892](https://github.com/Project-OSRM/osrm-backend/pull/6892) - CHANGED: Partial fix migration from boost::optional to std::optional [#6551](https://github.com/Project-OSRM/osrm-backend/issues/6551) diff --git a/scripts/update_dependencies.sh b/scripts/update_dependencies.sh index 2baedbaea..fdeecbac7 100755 --- a/scripts/update_dependencies.sh +++ b/scripts/update_dependencies.sh @@ -13,7 +13,7 @@ OSMIUM_PATH="osmcode/libosmium" OSMIUM_TAG=v2.14.0 VARIANT_PATH="mapbox/variant" -VARIANT_TAG=v1.1.3 +VARIANT_TAG=v1.2.0 SOL_PATH="ThePhD/sol2" SOL_TAG=v2.17.5 @@ -34,7 +34,7 @@ FMT_PATH="fmtlib/fmt" FMT_TAG=v10.2.1 function update_subtree () { - name=${1^^} + name=$(echo "$1" | tr '[:lower:]' '[:upper:]') path=$(tmpvar=${name}_PATH && echo ${!tmpvar}) tag=$(tmpvar=${name}_TAG && echo ${!tmpvar}) dir=$(basename $path) diff --git a/third_party/variant/.mason b/third_party/variant/.mason new file mode 160000 index 000000000..6adb14016 --- /dev/null +++ b/third_party/variant/.mason @@ -0,0 +1 @@ +Subproject commit 6adb140160cb549400f73ea35c1d9eb5782210e0 diff --git a/third_party/variant/.travis.yml b/third_party/variant/.travis.yml index 4119d9ef1..e45e137a9 100644 --- a/third_party/variant/.travis.yml +++ b/third_party/variant/.travis.yml @@ -2,102 +2,123 @@ language: generic sudo: false -# Save common build configurations as shortcuts, so we can reference them later. -addons_shortcuts: - addons_clang35: &clang35 - apt: - sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.5' ] - packages: [ 'clang-3.5', 'llvm-3.5-dev' ] - addons_clang36: &clang36 - apt: - sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6' ] - packages: [ 'clang-3.6' ] - addons_clang37: &clang37 - apt: - sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.7' ] - packages: [ 'clang-3.7' ] - addons_clang38: &clang38 - apt: - sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.8' ] - packages: [ 'clang-3.8'] - addons_clang39: &clang39 - apt: - sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise' ] - packages: [ 'clang-3.9'] - addons_gcc47: &gcc47 - apt: - sources: [ 'ubuntu-toolchain-r-test' ] - packages: [ 'g++-4.7' ] - addons_gcc48: &gcc48 - apt: - sources: [ 'ubuntu-toolchain-r-test' ] - packages: [ 'g++-4.8' ] - addons_gcc49: &gcc49 - apt: - sources: [ 'ubuntu-toolchain-r-test' ] - packages: [ 'g++-4.9' ] - addons_gcc5: &gcc5 - apt: - sources: [ 'ubuntu-toolchain-r-test' ] - packages: [ 'g++-5' ] - matrix: include: + # clang++ 4.0 via mason with -flto and -fsanitize=cfi + - os: linux + compiler: "clang++-40-mason" + env: CXX=clang++-4.0 CXXFLAGS="-flto -fsanitize=cfi -fvisibility=hidden" LDFLAGS="-flto -fsanitize=cfi -fvisibility=hidden" + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test' ] + packages: [ 'libstdc++-4.9-dev' ] + before_install: + - git submodule update --init + - ./.mason/mason install clang++ 4.0.1 + - export PATH=$(./.mason/mason prefix clang++ 4.0.1)/bin:${PATH} + - ./.mason/mason install binutils 2.27 + - export PATH=$(./.mason/mason prefix binutils 2.27)/bin:${PATH} + # clang++ 4.0 via mason with -fsanitize=address + - os: linux + compiler: "clang++-40-mason" + env: CXX=clang++-4.0 CXXFLAGS="-fsanitize=address -fsanitize-address-use-after-scope -fno-omit-frame-pointer -fno-common" LDFLAGS="-fsanitize=address" ASAN_OPTIONS=check_initialization_order=1:detect_stack_use_after_return=1 + sudo: required + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test' ] + packages: [ 'libstdc++-4.9-dev' ] + before_install: + - git submodule update --init + - ./.mason/mason install clang++ 4.0.1 + - export PATH=$(./.mason/mason prefix clang++ 4.0.1)/bin:${PATH} + # clang++ 4.0 via mason with -fsanitize=undefined + - os: linux + compiler: "clang++-40-mason" + env: CXX=clang++-4.0 CXXFLAGS="-fsanitize=undefined" LDFLAGS="-fsanitize=undefined" + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test' ] + packages: [ 'libstdc++-4.9-dev' ] + before_install: + - git submodule update --init + - ./.mason/mason install clang++ 4.0.1 + - export PATH=$(./.mason/mason prefix clang++ 4.0.1)/bin:${PATH} + # clang++ 4.0 via mason with -fsanitize=integer + - os: linux + compiler: "clang++-40-mason" + env: CXX=clang++-4.0 CXXFLAGS="-fsanitize=integer" LDFLAGS="-fsanitize=integer" + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test' ] + packages: [ 'libstdc++-4.9-dev' ] + before_install: + - git submodule update --init + - ./.mason/mason install clang++ 4.0.1 + - export PATH=$(./.mason/mason prefix clang++ 4.0.1)/bin:${PATH} + # clang++ 4.0 via mason with -fsanitize=safe-stack + - os: linux + compiler: "clang++-40-mason" + env: CXX=clang++-4.0 CXXFLAGS="-fsanitize=safe-stack" LDFLAGS="-fsanitize=safe-stack" + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test' ] + packages: [ 'libstdc++-4.9-dev' ] + before_install: + - git submodule update --init + - ./.mason/mason install clang++ 4.0.1 + - export PATH=$(./.mason/mason prefix clang++ 4.0.1)/bin:${PATH} - os: osx - osx_image: xcode7 - env: TEST_GYP_BUILD=True + osx_image: xcode8 + env: OSX_OLDEST_SUPPORTED=10.7 TEST_GYP_BUILD=True + compiler: clang + - os: osx + osx_image: xcode8 + env: OSX_OLDEST_SUPPORTED=10.12 compiler: clang - os: linux compiler: "clang35" env: CXX=clang++-3.5 COVERAGE=True - addons: *clang35 + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.5' ] + packages: [ 'clang-3.5', 'libstdc++-4.9-dev' ] - os: linux compiler: "clang36" env: CXX=clang++-3.6 - addons: *clang36 - - os: linux - compiler: "clang37" - env: CXX=clang++-3.7 - addons: *clang37 - - os: linux - compiler: "clang38" - env: CXX=clang++-3.8 - addons: *clang38 - - os: linux - compiler: "clang38" - env: CXX=clang++-3.8 CXX_STD=c++14 - addons: *clang38 - # not whitelisted yet: https://github.com/travis-ci/apt-package-whitelist/issues/2764 - #- os: linux - # compiler: "clang39" - # env: CXX=clang++-3.9 - # addons: *clang39 - - os: linux - compiler: "gcc47" - env: CXX=g++-4.7 - addons: *gcc47 + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6' ] + packages: [ 'clang-3.6' ] - os: linux compiler: "gcc48" env: CXX=g++-4.8 - addons: *gcc48 + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test' ] + packages: [ 'g++-4.8' ] - os: linux compiler: "gcc49" env: CXX=g++-4.9 - addons: *gcc49 - - os: linux - compiler: "gcc49" - env: CXX=g++-4.9 CXX_STD=c++14 - addons: *gcc49 + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test' ] + packages: [ 'g++-4.9' ] - os: linux compiler: "gcc5" - env: CXX=g++-5 CXXFLAGS="-D_GLIBCXX_USE_CXX11_ABI=0" - addons: *gcc5 + env: CXX=g++-5 + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test' ] + packages: [ 'g++-5' ] - os: linux - compiler: "gcc5" - env: CXX=g++-5 CXXFLAGS="-D_GLIBCXX_USE_CXX11_ABI=1" - addons: *gcc5 + compiler: "gcc6" + env: CXX=g++-6 CXX_STD=c++14 + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test' ] + packages: [ 'g++-6' ] -before_install: +install: - echo ${CXX} - if [[ $(uname -s) == 'Linux' ]]; then export PYTHONPATH=$(pwd)/.local/lib/python2.7/site-packages; @@ -108,7 +129,17 @@ before_install: PYTHONUSERBASE=$(pwd)/.local pip install --user cpp-coveralls; fi -install: +script: + # Build in Release + - make + - make test + - make bench + - make sizes + - scripts/run_compilation_failure_tests.sh + - make clean; + # Build in Debug + - export BUILDTYPE=Debug + - make - make test - make bench - make sizes @@ -118,11 +149,11 @@ install: make gyp; fi -script: +after_script: - if [[ ${COVERAGE:-0} == 'True' ]]; then make clean; make coverage; ./out/cov-test; cp unit*gc* test/; - ./.local/bin/cpp-coveralls --gcov /usr/bin/llvm-cov-3.5 --gcov-options '\-lp' -i optional.hpp -i recursive_wrapper.hpp -i variant.hpp -i variant_io.hpp; + ./.local/bin/cpp-coveralls --gcov /usr/bin/llvm-cov-3.5 --gcov-options '\-lp' -i optional.hpp -i recursive_wrapper.hpp -i variant.hpp -i variant_io.hpp variant_cast.hpp; fi diff --git a/third_party/variant/CHANGELOG.md b/third_party/variant/CHANGELOG.md new file mode 100644 index 000000000..f6c4ca044 --- /dev/null +++ b/third_party/variant/CHANGELOG.md @@ -0,0 +1,420 @@ +# Variant changelog + +## 1.2.0 + +Released: July 3, 2020 + +(82f9561) + +* Use perfect forwarding for internal value types deductions (#178) (#180) +* Implement support for "moving" values out (#142) (#178) (#180) +* Preserve ability to specify explicit `return_type` in visitors (#181) +* Add self-assignment checks in copy and move assignment operator= (#164) +* Add relevant tests + +## 1.1.6 + +Released: April 25, 2019 + +(a4f87dc) + +* make type used for `type_index` configurable via `type_index_t` typdef + use `unsigned int` by default. This addresses `sizeof` discrepancies between boost/std/mapbox variants (ref #19) [view commit](http://github.com/mapbox/variant/commit/9eec1fd48947d81af3debb82686c593b15f79aad) +* use `mapbox::util::type_index_t` (#19) [view commit](http://github.com/mapbox/variant/commit/05ee9aca16c3968e34db3b241c44eecb981344e0) +* Ensure internal index type is capable of holding all alternatives (ref #138) [view commit](http://github.com/mapbox/variant/commit/fa8e124a2367abc9c06f7e83926691085eed45c0) +* Add compile time check to disallow array types as alternatives. [view commit](http://github.com/mapbox/variant/commit/3f6fd131ef07a091338cec81ec8d23d6ca44528d) +* Make `type_index_t` configurable at compile time via `MAPBOX_VARIANT_MINIMIZE_SIZE` and `MAPBOX_VARIANT_OPTIMIZE_FOR_SPEED`. Default is `unsigned int`. (ref #138) [view commit](http://github.com/mapbox/variant/commit/35487cd39400b9a4bd30a18e6dfbf9cb1aaa80cd) +* add missing [view commit](http://github.com/mapbox/variant/commit/c839c666c324306a252b0fbcadd31e80e02e2898) +* Adds a test for polymorphic lambdas in match, resolves #140 [view commit](http://github.com/mapbox/variant/commit/9ac8978f5125182ac1bd9a1b68533bf9695f7289) +* Merge pull request #138 from mapbox/sizeof [view commit](http://github.com/mapbox/variant/commit/3d807d31621d52a8b27494c8f80aa50f6464f17d) +* Merge pull request #141 from mapbox/match-otherwise [view commit](http://github.com/mapbox/variant/commit/916139a2e51e125816efce6e19d428385601273f) +* Update bundled Catch to v1.9.0 [view commit](http://github.com/mapbox/variant/commit/f9c265d7e7a188aa4437f534d4c0648af0118b51) +* REQUIRE_THROWS etc take an expression not a block [view commit](http://github.com/mapbox/variant/commit/a064940e2ce7e40ef5e4db5710b399c68a71be4b) +* Merge pull request #143 from tomhughes/catch [view commit](http://github.com/mapbox/variant/commit/550ac2f159ca883d360c196149b466955c77a573) +* Add static_variant_cast, dynamic_variant_cast [view commit](http://github.com/mapbox/variant/commit/51fccd755b05ee9d3dec9da239acd15d0823c381) +* Merge pull request #144 from narizhny/Casts [view commit](http://github.com/mapbox/variant/commit/291121f6ac682c6cc5a7a69f253492f03ca7324f) +* recursive_wrapper fail to compile when used with 2 classes which are base and derived #146 [view commit](http://github.com/mapbox/variant/commit/7a541ba10d2eb9a9da0f11bb27319cd125d43a3d) +* recursive_wrapper test - avoid constructing new functor in recursive calls, call itself via `this` pointer. [view commit](http://github.com/mapbox/variant/commit/ea106db54b167b8dce7c0b3b9b59bb06b209db33) +* Merge branch 'master' of https://github.com/BlueSolei/variant into BlueSolei-master [view commit](http://github.com/mapbox/variant/commit/195367cfc19e1e08933487fa7cc56cb7f6d25cc8) +* Merge branch 'BlueSolei-master' [view commit](http://github.com/mapbox/variant/commit/e01b7bf3334e788fb99f4a510d5bc87a4a581342) +* add test for ref #147 + https://github.com/mapbox/variant/pull/147 [view commit](http://github.com/mapbox/variant/commit/6247207595902dbf898a430346335af2a3485c74) +* - Add a project mapbox_variant. - Use of the 'os' module to capture CXX_STD. - Common configs moved to project. - Built targets moved to 'out' directory. [view commit](http://github.com/mapbox/variant/commit/b2471ffc74c163194943b17b2b2c5758c59655ca) +* - Remove the use of boost libraries. - Add default build. [view commit](http://github.com/mapbox/variant/commit/561a09dd005468f9cdef651030471a1215f1885f) +* - Use of the module 'os' to get BOOST_DIR. - Add macro SINGLE_THREADED to single threading mode. - Define single threading mode as default. - Add lambda_overload_test and hashable_test. [view commit](http://github.com/mapbox/variant/commit/bd0a2d559724b8daa7d1ff33df90976e26a595aa) +* - Add auxiliar rule exe-test. [view commit](http://github.com/mapbox/variant/commit/04a6797a6aa2a86dd3eb6517893255c010f6e524) +* Merge pull request #153 from ricardocosme/boost-build [view commit](http://github.com/mapbox/variant/commit/266f68d9f1c3ad65e6d6c264f0130bc4c652618a) +* Use forwarding reference in make_visitor and visitor [view commit](http://github.com/mapbox/variant/commit/9f991da78d3146d32be67695a89c2b1197c826b2) +* Add copy assignment and move assignment operators. [view commit](http://github.com/mapbox/variant/commit/f0b50062b4fd2bf2b86aeada2efa8e36cfa6cb1c) +* Merge pull request #154 from ricardocosme/forwarding_reference_make_visitor [view commit](http://github.com/mapbox/variant/commit/b78b51548743737357e5b3bbe296f465d5f4fdae) +* add CHANGELOG.md skeleton [view commit](http://github.com/mapbox/variant/commit/555436f715e5e0929a13664f0911ecc4931356d1) +* add to CHANGELOG entries. [view commit](http://github.com/mapbox/variant/commit/6497bce683e6e8edf80f80bc4fed65235690b335) +* update CHANGELOG (git log ... --pretty=format:'* %s [view commit](http://github.com/mapbox/variant/commit/%H)' --reverse) [view commit](http://github.com/mapbox/variant/commit/75bb549d233eb94c74d2730dc3a8d8ed35c87f3d) +* use full sha1 [view commit](http://github.com/mapbox/variant/commit/ba3085a5eb6e874d43432dc75f3392092e1e7214) +* add intial `variant_alternative` implementation (#161 http://en.cppreference.com/w/cpp/utility/variant/variant_alternative) [view commit](http://github.com/mapbox/variant/commit/43357808cc93e69d2975e31e68986137ac5e88c9) +* add lost test check + remove stderr [view commit](http://github.com/mapbox/variant/commit/4b98c485cf7d74691f7921145054641daa66936e) +* alternative implementation of `variant_alternative` [view commit](http://github.com/mapbox/variant/commit/3449d00cf525d8ef98cee0f4a276e2928398a8f9) +* add `variant_alternative_t` [view commit](http://github.com/mapbox/variant/commit/3ffef950b005f31961f167242911b2f97d2634c3) +* add optimized 'variant_alternative' implementation usinh built-in `__type_pack_element` when available (clang++) [view commit](http://github.com/mapbox/variant/commit/ae193141379c1706e17098c67c5b4e4f48b19c48) +* add compile index in range check for __type_pack_element branch. [view commit](http://github.com/mapbox/variant/commit/8b1de314711bff2f9f3c748ac0ed7cd7d6400331) +* fix preprocessor logic [view commit](http://github.com/mapbox/variant/commit/30560e19e60c23227b29bc3434a163d2343333d3) +* add `variant_size` helper [view commit](http://github.com/mapbox/variant/commit/835ebc19321c6a9696a2072b7fbd5ca3de818860) +* Merge pull request #162 from mapbox/variant_alternative [view commit](http://github.com/mapbox/variant/commit/237f83cad2c76b1717ba4076c30aca32339336a8) +* Removes deprecated static_visitor to avoid msvc C4996 compiler warning [view commit](http://github.com/mapbox/variant/commit/215d64585ef92e16f18f5da81195b0279f53f599) +* Merge pull request #163 from MaxRis/master [view commit](http://github.com/mapbox/variant/commit/859a8c933a0c2ab18941acb9dcf834799c0de46c) +* Fix README.md issues [view commit](http://github.com/mapbox/variant/commit/0888a8e92df4c1cfd85419b05910355b2d78013b) +* Merge pull request #165 from nick70/master [view commit](http://github.com/mapbox/variant/commit/5eee328d69aaa805bd0b43bdaede12a8eb4632eb) +* Fix the noexcept specifications for move assignment and conversion. [view commit](http://github.com/mapbox/variant/commit/9c81bef8cf285d9fb45f1eaf9b29eba4fee08d1b) +* Merge pull request #160 from mlogan/master [view commit](http://github.com/mapbox/variant/commit/256ddd55582bb7c06c342315dbacc6a42fee4b34) +* report actual file size not allocated size. [view commit](http://github.com/mapbox/variant/commit/ef3856c85f389d4be7feb6555336168c4adcfa0e) +* use `ls -lah` as `du -h --apparent-size` is not universally supported. [view commit](http://github.com/mapbox/variant/commit/a64062576d9af09469183a94b9770c8e7f877a93) +* fix Makefile [view commit](http://github.com/mapbox/variant/commit/502e32b8bade6e19d7fe511f4634e7033f61235f) +* try fixing travis via upgrading clang++ from 3.9.1 -> 4.0.1 [view commit](http://github.com/mapbox/variant/commit/f31bcfb4bc97cf4c5e89b01bbfa7df4cd553f576) +* steady .. downgrade clang++ to 4.0.0 [view commit](http://github.com/mapbox/variant/commit/11a36a9f12adc30ac47afc9681ec8aa1a2d68c28) +* update mason [view commit](http://github.com/mapbox/variant/commit/fe0a0666fc734033f6a9cd2256226eec5943138a) +* update mason + update clang++ to 4.0.1 [view commit](http://github.com/mapbox/variant/commit/c1a14e7d9e9a10ea7d793d83043d9a18b974ca8e) +* Run ASAN builda in isolated VM via `sudo : required` [view commit](http://github.com/mapbox/variant/commit/5a5ecca5bef02072109a714bab840a36ea772f01) +* Merge pull request #167 from mapbox/clang++4 [view commit](http://github.com/mapbox/variant/commit/0f734f01e685a298e3756d30044a4164786c58c5) +* Moved to in-class initialization [view commit](http://github.com/mapbox/variant/commit/2fef61f08e44bcc99b1acc21ea78554b08d8f6e7) +* Merge pull request #171 from mapbox/jrex-mute-clang-analyzer [view commit](http://github.com/mapbox/variant/commit/0305fdb2a462ca39db7b8cce189561bed17b48 + +## 1.1.5 + +Released: January 7, 2017 + +(d2588a8f1d6b5d480d228e6d8a906ce634bdea9a) + +* add package.json for publishing to npm [view commit](http://github.com/mapbox/variant/commit/cb5635ba2556d76aaba97e4d0fc14b82b48f8b61) +* test with clang 3.9 and g++-6 [view commit](http://github.com/mapbox/variant/commit/efa75df2735d3f5a5fa2646528d4006bf9b5b3dc) +* travis: fix addons [view commit](http://github.com/mapbox/variant/commit/ce2eea64499cd37eec7932ddf82f72b9f1a1b79e) +* makefile improvements [view commit](http://github.com/mapbox/variant/commit/c81b475b40797d503f18ddad4c065f6b1694d341) +* upgrade boost to 1.62.0 [view commit](http://github.com/mapbox/variant/commit/b9c58d631a22e97f4069a819765f5c157525df6a) +* upgrade mason [view commit](http://github.com/mapbox/variant/commit/a760cea8dab53e587498470861b364f1256a5e0d) +* test clang++ via mason [view commit](http://github.com/mapbox/variant/commit/84eeb54c9408297db4bc57e3ea5a1b3d9f075a66) +* fix clang++ PATH [view commit](http://github.com/mapbox/variant/commit/e07a533a8f451fe541db683fc713eb0012730115) +* disable clang++ 3.9, will work on getting working in a branch [view commit](http://github.com/mapbox/variant/commit/702826365d1ea13710b82949c254af5920c92999) +* test with clang++ sanitizers and flto [view commit](http://github.com/mapbox/variant/commit/9b2de45460f4df3a22c6607043d50df730dd42af) +* fix LDFLAGS [view commit](http://github.com/mapbox/variant/commit/20d693ed9a04cc6b689f4c6a1ed58c96f964b08a) +* -fsanitize=cfi and -fsanitize=safe-stack [view commit](http://github.com/mapbox/variant/commit/d1bb6e54608c6bfe3970b2653971da49b3c15ef8) +* more sanitizer options [view commit](http://github.com/mapbox/variant/commit/4fe5ced5db2cb46b23a6e326b5bf9292d95c0642) +* re-enable older compilers, trim excess [view commit](http://github.com/mapbox/variant/commit/6317a0b7406395729139327a83a71cfa14513fac) +* avoid expensive instantiation of tuple constructor in noexcept [view commit](http://github.com/mapbox/variant/commit/4febf973c2a0fc297869d78fc82368a16ee7f4fb) +* Merge pull request #132 from lightmare/avoid-tuple-instantiation [view commit](http://github.com/mapbox/variant/commit/18919174da93165a79ca5fdbfde4b172182c6156) +* enable -Werror, suppress warnings from non variant headers using isystem [view commit](http://github.com/mapbox/variant/commit/253047f53549c3fb1df441859cfd42aecc9f3a8f) +* fix conversion warnings [view commit](http://github.com/mapbox/variant/commit/539d712746d08a172e68a47f7aa73ffdda40b70b) +* build in both release and debug on travis [view commit](http://github.com/mapbox/variant/commit/cf9a534991b5a36f86674975768e0a1600c776be) +* fortification flags + -pthreads for linux where needed [view commit](http://github.com/mapbox/variant/commit/886377de081dd8099ef4a7869ae3ff5d6c850c8d) +* try without pthreads [view commit](http://github.com/mapbox/variant/commit/1023f2d9adcf0a14be7cb729abab8156ecd962c5) +* limit some flags to clang++ [view commit](http://github.com/mapbox/variant/commit/904dcaee6d0d872eae52578e38199577418f9a32) +* Add -pthread [view commit](http://github.com/mapbox/variant/commit/b433986199b7dfbe59fa498eb80791a49c625170) +* upgrade libstdc++ for coverage build [view commit](http://github.com/mapbox/variant/commit/7b409402c3cf4a0260aa36ee768ee474aa3683c1) +* drop -Wstack-protector which gives unhelpful warnings [view commit](http://github.com/mapbox/variant/commit/c8ec829ffb4498a0aea8ffa9b62ba4932cdefbdf) +* disable -Wparentheses for older gcc [view commit](http://github.com/mapbox/variant/commit/a80beaafc20c2c015ace5fb1e4c99f6de8c24d75) +* Merge pull request #133 from mapbox/Werror [view commit](http://github.com/mapbox/variant/commit/a9707c3de095c715f84a1855684a3dc8e37a594a) +* remove useless and/or dubious compiler flags [view commit](http://github.com/mapbox/variant/commit/5141d8d21a5b648e9fef879bfc12b29ffac7288d) +* Merge pull request #134 from lightmare/warnings [view commit](http://github.com/mapbox/variant/commit/18a8055fef0e14110c313ca358f0f7c88290bed0) +* osx: test that will support both latest (10.12) and oldest with c++11 support: 10.7 [view commit](http://github.com/mapbox/variant/commit/4923eb527c129060ba970d622d639ad2ada42497) +* fix gyp build [view commit](http://github.com/mapbox/variant/commit/5baa948fa73313091bc082b9f3d17c5b5f600cac) +* upgrade to llvm 3.9.1 [view commit](http://github.com/mapbox/variant/commit/f5fb4661ebf1ecd0167bce50b00a8339604395b0) +* upgrade mason [view commit](http://github.com/mapbox/variant/commit/61f8acea1b09de639b46c8af0c5aae29f51dd05c) +* Merge pull request #135 from mapbox/llvm-3.9.1 [view commit](http://github.com/mapbox/variant/commit/05b7612aa86c28f95d39ba786c7b611e811e4bf8) +* Trivial missing comma in README example code [view commit](http://github.com/mapbox/variant/commit/d2588a8f1d6b5d480d228e6d8a906ce634bdea9a) + +## 1.1.4 + +Released: December 21, 2016 + +(02bd1ac4c07e6db9fe0f01267853e43b41637b74) + +* Provides Convenient Lambda Overload Visitor Interface, resolves #113. [view commit](http://github.com/mapbox/variant/commit/d09188640b6d5a637f391108f849a962d02dbb40) +* Removes ::type Usage [view commit](http://github.com/mapbox/variant/commit/2275a61974aaf117fa8d08c08d640ffd05935db8) +* Adds C++14 SFINAE Test [view commit](http://github.com/mapbox/variant/commit/4d462f27b2d852f66a3769e5983691c0f9233c9e) +* Merge branch 'daniel-j-h-lambda-visitor' [view commit](http://github.com/mapbox/variant/commit/9a115c5eb3c09509c70a57b25b283b6e1cbba919) +* Makes variant hashable iff Ts... are hashable, closes #125 [view commit](http://github.com/mapbox/variant/commit/97d0379f0afc87cd74a10be56fbccfa04785f568) +* Implements Pattern Matching for Sum Types via `.match` Member Function. [view commit](http://github.com/mapbox/variant/commit/720c23736bb318937d0f53a413a617ff05040b73) +* Adds Documentation for Readme, resolves #98 [view commit](http://github.com/mapbox/variant/commit/d0266436b18ea3b1f15c8a244985b57a0a2b3770) +* Merge pull request #126 from daniel-j-h/hashable [view commit](http://github.com/mapbox/variant/commit/3c17c37aea0d7e3d9e860b746d54160ec820e6a2) +* Merge pull request #128 from daniel-j-h/match [view commit](http://github.com/mapbox/variant/commit/ed84def128ed99a4b3b1ebcde278be7f761e782e) +* Merge pull request #129 from daniel-j-h/docs [view commit](http://github.com/mapbox/variant/commit/02bd1ac4c07e6db9fe0f01267853e43b41637b74) + + +## 1.1.3 + +Released: October 24, 2016 + +(a5a79a594f39d705a7ef969f54a0743516f0bc6d) + +* use C++17 disjunction for no-references and one-convertible tests [view commit](http://github.com/mapbox/variant/commit/2c7ddecdb7ec3b1c1a6bc1797528375e513b7ab0) +* Merge pull request #116 from lightmare/disjunction [view commit](http://github.com/mapbox/variant/commit/8e2f6964157885f1655c1673d65f3aea9b90fe18) +* Update README [view commit](http://github.com/mapbox/variant/commit/aaddee9270e3956cee98cdd7d04aea848d69f5f0) +* expose `using types = std::tuple;` - useful for adapting variant to `boost::spirit` (QI,Karma,X3) [view commit](http://github.com/mapbox/variant/commit/e5818212a8f7ef89df0aa76d5244eca78b8dbb8d) +* add `struct adapted_variant_tag;` [view commit](http://github.com/mapbox/variant/commit/173a7457952d0f24e2c55d5eb3ea785ad41639fb) +* Merge pull request #120 from mapbox/types [view commit](http://github.com/mapbox/variant/commit/84a426a31ad3b63c4b8f8d189841e19af48cda40) +* nicer stderr [view commit](http://github.com/mapbox/variant/commit/9b46167f5c42a19a3b66cb92eb60418486b4e424) +* Fix #122 by adding an extra compile check in universal ctor (via @lightmare) + test case [view commit](http://github.com/mapbox/variant/commit/a5a79a594f39d705a7ef969f54a0743516f0bc6d) + + +## 1.1.2 + +Released: July 26, 2016 + +(388376ac9f0102feba2d2122873b08e15a66a879) + +* Re-implement type matching logic to reject ambigious conversions [view commit](http://github.com/mapbox/variant/commit/71ac8fdf96e547dca34fe58c5cd8d1dce2ef0dac) +* update tests [view commit](http://github.com/mapbox/variant/commit/8be6a2aa8f85e1455198eff31a577a1fb95e1d46) +* comment out code [view commit](http://github.com/mapbox/variant/commit/075d9636fdfe563b535fa3ba087409f940c018e4) +* Merge pull request #114 from mapbox/strict-conversions [view commit](http://github.com/mapbox/variant/commit/388376ac9f0102feba2d2122873b08e15a66a879) + + +## 1.1.1 + +Released: July 18, 2016 + +(c511b2f34d966c09e02a1b833db33a9a1f9b2196) + +## 1.1.0 + +Released: February 11, 2016 + +(5aab5df0dc899b484c04ce9c649645787ee0bc5c) + +* remove erroneous `;` ref #96 [view commit](http://github.com/mapbox/variant/commit/3f025adbf599d8dd9bfca02d45b37e49a2cae841) +* fix coverage to avoid warning: unit.gcno:version '402*', prefer '406*' [view commit](http://github.com/mapbox/variant/commit/b0ee4729bfc9ea649abe40f279de384df75b79d1) +* fix clang 3.8 compile, try 3.9 [view commit](http://github.com/mapbox/variant/commit/f034d5571de987d14b404dba94e7269ac41fa583) +* remove invalid option for llvm-cov [view commit](http://github.com/mapbox/variant/commit/5f6ed7149d737edf9f1f019beb54cbe5289c474c) +* run coverage with clang 3.5 - fix clang 3.8 build [view commit](http://github.com/mapbox/variant/commit/82bb901b6cc0de8c238a07292163dc7c28a26ce4) +* issue warning `-Wweak-vtables` so this issue is not forgotten (https://github.com/mapbox/variant/issues/95) [view commit](http://github.com/mapbox/variant/commit/35ca16c74f5712afb4f042f45ea64078fa0b630e) +* move headers into include/mapbox folder - closes #99 [view commit](http://github.com/mapbox/variant/commit/f00b24bf65e8af7fddc56ac4a3abe67ed974b0a5) +* Update README.md [view commit](http://github.com/mapbox/variant/commit/13c631a6297d9abc9677c1afc1a3907fec7c16b4) +* Add include directory [view commit](http://github.com/mapbox/variant/commit/7e4a01189bb4050524954b2a88c82de7cb82ea4a) +* Merge branch 'master' into include [view commit](http://github.com/mapbox/variant/commit/9bd902536f533305186aaf70edb2f0b9713f6b6b) +* fix typo [view commit](http://github.com/mapbox/variant/commit/a606e90243dcf145cf06d46e8e30c447f85af178) +* ammend include dir [view commit](http://github.com/mapbox/variant/commit/343831611e60e324e311d67f05da953e357df0a1) +* update remaining `` to `` [view commit](http://github.com/mapbox/variant/commit/bfe0f19dd14dedad9c0a6f1e211e81bd1233564e) +* fix compilation [view commit](http://github.com/mapbox/variant/commit/390229a59703d2467347d62f3e134e67ea6835cc) +* Merge pull request #101 from mapbox/include [view commit](http://github.com/mapbox/variant/commit/1bc46e525a9dec71af28f822e5fc031c1352ad2e) +* Remove Xcode 6 from CI matrix [view commit](http://github.com/mapbox/variant/commit/9b2fc858ccd84509fd7164a4847e7dc95e58d5a5) +* Install boost with mason; eliminate boost::timer dependency [view commit](http://github.com/mapbox/variant/commit/04dc3a46b02d6b8714d00280bb85c88716727862) +* `is()` - add specialisation for recursive_wrapper + update tests (ref #102) [view commit](http://github.com/mapbox/variant/commit/c6ae1ea0acf8c4392a806ad3abd5b11eb3b8a8ce) +* remove expected error string - current implementation emits compiler specific error message e.g [view commit](http://github.com/mapbox/variant/commit/4368d75292ae5149034b59b483fc3f8b3956a839) +* Jamroot - add missing include directory ./test/include for auto_cpu_timer.hpp [view commit](http://github.com/mapbox/variant/commit/7f7470fee6a42c3c68f1fa359a28cf762df385c3) +* Update README.md [view commit](http://github.com/mapbox/variant/commit/8bdad6b6d73844ef8437f004654c0745f0cec96a) +* Fix building with GCC (g++-5.2.0) on OS X (Darwin) (ref #108) [view commit](http://github.com/mapbox/variant/commit/55579f03fba747500b3a105ae73a7dfe6059cfc1) +* Update README.md [view commit](http://github.com/mapbox/variant/commit/33e27ec4c7cc5e1669f2181d13eacdfff15dfb61) +* Merge pull request #109 from mapbox/darwin-build-flags [view commit](http://github.com/mapbox/variant/commit/2f8a4a381f2ad8f9c2d3d068a757a3e0e9994495) +* Add get_unchecked() to enable use with exceptions disabled [view commit](http://github.com/mapbox/variant/commit/434dab048d52e4141146bb95fdabdf7aa62e799b) +* remove unused internal metafunctions [view commit](http://github.com/mapbox/variant/commit/48d60445cca1d71fbebb6456b5d45a18bb9cb3b8) +* add static which() function to get a contained types' which value [view commit](http://github.com/mapbox/variant/commit/74ce146d9b96081965d5fcdf53feefab6199468c) +* Merge branch 'master' into 111-which-constexpr [view commit](http://github.com/mapbox/variant/commit/dca3d967c165de1d0fb3bb5e1c2d6b4bcd76782f) +* Merge branch '111-which-constexpr' [view commit](http://github.com/mapbox/variant/commit/bb8c2d20317191f5228341a87b0c362c4d15be5b) +* variant - yield return type of mapbox::util::get automatically and make interface consistent (addresses #82) [view commit](http://github.com/mapbox/variant/commit/adf0e02bceb74339b8ccc3e9e3f316917cb3cc22) +* uncomment tests ref #82 [view commit](http://github.com/mapbox/variant/commit/37acc5a7caef10d2f52dbdcee71be53b79dda027) +* Merge pull request #110 from mapbox/110-get_unchecked [view commit](http://github.com/mapbox/variant/commit/20e44accb1edf84d944d44f91ed7401198368aae) +* c++ apply formatting [view commit](http://github.com/mapbox/variant/commit/372d7c88fe796a138d0e578328914ac80e5a949a) +* use local HAS_EXCEPTIONS #define (__EXCEPTIONS is g++/clang specific macro) [view commit](http://github.com/mapbox/variant/commit/eedafd31f9fdbaffcb605d8d34a3a3443a4f7a2d) +* update .mason pkgs [view commit](http://github.com/mapbox/variant/commit/b5728ad76e1402c130a9330aa44b6f4b655b13b4) +* fix value_traits to be able to match T, T& and T const& to the direct type stored in variant (ref #112) [view commit](http://github.com/mapbox/variant/commit/b3a002d185afac295486e2ebd6b84c78a2267ba0) +* add test for b3a002d185afac295486e2ebd6b84c78a2267ba0 (ref #112) [view commit](http://github.com/mapbox/variant/commit/c511b2f34d966c09e02a1b833db33a9a1f9b2196) + +## 1.0 + +Released: April 1, 2015 + +(bf485dfb59aef26f3ef2183d7c8c1111ad97062b) + +* Initial commit [view commit](http://github.com/mapbox/variant/commit/9b82890ea11742eafd686f44b8cc7075029dbd7b) +* initial import [view commit](http://github.com/mapbox/variant/commit/bb95645b86a8fe427df9af799ecd139e4fab38ef) +* remove unused boost::static_visitor [view commit](http://github.com/mapbox/variant/commit/2dbc79fdd28c2ad9623c6516069ae616af11cdc5) +* call reserve in boost::variant test and change order [view commit](http://github.com/mapbox/variant/commit/9c1d245388396ac289ccee238b549e48a7916f9e) +* add readme and makefile [view commit](http://github.com/mapbox/variant/commit/5afa5b2e1756dfb0e332f3e11bffdfb33cd31f75) +* makefile fixups [view commit](http://github.com/mapbox/variant/commit/c627c07afc972b3f1bab3ca219416d6e918cd39b) +* turn on more aggressive warnings and fix them where appropriate [view commit](http://github.com/mapbox/variant/commit/20ee8ffe2889f9e0f4974ba2aba028ccd3ce347a) +* -Wsign-compare [view commit](http://github.com/mapbox/variant/commit/12ef94a8c0edb897858b82d0064093e251ac4024) +* remove unneeded headers [view commit](http://github.com/mapbox/variant/commit/eeba005f2f2e287f23661e9dd555e57e0365d22c) +* include [view commit](http://github.com/mapbox/variant/commit/a7b562eb0ef665b57e3a29a2ff83721bc3f66a03) +* include headers for new/size_t/move [view commit](http://github.com/mapbox/variant/commit/c3963a96aab7d3d2e380652e4268c5d729778a59) +* add sizes target to check object sizes [view commit](http://github.com/mapbox/variant/commit/a1685041726cba13aa9fb728ec4963f3e7872200) +* add tests [view commit](http://github.com/mapbox/variant/commit/df1d00cc50d3b5a1cce203305d52f536f622663c) +* interleave test runs to ensure first run penalty does not murky the results [view commit](http://github.com/mapbox/variant/commit/969a346db6ab41adb66f7b3cbbcc92cd15c3b339) +* add to gitignore [view commit](http://github.com/mapbox/variant/commit/e98fc0736064d3131a25e899420b41276e801a45) +* fix build on ubuntu/g++-4.8 [view commit](http://github.com/mapbox/variant/commit/b8ab4b8c6beb2d0d4bb2afc9fda901c83588d153) +* debug/release builds + profiling [view commit](http://github.com/mapbox/variant/commit/a25c3597deb4d889085bc9492d2cf4f6df847a5a) +* pass release flags to make sizes [view commit](http://github.com/mapbox/variant/commit/23f112ae5e551a731939e8220a16a6c7a29d844b) +* force inlining to reduce final object size (yes **reduce**, with clang++ at least) [view commit](http://github.com/mapbox/variant/commit/c65aa114402e8f7b1d44062ff973f7c807741d06) +* optimization: avoid overhead of function call for invalid type check [view commit](http://github.com/mapbox/variant/commit/cebd6a31e54d84811cccab62991d142aa8aacbad) +* test threaded [view commit](http://github.com/mapbox/variant/commit/5a84ea2f40ccd2cadbc1091fae05b37b1d69c85c) +* 10 threads [view commit](http://github.com/mapbox/variant/commit/c06ebf15268925ef5e0f3ce763db13fd0c27ef2b) +* use perfect forwarding instead of move [view commit](http://github.com/mapbox/variant/commit/cd81aed73c9f1176e26090abc60a9d2467f5d5bf) +* const as const can. also reimplementation of operator= to allow for ADL in swap [view commit](http://github.com/mapbox/variant/commit/33603114129c387c5938287f1839f852a8f3a5f2) +* Merge pull request #1 from artemp/consts_and_fwds [view commit](http://github.com/mapbox/variant/commit/dff07459931e6d76b4eea6092041cbf247e01f64) +* implement comparison operators (==,<) implement operator<< [view commit](http://github.com/mapbox/variant/commit/cb9374b1ebb4c2da42e06af71ca738e9316b0582) +* make THREADS=4 ( we want to test variant not an operating system, better still make it config (TODO)) add simple test for operator== and operator<< ( define VARIANT_LOGICAL_TESTS to enable) [view commit](http://github.com/mapbox/variant/commit/68aa114c5d06a979b8fb31f0cdd2647833544cb1) +* c++ : better names (#2) [view commit](http://github.com/mapbox/variant/commit/48dd83308ebb5ab21c295499948d27d42801b306) +* c++ : better names (#2) [view commit](http://github.com/mapbox/variant/commit/46f40479fd02170b9d4bf48655a6a6a7845d77cc) +* Merge branch 'master' of github.com:artemp/variant [view commit](http://github.com/mapbox/variant/commit/41d5626bee25a4edd36c2e2d05bde46751417baa) +* Added const [view commit](http://github.com/mapbox/variant/commit/9d7afc7362e095acf032b5a3d2057d960157ffcb) +* Fixed var names [view commit](http://github.com/mapbox/variant/commit/7423b70b907b640b477c9675d79b43abb6bf21ed) +* changing const keyword position to 'T const &' [view commit](http://github.com/mapbox/variant/commit/f6677d163a1c1df6d960baa7d0d7983edac95cc3) +* Add -march=native to release build flags, implies -mtune=.. [view commit](http://github.com/mapbox/variant/commit/3ff69626f348da3f6827fc1573e014400d5f6813) +* Merge pull request #7 from artemp/const_correctness_part2 [view commit](http://github.com/mapbox/variant/commit/f8ff1da09fdb3902b39cb6893fda048b2760da68) +* Merge pull request #8 from artemp/architecture-optimizations [view commit](http://github.com/mapbox/variant/commit/4b325da289df3e91bc32d71d4adceb28d3cf8215) +* fix remaining names [view commit](http://github.com/mapbox/variant/commit/2de14db6a431689e07630f42ee18a30101ed11b3) +* add -march=native to Jamroot [view commit](http://github.com/mapbox/variant/commit/8c03239ed115ed51d181ea52a40f0e9e8ec73f42) +* more name fixing ref #2 [view commit](http://github.com/mapbox/variant/commit/f27bd4c7f514f9f646eb0766cfd8f4b7e8f0ed33) +* structs dont have private members [view commit](http://github.com/mapbox/variant/commit/3d0072d34600bbf2fc21bee3db65770cf6796402) +* add pgo option to Makefile [view commit](http://github.com/mapbox/variant/commit/7f4f85e93d09630dd78ba5eb113bb03fb9804979) +* Merge pull request #9 from artemp/classes_and_structs [view commit](http://github.com/mapbox/variant/commit/21ced9474d93a96bb5c7cd527efa5e0ba742e84a) +* Merge pull request #10 from artemp/profile_guided_optimization [view commit](http://github.com/mapbox/variant/commit/0fc8f6b3911b6b6c0dbe72d5c440c7c094add899) +* + implement binary visitation [view commit](http://github.com/mapbox/variant/commit/943b24689b918f13ee1bdeb43111cbdf49139797) +* more realistic test [view commit](http://github.com/mapbox/variant/commit/e481fdb58ac2fec43a18ba119715f539ba000007) +* fix return types [view commit](http://github.com/mapbox/variant/commit/b41b4f69c21e552198310ebaf17c4776e2b4dc1f) +* Return uint64_t [view commit](http://github.com/mapbox/variant/commit/de6db67da287a8f80b956c85a5a6bff2af6cd52f) +* recursive_wrapper init impl [view commit](http://github.com/mapbox/variant/commit/f1c12747d921763288435beb53fbf23eafb6100d) +* Merge branch 'master' of github.com:artemp/variant [view commit](http://github.com/mapbox/variant/commit/49274509129f7f5957cc785b82771ad772fb2478) +* + add static_visitor requirement to ease return_type deduction [view commit](http://github.com/mapbox/variant/commit/80d999a3471b5647a17c14217ef237f8e9f82543) +* fix binary visitor test [view commit](http://github.com/mapbox/variant/commit/87207d76fb81f87b0bbde00c1ed44ec6043103fa) +* use static_visitor as a base class for all visitors [view commit](http://github.com/mapbox/variant/commit/48f78bcd767b99575a5af4aeb0490342e8a2b7a9) +* recursive_wrapper test (work-in-progress) [view commit](http://github.com/mapbox/variant/commit/05af9c4f21eefc91c713bc1e34e0e04b0556519a) +* include recursive_wrapper [view commit](http://github.com/mapbox/variant/commit/cbd9e2cf91e58baebd79ce336ba4b20148f303af) +* update test (FIXME - inteface is very clunky atm and needs more work) [view commit](http://github.com/mapbox/variant/commit/d2cda9a88684d2e7fad3d3527e53f7c64c84378e) +* unwrap recursive_wrapper [view commit](http://github.com/mapbox/variant/commit/bc65cb8d7be23806dc63c2adf03fc8e9b5557f66) +* + const [view commit](http://github.com/mapbox/variant/commit/6e9a10f43eab476d68399d6dcc51a2464cd2f0b8) +* recursive variant test using std::unique_ptr move semantics [view commit](http://github.com/mapbox/variant/commit/5ebf86772daade4490652218e3437d5d0ca7d1de) +* add missing test file [view commit](http://github.com/mapbox/variant/commit/b4abfa58efb676aa86a3dd2dfee04f7e95aee7fd) +* update recursive_wrapper and unique_ptr tests to accept arg [view commit](http://github.com/mapbox/variant/commit/edcb444afc193784cb6237616d68973b36e7920e) +* make test -> make bench [view commit](http://github.com/mapbox/variant/commit/fc80297390285e83394983201473d01edfa17d4c) +* fix compile of test/variant [view commit](http://github.com/mapbox/variant/commit/da417016e6af968cdcea1b2ec9f2023d3ee77cad) +* recursive_wrapper.hpp depends on boost checked delete for the moment [view commit](http://github.com/mapbox/variant/commit/a8d019d470e11eb3a569581ce140397bb6f6e31d) +* shuffle code, all build targets in out/ directory [view commit](http://github.com/mapbox/variant/commit/8e5abd0f14e298b9cc93f7428c2cd21ce732fad9) +* all tests in ./test directory [view commit](http://github.com/mapbox/variant/commit/dadea1f2a7d9c08e1a5979cc5bb824779fb73380) +* add travis [view commit](http://github.com/mapbox/variant/commit/7e775d10774261b5461474013b840fdd899db023) +* fix travis targets [view commit](http://github.com/mapbox/variant/commit/c6bd4f8131f6a58d6d6dfaf3017c8793a0da9819) +* travis: upgrade to gcc-4.8 for c++11 support [view commit](http://github.com/mapbox/variant/commit/84fdc9e2c0701980c083ff3cb8701f3076f10fa6) +* fix a few -Wsign-compare warnings [view commit](http://github.com/mapbox/variant/commit/d3d0704c59dba67f925b707d6c72babadd607ce9) +* fix -Wshadow warning [view commit](http://github.com/mapbox/variant/commit/e19d0e5aca3b4e4a76e29e34352d634b7c495202) +* fix linux compile of binary_visitor_test.cpp [view commit](http://github.com/mapbox/variant/commit/d8df077f29428c55688910d5f96e79b6d351d5e1) +* qualify c++11 int types [view commit](http://github.com/mapbox/variant/commit/62e7165925bfce3e4c11a2fce1cad8d486825613) +* fix #12 [view commit](http://github.com/mapbox/variant/commit/c0593af2c3066d13f30bf15241da313539539f18) +* test with both gcc 4.7 and 4.8 [view commit](http://github.com/mapbox/variant/commit/916719bb38cec2e9f4ff7c3d2ef480734badbb7d) +* Add BSD license [view commit](http://github.com/mapbox/variant/commit/cf7f7bef518d7dd67d74e84bdbaf40a6c5826114) +* add unit tests [view commit](http://github.com/mapbox/variant/commit/0ef558f9607844fa88b1857e6ff993121dd84d71) +* port logical tests to test/unit.cpp - refs #15 [view commit](http://github.com/mapbox/variant/commit/7109df9c4f423b846839240d720de4a89be101c8) +* version, starting at 0.1.0 [view commit](http://github.com/mapbox/variant/commit/e5c89779e59bc22571ce5beb27a823b94b2be786) +* try building on windows with gyp [view commit](http://github.com/mapbox/variant/commit/b97876a2e5ddaf8931cecd36b5a649fb3e1eb420) +* call gyp_main.py [view commit](http://github.com/mapbox/variant/commit/24cddfbc76c713046b130158c72231bd827d4a23) +* be explicit about config and platform [view commit](http://github.com/mapbox/variant/commit/bc80b31f8c4afd469f65046cfc3fe18bba95fbd3) +* also test building with gyp on travis [view commit](http://github.com/mapbox/variant/commit/51eda4f439c24223a964fe25d077ff1efa27dce0) +* try 'any cpu' [view commit](http://github.com/mapbox/variant/commit/41056fa781bf3c6a79182db338595b44d6370c25) +* put msbuild on path [view commit](http://github.com/mapbox/variant/commit/948a6ccb89ae2b4a3682805519b585bf573763a9) +* appveyor: try building 32 bit [view commit](http://github.com/mapbox/variant/commit/1b89ab841021d28da193e3b1fab27269651bdc17) +* Update README.md [view commit](http://github.com/mapbox/variant/commit/011b5125a4bb139f794bb37080bd33cae6519f7c) +* + it breaks builds for me - I have 'using clang ..' in $(boost-dir)/tools/build/v2/user-config.jam where it should be. [view commit](http://github.com/mapbox/variant/commit/1b2fc49bbc7e76bbaf2c64ea44cb3f287d5948a5) +* ```apply_visitor``` interface not-compatible with boost::apply_visitor (by changing args order) wasn't smart [view commit](http://github.com/mapbox/variant/commit/67ac560672e5ea7bf99a4a823b5259ccd6d1bd5d) +* fix syntax [view commit](http://github.com/mapbox/variant/commit/002ccdcde4aacfcc6b67ad4981c05402514c09f1) +* windows support [view commit](http://github.com/mapbox/variant/commit/64f8fb4473f3ef6f7117dc02f02a20645e415b72) +* update readme [view commit](http://github.com/mapbox/variant/commit/20f26eceebef717a0ee759b42bfe939a95874807) +* appeveyor: try building for just x86 [view commit](http://github.com/mapbox/variant/commit/b3f8117d05071981106b7d1fe15405a318d3c1df) +* fix setting of msbuild_toolset for c++11 support [view commit](http://github.com/mapbox/variant/commit/d0603d41a8597089c0c1cd099ebb8bcf5e1414d3) +* Add vcbuild.bat [view commit](http://github.com/mapbox/variant/commit/bb12b87574fcdaa7bdca6c1529d442eab2d52b97) +* remove sizeof checks since they are implementation depedenent [view commit](http://github.com/mapbox/variant/commit/f5af06c20329a0c762af9420a3ae2e3d22c02c91) +* comment failing test on windows [view commit](http://github.com/mapbox/variant/commit/7d4dc68667659e7585d28f743509956e92759255) +* appveyor: re-enable matrix [view commit](http://github.com/mapbox/variant/commit/6dbc0546bdab9e8dc291eb08d2e6dce13a0dcfe8) +* add to be include order agnostic [view commit](http://github.com/mapbox/variant/commit/9be8c519c67ef2ec8d3b5a7b736f0a8b870b43ed) +* Merge pull request #16 from DennisOSRM/master [view commit](http://github.com/mapbox/variant/commit/b27a14e98d9eadf2aabd628a602f9e4a5fcb0a5f) +* move detail tests + add initial comments (@artemp, please review) [view commit](http://github.com/mapbox/variant/commit/ea25e84aeec16588857ac83c1241d34a5df4adac) +* fix typo in code comment [skip ci] [view commit](http://github.com/mapbox/variant/commit/b773421f5868315eccd504204f32ee8c02e78dc6) +* rename internal id to index, add tests [view commit](http://github.com/mapbox/variant/commit/390e2315b97bbc71aa575d98bacc1ed760d0aa4a) +* Merge pull request #18 from mapbox/type_index [view commit](http://github.com/mapbox/variant/commit/046296a95c9d34224c23c843bc8bd6d502f81b47) +* modify tests slightly to output num_iter ((3+2)-4) [view commit](http://github.com/mapbox/variant/commit/602eceb753751ae30cd5ca7a25d3337179ba6b5e) +* boost is uneeded for unit.cpp tests [view commit](http://github.com/mapbox/variant/commit/e1de3d78e8827d5b6819cc91b430429a46093c95) +* enable ctor's for valid types at compile time [view commit](http://github.com/mapbox/variant/commit/31e3fd96fe4f86d9ac5ac53d6e3e07605307eb5c) +* [travis] multi-os [view commit](http://github.com/mapbox/variant/commit/2f1e36d25f152c31cc3b2673946a8670dd189e74) +* fix path to boost/variant.hpp on linux [view commit](http://github.com/mapbox/variant/commit/b31579c99042d36672fa9eefb09bcdb01e1bcc0a) +* move variant and friends into mapbox namespace for easy integration [view commit](http://github.com/mapbox/variant/commit/df55ab6ef48d1a28c58e409d71b3ee4b416cdf31) +* fix namespace [view commit](http://github.com/mapbox/variant/commit/397ed9c90bc55bbf2f3331f43a707789869d064a) +* inline accessors/setters [view commit](http://github.com/mapbox/variant/commit/2ebabd9b6cb1b95605b01708899498ac70b84201) +* default ctor : initialise with default contructed first type in parameters pack [view commit](http://github.com/mapbox/variant/commit/4d038f1462698c0977e14eacfd9abeb8da95c852) +* add default ctor test [view commit](http://github.com/mapbox/variant/commit/d7fa62b52f771f0d194441b5889409e792eff740) +* c++11 : use type aliases instead of typedefs [view commit](http://github.com/mapbox/variant/commit/03eb4c7d287b72eb43d2b6456ef38437c8c0ac34) +* converting operator= [view commit](http://github.com/mapbox/variant/commit/0eed7c3c1477e0673d379336b0745606d2780d8f) +* avoid wrapped object copying [view commit](http://github.com/mapbox/variant/commit/f7648ba392aea548fe0d583ceee2048ad57e2f54) +* fix move ctor + housekeeping [view commit](http://github.com/mapbox/variant/commit/6aee8c4a7543e0d38f64a07d67aaf394bea704d9) +* add [view commit](http://github.com/mapbox/variant/commit/82cc6e2335b377b5e61e9a3bc974e619658ab515) +* remove unused header [view commit](http://github.com/mapbox/variant/commit/74425f135a6b7cdd49d86d2dd84cf9bdfe9154c2) +* uncomment to test single threaded [view commit](http://github.com/mapbox/variant/commit/7296d18458e2dbfc7ddb39f0fe5b96962c264dc6) +* fix bug : assign correct index (in reverse order of args) e.g first type is sizeof...(Types) - 1 [view commit](http://github.com/mapbox/variant/commit/7eb748eb65729c2d91a7c4f16e5bd56eb3038bdd) +* fix default ctor unit test [view commit](http://github.com/mapbox/variant/commit/03af9e421b36e17a9b873cb24347f6bad7c3dc6d) +* [gyp] fix typo in Windows release targets [view commit](http://github.com/mapbox/variant/commit/2d8ca78704f69c6498a0e689222588275c8f33aa) +* add non-const visitor interface (#22) [view commit](http://github.com/mapbox/variant/commit/54d07c9d336c989fe3c1231bbb7f6465ebc68da2) +* add unary_visitor test [view commit](http://github.com/mapbox/variant/commit/3b31c34368613c4cd101e6f7ee9677c2c7b6b75e) +* support implicit type convertions [view commit](http://github.com/mapbox/variant/commit/724a40baec3ded142c631b66521502840d8d183f) +* update to use recursive_wrapper -> T conversions [view commit](http://github.com/mapbox/variant/commit/7e609812f15cbf41fab978fd3222c6029a5b67eb) +* unit test : update to use latest variant impl [view commit](http://github.com/mapbox/variant/commit/d1392fa431b5b5dac99cdb8b937ec05e13d98847) +* Mapbox name without the inner uppercase B. [view commit](http://github.com/mapbox/variant/commit/4e11d41723af714895c90cb8b9798527c946b2b4) +* Fix typo in comment. [view commit](http://github.com/mapbox/variant/commit/01509c76c30bea7a6bfffd0e59f9bbdbae8f1026) +* Formatting fixes. [view commit](http://github.com/mapbox/variant/commit/cfd7d991b23b2ecc659d18f22a2f78a05074450c) +* Use formatting "TYPE const&". [view commit](http://github.com/mapbox/variant/commit/9253ffdda65bd41ed21f9328a20a335fa0f5d582) +* Remove superfluous and inconsistent whitespace. [view commit](http://github.com/mapbox/variant/commit/d855ba8672fc5f439594325663899564f0632c94) +* Add comments for closing namespaces. [view commit](http://github.com/mapbox/variant/commit/3c2d662abb352bd429777251ddc613e751e49123) +* Merge branch 'joto-master' [view commit](http://github.com/mapbox/variant/commit/49e9f351a7a8d5615f8fb5ae2d4840042fc95bcc) +* Fix typos, whitespace and test tags. [view commit](http://github.com/mapbox/variant/commit/a12326984f323d10648115d94ace0b510d097b06) +* Add tests for implicit conversion and exceptions for wrong types. [view commit](http://github.com/mapbox/variant/commit/12c70938b00215ce86e48657d60a650dbfd8966e) +* Add test for printer visitor. [view commit](http://github.com/mapbox/variant/commit/fd470a6fde0941407a74c3d544c009eac0579e4f) +* Add test case for issue #25. [view commit](http://github.com/mapbox/variant/commit/74f1c5d9b02788fb9524ae674d43fdf07671d9cc) +* fix appveyor link in readme [view commit](http://github.com/mapbox/variant/commit/d49f82efb7689b8bc67be30633dd7f41b54f2e5a) +* Remove the need for the static_visitor class. [view commit](http://github.com/mapbox/variant/commit/e9283622a33231500c8cc3d1f27358d431c1e8a2) +* msvs: also define _DEBUG for debug builds [view commit](http://github.com/mapbox/variant/commit/c4bb359f883b568a88d5be8b3e4ce5c45626d71f) +* [appveyor] more debug flags [view commit](http://github.com/mapbox/variant/commit/b852c8d386e59a145a6b684d036ee7e327d0efe2) +* [appveyor][gyp] correct msvs_settings usage [view commit](http://github.com/mapbox/variant/commit/1f707864e37c305779e3398006b20e2ee5f14866) +* customize release builds [view commit](http://github.com/mapbox/variant/commit/4661a3c06b3738c7564c18474577d4faece6613a) +* [gyp][windows] add exception handling/rtti [view commit](http://github.com/mapbox/variant/commit/02556c9c317834ad3f05aa88dacc072ad9a63c99) +* use numeric values in valid range to avoid overflowing [view commit](http://github.com/mapbox/variant/commit/854c5a7a115d92b67698f3654d915edd483456b9) +* Removed wrong comments. [view commit](http://github.com/mapbox/variant/commit/8b6c0b34b76144d697ede0f386f1ea2f9c6ae2d1) +* Removed test case. [view commit](http://github.com/mapbox/variant/commit/08a7e04e3cf3ebd54e289201b1bd3d85225248ee) +* allow explicit un-initilised variant ctor [view commit](http://github.com/mapbox/variant/commit/d99139d4dee3ae4fa2b7465000d08fc5f5907e82) +* add boost::variant compatible accessors [view commit](http://github.com/mapbox/variant/commit/2afd3415cc28b8b7184e2750ac90e739c078b335) +* more verbose output to test script [view commit](http://github.com/mapbox/variant/commit/0c8b6b736788718ed28a0676cdb0b390249d5540) +* variant : make operator<< stricter to avoid unexpected instantiations [view commit](http://github.com/mapbox/variant/commit/76027798360405d910f8af9d6ae873fa5905be13) +* Add details on advantages of Mapbox variant [view commit](http://github.com/mapbox/variant/commit/a5ee02b2aacb0109725c611beaafc27dce89c12d) +* remove unneeded header [view commit](http://github.com/mapbox/variant/commit/73da70737f0e55b2276173ca923eb91bd8ae4f32) +* less debug info in release mode [view commit](http://github.com/mapbox/variant/commit/607ed1344f8117000e03da0b8bfb9268a1f826ee) +* rough cut of an optional type [view commit](http://github.com/mapbox/variant/commit/9badbd0fa37863240cf678be61116003d345a20a) +* add operator* and a static assert against reference types [view commit](http://github.com/mapbox/variant/commit/1141292eeed4a95f3e54c85e98f9ded180f84f32) +* Merge pull request #30 from DennisOSRM/optional [view commit](http://github.com/mapbox/variant/commit/32f971794c643d11f5bf374caef44cee295cdf7d) +* remove some whitespace [view commit](http://github.com/mapbox/variant/commit/f4bcf3ff733acbd4a798f1e9a5f80d5422bc9b79) +* explicit operator bool() const noexcept [view commit](http://github.com/mapbox/variant/commit/c77d98b3e8d06714ed0b0c288cd9ad457f2708c4) +* rename none_t -> none_type, move to private member of optional [view commit](http://github.com/mapbox/variant/commit/43e2e9a943555c09ce6ffb9c825a64bff229a3a6) +* remove instantiation of none_type as per @kkaefer's suggestion [view commit](http://github.com/mapbox/variant/commit/7242396fb5468defba37afd03e7a633075fa51ce) +* remove none_type and its complicated typedef from detail namespace, add it as private member class to optional [view commit](http://github.com/mapbox/variant/commit/c5f720515ad21fe3913fc841ea427ebdd1fa4c68) +* guard against self-assignment [view commit](http://github.com/mapbox/variant/commit/10a039c78b58857f8793dae3b0359cf1d6fb11ef) +* add unit tests for optional type [view commit](http://github.com/mapbox/variant/commit/607c6332cb96261a43a16fa94aea5d034ae58aac) +* Merge pull request #31 from mapbox/optional [view commit](http://github.com/mapbox/variant/commit/4cdd805a04175248b221753a0974de15c8f5b397) +* reformat optional.hpp to be more legible and less linty [view commit](http://github.com/mapbox/variant/commit/54e1dfe7631207da3f6a2bbc895c5240592ea5ea) +* universal-references: template variant(T && val) {} it turned out we don't need a separate converting copy ctor (fixes windows compiler) [view commit](http://github.com/mapbox/variant/commit/82df4ed9cd2a8335c7e50b913f827216604dab07) +* Merge pull request #32 from mapbox/universal-references [view commit](http://github.com/mapbox/variant/commit/804fb6f370a35eaea3e273ebfcb2f92ef7379e91) +* use std::forward for perfect forwarding (via @DennisOSRM) [view commit](http://github.com/mapbox/variant/commit/eb995158362307cea98e36eb782dd4ae63593d0f) +* fix assignment of optionals, adds a unit test [view commit](http://github.com/mapbox/variant/commit/f69f317069523cb6994fa9d594240deeb537753c) +* Merge pull request #33 from mapbox/fix_assignment_operator [view commit](http://github.com/mapbox/variant/commit/c56af229af98b71ba8ef637f3237cab828f0ec14) +* readme improvements [view commit](http://github.com/mapbox/variant/commit/5b8972b6ebcd095f35b810c43639785235218bbd) +* Merge branch 'master' into result_of [view commit](http://github.com/mapbox/variant/commit/4900027d0f66a7c701e22c667e92ce7b7521a73c) +* cast lhs to rhs type to avoid signed/unsigned comparisons warnings [view commit](http://github.com/mapbox/variant/commit/17074a3de539256ccab3871033df33c9d387dcbf) +* attempting to fix travis [view commit](http://github.com/mapbox/variant/commit/4fb9bd65ac4a204f8721f9528df9c14907a151f9) +* Merge branch 'master' into result_of [view commit](http://github.com/mapbox/variant/commit/fbcc50b57e58460e0cd90a232673271eff72311c) +* make deriving from static_visitor and providing result_type an option [view commit](http://github.com/mapbox/variant/commit/76ab6d4aa54664d7f8849695896ca57f4984bed0) +* Merge branch 'result_of' [view commit](http://github.com/mapbox/variant/commit/3cc2d708e0552ba4ebce11d4d67f798813f15aa2) +* fix automatic return_type calculation - ref #35 [view commit](http://github.com/mapbox/variant/commit/00ab88117ed25f78cdca2faa00beea0061271e85) +* test either g++ or clang++ [view commit](http://github.com/mapbox/variant/commit/199b3eca054075319a1978f09bdd77f9ff42e681) +* try adding code coverage / coveralls upload [view commit](http://github.com/mapbox/variant/commit/11e65282e335ed4a50bf261d21b8194230787ed8) +* fix bash syntax [view commit](http://github.com/mapbox/variant/commit/2fbfcd33998992df4d77f329e7a75c864616d0ab) +* add recursive wrapper to coverage [view commit](http://github.com/mapbox/variant/commit/f87a1cf10d62814d3bf4e72ac260a92e483ffb25) +* move operator<< into separate header [view commit](http://github.com/mapbox/variant/commit/24dcab23c4f70e54838e4a32a228aba8045ae17b) +* add coverage report [view commit](http://github.com/mapbox/variant/commit/89f8a41a4da81c2a01d39f83cf52e61997ce76ba) +* clean up coverage files in test directory too [skip ci] [view commit](http://github.com/mapbox/variant/commit/7dfdfa271e43780aff640852a632e88443b6fe32) +* add get() overloads for when T is stored in recursive_wrapper also makes get() a compile time error where T is not in Types... (ref #24) [view commit](http://github.com/mapbox/variant/commit/36f1e12f66570f2a1f04535dc0ac0485d48e2bbe) +* update unit test to match c64c74775a80474f2012c1a49ab2865e3666107a [view commit](http://github.com/mapbox/variant/commit/c117592337cc20c238d4ce9f9d8847aff0cd55ab) +* add which() method returning zero based index of stored T in Types... for boost::variant() compatibility [view commit](http://github.com/mapbox/variant/commit/3b02ca0e3ab1a36dd6ec9138e7f93eb3176ae5f7) +* add reference_wrapper test [view commit](http://github.com/mapbox/variant/commit/5a2d5c5f292ae2d6128d02e7ee2b3b3d72facfc2) +* remove boost variant header [view commit](http://github.com/mapbox/variant/commit/c53422fb2d4e44b7e276fcde65498b603f429a2f) +* add support for 'unwrapping' std::reference_wrapper and accessing std::reference_wrapper::type through get() + update test [view commit](http://github.com/mapbox/variant/commit/587519521ae0d9a24f997ab2dff77f13309aa5d2) +* pass F (functor) by ref/const ref [view commit](http://github.com/mapbox/variant/commit/2e0ce4a86d0b9d0ff9855838349fb599b15a274a) +* pass by const ref in 'apply_const' [view commit](http://github.com/mapbox/variant/commit/a3014f54651b71e25d81fbeaf99f29d63c625703) +* Revert "pass by const ref in 'apply_const'" [view commit](http://github.com/mapbox/variant/commit/e031c53d0c876ecf2b9d4a6e0a5383bd4169df71) +* Revert "pass F (functor) by ref/const ref" [view commit](http://github.com/mapbox/variant/commit/bf485dfb59aef26f3ef2183d7c8c1111ad97062b) diff --git a/third_party/variant/Jamroot b/third_party/variant/Jamroot index 3e863e25a..4e7a01f39 100644 --- a/third_party/variant/Jamroot +++ b/third_party/variant/Jamroot @@ -1,78 +1,62 @@ # Unofficial and incomplete build file using Boost build system. # You should use make unless you know what you are doing. -local BOOST_DIR = "/usr/local" ; +import os ; + +local boost_dir = [ os.environ BOOST_DIR ] ; +if ! $(boost_dir) +{ + boost_dir = "/usr/local" ; +} #using clang : : ; -lib system : : boost_system $(BOOST_DIR)/lib ; -lib timer : chrono : boost_timer $(BOOST_DIR)/lib ; -lib chrono : system : boost_chrono $(BOOST_DIR)/lib ; +local cxx_std = [ os.environ CXX_STD ] ; +if ! $(cxx_std) +{ + cxx_std = c++11 ; +} -exe variant-test - : - test/bench_variant.cpp - .//system - .//timer - .//chrono - : - $(BOOST_DIR)/include - ./include - ./test/include - #SINGLE_THREADED - release:"-march=native -Wweak-vtables" +project mapbox_variant + : requirements + -std=$(cxx_std) + $(boost_dir)/include + include + test/include + release:-march=native + single:SINGLE_THREADED + : default-build + release + speed + single ; +rule exe-test ( name : reqs * : deps * ) +{ + exe $(name) + : test/$(name).cpp + : $(reqs) + : $(deps) + ; + explicit $(name) ; +} -exe binary-visitor-test - : - test/binary_visitor_test.cpp - .//system - .//timer - .//chrono - : - $(BOOST_DIR)/include - ./include - ./test/include - release:-march=native +exe-test bench_variant + : release:-Wweak-vtables ; -exe recursive-wrapper-test - : - test/recursive_wrapper_test.cpp - .//system - .//timer - .//chrono - : - $(BOOST_DIR)/include - ./include - ./test/include - release:-march=native - ; +exe-test binary_visitor_test ; +exe-test recursive_wrapper_test ; +exe-test unique_ptr_test ; +exe-test reference_wrapper_test ; +exe-test lambda_overload_test ; +exe-test hashable_test ; -exe unique-ptr-test - : - test/unique_ptr_test.cpp - .//system - .//timer - .//chrono - : - $(BOOST_DIR)/include - ./include - ./test/include - release:-march=native - ; - - -exe reference_wrapper_test - : - test/reference_wrapper_test.cpp - .//system - .//timer - .//chrono - : - $(BOOST_DIR)/include - ./include - ./test/include - release:-march=native - ; +install out + : bench_variant + binary_visitor_test + unique_ptr_test + reference_wrapper_test + lambda_overload_test + hashable_test + ; diff --git a/third_party/variant/Makefile b/third_party/variant/Makefile index 10ab7b5f0..2559c0579 100644 --- a/third_party/variant/Makefile +++ b/third_party/variant/Makefile @@ -1,23 +1,53 @@ MASON = .mason/mason -BOOST_VERSION = boost 1.60.0 +BOOST_VERSION = 1.62.0 CXX := $(CXX) CXX_STD ?= c++11 -BOOST_FLAGS = `$(MASON) cflags $(BOOST_VERSION)` -RELEASE_FLAGS = -O3 -DNDEBUG -march=native -DSINGLE_THREADED -fvisibility-inlines-hidden -DEBUG_FLAGS = -O0 -g -DDEBUG -fno-inline-functions -COMMON_FLAGS = -Wall -pedantic -Wextra -Wsign-compare -Wsign-conversion -Wshadow -Wunused-parameter -std=$(CXX_STD) +BOOST_ROOT = $(shell $(MASON) prefix boost $(BOOST_VERSION)) +BOOST_FLAGS = -isystem $(BOOST_ROOT)/include/ +RELEASE_FLAGS = -O3 -DNDEBUG -march=native -DSINGLE_THREADED -fvisibility-inlines-hidden -fvisibility=hidden +DEBUG_FLAGS = -O0 -g -DDEBUG -fno-inline-functions -fno-omit-frame-pointer -fPIE +WARNING_FLAGS = -Werror -Wall -Wextra -pedantic \ + -Wformat=2 -Wsign-conversion -Wshadow -Wunused-parameter + +COMMON_FLAGS = -std=$(CXX_STD) +COMMON_FLAGS += $(WARNING_FLAGS) + CXXFLAGS := $(CXXFLAGS) LDFLAGS := $(LDFLAGS) +export BUILDTYPE ?= Release + +OS := $(shell uname -s) +ifeq ($(OS), Linux) + EXTRA_FLAGS = -pthread +endif +ifeq ($(OS), Darwin) + OSX_OLDEST_SUPPORTED ?= 10.7 + # we need to explicitly ask for libc++ otherwise the + # default will flip back to libstdc++ for mmacosx-version-min < 10.9 + EXTRA_FLAGS = -stdlib=libc++ -mmacosx-version-min=$(OSX_OLDEST_SUPPORTED) +endif + + +ifeq ($(BUILDTYPE),Release) + FINAL_CXXFLAGS := $(COMMON_FLAGS) $(RELEASE_FLAGS) $(CXXFLAGS) $(EXTRA_FLAGS) +else + FINAL_CXXFLAGS := $(COMMON_FLAGS) $(DEBUG_FLAGS) $(CXXFLAGS) $(EXTRA_FLAGS) +endif + + + ALL_HEADERS = $(shell find include/mapbox/ '(' -name '*.hpp' ')') all: out/bench-variant out/unique_ptr_test out/unique_ptr_test out/recursive_wrapper_test out/binary_visitor_test out/lambda_overload_test out/hashable_test -mason_packages: +$(MASON): git submodule update --init .mason - $(MASON) install $(BOOST_VERSION) + +mason_packages/headers/boost: $(MASON) + $(MASON) install boost $(BOOST_VERSION) ./deps/gyp: git clone --depth 1 https://chromium.googlesource.com/external/gyp.git ./deps/gyp @@ -25,35 +55,35 @@ mason_packages: gyp: ./deps/gyp deps/gyp/gyp --depth=. -Goutput_dir=./ --generator-output=./out -f make make V=1 -C ./out tests - ./out/Release/tests + ./out/$(BUILDTYPE)/tests -out/bench-variant-debug: Makefile mason_packages test/bench_variant.cpp +out/bench-variant-debug: Makefile mason_packages/headers/boost test/bench_variant.cpp mkdir -p ./out - $(CXX) -o out/bench-variant-debug test/bench_variant.cpp -I./include -Itest/include -pthreads $(DEBUG_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) + $(CXX) -o out/bench-variant-debug test/bench_variant.cpp -I./include -isystem test/include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) -out/bench-variant: Makefile mason_packages test/bench_variant.cpp +out/bench-variant: Makefile mason_packages/headers/boost test/bench_variant.cpp mkdir -p ./out - $(CXX) -o out/bench-variant test/bench_variant.cpp -I./include -Itest/include $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) + $(CXX) -o out/bench-variant test/bench_variant.cpp -I./include -isystem test/include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) -out/unique_ptr_test: Makefile mason_packages test/unique_ptr_test.cpp +out/unique_ptr_test: Makefile mason_packages/headers/boost test/unique_ptr_test.cpp mkdir -p ./out - $(CXX) -o out/unique_ptr_test test/unique_ptr_test.cpp -I./include -Itest/include $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) + $(CXX) -o out/unique_ptr_test test/unique_ptr_test.cpp -I./include -isystem test/include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) -out/recursive_wrapper_test: Makefile mason_packages test/recursive_wrapper_test.cpp +out/recursive_wrapper_test: Makefile mason_packages/headers/boost test/recursive_wrapper_test.cpp mkdir -p ./out - $(CXX) -o out/recursive_wrapper_test test/recursive_wrapper_test.cpp -I./include -Itest/include $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) + $(CXX) -o out/recursive_wrapper_test test/recursive_wrapper_test.cpp -I./include -isystem test/include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) -out/binary_visitor_test: Makefile mason_packages test/binary_visitor_test.cpp +out/binary_visitor_test: Makefile mason_packages/headers/boost test/binary_visitor_test.cpp mkdir -p ./out - $(CXX) -o out/binary_visitor_test test/binary_visitor_test.cpp -I./include -Itest/include $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) + $(CXX) -o out/binary_visitor_test test/binary_visitor_test.cpp -I./include -isystem test/include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) -out/lambda_overload_test: Makefile mason_packages test/lambda_overload_test.cpp +out/lambda_overload_test: Makefile mason_packages/headers/boost test/lambda_overload_test.cpp mkdir -p ./out - $(CXX) -o out/lambda_overload_test test/lambda_overload_test.cpp -I./include -Itest/include $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) + $(CXX) -o out/lambda_overload_test test/lambda_overload_test.cpp -I./include -isystem test/include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) -out/hashable_test: Makefile mason_packages test/hashable_test.cpp +out/hashable_test: Makefile mason_packages/headers/boost test/hashable_test.cpp mkdir -p ./out - $(CXX) -o out/hashable_test test/hashable_test.cpp -I./include -Itest/include $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) + $(CXX) -o out/hashable_test test/hashable_test.cpp -I./include -isystem test/include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) bench: out/bench-variant out/unique_ptr_test out/unique_ptr_test out/recursive_wrapper_test out/binary_visitor_test ./out/bench-variant 100000 @@ -63,13 +93,31 @@ bench: out/bench-variant out/unique_ptr_test out/unique_ptr_test out/recursive_w out/unit.o: Makefile test/unit.cpp mkdir -p ./out - $(CXX) -c -o $@ test/unit.cpp -Itest/include $(DEBUG_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) + $(CXX) -c -o $@ test/unit.cpp -isystem test/include $(FINAL_CXXFLAGS) out/%.o: test/t/%.cpp Makefile $(ALL_HEADERS) mkdir -p ./out - $(CXX) -c -o $@ $< -Iinclude -Itest/include $(DEBUG_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) + $(CXX) -c -o $@ $< -Iinclude -isystem test/include $(FINAL_CXXFLAGS) + +out/unit: out/unit.o \ + out/binary_visitor_1.o \ + out/binary_visitor_2.o \ + out/binary_visitor_3.o \ + out/binary_visitor_4.o \ + out/binary_visitor_5.o \ + out/binary_visitor_6.o \ + out/issue21.o \ + out/issue122.o \ + out/mutating_visitor.o \ + out/optional.o \ + out/recursive_wrapper.o \ + out/sizeof.o \ + out/unary_visitor.o \ + out/variant.o \ + out/variant_alternative.o \ + out/nothrow_move.o \ + out/visitor_result_type.o \ -out/unit: out/unit.o out/binary_visitor_1.o out/binary_visitor_2.o out/binary_visitor_3.o out/binary_visitor_4.o out/binary_visitor_5.o out/binary_visitor_6.o out/issue21.o out/issue122.o out/mutating_visitor.o out/optional.o out/recursive_wrapper.o out/sizeof.o out/unary_visitor.o out/variant.o mkdir -p ./out $(CXX) -o $@ $^ $(LDFLAGS) @@ -78,14 +126,14 @@ test: out/unit coverage: mkdir -p ./out - $(CXX) -o out/cov-test --coverage test/unit.cpp test/t/*.cpp -I./include -Itest/include $(DEBUG_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) + $(CXX) -o out/cov-test --coverage test/unit.cpp test/t/*.cpp -I./include -isystem test/include $(FINAL_CXXFLAGS) $(LDFLAGS) sizes: Makefile mkdir -p ./out - @$(CXX) -o ./out/our_variant_hello_world.out include/mapbox/variant.hpp -I./include $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) && du -h ./out/our_variant_hello_world.out - @$(CXX) -o ./out/boost_variant_hello_world.out `$(MASON) prefix boost 1.60.0`/include/boost/variant.hpp -I./include $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(BOOST_FLAGS) && du -h ./out/boost_variant_hello_world.out - @$(CXX) -o ./out/our_variant_hello_world ./test/our_variant_hello_world.cpp -I./include $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) && du -h ./out/our_variant_hello_world - @$(CXX) -o ./out/boost_variant_hello_world ./test/boost_variant_hello_world.cpp -I./include $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(BOOST_FLAGS) && du -h ./out/boost_variant_hello_world + @$(CXX) -o ./out/our_variant_hello_world.out include/mapbox/variant.hpp -I./include $(FINAL_CXXFLAGS) && ls -lah ./out/our_variant_hello_world.out + @$(CXX) -o ./out/boost_variant_hello_world.out $(BOOST_ROOT)/include/boost/variant.hpp -I./include $(FINAL_CXXFLAGS) $(BOOST_FLAGS) && ls -lah ./out/boost_variant_hello_world.out + @$(CXX) -o ./out/our_variant_hello_world ./test/our_variant_hello_world.cpp -I./include $(FINAL_CXXFLAGS) && ls -lah ./out/our_variant_hello_world + @$(CXX) -o ./out/boost_variant_hello_world ./test/boost_variant_hello_world.cpp -I./include $(FINAL_CXXFLAGS) $(BOOST_FLAGS) && ls -lah ./out/boost_variant_hello_world profile: out/bench-variant-debug mkdir -p profiling/ @@ -102,8 +150,8 @@ clean: rm -f *.gcda *.gcno pgo: out Makefile - $(CXX) -o out/bench-variant test/bench_variant.cpp -I./include $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) -pg -fprofile-generate + $(CXX) -o out/bench-variant test/bench_variant.cpp -I./include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) -pg -fprofile-generate ./test-variant 500000 >/dev/null 2>/dev/null - $(CXX) -o out/bench-variant test/bench_variant.cpp -I./include $(RELEASE_FLAGS) $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) -fprofile-use + $(CXX) -o out/bench-variant test/bench_variant.cpp -I./include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) -fprofile-use .PHONY: sizes test diff --git a/third_party/variant/README.md b/third_party/variant/README.md index 0e274b03b..6bf97f0c4 100644 --- a/third_party/variant/README.md +++ b/third_party/variant/README.md @@ -9,13 +9,13 @@ An header-only alternative to `boost::variant` for C++11 and C++14 ## Introduction Variant's basic building blocks are: + - `variant` - a type-safe representation for sum-types / discriminated unions - `recursive_wrapper` - a helper type to represent recursive "tree-like" variants - `apply_visitor(visitor, myVariant)` - to invoke a custom visitor on the variant's underlying type - `get()` - a function to directly unwrap a variant's underlying type - `.match([](Type){})` - a variant convenience member function taking an arbitrary number of lambdas creating a visitor behind the scenes and applying it to the variant - ### Basic Usage - HTTP API Example Suppose you want to represent a HTTP API response which is either a JSON result or an error: @@ -46,7 +46,7 @@ Response ret = makeRequest(); To see which type the `Response` holds you pattern match on the variant unwrapping the underlying value: ```c++ -ret.match([] (Result r) { print(r.object); } +ret.match([] (Result r) { print(r.object); }, [] (Error e) { print(e.message); }); ``` @@ -69,12 +69,10 @@ apply_visitor(visitor, ret); In both cases the compiler makes sure you handle all types the variant can represent at compile. - ### Recursive Variants - JSON Example [JSON](http://www.json.org/) consists of types `String`, `Number`, `True`, `False`, `Null`, `Array` and `Object`. - ```c++ struct String { string value; }; struct Number { double value; }; @@ -111,7 +109,7 @@ struct Object { }; ``` -For walkig the JSON representation you can again either create a `JSONVisitor`: +For walking the JSON representation you can again either create a `JSONVisitor`: ```c++ struct JSONVisitor { @@ -146,6 +144,7 @@ struct Node { uint64_t value; } ``` + ### Advanced Usage Tips Creating type aliases for variants is a great way to reduce repetition. @@ -164,7 +163,6 @@ struct APIResult : variant { } ``` - ## Why use Mapbox Variant? Mapbox variant has the same speedy performance of `boost::variant` but is @@ -180,7 +178,6 @@ Time to compile header | 185 ms | 675 ms (Numbers from an older version of Mapbox variant.) - ## Goals Mapbox `variant` has been a very valuable, lightweight alternative for apps @@ -206,24 +203,22 @@ Want to know more about the upcoming standard? Have a look at our Most modern high-level languages provide ways to express sum types directly. If you're curious have a look at Haskell's pattern matching or Rust's and Swift's enums. - ## Depends - - Compiler supporting `-std=c++11` or `-std=c++14` +- Compiler supporting `-std=c++11` or `-std=c++14` Tested with: - - g++-4.7 - - g++-4.8 - - g++-4.9 - - g++-5.2 - - clang++-3.5 - - clang++-3.6 - - clang++-3.7 - - clang++-3.8 - - clang++-3.9 - - Visual Studio 2015 - +- g++-4.7 +- g++-4.8 +- g++-4.9 +- g++-5.2 +- clang++-3.5 +- clang++-3.6 +- clang++-3.7 +- clang++-3.8 +- clang++-3.9 +- Visual Studio 2015 ## Unit Tests @@ -231,28 +226,23 @@ On Unix systems compile and run the unit tests with `make test`. On Windows run `scripts/build-local.bat`. - ## Limitations -* The `variant` can not hold references (something like `variant` is +- The `variant` can not hold references (something like `variant` is not possible). You might want to try `std::reference_wrapper` instead. - ## Deprecations -* The included implementation of `optional` is deprecated and will be removed - in a future version. See https://github.com/mapbox/variant/issues/64. -* Old versions of the code needed visitors to derive from `static_visitor`. +- The included implementation of `optional` is deprecated and will be removed + in a future version. See [issue #64](https://github.com/mapbox/variant/issues/64). +- Old versions of the code needed visitors to derive from `static_visitor`. This is not needed any more and marked as deprecated. The `static_visitor` class will be removed in future versions. - ## Benchmarks make bench - ## Check object sizes make sizes /path/to/boost/variant.hpp - diff --git a/third_party/variant/include/mapbox/variant.hpp b/third_party/variant/include/mapbox/variant.hpp index fb0f77e6d..06a46abe5 100644 --- a/third_party/variant/include/mapbox/variant.hpp +++ b/third_party/variant/include/mapbox/variant.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -74,19 +75,19 @@ public: }; // class bad_variant_access -template -struct MAPBOX_VARIANT_DEPRECATED static_visitor -{ - using result_type = R; - -protected: - static_visitor() {} - ~static_visitor() {} -}; +#if !defined(MAPBOX_VARIANT_MINIMIZE_SIZE) +using type_index_t = unsigned int; +#else +#if defined(MAPBOX_VARIANT_OPTIMIZE_FOR_SPEED) +using type_index_t = std::uint_fast8_t; +#else +using type_index_t = std::uint_least8_t; +#endif +#endif namespace detail { -static constexpr std::size_t invalid_value = std::size_t(-1); +static constexpr type_index_t invalid_value = type_index_t(-1); template struct direct_type; @@ -94,7 +95,7 @@ struct direct_type; template struct direct_type { - static constexpr std::size_t index = std::is_same::value + static constexpr type_index_t index = std::is_same::value ? sizeof...(Types) : direct_type::index; }; @@ -102,15 +103,28 @@ struct direct_type template struct direct_type { - static constexpr std::size_t index = invalid_value; + static constexpr type_index_t index = invalid_value; }; #if __cpp_lib_logical_traits >= 201510L +using std::conjunction; using std::disjunction; #else +template +struct conjunction : std::true_type {}; + +template +struct conjunction : B1 {}; + +template +struct conjunction : std::conditional::type {}; + +template +struct conjunction : std::conditional, B1>::type {}; + template struct disjunction : std::false_type {}; @@ -131,7 +145,7 @@ struct convertible_type; template struct convertible_type { - static constexpr std::size_t index = std::is_convertible::value + static constexpr type_index_t index = std::is_convertible::value ? disjunction...>::value ? invalid_value : sizeof...(Types) : convertible_type::index; }; @@ -139,64 +153,88 @@ struct convertible_type template struct convertible_type { - static constexpr std::size_t index = invalid_value; + static constexpr type_index_t index = invalid_value; }; template struct value_traits { using value_type = typename std::remove_const::type>::type; - static constexpr std::size_t direct_index = direct_type::index; + using value_type_wrapper = recursive_wrapper; + static constexpr type_index_t direct_index = direct_type::index; static constexpr bool is_direct = direct_index != invalid_value; - static constexpr std::size_t index = is_direct ? direct_index : convertible_type::index; + static constexpr type_index_t index_direct_or_wrapper = is_direct ? direct_index : direct_type::index; + static constexpr bool is_direct_or_wrapper = index_direct_or_wrapper != invalid_value; + static constexpr type_index_t index = is_direct_or_wrapper ? index_direct_or_wrapper : convertible_type::index; static constexpr bool is_valid = index != invalid_value; - static constexpr std::size_t tindex = is_valid ? sizeof...(Types)-index : 0; + static constexpr type_index_t tindex = is_valid ? sizeof...(Types)-index : 0; using target_type = typename std::tuple_element>::type; }; -template -struct enable_if_type +template +struct copy_cvref { - using type = R; + using type = Dest; }; -template -struct result_of_unary_visit +template +struct copy_cvref { - using type = typename std::result_of::type; + using type = Dest const&; }; -template -struct result_of_unary_visit::type> +template +struct copy_cvref { - using type = typename F::result_type; + using type = Dest&; }; -template -struct result_of_binary_visit +template +struct copy_cvref { - using type = typename std::result_of::type; + using type = Dest&&; }; -template -struct result_of_binary_visit::type> +template +struct deduced_result_type +{}; + +template +struct deduced_result_type()(std::declval()...))> { - using type = typename F::result_type; + using type = decltype(std::declval()(std::declval()...)); }; -template +template +struct visitor_result_type : deduced_result_type +{}; + +// specialization for explicit result_type member in visitor class +template +struct visitor_result_type::type::result_type>())> +{ + using type = typename std::decay::type::result_type; +}; + +template +using result_of_unary_visit = typename visitor_result_type::type; + +template +using result_of_binary_visit = typename visitor_result_type::type; + +template struct static_max; -template +template struct static_max { - static const std::size_t value = arg; + static const type_index_t value = arg; }; -template +template struct static_max { - static const std::size_t value = arg1 >= arg2 ? static_max::value : static_max::value; + static const type_index_t value = arg1 >= arg2 ? static_max::value : static_max::value; }; template @@ -205,7 +243,7 @@ struct variant_helper; template struct variant_helper { - VARIANT_INLINE static void destroy(const std::size_t type_index, void* data) + VARIANT_INLINE static void destroy(const type_index_t type_index, void* data) { if (type_index == sizeof...(Types)) { @@ -217,7 +255,7 @@ struct variant_helper } } - VARIANT_INLINE static void move(const std::size_t old_type_index, void* old_value, void* new_value) + VARIANT_INLINE static void move(const type_index_t old_type_index, void* old_value, void* new_value) { if (old_type_index == sizeof...(Types)) { @@ -229,7 +267,7 @@ struct variant_helper } } - VARIANT_INLINE static void copy(const std::size_t old_type_index, const void* old_value, void* new_value) + VARIANT_INLINE static void copy(const type_index_t old_type_index, const void* old_value, void* new_value) { if (old_type_index == sizeof...(Types)) { @@ -245,253 +283,182 @@ struct variant_helper template <> struct variant_helper<> { - VARIANT_INLINE static void destroy(const std::size_t, void*) {} - VARIANT_INLINE static void move(const std::size_t, void*, void*) {} - VARIANT_INLINE static void copy(const std::size_t, const void*, void*) {} + VARIANT_INLINE static void destroy(const type_index_t, void*) {} + VARIANT_INLINE static void move(const type_index_t, void*, void*) {} + VARIANT_INLINE static void copy(const type_index_t, const void*, void*) {} }; template struct unwrapper { - static T const& apply_const(T const& obj) { return obj; } - static T& apply(T& obj) { return obj; } -}; + using value_type = T; -template -struct unwrapper> -{ - static auto apply_const(recursive_wrapper const& obj) - -> typename recursive_wrapper::type const& + template + static auto apply(typename std::remove_reference::type& var) + -> typename std::enable_if::value, + decltype(var.template get_unchecked())>::type { - return obj.get(); + return var.template get_unchecked(); } - static auto apply(recursive_wrapper& obj) - -> typename recursive_wrapper::type& + + template + static auto apply(typename std::remove_reference::type& var) + -> typename std::enable_if::value, + decltype(std::move(var.template get_unchecked()))>::type { - return obj.get(); + return std::move(var.template get_unchecked()); } }; template -struct unwrapper> -{ - static auto apply_const(std::reference_wrapper const& obj) - -> typename std::reference_wrapper::type const& - { - return obj.get(); - } - static auto apply(std::reference_wrapper& obj) - -> typename std::reference_wrapper::type& - { - return obj.get(); - } -}; +struct unwrapper> : unwrapper +{}; -template +template +struct unwrapper> : unwrapper +{}; + +template struct dispatcher; -template -struct dispatcher +template +struct dispatcher { - VARIANT_INLINE static R apply_const(V const& v, F&& f) + template + VARIANT_INLINE static R apply(V&& v, F&& f) { if (v.template is()) { - return f(unwrapper::apply_const(v.template get_unchecked())); + return std::forward(f)(unwrapper::template apply(v)); } else { - return dispatcher::apply_const(v, std::forward(f)); - } - } - - VARIANT_INLINE static R apply(V& v, F&& f) - { - if (v.template is()) - { - return f(unwrapper::apply(v.template get_unchecked())); - } - else - { - return dispatcher::apply(v, std::forward(f)); + return dispatcher::apply(std::forward(v), std::forward(f)); } } }; -template -struct dispatcher +template +struct dispatcher { - VARIANT_INLINE static R apply_const(V const& v, F&& f) + template + VARIANT_INLINE static R apply(V&& v, F&& f) { - return f(unwrapper::apply_const(v.template get_unchecked())); - } - - VARIANT_INLINE static R apply(V& v, F&& f) - { - return f(unwrapper::apply(v.template get_unchecked())); + return std::forward(f)(unwrapper::template apply(v)); } }; -template +template struct binary_dispatcher_rhs; -template -struct binary_dispatcher_rhs +template +struct binary_dispatcher_rhs { - VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f) + template + VARIANT_INLINE static R apply(V&& lhs, V&& rhs, F&& f) { if (rhs.template is()) // call binary functor { - return f(unwrapper::apply_const(lhs.template get_unchecked()), - unwrapper::apply_const(rhs.template get_unchecked())); + return std::forward(f)(unwrapper::template apply(lhs), + unwrapper::template apply(rhs)); } else { - return binary_dispatcher_rhs::apply_const(lhs, rhs, std::forward(f)); - } - } - - VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f) - { - if (rhs.template is()) // call binary functor - { - return f(unwrapper::apply(lhs.template get_unchecked()), - unwrapper::apply(rhs.template get_unchecked())); - } - else - { - return binary_dispatcher_rhs::apply(lhs, rhs, std::forward(f)); + return binary_dispatcher_rhs::apply(std::forward(lhs), + std::forward(rhs), + std::forward(f)); } } }; -template -struct binary_dispatcher_rhs +template +struct binary_dispatcher_rhs { - VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f) + template + VARIANT_INLINE static R apply(V&& lhs, V&& rhs, F&& f) { - return f(unwrapper::apply_const(lhs.template get_unchecked()), - unwrapper::apply_const(rhs.template get_unchecked())); - } - - VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f) - { - return f(unwrapper::apply(lhs.template get_unchecked()), - unwrapper::apply(rhs.template get_unchecked())); + return std::forward(f)(unwrapper::template apply(lhs), + unwrapper::template apply(rhs)); } }; -template +template struct binary_dispatcher_lhs; -template -struct binary_dispatcher_lhs +template +struct binary_dispatcher_lhs { - VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f) + template + VARIANT_INLINE static R apply(V&& lhs, V&& rhs, F&& f) { if (lhs.template is()) // call binary functor { - return f(unwrapper::apply_const(lhs.template get_unchecked()), - unwrapper::apply_const(rhs.template get_unchecked())); + return std::forward(f)(unwrapper::template apply(lhs), + unwrapper::template apply(rhs)); } else { - return binary_dispatcher_lhs::apply_const(lhs, rhs, std::forward(f)); - } - } - - VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f) - { - if (lhs.template is()) // call binary functor - { - return f(unwrapper::apply(lhs.template get_unchecked()), - unwrapper::apply(rhs.template get_unchecked())); - } - else - { - return binary_dispatcher_lhs::apply(lhs, rhs, std::forward(f)); + return binary_dispatcher_lhs::apply(std::forward(lhs), + std::forward(rhs), + std::forward(f)); } } }; -template -struct binary_dispatcher_lhs +template +struct binary_dispatcher_lhs { - VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f) + template + VARIANT_INLINE static R apply(V&& lhs, V&& rhs, F&& f) { - return f(unwrapper::apply_const(lhs.template get_unchecked()), - unwrapper::apply_const(rhs.template get_unchecked())); - } - - VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f) - { - return f(unwrapper::apply(lhs.template get_unchecked()), - unwrapper::apply(rhs.template get_unchecked())); + return std::forward(f)(unwrapper::template apply(lhs), + unwrapper::template apply(rhs)); } }; -template +template struct binary_dispatcher; -template -struct binary_dispatcher +template +struct binary_dispatcher { - VARIANT_INLINE static R apply_const(V const& v0, V const& v1, F&& f) + template + VARIANT_INLINE static R apply(V&& v0, V&& v1, F&& f) { if (v0.template is()) { if (v1.template is()) { - return f(unwrapper::apply_const(v0.template get_unchecked()), - unwrapper::apply_const(v1.template get_unchecked())); // call binary functor + return std::forward(f)(unwrapper::template apply(v0), + unwrapper::template apply(v1)); // call binary functor } else { - return binary_dispatcher_rhs::apply_const(v0, v1, std::forward(f)); + return binary_dispatcher_rhs::apply(std::forward(v0), + std::forward(v1), + std::forward(f)); } } else if (v1.template is()) { - return binary_dispatcher_lhs::apply_const(v0, v1, std::forward(f)); + return binary_dispatcher_lhs::apply(std::forward(v0), + std::forward(v1), + std::forward(f)); } - return binary_dispatcher::apply_const(v0, v1, std::forward(f)); - } - - VARIANT_INLINE static R apply(V& v0, V& v1, F&& f) - { - if (v0.template is()) - { - if (v1.template is()) - { - return f(unwrapper::apply(v0.template get_unchecked()), - unwrapper::apply(v1.template get_unchecked())); // call binary functor - } - else - { - return binary_dispatcher_rhs::apply(v0, v1, std::forward(f)); - } - } - else if (v1.template is()) - { - return binary_dispatcher_lhs::apply(v0, v1, std::forward(f)); - } - return binary_dispatcher::apply(v0, v1, std::forward(f)); + return binary_dispatcher::apply(std::forward(v0), + std::forward(v1), + std::forward(f)); } }; -template -struct binary_dispatcher +template +struct binary_dispatcher { - VARIANT_INLINE static R apply_const(V const& v0, V const& v1, F&& f) + template + VARIANT_INLINE static R apply(V&& v0, V&& v1, F&& f) { - return f(unwrapper::apply_const(v0.template get_unchecked()), - unwrapper::apply_const(v1.template get_unchecked())); // call binary functor - } - - VARIANT_INLINE static R apply(V& v0, V& v1, F&& f) - { - return f(unwrapper::apply(v0.template get_unchecked()), - unwrapper::apply(v1.template get_unchecked())); // call binary functor + return std::forward(f)(unwrapper::template apply(v0), + unwrapper::template apply(v1)); // call binary functor } }; @@ -545,16 +512,15 @@ struct hasher } // namespace detail -struct no_init -{ -}; +struct no_init {}; template class variant { - static_assert(sizeof...(Types) > 0, "Template parameter type list of variant can not be empty"); + static_assert(sizeof...(Types) > 0, "Template parameter type list of variant can not be empty."); static_assert(!detail::disjunction...>::value, "Variant can not hold reference types. Maybe use std::reference_wrapper?"); - + static_assert(!detail::disjunction...>::value, "Variant can not hold array types."); + static_assert(sizeof...(Types) < std::numeric_limits::max(), "Internal index type must be able to accommodate all alternatives."); private: static const std::size_t data_size = detail::static_max::value; static const std::size_t data_align = detail::static_max::value; @@ -563,17 +529,25 @@ public: using types = std::tuple; private: using first_type = typename std::tuple_element<0, types>::type; + using unwrap_first_type = typename detail::unwrapper::value_type; using data_type = typename std::aligned_storage::type; using helper_type = detail::variant_helper; - std::size_t type_index; + template + using alternative_ref = typename detail::copy_cvref::type; + + type_index_t type_index; +#ifdef __clang_analyzer__ + data_type data {}; +#else data_type data; +#endif public: VARIANT_INLINE variant() noexcept(std::is_nothrow_default_constructible::value) : type_index(sizeof...(Types)-1) { - static_assert(std::is_default_constructible::value, "First type in variant must be default constructible to allow default construction of variant"); + static_assert(std::is_default_constructible::value, "First type in variant must be default constructible to allow default construction of variant."); new (&data) first_type(); } @@ -595,7 +569,8 @@ public: helper_type::copy(old.type_index, &old.data, &data); } - VARIANT_INLINE variant(variant&& old) noexcept(std::is_nothrow_move_constructible::value) + VARIANT_INLINE variant(variant&& old) + noexcept(detail::conjunction...>::value) : type_index(old.type_index) { helper_type::move(old.type_index, &old.data, &data); @@ -620,21 +595,34 @@ private: public: VARIANT_INLINE variant& operator=(variant&& other) + // note we check for nothrow-constructible, not nothrow-assignable, since + // move_assign uses move-construction via placement new. + noexcept(detail::conjunction...>::value) { + if (this == &other) { // playing safe in release mode, hit assertion in debug. + assert(false); + return *this; + } move_assign(std::move(other)); return *this; } VARIANT_INLINE variant& operator=(variant const& other) { - copy_assign(other); + if (this != &other) + copy_assign(other); return *this; } // conversions // move-assign - template - VARIANT_INLINE variant& operator=(T&& rhs) noexcept + template , + typename Enable = typename std::enable_if, typename Traits::value_type>::value>::type > + VARIANT_INLINE variant& operator=(T&& rhs) + // not that we check is_nothrow_constructible, not is_nothrow_move_assignable, + // since we construct a temporary + noexcept(std::is_nothrow_constructible::value + && std::is_nothrow_move_assignable>::value) { variant temp(std::forward(rhs)); move_assign(std::move(temp)); @@ -824,14 +812,14 @@ public: // This function is deprecated because it returns an internal index field. // Use which() instead. - MAPBOX_VARIANT_DEPRECATED VARIANT_INLINE std::size_t get_type_index() const + MAPBOX_VARIANT_DEPRECATED VARIANT_INLINE type_index_t get_type_index() const { return type_index; } VARIANT_INLINE int which() const noexcept { - return static_cast(sizeof...(Types)-type_index - 1); + return static_cast(sizeof...(Types) - type_index - 1); } template ::type> - auto VARIANT_INLINE static visit(V const& v, F&& f) - -> decltype(detail::dispatcher::apply_const(v, std::forward(f))) + template , + typename R = detail::result_of_unary_visit> + VARIANT_INLINE static R visit(V&& v, F&& f) { - return detail::dispatcher::apply_const(v, std::forward(f)); - } - // non-const - template ::type> - auto VARIANT_INLINE static visit(V& v, F&& f) - -> decltype(detail::dispatcher::apply(v, std::forward(f))) - { - return detail::dispatcher::apply(v, std::forward(f)); + return detail::dispatcher::apply(std::forward(v), std::forward(f)); } // binary - // const - template ::type> - auto VARIANT_INLINE static binary_visit(V const& v0, V const& v1, F&& f) - -> decltype(detail::binary_dispatcher::apply_const(v0, v1, std::forward(f))) + template , + typename R = detail::result_of_binary_visit> + VARIANT_INLINE static R binary_visit(V&& v0, V&& v1, F&& f) { - return detail::binary_dispatcher::apply_const(v0, v1, std::forward(f)); - } - // non-const - template ::type> - auto VARIANT_INLINE static binary_visit(V& v0, V& v1, F&& f) - -> decltype(detail::binary_dispatcher::apply(v0, v1, std::forward(f))) - { - return detail::binary_dispatcher::apply(v0, v1, std::forward(f)); + return detail::binary_dispatcher::apply(std::forward(v0), + std::forward(v1), + std::forward(f)); } // match // unary template - auto VARIANT_INLINE match(Fs&&... fs) const + auto VARIANT_INLINE match(Fs&&... fs) const& -> decltype(variant::visit(*this, ::mapbox::util::make_visitor(std::forward(fs)...))) { return variant::visit(*this, ::mapbox::util::make_visitor(std::forward(fs)...)); } // non-const template - auto VARIANT_INLINE match(Fs&&... fs) + auto VARIANT_INLINE match(Fs&&... fs) & -> decltype(variant::visit(*this, ::mapbox::util::make_visitor(std::forward(fs)...))) { return variant::visit(*this, ::mapbox::util::make_visitor(std::forward(fs)...)); } + template + auto VARIANT_INLINE match(Fs&&... fs) && + -> decltype(variant::visit(std::move(*this), ::mapbox::util::make_visitor(std::forward(fs)...))) + { + return variant::visit(std::move(*this), ::mapbox::util::make_visitor(std::forward(fs)...)); + } ~variant() noexcept // no-throw destructor { @@ -938,33 +919,19 @@ public: }; // unary visitor interface -// const template -auto VARIANT_INLINE apply_visitor(F&& f, V const& v) -> decltype(V::visit(v, std::forward(f))) +auto VARIANT_INLINE apply_visitor(F&& f, V&& v) + -> decltype(v.visit(std::forward(v), std::forward(f))) { - return V::visit(v, std::forward(f)); -} - -// non-const -template -auto VARIANT_INLINE apply_visitor(F&& f, V& v) -> decltype(V::visit(v, std::forward(f))) -{ - return V::visit(v, std::forward(f)); + return v.visit(std::forward(v), std::forward(f)); } // binary visitor interface -// const template -auto VARIANT_INLINE apply_visitor(F&& f, V const& v0, V const& v1) -> decltype(V::binary_visit(v0, v1, std::forward(f))) +auto VARIANT_INLINE apply_visitor(F&& f, V&& v0, V&& v1) + -> decltype(v0.binary_visit(std::forward(v0), std::forward(v1), std::forward(f))) { - return V::binary_visit(v0, v1, std::forward(f)); -} - -// non-const -template -auto VARIANT_INLINE apply_visitor(F&& f, V& v0, V& v1) -> decltype(V::binary_visit(v0, v1, std::forward(f))) -{ - return V::binary_visit(v0, v1, std::forward(f)); + return v0.binary_visit(std::forward(v0), std::forward(v1), std::forward(f)); } // getter interface @@ -996,6 +963,78 @@ ResultType const& get_unchecked(T const& var) { return var.template get_unchecked(); } +// variant_size +template +struct variant_size; + +//variable templates is c++14 +//template +//constexpr std::size_t variant_size_v = variant_size::value; + +template +struct variant_size + : variant_size {}; + +template +struct variant_size + : variant_size {}; + +template +struct variant_size + : variant_size {}; + +template +struct variant_size> + : std::integral_constant {}; + +// variant_alternative +template +struct variant_alternative; + +#if defined(__clang__) +#if __has_builtin(__type_pack_element) +#define has_type_pack_element +#endif +#endif + +#if defined(has_type_pack_element) +template +struct variant_alternative> +{ + static_assert(sizeof...(Types) > Index , "Index out of range"); + using type = __type_pack_element; +}; +#else +template +struct variant_alternative> + : variant_alternative> +{ + static_assert(sizeof...(Types) > Index -1 , "Index out of range"); +}; + +template +struct variant_alternative<0, variant> +{ + using type = First; +}; + +#endif + +template +using variant_alternative_t = typename variant_alternative::type; + +template +struct variant_alternative + : std::add_const> {}; + +template +struct variant_alternative + : std::add_volatile> {}; + +template +struct variant_alternative + : std::add_cv> {}; + } // namespace util } // namespace mapbox @@ -1008,6 +1047,7 @@ struct hash< ::mapbox::util::variant> { return ::mapbox::util::apply_visitor(::mapbox::util::detail::hasher{}, v); } }; + } #endif // MAPBOX_UTIL_VARIANT_HPP diff --git a/third_party/variant/include/mapbox/variant_cast.hpp b/third_party/variant/include/mapbox/variant_cast.hpp new file mode 100644 index 000000000..fe1ab3543 --- /dev/null +++ b/third_party/variant/include/mapbox/variant_cast.hpp @@ -0,0 +1,85 @@ +#ifndef VARIANT_CAST_HPP +#define VARIANT_CAST_HPP + +#include + +namespace mapbox { +namespace util { + +namespace detail { + +template +class static_caster +{ +public: + template + T& operator()(V& v) const + { + return static_cast(v); + } +}; + +template +class dynamic_caster +{ +public: + using result_type = T&; + template + T& operator()(V& v, typename std::enable_if::value>::type* = nullptr) const + { + throw std::bad_cast(); + } + template + T& operator()(V& v, typename std::enable_if::value>::type* = nullptr) const + { + return dynamic_cast(v); + } +}; + +template +class dynamic_caster +{ +public: + using result_type = T*; + template + T* operator()(V& v, typename std::enable_if::value>::type* = nullptr) const + { + return nullptr; + } + template + T* operator()(V& v, typename std::enable_if::value>::type* = nullptr) const + { + return dynamic_cast(&v); + } +}; +} + +template +typename detail::dynamic_caster::result_type +dynamic_variant_cast(V& v) +{ + return mapbox::util::apply_visitor(detail::dynamic_caster(), v); +} + +template +typename detail::dynamic_caster::result_type +dynamic_variant_cast(const V& v) +{ + return mapbox::util::apply_visitor(detail::dynamic_caster(), v); +} + +template +T& static_variant_cast(V& v) +{ + return mapbox::util::apply_visitor(detail::static_caster(), v); +} + +template +const T& static_variant_cast(const V& v) +{ + return mapbox::util::apply_visitor(detail::static_caster(), v); +} +} +} + +#endif // VARIANT_CAST_HPP diff --git a/third_party/variant/include/mapbox/variant_visitor.hpp b/third_party/variant/include/mapbox/variant_visitor.hpp index 481eb6520..54ddba0e1 100644 --- a/third_party/variant/include/mapbox/variant_visitor.hpp +++ b/third_party/variant/include/mapbox/variant_visitor.hpp @@ -1,6 +1,8 @@ #ifndef MAPBOX_UTIL_VARIANT_VISITOR_HPP #define MAPBOX_UTIL_VARIANT_VISITOR_HPP +#include + namespace mapbox { namespace util { @@ -10,28 +12,31 @@ struct visitor; template struct visitor : Fn { - using type = Fn; using Fn::operator(); - visitor(Fn fn) : Fn(fn) {} + template + visitor(T&& fn) : Fn(std::forward(fn)) {} }; template struct visitor : Fn, visitor { - using type = visitor; using Fn::operator(); using visitor::operator(); - visitor(Fn fn, Fns... fns) : Fn(fn), visitor(fns...) {} + template + visitor(T&& fn, Ts&&... fns) + : Fn(std::forward(fn)) + , visitor(std::forward(fns)...) {} }; template -visitor make_visitor(Fns... fns) +visitor::type...> make_visitor(Fns&&... fns) { - return visitor(fns...); + return visitor::type...> + (std::forward(fns)...); } - + } // namespace util } // namespace mapbox diff --git a/third_party/variant/package.json b/third_party/variant/package.json new file mode 100644 index 000000000..abca950f8 --- /dev/null +++ b/third_party/variant/package.json @@ -0,0 +1,10 @@ +{ + "name": "variant", + "version": "1.1.6", + "description": "C++11/C++14 variant", + "main": "./package.json", + "repository" : { + "type" : "git", + "url" : "git://github.com/mapbox/variant.git" + } +} diff --git a/third_party/variant/test/compilation_failure/get_type.cpp b/third_party/variant/test/compilation_failure/get_type.cpp index 490338926..744602fd5 100644 --- a/third_party/variant/test/compilation_failure/get_type.cpp +++ b/third_party/variant/test/compilation_failure/get_type.cpp @@ -1,4 +1,4 @@ -// @EXPECTED: enable_if +// @EXPECTED: no matching .*\ #include diff --git a/third_party/variant/test/compilation_failure/mutating_visitor_on_const.cpp b/third_party/variant/test/compilation_failure/mutating_visitor_on_const.cpp index 1706cbff5..eb0ed18ee 100644 --- a/third_party/variant/test/compilation_failure/mutating_visitor_on_const.cpp +++ b/third_party/variant/test/compilation_failure/mutating_visitor_on_const.cpp @@ -1,4 +1,4 @@ -// @EXPECTED: const int +// @EXPECTED: no matching function for call to .*\ #include diff --git a/third_party/variant/test/include/catch.hpp b/third_party/variant/test/include/catch.hpp index dd6e3ede8..f6c44e427 100644 --- a/third_party/variant/test/include/catch.hpp +++ b/third_party/variant/test/include/catch.hpp @@ -1,6 +1,6 @@ /* - * Catch v1.3.2 - * Generated: 2015-12-28 15:07:07.166291 + * Catch v1.9.0 + * Generated: 2017-04-07 22:51:48.249456 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. @@ -14,44 +14,46 @@ #define TWOBLUECUBES_CATCH_HPP_INCLUDED #ifdef __clang__ -#pragma clang system_header +# pragma clang system_header #elif defined __GNUC__ -#pragma GCC system_header +# pragma GCC system_header #endif // #included from: internal/catch_suppress_warnings.h #ifdef __clang__ -#ifdef __ICC // icpc defines the __clang__ macro -#pragma warning(push) -#pragma warning(disable : 161 1682) -#else // __ICC -#pragma clang diagnostic ignored "-Wglobal-constructors" -#pragma clang diagnostic ignored "-Wvariadic-macros" -#pragma clang diagnostic ignored "-Wc99-extensions" -#pragma clang diagnostic ignored "-Wunused-variable" -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -#pragma clang diagnostic ignored "-Wc++98-compat" -#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" -#pragma clang diagnostic ignored "-Wswitch-enum" -#pragma clang diagnostic ignored "-Wcovered-switch-default" -#endif +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(push) +# pragma warning(disable: 161 1682) +# else // __ICC +# pragma clang diagnostic ignored "-Wglobal-constructors" +# pragma clang diagnostic ignored "-Wvariadic-macros" +# pragma clang diagnostic ignored "-Wc99-extensions" +# pragma clang diagnostic ignored "-Wunused-variable" +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpadded" +# pragma clang diagnostic ignored "-Wc++98-compat" +# pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +# pragma clang diagnostic ignored "-Wswitch-enum" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +# endif #elif defined __GNUC__ -#pragma GCC diagnostic ignored "-Wvariadic-macros" -#pragma GCC diagnostic ignored "-Wunused-variable" -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpadded" +# pragma GCC diagnostic ignored "-Wvariadic-macros" +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wparentheses" + +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wpadded" #endif #if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) -#define CATCH_IMPL +# define CATCH_IMPL #endif #ifdef CATCH_IMPL -#ifndef CLARA_CONFIG_MAIN -#define CLARA_CONFIG_MAIN_NOT_DEFINED -#define CLARA_CONFIG_MAIN -#endif +# ifndef CLARA_CONFIG_MAIN +# define CLARA_CONFIG_MAIN_NOT_DEFINED +# define CLARA_CONFIG_MAIN +# endif #endif // #included from: internal/catch_notimplemented_exception.h @@ -60,17 +62,6 @@ // #included from: catch_common.h #define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED -#define INTERNAL_CATCH_UNIQUE_NAME_LINE2(name, line) name##line -#define INTERNAL_CATCH_UNIQUE_NAME_LINE(name, line) INTERNAL_CATCH_UNIQUE_NAME_LINE2(name, line) -#define INTERNAL_CATCH_UNIQUE_NAME(name) INTERNAL_CATCH_UNIQUE_NAME_LINE(name, __LINE__) - -#define INTERNAL_CATCH_STRINGIFY2(expr) #expr -#define INTERNAL_CATCH_STRINGIFY(expr) INTERNAL_CATCH_STRINGIFY2(expr) - -#include -#include -#include - // #included from: catch_compiler_capabilities.h #define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED @@ -85,11 +76,15 @@ // CATCH_CONFIG_CPP11_LONG_LONG : is long long supported? // CATCH_CONFIG_CPP11_OVERRIDE : is override supported? // CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) +// CATCH_CONFIG_CPP11_SHUFFLE : is std::shuffle supported? +// CATCH_CONFIG_CPP11_TYPE_TRAITS : are type_traits and enable_if supported? // CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported? // CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported? - +// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? // **************** // Note to maintainers: if new toggles are added please document them // in configuration.md, too @@ -102,18 +97,58 @@ // All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11 +#ifdef __cplusplus + +# if __cplusplus >= 201103L +# define CATCH_CPP11_OR_GREATER +# endif + +# if __cplusplus >= 201402L +# define CATCH_CPP14_OR_GREATER +# endif + +#endif + #ifdef __clang__ -#if __has_feature(cxx_nullptr) -#define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -#endif +# if __has_feature(cxx_nullptr) +# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR +# endif -#if __has_feature(cxx_noexcept) -#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -#endif +# if __has_feature(cxx_noexcept) +# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT +# endif + +# if defined(CATCH_CPP11_OR_GREATER) +# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic pop" ) +# endif #endif // __clang__ +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +# if !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# endif + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +# define _BSD_SOURCE + +#endif // __CYGWIN__ + //////////////////////////////////////////////////////////////////////////////// // Borland #ifdef __BORLANDC__ @@ -136,9 +171,9 @@ // GCC #ifdef __GNUC__ -#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) -#define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -#endif +# if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) +# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR +# endif // - otherwise more recent versions define __cplusplus >= 201103L // and will get picked up below @@ -149,14 +184,18 @@ // Visual C++ #ifdef _MSC_VER +#define CATCH_INTERNAL_CONFIG_WINDOWS_SEH + #if (_MSC_VER >= 1600) -#define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -#define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR +# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR +# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR #endif -#if (_MSC_VER >= 1900) // (VC++ 13 (VS2015)) +#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) #define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT #define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE +#define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS #endif #endif // _MSC_VER @@ -164,273 +203,313 @@ //////////////////////////////////////////////////////////////////////////////// // Use variadic macros if the compiler supports them -#if (defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \ - (defined __WAVE__ && __WAVE_HAS_VARIADICS) || \ - (defined __GNUC__ && __GNUC__ >= 3) || \ - (!defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L) +#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \ + ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \ + ( defined __GNUC__ && __GNUC__ >= 3 ) || \ + ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L ) #define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS #endif +// Use __COUNTER__ if the compiler supports it +#if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \ + ( defined __GNUC__ && __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 ) || \ + ( defined __clang__ && __clang_major__ >= 3 ) + +#define CATCH_INTERNAL_CONFIG_COUNTER + +#endif + //////////////////////////////////////////////////////////////////////////////// // C++ language feature support // catch all support for C++11 -#if defined(__cplusplus) && __cplusplus >= 201103L +#if defined(CATCH_CPP11_OR_GREATER) -#define CATCH_CPP11_OR_GREATER +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) +# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR +# endif -#if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) -#define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -#endif +# ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT +# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT +# endif -#ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -#endif +# ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +# define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +# endif -#ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#endif +# ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM +# define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM +# endif -#ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM -#define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM -#endif +# ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE +# define CATCH_INTERNAL_CONFIG_CPP11_TUPLE +# endif -#ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE -#define CATCH_INTERNAL_CONFIG_CPP11_TUPLE -#endif +# ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS +# define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS +# endif -#ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS -#define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS -#endif +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) +# define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG +# endif -#if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) -#define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG -#endif - -#if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) -#define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE -#endif -#if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) -#define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -#endif +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) +# define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE +# endif +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) +# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR +# endif +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) +# define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE +# endif +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) +# define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS +# endif #endif // __cplusplus >= 201103L // Now set the actual defines based on the above + anything the user has configured #if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11) -#define CATCH_CONFIG_CPP11_NULLPTR +# define CATCH_CONFIG_CPP11_NULLPTR #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11) -#define CATCH_CONFIG_CPP11_NOEXCEPT +# define CATCH_CONFIG_CPP11_NOEXCEPT #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11) -#define CATCH_CONFIG_CPP11_GENERATED_METHODS +# define CATCH_CONFIG_CPP11_GENERATED_METHODS #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11) -#define CATCH_CONFIG_CPP11_IS_ENUM +# define CATCH_CONFIG_CPP11_IS_ENUM #endif #if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11) -#define CATCH_CONFIG_CPP11_TUPLE +# define CATCH_CONFIG_CPP11_TUPLE #endif #if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS) -#define CATCH_CONFIG_VARIADIC_MACROS +# define CATCH_CONFIG_VARIADIC_MACROS #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11) -#define CATCH_CONFIG_CPP11_LONG_LONG +#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_LONG_LONG #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11) -#define CATCH_CONFIG_CPP11_OVERRIDE +#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_OVERRIDE #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) -#define CATCH_CONFIG_CPP11_UNIQUE_PTR +#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_UNIQUE_PTR +#endif +// Use of __COUNTER__ is suppressed if __JETBRAINS_IDE__ is #defined (meaning we're being parsed by a JetBrains IDE for +// analytics) because, at time of writing, __COUNTER__ is not properly handled by it. +// This does not affect compilation +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) && !defined(__JETBRAINS_IDE__) +# define CATCH_CONFIG_COUNTER +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_NO_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_SHUFFLE +#endif +# if defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_NO_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_TYPE_TRAITS +# endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS #endif // noexcept support: #if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT) -#define CATCH_NOEXCEPT noexcept -#define CATCH_NOEXCEPT_IS(x) noexcept(x) +# define CATCH_NOEXCEPT noexcept +# define CATCH_NOEXCEPT_IS(x) noexcept(x) #else -#define CATCH_NOEXCEPT throw() -#define CATCH_NOEXCEPT_IS(x) +# define CATCH_NOEXCEPT throw() +# define CATCH_NOEXCEPT_IS(x) #endif // nullptr support #ifdef CATCH_CONFIG_CPP11_NULLPTR -#define CATCH_NULL nullptr +# define CATCH_NULL nullptr #else -#define CATCH_NULL NULL +# define CATCH_NULL NULL #endif // override support #ifdef CATCH_CONFIG_CPP11_OVERRIDE -#define CATCH_OVERRIDE override +# define CATCH_OVERRIDE override #else -#define CATCH_OVERRIDE +# define CATCH_OVERRIDE #endif // unique_ptr support #ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR -#define CATCH_AUTO_PTR(T) std::unique_ptr +# define CATCH_AUTO_PTR( T ) std::unique_ptr #else -#define CATCH_AUTO_PTR(T) std::auto_ptr +# define CATCH_AUTO_PTR( T ) std::auto_ptr #endif +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr +#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) + +#include +#include + namespace Catch { -struct IConfig; + struct IConfig; -struct CaseSensitive -{ - enum Choice - { + struct CaseSensitive { enum Choice { Yes, No - }; -}; + }; }; -class NonCopyable -{ + class NonCopyable { #ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - NonCopyable(NonCopyable const&) = delete; - NonCopyable(NonCopyable&&) = delete; - NonCopyable& operator=(NonCopyable const&) = delete; - NonCopyable& operator=(NonCopyable&&) = delete; + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable& operator = ( NonCopyable const& ) = delete; + NonCopyable& operator = ( NonCopyable && ) = delete; #else - NonCopyable(NonCopyable const& info); - NonCopyable& operator=(NonCopyable const&); + NonCopyable( NonCopyable const& info ); + NonCopyable& operator = ( NonCopyable const& ); #endif - protected: - NonCopyable() {} - virtual ~NonCopyable(); -}; + protected: + NonCopyable() {} + virtual ~NonCopyable(); + }; -class SafeBool -{ - public: - typedef void (SafeBool::*type)() const; + class SafeBool { + public: + typedef void (SafeBool::*type)() const; - static type makeSafe(bool value) - { - return value ? &SafeBool::trueValue : 0; + static type makeSafe( bool value ) { + return value ? &SafeBool::trueValue : 0; + } + private: + void trueValue() const {} + }; + + template + inline void deleteAll( ContainerT& container ) { + typename ContainerT::const_iterator it = container.begin(); + typename ContainerT::const_iterator itEnd = container.end(); + for(; it != itEnd; ++it ) + delete *it; + } + template + inline void deleteAllValues( AssociativeContainerT& container ) { + typename AssociativeContainerT::const_iterator it = container.begin(); + typename AssociativeContainerT::const_iterator itEnd = container.end(); + for(; it != itEnd; ++it ) + delete it->second; } - private: - void trueValue() const {} -}; + bool startsWith( std::string const& s, std::string const& prefix ); + bool startsWith( std::string const& s, char prefix ); + bool endsWith( std::string const& s, std::string const& suffix ); + bool endsWith( std::string const& s, char suffix ); + bool contains( std::string const& s, std::string const& infix ); + void toLowerInPlace( std::string& s ); + std::string toLower( std::string const& s ); + std::string trim( std::string const& str ); + bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); -template -inline void deleteAll(ContainerT& container) -{ - typename ContainerT::const_iterator it = container.begin(); - typename ContainerT::const_iterator itEnd = container.end(); - for (; it != itEnd; ++it) - delete *it; -} -template -inline void deleteAllValues(AssociativeContainerT& container) -{ - typename AssociativeContainerT::const_iterator it = container.begin(); - typename AssociativeContainerT::const_iterator itEnd = container.end(); - for (; it != itEnd; ++it) - delete it->second; -} + struct pluralise { + pluralise( std::size_t count, std::string const& label ); -bool startsWith(std::string const& s, std::string const& prefix); -bool endsWith(std::string const& s, std::string const& suffix); -bool contains(std::string const& s, std::string const& infix); -void toLowerInPlace(std::string& s); -std::string toLower(std::string const& s); -std::string trim(std::string const& str); -bool replaceInPlace(std::string& str, std::string const& replaceThis, std::string const& withThis); + friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); -struct pluralise -{ - pluralise(std::size_t count, std::string const& label); + std::size_t m_count; + std::string m_label; + }; - friend std::ostream& operator<<(std::ostream& os, pluralise const& pluraliser); + struct SourceLineInfo { - std::size_t m_count; - std::string m_label; -}; + SourceLineInfo(); + SourceLineInfo( char const* _file, std::size_t _line ); +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + SourceLineInfo(SourceLineInfo const& other) = default; + SourceLineInfo( SourceLineInfo && ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo& operator = ( SourceLineInfo && ) = default; +# endif + bool empty() const; + bool operator == ( SourceLineInfo const& other ) const; + bool operator < ( SourceLineInfo const& other ) const; -struct SourceLineInfo -{ + char const* file; + std::size_t line; + }; - SourceLineInfo(); - SourceLineInfo(char const* _file, std::size_t _line); - SourceLineInfo(SourceLineInfo const& other); -#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - SourceLineInfo(SourceLineInfo&&) = default; - SourceLineInfo& operator=(SourceLineInfo const&) = default; - SourceLineInfo& operator=(SourceLineInfo&&) = default; -#endif - bool empty() const; - bool operator==(SourceLineInfo const& other) const; - bool operator<(SourceLineInfo const& other) const; + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); - std::string file; - std::size_t line; -}; + // This is just here to avoid compiler warnings with macro constants and boolean literals + inline bool isTrue( bool value ){ return value; } + inline bool alwaysTrue() { return true; } + inline bool alwaysFalse() { return false; } -std::ostream& operator<<(std::ostream& os, SourceLineInfo const& info); + void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ); -// This is just here to avoid compiler warnings with macro constants and boolean literals -inline bool isTrue(bool value) { return value; } -inline bool alwaysTrue() { return true; } -inline bool alwaysFalse() { return false; } + void seedRng( IConfig const& config ); + unsigned int rngSeed(); -void throwLogicError(std::string const& message, SourceLineInfo const& locationInfo); - -void seedRng(IConfig const& config); -unsigned int rngSeed(); - -// Use this in variadic streaming macros to allow -// >> +StreamEndStop -// as well as -// >> stuff +StreamEndStop -struct StreamEndStop -{ - std::string operator+() - { - return std::string(); + // Use this in variadic streaming macros to allow + // >> +StreamEndStop + // as well as + // >> stuff +StreamEndStop + struct StreamEndStop { + std::string operator+() { + return std::string(); + } + }; + template + T const& operator + ( T const& value, StreamEndStop ) { + return value; } -}; -template -T const& operator+(T const& value, StreamEndStop) -{ - return value; -} } -#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo(__FILE__, static_cast(__LINE__)) -#define CATCH_INTERNAL_ERROR(msg) ::Catch::throwLogicError(msg, CATCH_INTERNAL_LINEINFO); - -#include +#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) +#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO ); namespace Catch { -class NotImplementedException : public std::exception -{ - public: - NotImplementedException(SourceLineInfo const& lineInfo); - NotImplementedException(NotImplementedException const&) {} + class NotImplementedException : public std::exception + { + public: + NotImplementedException( SourceLineInfo const& lineInfo ); + NotImplementedException( NotImplementedException const& ) {} - virtual ~NotImplementedException() CATCH_NOEXCEPT {} + virtual ~NotImplementedException() CATCH_NOEXCEPT {} - virtual const char* what() const CATCH_NOEXCEPT; + virtual const char* what() const CATCH_NOEXCEPT; - private: - std::string m_what; - SourceLineInfo m_lineInfo; -}; + private: + std::string m_what; + SourceLineInfo m_lineInfo; + }; } // end namespace Catch /////////////////////////////////////////////////////////////////////////////// -#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException(CATCH_INTERNAL_LINEINFO) +#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO ) // #included from: internal/catch_context.h #define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED @@ -442,22 +521,20 @@ class NotImplementedException : public std::exception namespace Catch { -struct IGeneratorInfo -{ - virtual ~IGeneratorInfo(); - virtual bool moveNext() = 0; - virtual std::size_t getCurrentIndex() const = 0; -}; + struct IGeneratorInfo { + virtual ~IGeneratorInfo(); + virtual bool moveNext() = 0; + virtual std::size_t getCurrentIndex() const = 0; + }; -struct IGeneratorsForTest -{ - virtual ~IGeneratorsForTest(); + struct IGeneratorsForTest { + virtual ~IGeneratorsForTest(); - virtual IGeneratorInfo& getGeneratorInfo(std::string const& fileInfo, std::size_t size) = 0; - virtual bool moveNext() = 0; -}; + virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0; + virtual bool moveNext() = 0; + }; -IGeneratorsForTest* createGeneratorsForTest(); + IGeneratorsForTest* createGeneratorsForTest(); } // end namespace Catch @@ -471,83 +548,72 @@ IGeneratorsForTest* createGeneratorsForTest(); namespace Catch { -// An intrusive reference counting smart pointer. -// T must implement addRef() and release() methods -// typically implementing the IShared interface -template -class Ptr -{ - public: - Ptr() : m_p(CATCH_NULL) {} - Ptr(T* p) : m_p(p) - { - if (m_p) - m_p->addRef(); - } - Ptr(Ptr const& other) : m_p(other.m_p) - { - if (m_p) - m_p->addRef(); - } - ~Ptr() - { - if (m_p) - m_p->release(); - } - void reset() - { - if (m_p) - m_p->release(); - m_p = CATCH_NULL; - } - Ptr& operator=(T* p) - { - Ptr temp(p); - swap(temp); - return *this; - } - Ptr& operator=(Ptr const& other) - { - Ptr temp(other); - swap(temp); - return *this; - } - void swap(Ptr& other) { std::swap(m_p, other.m_p); } - T* get() const { return m_p; } - T& operator*() const { return *m_p; } - T* operator->() const { return m_p; } - bool operator!() const { return m_p == CATCH_NULL; } - operator SafeBool::type() const { return SafeBool::makeSafe(m_p != CATCH_NULL); } + // An intrusive reference counting smart pointer. + // T must implement addRef() and release() methods + // typically implementing the IShared interface + template + class Ptr { + public: + Ptr() : m_p( CATCH_NULL ){} + Ptr( T* p ) : m_p( p ){ + if( m_p ) + m_p->addRef(); + } + Ptr( Ptr const& other ) : m_p( other.m_p ){ + if( m_p ) + m_p->addRef(); + } + ~Ptr(){ + if( m_p ) + m_p->release(); + } + void reset() { + if( m_p ) + m_p->release(); + m_p = CATCH_NULL; + } + Ptr& operator = ( T* p ){ + Ptr temp( p ); + swap( temp ); + return *this; + } + Ptr& operator = ( Ptr const& other ){ + Ptr temp( other ); + swap( temp ); + return *this; + } + void swap( Ptr& other ) { std::swap( m_p, other.m_p ); } + T* get() const{ return m_p; } + T& operator*() const { return *m_p; } + T* operator->() const { return m_p; } + bool operator !() const { return m_p == CATCH_NULL; } + operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_NULL ); } - private: - T* m_p; -}; + private: + T* m_p; + }; -struct IShared : NonCopyable -{ - virtual ~IShared(); - virtual void addRef() const = 0; - virtual void release() const = 0; -}; + struct IShared : NonCopyable { + virtual ~IShared(); + virtual void addRef() const = 0; + virtual void release() const = 0; + }; -template -struct SharedImpl : T -{ + template + struct SharedImpl : T { - SharedImpl() : m_rc(0) {} + SharedImpl() : m_rc( 0 ){} - virtual void addRef() const - { - ++m_rc; - } - virtual void release() const - { - if (--m_rc == 0) - delete this; - } + virtual void addRef() const { + ++m_rc; + } + virtual void release() const { + if( --m_rc == 0 ) + delete this; + } - mutable unsigned int m_rc; -}; + mutable unsigned int m_rc; + }; } // end namespace Catch @@ -555,42 +621,39 @@ struct SharedImpl : T #pragma clang diagnostic pop #endif -#include -#include -#include - namespace Catch { -class TestCase; -class Stream; -struct IResultCapture; -struct IRunner; -struct IGeneratorsForTest; -struct IConfig; + class TestCase; + class Stream; + struct IResultCapture; + struct IRunner; + struct IGeneratorsForTest; + struct IConfig; -struct IContext -{ - virtual ~IContext(); + struct IContext + { + virtual ~IContext(); - virtual IResultCapture* getResultCapture() = 0; - virtual IRunner* getRunner() = 0; - virtual size_t getGeneratorIndex(std::string const& fileInfo, size_t totalSize) = 0; - virtual bool advanceGeneratorsForCurrentTest() = 0; - virtual Ptr getConfig() const = 0; -}; + virtual IResultCapture* getResultCapture() = 0; + virtual IRunner* getRunner() = 0; + virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0; + virtual bool advanceGeneratorsForCurrentTest() = 0; + virtual Ptr getConfig() const = 0; + }; -struct IMutableContext : IContext -{ - virtual ~IMutableContext(); - virtual void setResultCapture(IResultCapture* resultCapture) = 0; - virtual void setRunner(IRunner* runner) = 0; - virtual void setConfig(Ptr const& config) = 0; -}; + struct IMutableContext : IContext + { + virtual ~IMutableContext(); + virtual void setResultCapture( IResultCapture* resultCapture ) = 0; + virtual void setRunner( IRunner* runner ) = 0; + virtual void setConfig( Ptr const& config ) = 0; + }; + + IContext& getCurrentContext(); + IMutableContext& getCurrentMutableContext(); + void cleanUpContext(); + Stream createStream( std::string const& streamName ); -IContext& getCurrentContext(); -IMutableContext& getCurrentMutableContext(); -void cleanUpContext(); -Stream createStream(std::string const& streamName); } // #included from: internal/catch_test_registry.hpp @@ -603,162 +666,174 @@ Stream createStream(std::string const& streamName); namespace Catch { -class TestSpec; + class TestSpec; -struct ITestCase : IShared -{ - virtual void invoke() const = 0; + struct ITestCase : IShared { + virtual void invoke () const = 0; + protected: + virtual ~ITestCase(); + }; - protected: - virtual ~ITestCase(); -}; + class TestCase; + struct IConfig; -class TestCase; -struct IConfig; + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; + }; -struct ITestCaseRegistry -{ - virtual ~ITestCaseRegistry(); - virtual std::vector const& getAllTests() const = 0; - virtual std::vector const& getAllTestsSorted(IConfig const& config) const = 0; -}; + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); -bool matchTest(TestCase const& testCase, TestSpec const& testSpec, IConfig const& config); -std::vector filterTests(std::vector const& testCases, TestSpec const& testSpec, IConfig const& config); -std::vector const& getAllTestCasesSorted(IConfig const& config); } namespace Catch { -template -class MethodTestCase : public SharedImpl -{ +template +class MethodTestCase : public SharedImpl { - public: - MethodTestCase(void (C::*method)()) : m_method(method) {} +public: + MethodTestCase( void (C::*method)() ) : m_method( method ) {} - virtual void invoke() const - { + virtual void invoke() const { C obj; (obj.*m_method)(); } - private: +private: virtual ~MethodTestCase() {} void (C::*m_method)(); }; -typedef void (*TestFunction)(); +typedef void(*TestFunction)(); -struct NameAndDesc -{ - NameAndDesc(const char* _name = "", const char* _description = "") - : name(_name), description(_description) - { - } +struct NameAndDesc { + NameAndDesc( const char* _name = "", const char* _description= "" ) + : name( _name ), description( _description ) + {} const char* name; const char* description; }; -void registerTestCase(ITestCase* testCase, - char const* className, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo); +void registerTestCase + ( ITestCase* testCase, + char const* className, + NameAndDesc const& nameAndDesc, + SourceLineInfo const& lineInfo ); -struct AutoReg -{ +struct AutoReg { - AutoReg(TestFunction function, + AutoReg + ( TestFunction function, SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc); + NameAndDesc const& nameAndDesc ); - template - AutoReg(void (C::*method)(), + template + AutoReg + ( void (C::*method)(), char const* className, NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo) - { + SourceLineInfo const& lineInfo ) { - registerTestCase(new MethodTestCase(method), - className, - nameAndDesc, - lineInfo); + registerTestCase + ( new MethodTestCase( method ), + className, + nameAndDesc, + lineInfo ); } ~AutoReg(); - private: - AutoReg(AutoReg const&); - void operator=(AutoReg const&); +private: + AutoReg( AutoReg const& ); + void operator= ( AutoReg const& ); }; -void registerTestCaseFunction(TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc); +void registerTestCaseFunction + ( TestFunction function, + SourceLineInfo const& lineInfo, + NameAndDesc const& nameAndDesc ); } // end namespace Catch #ifdef CATCH_CONFIG_VARIADIC_MACROS -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_TESTCASE(...) \ - static void INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)(); \ - namespace { \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)(&INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc(__VA_ARGS__)); \ - } \ - static void INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)() + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ + static void TestName(); \ + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); } \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ + static void TestName() + #define INTERNAL_CATCH_TESTCASE( ... ) \ + INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ ) -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_METHOD_AS_TEST_CASE(QualifiedMethod, ...) \ - namespace { \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)(&QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc(__VA_ARGS__), CATCH_INTERNAL_LINEINFO); \ - } + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_TEST_CASE_METHOD(ClassName, ...) \ - namespace { \ - struct INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____) : ClassName \ - { \ - void test(); \ - }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)(&INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)::test, #ClassName, Catch::NameAndDesc(__VA_ARGS__), CATCH_INTERNAL_LINEINFO); \ - } \ - void INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)::test() + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + namespace{ \ + struct TestName : ClassName{ \ + void test(); \ + }; \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \ + } \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ + void TestName::test() + #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ + INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ ) -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_REGISTER_TESTCASE(Function, ...) \ - Catch::AutoReg(Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc(__VA_ARGS__)); + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS #else -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_TESTCASE(Name, Desc) \ - static void INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)(); \ - namespace { \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)(&INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc(Name, Desc)); \ - } \ - static void INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)() + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \ + static void TestName(); \ + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ + static void TestName() + #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ + INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc ) -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_METHOD_AS_TEST_CASE(QualifiedMethod, Name, Desc) \ - namespace { \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)(&QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc(Name, Desc), CATCH_INTERNAL_LINEINFO); \ - } + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \ + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_TEST_CASE_METHOD(ClassName, TestName, Desc) \ - namespace { \ - struct INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____) : ClassName \ - { \ - void test(); \ - }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)(&INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)::test, #ClassName, Catch::NameAndDesc(TestName, Desc), CATCH_INTERNAL_LINEINFO); \ - } \ - void INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)::test() + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\ + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + namespace{ \ + struct TestCaseName : ClassName{ \ + void test(); \ + }; \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \ + } \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ + void TestCaseName::test() + #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ + INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc ) + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \ + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_REGISTER_TESTCASE(Function, Name, Desc) \ - Catch::AutoReg(Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc(Name, Desc)); #endif // #included from: internal/catch_capture.hpp @@ -772,11 +847,8 @@ void registerTestCaseFunction(TestFunction function, namespace Catch { -// ResultWas::OfType enum -struct ResultWas -{ - enum OfType - { + // ResultWas::OfType enum + struct ResultWas { enum OfType { Unknown = -1, Ok = 0, Info = 1, @@ -794,39 +866,31 @@ struct ResultWas FatalErrorCondition = 0x200 | FailureBit - }; -}; + }; }; -inline bool isOk(ResultWas::OfType resultType) -{ - return (resultType & ResultWas::FailureBit) == 0; -} -inline bool isJustInfo(int flags) -{ - return flags == ResultWas::Info; -} + inline bool isOk( ResultWas::OfType resultType ) { + return ( resultType & ResultWas::FailureBit ) == 0; + } + inline bool isJustInfo( int flags ) { + return flags == ResultWas::Info; + } -// ResultDisposition::Flags enum -struct ResultDisposition -{ - enum Flags - { + // ResultDisposition::Flags enum + struct ResultDisposition { enum Flags { Normal = 0x01, - ContinueOnFailure = 0x02, // Failures fail test, but execution continues - FalseTest = 0x04, // Prefix expression with ! - SuppressFail = 0x08 // Failures are reported but do not fail the test - }; -}; + ContinueOnFailure = 0x02, // Failures fail test, but execution continues + FalseTest = 0x04, // Prefix expression with ! + SuppressFail = 0x08 // Failures are reported but do not fail the test + }; }; -inline ResultDisposition::Flags operator|(ResultDisposition::Flags lhs, ResultDisposition::Flags rhs) -{ - return static_cast(static_cast(lhs) | static_cast(rhs)); -} + inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { + return static_cast( static_cast( lhs ) | static_cast( rhs ) ); + } -inline bool shouldContinueOnFailure(int flags) { return (flags & ResultDisposition::ContinueOnFailure) != 0; } -inline bool isFalseTest(int flags) { return (flags & ResultDisposition::FalseTest) != 0; } -inline bool shouldSuppressFailure(int flags) { return (flags & ResultDisposition::SuppressFail) != 0; } + inline bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } + inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } + inline bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } } // end namespace Catch @@ -837,59 +901,114 @@ inline bool shouldSuppressFailure(int flags) { return (flags & ResultDisposition namespace Catch { -struct AssertionInfo -{ - AssertionInfo() {} - AssertionInfo(std::string const& _macroName, - SourceLineInfo const& _lineInfo, - std::string const& _capturedExpression, - ResultDisposition::Flags _resultDisposition); + struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; - std::string macroName; - SourceLineInfo lineInfo; - std::string capturedExpression; - ResultDisposition::Flags resultDisposition; -}; + struct DecomposedExpression + { + virtual ~DecomposedExpression() {} + virtual bool isBinaryExpression() const { + return false; + } + virtual void reconstructExpression( std::string& dest ) const = 0; -struct AssertionResultData -{ - AssertionResultData() : resultType(ResultWas::Unknown) {} + // Only simple binary comparisons can be decomposed. + // If more complex check is required then wrap sub-expressions in parentheses. + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& ); - std::string reconstructedExpression; - std::string message; - ResultWas::OfType resultType; -}; + private: + DecomposedExpression& operator = (DecomposedExpression const&); + }; -class AssertionResult -{ - public: - AssertionResult(); - AssertionResult(AssertionInfo const& info, AssertionResultData const& data); - ~AssertionResult(); -#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - AssertionResult(AssertionResult const&) = default; - AssertionResult(AssertionResult&&) = default; - AssertionResult& operator=(AssertionResult const&) = default; - AssertionResult& operator=(AssertionResult&&) = default; -#endif + struct AssertionInfo + { + AssertionInfo() {} + AssertionInfo( std::string const& _macroName, + SourceLineInfo const& _lineInfo, + std::string const& _capturedExpression, + ResultDisposition::Flags _resultDisposition ); - bool isOk() const; - bool succeeded() const; - ResultWas::OfType getResultType() const; - bool hasExpression() const; - bool hasMessage() const; - std::string getExpression() const; - std::string getExpressionInMacro() const; - bool hasExpandedExpression() const; - std::string getExpandedExpression() const; - std::string getMessage() const; - SourceLineInfo getSourceInfo() const; - std::string getTestMacroName() const; + std::string macroName; + SourceLineInfo lineInfo; + std::string capturedExpression; + ResultDisposition::Flags resultDisposition; + }; - protected: - AssertionInfo m_info; - AssertionResultData m_resultData; -}; + struct AssertionResultData + { + AssertionResultData() : decomposedExpression( CATCH_NULL ) + , resultType( ResultWas::Unknown ) + , negated( false ) + , parenthesized( false ) {} + + void negate( bool parenthesize ) { + negated = !negated; + parenthesized = parenthesize; + if( resultType == ResultWas::Ok ) + resultType = ResultWas::ExpressionFailed; + else if( resultType == ResultWas::ExpressionFailed ) + resultType = ResultWas::Ok; + } + + std::string const& reconstructExpression() const { + if( decomposedExpression != CATCH_NULL ) { + decomposedExpression->reconstructExpression( reconstructedExpression ); + if( parenthesized ) { + reconstructedExpression.insert( 0, 1, '(' ); + reconstructedExpression.append( 1, ')' ); + } + if( negated ) { + reconstructedExpression.insert( 0, 1, '!' ); + } + decomposedExpression = CATCH_NULL; + } + return reconstructedExpression; + } + + mutable DecomposedExpression const* decomposedExpression; + mutable std::string reconstructedExpression; + std::string message; + ResultWas::OfType resultType; + bool negated; + bool parenthesized; + }; + + class AssertionResult { + public: + AssertionResult(); + AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); + ~AssertionResult(); +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + AssertionResult( AssertionResult const& ) = default; + AssertionResult( AssertionResult && ) = default; + AssertionResult& operator = ( AssertionResult const& ) = default; + AssertionResult& operator = ( AssertionResult && ) = default; +# endif + + bool isOk() const; + bool succeeded() const; + ResultWas::OfType getResultType() const; + bool hasExpression() const; + bool hasMessage() const; + std::string getExpression() const; + std::string getExpressionInMacro() const; + bool hasExpandedExpression() const; + std::string getExpandedExpression() const; + std::string getMessage() const; + SourceLineInfo getSourceInfo() const; + std::string getTestMacroName() const; + void discardDecomposedExpression() const; + void expandDecomposedExpression() const; + + protected: + AssertionInfo m_info; + AssertionResultData m_resultData; + }; } // end namespace Catch @@ -898,453 +1017,239 @@ class AssertionResult namespace Catch { namespace Matchers { -namespace Impl { + namespace Impl { -namespace Generic { -template -class AllOf; -template -class AnyOf; -template -class Not; -} + template struct MatchAllOf; + template struct MatchAnyOf; + template struct MatchNotOf; -template -struct Matcher : SharedImpl -{ - typedef ExpressionT ExpressionType; + class MatcherUntypedBase { + public: + std::string toString() const { + if( m_cachedToString.empty() ) + m_cachedToString = describe(); + return m_cachedToString; + } - virtual ~Matcher() {} - virtual Ptr clone() const = 0; - virtual bool match(ExpressionT const& expr) const = 0; - virtual std::string toString() const = 0; + protected: + virtual ~MatcherUntypedBase(); + virtual std::string describe() const = 0; + mutable std::string m_cachedToString; + private: + MatcherUntypedBase& operator = ( MatcherUntypedBase const& ); + }; - Generic::AllOf operator&&(Matcher const& other) const; - Generic::AnyOf operator||(Matcher const& other) const; - Generic::Not operator!() const; -}; + template + struct MatcherMethod { + virtual bool match( ObjectT const& arg ) const = 0; + }; + template + struct MatcherMethod { + virtual bool match( PtrT* arg ) const = 0; + }; -template -struct MatcherImpl : Matcher -{ + template + struct MatcherBase : MatcherUntypedBase, MatcherMethod { - virtual Ptr> clone() const - { - return Ptr>(new DerivedT(static_cast(*this))); - } -}; + MatchAllOf operator && ( MatcherBase const& other ) const; + MatchAnyOf operator || ( MatcherBase const& other ) const; + MatchNotOf operator ! () const; + }; -namespace Generic { -template -class Not : public MatcherImpl, ExpressionT> -{ - public: - explicit Not(Matcher const& matcher) : m_matcher(matcher.clone()) {} - Not(Not const& other) : m_matcher(other.m_matcher) {} - - virtual bool match(ExpressionT const& expr) const CATCH_OVERRIDE - { - return !m_matcher->match(expr); - } - - virtual std::string toString() const CATCH_OVERRIDE - { - return "not " + m_matcher->toString(); - } - - private: - Ptr> m_matcher; -}; - -template -class AllOf : public MatcherImpl, ExpressionT> -{ - public: - AllOf() {} - AllOf(AllOf const& other) : m_matchers(other.m_matchers) {} - - AllOf& add(Matcher const& matcher) - { - m_matchers.push_back(matcher.clone()); - return *this; - } - virtual bool match(ExpressionT const& expr) const - { - for (std::size_t i = 0; i < m_matchers.size(); ++i) - if (!m_matchers[i]->match(expr)) - return false; - return true; - } - virtual std::string toString() const - { - std::ostringstream oss; - oss << "( "; - for (std::size_t i = 0; i < m_matchers.size(); ++i) - { - if (i != 0) - oss << " and "; - oss << m_matchers[i]->toString(); - } - oss << " )"; - return oss.str(); - } - - AllOf operator&&(Matcher const& other) const - { - AllOf allOfExpr(*this); - allOfExpr.add(other); - return allOfExpr; - } - - private: - std::vector>> m_matchers; -}; - -template -class AnyOf : public MatcherImpl, ExpressionT> -{ - public: - AnyOf() {} - AnyOf(AnyOf const& other) : m_matchers(other.m_matchers) {} - - AnyOf& add(Matcher const& matcher) - { - m_matchers.push_back(matcher.clone()); - return *this; - } - virtual bool match(ExpressionT const& expr) const - { - for (std::size_t i = 0; i < m_matchers.size(); ++i) - if (m_matchers[i]->match(expr)) + template + struct MatchAllOf : MatcherBase { + virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if (!m_matchers[i]->match(arg)) + return false; + } return true; - return false; - } - virtual std::string toString() const - { - std::ostringstream oss; - oss << "( "; - for (std::size_t i = 0; i < m_matchers.size(); ++i) - { - if (i != 0) - oss << " or "; - oss << m_matchers[i]->toString(); + } + virtual std::string describe() const CATCH_OVERRIDE { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if( i != 0 ) + description += " and "; + description += m_matchers[i]->toString(); + } + description += " )"; + return description; + } + + MatchAllOf& operator && ( MatcherBase const& other ) { + m_matchers.push_back( &other ); + return *this; + } + + std::vector const*> m_matchers; + }; + template + struct MatchAnyOf : MatcherBase { + + virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if (m_matchers[i]->match(arg)) + return true; + } + return false; + } + virtual std::string describe() const CATCH_OVERRIDE { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if( i != 0 ) + description += " or "; + description += m_matchers[i]->toString(); + } + description += " )"; + return description; + } + + MatchAnyOf& operator || ( MatcherBase const& other ) { + m_matchers.push_back( &other ); + return *this; + } + + std::vector const*> m_matchers; + }; + + template + struct MatchNotOf : MatcherBase { + + MatchNotOf( MatcherBase const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {} + + virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { + return !m_underlyingMatcher.match( arg ); + } + + virtual std::string describe() const CATCH_OVERRIDE { + return "not " + m_underlyingMatcher.toString(); + } + MatcherBase const& m_underlyingMatcher; + }; + + template + MatchAllOf MatcherBase::operator && ( MatcherBase const& other ) const { + return MatchAllOf() && *this && other; } - oss << " )"; - return oss.str(); + template + MatchAnyOf MatcherBase::operator || ( MatcherBase const& other ) const { + return MatchAnyOf() || *this || other; + } + template + MatchNotOf MatcherBase::operator ! () const { + return MatchNotOf( *this ); + } + + } // namespace Impl + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + // - deprecated: prefer ||, && and ! + template + inline Impl::MatchNotOf Not( Impl::MatcherBase const& underlyingMatcher ) { + return Impl::MatchNotOf( underlyingMatcher ); } - - AnyOf operator||(Matcher const& other) const - { - AnyOf anyOfExpr(*this); - anyOfExpr.add(other); - return anyOfExpr; + template + inline Impl::MatchAllOf AllOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2 ) { + return Impl::MatchAllOf() && m1 && m2; } - - private: - std::vector>> m_matchers; -}; - -} // namespace Generic - -template -Generic::AllOf Matcher::operator&&(Matcher const& other) const -{ - Generic::AllOf allOfExpr; - allOfExpr.add(*this); - allOfExpr.add(other); - return allOfExpr; -} - -template -Generic::AnyOf Matcher::operator||(Matcher const& other) const -{ - Generic::AnyOf anyOfExpr; - anyOfExpr.add(*this); - anyOfExpr.add(other); - return anyOfExpr; -} - -template -Generic::Not Matcher::operator!() const -{ - return Generic::Not(*this); -} - -namespace StdString { - -inline std::string makeString(std::string const& str) { return str; } -inline std::string makeString(const char* str) { return str ? std::string(str) : std::string(); } - -struct CasedString -{ - CasedString(std::string const& str, CaseSensitive::Choice caseSensitivity) - : m_caseSensitivity(caseSensitivity), - m_str(adjustString(str)) - { + template + inline Impl::MatchAllOf AllOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2, Impl::MatcherBase const& m3 ) { + return Impl::MatchAllOf() && m1 && m2 && m3; } - std::string adjustString(std::string const& str) const - { - return m_caseSensitivity == CaseSensitive::No - ? toLower(str) - : str; + template + inline Impl::MatchAnyOf AnyOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2 ) { + return Impl::MatchAnyOf() || m1 || m2; } - std::string toStringSuffix() const - { - return m_caseSensitivity == CaseSensitive::No - ? " (case insensitive)" - : ""; + template + inline Impl::MatchAnyOf AnyOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2, Impl::MatcherBase const& m3 ) { + return Impl::MatchAnyOf() || m1 || m2 || m3; } - CaseSensitive::Choice m_caseSensitivity; - std::string m_str; -}; - -struct Equals : MatcherImpl -{ - Equals(std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) - : m_data(str, caseSensitivity) - { - } - Equals(Equals const& other) : m_data(other.m_data) {} - - virtual ~Equals(); - - virtual bool match(std::string const& expr) const - { - return m_data.m_str == m_data.adjustString(expr); - ; - } - virtual std::string toString() const - { - return "equals: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); - } - - CasedString m_data; -}; - -struct Contains : MatcherImpl -{ - Contains(std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) - : m_data(substr, caseSensitivity) {} - Contains(Contains const& other) : m_data(other.m_data) {} - - virtual ~Contains(); - - virtual bool match(std::string const& expr) const - { - return m_data.adjustString(expr).find(m_data.m_str) != std::string::npos; - } - virtual std::string toString() const - { - return "contains: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); - } - - CasedString m_data; -}; - -struct StartsWith : MatcherImpl -{ - StartsWith(std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) - : m_data(substr, caseSensitivity) {} - - StartsWith(StartsWith const& other) : m_data(other.m_data) {} - - virtual ~StartsWith(); - - virtual bool match(std::string const& expr) const - { - return m_data.adjustString(expr).find(m_data.m_str) == 0; - } - virtual std::string toString() const - { - return "starts with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); - } - - CasedString m_data; -}; - -struct EndsWith : MatcherImpl -{ - EndsWith(std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) - : m_data(substr, caseSensitivity) {} - EndsWith(EndsWith const& other) : m_data(other.m_data) {} - - virtual ~EndsWith(); - - virtual bool match(std::string const& expr) const - { - return m_data.adjustString(expr).find(m_data.m_str) == expr.size() - m_data.m_str.size(); - } - virtual std::string toString() const - { - return "ends with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); - } - - CasedString m_data; -}; -} // namespace StdString -} // namespace Impl - -// The following functions create the actual matcher objects. -// This allows the types to be inferred -template -inline Impl::Generic::Not Not(Impl::Matcher const& m) -{ - return Impl::Generic::Not(m); -} - -template -inline Impl::Generic::AllOf AllOf(Impl::Matcher const& m1, - Impl::Matcher const& m2) -{ - return Impl::Generic::AllOf().add(m1).add(m2); -} -template -inline Impl::Generic::AllOf AllOf(Impl::Matcher const& m1, - Impl::Matcher const& m2, - Impl::Matcher const& m3) -{ - return Impl::Generic::AllOf().add(m1).add(m2).add(m3); -} -template -inline Impl::Generic::AnyOf AnyOf(Impl::Matcher const& m1, - Impl::Matcher const& m2) -{ - return Impl::Generic::AnyOf().add(m1).add(m2); -} -template -inline Impl::Generic::AnyOf AnyOf(Impl::Matcher const& m1, - Impl::Matcher const& m2, - Impl::Matcher const& m3) -{ - return Impl::Generic::AnyOf().add(m1).add(m2).add(m3); -} - -inline Impl::StdString::Equals Equals(std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) -{ - return Impl::StdString::Equals(str, caseSensitivity); -} -inline Impl::StdString::Equals Equals(const char* str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) -{ - return Impl::StdString::Equals(Impl::StdString::makeString(str), caseSensitivity); -} -inline Impl::StdString::Contains Contains(std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) -{ - return Impl::StdString::Contains(substr, caseSensitivity); -} -inline Impl::StdString::Contains Contains(const char* substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) -{ - return Impl::StdString::Contains(Impl::StdString::makeString(substr), caseSensitivity); -} -inline Impl::StdString::StartsWith StartsWith(std::string const& substr) -{ - return Impl::StdString::StartsWith(substr); -} -inline Impl::StdString::StartsWith StartsWith(const char* substr) -{ - return Impl::StdString::StartsWith(Impl::StdString::makeString(substr)); -} -inline Impl::StdString::EndsWith EndsWith(std::string const& substr) -{ - return Impl::StdString::EndsWith(substr); -} -inline Impl::StdString::EndsWith EndsWith(const char* substr) -{ - return Impl::StdString::EndsWith(Impl::StdString::makeString(substr)); -} } // namespace Matchers using namespace Matchers; +using Matchers::Impl::MatcherBase; } // namespace Catch namespace Catch { -struct TestFailureException -{ -}; + struct TestFailureException{}; -template -class ExpressionLhs; + template class ExpressionLhs; -struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; + struct CopyableStream { + CopyableStream() {} + CopyableStream( CopyableStream const& other ) { + oss << other.oss.str(); + } + CopyableStream& operator=( CopyableStream const& other ) { + oss.str(std::string()); + oss << other.oss.str(); + return *this; + } + std::ostringstream oss; + }; -struct CopyableStream -{ - CopyableStream() {} - CopyableStream(CopyableStream const& other) - { - oss << other.oss.str(); - } - CopyableStream& operator=(CopyableStream const& other) - { - oss.str(""); - oss << other.oss.str(); - return *this; - } - std::ostringstream oss; -}; + class ResultBuilder : public DecomposedExpression { + public: + ResultBuilder( char const* macroName, + SourceLineInfo const& lineInfo, + char const* capturedExpression, + ResultDisposition::Flags resultDisposition, + char const* secondArg = "" ); + ~ResultBuilder(); -class ResultBuilder -{ - public: - ResultBuilder(char const* macroName, - SourceLineInfo const& lineInfo, - char const* capturedExpression, - ResultDisposition::Flags resultDisposition, - char const* secondArg = ""); + template + ExpressionLhs operator <= ( T const& operand ); + ExpressionLhs operator <= ( bool value ); - template - ExpressionLhs operator<=(T const& operand); - ExpressionLhs operator<=(bool value); + template + ResultBuilder& operator << ( T const& value ) { + m_stream.oss << value; + return *this; + } - template - ResultBuilder& operator<<(T const& value) - { - m_stream.oss << value; - return *this; - } + ResultBuilder& setResultType( ResultWas::OfType result ); + ResultBuilder& setResultType( bool result ); - template - STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator&&(RhsT const&); - template - STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator||(RhsT const&); + void endExpression( DecomposedExpression const& expr ); - ResultBuilder& setResultType(ResultWas::OfType result); - ResultBuilder& setResultType(bool result); - ResultBuilder& setLhs(std::string const& lhs); - ResultBuilder& setRhs(std::string const& rhs); - ResultBuilder& setOp(std::string const& op); + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE; - void endExpression(); + AssertionResult build() const; + AssertionResult build( DecomposedExpression const& expr ) const; - std::string reconstructExpression() const; - AssertionResult build() const; + void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal ); + void captureResult( ResultWas::OfType resultType ); + void captureExpression(); + void captureExpectedException( std::string const& expectedMessage ); + void captureExpectedException( Matchers::Impl::MatcherBase const& matcher ); + void handleResult( AssertionResult const& result ); + void react(); + bool shouldDebugBreak() const; + bool allowThrows() const; - void useActiveException(ResultDisposition::Flags resultDisposition = ResultDisposition::Normal); - void captureResult(ResultWas::OfType resultType); - void captureExpression(); - void captureExpectedException(std::string const& expectedMessage); - void captureExpectedException(Matchers::Impl::Matcher const& matcher); - void handleResult(AssertionResult const& result); - void react(); - bool shouldDebugBreak() const; - bool allowThrows() const; + template + void captureMatch( ArgT const& arg, MatcherT const& matcher, char const* matcherString ); - private: - AssertionInfo m_assertionInfo; - AssertionResultData m_data; - struct ExprComponents - { - ExprComponents() : testFalse(false) {} - bool testFalse; - std::string lhs, rhs, op; - } m_exprComponents; - CopyableStream m_stream; + void setExceptionGuard(); + void unsetExceptionGuard(); - bool m_shouldDebugBreak; - bool m_shouldThrow; -}; + private: + AssertionInfo m_assertionInfo; + AssertionResultData m_data; + CopyableStream m_stream; + + bool m_shouldDebugBreak; + bool m_shouldThrow; + bool m_guardException; + }; } // namespace Catch @@ -1357,7 +1262,8 @@ class ResultBuilder #ifdef _MSC_VER #pragma warning(push) -#pragma warning(disable : 4389) // '==' : signed/unsigned mismatch +#pragma warning(disable:4389) // '==' : signed/unsigned mismatch +#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) #endif #include @@ -1365,300 +1271,193 @@ class ResultBuilder namespace Catch { namespace Internal { -enum Operator -{ - IsEqualTo, - IsNotEqualTo, - IsLessThan, - IsGreaterThan, - IsLessThanOrEqualTo, - IsGreaterThanOrEqualTo -}; + enum Operator { + IsEqualTo, + IsNotEqualTo, + IsLessThan, + IsGreaterThan, + IsLessThanOrEqualTo, + IsGreaterThanOrEqualTo + }; -template -struct OperatorTraits -{ - static const char* getName() { return "*error*"; } -}; -template <> -struct OperatorTraits -{ - static const char* getName() { return "=="; } -}; -template <> -struct OperatorTraits -{ - static const char* getName() { return "!="; } -}; -template <> -struct OperatorTraits -{ - static const char* getName() { return "<"; } -}; -template <> -struct OperatorTraits -{ - static const char* getName() { return ">"; } -}; -template <> -struct OperatorTraits -{ - static const char* getName() { return "<="; } -}; -template <> -struct OperatorTraits -{ - static const char* getName() { return ">="; } -}; + template struct OperatorTraits { static const char* getName(){ return "*error*"; } }; + template<> struct OperatorTraits { static const char* getName(){ return "=="; } }; + template<> struct OperatorTraits { static const char* getName(){ return "!="; } }; + template<> struct OperatorTraits { static const char* getName(){ return "<"; } }; + template<> struct OperatorTraits { static const char* getName(){ return ">"; } }; + template<> struct OperatorTraits { static const char* getName(){ return "<="; } }; + template<> struct OperatorTraits{ static const char* getName(){ return ">="; } }; -template -inline T& opCast(T const& t) -{ - return const_cast(t); -} + template + inline T& opCast(T const& t) { return const_cast(t); } // nullptr_t support based on pull request #154 from Konstantin Baumann #ifdef CATCH_CONFIG_CPP11_NULLPTR -inline std::nullptr_t opCast(std::nullptr_t) -{ - return nullptr; -} + inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; } #endif // CATCH_CONFIG_CPP11_NULLPTR -// So the compare overloads can be operator agnostic we convey the operator as a template -// enum, which is used to specialise an Evaluator for doing the comparison. -template -class Evaluator -{ -}; + // So the compare overloads can be operator agnostic we convey the operator as a template + // enum, which is used to specialise an Evaluator for doing the comparison. + template + class Evaluator{}; -template -struct Evaluator -{ - static bool evaluate(T1 const& lhs, T2 const& rhs) - { - return opCast(lhs) == opCast(rhs); + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs) { + return bool( opCast( lhs ) == opCast( rhs ) ); + } + }; + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return bool( opCast( lhs ) != opCast( rhs ) ); + } + }; + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return bool( opCast( lhs ) < opCast( rhs ) ); + } + }; + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return bool( opCast( lhs ) > opCast( rhs ) ); + } + }; + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return bool( opCast( lhs ) >= opCast( rhs ) ); + } + }; + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return bool( opCast( lhs ) <= opCast( rhs ) ); + } + }; + + template + bool applyEvaluator( T1 const& lhs, T2 const& rhs ) { + return Evaluator::evaluate( lhs, rhs ); } -}; -template -struct Evaluator -{ - static bool evaluate(T1 const& lhs, T2 const& rhs) - { - return opCast(lhs) != opCast(rhs); + + // This level of indirection allows us to specialise for integer types + // to avoid signed/ unsigned warnings + + // "base" overload + template + bool compare( T1 const& lhs, T2 const& rhs ) { + return Evaluator::evaluate( lhs, rhs ); } -}; -template -struct Evaluator -{ - static bool evaluate(T1 const& lhs, T2 const& rhs) - { - return opCast(lhs) < opCast(rhs); + + // unsigned X to int + template bool compare( unsigned int lhs, int rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); } -}; -template -struct Evaluator -{ - static bool evaluate(T1 const& lhs, T2 const& rhs) - { - return opCast(lhs) > opCast(rhs); + template bool compare( unsigned long lhs, int rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); } -}; -template -struct Evaluator -{ - static bool evaluate(T1 const& lhs, T2 const& rhs) - { - return opCast(lhs) >= opCast(rhs); + template bool compare( unsigned char lhs, int rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); } -}; -template -struct Evaluator -{ - static bool evaluate(T1 const& lhs, T2 const& rhs) - { - return opCast(lhs) <= opCast(rhs); + + // unsigned X to long + template bool compare( unsigned int lhs, long rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + template bool compare( unsigned long lhs, long rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + template bool compare( unsigned char lhs, long rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); } -}; -template -bool applyEvaluator(T1 const& lhs, T2 const& rhs) -{ - return Evaluator::evaluate(lhs, rhs); -} + // int to unsigned X + template bool compare( int lhs, unsigned int rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( int lhs, unsigned long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( int lhs, unsigned char rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } -// This level of indirection allows us to specialise for integer types -// to avoid signed/ unsigned warnings + // long to unsigned X + template bool compare( long lhs, unsigned int rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( long lhs, unsigned long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( long lhs, unsigned char rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } -// "base" overload -template -bool compare(T1 const& lhs, T2 const& rhs) -{ - return Evaluator::evaluate(lhs, rhs); -} + // pointer to long (when comparing against NULL) + template bool compare( long lhs, T* rhs ) { + return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); + } + template bool compare( T* lhs, long rhs ) { + return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); + } -// unsigned X to int -template -bool compare(unsigned int lhs, int rhs) -{ - return applyEvaluator(lhs, static_cast(rhs)); -} -template -bool compare(unsigned long lhs, int rhs) -{ - return applyEvaluator(lhs, static_cast(rhs)); -} -template -bool compare(unsigned char lhs, int rhs) -{ - return applyEvaluator(lhs, static_cast(rhs)); -} - -// unsigned X to long -template -bool compare(unsigned int lhs, long rhs) -{ - return applyEvaluator(lhs, static_cast(rhs)); -} -template -bool compare(unsigned long lhs, long rhs) -{ - return applyEvaluator(lhs, static_cast(rhs)); -} -template -bool compare(unsigned char lhs, long rhs) -{ - return applyEvaluator(lhs, static_cast(rhs)); -} - -// int to unsigned X -template -bool compare(int lhs, unsigned int rhs) -{ - return applyEvaluator(static_cast(lhs), rhs); -} -template -bool compare(int lhs, unsigned long rhs) -{ - return applyEvaluator(static_cast(lhs), rhs); -} -template -bool compare(int lhs, unsigned char rhs) -{ - return applyEvaluator(static_cast(lhs), rhs); -} - -// long to unsigned X -template -bool compare(long lhs, unsigned int rhs) -{ - return applyEvaluator(static_cast(lhs), rhs); -} -template -bool compare(long lhs, unsigned long rhs) -{ - return applyEvaluator(static_cast(lhs), rhs); -} -template -bool compare(long lhs, unsigned char rhs) -{ - return applyEvaluator(static_cast(lhs), rhs); -} - -// pointer to long (when comparing against NULL) -template -bool compare(long lhs, T* rhs) -{ - return Evaluator::evaluate(reinterpret_cast(lhs), rhs); -} -template -bool compare(T* lhs, long rhs) -{ - return Evaluator::evaluate(lhs, reinterpret_cast(rhs)); -} - -// pointer to int (when comparing against NULL) -template -bool compare(int lhs, T* rhs) -{ - return Evaluator::evaluate(reinterpret_cast(lhs), rhs); -} -template -bool compare(T* lhs, int rhs) -{ - return Evaluator::evaluate(lhs, reinterpret_cast(rhs)); -} + // pointer to int (when comparing against NULL) + template bool compare( int lhs, T* rhs ) { + return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); + } + template bool compare( T* lhs, int rhs ) { + return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); + } #ifdef CATCH_CONFIG_CPP11_LONG_LONG -// long long to unsigned X -template -bool compare(long long lhs, unsigned int rhs) -{ - return applyEvaluator(static_cast(lhs), rhs); -} -template -bool compare(long long lhs, unsigned long rhs) -{ - return applyEvaluator(static_cast(lhs), rhs); -} -template -bool compare(long long lhs, unsigned long long rhs) -{ - return applyEvaluator(static_cast(lhs), rhs); -} -template -bool compare(long long lhs, unsigned char rhs) -{ - return applyEvaluator(static_cast(lhs), rhs); -} + // long long to unsigned X + template bool compare( long long lhs, unsigned int rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( long long lhs, unsigned long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( long long lhs, unsigned long long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( long long lhs, unsigned char rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } -// unsigned long long to X -template -bool compare(unsigned long long lhs, int rhs) -{ - return applyEvaluator(static_cast(lhs), rhs); -} -template -bool compare(unsigned long long lhs, long rhs) -{ - return applyEvaluator(static_cast(lhs), rhs); -} -template -bool compare(unsigned long long lhs, long long rhs) -{ - return applyEvaluator(static_cast(lhs), rhs); -} -template -bool compare(unsigned long long lhs, char rhs) -{ - return applyEvaluator(static_cast(lhs), rhs); -} + // unsigned long long to X + template bool compare( unsigned long long lhs, int rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( unsigned long long lhs, long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( unsigned long long lhs, long long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( unsigned long long lhs, char rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } -// pointer to long long (when comparing against NULL) -template -bool compare(long long lhs, T* rhs) -{ - return Evaluator::evaluate(reinterpret_cast(lhs), rhs); -} -template -bool compare(T* lhs, long long rhs) -{ - return Evaluator::evaluate(lhs, reinterpret_cast(rhs)); -} + // pointer to long long (when comparing against NULL) + template bool compare( long long lhs, T* rhs ) { + return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); + } + template bool compare( T* lhs, long long rhs ) { + return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); + } #endif // CATCH_CONFIG_CPP11_LONG_LONG #ifdef CATCH_CONFIG_CPP11_NULLPTR -// pointer to nullptr_t (when comparing against nullptr) -template -bool compare(std::nullptr_t, T* rhs) -{ - return Evaluator::evaluate(nullptr, rhs); -} -template -bool compare(T* lhs, std::nullptr_t) -{ - return Evaluator::evaluate(lhs, nullptr); -} + // pointer to nullptr_t (when comparing against nullptr) + template bool compare( std::nullptr_t, T* rhs ) { + return Evaluator::evaluate( nullptr, rhs ); + } + template bool compare( T* lhs, std::nullptr_t ) { + return Evaluator::evaluate( lhs, nullptr ); + } #endif // CATCH_CONFIG_CPP11_NULLPTR } // end of namespace Internal @@ -1671,11 +1470,11 @@ bool compare(T* lhs, std::nullptr_t) // #included from: catch_tostring.h #define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED -#include +#include #include #include -#include #include +#include #ifdef __OBJC__ // #included from: catch_objc_arc.hpp @@ -1689,34 +1488,29 @@ bool compare(T* lhs, std::nullptr_t) #define CATCH_ARC_ENABLED 0 #endif -void arcSafeRelease(NSObject* obj); -id performOptionalSelector(id obj, SEL sel); +void arcSafeRelease( NSObject* obj ); +id performOptionalSelector( id obj, SEL sel ); #if !CATCH_ARC_ENABLED -inline void arcSafeRelease(NSObject* obj) -{ +inline void arcSafeRelease( NSObject* obj ) { [obj release]; } -inline id performOptionalSelector(id obj, SEL sel) -{ - if ([obj respondsToSelector:sel]) - return [obj performSelector:sel]; +inline id performOptionalSelector( id obj, SEL sel ) { + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; return nil; } #define CATCH_UNSAFE_UNRETAINED #define CATCH_ARC_STRONG #else -inline void arcSafeRelease(NSObject*) -{ -} -inline id performOptionalSelector(id obj, SEL sel) -{ +inline void arcSafeRelease( NSObject* ){} +inline id performOptionalSelector( id obj, SEL sel ) { #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" #endif - if ([obj respondsToSelector:sel]) - return [obj performSelector:sel]; + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; #ifdef __clang__ #pragma clang diagnostic pop #endif @@ -1739,168 +1533,161 @@ inline id performOptionalSelector(id obj, SEL sel) namespace Catch { // Why we're here. -template -std::string toString(T const& value); +template +std::string toString( T const& value ); // Built in overloads -std::string toString(std::string const& value); -std::string toString(std::wstring const& value); -std::string toString(const char* const value); -std::string toString(char* const value); -std::string toString(const wchar_t* const value); -std::string toString(wchar_t* const value); -std::string toString(int value); -std::string toString(unsigned long value); -std::string toString(unsigned int value); -std::string toString(const double value); -std::string toString(const float value); -std::string toString(bool value); -std::string toString(char value); -std::string toString(signed char value); -std::string toString(unsigned char value); +std::string toString( std::string const& value ); +std::string toString( std::wstring const& value ); +std::string toString( const char* const value ); +std::string toString( char* const value ); +std::string toString( const wchar_t* const value ); +std::string toString( wchar_t* const value ); +std::string toString( int value ); +std::string toString( unsigned long value ); +std::string toString( unsigned int value ); +std::string toString( const double value ); +std::string toString( const float value ); +std::string toString( bool value ); +std::string toString( char value ); +std::string toString( signed char value ); +std::string toString( unsigned char value ); #ifdef CATCH_CONFIG_CPP11_LONG_LONG -std::string toString(long long value); -std::string toString(unsigned long long value); +std::string toString( long long value ); +std::string toString( unsigned long long value ); #endif #ifdef CATCH_CONFIG_CPP11_NULLPTR -std::string toString(std::nullptr_t); +std::string toString( std::nullptr_t ); #endif #ifdef __OBJC__ -std::string toString(NSString const* const& nsstring); -std::string toString(NSString* CATCH_ARC_STRONG const& nsstring); -std::string toString(NSObject* const& nsObject); + std::string toString( NSString const * const& nsstring ); + std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ); + std::string toString( NSObject* const& nsObject ); #endif namespace Detail { -extern const std::string unprintableString; + extern const std::string unprintableString; -struct BorgType -{ - template - BorgType(T const&); -}; - -struct TrueType -{ - char sizer[1]; -}; -struct FalseType -{ - char sizer[2]; -}; - -TrueType& testStreamable(std::ostream&); -FalseType testStreamable(FalseType); - -FalseType operator<<(std::ostream const&, BorgType const&); - -template -struct IsStreamInsertable -{ - static std::ostream& s; - static T const& t; - enum - { - value = sizeof(testStreamable(s << t)) == sizeof(TrueType) + #if !defined(CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK) + struct BorgType { + template BorgType( T const& ); }; -}; -#if defined(CATCH_CONFIG_CPP11_IS_ENUM) -template ::value> -struct EnumStringMaker -{ - static std::string convert(T const&) { return unprintableString; } -}; + struct TrueType { char sizer[1]; }; + struct FalseType { char sizer[2]; }; -template -struct EnumStringMaker -{ - static std::string convert(T const& v) - { - return ::Catch::toString( - static_cast::type>(v)); - } -}; -#endif -template -struct StringMakerBase -{ -#if defined(CATCH_CONFIG_CPP11_IS_ENUM) - template - static std::string convert(T const& v) - { - return EnumStringMaker::convert(v); - } + TrueType& testStreamable( std::ostream& ); + FalseType testStreamable( FalseType ); + + FalseType operator<<( std::ostream const&, BorgType const& ); + + template + struct IsStreamInsertable { + static std::ostream &s; + static T const&t; + enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) }; + }; #else - template - static std::string convert(T const&) - { - return unprintableString; - } + template + class IsStreamInsertable { + template + static auto test(int) + -> decltype( std::declval() << std::declval(), std::true_type() ); + + template + static auto test(...) -> std::false_type; + + public: + static const bool value = decltype(test(0))::value; + }; #endif -}; -template <> -struct StringMakerBase -{ - template - static std::string convert(T const& _value) +#if defined(CATCH_CONFIG_CPP11_IS_ENUM) + template::value + > + struct EnumStringMaker { - std::ostringstream oss; - oss << _value; - return oss.str(); + static std::string convert( T const& ) { return unprintableString; } + }; + + template + struct EnumStringMaker + { + static std::string convert( T const& v ) + { + return ::Catch::toString( + static_cast::type>(v) + ); + } + }; +#endif + template + struct StringMakerBase { +#if defined(CATCH_CONFIG_CPP11_IS_ENUM) + template + static std::string convert( T const& v ) + { + return EnumStringMaker::convert( v ); + } +#else + template + static std::string convert( T const& ) { return unprintableString; } +#endif + }; + + template<> + struct StringMakerBase { + template + static std::string convert( T const& _value ) { + std::ostringstream oss; + oss << _value; + return oss.str(); + } + }; + + std::string rawMemoryToString( const void *object, std::size_t size ); + + template + inline std::string rawMemoryToString( const T& object ) { + return rawMemoryToString( &object, sizeof(object) ); } -}; - -std::string rawMemoryToString(const void* object, std::size_t size); - -template -inline std::string rawMemoryToString(const T& object) -{ - return rawMemoryToString(&object, sizeof(object)); -} } // end namespace Detail -template -struct StringMaker : Detail::StringMakerBase::value> -{ -}; +template +struct StringMaker : + Detail::StringMakerBase::value> {}; -template -struct StringMaker -{ - template - static std::string convert(U* p) - { - if (!p) +template +struct StringMaker { + template + static std::string convert( U* p ) { + if( !p ) return "NULL"; else - return Detail::rawMemoryToString(p); + return Detail::rawMemoryToString( p ); } }; -template -struct StringMaker -{ - static std::string convert(R C::*p) - { - if (!p) +template +struct StringMaker { + static std::string convert( R C::* p ) { + if( !p ) return "NULL"; else - return Detail::rawMemoryToString(p); + return Detail::rawMemoryToString( p ); } }; namespace Detail { -template -std::string rangeToString(InputIterator first, InputIterator last); + template + std::string rangeToString( InputIterator first, InputIterator last ); } //template @@ -1910,48 +1697,47 @@ std::string rangeToString(InputIterator first, InputIterator last); // } //}; -template -std::string toString(std::vector const& v) -{ - return Detail::rangeToString(v.begin(), v.end()); +template +std::string toString( std::vector const& v ) { + return Detail::rangeToString( v.begin(), v.end() ); } #ifdef CATCH_CONFIG_CPP11_TUPLE // toString for tuples namespace TupleDetail { -template < - typename Tuple, - std::size_t N = 0, - bool = (N < std::tuple_size::value)> -struct ElementPrinter -{ - static void print(const Tuple& tuple, std::ostream& os) - { - os << (N ? ", " : " ") - << Catch::toString(std::get(tuple)); - ElementPrinter::print(tuple, os); - } -}; + template< + typename Tuple, + std::size_t N = 0, + bool = (N < std::tuple_size::value) + > + struct ElementPrinter { + static void print( const Tuple& tuple, std::ostream& os ) + { + os << ( N ? ", " : " " ) + << Catch::toString(std::get(tuple)); + ElementPrinter::print(tuple,os); + } + }; + + template< + typename Tuple, + std::size_t N + > + struct ElementPrinter { + static void print( const Tuple&, std::ostream& ) {} + }; -template < - typename Tuple, - std::size_t N> -struct ElementPrinter -{ - static void print(const Tuple&, std::ostream&) {} -}; } -template -struct StringMaker> -{ +template +struct StringMaker> { - static std::string convert(const std::tuple& tuple) + static std::string convert( const std::tuple& tuple ) { std::ostringstream os; os << '{'; - TupleDetail::ElementPrinter>::print(tuple, os); + TupleDetail::ElementPrinter>::print( tuple, os ); os << " }"; return os.str(); } @@ -1959,11 +1745,10 @@ struct StringMaker> #endif // CATCH_CONFIG_CPP11_TUPLE namespace Detail { -template -std::string makeString(T const& value) -{ - return StringMaker::convert(value); -} + template + std::string makeString( T const& value ) { + return StringMaker::convert( value ); + } } // end namespace Detail /// \brief converts any type to a string @@ -1973,150 +1758,206 @@ std::string makeString(T const& value) /// that and writes {?}. /// Overload (not specialise) this template for custom typs that you don't want /// to provide an ostream overload for. -template -std::string toString(T const& value) -{ - return StringMaker::convert(value); +template +std::string toString( T const& value ) { + return StringMaker::convert( value ); } -namespace Detail { -template -std::string rangeToString(InputIterator first, InputIterator last) -{ - std::ostringstream oss; - oss << "{ "; - if (first != last) - { - oss << Catch::toString(*first); - for (++first; first != last; ++first) - oss << ", " << Catch::toString(*first); + namespace Detail { + template + std::string rangeToString( InputIterator first, InputIterator last ) { + std::ostringstream oss; + oss << "{ "; + if( first != last ) { + oss << Catch::toString( *first ); + for( ++first ; first != last ; ++first ) + oss << ", " << Catch::toString( *first ); + } + oss << " }"; + return oss.str(); } - oss << " }"; - return oss.str(); -} } } // end namespace Catch namespace Catch { -// Wraps the LHS of an expression and captures the operator and RHS (if any) - -// wrapping them all in a ResultBuilder object -template -class ExpressionLhs -{ - ExpressionLhs& operator=(ExpressionLhs const&); -#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - ExpressionLhs& operator=(ExpressionLhs&&) = delete; -#endif +template +class BinaryExpression; - public: - ExpressionLhs(ResultBuilder& rb, T lhs) : m_rb(rb), m_lhs(lhs) {} -#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - ExpressionLhs(ExpressionLhs const&) = default; - ExpressionLhs(ExpressionLhs&&) = default; -#endif +template +class MatchExpression; - template - ResultBuilder& operator==(RhsT const& rhs) - { - return captureExpression(rhs); +// Wraps the LHS of an expression and overloads comparison operators +// for also capturing those and RHS (if any) +template +class ExpressionLhs : public DecomposedExpression { +public: + ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ), m_truthy(false) {} + + ExpressionLhs& operator = ( const ExpressionLhs& ); + + template + BinaryExpression + operator == ( RhsT const& rhs ) { + return captureExpression( rhs ); } - template - ResultBuilder& operator!=(RhsT const& rhs) - { - return captureExpression(rhs); + template + BinaryExpression + operator != ( RhsT const& rhs ) { + return captureExpression( rhs ); } - template - ResultBuilder& operator<(RhsT const& rhs) - { - return captureExpression(rhs); + template + BinaryExpression + operator < ( RhsT const& rhs ) { + return captureExpression( rhs ); } - template - ResultBuilder& operator>(RhsT const& rhs) - { - return captureExpression(rhs); + template + BinaryExpression + operator > ( RhsT const& rhs ) { + return captureExpression( rhs ); } - template - ResultBuilder& operator<=(RhsT const& rhs) - { - return captureExpression(rhs); + template + BinaryExpression + operator <= ( RhsT const& rhs ) { + return captureExpression( rhs ); } - template - ResultBuilder& operator>=(RhsT const& rhs) - { - return captureExpression(rhs); + template + BinaryExpression + operator >= ( RhsT const& rhs ) { + return captureExpression( rhs ); } - ResultBuilder& operator==(bool rhs) - { - return captureExpression(rhs); + BinaryExpression operator == ( bool rhs ) { + return captureExpression( rhs ); } - ResultBuilder& operator!=(bool rhs) - { - return captureExpression(rhs); + BinaryExpression operator != ( bool rhs ) { + return captureExpression( rhs ); } - void endExpression() - { - bool value = m_lhs ? true : false; + void endExpression() { + m_truthy = m_lhs ? true : false; m_rb - .setLhs(Catch::toString(value)) - .setResultType(value) - .endExpression(); + .setResultType( m_truthy ) + .endExpression( *this ); } - // Only simple binary expressions are allowed on the LHS. - // If more complex compositions are required then place the sub expression in parentheses - template - STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator+(RhsT const&); - template - STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator-(RhsT const&); - template - STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator/(RhsT const&); - template - STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator*(RhsT const&); - template - STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator&&(RhsT const&); - template - STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator||(RhsT const&); - - private: - template - ResultBuilder& captureExpression(RhsT const& rhs) - { - return m_rb - .setResultType(Internal::compare(m_lhs, rhs)) - .setLhs(Catch::toString(m_lhs)) - .setRhs(Catch::toString(rhs)) - .setOp(Internal::OperatorTraits::getName()); + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { + dest = Catch::toString( m_truthy ); } - private: +private: + template + BinaryExpression captureExpression( RhsT& rhs ) const { + return BinaryExpression( m_rb, m_lhs, rhs ); + } + + template + BinaryExpression captureExpression( bool rhs ) const { + return BinaryExpression( m_rb, m_lhs, rhs ); + } + +private: ResultBuilder& m_rb; T m_lhs; + bool m_truthy; +}; + +template +class BinaryExpression : public DecomposedExpression { +public: + BinaryExpression( ResultBuilder& rb, LhsT lhs, RhsT rhs ) + : m_rb( rb ), m_lhs( lhs ), m_rhs( rhs ) {} + + BinaryExpression& operator = ( BinaryExpression& ); + + void endExpression() const { + m_rb + .setResultType( Internal::compare( m_lhs, m_rhs ) ) + .endExpression( *this ); + } + + virtual bool isBinaryExpression() const CATCH_OVERRIDE { + return true; + } + + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { + std::string lhs = Catch::toString( m_lhs ); + std::string rhs = Catch::toString( m_rhs ); + char delim = lhs.size() + rhs.size() < 40 && + lhs.find('\n') == std::string::npos && + rhs.find('\n') == std::string::npos ? ' ' : '\n'; + dest.reserve( 7 + lhs.size() + rhs.size() ); + // 2 for spaces around operator + // 2 for operator + // 2 for parentheses (conditionally added later) + // 1 for negation (conditionally added later) + dest = lhs; + dest += delim; + dest += Internal::OperatorTraits::getName(); + dest += delim; + dest += rhs; + } + +private: + ResultBuilder& m_rb; + LhsT m_lhs; + RhsT m_rhs; +}; + +template +class MatchExpression : public DecomposedExpression { +public: + MatchExpression( ArgT arg, MatcherT matcher, char const* matcherString ) + : m_arg( arg ), m_matcher( matcher ), m_matcherString( matcherString ) {} + + virtual bool isBinaryExpression() const CATCH_OVERRIDE { + return true; + } + + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { + std::string matcherAsString = m_matcher.toString(); + dest = Catch::toString( m_arg ); + dest += ' '; + if( matcherAsString == Detail::unprintableString ) + dest += m_matcherString; + else + dest += matcherAsString; + } + +private: + ArgT m_arg; + MatcherT m_matcher; + char const* m_matcherString; }; } // end namespace Catch + namespace Catch { -template -inline ExpressionLhs ResultBuilder::operator<=(T const& operand) -{ - return ExpressionLhs(*this, operand); -} + template + inline ExpressionLhs ResultBuilder::operator <= ( T const& operand ) { + return ExpressionLhs( *this, operand ); + } -inline ExpressionLhs ResultBuilder::operator<=(bool value) -{ - return ExpressionLhs(*this, value); -} + inline ExpressionLhs ResultBuilder::operator <= ( bool value ) { + return ExpressionLhs( *this, value ); + } + + template + inline void ResultBuilder::captureMatch( ArgT const& arg, MatcherT const& matcher, + char const* matcherString ) { + MatchExpression expr( arg, matcher, matcherString ); + setResultType( matcher.match( arg ) ); + endExpression( expr ); + } } // namespace Catch @@ -2127,60 +1968,52 @@ inline ExpressionLhs ResultBuilder::operator<=(bool value) namespace Catch { -struct MessageInfo -{ - MessageInfo(std::string const& _macroName, - SourceLineInfo const& _lineInfo, - ResultWas::OfType _type); + struct MessageInfo { + MessageInfo( std::string const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ); - std::string macroName; - SourceLineInfo lineInfo; - ResultWas::OfType type; - std::string message; - unsigned int sequence; + std::string macroName; + SourceLineInfo lineInfo; + ResultWas::OfType type; + std::string message; + unsigned int sequence; - bool operator==(MessageInfo const& other) const - { - return sequence == other.sequence; - } - bool operator<(MessageInfo const& other) const - { - return sequence < other.sequence; - } + bool operator == ( MessageInfo const& other ) const { + return sequence == other.sequence; + } + bool operator < ( MessageInfo const& other ) const { + return sequence < other.sequence; + } + private: + static unsigned int globalCount; + }; - private: - static unsigned int globalCount; -}; + struct MessageBuilder { + MessageBuilder( std::string const& macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType type ) + : m_info( macroName, lineInfo, type ) + {} -struct MessageBuilder -{ - MessageBuilder(std::string const& macroName, - SourceLineInfo const& lineInfo, - ResultWas::OfType type) - : m_info(macroName, lineInfo, type) - { - } + template + MessageBuilder& operator << ( T const& value ) { + m_stream << value; + return *this; + } - template - MessageBuilder& operator<<(T const& value) - { - m_stream << value; - return *this; - } + MessageInfo m_info; + std::ostringstream m_stream; + }; - MessageInfo m_info; - std::ostringstream m_stream; -}; + class ScopedMessage { + public: + ScopedMessage( MessageBuilder const& builder ); + ScopedMessage( ScopedMessage const& other ); + ~ScopedMessage(); -class ScopedMessage -{ - public: - ScopedMessage(MessageBuilder const& builder); - ScopedMessage(ScopedMessage const& other); - ~ScopedMessage(); - - MessageInfo m_info; -}; + MessageInfo m_info; + }; } // end namespace Catch @@ -2191,35 +2024,36 @@ class ScopedMessage namespace Catch { -class TestCase; -class AssertionResult; -struct AssertionInfo; -struct SectionInfo; -struct SectionEndInfo; -struct MessageInfo; -class ScopedMessageBuilder; -struct Counts; + class TestCase; + class AssertionResult; + struct AssertionInfo; + struct SectionInfo; + struct SectionEndInfo; + struct MessageInfo; + class ScopedMessageBuilder; + struct Counts; -struct IResultCapture -{ + struct IResultCapture { - virtual ~IResultCapture(); + virtual ~IResultCapture(); - virtual void assertionEnded(AssertionResult const& result) = 0; - virtual bool sectionStarted(SectionInfo const& sectionInfo, - Counts& assertions) = 0; - virtual void sectionEnded(SectionEndInfo const& endInfo) = 0; - virtual void sectionEndedEarly(SectionEndInfo const& endInfo) = 0; - virtual void pushScopedMessage(MessageInfo const& message) = 0; - virtual void popScopedMessage(MessageInfo const& message) = 0; + virtual void assertionEnded( AssertionResult const& result ) = 0; + virtual bool sectionStarted( SectionInfo const& sectionInfo, + Counts& assertions ) = 0; + virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; + virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; + virtual void pushScopedMessage( MessageInfo const& message ) = 0; + virtual void popScopedMessage( MessageInfo const& message ) = 0; - virtual std::string getCurrentTestName() const = 0; - virtual const AssertionResult* getLastResult() const = 0; + virtual std::string getCurrentTestName() const = 0; + virtual const AssertionResult* getLastResult() const = 0; - virtual void handleFatalErrorCondition(std::string const& message) = 0; -}; + virtual void exceptionEarlyReported() = 0; -IResultCapture& getResultCapture(); + virtual void handleFatalErrorCondition( std::string const& message ) = 0; + }; + + IResultCapture& getResultCapture(); } // #included from: catch_debugger.h @@ -2229,220 +2063,233 @@ IResultCapture& getResultCapture(); #define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) -#define CATCH_PLATFORM_MAC -#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) -#define CATCH_PLATFORM_IPHONE +# define CATCH_PLATFORM_MAC +#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +# define CATCH_PLATFORM_IPHONE +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX #elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) -#define CATCH_PLATFORM_WINDOWS +# define CATCH_PLATFORM_WINDOWS +# if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) +# define CATCH_DEFINES_NOMINMAX +# endif +# if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) +# define CATCH_DEFINES_WIN32_LEAN_AND_MEAN +# endif #endif #include -namespace Catch { +namespace Catch{ -bool isDebuggerActive(); -void writeToDebugConsole(std::string const& text); + bool isDebuggerActive(); + void writeToDebugConsole( std::string const& text ); } #ifdef CATCH_PLATFORM_MAC -// The following code snippet based on: -// http://cocoawithlove.com/2008/03/break-into-debugger.html -#ifdef DEBUG -#if defined(__ppc64__) || defined(__ppc__) -#define CATCH_BREAK_INTO_DEBUGGER() \ - if (Catch::isDebuggerActive()) \ - { \ - __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ - : \ - : \ - : "memory", "r0", "r3", "r4"); \ - } -#else -#define CATCH_BREAK_INTO_DEBUGGER() \ - if (Catch::isDebuggerActive()) \ - { \ - __asm__("int $3\n" \ - : \ - :); \ - } -#endif -#endif + // The following code snippet based on: + // http://cocoawithlove.com/2008/03/break-into-debugger.html + #if defined(__ppc64__) || defined(__ppc__) + #define CATCH_TRAP() \ + __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ + : : : "memory","r0","r3","r4" ) + #else + #define CATCH_TRAP() __asm__("int $3\n" : : ) + #endif +#elif defined(CATCH_PLATFORM_LINUX) + // If we can use inline assembler, do it because this allows us to break + // directly at the location of the failing check instead of breaking inside + // raise() called from it, i.e. one stack frame below. + #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) + #define CATCH_TRAP() asm volatile ("int $3") + #else // Fall back to the generic way. + #include + + #define CATCH_TRAP() raise(SIGTRAP) + #endif #elif defined(_MSC_VER) -#define CATCH_BREAK_INTO_DEBUGGER() \ - if (Catch::isDebuggerActive()) \ - { \ - __debugbreak(); \ - } + #define CATCH_TRAP() __debugbreak() #elif defined(__MINGW32__) -extern "C" __declspec(dllimport) void __stdcall DebugBreak(); -#define CATCH_BREAK_INTO_DEBUGGER() \ - if (Catch::isDebuggerActive()) \ - { \ - DebugBreak(); \ - } + extern "C" __declspec(dllimport) void __stdcall DebugBreak(); + #define CATCH_TRAP() DebugBreak() #endif -#ifndef CATCH_BREAK_INTO_DEBUGGER -#define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); +#ifdef CATCH_TRAP + #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } +#else + #define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); #endif // #included from: catch_interfaces_runner.h #define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED namespace Catch { -class TestCase; + class TestCase; -struct IRunner -{ - virtual ~IRunner(); - virtual bool aborting() const = 0; -}; + struct IRunner { + virtual ~IRunner(); + virtual bool aborting() const = 0; + }; } +#if defined(CATCH_CONFIG_FAST_COMPILE) +/////////////////////////////////////////////////////////////////////////////// +// We can speedup compilation significantly by breaking into debugger lower in +// the callstack, because then we don't have to expand CATCH_BREAK_INTO_DEBUGGER +// macro in each assertion +#define INTERNAL_CATCH_REACT( resultBuilder ) \ + resultBuilder.react(); + +/////////////////////////////////////////////////////////////////////////////// +// Another way to speed-up compilation is to omit local try-catch for REQUIRE* +// macros. +// This can potentially cause false negative, if the test code catches +// the exception before it propagates back up to the runner. +#define INTERNAL_CATCH_TEST_NO_TRY( macroName, resultDisposition, expr ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + __catchResult.setExceptionGuard(); \ + CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + ( __catchResult <= expr ).endExpression(); \ + CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + __catchResult.unsetExceptionGuard(); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::isTrue( false && static_cast( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look +// The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. + +#define INTERNAL_CHECK_THAT_NO_TRY( macroName, matcher, resultDisposition, arg ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \ + __catchResult.setExceptionGuard(); \ + __catchResult.captureMatch( arg, matcher, #matcher ); \ + __catchResult.unsetExceptionGuard(); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) + +#else /////////////////////////////////////////////////////////////////////////////// // In the event of a failure works out if the debugger needs to be invoked // and/or an exception thrown and takes appropriate action. // This needs to be done as a macro so the debugger will stop in the user // source code rather than in Catch library code -#define INTERNAL_CATCH_REACT(resultBuilder) \ - if (resultBuilder.shouldDebugBreak()) CATCH_BREAK_INTO_DEBUGGER(); \ +#define INTERNAL_CATCH_REACT( resultBuilder ) \ + if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ resultBuilder.react(); - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_TEST(expr, resultDisposition, macroName) \ - do \ - { \ - Catch::ResultBuilder __catchResult(macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition); \ - try \ - { \ - (__catchResult <= expr).endExpression(); \ - } \ - catch (...) \ - { \ - __catchResult.useActiveException(Catch::ResultDisposition::Normal); \ - } \ - INTERNAL_CATCH_REACT(__catchResult) \ - } while (Catch::isTrue(false && (expr))) // expr here is never evaluated at runtime but it forces the compiler to give it a look - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_IF(expr, resultDisposition, macroName) \ - INTERNAL_CATCH_TEST(expr, resultDisposition, macroName); \ - if (Catch::getResultCapture().getLastResult()->succeeded()) - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_ELSE(expr, resultDisposition, macroName) \ - INTERNAL_CATCH_TEST(expr, resultDisposition, macroName); \ - if (!Catch::getResultCapture().getLastResult()->succeeded()) - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_NO_THROW(expr, resultDisposition, macroName) \ - do \ - { \ - Catch::ResultBuilder __catchResult(macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition); \ - try \ - { \ - expr; \ - __catchResult.captureResult(Catch::ResultWas::Ok); \ - } \ - catch (...) \ - { \ - __catchResult.useActiveException(resultDisposition); \ - } \ - INTERNAL_CATCH_REACT(__catchResult) \ - } while (Catch::alwaysFalse()) - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS(expr, resultDisposition, matcher, macroName) \ - do \ - { \ - Catch::ResultBuilder __catchResult(macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher); \ - if (__catchResult.allowThrows()) \ - try \ - { \ - expr; \ - __catchResult.captureResult(Catch::ResultWas::DidntThrowException); \ - } \ - catch (...) \ - { \ - __catchResult.captureExpectedException(matcher); \ - } \ - else \ - __catchResult.captureResult(Catch::ResultWas::Ok); \ - INTERNAL_CATCH_REACT(__catchResult) \ - } while (Catch::alwaysFalse()) - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS_AS(expr, exceptionType, resultDisposition, macroName) \ - do \ - { \ - Catch::ResultBuilder __catchResult(macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition); \ - if (__catchResult.allowThrows()) \ - try \ - { \ - expr; \ - __catchResult.captureResult(Catch::ResultWas::DidntThrowException); \ - } \ - catch (exceptionType) \ - { \ - __catchResult.captureResult(Catch::ResultWas::Ok); \ - } \ - catch (...) \ - { \ - __catchResult.useActiveException(resultDisposition); \ - } \ - else \ - __catchResult.captureResult(Catch::ResultWas::Ok); \ - INTERNAL_CATCH_REACT(__catchResult) \ - } while (Catch::alwaysFalse()) - -/////////////////////////////////////////////////////////////////////////////// -#ifdef CATCH_CONFIG_VARIADIC_MACROS -#define INTERNAL_CATCH_MSG(messageType, resultDisposition, macroName, ...) \ - do \ - { \ - Catch::ResultBuilder __catchResult(macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition); \ - __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \ - __catchResult.captureResult(messageType); \ - INTERNAL_CATCH_REACT(__catchResult) \ - } while (Catch::alwaysFalse()) -#else -#define INTERNAL_CATCH_MSG(messageType, resultDisposition, macroName, log) \ - do \ - { \ - Catch::ResultBuilder __catchResult(macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition); \ - __catchResult << log + ::Catch::StreamEndStop(); \ - __catchResult.captureResult(messageType); \ - INTERNAL_CATCH_REACT(__catchResult) \ - } while (Catch::alwaysFalse()) #endif /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_INFO(log, macroName) \ - Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME(scopedMessage) = Catch::MessageBuilder(macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info) << log; +#define INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + try { \ + CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + ( __catchResult <= expr ).endExpression(); \ + CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + } \ + catch( ... ) { \ + __catchResult.useActiveException( resultDisposition ); \ + } \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::isTrue( false && static_cast( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look + // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CHECK_THAT(arg, matcher, resultDisposition, macroName) \ - do \ - { \ - Catch::ResultBuilder __catchResult(macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition); \ - try \ - { \ - std::string matcherAsString = (matcher).toString(); \ - __catchResult \ - .setLhs(Catch::toString(arg)) \ - .setRhs(matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString) \ - .setOp("matches") \ - .setResultType((matcher).match(arg)); \ - __catchResult.captureExpression(); \ - } \ - catch (...) \ - { \ - __catchResult.useActiveException(resultDisposition | Catch::ResultDisposition::ContinueOnFailure); \ - } \ - INTERNAL_CATCH_REACT(__catchResult) \ - } while (Catch::alwaysFalse()) +#define INTERNAL_CATCH_IF( macroName, resultDisposition, expr ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \ + if( Catch::getResultCapture().getLastResult()->succeeded() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, expr ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \ + if( !Catch::getResultCapture().getLastResult()->succeeded() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, expr ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + try { \ + static_cast(expr); \ + __catchResult.captureResult( Catch::ResultWas::Ok ); \ + } \ + catch( ... ) { \ + __catchResult.useActiveException( resultDisposition ); \ + } \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, matcher, expr ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \ + if( __catchResult.allowThrows() ) \ + try { \ + static_cast(expr); \ + __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ + } \ + catch( ... ) { \ + __catchResult.captureExpectedException( matcher ); \ + } \ + else \ + __catchResult.captureResult( Catch::ResultWas::Ok ); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr ", " #exceptionType, resultDisposition ); \ + if( __catchResult.allowThrows() ) \ + try { \ + static_cast(expr); \ + __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ + } \ + catch( exceptionType ) { \ + __catchResult.captureResult( Catch::ResultWas::Ok ); \ + } \ + catch( ... ) { \ + __catchResult.useActiveException( resultDisposition ); \ + } \ + else \ + __catchResult.captureResult( Catch::ResultWas::Ok ); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) + +/////////////////////////////////////////////////////////////////////////////// +#ifdef CATCH_CONFIG_VARIADIC_MACROS + #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ + __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \ + __catchResult.captureResult( messageType ); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) +#else + #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ + __catchResult << log + ::Catch::StreamEndStop(); \ + __catchResult.captureResult( messageType ); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) +#endif + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_INFO( macroName, log ) \ + Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log; + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \ + try { \ + __catchResult.captureMatch( arg, matcher, #matcher ); \ + } catch( ... ) { \ + __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \ + } \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) // #included from: internal/catch_section.h #define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED @@ -2457,103 +2304,93 @@ struct IRunner namespace Catch { -struct Counts -{ - Counts() : passed(0), failed(0), failedButOk(0) {} + struct Counts { + Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {} - Counts operator-(Counts const& other) const - { - Counts diff; - diff.passed = passed - other.passed; - diff.failed = failed - other.failed; - diff.failedButOk = failedButOk - other.failedButOk; - return diff; - } - Counts& operator+=(Counts const& other) - { - passed += other.passed; - failed += other.failed; - failedButOk += other.failedButOk; - return *this; - } + Counts operator - ( Counts const& other ) const { + Counts diff; + diff.passed = passed - other.passed; + diff.failed = failed - other.failed; + diff.failedButOk = failedButOk - other.failedButOk; + return diff; + } + Counts& operator += ( Counts const& other ) { + passed += other.passed; + failed += other.failed; + failedButOk += other.failedButOk; + return *this; + } - std::size_t total() const - { - return passed + failed + failedButOk; - } - bool allPassed() const - { - return failed == 0 && failedButOk == 0; - } - bool allOk() const - { - return failed == 0; - } + std::size_t total() const { + return passed + failed + failedButOk; + } + bool allPassed() const { + return failed == 0 && failedButOk == 0; + } + bool allOk() const { + return failed == 0; + } - std::size_t passed; - std::size_t failed; - std::size_t failedButOk; -}; + std::size_t passed; + std::size_t failed; + std::size_t failedButOk; + }; -struct Totals -{ + struct Totals { - Totals operator-(Totals const& other) const - { - Totals diff; - diff.assertions = assertions - other.assertions; - diff.testCases = testCases - other.testCases; - return diff; - } + Totals operator - ( Totals const& other ) const { + Totals diff; + diff.assertions = assertions - other.assertions; + diff.testCases = testCases - other.testCases; + return diff; + } - Totals delta(Totals const& prevTotals) const - { - Totals diff = *this - prevTotals; - if (diff.assertions.failed > 0) - ++diff.testCases.failed; - else if (diff.assertions.failedButOk > 0) - ++diff.testCases.failedButOk; - else - ++diff.testCases.passed; - return diff; - } + Totals delta( Totals const& prevTotals ) const { + Totals diff = *this - prevTotals; + if( diff.assertions.failed > 0 ) + ++diff.testCases.failed; + else if( diff.assertions.failedButOk > 0 ) + ++diff.testCases.failedButOk; + else + ++diff.testCases.passed; + return diff; + } - Totals& operator+=(Totals const& other) - { - assertions += other.assertions; - testCases += other.testCases; - return *this; - } + Totals& operator += ( Totals const& other ) { + assertions += other.assertions; + testCases += other.testCases; + return *this; + } - Counts assertions; - Counts testCases; -}; + Counts assertions; + Counts testCases; + }; } +#include + namespace Catch { -struct SectionInfo -{ - SectionInfo(SourceLineInfo const& _lineInfo, + struct SectionInfo { + SectionInfo + ( SourceLineInfo const& _lineInfo, std::string const& _name, - std::string const& _description = std::string()); + std::string const& _description = std::string() ); - std::string name; - std::string description; - SourceLineInfo lineInfo; -}; + std::string name; + std::string description; + SourceLineInfo lineInfo; + }; -struct SectionEndInfo -{ - SectionEndInfo(SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds) - : sectionInfo(_sectionInfo), prevAssertions(_prevAssertions), durationInSeconds(_durationInSeconds) - { - } + struct SectionEndInfo { + SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ) + : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) + {} - SectionInfo sectionInfo; - Counts prevAssertions; - double durationInSeconds; -}; + SectionInfo sectionInfo; + Counts prevAssertions; + double durationInSeconds; + }; } // end namespace Catch @@ -2568,18 +2405,17 @@ typedef unsigned long long uint64_t; namespace Catch { -class Timer -{ - public: - Timer() : m_ticks(0) {} - void start(); - unsigned int getElapsedMicroseconds() const; - unsigned int getElapsedMilliseconds() const; - double getElapsedSeconds() const; + class Timer { + public: + Timer() : m_ticks( 0 ) {} + void start(); + unsigned int getElapsedMicroseconds() const; + unsigned int getElapsedMilliseconds() const; + double getElapsedSeconds() const; - private: - uint64_t m_ticks; -}; + private: + uint64_t m_ticks; + }; } // namespace Catch @@ -2587,166 +2423,150 @@ class Timer namespace Catch { -class Section : NonCopyable -{ - public: - Section(SectionInfo const& info); - ~Section(); + class Section : NonCopyable { + public: + Section( SectionInfo const& info ); + ~Section(); - // This indicates whether the section should be executed or not - operator bool() const; + // This indicates whether the section should be executed or not + operator bool() const; - private: - SectionInfo m_info; + private: + SectionInfo m_info; - std::string m_name; - Counts m_assertions; - bool m_sectionIncluded; - Timer m_timer; -}; + std::string m_name; + Counts m_assertions; + bool m_sectionIncluded; + Timer m_timer; + }; } // end namespace Catch #ifdef CATCH_CONFIG_VARIADIC_MACROS -#define INTERNAL_CATCH_SECTION(...) \ - if (Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME(catch_internal_Section) = Catch::SectionInfo(CATCH_INTERNAL_LINEINFO, __VA_ARGS__)) + #define INTERNAL_CATCH_SECTION( ... ) \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) #else -#define INTERNAL_CATCH_SECTION(name, desc) \ - if (Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME(catch_internal_Section) = Catch::SectionInfo(CATCH_INTERNAL_LINEINFO, name, desc)) + #define INTERNAL_CATCH_SECTION( name, desc ) \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) ) #endif // #included from: internal/catch_generators.hpp #define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED -#include -#include -#include #include +#include +#include namespace Catch { -template -struct IGenerator -{ +template +struct IGenerator { virtual ~IGenerator() {} - virtual T getValue(std::size_t index) const = 0; - virtual std::size_t size() const = 0; + virtual T getValue( std::size_t index ) const = 0; + virtual std::size_t size () const = 0; }; -template -class BetweenGenerator : public IGenerator -{ - public: - BetweenGenerator(T from, T to) : m_from(from), m_to(to) {} +template +class BetweenGenerator : public IGenerator { +public: + BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){} - virtual T getValue(std::size_t index) const - { - return m_from + static_cast(index); + virtual T getValue( std::size_t index ) const { + return m_from+static_cast( index ); } - virtual std::size_t size() const - { - return static_cast(1 + m_to - m_from); + virtual std::size_t size() const { + return static_cast( 1+m_to-m_from ); } - private: +private: + T m_from; T m_to; }; -template -class ValuesGenerator : public IGenerator -{ - public: - ValuesGenerator() {} +template +class ValuesGenerator : public IGenerator { +public: + ValuesGenerator(){} - void add(T value) - { - m_values.push_back(value); + void add( T value ) { + m_values.push_back( value ); } - virtual T getValue(std::size_t index) const - { + virtual T getValue( std::size_t index ) const { return m_values[index]; } - virtual std::size_t size() const - { + virtual std::size_t size() const { return m_values.size(); } - private: +private: std::vector m_values; }; -template -class CompositeGenerator -{ - public: - CompositeGenerator() : m_totalSize(0) {} +template +class CompositeGenerator { +public: + CompositeGenerator() : m_totalSize( 0 ) {} // *** Move semantics, similar to auto_ptr *** - CompositeGenerator(CompositeGenerator& other) - : m_fileInfo(other.m_fileInfo), - m_totalSize(0) + CompositeGenerator( CompositeGenerator& other ) + : m_fileInfo( other.m_fileInfo ), + m_totalSize( 0 ) { - move(other); + move( other ); } - CompositeGenerator& setFileInfo(const char* fileInfo) - { + CompositeGenerator& setFileInfo( const char* fileInfo ) { m_fileInfo = fileInfo; return *this; } - ~CompositeGenerator() - { - deleteAll(m_composed); + ~CompositeGenerator() { + deleteAll( m_composed ); } - operator T() const - { - size_t overallIndex = getCurrentContext().getGeneratorIndex(m_fileInfo, m_totalSize); + operator T () const { + size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize ); typename std::vector*>::const_iterator it = m_composed.begin(); typename std::vector*>::const_iterator itEnd = m_composed.end(); - for (size_t index = 0; it != itEnd; ++it) + for( size_t index = 0; it != itEnd; ++it ) { const IGenerator* generator = *it; - if (overallIndex >= index && overallIndex < index + generator->size()) + if( overallIndex >= index && overallIndex < index + generator->size() ) { - return generator->getValue(overallIndex - index); + return generator->getValue( overallIndex-index ); } index += generator->size(); } - CATCH_INTERNAL_ERROR("Indexed past end of generated range"); + CATCH_INTERNAL_ERROR( "Indexed past end of generated range" ); return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so } - void add(const IGenerator* generator) - { + void add( const IGenerator* generator ) { m_totalSize += generator->size(); - m_composed.push_back(generator); + m_composed.push_back( generator ); } - CompositeGenerator& then(CompositeGenerator& other) - { - move(other); + CompositeGenerator& then( CompositeGenerator& other ) { + move( other ); return *this; } - CompositeGenerator& then(T value) - { + CompositeGenerator& then( T value ) { ValuesGenerator* valuesGen = new ValuesGenerator(); - valuesGen->add(value); - add(valuesGen); + valuesGen->add( value ); + add( valuesGen ); return *this; } - private: - void move(CompositeGenerator& other) - { - std::copy(other.m_composed.begin(), other.m_composed.end(), std::back_inserter(m_composed)); +private: + + void move( CompositeGenerator& other ) { + m_composed.insert( m_composed.end(), other.m_composed.begin(), other.m_composed.end() ); m_totalSize += other.m_totalSize; other.m_composed.clear(); } @@ -2756,50 +2576,47 @@ class CompositeGenerator size_t m_totalSize; }; -namespace Generators { -template -CompositeGenerator between(T from, T to) +namespace Generators { - CompositeGenerator generators; - generators.add(new BetweenGenerator(from, to)); - return generators; -} + template + CompositeGenerator between( T from, T to ) { + CompositeGenerator generators; + generators.add( new BetweenGenerator( from, to ) ); + return generators; + } -template -CompositeGenerator values(T val1, T val2) -{ - CompositeGenerator generators; - ValuesGenerator* valuesGen = new ValuesGenerator(); - valuesGen->add(val1); - valuesGen->add(val2); - generators.add(valuesGen); - return generators; -} + template + CompositeGenerator values( T val1, T val2 ) { + CompositeGenerator generators; + ValuesGenerator* valuesGen = new ValuesGenerator(); + valuesGen->add( val1 ); + valuesGen->add( val2 ); + generators.add( valuesGen ); + return generators; + } -template -CompositeGenerator values(T val1, T val2, T val3) -{ - CompositeGenerator generators; - ValuesGenerator* valuesGen = new ValuesGenerator(); - valuesGen->add(val1); - valuesGen->add(val2); - valuesGen->add(val3); - generators.add(valuesGen); - return generators; -} + template + CompositeGenerator values( T val1, T val2, T val3 ){ + CompositeGenerator generators; + ValuesGenerator* valuesGen = new ValuesGenerator(); + valuesGen->add( val1 ); + valuesGen->add( val2 ); + valuesGen->add( val3 ); + generators.add( valuesGen ); + return generators; + } -template -CompositeGenerator values(T val1, T val2, T val3, T val4) -{ - CompositeGenerator generators; - ValuesGenerator* valuesGen = new ValuesGenerator(); - valuesGen->add(val1); - valuesGen->add(val2); - valuesGen->add(val3); - valuesGen->add(val4); - generators.add(valuesGen); - return generators; -} + template + CompositeGenerator values( T val1, T val2, T val3, T val4 ) { + CompositeGenerator generators; + ValuesGenerator* valuesGen = new ValuesGenerator(); + valuesGen->add( val1 ); + valuesGen->add( val2 ); + valuesGen->add( val3 ); + valuesGen->add( val4 ); + generators.add( valuesGen ); + return generators; + } } // end namespace Generators @@ -2807,10 +2624,10 @@ using namespace Generators; } // end namespace Catch -#define INTERNAL_CATCH_LINESTR2(line) #line -#define INTERNAL_CATCH_LINESTR(line) INTERNAL_CATCH_LINESTR2(line) +#define INTERNAL_CATCH_LINESTR2( line ) #line +#define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line ) -#define INTERNAL_CATCH_GENERATE(expr) expr.setFileInfo(__FILE__ "(" INTERNAL_CATCH_LINESTR(__LINE__) ")") +#define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" ) // #included from: internal/catch_interfaces_exception.h #define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED @@ -2825,103 +2642,99 @@ using namespace Generators; namespace Catch { -class TestCase; -struct ITestCaseRegistry; -struct IExceptionTranslatorRegistry; -struct IExceptionTranslator; -struct IReporterRegistry; -struct IReporterFactory; + class TestCase; + struct ITestCaseRegistry; + struct IExceptionTranslatorRegistry; + struct IExceptionTranslator; + struct IReporterRegistry; + struct IReporterFactory; + struct ITagAliasRegistry; -struct IRegistryHub -{ - virtual ~IRegistryHub(); + struct IRegistryHub { + virtual ~IRegistryHub(); - virtual IReporterRegistry const& getReporterRegistry() const = 0; - virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; - virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; -}; + virtual IReporterRegistry const& getReporterRegistry() const = 0; + virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; + virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; -struct IMutableRegistryHub -{ - virtual ~IMutableRegistryHub(); - virtual void registerReporter(std::string const& name, Ptr const& factory) = 0; - virtual void registerListener(Ptr const& factory) = 0; - virtual void registerTest(TestCase const& testInfo) = 0; - virtual void registerTranslator(const IExceptionTranslator* translator) = 0; -}; + virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; + }; + + struct IMutableRegistryHub { + virtual ~IMutableRegistryHub(); + virtual void registerReporter( std::string const& name, Ptr const& factory ) = 0; + virtual void registerListener( Ptr const& factory ) = 0; + virtual void registerTest( TestCase const& testInfo ) = 0; + virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; + virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; + }; + + IRegistryHub& getRegistryHub(); + IMutableRegistryHub& getMutableRegistryHub(); + void cleanUp(); + std::string translateActiveException(); -IRegistryHub& getRegistryHub(); -IMutableRegistryHub& getMutableRegistryHub(); -void cleanUp(); -std::string translateActiveException(); } namespace Catch { -typedef std::string (*exceptionTranslateFunction)(); + typedef std::string(*exceptionTranslateFunction)(); -struct IExceptionTranslator; -typedef std::vector ExceptionTranslators; + struct IExceptionTranslator; + typedef std::vector ExceptionTranslators; -struct IExceptionTranslator -{ - virtual ~IExceptionTranslator(); - virtual std::string translate(ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd) const = 0; -}; - -struct IExceptionTranslatorRegistry -{ - virtual ~IExceptionTranslatorRegistry(); - - virtual std::string translateActiveException() const = 0; -}; - -class ExceptionTranslatorRegistrar -{ - template - class ExceptionTranslator : public IExceptionTranslator - { - public: - ExceptionTranslator(std::string (*translateFunction)(T&)) - : m_translateFunction(translateFunction) - { - } - - virtual std::string translate(ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd) const CATCH_OVERRIDE - { - try - { - if (it == itEnd) - throw; - else - return (*it)->translate(it + 1, itEnd); - } - catch (T& ex) - { - return m_translateFunction(ex); - } - } - - protected: - std::string (*m_translateFunction)(T&); + struct IExceptionTranslator { + virtual ~IExceptionTranslator(); + virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0; }; - public: - template - ExceptionTranslatorRegistrar(std::string (*translateFunction)(T&)) - { - getMutableRegistryHub().registerTranslator(new ExceptionTranslator(translateFunction)); - } -}; + struct IExceptionTranslatorRegistry { + virtual ~IExceptionTranslatorRegistry(); + + virtual std::string translateActiveException() const = 0; + }; + + class ExceptionTranslatorRegistrar { + template + class ExceptionTranslator : public IExceptionTranslator { + public: + + ExceptionTranslator( std::string(*translateFunction)( T& ) ) + : m_translateFunction( translateFunction ) + {} + + virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const CATCH_OVERRIDE { + try { + if( it == itEnd ) + throw; + else + return (*it)->translate( it+1, itEnd ); + } + catch( T& ex ) { + return m_translateFunction( ex ); + } + } + + protected: + std::string(*m_translateFunction)( T& ); + }; + + public: + template + ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) { + getMutableRegistryHub().registerTranslator + ( new ExceptionTranslator( translateFunction ) ); + } + }; } /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_TRANSLATE_EXCEPTION(signature) \ - static std::string INTERNAL_CATCH_UNIQUE_NAME(catch_internal_ExceptionTranslator)(signature); \ - namespace { \ - Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME(catch_internal_ExceptionRegistrar)(&INTERNAL_CATCH_UNIQUE_NAME(catch_internal_ExceptionTranslator)); \ - } \ - static std::string INTERNAL_CATCH_UNIQUE_NAME(catch_internal_ExceptionTranslator)(signature) +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \ + static std::string translatorName( signature ); \ + namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\ + static std::string translatorName( signature ) + +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) // #included from: internal/catch_approx.hpp #define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED @@ -2929,94 +2742,314 @@ class ExceptionTranslatorRegistrar #include #include +#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) +#include +#endif + namespace Catch { namespace Detail { -class Approx -{ - public: - explicit Approx(double value) - : m_epsilon(std::numeric_limits::epsilon() * 100), - m_scale(1.0), - m_value(value) - { - } + class Approx { + public: + explicit Approx ( double value ) + : m_epsilon( std::numeric_limits::epsilon()*100 ), + m_margin( 0.0 ), + m_scale( 1.0 ), + m_value( value ) + {} - Approx(Approx const& other) - : m_epsilon(other.m_epsilon), - m_scale(other.m_scale), - m_value(other.m_value) - { - } + Approx( Approx const& other ) + : m_epsilon( other.m_epsilon ), + m_margin( other.m_margin ), + m_scale( other.m_scale ), + m_value( other.m_value ) + {} - static Approx custom() - { - return Approx(0); - } + static Approx custom() { + return Approx( 0 ); + } - Approx operator()(double value) - { - Approx approx(value); - approx.epsilon(m_epsilon); - approx.scale(m_scale); - return approx; - } + Approx operator()( double value ) { + Approx approx( value ); + approx.epsilon( m_epsilon ); + approx.margin( m_margin ); + approx.scale( m_scale ); + return approx; + } - friend bool operator==(double lhs, Approx const& rhs) - { - // Thanks to Richard Harris for his help refining this formula - return fabs(lhs - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + (std::max)(fabs(lhs), fabs(rhs.m_value))); - } +#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) - friend bool operator==(Approx const& lhs, double rhs) - { - return operator==(rhs, lhs); - } + template ::value>::type> + explicit Approx( T value ): Approx(static_cast(value)) + {} - friend bool operator!=(double lhs, Approx const& rhs) - { - return !operator==(lhs, rhs); - } + template ::value>::type> + friend bool operator == ( const T& lhs, Approx const& rhs ) { + // Thanks to Richard Harris for his help refining this formula + auto lhs_v = double(lhs); + bool relativeOK = std::fabs(lhs_v - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + (std::max)(std::fabs(lhs_v), std::fabs(rhs.m_value))); + if (relativeOK) { + return true; + } + return std::fabs(lhs_v - rhs.m_value) < rhs.m_margin; + } - friend bool operator!=(Approx const& lhs, double rhs) - { - return !operator==(rhs, lhs); - } + template ::value>::type> + friend bool operator == ( Approx const& lhs, const T& rhs ) { + return operator==( rhs, lhs ); + } - Approx& epsilon(double newEpsilon) - { - m_epsilon = newEpsilon; - return *this; - } + template ::value>::type> + friend bool operator != ( T lhs, Approx const& rhs ) { + return !operator==( lhs, rhs ); + } - Approx& scale(double newScale) - { - m_scale = newScale; - return *this; - } + template ::value>::type> + friend bool operator != ( Approx const& lhs, T rhs ) { + return !operator==( rhs, lhs ); + } - std::string toString() const - { - std::ostringstream oss; - oss << "Approx( " << Catch::toString(m_value) << " )"; - return oss.str(); - } + template ::value>::type> + friend bool operator <= ( T lhs, Approx const& rhs ) { + return double(lhs) < rhs.m_value || lhs == rhs; + } - private: - double m_epsilon; - double m_scale; - double m_value; -}; + template ::value>::type> + friend bool operator <= ( Approx const& lhs, T rhs ) { + return lhs.m_value < double(rhs) || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( T lhs, Approx const& rhs ) { + return double(lhs) > rhs.m_value || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( Approx const& lhs, T rhs ) { + return lhs.m_value > double(rhs) || lhs == rhs; + } +#else + friend bool operator == ( double lhs, Approx const& rhs ) { + // Thanks to Richard Harris for his help refining this formula + bool relativeOK = std::fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs), std::fabs(rhs.m_value) ) ); + if (relativeOK) { + return true; + } + return std::fabs(lhs - rhs.m_value) < rhs.m_margin; + } + + friend bool operator == ( Approx const& lhs, double rhs ) { + return operator==( rhs, lhs ); + } + + friend bool operator != ( double lhs, Approx const& rhs ) { + return !operator==( lhs, rhs ); + } + + friend bool operator != ( Approx const& lhs, double rhs ) { + return !operator==( rhs, lhs ); + } + + friend bool operator <= ( double lhs, Approx const& rhs ) { + return lhs < rhs.m_value || lhs == rhs; + } + + friend bool operator <= ( Approx const& lhs, double rhs ) { + return lhs.m_value < rhs || lhs == rhs; + } + + friend bool operator >= ( double lhs, Approx const& rhs ) { + return lhs > rhs.m_value || lhs == rhs; + } + + friend bool operator >= ( Approx const& lhs, double rhs ) { + return lhs.m_value > rhs || lhs == rhs; + } +#endif + + Approx& epsilon( double newEpsilon ) { + m_epsilon = newEpsilon; + return *this; + } + + Approx& margin( double newMargin ) { + m_margin = newMargin; + return *this; + } + + Approx& scale( double newScale ) { + m_scale = newScale; + return *this; + } + + std::string toString() const { + std::ostringstream oss; + oss << "Approx( " << Catch::toString( m_value ) << " )"; + return oss.str(); + } + + private: + double m_epsilon; + double m_margin; + double m_scale; + double m_value; + }; } -template <> -inline std::string toString(Detail::Approx const& value) -{ +template<> +inline std::string toString( Detail::Approx const& value ) { return value.toString(); } } // end namespace Catch +// #included from: internal/catch_matchers_string.h +#define TWOBLUECUBES_CATCH_MATCHERS_STRING_H_INCLUDED + +namespace Catch { +namespace Matchers { + + namespace StdString { + + struct CasedString + { + CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ); + std::string adjustString( std::string const& str ) const; + std::string caseSensitivitySuffix() const; + + CaseSensitive::Choice m_caseSensitivity; + std::string m_str; + }; + + struct StringMatcherBase : MatcherBase { + StringMatcherBase( std::string const& operation, CasedString const& comparator ); + virtual std::string describe() const CATCH_OVERRIDE; + + CasedString m_comparator; + std::string m_operation; + }; + + struct EqualsMatcher : StringMatcherBase { + EqualsMatcher( CasedString const& comparator ); + virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + }; + struct ContainsMatcher : StringMatcherBase { + ContainsMatcher( CasedString const& comparator ); + virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + }; + struct StartsWithMatcher : StringMatcherBase { + StartsWithMatcher( CasedString const& comparator ); + virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + }; + struct EndsWithMatcher : StringMatcherBase { + EndsWithMatcher( CasedString const& comparator ); + virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + }; + + } // namespace StdString + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + +} // namespace Matchers +} // namespace Catch + +// #included from: internal/catch_matchers_vector.h +#define TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED + +namespace Catch { +namespace Matchers { + + namespace Vector { + + template + struct ContainsElementMatcher : MatcherBase, T> { + + ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} + + bool match(std::vector const &v) const CATCH_OVERRIDE { + return std::find(v.begin(), v.end(), m_comparator) != v.end(); + } + + virtual std::string describe() const CATCH_OVERRIDE { + return "Contains: " + Catch::toString( m_comparator ); + } + + T const& m_comparator; + }; + + template + struct ContainsMatcher : MatcherBase, std::vector > { + + ContainsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector const &v) const CATCH_OVERRIDE { + // !TBD: see note in EqualsMatcher + if (m_comparator.size() > v.size()) + return false; + for (size_t i = 0; i < m_comparator.size(); ++i) + if (std::find(v.begin(), v.end(), m_comparator[i]) == v.end()) + return false; + return true; + } + virtual std::string describe() const CATCH_OVERRIDE { + return "Contains: " + Catch::toString( m_comparator ); + } + + std::vector const& m_comparator; + }; + + template + struct EqualsMatcher : MatcherBase, std::vector > { + + EqualsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector const &v) const CATCH_OVERRIDE { + // !TBD: This currently works if all elements can be compared using != + // - a more general approach would be via a compare template that defaults + // to using !=. but could be specialised for, e.g. std::vector etc + // - then just call that directly + if (m_comparator.size() != v.size()) + return false; + for (size_t i = 0; i < v.size(); ++i) + if (m_comparator[i] != v[i]) + return false; + return true; + } + virtual std::string describe() const CATCH_OVERRIDE { + return "Equals: " + Catch::toString( m_comparator ); + } + std::vector const& m_comparator; + }; + + } // namespace Vector + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + template + Vector::ContainsMatcher Contains( std::vector const& comparator ) { + return Vector::ContainsMatcher( comparator ); + } + + template + Vector::ContainsElementMatcher VectorContains( T const& comparator ) { + return Vector::ContainsElementMatcher( comparator ); + } + + template + Vector::EqualsMatcher Equals( std::vector const& comparator ) { + return Vector::EqualsMatcher( comparator ); + } + +} // namespace Matchers +} // namespace Catch + // #included from: internal/catch_interfaces_tag_alias_registry.h #define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED @@ -3027,110 +3060,94 @@ inline std::string toString(Detail::Approx const& value) namespace Catch { -struct TagAlias -{ - TagAlias(std::string _tag, SourceLineInfo _lineInfo) : tag(_tag), lineInfo(_lineInfo) {} + struct TagAlias { + TagAlias( std::string const& _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {} - std::string tag; - SourceLineInfo lineInfo; -}; + std::string tag; + SourceLineInfo lineInfo; + }; -struct RegistrarForTagAliases -{ - RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo); -}; + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + }; } // end namespace Catch -#define CATCH_REGISTER_TAG_ALIAS(alias, spec) \ - namespace { \ - Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME(AutoRegisterTagAlias)(alias, spec, CATCH_INTERNAL_LINEINFO); \ - } +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } // #included from: catch_option.hpp #define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED namespace Catch { -// An optional type -template -class Option -{ - public: - Option() : nullableValue(CATCH_NULL) {} - Option(T const& _value) - : nullableValue(new (storage) T(_value)) - { - } - Option(Option const& _other) - : nullableValue(_other ? new (storage) T(*_other) : CATCH_NULL) - { - } + // An optional type + template + class Option { + public: + Option() : nullableValue( CATCH_NULL ) {} + Option( T const& _value ) + : nullableValue( new( storage ) T( _value ) ) + {} + Option( Option const& _other ) + : nullableValue( _other ? new( storage ) T( *_other ) : CATCH_NULL ) + {} - ~Option() - { - reset(); - } - - Option& operator=(Option const& _other) - { - if (&_other != this) - { + ~Option() { reset(); - if (_other) - nullableValue = new (storage) T(*_other); } - return *this; - } - Option& operator=(T const& _value) - { - reset(); - nullableValue = new (storage) T(_value); - return *this; - } - void reset() - { - if (nullableValue) - nullableValue->~T(); - nullableValue = CATCH_NULL; - } + Option& operator= ( Option const& _other ) { + if( &_other != this ) { + reset(); + if( _other ) + nullableValue = new( storage ) T( *_other ); + } + return *this; + } + Option& operator = ( T const& _value ) { + reset(); + nullableValue = new( storage ) T( _value ); + return *this; + } - T& operator*() { return *nullableValue; } - T const& operator*() const { return *nullableValue; } - T* operator->() { return nullableValue; } - const T* operator->() const { return nullableValue; } + void reset() { + if( nullableValue ) + nullableValue->~T(); + nullableValue = CATCH_NULL; + } - T valueOr(T const& defaultValue) const - { - return nullableValue ? *nullableValue : defaultValue; - } + T& operator*() { return *nullableValue; } + T const& operator*() const { return *nullableValue; } + T* operator->() { return nullableValue; } + const T* operator->() const { return nullableValue; } - bool some() const { return nullableValue != CATCH_NULL; } - bool none() const { return nullableValue == CATCH_NULL; } + T valueOr( T const& defaultValue ) const { + return nullableValue ? *nullableValue : defaultValue; + } - bool operator!() const { return nullableValue == CATCH_NULL; } - operator SafeBool::type() const - { - return SafeBool::makeSafe(some()); - } + bool some() const { return nullableValue != CATCH_NULL; } + bool none() const { return nullableValue == CATCH_NULL; } - private: - T* nullableValue; - char storage[sizeof(T)]; -}; + bool operator !() const { return nullableValue == CATCH_NULL; } + operator SafeBool::type() const { + return SafeBool::makeSafe( some() ); + } + + private: + T* nullableValue; + char storage[sizeof(T)]; + }; } // end namespace Catch namespace Catch { -struct ITagAliasRegistry -{ - virtual ~ITagAliasRegistry(); - virtual Option find(std::string const& alias) const = 0; - virtual std::string expandAliases(std::string const& unexpandedTestSpec) const = 0; + struct ITagAliasRegistry { + virtual ~ITagAliasRegistry(); + virtual Option find( std::string const& alias ) const = 0; + virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; - static ITagAliasRegistry const& get(); -}; + static ITagAliasRegistry const& get(); + }; } // end namespace Catch @@ -3139,8 +3156,8 @@ struct ITagAliasRegistry // #included from: internal/catch_test_case_info.h #define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED -#include #include +#include #ifdef __clang__ #pragma clang diagnostic push @@ -3149,76 +3166,76 @@ struct ITagAliasRegistry namespace Catch { -struct ITestCase; + struct ITestCase; -struct TestCaseInfo -{ - enum SpecialProperties - { - None = 0, - IsHidden = 1 << 1, - ShouldFail = 1 << 2, - MayFail = 1 << 3, - Throws = 1 << 4 + struct TestCaseInfo { + enum SpecialProperties{ + None = 0, + IsHidden = 1 << 1, + ShouldFail = 1 << 2, + MayFail = 1 << 3, + Throws = 1 << 4, + NonPortable = 1 << 5 + }; + + TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::set const& _tags, + SourceLineInfo const& _lineInfo ); + + TestCaseInfo( TestCaseInfo const& other ); + + friend void setTags( TestCaseInfo& testCaseInfo, std::set const& tags ); + + bool isHidden() const; + bool throws() const; + bool okToFail() const; + bool expectedToFail() const; + + std::string name; + std::string className; + std::string description; + std::set tags; + std::set lcaseTags; + std::string tagsAsString; + SourceLineInfo lineInfo; + SpecialProperties properties; }; - TestCaseInfo(std::string const& _name, - std::string const& _className, - std::string const& _description, - std::set const& _tags, - SourceLineInfo const& _lineInfo); + class TestCase : public TestCaseInfo { + public: - TestCaseInfo(TestCaseInfo const& other); + TestCase( ITestCase* testCase, TestCaseInfo const& info ); + TestCase( TestCase const& other ); - friend void setTags(TestCaseInfo& testCaseInfo, std::set const& tags); + TestCase withName( std::string const& _newName ) const; - bool isHidden() const; - bool throws() const; - bool okToFail() const; - bool expectedToFail() const; + void invoke() const; - std::string name; - std::string className; - std::string description; - std::set tags; - std::set lcaseTags; - std::string tagsAsString; - SourceLineInfo lineInfo; - SpecialProperties properties; -}; + TestCaseInfo const& getTestCaseInfo() const; -class TestCase : public TestCaseInfo -{ - public: - TestCase(ITestCase* testCase, TestCaseInfo const& info); - TestCase(TestCase const& other); + void swap( TestCase& other ); + bool operator == ( TestCase const& other ) const; + bool operator < ( TestCase const& other ) const; + TestCase& operator = ( TestCase const& other ); - TestCase withName(std::string const& _newName) const; + private: + Ptr test; + }; - void invoke() const; - - TestCaseInfo const& getTestCaseInfo() const; - - void swap(TestCase& other); - bool operator==(TestCase const& other) const; - bool operator<(TestCase const& other) const; - TestCase& operator=(TestCase const& other); - - private: - Ptr test; -}; - -TestCase makeTestCase(ITestCase* testCase, - std::string const& className, - std::string const& name, - std::string const& description, - SourceLineInfo const& lineInfo); + TestCase makeTestCase( ITestCase* testCase, + std::string const& className, + std::string const& name, + std::string const& description, + SourceLineInfo const& lineInfo ); } #ifdef __clang__ #pragma clang diagnostic pop #endif + #ifdef __OBJC__ // #included from: internal/catch_objc.hpp #define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED @@ -3238,204 +3255,208 @@ TestCase makeTestCase(ITestCase* testCase, @optional -- (void)setUp; -- (void)tearDown; +-(void) setUp; +-(void) tearDown; @end namespace Catch { -class OcMethod : public SharedImpl -{ + class OcMethod : public SharedImpl { - public: - OcMethod(Class cls, SEL sel) : m_cls(cls), m_sel(sel) {} + public: + OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} - virtual void invoke() const - { - id obj = [[m_cls alloc] init]; + virtual void invoke() const { + id obj = [[m_cls alloc] init]; - performOptionalSelector(obj, @selector(setUp)); - performOptionalSelector(obj, m_sel); - performOptionalSelector(obj, @selector(tearDown)); + performOptionalSelector( obj, @selector(setUp) ); + performOptionalSelector( obj, m_sel ); + performOptionalSelector( obj, @selector(tearDown) ); - arcSafeRelease(obj); - } + arcSafeRelease( obj ); + } + private: + virtual ~OcMethod() {} - private: - virtual ~OcMethod() {} + Class m_cls; + SEL m_sel; + }; - Class m_cls; - SEL m_sel; -}; + namespace Detail{ -namespace Detail { - -inline std::string getAnnotation(Class cls, - std::string const& annotationName, - std::string const& testCaseName) -{ - NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; - SEL sel = NSSelectorFromString(selStr); - arcSafeRelease(selStr); - id value = performOptionalSelector(cls, sel); - if (value) - return [(NSString*)value UTF8String]; - return ""; -} -} - -inline size_t registerTestMethods() -{ - size_t noTestMethods = 0; - int noClasses = objc_getClassList(CATCH_NULL, 0); - - Class* classes = (CATCH_UNSAFE_UNRETAINED Class*)malloc(sizeof(Class) * noClasses); - objc_getClassList(classes, noClasses); - - for (int c = 0; c < noClasses; c++) - { - Class cls = classes[c]; - { - u_int count; - Method* methods = class_copyMethodList(cls, &count); - for (u_int m = 0; m < count; m++) - { - SEL selector = method_getName(methods[m]); - std::string methodName = sel_getName(selector); - if (startsWith(methodName, "Catch_TestCase_")) - { - std::string testCaseName = methodName.substr(15); - std::string name = Detail::getAnnotation(cls, "Name", testCaseName); - std::string desc = Detail::getAnnotation(cls, "Description", testCaseName); - const char* className = class_getName(cls); - - getMutableRegistryHub().registerTest(makeTestCase(new OcMethod(cls, selector), className, name.c_str(), desc.c_str(), SourceLineInfo())); - noTestMethods++; - } - } - free(methods); + inline std::string getAnnotation( Class cls, + std::string const& annotationName, + std::string const& testCaseName ) { + NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; + SEL sel = NSSelectorFromString( selStr ); + arcSafeRelease( selStr ); + id value = performOptionalSelector( cls, sel ); + if( value ) + return [(NSString*)value UTF8String]; + return ""; } } - return noTestMethods; -} -namespace Matchers { -namespace Impl { -namespace NSStringMatchers { + inline size_t registerTestMethods() { + size_t noTestMethods = 0; + int noClasses = objc_getClassList( CATCH_NULL, 0 ); -template -struct StringHolder : MatcherImpl -{ - StringHolder(NSString* substr) : m_substr([substr copy]) {} - StringHolder(StringHolder const& other) : m_substr([other.m_substr copy]) {} - StringHolder() - { - arcSafeRelease(m_substr); + Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); + objc_getClassList( classes, noClasses ); + + for( int c = 0; c < noClasses; c++ ) { + Class cls = classes[c]; + { + u_int count; + Method* methods = class_copyMethodList( cls, &count ); + for( u_int m = 0; m < count ; m++ ) { + SEL selector = method_getName(methods[m]); + std::string methodName = sel_getName(selector); + if( startsWith( methodName, "Catch_TestCase_" ) ) { + std::string testCaseName = methodName.substr( 15 ); + std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); + std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); + const char* className = class_getName( cls ); + + getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) ); + noTestMethods++; + } + } + free(methods); + } + } + return noTestMethods; } - NSString* m_substr; -}; + namespace Matchers { + namespace Impl { + namespace NSStringMatchers { -struct Equals : StringHolder -{ - Equals(NSString* substr) : StringHolder(substr) {} + struct StringHolder : MatcherBase{ + StringHolder( NSString* substr ) : m_substr( [substr copy] ){} + StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} + StringHolder() { + arcSafeRelease( m_substr ); + } - virtual bool match(ExpressionType const& str) const - { - return (str != nil || m_substr == nil) && - [str isEqualToString:m_substr]; - } + virtual bool match( NSString* arg ) const CATCH_OVERRIDE { + return false; + } - virtual std::string toString() const - { - return "equals string: " + Catch::toString(m_substr); - } -}; + NSString* m_substr; + }; -struct Contains : StringHolder -{ - Contains(NSString* substr) : StringHolder(substr) {} + struct Equals : StringHolder { + Equals( NSString* substr ) : StringHolder( substr ){} - virtual bool match(ExpressionType const& str) const - { - return (str != nil || m_substr == nil) && - [str rangeOfString:m_substr].location != NSNotFound; - } + virtual bool match( NSString* str ) const CATCH_OVERRIDE { + return (str != nil || m_substr == nil ) && + [str isEqualToString:m_substr]; + } - virtual std::string toString() const - { - return "contains string: " + Catch::toString(m_substr); - } -}; + virtual std::string describe() const CATCH_OVERRIDE { + return "equals string: " + Catch::toString( m_substr ); + } + }; -struct StartsWith : StringHolder -{ - StartsWith(NSString* substr) : StringHolder(substr) {} + struct Contains : StringHolder { + Contains( NSString* substr ) : StringHolder( substr ){} - virtual bool match(ExpressionType const& str) const - { - return (str != nil || m_substr == nil) && - [str rangeOfString:m_substr].location == 0; - } + virtual bool match( NSString* str ) const { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location != NSNotFound; + } - virtual std::string toString() const - { - return "starts with: " + Catch::toString(m_substr); - } -}; -struct EndsWith : StringHolder -{ - EndsWith(NSString* substr) : StringHolder(substr) {} + virtual std::string describe() const CATCH_OVERRIDE { + return "contains string: " + Catch::toString( m_substr ); + } + }; - virtual bool match(ExpressionType const& str) const - { - return (str != nil || m_substr == nil) && - [str rangeOfString:m_substr].location == [str length] - [m_substr length]; - } + struct StartsWith : StringHolder { + StartsWith( NSString* substr ) : StringHolder( substr ){} - virtual std::string toString() const - { - return "ends with: " + Catch::toString(m_substr); - } -}; + virtual bool match( NSString* str ) const { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location == 0; + } -} // namespace NSStringMatchers -} // namespace Impl + virtual std::string describe() const CATCH_OVERRIDE { + return "starts with: " + Catch::toString( m_substr ); + } + }; + struct EndsWith : StringHolder { + EndsWith( NSString* substr ) : StringHolder( substr ){} -inline Impl::NSStringMatchers::Equals -Equals(NSString* substr) { return Impl::NSStringMatchers::Equals(substr); } + virtual bool match( NSString* str ) const { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location == [str length] - [m_substr length]; + } -inline Impl::NSStringMatchers::Contains -Contains(NSString* substr) { return Impl::NSStringMatchers::Contains(substr); } + virtual std::string describe() const CATCH_OVERRIDE { + return "ends with: " + Catch::toString( m_substr ); + } + }; -inline Impl::NSStringMatchers::StartsWith -StartsWith(NSString* substr) { return Impl::NSStringMatchers::StartsWith(substr); } + } // namespace NSStringMatchers + } // namespace Impl -inline Impl::NSStringMatchers::EndsWith -EndsWith(NSString* substr) { return Impl::NSStringMatchers::EndsWith(substr); } + inline Impl::NSStringMatchers::Equals + Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); } -} // namespace Matchers + inline Impl::NSStringMatchers::Contains + Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); } -using namespace Matchers; + inline Impl::NSStringMatchers::StartsWith + StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); } + + inline Impl::NSStringMatchers::EndsWith + EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); } + + } // namespace Matchers + + using namespace Matchers; } // namespace Catch /////////////////////////////////////////////////////////////////////////////// -#define OC_TEST_CASE(name, desc) \ - +(NSString*)INTERNAL_CATCH_UNIQUE_NAME(Catch_Name_test) \ - { \ - return @name; \ - } \ - +(NSString*)INTERNAL_CATCH_UNIQUE_NAME(Catch_Description_test) \ - { \ - return @desc; \ - } \ - -(void)INTERNAL_CATCH_UNIQUE_NAME(Catch_TestCase_test) +#define OC_TEST_CASE( name, desc )\ ++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \ +{\ +return @ name; \ +}\ ++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \ +{ \ +return @ desc; \ +} \ +-(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test ) #endif #ifdef CATCH_IMPL + +// !TBD: Move the leak detector code into a separate header +#ifdef CATCH_CONFIG_WINDOWS_CRTDBG +#include +class LeakDetector { +public: + LeakDetector() { + int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + flag |= _CRTDBG_LEAK_CHECK_DF; + flag |= _CRTDBG_ALLOC_MEM_DF; + _CrtSetDbgFlag(flag); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + // Change this to leaking allocation's number to break there + _CrtSetBreakAlloc(-1); + } +}; +#else +class LeakDetector {}; +#endif + +LeakDetector leakDetector; + // #included from: internal/catch_impl.hpp #define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED @@ -3475,68 +3496,64 @@ using namespace Matchers; // #included from: catch_wildcard_pattern.hpp #define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED -namespace Catch { -class WildcardPattern -{ - enum WildcardPosition - { - NoWildcard = 0, - WildcardAtStart = 1, - WildcardAtEnd = 2, - WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd - }; +#include - public: - WildcardPattern(std::string const& pattern, CaseSensitive::Choice caseSensitivity) - : m_caseSensitivity(caseSensitivity), - m_wildcard(NoWildcard), - m_pattern(adjustCase(pattern)) - { - if (startsWith(m_pattern, "*")) +namespace Catch +{ + class WildcardPattern { + enum WildcardPosition { + NoWildcard = 0, + WildcardAtStart = 1, + WildcardAtEnd = 2, + WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd + }; + + public: + + WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_wildcard( NoWildcard ), + m_pattern( adjustCase( pattern ) ) { - m_pattern = m_pattern.substr(1); - m_wildcard = WildcardAtStart; - } - if (endsWith(m_pattern, "*")) - { - m_pattern = m_pattern.substr(0, m_pattern.size() - 1); - m_wildcard = static_cast(m_wildcard | WildcardAtEnd); - } - } - virtual ~WildcardPattern(); - virtual bool matches(std::string const& str) const - { - switch (m_wildcard) - { - case NoWildcard: - return m_pattern == adjustCase(str); - case WildcardAtStart: - return endsWith(adjustCase(str), m_pattern); - case WildcardAtEnd: - return startsWith(adjustCase(str), m_pattern); - case WildcardAtBothEnds: - return contains(adjustCase(str), m_pattern); + if( startsWith( m_pattern, '*' ) ) { + m_pattern = m_pattern.substr( 1 ); + m_wildcard = WildcardAtStart; + } + if( endsWith( m_pattern, '*' ) ) { + m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); + m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); + } } + virtual ~WildcardPattern(); + virtual bool matches( std::string const& str ) const { + switch( m_wildcard ) { + case NoWildcard: + return m_pattern == adjustCase( str ); + case WildcardAtStart: + return endsWith( adjustCase( str ), m_pattern ); + case WildcardAtEnd: + return startsWith( adjustCase( str ), m_pattern ); + case WildcardAtBothEnds: + return contains( adjustCase( str ), m_pattern ); + } #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunreachable-code" #endif - throw std::logic_error("Unknown enum"); + throw std::logic_error( "Unknown enum" ); #ifdef __clang__ #pragma clang diagnostic pop #endif - } - - private: - std::string adjustCase(std::string const& str) const - { - return m_caseSensitivity == CaseSensitive::No ? toLower(str) : str; - } - CaseSensitive::Choice m_caseSensitivity; - WildcardPosition m_wildcard; - std::string m_pattern; -}; + } + private: + std::string adjustCase( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; + } + CaseSensitive::Choice m_caseSensitivity; + WildcardPosition m_wildcard; + std::string m_pattern; + }; } #include @@ -3544,87 +3561,74 @@ class WildcardPattern namespace Catch { -class TestSpec -{ - struct Pattern : SharedImpl<> - { - virtual ~Pattern(); - virtual bool matches(TestCaseInfo const& testCase) const = 0; - }; - class NamePattern : public Pattern - { - public: - NamePattern(std::string const& name) - : m_wildcardPattern(toLower(name), CaseSensitive::No) - { - } - virtual ~NamePattern(); - virtual bool matches(TestCaseInfo const& testCase) const - { - return m_wildcardPattern.matches(toLower(testCase.name)); - } + class TestSpec { + struct Pattern : SharedImpl<> { + virtual ~Pattern(); + virtual bool matches( TestCaseInfo const& testCase ) const = 0; + }; + class NamePattern : public Pattern { + public: + NamePattern( std::string const& name ) + : m_wildcardPattern( toLower( name ), CaseSensitive::No ) + {} + virtual ~NamePattern(); + virtual bool matches( TestCaseInfo const& testCase ) const { + return m_wildcardPattern.matches( toLower( testCase.name ) ); + } + private: + WildcardPattern m_wildcardPattern; + }; - private: - WildcardPattern m_wildcardPattern; - }; + class TagPattern : public Pattern { + public: + TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} + virtual ~TagPattern(); + virtual bool matches( TestCaseInfo const& testCase ) const { + return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end(); + } + private: + std::string m_tag; + }; - class TagPattern : public Pattern - { - public: - TagPattern(std::string const& tag) : m_tag(toLower(tag)) {} - virtual ~TagPattern(); - virtual bool matches(TestCaseInfo const& testCase) const - { - return testCase.lcaseTags.find(m_tag) != testCase.lcaseTags.end(); - } + class ExcludedPattern : public Pattern { + public: + ExcludedPattern( Ptr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} + virtual ~ExcludedPattern(); + virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } + private: + Ptr m_underlyingPattern; + }; - private: - std::string m_tag; - }; + struct Filter { + std::vector > m_patterns; - class ExcludedPattern : public Pattern - { - public: - ExcludedPattern(Ptr const& underlyingPattern) : m_underlyingPattern(underlyingPattern) {} - virtual ~ExcludedPattern(); - virtual bool matches(TestCaseInfo const& testCase) const { return !m_underlyingPattern->matches(testCase); } - private: - Ptr m_underlyingPattern; - }; - - struct Filter - { - std::vector> m_patterns; - - bool matches(TestCaseInfo const& testCase) const - { - // All patterns in a filter must match for the filter to be a match - for (std::vector>::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it) - if (!(*it)->matches(testCase)) - return false; - return true; - } - }; - - public: - bool hasFilters() const - { - return !m_filters.empty(); - } - bool matches(TestCaseInfo const& testCase) const - { - // A TestSpec matches if any filter matches - for (std::vector::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it) - if (it->matches(testCase)) + bool matches( TestCaseInfo const& testCase ) const { + // All patterns in a filter must match for the filter to be a match + for( std::vector >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) { + if( !(*it)->matches( testCase ) ) + return false; + } return true; - return false; - } + } + }; - private: - std::vector m_filters; + public: + bool hasFilters() const { + return !m_filters.empty(); + } + bool matches( TestCaseInfo const& testCase ) const { + // A TestSpec matches if any filter matches + for( std::vector::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it ) + if( it->matches( testCase ) ) + return true; + return false; + } - friend class TestSpecParser; -}; + private: + std::vector m_filters; + + friend class TestSpecParser; + }; } #ifdef __clang__ @@ -3633,124 +3637,110 @@ class TestSpec namespace Catch { -class TestSpecParser -{ - enum Mode - { - None, - Name, - QuotedName, - Tag - }; - Mode m_mode; - bool m_exclusion; - std::size_t m_start, m_pos; - std::string m_arg; - TestSpec::Filter m_currentFilter; - TestSpec m_testSpec; - ITagAliasRegistry const* m_tagAliases; + class TestSpecParser { + enum Mode{ None, Name, QuotedName, Tag, EscapedName }; + Mode m_mode; + bool m_exclusion; + std::size_t m_start, m_pos; + std::string m_arg; + std::vector m_escapeChars; + TestSpec::Filter m_currentFilter; + TestSpec m_testSpec; + ITagAliasRegistry const* m_tagAliases; - public: - TestSpecParser(ITagAliasRegistry const& tagAliases) : m_tagAliases(&tagAliases) {} + public: + TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} - TestSpecParser& parse(std::string const& arg) - { - m_mode = None; - m_exclusion = false; - m_start = std::string::npos; - m_arg = m_tagAliases->expandAliases(arg); - for (m_pos = 0; m_pos < m_arg.size(); ++m_pos) - visitChar(m_arg[m_pos]); - if (m_mode == Name) - addPattern(); - return *this; - } - TestSpec testSpec() - { - addFilter(); - return m_testSpec; - } - - private: - void visitChar(char c) - { - if (m_mode == None) - { - switch (c) - { - case ' ': - return; - case '~': - m_exclusion = true; - return; - case '[': - return startNewMode(Tag, ++m_pos); - case '"': - return startNewMode(QuotedName, ++m_pos); - default: - startNewMode(Name, m_pos); - break; - } - } - if (m_mode == Name) - { - if (c == ',') - { + TestSpecParser& parse( std::string const& arg ) { + m_mode = None; + m_exclusion = false; + m_start = std::string::npos; + m_arg = m_tagAliases->expandAliases( arg ); + m_escapeChars.clear(); + for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) + visitChar( m_arg[m_pos] ); + if( m_mode == Name ) addPattern(); - addFilter(); + return *this; + } + TestSpec testSpec() { + addFilter(); + return m_testSpec; + } + private: + void visitChar( char c ) { + if( m_mode == None ) { + switch( c ) { + case ' ': return; + case '~': m_exclusion = true; return; + case '[': return startNewMode( Tag, ++m_pos ); + case '"': return startNewMode( QuotedName, ++m_pos ); + case '\\': return escape(); + default: startNewMode( Name, m_pos ); break; + } } - else if (c == '[') - { - if (subString() == "exclude:") - m_exclusion = true; - else + if( m_mode == Name ) { + if( c == ',' ) { addPattern(); - startNewMode(Tag, ++m_pos); + addFilter(); + } + else if( c == '[' ) { + if( subString() == "exclude:" ) + m_exclusion = true; + else + addPattern(); + startNewMode( Tag, ++m_pos ); + } + else if( c == '\\' ) + escape(); + } + else if( m_mode == EscapedName ) + m_mode = Name; + else if( m_mode == QuotedName && c == '"' ) + addPattern(); + else if( m_mode == Tag && c == ']' ) + addPattern(); + } + void startNewMode( Mode mode, std::size_t start ) { + m_mode = mode; + m_start = start; + } + void escape() { + if( m_mode == None ) + m_start = m_pos; + m_mode = EscapedName; + m_escapeChars.push_back( m_pos ); + } + std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); } + template + void addPattern() { + std::string token = subString(); + for( size_t i = 0; i < m_escapeChars.size(); ++i ) + token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 ); + m_escapeChars.clear(); + if( startsWith( token, "exclude:" ) ) { + m_exclusion = true; + token = token.substr( 8 ); + } + if( !token.empty() ) { + Ptr pattern = new T( token ); + if( m_exclusion ) + pattern = new TestSpec::ExcludedPattern( pattern ); + m_currentFilter.m_patterns.push_back( pattern ); + } + m_exclusion = false; + m_mode = None; + } + void addFilter() { + if( !m_currentFilter.m_patterns.empty() ) { + m_testSpec.m_filters.push_back( m_currentFilter ); + m_currentFilter = TestSpec::Filter(); } } - else if (m_mode == QuotedName && c == '"') - addPattern(); - else if (m_mode == Tag && c == ']') - addPattern(); + }; + inline TestSpec parseTestSpec( std::string const& arg ) { + return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); } - void startNewMode(Mode mode, std::size_t start) - { - m_mode = mode; - m_start = start; - } - std::string subString() const { return m_arg.substr(m_start, m_pos - m_start); } - template - void addPattern() - { - std::string token = subString(); - if (startsWith(token, "exclude:")) - { - m_exclusion = true; - token = token.substr(8); - } - if (!token.empty()) - { - Ptr pattern = new T(token); - if (m_exclusion) - pattern = new TestSpec::ExcludedPattern(pattern); - m_currentFilter.m_patterns.push_back(pattern); - } - m_exclusion = false; - m_mode = None; - } - void addFilter() - { - if (!m_currentFilter.m_patterns.empty()) - { - m_testSpec.m_filters.push_back(m_currentFilter); - m_currentFilter = TestSpec::Filter(); - } - } -}; -inline TestSpec parseTestSpec(std::string const& arg) -{ - return TestSpecParser(ITagAliasRegistry::get()).parse(arg).testSpec(); -} } // namespace Catch @@ -3761,71 +3751,61 @@ inline TestSpec parseTestSpec(std::string const& arg) // #included from: catch_interfaces_config.h #define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED -#include +#include #include #include namespace Catch { -struct Verbosity -{ - enum Level - { + struct Verbosity { enum Level { NoOutput = 0, Quiet, Normal - }; -}; + }; }; -struct WarnAbout -{ - enum What - { + struct WarnAbout { enum What { Nothing = 0x00, NoAssertions = 0x01 - }; -}; + }; }; -struct ShowDurations -{ - enum OrNot - { + struct ShowDurations { enum OrNot { DefaultForReporter, Always, Never - }; -}; -struct RunTests -{ - enum InWhatOrder - { + }; }; + struct RunTests { enum InWhatOrder { InDeclarationOrder, InLexicographicalOrder, InRandomOrder + }; }; + struct UseColour { enum YesOrNo { + Auto, + Yes, + No + }; }; + + class TestSpec; + + struct IConfig : IShared { + + virtual ~IConfig(); + + virtual bool allowThrows() const = 0; + virtual std::ostream& stream() const = 0; + virtual std::string name() const = 0; + virtual bool includeSuccessfulResults() const = 0; + virtual bool shouldDebugBreak() const = 0; + virtual bool warnAboutMissingAssertions() const = 0; + virtual int abortAfter() const = 0; + virtual bool showInvisibles() const = 0; + virtual ShowDurations::OrNot showDurations() const = 0; + virtual TestSpec const& testSpec() const = 0; + virtual RunTests::InWhatOrder runOrder() const = 0; + virtual unsigned int rngSeed() const = 0; + virtual UseColour::YesOrNo useColour() const = 0; + virtual std::vector const& getSectionsToRun() const = 0; + }; -}; - -class TestSpec; - -struct IConfig : IShared -{ - - virtual ~IConfig(); - - virtual bool allowThrows() const = 0; - virtual std::ostream& stream() const = 0; - virtual std::string name() const = 0; - virtual bool includeSuccessfulResults() const = 0; - virtual bool shouldDebugBreak() const = 0; - virtual bool warnAboutMissingAssertions() const = 0; - virtual int abortAfter() const = 0; - virtual bool showInvisibles() const = 0; - virtual ShowDurations::OrNot showDurations() const = 0; - virtual TestSpec const& testSpec() const = 0; - virtual RunTests::InWhatOrder runOrder() const = 0; - virtual unsigned int rngSeed() const = 0; - virtual bool forceColour() const = 0; -}; } // #included from: catch_stream.h @@ -3838,71 +3818,62 @@ struct IConfig : IShared namespace Catch { -class StreamBufBase : public std::streambuf -{ - public: - virtual ~StreamBufBase() CATCH_NOEXCEPT; -}; + class StreamBufBase : public std::streambuf { + public: + virtual ~StreamBufBase() CATCH_NOEXCEPT; + }; } -#include -#include #include +#include +#include +#include namespace Catch { -std::ostream& cout(); -std::ostream& cerr(); + std::ostream& cout(); + std::ostream& cerr(); -struct IStream -{ - virtual ~IStream() CATCH_NOEXCEPT; - virtual std::ostream& stream() const = 0; -}; + struct IStream { + virtual ~IStream() CATCH_NOEXCEPT; + virtual std::ostream& stream() const = 0; + }; -class FileStream : public IStream -{ - mutable std::ofstream m_ofs; + class FileStream : public IStream { + mutable std::ofstream m_ofs; + public: + FileStream( std::string const& filename ); + virtual ~FileStream() CATCH_NOEXCEPT; + public: // IStream + virtual std::ostream& stream() const CATCH_OVERRIDE; + }; - public: - FileStream(std::string const& filename); - virtual ~FileStream() CATCH_NOEXCEPT; + class CoutStream : public IStream { + mutable std::ostream m_os; + public: + CoutStream(); + virtual ~CoutStream() CATCH_NOEXCEPT; - public: // IStream - virtual std::ostream& stream() const CATCH_OVERRIDE; -}; + public: // IStream + virtual std::ostream& stream() const CATCH_OVERRIDE; + }; -class CoutStream : public IStream -{ - mutable std::ostream m_os; + class DebugOutStream : public IStream { + CATCH_AUTO_PTR( StreamBufBase ) m_streamBuf; + mutable std::ostream m_os; + public: + DebugOutStream(); + virtual ~DebugOutStream() CATCH_NOEXCEPT; - public: - CoutStream(); - virtual ~CoutStream() CATCH_NOEXCEPT; - - public: // IStream - virtual std::ostream& stream() const CATCH_OVERRIDE; -}; - -class DebugOutStream : public IStream -{ - std::auto_ptr m_streamBuf; - mutable std::ostream m_os; - - public: - DebugOutStream(); - virtual ~DebugOutStream() CATCH_NOEXCEPT; - - public: // IStream - virtual std::ostream& stream() const CATCH_OVERRIDE; -}; + public: // IStream + virtual std::ostream& stream() const CATCH_OVERRIDE; + }; } -#include -#include #include -#include #include +#include +#include #ifndef CATCH_CONFIG_CONSOLE_WIDTH #define CATCH_CONFIG_CONSOLE_WIDTH 80 @@ -3910,142 +3881,133 @@ class DebugOutStream : public IStream namespace Catch { -struct ConfigData -{ + struct ConfigData { - ConfigData() - : listTests(false), - listTags(false), - listReporters(false), - listTestNamesOnly(false), - showSuccessfulTests(false), - shouldDebugBreak(false), - noThrow(false), - showHelp(false), - showInvisibles(false), - forceColour(false), - filenamesAsTags(false), - abortAfter(-1), - rngSeed(0), - verbosity(Verbosity::Normal), - warnings(WarnAbout::Nothing), - showDurations(ShowDurations::DefaultForReporter), - runOrder(RunTests::InDeclarationOrder) - { - } + ConfigData() + : listTests( false ), + listTags( false ), + listReporters( false ), + listTestNamesOnly( false ), + showSuccessfulTests( false ), + shouldDebugBreak( false ), + noThrow( false ), + showHelp( false ), + showInvisibles( false ), + filenamesAsTags( false ), + abortAfter( -1 ), + rngSeed( 0 ), + verbosity( Verbosity::Normal ), + warnings( WarnAbout::Nothing ), + showDurations( ShowDurations::DefaultForReporter ), + runOrder( RunTests::InDeclarationOrder ), + useColour( UseColour::Auto ) + {} - bool listTests; - bool listTags; - bool listReporters; - bool listTestNamesOnly; + bool listTests; + bool listTags; + bool listReporters; + bool listTestNamesOnly; - bool showSuccessfulTests; - bool shouldDebugBreak; - bool noThrow; - bool showHelp; - bool showInvisibles; - bool forceColour; - bool filenamesAsTags; + bool showSuccessfulTests; + bool shouldDebugBreak; + bool noThrow; + bool showHelp; + bool showInvisibles; + bool filenamesAsTags; - int abortAfter; - unsigned int rngSeed; + int abortAfter; + unsigned int rngSeed; - Verbosity::Level verbosity; - WarnAbout::What warnings; - ShowDurations::OrNot showDurations; - RunTests::InWhatOrder runOrder; + Verbosity::Level verbosity; + WarnAbout::What warnings; + ShowDurations::OrNot showDurations; + RunTests::InWhatOrder runOrder; + UseColour::YesOrNo useColour; - std::string outputFilename; - std::string name; - std::string processName; + std::string outputFilename; + std::string name; + std::string processName; - std::vector reporterNames; - std::vector testsOrTags; -}; + std::vector reporterNames; + std::vector testsOrTags; + std::vector sectionsToRun; + }; -class Config : public SharedImpl -{ - private: - Config(Config const& other); - Config& operator=(Config const& other); - virtual void dummy(); + class Config : public SharedImpl { + private: + Config( Config const& other ); + Config& operator = ( Config const& other ); + virtual void dummy(); + public: - public: - Config() - { - } + Config() + {} - Config(ConfigData const& data) - : m_data(data), - m_stream(openStream()) - { - if (!data.testsOrTags.empty()) + Config( ConfigData const& data ) + : m_data( data ), + m_stream( openStream() ) { - TestSpecParser parser(ITagAliasRegistry::get()); - for (std::size_t i = 0; i < data.testsOrTags.size(); ++i) - parser.parse(data.testsOrTags[i]); - m_testSpec = parser.testSpec(); + if( !data.testsOrTags.empty() ) { + TestSpecParser parser( ITagAliasRegistry::get() ); + for( std::size_t i = 0; i < data.testsOrTags.size(); ++i ) + parser.parse( data.testsOrTags[i] ); + m_testSpec = parser.testSpec(); + } } - } - virtual ~Config() - { - } + virtual ~Config() {} - std::string const& getFilename() const - { - return m_data.outputFilename; - } + std::string const& getFilename() const { + return m_data.outputFilename ; + } - bool listTests() const { return m_data.listTests; } - bool listTestNamesOnly() const { return m_data.listTestNamesOnly; } - bool listTags() const { return m_data.listTags; } - bool listReporters() const { return m_data.listReporters; } + bool listTests() const { return m_data.listTests; } + bool listTestNamesOnly() const { return m_data.listTestNamesOnly; } + bool listTags() const { return m_data.listTags; } + bool listReporters() const { return m_data.listReporters; } - std::string getProcessName() const { return m_data.processName; } + std::string getProcessName() const { return m_data.processName; } - bool shouldDebugBreak() const { return m_data.shouldDebugBreak; } + std::vector const& getReporterNames() const { return m_data.reporterNames; } + std::vector const& getSectionsToRun() const CATCH_OVERRIDE { return m_data.sectionsToRun; } - std::vector getReporterNames() const { return m_data.reporterNames; } + virtual TestSpec const& testSpec() const CATCH_OVERRIDE { return m_testSpec; } - int abortAfter() const { return m_data.abortAfter; } + bool showHelp() const { return m_data.showHelp; } - TestSpec const& testSpec() const { return m_testSpec; } + // IConfig interface + virtual bool allowThrows() const CATCH_OVERRIDE { return !m_data.noThrow; } + virtual std::ostream& stream() const CATCH_OVERRIDE { return m_stream->stream(); } + virtual std::string name() const CATCH_OVERRIDE { return m_data.name.empty() ? m_data.processName : m_data.name; } + virtual bool includeSuccessfulResults() const CATCH_OVERRIDE { return m_data.showSuccessfulTests; } + virtual bool warnAboutMissingAssertions() const CATCH_OVERRIDE { return m_data.warnings & WarnAbout::NoAssertions; } + virtual ShowDurations::OrNot showDurations() const CATCH_OVERRIDE { return m_data.showDurations; } + virtual RunTests::InWhatOrder runOrder() const CATCH_OVERRIDE { return m_data.runOrder; } + virtual unsigned int rngSeed() const CATCH_OVERRIDE { return m_data.rngSeed; } + virtual UseColour::YesOrNo useColour() const CATCH_OVERRIDE { return m_data.useColour; } + virtual bool shouldDebugBreak() const CATCH_OVERRIDE { return m_data.shouldDebugBreak; } + virtual int abortAfter() const CATCH_OVERRIDE { return m_data.abortAfter; } + virtual bool showInvisibles() const CATCH_OVERRIDE { return m_data.showInvisibles; } - bool showHelp() const { return m_data.showHelp; } - bool showInvisibles() const { return m_data.showInvisibles; } + private: - // IConfig interface - virtual bool allowThrows() const { return !m_data.noThrow; } - virtual std::ostream& stream() const { return m_stream->stream(); } - virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } - virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; } - virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } - virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; } - virtual RunTests::InWhatOrder runOrder() const { return m_data.runOrder; } - virtual unsigned int rngSeed() const { return m_data.rngSeed; } - virtual bool forceColour() const { return m_data.forceColour; } - - private: - IStream const* openStream() - { - if (m_data.outputFilename.empty()) - return new CoutStream(); - else if (m_data.outputFilename[0] == '%') - { - if (m_data.outputFilename == "%debug") - return new DebugOutStream(); + IStream const* openStream() { + if( m_data.outputFilename.empty() ) + return new CoutStream(); + else if( m_data.outputFilename[0] == '%' ) { + if( m_data.outputFilename == "%debug" ) + return new DebugOutStream(); + else + throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename ); + } else - throw std::domain_error("Unrecognised stream: " + m_data.outputFilename); + return new FileStream( m_data.outputFilename ); } - else - return new FileStream(m_data.outputFilename); - } - ConfigData m_data; + ConfigData m_data; - std::auto_ptr m_stream; - TestSpec m_testSpec; -}; + CATCH_AUTO_PTR( IStream const ) m_stream; + TestSpec m_testSpec; + }; } // end namespace Catch @@ -4063,6 +4025,8 @@ class Config : public SharedImpl #define STITCH_CLARA_OPEN_NAMESPACE namespace Catch { // #included from: ../external/clara.h +// Version 0.0.2.4 + // Only use header guard if we are not using an outer namespace #if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE) @@ -4084,9 +4048,11 @@ class Config : public SharedImpl #define TBC_TEXT_FORMAT_H_INCLUDED #endif -#include #include #include +#include +#include +#include // Use optional outer namespace #ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE @@ -4096,153 +4062,121 @@ namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE { namespace Tbc { #ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH -const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; + const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; #else -const unsigned int consoleWidth = 80; + const unsigned int consoleWidth = 80; #endif -struct TextAttributes -{ - TextAttributes() - : initialIndent(std::string::npos), - indent(0), - width(consoleWidth - 1), - tabChar('\t') - { - } + struct TextAttributes { + TextAttributes() + : initialIndent( std::string::npos ), + indent( 0 ), + width( consoleWidth-1 ), + tabChar( '\t' ) + {} - TextAttributes& setInitialIndent(std::size_t _value) - { - initialIndent = _value; - return *this; - } - TextAttributes& setIndent(std::size_t _value) - { - indent = _value; - return *this; - } - TextAttributes& setWidth(std::size_t _value) - { - width = _value; - return *this; - } - TextAttributes& setTabChar(char _value) - { - tabChar = _value; - return *this; - } + TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } + TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } + TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } + TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } - std::size_t initialIndent; // indent of first line, or npos - std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos - std::size_t width; // maximum width of text, including indent. Longer text will wrap - char tabChar; // If this char is seen the indent is changed to current pos -}; + std::size_t initialIndent; // indent of first line, or npos + std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos + std::size_t width; // maximum width of text, including indent. Longer text will wrap + char tabChar; // If this char is seen the indent is changed to current pos + }; -class Text -{ - public: - Text(std::string const& _str, TextAttributes const& _attr = TextAttributes()) - : attr(_attr) - { - std::string wrappableChars = " [({.,/|\\-"; - std::size_t indent = _attr.initialIndent != std::string::npos - ? _attr.initialIndent - : _attr.indent; - std::string remainder = _str; - - while (!remainder.empty()) + class Text { + public: + Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) + : attr( _attr ) { - if (lines.size() >= 1000) - { - lines.push_back("... message truncated due to excessive size"); - return; - } - std::size_t tabPos = std::string::npos; - std::size_t width = (std::min)(remainder.size(), _attr.width - indent); - std::size_t pos = remainder.find_first_of('\n'); - if (pos <= width) - { - width = pos; - } - pos = remainder.find_last_of(_attr.tabChar, width); - if (pos != std::string::npos) - { - tabPos = pos; - if (remainder[width] == '\n') - width--; - remainder = remainder.substr(0, tabPos) + remainder.substr(tabPos + 1); - } + std::string wrappableChars = " [({.,/|\\-"; + std::size_t indent = _attr.initialIndent != std::string::npos + ? _attr.initialIndent + : _attr.indent; + std::string remainder = _str; - if (width == remainder.size()) - { - spliceLine(indent, remainder, width); - } - else if (remainder[width] == '\n') - { - spliceLine(indent, remainder, width); - if (width <= 1 || remainder.size() != 1) - remainder = remainder.substr(1); - indent = _attr.indent; - } - else - { - pos = remainder.find_last_of(wrappableChars, width); - if (pos != std::string::npos && pos > 0) - { - spliceLine(indent, remainder, pos); - if (remainder[0] == ' ') - remainder = remainder.substr(1); + while( !remainder.empty() ) { + if( lines.size() >= 1000 ) { + lines.push_back( "... message truncated due to excessive size" ); + return; } - else - { - spliceLine(indent, remainder, width - 1); - lines.back() += "-"; + std::size_t tabPos = std::string::npos; + std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); + std::size_t pos = remainder.find_first_of( '\n' ); + if( pos <= width ) { + width = pos; } - if (lines.size() == 1) + pos = remainder.find_last_of( _attr.tabChar, width ); + if( pos != std::string::npos ) { + tabPos = pos; + if( remainder[width] == '\n' ) + width--; + remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); + } + + if( width == remainder.size() ) { + spliceLine( indent, remainder, width ); + } + else if( remainder[width] == '\n' ) { + spliceLine( indent, remainder, width ); + if( width <= 1 || remainder.size() != 1 ) + remainder = remainder.substr( 1 ); indent = _attr.indent; - if (tabPos != std::string::npos) - indent += tabPos; + } + else { + pos = remainder.find_last_of( wrappableChars, width ); + if( pos != std::string::npos && pos > 0 ) { + spliceLine( indent, remainder, pos ); + if( remainder[0] == ' ' ) + remainder = remainder.substr( 1 ); + } + else { + spliceLine( indent, remainder, width-1 ); + lines.back() += "-"; + } + if( lines.size() == 1 ) + indent = _attr.indent; + if( tabPos != std::string::npos ) + indent += tabPos; + } } } - } - void spliceLine(std::size_t _indent, std::string& _remainder, std::size_t _pos) - { - lines.push_back(std::string(_indent, ' ') + _remainder.substr(0, _pos)); - _remainder = _remainder.substr(_pos); - } - - typedef std::vector::const_iterator const_iterator; - - const_iterator begin() const { return lines.begin(); } - const_iterator end() const { return lines.end(); } - std::string const& last() const { return lines.back(); } - std::size_t size() const { return lines.size(); } - std::string const& operator[](std::size_t _index) const { return lines[_index]; } - std::string toString() const - { - std::ostringstream oss; - oss << *this; - return oss.str(); - } - - inline friend std::ostream& operator<<(std::ostream& _stream, Text const& _text) - { - for (Text::const_iterator it = _text.begin(), itEnd = _text.end(); - it != itEnd; ++it) - { - if (it != _text.begin()) - _stream << "\n"; - _stream << *it; + void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { + lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); + _remainder = _remainder.substr( _pos ); } - return _stream; - } - private: - std::string str; - TextAttributes attr; - std::vector lines; -}; + typedef std::vector::const_iterator const_iterator; + + const_iterator begin() const { return lines.begin(); } + const_iterator end() const { return lines.end(); } + std::string const& last() const { return lines.back(); } + std::size_t size() const { return lines.size(); } + std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } + std::string toString() const { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + + inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { + for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); + it != itEnd; ++it ) { + if( it != _text.begin() ) + _stream << "\n"; + _stream << *it; + } + return _stream; + } + + private: + std::string str; + TextAttributes attr; + std::vector lines; + }; } // end namespace Tbc @@ -4253,14 +4187,164 @@ class Text #endif // TBC_TEXT_FORMAT_H_INCLUDED // ----------- end of #include from tbc_text_format.h ----------- -// ........... back in /Users/philnash/Dev/OSS/Clara/srcs/clara.h +// ........... back in clara.h #undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE -#include +// ----------- #included from clara_compilers.h ----------- + +#ifndef TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED +#define TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED + +// Detect a number of compiler features - mostly C++11/14 conformance - by compiler +// The following features are defined: +// +// CLARA_CONFIG_CPP11_NULLPTR : is nullptr supported? +// CLARA_CONFIG_CPP11_NOEXCEPT : is noexcept supported? +// CLARA_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods +// CLARA_CONFIG_CPP11_OVERRIDE : is override supported? +// CLARA_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) + +// CLARA_CONFIG_CPP11_OR_GREATER : Is C++11 supported? + +// CLARA_CONFIG_VARIADIC_MACROS : are variadic macros supported? + +// In general each macro has a _NO_ form +// (e.g. CLARA_CONFIG_CPP11_NO_NULLPTR) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +// All the C++11 features can be disabled with CLARA_CONFIG_NO_CPP11 + +#ifdef __clang__ + +#if __has_feature(cxx_nullptr) +#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR +#endif + +#if __has_feature(cxx_noexcept) +#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT +#endif + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// GCC +#ifdef __GNUC__ + +#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) +#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR +#endif + +// - otherwise more recent versions define __cplusplus >= 201103L +// and will get picked up below + +#endif // __GNUC__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#ifdef _MSC_VER + +#if (_MSC_VER >= 1600) +#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR +#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR +#endif + +#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) +#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT +#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#endif + +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// C++ language feature support + +// catch all support for C++11 +#if defined(__cplusplus) && __cplusplus >= 201103L + +#define CLARA_CPP11_OR_GREATER + +#if !defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) +#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR +#endif + +#ifndef CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT +#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT +#endif + +#ifndef CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#endif + +#if !defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) +#define CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE +#endif +#if !defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) +#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR +#endif + +#endif // __cplusplus >= 201103L + +// Now set the actual defines based on the above + anything the user has configured +#if defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NO_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_NULLPTR +#endif +#if defined(CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_NOEXCEPT +#endif +#if defined(CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_GENERATED_METHODS +#endif +#if defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_OVERRIDE) && !defined(CLARA_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_OVERRIDE +#endif +#if defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_UNIQUE_PTR) && !defined(CLARA_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_UNIQUE_PTR +#endif + +// noexcept support: +#if defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_NOEXCEPT) +#define CLARA_NOEXCEPT noexcept +# define CLARA_NOEXCEPT_IS(x) noexcept(x) +#else +#define CLARA_NOEXCEPT throw() +# define CLARA_NOEXCEPT_IS(x) +#endif + +// nullptr support +#ifdef CLARA_CONFIG_CPP11_NULLPTR +#define CLARA_NULL nullptr +#else +#define CLARA_NULL NULL +#endif + +// override support +#ifdef CLARA_CONFIG_CPP11_OVERRIDE +#define CLARA_OVERRIDE override +#else +#define CLARA_OVERRIDE +#endif + +// unique_ptr support +#ifdef CLARA_CONFIG_CPP11_UNIQUE_PTR +# define CLARA_AUTO_PTR( T ) std::unique_ptr +#else +# define CLARA_AUTO_PTR( T ) std::auto_ptr +#endif + +#endif // TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED + +// ----------- end of #include from clara_compilers.h ----------- +// ........... back in clara.h + #include -#include #include +#include + +#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) +#define CLARA_PLATFORM_WINDOWS +#endif // Use optional outer namespace #ifdef STITCH_CLARA_OPEN_NAMESPACE @@ -4269,795 +4353,688 @@ STITCH_CLARA_OPEN_NAMESPACE namespace Clara { -struct UnpositionalTag -{ -}; + struct UnpositionalTag {}; -extern UnpositionalTag _; + extern UnpositionalTag _; #ifdef CLARA_CONFIG_MAIN -UnpositionalTag _; + UnpositionalTag _; #endif -namespace Detail { + namespace Detail { #ifdef CLARA_CONSOLE_WIDTH -const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; + const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; #else -const unsigned int consoleWidth = 80; + const unsigned int consoleWidth = 80; #endif -using namespace Tbc; + using namespace Tbc; -inline bool startsWith(std::string const& str, std::string const& prefix) -{ - return str.size() >= prefix.size() && str.substr(0, prefix.size()) == prefix; -} + inline bool startsWith( std::string const& str, std::string const& prefix ) { + return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix; + } -template -struct RemoveConstRef -{ - typedef T type; -}; -template -struct RemoveConstRef -{ - typedef T type; -}; -template -struct RemoveConstRef -{ - typedef T type; -}; -template -struct RemoveConstRef -{ - typedef T type; -}; + template struct RemoveConstRef{ typedef T type; }; + template struct RemoveConstRef{ typedef T type; }; + template struct RemoveConstRef{ typedef T type; }; + template struct RemoveConstRef{ typedef T type; }; -template -struct IsBool -{ - static const bool value = false; -}; -template <> -struct IsBool -{ - static const bool value = true; -}; + template struct IsBool { static const bool value = false; }; + template<> struct IsBool { static const bool value = true; }; -template -void convertInto(std::string const& _source, T& _dest) -{ - std::stringstream ss; - ss << _source; - ss >> _dest; - if (ss.fail()) - throw std::runtime_error("Unable to convert " + _source + " to destination type"); -} -inline void convertInto(std::string const& _source, std::string& _dest) -{ - _dest = _source; -} -inline void convertInto(std::string const& _source, bool& _dest) -{ - std::string sourceLC = _source; - std::transform(sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower); - if (sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on") - _dest = true; - else if (sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off") - _dest = false; - else - throw std::runtime_error("Expected a boolean value but did not recognise:\n '" + _source + "'"); -} -inline void convertInto(bool _source, bool& _dest) -{ - _dest = _source; -} -template -inline void convertInto(bool, T&) -{ - throw std::runtime_error("Invalid conversion"); -} + template + void convertInto( std::string const& _source, T& _dest ) { + std::stringstream ss; + ss << _source; + ss >> _dest; + if( ss.fail() ) + throw std::runtime_error( "Unable to convert " + _source + " to destination type" ); + } + inline void convertInto( std::string const& _source, std::string& _dest ) { + _dest = _source; + } + char toLowerCh(char c) { + return static_cast( std::tolower( c ) ); + } + inline void convertInto( std::string const& _source, bool& _dest ) { + std::string sourceLC = _source; + std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), toLowerCh ); + if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" ) + _dest = true; + else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" ) + _dest = false; + else + throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" ); + } -template -struct IArgFunction -{ - virtual ~IArgFunction() {} -#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - IArgFunction() = default; - IArgFunction(IArgFunction const&) = default; + template + struct IArgFunction { + virtual ~IArgFunction() {} +#ifdef CLARA_CONFIG_CPP11_GENERATED_METHODS + IArgFunction() = default; + IArgFunction( IArgFunction const& ) = default; #endif - virtual void set(ConfigT& config, std::string const& value) const = 0; - virtual void setFlag(ConfigT& config) const = 0; - virtual bool takesArg() const = 0; - virtual IArgFunction* clone() const = 0; -}; - -template -class BoundArgFunction -{ - public: - BoundArgFunction() : functionObj(CATCH_NULL) {} - BoundArgFunction(IArgFunction* _functionObj) : functionObj(_functionObj) {} - BoundArgFunction(BoundArgFunction const& other) : functionObj(other.functionObj ? other.functionObj->clone() : CATCH_NULL) {} - BoundArgFunction& operator=(BoundArgFunction const& other) - { - IArgFunction* newFunctionObj = other.functionObj ? other.functionObj->clone() : CATCH_NULL; - delete functionObj; - functionObj = newFunctionObj; - return *this; - } - ~BoundArgFunction() { delete functionObj; } - - void set(ConfigT& config, std::string const& value) const - { - functionObj->set(config, value); - } - void setFlag(ConfigT& config) const - { - functionObj->setFlag(config); - } - bool takesArg() const { return functionObj->takesArg(); } - - bool isSet() const - { - return functionObj != CATCH_NULL; - } - - private: - IArgFunction* functionObj; -}; - -template -struct NullBinder : IArgFunction -{ - virtual void set(C&, std::string const&) const {} - virtual void setFlag(C&) const {} - virtual bool takesArg() const { return true; } - virtual IArgFunction* clone() const { return new NullBinder(*this); } -}; - -template -struct BoundDataMember : IArgFunction -{ - BoundDataMember(M C::*_member) : member(_member) {} - virtual void set(C& p, std::string const& stringValue) const - { - convertInto(stringValue, p.*member); - } - virtual void setFlag(C& p) const - { - convertInto(true, p.*member); - } - virtual bool takesArg() const { return !IsBool::value; } - virtual IArgFunction* clone() const { return new BoundDataMember(*this); } - M C::*member; -}; -template -struct BoundUnaryMethod : IArgFunction -{ - BoundUnaryMethod(void (C::*_member)(M)) : member(_member) {} - virtual void set(C& p, std::string const& stringValue) const - { - typename RemoveConstRef::type value; - convertInto(stringValue, value); - (p.*member)(value); - } - virtual void setFlag(C& p) const - { - typename RemoveConstRef::type value; - convertInto(true, value); - (p.*member)(value); - } - virtual bool takesArg() const { return !IsBool::value; } - virtual IArgFunction* clone() const { return new BoundUnaryMethod(*this); } - void (C::*member)(M); -}; -template -struct BoundNullaryMethod : IArgFunction -{ - BoundNullaryMethod(void (C::*_member)()) : member(_member) {} - virtual void set(C& p, std::string const& stringValue) const - { - bool value; - convertInto(stringValue, value); - if (value) - (p.*member)(); - } - virtual void setFlag(C& p) const - { - (p.*member)(); - } - virtual bool takesArg() const { return false; } - virtual IArgFunction* clone() const { return new BoundNullaryMethod(*this); } - void (C::*member)(); -}; - -template -struct BoundUnaryFunction : IArgFunction -{ - BoundUnaryFunction(void (*_function)(C&)) : function(_function) {} - virtual void set(C& obj, std::string const& stringValue) const - { - bool value; - convertInto(stringValue, value); - if (value) - function(obj); - } - virtual void setFlag(C& p) const - { - function(p); - } - virtual bool takesArg() const { return false; } - virtual IArgFunction* clone() const { return new BoundUnaryFunction(*this); } - void (*function)(C&); -}; - -template -struct BoundBinaryFunction : IArgFunction -{ - BoundBinaryFunction(void (*_function)(C&, T)) : function(_function) {} - virtual void set(C& obj, std::string const& stringValue) const - { - typename RemoveConstRef::type value; - convertInto(stringValue, value); - function(obj, value); - } - virtual void setFlag(C& obj) const - { - typename RemoveConstRef::type value; - convertInto(true, value); - function(obj, value); - } - virtual bool takesArg() const { return !IsBool::value; } - virtual IArgFunction* clone() const { return new BoundBinaryFunction(*this); } - void (*function)(C&, T); -}; - -} // namespace Detail - -struct Parser -{ - Parser() : separators(" \t=:") {} - - struct Token - { - enum Type - { - Positional, - ShortOpt, - LongOpt + virtual void set( ConfigT& config, std::string const& value ) const = 0; + virtual bool takesArg() const = 0; + virtual IArgFunction* clone() const = 0; }; - Token(Type _type, std::string const& _data) : type(_type), data(_data) {} - Type type; - std::string data; + + template + class BoundArgFunction { + public: + BoundArgFunction() : functionObj( CLARA_NULL ) {} + BoundArgFunction( IArgFunction* _functionObj ) : functionObj( _functionObj ) {} + BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CLARA_NULL ) {} + BoundArgFunction& operator = ( BoundArgFunction const& other ) { + IArgFunction* newFunctionObj = other.functionObj ? other.functionObj->clone() : CLARA_NULL; + delete functionObj; + functionObj = newFunctionObj; + return *this; + } + ~BoundArgFunction() { delete functionObj; } + + void set( ConfigT& config, std::string const& value ) const { + functionObj->set( config, value ); + } + bool takesArg() const { return functionObj->takesArg(); } + + bool isSet() const { + return functionObj != CLARA_NULL; + } + private: + IArgFunction* functionObj; + }; + + template + struct NullBinder : IArgFunction{ + virtual void set( C&, std::string const& ) const {} + virtual bool takesArg() const { return true; } + virtual IArgFunction* clone() const { return new NullBinder( *this ); } + }; + + template + struct BoundDataMember : IArgFunction{ + BoundDataMember( M C::* _member ) : member( _member ) {} + virtual void set( C& p, std::string const& stringValue ) const { + convertInto( stringValue, p.*member ); + } + virtual bool takesArg() const { return !IsBool::value; } + virtual IArgFunction* clone() const { return new BoundDataMember( *this ); } + M C::* member; + }; + template + struct BoundUnaryMethod : IArgFunction{ + BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {} + virtual void set( C& p, std::string const& stringValue ) const { + typename RemoveConstRef::type value; + convertInto( stringValue, value ); + (p.*member)( value ); + } + virtual bool takesArg() const { return !IsBool::value; } + virtual IArgFunction* clone() const { return new BoundUnaryMethod( *this ); } + void (C::*member)( M ); + }; + template + struct BoundNullaryMethod : IArgFunction{ + BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {} + virtual void set( C& p, std::string const& stringValue ) const { + bool value; + convertInto( stringValue, value ); + if( value ) + (p.*member)(); + } + virtual bool takesArg() const { return false; } + virtual IArgFunction* clone() const { return new BoundNullaryMethod( *this ); } + void (C::*member)(); + }; + + template + struct BoundUnaryFunction : IArgFunction{ + BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {} + virtual void set( C& obj, std::string const& stringValue ) const { + bool value; + convertInto( stringValue, value ); + if( value ) + function( obj ); + } + virtual bool takesArg() const { return false; } + virtual IArgFunction* clone() const { return new BoundUnaryFunction( *this ); } + void (*function)( C& ); + }; + + template + struct BoundBinaryFunction : IArgFunction{ + BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {} + virtual void set( C& obj, std::string const& stringValue ) const { + typename RemoveConstRef::type value; + convertInto( stringValue, value ); + function( obj, value ); + } + virtual bool takesArg() const { return !IsBool::value; } + virtual IArgFunction* clone() const { return new BoundBinaryFunction( *this ); } + void (*function)( C&, T ); + }; + + } // namespace Detail + + inline std::vector argsToVector( int argc, char const* const* const argv ) { + std::vector args( static_cast( argc ) ); + for( std::size_t i = 0; i < static_cast( argc ); ++i ) + args[i] = argv[i]; + + return args; + } + + class Parser { + enum Mode { None, MaybeShortOpt, SlashOpt, ShortOpt, LongOpt, Positional }; + Mode mode; + std::size_t from; + bool inQuotes; + public: + + struct Token { + enum Type { Positional, ShortOpt, LongOpt }; + Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {} + Type type; + std::string data; + }; + + Parser() : mode( None ), from( 0 ), inQuotes( false ){} + + void parseIntoTokens( std::vector const& args, std::vector& tokens ) { + const std::string doubleDash = "--"; + for( std::size_t i = 1; i < args.size() && args[i] != doubleDash; ++i ) + parseIntoTokens( args[i], tokens); + } + + void parseIntoTokens( std::string const& arg, std::vector& tokens ) { + for( std::size_t i = 0; i < arg.size(); ++i ) { + char c = arg[i]; + if( c == '"' ) + inQuotes = !inQuotes; + mode = handleMode( i, c, arg, tokens ); + } + mode = handleMode( arg.size(), '\0', arg, tokens ); + } + Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { + switch( mode ) { + case None: return handleNone( i, c ); + case MaybeShortOpt: return handleMaybeShortOpt( i, c ); + case ShortOpt: + case LongOpt: + case SlashOpt: return handleOpt( i, c, arg, tokens ); + case Positional: return handlePositional( i, c, arg, tokens ); + default: throw std::logic_error( "Unknown mode" ); + } + } + + Mode handleNone( std::size_t i, char c ) { + if( inQuotes ) { + from = i; + return Positional; + } + switch( c ) { + case '-': return MaybeShortOpt; +#ifdef CLARA_PLATFORM_WINDOWS + case '/': from = i+1; return SlashOpt; +#endif + default: from = i; return Positional; + } + } + Mode handleMaybeShortOpt( std::size_t i, char c ) { + switch( c ) { + case '-': from = i+1; return LongOpt; + default: from = i; return ShortOpt; + } + } + + Mode handleOpt( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { + if( std::string( ":=\0", 3 ).find( c ) == std::string::npos ) + return mode; + + std::string optName = arg.substr( from, i-from ); + if( mode == ShortOpt ) + for( std::size_t j = 0; j < optName.size(); ++j ) + tokens.push_back( Token( Token::ShortOpt, optName.substr( j, 1 ) ) ); + else if( mode == SlashOpt && optName.size() == 1 ) + tokens.push_back( Token( Token::ShortOpt, optName ) ); + else + tokens.push_back( Token( Token::LongOpt, optName ) ); + return None; + } + Mode handlePositional( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { + if( inQuotes || std::string( "\0", 1 ).find( c ) == std::string::npos ) + return mode; + + std::string data = arg.substr( from, i-from ); + tokens.push_back( Token( Token::Positional, data ) ); + return None; + } }; - void parseIntoTokens(int argc, char const* const* argv, std::vector& tokens) const - { - const std::string doubleDash = "--"; - for (int i = 1; i < argc && argv[i] != doubleDash; ++i) - parseIntoTokens(argv[i], tokens); - } - void parseIntoTokens(std::string arg, std::vector& tokens) const - { - while (!arg.empty()) - { - Parser::Token token(Parser::Token::Positional, arg); - arg = ""; - if (token.data[0] == '-') - { - if (token.data.size() > 1 && token.data[1] == '-') - { - token = Parser::Token(Parser::Token::LongOpt, token.data.substr(2)); + template + struct CommonArgProperties { + CommonArgProperties() {} + CommonArgProperties( Detail::BoundArgFunction const& _boundField ) : boundField( _boundField ) {} + + Detail::BoundArgFunction boundField; + std::string description; + std::string detail; + std::string placeholder; // Only value if boundField takes an arg + + bool takesArg() const { + return !placeholder.empty(); + } + void validate() const { + if( !boundField.isSet() ) + throw std::logic_error( "option not bound" ); + } + }; + struct OptionArgProperties { + std::vector shortNames; + std::string longName; + + bool hasShortName( std::string const& shortName ) const { + return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end(); + } + bool hasLongName( std::string const& _longName ) const { + return _longName == longName; + } + }; + struct PositionalArgProperties { + PositionalArgProperties() : position( -1 ) {} + int position; // -1 means non-positional (floating) + + bool isFixedPositional() const { + return position != -1; + } + }; + + template + class CommandLine { + + struct Arg : CommonArgProperties, OptionArgProperties, PositionalArgProperties { + Arg() {} + Arg( Detail::BoundArgFunction const& _boundField ) : CommonArgProperties( _boundField ) {} + + using CommonArgProperties::placeholder; // !TBD + + std::string dbgName() const { + if( !longName.empty() ) + return "--" + longName; + if( !shortNames.empty() ) + return "-" + shortNames[0]; + return "positional args"; + } + std::string commands() const { + std::ostringstream oss; + bool first = true; + std::vector::const_iterator it = shortNames.begin(), itEnd = shortNames.end(); + for(; it != itEnd; ++it ) { + if( first ) + first = false; + else + oss << ", "; + oss << "-" << *it; } - else - { - token = Parser::Token(Parser::Token::ShortOpt, token.data.substr(1)); - if (token.data.size() > 1 && separators.find(token.data[1]) == std::string::npos) - { - arg = "-" + token.data.substr(1); - token.data = token.data.substr(0, 1); - } + if( !longName.empty() ) { + if( !first ) + oss << ", "; + oss << "--" << longName; + } + if( !placeholder.empty() ) + oss << " <" << placeholder << ">"; + return oss.str(); + } + }; + + typedef CLARA_AUTO_PTR( Arg ) ArgAutoPtr; + + friend void addOptName( Arg& arg, std::string const& optName ) + { + if( optName.empty() ) + return; + if( Detail::startsWith( optName, "--" ) ) { + if( !arg.longName.empty() ) + throw std::logic_error( "Only one long opt may be specified. '" + + arg.longName + + "' already specified, now attempting to add '" + + optName + "'" ); + arg.longName = optName.substr( 2 ); + } + else if( Detail::startsWith( optName, "-" ) ) + arg.shortNames.push_back( optName.substr( 1 ) ); + else + throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" ); + } + friend void setPositionalArg( Arg& arg, int position ) + { + arg.position = position; + } + + class ArgBuilder { + public: + ArgBuilder( Arg* arg ) : m_arg( arg ) {} + + // Bind a non-boolean data member (requires placeholder string) + template + void bind( M C::* field, std::string const& placeholder ) { + m_arg->boundField = new Detail::BoundDataMember( field ); + m_arg->placeholder = placeholder; + } + // Bind a boolean data member (no placeholder required) + template + void bind( bool C::* field ) { + m_arg->boundField = new Detail::BoundDataMember( field ); + } + + // Bind a method taking a single, non-boolean argument (requires a placeholder string) + template + void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) { + m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); + m_arg->placeholder = placeholder; + } + + // Bind a method taking a single, boolean argument (no placeholder string required) + template + void bind( void (C::* unaryMethod)( bool ) ) { + m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); + } + + // Bind a method that takes no arguments (will be called if opt is present) + template + void bind( void (C::* nullaryMethod)() ) { + m_arg->boundField = new Detail::BoundNullaryMethod( nullaryMethod ); + } + + // Bind a free function taking a single argument - the object to operate on (no placeholder string required) + template + void bind( void (* unaryFunction)( C& ) ) { + m_arg->boundField = new Detail::BoundUnaryFunction( unaryFunction ); + } + + // Bind a free function taking a single argument - the object to operate on (requires a placeholder string) + template + void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) { + m_arg->boundField = new Detail::BoundBinaryFunction( binaryFunction ); + m_arg->placeholder = placeholder; + } + + ArgBuilder& describe( std::string const& description ) { + m_arg->description = description; + return *this; + } + ArgBuilder& detail( std::string const& detail ) { + m_arg->detail = detail; + return *this; + } + + protected: + Arg* m_arg; + }; + + class OptBuilder : public ArgBuilder { + public: + OptBuilder( Arg* arg ) : ArgBuilder( arg ) {} + OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {} + + OptBuilder& operator[]( std::string const& optName ) { + addOptName( *ArgBuilder::m_arg, optName ); + return *this; + } + }; + + public: + + CommandLine() + : m_boundProcessName( new Detail::NullBinder() ), + m_highestSpecifiedArgPosition( 0 ), + m_throwOnUnrecognisedTokens( false ) + {} + CommandLine( CommandLine const& other ) + : m_boundProcessName( other.m_boundProcessName ), + m_options ( other.m_options ), + m_positionalArgs( other.m_positionalArgs ), + m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ), + m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens ) + { + if( other.m_floatingArg.get() ) + m_floatingArg.reset( new Arg( *other.m_floatingArg ) ); + } + + CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) { + m_throwOnUnrecognisedTokens = shouldThrow; + return *this; + } + + OptBuilder operator[]( std::string const& optName ) { + m_options.push_back( Arg() ); + addOptName( m_options.back(), optName ); + OptBuilder builder( &m_options.back() ); + return builder; + } + + ArgBuilder operator[]( int position ) { + m_positionalArgs.insert( std::make_pair( position, Arg() ) ); + if( position > m_highestSpecifiedArgPosition ) + m_highestSpecifiedArgPosition = position; + setPositionalArg( m_positionalArgs[position], position ); + ArgBuilder builder( &m_positionalArgs[position] ); + return builder; + } + + // Invoke this with the _ instance + ArgBuilder operator[]( UnpositionalTag ) { + if( m_floatingArg.get() ) + throw std::logic_error( "Only one unpositional argument can be added" ); + m_floatingArg.reset( new Arg() ); + ArgBuilder builder( m_floatingArg.get() ); + return builder; + } + + template + void bindProcessName( M C::* field ) { + m_boundProcessName = new Detail::BoundDataMember( field ); + } + template + void bindProcessName( void (C::*_unaryMethod)( M ) ) { + m_boundProcessName = new Detail::BoundUnaryMethod( _unaryMethod ); + } + + void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const { + typename std::vector::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it; + std::size_t maxWidth = 0; + for( it = itBegin; it != itEnd; ++it ) + maxWidth = (std::max)( maxWidth, it->commands().size() ); + + for( it = itBegin; it != itEnd; ++it ) { + Detail::Text usage( it->commands(), Detail::TextAttributes() + .setWidth( maxWidth+indent ) + .setIndent( indent ) ); + Detail::Text desc( it->description, Detail::TextAttributes() + .setWidth( width - maxWidth - 3 ) ); + + for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) { + std::string usageCol = i < usage.size() ? usage[i] : ""; + os << usageCol; + + if( i < desc.size() && !desc[i].empty() ) + os << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' ) + << desc[i]; + os << "\n"; } } - if (token.type != Parser::Token::Positional) - { - std::size_t pos = token.data.find_first_of(separators); - if (pos != std::string::npos) - { - arg = token.data.substr(pos + 1); - token.data = token.data.substr(0, pos); - } - } - tokens.push_back(token); } - } - std::string separators; -}; - -template -struct CommonArgProperties -{ - CommonArgProperties() {} - CommonArgProperties(Detail::BoundArgFunction const& _boundField) : boundField(_boundField) {} - - Detail::BoundArgFunction boundField; - std::string description; - std::string detail; - std::string placeholder; // Only value if boundField takes an arg - - bool takesArg() const - { - return !placeholder.empty(); - } - void validate() const - { - if (!boundField.isSet()) - throw std::logic_error("option not bound"); - } -}; -struct OptionArgProperties -{ - std::vector shortNames; - std::string longName; - - bool hasShortName(std::string const& shortName) const - { - return std::find(shortNames.begin(), shortNames.end(), shortName) != shortNames.end(); - } - bool hasLongName(std::string const& _longName) const - { - return _longName == longName; - } -}; -struct PositionalArgProperties -{ - PositionalArgProperties() : position(-1) {} - int position; // -1 means non-positional (floating) - - bool isFixedPositional() const - { - return position != -1; - } -}; - -template -class CommandLine -{ - - struct Arg : CommonArgProperties, OptionArgProperties, PositionalArgProperties - { - Arg() {} - Arg(Detail::BoundArgFunction const& _boundField) : CommonArgProperties(_boundField) {} - - using CommonArgProperties::placeholder; // !TBD - - std::string dbgName() const - { - if (!longName.empty()) - return "--" + longName; - if (!shortNames.empty()) - return "-" + shortNames[0]; - return "positional args"; - } - std::string commands() const - { + std::string optUsage() const { std::ostringstream oss; - bool first = true; - std::vector::const_iterator it = shortNames.begin(), itEnd = shortNames.end(); - for (; it != itEnd; ++it) - { - if (first) - first = false; - else - oss << ", "; - oss << "-" << *it; - } - if (!longName.empty()) - { - if (!first) - oss << ", "; - oss << "--" << longName; - } - if (!placeholder.empty()) - oss << " <" << placeholder << ">"; + optUsage( oss ); return oss.str(); } - }; - typedef CATCH_AUTO_PTR(Arg) ArgAutoPtr; - - friend void addOptName(Arg& arg, std::string const& optName) - { - if (optName.empty()) - return; - if (Detail::startsWith(optName, "--")) - { - if (!arg.longName.empty()) - throw std::logic_error("Only one long opt may be specified. '" + arg.longName + "' already specified, now attempting to add '" + optName + "'"); - arg.longName = optName.substr(2); - } - else if (Detail::startsWith(optName, "-")) - arg.shortNames.push_back(optName.substr(1)); - else - throw std::logic_error("option must begin with - or --. Option was: '" + optName + "'"); - } - friend void setPositionalArg(Arg& arg, int position) - { - arg.position = position; - } - - class ArgBuilder - { - public: - ArgBuilder(Arg* arg) : m_arg(arg) {} - - // Bind a non-boolean data member (requires placeholder string) - template - void bind(M C::*field, std::string const& placeholder) - { - m_arg->boundField = new Detail::BoundDataMember(field); - m_arg->placeholder = placeholder; - } - // Bind a boolean data member (no placeholder required) - template - void bind(bool C::*field) - { - m_arg->boundField = new Detail::BoundDataMember(field); - } - - // Bind a method taking a single, non-boolean argument (requires a placeholder string) - template - void bind(void (C::*unaryMethod)(M), std::string const& placeholder) - { - m_arg->boundField = new Detail::BoundUnaryMethod(unaryMethod); - m_arg->placeholder = placeholder; - } - - // Bind a method taking a single, boolean argument (no placeholder string required) - template - void bind(void (C::*unaryMethod)(bool)) - { - m_arg->boundField = new Detail::BoundUnaryMethod(unaryMethod); - } - - // Bind a method that takes no arguments (will be called if opt is present) - template - void bind(void (C::*nullaryMethod)()) - { - m_arg->boundField = new Detail::BoundNullaryMethod(nullaryMethod); - } - - // Bind a free function taking a single argument - the object to operate on (no placeholder string required) - template - void bind(void (*unaryFunction)(C&)) - { - m_arg->boundField = new Detail::BoundUnaryFunction(unaryFunction); - } - - // Bind a free function taking a single argument - the object to operate on (requires a placeholder string) - template - void bind(void (*binaryFunction)(C&, T), std::string const& placeholder) - { - m_arg->boundField = new Detail::BoundBinaryFunction(binaryFunction); - m_arg->placeholder = placeholder; - } - - ArgBuilder& describe(std::string const& description) - { - m_arg->description = description; - return *this; - } - ArgBuilder& detail(std::string const& _detail) - { - m_arg->detail = _detail; - return *this; - } - - protected: - Arg* m_arg; - }; - - class OptBuilder : public ArgBuilder - { - public: - OptBuilder(Arg* arg) : ArgBuilder(arg) {} - OptBuilder(OptBuilder& other) : ArgBuilder(other) {} - - OptBuilder& operator[](std::string const& optName) - { - addOptName(*ArgBuilder::m_arg, optName); - return *this; - } - }; - - public: - CommandLine() - : m_boundProcessName(new Detail::NullBinder()), - m_highestSpecifiedArgPosition(0), - m_throwOnUnrecognisedTokens(false) - { - } - CommandLine(CommandLine const& other) - : m_boundProcessName(other.m_boundProcessName), - m_options(other.m_options), - m_positionalArgs(other.m_positionalArgs), - m_highestSpecifiedArgPosition(other.m_highestSpecifiedArgPosition), - m_throwOnUnrecognisedTokens(other.m_throwOnUnrecognisedTokens) - { - if (other.m_floatingArg.get()) - m_floatingArg.reset(new Arg(*other.m_floatingArg)); - } - - CommandLine& setThrowOnUnrecognisedTokens(bool shouldThrow = true) - { - m_throwOnUnrecognisedTokens = shouldThrow; - return *this; - } - - OptBuilder operator[](std::string const& optName) - { - m_options.push_back(Arg()); - addOptName(m_options.back(), optName); - OptBuilder builder(&m_options.back()); - return builder; - } - - ArgBuilder operator[](int position) - { - m_positionalArgs.insert(std::make_pair(position, Arg())); - if (position > m_highestSpecifiedArgPosition) - m_highestSpecifiedArgPosition = position; - setPositionalArg(m_positionalArgs[position], position); - ArgBuilder builder(&m_positionalArgs[position]); - return builder; - } - - // Invoke this with the _ instance - ArgBuilder operator[](UnpositionalTag) - { - if (m_floatingArg.get()) - throw std::logic_error("Only one unpositional argument can be added"); - m_floatingArg.reset(new Arg()); - ArgBuilder builder(m_floatingArg.get()); - return builder; - } - - template - void bindProcessName(M C::*field) - { - m_boundProcessName = new Detail::BoundDataMember(field); - } - template - void bindProcessName(void (C::*_unaryMethod)(M)) - { - m_boundProcessName = new Detail::BoundUnaryMethod(_unaryMethod); - } - - void optUsage(std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth) const - { - typename std::vector::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it; - std::size_t maxWidth = 0; - for (it = itBegin; it != itEnd; ++it) - maxWidth = (std::max)(maxWidth, it->commands().size()); - - for (it = itBegin; it != itEnd; ++it) - { - Detail::Text usageText(it->commands(), Detail::TextAttributes() - .setWidth(maxWidth + indent) - .setIndent(indent)); - Detail::Text desc(it->description, Detail::TextAttributes() - .setWidth(width - maxWidth - 3)); - - for (std::size_t i = 0; i < (std::max)(usageText.size(), desc.size()); ++i) - { - std::string usageCol = i < usageText.size() ? usageText[i] : ""; - os << usageCol; - - if (i < desc.size() && !desc[i].empty()) - os << std::string(indent + 2 + maxWidth - usageCol.size(), ' ') - << desc[i]; - os << "\n"; + void argSynopsis( std::ostream& os ) const { + for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) { + if( i > 1 ) + os << " "; + typename std::map::const_iterator it = m_positionalArgs.find( i ); + if( it != m_positionalArgs.end() ) + os << "<" << it->second.placeholder << ">"; + else if( m_floatingArg.get() ) + os << "<" << m_floatingArg->placeholder << ">"; + else + throw std::logic_error( "non consecutive positional arguments with no floating args" ); + } + // !TBD No indication of mandatory args + if( m_floatingArg.get() ) { + if( m_highestSpecifiedArgPosition > 1 ) + os << " "; + os << "[<" << m_floatingArg->placeholder << "> ...]"; } } - } - std::string optUsage() const - { - std::ostringstream oss; - optUsage(oss); - return oss.str(); - } - - void argSynopsis(std::ostream& os) const - { - for (int i = 1; i <= m_highestSpecifiedArgPosition; ++i) - { - if (i > 1) - os << " "; - typename std::map::const_iterator it = m_positionalArgs.find(i); - if (it != m_positionalArgs.end()) - os << "<" << it->second.placeholder << ">"; - else if (m_floatingArg.get()) - os << "<" << m_floatingArg->placeholder << ">"; - else - throw std::logic_error("non consecutive positional arguments with no floating args"); + std::string argSynopsis() const { + std::ostringstream oss; + argSynopsis( oss ); + return oss.str(); } - // !TBD No indication of mandatory args - if (m_floatingArg.get()) - { - if (m_highestSpecifiedArgPosition > 1) - os << " "; - os << "[<" << m_floatingArg->placeholder << "> ...]"; + + void usage( std::ostream& os, std::string const& procName ) const { + validate(); + os << "usage:\n " << procName << " "; + argSynopsis( os ); + if( !m_options.empty() ) { + os << " [options]\n\nwhere options are: \n"; + optUsage( os, 2 ); + } + os << "\n"; } - } - std::string argSynopsis() const - { - std::ostringstream oss; - argSynopsis(oss); - return oss.str(); - } - - void usage(std::ostream& os, std::string const& procName) const - { - validate(); - os << "usage:\n " << procName << " "; - argSynopsis(os); - if (!m_options.empty()) - { - os << " [options]\n\nwhere options are: \n"; - optUsage(os, 2); + std::string usage( std::string const& procName ) const { + std::ostringstream oss; + usage( oss, procName ); + return oss.str(); } - os << "\n"; - } - std::string usage(std::string const& procName) const - { - std::ostringstream oss; - usage(oss, procName); - return oss.str(); - } - ConfigT parse(int argc, char const* const* argv) const - { - ConfigT config; - parseInto(argc, argv, config); - return config; - } + ConfigT parse( std::vector const& args ) const { + ConfigT config; + parseInto( args, config ); + return config; + } - std::vector parseInto(int argc, char const* const* argv, ConfigT& config) const - { - std::string processName = argv[0]; - std::size_t lastSlash = processName.find_last_of("/\\"); - if (lastSlash != std::string::npos) - processName = processName.substr(lastSlash + 1); - m_boundProcessName.set(config, processName); - std::vector tokens; - Parser parser; - parser.parseIntoTokens(argc, argv, tokens); - return populate(tokens, config); - } + std::vector parseInto( std::vector const& args, ConfigT& config ) const { + std::string processName = args.empty() ? std::string() : args[0]; + std::size_t lastSlash = processName.find_last_of( "/\\" ); + if( lastSlash != std::string::npos ) + processName = processName.substr( lastSlash+1 ); + m_boundProcessName.set( config, processName ); + std::vector tokens; + Parser parser; + parser.parseIntoTokens( args, tokens ); + return populate( tokens, config ); + } - std::vector populate(std::vector const& tokens, ConfigT& config) const - { - validate(); - std::vector unusedTokens = populateOptions(tokens, config); - unusedTokens = populateFixedArgs(unusedTokens, config); - unusedTokens = populateFloatingArgs(unusedTokens, config); - return unusedTokens; - } + std::vector populate( std::vector const& tokens, ConfigT& config ) const { + validate(); + std::vector unusedTokens = populateOptions( tokens, config ); + unusedTokens = populateFixedArgs( unusedTokens, config ); + unusedTokens = populateFloatingArgs( unusedTokens, config ); + return unusedTokens; + } - std::vector populateOptions(std::vector const& tokens, ConfigT& config) const - { - std::vector unusedTokens; - std::vector errors; - for (std::size_t i = 0; i < tokens.size(); ++i) - { - Parser::Token const& token = tokens[i]; - typename std::vector::const_iterator it = m_options.begin(), itEnd = m_options.end(); - for (; it != itEnd; ++it) - { - Arg const& arg = *it; + std::vector populateOptions( std::vector const& tokens, ConfigT& config ) const { + std::vector unusedTokens; + std::vector errors; + for( std::size_t i = 0; i < tokens.size(); ++i ) { + Parser::Token const& token = tokens[i]; + typename std::vector::const_iterator it = m_options.begin(), itEnd = m_options.end(); + for(; it != itEnd; ++it ) { + Arg const& arg = *it; - try - { - if ((token.type == Parser::Token::ShortOpt && arg.hasShortName(token.data)) || - (token.type == Parser::Token::LongOpt && arg.hasLongName(token.data))) - { - if (arg.takesArg()) - { - if (i == tokens.size() - 1 || tokens[i + 1].type != Parser::Token::Positional) - errors.push_back("Expected argument to option: " + token.data); - else - arg.boundField.set(config, tokens[++i].data); + try { + if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) || + ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) { + if( arg.takesArg() ) { + if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional ) + errors.push_back( "Expected argument to option: " + token.data ); + else + arg.boundField.set( config, tokens[++i].data ); + } + else { + arg.boundField.set( config, "true" ); + } + break; } - else - { - arg.boundField.setFlag(config); - } - break; + } + catch( std::exception& ex ) { + errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" ); } } - catch (std::exception& ex) - { - errors.push_back(std::string(ex.what()) + "\n- while parsing: (" + arg.commands() + ")"); + if( it == itEnd ) { + if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens ) + unusedTokens.push_back( token ); + else if( errors.empty() && m_throwOnUnrecognisedTokens ) + errors.push_back( "unrecognised option: " + token.data ); } } - if (it == itEnd) - { - if (token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens) - unusedTokens.push_back(token); - else if (errors.empty() && m_throwOnUnrecognisedTokens) - errors.push_back("unrecognised option: " + token.data); + if( !errors.empty() ) { + std::ostringstream oss; + for( std::vector::const_iterator it = errors.begin(), itEnd = errors.end(); + it != itEnd; + ++it ) { + if( it != errors.begin() ) + oss << "\n"; + oss << *it; + } + throw std::runtime_error( oss.str() ); } + return unusedTokens; } - if (!errors.empty()) - { - std::ostringstream oss; - for (std::vector::const_iterator it = errors.begin(), itEnd = errors.end(); - it != itEnd; - ++it) - { - if (it != errors.begin()) - oss << "\n"; - oss << *it; + std::vector populateFixedArgs( std::vector const& tokens, ConfigT& config ) const { + std::vector unusedTokens; + int position = 1; + for( std::size_t i = 0; i < tokens.size(); ++i ) { + Parser::Token const& token = tokens[i]; + typename std::map::const_iterator it = m_positionalArgs.find( position ); + if( it != m_positionalArgs.end() ) + it->second.boundField.set( config, token.data ); + else + unusedTokens.push_back( token ); + if( token.type == Parser::Token::Positional ) + position++; } - throw std::runtime_error(oss.str()); + return unusedTokens; } - return unusedTokens; - } - std::vector populateFixedArgs(std::vector const& tokens, ConfigT& config) const - { - std::vector unusedTokens; - int position = 1; - for (std::size_t i = 0; i < tokens.size(); ++i) + std::vector populateFloatingArgs( std::vector const& tokens, ConfigT& config ) const { + if( !m_floatingArg.get() ) + return tokens; + std::vector unusedTokens; + for( std::size_t i = 0; i < tokens.size(); ++i ) { + Parser::Token const& token = tokens[i]; + if( token.type == Parser::Token::Positional ) + m_floatingArg->boundField.set( config, token.data ); + else + unusedTokens.push_back( token ); + } + return unusedTokens; + } + + void validate() const { - Parser::Token const& token = tokens[i]; - typename std::map::const_iterator it = m_positionalArgs.find(position); - if (it != m_positionalArgs.end()) - it->second.boundField.set(config, token.data); - else - unusedTokens.push_back(token); - if (token.type == Parser::Token::Positional) - position++; + if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() ) + throw std::logic_error( "No options or arguments specified" ); + + for( typename std::vector::const_iterator it = m_options.begin(), + itEnd = m_options.end(); + it != itEnd; ++it ) + it->validate(); } - return unusedTokens; - } - std::vector populateFloatingArgs(std::vector const& tokens, ConfigT& config) const - { - if (!m_floatingArg.get()) - return tokens; - std::vector unusedTokens; - for (std::size_t i = 0; i < tokens.size(); ++i) - { - Parser::Token const& token = tokens[i]; - if (token.type == Parser::Token::Positional) - m_floatingArg->boundField.set(config, token.data); - else - unusedTokens.push_back(token); - } - return unusedTokens; - } - void validate() const - { - if (m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get()) - throw std::logic_error("No options or arguments specified"); - - for (typename std::vector::const_iterator it = m_options.begin(), - itEnd = m_options.end(); - it != itEnd; ++it) - it->validate(); - } - - private: - Detail::BoundArgFunction m_boundProcessName; - std::vector m_options; - std::map m_positionalArgs; - ArgAutoPtr m_floatingArg; - int m_highestSpecifiedArgPosition; - bool m_throwOnUnrecognisedTokens; -}; + private: + Detail::BoundArgFunction m_boundProcessName; + std::vector m_options; + std::map m_positionalArgs; + ArgAutoPtr m_floatingArg; + int m_highestSpecifiedArgPosition; + bool m_throwOnUnrecognisedTokens; + }; } // end namespace Clara @@ -5075,185 +5052,202 @@ STITCH_CLARA_CLOSE_NAMESPACE #endif #include +#include namespace Catch { -inline void abortAfterFirst(ConfigData& config) { config.abortAfter = 1; } -inline void abortAfterX(ConfigData& config, int x) -{ - if (x < 1) - throw std::runtime_error("Value after -x or --abortAfter must be greater than zero"); - config.abortAfter = x; -} -inline void addTestOrTags(ConfigData& config, std::string const& _testSpec) { config.testsOrTags.push_back(_testSpec); } -inline void addReporterName(ConfigData& config, std::string const& _reporterName) { config.reporterNames.push_back(_reporterName); } - -inline void addWarning(ConfigData& config, std::string const& _warning) -{ - if (_warning == "NoAssertions") - config.warnings = static_cast(config.warnings | WarnAbout::NoAssertions); - else - throw std::runtime_error("Unrecognised warning: '" + _warning + "'"); -} -inline void setOrder(ConfigData& config, std::string const& order) -{ - if (startsWith("declared", order)) - config.runOrder = RunTests::InDeclarationOrder; - else if (startsWith("lexical", order)) - config.runOrder = RunTests::InLexicographicalOrder; - else if (startsWith("random", order)) - config.runOrder = RunTests::InRandomOrder; - else - throw std::runtime_error("Unrecognised ordering: '" + order + "'"); -} -inline void setRngSeed(ConfigData& config, std::string const& seed) -{ - if (seed == "time") - { - config.rngSeed = static_cast(std::time(0)); + inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; } + inline void abortAfterX( ConfigData& config, int x ) { + if( x < 1 ) + throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" ); + config.abortAfter = x; } - else - { - std::stringstream ss; - ss << seed; - ss >> config.rngSeed; - if (ss.fail()) - throw std::runtime_error("Argment to --rng-seed should be the word 'time' or a number"); + inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } + inline void addSectionToRun( ConfigData& config, std::string const& sectionName ) { config.sectionsToRun.push_back( sectionName ); } + inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); } + + inline void addWarning( ConfigData& config, std::string const& _warning ) { + if( _warning == "NoAssertions" ) + config.warnings = static_cast( config.warnings | WarnAbout::NoAssertions ); + else + throw std::runtime_error( "Unrecognised warning: '" + _warning + '\'' ); } -} -inline void setVerbosity(ConfigData& config, int level) -{ - // !TBD: accept strings? - config.verbosity = static_cast(level); -} -inline void setShowDurations(ConfigData& config, bool _showDurations) -{ - config.showDurations = _showDurations - ? ShowDurations::Always - : ShowDurations::Never; -} -inline void loadTestNamesFromFile(ConfigData& config, std::string const& _filename) -{ - std::ifstream f(_filename.c_str()); - if (!f.is_open()) - throw std::domain_error("Unable to load input file: " + _filename); - - std::string line; - while (std::getline(f, line)) - { - line = trim(line); - if (!line.empty() && !startsWith(line, "#")) - addTestOrTags(config, "\"" + line + "\","); + inline void setOrder( ConfigData& config, std::string const& order ) { + if( startsWith( "declared", order ) ) + config.runOrder = RunTests::InDeclarationOrder; + else if( startsWith( "lexical", order ) ) + config.runOrder = RunTests::InLexicographicalOrder; + else if( startsWith( "random", order ) ) + config.runOrder = RunTests::InRandomOrder; + else + throw std::runtime_error( "Unrecognised ordering: '" + order + '\'' ); } -} + inline void setRngSeed( ConfigData& config, std::string const& seed ) { + if( seed == "time" ) { + config.rngSeed = static_cast( std::time(0) ); + } + else { + std::stringstream ss; + ss << seed; + ss >> config.rngSeed; + if( ss.fail() ) + throw std::runtime_error( "Argument to --rng-seed should be the word 'time' or a number" ); + } + } + inline void setVerbosity( ConfigData& config, int level ) { + // !TBD: accept strings? + config.verbosity = static_cast( level ); + } + inline void setShowDurations( ConfigData& config, bool _showDurations ) { + config.showDurations = _showDurations + ? ShowDurations::Always + : ShowDurations::Never; + } + inline void setUseColour( ConfigData& config, std::string const& value ) { + std::string mode = toLower( value ); -inline Clara::CommandLine makeCommandLineParser() -{ + if( mode == "yes" ) + config.useColour = UseColour::Yes; + else if( mode == "no" ) + config.useColour = UseColour::No; + else if( mode == "auto" ) + config.useColour = UseColour::Auto; + else + throw std::runtime_error( "colour mode must be one of: auto, yes or no" ); + } + inline void forceColour( ConfigData& config ) { + config.useColour = UseColour::Yes; + } + inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) { + std::ifstream f( _filename.c_str() ); + if( !f.is_open() ) + throw std::domain_error( "Unable to load input file: " + _filename ); - using namespace Clara; - CommandLine cli; + std::string line; + while( std::getline( f, line ) ) { + line = trim(line); + if( !line.empty() && !startsWith( line, '#' ) ) { + if( !startsWith( line, '"' ) ) + line = '"' + line + '"'; + addTestOrTags( config, line + ',' ); + } + } + } - cli.bindProcessName(&ConfigData::processName); + inline Clara::CommandLine makeCommandLineParser() { - cli["-?"]["-h"]["--help"] - .describe("display usage information") - .bind(&ConfigData::showHelp); + using namespace Clara; + CommandLine cli; - cli["-l"]["--list-tests"] - .describe("list all/matching test cases") - .bind(&ConfigData::listTests); + cli.bindProcessName( &ConfigData::processName ); - cli["-t"]["--list-tags"] - .describe("list all/matching tags") - .bind(&ConfigData::listTags); + cli["-?"]["-h"]["--help"] + .describe( "display usage information" ) + .bind( &ConfigData::showHelp ); - cli["-s"]["--success"] - .describe("include successful tests in output") - .bind(&ConfigData::showSuccessfulTests); + cli["-l"]["--list-tests"] + .describe( "list all/matching test cases" ) + .bind( &ConfigData::listTests ); - cli["-b"]["--break"] - .describe("break into debugger on failure") - .bind(&ConfigData::shouldDebugBreak); + cli["-t"]["--list-tags"] + .describe( "list all/matching tags" ) + .bind( &ConfigData::listTags ); - cli["-e"]["--nothrow"] - .describe("skip exception tests") - .bind(&ConfigData::noThrow); + cli["-s"]["--success"] + .describe( "include successful tests in output" ) + .bind( &ConfigData::showSuccessfulTests ); - cli["-i"]["--invisibles"] - .describe("show invisibles (tabs, newlines)") - .bind(&ConfigData::showInvisibles); + cli["-b"]["--break"] + .describe( "break into debugger on failure" ) + .bind( &ConfigData::shouldDebugBreak ); - cli["-o"]["--out"] - .describe("output filename") - .bind(&ConfigData::outputFilename, "filename"); + cli["-e"]["--nothrow"] + .describe( "skip exception tests" ) + .bind( &ConfigData::noThrow ); - cli["-r"]["--reporter"] - // .placeholder( "name[:filename]" ) - .describe("reporter to use (defaults to console)") - .bind(&addReporterName, "name"); + cli["-i"]["--invisibles"] + .describe( "show invisibles (tabs, newlines)" ) + .bind( &ConfigData::showInvisibles ); - cli["-n"]["--name"] - .describe("suite name") - .bind(&ConfigData::name, "name"); + cli["-o"]["--out"] + .describe( "output filename" ) + .bind( &ConfigData::outputFilename, "filename" ); - cli["-a"]["--abort"] - .describe("abort at first failure") - .bind(&abortAfterFirst); + cli["-r"]["--reporter"] +// .placeholder( "name[:filename]" ) + .describe( "reporter to use (defaults to console)" ) + .bind( &addReporterName, "name" ); - cli["-x"]["--abortx"] - .describe("abort after x failures") - .bind(&abortAfterX, "no. failures"); + cli["-n"]["--name"] + .describe( "suite name" ) + .bind( &ConfigData::name, "name" ); - cli["-w"]["--warn"] - .describe("enable warnings") - .bind(&addWarning, "warning name"); + cli["-a"]["--abort"] + .describe( "abort at first failure" ) + .bind( &abortAfterFirst ); - // - needs updating if reinstated - // cli.into( &setVerbosity ) - // .describe( "level of verbosity (0=no output)" ) - // .shortOpt( "v") - // .longOpt( "verbosity" ) - // .placeholder( "level" ); + cli["-x"]["--abortx"] + .describe( "abort after x failures" ) + .bind( &abortAfterX, "no. failures" ); - cli[_] - .describe("which test or tests to use") - .bind(&addTestOrTags, "test name, pattern or tags"); + cli["-w"]["--warn"] + .describe( "enable warnings" ) + .bind( &addWarning, "warning name" ); - cli["-d"]["--durations"] - .describe("show test durations") - .bind(&setShowDurations, "yes/no"); +// - needs updating if reinstated +// cli.into( &setVerbosity ) +// .describe( "level of verbosity (0=no output)" ) +// .shortOpt( "v") +// .longOpt( "verbosity" ) +// .placeholder( "level" ); - cli["-f"]["--input-file"] - .describe("load test names to run from a file") - .bind(&loadTestNamesFromFile, "filename"); + cli[_] + .describe( "which test or tests to use" ) + .bind( &addTestOrTags, "test name, pattern or tags" ); - cli["-#"]["--filenames-as-tags"] - .describe("adds a tag for the filename") - .bind(&ConfigData::filenamesAsTags); + cli["-d"]["--durations"] + .describe( "show test durations" ) + .bind( &setShowDurations, "yes|no" ); - // Less common commands which don't have a short form - cli["--list-test-names-only"] - .describe("list all/matching test cases names only") - .bind(&ConfigData::listTestNamesOnly); + cli["-f"]["--input-file"] + .describe( "load test names to run from a file" ) + .bind( &loadTestNamesFromFile, "filename" ); - cli["--list-reporters"] - .describe("list all reporters") - .bind(&ConfigData::listReporters); + cli["-#"]["--filenames-as-tags"] + .describe( "adds a tag for the filename" ) + .bind( &ConfigData::filenamesAsTags ); - cli["--order"] - .describe("test case order (defaults to decl)") - .bind(&setOrder, "decl|lex|rand"); + cli["-c"]["--section"] + .describe( "specify section to run" ) + .bind( &addSectionToRun, "section name" ); - cli["--rng-seed"] - .describe("set a specific seed for random numbers") - .bind(&setRngSeed, "'time'|number"); + // Less common commands which don't have a short form + cli["--list-test-names-only"] + .describe( "list all/matching test cases names only" ) + .bind( &ConfigData::listTestNamesOnly ); - cli["--force-colour"] - .describe("force colourised output") - .bind(&ConfigData::forceColour); + cli["--list-reporters"] + .describe( "list all reporters" ) + .bind( &ConfigData::listReporters ); - return cli; -} + cli["--order"] + .describe( "test case order (defaults to decl)" ) + .bind( &setOrder, "decl|lex|rand" ); + + cli["--rng-seed"] + .describe( "set a specific seed for random numbers" ) + .bind( &setRngSeed, "'time'|number" ); + + cli["--force-colour"] + .describe( "force colourised output (deprecated)" ) + .bind( &forceColour ); + + cli["--use-colour"] + .describe( "should output be colourised" ) + .bind( &setUseColour, "yes|no" ); + + return cli; + } } // end namespace Catch @@ -5269,18 +5263,18 @@ inline Clara::CommandLine makeCommandLineParser() // #included from: ../external/tbc_text_format.h // Only use header guard if we are not using an outer namespace #ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE -#ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED -#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -#define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -#endif -#else -#define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED -#endif +# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED +# ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +# define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +# endif +# else +# define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED +# endif #endif #ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -#include #include #include +#include // Use optional outer namespace #ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE @@ -5290,153 +5284,132 @@ namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE { namespace Tbc { #ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH -const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; + const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; #else -const unsigned int consoleWidth = 80; + const unsigned int consoleWidth = 80; #endif -struct TextAttributes -{ - TextAttributes() - : initialIndent(std::string::npos), - indent(0), - width(consoleWidth - 1), - tabChar('\t') - { - } + struct TextAttributes { + TextAttributes() + : initialIndent( std::string::npos ), + indent( 0 ), + width( consoleWidth-1 ) + {} - TextAttributes& setInitialIndent(std::size_t _value) - { - initialIndent = _value; - return *this; - } - TextAttributes& setIndent(std::size_t _value) - { - indent = _value; - return *this; - } - TextAttributes& setWidth(std::size_t _value) - { - width = _value; - return *this; - } - TextAttributes& setTabChar(char _value) - { - tabChar = _value; - return *this; - } + TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } + TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } + TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } - std::size_t initialIndent; // indent of first line, or npos - std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos - std::size_t width; // maximum width of text, including indent. Longer text will wrap - char tabChar; // If this char is seen the indent is changed to current pos -}; + std::size_t initialIndent; // indent of first line, or npos + std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos + std::size_t width; // maximum width of text, including indent. Longer text will wrap + }; -class Text -{ - public: - Text(std::string const& _str, TextAttributes const& _attr = TextAttributes()) - : attr(_attr) - { - std::string wrappableChars = " [({.,/|\\-"; - std::size_t indent = _attr.initialIndent != std::string::npos - ? _attr.initialIndent - : _attr.indent; - std::string remainder = _str; - - while (!remainder.empty()) + class Text { + public: + Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) + : attr( _attr ) { - if (lines.size() >= 1000) - { - lines.push_back("... message truncated due to excessive size"); - return; - } - std::size_t tabPos = std::string::npos; - std::size_t width = (std::min)(remainder.size(), _attr.width - indent); - std::size_t pos = remainder.find_first_of('\n'); - if (pos <= width) - { - width = pos; - } - pos = remainder.find_last_of(_attr.tabChar, width); - if (pos != std::string::npos) - { - tabPos = pos; - if (remainder[width] == '\n') - width--; - remainder = remainder.substr(0, tabPos) + remainder.substr(tabPos + 1); - } + const std::string wrappableBeforeChars = "[({<\t"; + const std::string wrappableAfterChars = "])}>-,./|\\"; + const std::string wrappableInsteadOfChars = " \n\r"; + std::string indent = _attr.initialIndent != std::string::npos + ? std::string( _attr.initialIndent, ' ' ) + : std::string( _attr.indent, ' ' ); - if (width == remainder.size()) - { - spliceLine(indent, remainder, width); - } - else if (remainder[width] == '\n') - { - spliceLine(indent, remainder, width); - if (width <= 1 || remainder.size() != 1) - remainder = remainder.substr(1); - indent = _attr.indent; - } - else - { - pos = remainder.find_last_of(wrappableChars, width); - if (pos != std::string::npos && pos > 0) - { - spliceLine(indent, remainder, pos); - if (remainder[0] == ' ') - remainder = remainder.substr(1); + typedef std::string::const_iterator iterator; + iterator it = _str.begin(); + const iterator strEnd = _str.end(); + + while( it != strEnd ) { + + if( lines.size() >= 1000 ) { + lines.push_back( "... message truncated due to excessive size" ); + return; } - else - { - spliceLine(indent, remainder, width - 1); - lines.back() += "-"; + + std::string suffix; + std::size_t width = (std::min)( static_cast( strEnd-it ), _attr.width-static_cast( indent.size() ) ); + iterator itEnd = it+width; + iterator itNext = _str.end(); + + iterator itNewLine = std::find( it, itEnd, '\n' ); + if( itNewLine != itEnd ) + itEnd = itNewLine; + + if( itEnd != strEnd ) { + bool foundWrapPoint = false; + iterator findIt = itEnd; + do { + if( wrappableAfterChars.find( *findIt ) != std::string::npos && findIt != itEnd ) { + itEnd = findIt+1; + itNext = findIt+1; + foundWrapPoint = true; + } + else if( findIt > it && wrappableBeforeChars.find( *findIt ) != std::string::npos ) { + itEnd = findIt; + itNext = findIt; + foundWrapPoint = true; + } + else if( wrappableInsteadOfChars.find( *findIt ) != std::string::npos ) { + itNext = findIt+1; + itEnd = findIt; + foundWrapPoint = true; + } + if( findIt == it ) + break; + else + --findIt; + } + while( !foundWrapPoint ); + + if( !foundWrapPoint ) { + // No good wrap char, so we'll break mid word and add a hyphen + --itEnd; + itNext = itEnd; + suffix = "-"; + } + else { + while( itEnd > it && wrappableInsteadOfChars.find( *(itEnd-1) ) != std::string::npos ) + --itEnd; + } } - if (lines.size() == 1) - indent = _attr.indent; - if (tabPos != std::string::npos) - indent += tabPos; + lines.push_back( indent + std::string( it, itEnd ) + suffix ); + + if( indent.size() != _attr.indent ) + indent = std::string( _attr.indent, ' ' ); + it = itNext; } } - } - void spliceLine(std::size_t _indent, std::string& _remainder, std::size_t _pos) - { - lines.push_back(std::string(_indent, ' ') + _remainder.substr(0, _pos)); - _remainder = _remainder.substr(_pos); - } + typedef std::vector::const_iterator const_iterator; - typedef std::vector::const_iterator const_iterator; - - const_iterator begin() const { return lines.begin(); } - const_iterator end() const { return lines.end(); } - std::string const& last() const { return lines.back(); } - std::size_t size() const { return lines.size(); } - std::string const& operator[](std::size_t _index) const { return lines[_index]; } - std::string toString() const - { - std::ostringstream oss; - oss << *this; - return oss.str(); - } - - inline friend std::ostream& operator<<(std::ostream& _stream, Text const& _text) - { - for (Text::const_iterator it = _text.begin(), itEnd = _text.end(); - it != itEnd; ++it) - { - if (it != _text.begin()) - _stream << "\n"; - _stream << *it; + const_iterator begin() const { return lines.begin(); } + const_iterator end() const { return lines.end(); } + std::string const& last() const { return lines.back(); } + std::size_t size() const { return lines.size(); } + std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } + std::string toString() const { + std::ostringstream oss; + oss << *this; + return oss.str(); } - return _stream; - } - private: - std::string str; - TextAttributes attr; - std::vector lines; -}; + inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { + for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); + it != itEnd; ++it ) { + if( it != _text.begin() ) + _stream << "\n"; + _stream << *it; + } + return _stream; + } + + private: + std::string str; + TextAttributes attr; + std::vector lines; + }; } // end namespace Tbc @@ -5448,8 +5421,8 @@ class Text #undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE namespace Catch { -using Tbc::Text; -using Tbc::TextAttributes; + using Tbc::Text; + using Tbc::TextAttributes; } // #included from: catch_console_colour.hpp @@ -5457,510 +5430,473 @@ using Tbc::TextAttributes; namespace Catch { -struct Colour -{ - enum Code - { - None = 0, + struct Colour { + enum Code { + None = 0, - White, - Red, - Green, - Blue, - Cyan, - Yellow, - Grey, + White, + Red, + Green, + Blue, + Cyan, + Yellow, + Grey, - Bright = 0x10, + Bright = 0x10, - BrightRed = Bright | Red, - BrightGreen = Bright | Green, - LightGrey = Bright | Grey, - BrightWhite = Bright | White, + BrightRed = Bright | Red, + BrightGreen = Bright | Green, + LightGrey = Bright | Grey, + BrightWhite = Bright | White, - // By intention - FileName = LightGrey, - Warning = Yellow, - ResultError = BrightRed, - ResultSuccess = BrightGreen, - ResultExpectedFailure = Warning, + // By intention + FileName = LightGrey, + Warning = Yellow, + ResultError = BrightRed, + ResultSuccess = BrightGreen, + ResultExpectedFailure = Warning, - Error = BrightRed, - Success = Green, + Error = BrightRed, + Success = Green, - OriginalExpression = Cyan, - ReconstructedExpression = Yellow, + OriginalExpression = Cyan, + ReconstructedExpression = Yellow, - SecondaryText = LightGrey, - Headers = White + SecondaryText = LightGrey, + Headers = White + }; + + // Use constructed object for RAII guard + Colour( Code _colourCode ); + Colour( Colour const& other ); + ~Colour(); + + // Use static method for one-shot changes + static void use( Code _colourCode ); + + private: + bool m_moved; }; - // Use constructed object for RAII guard - Colour(Code _colourCode); - Colour(Colour const& other); - ~Colour(); - - // Use static method for one-shot changes - static void use(Code _colourCode); - - private: - bool m_moved; -}; - -inline std::ostream& operator<<(std::ostream& os, Colour const&) { return os; } + inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; } } // end namespace Catch // #included from: catch_interfaces_reporter.h #define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED -#include -#include -#include #include +#include +#include -namespace Catch { -struct ReporterConfig +namespace Catch { - explicit ReporterConfig(Ptr const& _fullConfig) - : m_stream(&_fullConfig->stream()), m_fullConfig(_fullConfig) {} + struct ReporterConfig { + explicit ReporterConfig( Ptr const& _fullConfig ) + : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} - ReporterConfig(Ptr const& _fullConfig, std::ostream& _stream) - : m_stream(&_stream), m_fullConfig(_fullConfig) {} + ReporterConfig( Ptr const& _fullConfig, std::ostream& _stream ) + : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} - std::ostream& stream() const { return *m_stream; } - Ptr fullConfig() const { return m_fullConfig; } + std::ostream& stream() const { return *m_stream; } + Ptr fullConfig() const { return m_fullConfig; } - private: - std::ostream* m_stream; - Ptr m_fullConfig; -}; + private: + std::ostream* m_stream; + Ptr m_fullConfig; + }; -struct ReporterPreferences -{ - ReporterPreferences() - : shouldRedirectStdOut(false) - { - } + struct ReporterPreferences { + ReporterPreferences() + : shouldRedirectStdOut( false ) + {} - bool shouldRedirectStdOut; -}; + bool shouldRedirectStdOut; + }; -template -struct LazyStat : Option -{ - LazyStat() : used(false) {} - LazyStat& operator=(T const& _value) - { - Option::operator=(_value); - used = false; - return *this; - } - void reset() - { - Option::reset(); - used = false; - } - bool used; -}; - -struct TestRunInfo -{ - TestRunInfo(std::string const& _name) : name(_name) {} - std::string name; -}; -struct GroupInfo -{ - GroupInfo(std::string const& _name, - std::size_t _groupIndex, - std::size_t _groupsCount) - : name(_name), - groupIndex(_groupIndex), - groupsCounts(_groupsCount) - { - } - - std::string name; - std::size_t groupIndex; - std::size_t groupsCounts; -}; - -struct AssertionStats -{ - AssertionStats(AssertionResult const& _assertionResult, - std::vector const& _infoMessages, - Totals const& _totals) - : assertionResult(_assertionResult), - infoMessages(_infoMessages), - totals(_totals) - { - if (assertionResult.hasMessage()) - { - // Copy message into messages list. - // !TBD This should have been done earlier, somewhere - MessageBuilder builder(assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType()); - builder << assertionResult.getMessage(); - builder.m_info.message = builder.m_stream.str(); - - infoMessages.push_back(builder.m_info); + template + struct LazyStat : Option { + LazyStat() : used( false ) {} + LazyStat& operator=( T const& _value ) { + Option::operator=( _value ); + used = false; + return *this; } - } - virtual ~AssertionStats(); + void reset() { + Option::reset(); + used = false; + } + bool used; + }; -#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - AssertionStats(AssertionStats const&) = default; - AssertionStats(AssertionStats&&) = default; - AssertionStats& operator=(AssertionStats const&) = default; - AssertionStats& operator=(AssertionStats&&) = default; -#endif + struct TestRunInfo { + TestRunInfo( std::string const& _name ) : name( _name ) {} + std::string name; + }; + struct GroupInfo { + GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ) + : name( _name ), + groupIndex( _groupIndex ), + groupsCounts( _groupsCount ) + {} - AssertionResult assertionResult; - std::vector infoMessages; - Totals totals; -}; + std::string name; + std::size_t groupIndex; + std::size_t groupsCounts; + }; -struct SectionStats -{ - SectionStats(SectionInfo const& _sectionInfo, - Counts const& _assertions, - double _durationInSeconds, - bool _missingAssertions) - : sectionInfo(_sectionInfo), - assertions(_assertions), - durationInSeconds(_durationInSeconds), - missingAssertions(_missingAssertions) - { - } - virtual ~SectionStats(); -#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - SectionStats(SectionStats const&) = default; - SectionStats(SectionStats&&) = default; - SectionStats& operator=(SectionStats const&) = default; - SectionStats& operator=(SectionStats&&) = default; -#endif + struct AssertionStats { + AssertionStats( AssertionResult const& _assertionResult, + std::vector const& _infoMessages, + Totals const& _totals ) + : assertionResult( _assertionResult ), + infoMessages( _infoMessages ), + totals( _totals ) + { + if( assertionResult.hasMessage() ) { + // Copy message into messages list. + // !TBD This should have been done earlier, somewhere + MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); + builder << assertionResult.getMessage(); + builder.m_info.message = builder.m_stream.str(); - SectionInfo sectionInfo; - Counts assertions; - double durationInSeconds; - bool missingAssertions; -}; + infoMessages.push_back( builder.m_info ); + } + } + virtual ~AssertionStats(); -struct TestCaseStats -{ - TestCaseStats(TestCaseInfo const& _testInfo, - Totals const& _totals, - std::string const& _stdOut, - std::string const& _stdErr, - bool _aborting) - : testInfo(_testInfo), - totals(_totals), - stdOut(_stdOut), - stdErr(_stdErr), - aborting(_aborting) - { - } - virtual ~TestCaseStats(); +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + AssertionStats( AssertionStats const& ) = default; + AssertionStats( AssertionStats && ) = default; + AssertionStats& operator = ( AssertionStats const& ) = default; + AssertionStats& operator = ( AssertionStats && ) = default; +# endif -#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - TestCaseStats(TestCaseStats const&) = default; - TestCaseStats(TestCaseStats&&) = default; - TestCaseStats& operator=(TestCaseStats const&) = default; - TestCaseStats& operator=(TestCaseStats&&) = default; -#endif + AssertionResult assertionResult; + std::vector infoMessages; + Totals totals; + }; - TestCaseInfo testInfo; - Totals totals; - std::string stdOut; - std::string stdErr; - bool aborting; -}; + struct SectionStats { + SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ) + : sectionInfo( _sectionInfo ), + assertions( _assertions ), + durationInSeconds( _durationInSeconds ), + missingAssertions( _missingAssertions ) + {} + virtual ~SectionStats(); +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + SectionStats( SectionStats const& ) = default; + SectionStats( SectionStats && ) = default; + SectionStats& operator = ( SectionStats const& ) = default; + SectionStats& operator = ( SectionStats && ) = default; +# endif -struct TestGroupStats -{ - TestGroupStats(GroupInfo const& _groupInfo, - Totals const& _totals, - bool _aborting) - : groupInfo(_groupInfo), - totals(_totals), - aborting(_aborting) - { - } - TestGroupStats(GroupInfo const& _groupInfo) - : groupInfo(_groupInfo), - aborting(false) - { - } - virtual ~TestGroupStats(); + SectionInfo sectionInfo; + Counts assertions; + double durationInSeconds; + bool missingAssertions; + }; -#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - TestGroupStats(TestGroupStats const&) = default; - TestGroupStats(TestGroupStats&&) = default; - TestGroupStats& operator=(TestGroupStats const&) = default; - TestGroupStats& operator=(TestGroupStats&&) = default; -#endif + struct TestCaseStats { + TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ) + : testInfo( _testInfo ), + totals( _totals ), + stdOut( _stdOut ), + stdErr( _stdErr ), + aborting( _aborting ) + {} + virtual ~TestCaseStats(); - GroupInfo groupInfo; - Totals totals; - bool aborting; -}; +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + TestCaseStats( TestCaseStats const& ) = default; + TestCaseStats( TestCaseStats && ) = default; + TestCaseStats& operator = ( TestCaseStats const& ) = default; + TestCaseStats& operator = ( TestCaseStats && ) = default; +# endif -struct TestRunStats -{ - TestRunStats(TestRunInfo const& _runInfo, - Totals const& _totals, - bool _aborting) - : runInfo(_runInfo), - totals(_totals), - aborting(_aborting) - { - } - virtual ~TestRunStats(); + TestCaseInfo testInfo; + Totals totals; + std::string stdOut; + std::string stdErr; + bool aborting; + }; -#ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS - TestRunStats(TestRunStats const& _other) - : runInfo(_other.runInfo), - totals(_other.totals), - aborting(_other.aborting) - { - } -#else - TestRunStats(TestRunStats const&) = default; - TestRunStats(TestRunStats&&) = default; - TestRunStats& operator=(TestRunStats const&) = default; - TestRunStats& operator=(TestRunStats&&) = default; -#endif + struct TestGroupStats { + TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ) + : groupInfo( _groupInfo ), + totals( _totals ), + aborting( _aborting ) + {} + TestGroupStats( GroupInfo const& _groupInfo ) + : groupInfo( _groupInfo ), + aborting( false ) + {} + virtual ~TestGroupStats(); - TestRunInfo runInfo; - Totals totals; - bool aborting; -}; +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + TestGroupStats( TestGroupStats const& ) = default; + TestGroupStats( TestGroupStats && ) = default; + TestGroupStats& operator = ( TestGroupStats const& ) = default; + TestGroupStats& operator = ( TestGroupStats && ) = default; +# endif -struct IStreamingReporter : IShared -{ - virtual ~IStreamingReporter(); + GroupInfo groupInfo; + Totals totals; + bool aborting; + }; - // Implementing class must also provide the following static method: - // static std::string getDescription(); + struct TestRunStats { + TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ) + : runInfo( _runInfo ), + totals( _totals ), + aborting( _aborting ) + {} + virtual ~TestRunStats(); - virtual ReporterPreferences getPreferences() const = 0; +# ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS + TestRunStats( TestRunStats const& _other ) + : runInfo( _other.runInfo ), + totals( _other.totals ), + aborting( _other.aborting ) + {} +# else + TestRunStats( TestRunStats const& ) = default; + TestRunStats( TestRunStats && ) = default; + TestRunStats& operator = ( TestRunStats const& ) = default; + TestRunStats& operator = ( TestRunStats && ) = default; +# endif - virtual void noMatchingTestCases(std::string const& spec) = 0; + TestRunInfo runInfo; + Totals totals; + bool aborting; + }; - virtual void testRunStarting(TestRunInfo const& testRunInfo) = 0; - virtual void testGroupStarting(GroupInfo const& groupInfo) = 0; + class MultipleReporters; - virtual void testCaseStarting(TestCaseInfo const& testInfo) = 0; - virtual void sectionStarting(SectionInfo const& sectionInfo) = 0; + struct IStreamingReporter : IShared { + virtual ~IStreamingReporter(); - virtual void assertionStarting(AssertionInfo const& assertionInfo) = 0; + // Implementing class must also provide the following static method: + // static std::string getDescription(); - // The return value indicates if the messages buffer should be cleared: - virtual bool assertionEnded(AssertionStats const& assertionStats) = 0; + virtual ReporterPreferences getPreferences() const = 0; - virtual void sectionEnded(SectionStats const& sectionStats) = 0; - virtual void testCaseEnded(TestCaseStats const& testCaseStats) = 0; - virtual void testGroupEnded(TestGroupStats const& testGroupStats) = 0; - virtual void testRunEnded(TestRunStats const& testRunStats) = 0; + virtual void noMatchingTestCases( std::string const& spec ) = 0; - virtual void skipTest(TestCaseInfo const& testInfo) = 0; -}; + virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; + virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; -struct IReporterFactory : IShared -{ - virtual ~IReporterFactory(); - virtual IStreamingReporter* create(ReporterConfig const& config) const = 0; - virtual std::string getDescription() const = 0; -}; + virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; + virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; -struct IReporterRegistry -{ - typedef std::map> FactoryMap; - typedef std::vector> Listeners; + virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; - virtual ~IReporterRegistry(); - virtual IStreamingReporter* create(std::string const& name, Ptr const& config) const = 0; - virtual FactoryMap const& getFactories() const = 0; - virtual Listeners const& getListeners() const = 0; -}; + // The return value indicates if the messages buffer should be cleared: + virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; + + virtual void sectionEnded( SectionStats const& sectionStats ) = 0; + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; + virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; + + virtual void skipTest( TestCaseInfo const& testInfo ) = 0; + + virtual MultipleReporters* tryAsMulti() { return CATCH_NULL; } + }; + + struct IReporterFactory : IShared { + virtual ~IReporterFactory(); + virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0; + virtual std::string getDescription() const = 0; + }; + + struct IReporterRegistry { + typedef std::map > FactoryMap; + typedef std::vector > Listeners; + + virtual ~IReporterRegistry(); + virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const = 0; + virtual FactoryMap const& getFactories() const = 0; + virtual Listeners const& getListeners() const = 0; + }; + + Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ); -Ptr addReporter(Ptr const& existingReporter, Ptr const& additionalReporter); } -#include #include +#include namespace Catch { -inline std::size_t listTests(Config const& config) -{ + inline std::size_t listTests( Config const& config ) { - TestSpec testSpec = config.testSpec(); - if (config.testSpec().hasFilters()) - Catch::cout() << "Matching test cases:\n"; - else - { - Catch::cout() << "All available test cases:\n"; - testSpec = TestSpecParser(ITagAliasRegistry::get()).parse("*").testSpec(); - } - - std::size_t matchedTests = 0; - TextAttributes nameAttr, tagsAttr; - nameAttr.setInitialIndent(2).setIndent(4); - tagsAttr.setIndent(6); - - std::vector matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); - for (std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); - it != itEnd; - ++it) - { - matchedTests++; - TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - Colour::Code colour = testCaseInfo.isHidden() - ? Colour::SecondaryText - : Colour::None; - Colour colourGuard(colour); - - Catch::cout() << Text(testCaseInfo.name, nameAttr) << std::endl; - if (!testCaseInfo.tags.empty()) - Catch::cout() << Text(testCaseInfo.tagsAsString, tagsAttr) << std::endl; - } - - if (!config.testSpec().hasFilters()) - Catch::cout() << pluralise(matchedTests, "test case") << "\n" - << std::endl; - else - Catch::cout() << pluralise(matchedTests, "matching test case") << "\n" - << std::endl; - return matchedTests; -} - -inline std::size_t listTestsNamesOnly(Config const& config) -{ - TestSpec testSpec = config.testSpec(); - if (!config.testSpec().hasFilters()) - testSpec = TestSpecParser(ITagAliasRegistry::get()).parse("*").testSpec(); - std::size_t matchedTests = 0; - std::vector matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); - for (std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); - it != itEnd; - ++it) - { - matchedTests++; - TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - Catch::cout() << testCaseInfo.name << std::endl; - } - return matchedTests; -} - -struct TagInfo -{ - TagInfo() : count(0) {} - void add(std::string const& spelling) - { - ++count; - spellings.insert(spelling); - } - std::string all() const - { - std::string out; - for (std::set::const_iterator it = spellings.begin(), itEnd = spellings.end(); - it != itEnd; - ++it) - out += "[" + *it + "]"; - return out; - } - std::set spellings; - std::size_t count; -}; - -inline std::size_t listTags(Config const& config) -{ - TestSpec testSpec = config.testSpec(); - if (config.testSpec().hasFilters()) - Catch::cout() << "Tags for matching test cases:\n"; - else - { - Catch::cout() << "All available tags:\n"; - testSpec = TestSpecParser(ITagAliasRegistry::get()).parse("*").testSpec(); - } - - std::map tagCounts; - - std::vector matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); - for (std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); - it != itEnd; - ++it) - { - for (std::set::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), - tagItEnd = it->getTestCaseInfo().tags.end(); - tagIt != tagItEnd; - ++tagIt) - { - std::string tagName = *tagIt; - std::string lcaseTagName = toLower(tagName); - std::map::iterator countIt = tagCounts.find(lcaseTagName); - if (countIt == tagCounts.end()) - countIt = tagCounts.insert(std::make_pair(lcaseTagName, TagInfo())).first; - countIt->second.add(tagName); + TestSpec testSpec = config.testSpec(); + if( config.testSpec().hasFilters() ) + Catch::cout() << "Matching test cases:\n"; + else { + Catch::cout() << "All available test cases:\n"; + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); } + + std::size_t matchedTests = 0; + TextAttributes nameAttr, tagsAttr; + nameAttr.setInitialIndent( 2 ).setIndent( 4 ); + tagsAttr.setIndent( 6 ); + + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); + it != itEnd; + ++it ) { + matchedTests++; + TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); + Colour::Code colour = testCaseInfo.isHidden() + ? Colour::SecondaryText + : Colour::None; + Colour colourGuard( colour ); + + Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl; + if( !testCaseInfo.tags.empty() ) + Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl; + } + + if( !config.testSpec().hasFilters() ) + Catch::cout() << pluralise( matchedTests, "test case" ) << '\n' << std::endl; + else + Catch::cout() << pluralise( matchedTests, "matching test case" ) << '\n' << std::endl; + return matchedTests; } - for (std::map::const_iterator countIt = tagCounts.begin(), - countItEnd = tagCounts.end(); - countIt != countItEnd; - ++countIt) - { - std::ostringstream oss; - oss << " " << std::setw(2) << countIt->second.count << " "; - Text wrapper(countIt->second.all(), TextAttributes() - .setInitialIndent(0) - .setIndent(oss.str().size()) - .setWidth(CATCH_CONFIG_CONSOLE_WIDTH - 10)); - Catch::cout() << oss.str() << wrapper << "\n"; + inline std::size_t listTestsNamesOnly( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( !config.testSpec().hasFilters() ) + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); + std::size_t matchedTests = 0; + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); + it != itEnd; + ++it ) { + matchedTests++; + TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); + if( startsWith( testCaseInfo.name, '#' ) ) + Catch::cout() << '"' << testCaseInfo.name << '"' << std::endl; + else + Catch::cout() << testCaseInfo.name << std::endl; + } + return matchedTests; } - Catch::cout() << pluralise(tagCounts.size(), "tag") << "\n" - << std::endl; - return tagCounts.size(); -} -inline std::size_t listReporters(Config const& /*config*/) -{ - Catch::cout() << "Available reporters:\n"; - IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); - IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it; - std::size_t maxNameLen = 0; - for (it = itBegin; it != itEnd; ++it) - maxNameLen = (std::max)(maxNameLen, it->first.size()); + struct TagInfo { + TagInfo() : count ( 0 ) {} + void add( std::string const& spelling ) { + ++count; + spellings.insert( spelling ); + } + std::string all() const { + std::string out; + for( std::set::const_iterator it = spellings.begin(), itEnd = spellings.end(); + it != itEnd; + ++it ) + out += "[" + *it + "]"; + return out; + } + std::set spellings; + std::size_t count; + }; - for (it = itBegin; it != itEnd; ++it) - { - Text wrapper(it->second->getDescription(), TextAttributes() - .setInitialIndent(0) - .setIndent(7 + maxNameLen) - .setWidth(CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8)); - Catch::cout() << " " - << it->first - << ":" - << std::string(maxNameLen - it->first.size() + 2, ' ') - << wrapper << "\n"; + inline std::size_t listTags( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( config.testSpec().hasFilters() ) + Catch::cout() << "Tags for matching test cases:\n"; + else { + Catch::cout() << "All available tags:\n"; + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); + } + + std::map tagCounts; + + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); + it != itEnd; + ++it ) { + for( std::set::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), + tagItEnd = it->getTestCaseInfo().tags.end(); + tagIt != tagItEnd; + ++tagIt ) { + std::string tagName = *tagIt; + std::string lcaseTagName = toLower( tagName ); + std::map::iterator countIt = tagCounts.find( lcaseTagName ); + if( countIt == tagCounts.end() ) + countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; + countIt->second.add( tagName ); + } + } + + for( std::map::const_iterator countIt = tagCounts.begin(), + countItEnd = tagCounts.end(); + countIt != countItEnd; + ++countIt ) { + std::ostringstream oss; + oss << " " << std::setw(2) << countIt->second.count << " "; + Text wrapper( countIt->second.all(), TextAttributes() + .setInitialIndent( 0 ) + .setIndent( oss.str().size() ) + .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) ); + Catch::cout() << oss.str() << wrapper << '\n'; + } + Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; + return tagCounts.size(); } - Catch::cout() << std::endl; - return factories.size(); -} -inline Option list(Config const& config) -{ - Option listedCount; - if (config.listTests()) - listedCount = listedCount.valueOr(0) + listTests(config); - if (config.listTestNamesOnly()) - listedCount = listedCount.valueOr(0) + listTestsNamesOnly(config); - if (config.listTags()) - listedCount = listedCount.valueOr(0) + listTags(config); - if (config.listReporters()) - listedCount = listedCount.valueOr(0) + listReporters(config); - return listedCount; -} + inline std::size_t listReporters( Config const& /*config*/ ) { + Catch::cout() << "Available reporters:\n"; + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it; + std::size_t maxNameLen = 0; + for(it = itBegin; it != itEnd; ++it ) + maxNameLen = (std::max)( maxNameLen, it->first.size() ); + + for(it = itBegin; it != itEnd; ++it ) { + Text wrapper( it->second->getDescription(), TextAttributes() + .setInitialIndent( 0 ) + .setIndent( 7+maxNameLen ) + .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) ); + Catch::cout() << " " + << it->first + << ':' + << std::string( maxNameLen - it->first.size() + 2, ' ' ) + << wrapper << '\n'; + } + Catch::cout() << std::endl; + return factories.size(); + } + + inline Option list( Config const& config ) { + Option listedCount; + if( config.listTests() ) + listedCount = listedCount.valueOr(0) + listTests( config ); + if( config.listTestNamesOnly() ) + listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); + if( config.listTags() ) + listedCount = listedCount.valueOr(0) + listTags( config ); + if( config.listReporters() ) + listedCount = listedCount.valueOr(0) + listReporters( config ); + return listedCount; + } } // end namespace Catch @@ -5970,349 +5906,350 @@ inline Option list(Config const& config) // #included from: catch_test_case_tracker.hpp #define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED -#include -#include +#include #include +#include #include +#include + +CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS namespace Catch { namespace TestCaseTracking { -struct ITracker : SharedImpl<> -{ - virtual ~ITracker(); + struct NameAndLocation { + std::string name; + SourceLineInfo location; - // static queries - virtual std::string name() const = 0; - - // dynamic queries - virtual bool isComplete() const = 0; // Successfully completed or failed - virtual bool isSuccessfullyCompleted() const = 0; - virtual bool isOpen() const = 0; // Started but not complete - virtual bool hasChildren() const = 0; - - virtual ITracker& parent() = 0; - - // actions - virtual void close() = 0; // Successfully complete - virtual void fail() = 0; - virtual void markAsNeedingAnotherRun() = 0; - - virtual void addChild(Ptr const& child) = 0; - virtual ITracker* findChild(std::string const& name) = 0; - virtual void openChild() = 0; -}; - -class TrackerContext -{ - - enum RunState - { - NotStarted, - Executing, - CompletedCycle + NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) + : name( _name ), + location( _location ) + {} }; - Ptr m_rootTracker; - ITracker* m_currentTracker; - RunState m_runState; + struct ITracker : SharedImpl<> { + virtual ~ITracker(); - public: - static TrackerContext& instance() - { - static TrackerContext s_instance; - return s_instance; - } + // static queries + virtual NameAndLocation const& nameAndLocation() const = 0; - TrackerContext() - : m_currentTracker(CATCH_NULL), - m_runState(NotStarted) - { - } + // dynamic queries + virtual bool isComplete() const = 0; // Successfully completed or failed + virtual bool isSuccessfullyCompleted() const = 0; + virtual bool isOpen() const = 0; // Started but not complete + virtual bool hasChildren() const = 0; - ITracker& startRun(); + virtual ITracker& parent() = 0; - void endRun() - { - m_rootTracker.reset(); - m_currentTracker = CATCH_NULL; - m_runState = NotStarted; - } + // actions + virtual void close() = 0; // Successfully complete + virtual void fail() = 0; + virtual void markAsNeedingAnotherRun() = 0; - void startCycle() - { - m_currentTracker = m_rootTracker.get(); - m_runState = Executing; - } - void completeCycle() - { - m_runState = CompletedCycle; - } + virtual void addChild( Ptr const& child ) = 0; + virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) = 0; + virtual void openChild() = 0; - bool completedCycle() const - { - return m_runState == CompletedCycle; - } - ITracker& currentTracker() - { - return *m_currentTracker; - } - void setCurrentTracker(ITracker* tracker) - { - m_currentTracker = tracker; - } -}; - -class TrackerBase : public ITracker -{ - protected: - enum CycleState - { - NotStarted, - Executing, - ExecutingChildren, - NeedsAnotherRun, - CompletedSuccessfully, - Failed + // Debug/ checking + virtual bool isSectionTracker() const = 0; + virtual bool isIndexTracker() const = 0; }; - class TrackerHasName - { - std::string m_name; - public: - TrackerHasName(std::string const& name) : m_name(name) {} - bool operator()(Ptr const& tracker) - { - return tracker->name() == m_name; + class TrackerContext { + + enum RunState { + NotStarted, + Executing, + CompletedCycle + }; + + Ptr m_rootTracker; + ITracker* m_currentTracker; + RunState m_runState; + + public: + + static TrackerContext& instance() { + static TrackerContext s_instance; + return s_instance; + } + + TrackerContext() + : m_currentTracker( CATCH_NULL ), + m_runState( NotStarted ) + {} + + ITracker& startRun(); + + void endRun() { + m_rootTracker.reset(); + m_currentTracker = CATCH_NULL; + m_runState = NotStarted; + } + + void startCycle() { + m_currentTracker = m_rootTracker.get(); + m_runState = Executing; + } + void completeCycle() { + m_runState = CompletedCycle; + } + + bool completedCycle() const { + return m_runState == CompletedCycle; + } + ITracker& currentTracker() { + return *m_currentTracker; + } + void setCurrentTracker( ITracker* tracker ) { + m_currentTracker = tracker; } }; - typedef std::vector> Children; - std::string m_name; - TrackerContext& m_ctx; - ITracker* m_parent; - Children m_children; - CycleState m_runState; - public: - TrackerBase(std::string const& name, TrackerContext& ctx, ITracker* parent) - : m_name(name), - m_ctx(ctx), - m_parent(parent), - m_runState(NotStarted) - { - } - virtual ~TrackerBase(); + class TrackerBase : public ITracker { + protected: + enum CycleState { + NotStarted, + Executing, + ExecutingChildren, + NeedsAnotherRun, + CompletedSuccessfully, + Failed + }; + class TrackerHasName { + NameAndLocation m_nameAndLocation; + public: + TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {} + bool operator ()( Ptr const& tracker ) { + return + tracker->nameAndLocation().name == m_nameAndLocation.name && + tracker->nameAndLocation().location == m_nameAndLocation.location; + } + }; + typedef std::vector > Children; + NameAndLocation m_nameAndLocation; + TrackerContext& m_ctx; + ITracker* m_parent; + Children m_children; + CycleState m_runState; + public: + TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : m_nameAndLocation( nameAndLocation ), + m_ctx( ctx ), + m_parent( parent ), + m_runState( NotStarted ) + {} + virtual ~TrackerBase(); - virtual std::string name() const CATCH_OVERRIDE - { - return m_name; - } - virtual bool isComplete() const CATCH_OVERRIDE - { - return m_runState == CompletedSuccessfully || m_runState == Failed; - } - virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE - { - return m_runState == CompletedSuccessfully; - } - virtual bool isOpen() const CATCH_OVERRIDE - { - return m_runState != NotStarted && !isComplete(); - } - virtual bool hasChildren() const CATCH_OVERRIDE - { - return !m_children.empty(); - } + virtual NameAndLocation const& nameAndLocation() const CATCH_OVERRIDE { + return m_nameAndLocation; + } + virtual bool isComplete() const CATCH_OVERRIDE { + return m_runState == CompletedSuccessfully || m_runState == Failed; + } + virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE { + return m_runState == CompletedSuccessfully; + } + virtual bool isOpen() const CATCH_OVERRIDE { + return m_runState != NotStarted && !isComplete(); + } + virtual bool hasChildren() const CATCH_OVERRIDE { + return !m_children.empty(); + } - virtual void addChild(Ptr const& child) CATCH_OVERRIDE - { - m_children.push_back(child); - } + virtual void addChild( Ptr const& child ) CATCH_OVERRIDE { + m_children.push_back( child ); + } - virtual ITracker* findChild(std::string const& name) CATCH_OVERRIDE - { - Children::const_iterator it = std::find_if(m_children.begin(), m_children.end(), TrackerHasName(name)); - return (it != m_children.end()) - ? it->get() - : CATCH_NULL; - } - virtual ITracker& parent() CATCH_OVERRIDE - { - assert(m_parent); // Should always be non-null except for root - return *m_parent; - } + virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) CATCH_OVERRIDE { + Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) ); + return( it != m_children.end() ) + ? it->get() + : CATCH_NULL; + } + virtual ITracker& parent() CATCH_OVERRIDE { + assert( m_parent ); // Should always be non-null except for root + return *m_parent; + } - virtual void openChild() CATCH_OVERRIDE - { - if (m_runState != ExecutingChildren) - { - m_runState = ExecutingChildren; - if (m_parent) + virtual void openChild() CATCH_OVERRIDE { + if( m_runState != ExecutingChildren ) { + m_runState = ExecutingChildren; + if( m_parent ) + m_parent->openChild(); + } + } + + virtual bool isSectionTracker() const CATCH_OVERRIDE { return false; } + virtual bool isIndexTracker() const CATCH_OVERRIDE { return false; } + + void open() { + m_runState = Executing; + moveToThis(); + if( m_parent ) m_parent->openChild(); } - } - void open() - { + + virtual void close() CATCH_OVERRIDE { + + // Close any still open children (e.g. generators) + while( &m_ctx.currentTracker() != this ) + m_ctx.currentTracker().close(); + + switch( m_runState ) { + case NotStarted: + case CompletedSuccessfully: + case Failed: + throw std::logic_error( "Illogical state" ); + + case NeedsAnotherRun: + break;; + + case Executing: + m_runState = CompletedSuccessfully; + break; + case ExecutingChildren: + if( m_children.empty() || m_children.back()->isComplete() ) + m_runState = CompletedSuccessfully; + break; + + default: + throw std::logic_error( "Unexpected state" ); + } + moveToParent(); + m_ctx.completeCycle(); + } + virtual void fail() CATCH_OVERRIDE { + m_runState = Failed; + if( m_parent ) + m_parent->markAsNeedingAnotherRun(); + moveToParent(); + m_ctx.completeCycle(); + } + virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE { + m_runState = NeedsAnotherRun; + } + private: + void moveToParent() { + assert( m_parent ); + m_ctx.setCurrentTracker( m_parent ); + } + void moveToThis() { + m_ctx.setCurrentTracker( this ); + } + }; + + class SectionTracker : public TrackerBase { + std::vector m_filters; + public: + SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ) + { + if( parent ) { + while( !parent->isSectionTracker() ) + parent = &parent->parent(); + + SectionTracker& parentSection = static_cast( *parent ); + addNextFilters( parentSection.m_filters ); + } + } + virtual ~SectionTracker(); + + virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; } + + static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { + SectionTracker* section = CATCH_NULL; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isSectionTracker() ); + section = static_cast( childTracker ); + } + else { + section = new SectionTracker( nameAndLocation, ctx, ¤tTracker ); + currentTracker.addChild( section ); + } + if( !ctx.completedCycle() ) + section->tryOpen(); + return *section; + } + + void tryOpen() { + if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) + open(); + } + + void addInitialFilters( std::vector const& filters ) { + if( !filters.empty() ) { + m_filters.push_back(""); // Root - should never be consulted + m_filters.push_back(""); // Test Case - not a section filter + m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); + } + } + void addNextFilters( std::vector const& filters ) { + if( filters.size() > 1 ) + m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); + } + }; + + class IndexTracker : public TrackerBase { + int m_size; + int m_index; + public: + IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) + : TrackerBase( nameAndLocation, ctx, parent ), + m_size( size ), + m_index( -1 ) + {} + virtual ~IndexTracker(); + + virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; } + + static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { + IndexTracker* tracker = CATCH_NULL; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isIndexTracker() ); + tracker = static_cast( childTracker ); + } + else { + tracker = new IndexTracker( nameAndLocation, ctx, ¤tTracker, size ); + currentTracker.addChild( tracker ); + } + + if( !ctx.completedCycle() && !tracker->isComplete() ) { + if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) + tracker->moveNext(); + tracker->open(); + } + + return *tracker; + } + + int index() const { return m_index; } + + void moveNext() { + m_index++; + m_children.clear(); + } + + virtual void close() CATCH_OVERRIDE { + TrackerBase::close(); + if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) + m_runState = Executing; + } + }; + + inline ITracker& TrackerContext::startRun() { + m_rootTracker = new SectionTracker( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, CATCH_NULL ); + m_currentTracker = CATCH_NULL; m_runState = Executing; - moveToThis(); - if (m_parent) - m_parent->openChild(); + return *m_rootTracker; } - virtual void close() CATCH_OVERRIDE - { - - // Close any still open children (e.g. generators) - while (&m_ctx.currentTracker() != this) - m_ctx.currentTracker().close(); - - switch (m_runState) - { - case NotStarted: - case CompletedSuccessfully: - case Failed: - throw std::logic_error("Illogical state"); - - case NeedsAnotherRun: - break; - ; - - case Executing: - m_runState = CompletedSuccessfully; - break; - case ExecutingChildren: - if (m_children.empty() || m_children.back()->isComplete()) - m_runState = CompletedSuccessfully; - break; - - default: - throw std::logic_error("Unexpected state"); - } - moveToParent(); - m_ctx.completeCycle(); - } - virtual void fail() CATCH_OVERRIDE - { - m_runState = Failed; - if (m_parent) - m_parent->markAsNeedingAnotherRun(); - moveToParent(); - m_ctx.completeCycle(); - } - virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE - { - m_runState = NeedsAnotherRun; - } - - private: - void moveToParent() - { - assert(m_parent); - m_ctx.setCurrentTracker(m_parent); - } - void moveToThis() - { - m_ctx.setCurrentTracker(this); - } -}; - -class SectionTracker : public TrackerBase -{ - public: - SectionTracker(std::string const& name, TrackerContext& ctx, ITracker* parent) - : TrackerBase(name, ctx, parent) - { - } - virtual ~SectionTracker(); - - static SectionTracker& acquire(TrackerContext& ctx, std::string const& name) - { - SectionTracker* section = CATCH_NULL; - - ITracker& currentTracker = ctx.currentTracker(); - if (ITracker* childTracker = currentTracker.findChild(name)) - { - section = dynamic_cast(childTracker); - assert(section); - } - else - { - section = new SectionTracker(name, ctx, ¤tTracker); - currentTracker.addChild(section); - } - if (!ctx.completedCycle() && !section->isComplete()) - { - - section->open(); - } - return *section; - } -}; - -class IndexTracker : public TrackerBase -{ - int m_size; - int m_index; - - public: - IndexTracker(std::string const& name, TrackerContext& ctx, ITracker* parent, int size) - : TrackerBase(name, ctx, parent), - m_size(size), - m_index(-1) - { - } - virtual ~IndexTracker(); - - static IndexTracker& acquire(TrackerContext& ctx, std::string const& name, int size) - { - IndexTracker* tracker = CATCH_NULL; - - ITracker& currentTracker = ctx.currentTracker(); - if (ITracker* childTracker = currentTracker.findChild(name)) - { - tracker = dynamic_cast(childTracker); - assert(tracker); - } - else - { - tracker = new IndexTracker(name, ctx, ¤tTracker, size); - currentTracker.addChild(tracker); - } - - if (!ctx.completedCycle() && !tracker->isComplete()) - { - if (tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun) - tracker->moveNext(); - tracker->open(); - } - - return *tracker; - } - - int index() const { return m_index; } - - void moveNext() - { - m_index++; - m_children.clear(); - } - - virtual void close() CATCH_OVERRIDE - { - TrackerBase::close(); - if (m_runState == CompletedSuccessfully && m_index < m_size - 1) - m_runState = Executing; - } -}; - -inline ITracker& TrackerContext::startRun() -{ - m_rootTracker = new SectionTracker("{root}", *this, CATCH_NULL); - m_currentTracker = CATCH_NULL; - m_runState = Executing; - return *m_rootTracker; -} - } // namespace TestCaseTracking using TestCaseTracking::ITracker; @@ -6322,90 +6259,216 @@ using TestCaseTracking::IndexTracker; } // namespace Catch +CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS + // #included from: catch_fatal_condition.hpp #define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED namespace Catch { -// Report the error condition then exit the process -inline void fatal(std::string const& message, int exitCode) -{ - IContext& context = Catch::getCurrentContext(); - IResultCapture* resultCapture = context.getResultCapture(); - resultCapture->handleFatalErrorCondition(message); - - if (Catch::alwaysTrue()) // avoids "no return" warnings - exit(exitCode); -} + // Report the error condition + inline void reportFatal( std::string const& message ) { + IContext& context = Catch::getCurrentContext(); + IResultCapture* resultCapture = context.getResultCapture(); + resultCapture->handleFatalErrorCondition( message ); + } } // namespace Catch -#if defined(CATCH_PLATFORM_WINDOWS) ///////////////////////////////////////// +#if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// +// #included from: catch_windows_h_proxy.h + +#define TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED + +#ifdef CATCH_DEFINES_NOMINMAX +# define NOMINMAX +#endif +#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif + +#ifdef __AFXDLL +#include +#else +#include +#endif + +#ifdef CATCH_DEFINES_NOMINMAX +# undef NOMINMAX +#endif +#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN +# undef WIN32_LEAN_AND_MEAN +#endif + + +# if !defined ( CATCH_CONFIG_WINDOWS_SEH ) + +namespace Catch { + struct FatalConditionHandler { + void reset() {} + }; +} + +# else // CATCH_CONFIG_WINDOWS_SEH is defined namespace Catch { -struct FatalConditionHandler -{ - void reset() {} -}; + struct SignalDefs { DWORD id; const char* name; }; + extern SignalDefs signalDefs[]; + // There is no 1-1 mapping between signals and windows exceptions. + // Windows can easily distinguish between SO and SigSegV, + // but SigInt, SigTerm, etc are handled differently. + SignalDefs signalDefs[] = { + { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, + { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, + { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, + { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, + }; + + struct FatalConditionHandler { + + static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { + for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) { + reportFatal(signalDefs[i].name); + } + } + // If its not an exception we care about, pass it along. + // This stops us from eating debugger breaks etc. + return EXCEPTION_CONTINUE_SEARCH; + } + + FatalConditionHandler() { + isSet = true; + // 32k seems enough for Catch to handle stack overflow, + // but the value was found experimentally, so there is no strong guarantee + guaranteeSize = 32 * 1024; + exceptionHandlerHandle = CATCH_NULL; + // Register as first handler in current chain + exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); + // Pass in guarantee size to be filled + SetThreadStackGuarantee(&guaranteeSize); + } + + static void reset() { + if (isSet) { + // Unregister handler and restore the old guarantee + RemoveVectoredExceptionHandler(exceptionHandlerHandle); + SetThreadStackGuarantee(&guaranteeSize); + exceptionHandlerHandle = CATCH_NULL; + isSet = false; + } + } + + ~FatalConditionHandler() { + reset(); + } + private: + static bool isSet; + static ULONG guaranteeSize; + static PVOID exceptionHandlerHandle; + }; + + bool FatalConditionHandler::isSet = false; + ULONG FatalConditionHandler::guaranteeSize = 0; + PVOID FatalConditionHandler::exceptionHandlerHandle = CATCH_NULL; } // namespace Catch +# endif // CATCH_CONFIG_WINDOWS_SEH + #else // Not Windows - assumed to be POSIX compatible ////////////////////////// +# if !defined(CATCH_CONFIG_POSIX_SIGNALS) + +namespace Catch { + struct FatalConditionHandler { + void reset() {} + }; +} + +# else // CATCH_CONFIG_POSIX_SIGNALS is defined + #include namespace Catch { -struct SignalDefs -{ - int id; - const char* name; -}; -extern SignalDefs signalDefs[]; -SignalDefs signalDefs[] = { - {SIGINT, "SIGINT - Terminal interrupt signal"}, - {SIGILL, "SIGILL - Illegal instruction signal"}, - {SIGFPE, "SIGFPE - Floating point error signal"}, - {SIGSEGV, "SIGSEGV - Segmentation violation signal"}, - {SIGTERM, "SIGTERM - Termination request signal"}, - {SIGABRT, "SIGABRT - Abort (abnormal termination) signal"}}; + struct SignalDefs { + int id; + const char* name; + }; + extern SignalDefs signalDefs[]; + SignalDefs signalDefs[] = { + { SIGINT, "SIGINT - Terminal interrupt signal" }, + { SIGILL, "SIGILL - Illegal instruction signal" }, + { SIGFPE, "SIGFPE - Floating point error signal" }, + { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, + { SIGTERM, "SIGTERM - Termination request signal" }, + { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } + }; -struct FatalConditionHandler -{ + struct FatalConditionHandler { - static void handleSignal(int sig) - { - for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) - if (sig == signalDefs[i].id) - fatal(signalDefs[i].name, -sig); - fatal("", -sig); - } + static bool isSet; + static struct sigaction oldSigActions [sizeof(signalDefs)/sizeof(SignalDefs)]; + static stack_t oldSigStack; + static char altStackMem[SIGSTKSZ]; - FatalConditionHandler() : m_isSet(true) - { - for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) - signal(signalDefs[i].id, handleSignal); - } - ~FatalConditionHandler() - { - reset(); - } - void reset() - { - if (m_isSet) - { - for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) - signal(signalDefs[i].id, SIG_DFL); - m_isSet = false; + static void handleSignal( int sig ) { + std::string name = ""; + for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + SignalDefs &def = signalDefs[i]; + if (sig == def.id) { + name = def.name; + break; + } + } + reset(); + reportFatal(name); + raise( sig ); } - } - bool m_isSet; -}; + FatalConditionHandler() { + isSet = true; + stack_t sigStack; + sigStack.ss_sp = altStackMem; + sigStack.ss_size = SIGSTKSZ; + sigStack.ss_flags = 0; + sigaltstack(&sigStack, &oldSigStack); + struct sigaction sa = { 0 }; + + sa.sa_handler = handleSignal; + sa.sa_flags = SA_ONSTACK; + for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); + } + } + + ~FatalConditionHandler() { + reset(); + } + static void reset() { + if( isSet ) { + // Set signals back to previous values -- hopefully nobody overwrote them in the meantime + for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { + sigaction(signalDefs[i].id, &oldSigActions[i], CATCH_NULL); + } + // Return the old stack + sigaltstack(&oldSigStack, CATCH_NULL); + isSet = false; + } + } + }; + + bool FatalConditionHandler::isSet = false; + struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; + stack_t FatalConditionHandler::oldSigStack = {}; + char FatalConditionHandler::altStackMem[SIGSTKSZ] = {}; } // namespace Catch +# endif // CATCH_CONFIG_POSIX_SIGNALS + #endif // not Windows #include @@ -6413,359 +6476,352 @@ struct FatalConditionHandler namespace Catch { -class StreamRedirect -{ + class StreamRedirect { - public: - StreamRedirect(std::ostream& stream, std::string& targetString) - : m_stream(stream), - m_prevBuf(stream.rdbuf()), - m_targetString(targetString) - { - stream.rdbuf(m_oss.rdbuf()); - } - - ~StreamRedirect() - { - m_targetString += m_oss.str(); - m_stream.rdbuf(m_prevBuf); - } - - private: - std::ostream& m_stream; - std::streambuf* m_prevBuf; - std::ostringstream m_oss; - std::string& m_targetString; -}; - -/////////////////////////////////////////////////////////////////////////// - -class RunContext : public IResultCapture, public IRunner -{ - - RunContext(RunContext const&); - void operator=(RunContext const&); - - public: - explicit RunContext(Ptr const& _config, Ptr const& reporter) - : m_runInfo(_config->name()), - m_context(getCurrentMutableContext()), - m_activeTestCase(CATCH_NULL), - m_config(_config), - m_reporter(reporter) - { - m_context.setRunner(this); - m_context.setConfig(m_config); - m_context.setResultCapture(this); - m_reporter->testRunStarting(m_runInfo); - } - - virtual ~RunContext() - { - m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting())); - } - - void testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount) - { - m_reporter->testGroupStarting(GroupInfo(testSpec, groupIndex, groupsCount)); - } - void testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount) - { - m_reporter->testGroupEnded(TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), totals, aborting())); - } - - Totals runTest(TestCase const& testCase) - { - Totals prevTotals = m_totals; - - std::string redirectedCout; - std::string redirectedCerr; - - TestCaseInfo testInfo = testCase.getTestCaseInfo(); - - m_reporter->testCaseStarting(testInfo); - - m_activeTestCase = &testCase; - - do + public: + StreamRedirect( std::ostream& stream, std::string& targetString ) + : m_stream( stream ), + m_prevBuf( stream.rdbuf() ), + m_targetString( targetString ) { - m_trackerContext.startRun(); - do - { - m_trackerContext.startCycle(); - m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, testInfo.name); - runCurrentTest(redirectedCout, redirectedCerr); - } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); + stream.rdbuf( m_oss.rdbuf() ); } - // !TBD: deprecated - this will be replaced by indexed trackers - while (getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting()); - Totals deltaTotals = m_totals.delta(prevTotals); - m_totals.testCases += deltaTotals.testCases; - m_reporter->testCaseEnded(TestCaseStats(testInfo, - deltaTotals, - redirectedCout, - redirectedCerr, - aborting())); - - m_activeTestCase = CATCH_NULL; - m_testCaseTracker = CATCH_NULL; - - return deltaTotals; - } - - Ptr config() const - { - return m_config; - } - - private: // IResultCapture - virtual void assertionEnded(AssertionResult const& result) - { - if (result.getResultType() == ResultWas::Ok) - { - m_totals.assertions.passed++; + ~StreamRedirect() { + m_targetString += m_oss.str(); + m_stream.rdbuf( m_prevBuf ); } - else if (!result.isOk()) + + private: + std::ostream& m_stream; + std::streambuf* m_prevBuf; + std::ostringstream m_oss; + std::string& m_targetString; + }; + + /////////////////////////////////////////////////////////////////////////// + + class RunContext : public IResultCapture, public IRunner { + + RunContext( RunContext const& ); + void operator =( RunContext const& ); + + public: + + explicit RunContext( Ptr const& _config, Ptr const& reporter ) + : m_runInfo( _config->name() ), + m_context( getCurrentMutableContext() ), + m_activeTestCase( CATCH_NULL ), + m_config( _config ), + m_reporter( reporter ), + m_shouldReportUnexpected ( true ) { + m_context.setRunner( this ); + m_context.setConfig( m_config ); + m_context.setResultCapture( this ); + m_reporter->testRunStarting( m_runInfo ); + } + + virtual ~RunContext() { + m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) ); + } + + void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) { + m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) ); + } + void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) { + m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) ); + } + + Totals runTest( TestCase const& testCase ) { + Totals prevTotals = m_totals; + + std::string redirectedCout; + std::string redirectedCerr; + + TestCaseInfo testInfo = testCase.getTestCaseInfo(); + + m_reporter->testCaseStarting( testInfo ); + + m_activeTestCase = &testCase; + + do { + ITracker& rootTracker = m_trackerContext.startRun(); + assert( rootTracker.isSectionTracker() ); + static_cast( rootTracker ).addInitialFilters( m_config->getSectionsToRun() ); + do { + m_trackerContext.startCycle(); + m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( testInfo.name, testInfo.lineInfo ) ); + runCurrentTest( redirectedCout, redirectedCerr ); + } + while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() ); + } + // !TBD: deprecated - this will be replaced by indexed trackers + while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() ); + + Totals deltaTotals = m_totals.delta( prevTotals ); + if( testInfo.expectedToFail() && deltaTotals.testCases.passed > 0 ) { + deltaTotals.assertions.failed++; + deltaTotals.testCases.passed--; + deltaTotals.testCases.failed++; + } + m_totals.testCases += deltaTotals.testCases; + m_reporter->testCaseEnded( TestCaseStats( testInfo, + deltaTotals, + redirectedCout, + redirectedCerr, + aborting() ) ); + + m_activeTestCase = CATCH_NULL; + m_testCaseTracker = CATCH_NULL; + + return deltaTotals; + } + + Ptr config() const { + return m_config; + } + + private: // IResultCapture + + virtual void assertionEnded( AssertionResult const& result ) { + if( result.getResultType() == ResultWas::Ok ) { + m_totals.assertions.passed++; + } + else if( !result.isOk() ) { + m_totals.assertions.failed++; + } + + if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) ) + m_messages.clear(); + + // Reset working state + m_lastAssertionInfo = AssertionInfo( std::string(), m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition ); + m_lastResult = result; + } + + virtual bool sectionStarted ( + SectionInfo const& sectionInfo, + Counts& assertions + ) + { + ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( sectionInfo.name, sectionInfo.lineInfo ) ); + if( !sectionTracker.isOpen() ) + return false; + m_activeSections.push_back( §ionTracker ); + + m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; + + m_reporter->sectionStarting( sectionInfo ); + + assertions = m_totals.assertions; + + return true; + } + bool testForMissingAssertions( Counts& assertions ) { + if( assertions.total() != 0 ) + return false; + if( !m_config->warnAboutMissingAssertions() ) + return false; + if( m_trackerContext.currentTracker().hasChildren() ) + return false; m_totals.assertions.failed++; + assertions.failed++; + return true; } - if (m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))) + virtual void sectionEnded( SectionEndInfo const& endInfo ) { + Counts assertions = m_totals.assertions - endInfo.prevAssertions; + bool missingAssertions = testForMissingAssertions( assertions ); + + if( !m_activeSections.empty() ) { + m_activeSections.back()->close(); + m_activeSections.pop_back(); + } + + m_reporter->sectionEnded( SectionStats( endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions ) ); + m_messages.clear(); + } + + virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) { + if( m_unfinishedSections.empty() ) + m_activeSections.back()->fail(); + else + m_activeSections.back()->close(); + m_activeSections.pop_back(); + + m_unfinishedSections.push_back( endInfo ); + } + + virtual void pushScopedMessage( MessageInfo const& message ) { + m_messages.push_back( message ); + } + + virtual void popScopedMessage( MessageInfo const& message ) { + m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() ); + } + + virtual std::string getCurrentTestName() const { + return m_activeTestCase + ? m_activeTestCase->getTestCaseInfo().name + : std::string(); + } + + virtual const AssertionResult* getLastResult() const { + return &m_lastResult; + } + + virtual void exceptionEarlyReported() { + m_shouldReportUnexpected = false; + } + + virtual void handleFatalErrorCondition( std::string const& message ) { + // Don't rebuild the result -- the stringification itself can cause more fatal errors + // Instead, fake a result data. + AssertionResultData tempResult; + tempResult.resultType = ResultWas::FatalErrorCondition; + tempResult.message = message; + AssertionResult result(m_lastAssertionInfo, tempResult); + + getResultCapture().assertionEnded(result); + + handleUnfinishedSections(); + + // Recreate section for test case (as we will lose the one that was in scope) + TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); + + Counts assertions; + assertions.failed = 1; + SectionStats testCaseSectionStats( testCaseSection, assertions, 0, false ); + m_reporter->sectionEnded( testCaseSectionStats ); + + TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo(); + + Totals deltaTotals; + deltaTotals.testCases.failed = 1; + m_reporter->testCaseEnded( TestCaseStats( testInfo, + deltaTotals, + std::string(), + std::string(), + false ) ); + m_totals.testCases.failed++; + testGroupEnded( std::string(), m_totals, 1, 1 ); + m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) ); + } + + public: + // !TBD We need to do this another way! + bool aborting() const { + return m_totals.assertions.failed == static_cast( m_config->abortAfter() ); + } + + private: + + void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) { + TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); + m_reporter->sectionStarting( testCaseSection ); + Counts prevAssertions = m_totals.assertions; + double duration = 0; + m_shouldReportUnexpected = true; + try { + m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, std::string(), ResultDisposition::Normal ); + + seedRng( *m_config ); + + Timer timer; + timer.start(); + if( m_reporter->getPreferences().shouldRedirectStdOut ) { + StreamRedirect coutRedir( Catch::cout(), redirectedCout ); + StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr ); + invokeActiveTestCase(); + } + else { + invokeActiveTestCase(); + } + duration = timer.getElapsedSeconds(); + } + catch( TestFailureException& ) { + // This just means the test was aborted due to failure + } + catch(...) { + // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions + // are reported without translation at the point of origin. +#ifdef CATCH_CONFIG_FAST_COMPILE + if (m_shouldReportUnexpected) { + makeUnexpectedResultBuilder().useActiveException(); + } +#endif + } + m_testCaseTracker->close(); + handleUnfinishedSections(); m_messages.clear(); - // Reset working state - m_lastAssertionInfo = AssertionInfo("", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}", m_lastAssertionInfo.resultDisposition); - m_lastResult = result; - } + Counts assertions = m_totals.assertions - prevAssertions; + bool missingAssertions = testForMissingAssertions( assertions ); - virtual bool sectionStarted( - SectionInfo const& sectionInfo, - Counts& assertions) - { - std::ostringstream oss; - oss << sectionInfo.name << "@" << sectionInfo.lineInfo; + if( testCaseInfo.okToFail() ) { + std::swap( assertions.failedButOk, assertions.failed ); + m_totals.assertions.failed -= assertions.failedButOk; + m_totals.assertions.failedButOk += assertions.failedButOk; + } - ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, oss.str()); - if (!sectionTracker.isOpen()) - return false; - m_activeSections.push_back(§ionTracker); - - m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; - - m_reporter->sectionStarting(sectionInfo); - - assertions = m_totals.assertions; - - return true; - } - bool testForMissingAssertions(Counts& assertions) - { - if (assertions.total() != 0) - return false; - if (!m_config->warnAboutMissingAssertions()) - return false; - if (m_trackerContext.currentTracker().hasChildren()) - return false; - m_totals.assertions.failed++; - assertions.failed++; - return true; - } - - virtual void sectionEnded(SectionEndInfo const& endInfo) - { - Counts assertions = m_totals.assertions - endInfo.prevAssertions; - bool missingAssertions = testForMissingAssertions(assertions); - - if (!m_activeSections.empty()) - { - m_activeSections.back()->close(); - m_activeSections.pop_back(); + SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions ); + m_reporter->sectionEnded( testCaseSectionStats ); } - m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions)); - m_messages.clear(); - } + void invokeActiveTestCase() { + FatalConditionHandler fatalConditionHandler; // Handle signals + m_activeTestCase->invoke(); + fatalConditionHandler.reset(); + } - virtual void sectionEndedEarly(SectionEndInfo const& endInfo) - { - if (m_unfinishedSections.empty()) - m_activeSections.back()->fail(); + private: + + ResultBuilder makeUnexpectedResultBuilder() const { + return ResultBuilder( m_lastAssertionInfo.macroName.c_str(), + m_lastAssertionInfo.lineInfo, + m_lastAssertionInfo.capturedExpression.c_str(), + m_lastAssertionInfo.resultDisposition ); + } + + void handleUnfinishedSections() { + // If sections ended prematurely due to an exception we stored their + // infos here so we can tear them down outside the unwind process. + for( std::vector::const_reverse_iterator it = m_unfinishedSections.rbegin(), + itEnd = m_unfinishedSections.rend(); + it != itEnd; + ++it ) + sectionEnded( *it ); + m_unfinishedSections.clear(); + } + + TestRunInfo m_runInfo; + IMutableContext& m_context; + TestCase const* m_activeTestCase; + ITracker* m_testCaseTracker; + ITracker* m_currentSectionTracker; + AssertionResult m_lastResult; + + Ptr m_config; + Totals m_totals; + Ptr m_reporter; + std::vector m_messages; + AssertionInfo m_lastAssertionInfo; + std::vector m_unfinishedSections; + std::vector m_activeSections; + TrackerContext m_trackerContext; + bool m_shouldReportUnexpected; + }; + + IResultCapture& getResultCapture() { + if( IResultCapture* capture = getCurrentContext().getResultCapture() ) + return *capture; else - m_activeSections.back()->close(); - m_activeSections.pop_back(); - - m_unfinishedSections.push_back(endInfo); + throw std::logic_error( "No result capture instance" ); } - virtual void pushScopedMessage(MessageInfo const& message) - { - m_messages.push_back(message); - } - - virtual void popScopedMessage(MessageInfo const& message) - { - m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end()); - } - - virtual std::string getCurrentTestName() const - { - return m_activeTestCase - ? m_activeTestCase->getTestCaseInfo().name - : ""; - } - - virtual const AssertionResult* getLastResult() const - { - return &m_lastResult; - } - - virtual void handleFatalErrorCondition(std::string const& message) - { - ResultBuilder resultBuilder = makeUnexpectedResultBuilder(); - resultBuilder.setResultType(ResultWas::FatalErrorCondition); - resultBuilder << message; - resultBuilder.captureExpression(); - - handleUnfinishedSections(); - - // Recreate section for test case (as we will lose the one that was in scope) - TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); - SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description); - - Counts assertions; - assertions.failed = 1; - SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false); - m_reporter->sectionEnded(testCaseSectionStats); - - TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo(); - - Totals deltaTotals; - deltaTotals.testCases.failed = 1; - m_reporter->testCaseEnded(TestCaseStats(testInfo, - deltaTotals, - "", - "", - false)); - m_totals.testCases.failed++; - testGroupEnded("", m_totals, 1, 1); - m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false)); - } - - public: - // !TBD We need to do this another way! - bool aborting() const - { - return m_totals.assertions.failed == static_cast(m_config->abortAfter()); - } - - private: - void runCurrentTest(std::string& redirectedCout, std::string& redirectedCerr) - { - TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); - SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description); - m_reporter->sectionStarting(testCaseSection); - Counts prevAssertions = m_totals.assertions; - double duration = 0; - try - { - m_lastAssertionInfo = AssertionInfo("TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal); - - seedRng(*m_config); - - Timer timer; - timer.start(); - if (m_reporter->getPreferences().shouldRedirectStdOut) - { - StreamRedirect coutRedir(Catch::cout(), redirectedCout); - StreamRedirect cerrRedir(Catch::cerr(), redirectedCerr); - invokeActiveTestCase(); - } - else - { - invokeActiveTestCase(); - } - duration = timer.getElapsedSeconds(); - } - catch (TestFailureException&) - { - // This just means the test was aborted due to failure - } - catch (...) - { - makeUnexpectedResultBuilder().useActiveException(); - } - m_testCaseTracker->close(); - handleUnfinishedSections(); - m_messages.clear(); - - Counts assertions = m_totals.assertions - prevAssertions; - bool missingAssertions = testForMissingAssertions(assertions); - - if (testCaseInfo.okToFail()) - { - std::swap(assertions.failedButOk, assertions.failed); - m_totals.assertions.failed -= assertions.failedButOk; - m_totals.assertions.failedButOk += assertions.failedButOk; - } - - SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); - m_reporter->sectionEnded(testCaseSectionStats); - } - - void invokeActiveTestCase() - { - FatalConditionHandler fatalConditionHandler; // Handle signals - m_activeTestCase->invoke(); - fatalConditionHandler.reset(); - } - - private: - ResultBuilder makeUnexpectedResultBuilder() const - { - return ResultBuilder(m_lastAssertionInfo.macroName.c_str(), - m_lastAssertionInfo.lineInfo, - m_lastAssertionInfo.capturedExpression.c_str(), - m_lastAssertionInfo.resultDisposition); - } - - void handleUnfinishedSections() - { - // If sections ended prematurely due to an exception we stored their - // infos here so we can tear them down outside the unwind process. - for (std::vector::const_reverse_iterator it = m_unfinishedSections.rbegin(), - itEnd = m_unfinishedSections.rend(); - it != itEnd; - ++it) - sectionEnded(*it); - m_unfinishedSections.clear(); - } - - TestRunInfo m_runInfo; - IMutableContext& m_context; - TestCase const* m_activeTestCase; - ITracker* m_testCaseTracker; - ITracker* m_currentSectionTracker; - AssertionResult m_lastResult; - - Ptr m_config; - Totals m_totals; - Ptr m_reporter; - std::vector m_messages; - AssertionInfo m_lastAssertionInfo; - std::vector m_unfinishedSections; - std::vector m_activeSections; - TrackerContext m_trackerContext; -}; - -IResultCapture& getResultCapture() -{ - if (IResultCapture* capture = getCurrentContext().getResultCapture()) - return *capture; - else - throw std::logic_error("No result capture instance"); -} - } // end namespace Catch // #included from: internal/catch_version.h @@ -6773,261 +6829,228 @@ IResultCapture& getResultCapture() namespace Catch { -// Versioning information -struct Version -{ - Version(unsigned int _majorVersion, - unsigned int _minorVersion, - unsigned int _patchNumber, - std::string const& _branchName, - unsigned int _buildNumber); + // Versioning information + struct Version { + Version( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + char const * const _branchName, + unsigned int _buildNumber ); - unsigned int const majorVersion; - unsigned int const minorVersion; - unsigned int const patchNumber; + unsigned int const majorVersion; + unsigned int const minorVersion; + unsigned int const patchNumber; - // buildNumber is only used if branchName is not null - std::string const branchName; - unsigned int const buildNumber; + // buildNumber is only used if branchName is not null + char const * const branchName; + unsigned int const buildNumber; - friend std::ostream& operator<<(std::ostream& os, Version const& version); + friend std::ostream& operator << ( std::ostream& os, Version const& version ); - private: - void operator=(Version const&); -}; + private: + void operator=( Version const& ); + }; -extern Version libraryVersion; + inline Version libraryVersion(); } #include -#include #include +#include namespace Catch { -Ptr createReporter(std::string const& reporterName, Ptr const& config) -{ - Ptr reporter = getRegistryHub().getReporterRegistry().create(reporterName, config.get()); - if (!reporter) - { - std::ostringstream oss; - oss << "No reporter registered with name: '" << reporterName << "'"; - throw std::domain_error(oss.str()); - } - return reporter; -} - -Ptr makeReporter(Ptr const& config) -{ - std::vector reporters = config->getReporterNames(); - if (reporters.empty()) - reporters.push_back("console"); - - Ptr reporter; - for (std::vector::const_iterator it = reporters.begin(), itEnd = reporters.end(); - it != itEnd; - ++it) - reporter = addReporter(reporter, createReporter(*it, config)); - return reporter; -} -Ptr addListeners(Ptr const& config, Ptr reporters) -{ - IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners(); - for (IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end(); - it != itEnd; - ++it) - reporters = addReporter(reporters, (*it)->create(ReporterConfig(config))); - return reporters; -} - -Totals runTests(Ptr const& config) -{ - - Ptr iconfig = config.get(); - - Ptr reporter = makeReporter(config); - reporter = addListeners(iconfig, reporter); - - RunContext context(iconfig, reporter); - - Totals totals; - - context.testGroupStarting(config->name(), 1, 1); - - TestSpec testSpec = config->testSpec(); - if (!testSpec.hasFilters()) - testSpec = TestSpecParser(ITagAliasRegistry::get()).parse("~[.]").testSpec(); // All not hidden tests - - std::vector const& allTestCases = getAllTestCasesSorted(*iconfig); - for (std::vector::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end(); - it != itEnd; - ++it) - { - if (!context.aborting() && matchTest(*it, testSpec, *iconfig)) - totals += context.runTest(*it); - else - reporter->skipTest(*it); - } - - context.testGroupEnded(iconfig->name(), totals, 1, 1); - return totals; -} - -void applyFilenamesAsTags(IConfig const& config) -{ - std::vector const& tests = getAllTestCasesSorted(config); - for (std::size_t i = 0; i < tests.size(); ++i) - { - TestCase& test = const_cast(tests[i]); - std::set tags = test.tags; - - std::string filename = test.lineInfo.file; - std::string::size_type lastSlash = filename.find_last_of("\\/"); - if (lastSlash != std::string::npos) - filename = filename.substr(lastSlash + 1); - - std::string::size_type lastDot = filename.find_last_of("."); - if (lastDot != std::string::npos) - filename = filename.substr(0, lastDot); - - tags.insert("#" + filename); - setTags(test, tags); - } -} - -class Session : NonCopyable -{ - static bool alreadyInstantiated; - - public: - struct OnUnusedOptions - { - enum DoWhat - { - Ignore, - Fail - }; - }; - - Session() - : m_cli(makeCommandLineParser()) - { - if (alreadyInstantiated) - { - std::string msg = "Only one instance of Catch::Session can ever be used"; - Catch::cerr() << msg << std::endl; - throw std::logic_error(msg); + Ptr createReporter( std::string const& reporterName, Ptr const& config ) { + Ptr reporter = getRegistryHub().getReporterRegistry().create( reporterName, config.get() ); + if( !reporter ) { + std::ostringstream oss; + oss << "No reporter registered with name: '" << reporterName << "'"; + throw std::domain_error( oss.str() ); } - alreadyInstantiated = true; - } - ~Session() - { - Catch::cleanUp(); + return reporter; } - void showHelp(std::string const& processName) - { - Catch::cout() << "\nCatch v" << libraryVersion << "\n"; + Ptr makeReporter( Ptr const& config ) { + std::vector reporters = config->getReporterNames(); + if( reporters.empty() ) + reporters.push_back( "console" ); - m_cli.usage(Catch::cout(), processName); - Catch::cout() << "For more detail usage please see the project docs\n" - << std::endl; + Ptr reporter; + for( std::vector::const_iterator it = reporters.begin(), itEnd = reporters.end(); + it != itEnd; + ++it ) + reporter = addReporter( reporter, createReporter( *it, config ) ); + return reporter; + } + Ptr addListeners( Ptr const& config, Ptr reporters ) { + IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners(); + for( IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end(); + it != itEnd; + ++it ) + reporters = addReporter(reporters, (*it)->create( ReporterConfig( config ) ) ); + return reporters; } - int applyCommandLine(int argc, char const* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail) - { - try - { - m_cli.setThrowOnUnrecognisedTokens(unusedOptionBehaviour == OnUnusedOptions::Fail); - m_unusedTokens = m_cli.parseInto(argc, argv, m_configData); - if (m_configData.showHelp) - showHelp(m_configData.processName); + Totals runTests( Ptr const& config ) { + + Ptr iconfig = config.get(); + + Ptr reporter = makeReporter( config ); + reporter = addListeners( iconfig, reporter ); + + RunContext context( iconfig, reporter ); + + Totals totals; + + context.testGroupStarting( config->name(), 1, 1 ); + + TestSpec testSpec = config->testSpec(); + if( !testSpec.hasFilters() ) + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests + + std::vector const& allTestCases = getAllTestCasesSorted( *iconfig ); + for( std::vector::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end(); + it != itEnd; + ++it ) { + if( !context.aborting() && matchTest( *it, testSpec, *iconfig ) ) + totals += context.runTest( *it ); + else + reporter->skipTest( *it ); + } + + context.testGroupEnded( iconfig->name(), totals, 1, 1 ); + return totals; + } + + void applyFilenamesAsTags( IConfig const& config ) { + std::vector const& tests = getAllTestCasesSorted( config ); + for(std::size_t i = 0; i < tests.size(); ++i ) { + TestCase& test = const_cast( tests[i] ); + std::set tags = test.tags; + + std::string filename = test.lineInfo.file; + std::string::size_type lastSlash = filename.find_last_of( "\\/" ); + if( lastSlash != std::string::npos ) + filename = filename.substr( lastSlash+1 ); + + std::string::size_type lastDot = filename.find_last_of( "." ); + if( lastDot != std::string::npos ) + filename = filename.substr( 0, lastDot ); + + tags.insert( "#" + filename ); + setTags( test, tags ); + } + } + + class Session : NonCopyable { + static bool alreadyInstantiated; + + public: + + struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; }; + + Session() + : m_cli( makeCommandLineParser() ) { + if( alreadyInstantiated ) { + std::string msg = "Only one instance of Catch::Session can ever be used"; + Catch::cerr() << msg << std::endl; + throw std::logic_error( msg ); + } + alreadyInstantiated = true; + } + ~Session() { + Catch::cleanUp(); + } + + void showHelp( std::string const& processName ) { + Catch::cout() << "\nCatch v" << libraryVersion() << "\n"; + + m_cli.usage( Catch::cout(), processName ); + Catch::cout() << "For more detail usage please see the project docs\n" << std::endl; + } + + int applyCommandLine( int argc, char const* const* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { + try { + m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail ); + m_unusedTokens = m_cli.parseInto( Clara::argsToVector( argc, argv ), m_configData ); + if( m_configData.showHelp ) + showHelp( m_configData.processName ); + m_config.reset(); + } + catch( std::exception& ex ) { + { + Colour colourGuard( Colour::Red ); + Catch::cerr() + << "\nError(s) in input:\n" + << Text( ex.what(), TextAttributes().setIndent(2) ) + << "\n\n"; + } + m_cli.usage( Catch::cout(), m_configData.processName ); + return (std::numeric_limits::max)(); + } + return 0; + } + + void useConfigData( ConfigData const& _configData ) { + m_configData = _configData; m_config.reset(); } - catch (std::exception& ex) - { + + int run( int argc, char const* const* const argv ) { + + int returnCode = applyCommandLine( argc, argv ); + if( returnCode == 0 ) + returnCode = run(); + return returnCode; + } + + int run() { + if( m_configData.showHelp ) + return 0; + + try { - Colour colourGuard(Colour::Red); - Catch::cerr() - << "\nError(s) in input:\n" - << Text(ex.what(), TextAttributes().setIndent(2)) - << "\n\n"; + config(); // Force config to be constructed + + seedRng( *m_config ); + + if( m_configData.filenamesAsTags ) + applyFilenamesAsTags( *m_config ); + + // Handle list request + if( Option listed = list( config() ) ) + return static_cast( *listed ); + + return static_cast( runTests( m_config ).assertions.failed ); + } + catch( std::exception& ex ) { + Catch::cerr() << ex.what() << std::endl; + return (std::numeric_limits::max)(); } - m_cli.usage(Catch::cout(), m_configData.processName); - return (std::numeric_limits::max)(); } - return 0; - } - void useConfigData(ConfigData const& _configData) - { - m_configData = _configData; - m_config.reset(); - } - - int run(int argc, char const* const argv[]) - { - - int returnCode = applyCommandLine(argc, argv); - if (returnCode == 0) - returnCode = run(); - return returnCode; - } - - int run() - { - if (m_configData.showHelp) - return 0; - - try - { - config(); // Force config to be constructed - - seedRng(*m_config); - - if (m_configData.filenamesAsTags) - applyFilenamesAsTags(*m_config); - - // Handle list request - if (Option listed = list(config())) - return static_cast(*listed); - - return static_cast(runTests(m_config).assertions.failed); + Clara::CommandLine const& cli() const { + return m_cli; } - catch (std::exception& ex) - { - Catch::cerr() << ex.what() << std::endl; - return (std::numeric_limits::max)(); + std::vector const& unusedTokens() const { + return m_unusedTokens; } - } + ConfigData& configData() { + return m_configData; + } + Config& config() { + if( !m_config ) + m_config = new Config( m_configData ); + return *m_config; + } + private: + Clara::CommandLine m_cli; + std::vector m_unusedTokens; + ConfigData m_configData; + Ptr m_config; + }; - Clara::CommandLine const& cli() const - { - return m_cli; - } - std::vector const& unusedTokens() const - { - return m_unusedTokens; - } - ConfigData& configData() - { - return m_configData; - } - Config& config() - { - if (!m_config) - m_config = new Config(m_configData); - return *m_config; - } - - private: - Clara::CommandLine m_cli; - std::vector m_unusedTokens; - ConfigData m_configData; - Ptr m_config; -}; - -bool Session::alreadyInstantiated = false; + bool Session::alreadyInstantiated = false; } // end namespace Catch @@ -7037,196 +7060,192 @@ bool Session::alreadyInstantiated = false; // #included from: catch_test_case_registry_impl.hpp #define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED -#include -#include +#include #include #include -#include +#include namespace Catch { -struct LexSort -{ - bool operator()(TestCase i, TestCase j) const { return (i < j); } -}; -struct RandomNumberGenerator -{ - int operator()(int n) const { return std::rand() % n; } -}; + struct RandomNumberGenerator { + typedef std::ptrdiff_t result_type; -inline std::vector sortTests(IConfig const& config, std::vector const& unsortedTestCases) -{ + result_type operator()( result_type n ) const { return std::rand() % n; } - std::vector sorted = unsortedTestCases; +#ifdef CATCH_CONFIG_CPP11_SHUFFLE + static constexpr result_type min() { return 0; } + static constexpr result_type max() { return 1000000; } + result_type operator()() const { return std::rand() % max(); } +#endif + template + static void shuffle( V& vector ) { + RandomNumberGenerator rng; +#ifdef CATCH_CONFIG_CPP11_SHUFFLE + std::shuffle( vector.begin(), vector.end(), rng ); +#else + std::random_shuffle( vector.begin(), vector.end(), rng ); +#endif + } + }; - switch (config.runOrder()) - { - case RunTests::InLexicographicalOrder: - std::sort(sorted.begin(), sorted.end(), LexSort()); - break; - case RunTests::InRandomOrder: - { - seedRng(config); + inline std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { - RandomNumberGenerator rng; - std::random_shuffle(sorted.begin(), sorted.end(), rng); + std::vector sorted = unsortedTestCases; + + switch( config.runOrder() ) { + case RunTests::InLexicographicalOrder: + std::sort( sorted.begin(), sorted.end() ); + break; + case RunTests::InRandomOrder: + { + seedRng( config ); + RandomNumberGenerator::shuffle( sorted ); + } + break; + case RunTests::InDeclarationOrder: + // already in declaration order + break; + } + return sorted; } - break; - case RunTests::InDeclarationOrder: - // already in declaration order - break; + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { + return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); } - return sorted; -} -bool matchTest(TestCase const& testCase, TestSpec const& testSpec, IConfig const& config) -{ - return testSpec.matches(testCase) && (config.allowThrows() || !testCase.throws()); -} -void enforceNoDuplicateTestCases(std::vector const& functions) -{ - std::set seenFunctions; - for (std::vector::const_iterator it = functions.begin(), itEnd = functions.end(); - it != itEnd; - ++it) - { - std::pair::const_iterator, bool> prev = seenFunctions.insert(*it); - if (!prev.second) - { - Catch::cerr() - << Colour(Colour::Red) - << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" - << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" - << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; - exit(1); + void enforceNoDuplicateTestCases( std::vector const& functions ) { + std::set seenFunctions; + for( std::vector::const_iterator it = functions.begin(), itEnd = functions.end(); + it != itEnd; + ++it ) { + std::pair::const_iterator, bool> prev = seenFunctions.insert( *it ); + if( !prev.second ) { + std::ostringstream ss; + + ss << Colour( Colour::Red ) + << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << '\n' + << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; + + throw std::runtime_error(ss.str()); + } } } -} -std::vector filterTests(std::vector const& testCases, TestSpec const& testSpec, IConfig const& config) -{ - std::vector filtered; - filtered.reserve(testCases.size()); - for (std::vector::const_iterator it = testCases.begin(), itEnd = testCases.end(); - it != itEnd; - ++it) - if (matchTest(*it, testSpec, config)) - filtered.push_back(*it); - return filtered; -} -std::vector const& getAllTestCasesSorted(IConfig const& config) -{ - return getRegistryHub().getTestCaseRegistry().getAllTestsSorted(config); -} - -class TestRegistry : public ITestCaseRegistry -{ - public: - TestRegistry() - : m_currentSortOrder(RunTests::InDeclarationOrder), - m_unnamedCount(0) - { + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { + std::vector filtered; + filtered.reserve( testCases.size() ); + for( std::vector::const_iterator it = testCases.begin(), itEnd = testCases.end(); + it != itEnd; + ++it ) + if( matchTest( *it, testSpec, config ) ) + filtered.push_back( *it ); + return filtered; + } + std::vector const& getAllTestCasesSorted( IConfig const& config ) { + return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); } - virtual ~TestRegistry(); - virtual void registerTest(TestCase const& testCase) - { - std::string name = testCase.getTestCaseInfo().name; - if (name == "") - { - std::ostringstream oss; - oss << "Anonymous test case " << ++m_unnamedCount; - return registerTest(testCase.withName(oss.str())); + class TestRegistry : public ITestCaseRegistry { + public: + TestRegistry() + : m_currentSortOrder( RunTests::InDeclarationOrder ), + m_unnamedCount( 0 ) + {} + virtual ~TestRegistry(); + + virtual void registerTest( TestCase const& testCase ) { + std::string name = testCase.getTestCaseInfo().name; + if( name.empty() ) { + std::ostringstream oss; + oss << "Anonymous test case " << ++m_unnamedCount; + return registerTest( testCase.withName( oss.str() ) ); + } + m_functions.push_back( testCase ); } - m_functions.push_back(testCase); - } - virtual std::vector const& getAllTests() const - { - return m_functions; - } - virtual std::vector const& getAllTestsSorted(IConfig const& config) const - { - if (m_sortedFunctions.empty()) - enforceNoDuplicateTestCases(m_functions); - - if (m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty()) - { - m_sortedFunctions = sortTests(config, m_functions); - m_currentSortOrder = config.runOrder(); + virtual std::vector const& getAllTests() const { + return m_functions; } - return m_sortedFunctions; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const { + if( m_sortedFunctions.empty() ) + enforceNoDuplicateTestCases( m_functions ); + + if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { + m_sortedFunctions = sortTests( config, m_functions ); + m_currentSortOrder = config.runOrder(); + } + return m_sortedFunctions; + } + + private: + std::vector m_functions; + mutable RunTests::InWhatOrder m_currentSortOrder; + mutable std::vector m_sortedFunctions; + size_t m_unnamedCount; + std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised + }; + + /////////////////////////////////////////////////////////////////////////// + + class FreeFunctionTestCase : public SharedImpl { + public: + + FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {} + + virtual void invoke() const { + m_fun(); + } + + private: + virtual ~FreeFunctionTestCase(); + + TestFunction m_fun; + }; + + inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) { + std::string className = classOrQualifiedMethodName; + if( startsWith( className, '&' ) ) + { + std::size_t lastColons = className.rfind( "::" ); + std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); + if( penultimateColons == std::string::npos ) + penultimateColons = 1; + className = className.substr( penultimateColons, lastColons-penultimateColons ); + } + return className; } - private: - std::vector m_functions; - mutable RunTests::InWhatOrder m_currentSortOrder; - mutable std::vector m_sortedFunctions; - size_t m_unnamedCount; - std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised -}; + void registerTestCase + ( ITestCase* testCase, + char const* classOrQualifiedMethodName, + NameAndDesc const& nameAndDesc, + SourceLineInfo const& lineInfo ) { -/////////////////////////////////////////////////////////////////////////// - -class FreeFunctionTestCase : public SharedImpl -{ - public: - FreeFunctionTestCase(TestFunction fun) : m_fun(fun) {} - - virtual void invoke() const - { - m_fun(); + getMutableRegistryHub().registerTest + ( makeTestCase + ( testCase, + extractClassName( classOrQualifiedMethodName ), + nameAndDesc.name, + nameAndDesc.description, + lineInfo ) ); + } + void registerTestCaseFunction + ( TestFunction function, + SourceLineInfo const& lineInfo, + NameAndDesc const& nameAndDesc ) { + registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo ); } - private: - virtual ~FreeFunctionTestCase(); + /////////////////////////////////////////////////////////////////////////// - TestFunction m_fun; -}; - -inline std::string extractClassName(std::string const& classOrQualifiedMethodName) -{ - std::string className = classOrQualifiedMethodName; - if (startsWith(className, "&")) - { - std::size_t lastColons = className.rfind("::"); - std::size_t penultimateColons = className.rfind("::", lastColons - 1); - if (penultimateColons == std::string::npos) - penultimateColons = 1; - className = className.substr(penultimateColons, lastColons - penultimateColons); + AutoReg::AutoReg + ( TestFunction function, + SourceLineInfo const& lineInfo, + NameAndDesc const& nameAndDesc ) { + registerTestCaseFunction( function, lineInfo, nameAndDesc ); } - return className; -} -void registerTestCase(ITestCase* testCase, - char const* classOrQualifiedMethodName, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo) -{ - - getMutableRegistryHub().registerTest(makeTestCase(testCase, - extractClassName(classOrQualifiedMethodName), - nameAndDesc.name, - nameAndDesc.description, - lineInfo)); -} -void registerTestCaseFunction(TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc) -{ - registerTestCase(new FreeFunctionTestCase(function), "", nameAndDesc, lineInfo); -} - -/////////////////////////////////////////////////////////////////////////// - -AutoReg::AutoReg(TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc) -{ - registerTestCaseFunction(function, lineInfo, nameAndDesc); -} - -AutoReg::~AutoReg() {} + AutoReg::~AutoReg() {} } // end namespace Catch @@ -7237,42 +7256,37 @@ AutoReg::~AutoReg() {} namespace Catch { -class ReporterRegistry : public IReporterRegistry -{ + class ReporterRegistry : public IReporterRegistry { - public: - virtual ~ReporterRegistry() CATCH_OVERRIDE {} + public: - virtual IStreamingReporter* create(std::string const& name, Ptr const& config) const CATCH_OVERRIDE - { - FactoryMap::const_iterator it = m_factories.find(name); - if (it == m_factories.end()) - return CATCH_NULL; - return it->second->create(ReporterConfig(config)); - } + virtual ~ReporterRegistry() CATCH_OVERRIDE {} - void registerReporter(std::string const& name, Ptr const& factory) - { - m_factories.insert(std::make_pair(name, factory)); - } - void registerListener(Ptr const& factory) - { - m_listeners.push_back(factory); - } + virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const CATCH_OVERRIDE { + FactoryMap::const_iterator it = m_factories.find( name ); + if( it == m_factories.end() ) + return CATCH_NULL; + return it->second->create( ReporterConfig( config ) ); + } - virtual FactoryMap const& getFactories() const CATCH_OVERRIDE - { - return m_factories; - } - virtual Listeners const& getListeners() const CATCH_OVERRIDE - { - return m_listeners; - } + void registerReporter( std::string const& name, Ptr const& factory ) { + m_factories.insert( std::make_pair( name, factory ) ); + } + void registerListener( Ptr const& factory ) { + m_listeners.push_back( factory ); + } - private: - FactoryMap m_factories; - Listeners m_listeners; -}; + virtual FactoryMap const& getFactories() const CATCH_OVERRIDE { + return m_factories; + } + virtual Listeners const& getListeners() const CATCH_OVERRIDE { + return m_listeners; + } + + private: + FactoryMap m_factories; + Listeners m_listeners; + }; } // #included from: catch_exception_translator_registry.hpp @@ -7284,174 +7298,172 @@ class ReporterRegistry : public IReporterRegistry namespace Catch { -class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry -{ - public: - ~ExceptionTranslatorRegistry() - { - deleteAll(m_translators); - } + class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { + public: + ~ExceptionTranslatorRegistry() { + deleteAll( m_translators ); + } - virtual void registerTranslator(const IExceptionTranslator* translator) - { - m_translators.push_back(translator); - } + virtual void registerTranslator( const IExceptionTranslator* translator ) { + m_translators.push_back( translator ); + } - virtual std::string translateActiveException() const - { - try - { + virtual std::string translateActiveException() const { + try { #ifdef __OBJC__ - // In Objective-C try objective-c exceptions first - @try - { - return tryTranslators(); - } - @catch (NSException* exception) - { - return Catch::toString([exception description]); - } + // In Objective-C try objective-c exceptions first + @try { + return tryTranslators(); + } + @catch (NSException *exception) { + return Catch::toString( [exception description] ); + } #else - return tryTranslators(); + return tryTranslators(); #endif + } + catch( TestFailureException& ) { + throw; + } + catch( std::exception& ex ) { + return ex.what(); + } + catch( std::string& msg ) { + return msg; + } + catch( const char* msg ) { + return msg; + } + catch(...) { + return "Unknown exception"; + } } - catch (TestFailureException&) - { - throw; - } - catch (std::exception& ex) - { - return ex.what(); - } - catch (std::string& msg) - { - return msg; - } - catch (const char* msg) - { - return msg; - } - catch (...) - { - return "Unknown exception"; - } - } - std::string tryTranslators() const - { - if (m_translators.empty()) - throw; - else - return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end()); - } + std::string tryTranslators() const { + if( m_translators.empty() ) + throw; + else + return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() ); + } - private: - std::vector m_translators; -}; + private: + std::vector m_translators; + }; } +// #included from: catch_tag_alias_registry.h +#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED + +#include + namespace Catch { -namespace { + class TagAliasRegistry : public ITagAliasRegistry { + public: + virtual ~TagAliasRegistry(); + virtual Option find( std::string const& alias ) const; + virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const; + void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ); -class RegistryHub : public IRegistryHub, public IMutableRegistryHub -{ + private: + std::map m_registry; + }; - RegistryHub(RegistryHub const&); - void operator=(RegistryHub const&); +} // end namespace Catch - public: // IRegistryHub - RegistryHub() - { - } - virtual IReporterRegistry const& getReporterRegistry() const CATCH_OVERRIDE - { - return m_reporterRegistry; - } - virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE - { - return m_testCaseRegistry; - } - virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE - { - return m_exceptionTranslatorRegistry; +namespace Catch { + + namespace { + + class RegistryHub : public IRegistryHub, public IMutableRegistryHub { + + RegistryHub( RegistryHub const& ); + void operator=( RegistryHub const& ); + + public: // IRegistryHub + RegistryHub() { + } + virtual IReporterRegistry const& getReporterRegistry() const CATCH_OVERRIDE { + return m_reporterRegistry; + } + virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE { + return m_testCaseRegistry; + } + virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE { + return m_exceptionTranslatorRegistry; + } + virtual ITagAliasRegistry const& getTagAliasRegistry() const CATCH_OVERRIDE { + return m_tagAliasRegistry; + } + + public: // IMutableRegistryHub + virtual void registerReporter( std::string const& name, Ptr const& factory ) CATCH_OVERRIDE { + m_reporterRegistry.registerReporter( name, factory ); + } + virtual void registerListener( Ptr const& factory ) CATCH_OVERRIDE { + m_reporterRegistry.registerListener( factory ); + } + virtual void registerTest( TestCase const& testInfo ) CATCH_OVERRIDE { + m_testCaseRegistry.registerTest( testInfo ); + } + virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE { + m_exceptionTranslatorRegistry.registerTranslator( translator ); + } + virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) CATCH_OVERRIDE { + m_tagAliasRegistry.add( alias, tag, lineInfo ); + } + + private: + TestRegistry m_testCaseRegistry; + ReporterRegistry m_reporterRegistry; + ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + TagAliasRegistry m_tagAliasRegistry; + }; + + // Single, global, instance + inline RegistryHub*& getTheRegistryHub() { + static RegistryHub* theRegistryHub = CATCH_NULL; + if( !theRegistryHub ) + theRegistryHub = new RegistryHub(); + return theRegistryHub; + } } - public: // IMutableRegistryHub - virtual void registerReporter(std::string const& name, Ptr const& factory) CATCH_OVERRIDE - { - m_reporterRegistry.registerReporter(name, factory); + IRegistryHub& getRegistryHub() { + return *getTheRegistryHub(); } - virtual void registerListener(Ptr const& factory) CATCH_OVERRIDE - { - m_reporterRegistry.registerListener(factory); + IMutableRegistryHub& getMutableRegistryHub() { + return *getTheRegistryHub(); } - virtual void registerTest(TestCase const& testInfo) CATCH_OVERRIDE - { - m_testCaseRegistry.registerTest(testInfo); + void cleanUp() { + delete getTheRegistryHub(); + getTheRegistryHub() = CATCH_NULL; + cleanUpContext(); } - virtual void registerTranslator(const IExceptionTranslator* translator) CATCH_OVERRIDE - { - m_exceptionTranslatorRegistry.registerTranslator(translator); + std::string translateActiveException() { + return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); } - private: - TestRegistry m_testCaseRegistry; - ReporterRegistry m_reporterRegistry; - ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; -}; - -// Single, global, instance -inline RegistryHub*& getTheRegistryHub() -{ - static RegistryHub* theRegistryHub = CATCH_NULL; - if (!theRegistryHub) - theRegistryHub = new RegistryHub(); - return theRegistryHub; -} -} - -IRegistryHub& getRegistryHub() -{ - return *getTheRegistryHub(); -} -IMutableRegistryHub& getMutableRegistryHub() -{ - return *getTheRegistryHub(); -} -void cleanUp() -{ - delete getTheRegistryHub(); - getTheRegistryHub() = CATCH_NULL; - cleanUpContext(); -} -std::string translateActiveException() -{ - return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); -} - } // end namespace Catch // #included from: catch_notimplemented_exception.hpp #define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED -#include +#include namespace Catch { -NotImplementedException::NotImplementedException(SourceLineInfo const& lineInfo) - : m_lineInfo(lineInfo) -{ - std::ostringstream oss; - oss << lineInfo << ": function "; - oss << "not implemented"; - m_what = oss.str(); -} + NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo ) + : m_lineInfo( lineInfo ) { + std::ostringstream oss; + oss << lineInfo << ": function "; + oss << "not implemented"; + m_what = oss.str(); + } -const char* NotImplementedException::what() const CATCH_NOEXCEPT -{ - return m_what.c_str(); -} + const char* NotImplementedException::what() const CATCH_NOEXCEPT { + return m_what.c_str(); + } } // end namespace Catch @@ -7461,410 +7473,369 @@ const char* NotImplementedException::what() const CATCH_NOEXCEPT // #included from: catch_stream.hpp #define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED +#include #include #include -#include namespace Catch { -template -class StreamBufImpl : public StreamBufBase -{ - char data[bufferSize]; - WriterF m_writer; + template + class StreamBufImpl : public StreamBufBase { + char data[bufferSize]; + WriterF m_writer; - public: - StreamBufImpl() - { - setp(data, data + sizeof(data)); - } - - ~StreamBufImpl() CATCH_NOEXCEPT - { - sync(); - } - - private: - int overflow(int c) - { - sync(); - - if (c != EOF) - { - if (pbase() == epptr()) - m_writer(std::string(1, static_cast(c))); - else - sputc(static_cast(c)); + public: + StreamBufImpl() { + setp( data, data + sizeof(data) ); } - return 0; - } - int sync() - { - if (pbase() != pptr()) - { - m_writer(std::string(pbase(), static_cast(pptr() - pbase()))); - setp(pbase(), epptr()); + ~StreamBufImpl() CATCH_NOEXCEPT { + sync(); + } + + private: + int overflow( int c ) { + sync(); + + if( c != EOF ) { + if( pbase() == epptr() ) + m_writer( std::string( 1, static_cast( c ) ) ); + else + sputc( static_cast( c ) ); + } + return 0; + } + + int sync() { + if( pbase() != pptr() ) { + m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); + setp( pbase(), epptr() ); + } + return 0; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + FileStream::FileStream( std::string const& filename ) { + m_ofs.open( filename.c_str() ); + if( m_ofs.fail() ) { + std::ostringstream oss; + oss << "Unable to open file: '" << filename << '\''; + throw std::domain_error( oss.str() ); } - return 0; } -}; -/////////////////////////////////////////////////////////////////////////// - -FileStream::FileStream(std::string const& filename) -{ - m_ofs.open(filename.c_str()); - if (m_ofs.fail()) - { - std::ostringstream oss; - oss << "Unable to open file: '" << filename << "'"; - throw std::domain_error(oss.str()); + std::ostream& FileStream::stream() const { + return m_ofs; } -} -std::ostream& FileStream::stream() const -{ - return m_ofs; -} + struct OutputDebugWriter { -struct OutputDebugWriter -{ + void operator()( std::string const&str ) { + writeToDebugConsole( str ); + } + }; - void operator()(std::string const& str) - { - writeToDebugConsole(str); + DebugOutStream::DebugOutStream() + : m_streamBuf( new StreamBufImpl() ), + m_os( m_streamBuf.get() ) + {} + + std::ostream& DebugOutStream::stream() const { + return m_os; } -}; -DebugOutStream::DebugOutStream() - : m_streamBuf(new StreamBufImpl()), - m_os(m_streamBuf.get()) -{ -} + // Store the streambuf from cout up-front because + // cout may get redirected when running tests + CoutStream::CoutStream() + : m_os( Catch::cout().rdbuf() ) + {} -std::ostream& DebugOutStream::stream() const -{ - return m_os; -} - -// Store the streambuf from cout up-front because -// cout may get redirected when running tests -CoutStream::CoutStream() - : m_os(Catch::cout().rdbuf()) -{ -} - -std::ostream& CoutStream::stream() const -{ - return m_os; -} + std::ostream& CoutStream::stream() const { + return m_os; + } #ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions -std::ostream& cout() -{ - return std::cout; -} -std::ostream& cerr() -{ - return std::cerr; -} + std::ostream& cout() { + return std::cout; + } + std::ostream& cerr() { + return std::cerr; + } #endif } namespace Catch { -class Context : public IMutableContext -{ + class Context : public IMutableContext { - Context() : m_config(CATCH_NULL), m_runner(CATCH_NULL), m_resultCapture(CATCH_NULL) {} - Context(Context const&); - void operator=(Context const&); + Context() : m_config( CATCH_NULL ), m_runner( CATCH_NULL ), m_resultCapture( CATCH_NULL ) {} + Context( Context const& ); + void operator=( Context const& ); - public: // IContext - virtual IResultCapture* getResultCapture() - { - return m_resultCapture; - } - virtual IRunner* getRunner() - { - return m_runner; - } - virtual size_t getGeneratorIndex(std::string const& fileInfo, size_t totalSize) - { - return getGeneratorsForCurrentTest() - .getGeneratorInfo(fileInfo, totalSize) - .getCurrentIndex(); - } - virtual bool advanceGeneratorsForCurrentTest() - { - IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); - return generators && generators->moveNext(); - } - - virtual Ptr getConfig() const - { - return m_config; - } - - public: // IMutableContext - virtual void setResultCapture(IResultCapture* resultCapture) - { - m_resultCapture = resultCapture; - } - virtual void setRunner(IRunner* runner) - { - m_runner = runner; - } - virtual void setConfig(Ptr const& config) - { - m_config = config; - } - - friend IMutableContext& getCurrentMutableContext(); - - private: - IGeneratorsForTest* findGeneratorsForCurrentTest() - { - std::string testName = getResultCapture()->getCurrentTestName(); - - std::map::const_iterator it = - m_generatorsByTestName.find(testName); - return it != m_generatorsByTestName.end() - ? it->second - : CATCH_NULL; - } - - IGeneratorsForTest& getGeneratorsForCurrentTest() - { - IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); - if (!generators) - { - std::string testName = getResultCapture()->getCurrentTestName(); - generators = createGeneratorsForTest(); - m_generatorsByTestName.insert(std::make_pair(testName, generators)); + public: + virtual ~Context() { + deleteAllValues( m_generatorsByTestName ); } - return *generators; + + public: // IContext + virtual IResultCapture* getResultCapture() { + return m_resultCapture; + } + virtual IRunner* getRunner() { + return m_runner; + } + virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) { + return getGeneratorsForCurrentTest() + .getGeneratorInfo( fileInfo, totalSize ) + .getCurrentIndex(); + } + virtual bool advanceGeneratorsForCurrentTest() { + IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); + return generators && generators->moveNext(); + } + + virtual Ptr getConfig() const { + return m_config; + } + + public: // IMutableContext + virtual void setResultCapture( IResultCapture* resultCapture ) { + m_resultCapture = resultCapture; + } + virtual void setRunner( IRunner* runner ) { + m_runner = runner; + } + virtual void setConfig( Ptr const& config ) { + m_config = config; + } + + friend IMutableContext& getCurrentMutableContext(); + + private: + IGeneratorsForTest* findGeneratorsForCurrentTest() { + std::string testName = getResultCapture()->getCurrentTestName(); + + std::map::const_iterator it = + m_generatorsByTestName.find( testName ); + return it != m_generatorsByTestName.end() + ? it->second + : CATCH_NULL; + } + + IGeneratorsForTest& getGeneratorsForCurrentTest() { + IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); + if( !generators ) { + std::string testName = getResultCapture()->getCurrentTestName(); + generators = createGeneratorsForTest(); + m_generatorsByTestName.insert( std::make_pair( testName, generators ) ); + } + return *generators; + } + + private: + Ptr m_config; + IRunner* m_runner; + IResultCapture* m_resultCapture; + std::map m_generatorsByTestName; + }; + + namespace { + Context* currentContext = CATCH_NULL; + } + IMutableContext& getCurrentMutableContext() { + if( !currentContext ) + currentContext = new Context(); + return *currentContext; + } + IContext& getCurrentContext() { + return getCurrentMutableContext(); } - private: - Ptr m_config; - IRunner* m_runner; - IResultCapture* m_resultCapture; - std::map m_generatorsByTestName; -}; - -namespace { -Context* currentContext = CATCH_NULL; -} -IMutableContext& getCurrentMutableContext() -{ - if (!currentContext) - currentContext = new Context(); - return *currentContext; -} -IContext& getCurrentContext() -{ - return getCurrentMutableContext(); -} - -void cleanUpContext() -{ - delete currentContext; - currentContext = CATCH_NULL; -} + void cleanUpContext() { + delete currentContext; + currentContext = CATCH_NULL; + } } // #included from: catch_console_colour_impl.hpp #define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED +// #included from: catch_errno_guard.hpp +#define TWOBLUECUBES_CATCH_ERRNO_GUARD_HPP_INCLUDED + +#include + namespace Catch { -namespace { -struct IColourImpl -{ - virtual ~IColourImpl() {} - virtual void use(Colour::Code _colourCode) = 0; -}; + class ErrnoGuard { + public: + ErrnoGuard():m_oldErrno(errno){} + ~ErrnoGuard() { errno = m_oldErrno; } + private: + int m_oldErrno; + }; -struct NoColourImpl : IColourImpl -{ - void use(Colour::Code) {} +} - static IColourImpl* instance() - { - static NoColourImpl s_instance; - return &s_instance; - } -}; +namespace Catch { + namespace { -} // anon namespace + struct IColourImpl { + virtual ~IColourImpl() {} + virtual void use( Colour::Code _colourCode ) = 0; + }; + + struct NoColourImpl : IColourImpl { + void use( Colour::Code ) {} + + static IColourImpl* instance() { + static NoColourImpl s_instance; + return &s_instance; + } + }; + + } // anon namespace } // namespace Catch -#if !defined(CATCH_CONFIG_COLOUR_NONE) && !defined(CATCH_CONFIG_COLOUR_WINDOWS) && !defined(CATCH_CONFIG_COLOUR_ANSI) -#ifdef CATCH_PLATFORM_WINDOWS -#define CATCH_CONFIG_COLOUR_WINDOWS -#else -#define CATCH_CONFIG_COLOUR_ANSI -#endif +#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI ) +# ifdef CATCH_PLATFORM_WINDOWS +# define CATCH_CONFIG_COLOUR_WINDOWS +# else +# define CATCH_CONFIG_COLOUR_ANSI +# endif #endif -#if defined(CATCH_CONFIG_COLOUR_WINDOWS) ///////////////////////////////////////// - -#ifndef NOMINMAX -#define NOMINMAX -#endif - -#ifdef __AFXDLL -#include -#else -#include -#endif +#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// namespace Catch { namespace { -class Win32ColourImpl : public IColourImpl -{ - public: - Win32ColourImpl() : stdoutHandle(GetStdHandle(STD_OUTPUT_HANDLE)) - { - CONSOLE_SCREEN_BUFFER_INFO csbiInfo; - GetConsoleScreenBufferInfo(stdoutHandle, &csbiInfo); - originalForegroundAttributes = csbiInfo.wAttributes & ~(BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY); - originalBackgroundAttributes = csbiInfo.wAttributes & ~(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY); - } - - virtual void use(Colour::Code _colourCode) - { - switch (_colourCode) + class Win32ColourImpl : public IColourImpl { + public: + Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) ) { - case Colour::None: - return setTextAttribute(originalForegroundAttributes); - case Colour::White: - return setTextAttribute(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); - case Colour::Red: - return setTextAttribute(FOREGROUND_RED); - case Colour::Green: - return setTextAttribute(FOREGROUND_GREEN); - case Colour::Blue: - return setTextAttribute(FOREGROUND_BLUE); - case Colour::Cyan: - return setTextAttribute(FOREGROUND_BLUE | FOREGROUND_GREEN); - case Colour::Yellow: - return setTextAttribute(FOREGROUND_RED | FOREGROUND_GREEN); - case Colour::Grey: - return setTextAttribute(0); - - case Colour::LightGrey: - return setTextAttribute(FOREGROUND_INTENSITY); - case Colour::BrightRed: - return setTextAttribute(FOREGROUND_INTENSITY | FOREGROUND_RED); - case Colour::BrightGreen: - return setTextAttribute(FOREGROUND_INTENSITY | FOREGROUND_GREEN); - case Colour::BrightWhite: - return setTextAttribute(FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); - - case Colour::Bright: - throw std::logic_error("not a colour"); + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); + originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY ); + originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); } - } - private: - void setTextAttribute(WORD _textAttribute) - { - SetConsoleTextAttribute(stdoutHandle, _textAttribute | originalBackgroundAttributes); - } - HANDLE stdoutHandle; - WORD originalForegroundAttributes; - WORD originalBackgroundAttributes; -}; + virtual void use( Colour::Code _colourCode ) { + switch( _colourCode ) { + case Colour::None: return setTextAttribute( originalForegroundAttributes ); + case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + case Colour::Red: return setTextAttribute( FOREGROUND_RED ); + case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); + case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); + case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); + case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); + case Colour::Grey: return setTextAttribute( 0 ); -IColourImpl* platformColourInstance() -{ - static Win32ColourImpl s_instance; - return &s_instance; -} + case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); + case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); + case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); + case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + + case Colour::Bright: throw std::logic_error( "not a colour" ); + } + } + + private: + void setTextAttribute( WORD _textAttribute ) { + SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes ); + } + HANDLE stdoutHandle; + WORD originalForegroundAttributes; + WORD originalBackgroundAttributes; + }; + + IColourImpl* platformColourInstance() { + static Win32ColourImpl s_instance; + + Ptr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config + ? config->useColour() + : UseColour::Auto; + if( colourMode == UseColour::Auto ) + colourMode = !isDebuggerActive() + ? UseColour::Yes + : UseColour::No; + return colourMode == UseColour::Yes + ? &s_instance + : NoColourImpl::instance(); + } } // end anon namespace } // end namespace Catch -#elif defined(CATCH_CONFIG_COLOUR_ANSI) ////////////////////////////////////// +#elif defined( CATCH_CONFIG_COLOUR_ANSI ) ////////////////////////////////////// #include namespace Catch { namespace { -// use POSIX/ ANSI console terminal codes -// Thanks to Adam Strzelecki for original contribution -// (http://github.com/nanoant) -// https://github.com/philsquared/Catch/pull/131 -class PosixColourImpl : public IColourImpl -{ - public: - virtual void use(Colour::Code _colourCode) - { - switch (_colourCode) - { - case Colour::None: - case Colour::White: - return setColour("[0m"); - case Colour::Red: - return setColour("[0;31m"); - case Colour::Green: - return setColour("[0;32m"); - case Colour::Blue: - return setColour("[0:34m"); - case Colour::Cyan: - return setColour("[0;36m"); - case Colour::Yellow: - return setColour("[0;33m"); - case Colour::Grey: - return setColour("[1;30m"); + // use POSIX/ ANSI console terminal codes + // Thanks to Adam Strzelecki for original contribution + // (http://github.com/nanoant) + // https://github.com/philsquared/Catch/pull/131 + class PosixColourImpl : public IColourImpl { + public: + virtual void use( Colour::Code _colourCode ) { + switch( _colourCode ) { + case Colour::None: + case Colour::White: return setColour( "[0m" ); + case Colour::Red: return setColour( "[0;31m" ); + case Colour::Green: return setColour( "[0;32m" ); + case Colour::Blue: return setColour( "[0;34m" ); + case Colour::Cyan: return setColour( "[0;36m" ); + case Colour::Yellow: return setColour( "[0;33m" ); + case Colour::Grey: return setColour( "[1;30m" ); - case Colour::LightGrey: - return setColour("[0;37m"); - case Colour::BrightRed: - return setColour("[1;31m"); - case Colour::BrightGreen: - return setColour("[1;32m"); - case Colour::BrightWhite: - return setColour("[1;37m"); + case Colour::LightGrey: return setColour( "[0;37m" ); + case Colour::BrightRed: return setColour( "[1;31m" ); + case Colour::BrightGreen: return setColour( "[1;32m" ); + case Colour::BrightWhite: return setColour( "[1;37m" ); - case Colour::Bright: - throw std::logic_error("not a colour"); + case Colour::Bright: throw std::logic_error( "not a colour" ); + } + } + static IColourImpl* instance() { + static PosixColourImpl s_instance; + return &s_instance; } - } - static IColourImpl* instance() - { - static PosixColourImpl s_instance; - return &s_instance; - } - private: - void setColour(const char* _escapeCode) - { - Catch::cout() << '\033' << _escapeCode; - } -}; + private: + void setColour( const char* _escapeCode ) { + Catch::cout() << '\033' << _escapeCode; + } + }; -IColourImpl* platformColourInstance() -{ - Ptr config = getCurrentContext().getConfig(); - return (config && config->forceColour()) || isatty(STDOUT_FILENO) - ? PosixColourImpl::instance() - : NoColourImpl::instance(); -} + IColourImpl* platformColourInstance() { + ErrnoGuard guard; + Ptr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config + ? config->useColour() + : UseColour::Auto; + if( colourMode == UseColour::Auto ) + colourMode = (!isDebuggerActive() && isatty(STDOUT_FILENO) ) + ? UseColour::Yes + : UseColour::No; + return colourMode == UseColour::Yes + ? PosixColourImpl::instance() + : NoColourImpl::instance(); + } } // end anon namespace } // end namespace Catch -#else // not Windows or ANSI /////////////////////////////////////////////// +#else // not Windows or ANSI /////////////////////////////////////////////// namespace Catch { -static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); } + static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); } } // end namespace Catch @@ -7872,106 +7843,89 @@ static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); namespace Catch { -Colour::Colour(Code _colourCode) : m_moved(false) { use(_colourCode); } -Colour::Colour(Colour const& _other) : m_moved(false) { const_cast(_other).m_moved = true; } -Colour::~Colour() -{ - if (!m_moved) use(None); -} + Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); } + Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast( _other ).m_moved = true; } + Colour::~Colour(){ if( !m_moved ) use( None ); } -void Colour::use(Code _colourCode) -{ - static IColourImpl* impl = isDebuggerActive() - ? NoColourImpl::instance() - : platformColourInstance(); - impl->use(_colourCode); -} + void Colour::use( Code _colourCode ) { + static IColourImpl* impl = platformColourInstance(); + impl->use( _colourCode ); + } } // end namespace Catch // #included from: catch_generators_impl.hpp #define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED -#include -#include #include +#include +#include namespace Catch { -struct GeneratorInfo : IGeneratorInfo -{ + struct GeneratorInfo : IGeneratorInfo { - GeneratorInfo(std::size_t size) - : m_size(size), - m_currentIndex(0) - { - } + GeneratorInfo( std::size_t size ) + : m_size( size ), + m_currentIndex( 0 ) + {} - bool moveNext() - { - if (++m_currentIndex == m_size) - { - m_currentIndex = 0; + bool moveNext() { + if( ++m_currentIndex == m_size ) { + m_currentIndex = 0; + return false; + } + return true; + } + + std::size_t getCurrentIndex() const { + return m_currentIndex; + } + + std::size_t m_size; + std::size_t m_currentIndex; + }; + + /////////////////////////////////////////////////////////////////////////// + + class GeneratorsForTest : public IGeneratorsForTest { + + public: + ~GeneratorsForTest() { + deleteAll( m_generatorsInOrder ); + } + + IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) { + std::map::const_iterator it = m_generatorsByName.find( fileInfo ); + if( it == m_generatorsByName.end() ) { + IGeneratorInfo* info = new GeneratorInfo( size ); + m_generatorsByName.insert( std::make_pair( fileInfo, info ) ); + m_generatorsInOrder.push_back( info ); + return *info; + } + return *it->second; + } + + bool moveNext() { + std::vector::const_iterator it = m_generatorsInOrder.begin(); + std::vector::const_iterator itEnd = m_generatorsInOrder.end(); + for(; it != itEnd; ++it ) { + if( (*it)->moveNext() ) + return true; + } return false; } - return true; - } - std::size_t getCurrentIndex() const + private: + std::map m_generatorsByName; + std::vector m_generatorsInOrder; + }; + + IGeneratorsForTest* createGeneratorsForTest() { - return m_currentIndex; + return new GeneratorsForTest(); } - std::size_t m_size; - std::size_t m_currentIndex; -}; - -/////////////////////////////////////////////////////////////////////////// - -class GeneratorsForTest : public IGeneratorsForTest -{ - - public: - ~GeneratorsForTest() - { - deleteAll(m_generatorsInOrder); - } - - IGeneratorInfo& getGeneratorInfo(std::string const& fileInfo, std::size_t size) - { - std::map::const_iterator it = m_generatorsByName.find(fileInfo); - if (it == m_generatorsByName.end()) - { - IGeneratorInfo* info = new GeneratorInfo(size); - m_generatorsByName.insert(std::make_pair(fileInfo, info)); - m_generatorsInOrder.push_back(info); - return *info; - } - return *it->second; - } - - bool moveNext() - { - std::vector::const_iterator it = m_generatorsInOrder.begin(); - std::vector::const_iterator itEnd = m_generatorsInOrder.end(); - for (; it != itEnd; ++it) - { - if ((*it)->moveNext()) - return true; - } - return false; - } - - private: - std::map m_generatorsByName; - std::vector m_generatorsInOrder; -}; - -IGeneratorsForTest* createGeneratorsForTest() -{ - return new GeneratorsForTest(); -} - } // end namespace Catch // #included from: catch_assertionresult.hpp @@ -7979,301 +7933,273 @@ IGeneratorsForTest* createGeneratorsForTest() namespace Catch { -AssertionInfo::AssertionInfo(std::string const& _macroName, - SourceLineInfo const& _lineInfo, - std::string const& _capturedExpression, - ResultDisposition::Flags _resultDisposition) - : macroName(_macroName), - lineInfo(_lineInfo), - capturedExpression(_capturedExpression), - resultDisposition(_resultDisposition) -{ -} + AssertionInfo::AssertionInfo( std::string const& _macroName, + SourceLineInfo const& _lineInfo, + std::string const& _capturedExpression, + ResultDisposition::Flags _resultDisposition ) + : macroName( _macroName ), + lineInfo( _lineInfo ), + capturedExpression( _capturedExpression ), + resultDisposition( _resultDisposition ) + {} -AssertionResult::AssertionResult() {} + AssertionResult::AssertionResult() {} -AssertionResult::AssertionResult(AssertionInfo const& info, AssertionResultData const& data) - : m_info(info), - m_resultData(data) -{ -} + AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) + : m_info( info ), + m_resultData( data ) + {} -AssertionResult::~AssertionResult() {} + AssertionResult::~AssertionResult() {} -// Result was a success -bool AssertionResult::succeeded() const -{ - return Catch::isOk(m_resultData.resultType); -} + // Result was a success + bool AssertionResult::succeeded() const { + return Catch::isOk( m_resultData.resultType ); + } -// Result was a success, or failure is suppressed -bool AssertionResult::isOk() const -{ - return Catch::isOk(m_resultData.resultType) || shouldSuppressFailure(m_info.resultDisposition); -} + // Result was a success, or failure is suppressed + bool AssertionResult::isOk() const { + return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); + } -ResultWas::OfType AssertionResult::getResultType() const -{ - return m_resultData.resultType; -} + ResultWas::OfType AssertionResult::getResultType() const { + return m_resultData.resultType; + } -bool AssertionResult::hasExpression() const -{ - return !m_info.capturedExpression.empty(); -} + bool AssertionResult::hasExpression() const { + return !m_info.capturedExpression.empty(); + } -bool AssertionResult::hasMessage() const -{ - return !m_resultData.message.empty(); -} + bool AssertionResult::hasMessage() const { + return !m_resultData.message.empty(); + } -std::string AssertionResult::getExpression() const -{ - if (isFalseTest(m_info.resultDisposition)) - return "!" + m_info.capturedExpression; - else - return m_info.capturedExpression; -} -std::string AssertionResult::getExpressionInMacro() const -{ - if (m_info.macroName.empty()) - return m_info.capturedExpression; - else - return m_info.macroName + "( " + m_info.capturedExpression + " )"; -} + std::string AssertionResult::getExpression() const { + if( isFalseTest( m_info.resultDisposition ) ) + return '!' + m_info.capturedExpression; + else + return m_info.capturedExpression; + } + std::string AssertionResult::getExpressionInMacro() const { + if( m_info.macroName.empty() ) + return m_info.capturedExpression; + else + return m_info.macroName + "( " + m_info.capturedExpression + " )"; + } -bool AssertionResult::hasExpandedExpression() const -{ - return hasExpression() && getExpandedExpression() != getExpression(); -} + bool AssertionResult::hasExpandedExpression() const { + return hasExpression() && getExpandedExpression() != getExpression(); + } -std::string AssertionResult::getExpandedExpression() const -{ - return m_resultData.reconstructedExpression; -} + std::string AssertionResult::getExpandedExpression() const { + return m_resultData.reconstructExpression(); + } -std::string AssertionResult::getMessage() const -{ - return m_resultData.message; -} -SourceLineInfo AssertionResult::getSourceInfo() const -{ - return m_info.lineInfo; -} + std::string AssertionResult::getMessage() const { + return m_resultData.message; + } + SourceLineInfo AssertionResult::getSourceInfo() const { + return m_info.lineInfo; + } -std::string AssertionResult::getTestMacroName() const -{ - return m_info.macroName; -} + std::string AssertionResult::getTestMacroName() const { + return m_info.macroName; + } + + void AssertionResult::discardDecomposedExpression() const { + m_resultData.decomposedExpression = CATCH_NULL; + } + + void AssertionResult::expandDecomposedExpression() const { + m_resultData.reconstructExpression(); + } } // end namespace Catch // #included from: catch_test_case_info.hpp #define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED +#include + namespace Catch { -inline TestCaseInfo::SpecialProperties parseSpecialTag(std::string const& tag) -{ - if (startsWith(tag, ".") || - tag == "hide" || - tag == "!hide") - return TestCaseInfo::IsHidden; - else if (tag == "!throws") - return TestCaseInfo::Throws; - else if (tag == "!shouldfail") - return TestCaseInfo::ShouldFail; - else if (tag == "!mayfail") - return TestCaseInfo::MayFail; - else - return TestCaseInfo::None; -} -inline bool isReservedTag(std::string const& tag) -{ - return parseSpecialTag(tag) == TestCaseInfo::None && tag.size() > 0 && !isalnum(tag[0]); -} -inline void enforceNotReservedTag(std::string const& tag, SourceLineInfo const& _lineInfo) -{ - if (isReservedTag(tag)) - { - { - Colour colourGuard(Colour::Red); - Catch::cerr() - << "Tag name [" << tag << "] not allowed.\n" - << "Tag names starting with non alpha-numeric characters are reserved\n"; - } - { - Colour colourGuard(Colour::FileName); - Catch::cerr() << _lineInfo << std::endl; - } - exit(1); - } -} - -TestCase makeTestCase(ITestCase* _testCase, - std::string const& _className, - std::string const& _name, - std::string const& _descOrTags, - SourceLineInfo const& _lineInfo) -{ - bool isHidden(startsWith(_name, "./")); // Legacy support - - // Parse out tags - std::set tags; - std::string desc, tag; - bool inTag = false; - for (std::size_t i = 0; i < _descOrTags.size(); ++i) - { - char c = _descOrTags[i]; - if (!inTag) - { - if (c == '[') - inTag = true; - else - desc += c; - } + inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { + if( startsWith( tag, '.' ) || + tag == "hide" || + tag == "!hide" ) + return TestCaseInfo::IsHidden; + else if( tag == "!throws" ) + return TestCaseInfo::Throws; + else if( tag == "!shouldfail" ) + return TestCaseInfo::ShouldFail; + else if( tag == "!mayfail" ) + return TestCaseInfo::MayFail; + else if( tag == "!nonportable" ) + return TestCaseInfo::NonPortable; else - { - if (c == ']') - { - TestCaseInfo::SpecialProperties prop = parseSpecialTag(tag); - if (prop == TestCaseInfo::IsHidden) - isHidden = true; - else if (prop == TestCaseInfo::None) - enforceNotReservedTag(tag, _lineInfo); - - tags.insert(tag); - tag.clear(); - inTag = false; - } - else - tag += c; + return TestCaseInfo::None; + } + inline bool isReservedTag( std::string const& tag ) { + return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] ); + } + inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { + if( isReservedTag( tag ) ) { + std::ostringstream ss; + ss << Colour(Colour::Red) + << "Tag name [" << tag << "] not allowed.\n" + << "Tag names starting with non alpha-numeric characters are reserved\n" + << Colour(Colour::FileName) + << _lineInfo << '\n'; + throw std::runtime_error(ss.str()); } } - if (isHidden) + + TestCase makeTestCase( ITestCase* _testCase, + std::string const& _className, + std::string const& _name, + std::string const& _descOrTags, + SourceLineInfo const& _lineInfo ) { - tags.insert("hide"); - tags.insert("."); + bool isHidden( startsWith( _name, "./" ) ); // Legacy support + + // Parse out tags + std::set tags; + std::string desc, tag; + bool inTag = false; + for( std::size_t i = 0; i < _descOrTags.size(); ++i ) { + char c = _descOrTags[i]; + if( !inTag ) { + if( c == '[' ) + inTag = true; + else + desc += c; + } + else { + if( c == ']' ) { + TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); + if( prop == TestCaseInfo::IsHidden ) + isHidden = true; + else if( prop == TestCaseInfo::None ) + enforceNotReservedTag( tag, _lineInfo ); + + tags.insert( tag ); + tag.clear(); + inTag = false; + } + else + tag += c; + } + } + if( isHidden ) { + tags.insert( "hide" ); + tags.insert( "." ); + } + + TestCaseInfo info( _name, _className, desc, tags, _lineInfo ); + return TestCase( _testCase, info ); } - TestCaseInfo info(_name, _className, desc, tags, _lineInfo); - return TestCase(_testCase, info); -} - -void setTags(TestCaseInfo& testCaseInfo, std::set const& tags) -{ - testCaseInfo.tags = tags; - testCaseInfo.lcaseTags.clear(); - - std::ostringstream oss; - for (std::set::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it) + void setTags( TestCaseInfo& testCaseInfo, std::set const& tags ) { - oss << "[" << *it << "]"; - std::string lcaseTag = toLower(*it); - testCaseInfo.properties = static_cast(testCaseInfo.properties | parseSpecialTag(lcaseTag)); - testCaseInfo.lcaseTags.insert(lcaseTag); + testCaseInfo.tags = tags; + testCaseInfo.lcaseTags.clear(); + + std::ostringstream oss; + for( std::set::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) { + oss << '[' << *it << ']'; + std::string lcaseTag = toLower( *it ); + testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); + testCaseInfo.lcaseTags.insert( lcaseTag ); + } + testCaseInfo.tagsAsString = oss.str(); } - testCaseInfo.tagsAsString = oss.str(); -} -TestCaseInfo::TestCaseInfo(std::string const& _name, - std::string const& _className, - std::string const& _description, - std::set const& _tags, - SourceLineInfo const& _lineInfo) - : name(_name), - className(_className), - description(_description), - lineInfo(_lineInfo), - properties(None) -{ - setTags(*this, _tags); -} + TestCaseInfo::TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::set const& _tags, + SourceLineInfo const& _lineInfo ) + : name( _name ), + className( _className ), + description( _description ), + lineInfo( _lineInfo ), + properties( None ) + { + setTags( *this, _tags ); + } -TestCaseInfo::TestCaseInfo(TestCaseInfo const& other) - : name(other.name), - className(other.className), - description(other.description), - tags(other.tags), - lcaseTags(other.lcaseTags), - tagsAsString(other.tagsAsString), - lineInfo(other.lineInfo), - properties(other.properties) -{ -} + TestCaseInfo::TestCaseInfo( TestCaseInfo const& other ) + : name( other.name ), + className( other.className ), + description( other.description ), + tags( other.tags ), + lcaseTags( other.lcaseTags ), + tagsAsString( other.tagsAsString ), + lineInfo( other.lineInfo ), + properties( other.properties ) + {} -bool TestCaseInfo::isHidden() const -{ - return (properties & IsHidden) != 0; -} -bool TestCaseInfo::throws() const -{ - return (properties & Throws) != 0; -} -bool TestCaseInfo::okToFail() const -{ - return (properties & (ShouldFail | MayFail)) != 0; -} -bool TestCaseInfo::expectedToFail() const -{ - return (properties & (ShouldFail)) != 0; -} + bool TestCaseInfo::isHidden() const { + return ( properties & IsHidden ) != 0; + } + bool TestCaseInfo::throws() const { + return ( properties & Throws ) != 0; + } + bool TestCaseInfo::okToFail() const { + return ( properties & (ShouldFail | MayFail ) ) != 0; + } + bool TestCaseInfo::expectedToFail() const { + return ( properties & (ShouldFail ) ) != 0; + } -TestCase::TestCase(ITestCase* testCase, TestCaseInfo const& info) : TestCaseInfo(info), test(testCase) {} + TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} -TestCase::TestCase(TestCase const& other) - : TestCaseInfo(other), - test(other.test) -{ -} + TestCase::TestCase( TestCase const& other ) + : TestCaseInfo( other ), + test( other.test ) + {} -TestCase TestCase::withName(std::string const& _newName) const -{ - TestCase other(*this); - other.name = _newName; - return other; -} + TestCase TestCase::withName( std::string const& _newName ) const { + TestCase other( *this ); + other.name = _newName; + return other; + } -void TestCase::swap(TestCase& other) -{ - test.swap(other.test); - name.swap(other.name); - className.swap(other.className); - description.swap(other.description); - tags.swap(other.tags); - lcaseTags.swap(other.lcaseTags); - tagsAsString.swap(other.tagsAsString); - std::swap(TestCaseInfo::properties, static_cast(other).properties); - std::swap(lineInfo, other.lineInfo); -} + void TestCase::swap( TestCase& other ) { + test.swap( other.test ); + name.swap( other.name ); + className.swap( other.className ); + description.swap( other.description ); + tags.swap( other.tags ); + lcaseTags.swap( other.lcaseTags ); + tagsAsString.swap( other.tagsAsString ); + std::swap( TestCaseInfo::properties, static_cast( other ).properties ); + std::swap( lineInfo, other.lineInfo ); + } -void TestCase::invoke() const -{ - test->invoke(); -} + void TestCase::invoke() const { + test->invoke(); + } -bool TestCase::operator==(TestCase const& other) const -{ - return test.get() == other.test.get() && - name == other.name && - className == other.className; -} + bool TestCase::operator == ( TestCase const& other ) const { + return test.get() == other.test.get() && + name == other.name && + className == other.className; + } -bool TestCase::operator<(TestCase const& other) const -{ - return name < other.name; -} -TestCase& TestCase::operator=(TestCase const& other) -{ - TestCase temp(other); - swap(temp); - return *this; -} + bool TestCase::operator < ( TestCase const& other ) const { + return name < other.name; + } + TestCase& TestCase::operator = ( TestCase const& other ) { + TestCase temp( other ); + swap( temp ); + return *this; + } -TestCaseInfo const& TestCase::getTestCaseInfo() const -{ - return *this; -} + TestCaseInfo const& TestCase::getTestCaseInfo() const + { + return *this; + } } // end namespace Catch @@ -8282,34 +8208,36 @@ TestCaseInfo const& TestCase::getTestCaseInfo() const namespace Catch { -Version::Version(unsigned int _majorVersion, - unsigned int _minorVersion, - unsigned int _patchNumber, - std::string const& _branchName, - unsigned int _buildNumber) - : majorVersion(_majorVersion), - minorVersion(_minorVersion), - patchNumber(_patchNumber), - branchName(_branchName), - buildNumber(_buildNumber) -{ -} + Version::Version + ( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + char const * const _branchName, + unsigned int _buildNumber ) + : majorVersion( _majorVersion ), + minorVersion( _minorVersion ), + patchNumber( _patchNumber ), + branchName( _branchName ), + buildNumber( _buildNumber ) + {} -std::ostream& operator<<(std::ostream& os, Version const& version) -{ - os << version.majorVersion << "." - << version.minorVersion << "." - << version.patchNumber; - - if (!version.branchName.empty()) - { - os << "-" << version.branchName - << "." << version.buildNumber; + std::ostream& operator << ( std::ostream& os, Version const& version ) { + os << version.majorVersion << '.' + << version.minorVersion << '.' + << version.patchNumber; + // branchName is never null -> 0th char is \0 if it is empty + if (version.branchName[0]) { + os << '-' << version.branchName + << '.' << version.buildNumber; + } + return os; + } + + inline Version libraryVersion() { + static Version version( 1, 9, 0, "", 0 ); + return version; } - return os; -} -Version libraryVersion(1, 3, 2, "", 0); } // #included from: catch_message.hpp @@ -8317,36 +8245,35 @@ Version libraryVersion(1, 3, 2, "", 0); namespace Catch { -MessageInfo::MessageInfo(std::string const& _macroName, - SourceLineInfo const& _lineInfo, - ResultWas::OfType _type) - : macroName(_macroName), - lineInfo(_lineInfo), - type(_type), - sequence(++globalCount) -{ -} + MessageInfo::MessageInfo( std::string const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ) + : macroName( _macroName ), + lineInfo( _lineInfo ), + type( _type ), + sequence( ++globalCount ) + {} -// This may need protecting if threading support is added -unsigned int MessageInfo::globalCount = 0; + // This may need protecting if threading support is added + unsigned int MessageInfo::globalCount = 0; -//////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////// -ScopedMessage::ScopedMessage(MessageBuilder const& builder) - : m_info(builder.m_info) -{ - m_info.message = builder.m_stream.str(); - getResultCapture().pushScopedMessage(m_info); -} -ScopedMessage::ScopedMessage(ScopedMessage const& other) - : m_info(other.m_info) -{ -} + ScopedMessage::ScopedMessage( MessageBuilder const& builder ) + : m_info( builder.m_info ) + { + m_info.message = builder.m_stream.str(); + getResultCapture().pushScopedMessage( m_info ); + } + ScopedMessage::ScopedMessage( ScopedMessage const& other ) + : m_info( other.m_info ) + {} -ScopedMessage::~ScopedMessage() -{ - getResultCapture().popScopedMessage(m_info); -} + ScopedMessage::~ScopedMessage() { + if ( !std::uncaught_exception() ){ + getResultCapture().popScopedMessage(m_info); + } + } } // end namespace Catch @@ -8356,136 +8283,122 @@ ScopedMessage::~ScopedMessage() // #included from: catch_legacy_reporter_adapter.h #define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED -namespace Catch { -// Deprecated -struct IReporter : IShared +namespace Catch { - virtual ~IReporter(); + // Deprecated + struct IReporter : IShared { + virtual ~IReporter(); - virtual bool shouldRedirectStdout() const = 0; + virtual bool shouldRedirectStdout() const = 0; - virtual void StartTesting() = 0; - virtual void EndTesting(Totals const& totals) = 0; - virtual void StartGroup(std::string const& groupName) = 0; - virtual void EndGroup(std::string const& groupName, Totals const& totals) = 0; - virtual void StartTestCase(TestCaseInfo const& testInfo) = 0; - virtual void EndTestCase(TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr) = 0; - virtual void StartSection(std::string const& sectionName, std::string const& description) = 0; - virtual void EndSection(std::string const& sectionName, Counts const& assertions) = 0; - virtual void NoAssertionsInSection(std::string const& sectionName) = 0; - virtual void NoAssertionsInTestCase(std::string const& testName) = 0; - virtual void Aborted() = 0; - virtual void Result(AssertionResult const& result) = 0; -}; + virtual void StartTesting() = 0; + virtual void EndTesting( Totals const& totals ) = 0; + virtual void StartGroup( std::string const& groupName ) = 0; + virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0; + virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0; + virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0; + virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0; + virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0; + virtual void NoAssertionsInSection( std::string const& sectionName ) = 0; + virtual void NoAssertionsInTestCase( std::string const& testName ) = 0; + virtual void Aborted() = 0; + virtual void Result( AssertionResult const& result ) = 0; + }; -class LegacyReporterAdapter : public SharedImpl -{ - public: - LegacyReporterAdapter(Ptr const& legacyReporter); - virtual ~LegacyReporterAdapter(); - - virtual ReporterPreferences getPreferences() const; - virtual void noMatchingTestCases(std::string const&); - virtual void testRunStarting(TestRunInfo const&); - virtual void testGroupStarting(GroupInfo const& groupInfo); - virtual void testCaseStarting(TestCaseInfo const& testInfo); - virtual void sectionStarting(SectionInfo const& sectionInfo); - virtual void assertionStarting(AssertionInfo const&); - virtual bool assertionEnded(AssertionStats const& assertionStats); - virtual void sectionEnded(SectionStats const& sectionStats); - virtual void testCaseEnded(TestCaseStats const& testCaseStats); - virtual void testGroupEnded(TestGroupStats const& testGroupStats); - virtual void testRunEnded(TestRunStats const& testRunStats); - virtual void skipTest(TestCaseInfo const&); - - private: - Ptr m_legacyReporter; -}; -} - -namespace Catch { -LegacyReporterAdapter::LegacyReporterAdapter(Ptr const& legacyReporter) - : m_legacyReporter(legacyReporter) -{ -} -LegacyReporterAdapter::~LegacyReporterAdapter() {} - -ReporterPreferences LegacyReporterAdapter::getPreferences() const -{ - ReporterPreferences prefs; - prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout(); - return prefs; -} - -void LegacyReporterAdapter::noMatchingTestCases(std::string const&) {} -void LegacyReporterAdapter::testRunStarting(TestRunInfo const&) -{ - m_legacyReporter->StartTesting(); -} -void LegacyReporterAdapter::testGroupStarting(GroupInfo const& groupInfo) -{ - m_legacyReporter->StartGroup(groupInfo.name); -} -void LegacyReporterAdapter::testCaseStarting(TestCaseInfo const& testInfo) -{ - m_legacyReporter->StartTestCase(testInfo); -} -void LegacyReporterAdapter::sectionStarting(SectionInfo const& sectionInfo) -{ - m_legacyReporter->StartSection(sectionInfo.name, sectionInfo.description); -} -void LegacyReporterAdapter::assertionStarting(AssertionInfo const&) -{ - // Not on legacy interface -} - -bool LegacyReporterAdapter::assertionEnded(AssertionStats const& assertionStats) -{ - if (assertionStats.assertionResult.getResultType() != ResultWas::Ok) + class LegacyReporterAdapter : public SharedImpl { - for (std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); - it != itEnd; - ++it) - { - if (it->type == ResultWas::Info) - { - ResultBuilder rb(it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal); - rb << it->message; - rb.setResultType(ResultWas::Info); - AssertionResult result = rb.build(); - m_legacyReporter->Result(result); + public: + LegacyReporterAdapter( Ptr const& legacyReporter ); + virtual ~LegacyReporterAdapter(); + + virtual ReporterPreferences getPreferences() const; + virtual void noMatchingTestCases( std::string const& ); + virtual void testRunStarting( TestRunInfo const& ); + virtual void testGroupStarting( GroupInfo const& groupInfo ); + virtual void testCaseStarting( TestCaseInfo const& testInfo ); + virtual void sectionStarting( SectionInfo const& sectionInfo ); + virtual void assertionStarting( AssertionInfo const& ); + virtual bool assertionEnded( AssertionStats const& assertionStats ); + virtual void sectionEnded( SectionStats const& sectionStats ); + virtual void testCaseEnded( TestCaseStats const& testCaseStats ); + virtual void testGroupEnded( TestGroupStats const& testGroupStats ); + virtual void testRunEnded( TestRunStats const& testRunStats ); + virtual void skipTest( TestCaseInfo const& ); + + private: + Ptr m_legacyReporter; + }; +} + +namespace Catch +{ + LegacyReporterAdapter::LegacyReporterAdapter( Ptr const& legacyReporter ) + : m_legacyReporter( legacyReporter ) + {} + LegacyReporterAdapter::~LegacyReporterAdapter() {} + + ReporterPreferences LegacyReporterAdapter::getPreferences() const { + ReporterPreferences prefs; + prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout(); + return prefs; + } + + void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {} + void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) { + m_legacyReporter->StartTesting(); + } + void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) { + m_legacyReporter->StartGroup( groupInfo.name ); + } + void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) { + m_legacyReporter->StartTestCase( testInfo ); + } + void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) { + m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description ); + } + void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) { + // Not on legacy interface + } + + bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) { + if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { + for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); + it != itEnd; + ++it ) { + if( it->type == ResultWas::Info ) { + ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal ); + rb << it->message; + rb.setResultType( ResultWas::Info ); + AssertionResult result = rb.build(); + m_legacyReporter->Result( result ); + } } } + m_legacyReporter->Result( assertionStats.assertionResult ); + return true; + } + void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) { + if( sectionStats.missingAssertions ) + m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name ); + m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions ); + } + void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) { + m_legacyReporter->EndTestCase + ( testCaseStats.testInfo, + testCaseStats.totals, + testCaseStats.stdOut, + testCaseStats.stdErr ); + } + void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) { + if( testGroupStats.aborting ) + m_legacyReporter->Aborted(); + m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals ); + } + void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) { + m_legacyReporter->EndTesting( testRunStats.totals ); + } + void LegacyReporterAdapter::skipTest( TestCaseInfo const& ) { } - m_legacyReporter->Result(assertionStats.assertionResult); - return true; -} -void LegacyReporterAdapter::sectionEnded(SectionStats const& sectionStats) -{ - if (sectionStats.missingAssertions) - m_legacyReporter->NoAssertionsInSection(sectionStats.sectionInfo.name); - m_legacyReporter->EndSection(sectionStats.sectionInfo.name, sectionStats.assertions); -} -void LegacyReporterAdapter::testCaseEnded(TestCaseStats const& testCaseStats) -{ - m_legacyReporter->EndTestCase(testCaseStats.testInfo, - testCaseStats.totals, - testCaseStats.stdOut, - testCaseStats.stdErr); -} -void LegacyReporterAdapter::testGroupEnded(TestGroupStats const& testGroupStats) -{ - if (testGroupStats.aborting) - m_legacyReporter->Aborted(); - m_legacyReporter->EndGroup(testGroupStats.groupInfo.name, testGroupStats.totals); -} -void LegacyReporterAdapter::testRunEnded(TestRunStats const& testRunStats) -{ - m_legacyReporter->EndTesting(testRunStats.totals); -} -void LegacyReporterAdapter::skipTest(TestCaseInfo const&) -{ -} } // #included from: catch_timer.hpp @@ -8496,53 +8409,48 @@ void LegacyReporterAdapter::skipTest(TestCaseInfo const&) #endif #ifdef CATCH_PLATFORM_WINDOWS -#include + #else + #include + #endif namespace Catch { -namespace { + namespace { #ifdef CATCH_PLATFORM_WINDOWS -uint64_t getCurrentTicks() -{ - static uint64_t hz = 0, hzo = 0; - if (!hz) - { - QueryPerformanceFrequency(reinterpret_cast(&hz)); - QueryPerformanceCounter(reinterpret_cast(&hzo)); - } - uint64_t t; - QueryPerformanceCounter(reinterpret_cast(&t)); - return ((t - hzo) * 1000000) / hz; -} + uint64_t getCurrentTicks() { + static uint64_t hz=0, hzo=0; + if (!hz) { + QueryPerformanceFrequency( reinterpret_cast( &hz ) ); + QueryPerformanceCounter( reinterpret_cast( &hzo ) ); + } + uint64_t t; + QueryPerformanceCounter( reinterpret_cast( &t ) ); + return ((t-hzo)*1000000)/hz; + } #else -uint64_t getCurrentTicks() -{ - timeval t; - gettimeofday(&t, CATCH_NULL); - return static_cast(t.tv_sec) * 1000000ull + static_cast(t.tv_usec); -} + uint64_t getCurrentTicks() { + timeval t; + gettimeofday(&t,CATCH_NULL); + return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); + } #endif -} + } -void Timer::start() -{ - m_ticks = getCurrentTicks(); -} -unsigned int Timer::getElapsedMicroseconds() const -{ - return static_cast(getCurrentTicks() - m_ticks); -} -unsigned int Timer::getElapsedMilliseconds() const -{ - return static_cast(getElapsedMicroseconds() / 1000); -} -double Timer::getElapsedSeconds() const -{ - return getElapsedMicroseconds() / 1000000.0; -} + void Timer::start() { + m_ticks = getCurrentTicks(); + } + unsigned int Timer::getElapsedMicroseconds() const { + return static_cast(getCurrentTicks() - m_ticks); + } + unsigned int Timer::getElapsedMilliseconds() const { + return static_cast(getElapsedMicroseconds()/1000); + } + double Timer::getElapsedSeconds() const { + return getElapsedMicroseconds()/1000000.0; + } } // namespace Catch @@ -8552,120 +8460,109 @@ double Timer::getElapsedSeconds() const // #included from: catch_common.hpp #define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED +#include +#include + namespace Catch { -bool startsWith(std::string const& s, std::string const& prefix) -{ - return s.size() >= prefix.size() && s.substr(0, prefix.size()) == prefix; -} -bool endsWith(std::string const& s, std::string const& suffix) -{ - return s.size() >= suffix.size() && s.substr(s.size() - suffix.size(), suffix.size()) == suffix; -} -bool contains(std::string const& s, std::string const& infix) -{ - return s.find(infix) != std::string::npos; -} -void toLowerInPlace(std::string& s) -{ - std::transform(s.begin(), s.end(), s.begin(), ::tolower); -} -std::string toLower(std::string const& s) -{ - std::string lc = s; - toLowerInPlace(lc); - return lc; -} -std::string trim(std::string const& str) -{ - static char const* whitespaceChars = "\n\r\t "; - std::string::size_type start = str.find_first_not_of(whitespaceChars); - std::string::size_type end = str.find_last_not_of(whitespaceChars); - - return start != std::string::npos ? str.substr(start, 1 + end - start) : ""; -} - -bool replaceInPlace(std::string& str, std::string const& replaceThis, std::string const& withThis) -{ - bool replaced = false; - std::size_t i = str.find(replaceThis); - while (i != std::string::npos) - { - replaced = true; - str = str.substr(0, i) + withThis + str.substr(i + replaceThis.size()); - if (i < str.size() - withThis.size()) - i = str.find(replaceThis, i + withThis.size()); - else - i = std::string::npos; + bool startsWith( std::string const& s, std::string const& prefix ) { + return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); } - return replaced; -} + bool startsWith( std::string const& s, char prefix ) { + return !s.empty() && s[0] == prefix; + } + bool endsWith( std::string const& s, std::string const& suffix ) { + return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); + } + bool endsWith( std::string const& s, char suffix ) { + return !s.empty() && s[s.size()-1] == suffix; + } + bool contains( std::string const& s, std::string const& infix ) { + return s.find( infix ) != std::string::npos; + } + char toLowerCh(char c) { + return static_cast( std::tolower( c ) ); + } + void toLowerInPlace( std::string& s ) { + std::transform( s.begin(), s.end(), s.begin(), toLowerCh ); + } + std::string toLower( std::string const& s ) { + std::string lc = s; + toLowerInPlace( lc ); + return lc; + } + std::string trim( std::string const& str ) { + static char const* whitespaceChars = "\n\r\t "; + std::string::size_type start = str.find_first_not_of( whitespaceChars ); + std::string::size_type end = str.find_last_not_of( whitespaceChars ); -pluralise::pluralise(std::size_t count, std::string const& label) - : m_count(count), - m_label(label) -{ -} + return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string(); + } -std::ostream& operator<<(std::ostream& os, pluralise const& pluraliser) -{ - os << pluraliser.m_count << " " << pluraliser.m_label; - if (pluraliser.m_count != 1) - os << "s"; - return os; -} + bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { + bool replaced = false; + std::size_t i = str.find( replaceThis ); + while( i != std::string::npos ) { + replaced = true; + str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() ); + if( i < str.size()-withThis.size() ) + i = str.find( replaceThis, i+withThis.size() ); + else + i = std::string::npos; + } + return replaced; + } -SourceLineInfo::SourceLineInfo() : line(0) {} -SourceLineInfo::SourceLineInfo(char const* _file, std::size_t _line) - : file(_file), - line(_line) -{ -} -SourceLineInfo::SourceLineInfo(SourceLineInfo const& other) - : file(other.file), - line(other.line) -{ -} -bool SourceLineInfo::empty() const -{ - return file.empty(); -} -bool SourceLineInfo::operator==(SourceLineInfo const& other) const -{ - return line == other.line && file == other.file; -} -bool SourceLineInfo::operator<(SourceLineInfo const& other) const -{ - return line < other.line || (line == other.line && file < other.file); -} + pluralise::pluralise( std::size_t count, std::string const& label ) + : m_count( count ), + m_label( label ) + {} -void seedRng(IConfig const& config) -{ - if (config.rngSeed() != 0) - std::srand(config.rngSeed()); -} -unsigned int rngSeed() -{ - return getCurrentContext().getConfig()->rngSeed(); -} + std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { + os << pluraliser.m_count << ' ' << pluraliser.m_label; + if( pluraliser.m_count != 1 ) + os << 's'; + return os; + } -std::ostream& operator<<(std::ostream& os, SourceLineInfo const& info) -{ + SourceLineInfo::SourceLineInfo() : file(""), line( 0 ){} + SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) + : file( _file ), + line( _line ) + {} + bool SourceLineInfo::empty() const { + return file[0] == '\0'; + } + bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const { + return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); + } + bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const { + return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0)); + } + + void seedRng( IConfig const& config ) { + if( config.rngSeed() != 0 ) + std::srand( config.rngSeed() ); + } + unsigned int rngSeed() { + return getCurrentContext().getConfig()->rngSeed(); + } + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { #ifndef __GNUG__ - os << info.file << "(" << info.line << ")"; + os << info.file << '(' << info.line << ')'; #else - os << info.file << ":" << info.line; + os << info.file << ':' << info.line; #endif - return os; -} + return os; + } -void throwLogicError(std::string const& message, SourceLineInfo const& locationInfo) -{ - std::ostringstream oss; - oss << locationInfo << ": Internal Catch error: '" << message << "'"; - if (alwaysTrue()) - throw std::logic_error(oss.str()); -} + void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) { + std::ostringstream oss; + oss << locationInfo << ": Internal Catch error: '" << message << '\''; + if( alwaysTrue() ) + throw std::logic_error( oss.str() ); + } } // #included from: catch_section.hpp @@ -8673,136 +8570,154 @@ void throwLogicError(std::string const& message, SourceLineInfo const& locationI namespace Catch { -SectionInfo::SectionInfo(SourceLineInfo const& _lineInfo, - std::string const& _name, - std::string const& _description) - : name(_name), - description(_description), - lineInfo(_lineInfo) -{ -} + SectionInfo::SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name, + std::string const& _description ) + : name( _name ), + description( _description ), + lineInfo( _lineInfo ) + {} -Section::Section(SectionInfo const& info) - : m_info(info), - m_sectionIncluded(getResultCapture().sectionStarted(m_info, m_assertions)) -{ - m_timer.start(); -} - -Section::~Section() -{ - if (m_sectionIncluded) + Section::Section( SectionInfo const& info ) + : m_info( info ), + m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) { - SectionEndInfo endInfo(m_info, m_assertions, m_timer.getElapsedSeconds()); - if (std::uncaught_exception()) - getResultCapture().sectionEndedEarly(endInfo); - else - getResultCapture().sectionEnded(endInfo); + m_timer.start(); } -} -// This indicates whether the section should be executed or not -Section::operator bool() const -{ - return m_sectionIncluded; -} + Section::~Section() { + if( m_sectionIncluded ) { + SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); + if( std::uncaught_exception() ) + getResultCapture().sectionEndedEarly( endInfo ); + else + getResultCapture().sectionEnded( endInfo ); + } + } + + // This indicates whether the section should be executed or not + Section::operator bool() const { + return m_sectionIncluded; + } } // end namespace Catch // #included from: catch_debugger.hpp #define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED -#include - #ifdef CATCH_PLATFORM_MAC -#include -#include -#include -#include -#include + #include + #include + #include + #include + #include -namespace Catch { + namespace Catch{ -// The following function is taken directly from the following technical note: -// http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html + // The following function is taken directly from the following technical note: + // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html -// Returns true if the current process is being debugged (either -// running under the debugger or has a debugger attached post facto). -bool isDebuggerActive() -{ + // Returns true if the current process is being debugged (either + // running under the debugger or has a debugger attached post facto). + bool isDebuggerActive(){ - int mib[4]; - struct kinfo_proc info; - size_t size; + int mib[4]; + struct kinfo_proc info; + size_t size; - // Initialize the flags so that, if sysctl fails for some bizarre - // reason, we get a predictable result. + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. - info.kp_proc.p_flag = 0; + info.kp_proc.p_flag = 0; - // Initialize mib, which tells sysctl the info we want, in this case - // we're looking for information about a specific process ID. + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; - mib[3] = getpid(); + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); - // Call sysctl. + // Call sysctl. - size = sizeof(info); - if (sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, CATCH_NULL, 0) != 0) - { - Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" - << std::endl; - return false; - } + size = sizeof(info); + if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, CATCH_NULL, 0) != 0 ) { + Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; + return false; + } - // We're being debugged if the P_TRACED flag is set. + // We're being debugged if the P_TRACED flag is set. - return ((info.kp_proc.p_flag & P_TRACED) != 0); -} -} // namespace Catch + return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); + } + } // namespace Catch +#elif defined(CATCH_PLATFORM_LINUX) + #include + #include + + namespace Catch{ + // The standard POSIX way of detecting a debugger is to attempt to + // ptrace() the process, but this needs to be done from a child and not + // this process itself to still allow attaching to this process later + // if wanted, so is rather heavy. Under Linux we have the PID of the + // "debugger" (which doesn't need to be gdb, of course, it could also + // be strace, for example) in /proc/$PID/status, so just get it from + // there instead. + bool isDebuggerActive(){ + // Libstdc++ has a bug, where std::ifstream sets errno to 0 + // This way our users can properly assert over errno values + ErrnoGuard guard; + std::ifstream in("/proc/self/status"); + for( std::string line; std::getline(in, line); ) { + static const int PREFIX_LEN = 11; + if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { + // We're traced if the PID is not 0 and no other PID starts + // with 0 digit, so it's enough to check for just a single + // character. + return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; + } + } + + return false; + } + } // namespace Catch #elif defined(_MSC_VER) -extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); -namespace Catch { -bool isDebuggerActive() -{ - return IsDebuggerPresent() != 0; -} -} + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } #elif defined(__MINGW32__) -extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); -namespace Catch { -bool isDebuggerActive() -{ - return IsDebuggerPresent() != 0; -} -} + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } #else -namespace Catch { -inline bool isDebuggerActive() { return false; } -} + namespace Catch { + inline bool isDebuggerActive() { return false; } + } #endif // Platform #ifdef CATCH_PLATFORM_WINDOWS -extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char*); -namespace Catch { -void writeToDebugConsole(std::string const& text) -{ - ::OutputDebugStringA(text.c_str()); -} -} + + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + ::OutputDebugStringA( text.c_str() ); + } + } #else -namespace Catch { -void writeToDebugConsole(std::string const& text) -{ - // !TBD: Need a version for Mac/ XCode and other IDEs - Catch::cout() << text; -} -} + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + // !TBD: Need a version for Mac/ XCode and other IDEs + Catch::cout() << text; + } + } #endif // Platform // #included from: catch_tostring.hpp @@ -8812,225 +8727,197 @@ namespace Catch { namespace Detail { -const std::string unprintableString = "{?}"; + const std::string unprintableString = "{?}"; -namespace { -const int hexThreshold = 255; + namespace { + const int hexThreshold = 255; -struct Endianness -{ - enum Arch - { - Big, - Little - }; + struct Endianness { + enum Arch { Big, Little }; - static Arch which() - { - union _ - { - int asInt; - char asChar[sizeof(int)]; - } u; + static Arch which() { + union _{ + int asInt; + char asChar[sizeof (int)]; + } u; - u.asInt = 1; - return (u.asChar[sizeof(int) - 1] == 1) ? Big : Little; - } -}; -} - -std::string rawMemoryToString(const void* object, std::size_t size) -{ - // Reverse order for little endian architectures - int i = 0, end = static_cast(size), inc = 1; - if (Endianness::which() == Endianness::Little) - { - i = end - 1; - end = inc = -1; - } - - unsigned char const* bytes = static_cast(object); - std::ostringstream os; - os << "0x" << std::setfill('0') << std::hex; - for (; i != end; i += inc) - os << std::setw(2) << static_cast(bytes[i]); - return os.str(); -} -} - -std::string toString(std::string const& value) -{ - std::string s = value; - if (getCurrentContext().getConfig()->showInvisibles()) - { - for (size_t i = 0; i < s.size(); ++i) - { - std::string subs; - switch (s[i]) - { - case '\n': - subs = "\\n"; - break; - case '\t': - subs = "\\t"; - break; - default: - break; + u.asInt = 1; + return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little; } - if (!subs.empty()) - { - s = s.substr(0, i) + subs + s.substr(i + 1); + }; + } + + std::string rawMemoryToString( const void *object, std::size_t size ) + { + // Reverse order for little endian architectures + int i = 0, end = static_cast( size ), inc = 1; + if( Endianness::which() == Endianness::Little ) { + i = end-1; + end = inc = -1; + } + + unsigned char const *bytes = static_cast(object); + std::ostringstream os; + os << "0x" << std::setfill('0') << std::hex; + for( ; i != end; i += inc ) + os << std::setw(2) << static_cast(bytes[i]); + return os.str(); + } +} + +std::string toString( std::string const& value ) { + std::string s = value; + if( getCurrentContext().getConfig()->showInvisibles() ) { + for(size_t i = 0; i < s.size(); ++i ) { + std::string subs; + switch( s[i] ) { + case '\n': subs = "\\n"; break; + case '\t': subs = "\\t"; break; + default: break; + } + if( !subs.empty() ) { + s = s.substr( 0, i ) + subs + s.substr( i+1 ); ++i; } } } - return "\"" + s + "\""; + return '"' + s + '"'; } -std::string toString(std::wstring const& value) -{ +std::string toString( std::wstring const& value ) { std::string s; - s.reserve(value.size()); - for (size_t i = 0; i < value.size(); ++i) - s += value[i] <= 0xff ? static_cast(value[i]) : '?'; - return Catch::toString(s); + s.reserve( value.size() ); + for(size_t i = 0; i < value.size(); ++i ) + s += value[i] <= 0xff ? static_cast( value[i] ) : '?'; + return Catch::toString( s ); } -std::string toString(const char* const value) -{ - return value ? Catch::toString(std::string(value)) : std::string("{null string}"); +std::string toString( const char* const value ) { + return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" ); } -std::string toString(char* const value) -{ - return Catch::toString(static_cast(value)); +std::string toString( char* const value ) { + return Catch::toString( static_cast( value ) ); } -std::string toString(const wchar_t* const value) +std::string toString( const wchar_t* const value ) { - return value ? Catch::toString(std::wstring(value)) : std::string("{null string}"); + return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" ); } -std::string toString(wchar_t* const value) +std::string toString( wchar_t* const value ) { - return Catch::toString(static_cast(value)); + return Catch::toString( static_cast( value ) ); } -std::string toString(int value) -{ +std::string toString( int value ) { std::ostringstream oss; oss << value; - if (value > Detail::hexThreshold) - oss << " (0x" << std::hex << value << ")"; + if( value > Detail::hexThreshold ) + oss << " (0x" << std::hex << value << ')'; return oss.str(); } -std::string toString(unsigned long value) -{ +std::string toString( unsigned long value ) { std::ostringstream oss; oss << value; - if (value > Detail::hexThreshold) - oss << " (0x" << std::hex << value << ")"; + if( value > Detail::hexThreshold ) + oss << " (0x" << std::hex << value << ')'; return oss.str(); } -std::string toString(unsigned int value) -{ - return Catch::toString(static_cast(value)); +std::string toString( unsigned int value ) { + return Catch::toString( static_cast( value ) ); } -template -std::string fpToString(T value, int precision) -{ +template +std::string fpToString( T value, int precision ) { std::ostringstream oss; - oss << std::setprecision(precision) + oss << std::setprecision( precision ) << std::fixed << value; std::string d = oss.str(); - std::size_t i = d.find_last_not_of('0'); - if (i != std::string::npos && i != d.size() - 1) - { - if (d[i] == '.') + std::size_t i = d.find_last_not_of( '0' ); + if( i != std::string::npos && i != d.size()-1 ) { + if( d[i] == '.' ) i++; - d = d.substr(0, i + 1); + d = d.substr( 0, i+1 ); } return d; } -std::string toString(const double value) -{ - return fpToString(value, 10); +std::string toString( const double value ) { + return fpToString( value, 10 ); } -std::string toString(const float value) -{ - return fpToString(value, 5) + "f"; +std::string toString( const float value ) { + return fpToString( value, 5 ) + 'f'; } -std::string toString(bool value) -{ +std::string toString( bool value ) { return value ? "true" : "false"; } -std::string toString(char value) -{ - return value < ' ' - ? toString(static_cast(value)) - : Detail::makeString(value); +std::string toString( char value ) { + if ( value == '\r' ) + return "'\\r'"; + if ( value == '\f' ) + return "'\\f'"; + if ( value == '\n' ) + return "'\\n'"; + if ( value == '\t' ) + return "'\\t'"; + if ( '\0' <= value && value < ' ' ) + return toString( static_cast( value ) ); + char chstr[] = "' '"; + chstr[1] = value; + return chstr; } -std::string toString(signed char value) -{ - return toString(static_cast(value)); +std::string toString( signed char value ) { + return toString( static_cast( value ) ); } -std::string toString(unsigned char value) -{ - return toString(static_cast(value)); +std::string toString( unsigned char value ) { + return toString( static_cast( value ) ); } #ifdef CATCH_CONFIG_CPP11_LONG_LONG -std::string toString(long long value) -{ +std::string toString( long long value ) { std::ostringstream oss; oss << value; - if (value > Detail::hexThreshold) - oss << " (0x" << std::hex << value << ")"; + if( value > Detail::hexThreshold ) + oss << " (0x" << std::hex << value << ')'; return oss.str(); } -std::string toString(unsigned long long value) -{ +std::string toString( unsigned long long value ) { std::ostringstream oss; oss << value; - if (value > Detail::hexThreshold) - oss << " (0x" << std::hex << value << ")"; + if( value > Detail::hexThreshold ) + oss << " (0x" << std::hex << value << ')'; return oss.str(); } #endif #ifdef CATCH_CONFIG_CPP11_NULLPTR -std::string toString(std::nullptr_t) -{ +std::string toString( std::nullptr_t ) { return "nullptr"; } #endif #ifdef __OBJC__ -std::string toString(NSString const* const& nsstring) -{ - if (!nsstring) - return "nil"; - return "@" + toString([nsstring UTF8String]); -} -std::string toString(NSString* CATCH_ARC_STRONG const& nsstring) -{ - if (!nsstring) - return "nil"; - return "@" + toString([nsstring UTF8String]); -} -std::string toString(NSObject* const& nsObject) -{ - return toString([nsObject description]); -} + std::string toString( NSString const * const& nsstring ) { + if( !nsstring ) + return "nil"; + return "@" + toString([nsstring UTF8String]); + } + std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) { + if( !nsstring ) + return "nil"; + return "@" + toString([nsstring UTF8String]); + } + std::string toString( NSObject* const& nsObject ) { + return toString( [nsObject description] ); + } #endif } // end namespace Catch @@ -9040,409 +8927,424 @@ std::string toString(NSObject* const& nsObject) namespace Catch { -std::string capturedExpressionWithSecondArgument(std::string const& capturedExpression, std::string const& secondArg) -{ - return secondArg.empty() || secondArg == "\"\"" - ? capturedExpression - : capturedExpression + ", " + secondArg; -} -ResultBuilder::ResultBuilder(char const* macroName, - SourceLineInfo const& lineInfo, - char const* capturedExpression, - ResultDisposition::Flags resultDisposition, - char const* secondArg) - : m_assertionInfo(macroName, lineInfo, capturedExpressionWithSecondArgument(capturedExpression, secondArg), resultDisposition), - m_shouldDebugBreak(false), - m_shouldThrow(false) -{ -} - -ResultBuilder& ResultBuilder::setResultType(ResultWas::OfType result) -{ - m_data.resultType = result; - return *this; -} -ResultBuilder& ResultBuilder::setResultType(bool result) -{ - m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed; - return *this; -} -ResultBuilder& ResultBuilder::setLhs(std::string const& lhs) -{ - m_exprComponents.lhs = lhs; - return *this; -} -ResultBuilder& ResultBuilder::setRhs(std::string const& rhs) -{ - m_exprComponents.rhs = rhs; - return *this; -} -ResultBuilder& ResultBuilder::setOp(std::string const& op) -{ - m_exprComponents.op = op; - return *this; -} - -void ResultBuilder::endExpression() -{ - m_exprComponents.testFalse = isFalseTest(m_assertionInfo.resultDisposition); - captureExpression(); -} - -void ResultBuilder::useActiveException(ResultDisposition::Flags resultDisposition) -{ - m_assertionInfo.resultDisposition = resultDisposition; - m_stream.oss << Catch::translateActiveException(); - captureResult(ResultWas::ThrewException); -} - -void ResultBuilder::captureResult(ResultWas::OfType resultType) -{ - setResultType(resultType); - captureExpression(); -} -void ResultBuilder::captureExpectedException(std::string const& expectedMessage) -{ - if (expectedMessage.empty()) - captureExpectedException(Matchers::Impl::Generic::AllOf()); - else - captureExpectedException(Matchers::Equals(expectedMessage)); -} - -void ResultBuilder::captureExpectedException(Matchers::Impl::Matcher const& matcher) -{ - - assert(m_exprComponents.testFalse == false); - AssertionResultData data = m_data; - data.resultType = ResultWas::Ok; - data.reconstructedExpression = m_assertionInfo.capturedExpression; - - std::string actualMessage = Catch::translateActiveException(); - if (!matcher.match(actualMessage)) - { - data.resultType = ResultWas::ExpressionFailed; - data.reconstructedExpression = actualMessage; + std::string capturedExpressionWithSecondArgument( std::string const& capturedExpression, std::string const& secondArg ) { + return secondArg.empty() || secondArg == "\"\"" + ? capturedExpression + : capturedExpression + ", " + secondArg; } - AssertionResult result(m_assertionInfo, data); - handleResult(result); -} + ResultBuilder::ResultBuilder( char const* macroName, + SourceLineInfo const& lineInfo, + char const* capturedExpression, + ResultDisposition::Flags resultDisposition, + char const* secondArg ) + : m_assertionInfo( macroName, lineInfo, capturedExpressionWithSecondArgument( capturedExpression, secondArg ), resultDisposition ), + m_shouldDebugBreak( false ), + m_shouldThrow( false ), + m_guardException( false ) + {} -void ResultBuilder::captureExpression() -{ - AssertionResult result = build(); - handleResult(result); -} -void ResultBuilder::handleResult(AssertionResult const& result) -{ - getResultCapture().assertionEnded(result); - - if (!result.isOk()) - { - if (getCurrentContext().getConfig()->shouldDebugBreak()) - m_shouldDebugBreak = true; - if (getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal)) - m_shouldThrow = true; + ResultBuilder::~ResultBuilder() { +#if defined(CATCH_CONFIG_FAST_COMPILE) + if ( m_guardException ) { + m_stream.oss << "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; + captureResult( ResultWas::ThrewException ); + getCurrentContext().getResultCapture()->exceptionEarlyReported(); + } +#endif } -} -void ResultBuilder::react() -{ - if (m_shouldThrow) - throw Catch::TestFailureException(); -} -bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; } -bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); } + ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) { + m_data.resultType = result; + return *this; + } + ResultBuilder& ResultBuilder::setResultType( bool result ) { + m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed; + return *this; + } -AssertionResult ResultBuilder::build() const -{ - assert(m_data.resultType != ResultWas::Unknown); + void ResultBuilder::endExpression( DecomposedExpression const& expr ) { + AssertionResult result = build( expr ); + handleResult( result ); + } - AssertionResultData data = m_data; + void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) { + m_assertionInfo.resultDisposition = resultDisposition; + m_stream.oss << Catch::translateActiveException(); + captureResult( ResultWas::ThrewException ); + } - // Flip bool results if testFalse is set - if (m_exprComponents.testFalse) - { - if (data.resultType == ResultWas::Ok) + void ResultBuilder::captureResult( ResultWas::OfType resultType ) { + setResultType( resultType ); + captureExpression(); + } + + void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) { + if( expectedMessage.empty() ) + captureExpectedException( Matchers::Impl::MatchAllOf() ); + else + captureExpectedException( Matchers::Equals( expectedMessage ) ); + } + + void ResultBuilder::captureExpectedException( Matchers::Impl::MatcherBase const& matcher ) { + + assert( !isFalseTest( m_assertionInfo.resultDisposition ) ); + AssertionResultData data = m_data; + data.resultType = ResultWas::Ok; + data.reconstructedExpression = m_assertionInfo.capturedExpression; + + std::string actualMessage = Catch::translateActiveException(); + if( !matcher.match( actualMessage ) ) { data.resultType = ResultWas::ExpressionFailed; - else if (data.resultType == ResultWas::ExpressionFailed) - data.resultType = ResultWas::Ok; + data.reconstructedExpression = actualMessage; + } + AssertionResult result( m_assertionInfo, data ); + handleResult( result ); } - data.message = m_stream.oss.str(); - data.reconstructedExpression = reconstructExpression(); - if (m_exprComponents.testFalse) - { - if (m_exprComponents.op == "") - data.reconstructedExpression = "!" + data.reconstructedExpression; - else - data.reconstructedExpression = "!(" + data.reconstructedExpression + ")"; + void ResultBuilder::captureExpression() { + AssertionResult result = build(); + handleResult( result ); } - return AssertionResult(m_assertionInfo, data); -} -std::string ResultBuilder::reconstructExpression() const -{ - if (m_exprComponents.op == "") - return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.op + m_exprComponents.lhs; - else if (m_exprComponents.op == "matches") - return m_exprComponents.lhs + " " + m_exprComponents.rhs; - else if (m_exprComponents.op != "!") + + void ResultBuilder::handleResult( AssertionResult const& result ) { - if (m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 && - m_exprComponents.lhs.find("\n") == std::string::npos && - m_exprComponents.rhs.find("\n") == std::string::npos) - return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs; - else - return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs; + getResultCapture().assertionEnded( result ); + + if( !result.isOk() ) { + if( getCurrentContext().getConfig()->shouldDebugBreak() ) + m_shouldDebugBreak = true; + if( getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal) ) + m_shouldThrow = true; + } + } + + void ResultBuilder::react() { +#if defined(CATCH_CONFIG_FAST_COMPILE) + if (m_shouldDebugBreak) { + /////////////////////////////////////////////////////////////////// + // To inspect the state during test, you need to go one level up the callstack + // To go back to the test and change execution, jump over the throw statement + /////////////////////////////////////////////////////////////////// + CATCH_BREAK_INTO_DEBUGGER(); + } +#endif + if( m_shouldThrow ) + throw Catch::TestFailureException(); + } + + bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; } + bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); } + + AssertionResult ResultBuilder::build() const + { + return build( *this ); + } + + // CAVEAT: The returned AssertionResult stores a pointer to the argument expr, + // a temporary DecomposedExpression, which in turn holds references to + // operands, possibly temporary as well. + // It should immediately be passed to handleResult; if the expression + // needs to be reported, its string expansion must be composed before + // the temporaries are destroyed. + AssertionResult ResultBuilder::build( DecomposedExpression const& expr ) const + { + assert( m_data.resultType != ResultWas::Unknown ); + AssertionResultData data = m_data; + + // Flip bool results if FalseTest flag is set + if( isFalseTest( m_assertionInfo.resultDisposition ) ) { + data.negate( expr.isBinaryExpression() ); + } + + data.message = m_stream.oss.str(); + data.decomposedExpression = &expr; // for lazy reconstruction + return AssertionResult( m_assertionInfo, data ); + } + + void ResultBuilder::reconstructExpression( std::string& dest ) const { + dest = m_assertionInfo.capturedExpression; + } + + void ResultBuilder::setExceptionGuard() { + m_guardException = true; + } + void ResultBuilder::unsetExceptionGuard() { + m_guardException = false; } - else - return "{can't expand - use " + m_assertionInfo.macroName + "_FALSE( " + m_assertionInfo.capturedExpression.substr(1) + " ) instead of " + m_assertionInfo.macroName + "( " + m_assertionInfo.capturedExpression + " ) for better diagnostics}"; -} } // end namespace Catch // #included from: catch_tag_alias_registry.hpp #define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED -// #included from: catch_tag_alias_registry.h -#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED - -#include - namespace Catch { -class TagAliasRegistry : public ITagAliasRegistry -{ - public: - virtual ~TagAliasRegistry(); - virtual Option find(std::string const& alias) const; - virtual std::string expandAliases(std::string const& unexpandedTestSpec) const; - void add(char const* alias, char const* tag, SourceLineInfo const& lineInfo); - static TagAliasRegistry& get(); + TagAliasRegistry::~TagAliasRegistry() {} - private: - std::map m_registry; -}; + Option TagAliasRegistry::find( std::string const& alias ) const { + std::map::const_iterator it = m_registry.find( alias ); + if( it != m_registry.end() ) + return it->second; + else + return Option(); + } -} // end namespace Catch + std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { + std::string expandedTestSpec = unexpandedTestSpec; + for( std::map::const_iterator it = m_registry.begin(), itEnd = m_registry.end(); + it != itEnd; + ++it ) { + std::size_t pos = expandedTestSpec.find( it->first ); + if( pos != std::string::npos ) { + expandedTestSpec = expandedTestSpec.substr( 0, pos ) + + it->second.tag + + expandedTestSpec.substr( pos + it->first.size() ); + } + } + return expandedTestSpec; + } -#include -#include + void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { -namespace Catch { - -TagAliasRegistry::~TagAliasRegistry() {} - -Option TagAliasRegistry::find(std::string const& alias) const -{ - std::map::const_iterator it = m_registry.find(alias); - if (it != m_registry.end()) - return it->second; - else - return Option(); -} - -std::string TagAliasRegistry::expandAliases(std::string const& unexpandedTestSpec) const -{ - std::string expandedTestSpec = unexpandedTestSpec; - for (std::map::const_iterator it = m_registry.begin(), itEnd = m_registry.end(); - it != itEnd; - ++it) - { - std::size_t pos = expandedTestSpec.find(it->first); - if (pos != std::string::npos) - { - expandedTestSpec = expandedTestSpec.substr(0, pos) + - it->second.tag + - expandedTestSpec.substr(pos + it->first.size()); + if( !startsWith( alias, "[@" ) || !endsWith( alias, ']' ) ) { + std::ostringstream oss; + oss << Colour( Colour::Red ) + << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" + << Colour( Colour::FileName ) + << lineInfo << '\n'; + throw std::domain_error( oss.str().c_str() ); + } + if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) { + std::ostringstream oss; + oss << Colour( Colour::Red ) + << "error: tag alias, \"" << alias << "\" already registered.\n" + << "\tFirst seen at " + << Colour( Colour::Red ) << find(alias)->lineInfo << '\n' + << Colour( Colour::Red ) << "\tRedefined at " + << Colour( Colour::FileName) << lineInfo << '\n'; + throw std::domain_error( oss.str().c_str() ); } } - return expandedTestSpec; -} -void TagAliasRegistry::add(char const* alias, char const* tag, SourceLineInfo const& lineInfo) -{ + ITagAliasRegistry::~ITagAliasRegistry() {} - if (!startsWith(alias, "[@") || !endsWith(alias, "]")) - { - std::ostringstream oss; - oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" - << lineInfo; - throw std::domain_error(oss.str().c_str()); + ITagAliasRegistry const& ITagAliasRegistry::get() { + return getRegistryHub().getTagAliasRegistry(); } - if (!m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second) - { - std::ostringstream oss; - oss << "error: tag alias, \"" << alias << "\" already registered.\n" - << "\tFirst seen at " << find(alias)->lineInfo << "\n" - << "\tRedefined at " << lineInfo; - throw std::domain_error(oss.str().c_str()); - } -} -TagAliasRegistry& TagAliasRegistry::get() -{ - static TagAliasRegistry instance; - return instance; -} - -ITagAliasRegistry::~ITagAliasRegistry() {} -ITagAliasRegistry const& ITagAliasRegistry::get() { return TagAliasRegistry::get(); } - -RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) -{ - try - { - TagAliasRegistry::get().add(alias, tag, lineInfo); + RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { + getMutableRegistryHub().registerTagAlias( alias, tag, lineInfo ); } - catch (std::exception& ex) - { - Colour colourGuard(Colour::Red); - Catch::cerr() << ex.what() << std::endl; - exit(1); - } -} } // end namespace Catch +// #included from: catch_matchers_string.hpp + +namespace Catch { +namespace Matchers { + + namespace StdString { + + CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_str( adjustString( str ) ) + {} + std::string CasedString::adjustString( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No + ? toLower( str ) + : str; + } + std::string CasedString::caseSensitivitySuffix() const { + return m_caseSensitivity == CaseSensitive::No + ? " (case insensitive)" + : std::string(); + } + + StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator ) + : m_comparator( comparator ), + m_operation( operation ) { + } + + std::string StringMatcherBase::describe() const { + std::string description; + description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + + m_comparator.caseSensitivitySuffix().size()); + description += m_operation; + description += ": \""; + description += m_comparator.m_str; + description += "\""; + description += m_comparator.caseSensitivitySuffix(); + return description; + } + + EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {} + + bool EqualsMatcher::match( std::string const& source ) const { + return m_comparator.adjustString( source ) == m_comparator.m_str; + } + + ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {} + + bool ContainsMatcher::match( std::string const& source ) const { + return contains( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {} + + bool StartsWithMatcher::match( std::string const& source ) const { + return startsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {} + + bool EndsWithMatcher::match( std::string const& source ) const { + return endsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + } // namespace StdString + + StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) ); + } + +} // namespace Matchers +} // namespace Catch // #included from: ../reporters/catch_reporter_multi.hpp #define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED namespace Catch { -class MultipleReporters : public SharedImpl -{ - typedef std::vector> Reporters; +class MultipleReporters : public SharedImpl { + typedef std::vector > Reporters; Reporters m_reporters; - public: - void add(Ptr const& reporter) - { - m_reporters.push_back(reporter); +public: + void add( Ptr const& reporter ) { + m_reporters.push_back( reporter ); } - public: // IStreamingReporter - virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE - { +public: // IStreamingReporter + + virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { return m_reporters[0]->getPreferences(); } - virtual void noMatchingTestCases(std::string const& spec) CATCH_OVERRIDE - { - for (Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it) - (*it)->noMatchingTestCases(spec); + virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->noMatchingTestCases( spec ); } - virtual void testRunStarting(TestRunInfo const& testRunInfo) CATCH_OVERRIDE - { - for (Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it) - (*it)->testRunStarting(testRunInfo); + virtual void testRunStarting( TestRunInfo const& testRunInfo ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->testRunStarting( testRunInfo ); } - virtual void testGroupStarting(GroupInfo const& groupInfo) CATCH_OVERRIDE - { - for (Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it) - (*it)->testGroupStarting(groupInfo); + virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->testGroupStarting( groupInfo ); } - virtual void testCaseStarting(TestCaseInfo const& testInfo) CATCH_OVERRIDE - { - for (Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it) - (*it)->testCaseStarting(testInfo); + virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->testCaseStarting( testInfo ); } - virtual void sectionStarting(SectionInfo const& sectionInfo) CATCH_OVERRIDE - { - for (Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it) - (*it)->sectionStarting(sectionInfo); + virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->sectionStarting( sectionInfo ); } - virtual void assertionStarting(AssertionInfo const& assertionInfo) CATCH_OVERRIDE - { - for (Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it) - (*it)->assertionStarting(assertionInfo); + virtual void assertionStarting( AssertionInfo const& assertionInfo ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->assertionStarting( assertionInfo ); } // The return value indicates if the messages buffer should be cleared: - virtual bool assertionEnded(AssertionStats const& assertionStats) CATCH_OVERRIDE - { + virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { bool clearBuffer = false; - for (Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it) - clearBuffer |= (*it)->assertionEnded(assertionStats); + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + clearBuffer |= (*it)->assertionEnded( assertionStats ); return clearBuffer; } - virtual void sectionEnded(SectionStats const& sectionStats) CATCH_OVERRIDE - { - for (Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it) - (*it)->sectionEnded(sectionStats); + virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->sectionEnded( sectionStats ); } - virtual void testCaseEnded(TestCaseStats const& testCaseStats) CATCH_OVERRIDE - { - for (Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it) - (*it)->testCaseEnded(testCaseStats); + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->testCaseEnded( testCaseStats ); } - virtual void testGroupEnded(TestGroupStats const& testGroupStats) CATCH_OVERRIDE - { - for (Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it) - (*it)->testGroupEnded(testGroupStats); + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->testGroupEnded( testGroupStats ); } - virtual void testRunEnded(TestRunStats const& testRunStats) CATCH_OVERRIDE - { - for (Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it) - (*it)->testRunEnded(testRunStats); + virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->testRunEnded( testRunStats ); } - virtual void skipTest(TestCaseInfo const& testInfo) CATCH_OVERRIDE - { - for (Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it) - (*it)->skipTest(testInfo); + virtual void skipTest( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->skipTest( testInfo ); } + + virtual MultipleReporters* tryAsMulti() CATCH_OVERRIDE { + return this; + } + }; -Ptr addReporter(Ptr const& existingReporter, Ptr const& additionalReporter) -{ +Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ) { Ptr resultingReporter; - if (existingReporter) - { - MultipleReporters* multi = dynamic_cast(existingReporter.get()); - if (!multi) - { + if( existingReporter ) { + MultipleReporters* multi = existingReporter->tryAsMulti(); + if( !multi ) { multi = new MultipleReporters; - resultingReporter = Ptr(multi); - if (existingReporter) - multi->add(existingReporter); + resultingReporter = Ptr( multi ); + if( existingReporter ) + multi->add( existingReporter ); } else resultingReporter = existingReporter; - multi->add(additionalReporter); + multi->add( additionalReporter ); } else resultingReporter = additionalReporter; @@ -9459,267 +9361,272 @@ Ptr addReporter(Ptr const& existingRepor #define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED #include +#include +#include +#include namespace Catch { -struct StreamingReporterBase : SharedImpl -{ + namespace { + // Because formatting using c++ streams is stateful, drop down to C is required + // Alternatively we could use stringstream, but its performance is... not good. + std::string getFormattedDuration( double duration ) { + // Max exponent + 1 is required to represent the whole part + // + 1 for decimal point + // + 3 for the 3 decimal places + // + 1 for null terminator + const size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; + char buffer[maxDoubleSize]; - StreamingReporterBase(ReporterConfig const& _config) - : m_config(_config.fullConfig()), - stream(_config.stream()) - { - m_reporterPrefs.shouldRedirectStdOut = false; - } - - virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE - { - return m_reporterPrefs; - } - - virtual ~StreamingReporterBase() CATCH_OVERRIDE; - - virtual void noMatchingTestCases(std::string const&) CATCH_OVERRIDE {} - - virtual void testRunStarting(TestRunInfo const& _testRunInfo) CATCH_OVERRIDE - { - currentTestRunInfo = _testRunInfo; - } - virtual void testGroupStarting(GroupInfo const& _groupInfo) CATCH_OVERRIDE - { - currentGroupInfo = _groupInfo; - } - - virtual void testCaseStarting(TestCaseInfo const& _testInfo) CATCH_OVERRIDE - { - currentTestCaseInfo = _testInfo; - } - virtual void sectionStarting(SectionInfo const& _sectionInfo) CATCH_OVERRIDE - { - m_sectionStack.push_back(_sectionInfo); - } - - virtual void sectionEnded(SectionStats const& /* _sectionStats */) CATCH_OVERRIDE - { - m_sectionStack.pop_back(); - } - virtual void testCaseEnded(TestCaseStats const& /* _testCaseStats */) CATCH_OVERRIDE - { - currentTestCaseInfo.reset(); - } - virtual void testGroupEnded(TestGroupStats const& /* _testGroupStats */) CATCH_OVERRIDE - { - currentGroupInfo.reset(); - } - virtual void testRunEnded(TestRunStats const& /* _testRunStats */) CATCH_OVERRIDE - { - currentTestCaseInfo.reset(); - currentGroupInfo.reset(); - currentTestRunInfo.reset(); - } - - virtual void skipTest(TestCaseInfo const&) CATCH_OVERRIDE - { - // Don't do anything with this by default. - // It can optionally be overridden in the derived class. - } - - Ptr m_config; - std::ostream& stream; - - LazyStat currentTestRunInfo; - LazyStat currentGroupInfo; - LazyStat currentTestCaseInfo; - - std::vector m_sectionStack; - ReporterPreferences m_reporterPrefs; -}; - -struct CumulativeReporterBase : SharedImpl -{ - template - struct Node : SharedImpl<> - { - explicit Node(T const& _value) : value(_value) {} - virtual ~Node() {} - - typedef std::vector> ChildNodes; - T value; - ChildNodes children; - }; - struct SectionNode : SharedImpl<> - { - explicit SectionNode(SectionStats const& _stats) : stats(_stats) {} - virtual ~SectionNode(); - - bool operator==(SectionNode const& other) const - { - return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; + // Save previous errno, to prevent sprintf from overwriting it + ErrnoGuard guard; +#ifdef _MSC_VER + sprintf_s(buffer, "%.3f", duration); +#else + sprintf(buffer, "%.3f", duration); +#endif + return std::string(buffer); } - bool operator==(Ptr const& other) const + } + + struct StreamingReporterBase : SharedImpl { + + StreamingReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) { - return operator==(*other); + m_reporterPrefs.shouldRedirectStdOut = false; } - SectionStats stats; - typedef std::vector> ChildSections; - typedef std::vector Assertions; - ChildSections childSections; - Assertions assertions; - std::string stdOut; - std::string stdErr; + virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { + return m_reporterPrefs; + } + + virtual ~StreamingReporterBase() CATCH_OVERRIDE; + + virtual void noMatchingTestCases( std::string const& ) CATCH_OVERRIDE {} + + virtual void testRunStarting( TestRunInfo const& _testRunInfo ) CATCH_OVERRIDE { + currentTestRunInfo = _testRunInfo; + } + virtual void testGroupStarting( GroupInfo const& _groupInfo ) CATCH_OVERRIDE { + currentGroupInfo = _groupInfo; + } + + virtual void testCaseStarting( TestCaseInfo const& _testInfo ) CATCH_OVERRIDE { + currentTestCaseInfo = _testInfo; + } + virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { + m_sectionStack.push_back( _sectionInfo ); + } + + virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) CATCH_OVERRIDE { + m_sectionStack.pop_back(); + } + virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) CATCH_OVERRIDE { + currentTestCaseInfo.reset(); + } + virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) CATCH_OVERRIDE { + currentGroupInfo.reset(); + } + virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) CATCH_OVERRIDE { + currentTestCaseInfo.reset(); + currentGroupInfo.reset(); + currentTestRunInfo.reset(); + } + + virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE { + // Don't do anything with this by default. + // It can optionally be overridden in the derived class. + } + + Ptr m_config; + std::ostream& stream; + + LazyStat currentTestRunInfo; + LazyStat currentGroupInfo; + LazyStat currentTestCaseInfo; + + std::vector m_sectionStack; + ReporterPreferences m_reporterPrefs; }; - struct BySectionInfo - { - BySectionInfo(SectionInfo const& other) : m_other(other) {} - BySectionInfo(BySectionInfo const& other) : m_other(other.m_other) {} - bool operator()(Ptr const& node) const - { - return node->stats.sectionInfo.lineInfo == m_other.lineInfo; - } + struct CumulativeReporterBase : SharedImpl { + template + struct Node : SharedImpl<> { + explicit Node( T const& _value ) : value( _value ) {} + virtual ~Node() {} - private: - void operator=(BySectionInfo const&); - SectionInfo const& m_other; - }; + typedef std::vector > ChildNodes; + T value; + ChildNodes children; + }; + struct SectionNode : SharedImpl<> { + explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {} + virtual ~SectionNode(); - typedef Node TestCaseNode; - typedef Node TestGroupNode; - typedef Node TestRunNode; - - CumulativeReporterBase(ReporterConfig const& _config) - : m_config(_config.fullConfig()), - stream(_config.stream()) - { - m_reporterPrefs.shouldRedirectStdOut = false; - } - ~CumulativeReporterBase(); - - virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE - { - return m_reporterPrefs; - } - - virtual void testRunStarting(TestRunInfo const&) CATCH_OVERRIDE {} - virtual void testGroupStarting(GroupInfo const&) CATCH_OVERRIDE {} - - virtual void testCaseStarting(TestCaseInfo const&) CATCH_OVERRIDE {} - - virtual void sectionStarting(SectionInfo const& sectionInfo) CATCH_OVERRIDE - { - SectionStats incompleteStats(sectionInfo, Counts(), 0, false); - Ptr node; - if (m_sectionStack.empty()) - { - if (!m_rootSection) - m_rootSection = new SectionNode(incompleteStats); - node = m_rootSection; - } - else - { - SectionNode& parentNode = *m_sectionStack.back(); - SectionNode::ChildSections::const_iterator it = - std::find_if(parentNode.childSections.begin(), - parentNode.childSections.end(), - BySectionInfo(sectionInfo)); - if (it == parentNode.childSections.end()) - { - node = new SectionNode(incompleteStats); - parentNode.childSections.push_back(node); + bool operator == ( SectionNode const& other ) const { + return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; } - else - node = *it; + bool operator == ( Ptr const& other ) const { + return operator==( *other ); + } + + SectionStats stats; + typedef std::vector > ChildSections; + typedef std::vector Assertions; + ChildSections childSections; + Assertions assertions; + std::string stdOut; + std::string stdErr; + }; + + struct BySectionInfo { + BySectionInfo( SectionInfo const& other ) : m_other( other ) {} + BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} + bool operator() ( Ptr const& node ) const { + return node->stats.sectionInfo.lineInfo == m_other.lineInfo; + } + private: + void operator=( BySectionInfo const& ); + SectionInfo const& m_other; + }; + + typedef Node TestCaseNode; + typedef Node TestGroupNode; + typedef Node TestRunNode; + + CumulativeReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = false; } - m_sectionStack.push_back(node); - m_deepestSection = node; + ~CumulativeReporterBase(); + + virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { + return m_reporterPrefs; + } + + virtual void testRunStarting( TestRunInfo const& ) CATCH_OVERRIDE {} + virtual void testGroupStarting( GroupInfo const& ) CATCH_OVERRIDE {} + + virtual void testCaseStarting( TestCaseInfo const& ) CATCH_OVERRIDE {} + + virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { + SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); + Ptr node; + if( m_sectionStack.empty() ) { + if( !m_rootSection ) + m_rootSection = new SectionNode( incompleteStats ); + node = m_rootSection; + } + else { + SectionNode& parentNode = *m_sectionStack.back(); + SectionNode::ChildSections::const_iterator it = + std::find_if( parentNode.childSections.begin(), + parentNode.childSections.end(), + BySectionInfo( sectionInfo ) ); + if( it == parentNode.childSections.end() ) { + node = new SectionNode( incompleteStats ); + parentNode.childSections.push_back( node ); + } + else + node = *it; + } + m_sectionStack.push_back( node ); + m_deepestSection = node; + } + + virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} + + virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { + assert( !m_sectionStack.empty() ); + SectionNode& sectionNode = *m_sectionStack.back(); + sectionNode.assertions.push_back( assertionStats ); + // AssertionResult holds a pointer to a temporary DecomposedExpression, + // which getExpandedExpression() calls to build the expression string. + // Our section stack copy of the assertionResult will likely outlive the + // temporary, so it must be expanded or discarded now to avoid calling + // a destroyed object later. + prepareExpandedExpression( sectionNode.assertions.back().assertionResult ); + return true; + } + virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { + assert( !m_sectionStack.empty() ); + SectionNode& node = *m_sectionStack.back(); + node.stats = sectionStats; + m_sectionStack.pop_back(); + } + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { + Ptr node = new TestCaseNode( testCaseStats ); + assert( m_sectionStack.size() == 0 ); + node->children.push_back( m_rootSection ); + m_testCases.push_back( node ); + m_rootSection.reset(); + + assert( m_deepestSection ); + m_deepestSection->stdOut = testCaseStats.stdOut; + m_deepestSection->stdErr = testCaseStats.stdErr; + } + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { + Ptr node = new TestGroupNode( testGroupStats ); + node->children.swap( m_testCases ); + m_testGroups.push_back( node ); + } + virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { + Ptr node = new TestRunNode( testRunStats ); + node->children.swap( m_testGroups ); + m_testRuns.push_back( node ); + testRunEndedCumulative(); + } + virtual void testRunEndedCumulative() = 0; + + virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {} + + virtual void prepareExpandedExpression( AssertionResult& result ) const { + if( result.isOk() ) + result.discardDecomposedExpression(); + else + result.expandDecomposedExpression(); + } + + Ptr m_config; + std::ostream& stream; + std::vector m_assertions; + std::vector > > m_sections; + std::vector > m_testCases; + std::vector > m_testGroups; + + std::vector > m_testRuns; + + Ptr m_rootSection; + Ptr m_deepestSection; + std::vector > m_sectionStack; + ReporterPreferences m_reporterPrefs; + + }; + + template + char const* getLineOfChars() { + static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; + if( !*line ) { + std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); + line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; + } + return line; } - virtual void assertionStarting(AssertionInfo const&) CATCH_OVERRIDE {} + struct TestEventListenerBase : StreamingReporterBase { + TestEventListenerBase( ReporterConfig const& _config ) + : StreamingReporterBase( _config ) + {} - virtual bool assertionEnded(AssertionStats const& assertionStats) - { - assert(!m_sectionStack.empty()); - SectionNode& sectionNode = *m_sectionStack.back(); - sectionNode.assertions.push_back(assertionStats); - return true; - } - virtual void sectionEnded(SectionStats const& sectionStats) CATCH_OVERRIDE - { - assert(!m_sectionStack.empty()); - SectionNode& node = *m_sectionStack.back(); - node.stats = sectionStats; - m_sectionStack.pop_back(); - } - virtual void testCaseEnded(TestCaseStats const& testCaseStats) CATCH_OVERRIDE - { - Ptr node = new TestCaseNode(testCaseStats); - assert(m_sectionStack.size() == 0); - node->children.push_back(m_rootSection); - m_testCases.push_back(node); - m_rootSection.reset(); - - assert(m_deepestSection); - m_deepestSection->stdOut = testCaseStats.stdOut; - m_deepestSection->stdErr = testCaseStats.stdErr; - } - virtual void testGroupEnded(TestGroupStats const& testGroupStats) CATCH_OVERRIDE - { - Ptr node = new TestGroupNode(testGroupStats); - node->children.swap(m_testCases); - m_testGroups.push_back(node); - } - virtual void testRunEnded(TestRunStats const& testRunStats) CATCH_OVERRIDE - { - Ptr node = new TestRunNode(testRunStats); - node->children.swap(m_testGroups); - m_testRuns.push_back(node); - testRunEndedCumulative(); - } - virtual void testRunEndedCumulative() = 0; - - virtual void skipTest(TestCaseInfo const&) CATCH_OVERRIDE {} - - Ptr m_config; - std::ostream& stream; - std::vector m_assertions; - std::vector>> m_sections; - std::vector> m_testCases; - std::vector> m_testGroups; - - std::vector> m_testRuns; - - Ptr m_rootSection; - Ptr m_deepestSection; - std::vector> m_sectionStack; - ReporterPreferences m_reporterPrefs; -}; - -template -char const* getLineOfChars() -{ - static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; - if (!*line) - { - memset(line, C, CATCH_CONFIG_CONSOLE_WIDTH - 1); - line[CATCH_CONFIG_CONSOLE_WIDTH - 1] = 0; - } - return line; -} - -struct TestEventListenerBase : StreamingReporterBase -{ - TestEventListenerBase(ReporterConfig const& _config) - : StreamingReporterBase(_config) - { - } - - virtual void assertionStarting(AssertionInfo const&) CATCH_OVERRIDE {} - virtual bool assertionEnded(AssertionStats const&) CATCH_OVERRIDE - { - return false; - } -}; + virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} + virtual bool assertionEnded( AssertionStats const& ) CATCH_OVERRIDE { + return false; + } + }; } // end namespace Catch @@ -9728,581 +9635,548 @@ struct TestEventListenerBase : StreamingReporterBase namespace Catch { -template -class LegacyReporterRegistrar -{ + template + class LegacyReporterRegistrar { - class ReporterFactory : public IReporterFactory - { - virtual IStreamingReporter* create(ReporterConfig const& config) const - { - return new LegacyReporterAdapter(new T(config)); - } + class ReporterFactory : public IReporterFactory { + virtual IStreamingReporter* create( ReporterConfig const& config ) const { + return new LegacyReporterAdapter( new T( config ) ); + } - virtual std::string getDescription() const - { - return T::getDescription(); + virtual std::string getDescription() const { + return T::getDescription(); + } + }; + + public: + + LegacyReporterRegistrar( std::string const& name ) { + getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); } }; - public: - LegacyReporterRegistrar(std::string const& name) - { - getMutableRegistryHub().registerReporter(name, new ReporterFactory()); - } -}; + template + class ReporterRegistrar { -template -class ReporterRegistrar -{ + class ReporterFactory : public SharedImpl { - class ReporterFactory : public SharedImpl - { + // *** Please Note ***: + // - If you end up here looking at a compiler error because it's trying to register + // your custom reporter class be aware that the native reporter interface has changed + // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via + // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter. + // However please consider updating to the new interface as the old one is now + // deprecated and will probably be removed quite soon! + // Please contact me via github if you have any questions at all about this. + // In fact, ideally, please contact me anyway to let me know you've hit this - as I have + // no idea who is actually using custom reporters at all (possibly no-one!). + // The new interface is designed to minimise exposure to interface changes in the future. + virtual IStreamingReporter* create( ReporterConfig const& config ) const { + return new T( config ); + } - // *** Please Note ***: - // - If you end up here looking at a compiler error because it's trying to register - // your custom reporter class be aware that the native reporter interface has changed - // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via - // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter. - // However please consider updating to the new interface as the old one is now - // deprecated and will probably be removed quite soon! - // Please contact me via github if you have any questions at all about this. - // In fact, ideally, please contact me anyway to let me know you've hit this - as I have - // no idea who is actually using custom reporters at all (possibly no-one!). - // The new interface is designed to minimise exposure to interface changes in the future. - virtual IStreamingReporter* create(ReporterConfig const& config) const - { - return new T(config); - } + virtual std::string getDescription() const { + return T::getDescription(); + } + }; - virtual std::string getDescription() const - { - return T::getDescription(); + public: + + ReporterRegistrar( std::string const& name ) { + getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); } }; - public: - ReporterRegistrar(std::string const& name) - { - getMutableRegistryHub().registerReporter(name, new ReporterFactory()); - } -}; + template + class ListenerRegistrar { -template -class ListenerRegistrar -{ + class ListenerFactory : public SharedImpl { - class ListenerFactory : public SharedImpl - { + virtual IStreamingReporter* create( ReporterConfig const& config ) const { + return new T( config ); + } + virtual std::string getDescription() const { + return std::string(); + } + }; - virtual IStreamingReporter* create(ReporterConfig const& config) const - { - return new T(config); - } - virtual std::string getDescription() const - { - return ""; + public: + + ListenerRegistrar() { + getMutableRegistryHub().registerListener( new ListenerFactory() ); } }; - - public: - ListenerRegistrar() - { - getMutableRegistryHub().registerListener(new ListenerFactory()); - } -}; } -#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER(name, reporterType) \ - namespace { \ - Catch::LegacyReporterRegistrar catch_internal_RegistrarFor##reporterType(name); \ - } +#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \ + namespace{ Catch::LegacyReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } -#define INTERNAL_CATCH_REGISTER_REPORTER(name, reporterType) \ - namespace { \ - Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType(name); \ - } +#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \ + namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } -#define INTERNAL_CATCH_REGISTER_LISTENER(listenerType) \ - namespace { \ - Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; \ - } +// Deprecated - use the form without INTERNAL_ +#define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \ + namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } + +#define CATCH_REGISTER_LISTENER( listenerType ) \ + namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } // #included from: ../internal/catch_xmlwriter.hpp #define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED -#include #include #include #include +#include namespace Catch { -class XmlEncode -{ - public: - enum ForWhat - { - ForTextNodes, - ForAttributes - }; + class XmlEncode { + public: + enum ForWhat { ForTextNodes, ForAttributes }; - XmlEncode(std::string const& str, ForWhat forWhat = ForTextNodes) - : m_str(str), - m_forWhat(forWhat) - { - } + XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ) + : m_str( str ), + m_forWhat( forWhat ) + {} - void encodeTo(std::ostream& os) const - { + void encodeTo( std::ostream& os ) const { - // Apostrophe escaping not necessary if we always use " to write attributes - // (see: http://www.w3.org/TR/xml/#syntax) + // Apostrophe escaping not necessary if we always use " to write attributes + // (see: http://www.w3.org/TR/xml/#syntax) - for (std::size_t i = 0; i < m_str.size(); ++i) - { - char c = m_str[i]; - switch (c) - { - case '<': - os << "<"; - break; - case '&': - os << "&"; - break; + for( std::size_t i = 0; i < m_str.size(); ++ i ) { + char c = m_str[i]; + switch( c ) { + case '<': os << "<"; break; + case '&': os << "&"; break; - case '>': - // See: http://www.w3.org/TR/xml/#syntax - if (i > 2 && m_str[i - 1] == ']' && m_str[i - 2] == ']') - os << ">"; - else - os << c; - break; + case '>': + // See: http://www.w3.org/TR/xml/#syntax + if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' ) + os << ">"; + else + os << c; + break; - case '\"': - if (m_forWhat == ForAttributes) - os << """; - else - os << c; - break; + case '\"': + if( m_forWhat == ForAttributes ) + os << """; + else + os << c; + break; - default: - // Escape control chars - based on contribution by @espenalb in PR #465 - if ((c < '\x09') || (c > '\x0D' && c < '\x20') || c == '\x7F') - os << "&#x" << std::uppercase << std::hex << static_cast(c); - else - os << c; + default: + // Escape control chars - based on contribution by @espenalb in PR #465 and + // by @mrpi PR #588 + if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) { + // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast( c ); + } + else + os << c; + } } } - } - friend std::ostream& operator<<(std::ostream& os, XmlEncode const& xmlEncode) - { - xmlEncode.encodeTo(os); - return os; - } - - private: - std::string m_str; - ForWhat m_forWhat; -}; - -class XmlWriter -{ - public: - class ScopedElement - { - public: - ScopedElement(XmlWriter* writer) - : m_writer(writer) - { + friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { + xmlEncode.encodeTo( os ); + return os; } - ScopedElement(ScopedElement const& other) - : m_writer(other.m_writer) - { - other.m_writer = CATCH_NULL; - } - - ~ScopedElement() - { - if (m_writer) - m_writer->endElement(); - } - - ScopedElement& writeText(std::string const& text, bool indent = true) - { - m_writer->writeText(text, indent); - return *this; - } - - template - ScopedElement& writeAttribute(std::string const& name, T const& attribute) - { - m_writer->writeAttribute(name, attribute); - return *this; - } - - private: - mutable XmlWriter* m_writer; + private: + std::string m_str; + ForWhat m_forWhat; }; - XmlWriter() - : m_tagIsOpen(false), - m_needsNewline(false), - m_os(&Catch::cout()) - { - } + class XmlWriter { + public: - XmlWriter(std::ostream& os) - : m_tagIsOpen(false), - m_needsNewline(false), - m_os(&os) - { - } + class ScopedElement { + public: + ScopedElement( XmlWriter* writer ) + : m_writer( writer ) + {} - ~XmlWriter() - { - while (!m_tags.empty()) - endElement(); - } + ScopedElement( ScopedElement const& other ) + : m_writer( other.m_writer ){ + other.m_writer = CATCH_NULL; + } - XmlWriter& startElement(std::string const& name) - { - ensureTagClosed(); - newlineIfNecessary(); - stream() << m_indent << "<" << name; - m_tags.push_back(name); - m_indent += " "; - m_tagIsOpen = true; - return *this; - } + ~ScopedElement() { + if( m_writer ) + m_writer->endElement(); + } - ScopedElement scopedElement(std::string const& name) - { - ScopedElement scoped(this); - startElement(name); - return scoped; - } + ScopedElement& writeText( std::string const& text, bool indent = true ) { + m_writer->writeText( text, indent ); + return *this; + } - XmlWriter& endElement() - { - newlineIfNecessary(); - m_indent = m_indent.substr(0, m_indent.size() - 2); - if (m_tagIsOpen) + template + ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { + m_writer->writeAttribute( name, attribute ); + return *this; + } + + private: + mutable XmlWriter* m_writer; + }; + + XmlWriter() + : m_tagIsOpen( false ), + m_needsNewline( false ), + m_os( Catch::cout() ) { - stream() << "/>\n"; - m_tagIsOpen = false; + writeDeclaration(); } - else + + XmlWriter( std::ostream& os ) + : m_tagIsOpen( false ), + m_needsNewline( false ), + m_os( os ) { - stream() << m_indent << "\n"; + writeDeclaration(); } - m_tags.pop_back(); - return *this; - } - XmlWriter& writeAttribute(std::string const& name, std::string const& attribute) - { - if (!name.empty() && !attribute.empty()) - stream() << " " << name << "=\"" << XmlEncode(attribute, XmlEncode::ForAttributes) << "\""; - return *this; - } + ~XmlWriter() { + while( !m_tags.empty() ) + endElement(); + } - XmlWriter& writeAttribute(std::string const& name, bool attribute) - { - stream() << " " << name << "=\"" << (attribute ? "true" : "false") << "\""; - return *this; - } - - template - XmlWriter& writeAttribute(std::string const& name, T const& attribute) - { - std::ostringstream oss; - oss << attribute; - return writeAttribute(name, oss.str()); - } - - XmlWriter& writeText(std::string const& text, bool indent = true) - { - if (!text.empty()) - { - bool tagWasOpen = m_tagIsOpen; + XmlWriter& startElement( std::string const& name ) { ensureTagClosed(); - if (tagWasOpen && indent) - stream() << m_indent; - stream() << XmlEncode(text); + newlineIfNecessary(); + m_os << m_indent << '<' << name; + m_tags.push_back( name ); + m_indent += " "; + m_tagIsOpen = true; + return *this; + } + + ScopedElement scopedElement( std::string const& name ) { + ScopedElement scoped( this ); + startElement( name ); + return scoped; + } + + XmlWriter& endElement() { + newlineIfNecessary(); + m_indent = m_indent.substr( 0, m_indent.size()-2 ); + if( m_tagIsOpen ) { + m_os << "/>"; + m_tagIsOpen = false; + } + else { + m_os << m_indent << ""; + } + m_os << std::endl; + m_tags.pop_back(); + return *this; + } + + XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) { + if( !name.empty() && !attribute.empty() ) + m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; + return *this; + } + + XmlWriter& writeAttribute( std::string const& name, bool attribute ) { + m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; + return *this; + } + + template + XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { + std::ostringstream oss; + oss << attribute; + return writeAttribute( name, oss.str() ); + } + + XmlWriter& writeText( std::string const& text, bool indent = true ) { + if( !text.empty() ){ + bool tagWasOpen = m_tagIsOpen; + ensureTagClosed(); + if( tagWasOpen && indent ) + m_os << m_indent; + m_os << XmlEncode( text ); + m_needsNewline = true; + } + return *this; + } + + XmlWriter& writeComment( std::string const& text ) { + ensureTagClosed(); + m_os << m_indent << ""; m_needsNewline = true; + return *this; } - return *this; - } - XmlWriter& writeComment(std::string const& text) - { - ensureTagClosed(); - stream() << m_indent << ""; - m_needsNewline = true; - return *this; - } - - XmlWriter& writeBlankLine() - { - ensureTagClosed(); - stream() << "\n"; - return *this; - } - - void setStream(std::ostream& os) - { - m_os = &os; - } - - private: - XmlWriter(XmlWriter const&); - void operator=(XmlWriter const&); - - std::ostream& stream() - { - return *m_os; - } - - void ensureTagClosed() - { - if (m_tagIsOpen) - { - stream() << ">\n"; - m_tagIsOpen = false; + void writeStylesheetRef( std::string const& url ) { + m_os << "\n"; } - } - void newlineIfNecessary() - { - if (m_needsNewline) - { - stream() << "\n"; - m_needsNewline = false; + XmlWriter& writeBlankLine() { + ensureTagClosed(); + m_os << '\n'; + return *this; } - } - bool m_tagIsOpen; - bool m_needsNewline; - std::vector m_tags; - std::string m_indent; - std::ostream* m_os; -}; + void ensureTagClosed() { + if( m_tagIsOpen ) { + m_os << ">" << std::endl; + m_tagIsOpen = false; + } + } + + private: + XmlWriter( XmlWriter const& ); + void operator=( XmlWriter const& ); + + void writeDeclaration() { + m_os << "\n"; + } + + void newlineIfNecessary() { + if( m_needsNewline ) { + m_os << std::endl; + m_needsNewline = false; + } + } + + bool m_tagIsOpen; + bool m_needsNewline; + std::vector m_tags; + std::string m_indent; + std::ostream& m_os; + }; + } // #included from: catch_reenable_warnings.h #define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED #ifdef __clang__ -#ifdef __ICC // icpc defines the __clang__ macro -#pragma warning(pop) -#else -#pragma clang diagnostic pop -#endif +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(pop) +# else +# pragma clang diagnostic pop +# endif #elif defined __GNUC__ -#pragma GCC diagnostic pop +# pragma GCC diagnostic pop #endif + namespace Catch { -class XmlReporter : public StreamingReporterBase -{ - public: - XmlReporter(ReporterConfig const& _config) - : StreamingReporterBase(_config), - m_sectionDepth(0) - { - m_reporterPrefs.shouldRedirectStdOut = true; - } - - virtual ~XmlReporter() CATCH_OVERRIDE; - - static std::string getDescription() - { - return "Reports test results as an XML document"; - } - - public: // StreamingReporterBase - virtual void noMatchingTestCases(std::string const& s) CATCH_OVERRIDE - { - StreamingReporterBase::noMatchingTestCases(s); - } - - virtual void testRunStarting(TestRunInfo const& testInfo) CATCH_OVERRIDE - { - StreamingReporterBase::testRunStarting(testInfo); - m_xml.setStream(stream); - m_xml.startElement("Catch"); - if (!m_config->name().empty()) - m_xml.writeAttribute("name", m_config->name()); - } - - virtual void testGroupStarting(GroupInfo const& groupInfo) CATCH_OVERRIDE - { - StreamingReporterBase::testGroupStarting(groupInfo); - m_xml.startElement("Group") - .writeAttribute("name", groupInfo.name); - } - - virtual void testCaseStarting(TestCaseInfo const& testInfo) CATCH_OVERRIDE - { - StreamingReporterBase::testCaseStarting(testInfo); - m_xml.startElement("TestCase").writeAttribute("name", trim(testInfo.name)); - - if (m_config->showDurations() == ShowDurations::Always) - m_testCaseTimer.start(); - } - - virtual void sectionStarting(SectionInfo const& sectionInfo) CATCH_OVERRIDE - { - StreamingReporterBase::sectionStarting(sectionInfo); - if (m_sectionDepth++ > 0) + class XmlReporter : public StreamingReporterBase { + public: + XmlReporter( ReporterConfig const& _config ) + : StreamingReporterBase( _config ), + m_xml(_config.stream()), + m_sectionDepth( 0 ) { - m_xml.startElement("Section") - .writeAttribute("name", trim(sectionInfo.name)) - .writeAttribute("description", sectionInfo.description); + m_reporterPrefs.shouldRedirectStdOut = true; } - } - virtual void assertionStarting(AssertionInfo const&) CATCH_OVERRIDE {} + virtual ~XmlReporter() CATCH_OVERRIDE; - virtual bool assertionEnded(AssertionStats const& assertionStats) CATCH_OVERRIDE - { - const AssertionResult& assertionResult = assertionStats.assertionResult; + static std::string getDescription() { + return "Reports test results as an XML document"; + } - // Print any info messages in tags. - if (assertionStats.assertionResult.getResultType() != ResultWas::Ok) - { - for (std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); - it != itEnd; - ++it) - { - if (it->type == ResultWas::Info) - { - m_xml.scopedElement("Info") - .writeText(it->message); - } - else if (it->type == ResultWas::Warning) - { - m_xml.scopedElement("Warning") - .writeText(it->message); - } + virtual std::string getStylesheetRef() const { + return std::string(); + } + + void writeSourceInfo( SourceLineInfo const& sourceInfo ) { + m_xml + .writeAttribute( "filename", sourceInfo.file ) + .writeAttribute( "line", sourceInfo.line ); + } + + public: // StreamingReporterBase + + virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE { + StreamingReporterBase::noMatchingTestCases( s ); + } + + virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE { + StreamingReporterBase::testRunStarting( testInfo ); + std::string stylesheetRef = getStylesheetRef(); + if( !stylesheetRef.empty() ) + m_xml.writeStylesheetRef( stylesheetRef ); + m_xml.startElement( "Catch" ); + if( !m_config->name().empty() ) + m_xml.writeAttribute( "name", m_config->name() ); + } + + virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { + StreamingReporterBase::testGroupStarting( groupInfo ); + m_xml.startElement( "Group" ) + .writeAttribute( "name", groupInfo.name ); + } + + virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { + StreamingReporterBase::testCaseStarting(testInfo); + m_xml.startElement( "TestCase" ) + .writeAttribute( "name", trim( testInfo.name ) ) + .writeAttribute( "description", testInfo.description ) + .writeAttribute( "tags", testInfo.tagsAsString ); + + writeSourceInfo( testInfo.lineInfo ); + + if ( m_config->showDurations() == ShowDurations::Always ) + m_testCaseTimer.start(); + m_xml.ensureTagClosed(); + } + + virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { + StreamingReporterBase::sectionStarting( sectionInfo ); + if( m_sectionDepth++ > 0 ) { + m_xml.startElement( "Section" ) + .writeAttribute( "name", trim( sectionInfo.name ) ) + .writeAttribute( "description", sectionInfo.description ); + writeSourceInfo( sectionInfo.lineInfo ); + m_xml.ensureTagClosed(); } } - // Drop out if result was successful but we're not printing them. - if (!m_config->includeSuccessfulResults() && isOk(assertionResult.getResultType())) + virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { } + + virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { + + AssertionResult const& result = assertionStats.assertionResult; + + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); + + if( includeResults ) { + // Print any info messages in tags. + for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); + it != itEnd; + ++it ) { + if( it->type == ResultWas::Info ) { + m_xml.scopedElement( "Info" ) + .writeText( it->message ); + } else if ( it->type == ResultWas::Warning ) { + m_xml.scopedElement( "Warning" ) + .writeText( it->message ); + } + } + } + + // Drop out if result was successful but we're not printing them. + if( !includeResults && result.getResultType() != ResultWas::Warning ) + return true; + + // Print the expression if there is one. + if( result.hasExpression() ) { + m_xml.startElement( "Expression" ) + .writeAttribute( "success", result.succeeded() ) + .writeAttribute( "type", result.getTestMacroName() ); + + writeSourceInfo( result.getSourceInfo() ); + + m_xml.scopedElement( "Original" ) + .writeText( result.getExpression() ); + m_xml.scopedElement( "Expanded" ) + .writeText( result.getExpandedExpression() ); + } + + // And... Print a result applicable to each result type. + switch( result.getResultType() ) { + case ResultWas::ThrewException: + m_xml.startElement( "Exception" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + case ResultWas::FatalErrorCondition: + m_xml.startElement( "FatalErrorCondition" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + case ResultWas::Info: + m_xml.scopedElement( "Info" ) + .writeText( result.getMessage() ); + break; + case ResultWas::Warning: + // Warning will already have been written + break; + case ResultWas::ExplicitFailure: + m_xml.startElement( "Failure" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + default: + break; + } + + if( result.hasExpression() ) + m_xml.endElement(); + return true; - - // Print the expression if there is one. - if (assertionResult.hasExpression()) - { - m_xml.startElement("Expression") - .writeAttribute("success", assertionResult.succeeded()) - .writeAttribute("type", assertionResult.getTestMacroName()) - .writeAttribute("filename", assertionResult.getSourceInfo().file) - .writeAttribute("line", assertionResult.getSourceInfo().line); - - m_xml.scopedElement("Original") - .writeText(assertionResult.getExpression()); - m_xml.scopedElement("Expanded") - .writeText(assertionResult.getExpandedExpression()); } - // And... Print a result applicable to each result type. - switch (assertionResult.getResultType()) - { - case ResultWas::ThrewException: - m_xml.scopedElement("Exception") - .writeAttribute("filename", assertionResult.getSourceInfo().file) - .writeAttribute("line", assertionResult.getSourceInfo().line) - .writeText(assertionResult.getMessage()); - break; - case ResultWas::FatalErrorCondition: - m_xml.scopedElement("Fatal Error Condition") - .writeAttribute("filename", assertionResult.getSourceInfo().file) - .writeAttribute("line", assertionResult.getSourceInfo().line) - .writeText(assertionResult.getMessage()); - break; - case ResultWas::Info: - m_xml.scopedElement("Info") - .writeText(assertionResult.getMessage()); - break; - case ResultWas::Warning: - // Warning will already have been written - break; - case ResultWas::ExplicitFailure: - m_xml.scopedElement("Failure") - .writeText(assertionResult.getMessage()); - break; - default: - break; + virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { + StreamingReporterBase::sectionEnded( sectionStats ); + if( --m_sectionDepth > 0 ) { + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); + e.writeAttribute( "successes", sectionStats.assertions.passed ); + e.writeAttribute( "failures", sectionStats.assertions.failed ); + e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); + + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); + + m_xml.endElement(); + } } - if (assertionResult.hasExpression()) - m_xml.endElement(); + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { + StreamingReporterBase::testCaseEnded( testCaseStats ); + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); + e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); - return true; - } + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); - virtual void sectionEnded(SectionStats const& sectionStats) CATCH_OVERRIDE - { - StreamingReporterBase::sectionEnded(sectionStats); - if (--m_sectionDepth > 0) - { - XmlWriter::ScopedElement e = m_xml.scopedElement("OverallResults"); - e.writeAttribute("successes", sectionStats.assertions.passed); - e.writeAttribute("failures", sectionStats.assertions.failed); - e.writeAttribute("expectedFailures", sectionStats.assertions.failedButOk); - - if (m_config->showDurations() == ShowDurations::Always) - e.writeAttribute("durationInSeconds", sectionStats.durationInSeconds); + if( !testCaseStats.stdOut.empty() ) + m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false ); + if( !testCaseStats.stdErr.empty() ) + m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false ); m_xml.endElement(); } - } - virtual void testCaseEnded(TestCaseStats const& testCaseStats) CATCH_OVERRIDE - { - StreamingReporterBase::testCaseEnded(testCaseStats); - XmlWriter::ScopedElement e = m_xml.scopedElement("OverallResult"); - e.writeAttribute("success", testCaseStats.totals.assertions.allOk()); + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { + StreamingReporterBase::testGroupEnded( testGroupStats ); + // TODO: Check testGroupStats.aborting and act accordingly. + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) + .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); + m_xml.endElement(); + } - if (m_config->showDurations() == ShowDurations::Always) - e.writeAttribute("durationInSeconds", m_testCaseTimer.getElapsedSeconds()); + virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { + StreamingReporterBase::testRunEnded( testRunStats ); + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testRunStats.totals.assertions.passed ) + .writeAttribute( "failures", testRunStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); + m_xml.endElement(); + } - m_xml.endElement(); - } + private: + Timer m_testCaseTimer; + XmlWriter m_xml; + int m_sectionDepth; + }; - virtual void testGroupEnded(TestGroupStats const& testGroupStats) CATCH_OVERRIDE - { - StreamingReporterBase::testGroupEnded(testGroupStats); - // TODO: Check testGroupStats.aborting and act accordingly. - m_xml.scopedElement("OverallResults") - .writeAttribute("successes", testGroupStats.totals.assertions.passed) - .writeAttribute("failures", testGroupStats.totals.assertions.failed) - .writeAttribute("expectedFailures", testGroupStats.totals.assertions.failedButOk); - m_xml.endElement(); - } - - virtual void testRunEnded(TestRunStats const& testRunStats) CATCH_OVERRIDE - { - StreamingReporterBase::testRunEnded(testRunStats); - m_xml.scopedElement("OverallResults") - .writeAttribute("successes", testRunStats.totals.assertions.passed) - .writeAttribute("failures", testRunStats.totals.assertions.failed) - .writeAttribute("expectedFailures", testRunStats.totals.assertions.failedButOk); - m_xml.endElement(); - } - - private: - Timer m_testCaseTimer; - XmlWriter m_xml; - int m_sectionDepth; -}; - -INTERNAL_CATCH_REGISTER_REPORTER("xml", XmlReporter) + INTERNAL_CATCH_REGISTER_REPORTER( "xml", XmlReporter ) } // end namespace Catch @@ -10313,724 +10187,667 @@ INTERNAL_CATCH_REGISTER_REPORTER("xml", XmlReporter) namespace Catch { -class JunitReporter : public CumulativeReporterBase -{ - public: - JunitReporter(ReporterConfig const& _config) - : CumulativeReporterBase(_config), - xml(_config.stream()) - { - m_reporterPrefs.shouldRedirectStdOut = true; - } + namespace { + std::string getCurrentTimestamp() { + // Beware, this is not reentrant because of backward compatibility issues + // Also, UTC only, again because of backward compatibility (%z is C++11) + time_t rawtime; + std::time(&rawtime); + const size_t timeStampSize = sizeof("2017-01-16T17:06:45Z"); - virtual ~JunitReporter() CATCH_OVERRIDE; +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &rawtime); +#else + std::tm* timeInfo; + timeInfo = std::gmtime(&rawtime); +#endif - static std::string getDescription() - { - return "Reports test results in an XML format that looks like Ant's junitreport target"; - } + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; - virtual void noMatchingTestCases(std::string const& /*spec*/) CATCH_OVERRIDE {} - - virtual void testRunStarting(TestRunInfo const& runInfo) CATCH_OVERRIDE - { - CumulativeReporterBase::testRunStarting(runInfo); - xml.startElement("testsuites"); - } - - virtual void testGroupStarting(GroupInfo const& groupInfo) CATCH_OVERRIDE - { - suiteTimer.start(); - stdOutForSuite.str(""); - stdErrForSuite.str(""); - unexpectedExceptions = 0; - CumulativeReporterBase::testGroupStarting(groupInfo); - } - - virtual bool assertionEnded(AssertionStats const& assertionStats) CATCH_OVERRIDE - { - if (assertionStats.assertionResult.getResultType() == ResultWas::ThrewException) - unexpectedExceptions++; - return CumulativeReporterBase::assertionEnded(assertionStats); - } - - virtual void testCaseEnded(TestCaseStats const& testCaseStats) CATCH_OVERRIDE - { - stdOutForSuite << testCaseStats.stdOut; - stdErrForSuite << testCaseStats.stdErr; - CumulativeReporterBase::testCaseEnded(testCaseStats); - } - - virtual void testGroupEnded(TestGroupStats const& testGroupStats) CATCH_OVERRIDE - { - double suiteTime = suiteTimer.getElapsedSeconds(); - CumulativeReporterBase::testGroupEnded(testGroupStats); - writeGroup(*m_testGroups.back(), suiteTime); - } - - virtual void testRunEndedCumulative() CATCH_OVERRIDE - { - xml.endElement(); - } - - void writeGroup(TestGroupNode const& groupNode, double suiteTime) - { - XmlWriter::ScopedElement e = xml.scopedElement("testsuite"); - TestGroupStats const& stats = groupNode.value; - xml.writeAttribute("name", stats.groupInfo.name); - xml.writeAttribute("errors", unexpectedExceptions); - xml.writeAttribute("failures", stats.totals.assertions.failed - unexpectedExceptions); - xml.writeAttribute("tests", stats.totals.assertions.total()); - xml.writeAttribute("hostname", "tbd"); // !TBD - if (m_config->showDurations() == ShowDurations::Never) - xml.writeAttribute("time", ""); - else - xml.writeAttribute("time", suiteTime); - xml.writeAttribute("timestamp", "tbd"); // !TBD - - // Write test cases - for (TestGroupNode::ChildNodes::const_iterator - it = groupNode.children.begin(), - itEnd = groupNode.children.end(); - it != itEnd; - ++it) - writeTestCase(**it); - - xml.scopedElement("system-out").writeText(trim(stdOutForSuite.str()), false); - xml.scopedElement("system-err").writeText(trim(stdErrForSuite.str()), false); - } - - void writeTestCase(TestCaseNode const& testCaseNode) - { - TestCaseStats const& stats = testCaseNode.value; - - // All test cases have exactly one section - which represents the - // test case itself. That section may have 0-n nested sections - assert(testCaseNode.children.size() == 1); - SectionNode const& rootSection = *testCaseNode.children.front(); - - std::string className = stats.testInfo.className; - - if (className.empty()) - { - if (rootSection.childSections.empty()) - className = "global"; +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp); } - writeSection(className, "", rootSection); + } - void writeSection(std::string const& className, - std::string const& rootName, - SectionNode const& sectionNode) - { - std::string name = trim(sectionNode.stats.sectionInfo.name); - if (!rootName.empty()) - name = rootName + "/" + name; - - if (!sectionNode.assertions.empty() || - !sectionNode.stdOut.empty() || - !sectionNode.stdErr.empty()) + class JunitReporter : public CumulativeReporterBase { + public: + JunitReporter( ReporterConfig const& _config ) + : CumulativeReporterBase( _config ), + xml( _config.stream() ) { - XmlWriter::ScopedElement e = xml.scopedElement("testcase"); - if (className.empty()) - { - xml.writeAttribute("classname", name); - xml.writeAttribute("name", "root"); - } + m_reporterPrefs.shouldRedirectStdOut = true; + } + + virtual ~JunitReporter() CATCH_OVERRIDE; + + static std::string getDescription() { + return "Reports test results in an XML format that looks like Ant's junitreport target"; + } + + virtual void noMatchingTestCases( std::string const& /*spec*/ ) CATCH_OVERRIDE {} + + virtual void testRunStarting( TestRunInfo const& runInfo ) CATCH_OVERRIDE { + CumulativeReporterBase::testRunStarting( runInfo ); + xml.startElement( "testsuites" ); + } + + virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { + suiteTimer.start(); + stdOutForSuite.str(""); + stdErrForSuite.str(""); + unexpectedExceptions = 0; + CumulativeReporterBase::testGroupStarting( groupInfo ); + } + + virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { + if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException ) + unexpectedExceptions++; + return CumulativeReporterBase::assertionEnded( assertionStats ); + } + + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { + stdOutForSuite << testCaseStats.stdOut; + stdErrForSuite << testCaseStats.stdErr; + CumulativeReporterBase::testCaseEnded( testCaseStats ); + } + + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { + double suiteTime = suiteTimer.getElapsedSeconds(); + CumulativeReporterBase::testGroupEnded( testGroupStats ); + writeGroup( *m_testGroups.back(), suiteTime ); + } + + virtual void testRunEndedCumulative() CATCH_OVERRIDE { + xml.endElement(); + } + + void writeGroup( TestGroupNode const& groupNode, double suiteTime ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); + TestGroupStats const& stats = groupNode.value; + xml.writeAttribute( "name", stats.groupInfo.name ); + xml.writeAttribute( "errors", unexpectedExceptions ); + xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); + xml.writeAttribute( "tests", stats.totals.assertions.total() ); + xml.writeAttribute( "hostname", "tbd" ); // !TBD + if( m_config->showDurations() == ShowDurations::Never ) + xml.writeAttribute( "time", "" ); else - { - xml.writeAttribute("classname", className); - xml.writeAttribute("name", name); - } - xml.writeAttribute("time", Catch::toString(sectionNode.stats.durationInSeconds)); + xml.writeAttribute( "time", suiteTime ); + xml.writeAttribute( "timestamp", getCurrentTimestamp() ); - writeAssertions(sectionNode); + // Write test cases + for( TestGroupNode::ChildNodes::const_iterator + it = groupNode.children.begin(), itEnd = groupNode.children.end(); + it != itEnd; + ++it ) + writeTestCase( **it ); - if (!sectionNode.stdOut.empty()) - xml.scopedElement("system-out").writeText(trim(sectionNode.stdOut), false); - if (!sectionNode.stdErr.empty()) - xml.scopedElement("system-err").writeText(trim(sectionNode.stdErr), false); + xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false ); + xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false ); } - for (SectionNode::ChildSections::const_iterator - it = sectionNode.childSections.begin(), - itEnd = sectionNode.childSections.end(); - it != itEnd; - ++it) - if (className.empty()) - writeSection(name, "", **it); - else - writeSection(className, name, **it); - } - void writeAssertions(SectionNode const& sectionNode) - { - for (SectionNode::Assertions::const_iterator - it = sectionNode.assertions.begin(), - itEnd = sectionNode.assertions.end(); - it != itEnd; - ++it) - writeAssertion(*it); - } - void writeAssertion(AssertionStats const& stats) - { - AssertionResult const& result = stats.assertionResult; - if (!result.isOk()) - { - std::string elementName; - switch (result.getResultType()) - { - case ResultWas::ThrewException: - case ResultWas::FatalErrorCondition: - elementName = "error"; - break; - case ResultWas::ExplicitFailure: - elementName = "failure"; - break; - case ResultWas::ExpressionFailed: - elementName = "failure"; - break; - case ResultWas::DidntThrowException: - elementName = "failure"; - break; + void writeTestCase( TestCaseNode const& testCaseNode ) { + TestCaseStats const& stats = testCaseNode.value; - // We should never see these here: - case ResultWas::Info: - case ResultWas::Warning: - case ResultWas::Ok: - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: - elementName = "internalError"; - break; + // All test cases have exactly one section - which represents the + // test case itself. That section may have 0-n nested sections + assert( testCaseNode.children.size() == 1 ); + SectionNode const& rootSection = *testCaseNode.children.front(); + + std::string className = stats.testInfo.className; + + if( className.empty() ) { + if( rootSection.childSections.empty() ) + className = "global"; } - - XmlWriter::ScopedElement e = xml.scopedElement(elementName); - - xml.writeAttribute("message", result.getExpandedExpression()); - xml.writeAttribute("type", result.getTestMacroName()); - - std::ostringstream oss; - if (!result.getMessage().empty()) - oss << result.getMessage() << "\n"; - for (std::vector::const_iterator - it = stats.infoMessages.begin(), - itEnd = stats.infoMessages.end(); - it != itEnd; - ++it) - if (it->type == ResultWas::Info) - oss << it->message << "\n"; - - oss << "at " << result.getSourceInfo(); - xml.writeText(oss.str(), false); + writeSection( className, "", rootSection ); } - } - XmlWriter xml; - Timer suiteTimer; - std::ostringstream stdOutForSuite; - std::ostringstream stdErrForSuite; - unsigned int unexpectedExceptions; -}; + void writeSection( std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode ) { + std::string name = trim( sectionNode.stats.sectionInfo.name ); + if( !rootName.empty() ) + name = rootName + '/' + name; -INTERNAL_CATCH_REGISTER_REPORTER("junit", JunitReporter) + if( !sectionNode.assertions.empty() || + !sectionNode.stdOut.empty() || + !sectionNode.stdErr.empty() ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); + if( className.empty() ) { + xml.writeAttribute( "classname", name ); + xml.writeAttribute( "name", "root" ); + } + else { + xml.writeAttribute( "classname", className ); + xml.writeAttribute( "name", name ); + } + xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) ); + + writeAssertions( sectionNode ); + + if( !sectionNode.stdOut.empty() ) + xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); + if( !sectionNode.stdErr.empty() ) + xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); + } + for( SectionNode::ChildSections::const_iterator + it = sectionNode.childSections.begin(), + itEnd = sectionNode.childSections.end(); + it != itEnd; + ++it ) + if( className.empty() ) + writeSection( name, "", **it ); + else + writeSection( className, name, **it ); + } + + void writeAssertions( SectionNode const& sectionNode ) { + for( SectionNode::Assertions::const_iterator + it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end(); + it != itEnd; + ++it ) + writeAssertion( *it ); + } + void writeAssertion( AssertionStats const& stats ) { + AssertionResult const& result = stats.assertionResult; + if( !result.isOk() ) { + std::string elementName; + switch( result.getResultType() ) { + case ResultWas::ThrewException: + case ResultWas::FatalErrorCondition: + elementName = "error"; + break; + case ResultWas::ExplicitFailure: + elementName = "failure"; + break; + case ResultWas::ExpressionFailed: + elementName = "failure"; + break; + case ResultWas::DidntThrowException: + elementName = "failure"; + break; + + // We should never see these here: + case ResultWas::Info: + case ResultWas::Warning: + case ResultWas::Ok: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + elementName = "internalError"; + break; + } + + XmlWriter::ScopedElement e = xml.scopedElement( elementName ); + + xml.writeAttribute( "message", result.getExpandedExpression() ); + xml.writeAttribute( "type", result.getTestMacroName() ); + + std::ostringstream oss; + if( !result.getMessage().empty() ) + oss << result.getMessage() << '\n'; + for( std::vector::const_iterator + it = stats.infoMessages.begin(), + itEnd = stats.infoMessages.end(); + it != itEnd; + ++it ) + if( it->type == ResultWas::Info ) + oss << it->message << '\n'; + + oss << "at " << result.getSourceInfo(); + xml.writeText( oss.str(), false ); + } + } + + XmlWriter xml; + Timer suiteTimer; + std::ostringstream stdOutForSuite; + std::ostringstream stdErrForSuite; + unsigned int unexpectedExceptions; + }; + + INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter ) } // end namespace Catch // #included from: ../reporters/catch_reporter_console.hpp #define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED +#include +#include + namespace Catch { -struct ConsoleReporter : StreamingReporterBase -{ - ConsoleReporter(ReporterConfig const& _config) - : StreamingReporterBase(_config), - m_headerPrinted(false) - { - } + struct ConsoleReporter : StreamingReporterBase { + ConsoleReporter( ReporterConfig const& _config ) + : StreamingReporterBase( _config ), + m_headerPrinted( false ) + {} - virtual ~ConsoleReporter() CATCH_OVERRIDE; - static std::string getDescription() - { - return "Reports test results as plain lines of text"; - } + virtual ~ConsoleReporter() CATCH_OVERRIDE; + static std::string getDescription() { + return "Reports test results as plain lines of text"; + } - virtual void noMatchingTestCases(std::string const& spec) CATCH_OVERRIDE - { - stream << "No test cases matched '" << spec << "'" << std::endl; - } + virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { + stream << "No test cases matched '" << spec << '\'' << std::endl; + } - virtual void assertionStarting(AssertionInfo const&) CATCH_OVERRIDE - { - } + virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { + } - virtual bool assertionEnded(AssertionStats const& _assertionStats) CATCH_OVERRIDE - { - AssertionResult const& result = _assertionStats.assertionResult; + virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE { + AssertionResult const& result = _assertionStats.assertionResult; - bool printInfoMessages = true; + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); - // Drop out if result was successful and we're not printing those - if (!m_config->includeSuccessfulResults() && result.isOk()) - { - if (result.getResultType() != ResultWas::Warning) + // Drop out if result was successful but we're not printing them. + if( !includeResults && result.getResultType() != ResultWas::Warning ) return false; - printInfoMessages = false; - } - lazyPrint(); - - AssertionPrinter printer(stream, _assertionStats, printInfoMessages); - printer.print(); - stream << std::endl; - return true; - } - - virtual void sectionStarting(SectionInfo const& _sectionInfo) CATCH_OVERRIDE - { - m_headerPrinted = false; - StreamingReporterBase::sectionStarting(_sectionInfo); - } - virtual void sectionEnded(SectionStats const& _sectionStats) CATCH_OVERRIDE - { - if (_sectionStats.missingAssertions) - { lazyPrint(); - Colour colour(Colour::ResultError); - if (m_sectionStack.size() > 1) - stream << "\nNo assertions in section"; - else - stream << "\nNo assertions in test case"; - stream << " '" << _sectionStats.sectionInfo.name << "'\n" - << std::endl; + + AssertionPrinter printer( stream, _assertionStats, includeResults ); + printer.print(); + stream << std::endl; + return true; } - if (m_headerPrinted) - { - if (m_config->showDurations() == ShowDurations::Always) - stream << "Completed in " << _sectionStats.durationInSeconds << "s" << std::endl; + + virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { + m_headerPrinted = false; + StreamingReporterBase::sectionStarting( _sectionInfo ); + } + virtual void sectionEnded( SectionStats const& _sectionStats ) CATCH_OVERRIDE { + if( _sectionStats.missingAssertions ) { + lazyPrint(); + Colour colour( Colour::ResultError ); + if( m_sectionStack.size() > 1 ) + stream << "\nNo assertions in section"; + else + stream << "\nNo assertions in test case"; + stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; + } + if( m_config->showDurations() == ShowDurations::Always ) { + stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + if( m_headerPrinted ) { + m_headerPrinted = false; + } + StreamingReporterBase::sectionEnded( _sectionStats ); + } + + virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) CATCH_OVERRIDE { + StreamingReporterBase::testCaseEnded( _testCaseStats ); m_headerPrinted = false; } - else - { - if (m_config->showDurations() == ShowDurations::Always) - stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl; + virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) CATCH_OVERRIDE { + if( currentGroupInfo.used ) { + printSummaryDivider(); + stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; + printTotals( _testGroupStats.totals ); + stream << '\n' << std::endl; + } + StreamingReporterBase::testGroupEnded( _testGroupStats ); } - StreamingReporterBase::sectionEnded(_sectionStats); - } - - virtual void testCaseEnded(TestCaseStats const& _testCaseStats) CATCH_OVERRIDE - { - StreamingReporterBase::testCaseEnded(_testCaseStats); - m_headerPrinted = false; - } - virtual void testGroupEnded(TestGroupStats const& _testGroupStats) CATCH_OVERRIDE - { - if (currentGroupInfo.used) - { - printSummaryDivider(); - stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; - printTotals(_testGroupStats.totals); - stream << "\n" - << std::endl; + virtual void testRunEnded( TestRunStats const& _testRunStats ) CATCH_OVERRIDE { + printTotalsDivider( _testRunStats.totals ); + printTotals( _testRunStats.totals ); + stream << std::endl; + StreamingReporterBase::testRunEnded( _testRunStats ); } - StreamingReporterBase::testGroupEnded(_testGroupStats); - } - virtual void testRunEnded(TestRunStats const& _testRunStats) CATCH_OVERRIDE - { - printTotalsDivider(_testRunStats.totals); - printTotals(_testRunStats.totals); - stream << std::endl; - StreamingReporterBase::testRunEnded(_testRunStats); - } - private: - class AssertionPrinter - { - void operator=(AssertionPrinter const&); + private: - public: - AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages) - : stream(_stream), - stats(_stats), - result(_stats.assertionResult), - colour(Colour::None), - message(result.getMessage()), - messages(_stats.infoMessages), - printInfoMessages(_printInfoMessages) - { - switch (result.getResultType()) + class AssertionPrinter { + void operator= ( AssertionPrinter const& ); + public: + AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) + : stream( _stream ), + stats( _stats ), + result( _stats.assertionResult ), + colour( Colour::None ), + message( result.getMessage() ), + messages( _stats.infoMessages ), + printInfoMessages( _printInfoMessages ) { - case ResultWas::Ok: - colour = Colour::Success; - passOrFail = "PASSED"; - //if( result.hasMessage() ) - if (_stats.infoMessages.size() == 1) - messageLabel = "with message"; - if (_stats.infoMessages.size() > 1) - messageLabel = "with messages"; - break; - case ResultWas::ExpressionFailed: - if (result.isOk()) - { - colour = Colour::Success; - passOrFail = "FAILED - but was ok"; + switch( result.getResultType() ) { + case ResultWas::Ok: + colour = Colour::Success; + passOrFail = "PASSED"; + //if( result.hasMessage() ) + if( _stats.infoMessages.size() == 1 ) + messageLabel = "with message"; + if( _stats.infoMessages.size() > 1 ) + messageLabel = "with messages"; + break; + case ResultWas::ExpressionFailed: + if( result.isOk() ) { + colour = Colour::Success; + passOrFail = "FAILED - but was ok"; + } + else { + colour = Colour::Error; + passOrFail = "FAILED"; + } + if( _stats.infoMessages.size() == 1 ) + messageLabel = "with message"; + if( _stats.infoMessages.size() > 1 ) + messageLabel = "with messages"; + break; + case ResultWas::ThrewException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to unexpected exception with "; + if (_stats.infoMessages.size() == 1) + messageLabel += "message"; + if (_stats.infoMessages.size() > 1) + messageLabel += "messages"; + break; + case ResultWas::FatalErrorCondition: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to a fatal error condition"; + break; + case ResultWas::DidntThrowException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "because no exception was thrown where one was expected"; + break; + case ResultWas::Info: + messageLabel = "info"; + break; + case ResultWas::Warning: + messageLabel = "warning"; + break; + case ResultWas::ExplicitFailure: + passOrFail = "FAILED"; + colour = Colour::Error; + if( _stats.infoMessages.size() == 1 ) + messageLabel = "explicitly with message"; + if( _stats.infoMessages.size() > 1 ) + messageLabel = "explicitly with messages"; + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + passOrFail = "** internal error **"; + colour = Colour::Error; + break; } - else - { - colour = Colour::Error; - passOrFail = "FAILED"; + } + + void print() const { + printSourceInfo(); + if( stats.totals.assertions.total() > 0 ) { + if( result.isOk() ) + stream << '\n'; + printResultType(); + printOriginalExpression(); + printReconstructedExpression(); } - if (_stats.infoMessages.size() == 1) - messageLabel = "with message"; - if (_stats.infoMessages.size() > 1) - messageLabel = "with messages"; - break; - case ResultWas::ThrewException: - colour = Colour::Error; - passOrFail = "FAILED"; - messageLabel = "due to unexpected exception with message"; - break; - case ResultWas::FatalErrorCondition: - colour = Colour::Error; - passOrFail = "FAILED"; - messageLabel = "due to a fatal error condition"; - break; - case ResultWas::DidntThrowException: - colour = Colour::Error; - passOrFail = "FAILED"; - messageLabel = "because no exception was thrown where one was expected"; - break; - case ResultWas::Info: - messageLabel = "info"; - break; - case ResultWas::Warning: - messageLabel = "warning"; - break; - case ResultWas::ExplicitFailure: - passOrFail = "FAILED"; - colour = Colour::Error; - if (_stats.infoMessages.size() == 1) - messageLabel = "explicitly with message"; - if (_stats.infoMessages.size() > 1) - messageLabel = "explicitly with messages"; - break; - // These cases are here to prevent compiler warnings - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: - passOrFail = "** internal error **"; - colour = Colour::Error; - break; + else { + stream << '\n'; + } + printMessage(); + } + + private: + void printResultType() const { + if( !passOrFail.empty() ) { + Colour colourGuard( colour ); + stream << passOrFail << ":\n"; + } + } + void printOriginalExpression() const { + if( result.hasExpression() ) { + Colour colourGuard( Colour::OriginalExpression ); + stream << " "; + stream << result.getExpressionInMacro(); + stream << '\n'; + } + } + void printReconstructedExpression() const { + if( result.hasExpandedExpression() ) { + stream << "with expansion:\n"; + Colour colourGuard( Colour::ReconstructedExpression ); + stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << '\n'; + } + } + void printMessage() const { + if( !messageLabel.empty() ) + stream << messageLabel << ':' << '\n'; + for( std::vector::const_iterator it = messages.begin(), itEnd = messages.end(); + it != itEnd; + ++it ) { + // If this assertion is a warning ignore any INFO messages + if( printInfoMessages || it->type != ResultWas::Info ) + stream << Text( it->message, TextAttributes().setIndent(2) ) << '\n'; + } + } + void printSourceInfo() const { + Colour colourGuard( Colour::FileName ); + stream << result.getSourceInfo() << ": "; + } + + std::ostream& stream; + AssertionStats const& stats; + AssertionResult const& result; + Colour::Code colour; + std::string passOrFail; + std::string messageLabel; + std::string message; + std::vector messages; + bool printInfoMessages; + }; + + void lazyPrint() { + + if( !currentTestRunInfo.used ) + lazyPrintRunInfo(); + if( !currentGroupInfo.used ) + lazyPrintGroupInfo(); + + if( !m_headerPrinted ) { + printTestCaseAndSectionHeader(); + m_headerPrinted = true; + } + } + void lazyPrintRunInfo() { + stream << '\n' << getLineOfChars<'~'>() << '\n'; + Colour colour( Colour::SecondaryText ); + stream << currentTestRunInfo->name + << " is a Catch v" << libraryVersion() << " host application.\n" + << "Run with -? for options\n\n"; + + if( m_config->rngSeed() != 0 ) + stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; + + currentTestRunInfo.used = true; + } + void lazyPrintGroupInfo() { + if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) { + printClosedHeader( "Group: " + currentGroupInfo->name ); + currentGroupInfo.used = true; + } + } + void printTestCaseAndSectionHeader() { + assert( !m_sectionStack.empty() ); + printOpenHeader( currentTestCaseInfo->name ); + + if( m_sectionStack.size() > 1 ) { + Colour colourGuard( Colour::Headers ); + + std::vector::const_iterator + it = m_sectionStack.begin()+1, // Skip first section (test case) + itEnd = m_sectionStack.end(); + for( ; it != itEnd; ++it ) + printHeaderString( it->name, 2 ); + } + + SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; + + if( !lineInfo.empty() ){ + stream << getLineOfChars<'-'>() << '\n'; + Colour colourGuard( Colour::FileName ); + stream << lineInfo << '\n'; + } + stream << getLineOfChars<'.'>() << '\n' << std::endl; + } + + void printClosedHeader( std::string const& _name ) { + printOpenHeader( _name ); + stream << getLineOfChars<'.'>() << '\n'; + } + void printOpenHeader( std::string const& _name ) { + stream << getLineOfChars<'-'>() << '\n'; + { + Colour colourGuard( Colour::Headers ); + printHeaderString( _name ); } } - void print() const - { - printSourceInfo(); - if (stats.totals.assertions.total() > 0) - { - if (result.isOk()) - stream << "\n"; - printResultType(); - printOriginalExpression(); - printReconstructedExpression(); - } + // if string has a : in first line will set indent to follow it on + // subsequent lines + void printHeaderString( std::string const& _string, std::size_t indent = 0 ) { + std::size_t i = _string.find( ": " ); + if( i != std::string::npos ) + i+=2; else - { - stream << "\n"; - } - printMessage(); + i = 0; + stream << Text( _string, TextAttributes() + .setIndent( indent+i) + .setInitialIndent( indent ) ) << '\n'; } - private: - void printResultType() const - { - if (!passOrFail.empty()) - { - Colour colourGuard(colour); - stream << passOrFail << ":\n"; + struct SummaryColumn { + + SummaryColumn( std::string const& _label, Colour::Code _colour ) + : label( _label ), + colour( _colour ) + {} + SummaryColumn addRow( std::size_t count ) { + std::ostringstream oss; + oss << count; + std::string row = oss.str(); + for( std::vector::iterator it = rows.begin(); it != rows.end(); ++it ) { + while( it->size() < row.size() ) + *it = ' ' + *it; + while( it->size() > row.size() ) + row = ' ' + row; + } + rows.push_back( row ); + return *this; + } + + std::string label; + Colour::Code colour; + std::vector rows; + + }; + + void printTotals( Totals const& totals ) { + if( totals.testCases.total() == 0 ) { + stream << Colour( Colour::Warning ) << "No tests ran\n"; + } + else if( totals.assertions.total() > 0 && totals.testCases.allPassed() ) { + stream << Colour( Colour::ResultSuccess ) << "All tests passed"; + stream << " (" + << pluralise( totals.assertions.passed, "assertion" ) << " in " + << pluralise( totals.testCases.passed, "test case" ) << ')' + << '\n'; + } + else { + + std::vector columns; + columns.push_back( SummaryColumn( "", Colour::None ) + .addRow( totals.testCases.total() ) + .addRow( totals.assertions.total() ) ); + columns.push_back( SummaryColumn( "passed", Colour::Success ) + .addRow( totals.testCases.passed ) + .addRow( totals.assertions.passed ) ); + columns.push_back( SummaryColumn( "failed", Colour::ResultError ) + .addRow( totals.testCases.failed ) + .addRow( totals.assertions.failed ) ); + columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure ) + .addRow( totals.testCases.failedButOk ) + .addRow( totals.assertions.failedButOk ) ); + + printSummaryRow( "test cases", columns, 0 ); + printSummaryRow( "assertions", columns, 1 ); } } - void printOriginalExpression() const - { - if (result.hasExpression()) - { - Colour colourGuard(Colour::OriginalExpression); - stream << " "; - stream << result.getExpressionInMacro(); - stream << "\n"; + void printSummaryRow( std::string const& label, std::vector const& cols, std::size_t row ) { + for( std::vector::const_iterator it = cols.begin(); it != cols.end(); ++it ) { + std::string value = it->rows[row]; + if( it->label.empty() ) { + stream << label << ": "; + if( value != "0" ) + stream << value; + else + stream << Colour( Colour::Warning ) << "- none -"; + } + else if( value != "0" ) { + stream << Colour( Colour::LightGrey ) << " | "; + stream << Colour( it->colour ) + << value << ' ' << it->label; + } } - } - void printReconstructedExpression() const - { - if (result.hasExpandedExpression()) - { - stream << "with expansion:\n"; - Colour colourGuard(Colour::ReconstructedExpression); - stream << Text(result.getExpandedExpression(), TextAttributes().setIndent(2)) << "\n"; - } - } - void printMessage() const - { - if (!messageLabel.empty()) - stream << messageLabel << ":" - << "\n"; - for (std::vector::const_iterator it = messages.begin(), itEnd = messages.end(); - it != itEnd; - ++it) - { - // If this assertion is a warning ignore any INFO messages - if (printInfoMessages || it->type != ResultWas::Info) - stream << Text(it->message, TextAttributes().setIndent(2)) << "\n"; - } - } - void printSourceInfo() const - { - Colour colourGuard(Colour::FileName); - stream << result.getSourceInfo() << ": "; + stream << '\n'; } - std::ostream& stream; - AssertionStats const& stats; - AssertionResult const& result; - Colour::Code colour; - std::string passOrFail; - std::string messageLabel; - std::string message; - std::vector messages; - bool printInfoMessages; + static std::size_t makeRatio( std::size_t number, std::size_t total ) { + std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0; + return ( ratio == 0 && number > 0 ) ? 1 : ratio; + } + static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) { + if( i > j && i > k ) + return i; + else if( j > k ) + return j; + else + return k; + } + + void printTotalsDivider( Totals const& totals ) { + if( totals.testCases.total() > 0 ) { + std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() ); + std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() ); + std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() ); + while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 ) + findMax( failedRatio, failedButOkRatio, passedRatio )++; + while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 ) + findMax( failedRatio, failedButOkRatio, passedRatio )--; + + stream << Colour( Colour::Error ) << std::string( failedRatio, '=' ); + stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' ); + if( totals.testCases.allPassed() ) + stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' ); + else + stream << Colour( Colour::Success ) << std::string( passedRatio, '=' ); + } + else { + stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' ); + } + stream << '\n'; + } + void printSummaryDivider() { + stream << getLineOfChars<'-'>() << '\n'; + } + + private: + bool m_headerPrinted; }; - void lazyPrint() - { - - if (!currentTestRunInfo.used) - lazyPrintRunInfo(); - if (!currentGroupInfo.used) - lazyPrintGroupInfo(); - - if (!m_headerPrinted) - { - printTestCaseAndSectionHeader(); - m_headerPrinted = true; - } - } - void lazyPrintRunInfo() - { - stream << "\n" - << getLineOfChars<'~'>() << "\n"; - Colour colour(Colour::SecondaryText); - stream << currentTestRunInfo->name - << " is a Catch v" << libraryVersion << " host application.\n" - << "Run with -? for options\n\n"; - - if (m_config->rngSeed() != 0) - stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; - - currentTestRunInfo.used = true; - } - void lazyPrintGroupInfo() - { - if (!currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1) - { - printClosedHeader("Group: " + currentGroupInfo->name); - currentGroupInfo.used = true; - } - } - void printTestCaseAndSectionHeader() - { - assert(!m_sectionStack.empty()); - printOpenHeader(currentTestCaseInfo->name); - - if (m_sectionStack.size() > 1) - { - Colour colourGuard(Colour::Headers); - - std::vector::const_iterator - it = m_sectionStack.begin() + 1, // Skip first section (test case) - itEnd = m_sectionStack.end(); - for (; it != itEnd; ++it) - printHeaderString(it->name, 2); - } - - SourceLineInfo lineInfo = m_sectionStack.front().lineInfo; - - if (!lineInfo.empty()) - { - stream << getLineOfChars<'-'>() << "\n"; - Colour colourGuard(Colour::FileName); - stream << lineInfo << "\n"; - } - stream << getLineOfChars<'.'>() << "\n" - << std::endl; - } - - void printClosedHeader(std::string const& _name) - { - printOpenHeader(_name); - stream << getLineOfChars<'.'>() << "\n"; - } - void printOpenHeader(std::string const& _name) - { - stream << getLineOfChars<'-'>() << "\n"; - { - Colour colourGuard(Colour::Headers); - printHeaderString(_name); - } - } - - // if string has a : in first line will set indent to follow it on - // subsequent lines - void printHeaderString(std::string const& _string, std::size_t indent = 0) - { - std::size_t i = _string.find(": "); - if (i != std::string::npos) - i += 2; - else - i = 0; - stream << Text(_string, TextAttributes() - .setIndent(indent + i) - .setInitialIndent(indent)) - << "\n"; - } - - struct SummaryColumn - { - - SummaryColumn(std::string const& _label, Colour::Code _colour) - : label(_label), - colour(_colour) - { - } - SummaryColumn addRow(std::size_t count) - { - std::ostringstream oss; - oss << count; - std::string row = oss.str(); - for (std::vector::iterator it = rows.begin(); it != rows.end(); ++it) - { - while (it->size() < row.size()) - *it = " " + *it; - while (it->size() > row.size()) - row = " " + row; - } - rows.push_back(row); - return *this; - } - - std::string label; - Colour::Code colour; - std::vector rows; - }; - - void printTotals(Totals const& totals) - { - if (totals.testCases.total() == 0) - { - stream << Colour(Colour::Warning) << "No tests ran\n"; - } - else if (totals.assertions.total() > 0 && totals.assertions.allPassed()) - { - stream << Colour(Colour::ResultSuccess) << "All tests passed"; - stream << " (" - << pluralise(totals.assertions.passed, "assertion") << " in " - << pluralise(totals.testCases.passed, "test case") << ")" - << "\n"; - } - else - { - - std::vector columns; - columns.push_back(SummaryColumn("", Colour::None) - .addRow(totals.testCases.total()) - .addRow(totals.assertions.total())); - columns.push_back(SummaryColumn("passed", Colour::Success) - .addRow(totals.testCases.passed) - .addRow(totals.assertions.passed)); - columns.push_back(SummaryColumn("failed", Colour::ResultError) - .addRow(totals.testCases.failed) - .addRow(totals.assertions.failed)); - columns.push_back(SummaryColumn("failed as expected", Colour::ResultExpectedFailure) - .addRow(totals.testCases.failedButOk) - .addRow(totals.assertions.failedButOk)); - - printSummaryRow("test cases", columns, 0); - printSummaryRow("assertions", columns, 1); - } - } - void printSummaryRow(std::string const& label, std::vector const& cols, std::size_t row) - { - for (std::vector::const_iterator it = cols.begin(); it != cols.end(); ++it) - { - std::string value = it->rows[row]; - if (it->label.empty()) - { - stream << label << ": "; - if (value != "0") - stream << value; - else - stream << Colour(Colour::Warning) << "- none -"; - } - else if (value != "0") - { - stream << Colour(Colour::LightGrey) << " | "; - stream << Colour(it->colour) - << value << " " << it->label; - } - } - stream << "\n"; - } - - static std::size_t makeRatio(std::size_t number, std::size_t total) - { - std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0; - return (ratio == 0 && number > 0) ? 1 : ratio; - } - static std::size_t& findMax(std::size_t& i, std::size_t& j, std::size_t& k) - { - if (i > j && i > k) - return i; - else if (j > k) - return j; - else - return k; - } - - void printTotalsDivider(Totals const& totals) - { - if (totals.testCases.total() > 0) - { - std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total()); - std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total()); - std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total()); - while (failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1) - findMax(failedRatio, failedButOkRatio, passedRatio)++; - while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1) - findMax(failedRatio, failedButOkRatio, passedRatio)--; - - stream << Colour(Colour::Error) << std::string(failedRatio, '='); - stream << Colour(Colour::ResultExpectedFailure) << std::string(failedButOkRatio, '='); - if (totals.testCases.allPassed()) - stream << Colour(Colour::ResultSuccess) << std::string(passedRatio, '='); - else - stream << Colour(Colour::Success) << std::string(passedRatio, '='); - } - else - { - stream << Colour(Colour::Warning) << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '='); - } - stream << "\n"; - } - void printSummaryDivider() - { - stream << getLineOfChars<'-'>() << "\n"; - } - - private: - bool m_headerPrinted; -}; - -INTERNAL_CATCH_REGISTER_REPORTER("console", ConsoleReporter) + INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter ) } // end namespace Catch @@ -11039,384 +10856,351 @@ INTERNAL_CATCH_REGISTER_REPORTER("console", ConsoleReporter) namespace Catch { -struct CompactReporter : StreamingReporterBase -{ + struct CompactReporter : StreamingReporterBase { - CompactReporter(ReporterConfig const& _config) - : StreamingReporterBase(_config) - { - } + CompactReporter( ReporterConfig const& _config ) + : StreamingReporterBase( _config ) + {} - virtual ~CompactReporter(); + virtual ~CompactReporter(); - static std::string getDescription() - { - return "Reports test results on a single line, suitable for IDEs"; - } - - virtual ReporterPreferences getPreferences() const - { - ReporterPreferences prefs; - prefs.shouldRedirectStdOut = false; - return prefs; - } - - virtual void noMatchingTestCases(std::string const& spec) - { - stream << "No test cases matched '" << spec << "'" << std::endl; - } - - virtual void assertionStarting(AssertionInfo const&) - { - } - - virtual bool assertionEnded(AssertionStats const& _assertionStats) - { - AssertionResult const& result = _assertionStats.assertionResult; - - bool printInfoMessages = true; - - // Drop out if result was successful and we're not printing those - if (!m_config->includeSuccessfulResults() && result.isOk()) - { - if (result.getResultType() != ResultWas::Warning) - return false; - printInfoMessages = false; + static std::string getDescription() { + return "Reports test results on a single line, suitable for IDEs"; } - AssertionPrinter printer(stream, _assertionStats, printInfoMessages); - printer.print(); - - stream << std::endl; - return true; - } - - virtual void testRunEnded(TestRunStats const& _testRunStats) - { - printTotals(_testRunStats.totals); - stream << "\n" - << std::endl; - StreamingReporterBase::testRunEnded(_testRunStats); - } - - private: - class AssertionPrinter - { - void operator=(AssertionPrinter const&); - - public: - AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages) - : stream(_stream), stats(_stats), result(_stats.assertionResult), messages(_stats.infoMessages), itMessage(_stats.infoMessages.begin()), printInfoMessages(_printInfoMessages) - { + virtual ReporterPreferences getPreferences() const { + ReporterPreferences prefs; + prefs.shouldRedirectStdOut = false; + return prefs; } - void print() - { - printSourceInfo(); + virtual void noMatchingTestCases( std::string const& spec ) { + stream << "No test cases matched '" << spec << '\'' << std::endl; + } - itMessage = messages.begin(); + virtual void assertionStarting( AssertionInfo const& ) {} - switch (result.getResultType()) - { - case ResultWas::Ok: - printResultType(Colour::ResultSuccess, passedString()); - printOriginalExpression(); - printReconstructedExpression(); - if (!result.hasExpression()) - printRemainingMessages(Colour::None); - else - printRemainingMessages(); - break; - case ResultWas::ExpressionFailed: - if (result.isOk()) - printResultType(Colour::ResultSuccess, failedString() + std::string(" - but was ok")); - else - printResultType(Colour::Error, failedString()); - printOriginalExpression(); - printReconstructedExpression(); - printRemainingMessages(); - break; - case ResultWas::ThrewException: - printResultType(Colour::Error, failedString()); - printIssue("unexpected exception with message:"); - printMessage(); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::FatalErrorCondition: - printResultType(Colour::Error, failedString()); - printIssue("fatal error condition with message:"); - printMessage(); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::DidntThrowException: - printResultType(Colour::Error, failedString()); - printIssue("expected exception, got none"); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::Info: - printResultType(Colour::None, "info"); - printMessage(); - printRemainingMessages(); - break; - case ResultWas::Warning: - printResultType(Colour::None, "warning"); - printMessage(); - printRemainingMessages(); - break; - case ResultWas::ExplicitFailure: - printResultType(Colour::Error, failedString()); - printIssue("explicitly"); - printRemainingMessages(Colour::None); - break; - // These cases are here to prevent compiler warnings - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: - printResultType(Colour::Error, "** internal error **"); - break; + virtual bool assertionEnded( AssertionStats const& _assertionStats ) { + AssertionResult const& result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if( !m_config->includeSuccessfulResults() && result.isOk() ) { + if( result.getResultType() != ResultWas::Warning ) + return false; + printInfoMessages = false; + } + + AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); + printer.print(); + + stream << std::endl; + return true; + } + + virtual void sectionEnded(SectionStats const& _sectionStats) CATCH_OVERRIDE { + if (m_config->showDurations() == ShowDurations::Always) { + stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; } } - private: - // Colour::LightGrey + virtual void testRunEnded( TestRunStats const& _testRunStats ) { + printTotals( _testRunStats.totals ); + stream << '\n' << std::endl; + StreamingReporterBase::testRunEnded( _testRunStats ); + } - static Colour::Code dimColour() { return Colour::FileName; } + private: + class AssertionPrinter { + void operator= ( AssertionPrinter const& ); + public: + AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) + : stream( _stream ) + , stats( _stats ) + , result( _stats.assertionResult ) + , messages( _stats.infoMessages ) + , itMessage( _stats.infoMessages.begin() ) + , printInfoMessages( _printInfoMessages ) + {} + + void print() { + printSourceInfo(); + + itMessage = messages.begin(); + + switch( result.getResultType() ) { + case ResultWas::Ok: + printResultType( Colour::ResultSuccess, passedString() ); + printOriginalExpression(); + printReconstructedExpression(); + if ( ! result.hasExpression() ) + printRemainingMessages( Colour::None ); + else + printRemainingMessages(); + break; + case ResultWas::ExpressionFailed: + if( result.isOk() ) + printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) ); + else + printResultType( Colour::Error, failedString() ); + printOriginalExpression(); + printReconstructedExpression(); + printRemainingMessages(); + break; + case ResultWas::ThrewException: + printResultType( Colour::Error, failedString() ); + printIssue( "unexpected exception with message:" ); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::FatalErrorCondition: + printResultType( Colour::Error, failedString() ); + printIssue( "fatal error condition with message:" ); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::DidntThrowException: + printResultType( Colour::Error, failedString() ); + printIssue( "expected exception, got none" ); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::Info: + printResultType( Colour::None, "info" ); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::Warning: + printResultType( Colour::None, "warning" ); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::ExplicitFailure: + printResultType( Colour::Error, failedString() ); + printIssue( "explicitly" ); + printRemainingMessages( Colour::None ); + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + printResultType( Colour::Error, "** internal error **" ); + break; + } + } + + private: + // Colour::LightGrey + + static Colour::Code dimColour() { return Colour::FileName; } #ifdef CATCH_PLATFORM_MAC - static const char* failedString() - { - return "FAILED"; - } - static const char* passedString() { return "PASSED"; } + static const char* failedString() { return "FAILED"; } + static const char* passedString() { return "PASSED"; } #else - static const char* failedString() - { - return "failed"; - } - static const char* passedString() { return "passed"; } + static const char* failedString() { return "failed"; } + static const char* passedString() { return "passed"; } #endif - void printSourceInfo() const - { - Colour colourGuard(Colour::FileName); - stream << result.getSourceInfo() << ":"; - } - - void printResultType(Colour::Code colour, std::string passOrFail) const - { - if (!passOrFail.empty()) - { - { - Colour colourGuard(colour); - stream << " " << passOrFail; - } - stream << ":"; - } - } - - void printIssue(std::string issue) const - { - stream << " " << issue; - } - - void printExpressionWas() - { - if (result.hasExpression()) - { - stream << ";"; - { - Colour colour(dimColour()); - stream << " expression was:"; - } - printOriginalExpression(); - } - } - - void printOriginalExpression() const - { - if (result.hasExpression()) - { - stream << " " << result.getExpression(); - } - } - - void printReconstructedExpression() const - { - if (result.hasExpandedExpression()) - { - { - Colour colour(dimColour()); - stream << " for: "; - } - stream << result.getExpandedExpression(); - } - } - - void printMessage() - { - if (itMessage != messages.end()) - { - stream << " '" << itMessage->message << "'"; - ++itMessage; - } - } - - void printRemainingMessages(Colour::Code colour = dimColour()) - { - if (itMessage == messages.end()) - return; - - // using messages.end() directly yields compilation error: - std::vector::const_iterator itEnd = messages.end(); - const std::size_t N = static_cast(std::distance(itMessage, itEnd)); - - { - Colour colourGuard(colour); - stream << " with " << pluralise(N, "message") << ":"; + void printSourceInfo() const { + Colour colourGuard( Colour::FileName ); + stream << result.getSourceInfo() << ':'; } - for (; itMessage != itEnd;) - { - // If this assertion is a warning ignore any INFO messages - if (printInfoMessages || itMessage->type != ResultWas::Info) - { - stream << " '" << itMessage->message << "'"; - if (++itMessage != itEnd) + void printResultType( Colour::Code colour, std::string const& passOrFail ) const { + if( !passOrFail.empty() ) { { - Colour colourGuard(dimColour()); - stream << " and"; + Colour colourGuard( colour ); + stream << ' ' << passOrFail; + } + stream << ':'; + } + } + + void printIssue( std::string const& issue ) const { + stream << ' ' << issue; + } + + void printExpressionWas() { + if( result.hasExpression() ) { + stream << ';'; + { + Colour colour( dimColour() ); + stream << " expression was:"; + } + printOriginalExpression(); + } + } + + void printOriginalExpression() const { + if( result.hasExpression() ) { + stream << ' ' << result.getExpression(); + } + } + + void printReconstructedExpression() const { + if( result.hasExpandedExpression() ) { + { + Colour colour( dimColour() ); + stream << " for: "; + } + stream << result.getExpandedExpression(); + } + } + + void printMessage() { + if ( itMessage != messages.end() ) { + stream << " '" << itMessage->message << '\''; + ++itMessage; + } + } + + void printRemainingMessages( Colour::Code colour = dimColour() ) { + if ( itMessage == messages.end() ) + return; + + // using messages.end() directly yields compilation error: + std::vector::const_iterator itEnd = messages.end(); + const std::size_t N = static_cast( std::distance( itMessage, itEnd ) ); + + { + Colour colourGuard( colour ); + stream << " with " << pluralise( N, "message" ) << ':'; + } + + for(; itMessage != itEnd; ) { + // If this assertion is a warning ignore any INFO messages + if( printInfoMessages || itMessage->type != ResultWas::Info ) { + stream << " '" << itMessage->message << '\''; + if ( ++itMessage != itEnd ) { + Colour colourGuard( dimColour() ); + stream << " and"; + } } } } + + private: + std::ostream& stream; + AssertionStats const& stats; + AssertionResult const& result; + std::vector messages; + std::vector::const_iterator itMessage; + bool printInfoMessages; + }; + + // Colour, message variants: + // - white: No tests ran. + // - red: Failed [both/all] N test cases, failed [both/all] M assertions. + // - white: Passed [both/all] N test cases (no assertions). + // - red: Failed N tests cases, failed M assertions. + // - green: Passed [both/all] N tests cases with M assertions. + + std::string bothOrAll( std::size_t count ) const { + return count == 1 ? std::string() : count == 2 ? "both " : "all " ; } - private: - std::ostream& stream; - AssertionStats const& stats; - AssertionResult const& result; - std::vector messages; - std::vector::const_iterator itMessage; - bool printInfoMessages; + void printTotals( const Totals& totals ) const { + if( totals.testCases.total() == 0 ) { + stream << "No tests ran."; + } + else if( totals.testCases.failed == totals.testCases.total() ) { + Colour colour( Colour::ResultError ); + const std::string qualify_assertions_failed = + totals.assertions.failed == totals.assertions.total() ? + bothOrAll( totals.assertions.failed ) : std::string(); + stream << + "Failed " << bothOrAll( totals.testCases.failed ) + << pluralise( totals.testCases.failed, "test case" ) << ", " + "failed " << qualify_assertions_failed << + pluralise( totals.assertions.failed, "assertion" ) << '.'; + } + else if( totals.assertions.total() == 0 ) { + stream << + "Passed " << bothOrAll( totals.testCases.total() ) + << pluralise( totals.testCases.total(), "test case" ) + << " (no assertions)."; + } + else if( totals.assertions.failed ) { + Colour colour( Colour::ResultError ); + stream << + "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " + "failed " << pluralise( totals.assertions.failed, "assertion" ) << '.'; + } + else { + Colour colour( Colour::ResultSuccess ); + stream << + "Passed " << bothOrAll( totals.testCases.passed ) + << pluralise( totals.testCases.passed, "test case" ) << + " with " << pluralise( totals.assertions.passed, "assertion" ) << '.'; + } + } }; - // Colour, message variants: - // - white: No tests ran. - // - red: Failed [both/all] N test cases, failed [both/all] M assertions. - // - white: Passed [both/all] N test cases (no assertions). - // - red: Failed N tests cases, failed M assertions. - // - green: Passed [both/all] N tests cases with M assertions. - - std::string bothOrAll(std::size_t count) const - { - return count == 1 ? "" : count == 2 ? "both " : "all "; - } - - void printTotals(const Totals& totals) const - { - if (totals.testCases.total() == 0) - { - stream << "No tests ran."; - } - else if (totals.testCases.failed == totals.testCases.total()) - { - Colour colour(Colour::ResultError); - const std::string qualify_assertions_failed = - totals.assertions.failed == totals.assertions.total() ? bothOrAll(totals.assertions.failed) : ""; - stream << "Failed " << bothOrAll(totals.testCases.failed) - << pluralise(totals.testCases.failed, "test case") << ", " - "failed " - << qualify_assertions_failed << pluralise(totals.assertions.failed, "assertion") << "."; - } - else if (totals.assertions.total() == 0) - { - stream << "Passed " << bothOrAll(totals.testCases.total()) - << pluralise(totals.testCases.total(), "test case") - << " (no assertions)."; - } - else if (totals.assertions.failed) - { - Colour colour(Colour::ResultError); - stream << "Failed " << pluralise(totals.testCases.failed, "test case") << ", " - "failed " - << pluralise(totals.assertions.failed, "assertion") << "."; - } - else - { - Colour colour(Colour::ResultSuccess); - stream << "Passed " << bothOrAll(totals.testCases.passed) - << pluralise(totals.testCases.passed, "test case") << " with " << pluralise(totals.assertions.passed, "assertion") << "."; - } - } -}; - -INTERNAL_CATCH_REGISTER_REPORTER("compact", CompactReporter) + INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter ) } // end namespace Catch namespace Catch { -// These are all here to avoid warnings about not having any out of line -// virtual methods -NonCopyable::~NonCopyable() {} -IShared::~IShared() {} -IStream::~IStream() CATCH_NOEXCEPT {} -FileStream::~FileStream() CATCH_NOEXCEPT {} -CoutStream::~CoutStream() CATCH_NOEXCEPT {} -DebugOutStream::~DebugOutStream() CATCH_NOEXCEPT {} -StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {} -IContext::~IContext() {} -IResultCapture::~IResultCapture() {} -ITestCase::~ITestCase() {} -ITestCaseRegistry::~ITestCaseRegistry() {} -IRegistryHub::~IRegistryHub() {} -IMutableRegistryHub::~IMutableRegistryHub() {} -IExceptionTranslator::~IExceptionTranslator() {} -IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {} -IReporter::~IReporter() {} -IReporterFactory::~IReporterFactory() {} -IReporterRegistry::~IReporterRegistry() {} -IStreamingReporter::~IStreamingReporter() {} -AssertionStats::~AssertionStats() {} -SectionStats::~SectionStats() {} -TestCaseStats::~TestCaseStats() {} -TestGroupStats::~TestGroupStats() {} -TestRunStats::~TestRunStats() {} -CumulativeReporterBase::SectionNode::~SectionNode() {} -CumulativeReporterBase::~CumulativeReporterBase() {} + // These are all here to avoid warnings about not having any out of line + // virtual methods + NonCopyable::~NonCopyable() {} + IShared::~IShared() {} + IStream::~IStream() CATCH_NOEXCEPT {} + FileStream::~FileStream() CATCH_NOEXCEPT {} + CoutStream::~CoutStream() CATCH_NOEXCEPT {} + DebugOutStream::~DebugOutStream() CATCH_NOEXCEPT {} + StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {} + IContext::~IContext() {} + IResultCapture::~IResultCapture() {} + ITestCase::~ITestCase() {} + ITestCaseRegistry::~ITestCaseRegistry() {} + IRegistryHub::~IRegistryHub() {} + IMutableRegistryHub::~IMutableRegistryHub() {} + IExceptionTranslator::~IExceptionTranslator() {} + IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {} + IReporter::~IReporter() {} + IReporterFactory::~IReporterFactory() {} + IReporterRegistry::~IReporterRegistry() {} + IStreamingReporter::~IStreamingReporter() {} + AssertionStats::~AssertionStats() {} + SectionStats::~SectionStats() {} + TestCaseStats::~TestCaseStats() {} + TestGroupStats::~TestGroupStats() {} + TestRunStats::~TestRunStats() {} + CumulativeReporterBase::SectionNode::~SectionNode() {} + CumulativeReporterBase::~CumulativeReporterBase() {} -StreamingReporterBase::~StreamingReporterBase() {} -ConsoleReporter::~ConsoleReporter() {} -CompactReporter::~CompactReporter() {} -IRunner::~IRunner() {} -IMutableContext::~IMutableContext() {} -IConfig::~IConfig() {} -XmlReporter::~XmlReporter() {} -JunitReporter::~JunitReporter() {} -TestRegistry::~TestRegistry() {} -FreeFunctionTestCase::~FreeFunctionTestCase() {} -IGeneratorInfo::~IGeneratorInfo() {} -IGeneratorsForTest::~IGeneratorsForTest() {} -WildcardPattern::~WildcardPattern() {} -TestSpec::Pattern::~Pattern() {} -TestSpec::NamePattern::~NamePattern() {} -TestSpec::TagPattern::~TagPattern() {} -TestSpec::ExcludedPattern::~ExcludedPattern() {} + StreamingReporterBase::~StreamingReporterBase() {} + ConsoleReporter::~ConsoleReporter() {} + CompactReporter::~CompactReporter() {} + IRunner::~IRunner() {} + IMutableContext::~IMutableContext() {} + IConfig::~IConfig() {} + XmlReporter::~XmlReporter() {} + JunitReporter::~JunitReporter() {} + TestRegistry::~TestRegistry() {} + FreeFunctionTestCase::~FreeFunctionTestCase() {} + IGeneratorInfo::~IGeneratorInfo() {} + IGeneratorsForTest::~IGeneratorsForTest() {} + WildcardPattern::~WildcardPattern() {} + TestSpec::Pattern::~Pattern() {} + TestSpec::NamePattern::~NamePattern() {} + TestSpec::TagPattern::~TagPattern() {} + TestSpec::ExcludedPattern::~ExcludedPattern() {} + Matchers::Impl::MatcherUntypedBase::~MatcherUntypedBase() {} -Matchers::Impl::StdString::Equals::~Equals() {} -Matchers::Impl::StdString::Contains::~Contains() {} -Matchers::Impl::StdString::StartsWith::~StartsWith() {} -Matchers::Impl::StdString::EndsWith::~EndsWith() {} + void Config::dummy() {} -void Config::dummy() {} - -namespace TestCaseTracking { -ITracker::~ITracker() {} -TrackerBase::~TrackerBase() {} -SectionTracker::~SectionTracker() {} -IndexTracker::~IndexTracker() {} -} + namespace TestCaseTracking { + ITracker::~ITracker() {} + TrackerBase::~TrackerBase() {} + SectionTracker::~SectionTracker() {} + IndexTracker::~IndexTracker() {} + } } #ifdef __clang__ @@ -11432,28 +11216,27 @@ IndexTracker::~IndexTracker() {} #ifndef __OBJC__ // Standard C/C++ main entry point -int main(int argc, char* argv[]) -{ - return Catch::Session().run(argc, argv); +int main (int argc, char * argv[]) { + int result = Catch::Session().run( argc, argv ); + return ( result < 0xff ? result : 0xff ); } #else // __OBJC__ // Objective-C entry point -int main(int argc, char* const argv[]) -{ +int main (int argc, char * const argv[]) { #if !CATCH_ARC_ENABLED - NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; #endif Catch::registerTestMethods(); - int result = Catch::Session().run(argc, (char* const*)argv); + int result = Catch::Session().run( argc, (char* const*)argv ); #if !CATCH_ARC_ENABLED [pool drain]; #endif - return result; + return ( result < 0xff ? result : 0xff ); } #endif // __OBJC__ @@ -11461,7 +11244,7 @@ int main(int argc, char* const argv[]) #endif #ifdef CLARA_CONFIG_MAIN_NOT_DEFINED -#undef CLARA_CONFIG_MAIN +# undef CLARA_CONFIG_MAIN #endif ////// @@ -11469,145 +11252,171 @@ int main(int argc, char* const argv[]) // If this config identifier is defined then all CATCH macros are prefixed with CATCH_ #ifdef CATCH_CONFIG_PREFIX_ALL -#define CATCH_REQUIRE(expr) INTERNAL_CATCH_TEST(expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE") -#define CATCH_REQUIRE_FALSE(expr) INTERNAL_CATCH_TEST(expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE") +#if defined(CATCH_CONFIG_FAST_COMPILE) +#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr ) +#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) +#else +#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr ) +#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) +#endif -#define CATCH_REQUIRE_THROWS(expr) INTERNAL_CATCH_THROWS(expr, Catch::ResultDisposition::Normal, "", "CATCH_REQUIRE_THROWS") -#define CATCH_REQUIRE_THROWS_AS(expr, exceptionType) INTERNAL_CATCH_THROWS_AS(expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS") -#define CATCH_REQUIRE_THROWS_WITH(expr, matcher) INTERNAL_CATCH_THROWS(expr, Catch::ResultDisposition::Normal, matcher, "CATCH_REQUIRE_THROWS_WITH") -#define CATCH_REQUIRE_NOTHROW(expr) INTERNAL_CATCH_NO_THROW(expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW") +#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr ) +#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) +#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr ) -#define CATCH_CHECK(expr) INTERNAL_CATCH_TEST(expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK") -#define CATCH_CHECK_FALSE(expr) INTERNAL_CATCH_TEST(expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CATCH_CHECK_FALSE") -#define CATCH_CHECKED_IF(expr) INTERNAL_CATCH_IF(expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF") -#define CATCH_CHECKED_ELSE(expr) INTERNAL_CATCH_ELSE(expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE") -#define CATCH_CHECK_NOFAIL(expr) INTERNAL_CATCH_TEST(expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL") +#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr ) +#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr ) -#define CATCH_CHECK_THROWS(expr) INTERNAL_CATCH_THROWS(expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS") -#define CATCH_CHECK_THROWS_AS(expr, exceptionType) INTERNAL_CATCH_THROWS_AS(expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS") -#define CATCH_CHECK_THROWS_WITH(expr, matcher) INTERNAL_CATCH_THROWS(expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CATCH_CHECK_THROWS_WITH") -#define CATCH_CHECK_NOTHROW(expr) INTERNAL_CATCH_NO_THROW(expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW") +#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr ) +#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CHECK_THAT(arg, matcher) INTERNAL_CHECK_THAT(arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT") -#define CATCH_REQUIRE_THAT(arg, matcher) INTERNAL_CHECK_THAT(arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT") +#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) -#define CATCH_INFO(msg) INTERNAL_CATCH_INFO(msg, "CATCH_INFO") -#define CATCH_WARN(msg) INTERNAL_CATCH_MSG(Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg) -#define CATCH_SCOPED_INFO(msg) INTERNAL_CATCH_INFO(msg, "CATCH_INFO") -#define CATCH_CAPTURE(msg) INTERNAL_CATCH_INFO(#msg " := " << msg, "CATCH_CAPTURE") -#define CATCH_SCOPED_CAPTURE(msg) INTERNAL_CATCH_INFO(#msg " := " << msg, "CATCH_CAPTURE") +#if defined(CATCH_CONFIG_FAST_COMPILE) +#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#else +#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif + +#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) +#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) +#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) +#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) ) +#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) ) #ifdef CATCH_CONFIG_VARIADIC_MACROS -#define CATCH_TEST_CASE(...) INTERNAL_CATCH_TESTCASE(__VA_ARGS__) -#define CATCH_TEST_CASE_METHOD(className, ...) INTERNAL_CATCH_TEST_CASE_METHOD(className, __VA_ARGS__) -#define CATCH_METHOD_AS_TEST_CASE(method, ...) INTERNAL_CATCH_METHOD_AS_TEST_CASE(method, __VA_ARGS__) -#define CATCH_REGISTER_TEST_CASE(...) INTERNAL_CATCH_REGISTER_TESTCASE(__VA_ARGS__) -#define CATCH_SECTION(...) INTERNAL_CATCH_SECTION(__VA_ARGS__) -#define CATCH_FAIL(...) INTERNAL_CATCH_MSG(Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__) -#define CATCH_SUCCEED(...) INTERNAL_CATCH_MSG(Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__) + #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) + #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) + #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) + #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) + #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) + #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ ) + #define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #else -#define CATCH_TEST_CASE(name, description) INTERNAL_CATCH_TESTCASE(name, description) -#define CATCH_TEST_CASE_METHOD(className, name, description) INTERNAL_CATCH_TEST_CASE_METHOD(className, name, description) -#define CATCH_METHOD_AS_TEST_CASE(method, name, description) INTERNAL_CATCH_METHOD_AS_TEST_CASE(method, name, description) -#define CATCH_REGISTER_TEST_CASE(function, name, description) INTERNAL_CATCH_REGISTER_TESTCASE(function, name, description) -#define CATCH_SECTION(name, description) INTERNAL_CATCH_SECTION(name, description) -#define CATCH_FAIL(msg) INTERNAL_CATCH_MSG(Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg) -#define CATCH_SUCCEED(msg) INTERNAL_CATCH_MSG(Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg) + #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) + #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) + #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) + #define CATCH_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, name, description ) + #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) + #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg ) + #define CATCH_FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg ) + #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg ) #endif -#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE("", "") +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) -#define CATCH_REGISTER_REPORTER(name, reporterType) INTERNAL_CATCH_REGISTER_REPORTER(name, reporterType) -#define CATCH_REGISTER_LEGACY_REPORTER(name, reporterType) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER(name, reporterType) +#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) +#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) -#define CATCH_GENERATE(expr) INTERNAL_CATCH_GENERATE(expr) +#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) // "BDD-style" convenience wrappers #ifdef CATCH_CONFIG_VARIADIC_MACROS -#define CATCH_SCENARIO(...) CATCH_TEST_CASE("Scenario: " __VA_ARGS__) -#define CATCH_SCENARIO_METHOD(className, ...) INTERNAL_CATCH_TEST_CASE_METHOD(className, "Scenario: " __VA_ARGS__) +#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) +#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) #else -#define CATCH_SCENARIO(name, tags) CATCH_TEST_CASE("Scenario: " name, tags) -#define CATCH_SCENARIO_METHOD(className, name, tags) INTERNAL_CATCH_TEST_CASE_METHOD(className, "Scenario: " name, tags) +#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags ) +#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) #endif -#define CATCH_GIVEN(desc) CATCH_SECTION(std::string("Given: ") + desc, "") -#define CATCH_WHEN(desc) CATCH_SECTION(std::string(" When: ") + desc, "") -#define CATCH_AND_WHEN(desc) CATCH_SECTION(std::string(" And: ") + desc, "") -#define CATCH_THEN(desc) CATCH_SECTION(std::string(" Then: ") + desc, "") -#define CATCH_AND_THEN(desc) CATCH_SECTION(std::string(" And: ") + desc, "") +#define CATCH_GIVEN( desc ) CATCH_SECTION( std::string( "Given: ") + desc, "" ) +#define CATCH_WHEN( desc ) CATCH_SECTION( std::string( " When: ") + desc, "" ) +#define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" ) +#define CATCH_THEN( desc ) CATCH_SECTION( std::string( " Then: ") + desc, "" ) +#define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" ) // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required #else -#define REQUIRE(expr) INTERNAL_CATCH_TEST(expr, Catch::ResultDisposition::Normal, "REQUIRE") -#define REQUIRE_FALSE(expr) INTERNAL_CATCH_TEST(expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE") +#if defined(CATCH_CONFIG_FAST_COMPILE) +#define REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE", Catch::ResultDisposition::Normal, expr ) +#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) -#define REQUIRE_THROWS(expr) INTERNAL_CATCH_THROWS(expr, Catch::ResultDisposition::Normal, "", "REQUIRE_THROWS") -#define REQUIRE_THROWS_AS(expr, exceptionType) INTERNAL_CATCH_THROWS_AS(expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS") -#define REQUIRE_THROWS_WITH(expr, matcher) INTERNAL_CATCH_THROWS(expr, Catch::ResultDisposition::Normal, matcher, "REQUIRE_THROWS_WITH") -#define REQUIRE_NOTHROW(expr) INTERNAL_CATCH_NO_THROW(expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW") +#else +#define REQUIRE( expr ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, expr ) +#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) +#endif -#define CHECK(expr) INTERNAL_CATCH_TEST(expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK") -#define CHECK_FALSE(expr) INTERNAL_CATCH_TEST(expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CHECK_FALSE") -#define CHECKED_IF(expr) INTERNAL_CATCH_IF(expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF") -#define CHECKED_ELSE(expr) INTERNAL_CATCH_ELSE(expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE") -#define CHECK_NOFAIL(expr) INTERNAL_CATCH_TEST(expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL") +#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr ) +#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) +#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr ) -#define CHECK_THROWS(expr) INTERNAL_CATCH_THROWS(expr, Catch::ResultDisposition::ContinueOnFailure, "", "CHECK_THROWS") -#define CHECK_THROWS_AS(expr, exceptionType) INTERNAL_CATCH_THROWS_AS(expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS") -#define CHECK_THROWS_WITH(expr, matcher) INTERNAL_CATCH_THROWS(expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CHECK_THROWS_WITH") -#define CHECK_NOTHROW(expr) INTERNAL_CATCH_NO_THROW(expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW") +#define CHECK( expr ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr ) +#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr ) -#define CHECK_THAT(arg, matcher) INTERNAL_CHECK_THAT(arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT") -#define REQUIRE_THAT(arg, matcher) INTERNAL_CHECK_THAT(arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT") +#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr ) +#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define INFO(msg) INTERNAL_CATCH_INFO(msg, "INFO") -#define WARN(msg) INTERNAL_CATCH_MSG(Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg) -#define SCOPED_INFO(msg) INTERNAL_CATCH_INFO(msg, "INFO") -#define CAPTURE(msg) INTERNAL_CATCH_INFO(#msg " := " << msg, "CAPTURE") -#define SCOPED_CAPTURE(msg) INTERNAL_CATCH_INFO(#msg " := " << msg, "CAPTURE") +#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) + +#if defined(CATCH_CONFIG_FAST_COMPILE) +#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#else +#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif + +#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) +#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) +#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) +#define CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) ) +#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) ) #ifdef CATCH_CONFIG_VARIADIC_MACROS -#define TEST_CASE(...) INTERNAL_CATCH_TESTCASE(__VA_ARGS__) -#define TEST_CASE_METHOD(className, ...) INTERNAL_CATCH_TEST_CASE_METHOD(className, __VA_ARGS__) -#define METHOD_AS_TEST_CASE(method, ...) INTERNAL_CATCH_METHOD_AS_TEST_CASE(method, __VA_ARGS__) -#define REGISTER_TEST_CASE(...) INTERNAL_CATCH_REGISTER_TESTCASE(__VA_ARGS__) -#define SECTION(...) INTERNAL_CATCH_SECTION(__VA_ARGS__) -#define FAIL(...) INTERNAL_CATCH_MSG(Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__) -#define SUCCEED(...) INTERNAL_CATCH_MSG(Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__) +#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) +#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) +#define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) +#define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) +#define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #else -#define TEST_CASE(name, description) INTERNAL_CATCH_TESTCASE(name, description) -#define TEST_CASE_METHOD(className, name, description) INTERNAL_CATCH_TEST_CASE_METHOD(className, name, description) -#define METHOD_AS_TEST_CASE(method, name, description) INTERNAL_CATCH_METHOD_AS_TEST_CASE(method, name, description) -#define REGISTER_TEST_CASE(method, name, description) INTERNAL_CATCH_REGISTER_TESTCASE(method, name, description) -#define SECTION(name, description) INTERNAL_CATCH_SECTION(name, description) -#define FAIL(msg) INTERNAL_CATCH_MSG(Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg) -#define SUCCEED(msg) INTERNAL_CATCH_MSG(Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg) +#define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) + #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) + #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) + #define REGISTER_TEST_CASE( method, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( method, name, description ) + #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) + #define FAIL( msg ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg ) + #define FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg ) + #define SUCCEED( msg ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg ) #endif -#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE("", "") +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) -#define REGISTER_REPORTER(name, reporterType) INTERNAL_CATCH_REGISTER_REPORTER(name, reporterType) -#define REGISTER_LEGACY_REPORTER(name, reporterType) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER(name, reporterType) +#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) +#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) -#define GENERATE(expr) INTERNAL_CATCH_GENERATE(expr) +#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) #endif -#define CATCH_TRANSLATE_EXCEPTION(signature) INTERNAL_CATCH_TRANSLATE_EXCEPTION(signature) +#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) // "BDD-style" convenience wrappers #ifdef CATCH_CONFIG_VARIADIC_MACROS -#define SCENARIO(...) TEST_CASE("Scenario: " __VA_ARGS__) -#define SCENARIO_METHOD(className, ...) INTERNAL_CATCH_TEST_CASE_METHOD(className, "Scenario: " __VA_ARGS__) +#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) +#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) #else -#define SCENARIO(name, tags) TEST_CASE("Scenario: " name, tags) -#define SCENARIO_METHOD(className, name, tags) INTERNAL_CATCH_TEST_CASE_METHOD(className, "Scenario: " name, tags) +#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags ) +#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) #endif -#define GIVEN(desc) SECTION(std::string(" Given: ") + desc, "") -#define WHEN(desc) SECTION(std::string(" When: ") + desc, "") -#define AND_WHEN(desc) SECTION(std::string("And when: ") + desc, "") -#define THEN(desc) SECTION(std::string(" Then: ") + desc, "") -#define AND_THEN(desc) SECTION(std::string(" And: ") + desc, "") +#define GIVEN( desc ) SECTION( std::string(" Given: ") + desc, "" ) +#define WHEN( desc ) SECTION( std::string(" When: ") + desc, "" ) +#define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc, "" ) +#define THEN( desc ) SECTION( std::string(" Then: ") + desc, "" ) +#define AND_THEN( desc ) SECTION( std::string(" And: ") + desc, "" ) using Catch::Detail::Approx; #endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED + diff --git a/third_party/variant/test/lambda_overload_test.cpp b/third_party/variant/test/lambda_overload_test.cpp index 9e76d991b..764921e63 100644 --- a/third_party/variant/test/lambda_overload_test.cpp +++ b/third_party/variant/test/lambda_overload_test.cpp @@ -10,6 +10,15 @@ using namespace mapbox::util; +template +struct tag +{ + static void dump(const char* prefix) + { + std::cout << prefix << ": " << typeid(tag).name() << std::endl; + } +}; + template using Either = mapbox::util::variant; @@ -55,6 +64,37 @@ void test_singleton_variant() apply_visitor(make_visitor([](int) {}), singleton); } +// See #180 +struct test_call_nonconst_member_visitor +{ + template + void operator() (T & obj) const + { + tag::dump("test_call_nonconst_member: visitor"); + obj.foo(); + } +}; + +void test_call_nonconst_member() +{ + struct object + { + void foo() { val = 42;} + int val = 0; + }; + + variant v = object{}; + apply_visitor(test_call_nonconst_member_visitor{}, v); + +#ifdef HAS_CPP14_SUPPORT + apply_visitor([](auto& obj) + { + tag::dump("test_call_nonconst_member: lambda"); + obj.foo(); + }, v); +#endif +} + void test_lambda_overloads_sfinae() #ifdef HAS_CPP14_SUPPORT { @@ -85,6 +125,9 @@ void test_match_singleton() { variant singleton = 5; singleton.match([](int) {}); + + auto lambda = [](int) {}; + singleton.match(lambda); } void test_match_overloads() @@ -112,16 +155,128 @@ void test_match_overloads_capture() std::cout << "Got " << ok << " ok, " << err << " err" << std::endl; } +struct MovableOnly +{ + MovableOnly() = default; + + MovableOnly(MovableOnly&&) = default; + MovableOnly& operator=(MovableOnly&&) = default; +}; + +struct MovableCopyable +{ + MovableCopyable() = default; + + MovableCopyable(MovableCopyable&&) = default; + MovableCopyable& operator=(MovableCopyable&&) = default; + + MovableCopyable(const MovableCopyable&) = default; + MovableCopyable& operator=(const MovableCopyable&) = default; +}; + +void test_match_overloads_init_capture() +#ifdef HAS_CPP14_SUPPORT +{ + Either rv; + + rv = Error{}; + + rv.match([p = MovableOnly{}](auto&&) {}); + { + auto lambda = [p = MovableCopyable{}](auto&&) {}; + rv.match(lambda); + + rv.match([p = MovableOnly{}](Response) { std::cout << "Response\n"; }, + [p = MovableOnly{}](Error) { std::cout << "Error\n"; }); + } + { + auto lambda = [](Error) { std::cout << "Error\n"; }; + rv.match([p = MovableOnly{}](Response) { std::cout << "Response\n"; }, + lambda); + rv.match(lambda, + [p = MovableOnly{}](Response) { std::cout << "Response\n"; }); + } +} +#else +{ +} +#endif + +// See #140 +void test_match_overloads_otherwise() +#ifdef HAS_CPP14_SUPPORT +{ + + struct Center + { + }; + struct Indent + { + }; + struct Justify + { + }; + struct None + { + }; + + using Properties = mapbox::util::variant; + + Properties props = Justify{}; + + props.match([&](Center) { std::cout << "Center\n"; }, // + [&](Indent) { std::cout << "Indent\n"; }, // + [&](auto&&) { std::cout << "Otherwise\n"; }); // +} +#else +{ +} +#endif + +template +struct Moveable +{ + Moveable() = default; // Default constructible + + Moveable(const Moveable&) = delete; // Disable copy ctor + Moveable& operator=(const Moveable&) = delete; // Disable copy assign op + + Moveable(Moveable&&) = default; // Enable move ctor + Moveable& operator=(Moveable&&) = default; // Enable move assign op +}; + +void test_match_move_out_of_variant() +{ + // Distinguishable at type level + using T1 = Moveable; + using T2 = Moveable; + using T3 = mapbox::util::recursive_wrapper; + + mapbox::util::variant v = T1{}; + + std::move(v).match([](T1&&) {}, // Consume T1 by value + [](T2&&) {}); // Consume T2 by value + + mapbox::util::variant w = T2{}; + + std::move(w).match([](int&&) {}, // Consume unwrapped int + [](T2&&) {}); // Consume T2 by value +} + int main() { test_lambda_overloads(); test_singleton_variant(); + test_call_nonconst_member(); test_lambda_overloads_capture(); test_lambda_overloads_sfinae(); test_match_singleton(); test_match_overloads(); test_match_overloads_capture(); + test_match_overloads_init_capture(); + test_match_overloads_otherwise(); + test_match_move_out_of_variant(); } #undef HAS_CPP14_SUPPORT diff --git a/third_party/variant/test/recursive_wrapper_test.cpp b/third_party/variant/test/recursive_wrapper_test.cpp index 57143cbf9..2d9f5f9d5 100644 --- a/third_party/variant/test/recursive_wrapper_test.cpp +++ b/third_party/variant/test/recursive_wrapper_test.cpp @@ -1,4 +1,3 @@ - #include #include #include @@ -64,12 +63,12 @@ struct calculator int operator()(binary_op const& binary) const { - return util::apply_visitor(calculator(), binary.left) + util::apply_visitor(calculator(), binary.right); + return util::apply_visitor(*this, binary.left) + util::apply_visitor(*this, binary.right); } int operator()(binary_op const& binary) const { - return util::apply_visitor(calculator(), binary.left) - util::apply_visitor(calculator(), binary.right); + return util::apply_visitor(*this, binary.left) - util::apply_visitor(*this, binary.right); } }; @@ -83,12 +82,12 @@ struct to_string std::string operator()(binary_op const& binary) const { - return util::apply_visitor(to_string(), binary.left) + std::string("+") + util::apply_visitor(to_string(), binary.right); + return util::apply_visitor(*this, binary.left) + std::string("+") + util::apply_visitor(*this, binary.right); } std::string operator()(binary_op const& binary) const { - return util::apply_visitor(to_string(), binary.left) + std::string("-") + util::apply_visitor(to_string(), binary.right); + return util::apply_visitor(*this, binary.left) + std::string("-") + util::apply_visitor(*this, binary.right); } }; diff --git a/third_party/variant/test/t/binary_visitor_impl.hpp b/third_party/variant/test/t/binary_visitor_impl.hpp index f2db68b75..4ee1f08e7 100644 --- a/third_party/variant/test/t/binary_visitor_impl.hpp +++ b/third_party/variant/test/t/binary_visitor_impl.hpp @@ -160,8 +160,8 @@ struct swap_visitor { using T = typename std::common_type::type; T tmp = a; - a = b; - b = tmp; + a = static_cast(b); + b = static_cast(tmp); } }; diff --git a/third_party/variant/test/t/issue21.cpp b/third_party/variant/test/t/issue21.cpp index a33d35982..4e6be8a6f 100644 --- a/third_party/variant/test/t/issue21.cpp +++ b/third_party/variant/test/t/issue21.cpp @@ -46,9 +46,7 @@ TEST_CASE("set() works cleanly even if the constructor throws ", "[variant]") variant_type v = obj; REQUIRE(v.is()); REQUIRE(v.get().value == 42); - REQUIRE_THROWS({ - v.set(13); - }); + REQUIRE_THROWS(v.set(13)); } REQUIRE(count == 0); } diff --git a/third_party/variant/test/t/nothrow_move.cpp b/third_party/variant/test/t/nothrow_move.cpp new file mode 100644 index 000000000..b41c8fe9b --- /dev/null +++ b/third_party/variant/test/t/nothrow_move.cpp @@ -0,0 +1,66 @@ +#include +#include + +#include + +using namespace mapbox; + +namespace test { + +struct t_noexcept_true_1 { + t_noexcept_true_1(t_noexcept_true_1&&) noexcept = default; + t_noexcept_true_1& operator=(t_noexcept_true_1&&) noexcept = default; +}; + +struct t_noexcept_true_2 { + t_noexcept_true_2(t_noexcept_true_2&&) noexcept = default; + t_noexcept_true_2& operator=(t_noexcept_true_2&&) noexcept = default; +}; + +struct t_noexcept_false_1 { + t_noexcept_false_1(t_noexcept_false_1&&) noexcept(false) {} + t_noexcept_false_1& operator=(t_noexcept_false_1&&) noexcept(false) { return *this; } +}; + +using should_be_no_throw_copyable = util::variant; +static_assert(std::is_nothrow_move_assignable::value, + "variants with no-throw move assignable types should be " + "no-throw move nothrow assignable"); + +using should_be_no_throw_assignable = util::variant; +static_assert(std::is_nothrow_move_constructible::value, + "variants with no-throw move assignable types should be " + "no-throw move nothrow assignable"); + +using should_not_be_no_throw_copyable = util::variant; +static_assert(not std::is_nothrow_move_assignable::value, + "variants with no-throw move assignable types should be " + "no-throw move nothrow assignable"); + +using should_not_be_no_throw_assignable = util::variant; +static_assert(not std::is_nothrow_move_constructible::value, + "variants with no-throw move assignable types should be " + "no-throw move nothrow assignable"); + + +// this type cannot be nothrow converted from either of its types, even the nothrow moveable one, +// because the conversion operator moves the whole variant. +using convertable_test_type = util::variant; + +// this type can be nothrow converted from either of its types. +using convertable_test_type_2 = util::variant; + +static_assert(not std::is_nothrow_assignable::value, + "variants with noexcept(true) move constructible types should be nothrow-convertible " + "from those types only IF the variant itself is nothrow_move_assignable"); + +static_assert(not std::is_nothrow_assignable::value, + "variants with noexcept(false) move constructible types should not be nothrow-convertible " + "from those types"); + +static_assert(std::is_nothrow_assignable::value, + "variants with noexcept(true) move constructible types should be nothrow-convertible " + "from those types only IF the variant itself is nothrow_move_assignable"); + + +} // namespace test diff --git a/third_party/variant/test/t/optional.cpp b/third_party/variant/test/t/optional.cpp index 8f08f7cd9..4f77a0af9 100644 --- a/third_party/variant/test/t/optional.cpp +++ b/third_party/variant/test/t/optional.cpp @@ -1,4 +1,3 @@ - #include "catch.hpp" #include @@ -97,6 +96,8 @@ TEST_CASE("self assignment", "[optional]") a = 1; REQUIRE(a.get() == 1); +#if !defined(__clang__) a = a; REQUIRE(a.get() == 1); +#endif } diff --git a/third_party/variant/test/t/recursive_wrapper.cpp b/third_party/variant/test/t/recursive_wrapper.cpp index c3d53f0f8..0a6848da8 100644 --- a/third_party/variant/test/t/recursive_wrapper.cpp +++ b/third_party/variant/test/t/recursive_wrapper.cpp @@ -1,6 +1,6 @@ - #include "catch.hpp" +#include #include #include @@ -153,6 +153,33 @@ TEST_CASE("recursive wrapper of pair") b = std::move(c); REQUIRE(b.get().first == 5); REQUIRE(b.get().second == 6); - // REQUIRE(c.get_pointer() == nullptr); + //REQUIRE(c.get_pointer() == nullptr); + } + + SECTION("Multiple recurssive wrappers of polymorphic types") + { + // https://github.com/mapbox/variant/issues/146 + // (Visual Studio 2015 update 3) + using namespace mapbox::util; + struct Base; + struct Derived; + using Variant = variant, recursive_wrapper>; + struct Base { }; + struct Derived : public Base { }; + { + Base base; + Derived derived; + Variant v; + v = base; + v = derived; // compile error prior https://github.com/mapbox/variant/pull/147 + CHECK(v.is()); + } + { + Derived derived; + Variant v(derived); // compile error prior https://github.com/mapbox/variant/pull/147 + CHECK(v.is()); + } + + } } diff --git a/third_party/variant/test/t/sizeof.cpp b/third_party/variant/test/t/sizeof.cpp index 607fabc6d..72314e2b8 100644 --- a/third_party/variant/test/t/sizeof.cpp +++ b/third_party/variant/test/t/sizeof.cpp @@ -1,4 +1,3 @@ - #include #include #include @@ -15,7 +14,7 @@ struct some_struct std::string c; }; -using variant_internal_index_type = size_t; +using variant_internal_index_type = mapbox::util::type_index_t; TEST_CASE("size of variants") { diff --git a/third_party/variant/test/t/variant.cpp b/third_party/variant/test/t/variant.cpp index b4322a26f..2c6d6669d 100644 --- a/third_party/variant/test/t/variant.cpp +++ b/third_party/variant/test/t/variant.cpp @@ -208,9 +208,7 @@ TEST_CASE("get with wrong type (here: double) should throw", "[variant]") REQUIRE(var.is()); REQUIRE_FALSE(var.is()); REQUIRE(var.get() == 5); - REQUIRE_THROWS_AS({ - var.get(); - }, + REQUIRE_THROWS_AS(var.get(), mapbox::util::bad_variant_access&); } @@ -222,13 +220,9 @@ TEST_CASE("get with wrong type (here: int) should throw", "[variant]") REQUIRE_FALSE(var.is()); REQUIRE(var.get() == 5.0); REQUIRE(mapbox::util::get(var) == 5.0); - REQUIRE_THROWS_AS({ - var.get(); - }, + REQUIRE_THROWS_AS(var.get(), mapbox::util::bad_variant_access&); - REQUIRE_THROWS_AS({ - mapbox::util::get(var); - }, + REQUIRE_THROWS_AS(mapbox::util::get(var), mapbox::util::bad_variant_access&); } @@ -240,26 +234,18 @@ TEST_CASE("get on const varint with wrong type (here: int) should throw", "[vari REQUIRE_FALSE(var.is()); REQUIRE(var.get() == 5.0); REQUIRE(mapbox::util::get(var) == 5.0); - REQUIRE_THROWS_AS({ - var.get(); - }, + REQUIRE_THROWS_AS(var.get(), mapbox::util::bad_variant_access&); - REQUIRE_THROWS_AS({ - mapbox::util::get(var); - }, + REQUIRE_THROWS_AS(mapbox::util::get(var), mapbox::util::bad_variant_access&); } TEST_CASE("get with any type should throw if not initialized", "[variant]") { mapbox::util::variant var{mapbox::util::no_init()}; - REQUIRE_THROWS_AS({ - var.get(); - }, + REQUIRE_THROWS_AS(var.get(), mapbox::util::bad_variant_access&); - REQUIRE_THROWS_AS({ - var.get(); - }, + REQUIRE_THROWS_AS(var.get(), mapbox::util::bad_variant_access&); } @@ -273,16 +259,12 @@ TEST_CASE("no_init variant can be copied and moved from", "[variant]") REQUIRE(v2.get() == 42); v2 = v1; - REQUIRE_THROWS_AS({ - v2.get(); - }, + REQUIRE_THROWS_AS(v2.get(), mapbox::util::bad_variant_access&); REQUIRE(v3.get() == 23); v3 = std::move(v1); - REQUIRE_THROWS_AS({ - v3.get(); - }, + REQUIRE_THROWS_AS(v3.get(), mapbox::util::bad_variant_access&); } @@ -294,9 +276,7 @@ TEST_CASE("no_init variant can be copied and moved to", "[variant]") variant_type v2{mapbox::util::no_init()}; variant_type v3{mapbox::util::no_init()}; - REQUIRE_THROWS_AS({ - v2.get(); - }, + REQUIRE_THROWS_AS(v2.get(), mapbox::util::bad_variant_access&); REQUIRE(v1.get() == 42); @@ -304,9 +284,7 @@ TEST_CASE("no_init variant can be copied and moved to", "[variant]") REQUIRE(v2.get() == 42); REQUIRE(v1.get() == 42); - REQUIRE_THROWS_AS({ - v3.get(); - }, + REQUIRE_THROWS_AS(v3.get(), mapbox::util::bad_variant_access&); v3 = std::move(v1); @@ -327,9 +305,7 @@ TEST_CASE("implicit conversion to first type in variant type list", "[variant][i using variant_type = mapbox::util::variant; variant_type var = 5l; // converted to long REQUIRE(var.get() == 5); - REQUIRE_THROWS_AS({ - var.get(); - }, + REQUIRE_THROWS_AS(var.get(), mapbox::util::bad_variant_access&); } @@ -498,13 +474,9 @@ TEST_CASE("storing reference wrappers works") variant_type v{std::ref(a)}; REQUIRE(v.get() == 1); REQUIRE(mapbox::util::get(v) == 1); - REQUIRE_THROWS_AS({ - v.get(); - }, + REQUIRE_THROWS_AS(v.get(), mapbox::util::bad_variant_access&); - REQUIRE_THROWS_AS({ - mapbox::util::get(v); - }, + REQUIRE_THROWS_AS(mapbox::util::get(v), mapbox::util::bad_variant_access&); a = 2; REQUIRE(v.get() == 2); @@ -515,13 +487,9 @@ TEST_CASE("storing reference wrappers works") v = std::ref(b); REQUIRE(v.get() == Approx(3.141)); REQUIRE(mapbox::util::get(v) == Approx(3.141)); - REQUIRE_THROWS_AS({ - v.get(); - }, + REQUIRE_THROWS_AS(v.get(), mapbox::util::bad_variant_access&); - REQUIRE_THROWS_AS({ - mapbox::util::get(v); - }, + REQUIRE_THROWS_AS(mapbox::util::get(v), mapbox::util::bad_variant_access&); b = 2.718; REQUIRE(v.get() == Approx(2.718)); @@ -530,9 +498,7 @@ TEST_CASE("storing reference wrappers works") v.get() = 4.1; REQUIRE(b == Approx(4.1)); - REQUIRE_THROWS_AS({ - v.get() = 4; - }, + REQUIRE_THROWS_AS(v.get() = 4, mapbox::util::bad_variant_access&); } @@ -546,26 +512,18 @@ TEST_CASE("storing reference wrappers to consts works") REQUIRE(v.get() == 1); REQUIRE(mapbox::util::get(v) == 1); REQUIRE(mapbox::util::get(v) == 1); - REQUIRE_THROWS_AS({ - v.get(); - }, + REQUIRE_THROWS_AS(v.get(), mapbox::util::bad_variant_access&); - REQUIRE_THROWS_AS({ - mapbox::util::get(v); - }, + REQUIRE_THROWS_AS(mapbox::util::get(v), mapbox::util::bad_variant_access&); double b = 3.141; v = std::cref(b); REQUIRE(v.get() == Approx(3.141)); REQUIRE(mapbox::util::get(v) == Approx(3.141)); - REQUIRE_THROWS_AS({ - v.get(); - }, + REQUIRE_THROWS_AS(v.get(), mapbox::util::bad_variant_access&); - REQUIRE_THROWS_AS({ - mapbox::util::get(v); - }, + REQUIRE_THROWS_AS(mapbox::util::get(v), mapbox::util::bad_variant_access&); } diff --git a/third_party/variant/test/t/variant_alternative.cpp b/third_party/variant/test/t/variant_alternative.cpp new file mode 100644 index 000000000..eedfe5cfc --- /dev/null +++ b/third_party/variant/test/t/variant_alternative.cpp @@ -0,0 +1,31 @@ +#include "catch.hpp" + +#include +#include + +#include + +TEST_CASE("variant_alternative", "[types]") +{ + using variant_type = mapbox::util::variant; + using type_0 = mapbox::util::variant_alternative<0, variant_type>::type; + using type_1 = mapbox::util::variant_alternative<1, variant_type>::type; + using type_2 = mapbox::util::variant_alternative<2, variant_type>::type; + //using type_3 = mapbox::util::variant_alternative<3, variant_type>::type; // compile error + constexpr bool check_0 = std::is_same::value; + constexpr bool check_1 = std::is_same::value; + constexpr bool check_2 = std::is_same::value; + CHECK(check_0); + CHECK(check_1); + CHECK(check_2); +} + +TEST_CASE("variant_size", "[types]") +{ + constexpr auto value_0 = mapbox::util::variant_size>::value; + constexpr auto value_1 = mapbox::util::variant_size>::value; + constexpr auto value_2 = mapbox::util::variant_size>::value; + CHECK(value_0 == 0); + CHECK(value_1 == 1); + CHECK(value_2 == 2); +} diff --git a/third_party/variant/test/t/visitor_result_type.cpp b/third_party/variant/test/t/visitor_result_type.cpp new file mode 100644 index 000000000..f673ae4cb --- /dev/null +++ b/third_party/variant/test/t/visitor_result_type.cpp @@ -0,0 +1,52 @@ +#include + +using namespace mapbox::util; + +namespace { + +template +struct tag {}; + +struct deduced_result_visitor +{ + template + tag operator() (T); + + template + tag operator() (T) const; + + template + tag operator() (T, U); + + template + tag operator() (T, U) const; +}; + +struct explicit_result_visitor : deduced_result_visitor +{ + using result_type = tag; +}; + +// Doing this compile-time test via assignment to typed tag objects gives +// more useful error messages when something goes wrong, than std::is_same +// in a static_assert would. Here if result_of_unary_visit returns anything +// other than the expected type on the left hand side, the conversion error +// message will tell you exactly what it was. + +#ifdef __clang__ +# pragma clang diagnostic ignored "-Wunused-variable" +#endif + +tag d1m = detail::result_of_unary_visit{}; +tag d1c = detail::result_of_unary_visit{}; + +tag e1m = detail::result_of_unary_visit{}; +tag e1c = detail::result_of_unary_visit{}; + +tag d2m = detail::result_of_binary_visit{}; +tag d2c = detail::result_of_binary_visit{}; + +tag e2m = detail::result_of_binary_visit{}; +tag e2c = detail::result_of_binary_visit{}; + +} // namespace From 640df69aa124668ee879c1c93bbb4b3234ae13b2 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Fri, 24 May 2024 22:43:03 +0200 Subject: [PATCH 03/28] Remove dead CRC32 processor code (#6900) --- include/contractor/crc32_processor.hpp | 128 ------------------------- src/contractor/contractor.cpp | 1 - 2 files changed, 129 deletions(-) delete mode 100644 include/contractor/crc32_processor.hpp diff --git a/include/contractor/crc32_processor.hpp b/include/contractor/crc32_processor.hpp deleted file mode 100644 index 81b0baa08..000000000 --- a/include/contractor/crc32_processor.hpp +++ /dev/null @@ -1,128 +0,0 @@ -#ifndef ITERATOR_BASED_CRC32_H -#define ITERATOR_BASED_CRC32_H - -#if defined(__x86_64__) && !defined(__MINGW64__) -#include -#endif - -#include // for boost::crc_32_type - -#include - -namespace osrm::contractor -{ - -class IteratorbasedCRC32 -{ - public: - bool UsingHardware() const { return use_hardware_implementation; } - - IteratorbasedCRC32() : crc(0) { use_hardware_implementation = DetectHardwareSupport(); } - - template unsigned operator()(Iterator iter, const Iterator end) - { - unsigned crc = 0; - while (iter != end) - { - using value_type = typename std::iterator_traits::value_type; - const char *data = reinterpret_cast(&(*iter)); - - if (use_hardware_implementation) - { - crc = ComputeInHardware(data, sizeof(value_type)); - } - else - { - crc = ComputeInSoftware(data, sizeof(value_type)); - } - ++iter; - } - return crc; - } - - private: - bool DetectHardwareSupport() const - { - static const int sse42_bit = 0x00100000; - const unsigned ecx = cpuid(); - const bool sse42_found = (ecx & sse42_bit) != 0; - return sse42_found; - } - - unsigned ComputeInSoftware(const char *str, unsigned len) - { - crc_processor.process_bytes(str, len); - return crc_processor.checksum(); - } - - // adapted from http://byteworm.com/2010/10/13/crc32/ - unsigned ComputeInHardware(const char *str, unsigned len) - { -#if defined(__x86_64__) - unsigned q = len / sizeof(unsigned); - unsigned r = len % sizeof(unsigned); - unsigned *p = (unsigned *)str; - - // crc=0; - while (q--) - { - __asm__ __volatile__(".byte 0xf2, 0xf, 0x38, 0xf1, 0xf1;" - : "=S"(crc) - : "0"(crc), "c"(*p)); - ++p; - } - - str = reinterpret_cast(p); - while (r--) - { - __asm__ __volatile__(".byte 0xf2, 0xf, 0x38, 0xf1, 0xf1;" - : "=S"(crc) - : "0"(crc), "c"(*str)); - ++str; - } -#else - (void)str; - (void)len; -#endif - return crc; - } - - inline unsigned cpuid() const - { - unsigned eax = 0, ebx = 0, ecx = 0, edx = 0; - // on X64 this calls hardware cpuid(.) instr. otherwise a dummy impl. - __get_cpuid(1, &eax, &ebx, &ecx, &edx); - return ecx; - } - -#if defined(__MINGW64__) || defined(_MSC_VER) || !defined(__x86_64__) - inline void __get_cpuid(int /*param*/, - unsigned * /*eax*/, - unsigned * /*ebx*/, - unsigned *ecx, - unsigned * /*edx*/) const - { - *ecx = 0; - } -#endif - - boost::crc_optimal<32, 0x1EDC6F41, 0x0, 0x0, true, true> crc_processor; - unsigned crc; - bool use_hardware_implementation; -}; - -struct RangebasedCRC32 -{ - template unsigned operator()(const Iteratable &iterable) - { - return crc32(std::begin(iterable), std::end(iterable)); - } - - bool UsingHardware() const { return crc32.UsingHardware(); } - - private: - IteratorbasedCRC32 crc32; -}; -} // namespace osrm::contractor - -#endif /* ITERATOR_BASED_CRC32_H */ diff --git a/src/contractor/contractor.cpp b/src/contractor/contractor.cpp index 87ab042cb..a0b430aa8 100644 --- a/src/contractor/contractor.cpp +++ b/src/contractor/contractor.cpp @@ -1,7 +1,6 @@ #include "contractor/contractor.hpp" #include "contractor/contract_excludable_graph.hpp" #include "contractor/contracted_edge_container.hpp" -#include "contractor/crc32_processor.hpp" #include "contractor/files.hpp" #include "contractor/graph_contractor.hpp" #include "contractor/graph_contractor_adaptors.hpp" From 8306ed8ae3e8a8d5c4c8365b413c3d28a4840dec Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Sat, 25 May 2024 09:17:45 +0200 Subject: [PATCH 04/28] Remove superflous iostreams include from header files (#6901) --- include/nodejs/node_osrm_support.hpp | 1 - include/storage/io.hpp | 1 + include/util/debug.hpp | 2 +- include/util/exception.hpp | 1 - include/util/exception_utils.hpp | 8 +++++--- include/util/lua_util.hpp | 1 - include/util/node_based_graph.hpp | 1 - src/benchmarks/packed_vector.cpp | 1 + src/extractor/compressed_edge_container.cpp | 2 -- src/extractor/extractor.cpp | 1 - src/partitioner/bisection_graph_view.cpp | 1 - src/storage/storage.cpp | 1 - src/storage/storage_config.cpp | 3 +++ src/tools/extract.cpp | 1 + src/util/coordinate.cpp | 14 +++----------- src/util/exception.cpp | 2 -- src/util/guidance/turn_lanes.cpp | 4 ---- 17 files changed, 15 insertions(+), 30 deletions(-) diff --git a/include/nodejs/node_osrm_support.hpp b/include/nodejs/node_osrm_support.hpp index 24af5f1b1..f69d8e813 100644 --- a/include/nodejs/node_osrm_support.hpp +++ b/include/nodejs/node_osrm_support.hpp @@ -24,7 +24,6 @@ #include #include -#include #include #include #include diff --git a/include/storage/io.hpp b/include/storage/io.hpp index a172c660f..21095576e 100644 --- a/include/storage/io.hpp +++ b/include/storage/io.hpp @@ -18,6 +18,7 @@ #include #include +#include #include #include diff --git a/include/util/debug.hpp b/include/util/debug.hpp index 792c3be42..2f60c369b 100644 --- a/include/util/debug.hpp +++ b/include/util/debug.hpp @@ -12,7 +12,7 @@ #include "util/typedefs.hpp" #include -#include +#include #include #include #include diff --git a/include/util/exception.hpp b/include/util/exception.hpp index 83800563f..660bae69a 100644 --- a/include/util/exception.hpp +++ b/include/util/exception.hpp @@ -30,7 +30,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#include #include #include diff --git a/include/util/exception_utils.hpp b/include/util/exception_utils.hpp index 58c128af0..cb96ff6bd 100644 --- a/include/util/exception_utils.hpp +++ b/include/util/exception_utils.hpp @@ -1,6 +1,8 @@ -#ifndef SOURCE_MACROS_HPP -#define SOURCE_MACROS_HPP +#ifndef EXCEPTION_UTILS_HPP +#define EXCEPTION_UTILS_HPP + #include +#include // Helper macros, don't use these ones // STRIP the OSRM_PROJECT_DIR from the front of a filename. Expected to come @@ -12,4 +14,4 @@ // This is the macro to use #define SOURCE_REF (OSRM_SOURCE_FILE_ + ":" + std::to_string(__LINE__)) -#endif // SOURCE_MACROS_HPP +#endif // EXCEPTION_UTILS_HPP diff --git a/include/util/lua_util.hpp b/include/util/lua_util.hpp index cf006dfa2..75811db1d 100644 --- a/include/util/lua_util.hpp +++ b/include/util/lua_util.hpp @@ -10,7 +10,6 @@ extern "C" #include -#include #include namespace osrm::util diff --git a/include/util/node_based_graph.hpp b/include/util/node_based_graph.hpp index b45358fee..cb17b5a22 100644 --- a/include/util/node_based_graph.hpp +++ b/include/util/node_based_graph.hpp @@ -9,7 +9,6 @@ #include -#include #include #include diff --git a/src/benchmarks/packed_vector.cpp b/src/benchmarks/packed_vector.cpp index 62aa1634d..7049c1852 100644 --- a/src/benchmarks/packed_vector.cpp +++ b/src/benchmarks/packed_vector.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include diff --git a/src/extractor/compressed_edge_container.cpp b/src/extractor/compressed_edge_container.cpp index 8504c8d02..916b1d489 100644 --- a/src/extractor/compressed_edge_container.cpp +++ b/src/extractor/compressed_edge_container.cpp @@ -9,8 +9,6 @@ #include #include -#include - namespace osrm::extractor { diff --git a/src/extractor/extractor.cpp b/src/extractor/extractor.cpp index 634395457..c4394dbaa 100644 --- a/src/extractor/extractor.cpp +++ b/src/extractor/extractor.cpp @@ -50,7 +50,6 @@ #include #include #include -#include #include #include #include diff --git a/src/partitioner/bisection_graph_view.cpp b/src/partitioner/bisection_graph_view.cpp index f5f35b4f9..61ce7687f 100644 --- a/src/partitioner/bisection_graph_view.cpp +++ b/src/partitioner/bisection_graph_view.cpp @@ -1,6 +1,5 @@ #include "partitioner/bisection_graph_view.hpp" -#include #include #include diff --git a/src/storage/storage.cpp b/src/storage/storage.cpp index 65f422a31..d3e2041e5 100644 --- a/src/storage/storage.cpp +++ b/src/storage/storage.cpp @@ -31,7 +31,6 @@ #include #include -#include #include #include #include diff --git a/src/storage/storage_config.cpp b/src/storage/storage_config.cpp index 40ca707a6..219304bd1 100644 --- a/src/storage/storage_config.cpp +++ b/src/storage/storage_config.cpp @@ -3,6 +3,9 @@ #include "util/exception_utils.hpp" #include +#include +#include + namespace osrm::storage { std::istream &operator>>(std::istream &in, FeatureDataset &datasets) diff --git a/src/tools/extract.cpp b/src/tools/extract.cpp index 0fc615c49..77964727b 100644 --- a/src/tools/extract.cpp +++ b/src/tools/extract.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include diff --git a/src/util/coordinate.cpp b/src/util/coordinate.cpp index 11e76074c..09509b804 100644 --- a/src/util/coordinate.cpp +++ b/src/util/coordinate.cpp @@ -1,16 +1,8 @@ -#include "util/coordinate_calculation.hpp" - -#ifndef NDEBUG -#include "util/log.hpp" -#endif #include "osrm/coordinate.hpp" -#ifndef NDEBUG -#include -#endif -#include -#include -#include +#include "util/coordinate_calculation.hpp" + +#include namespace osrm::util { diff --git a/src/util/exception.cpp b/src/util/exception.cpp index a4146f289..2471b4895 100644 --- a/src/util/exception.cpp +++ b/src/util/exception.cpp @@ -1,7 +1,5 @@ #include "util/exception.hpp" -#include - // This function exists to 'anchor' the class, and stop the compiler from // copying vtable and RTTI info into every object file that includes // this header. (Caught by -Wweak-vtables under Clang.) diff --git a/src/util/guidance/turn_lanes.cpp b/src/util/guidance/turn_lanes.cpp index c4594ecf9..e44dca9d5 100644 --- a/src/util/guidance/turn_lanes.cpp +++ b/src/util/guidance/turn_lanes.cpp @@ -1,11 +1,7 @@ #include "util/guidance/turn_lanes.hpp" -#include -#include #include -#include - namespace osrm::util::guidance { LaneTuple::LaneTuple() : lanes_in_turn(0), first_lane_from_the_right(INVALID_LANEID) From b7a990d0b5a42d12f3dcd0d816778192b204de83 Mon Sep 17 00:00:00 2001 From: Siarhei Fedartsou Date: Sat, 25 May 2024 18:25:13 +0200 Subject: [PATCH 05/28] Use C++20 (#6877) --- .clang-tidy | 2 ++ .github/workflows/osrm-backend.yml | 31 +++++++++++++++++++++++++++++- CMakeLists.txt | 2 +- cmake/warnings.cmake | 7 ++++++- include/util/static_assert.hpp | 6 +++--- 5 files changed, 42 insertions(+), 6 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 229e2e69a..c724cc0c4 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -54,6 +54,7 @@ Checks: > performance-*, -performance-noexcept-move-constructor, -performance-no-int-to-ptr, + -performance-type-promotion-in-math-fn, readability-*, -readability-avoid-const-params-in-decls, -readability-braces-around-statements, @@ -82,6 +83,7 @@ Checks: > -readability-make-member-function-const, -readability-redundant-string-init, -readability-non-const-parameter, + -readability-container-contains, -readability-static-accessed-through-instance WarningsAsErrors: '*' diff --git a/.github/workflows/osrm-backend.yml b/.github/workflows/osrm-backend.yml index ed0407f68..63709e51a 100644 --- a/.github/workflows/osrm-backend.yml +++ b/.github/workflows/osrm-backend.yml @@ -396,7 +396,36 @@ jobs: elif [[ "${RUNNER_OS}" == "macOS" ]]; then echo "JOBS=$((`sysctl -n hw.ncpu` + 1))" >> $GITHUB_ENV fi + # See: https://github.com/actions/toolkit/issues/946#issuecomment-1590016041 + # We need it to be able to access system folders while restoring cached Boost below + - name: Give tar root ownership + if: runner.os == 'Linux' && matrix.ENABLE_CONAN != 'ON' + run: sudo chown root /bin/tar && sudo chmod u+s /bin/tar + - name: Cache Boost + if: runner.os == 'Linux' && matrix.ENABLE_CONAN != 'ON' + id: cache-boost + uses: actions/cache@v4 + with: + path: | + /usr/local/include/boost + /usr/local/lib/libboost* + key: v1-boost-${{ runner.os }}-${{ runner.arch }}-${{ matrix.runs-on }} + restore-keys: | + v1-boost-${{ runner.os }}-${{ runner.arch }}-${{ matrix.runs-on }} + - name: Install Boost + if: steps.cache-boost.outputs.cache-hit != 'true' && runner.os == 'Linux' && matrix.ENABLE_CONAN != 'ON' + run: | + BOOST_VERSION="1.85.0" + BOOST_VERSION_UNDERSCORE="${BOOST_VERSION//./_}" + wget -q https://boostorg.jfrog.io/artifactory/main/release/${BOOST_VERSION}/source/boost_${BOOST_VERSION_UNDERSCORE}.tar.gz + tar xzf boost_${BOOST_VERSION_UNDERSCORE}.tar.gz + cd boost_${BOOST_VERSION_UNDERSCORE} + sudo ./bootstrap.sh + sudo ./b2 install + cd .. + sudo rm -rf boost_${BOOST_VERSION_UNDERSCORE}* + - name: Install dev dependencies run: | python3 -m pip install "conan<2.0.0" || python3 -m pip install "conan<2.0.0" --break-system-packages @@ -417,7 +446,7 @@ jobs: # Linux dev packages if [ "${ENABLE_CONAN}" != "ON" ]; then sudo apt-get update -y - sudo apt-get install -y libbz2-dev libxml2-dev libzip-dev liblua5.2-dev libboost-all-dev + sudo apt-get install -y libbz2-dev libxml2-dev libzip-dev liblua5.2-dev if [[ "${CCOMPILER}" != clang-* ]]; then sudo apt-get install -y ${CXXCOMPILER} fi diff --git a/CMakeLists.txt b/CMakeLists.txt index 23bd0bcad..fb135888b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.18) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/cmake/warnings.cmake b/cmake/warnings.cmake index 89040aa0e..4006b0f2e 100644 --- a/cmake/warnings.cmake +++ b/cmake/warnings.cmake @@ -78,4 +78,9 @@ add_warning(switch-bool) add_warning(tautological-compare) add_warning(trampolines) # these warnings are not enabled by default -# no_warning(name-of-warning) \ No newline at end of file +# no_warning(name-of-warning) +no_warning(deprecated-comma-subscript) +no_warning(comma-subscript) +no_warning(ambiguous-reversed-operator) +no_warning(restrict) +no_warning(free-nonheap-object) diff --git a/include/util/static_assert.hpp b/include/util/static_assert.hpp index 234b26391..28e922fd3 100644 --- a/include/util/static_assert.hpp +++ b/include/util/static_assert.hpp @@ -1,6 +1,7 @@ #ifndef OSRM_STATIC_ASSERT_HPP #define OSRM_STATIC_ASSERT_HPP +#include #include namespace osrm::util @@ -8,14 +9,13 @@ namespace osrm::util template inline void static_assert_iter_value() { - using IterValueType = typename std::iterator_traits::value_type; - static_assert(std::is_same::value, ""); + static_assert(std::is_same_v, Value>, ""); } template inline void static_assert_iter_category() { using IterCategoryType = typename std::iterator_traits::iterator_category; - static_assert(std::is_base_of::value, ""); + static_assert(std::is_base_of_v, ""); } } // namespace osrm::util From 667fd198ac9330de0185911ca2740d13f44bc013 Mon Sep 17 00:00:00 2001 From: Siarhei Fedartsou Date: Mon, 27 May 2024 08:30:44 +0200 Subject: [PATCH 06/28] Use the latest Debian version in Dockerfile (#6904) --- .github/workflows/osrm-backend.yml | 2 +- CHANGELOG.md | 1 + docker/Dockerfile | 15 ++++++++------- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/workflows/osrm-backend.yml b/.github/workflows/osrm-backend.yml index 63709e51a..090e9fea7 100644 --- a/.github/workflows/osrm-backend.yml +++ b/.github/workflows/osrm-backend.yml @@ -456,7 +456,7 @@ jobs: fi # TBB - TBB_VERSION=2021.3.0 + TBB_VERSION=2021.12.0 if [[ "${RUNNER_OS}" == "Linux" ]]; then TBB_URL="https://github.com/oneapi-src/oneTBB/releases/download/v${TBB_VERSION}/oneapi-tbb-${TBB_VERSION}-lin.tgz" elif [[ "${RUNNER_OS}" == "macOS" ]]; then diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a30ef80d..51605b3ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - ADDED: Add support for opposite approach request parameter. [#6842](https://github.com/Project-OSRM/osrm-backend/pull/6842) - ADDED: Add support for accessing edge flags in `process_segment` [#6658](https://github.com/Project-OSRM/osrm-backend/pull/6658) - Build: + - CHANGED: Use Debian Bookworm as base Docker image [#6904](https://github.com/Project-OSRM/osrm-backend/pull/6904) - CHANGED: Upgrade CI actions to latest versions [#6893](https://github.com/Project-OSRM/osrm-backend/pull/6893) - CHANGED: Remove outdated warnings #6894 [#6894](https://github.com/Project-OSRM/osrm-backend/pull/6894) - ADDED: Add CI job which builds OSRM with gcc 12. [#6455](https://github.com/Project-OSRM/osrm-backend/pull/6455) diff --git a/docker/Dockerfile b/docker/Dockerfile index 7bb158132..ca4ae165d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,15 +1,15 @@ -FROM debian:bullseye-slim as builder +FROM debian:bookworm-slim as builder ARG DOCKER_TAG ARG BUILD_CONCURRENCY RUN mkdir -p /src && mkdir -p /opt RUN apt-get update && \ apt-get -y --no-install-recommends install ca-certificates cmake make git gcc g++ libbz2-dev libxml2-dev wget \ - libzip-dev libboost1.74-all-dev lua5.4 liblua5.4-dev pkg-config -o APT::Install-Suggests=0 -o APT::Install-Recommends=0 + libzip-dev libboost1.81-all-dev lua5.4 liblua5.4-dev pkg-config -o APT::Install-Suggests=0 -o APT::Install-Recommends=0 RUN NPROC=${BUILD_CONCURRENCY:-$(nproc)} && \ ldconfig /usr/local/lib && \ - git clone --branch v2021.3.0 --single-branch https://github.com/oneapi-src/oneTBB.git && \ + git clone --branch v2021.12.0 --single-branch https://github.com/oneapi-src/oneTBB.git && \ cd oneTBB && \ mkdir build && \ cd build && \ @@ -21,6 +21,7 @@ COPY . /src WORKDIR /src RUN NPROC=${BUILD_CONCURRENCY:-$(nproc)} && \ + export CXXFLAGS="-Wno-array-bounds -Wno-uninitialized" && \ echo "Building OSRM ${DOCKER_TAG}" && \ git show --format="%H" | head -n1 > /opt/OSRM_GITSHA && \ echo "Building OSRM gitsha $(cat /opt/OSRM_GITSHA)" && \ @@ -42,15 +43,15 @@ RUN NPROC=${BUILD_CONCURRENCY:-$(nproc)} && \ # Multistage build to reduce image size - https://docs.docker.com/engine/userguide/eng-image/multistage-build/#use-multi-stage-builds # Only the content below ends up in the image, this helps remove /src from the image (which is large) -FROM debian:bullseye-slim as runstage +FROM debian:bookworm-slim as runstage COPY --from=builder /usr/local /usr/local COPY --from=builder /opt /opt RUN apt-get update && \ - apt-get install -y --no-install-recommends libboost-program-options1.74.0 libboost-regex1.74.0 \ - libboost-date-time1.74.0 libboost-chrono1.74.0 libboost-filesystem1.74.0 \ - libboost-iostreams1.74.0 libboost-system1.74.0 libboost-thread1.74.0 \ + apt-get install -y --no-install-recommends libboost-program-options1.81.0 libboost-regex1.81.0 \ + libboost-date-time1.81.0 libboost-chrono1.81.0 libboost-filesystem1.81.0 \ + libboost-iostreams1.81.0 libboost-system1.81.0 libboost-thread1.81.0 \ expat liblua5.4-0 && \ rm -rf /var/lib/apt/lists/* && \ # add /usr/local/lib to ldconfig to allow loading libraries from there From 163a2cfe3c84befa4f24489ee068fd50af9cec5f Mon Sep 17 00:00:00 2001 From: Siarhei Fedartsou Date: Mon, 27 May 2024 08:31:59 +0200 Subject: [PATCH 07/28] Bump rapidjson version (#6906) --- CHANGELOG.md | 1 + scripts/update_dependencies.sh | 2 +- third_party/rapidjson/.gitignore | 4 + third_party/rapidjson/.travis.yml | 120 +- third_party/rapidjson/CHANGELOG.md | 14 +- third_party/rapidjson/CMakeLists.txt | 135 +- third_party/rapidjson/RapidJSON.pc.in | 2 +- .../rapidjson/RapidJSONConfig.cmake.in | 22 +- third_party/rapidjson/appveyor.yml | 81 +- third_party/rapidjson/bin/data/abcde.txt | 1 + .../bin/jsonschema/remotes/.DS_Store | Bin 6148 -> 0 bytes .../rapidjson/bin/jsonschema/tests/.DS_Store | Bin 6148 -> 0 bytes .../bin/jsonschema/tests/draft4/.DS_Store | Bin 6148 -> 0 bytes .../rapidjson/bin/types/alotofkeys.json | Bin 0 -> 30003 bytes third_party/rapidjson/bin/types/booleans.json | Bin third_party/rapidjson/bin/types/floats.json | Bin third_party/rapidjson/bin/types/guids.json | Bin third_party/rapidjson/bin/types/integers.json | Bin third_party/rapidjson/bin/types/mixed.json | Bin third_party/rapidjson/bin/types/nulls.json | Bin .../rapidjson/bin/types/paragraphs.json | Bin .../rapidjson/bin/unittestschema/address.json | Bin 0 -> 3150 bytes .../bin/unittestschema/allOf_address.json | Bin 0 -> 84 bytes .../bin/unittestschema/anyOf_address.json | Bin 0 -> 84 bytes .../bin/unittestschema/idandref.json | Bin 0 -> 1315 bytes .../bin/unittestschema/oneOf_address.json | Bin 0 -> 84 bytes third_party/rapidjson/contrib/natvis/LICENSE | 45 + .../rapidjson/contrib/natvis/README.md | 7 + .../rapidjson/contrib/natvis/rapidjson.natvis | 38 + third_party/rapidjson/doc/CMakeLists.txt | 4 +- third_party/rapidjson/doc/Doxyfile.in | 2 +- third_party/rapidjson/doc/Doxyfile.zh-cn.in | 4 +- third_party/rapidjson/doc/diagram/move2.dot | 8 +- third_party/rapidjson/doc/diagram/move3.dot | 8 +- third_party/rapidjson/doc/dom.md | 5 +- third_party/rapidjson/doc/dom.zh-cn.md | 5 +- third_party/rapidjson/doc/encoding.md | 2 +- third_party/rapidjson/doc/encoding.zh-cn.md | 2 +- third_party/rapidjson/doc/faq.md | 30 +- third_party/rapidjson/doc/faq.zh-cn.md | 24 +- third_party/rapidjson/doc/features.md | 12 +- third_party/rapidjson/doc/features.zh-cn.md | 4 +- third_party/rapidjson/doc/internals.md | 15 +- third_party/rapidjson/doc/internals.zh-cn.md | 363 +++ third_party/rapidjson/doc/misc/header.html | 2 +- third_party/rapidjson/doc/npm.md | 2 +- third_party/rapidjson/doc/performance.md | 6 +- .../rapidjson/doc/performance.zh-cn.md | 4 +- third_party/rapidjson/doc/pointer.md | 2 +- third_party/rapidjson/doc/pointer.zh-cn.md | 2 +- third_party/rapidjson/doc/sax.md | 53 +- third_party/rapidjson/doc/sax.zh-cn.md | 6 +- third_party/rapidjson/doc/schema.md | 306 +- third_party/rapidjson/doc/schema.zh-cn.md | 28 +- third_party/rapidjson/doc/stream.md | 19 +- third_party/rapidjson/doc/stream.zh-cn.md | 5 +- third_party/rapidjson/doc/tutorial.md | 84 +- third_party/rapidjson/doc/tutorial.zh-cn.md | 33 +- third_party/rapidjson/example/CMakeLists.txt | 10 +- .../rapidjson/example/archiver/archiver.cpp | 292 ++ .../rapidjson/example/archiver/archiver.h | 145 + .../example/archiver/archivertest.cpp | 287 ++ third_party/rapidjson/example/jsonx/jsonx.cpp | 2 +- .../lookaheadparser/lookaheadparser.cpp | 350 +++ .../example/parsebyparts/parsebyparts.cpp | 9 +- .../schemavalidator/schemavalidator.cpp | 127 + .../simplepullreader/simplepullreader.cpp | 53 + .../rapidjson/example/sortkeys/sortkeys.cpp | 62 + .../rapidjson/example/traverseaspointer.cpp | 39 + .../rapidjson/example/tutorial/tutorial.cpp | 2 +- .../rapidjson/include/rapidjson/allocators.h | 534 +++- .../include/rapidjson/cursorstreamwrapper.h | 78 + .../rapidjson/include/rapidjson/document.h | 794 +++-- .../include/rapidjson/encodedstream.h | 4 +- .../rapidjson/include/rapidjson/encodings.h | 88 +- .../rapidjson/include/rapidjson/error/en.h | 118 +- .../rapidjson/include/rapidjson/error/error.h | 146 +- .../include/rapidjson/filereadstream.h | 6 +- .../include/rapidjson/filewritestream.h | 6 +- third_party/rapidjson/include/rapidjson/fwd.h | 4 +- .../include/rapidjson/internal/biginteger.h | 27 +- .../include/rapidjson/internal/clzll.h | 71 + .../include/rapidjson/internal/diyfp.h | 61 +- .../include/rapidjson/internal/dtoa.h | 18 +- .../include/rapidjson/internal/ieee754.h | 6 +- .../include/rapidjson/internal/itoa.h | 84 +- .../include/rapidjson/internal/meta.h | 11 +- .../include/rapidjson/internal/pow10.h | 2 +- .../include/rapidjson/internal/regex.h | 324 ++- .../include/rapidjson/internal/stack.h | 12 +- .../include/rapidjson/internal/strfunc.h | 30 +- .../include/rapidjson/internal/strtod.h | 122 +- .../include/rapidjson/internal/swap.h | 2 +- .../include/rapidjson/istreamwrapper.h | 89 +- .../include/rapidjson/memorybuffer.h | 2 +- .../include/rapidjson/memorystream.h | 2 +- .../include/rapidjson/ostreamwrapper.h | 2 +- .../rapidjson/include/rapidjson/pointer.h | 208 +- .../include/rapidjson/prettywriter.h | 64 +- .../rapidjson/include/rapidjson/rapidjson.h | 218 +- .../rapidjson/include/rapidjson/reader.h | 677 ++++- .../rapidjson/include/rapidjson/schema.h | 2008 ++++++++++--- .../rapidjson/include/rapidjson/stream.h | 54 +- .../include/rapidjson/stringbuffer.h | 6 +- third_party/rapidjson/include/rapidjson/uri.h | 481 +++ .../rapidjson/include/rapidjson/writer.h | 179 +- third_party/rapidjson/library.json | Bin 313 -> 355 bytes third_party/rapidjson/package.json | Bin 561 -> 561 bytes third_party/rapidjson/rapidjson.autopkg | 10 +- third_party/rapidjson/readme.md | 114 +- third_party/rapidjson/readme.zh-cn.md | 66 +- .../rapidjson/test/perftest/CMakeLists.txt | 2 + .../rapidjson/test/perftest/misctest.cpp | 6 +- .../rapidjson/test/perftest/perftest.cpp | 2 +- .../rapidjson/test/perftest/perftest.h | 14 +- .../rapidjson/test/perftest/platformtest.cpp | 4 +- .../rapidjson/test/perftest/rapidjsontest.cpp | 129 +- .../rapidjson/test/perftest/schematest.cpp | 7 + .../rapidjson/test/unittest/CMakeLists.txt | 13 +- .../test/unittest/allocatorstest.cpp | 222 +- .../test/unittest/bigintegertest.cpp | 7 +- .../rapidjson/test/unittest/clzlltest.cpp | 34 + .../test/unittest/cursorstreamwrappertest.cpp | 115 + .../rapidjson/test/unittest/documenttest.cpp | 28 +- .../rapidjson/test/unittest/dtoatest.cpp | 3 +- .../test/unittest/encodedstreamtest.cpp | 4 +- .../rapidjson/test/unittest/encodingstest.cpp | 4 +- .../test/unittest/filestreamtest.cpp | 47 +- .../rapidjson/test/unittest/fwdtest.cpp | 27 +- .../test/unittest/istreamwrappertest.cpp | 24 +- .../rapidjson/test/unittest/itoatest.cpp | 24 +- .../test/unittest/jsoncheckertest.cpp | 54 +- .../rapidjson/test/unittest/namespacetest.cpp | 2 +- .../test/unittest/ostreamwrappertest.cpp | 7 +- .../rapidjson/test/unittest/platformtest.cpp | 40 + .../rapidjson/test/unittest/pointertest.cpp | 256 +- .../test/unittest/prettywritertest.cpp | 172 +- .../rapidjson/test/unittest/readertest.cpp | 704 ++++- .../rapidjson/test/unittest/regextest.cpp | 625 ++-- .../rapidjson/test/unittest/schematest.cpp | 2583 ++++++++++++++++- .../rapidjson/test/unittest/simdtest.cpp | 20 +- .../rapidjson/test/unittest/strfunctest.cpp | 2 +- .../test/unittest/stringbuffertest.cpp | 30 +- .../rapidjson/test/unittest/strtodtest.cpp | 4 +- .../rapidjson/test/unittest/unittest.cpp | 2 +- .../rapidjson/test/unittest/unittest.h | 14 +- .../rapidjson/test/unittest/uritest.cpp | 725 +++++ .../rapidjson/test/unittest/valuetest.cpp | 148 +- .../rapidjson/test/unittest/writertest.cpp | 135 +- third_party/rapidjson/test/valgrind.supp | 26 + third_party/rapidjson/travis-doxygen.sh | 24 +- 151 files changed, 13549 insertions(+), 2358 deletions(-) create mode 100644 third_party/rapidjson/bin/data/abcde.txt delete mode 100644 third_party/rapidjson/bin/jsonschema/remotes/.DS_Store delete mode 100644 third_party/rapidjson/bin/jsonschema/tests/.DS_Store delete mode 100644 third_party/rapidjson/bin/jsonschema/tests/draft4/.DS_Store create mode 100644 third_party/rapidjson/bin/types/alotofkeys.json mode change 100755 => 100644 third_party/rapidjson/bin/types/booleans.json mode change 100755 => 100644 third_party/rapidjson/bin/types/floats.json mode change 100755 => 100644 third_party/rapidjson/bin/types/guids.json mode change 100755 => 100644 third_party/rapidjson/bin/types/integers.json mode change 100755 => 100644 third_party/rapidjson/bin/types/mixed.json mode change 100755 => 100644 third_party/rapidjson/bin/types/nulls.json mode change 100755 => 100644 third_party/rapidjson/bin/types/paragraphs.json create mode 100644 third_party/rapidjson/bin/unittestschema/address.json create mode 100644 third_party/rapidjson/bin/unittestschema/allOf_address.json create mode 100644 third_party/rapidjson/bin/unittestschema/anyOf_address.json create mode 100644 third_party/rapidjson/bin/unittestschema/idandref.json create mode 100644 third_party/rapidjson/bin/unittestschema/oneOf_address.json create mode 100644 third_party/rapidjson/contrib/natvis/LICENSE create mode 100644 third_party/rapidjson/contrib/natvis/README.md create mode 100644 third_party/rapidjson/contrib/natvis/rapidjson.natvis create mode 100644 third_party/rapidjson/doc/internals.zh-cn.md create mode 100644 third_party/rapidjson/example/archiver/archiver.cpp create mode 100644 third_party/rapidjson/example/archiver/archiver.h create mode 100644 third_party/rapidjson/example/archiver/archivertest.cpp create mode 100644 third_party/rapidjson/example/lookaheadparser/lookaheadparser.cpp create mode 100644 third_party/rapidjson/example/simplepullreader/simplepullreader.cpp create mode 100644 third_party/rapidjson/example/sortkeys/sortkeys.cpp create mode 100644 third_party/rapidjson/example/traverseaspointer.cpp create mode 100644 third_party/rapidjson/include/rapidjson/cursorstreamwrapper.h create mode 100644 third_party/rapidjson/include/rapidjson/internal/clzll.h create mode 100644 third_party/rapidjson/include/rapidjson/uri.h create mode 100644 third_party/rapidjson/test/unittest/clzlltest.cpp create mode 100644 third_party/rapidjson/test/unittest/cursorstreamwrappertest.cpp create mode 100644 third_party/rapidjson/test/unittest/platformtest.cpp create mode 100644 third_party/rapidjson/test/unittest/uritest.cpp create mode 100644 third_party/rapidjson/test/valgrind.supp diff --git a/CHANGELOG.md b/CHANGELOG.md index 51605b3ab..c19a18d90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - NodeJS: - CHANGED: Use node-api instead of NAN. [#6452](https://github.com/Project-OSRM/osrm-backend/pull/6452) - Misc: + - CHANGED: Bump rapidjson to version f9d53419e912910fd8fa57d5705fa41425428c35 [#6906](https://github.com/Project-OSRM/osrm-backend/pull/6906) - CHANGED: Bump mapbox/variant to version 1.2.0 [#6898](https://github.com/Project-OSRM/osrm-backend/pull/6898) - CHANGED: Avoid copy of std::function-based callback in path unpacking [#6895](https://github.com/Project-OSRM/osrm-backend/pull/6895) - CHANGED: Replace boost::hash by std::hash [#6892](https://github.com/Project-OSRM/osrm-backend/pull/6892) diff --git a/scripts/update_dependencies.sh b/scripts/update_dependencies.sh index fdeecbac7..9e8e231bf 100755 --- a/scripts/update_dependencies.sh +++ b/scripts/update_dependencies.sh @@ -19,7 +19,7 @@ SOL_PATH="ThePhD/sol2" SOL_TAG=v2.17.5 RAPIDJSON_PATH="Tencent/rapidjson" -RAPIDJSON_TAG=v1.1.0 +RAPIDJSON_TAG=f9d53419e912910fd8fa57d5705fa41425428c35 MICROTAR_PATH="rxi/microtar" MICROTAR_TAG=v0.1.0 diff --git a/third_party/rapidjson/.gitignore b/third_party/rapidjson/.gitignore index e7e8fba9b..5932e82c2 100644 --- a/third_party/rapidjson/.gitignore +++ b/third_party/rapidjson/.gitignore @@ -3,6 +3,7 @@ !/bin/encodings !/bin/jsonchecker !/bin/types +!/bin/unittestschema /build /doc/html /doc/doxygen_*.db @@ -23,3 +24,6 @@ Doxyfile Doxyfile.zh-cn DartConfiguration.tcl *.nupkg + +# Files created by OS +*.DS_Store diff --git a/third_party/rapidjson/.travis.yml b/third_party/rapidjson/.travis.yml index f9319f2ed..17d8f03d6 100644 --- a/third_party/rapidjson/.travis.yml +++ b/third_party/rapidjson/.travis.yml @@ -1,10 +1,18 @@ sudo: required -dist: precise +dist: xenial language: cpp cache: - ccache +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - cmake + - valgrind + - clang-8 env: global: - USE_CCACHE=1 @@ -13,49 +21,96 @@ env: - CCACHE_MAXSIZE=100M - ARCH_FLAGS_x86='-m32' # #266: don't use SSE on 32-bit - ARCH_FLAGS_x86_64='-msse4.2' # use SSE4.2 on 64-bit - - GITHUB_REPO='miloyip/rapidjson' + - ARCH_FLAGS_aarch64='-march=armv8-a' + - GITHUB_REPO='Tencent/rapidjson' - secure: "HrsaCb+N66EG1HR+LWH1u51SjaJyRwJEDzqJGYMB7LJ/bfqb9mWKF1fLvZGk46W5t7TVaXRDD5KHFx9DPWvKn4gRUVkwTHEy262ah5ORh8M6n/6VVVajeV/AYt2C0sswdkDBDO4Xq+xy5gdw3G8s1A4Inbm73pUh+6vx+7ltBbk=" -before_install: - - sudo apt-add-repository -y ppa:ubuntu-toolchain-r/test - - sudo apt-get update -qq - - sudo apt-get install -y cmake valgrind g++-multilib libc6-dbg:i386 - matrix: include: # gcc - - env: CONF=release ARCH=x86 CXX11=ON + - env: CONF=release ARCH=x86 CXX11=ON CXX17=OFF MEMBERSMAP=OFF compiler: gcc - - env: CONF=release ARCH=x86_64 CXX11=ON + arch: amd64 + - env: CONF=release ARCH=x86_64 CXX11=ON CXX17=OFF MEMBERSMAP=OFF compiler: gcc - - env: CONF=debug ARCH=x86 CXX11=OFF + arch: amd64 + - env: CONF=release ARCH=x86_64 CXX11=ON CXX17=OFF MEMBERSMAP=ON compiler: gcc - - env: CONF=debug ARCH=x86_64 CXX11=OFF + arch: amd64 + - env: CONF=debug ARCH=x86 CXX11=OFF CXX17=OFF MEMBERSMAP=OFF compiler: gcc + arch: amd64 + - env: CONF=debug ARCH=x86_64 CXX11=OFF CXX17=OFF MEMBERSMAP=OFF + compiler: gcc + arch: amd64 + - env: CONF=debug ARCH=x86 CXX11=OFF CXX17=ON MEMBERSMAP=ON CXX_FLAGS='-D_GLIBCXX_DEBUG' + compiler: gcc + arch: amd64 + - env: CONF=debug ARCH=x86_64 CXX11=OFF CXX17=ON MEMBERSMAP=ON CXX_FLAGS='-D_GLIBCXX_DEBUG' + compiler: gcc + arch: amd64 + - env: CONF=release ARCH=aarch64 CXX11=ON CXX17=OFF MEMBERSMAP=OFF + compiler: gcc + arch: arm64 + - env: CONF=release ARCH=aarch64 CXX11=OFF CXX17=OFF MEMBERSMAP=OFF + compiler: gcc + arch: arm64 + - env: CONF=release ARCH=aarch64 CXX11=OFF CXX17=ON MEMBERSMAP=ON + compiler: gcc + arch: arm64 # clang - - env: CONF=debug ARCH=x86 CXX11=ON CCACHE_CPP2=yes + - env: CONF=release ARCH=x86 CXX11=ON CXX17=OFF MEMBERSMAP=ON CCACHE_CPP2=yes compiler: clang - - env: CONF=debug ARCH=x86_64 CXX11=ON CCACHE_CPP2=yes + arch: amd64 + - env: CONF=release ARCH=x86_64 CXX11=ON CXX17=OFF MEMBERSMAP=ON CCACHE_CPP2=yes compiler: clang - - env: CONF=debug ARCH=x86 CXX11=OFF CCACHE_CPP2=yes + arch: amd64 + - env: CONF=release ARCH=x86_64 CXX11=ON CXX17=OFF MEMBERSMAP=OFF CCACHE_CPP2=yes compiler: clang - - env: CONF=debug ARCH=x86_64 CXX11=OFF CCACHE_CPP2=yes + arch: amd64 + - env: CONF=debug ARCH=x86 CXX11=OFF CXX17=OFF MEMBERSMAP=ON CCACHE_CPP2=yes compiler: clang - - env: CONF=release ARCH=x86 CXX11=ON CCACHE_CPP2=yes + arch: amd64 + - env: CONF=debug ARCH=x86_64 CXX11=OFF CXX17=OFF MEMBERSMAP=ON CCACHE_CPP2=yes compiler: clang - - env: CONF=release ARCH=x86_64 CXX11=ON CCACHE_CPP2=yes + arch: amd64 + - env: CONF=debug ARCH=x86 CXX11=OFF CXX17=ON MEMBERSMAP=OFF CCACHE_CPP2=yes compiler: clang + arch: amd64 + - env: CONF=debug ARCH=x86_64 CXX11=OFF CXX17=ON MEMBERSMAP=OFF CCACHE_CPP2=yes + compiler: clang + arch: amd64 + - env: CONF=debug ARCH=aarch64 CXX11=ON CXX17=OFF MEMBERSMAP=ON CCACHE_CPP2=yes + compiler: clang + arch: arm64 + - env: CONF=debug ARCH=aarch64 CXX11=OFF CXX17=OFF MEMBERSMAP=ON CCACHE_CPP2=yes + compiler: clang + arch: arm64 + - env: CONF=debug ARCH=aarch64 CXX11=OFF CXX17=ON MEMBERSMAP=OFF CCACHE_CPP2=yes + compiler: clang + arch: arm64 # coverage report - - env: CONF=debug ARCH=x86 CXX11=ON GCOV_FLAGS='--coverage' + - env: CONF=debug ARCH=x86 GCOV_FLAGS='--coverage' CXX_FLAGS='-O0' CXX11=OFF CXX17=OFF compiler: gcc + arch: amd64 cache: - ccache - pip after_success: - pip install --user cpp-coveralls - coveralls -r .. --gcov-options '\-lp' -e thirdparty -e example -e test -e build/CMakeFiles -e include/rapidjson/msinttypes -e include/rapidjson/internal/meta.h -e include/rapidjson/error/en.h - - env: CONF=debug ARCH=x86_64 GCOV_FLAGS='--coverage' + - env: CONF=debug ARCH=x86_64 GCOV_FLAGS='--coverage' CXX_FLAGS='-O0' CXX11=ON CXX17=OFF MEMBERSMAP=ON compiler: gcc + arch: amd64 + cache: + - ccache + - pip + after_success: + - pip install --user cpp-coveralls + - coveralls -r .. --gcov-options '\-lp' -e thirdparty -e example -e test -e build/CMakeFiles -e include/rapidjson/msinttypes -e include/rapidjson/internal/meta.h -e include/rapidjson/error/en.h + - env: CONF=debug ARCH=aarch64 GCOV_FLAGS='--coverage' CXX_FLAGS='-O0' CXX11=OFF CXX17=ON + compiler: gcc + arch: arm64 cache: - ccache - pip @@ -72,13 +127,24 @@ matrix: packages: - doxygen +before_install: + - if [ "x86_64" = "$(arch)" ]; then sudo apt-get install -y g++-multilib libc6-dbg:i386 --allow-unauthenticated; fi + before_script: - - ccache -s - # hack to avoid Valgrind bug (https://bugs.kde.org/show_bug.cgi?id=326469), - # exposed by merging PR#163 (using -march=native) - # TODO: Since this bug is already fixed. Remove this when valgrind can be upgraded. - - sed -i "s/-march=native//" CMakeLists.txt - - mkdir build + # travis provides clang-7 for amd64 and clang-3.8 for arm64 + # here use clang-8 to all architectures as clang-7 is not available for arm64 + - if [ -f /usr/bin/clang++-8 ]; then + sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-8 1000; + sudo update-alternatives --config clang++; + export PATH=/usr/bin:$PATH; + fi + - if [ "$CXX" = "clang++" ]; then export CCACHE_CPP2=yes; fi + - ccache -s + # hack to avoid Valgrind bug (https://bugs.kde.org/show_bug.cgi?id=326469), + # exposed by merging PR#163 (using -march=native) + # TODO: Since this bug is already fixed. Remove this when valgrind can be upgraded. + - sed -i "s/-march=native//" CMakeLists.txt + - mkdir build script: - if [ "$CXX" = "clang++" ]; then export CXXFLAGS="-stdlib=libc++ ${CXXFLAGS}"; fi @@ -86,10 +152,12 @@ script: eval "ARCH_FLAGS=\${ARCH_FLAGS_${ARCH}}" ; (cd build && cmake -DRAPIDJSON_HAS_STDSTRING=ON + -DRAPIDJSON_USE_MEMBERSMAP=$MEMBERSMAP -DRAPIDJSON_BUILD_CXX11=$CXX11 + -DRAPIDJSON_BUILD_CXX17=$CXX17 -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_BUILD_TYPE=$CONF - -DCMAKE_CXX_FLAGS="$ARCH_FLAGS $GCOV_FLAGS" + -DCMAKE_CXX_FLAGS="$ARCH_FLAGS $GCOV_FLAGS $CXX_FLAGS" -DCMAKE_EXE_LINKER_FLAGS=$GCOV_FLAGS ..) - cd build diff --git a/third_party/rapidjson/CHANGELOG.md b/third_party/rapidjson/CHANGELOG.md index 0205e7b89..1c580bd14 100644 --- a/third_party/rapidjson/CHANGELOG.md +++ b/third_party/rapidjson/CHANGELOG.md @@ -109,7 +109,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [1.0.0] - 2015-04-22 ### Added -* 100% [Coverall](https://coveralls.io/r/miloyip/rapidjson?branch=master) coverage. +* 100% [Coverall](https://coveralls.io/r/Tencent/rapidjson?branch=master) coverage. * Version macros (#311) ### Fixed @@ -140,7 +140,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). * Redo all documentation (English, Simplified Chinese) ### Changed -* Copyright ownership transfered to THL A29 Limited (a Tencent company). +* Copyright ownership transferred to THL A29 Limited (a Tencent company). * Migrating from Premake to CMAKE (#192) * Resolve all warning reports @@ -151,8 +151,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## 0.1 - 2011-11-18 -[Unreleased]: https://github.com/miloyip/rapidjson/compare/v1.1.0...HEAD -[1.1.0]: https://github.com/miloyip/rapidjson/compare/v1.0.2...v1.1.0 -[1.0.2]: https://github.com/miloyip/rapidjson/compare/v1.0.1...v1.0.2 -[1.0.1]: https://github.com/miloyip/rapidjson/compare/v1.0.0...v1.0.1 -[1.0.0]: https://github.com/miloyip/rapidjson/compare/v1.0-beta...v1.0.0 +[Unreleased]: https://github.com/Tencent/rapidjson/compare/v1.1.0...HEAD +[1.1.0]: https://github.com/Tencent/rapidjson/compare/v1.0.2...v1.1.0 +[1.0.2]: https://github.com/Tencent/rapidjson/compare/v1.0.1...v1.0.2 +[1.0.1]: https://github.com/Tencent/rapidjson/compare/v1.0.0...v1.0.1 +[1.0.0]: https://github.com/Tencent/rapidjson/compare/v1.0-beta...v1.0.0 diff --git a/third_party/rapidjson/CMakeLists.txt b/third_party/rapidjson/CMakeLists.txt index ceda71b1b..603341517 100644 --- a/third_party/rapidjson/CMakeLists.txt +++ b/third_party/rapidjson/CMakeLists.txt @@ -1,4 +1,4 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 2.8) +CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12) if(POLICY CMP0025) # detect Apple's Clang cmake_policy(SET CMP0025 NEW) @@ -9,13 +9,18 @@ endif() SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules) -PROJECT(RapidJSON CXX) - set(LIB_MAJOR_VERSION "1") set(LIB_MINOR_VERSION "1") set(LIB_PATCH_VERSION "0") set(LIB_VERSION_STRING "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_PATCH_VERSION}") +if (CMAKE_VERSION VERSION_LESS 3.0) + PROJECT(RapidJSON CXX) +else() + cmake_policy(SET CMP0048 NEW) + PROJECT(RapidJSON VERSION "${LIB_VERSION_STRING}" LANGUAGES CXX) +endif() + # compile in release with debug info mode by default if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) @@ -30,16 +35,28 @@ option(RAPIDJSON_BUILD_TESTS "Build rapidjson perftests and unittests." ON) option(RAPIDJSON_BUILD_THIRDPARTY_GTEST "Use gtest installation in `thirdparty/gtest` by default if available" OFF) -option(RAPIDJSON_BUILD_CXX11 "Build rapidjson with C++11 (gcc/clang)" ON) +option(RAPIDJSON_BUILD_CXX11 "Build rapidjson with C++11" ON) +option(RAPIDJSON_BUILD_CXX17 "Build rapidjson with C++17" OFF) +if(RAPIDJSON_BUILD_CXX11) + set(CMAKE_CXX_STANDARD 11) + set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +endif() option(RAPIDJSON_BUILD_ASAN "Build rapidjson with address sanitizer (gcc/clang)" OFF) option(RAPIDJSON_BUILD_UBSAN "Build rapidjson with undefined behavior sanitizer (gcc/clang)" OFF) +option(RAPIDJSON_ENABLE_INSTRUMENTATION_OPT "Build rapidjson with -march or -mcpu options" ON) + option(RAPIDJSON_HAS_STDSTRING "" OFF) if(RAPIDJSON_HAS_STDSTRING) add_definitions(-DRAPIDJSON_HAS_STDSTRING) endif() +option(RAPIDJSON_USE_MEMBERSMAP "" OFF) +if(RAPIDJSON_USE_MEMBERSMAP) + add_definitions(-DRAPIDJSON_USE_MEMBERSMAP=1) +endif() + find_program(CCACHE_FOUND ccache) if(CCACHE_FOUND) set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) @@ -49,14 +66,25 @@ if(CCACHE_FOUND) endif() endif(CCACHE_FOUND) -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -Wall -Wextra -Werror") - if (RAPIDJSON_BUILD_CXX11) +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + if(RAPIDJSON_ENABLE_INSTRUMENTATION_OPT AND NOT CMAKE_CROSSCOMPILING) + if(CMAKE_SYSTEM_PROCESSOR STREQUAL "powerpc" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc64le") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=native") + else() + #FIXME: x86 is -march=native, but doesn't mean every arch is this option. To keep original project's compatibility, I leave this except POWER. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native") + endif() + endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror") + set(EXTRA_CXX_FLAGS -Weffc++ -Wswitch-default -Wfloat-equal -Wconversion -Wsign-conversion) + if (RAPIDJSON_BUILD_CXX11 AND CMAKE_VERSION VERSION_LESS 3.1) if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.7.0") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") endif() + elseif (RAPIDJSON_BUILD_CXX17 AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS "5.0") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") endif() if (RAPIDJSON_BUILD_ASAN) if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.8.0") @@ -73,9 +101,20 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") endif() endif() elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -Wall -Wextra -Werror -Wno-missing-field-initializers") - if (RAPIDJSON_BUILD_CXX11) + if(NOT CMAKE_CROSSCOMPILING) + if(CMAKE_SYSTEM_PROCESSOR STREQUAL "powerpc" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc64le") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=native") + else() + #FIXME: x86 is -march=native, but doesn't mean every arch is this option. To keep original project's compatibility, I leave this except POWER. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native") + endif() + endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror -Wno-missing-field-initializers") + set(EXTRA_CXX_FLAGS -Weffc++ -Wswitch-default -Wfloat-equal -Wconversion -Wimplicit-fallthrough) + if (RAPIDJSON_BUILD_CXX11 AND CMAKE_VERSION VERSION_LESS 3.1) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + elseif (RAPIDJSON_BUILD_CXX17 AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.0") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") endif() if (RAPIDJSON_BUILD_ASAN) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") @@ -87,9 +126,24 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined") endif() endif() -elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") +elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") add_definitions(-D_CRT_SECURE_NO_WARNINGS=1) + add_definitions(-DNOMINMAX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc") + # CMake >= 3.10 should handle the above CMAKE_CXX_STANDARD fine, otherwise use /std:c++XX with MSVC >= 19.10 + if (RAPIDJSON_BUILD_CXX11 AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS "19.10") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++11") + elseif (RAPIDJSON_BUILD_CXX17 AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS "19.14") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++17") + endif() + # Always compile with /WX + if(CMAKE_CXX_FLAGS MATCHES "/WX-") + string(REGEX REPLACE "/WX-" "/WX" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /WX") + endif() +elseif (CMAKE_CXX_COMPILER_ID MATCHES "XL") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -qarch=auto") endif() #add extra search paths for libraries and includes @@ -102,7 +156,7 @@ IF(UNIX OR CYGWIN) ELSEIF(WIN32) SET(_CMAKE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/cmake") ENDIF() -SET(CMAKE_INSTALL_DIR "${_CMAKE_INSTALL_DIR}" CACHE PATH "The directory cmake fiels are installed in") +SET(CMAKE_INSTALL_DIR "${_CMAKE_INSTALL_DIR}" CACHE PATH "The directory cmake files are installed in") include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) @@ -141,6 +195,11 @@ install(FILES readme.md DESTINATION "${DOC_INSTALL_DIR}" COMPONENT doc) +# Add an interface target to export it +add_library(RapidJSON INTERFACE) + +target_include_directories(RapidJSON INTERFACE $) + install(DIRECTORY include/rapidjson DESTINATION "${INCLUDE_INSTALL_DIR}" COMPONENT dev) @@ -157,17 +216,45 @@ install(DIRECTORY example/ # Provide config and version files to be used by other applications # =============================== -export(PACKAGE ${PROJECT_NAME}) +################################################################################ +# Export package for use from the build tree +EXPORT( PACKAGE ${PROJECT_NAME} ) -# cmake-modules -CONFIGURE_FILE(${PROJECT_NAME}Config.cmake.in - ${PROJECT_NAME}Config.cmake - @ONLY) -CONFIGURE_FILE(${PROJECT_NAME}ConfigVersion.cmake.in - ${PROJECT_NAME}ConfigVersion.cmake - @ONLY) -INSTALL(FILES - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake - DESTINATION "${CMAKE_INSTALL_DIR}" - COMPONENT dev) +# Create the RapidJSONConfig.cmake file for other cmake projects. +# ... for the build tree +SET( CONFIG_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +SET( CONFIG_DIR ${CMAKE_CURRENT_BINARY_DIR}) +SET( ${PROJECT_NAME}_INCLUDE_DIR "\${${PROJECT_NAME}_SOURCE_DIR}/include" ) + +INCLUDE(CMakePackageConfigHelpers) +CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake @ONLY ) +CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}ConfigVersion.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake @ONLY) + +# ... for the install tree +SET( CMAKECONFIG_INSTALL_DIR ${LIB_INSTALL_DIR}/cmake/${PROJECT_NAME} ) +FILE( RELATIVE_PATH REL_INCLUDE_DIR + "${CMAKECONFIG_INSTALL_DIR}" + "${CMAKE_INSTALL_PREFIX}/include" ) + +SET( ${PROJECT_NAME}_INCLUDE_DIR "\${${PROJECT_NAME}_CMAKE_DIR}/${REL_INCLUDE_DIR}" ) +SET( CONFIG_SOURCE_DIR ) +SET( CONFIG_DIR ) +CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PROJECT_NAME}Config.cmake @ONLY ) + +INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PROJECT_NAME}Config.cmake" + DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) + +# Install files +IF(CMAKE_INSTALL_DIR) + INSTALL(FILES + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + DESTINATION "${CMAKE_INSTALL_DIR}" + COMPONENT dev) + + INSTALL(TARGETS RapidJSON EXPORT RapidJSON-targets) + INSTALL(EXPORT RapidJSON-targets DESTINATION ${CMAKE_INSTALL_DIR}) +ENDIF() diff --git a/third_party/rapidjson/RapidJSON.pc.in b/third_party/rapidjson/RapidJSON.pc.in index 7467f9779..6afb079f8 100644 --- a/third_party/rapidjson/RapidJSON.pc.in +++ b/third_party/rapidjson/RapidJSON.pc.in @@ -3,5 +3,5 @@ includedir=@INCLUDE_INSTALL_DIR@ Name: @PROJECT_NAME@ Description: A fast JSON parser/generator for C++ with both SAX/DOM style API Version: @LIB_VERSION_STRING@ -URL: https://github.com/miloyip/rapidjson +URL: https://github.com/Tencent/rapidjson Cflags: -I${includedir} diff --git a/third_party/rapidjson/RapidJSONConfig.cmake.in b/third_party/rapidjson/RapidJSONConfig.cmake.in index 9fa12186a..a8ca78f7b 100644 --- a/third_party/rapidjson/RapidJSONConfig.cmake.in +++ b/third_party/rapidjson/RapidJSONConfig.cmake.in @@ -1,3 +1,19 @@ -get_filename_component(RAPIDJSON_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) -set(RAPIDJSON_INCLUDE_DIRS "@INCLUDE_INSTALL_DIR@") -message(STATUS "RapidJSON found. Headers: ${RAPIDJSON_INCLUDE_DIRS}") +@PACKAGE_INIT@ + +include ("${CMAKE_CURRENT_LIST_DIR}/RapidJSON-targets.cmake") + +################################################################################ +# RapidJSON source dir +set( RapidJSON_SOURCE_DIR "@CONFIG_SOURCE_DIR@") + +################################################################################ +# RapidJSON build dir +set( RapidJSON_DIR "@CONFIG_DIR@") + +################################################################################ +# Compute paths +get_filename_component(RapidJSON_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) + +get_target_property(RapidJSON_INCLUDE_DIR RapidJSON INTERFACE_INCLUDE_DIRECTORIES) + +set( RapidJSON_INCLUDE_DIRS ${RapidJSON_INCLUDE_DIR} ) diff --git a/third_party/rapidjson/appveyor.yml b/third_party/rapidjson/appveyor.yml index dfedf9c29..4044ba664 100644 --- a/third_party/rapidjson/appveyor.yml +++ b/third_party/rapidjson/appveyor.yml @@ -1,4 +1,3 @@ -os: Visual Studio 2015 CTP version: 1.1.0.{build} configuration: @@ -11,26 +10,88 @@ environment: # VS_PLATFORM: win32 # - VS_VERSION: 9 2008 # VS_PLATFORM: x64 - - VS_VERSION: 10 2010 + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013 + VS_VERSION: 10 2010 VS_PLATFORM: win32 - - VS_VERSION: 10 2010 + CXX11: OFF + CXX17: OFF + MEMBERSMAP: OFF + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013 + VS_VERSION: 10 2010 VS_PLATFORM: x64 - - VS_VERSION: 11 2012 + CXX11: OFF + CXX17: OFF + MEMBERSMAP: ON + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013 + VS_VERSION: 11 2012 VS_PLATFORM: win32 - - VS_VERSION: 11 2012 + CXX11: OFF + CXX17: OFF + MEMBERSMAP: ON + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013 + VS_VERSION: 11 2012 VS_PLATFORM: x64 - - VS_VERSION: 12 2013 + CXX11: OFF + CXX17: OFF + MEMBERSMAP: OFF + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013 + VS_VERSION: 12 2013 VS_PLATFORM: win32 - - VS_VERSION: 12 2013 + CXX11: OFF + CXX17: OFF + MEMBERSMAP: OFF + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013 + VS_VERSION: 12 2013 VS_PLATFORM: x64 - - VS_VERSION: 14 2015 + CXX11: OFF + CXX17: OFF + MEMBERSMAP: ON + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + VS_VERSION: 14 2015 VS_PLATFORM: win32 - - VS_VERSION: 14 2015 + CXX11: OFF + CXX17: OFF + MEMBERSMAP: ON + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + VS_VERSION: 14 2015 VS_PLATFORM: x64 + CXX11: OFF + CXX17: OFF + MEMBERSMAP: OFF + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + VS_VERSION: 15 2017 + VS_PLATFORM: win32 + CXX11: OFF + CXX17: OFF + MEMBERSMAP: OFF + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + VS_VERSION: 15 2017 + VS_PLATFORM: x64 + CXX11: OFF + CXX17: OFF + MEMBERSMAP: ON + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + VS_VERSION: 15 2017 + VS_PLATFORM: x64 + CXX11: ON + CXX17: OFF + MEMBERSMAP: OFF + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + VS_VERSION: 15 2017 + VS_PLATFORM: x64 + CXX11: OFF + CXX17: ON + MEMBERSMAP: OFF + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + VS_VERSION: 16 2019 + VS_PLATFORM: x64 + CXX11: OFF + CXX17: ON + MEMBERSMAP: ON before_build: - git submodule update --init --recursive -- cmake -H. -BBuild/VS -G "Visual Studio %VS_VERSION%" -DCMAKE_GENERATOR_PLATFORM=%VS_PLATFORM% -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_SHARED_LIBS=true -Wno-dev +- cmake -H. -BBuild/VS -G "Visual Studio %VS_VERSION%" -DCMAKE_GENERATOR_PLATFORM=%VS_PLATFORM% -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_SHARED_LIBS=true -DRAPIDJSON_BUILD_CXX11=%CXX11% -DRAPIDJSON_BUILD_CXX17=%CXX17% -DRAPIDJSON_USE_MEMBERSMAP=%MEMBERSMAP% -Wno-dev build: project: Build\VS\RapidJSON.sln diff --git a/third_party/rapidjson/bin/data/abcde.txt b/third_party/rapidjson/bin/data/abcde.txt new file mode 100644 index 000000000..6a8165460 --- /dev/null +++ b/third_party/rapidjson/bin/data/abcde.txt @@ -0,0 +1 @@ +abcde \ No newline at end of file diff --git a/third_party/rapidjson/bin/jsonschema/remotes/.DS_Store b/third_party/rapidjson/bin/jsonschema/remotes/.DS_Store deleted file mode 100644 index 1d098a4103d67f2b3b336934f3d7f42b462861a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKOHRWu5S@X7Ds|H(?0kjZAgaO%dI3btXOYNNp?e>&D;7NuZ$2PWB6c8zW+eOB z^Ks%A#p59&UhngYXh}qKG(ncZgot|5bmq<%K-M+xX_ue7{;rgMVxhmNl6SwP2P)K4 zrjz#{8T!Z7rYpnNcX53hIFz={`93>*C>7{`CI$;>GS&XK|+FoU?3O>27-Z~ zU;sH=WWF$rJ{SlFf`JbPv%iZPLMpAHP(0stF?-7xoF z0$3~ntcg<~A}|dqFsPa>h6Ww+l6f_83JkhvHXoWdYj!B=x8wZc>7q4|BNdLa}rlnfC~I81+?j&yFH$iwRQ10tF;CG0=JwmxEbb7!QkZ>=;as-E60zX b6nVww*sqCGpwkg|I*>mDrVEV<{I&w$e{~fm diff --git a/third_party/rapidjson/bin/jsonschema/tests/draft4/.DS_Store b/third_party/rapidjson/bin/jsonschema/tests/draft4/.DS_Store deleted file mode 100644 index ef142295ea00d1d19fc3d30cc4e82dd207530825..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%}T^D5T4N$3SRc8x4go>L0IYo$i9Huf(orExO?vd?#YAC<2OGpmKHn+A~FM+ zFPY3tnh%;}h={j`c0;r#q6$rrrL!PnUYt5}=L;ZfjzTYVPhI=kbPI|8qDj8JqCx}h z=^1$X{)bX@53|YcakFbmKiF=rZcj9aqIv5BBrVO0ha4q-$4St!$ zB7YhZqhKHy_-738s@~OGY|8J}+4khFO=x#$BH}kn2ZH|O5rBc5BUd_U^GW*f%Z{U= TWD&cD1LGl}goFwPeu04xBO^7X diff --git a/third_party/rapidjson/bin/types/alotofkeys.json b/third_party/rapidjson/bin/types/alotofkeys.json new file mode 100644 index 0000000000000000000000000000000000000000..3fc052e34fdd414108ae7d6f1ce0ef1213e175fd GIT binary patch literal 30003 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 WQmKUbi1#2ZV>6{S3B0b~tp5RR=_+9W literal 0 HcmV?d00001 diff --git a/third_party/rapidjson/bin/unittestschema/allOf_address.json b/third_party/rapidjson/bin/unittestschema/allOf_address.json new file mode 100644 index 0000000000000000000000000000000000000000..fd501f66d457484de2a462ff5db2441e80210c5b GIT binary patch literal 84 zcmb>CQczGz%*pXjQ?gQs267Y>s=*{sSfwZxB&w8AQc_^0ub-2joS2i5UtD5kXk=`n apO}(Tlv-S@msOmfr>q3lQwtCQczGz%&YWIQ?gQs267Y>s=*{sSfwZxB&w8AQc_^0ub-2joS2i5UtD5kXk=`n apO}(Tlv-S@msOmfr>q3lQwtf+Fh<`7;fNfkizyZ**Gdnws zZ?h<3^2k;ZJ8505I2Ygfr8qTQy)?(sD#o|*#CnVyjbd6Mb*&- zv8y)I-S@uQKJiXgvVaJ-NuC_7te<)>azo3L)`31Yn`9B<8^J7t7z`^dXvnjm*dh5k zlnBePaHC}Qy+_f-!cr{VL`CQczIJ&r9`BQ?gQs267Y>s=*{sSfwZxB&w8AQc_^0ub-2joS2i5UtD5kXk=`n apO}(Tlv-S@msOmfr>q3lQwt + + + + null + true + false + {(const Ch*)data_.ss.str,na} + {(const Ch*)((size_t)data_.s.str & 0x0000FFFFFFFFFFFF),[data_.s.length]na} + {data_.n.i.i} + {data_.n.u.u} + {data_.n.i64} + {data_.n.u64} + {data_.n.d} + Object members={data_.o.size} + Array members={data_.a.size} + + data_.o.size + data_.o.capacity + + data_.o.size + + (rapidjson::GenericMember<$T1,$T2>*)(((size_t)data_.o.members) & 0x0000FFFFFFFFFFFF) + + + data_.a.size + data_.a.capacity + + data_.a.size + + (rapidjson::GenericValue<$T1,$T2>*)(((size_t)data_.a.elements) & 0x0000FFFFFFFFFFFF) + + + + + + + diff --git a/third_party/rapidjson/doc/CMakeLists.txt b/third_party/rapidjson/doc/CMakeLists.txt index c1f165a37..c5345ba69 100644 --- a/third_party/rapidjson/doc/CMakeLists.txt +++ b/third_party/rapidjson/doc/CMakeLists.txt @@ -10,11 +10,13 @@ ELSE() CONFIGURE_FILE(Doxyfile.in Doxyfile @ONLY) CONFIGURE_FILE(Doxyfile.zh-cn.in Doxyfile.zh-cn @ONLY) + file(GLOB DOXYFILES ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile*) + add_custom_command(OUTPUT html COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile.zh-cn COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/html - DEPENDS ${MARKDOWN_DOC} ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile* + DEPENDS ${MARKDOWN_DOC} ${SOURCES} ${DOXYFILES} WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/../ ) diff --git a/third_party/rapidjson/doc/Doxyfile.in b/third_party/rapidjson/doc/Doxyfile.in index ca1423396..6e79f9371 100644 --- a/third_party/rapidjson/doc/Doxyfile.in +++ b/third_party/rapidjson/doc/Doxyfile.in @@ -1126,7 +1126,7 @@ HTML_STYLESHEET = # defined cascading style sheet that is included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefor more robust against future updates. +# standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet file to the output directory. For an example # see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. diff --git a/third_party/rapidjson/doc/Doxyfile.zh-cn.in b/third_party/rapidjson/doc/Doxyfile.zh-cn.in index 87dd8661b..6a08f72d6 100644 --- a/third_party/rapidjson/doc/Doxyfile.zh-cn.in +++ b/third_party/rapidjson/doc/Doxyfile.zh-cn.in @@ -777,7 +777,7 @@ INPUT = readme.zh-cn.md \ doc/sax.zh-cn.md \ doc/schema.zh-cn.md \ doc/performance.zh-cn.md \ - doc/internals.md \ + doc/internals.zh-cn.md \ doc/faq.zh-cn.md # This tag can be used to specify the character encoding of the source files @@ -1126,7 +1126,7 @@ HTML_STYLESHEET = # defined cascading style sheet that is included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefor more robust against future updates. +# standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet file to the output directory. For an example # see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. diff --git a/third_party/rapidjson/doc/diagram/move2.dot b/third_party/rapidjson/doc/diagram/move2.dot index 7037ea6cb..2319871b9 100644 --- a/third_party/rapidjson/doc/diagram/move2.dot +++ b/third_party/rapidjson/doc/diagram/move2.dot @@ -18,7 +18,7 @@ digraph { node [shape=Mrecord, style=filled, colorscheme=spectral7] - c1 [label="{contact:array|}", fillcolor=4] + c1 [label="{contacts:array|}", fillcolor=4] c11 [label="{|}"] c12 [label="{|}"] c13 [shape="none", label="...", style="solid"] @@ -41,13 +41,13 @@ digraph { node [shape=Mrecord, style=filled, colorscheme=spectral7] - c2 [label="{contact:array|}", fillcolor=4] + c2 [label="{contacts:array|}", fillcolor=4] c3 [label="{array|}", fillcolor=4] c21 [label="{|}"] c22 [label="{|}"] c23 [shape=none, label="...", style="solid"] o2 [label="{o:object|}", fillcolor=3] - cs [label="{string|\"contact\"}", fillcolor=5] + cs [label="{string|\"contacts\"}", fillcolor=5] c31 [label="{|}"] c32 [label="{|}"] c33 [shape="none", label="...", style="solid"] @@ -59,4 +59,4 @@ digraph { c3 -> { c31; c32; c33 } } ghost -> o2 [style=invis] -} \ No newline at end of file +} diff --git a/third_party/rapidjson/doc/diagram/move3.dot b/third_party/rapidjson/doc/diagram/move3.dot index c197b99df..57adb4f9d 100644 --- a/third_party/rapidjson/doc/diagram/move3.dot +++ b/third_party/rapidjson/doc/diagram/move3.dot @@ -19,7 +19,7 @@ digraph { node [shape=Mrecord, style=filled, colorscheme=spectral7] - c1 [label="{contact:array|}", fillcolor=4] + c1 [label="{contacts:array|}", fillcolor=4] c11 [label="{|}"] c12 [label="{|}"] c13 [shape=none, label="...", style="solid"] @@ -42,13 +42,13 @@ digraph { node [shape=Mrecord, style=filled, colorscheme=spectral7] - c2 [label="{contact:null|}", fillcolor=1] + c2 [label="{contacts:null|}", fillcolor=1] c3 [label="{array|}", fillcolor=4] c21 [label="{|}"] c22 [label="{|}"] c23 [shape="none", label="...", style="solid"] o2 [label="{o:object|}", fillcolor=3] - cs [label="{string|\"contact\"}", fillcolor=5] + cs [label="{string|\"contacts\"}", fillcolor=5] c2 -> o2 [style="dashed", constraint=false, label="AddMember", style=invis] edge [arrowhead=vee] @@ -57,4 +57,4 @@ digraph { cs -> c3 [arrowhead=none] } ghost -> o2 [style=invis] -} \ No newline at end of file +} diff --git a/third_party/rapidjson/doc/dom.md b/third_party/rapidjson/doc/dom.md index 6c541fe93..6992e77d1 100644 --- a/third_party/rapidjson/doc/dom.md +++ b/third_party/rapidjson/doc/dom.md @@ -119,6 +119,7 @@ Parse flags | Meaning `kParseNumbersAsStringsFlag` | Parse numerical type values as strings. `kParseTrailingCommasFlag` | Allow trailing commas at the end of objects and arrays (relaxed JSON syntax). `kParseNanAndInfFlag` | Allow parsing `NaN`, `Inf`, `Infinity`, `-Inf` and `-Infinity` as `double` values (relaxed JSON syntax). +`kParseEscapedApostropheFlag` | Allow escaped apostrophe `\'` in strings (relaxed JSON syntax). By using a non-type template parameter, instead of a function parameter, C++ compiler can generate code which is optimized for specified combinations, improving speed, and reducing code size (if only using a single specialization). The downside is the flags needed to be determined in compile-time. @@ -128,7 +129,7 @@ And the `InputStream` is type of input stream. ## Parse Error {#ParseError} -When the parse processing succeeded, the `Document` contains the parse results. When there is an error, the original DOM is *unchanged*. And the error state of parsing can be obtained by `bool HasParseError()`, `ParseErrorCode GetParseError()` and `size_t GetParseOffset()`. +When the parse processing succeeded, the `Document` contains the parse results. When there is an error, the original DOM is *unchanged*. And the error state of parsing can be obtained by `bool HasParseError()`, `ParseErrorCode GetParseError()` and `size_t GetErrorOffset()`. Parse Error Code | Description --------------------------------------------|--------------------------------------------------- @@ -241,7 +242,7 @@ Some techniques about using DOM API is discussed here. ## DOM as SAX Event Publisher -In RapidJSON, stringifying a DOM with `Writer` may be look a little bit weired. +In RapidJSON, stringifying a DOM with `Writer` may be look a little bit weird. ~~~~~~~~~~cpp // ... diff --git a/third_party/rapidjson/doc/dom.zh-cn.md b/third_party/rapidjson/doc/dom.zh-cn.md index d93f6036b..7a555dc1c 100644 --- a/third_party/rapidjson/doc/dom.zh-cn.md +++ b/third_party/rapidjson/doc/dom.zh-cn.md @@ -1,6 +1,6 @@ # DOM -文档对象模型(Document Object Model, DOM)是一种罝于内存中的 JSON 表示方式,以供查询及操作。我们己于 [教程](doc/tutorial.zh-cn.md) 中介绍了 DOM 的基本用法,本节将讲述一些细节及高级用法。 +文档对象模型(Document Object Model, DOM)是一种罝于内存中的 JSON 表示方式,以供查询及操作。我们已于 [教程](doc/tutorial.zh-cn.md) 中介绍了 DOM 的基本用法,本节将讲述一些细节及高级用法。 [TOC] @@ -119,6 +119,7 @@ GenericDocument& GenericDocument::Parse(const Ch* str); `kParseNumbersAsStringsFlag` | 把数字类型解析成字符串。 `kParseTrailingCommasFlag` | 容许在对象和数组结束前含有逗号(放宽的 JSON 语法)。 `kParseNanAndInfFlag` | 容许 `NaN`、`Inf`、`Infinity`、`-Inf` 及 `-Infinity` 作为 `double` 值(放宽的 JSON 语法)。 +`kParseEscapedApostropheFlag` | 容许字符串中转义单引号 `\'` (放宽的 JSON 语法)。 由于使用了非类型模板参数,而不是函数参数,C++ 编译器能为个别组合生成代码,以改善性能及减少代码尺寸(当只用单种特化)。缺点是需要在编译期决定标志。 @@ -128,7 +129,7 @@ GenericDocument& GenericDocument::Parse(const Ch* str); ## 解析错误 {#ParseError} -当解析过程顺利完成,`Document` 便会含有解析结果。当过程出现错误,原来的 DOM 会 * 维持不便 *。可使用 `bool HasParseError()`、`ParseErrorCode GetParseError()` 及 `size_t GetParseOffset()` 获取解析的错误状态。 +当解析过程顺利完成,`Document` 便会含有解析结果。当过程出现错误,原来的 DOM 会*维持不变*。可使用 `bool HasParseError()`、`ParseErrorCode GetParseError()` 及 `size_t GetErrorOffset()` 获取解析的错误状态。 解析错误代号 | 描述 --------------------------------------------|--------------------------------------------------- diff --git a/third_party/rapidjson/doc/encoding.md b/third_party/rapidjson/doc/encoding.md index 8f8ff7f45..e663aeac9 100644 --- a/third_party/rapidjson/doc/encoding.md +++ b/third_party/rapidjson/doc/encoding.md @@ -10,7 +10,7 @@ The earlier [RFC4627](http://www.ietf.org/rfc/rfc4627.txt) stated that, > (in §6) JSON may be represented using UTF-8, UTF-16, or UTF-32. When JSON is written in UTF-8, JSON is 8bit compatible. When JSON is written in UTF-16 or UTF-32, the binary content-transfer-encoding must be used. -RapidJSON supports various encodings. It can also validate the encodings of JSON, and transconding JSON among encodings. All these features are implemented internally, without the need for external libraries (e.g. [ICU](http://site.icu-project.org/)). +RapidJSON supports various encodings. It can also validate the encodings of JSON, and transcoding JSON among encodings. All these features are implemented internally, without the need for external libraries (e.g. [ICU](http://site.icu-project.org/)). [TOC] diff --git a/third_party/rapidjson/doc/encoding.zh-cn.md b/third_party/rapidjson/doc/encoding.zh-cn.md index 681692355..808ba525f 100644 --- a/third_party/rapidjson/doc/encoding.zh-cn.md +++ b/third_party/rapidjson/doc/encoding.zh-cn.md @@ -14,7 +14,7 @@ > (in §6) JSON may be represented using UTF-8, UTF-16, or UTF-32. When JSON is written in UTF-8, JSON is 8bit compatible. When JSON is written in UTF-16 or UTF-32, the binary content-transfer-encoding must be used. > -> 翻译:JSON 可使用 UTF-8、UTF-16 或 UTF-18 表示。当 JSON 以 UTF-8 写入,该 JSON 是 8 位兼容的。当 JSON 以 UTF-16 或 UTF-32 写入,就必须使用二进制的内容传送编码。 +> 翻译:JSON 可使用 UTF-8、UTF-16 或 UTF-32 表示。当 JSON 以 UTF-8 写入,该 JSON 是 8 位兼容的。当 JSON 以 UTF-16 或 UTF-32 写入,就必须使用二进制的内容传送编码。 RapidJSON 支持多种编码。它也能检查 JSON 的编码,以及在不同编码中进行转码。所有这些功能都是在内部实现,无需使用外部的程序库(如 [ICU](http://site.icu-project.org/))。 diff --git a/third_party/rapidjson/doc/faq.md b/third_party/rapidjson/doc/faq.md index 1b0541c27..55fa2af88 100644 --- a/third_party/rapidjson/doc/faq.md +++ b/third_party/rapidjson/doc/faq.md @@ -18,7 +18,7 @@ 4. Is RapidJSON free? - Yes, it is free under MIT license. It can be used in commercial applications. Please check the details in [license.txt](https://github.com/miloyip/rapidjson/blob/master/license.txt). + Yes, it is free under MIT license. It can be used in commercial applications. Please check the details in [license.txt](https://github.com/Tencent/rapidjson/blob/master/license.txt). 5. Is RapidJSON small? What are its dependencies? @@ -44,7 +44,7 @@ 10. How RapidJSON is tested? - RapidJSON contains a unit test suite for automatic testing. [Travis](https://travis-ci.org/miloyip/rapidjson/)(for Linux) and [AppVeyor](https://ci.appveyor.com/project/miloyip/rapidjson/)(for Windows) will compile and run the unit test suite for all modifications. The test process also uses Valgrind (in Linux) to detect memory leaks. + RapidJSON contains a unit test suite for automatic testing. [Travis](https://travis-ci.org/Tencent/rapidjson/)(for Linux) and [AppVeyor](https://ci.appveyor.com/project/Tencent/rapidjson/)(for Windows) will compile and run the unit test suite for all modifications. The test process also uses Valgrind (in Linux) to detect memory leaks. 11. Is RapidJSON well documented? @@ -64,13 +64,13 @@ JSON are commonly used in web applications for transferring structured data. It is also used as a file format for data persistence. -2. Does RapidJSON conform to the JSON standard? +3. Does RapidJSON conform to the JSON standard? Yes. RapidJSON is fully compliance with [RFC7159](http://www.ietf.org/rfc/rfc7159.txt) and [ECMA-404](http://www.ecma-international.org/publications/standards/Ecma-404.htm). It can handle corner cases, such as supporting null character and surrogate pairs in JSON strings. -3. Does RapidJSON support relaxed syntax? +4. Does RapidJSON support relaxed syntax? - Currently no. RapidJSON only support the strict standardized format. Support on related syntax is under discussion in this [issue](https://github.com/miloyip/rapidjson/issues/36). + Currently no. RapidJSON only support the strict standardized format. Support on related syntax is under discussion in this [issue](https://github.com/Tencent/rapidjson/issues/36). ## DOM and SAX @@ -116,7 +116,7 @@ ~~~~~~~~~~cpp Value(kObjectType).Swap(d); ~~~~~~~~~~ - or equivalent, but sightly longer to type: + or equivalent, but slightly longer to type: ~~~~~~~~~~cpp d.Swap(Value(kObjectType).Move()); ~~~~~~~~~~ @@ -140,11 +140,11 @@ } ~~~~~~~~~~ - The most important requirement to take care of document and value life-cycle as well as consistent memory managent using the right allocator during the value transfer. + The most important requirement to take care of document and value life-cycle as well as consistent memory management using the right allocator during the value transfer. Simple yet most efficient way to achieve that is to modify the `address` definition above to initialize it with allocator of the `person` document, then we just add the root member of the value: ~~~~~~~~~~cpp - Documnet address(person.GetAllocator()); + Document address(&person.GetAllocator()); ... person["person"].AddMember("address", address["address"], person.GetAllocator()); ~~~~~~~~~~ @@ -174,7 +174,7 @@ Alternatively, if we don't want to explicitly refer to the root value of `addres 3. Why do I need to provide the length of string? - Since C string is null-terminated, the length of string needs to be computed via `strlen()`, with linear runtime complexity. This incurs an unncessary overhead of many operations, if the user already knows the length of string. + Since C string is null-terminated, the length of string needs to be computed via `strlen()`, with linear runtime complexity. This incurs an unnecessary overhead of many operations, if the user already knows the length of string. Also, RapidJSON can handle `\u0000` (null character) within a string. If a string contains null characters, `strlen()` cannot return the true length of it. In such case user must provide the length of string explicitly. @@ -204,7 +204,7 @@ Alternatively, if we don't want to explicitly refer to the root value of `addres 2. Can it validate the encoding? - Yes, just pass `kParseValidateEncodingFlag` to `Parse()`. If there is invalid encoding in the stream, it wil generate `kParseErrorStringInvalidEncoding` error. + Yes, just pass `kParseValidateEncodingFlag` to `Parse()`. If there is invalid encoding in the stream, it will generate `kParseErrorStringInvalidEncoding` error. 3. What is surrogate pair? Does RapidJSON support it? @@ -236,7 +236,7 @@ Alternatively, if we don't want to explicitly refer to the root value of `addres 4. What is BOM? How RapidJSON handle it? - [Byte order mark (BOM)](http://en.wikipedia.org/wiki/Byte_order_mark) sometimes reside at the beginning of file/stream to indiciate the UTF encoding type of it. + [Byte order mark (BOM)](http://en.wikipedia.org/wiki/Byte_order_mark) sometimes reside at the beginning of file/stream to indicate the UTF encoding type of it. RapidJSON's `EncodedInputStream` can detect/consume BOM. `EncodedOutputStream` can optionally write a BOM. See [Encoded Streams](doc/stream.md) for example. @@ -248,7 +248,7 @@ Alternatively, if we don't want to explicitly refer to the root value of `addres 1. Is RapidJSON really fast? - Yes. It may be the fastest open source JSON library. There is a [benchmark](https://github.com/miloyip/nativejson-benchmark) for evaluating performance of C/C++ JSON libaries. + Yes. It may be the fastest open source JSON library. There is a [benchmark](https://github.com/miloyip/nativejson-benchmark) for evaluating performance of C/C++ JSON libraries. 2. Why is it fast? @@ -256,19 +256,19 @@ Alternatively, if we don't want to explicitly refer to the root value of `addres 3. What is SIMD? How it is applied in RapidJSON? - [SIMD](http://en.wikipedia.org/wiki/SIMD) instructions can perform parallel computation in modern CPUs. RapidJSON support Intel's SSE2/SSE4.2 to accelerate whitespace skipping. This improves performance of parsing indent formatted JSON. Define `RAPIDJSON_SSE2` or `RAPIDJSON_SSE42` macro to enable this feature. However, running the executable on a machine without such instruction set support will make it crash. + [SIMD](http://en.wikipedia.org/wiki/SIMD) instructions can perform parallel computation in modern CPUs. RapidJSON support Intel's SSE2/SSE4.2 and ARM's Neon to accelerate whitespace/tabspace/carriage-return/line-feed skipping. This improves performance of parsing indent formatted JSON. Define `RAPIDJSON_SSE2`, `RAPIDJSON_SSE42` or `RAPIDJSON_NEON` macro to enable this feature. However, running the executable on a machine without such instruction set support will make it crash. 4. Does it consume a lot of memory? The design of RapidJSON aims at reducing memory footprint. - In the SAX API, `Reader` consumes memory portional to maximum depth of JSON tree, plus maximum length of JSON string. + In the SAX API, `Reader` consumes memory proportional to maximum depth of JSON tree, plus maximum length of JSON string. In the DOM API, each `Value` consumes exactly 16/24 bytes for 32/64-bit architecture respectively. RapidJSON also uses a special memory allocator to minimize overhead of allocations. 5. What is the purpose of being high performance? - Some applications need to process very large JSON files. Some server-side applications need to process huge amount of JSONs. Being high performance can improve both latency and throuput. In a broad sense, it will also save energy. + Some applications need to process very large JSON files. Some server-side applications need to process huge amount of JSONs. Being high performance can improve both latency and throughput. In a broad sense, it will also save energy. ## Gossip diff --git a/third_party/rapidjson/doc/faq.zh-cn.md b/third_party/rapidjson/doc/faq.zh-cn.md index ed100e112..cf1124d82 100644 --- a/third_party/rapidjson/doc/faq.zh-cn.md +++ b/third_party/rapidjson/doc/faq.zh-cn.md @@ -18,7 +18,7 @@ 4. RapidJSON 是免费的么? - 是的,它在 MIT 特許條款下免费。它可用于商业软件。详情请参看 [license.txt](https://github.com/miloyip/rapidjson/blob/master/license.txt)。 + 是的,它在 MIT 协议下免费。它可用于商业软件。详情请参看 [license.txt](https://github.com/Tencent/rapidjson/blob/master/license.txt)。 5. RapidJSON 很小么?它有何依赖? @@ -44,7 +44,7 @@ 10. RapidJSON 是如何被测试的? - RapidJSON 包含一组单元测试去执行自动测试。[Travis](https://travis-ci.org/miloyip/rapidjson/)(供 Linux 平台)及 [AppVeyor](https://ci.appveyor.com/project/miloyip/rapidjson/)(供 Windows 平台)会对所有修改进行编译及执行单元测试。在 Linux 下还会使用 Valgrind 去检测内存泄漏。 + RapidJSON 包含一组单元测试去执行自动测试。[Travis](https://travis-ci.org/Tencent/rapidjson/)(供 Linux 平台)及 [AppVeyor](https://ci.appveyor.com/project/Tencent/rapidjson/)(供 Windows 平台)会对所有修改进行编译及执行单元测试。在 Linux 下还会使用 Valgrind 去检测内存泄漏。 11. RapidJSON 是否有完整的文档? @@ -64,13 +64,13 @@ JSON 常用于网页应用程序,以传送结构化数据。它也可作为文件格式用于数据持久化。 -2. RapidJSON 是否符合 JSON 标准? +3. RapidJSON 是否符合 JSON 标准? 是。RapidJSON 完全符合 [RFC7159](http://www.ietf.org/rfc/rfc7159.txt) 及 [ECMA-404](http://www.ecma-international.org/publications/standards/Ecma-404.htm)。它能处理一些特殊情况,例如支持 JSON 字符串中含有空字符及代理对(surrogate pair)。 -3. RapidJSON 是否支持宽松的语法? +4. RapidJSON 是否支持宽松的语法? - 现时不支持。RapidJSON 只支持严格的标准格式。宽松语法现时在这 [issue](https://github.com/miloyip/rapidjson/issues/36) 中进行讨论。 + 目前不支持。RapidJSON 只支持严格的标准格式。宽松语法可以在这个 [issue](https://github.com/Tencent/rapidjson/issues/36) 中进行讨论。 ## DOM 与 SAX @@ -145,7 +145,7 @@ 一个简单有效的方法就是修改上述 `address` 变量的定义,让其使用 `person` 的 allocator 初始化,然后将其添加到根节点。 ~~~~~~~~~~cpp - Documnet address(person.GetAllocator()); + Documnet address(&person.GetAllocator()); ... person["person"].AddMember("address", address["address"], person.GetAllocator()); ~~~~~~~~~~ @@ -163,9 +163,9 @@ ## Document/Value (DOM) -1. 什么是转移语意?为什么? +1. 什么是转移语义?为什么? - `Value` 不用复制语意,而使用了转移语意。这是指,当把来源值赋值于目标值时,来源值的所有权会转移至目标值。 + `Value` 不用复制语义,而使用了转移语义。这是指,当把来源值赋值于目标值时,来源值的所有权会转移至目标值。 由于转移快于复制,此设计决定强迫使用者注意到复制的消耗。 @@ -257,7 +257,7 @@ 3. 什是是 SIMD?它如何用于 RapidJSON? - [SIMD](http://en.wikipedia.org/wiki/SIMD) 指令可以在现代 CPU 中执行并行运算。RapidJSON 支持了 Intel 的 SSE2/SSE4.2 去加速跳过空白字符。在解析含缩进的 JSON 时,这能提升性能。只要定义名为 `RAPIDJSON_SSE2` 或 `RAPIDJSON_SSE42` 的宏,就能启动这个功能。然而,若在不支持这些指令集的机器上执行这些可执行文件,会导致崩溃。 + [SIMD](http://en.wikipedia.org/wiki/SIMD) 指令可以在现代 CPU 中执行并行运算。RapidJSON 支持使用 Intel 的 SSE2/SSE4.2 和 ARM 的 Neon 来加速对空白符、制表符、回车符和换行符的过滤处理。在解析含缩进的 JSON 时,这能提升性能。只要定义名为 `RAPIDJSON_SSE2` ,`RAPIDJSON_SSE42` 或 `RAPIDJSON_NEON` 的宏,就能启动这个功能。然而,若在不支持这些指令集的机器上执行这些可执行文件,会导致崩溃。 4. 它会消耗许多内存么? @@ -271,7 +271,7 @@ 有些应用程序需要处理非常大的 JSON 文件。而有些后台应用程序需要处理大量的 JSON。达到高性能同时改善延时及吞吐量。更广义来说,这也可以节省能源。 -## 八挂 +## 八卦 1. 谁是 RapidJSON 的开发者? @@ -279,11 +279,11 @@ 2. 为何你要开发 RapidJSON? - 在 2011 年开始这项目是,它仅一个兴趣项目。Milo Yip 是一个游戏程序员,他在那时候认识到 JSON 并希望在未来的项目中使用。由于 JSON 好像很简单,他希望写一个仅有头文件并且快速的程序库。 + 在 2011 年开始这项目时,它只是一个兴趣项目。Milo Yip 是一个游戏程序员,他在那时候认识到 JSON 并希望在未来的项目中使用。由于 JSON 好像很简单,他希望写一个快速的仅有头文件的程序库。 3. 为什么开发中段有一段长期空档? - 主要是个人因素,例如加入新家庭成员。另外,Milo Yip 也花了许多业馀时间去翻译 Jason Gregory 的《Game Engine Architecture》至中文版《游戏引擎架构》。 + 主要是个人因素,例如加入新家庭成员。另外,Milo Yip 也花了许多业余时间去翻译 Jason Gregory 的《Game Engine Architecture》至中文版《游戏引擎架构》。 4. 为什么这个项目从 Google Code 搬到 GitHub? diff --git a/third_party/rapidjson/doc/features.md b/third_party/rapidjson/doc/features.md index 732fb21f4..4d159370a 100644 --- a/third_party/rapidjson/doc/features.md +++ b/third_party/rapidjson/doc/features.md @@ -22,14 +22,16 @@ * RapidJSON should be fully RFC4627/ECMA-404 compliance. * Support JSON Pointer (RFC6901). * Support JSON Schema Draft v4. +* Support Swagger v2 schema. +* Support OpenAPI v3.0.x schema. * Support Unicode surrogate. * Support null character (`"\u0000"`) - * For example, `["Hello\u0000World"]` can be parsed and handled gracefully. There is API for getting/setting lengths of string. +* For example, `["Hello\u0000World"]` can be parsed and handled gracefully. There is API for getting/setting lengths of string. * Support optional relaxed syntax. - * Single line (`// ...`) and multiple line (`/* ... */`) comments (`kParseCommentsFlag`). - * Trailing commas at the end of objects and arrays (`kParseTrailingCommasFlag`). - * `NaN`, `Inf`, `Infinity`, `-Inf` and `-Infinity` as `double` values (`kParseNanAndInfFlag`) -* [NPM compliant](http://github.com/miloyip/rapidjson/blob/master/doc/npm.md). +* Single line (`// ...`) and multiple line (`/* ... */`) comments (`kParseCommentsFlag`). +* Trailing commas at the end of objects and arrays (`kParseTrailingCommasFlag`). +* `NaN`, `Inf`, `Infinity`, `-Inf` and `-Infinity` as `double` values (`kParseNanAndInfFlag`) +* [NPM compliant](http://github.com/Tencent/rapidjson/blob/master/doc/npm.md). ## Unicode diff --git a/third_party/rapidjson/doc/features.zh-cn.md b/third_party/rapidjson/doc/features.zh-cn.md index fd3fd4d66..7662cc13e 100644 --- a/third_party/rapidjson/doc/features.zh-cn.md +++ b/third_party/rapidjson/doc/features.zh-cn.md @@ -22,14 +22,14 @@ * RapidJSON 应完全符合 RFC4627/ECMA-404 标准。 * 支持 JSON Pointer (RFC6901). * 支持 JSON Schema Draft v4. -* 支持 Unicod 代理对(surrogate pair)。 +* 支持 Unicode 代理对(surrogate pair)。 * 支持空字符(`"\u0000"`)。 * 例如,可以优雅地解析及处理 `["Hello\u0000World"]`。含读写字符串长度的 API。 * 支持可选的放宽语法 * 单行(`// ...`)及多行(`/* ... */`) 注释 (`kParseCommentsFlag`)。 * 在对象和数组结束前含逗号 (`kParseTrailingCommasFlag`)。 * `NaN`、`Inf`、`Infinity`、`-Inf` 及 `-Infinity` 作为 `double` 值 (`kParseNanAndInfFlag`) -* [NPM 兼容](https://github.com/miloyip/rapidjson/blob/master/doc/npm.md). +* [NPM 兼容](https://github.com/Tencent/rapidjson/blob/master/doc/npm.md). ## Unicode diff --git a/third_party/rapidjson/doc/internals.md b/third_party/rapidjson/doc/internals.md index 49802a0fd..81fe9c16e 100644 --- a/third_party/rapidjson/doc/internals.md +++ b/third_party/rapidjson/doc/internals.md @@ -28,7 +28,7 @@ Both SAX and DOM APIs depends on 3 additional concepts: `Allocator`, `Encoding` ## Data Layout {#DataLayout} -`Value` is a [variant type](http://en.wikipedia.org/wiki/Variant_type). In RapidJSON's context, an instance of `Value` can contain 1 of 6 JSON value types. This is possible by using `union`. Each `Value` contains two members: `union Data data_` and a`unsigned flags_`. The `flags_` indiciates the JSON type, and also additional information. +`Value` is a [variant type](http://en.wikipedia.org/wiki/Variant_type). In RapidJSON's context, an instance of `Value` can contain 1 of 6 JSON value types. This is possible by using `union`. Each `Value` contains two members: `union Data data_` and a`unsigned flags_`. The `flags_` indicates the JSON type, and also additional information. The following tables show the data layout of each type. The 32-bit/64-bit columns indicates the size of the field in bytes. @@ -79,7 +79,7 @@ The following tables show the data layout of each type. The 32-bit/64-bit column | `unsigned u` | 32-bit unsigned integer |4 |4 | | (zero padding) | 0 |4 |4 | | (unused) | |4 |8 | -| `unsigned flags_` | `kNumberType kNumberFlag kUIntFlag kUInt64Flag ...` |4 |4 | +| `unsigned flags_` | `kNumberType kNumberFlag kUintFlag kUint64Flag ...` |4 |4 | | Number (Int64) | |32-bit|64-bit| |---------------------|-------------------------------------|:----:|:----:| @@ -101,7 +101,7 @@ The following tables show the data layout of each type. The 32-bit/64-bit column Here are some notes: * To reduce memory consumption for 64-bit architecture, `SizeType` is typedef as `unsigned` instead of `size_t`. -* Zero padding for 32-bit number may be placed after or before the actual type, according to the endianess. This makes possible for interpreting a 32-bit integer as a 64-bit integer, without any conversion. +* Zero padding for 32-bit number may be placed after or before the actual type, according to the endianness. This makes possible for interpreting a 32-bit integer as a 64-bit integer, without any conversion. * An `Int` is always an `Int64`, but the converse is not always true. ## Flags {#Flags} @@ -183,17 +183,20 @@ void SkipWhitespace(InputStream& s) { However, this requires 4 comparisons and a few branching for each character. This was found to be a hot spot. -To accelerate this process, SIMD was applied to compare 16 characters with 4 white spaces for each iteration. Currently RapidJSON only supports SSE2 and SSE4.2 instructions for this. And it is only activated for UTF-8 memory streams, including string stream or *in situ* parsing. +To accelerate this process, SIMD was applied to compare 16 characters with 4 white spaces for each iteration. Currently RapidJSON supports SSE2, SSE4.2 and ARM Neon instructions for this. And it is only activated for UTF-8 memory streams, including string stream or *in situ* parsing. -To enable this optimization, need to define `RAPIDJSON_SSE2` or `RAPIDJSON_SSE42` before including `rapidjson.h`. Some compilers can detect the setting, as in `perftest.h`: +To enable this optimization, need to define `RAPIDJSON_SSE2`, `RAPIDJSON_SSE42` or `RAPIDJSON_NEON` before including `rapidjson.h`. Some compilers can detect the setting, as in `perftest.h`: ~~~cpp // __SSE2__ and __SSE4_2__ are recognized by gcc, clang, and the Intel compiler. // We use -march=native with gmake to enable -msse2 and -msse4.2, if supported. +// Likewise, __ARM_NEON is used to detect Neon. #if defined(__SSE4_2__) # define RAPIDJSON_SSE42 #elif defined(__SSE2__) # define RAPIDJSON_SSE2 +#elif defined(__ARM_NEON) +# define RAPIDJSON_NEON #endif ~~~ @@ -211,7 +214,7 @@ In [Intel® 64 and IA-32 Architectures Optimization Reference Manual This is not feasible as RapidJSON should not enforce such requirement. -To fix this issue, currently the routine process bytes up to the next aligned address. After tha, use aligned read to perform SIMD processing. Also see [#85](https://github.com/miloyip/rapidjson/issues/85). +To fix this issue, currently the routine process bytes up to the next aligned address. After tha, use aligned read to perform SIMD processing. Also see [#85](https://github.com/Tencent/rapidjson/issues/85). ## Local Stream Copy {#LocalStreamCopy} diff --git a/third_party/rapidjson/doc/internals.zh-cn.md b/third_party/rapidjson/doc/internals.zh-cn.md new file mode 100644 index 000000000..d414fc140 --- /dev/null +++ b/third_party/rapidjson/doc/internals.zh-cn.md @@ -0,0 +1,363 @@ +# 内部架构 + +本部分记录了一些设计和实现细节。 + +[TOC] + +# 架构 {#Architecture} + +## SAX 和 DOM + +下面的 UML 图显示了 SAX 和 DOM 的基本关系。 + +![架构 UML 类图](diagram/architecture.png) + +关系的核心是 `Handler` 概念。在 SAX 一边,`Reader` 从流解析 JSON 并将事件发送到 `Handler`。`Writer` 实现了 `Handler` 概念,用于处理相同的事件。在 DOM 一边,`Document` 实现了 `Handler` 概念,用于通过这些时间来构建 DOM。`Value` 支持了 `Value::Accept(Handler&)` 函数,它可以将 DOM 转换为事件进行发送。 + +在这个设计,SAX 是不依赖于 DOM 的。甚至 `Reader` 和 `Writer` 之间也没有依赖。这提供了连接事件发送器和处理器的灵活性。除此之外,`Value` 也是不依赖于 SAX 的。所以,除了将 DOM 序列化为 JSON 之外,用户也可以将其序列化为 XML,或者做任何其他事情。 + +## 工具类 + +SAX 和 DOM API 都依赖于3个额外的概念:`Allocator`、`Encoding` 和 `Stream`。它们的继承层次结构如下图所示。 + +![工具类 UML 类图](diagram/utilityclass.png) + +# 值(Value) {#Value} + +`Value` (实际上被定义为 `GenericValue>`)是 DOM API 的核心。本部分描述了它的设计。 + +## 数据布局 {#DataLayout} + +`Value` 是[可变类型](http://en.wikipedia.org/wiki/Variant_type)。在 RapidJSON 的上下文中,一个 `Value` 的实例可以包含6种 JSON 数据类型之一。通过使用 `union` ,这是可能实现的。每一个 `Value` 包含两个成员:`union Data data_` 和 `unsigned flags_`。`flags_` 表明了 JSON 类型,以及附加的信息。 + +下表显示了所有类型的数据布局。32位/64位列表明了字段所占用的字节数。 + +| Null | | 32位 | 64位 | +|-------------------|----------------------------------|:----:|:----:| +| (未使用) | |4 |8 | +| (未使用) | |4 |4 | +| (未使用) | |4 |4 | +| `unsigned flags_` | `kNullType kNullFlag` |4 |4 | + +| Bool | | 32位 | 64位 | +|-------------------|----------------------------------------------------|:----:|:----:| +| (未使用) | |4 |8 | +| (未使用) | |4 |4 | +| (未使用) | |4 |4 | +| `unsigned flags_` | `kBoolType` (either `kTrueFlag` or `kFalseFlag`) |4 |4 | + +| String | | 32位 | 64位 | +|---------------------|-------------------------------------|:----:|:----:| +| `Ch* str` | 指向字符串的指针(可能拥有所有权) |4 |8 | +| `SizeType length` | 字符串长度 |4 |4 | +| (未使用) | |4 |4 | +| `unsigned flags_` | `kStringType kStringFlag ...` |4 |4 | + +| Object | | 32位 | 64位 | +|---------------------|-------------------------------------|:----:|:----:| +| `Member* members` | 指向成员数组的指针(拥有所有权) |4 |8 | +| `SizeType size` | 成员数量 |4 |4 | +| `SizeType capacity` | 成员容量 |4 |4 | +| `unsigned flags_` | `kObjectType kObjectFlag` |4 |4 | + +| Array | | 32位 | 64位 | +|---------------------|-------------------------------------|:----:|:----:| +| `Value* values` | 指向值数组的指针(拥有所有权) |4 |8 | +| `SizeType size` | 值数量 |4 |4 | +| `SizeType capacity` | 值容量 |4 |4 | +| `unsigned flags_` | `kArrayType kArrayFlag` |4 |4 | + +| Number (Int) | | 32位 | 64位 | +|---------------------|-------------------------------------|:----:|:----:| +| `int i` | 32位有符号整数 |4 |4 | +| (零填充) | 0 |4 |4 | +| (未使用) | |4 |8 | +| `unsigned flags_` | `kNumberType kNumberFlag kIntFlag kInt64Flag ...` |4 |4 | + +| Number (UInt) | | 32位 | 64位 | +|---------------------|-------------------------------------|:----:|:----:| +| `unsigned u` | 32位无符号整数 |4 |4 | +| (零填充) | 0 |4 |4 | +| (未使用) | |4 |8 | +| `unsigned flags_` | `kNumberType kNumberFlag kUintFlag kUint64Flag ...` |4 |4 | + +| Number (Int64) | | 32位 | 64位 | +|---------------------|-------------------------------------|:----:|:----:| +| `int64_t i64` | 64位有符号整数 |8 |8 | +| (未使用) | |4 |8 | +| `unsigned flags_` | `kNumberType kNumberFlag kInt64Flag ...` |4 |4 | + +| Number (Uint64) | | 32位 | 64位 | +|---------------------|-------------------------------------|:----:|:----:| +| `uint64_t i64` | 64位无符号整数 |8 |8 | +| (未使用) | |4 |8 | +| `unsigned flags_` | `kNumberType kNumberFlag kInt64Flag ...` |4 |4 | + +| Number (Double) | | 32位 | 64位 | +|---------------------|-------------------------------------|:----:|:----:| +| `uint64_t i64` | 双精度浮点数 |8 |8 | +| (未使用) | |4 |8 | +| `unsigned flags_` |`kNumberType kNumberFlag kDoubleFlag`|4 |4 | + +这里有一些需要注意的地方: +* 为了减少在64位架构上的内存消耗,`SizeType` 被定义为 `unsigned` 而不是 `size_t`。 +* 32位整数的零填充可能被放在实际类型的前面或后面,这依赖于字节序。这使得它可以将32位整数不经过任何转换就可以解释为64位整数。 +* `Int` 永远是 `Int64`,反之不然。 + +## 标志 {#Flags} + +32位的 `flags_` 包含了 JSON 类型和其他信息。如前文中的表所述,每一种 JSON 类型包含了冗余的 `kXXXType` 和 `kXXXFlag`。这个设计是为了优化测试位标志(`IsNumber()`)和获取每一种类型的序列号(`GetType()`)。 + +字符串有两个可选的标志。`kCopyFlag` 表明这个字符串拥有字符串拷贝的所有权。而 `kInlineStrFlag` 意味着使用了[短字符串优化](#ShortString)。 + +数字更加复杂一些。对于普通的整数值,它可以包含 `kIntFlag`、`kUintFlag`、 `kInt64Flag` 和/或 `kUint64Flag`,这由整数的范围决定。带有小数或者超过64位所能表达的范围的整数的数字会被存储为带有 `kDoubleFlag` 的 `double`。 + +## 短字符串优化 {#ShortString} + +[Kosta](https://github.com/Kosta-Github) 提供了很棒的短字符串优化。这个优化的xxx如下所述。除去 `flags_` ,`Value` 有12或16字节(对于32位或64位)来存储实际的数据。这为在其内部直接存储短字符串而不是存储字符串的指针创造了可能。对于1字节的字符类型(例如 `char`),它可以在 `Value` 类型内部存储至多11或15个字符的字符串。 + +|ShortString (Ch=char)| | 32位 | 64位 | +|---------------------|-------------------------------------|:----:|:----:| +| `Ch str[MaxChars]` | 字符串缓冲区 |11 |15 | +| `Ch invLength` | MaxChars - Length |1 |1 | +| `unsigned flags_` | `kStringType kStringFlag ...` |4 |4 | + +这里使用了一项特殊的技术。它存储了 (MaxChars - length) 而不直接存储字符串的长度。这使得存储11个字符并且带有后缀 `\0` 成为可能。 + +这个优化可以减少字符串拷贝内存占用。它也改善了缓存一致性,并进一步提高了运行时性能。 + +# 分配器(Allocator) {#InternalAllocator} + +`Allocator` 是 RapidJSON 中的概念: +~~~cpp +concept Allocator { + static const bool kNeedFree; //!< 表明这个分配器是否需要调用 Free()。 + + // 申请内存块。 + // \param size 内存块的大小,以字节记。 + // \returns 指向内存块的指针。 + void* Malloc(size_t size); + + // 调整内存块的大小。 + // \param originalPtr 当前内存块的指针。空指针是被允许的。 + // \param originalSize 当前大小,以字节记。(设计问题:因为有些分配器可能不会记录它,显示的传递它可以节约内存。) + // \param newSize 新大小,以字节记。 + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize); + + // 释放内存块。 + // \param ptr 指向内存块的指针。空指针是被允许的。 + static void Free(void *ptr); +}; +~~~ + +需要注意的是 `Malloc()` 和 `Realloc()` 是成员函数而 `Free()` 是静态成员函数。 + +## MemoryPoolAllocator {#MemoryPoolAllocator} + +`MemoryPoolAllocator` 是 DOM 的默认内存分配器。它只申请内存而不释放内存。这对于构建 DOM 树非常合适。 + +在它的内部,它从基础的内存分配器申请内存块(默认为 `CrtAllocator`)并将这些内存块存储为单向链表。当用户请求申请内存,它会遵循下列步骤来申请内存: + +1. 如果可用,使用用户提供的缓冲区。(见 [User Buffer section in DOM](doc/dom.md)) +2. 如果用户提供的缓冲区已满,使用当前内存块。 +3. 如果当前内存块已满,申请新的内存块。 + +# 解析优化 {#ParsingOptimization} + +## 使用 SIMD 跳过空格 {#SkipwhitespaceWithSIMD} + +当从流中解析 JSON 时,解析器需要跳过4种空格字符: + +1. 空格 (`U+0020`) +2. 制表符 (`U+000B`) +3. 换行 (`U+000A`) +4. 回车 (`U+000D`) + +这是一份简单的实现: +~~~cpp +void SkipWhitespace(InputStream& s) { + while (s.Peek() == ' ' || s.Peek() == '\n' || s.Peek() == '\r' || s.Peek() == '\t') + s.Take(); +} +~~~ + +但是,这需要对每个字符进行4次比较以及一些分支。这被发现是一个热点。 + +为了加速这一处理,RapidJSON 使用 SIMD 来在一次迭代中比较16个字符和4个空格。目前 RapidJSON 支持 SSE2 , SSE4.2 和 ARM Neon 指令。同时它也只会对 UTF-8 内存流启用,包括字符串流或 *原位* 解析。 + +你可以通过在包含 `rapidjson.h` 之前定义 `RAPIDJSON_SSE2` , `RAPIDJSON_SSE42` 或 `RAPIDJSON_NEON` 来启用这个优化。一些编译器可以检测这个设置,如 `perftest.h`: + +~~~cpp +// __SSE2__ 和 __SSE4_2__ 可被 gcc、clang 和 Intel 编译器识别: +// 如果支持的话,我们在 gmake 中使用了 -march=native 来启用 -msse2 和 -msse4.2 +// 同样的, __ARM_NEON 被用于识别Neon +#if defined(__SSE4_2__) +# define RAPIDJSON_SSE42 +#elif defined(__SSE2__) +# define RAPIDJSON_SSE2 +#elif defined(__ARM_NEON) +# define RAPIDJSON_NEON +#endif +~~~ + +需要注意的是,这是编译期的设置。在不支持这些指令的机器上运行可执行文件会使它崩溃。 + +### 页面对齐问题 + +在 RapidJSON 的早期版本中,被报告了[一个问题](https://code.google.com/archive/p/rapidjson/issues/104):`SkipWhitespace_SIMD()` 会罕见地导致崩溃(约五十万分之一的几率)。在调查之后,怀疑是 `_mm_loadu_si128()` 访问了 `'\0'` 之后的内存,并越过被保护的页面边界。 + +在 [Intel® 64 and IA-32 Architectures Optimization Reference Manual +](http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-optimization-manual.html) 中,章节 10.2.1: + +> 为了支持需要费对齐的128位 SIMD 内存访问的算法,调用者的内存缓冲区申请应当考虑添加一些填充空间,这样被调用的函数可以安全地将地址指针用于未对齐的128位 SIMD 内存操作。 +> 在结合非对齐的 SIMD 内存操作中,最小的对齐大小应该等于 SIMD 寄存器的大小。 + +对于 RapidJSON 来说,这显然是不可行的,因为 RapidJSON 不应当强迫用户进行内存对齐。 + +为了修复这个问题,当前的代码会先按字节处理直到下一个对齐的地址。在这之后,使用对齐读取来进行 SIMD 处理。见 [#85](https://github.com/Tencent/rapidjson/issues/85)。 + +## 局部流拷贝 {#LocalStreamCopy} + +在优化的过程中,我们发现一些编译器不能将访问流的一些成员数据放入局部变量或者寄存器中。测试结果显示,对于一些流类型,创建流的拷贝并将其用于内层循环中可以改善性能。例如,实际(非 SIMD)的 `SkipWhitespace()` 被实现为: + +~~~cpp +template +void SkipWhitespace(InputStream& is) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + while (s.Peek() == ' ' || s.Peek() == '\n' || s.Peek() == '\r' || s.Peek() == '\t') + s.Take(); +} +~~~ + +基于流的特征,`StreamLocalCopy` 会创建(或不创建)流对象的拷贝,在局部使用它并将流的状态拷贝回原来的流。 + +## 解析为双精度浮点数 {#ParsingDouble} + +将字符串解析为 `double` 并不简单。标准库函数 `strtod()` 可以胜任这项工作,但它比较缓慢。默认情况下,解析器使用默认的精度设置。这最多有 3[ULP](http://en.wikipedia.org/wiki/Unit_in_the_last_place) 的误差,并实现在 `internal::StrtodNormalPrecision()` 中。 + +当使用 `kParseFullPrecisionFlag` 时,编译器会改为调用 `internal::StrtodFullPrecision()` ,这个函数会自动调用三个版本的转换。 +1. [Fast-Path](http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/)。 +2. [double-conversion](https://github.com/floitsch/double-conversion) 中的自定义 DIY-FP 实现。 +3. (Clinger, William D. How to read floating point numbers accurately. Vol. 25. No. 6. ACM, 1990) 中的大整数算法。 + +如果第一个转换方法失败,则尝试使用第二种方法,以此类推。 + +# 生成优化 {#GenerationOptimization} + +## 整数到字符串的转换 {#itoa} + +整数到字符串转换的朴素算法需要对每一个十进制位进行一次除法。我们实现了若干版本并在 [itoa-benchmark](https://github.com/miloyip/itoa-benchmark) 中对它们进行了评估。 + +虽然 SSE2 版本是最快的,但它和第二快的 `branchlut` 差距不大。而且 `branchlut` 是纯C++实现,所以我们在 RapidJSON 中使用了 `branchlut`。 + +## 双精度浮点数到字符串的转换 {#dtoa} + +原来 RapidJSON 使用 `snprintf(..., ..., "%g")` 来进行双精度浮点数到字符串的转换。这是不准确的,因为默认的精度是6。随后我们发现它很缓慢,而且有其它的替代品。 + +Google 的 V8 [double-conversion](https://github.com/floitsch/double-conversion +) 实现了更新的、快速的被称为 Grisu3 的算法(Loitsch, Florian. "Printing floating-point numbers quickly and accurately with integers." ACM Sigplan Notices 45.6 (2010): 233-243.)。 + +然而,这个实现不是仅头文件的,所以我们实现了一个仅头文件的 Grisu2 版本。这个算法保证了结果永远精确。而且在大多数情况下,它会生成最短的(可选)字符串表示。 + +这个仅头文件的转换函数在 [dtoa-benchmark](https://github.com/miloyip/dtoa-benchmark) 中进行评估。 + +# 解析器 {#Parser} + +## 迭代解析 {#IterativeParser} + +迭代解析器是一个以非递归方式实现的递归下降的 LL(1) 解析器。 + +### 语法 {#IterativeParserGrammar} + +解析器使用的语法是基于严格 JSON 语法的: +~~~~~~~~~~ +S -> array | object +array -> [ values ] +object -> { members } +values -> non-empty-values | ε +non-empty-values -> value addition-values +addition-values -> ε | , non-empty-values +members -> non-empty-members | ε +non-empty-members -> member addition-members +addition-members -> ε | , non-empty-members +member -> STRING : value +value -> STRING | NUMBER | NULL | BOOLEAN | object | array +~~~~~~~~~~ + +注意到左因子被加入了非终结符的 `values` 和 `members` 来保证语法是 LL(1) 的。 + +### 解析表 {#IterativeParserParsingTable} + +基于这份语法,我们可以构造 FIRST 和 FOLLOW 集合。 + +非终结符的 FIRST 集合如下所示: + +| NON-TERMINAL | FIRST | +|:-----------------:|:--------------------------------:| +| array | [ | +| object | { | +| values | ε STRING NUMBER NULL BOOLEAN { [ | +| addition-values | ε COMMA | +| members | ε STRING | +| addition-members | ε COMMA | +| member | STRING | +| value | STRING NUMBER NULL BOOLEAN { [ | +| S | [ { | +| non-empty-members | STRING | +| non-empty-values | STRING NUMBER NULL BOOLEAN { [ | + +FOLLOW 集合如下所示: + +| NON-TERMINAL | FOLLOW | +|:-----------------:|:-------:| +| S | $ | +| array | , $ } ] | +| object | , $ } ] | +| values | ] | +| non-empty-values | ] | +| addition-values | ] | +| members | } | +| non-empty-members | } | +| addition-members | } | +| member | , } | +| value | , } ] | + +最终可以从 FIRST 和 FOLLOW 集合生成解析表: + +| NON-TERMINAL | [ | { | , | : | ] | } | STRING | NUMBER | NULL | BOOLEAN | +|:-----------------:|:---------------------:|:---------------------:|:-------------------:|:-:|:-:|:-:|:-----------------------:|:---------------------:|:---------------------:|:---------------------:| +| S | array | object | | | | | | | | | +| array | [ values ] | | | | | | | | | | +| object | | { members } | | | | | | | | | +| values | non-empty-values | non-empty-values | | | ε | | non-empty-values | non-empty-values | non-empty-values | non-empty-values | +| non-empty-values | value addition-values | value addition-values | | | | | value addition-values | value addition-values | value addition-values | value addition-values | +| addition-values | | | , non-empty-values | | ε | | | | | | +| members | | | | | | ε | non-empty-members | | | | +| non-empty-members | | | | | | | member addition-members | | | | +| addition-members | | | , non-empty-members | | | ε | | | | | +| member | | | | | | | STRING : value | | | | +| value | array | object | | | | | STRING | NUMBER | NULL | BOOLEAN | + +对于上面的语法分析,这里有一个很棒的[工具](http://hackingoff.com/compilers/predict-first-follow-set)。 + +### 实现 {#IterativeParserImplementation} + +基于这份解析表,一个直接的(常规的)将规则反向入栈的实现可以正常工作。 + +在 RapidJSON 中,对直接的实现进行了一些修改: + +首先,在 RapidJSON 中,这份解析表被编码为状态机。 +规则由头部和主体组成。 +状态转换由规则构造。 +除此之外,额外的状态被添加到与 `array` 和 `object` 有关的规则。 +通过这种方式,生成数组值或对象成员可以只用一次状态转移便可完成, +而不需要在直接的实现中的多次出栈/入栈操作。 +这也使得估计栈的大小更加容易。 + +状态图如如下所示: + +![状态图](diagram/iterative-parser-states-diagram.png) + +第二,迭代解析器也在内部栈保存了数组的值个数和对象成员的数量,这也与传统的实现不同。 diff --git a/third_party/rapidjson/doc/misc/header.html b/third_party/rapidjson/doc/misc/header.html index 2dbe72146..a89ba46b4 100644 --- a/third_party/rapidjson/doc/misc/header.html +++ b/third_party/rapidjson/doc/misc/header.html @@ -18,7 +18,7 @@ $extrastylesheet
-
+
$searchbox diff --git a/third_party/rapidjson/doc/npm.md b/third_party/rapidjson/doc/npm.md index 5efa76821..6f4e85ad9 100644 --- a/third_party/rapidjson/doc/npm.md +++ b/third_party/rapidjson/doc/npm.md @@ -7,7 +7,7 @@ ... "dependencies": { ... - "rapidjson": "git@github.com:miloyip/rapidjson.git" + "rapidjson": "git@github.com:Tencent/rapidjson.git" }, ... "gypfile": true diff --git a/third_party/rapidjson/doc/performance.md b/third_party/rapidjson/doc/performance.md index 988e799e9..6f9e1bf8b 100644 --- a/third_party/rapidjson/doc/performance.md +++ b/third_party/rapidjson/doc/performance.md @@ -1,6 +1,6 @@ # Performance -There is a [native JSON benchmark collection] [1] which evaluates speed, memory usage and code size of various operations among 37 JSON libaries. +There is a [native JSON benchmark collection] [1] which evaluates speed, memory usage and code size of various operations among 37 JSON libraries. [1]: https://github.com/miloyip/nativejson-benchmark @@ -15,12 +15,12 @@ Additionally, you may refer to the following third-party benchmarks. * [json_spirit](https://github.com/cierelabs/json_spirit) * [jsoncpp](http://jsoncpp.sourceforge.net/) * [libjson](http://sourceforge.net/projects/libjson/) - * [rapidjson](https://github.com/miloyip/rapidjson/) + * [rapidjson](https://github.com/Tencent/rapidjson/) * [QJsonDocument](http://qt-project.org/doc/qt-5.0/qtcore/qjsondocument.html) * [JSON Parser Benchmarking](http://chadaustin.me/2013/01/json-parser-benchmarking/) by Chad Austin (Jan 2013) * [sajson](https://github.com/chadaustin/sajson) - * [rapidjson](https://github.com/miloyip/rapidjson/) + * [rapidjson](https://github.com/Tencent/rapidjson/) * [vjson](https://code.google.com/p/vjson/) * [YAJL](http://lloyd.github.com/yajl/) * [Jansson](http://www.digip.org/jansson/) diff --git a/third_party/rapidjson/doc/performance.zh-cn.md b/third_party/rapidjson/doc/performance.zh-cn.md index c20c5050f..2322c9c49 100644 --- a/third_party/rapidjson/doc/performance.zh-cn.md +++ b/third_party/rapidjson/doc/performance.zh-cn.md @@ -15,12 +15,12 @@ RapidJSON 0.1 版本的性能测试文章位于 [这里](https://code.google.com * [json_spirit](https://github.com/cierelabs/json_spirit) * [jsoncpp](http://jsoncpp.sourceforge.net/) * [libjson](http://sourceforge.net/projects/libjson/) - * [rapidjson](https://github.com/miloyip/rapidjson/) + * [rapidjson](https://github.com/Tencent/rapidjson/) * [QJsonDocument](http://qt-project.org/doc/qt-5.0/qtcore/qjsondocument.html) * [JSON Parser Benchmarking](http://chadaustin.me/2013/01/json-parser-benchmarking/) by Chad Austin (Jan 2013) * [sajson](https://github.com/chadaustin/sajson) - * [rapidjson](https://github.com/miloyip/rapidjson/) + * [rapidjson](https://github.com/Tencent/rapidjson/) * [vjson](https://code.google.com/p/vjson/) * [YAJL](http://lloyd.github.com/yajl/) * [Jansson](http://www.digip.org/jansson/) diff --git a/third_party/rapidjson/doc/pointer.md b/third_party/rapidjson/doc/pointer.md index b343d78ed..9a0e5ca03 100644 --- a/third_party/rapidjson/doc/pointer.md +++ b/third_party/rapidjson/doc/pointer.md @@ -211,7 +211,7 @@ p.Stringify(sb); std::cout << sb.GetString() << std::endl; ~~~ -It can also stringify to URI fragment reprsentation by `StringifyUriFragment()`. +It can also stringify to URI fragment representation by `StringifyUriFragment()`. # User-Supplied Tokens {#UserSuppliedTokens} diff --git a/third_party/rapidjson/doc/pointer.zh-cn.md b/third_party/rapidjson/doc/pointer.zh-cn.md index f58f55f3d..239569d4a 100644 --- a/third_party/rapidjson/doc/pointer.zh-cn.md +++ b/third_party/rapidjson/doc/pointer.zh-cn.md @@ -181,7 +181,7 @@ private: `Pointer` 在其建构函数里会解译源字符串。若有解析错误,`Pointer::IsValid()` 返回 `false`。你可使用 `Pointer::GetParseErrorCode()` 和 `GetParseErrorOffset()` 去获取错信息。 -要注意的是,所有解析函数都假设 pointer 是合法的。对一个非法 pointer 解析会做成断言失败。 +要注意的是,所有解析函数都假设 pointer 是合法的。对一个非法 pointer 解析会造成断言失败。 # URI 片段表示方式 {#URIFragment} diff --git a/third_party/rapidjson/doc/sax.md b/third_party/rapidjson/doc/sax.md index 1d4fc2ae5..d42d04388 100644 --- a/third_party/rapidjson/doc/sax.md +++ b/third_party/rapidjson/doc/sax.md @@ -8,7 +8,7 @@ In RapidJSON, `Reader` (typedef of `GenericReader<...>`) is the SAX-style parser # Reader {#Reader} -`Reader` parses a JSON from a stream. While it reads characters from the stream, it analyze the characters according to the syntax of JSON, and publish events to a handler. +`Reader` parses a JSON from a stream. While it reads characters from the stream, it analyzes the characters according to the syntax of JSON, and publishes events to a handler. For example, here is a JSON. @@ -24,7 +24,7 @@ For example, here is a JSON. } ~~~~~~~~~~ -While a `Reader` parses this JSON, it publishes the following events to the handler sequentially: +When a `Reader` parses this JSON, it publishes the following events to the handler sequentially: ~~~~~~~~~~ StartObject() @@ -37,7 +37,7 @@ Bool(false) Key("n", 1, true) Null() Key("i") -UInt(123) +Uint(123) Key("pi") Double(3.1416) Key("a") @@ -50,7 +50,7 @@ EndArray(4) EndObject(7) ~~~~~~~~~~ -These events can be easily matched with the JSON, except some event parameters need further explanation. Let's see the `simplereader` example which produces exactly the same output as above: +These events can be easily matched with the JSON, but some event parameters need further explanation. Let's see the `simplereader` example which produces exactly the same output as above: ~~~~~~~~~~cpp #include "rapidjson/reader.h" @@ -91,11 +91,11 @@ void main() { } ~~~~~~~~~~ -Note that, RapidJSON uses template to statically bind the `Reader` type and the handler type, instead of using class with virtual functions. This paradigm can improve the performance by inlining functions. +Note that RapidJSON uses templates to statically bind the `Reader` type and the handler type, instead of using classes with virtual functions. This paradigm can improve performance by inlining functions. ## Handler {#Handler} -As the previous example showed, user needs to implement a handler, which consumes the events (function calls) from `Reader`. The handler must contain the following member functions. +As shown in the previous example, the user needs to implement a handler which consumes the events (via function calls) from the `Reader`. The handler must contain the following member functions. ~~~~~~~~~~cpp class Handler { @@ -122,15 +122,15 @@ class Handler { When the `Reader` encounters a JSON number, it chooses a suitable C++ type mapping. And then it calls *one* function out of `Int(int)`, `Uint(unsigned)`, `Int64(int64_t)`, `Uint64(uint64_t)` and `Double(double)`. If `kParseNumbersAsStrings` is enabled, `Reader` will always calls `RawNumber()` instead. -`String(const char* str, SizeType length, bool copy)` is called when the `Reader` encounters a string. The first parameter is pointer to the string. The second parameter is the length of the string (excluding the null terminator). Note that RapidJSON supports null character `'\0'` inside a string. If such situation happens, `strlen(str) < length`. The last `copy` indicates whether the handler needs to make a copy of the string. For normal parsing, `copy = true`. Only when *insitu* parsing is used, `copy = false`. And beware that, the character type depends on the target encoding, which will be explained later. +`String(const char* str, SizeType length, bool copy)` is called when the `Reader` encounters a string. The first parameter is pointer to the string. The second parameter is the length of the string (excluding the null terminator). Note that RapidJSON supports null character `\0` inside a string. If such situation happens, `strlen(str) < length`. The last `copy` indicates whether the handler needs to make a copy of the string. For normal parsing, `copy = true`. Only when *insitu* parsing is used, `copy = false`. And be aware that the character type depends on the target encoding, which will be explained later. -When the `Reader` encounters the beginning of an object, it calls `StartObject()`. An object in JSON is a set of name-value pairs. If the object contains members it first calls `Key()` for the name of member, and then calls functions depending on the type of the value. These calls of name-value pairs repeats until calling `EndObject(SizeType memberCount)`. Note that the `memberCount` parameter is just an aid for the handler, user may not need this parameter. +When the `Reader` encounters the beginning of an object, it calls `StartObject()`. An object in JSON is a set of name-value pairs. If the object contains members it first calls `Key()` for the name of member, and then calls functions depending on the type of the value. These calls of name-value pairs repeat until calling `EndObject(SizeType memberCount)`. Note that the `memberCount` parameter is just an aid for the handler; users who do not need this parameter may ignore it. -Array is similar to object but simpler. At the beginning of an array, the `Reader` calls `BeginArary()`. If there is elements, it calls functions according to the types of element. Similarly, in the last call `EndArray(SizeType elementCount)`, the parameter `elementCount` is just an aid for the handler. +Arrays are similar to objects, but simpler. At the beginning of an array, the `Reader` calls `BeginArray()`. If there is elements, it calls functions according to the types of element. Similarly, in the last call `EndArray(SizeType elementCount)`, the parameter `elementCount` is just an aid for the handler. -Every handler functions returns a `bool`. Normally it should returns `true`. If the handler encounters an error, it can return `false` to notify event publisher to stop further processing. +Every handler function returns a `bool`. Normally it should return `true`. If the handler encounters an error, it can return `false` to notify the event publisher to stop further processing. -For example, when we parse a JSON with `Reader` and the handler detected that the JSON does not conform to the required schema, then the handler can return `false` and let the `Reader` stop further parsing. And the `Reader` will be in error state with error code `kParseErrorTermination`. +For example, when we parse a JSON with `Reader` and the handler detects that the JSON does not conform to the required schema, the handler can return `false` and let the `Reader` stop further parsing. This will place the `Reader` in an error state, with error code `kParseErrorTermination`. ## GenericReader {#GenericReader} @@ -149,19 +149,19 @@ typedef GenericReader, UTF8<> > Reader; } // namespace rapidjson ~~~~~~~~~~ -The `Reader` uses UTF-8 as both source and target encoding. The source encoding means the encoding in the JSON stream. The target encoding means the encoding of the `str` parameter in `String()` calls. For example, to parse a UTF-8 stream and outputs UTF-16 string events, you can define a reader by: +The `Reader` uses UTF-8 as both source and target encoding. The source encoding means the encoding in the JSON stream. The target encoding means the encoding of the `str` parameter in `String()` calls. For example, to parse a UTF-8 stream and output UTF-16 string events, you can define a reader by: ~~~~~~~~~~cpp GenericReader, UTF16<> > reader; ~~~~~~~~~~ -Note that, the default character type of `UTF16` is `wchar_t`. So this `reader`needs to call `String(const wchar_t*, SizeType, bool)` of the handler. +Note that, the default character type of `UTF16` is `wchar_t`. So this `reader` needs to call `String(const wchar_t*, SizeType, bool)` of the handler. The third template parameter `Allocator` is the allocator type for internal data structure (actually a stack). ## Parsing {#SaxParsing} -The one and only one function of `Reader` is to parse JSON. +The main function of `Reader` is used to parse JSON. ~~~~~~~~~~cpp template @@ -172,7 +172,30 @@ template bool Parse(InputStream& is, Handler& handler); ~~~~~~~~~~ -If an error occurs during parsing, it will return `false`. User can also calls `bool HasParseEror()`, `ParseErrorCode GetParseErrorCode()` and `size_t GetErrorOffset()` to obtain the error states. Actually `Document` uses these `Reader` functions to obtain parse errors. Please refer to [DOM](doc/dom.md) for details about parse error. +If an error occurs during parsing, it will return `false`. User can also call `bool HasParseError()`, `ParseErrorCode GetParseErrorCode()` and `size_t GetErrorOffset()` to obtain the error states. In fact, `Document` uses these `Reader` functions to obtain parse errors. Please refer to [DOM](doc/dom.md) for details about parse errors. + +## Token-by-Token Parsing {#TokenByTokenParsing} + +Some users may wish to parse a JSON input stream a single token at a time, instead of immediately parsing an entire document without stopping. To parse JSON this way, instead of calling `Parse`, you can use the `IterativeParse` set of functions: + +~~~~~~~~~~cpp + void IterativeParseInit(); + + template + bool IterativeParseNext(InputStream& is, Handler& handler); + + bool IterativeParseComplete(); +~~~~~~~~~~ + +Here is an example of iteratively parsing JSON, token by token: + +~~~~~~~~~~cpp + reader.IterativeParseInit(); + while (!reader.IterativeParseComplete()) { + reader.IterativeParseNext(is, handler); + // Your handler has been called once. + } +~~~~~~~~~~ # Writer {#Writer} diff --git a/third_party/rapidjson/doc/sax.zh-cn.md b/third_party/rapidjson/doc/sax.zh-cn.md index b20286de9..9b11e7683 100644 --- a/third_party/rapidjson/doc/sax.zh-cn.md +++ b/third_party/rapidjson/doc/sax.zh-cn.md @@ -37,7 +37,7 @@ Bool(false) Key("n", 1, true) Null() Key("i") -UInt(123) +Uint(123) Key("pi") Double(3.1416) Key("a") @@ -91,7 +91,7 @@ void main() { } ~~~~~~~~~~ -注意 RapidJSON 使用模板去静态挷定 `Reader` 类型及处理器的类形,而不是使用含虚函数的类。这个范式可以通过把函数内联而改善性能。 +注意 RapidJSON 使用模板去静态挷定 `Reader` 类型及处理器的类型,而不是使用含虚函数的类。这个范式可以通过把函数内联而改善性能。 ## 处理器 {#Handler} @@ -122,7 +122,7 @@ class Handler { 当 `Reader` 遇到 JSON number,它会选择一个合适的 C++ 类型映射,然后调用 `Int(int)`、`Uint(unsigned)`、`Int64(int64_t)`、`Uint64(uint64_t)` 及 `Double(double)` 的 * 其中之一个 *。 若开启了 `kParseNumbersAsStrings` 选项,`Reader` 便会改为调用 `RawNumber()`。 -当 `Reader` 遇到 JSON string,它会调用 `String(const char* str, SizeType length, bool copy)`。第一个参数是字符串的指针。第二个参数是字符串的长度(不包含空终止符号)。注意 RapidJSON 支持字串中含有空字符 `'\0'`。若出现这种情况,便会有 `strlen(str) < length`。最后的 `copy` 参数表示处理器是否需要复制该字符串。在正常解析时,`copy = true`。仅当使用原位解析时,`copy = false`。此外,还要注意字符的类型与目标编码相关,我们稍后会再谈这一点。 +当 `Reader` 遇到 JSON string,它会调用 `String(const char* str, SizeType length, bool copy)`。第一个参数是字符串的指针。第二个参数是字符串的长度(不包含空终止符号)。注意 RapidJSON 支持字串中含有空字符 `\0`。若出现这种情况,便会有 `strlen(str) < length`。最后的 `copy` 参数表示处理器是否需要复制该字符串。在正常解析时,`copy = true`。仅当使用原位解析时,`copy = false`。此外,还要注意字符的类型与目标编码相关,我们稍后会再谈这一点。 当 `Reader` 遇到 JSON object 的开始之时,它会调用 `StartObject()`。JSON 的 object 是一个键值对(成员)的集合。若 object 包含成员,它会先为成员的名字调用 `Key()`,然后再按值的类型调用函数。它不断调用这些键值对,直至最终调用 `EndObject(SizeType memberCount)`。注意 `memberCount` 参数对处理器来说只是协助性质,使用者可能不需要此参数。 diff --git a/third_party/rapidjson/doc/schema.md b/third_party/rapidjson/doc/schema.md index a83cebcae..4da4474b2 100644 --- a/third_party/rapidjson/doc/schema.md +++ b/third_party/rapidjson/doc/schema.md @@ -8,7 +8,7 @@ RapidJSON implemented a JSON Schema validator for [JSON Schema Draft v4](http:// [TOC] -## Basic Usage +# Basic Usage {#Basic} First of all, you need to parse a JSON Schema into `Document`, and then compile the `Document` into a `SchemaDocument`. @@ -20,15 +20,23 @@ Secondly, construct a `SchemaValidator` with the `SchemaDocument`. It is similar // ... Document sd; -if (!sd.Parse(schemaJson).HasParseError()) { +if (sd.Parse(schemaJson).HasParseError()) { // the schema is not a valid JSON. // ... } + SchemaDocument schema(sd); // Compile a Document to SchemaDocument +if (!schema.GetError().ObjectEmpty()) { + // there was a problem compiling the schema + StringBuffer sb; + Writer w(sb); + schema.GetError().Accept(w); + printf("Invalid schema: %s\n", sb.GetString()); +} // sd is no longer needed here. Document d; -if (!d.Parse(inputJson).HasParseError()) { +if (d.Parse(inputJson).HasParseError()) { // the input is not a valid JSON. // ... } @@ -49,14 +57,14 @@ if (!d.Accept(validator)) { Some notes: -* One `SchemaDocment` can be referenced by multiple `SchemaValidator`s. It will not be modified by `SchemaValidator`s. +* One `SchemaDocument` can be referenced by multiple `SchemaValidator`s. It will not be modified by `SchemaValidator`s. * A `SchemaValidator` may be reused to validate multiple documents. To run it for other documents, call `validator.Reset()` first. -## Validation during parsing/serialization +# Validation during parsing/serialization {#Fused} Unlike most JSON Schema validator implementations, RapidJSON provides a SAX-based schema validator. Therefore, you can parse a JSON from a stream while validating it on the fly. If the validator encounters a JSON value that invalidates the supplied schema, the parsing will be terminated immediately. This design is especially useful for parsing large JSON files. -### DOM parsing +## DOM parsing {#DOM} For using DOM in parsing, `Document` needs some preparation and finalizing tasks, in addition to receiving SAX events, thus it needs some work to route the reader, validator and the document. `SchemaValidatingReader` is a helper class that doing such work. @@ -97,7 +105,7 @@ if (!reader.GetParseResult()) { } ~~~ -### SAX parsing +## SAX parsing {#SAX} For using SAX in parsing, it is much simpler. If it only need to validate the JSON without further processing, it is simply: @@ -126,7 +134,7 @@ if (!reader.Parse(ss, validator)) { } ~~~ -### Serialization +## Serialization {#Serialization} It is also possible to do validation during serializing. This can ensure the result JSON is valid according to the JSON schema. @@ -144,7 +152,7 @@ if (!d.Accept(validator)) { Of course, if your application only needs SAX-style serialization, it can simply send SAX events to `SchemaValidator` instead of `Writer`. -## Remote Schema +# Remote Schema {#Remote} JSON Schema supports [`$ref` keyword](http://spacetelescope.github.io/understanding-json-schema/structuring.html), which is a [JSON pointer](doc/pointer.md) referencing to a local or remote schema. Local pointer is prefixed with `#`, while remote pointer is an relative or absolute URI. For example: @@ -157,7 +165,7 @@ As `SchemaDocument` does not know how to resolve such URI, it needs a user-provi ~~~ class MyRemoteSchemaDocumentProvider : public IRemoteSchemaDocumentProvider { public: - virtual const SchemaDocument* GetRemoteDocument(const char* uri, SizeTyp length) { + virtual const SchemaDocument* GetRemoteDocument(const char* uri, SizeType length) { // Resolve the uri and returns a pointer to that schema. } }; @@ -168,7 +176,7 @@ MyRemoteSchemaDocumentProvider provider; SchemaDocument schema(sd, &provider); ~~~ -## Conformance +# Conformance {#Conformance} RapidJSON passed 262 out of 263 tests in [JSON Schema Test Suite](https://github.com/json-schema/JSON-Schema-Test-Suite) (Json Schema draft 4). @@ -176,7 +184,7 @@ The failed test is "changed scope ref invalid" of "change resolution scope" in ` Besides, the `format` schema keyword for string values is ignored, since it is not required by the specification. -### Regular Expression +## Regular Expression {#Regex} The schema keyword `pattern` and `patternProperties` uses regular expression to match the required pattern. @@ -185,7 +193,7 @@ RapidJSON implemented a simple NFA regular expression engine, which is used by d |Syntax|Description| |------|-----------| |`ab` | Concatenation | -|`a|b` | Alternation | +|a|b | Alternation | |`a?` | Zero or one | |`a*` | Zero or more | |`a+` | One or more | @@ -202,7 +210,7 @@ RapidJSON implemented a simple NFA regular expression engine, which is used by d |`[^abc]` | Negated character classes | |`[^a-c]` | Negated character class range | |`[\b]` | Backspace (U+0008) | -|`\|`, `\\`, ... | Escape characters | +|\\|, `\\`, ... | Escape characters | |`\f` | Form feed (U+000C) | |`\n` | Line feed (U+000A) | |`\r` | Carriage return (U+000D) | @@ -211,7 +219,7 @@ RapidJSON implemented a simple NFA regular expression engine, which is used by d For C++11 compiler, it is also possible to use the `std::regex` by defining `RAPIDJSON_SCHEMA_USE_INTERNALREGEX=0` and `RAPIDJSON_SCHEMA_USE_STDREGEX=1`. If your schemas do not need `pattern` and `patternProperties`, you can set both macros to zero to disable this feature, which will reduce some code size. -## Performance +# Performance {#Performance} Most C++ JSON libraries do not yet support JSON Schema. So we tried to evaluate the performance of RapidJSON's JSON Schema validator according to [json-schema-benchmark](https://github.com/ebdrup/json-schema-benchmark), which tests 11 JavaScript libraries running on Node.js. @@ -235,3 +243,271 @@ On a Mac Book Pro (2.8 GHz Intel Core i7), the following results are collected. |[`jayschema`](https://github.com/natesilva/jayschema)|0.1%|21 (± 1.14%)| That is, RapidJSON is about 1.5x faster than the fastest JavaScript library (ajv). And 1400x faster than the slowest one. + +# Schema violation reporting {#Reporting} + +(Unreleased as of 2017-09-20) + +When validating an instance against a JSON Schema, +it is often desirable to report not only whether the instance is valid, +but also the ways in which it violates the schema. + +The `SchemaValidator` class +collects errors encountered during validation +into a JSON `Value`. +This error object can then be accessed as `validator.GetError()`. + +The structure of the error object is subject to change +in future versions of RapidJSON, +as there is no standard schema for violations. +The details below this point are provisional only. + +## General provisions {#ReportingGeneral} + +Validation of an instance value against a schema +produces an error value. +The error value is always an object. +An empty object `{}` indicates the instance is valid. + +* The name of each member + corresponds to the JSON Schema keyword that is violated. +* The value is either an object describing a single violation, + or an array of such objects. + +Each violation object contains two string-valued members +named `instanceRef` and `schemaRef`. +`instanceRef` contains the URI fragment serialization +of a JSON Pointer to the instance subobject +in which the violation was detected. +`schemaRef` contains the URI of the schema +and the fragment serialization of a JSON Pointer +to the subschema that was violated. + +Individual violation objects can contain other keyword-specific members. +These are detailed further. + +For example, validating this instance: + +~~~json +{"numbers": [1, 2, "3", 4, 5]} +~~~ + +against this schema: + +~~~json +{ + "type": "object", + "properties": { + "numbers": {"$ref": "numbers.schema.json"} + } +} +~~~ + +where `numbers.schema.json` refers +(via a suitable `IRemoteSchemaDocumentProvider`) +to this schema: + +~~~json +{ + "type": "array", + "items": {"type": "number"} +} +~~~ + +produces the following error object: + +~~~json +{ + "type": { + "instanceRef": "#/numbers/2", + "schemaRef": "numbers.schema.json#/items", + "expected": ["number"], + "actual": "string" + } +} +~~~ + +## Validation keywords for numbers {#Numbers} + +### multipleOf {#multipleof} + +* `expected`: required number strictly greater than 0. + The value of the `multipleOf` keyword specified in the schema. +* `actual`: required number. + The instance value. + +### maximum {#maximum} + +* `expected`: required number. + The value of the `maximum` keyword specified in the schema. +* `exclusiveMaximum`: optional boolean. + This will be true if the schema specified `"exclusiveMaximum": true`, + and will be omitted otherwise. +* `actual`: required number. + The instance value. + +### minimum {#minimum} + +* `expected`: required number. + The value of the `minimum` keyword specified in the schema. +* `exclusiveMinimum`: optional boolean. + This will be true if the schema specified `"exclusiveMinimum": true`, + and will be omitted otherwise. +* `actual`: required number. + The instance value. + +## Validation keywords for strings {#Strings} + +### maxLength {#maxLength} + +* `expected`: required number greater than or equal to 0. + The value of the `maxLength` keyword specified in the schema. +* `actual`: required string. + The instance value. + +### minLength {#minLength} + +* `expected`: required number greater than or equal to 0. + The value of the `minLength` keyword specified in the schema. +* `actual`: required string. + The instance value. + +### pattern {#pattern} + +* `actual`: required string. + The instance value. + +(The expected pattern is not reported +because the internal representation in `SchemaDocument` +does not store the pattern in original string form.) + +## Validation keywords for arrays {#Arrays} + +### additionalItems {#additionalItems} + +This keyword is reported +when the value of `items` schema keyword is an array, +the value of `additionalItems` is `false`, +and the instance is an array +with more items than specified in the `items` array. + +* `disallowed`: required integer greater than or equal to 0. + The index of the first item that has no corresponding schema. + +### maxItems and minItems {#maxItems-minItems} + +* `expected`: required integer greater than or equal to 0. + The value of `maxItems` (respectively, `minItems`) + specified in the schema. +* `actual`: required integer greater than or equal to 0. + Number of items in the instance array. + +### uniqueItems {#uniqueItems} + +* `duplicates`: required array + whose items are integers greater than or equal to 0. + Indices of items of the instance that are equal. + +(RapidJSON only reports the first two equal items, +for performance reasons.) + +## Validation keywords for objects + +### maxProperties and minProperties {#maxProperties-minProperties} + +* `expected`: required integer greater than or equal to 0. + The value of `maxProperties` (respectively, `minProperties`) + specified in the schema. +* `actual`: required integer greater than or equal to 0. + Number of properties in the instance object. + +### required {#required} + +* `missing`: required array of one or more unique strings. + The names of properties + that are listed in the value of the `required` schema keyword + but not present in the instance object. + +### additionalProperties {#additionalProperties} + +This keyword is reported +when the schema specifies `additionalProperties: false` +and the name of a property of the instance is +neither listed in the `properties` keyword +nor matches any regular expression in the `patternProperties` keyword. + +* `disallowed`: required string. + Name of the offending property of the instance. + +(For performance reasons, +RapidJSON only reports the first such property encountered.) + +### dependencies {#dependencies} + +* `errors`: required object with one or more properties. + Names and values of its properties are described below. + +Recall that JSON Schema Draft 04 supports +*schema dependencies*, +where presence of a named *controlling* property +requires the instance object to be valid against a subschema, +and *property dependencies*, +where presence of a controlling property +requires other *dependent* properties to be also present. + +For a violated schema dependency, +`errors` will contain a property +with the name of the controlling property +and its value will be the error object +produced by validating the instance object +against the dependent schema. + +For a violated property dependency, +`errors` will contain a property +with the name of the controlling property +and its value will be an array of one or more unique strings +listing the missing dependent properties. + +## Validation keywords for any instance type {#AnyTypes} + +### enum {#enum} + +This keyword has no additional properties +beyond `instanceRef` and `schemaRef`. + +* The allowed values are not listed + because `SchemaDocument` does not store them in original form. +* The violating value is not reported + because it might be unwieldy. + +If you need to report these details to your users, +you can access the necessary information +by following `instanceRef` and `schemaRef`. + +### type {#type} + +* `expected`: required array of one or more unique strings, + each of which is one of the seven primitive types + defined by the JSON Schema Draft 04 Core specification. + Lists the types allowed by the `type` schema keyword. +* `actual`: required string, also one of seven primitive types. + The primitive type of the instance. + +### allOf, anyOf, and oneOf {#allOf-anyOf-oneOf} + +* `errors`: required array of at least one object. + There will be as many items as there are subschemas + in the `allOf`, `anyOf` or `oneOf` schema keyword, respectively. + Each item will be the error value + produced by validating the instance + against the corresponding subschema. + +For `allOf`, at least one error value will be non-empty. +For `anyOf`, all error values will be non-empty. +For `oneOf`, either all error values will be non-empty, +or more than one will be empty. + +### not {#not} + +This keyword has no additional properties +apart from `instanceRef` and `schemaRef`. diff --git a/third_party/rapidjson/doc/schema.zh-cn.md b/third_party/rapidjson/doc/schema.zh-cn.md index a01c1b10e..c85177f9f 100644 --- a/third_party/rapidjson/doc/schema.zh-cn.md +++ b/third_party/rapidjson/doc/schema.zh-cn.md @@ -8,7 +8,7 @@ RapidJSON 实现了一个 [JSON Schema Draft v4](http://json-schema.org/document [TOC] -## 基本用法 +# 基本用法 {#BasicUsage} 首先,你要把 JSON Schema 解析成 `Document`,再把它编译成一个 `SchemaDocument`。 @@ -20,7 +20,7 @@ RapidJSON 实现了一个 [JSON Schema Draft v4](http://json-schema.org/document // ... Document sd; -if (!sd.Parse(schemaJson).HasParseError()) { +if (sd.Parse(schemaJson).HasParseError()) { // 此 schema 不是合法的 JSON // ... } @@ -28,7 +28,7 @@ SchemaDocument schema(sd); // 把一个 Document 编译至 SchemaDocument // 之后不再需要 sd Document d; -if (!d.Parse(inputJson).HasParseError()) { +if (d.Parse(inputJson).HasParseError()) { // 输入不是一个合法的 JSON // ... } @@ -52,11 +52,11 @@ if (!d.Accept(validator)) { * 一个 `SchemaDocment` 能被多个 `SchemaValidator` 引用。它不会被 `SchemaValidator` 修改。 * 可以重复使用一个 `SchemaValidator` 来校验多个文件。在校验其他文件前,须先调用 `validator.Reset()`。 -## 在解析/生成时进行校验 +# 在解析/生成时进行校验 {#ParsingSerialization} 与大部分 JSON Schema 校验器有所不同,RapidJSON 提供了一个基于 SAX 的 schema 校验器实现。因此,你可以在输入流解析 JSON 的同时进行校验。若校验器遇到一个与 schema 不符的值,就会立即终止解析。这设计对于解析大型 JSON 文件时特别有用。 -### DOM 解析 +## DOM 解析 {#DomParsing} 在使用 DOM 进行解析时,`Document` 除了接收 SAX 事件外,还需做一些准备及结束工作,因此,为了连接 `Reader`、`SchemaValidator` 和 `Document` 要做多一点事情。`SchemaValidatingReader` 是一个辅助类去做那些工作。 @@ -97,7 +97,7 @@ if (!reader.GetParseResult()) { } ~~~ -### SAX 解析 +## SAX 解析 {#SaxParsing} 使用 SAX 解析时,情况就简单得多。若只需要校验 JSON 而无需进一步处理,那么仅需要: @@ -126,7 +126,7 @@ if (!reader.Parse(ss, validator)) { } ~~~ -### 生成 +## 生成 {#Serialization} 我们也可以在生成(serialization)的时候进行校验。这能确保输出的 JSON 符合一个 JSON Schema。 @@ -144,7 +144,7 @@ if (!d.Accept(validator)) { 当然,如果你的应用仅需要 SAX 风格的生成,那么只需要把 SAX 事件由原来发送到 `Writer`,改为发送到 `SchemaValidator`。 -## 远程 Schema +# 远程 Schema {#RemoteSchema} JSON Schema 支持 [`$ref` 关键字](http://spacetelescope.github.io/understanding-json-schema/structuring.html),它是一个 [JSON pointer](doc/pointer.zh-cn.md) 引用至一个本地(local)或远程(remote) schema。本地指针的首字符是 `#`,而远程指针是一个相对或绝对 URI。例如: @@ -157,7 +157,7 @@ JSON Schema 支持 [`$ref` 关键字](http://spacetelescope.github.io/understand ~~~ class MyRemoteSchemaDocumentProvider : public IRemoteSchemaDocumentProvider { public: - virtual const SchemaDocument* GetRemoteDocument(const char* uri, SizeTyp length) { + virtual const SchemaDocument* GetRemoteDocument(const char* uri, SizeType length) { // Resolve the uri and returns a pointer to that schema. } }; @@ -168,7 +168,7 @@ MyRemoteSchemaDocumentProvider provider; SchemaDocument schema(sd, &provider); ~~~ -## 标准的符合程度 +# 标准的符合程度 {#Conformance} RapidJSON 通过了 [JSON Schema Test Suite](https://github.com/json-schema/JSON-Schema-Test-Suite) (Json Schema draft 4) 中 263 个测试的 262 个。 @@ -176,7 +176,7 @@ RapidJSON 通过了 [JSON Schema Test Suite](https://github.com/json-schema/JSON 除此以外,关于字符串类型的 `format` schema 关键字也会被忽略,因为标准中并没需求必须实现。 -### 正则表达式 +## 正则表达式 {#RegEx} `pattern` 及 `patternProperties` 这两个 schema 关键字使用了正则表达式去匹配所需的模式。 @@ -185,7 +185,7 @@ RapidJSON 实现了一个简单的 NFA 正则表达式引擎,并预设使用 |语法|描述| |------|-----------| |`ab` | 串联 | -|`a|b` | 交替 | +|a|b | 交替 | |`a?` | 零或一次 | |`a*` | 零或多次 | |`a+` | 一或多次 | @@ -202,7 +202,7 @@ RapidJSON 实现了一个简单的 NFA 正则表达式引擎,并预设使用 |`[^abc]` | 字符组取反 | |`[^a-c]` | 字符组范围取反 | |`[\b]` | 退格符 (U+0008) | -|`\|`, `\\`, ... | 转义字符 | +|\\|, `\\`, ... | 转义字符 | |`\f` | 馈页 (U+000C) | |`\n` | 馈行 (U+000A) | |`\r` | 回车 (U+000D) | @@ -211,7 +211,7 @@ RapidJSON 实现了一个简单的 NFA 正则表达式引擎,并预设使用 对于使用 C++11 编译器的使用者,也可使用 `std::regex`,只需定义 `RAPIDJSON_SCHEMA_USE_INTERNALREGEX=0` 及 `RAPIDJSON_SCHEMA_USE_STDREGEX=1`。若你的 schema 无需使用 `pattern` 或 `patternProperties`,可以把两个宏都设为零,以禁用此功能,这样做可节省一些代码体积。 -## 性能 +# 性能 {#Performance} 大部分 C++ JSON 库都未支持 JSON Schema。因此我们尝试按照 [json-schema-benchmark](https://github.com/ebdrup/json-schema-benchmark) 去评估 RapidJSON 的 JSON Schema 校验器。该评测测试了 11 个运行在 node.js 上的 JavaScript 库。 diff --git a/third_party/rapidjson/doc/stream.md b/third_party/rapidjson/doc/stream.md index b79ce537a..5d0b0f35e 100644 --- a/third_party/rapidjson/doc/stream.md +++ b/third_party/rapidjson/doc/stream.md @@ -1,6 +1,6 @@ # Stream -In RapidJSON, `rapidjson::Stream` is a concept for reading/writing JSON. Here we first show how to use streams provided. And then see how to create a custom stream. +In RapidJSON, `rapidjson::Stream` is a concept for reading/writing JSON. Here we'll first show you how to use provided streams. And then see how to create a custom stream. [TOC] @@ -42,6 +42,7 @@ Note that, `StringStream` is a typedef of `GenericStringStream >`, user m ~~~~~~~~~~cpp #include "rapidjson/stringbuffer.h" +#include StringBuffer buffer; Writer writer(buffer); @@ -50,7 +51,7 @@ d.Accept(writer); const char* output = buffer.GetString(); ~~~~~~~~~~ -When the buffer is full, it will increases the capacity automatically. The default capacity is 256 characters (256 bytes for UTF8, 512 bytes for UTF16, etc.). User can provide an allocator and a initial capacity. +When the buffer is full, it will increases the capacity automatically. The default capacity is 256 characters (256 bytes for UTF8, 512 bytes for UTF16, etc.). User can provide an allocator and an initial capacity. ~~~~~~~~~~cpp StringBuffer buffer1(0, 1024); // Use its allocator, initial size = 1024 @@ -88,7 +89,7 @@ d.ParseStream(is); fclose(fp); ~~~~~~~~~~ -Different from string streams, `FileReadStream` is byte stream. It does not handle encodings. If the file is not UTF-8, the byte stream can be wrapped in a `EncodedInputStream`. It will be discussed very soon. +Different from string streams, `FileReadStream` is byte stream. It does not handle encodings. If the file is not UTF-8, the byte stream can be wrapped in a `EncodedInputStream`. We will discuss more about this later in this tutorial. Apart from reading file, user can also use `FileReadStream` to read `stdin`. @@ -98,6 +99,7 @@ Apart from reading file, user can also use `FileReadStream` to read `stdin`. ~~~~~~~~~~cpp #include "rapidjson/filewritestream.h" +#include #include using namespace rapidjson; @@ -117,15 +119,15 @@ d.Accept(writer); fclose(fp); ~~~~~~~~~~ -It can also directs the output to `stdout`. +It can also redirect the output to `stdout`. # iostream Wrapper {#iostreamWrapper} -Due to users' requests, RapidJSON provided official wrappers for `std::basic_istream` and `std::basic_ostream`. However, please note that the performance will be much lower than the other streams above. +Due to users' requests, RapidJSON also provides official wrappers for `std::basic_istream` and `std::basic_ostream`. However, please note that the performance will be much lower than the other streams above. ## IStreamWrapper {#IStreamWrapper} -`IStreamWrapper` wraps any class drived from `std::istream`, such as `std::istringstream`, `std::stringstream`, `std::ifstream`, `std::fstream`, into RapidJSON's input stream. +`IStreamWrapper` wraps any class derived from `std::istream`, such as `std::istringstream`, `std::stringstream`, `std::ifstream`, `std::fstream`, into RapidJSON's input stream. ~~~cpp #include @@ -179,7 +181,7 @@ As mentioned above, UTF-8 byte streams can be read directly. However, UTF-16 and Besides, it also need to handle [byte order mark (BOM)](http://en.wikipedia.org/wiki/Byte_order_mark). When reading from a byte stream, it is needed to detect or just consume the BOM if exists. When writing to a byte stream, it can optionally write BOM. -If the encoding of stream is known in compile-time, you may use `EncodedInputStream` and `EncodedOutputStream`. If the stream can be UTF-8, UTF-16LE, UTF-16BE, UTF-32LE, UTF-32BE JSON, and it is only known in runtime, you may use `AutoUTFInputStream` and `AutoUTFOutputStream`. These streams are defined in `rapidjson/encodedstream.h`. +If the encoding of stream is known during compile-time, you may use `EncodedInputStream` and `EncodedOutputStream`. If the stream can be UTF-8, UTF-16LE, UTF-16BE, UTF-32LE, UTF-32BE JSON, and it is only known in runtime, you may use `AutoUTFInputStream` and `AutoUTFOutputStream`. These streams are defined in `rapidjson/encodedstream.h`. Note that, these encoded streams can be applied to streams other than file. For example, you may have a file in memory, or a custom byte stream, be wrapped in encoded streams. @@ -215,6 +217,7 @@ fclose(fp); ~~~~~~~~~~cpp #include "rapidjson/filewritestream.h" // FileWriteStream #include "rapidjson/encodedstream.h" // EncodedOutputStream +#include #include Document d; // Document is GenericDocument > @@ -228,7 +231,7 @@ FileWriteStream bos(fp, writeBuffer, sizeof(writeBuffer)); typedef EncodedOutputStream, FileWriteStream> OutputStream; OutputStream eos(bos, true); // Write BOM -Writer, UTF8<>> writer(eos); +Writer, UTF32LE<>> writer(eos); d.Accept(writer); // This generates UTF32-LE file from UTF-8 in memory fclose(fp); diff --git a/third_party/rapidjson/doc/stream.zh-cn.md b/third_party/rapidjson/doc/stream.zh-cn.md index f2c54f798..6e379bbd6 100644 --- a/third_party/rapidjson/doc/stream.zh-cn.md +++ b/third_party/rapidjson/doc/stream.zh-cn.md @@ -42,6 +42,7 @@ d.Parse(json); ~~~~~~~~~~cpp #include "rapidjson/stringbuffer.h" +#include StringBuffer buffer; Writer writer(buffer); @@ -98,6 +99,7 @@ fclose(fp); ~~~~~~~~~~cpp #include "rapidjson/filewritestream.h" +#include #include using namespace rapidjson; @@ -215,6 +217,7 @@ fclose(fp); ~~~~~~~~~~cpp #include "rapidjson/filewritestream.h" // FileWriteStream #include "rapidjson/encodedstream.h" // EncodedOutputStream +#include #include Document d; // Document 为 GenericDocument > @@ -228,7 +231,7 @@ FileWriteStream bos(fp, writeBuffer, sizeof(writeBuffer)); typedef EncodedOutputStream, FileWriteStream> OutputStream; OutputStream eos(bos, true); // 写入 BOM -Writer, UTF8<>> writer(eos); +Writer, UTF32LE<>> writer(eos); d.Accept(writer); // 这里从内存的 UTF-8 生成 UTF32-LE 文件 fclose(fp); diff --git a/third_party/rapidjson/doc/tutorial.md b/third_party/rapidjson/doc/tutorial.md index cb76b4b0b..a86aafdfc 100644 --- a/third_party/rapidjson/doc/tutorial.md +++ b/third_party/rapidjson/doc/tutorial.md @@ -2,7 +2,7 @@ This tutorial introduces the basics of the Document Object Model(DOM) API. -As shown in [Usage at a glance](@ref index), a JSON can be parsed into DOM, and then the DOM can be queried and modified easily, and finally be converted back to JSON. +As shown in [Usage at a glance](@ref index), JSON can be parsed into a DOM, and then the DOM can be queried and modified easily, and finally be converted back to JSON. [TOC] @@ -12,9 +12,9 @@ Each JSON value is stored in a type called `Value`. A `Document`, representing t # Query Value {#QueryValue} -In this section, we will use excerpt of `example/tutorial/tutorial.cpp`. +In this section, we will use excerpt from `example/tutorial/tutorial.cpp`. -Assumes we have a JSON stored in a C string (`const char* json`): +Assume we have the following JSON stored in a C string (`const char* json`): ~~~~~~~~~~js { "hello": "world", @@ -55,7 +55,7 @@ printf("hello = %s\n", document["hello"].GetString()); ~~~~~~~~~~ ~~~~~~~~~~ -world +hello = world ~~~~~~~~~~ JSON true/false values are represented as `bool`. @@ -65,16 +65,16 @@ printf("t = %s\n", document["t"].GetBool() ? "true" : "false"); ~~~~~~~~~~ ~~~~~~~~~~ -true +t = true ~~~~~~~~~~ -JSON null can be queryed by `IsNull()`. +JSON null can be queried with `IsNull()`. ~~~~~~~~~~cpp printf("n = %s\n", document["n"].IsNull() ? "null" : "?"); ~~~~~~~~~~ ~~~~~~~~~~ -null +n = null ~~~~~~~~~~ JSON number type represents all numeric values. However, C++ needs more specific type for manipulation. @@ -82,10 +82,10 @@ JSON number type represents all numeric values. However, C++ needs more specific ~~~~~~~~~~cpp assert(document["i"].IsNumber()); -// In this case, IsUint()/IsInt64()/IsUInt64() also return true. +// In this case, IsUint()/IsInt64()/IsUint64() also return true. assert(document["i"].IsInt()); printf("i = %d\n", document["i"].GetInt()); -// Alternative (int)document["i"] +// Alternatively (int)document["i"] assert(document["pi"].IsNumber()); assert(document["pi"].IsDouble()); @@ -113,17 +113,17 @@ a[2] = 3 a[3] = 4 ~~~~~~~~~~ -Note that, RapidJSON does not automatically convert values between JSON types. If a value is a string, it is invalid to call `GetInt()`, for example. In debug mode it will fail an assertion. In release mode, the behavior is undefined. +Note that, RapidJSON does not automatically convert values between JSON types. For example, if a value is a string, it is invalid to call `GetInt()`. In debug mode it will fail on assertion. In release mode, the behavior is undefined. -In the following, details about querying individual types are discussed. +In the following sections we discuss details about querying individual types. ## Query Array {#QueryArray} -By default, `SizeType` is typedef of `unsigned`. In most systems, array is limited to store up to 2^32-1 elements. +By default, `SizeType` is typedef of `unsigned`. In most systems, an array is limited to store up to 2^32-1 elements. -You may access the elements in array by integer literal, for example, `a[0]`, `a[1]`, `a[2]`. +You may access the elements in an array by integer literal, for example, `a[0]`, `a[1]`, `a[2]`. -Array is similar to `std::vector`, instead of using indices, you may also use iterator to access all the elements. +Array is similar to `std::vector`: instead of using indices, you may also use iterator to access all the elements. ~~~~~~~~~~cpp for (Value::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr) printf("%d ", itr->GetInt()); @@ -144,7 +144,7 @@ for (auto& v : a.GetArray()) ## Query Object {#QueryObject} -Similar to array, we can access all object members by iterator: +Similar to Array, we can access all object members by iterator: ~~~~~~~~~~cpp static const char* kTypeNames[] = @@ -168,9 +168,9 @@ Type of member pi is Number Type of member a is Array ~~~~~~~~~~ -Note that, when `operator[](const char*)` cannot find the member, it will fail an assertion. +Note that, when `operator[](const char*)` cannot find the member, it will fail on assertion. -If we are unsure whether a member exists, we need to call `HasMember()` before calling `operator[](const char*)`. However, this incurs two lookup. A better way is to call `FindMember()`, which can check the existence of member and obtain its value at once: +If we are unsure whether a member exists, we need to call `HasMember()` before calling `operator[](const char*)`. However, this incurs two lookup. A better way is to call `FindMember()`, which can check the existence of a member and obtain its value at once: ~~~~~~~~~~cpp Value::ConstMemberIterator itr = document.FindMember("hello"); @@ -190,11 +190,11 @@ for (auto& m : document.GetObject()) ## Querying Number {#QueryNumber} -JSON provide a single numerical type called Number. Number can be integer or real numbers. RFC 4627 says the range of Number is specified by parser. +JSON provides a single numerical type called Number. Number can be an integer or a real number. RFC 4627 says the range of Number is specified by the parser implementation. -As C++ provides several integer and floating point number types, the DOM tries to handle these with widest possible range and good performance. +As C++ provides several integer and floating point number types, the DOM tries to handle these with the widest possible range and good performance. -When a Number is parsed, it is stored in the DOM as either one of the following type: +When a Number is parsed, it is stored in the DOM as one of the following types: Type | Description -----------|--------------------------------------- @@ -204,7 +204,7 @@ Type | Description `int64_t` | 64-bit signed integer `double` | 64-bit double precision floating point -When querying a number, you can check whether the number can be obtained as target type: +When querying a number, you can check whether the number can be obtained as the target type: Checking | Obtaining ------------------|--------------------- @@ -215,24 +215,24 @@ Checking | Obtaining `bool IsInt64()` | `int64_t GetInt64()` `bool IsDouble()` | `double GetDouble()` -Note that, an integer value may be obtained in various ways without conversion. For example, A value `x` containing 123 will make `x.IsInt() == x.IsUint() == x.IsInt64() == x.IsUint64() == true`. But a value `y` containing -3000000000 will only makes `x.IsInt64() == true`. +Note that, an integer value may be obtained in various ways without conversion. For example, A value `x` containing 123 will make `x.IsInt() == x.IsUint() == x.IsInt64() == x.IsUint64() == true`. But a value `y` containing -3000000000 will only make `x.IsInt64() == true`. -When obtaining the numeric values, `GetDouble()` will convert internal integer representation to a `double`. Note that, `int` and `unsigned` can be safely convert to `double`, but `int64_t` and `uint64_t` may lose precision (since mantissa of `double` is only 52-bits). +When obtaining the numeric values, `GetDouble()` will convert internal integer representation to a `double`. Note that, `int` and `unsigned` can be safely converted to `double`, but `int64_t` and `uint64_t` may lose precision (since mantissa of `double` is only 52-bits). ## Query String {#QueryString} -In addition to `GetString()`, the `Value` class also contains `GetStringLength()`. Here explains why. +In addition to `GetString()`, the `Value` class also contains `GetStringLength()`. Here explains why: -According to RFC 4627, JSON strings can contain Unicode character `U+0000`, which must be escaped as `"\u0000"`. The problem is that, C/C++ often uses null-terminated string, which treats ``\0'` as the terminator symbol. +According to RFC 4627, JSON strings can contain Unicode character `U+0000`, which must be escaped as `"\u0000"`. The problem is that, C/C++ often uses null-terminated string, which treats `\0` as the terminator symbol. -To conform RFC 4627, RapidJSON supports string containing `U+0000`. If you need to handle this, you can use `GetStringLength()` API to obtain the correct length of string. +To conform with RFC 4627, RapidJSON supports string containing `U+0000` character. If you need to handle this, you can use `GetStringLength()` to obtain the correct string length. -For example, after parsing a the following JSON to `Document d`: +For example, after parsing the following JSON to `Document d`: ~~~~~~~~~~js { "s" : "a\u0000b" } ~~~~~~~~~~ -The correct length of the value `"a\u0000b"` is 3. But `strlen()` returns 1. +The correct length of the string `"a\u0000b"` is 3, as returned by `GetStringLength()`. But `strlen()` returns 1. `GetStringLength()` can also improve performance, as user may often need to call `strlen()` for allocating buffer. @@ -246,7 +246,7 @@ which accepts the length of string as parameter. This constructor supports stori ## Comparing values -You can use `==` and `!=` to compare values. Two values are equal if and only if they are have same type and contents. You can also compare values with primitive types. Here is an example. +You can use `==` and `!=` to compare values. Two values are equal if and only if they have same type and contents. You can also compare values with primitive types. Here is an example: ~~~~~~~~~~cpp if (document["hello"] == document["n"]) /*...*/; // Compare values @@ -264,7 +264,7 @@ Note that, currently if an object contains duplicated named member, comparing eq There are several ways to create values. After a DOM tree is created and/or modified, it can be saved as JSON again using `Writer`. ## Change Value Type {#ChangeValueType} -When creating a Value or Document by default constructor, its type is Null. To change its type, call `SetXXX()` or assignment operator, for example: +When creating a `Value` or `Document` by default constructor, its type is Null. To change its type, call `SetXXX()` or assignment operator, for example: ~~~~~~~~~~cpp Document d; // Null @@ -285,7 +285,7 @@ Value u(123u); // calls Value(unsigned) Value d(1.5); // calls Value(double) ~~~~~~~~~~ -To create empty object or array, you may use `SetObject()`/`SetArray()` after default constructor, or using the `Value(Type)` in one shot: +To create empty object or array, you may use `SetObject()`/`SetArray()` after default constructor, or using the `Value(Type)` in one call: ~~~~~~~~~~cpp Value o(kObjectType); @@ -299,7 +299,7 @@ A very special decision during design of RapidJSON is that, assignment of value ~~~~~~~~~~cpp Value a(123); Value b(456); -b = a; // a becomes a Null value, b becomes number 123. +a = b; // b becomes a Null value, a becomes number 456. ~~~~~~~~~~ ![Assignment with move semantics.](diagram/move1.png) @@ -360,14 +360,14 @@ a.PushBack(Value(42).Move(), allocator); // same as above ~~~~~~~~~~ ## Create String {#CreateString} -RapidJSON provide two strategies for storing string. +RapidJSON provides two strategies for storing string. 1. copy-string: allocates a buffer, and then copy the source data into it. 2. const-string: simply store a pointer of string. -Copy-string is always safe because it owns a copy of the data. Const-string can be used for storing string literal, and in-situ parsing which we will mentioned in Document section. +Copy-string is always safe because it owns a copy of the data. Const-string can be used for storing a string literal, and for in-situ parsing which will be mentioned in the DOM section. -To make memory allocation customizable, RapidJSON requires user to pass an instance of allocator, whenever an operation may require allocation. This design is needed to prevent storing a allocator (or Document) pointer per Value. +To make memory allocation customizable, RapidJSON requires users to pass an instance of allocator, whenever an operation may require allocation. This design is needed to prevent storing an allocator (or Document) pointer per Value. Therefore, when we assign a copy-string, we call this overloaded `SetString()` with allocator: @@ -385,7 +385,7 @@ In this example, we get the allocator from a `Document` instance. This is a comm Besides, the above `SetString()` requires length. This can handle null characters within a string. There is another `SetString()` overloaded function without the length parameter. And it assumes the input is null-terminated and calls a `strlen()`-like function to obtain the length. -Finally, for string literal or string with safe life-cycle can use const-string version of `SetString()`, which lacks allocator parameter. For string literals (or constant character arrays), simply passing the literal as parameter is safe and efficient: +Finally, for a string literal or string with a safe life-cycle one can use the const-string version of `SetString()`, which lacks an allocator parameter. For string literals (or constant character arrays), simply passing the literal as parameter is safe and efficient: ~~~~~~~~~~cpp Value s; @@ -393,7 +393,7 @@ s.SetString("rapidjson"); // can contain null character, length derived at co s = "rapidjson"; // shortcut, same as above ~~~~~~~~~~ -For character pointer, the RapidJSON requires to mark it as safe before using it without copying. This can be achieved by using the `StringRef` function: +For a character pointer, RapidJSON requires it to be marked as safe before using it without copying. This can be achieved by using the `StringRef` function: ~~~~~~~~~cpp const char * cstr = getenv("USER"); @@ -408,7 +408,7 @@ s = StringRef(cstr,cstr_len); // shortcut, same as above ~~~~~~~~~ ## Modify Array {#ModifyArray} -Value with array type provides similar APIs as `std::vector`. +Value with array type provides an API similar to `std::vector`. * `Clear()` * `Reserve(SizeType, Allocator&)` @@ -418,7 +418,7 @@ Value with array type provides similar APIs as `std::vector`. * `ValueIterator Erase(ConstValueIterator pos)` * `ValueIterator Erase(ConstValueIterator first, ConstValueIterator last)` -Note that, `Reserve(...)` and `PushBack(...)` may allocate memory for the array elements, therefore require an allocator. +Note that, `Reserve(...)` and `PushBack(...)` may allocate memory for the array elements, therefore requiring an allocator. Here is an example of `PushBack()`: @@ -433,7 +433,7 @@ for (int i = 5; i <= 10; i++) a.PushBack("Lua", allocator).PushBack("Mio", allocator); ~~~~~~~~~~ -Differs from STL, `PushBack()`/`PopBack()` returns the array reference itself. This is called _fluent interface_. +This API differs from STL in that `PushBack()`/`PopBack()` return the array reference itself. This is called _fluent interface_. If you want to add a non-constant string or a string without sufficient lifetime (see [Create String](#CreateString)) to the array, you need to create a string Value by using the copy-string API. To avoid the need for an intermediate variable, you can use a [temporary value](#TemporaryValues) in place: @@ -448,7 +448,7 @@ contact.PushBack(val, document.GetAllocator()); ~~~~~~~~~~ ## Modify Object {#ModifyObject} -Object is a collection of key-value pairs (members). Each key must be a string value. To modify an object, either add or remove members. THe following APIs are for adding members: +The Object class is a collection of key-value pairs (members). Each key must be a string value. To modify an object, either add or remove members. The following API is for adding members: * `Value& AddMember(Value&, Value&, Allocator& allocator)` * `Value& AddMember(StringRefType, Value&, Allocator&)` @@ -462,7 +462,7 @@ contact.AddMember("name", "Milo", document.GetAllocator()); contact.AddMember("married", true, document.GetAllocator()); ~~~~~~~~~~ -The name parameter with `StringRefType` is similar to the interface of `SetString` function for string values. These overloads are used to avoid the need for copying the `name` string, as constant key names are very common in JSON objects. +The name parameter with `StringRefType` is similar to the interface of the `SetString` function for string values. These overloads are used to avoid the need for copying the `name` string, since constant key names are very common in JSON objects. If you need to create a name from a non-constant string or a string without sufficient lifetime (see [Create String](#CreateString)), you need to create a string Value by using the copy-string API. To avoid the need for an intermediate variable, you can use a [temporary value](#TemporaryValues) in place: diff --git a/third_party/rapidjson/doc/tutorial.zh-cn.md b/third_party/rapidjson/doc/tutorial.zh-cn.md index 61fb0b243..8b24ff11f 100644 --- a/third_party/rapidjson/doc/tutorial.zh-cn.md +++ b/third_party/rapidjson/doc/tutorial.zh-cn.md @@ -82,7 +82,7 @@ JSON Number 类型表示所有数值。然而,C++ 需要使用更专门的类 ~~~~~~~~~~cpp assert(document["i"].IsNumber()); -// 在此情况下,IsUint()/IsInt64()/IsUInt64() 也会返回 true +// 在此情况下,IsUint()/IsInt64()/IsUint64() 也会返回 true assert(document["i"].IsInt()); printf("i = %d\n", document["i"].GetInt()); // 另一种用法: (int)document["i"] @@ -250,7 +250,7 @@ string(const char* s, size_t count); ~~~~~~~~~~cpp if (document["hello"] == document["n"]) /*...*/; // 比较两个值 -if (document["hello"] == "world") /*...*/; // 与字符串家面量作比较 +if (document["hello"] == "world") /*...*/; // 与字符串字面量作比较 if (document["i"] != 123) /*...*/; // 与整数作比较 if (document["pi"] != 3.14) /*...*/; // 与 double 作比较 ~~~~~~~~~~ @@ -292,9 +292,9 @@ Value o(kObjectType); Value a(kArrayType); ~~~~~~~~~~ -## 转移语意(Move Semantics) {#MoveSemantics} +## 转移语义(Move Semantics) {#MoveSemantics} -在设计 RapidJSON 时有一个非常特别的决定,就是 Value 赋值并不是把来源 Value 复制至目的 Value,而是把把来源 Value 转移(move)至目的 Value。例如: +在设计 RapidJSON 时有一个非常特别的决定,就是 Value 赋值并不是把来源 Value 复制至目的 Value,而是把来源 Value 转移(move)至目的 Value。例如: ~~~~~~~~~~cpp Value a(123); @@ -302,13 +302,13 @@ Value b(456); b = a; // a 变成 Null,b 变成数字 123。 ~~~~~~~~~~ -![使用移动语意赋值。](diagram/move1.png) +![使用移动语义赋值。](diagram/move1.png) -为什么?此语意有何优点? +为什么?此语义有何优点? 最简单的答案就是性能。对于固定大小的 JSON 类型(Number、True、False、Null),复制它们是简单快捷。然而,对于可变大小的 JSON 类型(String、Array、Object),复制它们会产生大量开销,而且这些开销常常不被察觉。尤其是当我们需要创建临时 Object,把它复制至另一变量,然后再析构它。 -例如,若使用正常 * 复制 * 语意: +例如,若使用正常 * 复制 * 语义: ~~~~~~~~~~cpp Value o(kObjectType); @@ -321,15 +321,15 @@ Value o(kObjectType); } ~~~~~~~~~~ -![复制语意产生大量的复制操作。](diagram/move2.png) +![复制语义产生大量的复制操作。](diagram/move2.png) 那个 `o` Object 需要分配一个和 contacts 相同大小的缓冲区,对 conacts 做深度复制,并最终要析构 contacts。这样会产生大量无必要的内存分配/释放,以及内存复制。 有一些方案可避免实质地复制这些数据,例如引用计数(reference counting)、垃圾回收(garbage collection, GC)。 -为了使 RapidJSON 简单及快速,我们选择了对赋值采用 * 转移 * 语意。这方法与 `std::auto_ptr` 相似,都是在赋值时转移拥有权。转移快得多简单得多,只需要析构原来的 Value,把来源 `memcpy()` 至目标,最后把来源设置为 Null 类型。 +为了使 RapidJSON 简单及快速,我们选择了对赋值采用 * 转移 * 语义。这方法与 `std::auto_ptr` 相似,都是在赋值时转移拥有权。转移快得多简单得多,只需要析构原来的 Value,把来源 `memcpy()` 至目标,最后把来源设置为 Null 类型。 -因此,使用转移语意后,上面的例子变成: +因此,使用转移语义后,上面的例子变成: ~~~~~~~~~~cpp Value o(kObjectType); @@ -341,11 +341,11 @@ Value o(kObjectType); } ~~~~~~~~~~ -![转移语意不需复制。](diagram/move3.png) +![转移语义不需复制。](diagram/move3.png) -在 C++11 中这称为转移赋值操作(move assignment operator)。由于 RapidJSON 支持 C++03,它在赋值操作采用转移语意,其它修改形函数如 `AddMember()`, `PushBack()` 也采用转移语意。 +在 C++11 中这称为转移赋值操作(move assignment operator)。由于 RapidJSON 支持 C++03,它在赋值操作采用转移语义,其它修改型函数如 `AddMember()`, `PushBack()` 也采用转移语义。 -### 转移语意及临时值 {#TemporaryValues} +### 转移语义及临时值 {#TemporaryValues} 有时候,我们想直接构造一个 Value 并传递给一个“转移”函数(如 `PushBack()`、`AddMember()`)。由于临时对象是不能转换为正常的 Value 引用,我们加入了一个方便的 `Move()` 函数: @@ -383,11 +383,12 @@ memset(buffer, 0, sizeof(buffer)); 另外,上面的 `SetString()` 需要长度参数。这个 API 能处理含有空字符的字符串。另一个 `SetString()` 重载函数没有长度参数,它假设输入是空字符结尾的,并会调用类似 `strlen()` 的函数去获取长度。 -最后,对于字符串字面量或有安全生命周期的字符串,可以使用 const-string 版本的 `SetString()`,它没有 allocator 参数。对于字符串家面量(或字符数组常量),只需简单地传递字面量,又安全又高效: +最后,对于字符串字面量或有安全生命周期的字符串,可以使用 const-string 版本的 `SetString()`,它没有 +allocator 参数。对于字符串字面量(或字符数组常量),只需简单地传递字面量,又安全又高效: ~~~~~~~~~~cpp Value s; -s.SetString("rapidjson"); // 可包含空字符,长度在编译萁推导 +s.SetString("rapidjson"); // 可包含空字符,长度在编译期推导 s = "rapidjson"; // 上行的缩写 ~~~~~~~~~~ @@ -446,7 +447,7 @@ contact.PushBack(val, document.GetAllocator()); ~~~~~~~~~~ ## 修改 Object {#ModifyObject} -Object 是键值对的集合。每个键必须为 String。要修改 Object,方法是增加或移除成员。以下的 API 用来增加城员: +Object 是键值对的集合。每个键必须为 String。要修改 Object,方法是增加或移除成员。以下的 API 用来增加成员: * `Value& AddMember(Value&, Value&, Allocator& allocator)` * `Value& AddMember(StringRefType, Value&, Allocator&)` diff --git a/third_party/rapidjson/example/CMakeLists.txt b/third_party/rapidjson/example/CMakeLists.txt index 4d448ccc0..9f53c9aad 100644 --- a/third_party/rapidjson/example/CMakeLists.txt +++ b/third_party/rapidjson/example/CMakeLists.txt @@ -10,6 +10,7 @@ set(EXAMPLES filterkey filterkeydom jsonx + lookaheadparser messagereader parsebyparts pretty @@ -18,19 +19,22 @@ set(EXAMPLES serialize simpledom simplereader + simplepullreader simplewriter + sortkeys tutorial) include_directories("../include/") add_definitions(-D__STDC_FORMAT_MACROS) +set_property(DIRECTORY PROPERTY COMPILE_OPTIONS ${EXTRA_CXX_FLAGS}) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -Werror -Wall -Wextra -Weffc++ -Wswitch-default") -elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Weffc++ -Wswitch-default -Wfloat-equal -Wimplicit-fallthrough -Weverything") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") endif() +add_executable(archivertest archiver/archiver.cpp archiver/archivertest.cpp) + foreach (example ${EXAMPLES}) add_executable(${example} ${example}/${example}.cpp) endforeach() diff --git a/third_party/rapidjson/example/archiver/archiver.cpp b/third_party/rapidjson/example/archiver/archiver.cpp new file mode 100644 index 000000000..59ae4c410 --- /dev/null +++ b/third_party/rapidjson/example/archiver/archiver.cpp @@ -0,0 +1,292 @@ +#include "archiver.h" +#include +#include +#include "rapidjson/document.h" +#include "rapidjson/prettywriter.h" +#include "rapidjson/stringbuffer.h" + +using namespace rapidjson; + +struct JsonReaderStackItem { + enum State { + BeforeStart, //!< An object/array is in the stack but it is not yet called by StartObject()/StartArray(). + Started, //!< An object/array is called by StartObject()/StartArray(). + Closed //!< An array is closed after read all element, but before EndArray(). + }; + + JsonReaderStackItem(const Value* value, State state) : value(value), state(state), index() {} + + const Value* value; + State state; + SizeType index; // For array iteration +}; + +typedef std::stack JsonReaderStack; + +#define DOCUMENT reinterpret_cast(mDocument) +#define STACK (reinterpret_cast(mStack)) +#define TOP (STACK->top()) +#define CURRENT (*TOP.value) + +JsonReader::JsonReader(const char* json) : mDocument(), mStack(), mError(false) { + mDocument = new Document; + DOCUMENT->Parse(json); + if (DOCUMENT->HasParseError()) + mError = true; + else { + mStack = new JsonReaderStack; + STACK->push(JsonReaderStackItem(DOCUMENT, JsonReaderStackItem::BeforeStart)); + } +} + +JsonReader::~JsonReader() { + delete DOCUMENT; + delete STACK; +} + +// Archive concept +JsonReader& JsonReader::StartObject() { + if (!mError) { + if (CURRENT.IsObject() && TOP.state == JsonReaderStackItem::BeforeStart) + TOP.state = JsonReaderStackItem::Started; + else + mError = true; + } + return *this; +} + +JsonReader& JsonReader::EndObject() { + if (!mError) { + if (CURRENT.IsObject() && TOP.state == JsonReaderStackItem::Started) + Next(); + else + mError = true; + } + return *this; +} + +JsonReader& JsonReader::Member(const char* name) { + if (!mError) { + if (CURRENT.IsObject() && TOP.state == JsonReaderStackItem::Started) { + Value::ConstMemberIterator memberItr = CURRENT.FindMember(name); + if (memberItr != CURRENT.MemberEnd()) + STACK->push(JsonReaderStackItem(&memberItr->value, JsonReaderStackItem::BeforeStart)); + else + mError = true; + } + else + mError = true; + } + return *this; +} + +bool JsonReader::HasMember(const char* name) const { + if (!mError && CURRENT.IsObject() && TOP.state == JsonReaderStackItem::Started) + return CURRENT.HasMember(name); + return false; +} + +JsonReader& JsonReader::StartArray(size_t* size) { + if (!mError) { + if (CURRENT.IsArray() && TOP.state == JsonReaderStackItem::BeforeStart) { + TOP.state = JsonReaderStackItem::Started; + if (size) + *size = CURRENT.Size(); + + if (!CURRENT.Empty()) { + const Value* value = &CURRENT[TOP.index]; + STACK->push(JsonReaderStackItem(value, JsonReaderStackItem::BeforeStart)); + } + else + TOP.state = JsonReaderStackItem::Closed; + } + else + mError = true; + } + return *this; +} + +JsonReader& JsonReader::EndArray() { + if (!mError) { + if (CURRENT.IsArray() && TOP.state == JsonReaderStackItem::Closed) + Next(); + else + mError = true; + } + return *this; +} + +JsonReader& JsonReader::operator&(bool& b) { + if (!mError) { + if (CURRENT.IsBool()) { + b = CURRENT.GetBool(); + Next(); + } + else + mError = true; + } + return *this; +} + +JsonReader& JsonReader::operator&(unsigned& u) { + if (!mError) { + if (CURRENT.IsUint()) { + u = CURRENT.GetUint(); + Next(); + } + else + mError = true; + } + return *this; +} + +JsonReader& JsonReader::operator&(int& i) { + if (!mError) { + if (CURRENT.IsInt()) { + i = CURRENT.GetInt(); + Next(); + } + else + mError = true; + } + return *this; +} + +JsonReader& JsonReader::operator&(double& d) { + if (!mError) { + if (CURRENT.IsNumber()) { + d = CURRENT.GetDouble(); + Next(); + } + else + mError = true; + } + return *this; +} + +JsonReader& JsonReader::operator&(std::string& s) { + if (!mError) { + if (CURRENT.IsString()) { + s = CURRENT.GetString(); + Next(); + } + else + mError = true; + } + return *this; +} + +JsonReader& JsonReader::SetNull() { + // This function is for JsonWriter only. + mError = true; + return *this; +} + +void JsonReader::Next() { + if (!mError) { + assert(!STACK->empty()); + STACK->pop(); + + if (!STACK->empty() && CURRENT.IsArray()) { + if (TOP.state == JsonReaderStackItem::Started) { // Otherwise means reading array item pass end + if (TOP.index < CURRENT.Size() - 1) { + const Value* value = &CURRENT[++TOP.index]; + STACK->push(JsonReaderStackItem(value, JsonReaderStackItem::BeforeStart)); + } + else + TOP.state = JsonReaderStackItem::Closed; + } + else + mError = true; + } + } +} + +#undef DOCUMENT +#undef STACK +#undef TOP +#undef CURRENT + +//////////////////////////////////////////////////////////////////////////////// +// JsonWriter + +#define WRITER reinterpret_cast*>(mWriter) +#define STREAM reinterpret_cast(mStream) + +JsonWriter::JsonWriter() : mWriter(), mStream() { + mStream = new StringBuffer; + mWriter = new PrettyWriter(*STREAM); +} + +JsonWriter::~JsonWriter() { + delete WRITER; + delete STREAM; +} + +const char* JsonWriter::GetString() const { + return STREAM->GetString(); +} + +JsonWriter& JsonWriter::StartObject() { + WRITER->StartObject(); + return *this; +} + +JsonWriter& JsonWriter::EndObject() { + WRITER->EndObject(); + return *this; +} + +JsonWriter& JsonWriter::Member(const char* name) { + WRITER->String(name, static_cast(strlen(name))); + return *this; +} + +bool JsonWriter::HasMember(const char*) const { + // This function is for JsonReader only. + assert(false); + return false; +} + +JsonWriter& JsonWriter::StartArray(size_t*) { + WRITER->StartArray(); + return *this; +} + +JsonWriter& JsonWriter::EndArray() { + WRITER->EndArray(); + return *this; +} + +JsonWriter& JsonWriter::operator&(bool& b) { + WRITER->Bool(b); + return *this; +} + +JsonWriter& JsonWriter::operator&(unsigned& u) { + WRITER->Uint(u); + return *this; +} + +JsonWriter& JsonWriter::operator&(int& i) { + WRITER->Int(i); + return *this; +} + +JsonWriter& JsonWriter::operator&(double& d) { + WRITER->Double(d); + return *this; +} + +JsonWriter& JsonWriter::operator&(std::string& s) { + WRITER->String(s.c_str(), static_cast(s.size())); + return *this; +} + +JsonWriter& JsonWriter::SetNull() { + WRITER->Null(); + return *this; +} + +#undef STREAM +#undef WRITER diff --git a/third_party/rapidjson/example/archiver/archiver.h b/third_party/rapidjson/example/archiver/archiver.h new file mode 100644 index 000000000..285ca73d6 --- /dev/null +++ b/third_party/rapidjson/example/archiver/archiver.h @@ -0,0 +1,145 @@ +#ifndef ARCHIVER_H_ +#define ARCHIVER_H_ + +#include +#include + +/** +\class Archiver +\brief Archiver concept + +Archiver can be a reader or writer for serialization or deserialization respectively. + +class Archiver { +public: + /// \returns true if the archiver is in normal state. false if it has errors. + operator bool() const; + + /// Starts an object + Archiver& StartObject(); + + /// After calling StartObject(), assign a member with a name + Archiver& Member(const char* name); + + /// After calling StartObject(), check if a member presents + bool HasMember(const char* name) const; + + /// Ends an object + Archiver& EndObject(); + + /// Starts an array + /// \param size If Archiver::IsReader is true, the size of array is written. + Archiver& StartArray(size_t* size = 0); + + /// Ends an array + Archiver& EndArray(); + + /// Read/Write primitive types. + Archiver& operator&(bool& b); + Archiver& operator&(unsigned& u); + Archiver& operator&(int& i); + Archiver& operator&(double& d); + Archiver& operator&(std::string& s); + + /// Write primitive types. + Archiver& SetNull(); + + //! Whether it is a reader. + static const bool IsReader; + + //! Whether it is a writer. + static const bool IsWriter; +}; +*/ + +/// Represents a JSON reader which implements Archiver concept. +class JsonReader { +public: + /// Constructor. + /** + \param json A non-const source json string for in-situ parsing. + \note in-situ means the source JSON string will be modified after parsing. + */ + JsonReader(const char* json); + + /// Destructor. + ~JsonReader(); + + // Archive concept + + operator bool() const { return !mError; } + + JsonReader& StartObject(); + JsonReader& Member(const char* name); + bool HasMember(const char* name) const; + JsonReader& EndObject(); + + JsonReader& StartArray(size_t* size = 0); + JsonReader& EndArray(); + + JsonReader& operator&(bool& b); + JsonReader& operator&(unsigned& u); + JsonReader& operator&(int& i); + JsonReader& operator&(double& d); + JsonReader& operator&(std::string& s); + + JsonReader& SetNull(); + + static const bool IsReader = true; + static const bool IsWriter = !IsReader; + +private: + JsonReader(const JsonReader&); + JsonReader& operator=(const JsonReader&); + + void Next(); + + // PIMPL + void* mDocument; ///< DOM result of parsing. + void* mStack; ///< Stack for iterating the DOM + bool mError; ///< Whether an error has occurred. +}; + +class JsonWriter { +public: + /// Constructor. + JsonWriter(); + + /// Destructor. + ~JsonWriter(); + + /// Obtains the serialized JSON string. + const char* GetString() const; + + // Archive concept + + operator bool() const { return true; } + + JsonWriter& StartObject(); + JsonWriter& Member(const char* name); + bool HasMember(const char* name) const; + JsonWriter& EndObject(); + + JsonWriter& StartArray(size_t* size = 0); + JsonWriter& EndArray(); + + JsonWriter& operator&(bool& b); + JsonWriter& operator&(unsigned& u); + JsonWriter& operator&(int& i); + JsonWriter& operator&(double& d); + JsonWriter& operator&(std::string& s); + JsonWriter& SetNull(); + + static const bool IsReader = false; + static const bool IsWriter = !IsReader; + +private: + JsonWriter(const JsonWriter&); + JsonWriter& operator=(const JsonWriter&); + + // PIMPL idiom + void* mWriter; ///< JSON writer. + void* mStream; ///< Stream buffer. +}; + +#endif // ARCHIVER_H__ diff --git a/third_party/rapidjson/example/archiver/archivertest.cpp b/third_party/rapidjson/example/archiver/archivertest.cpp new file mode 100644 index 000000000..417a421a3 --- /dev/null +++ b/third_party/rapidjson/example/archiver/archivertest.cpp @@ -0,0 +1,287 @@ +#include "archiver.h" +#include +#include + +////////////////////////////////////////////////////////////////////////////// +// Test1: simple object + +struct Student { + Student() : name(), age(), height(), canSwim() {} + Student(const std::string name, unsigned age, double height, bool canSwim) : + name(name), age(age), height(height), canSwim(canSwim) + {} + + std::string name; + unsigned age; + double height; + bool canSwim; +}; + +template +Archiver& operator&(Archiver& ar, Student& s) { + ar.StartObject(); + ar.Member("name") & s.name; + ar.Member("age") & s.age; + ar.Member("height") & s.height; + ar.Member("canSwim") & s.canSwim; + return ar.EndObject(); +} + +std::ostream& operator<<(std::ostream& os, const Student& s) { + return os << s.name << " " << s.age << " " << s.height << " " << s.canSwim; +} + +void test1() { + std::string json; + + // Serialize + { + Student s("Lua", 9, 150.5, true); + + JsonWriter writer; + writer & s; + json = writer.GetString(); + std::cout << json << std::endl; + } + + // Deserialize + { + Student s; + JsonReader reader(json.c_str()); + reader & s; + std::cout << s << std::endl; + } +} + +////////////////////////////////////////////////////////////////////////////// +// Test2: std::vector <=> JSON array +// +// You can map a JSON array to other data structures as well + +struct Group { + Group() : groupName(), students() {} + std::string groupName; + std::vector students; +}; + +template +Archiver& operator&(Archiver& ar, Group& g) { + ar.StartObject(); + + ar.Member("groupName"); + ar & g.groupName; + + ar.Member("students"); + size_t studentCount = g.students.size(); + ar.StartArray(&studentCount); + if (ar.IsReader) + g.students.resize(studentCount); + for (size_t i = 0; i < studentCount; i++) + ar & g.students[i]; + ar.EndArray(); + + return ar.EndObject(); +} + +std::ostream& operator<<(std::ostream& os, const Group& g) { + os << g.groupName << std::endl; + for (std::vector::const_iterator itr = g.students.begin(); itr != g.students.end(); ++itr) + os << *itr << std::endl; + return os; +} + +void test2() { + std::string json; + + // Serialize + { + Group g; + g.groupName = "Rainbow"; + + Student s1("Lua", 9, 150.5, true); + Student s2("Mio", 7, 120.0, false); + g.students.push_back(s1); + g.students.push_back(s2); + + JsonWriter writer; + writer & g; + json = writer.GetString(); + std::cout << json << std::endl; + } + + // Deserialize + { + Group g; + JsonReader reader(json.c_str()); + reader & g; + std::cout << g << std::endl; + } +} + +////////////////////////////////////////////////////////////////////////////// +// Test3: polymorphism & friend +// +// Note that friendship is not necessary but make things simpler. + +class Shape { +public: + virtual ~Shape() {} + virtual const char* GetType() const = 0; + virtual void Print(std::ostream& os) const = 0; + +protected: + Shape() : x_(), y_() {} + Shape(double x, double y) : x_(x), y_(y) {} + + template + friend Archiver& operator&(Archiver& ar, Shape& s); + + double x_, y_; +}; + +template +Archiver& operator&(Archiver& ar, Shape& s) { + ar.Member("x") & s.x_; + ar.Member("y") & s.y_; + return ar; +} + +class Circle : public Shape { +public: + Circle() : radius_() {} + Circle(double x, double y, double radius) : Shape(x, y), radius_(radius) {} + ~Circle() {} + + const char* GetType() const { return "Circle"; } + + void Print(std::ostream& os) const { + os << "Circle (" << x_ << ", " << y_ << ")" << " radius = " << radius_; + } + +private: + template + friend Archiver& operator&(Archiver& ar, Circle& c); + + double radius_; +}; + +template +Archiver& operator&(Archiver& ar, Circle& c) { + ar & static_cast(c); + ar.Member("radius") & c.radius_; + return ar; +} + +class Box : public Shape { +public: + Box() : width_(), height_() {} + Box(double x, double y, double width, double height) : Shape(x, y), width_(width), height_(height) {} + ~Box() {} + + const char* GetType() const { return "Box"; } + + void Print(std::ostream& os) const { + os << "Box (" << x_ << ", " << y_ << ")" << " width = " << width_ << " height = " << height_; + } + +private: + template + friend Archiver& operator&(Archiver& ar, Box& b); + + double width_, height_; +}; + +template +Archiver& operator&(Archiver& ar, Box& b) { + ar & static_cast(b); + ar.Member("width") & b.width_; + ar.Member("height") & b.height_; + return ar; +} + +class Canvas { +public: + Canvas() : shapes_() {} + ~Canvas() { Clear(); } + + void Clear() { + for (std::vector::iterator itr = shapes_.begin(); itr != shapes_.end(); ++itr) + delete *itr; + } + + void AddShape(Shape* shape) { shapes_.push_back(shape); } + + void Print(std::ostream& os) { + for (std::vector::iterator itr = shapes_.begin(); itr != shapes_.end(); ++itr) { + (*itr)->Print(os); + std::cout << std::endl; + } + } + +private: + template + friend Archiver& operator&(Archiver& ar, Canvas& c); + + std::vector shapes_; +}; + +template +Archiver& operator&(Archiver& ar, Shape*& shape) { + std::string type = ar.IsReader ? "" : shape->GetType(); + ar.StartObject(); + ar.Member("type") & type; + if (type == "Circle") { + if (ar.IsReader) shape = new Circle; + ar & static_cast(*shape); + } + else if (type == "Box") { + if (ar.IsReader) shape = new Box; + ar & static_cast(*shape); + } + return ar.EndObject(); +} + +template +Archiver& operator&(Archiver& ar, Canvas& c) { + size_t shapeCount = c.shapes_.size(); + ar.StartArray(&shapeCount); + if (ar.IsReader) { + c.Clear(); + c.shapes_.resize(shapeCount); + } + for (size_t i = 0; i < shapeCount; i++) + ar & c.shapes_[i]; + return ar.EndArray(); +} + +void test3() { + std::string json; + + // Serialize + { + Canvas c; + c.AddShape(new Circle(1.0, 2.0, 3.0)); + c.AddShape(new Box(4.0, 5.0, 6.0, 7.0)); + + JsonWriter writer; + writer & c; + json = writer.GetString(); + std::cout << json << std::endl; + } + + // Deserialize + { + Canvas c; + JsonReader reader(json.c_str()); + reader & c; + c.Print(std::cout); + } +} + +////////////////////////////////////////////////////////////////////////////// + +int main() { + test1(); + test2(); + test3(); +} diff --git a/third_party/rapidjson/example/jsonx/jsonx.cpp b/third_party/rapidjson/example/jsonx/jsonx.cpp index 1346b578c..954aa2b90 100644 --- a/third_party/rapidjson/example/jsonx/jsonx.cpp +++ b/third_party/rapidjson/example/jsonx/jsonx.cpp @@ -1,4 +1,4 @@ -// JSON to JSONx conversion exmaple, using SAX API. +// JSON to JSONx conversion example, using SAX API. // JSONx is an IBM standard format to represent JSON as XML. // https://www-01.ibm.com/support/knowledgecenter/SS9H2Y_7.1.0/com.ibm.dp.doc/json_jsonx.html // This example parses JSON text from stdin with validation, diff --git a/third_party/rapidjson/example/lookaheadparser/lookaheadparser.cpp b/third_party/rapidjson/example/lookaheadparser/lookaheadparser.cpp new file mode 100644 index 000000000..f627f4d86 --- /dev/null +++ b/third_party/rapidjson/example/lookaheadparser/lookaheadparser.cpp @@ -0,0 +1,350 @@ +#include "rapidjson/reader.h" +#include "rapidjson/document.h" +#include + +RAPIDJSON_DIAG_PUSH +#ifdef __GNUC__ +RAPIDJSON_DIAG_OFF(effc++) +#endif + +// This example demonstrates JSON token-by-token parsing with an API that is +// more direct; you don't need to design your logic around a handler object and +// callbacks. Instead, you retrieve values from the JSON stream by calling +// GetInt(), GetDouble(), GetString() and GetBool(), traverse into structures +// by calling EnterObject() and EnterArray(), and skip over unwanted data by +// calling SkipValue(). When you know your JSON's structure, this can be quite +// convenient. +// +// If you aren't sure of what's next in the JSON data, you can use PeekType() and +// PeekValue() to look ahead to the next object before reading it. +// +// If you call the wrong retrieval method--e.g. GetInt when the next JSON token is +// not an int, EnterObject or EnterArray when there isn't actually an object or array +// to read--the stream parsing will end immediately and no more data will be delivered. +// +// After calling EnterObject, you retrieve keys via NextObjectKey() and values via +// the normal getters. When NextObjectKey() returns null, you have exited the +// object, or you can call SkipObject() to skip to the end of the object +// immediately. If you fetch the entire object (i.e. NextObjectKey() returned null), +// you should not call SkipObject(). +// +// After calling EnterArray(), you must alternate between calling NextArrayValue() +// to see if the array has more data, and then retrieving values via the normal +// getters. You can call SkipArray() to skip to the end of the array immediately. +// If you fetch the entire array (i.e. NextArrayValue() returned null), +// you should not call SkipArray(). +// +// This parser uses in-situ strings, so the JSON buffer will be altered during the +// parse. + +using namespace rapidjson; + + +class LookaheadParserHandler { +public: + bool Null() { st_ = kHasNull; v_.SetNull(); return true; } + bool Bool(bool b) { st_ = kHasBool; v_.SetBool(b); return true; } + bool Int(int i) { st_ = kHasNumber; v_.SetInt(i); return true; } + bool Uint(unsigned u) { st_ = kHasNumber; v_.SetUint(u); return true; } + bool Int64(int64_t i) { st_ = kHasNumber; v_.SetInt64(i); return true; } + bool Uint64(uint64_t u) { st_ = kHasNumber; v_.SetUint64(u); return true; } + bool Double(double d) { st_ = kHasNumber; v_.SetDouble(d); return true; } + bool RawNumber(const char*, SizeType, bool) { return false; } + bool String(const char* str, SizeType length, bool) { st_ = kHasString; v_.SetString(str, length); return true; } + bool StartObject() { st_ = kEnteringObject; return true; } + bool Key(const char* str, SizeType length, bool) { st_ = kHasKey; v_.SetString(str, length); return true; } + bool EndObject(SizeType) { st_ = kExitingObject; return true; } + bool StartArray() { st_ = kEnteringArray; return true; } + bool EndArray(SizeType) { st_ = kExitingArray; return true; } + +protected: + LookaheadParserHandler(char* str); + void ParseNext(); + +protected: + enum LookaheadParsingState { + kInit, + kError, + kHasNull, + kHasBool, + kHasNumber, + kHasString, + kHasKey, + kEnteringObject, + kExitingObject, + kEnteringArray, + kExitingArray + }; + + Value v_; + LookaheadParsingState st_; + Reader r_; + InsituStringStream ss_; + + static const int parseFlags = kParseDefaultFlags | kParseInsituFlag; +}; + +LookaheadParserHandler::LookaheadParserHandler(char* str) : v_(), st_(kInit), r_(), ss_(str) { + r_.IterativeParseInit(); + ParseNext(); +} + +void LookaheadParserHandler::ParseNext() { + if (r_.HasParseError()) { + st_ = kError; + return; + } + + r_.IterativeParseNext(ss_, *this); +} + +class LookaheadParser : protected LookaheadParserHandler { +public: + LookaheadParser(char* str) : LookaheadParserHandler(str) {} + + bool EnterObject(); + bool EnterArray(); + const char* NextObjectKey(); + bool NextArrayValue(); + int GetInt(); + double GetDouble(); + const char* GetString(); + bool GetBool(); + void GetNull(); + + void SkipObject(); + void SkipArray(); + void SkipValue(); + Value* PeekValue(); + int PeekType(); // returns a rapidjson::Type, or -1 for no value (at end of object/array) + + bool IsValid() { return st_ != kError; } + +protected: + void SkipOut(int depth); +}; + +bool LookaheadParser::EnterObject() { + if (st_ != kEnteringObject) { + st_ = kError; + return false; + } + + ParseNext(); + return true; +} + +bool LookaheadParser::EnterArray() { + if (st_ != kEnteringArray) { + st_ = kError; + return false; + } + + ParseNext(); + return true; +} + +const char* LookaheadParser::NextObjectKey() { + if (st_ == kHasKey) { + const char* result = v_.GetString(); + ParseNext(); + return result; + } + + if (st_ != kExitingObject) { + st_ = kError; + return 0; + } + + ParseNext(); + return 0; +} + +bool LookaheadParser::NextArrayValue() { + if (st_ == kExitingArray) { + ParseNext(); + return false; + } + + if (st_ == kError || st_ == kExitingObject || st_ == kHasKey) { + st_ = kError; + return false; + } + + return true; +} + +int LookaheadParser::GetInt() { + if (st_ != kHasNumber || !v_.IsInt()) { + st_ = kError; + return 0; + } + + int result = v_.GetInt(); + ParseNext(); + return result; +} + +double LookaheadParser::GetDouble() { + if (st_ != kHasNumber) { + st_ = kError; + return 0.; + } + + double result = v_.GetDouble(); + ParseNext(); + return result; +} + +bool LookaheadParser::GetBool() { + if (st_ != kHasBool) { + st_ = kError; + return false; + } + + bool result = v_.GetBool(); + ParseNext(); + return result; +} + +void LookaheadParser::GetNull() { + if (st_ != kHasNull) { + st_ = kError; + return; + } + + ParseNext(); +} + +const char* LookaheadParser::GetString() { + if (st_ != kHasString) { + st_ = kError; + return 0; + } + + const char* result = v_.GetString(); + ParseNext(); + return result; +} + +void LookaheadParser::SkipOut(int depth) { + do { + if (st_ == kEnteringArray || st_ == kEnteringObject) { + ++depth; + } + else if (st_ == kExitingArray || st_ == kExitingObject) { + --depth; + } + else if (st_ == kError) { + return; + } + + ParseNext(); + } + while (depth > 0); +} + +void LookaheadParser::SkipValue() { + SkipOut(0); +} + +void LookaheadParser::SkipArray() { + SkipOut(1); +} + +void LookaheadParser::SkipObject() { + SkipOut(1); +} + +Value* LookaheadParser::PeekValue() { + if (st_ >= kHasNull && st_ <= kHasKey) { + return &v_; + } + + return 0; +} + +int LookaheadParser::PeekType() { + if (st_ >= kHasNull && st_ <= kHasKey) { + return v_.GetType(); + } + + if (st_ == kEnteringArray) { + return kArrayType; + } + + if (st_ == kEnteringObject) { + return kObjectType; + } + + return -1; +} + +//------------------------------------------------------------------------- + +int main() { + using namespace std; + + char json[] = " { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null," + "\"i\":123, \"pi\": 3.1416, \"a\":[-1, 2, 3, 4, \"array\", []], \"skipArrays\":[1, 2, [[[3]]]], " + "\"skipObject\":{ \"i\":0, \"t\":true, \"n\":null, \"d\":123.45 }, " + "\"skipNested\":[[[[{\"\":0}, {\"\":[-9.87]}]]], [], []], " + "\"skipString\":\"zzz\", \"reachedEnd\":null, \"t\":true }"; + + LookaheadParser r(json); + + RAPIDJSON_ASSERT(r.PeekType() == kObjectType); + + r.EnterObject(); + while (const char* key = r.NextObjectKey()) { + if (0 == strcmp(key, "hello")) { + RAPIDJSON_ASSERT(r.PeekType() == kStringType); + cout << key << ":" << r.GetString() << endl; + } + else if (0 == strcmp(key, "t") || 0 == strcmp(key, "f")) { + RAPIDJSON_ASSERT(r.PeekType() == kTrueType || r.PeekType() == kFalseType); + cout << key << ":" << r.GetBool() << endl; + continue; + } + else if (0 == strcmp(key, "n")) { + RAPIDJSON_ASSERT(r.PeekType() == kNullType); + r.GetNull(); + cout << key << endl; + continue; + } + else if (0 == strcmp(key, "pi")) { + RAPIDJSON_ASSERT(r.PeekType() == kNumberType); + cout << key << ":" << r.GetDouble() << endl; + continue; + } + else if (0 == strcmp(key, "a")) { + RAPIDJSON_ASSERT(r.PeekType() == kArrayType); + + r.EnterArray(); + + cout << key << ":[ "; + while (r.NextArrayValue()) { + if (r.PeekType() == kNumberType) { + cout << r.GetDouble() << " "; + } + else if (r.PeekType() == kStringType) { + cout << r.GetString() << " "; + } + else { + r.SkipArray(); + break; + } + } + + cout << "]" << endl; + } + else { + cout << key << ":skipped" << endl; + r.SkipValue(); + } + } + + return 0; +} + +RAPIDJSON_DIAG_POP diff --git a/third_party/rapidjson/example/parsebyparts/parsebyparts.cpp b/third_party/rapidjson/example/parsebyparts/parsebyparts.cpp index 57eed005d..ff735394e 100644 --- a/third_party/rapidjson/example/parsebyparts/parsebyparts.cpp +++ b/third_party/rapidjson/example/parsebyparts/parsebyparts.cpp @@ -21,12 +21,15 @@ public: AsyncDocumentParser(Document& d) : stream_(*this) , d_(d) - , parseThread_(&AsyncDocumentParser::Parse, this) + , parseThread_() , mutex_() , notEmpty_() , finish_() , completed_() - {} + { + // Create and execute thread after all member variables are initialized. + parseThread_ = std::thread(&AsyncDocumentParser::Parse, this); + } ~AsyncDocumentParser() { if (!parseThread_.joinable()) @@ -140,7 +143,7 @@ int main() { AsyncDocumentParser<> parser(d); const char json1[] = " { \"hello\" : \"world\", \"t\" : tr"; - //const char json1[] = " { \"hello\" : \"world\", \"t\" : trX"; // Fot test parsing error + //const char json1[] = " { \"hello\" : \"world\", \"t\" : trX"; // For test parsing error const char json2[] = "ue, \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.14"; const char json3[] = "16, \"a\":[1, 2, 3, 4] } "; diff --git a/third_party/rapidjson/example/schemavalidator/schemavalidator.cpp b/third_party/rapidjson/example/schemavalidator/schemavalidator.cpp index ce36ea95f..6ce3c39e5 100644 --- a/third_party/rapidjson/example/schemavalidator/schemavalidator.cpp +++ b/third_party/rapidjson/example/schemavalidator/schemavalidator.cpp @@ -2,13 +2,132 @@ // The example validates JSON text from stdin with a JSON schema specified in the argument. +#define RAPIDJSON_HAS_STDSTRING 1 + #include "rapidjson/error/en.h" #include "rapidjson/filereadstream.h" #include "rapidjson/schema.h" #include "rapidjson/stringbuffer.h" +#include "rapidjson/prettywriter.h" +#include +#include +#include using namespace rapidjson; +typedef GenericValue, CrtAllocator > ValueType; + +// Forward ref +static void CreateErrorMessages(const ValueType& errors, size_t depth, const char* context); + +// Convert GenericValue to std::string +static std::string GetString(const ValueType& val) { + std::ostringstream s; + if (val.IsString()) + s << val.GetString(); + else if (val.IsDouble()) + s << val.GetDouble(); + else if (val.IsUint()) + s << val.GetUint(); + else if (val.IsInt()) + s << val.GetInt(); + else if (val.IsUint64()) + s << val.GetUint64(); + else if (val.IsInt64()) + s << val.GetInt64(); + else if (val.IsBool() && val.GetBool()) + s << "true"; + else if (val.IsBool()) + s << "false"; + else if (val.IsFloat()) + s << val.GetFloat(); + return s.str(); +} + +// Create the error message for a named error +// The error object can either be empty or contain at least member properties: +// {"errorCode": , "instanceRef": "", "schemaRef": "" } +// Additional properties may be present for use as inserts. +// An "errors" property may be present if there are child errors. +static void HandleError(const char* errorName, const ValueType& error, size_t depth, const char* context) { + if (!error.ObjectEmpty()) { + // Get error code and look up error message text (English) + int code = error["errorCode"].GetInt(); + std::string message(GetValidateError_En(static_cast(code))); + // For each member property in the error, see if its name exists as an insert in the error message and if so replace with the stringified property value + // So for example - "Number '%actual' is not a multiple of the 'multipleOf' value '%expected'." - we would expect "actual" and "expected" members. + for (ValueType::ConstMemberIterator insertsItr = error.MemberBegin(); + insertsItr != error.MemberEnd(); ++insertsItr) { + std::string insertName("%"); + insertName += insertsItr->name.GetString(); // eg "%actual" + size_t insertPos = message.find(insertName); + if (insertPos != std::string::npos) { + std::string insertString(""); + const ValueType &insert = insertsItr->value; + if (insert.IsArray()) { + // Member is an array so create comma-separated list of items for the insert string + for (ValueType::ConstValueIterator itemsItr = insert.Begin(); itemsItr != insert.End(); ++itemsItr) { + if (itemsItr != insert.Begin()) insertString += ","; + insertString += GetString(*itemsItr); + } + } else { + insertString += GetString(insert); + } + message.replace(insertPos, insertName.length(), insertString); + } + } + // Output error message, references, context + std::string indent(depth * 2, ' '); + std::cout << indent << "Error Name: " << errorName << std::endl; + std::cout << indent << "Message: " << message.c_str() << std::endl; + std::cout << indent << "Instance: " << error["instanceRef"].GetString() << std::endl; + std::cout << indent << "Schema: " << error["schemaRef"].GetString() << std::endl; + if (depth > 0) std::cout << indent << "Context: " << context << std::endl; + std::cout << std::endl; + + // If child errors exist, apply the process recursively to each error structure. + // This occurs for "oneOf", "allOf", "anyOf" and "dependencies" errors, so pass the error name as context. + if (error.HasMember("errors")) { + depth++; + const ValueType &childErrors = error["errors"]; + if (childErrors.IsArray()) { + // Array - each item is an error structure - example + // "anyOf": {"errorCode": ..., "errors":[{"pattern": {"errorCode\": ...\"}}, {"pattern": {"errorCode\": ...}}] + for (ValueType::ConstValueIterator errorsItr = childErrors.Begin(); + errorsItr != childErrors.End(); ++errorsItr) { + CreateErrorMessages(*errorsItr, depth, errorName); + } + } else if (childErrors.IsObject()) { + // Object - each member is an error structure - example + // "dependencies": {"errorCode": ..., "errors": {"address": {"required": {"errorCode": ...}}, "name": {"required": {"errorCode": ...}}} + for (ValueType::ConstMemberIterator propsItr = childErrors.MemberBegin(); + propsItr != childErrors.MemberEnd(); ++propsItr) { + CreateErrorMessages(propsItr->value, depth, errorName); + } + } + } + } +} + +// Create error message for all errors in an error structure +// Context is used to indicate whether the error structure has a parent 'dependencies', 'allOf', 'anyOf' or 'oneOf' error +static void CreateErrorMessages(const ValueType& errors, size_t depth = 0, const char* context = 0) { + // Each member property contains one or more errors of a given type + for (ValueType::ConstMemberIterator errorTypeItr = errors.MemberBegin(); errorTypeItr != errors.MemberEnd(); ++errorTypeItr) { + const char* errorName = errorTypeItr->name.GetString(); + const ValueType& errorContent = errorTypeItr->value; + if (errorContent.IsArray()) { + // Member is an array where each item is an error - eg "type": [{"errorCode": ...}, {"errorCode": ...}] + for (ValueType::ConstValueIterator contentItr = errorContent.Begin(); contentItr != errorContent.End(); ++contentItr) { + HandleError(errorName, *contentItr, depth, context); + } + } else if (errorContent.IsObject()) { + // Member is an object which is a single error - eg "type": {"errorCode": ... } + HandleError(errorName, errorContent, depth, context); + } + } +} + int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "Usage: schemavalidator schema.json < input.json\n"); @@ -64,9 +183,17 @@ int main(int argc, char *argv[]) { validator.GetInvalidSchemaPointer().StringifyUriFragment(sb); fprintf(stderr, "Invalid schema: %s\n", sb.GetString()); fprintf(stderr, "Invalid keyword: %s\n", validator.GetInvalidSchemaKeyword()); + fprintf(stderr, "Invalid code: %d\n", validator.GetInvalidSchemaCode()); + fprintf(stderr, "Invalid message: %s\n", GetValidateError_En(validator.GetInvalidSchemaCode())); sb.Clear(); validator.GetInvalidDocumentPointer().StringifyUriFragment(sb); fprintf(stderr, "Invalid document: %s\n", sb.GetString()); + // Detailed violation report is available as a JSON value + sb.Clear(); + PrettyWriter w(sb); + validator.GetError().Accept(w); + fprintf(stderr, "Error report:\n%s\n", sb.GetString()); + CreateErrorMessages(validator.GetError()); return EXIT_FAILURE; } } diff --git a/third_party/rapidjson/example/simplepullreader/simplepullreader.cpp b/third_party/rapidjson/example/simplepullreader/simplepullreader.cpp new file mode 100644 index 000000000..a4fb1161a --- /dev/null +++ b/third_party/rapidjson/example/simplepullreader/simplepullreader.cpp @@ -0,0 +1,53 @@ +#include "rapidjson/reader.h" +#include +#include + +using namespace rapidjson; +using namespace std; + +// If you can require C++11, you could use std::to_string here +template std::string stringify(T x) { + std::stringstream ss; + ss << x; + return ss.str(); +} + +struct MyHandler { + const char* type; + std::string data; + + MyHandler() : type(), data() {} + + bool Null() { type = "Null"; data.clear(); return true; } + bool Bool(bool b) { type = "Bool:"; data = b? "true": "false"; return true; } + bool Int(int i) { type = "Int:"; data = stringify(i); return true; } + bool Uint(unsigned u) { type = "Uint:"; data = stringify(u); return true; } + bool Int64(int64_t i) { type = "Int64:"; data = stringify(i); return true; } + bool Uint64(uint64_t u) { type = "Uint64:"; data = stringify(u); return true; } + bool Double(double d) { type = "Double:"; data = stringify(d); return true; } + bool RawNumber(const char* str, SizeType length, bool) { type = "Number:"; data = std::string(str, length); return true; } + bool String(const char* str, SizeType length, bool) { type = "String:"; data = std::string(str, length); return true; } + bool StartObject() { type = "StartObject"; data.clear(); return true; } + bool Key(const char* str, SizeType length, bool) { type = "Key:"; data = std::string(str, length); return true; } + bool EndObject(SizeType memberCount) { type = "EndObject:"; data = stringify(memberCount); return true; } + bool StartArray() { type = "StartArray"; data.clear(); return true; } + bool EndArray(SizeType elementCount) { type = "EndArray:"; data = stringify(elementCount); return true; } +private: + MyHandler(const MyHandler& noCopyConstruction); + MyHandler& operator=(const MyHandler& noAssignment); +}; + +int main() { + const char json[] = " { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } "; + + MyHandler handler; + Reader reader; + StringStream ss(json); + reader.IterativeParseInit(); + while (!reader.IterativeParseComplete()) { + reader.IterativeParseNext(ss, handler); + cout << handler.type << handler.data << endl; + } + + return 0; +} diff --git a/third_party/rapidjson/example/sortkeys/sortkeys.cpp b/third_party/rapidjson/example/sortkeys/sortkeys.cpp new file mode 100644 index 000000000..7ede9fb93 --- /dev/null +++ b/third_party/rapidjson/example/sortkeys/sortkeys.cpp @@ -0,0 +1,62 @@ +#include "rapidjson/document.h" +#include "rapidjson/filewritestream.h" +#include + +#include +#include + +using namespace rapidjson; +using namespace std; + +static void printIt(const Value &doc) { + char writeBuffer[65536]; + FileWriteStream os(stdout, writeBuffer, sizeof(writeBuffer)); + PrettyWriter writer(os); + doc.Accept(writer); + cout << endl; +} + +struct NameComparator { + bool operator()(const Value::Member &lhs, const Value::Member &rhs) const { + return (strcmp(lhs.name.GetString(), rhs.name.GetString()) < 0); + } +}; + +int main() { + Document d(kObjectType); + Document::AllocatorType &allocator = d.GetAllocator(); + + d.AddMember("zeta", Value().SetBool(false), allocator); + d.AddMember("gama", Value().SetString("test string", allocator), allocator); + d.AddMember("delta", Value().SetInt(123), allocator); + d.AddMember("alpha", Value(kArrayType).Move(), allocator); + + printIt(d); + +/* +{ + "zeta": false, + "gama": "test string", + "delta": 123, + "alpha": [] +} +*/ + +// C++11 supports std::move() of Value so it always have no problem for std::sort(). +// Some C++03 implementations of std::sort() requires copy constructor which causes compilation error. +// Needs a sorting function only depends on std::swap() instead. +#if __cplusplus >= 201103L || (!defined(__GLIBCXX__) && (!defined(_MSC_VER) || _MSC_VER >= 1900)) + std::sort(d.MemberBegin(), d.MemberEnd(), NameComparator()); + + printIt(d); + +/* +{ + "alpha": [], + "delta": 123, + "gama": "test string", + "zeta": false +} +*/ +#endif +} diff --git a/third_party/rapidjson/example/traverseaspointer.cpp b/third_party/rapidjson/example/traverseaspointer.cpp new file mode 100644 index 000000000..7e0c89923 --- /dev/null +++ b/third_party/rapidjson/example/traverseaspointer.cpp @@ -0,0 +1,39 @@ +#include "rapidjson/document.h" +#include "rapidjson/filereadstream.h" +#include "rapidjson/pointer.h" +#include "rapidjson/stringbuffer.h" +#include + +using namespace rapidjson; + +void traverse(const Value& v, const Pointer& p) { + StringBuffer sb; + p.Stringify(sb); + std::cout << sb.GetString() << std::endl; + + switch (v.GetType()) { + case kArrayType: + for (SizeType i = 0; i != v.Size(); ++i) + traverse(v[i], p.Append(i)); + break; + case kObjectType: + for (Value::ConstMemberIterator m = v.MemberBegin(); m != v.MemberEnd(); ++m) + traverse(m->value, p.Append(m->name.GetString(), m->name.GetStringLength())); + break; + default: + break; + } +} + +int main(int, char*[]) { + char readBuffer[65536]; + FileReadStream is(stdin, readBuffer, sizeof(readBuffer)); + + Document d; + d.ParseStream(is); + + Pointer root; + traverse(d, root); + + return 0; +} diff --git a/third_party/rapidjson/example/tutorial/tutorial.cpp b/third_party/rapidjson/example/tutorial/tutorial.cpp index c8bfcc14c..d6021c668 100644 --- a/third_party/rapidjson/example/tutorial/tutorial.cpp +++ b/third_party/rapidjson/example/tutorial/tutorial.cpp @@ -57,7 +57,7 @@ int main(int, char*[]) { printf("n = %s\n", document["n"].IsNull() ? "null" : "?"); assert(document["i"].IsNumber()); // Number is a JSON type, but C++ needs more specific type. - assert(document["i"].IsInt()); // In this case, IsUint()/IsInt64()/IsUInt64() also return true. + assert(document["i"].IsInt()); // In this case, IsUint()/IsInt64()/IsUint64() also return true. printf("i = %d\n", document["i"].GetInt()); // Alternative (int)document["i"] assert(document["pi"].IsNumber()); diff --git a/third_party/rapidjson/include/rapidjson/allocators.h b/third_party/rapidjson/include/rapidjson/allocators.h index 98affe03f..275417bd8 100644 --- a/third_party/rapidjson/include/rapidjson/allocators.h +++ b/third_party/rapidjson/include/rapidjson/allocators.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -16,6 +16,14 @@ #define RAPIDJSON_ALLOCATORS_H_ #include "rapidjson.h" +#include "internal/meta.h" + +#include +#include + +#if RAPIDJSON_HAS_CXX11 +#include +#endif RAPIDJSON_NAMESPACE_BEGIN @@ -52,6 +60,19 @@ concept Allocator { \endcode */ + +/*! \def RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User-defined kDefaultChunkCapacity definition. + + User can define this as any \c size that is a power of 2. +*/ + +#ifndef RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY +#define RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY (64 * 1024) +#endif + + /////////////////////////////////////////////////////////////////////////////// // CrtAllocator @@ -64,19 +85,26 @@ public: static const bool kNeedFree = true; void* Malloc(size_t size) { if (size) // behavior of malloc(0) is implementation defined. - return std::malloc(size); + return RAPIDJSON_MALLOC(size); else return NULL; // standardize to returning NULL. } void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { (void)originalSize; if (newSize == 0) { - std::free(originalPtr); + RAPIDJSON_FREE(originalPtr); return NULL; } - return std::realloc(originalPtr, newSize); + return RAPIDJSON_REALLOC(originalPtr, newSize); + } + static void Free(void *ptr) RAPIDJSON_NOEXCEPT { RAPIDJSON_FREE(ptr); } + + bool operator==(const CrtAllocator&) const RAPIDJSON_NOEXCEPT { + return true; + } + bool operator!=(const CrtAllocator&) const RAPIDJSON_NOEXCEPT { + return false; } - static void Free(void *ptr) { std::free(ptr); } }; /////////////////////////////////////////////////////////////////////////////// @@ -100,16 +128,64 @@ public: */ template class MemoryPoolAllocator { + //! Chunk header for perpending to each chunk. + /*! Chunks are stored as a singly linked list. + */ + struct ChunkHeader { + size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself). + size_t size; //!< Current size of allocated memory in bytes. + ChunkHeader *next; //!< Next chunk in the linked list. + }; + + struct SharedData { + ChunkHeader *chunkHead; //!< Head of the chunk linked-list. Only the head chunk serves allocation. + BaseAllocator* ownBaseAllocator; //!< base allocator created by this object. + size_t refcount; + bool ownBuffer; + }; + + static const size_t SIZEOF_SHARED_DATA = RAPIDJSON_ALIGN(sizeof(SharedData)); + static const size_t SIZEOF_CHUNK_HEADER = RAPIDJSON_ALIGN(sizeof(ChunkHeader)); + + static inline ChunkHeader *GetChunkHead(SharedData *shared) + { + return reinterpret_cast(reinterpret_cast(shared) + SIZEOF_SHARED_DATA); + } + static inline uint8_t *GetChunkBuffer(SharedData *shared) + { + return reinterpret_cast(shared->chunkHead) + SIZEOF_CHUNK_HEADER; + } + + static const size_t kDefaultChunkCapacity = RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY; //!< Default chunk capacity. + public: static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator) + static const bool kRefCounted = true; //!< Tell users that this allocator is reference counted on copy //! Constructor with chunkSize. /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. \param baseAllocator The allocator for allocating memory chunks. */ + explicit MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : - chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0) + chunk_capacity_(chunkSize), + baseAllocator_(baseAllocator ? baseAllocator : RAPIDJSON_NEW(BaseAllocator)()), + shared_(static_cast(baseAllocator_ ? baseAllocator_->Malloc(SIZEOF_SHARED_DATA + SIZEOF_CHUNK_HEADER) : 0)) { + RAPIDJSON_ASSERT(baseAllocator_ != 0); + RAPIDJSON_ASSERT(shared_ != 0); + if (baseAllocator) { + shared_->ownBaseAllocator = 0; + } + else { + shared_->ownBaseAllocator = baseAllocator_; + } + shared_->chunkHead = GetChunkHead(shared_); + shared_->chunkHead->capacity = 0; + shared_->chunkHead->size = 0; + shared_->chunkHead->next = 0; + shared_->ownBuffer = true; + shared_->refcount = 1; } //! Constructor with user-supplied buffer. @@ -123,41 +199,101 @@ public: \param baseAllocator The allocator for allocating memory chunks. */ MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : - chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0) + chunk_capacity_(chunkSize), + baseAllocator_(baseAllocator), + shared_(static_cast(AlignBuffer(buffer, size))) { - RAPIDJSON_ASSERT(buffer != 0); - RAPIDJSON_ASSERT(size > sizeof(ChunkHeader)); - chunkHead_ = reinterpret_cast(buffer); - chunkHead_->capacity = size - sizeof(ChunkHeader); - chunkHead_->size = 0; - chunkHead_->next = 0; + RAPIDJSON_ASSERT(size >= SIZEOF_SHARED_DATA + SIZEOF_CHUNK_HEADER); + shared_->chunkHead = GetChunkHead(shared_); + shared_->chunkHead->capacity = size - SIZEOF_SHARED_DATA - SIZEOF_CHUNK_HEADER; + shared_->chunkHead->size = 0; + shared_->chunkHead->next = 0; + shared_->ownBaseAllocator = 0; + shared_->ownBuffer = false; + shared_->refcount = 1; } + MemoryPoolAllocator(const MemoryPoolAllocator& rhs) RAPIDJSON_NOEXCEPT : + chunk_capacity_(rhs.chunk_capacity_), + baseAllocator_(rhs.baseAllocator_), + shared_(rhs.shared_) + { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + ++shared_->refcount; + } + MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) RAPIDJSON_NOEXCEPT + { + RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0); + ++rhs.shared_->refcount; + this->~MemoryPoolAllocator(); + baseAllocator_ = rhs.baseAllocator_; + chunk_capacity_ = rhs.chunk_capacity_; + shared_ = rhs.shared_; + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + MemoryPoolAllocator(MemoryPoolAllocator&& rhs) RAPIDJSON_NOEXCEPT : + chunk_capacity_(rhs.chunk_capacity_), + baseAllocator_(rhs.baseAllocator_), + shared_(rhs.shared_) + { + RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0); + rhs.shared_ = 0; + } + MemoryPoolAllocator& operator=(MemoryPoolAllocator&& rhs) RAPIDJSON_NOEXCEPT + { + RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0); + this->~MemoryPoolAllocator(); + baseAllocator_ = rhs.baseAllocator_; + chunk_capacity_ = rhs.chunk_capacity_; + shared_ = rhs.shared_; + rhs.shared_ = 0; + return *this; + } +#endif + //! Destructor. /*! This deallocates all memory chunks, excluding the user-supplied buffer. */ - ~MemoryPoolAllocator() { + ~MemoryPoolAllocator() RAPIDJSON_NOEXCEPT { + if (!shared_) { + // do nothing if moved + return; + } + if (shared_->refcount > 1) { + --shared_->refcount; + return; + } Clear(); - RAPIDJSON_DELETE(ownBaseAllocator_); + BaseAllocator *a = shared_->ownBaseAllocator; + if (shared_->ownBuffer) { + baseAllocator_->Free(shared_); + } + RAPIDJSON_DELETE(a); } - //! Deallocates all memory chunks, excluding the user-supplied buffer. - void Clear() { - while (chunkHead_ && chunkHead_ != userBuffer_) { - ChunkHeader* next = chunkHead_->next; - baseAllocator_->Free(chunkHead_); - chunkHead_ = next; + //! Deallocates all memory chunks, excluding the first/user one. + void Clear() RAPIDJSON_NOEXCEPT { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + for (;;) { + ChunkHeader* c = shared_->chunkHead; + if (!c->next) { + break; + } + shared_->chunkHead = c->next; + baseAllocator_->Free(c); } - if (chunkHead_ && chunkHead_ == userBuffer_) - chunkHead_->size = 0; // Clear user buffer + shared_->chunkHead->size = 0; } //! Computes the total capacity of allocated memory chunks. /*! \return total capacity in bytes. */ - size_t Capacity() const { + size_t Capacity() const RAPIDJSON_NOEXCEPT { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); size_t capacity = 0; - for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) + for (ChunkHeader* c = shared_->chunkHead; c != 0; c = c->next) capacity += c->capacity; return capacity; } @@ -165,25 +301,35 @@ public: //! Computes the memory blocks allocated. /*! \return total used bytes. */ - size_t Size() const { + size_t Size() const RAPIDJSON_NOEXCEPT { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); size_t size = 0; - for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) + for (ChunkHeader* c = shared_->chunkHead; c != 0; c = c->next) size += c->size; return size; } + //! Whether the allocator is shared. + /*! \return true or false. + */ + bool Shared() const RAPIDJSON_NOEXCEPT { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + return shared_->refcount > 1; + } + //! Allocates a memory block. (concept Allocator) void* Malloc(size_t size) { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); if (!size) return NULL; size = RAPIDJSON_ALIGN(size); - if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity) + if (RAPIDJSON_UNLIKELY(shared_->chunkHead->size + size > shared_->chunkHead->capacity)) if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size)) return NULL; - void *buffer = reinterpret_cast(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size; - chunkHead_->size += size; + void *buffer = GetChunkBuffer(shared_) + shared_->chunkHead->size; + shared_->chunkHead->size += size; return buffer; } @@ -192,6 +338,7 @@ public: if (originalPtr == 0) return Malloc(newSize); + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); if (newSize == 0) return NULL; @@ -203,10 +350,10 @@ public: return originalPtr; // Simply expand it if it is the last allocation and there is sufficient space - if (originalPtr == reinterpret_cast(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) { + if (originalPtr == GetChunkBuffer(shared_) + shared_->chunkHead->size - originalSize) { size_t increment = static_cast(newSize - originalSize); - if (chunkHead_->size + increment <= chunkHead_->capacity) { - chunkHead_->size += increment; + if (shared_->chunkHead->size + increment <= shared_->chunkHead->capacity) { + shared_->chunkHead->size += increment; return originalPtr; } } @@ -222,50 +369,325 @@ public: } //! Frees a memory block (concept Allocator) - static void Free(void *ptr) { (void)ptr; } // Do nothing + static void Free(void *ptr) RAPIDJSON_NOEXCEPT { (void)ptr; } // Do nothing + + //! Compare (equality) with another MemoryPoolAllocator + bool operator==(const MemoryPoolAllocator& rhs) const RAPIDJSON_NOEXCEPT { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0); + return shared_ == rhs.shared_; + } + //! Compare (inequality) with another MemoryPoolAllocator + bool operator!=(const MemoryPoolAllocator& rhs) const RAPIDJSON_NOEXCEPT { + return !operator==(rhs); + } private: - //! Copy constructor is not permitted. - MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */; - //! Copy assignment operator is not permitted. - MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */; - //! Creates a new chunk. /*! \param capacity Capacity of the chunk in bytes. \return true if success. */ bool AddChunk(size_t capacity) { if (!baseAllocator_) - ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator()); - if (ChunkHeader* chunk = reinterpret_cast(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) { + shared_->ownBaseAllocator = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)(); + if (ChunkHeader* chunk = static_cast(baseAllocator_->Malloc(SIZEOF_CHUNK_HEADER + capacity))) { chunk->capacity = capacity; chunk->size = 0; - chunk->next = chunkHead_; - chunkHead_ = chunk; + chunk->next = shared_->chunkHead; + shared_->chunkHead = chunk; return true; } else return false; } - static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity. + static inline void* AlignBuffer(void* buf, size_t &size) + { + RAPIDJSON_NOEXCEPT_ASSERT(buf != 0); + const uintptr_t mask = sizeof(void*) - 1; + const uintptr_t ubuf = reinterpret_cast(buf); + if (RAPIDJSON_UNLIKELY(ubuf & mask)) { + const uintptr_t abuf = (ubuf + mask) & ~mask; + RAPIDJSON_ASSERT(size >= abuf - ubuf); + buf = reinterpret_cast(abuf); + size -= abuf - ubuf; + } + return buf; + } - //! Chunk header for perpending to each chunk. - /*! Chunks are stored as a singly linked list. - */ - struct ChunkHeader { - size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself). - size_t size; //!< Current size of allocated memory in bytes. - ChunkHeader *next; //!< Next chunk in the linked list. + size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated. + BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks. + SharedData *shared_; //!< The shared data of the allocator +}; + +namespace internal { + template + struct IsRefCounted : + public FalseType + { }; + template + struct IsRefCounted::Type> : + public TrueType + { }; +} + +template +inline T* Realloc(A& a, T* old_p, size_t old_n, size_t new_n) +{ + RAPIDJSON_NOEXCEPT_ASSERT(old_n <= (std::numeric_limits::max)() / sizeof(T) && new_n <= (std::numeric_limits::max)() / sizeof(T)); + return static_cast(a.Realloc(old_p, old_n * sizeof(T), new_n * sizeof(T))); +} + +template +inline T *Malloc(A& a, size_t n = 1) +{ + return Realloc(a, NULL, 0, n); +} + +template +inline void Free(A& a, T *p, size_t n = 1) +{ + static_cast(Realloc(a, p, n, 0)); +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) // std::allocator can safely be inherited +#endif + +template +class StdAllocator : + public std::allocator +{ + typedef std::allocator allocator_type; +#if RAPIDJSON_HAS_CXX11 + typedef std::allocator_traits traits_type; +#else + typedef allocator_type traits_type; +#endif + +public: + typedef BaseAllocator BaseAllocatorType; + + StdAllocator() RAPIDJSON_NOEXCEPT : + allocator_type(), + baseAllocator_() + { } + + StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT : + allocator_type(rhs), + baseAllocator_(rhs.baseAllocator_) + { } + + template + StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT : + allocator_type(rhs), + baseAllocator_(rhs.baseAllocator_) + { } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + StdAllocator(StdAllocator&& rhs) RAPIDJSON_NOEXCEPT : + allocator_type(std::move(rhs)), + baseAllocator_(std::move(rhs.baseAllocator_)) + { } +#endif +#if RAPIDJSON_HAS_CXX11 + using propagate_on_container_move_assignment = std::true_type; + using propagate_on_container_swap = std::true_type; +#endif + + /* implicit */ + StdAllocator(const BaseAllocator& baseAllocator) RAPIDJSON_NOEXCEPT : + allocator_type(), + baseAllocator_(baseAllocator) + { } + + ~StdAllocator() RAPIDJSON_NOEXCEPT + { } + + template + struct rebind { + typedef StdAllocator other; }; - ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation. - size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated. - void *userBuffer_; //!< User supplied buffer. - BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks. - BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object. + typedef typename traits_type::size_type size_type; + typedef typename traits_type::difference_type difference_type; + + typedef typename traits_type::value_type value_type; + typedef typename traits_type::pointer pointer; + typedef typename traits_type::const_pointer const_pointer; + +#if RAPIDJSON_HAS_CXX11 + + typedef typename std::add_lvalue_reference::type &reference; + typedef typename std::add_lvalue_reference::type>::type &const_reference; + + pointer address(reference r) const RAPIDJSON_NOEXCEPT + { + return std::addressof(r); + } + const_pointer address(const_reference r) const RAPIDJSON_NOEXCEPT + { + return std::addressof(r); + } + + size_type max_size() const RAPIDJSON_NOEXCEPT + { + return traits_type::max_size(*this); + } + + template + void construct(pointer p, Args&&... args) + { + traits_type::construct(*this, p, std::forward(args)...); + } + void destroy(pointer p) + { + traits_type::destroy(*this, p); + } + +#else // !RAPIDJSON_HAS_CXX11 + + typedef typename allocator_type::reference reference; + typedef typename allocator_type::const_reference const_reference; + + pointer address(reference r) const RAPIDJSON_NOEXCEPT + { + return allocator_type::address(r); + } + const_pointer address(const_reference r) const RAPIDJSON_NOEXCEPT + { + return allocator_type::address(r); + } + + size_type max_size() const RAPIDJSON_NOEXCEPT + { + return allocator_type::max_size(); + } + + void construct(pointer p, const_reference r) + { + allocator_type::construct(p, r); + } + void destroy(pointer p) + { + allocator_type::destroy(p); + } + +#endif // !RAPIDJSON_HAS_CXX11 + + template + U* allocate(size_type n = 1, const void* = 0) + { + return RAPIDJSON_NAMESPACE::Malloc(baseAllocator_, n); + } + template + void deallocate(U* p, size_type n = 1) + { + RAPIDJSON_NAMESPACE::Free(baseAllocator_, p, n); + } + + pointer allocate(size_type n = 1, const void* = 0) + { + return allocate(n); + } + void deallocate(pointer p, size_type n = 1) + { + deallocate(p, n); + } + +#if RAPIDJSON_HAS_CXX11 + using is_always_equal = std::is_empty; +#endif + + template + bool operator==(const StdAllocator& rhs) const RAPIDJSON_NOEXCEPT + { + return baseAllocator_ == rhs.baseAllocator_; + } + template + bool operator!=(const StdAllocator& rhs) const RAPIDJSON_NOEXCEPT + { + return !operator==(rhs); + } + + //! rapidjson Allocator concept + static const bool kNeedFree = BaseAllocator::kNeedFree; + static const bool kRefCounted = internal::IsRefCounted::Value; + void* Malloc(size_t size) + { + return baseAllocator_.Malloc(size); + } + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) + { + return baseAllocator_.Realloc(originalPtr, originalSize, newSize); + } + static void Free(void *ptr) RAPIDJSON_NOEXCEPT + { + BaseAllocator::Free(ptr); + } + +private: + template + friend class StdAllocator; // access to StdAllocator.* + + BaseAllocator baseAllocator_; }; +#if !RAPIDJSON_HAS_CXX17 // std::allocator deprecated in C++17 +template +class StdAllocator : + public std::allocator +{ + typedef std::allocator allocator_type; + +public: + typedef BaseAllocator BaseAllocatorType; + + StdAllocator() RAPIDJSON_NOEXCEPT : + allocator_type(), + baseAllocator_() + { } + + StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT : + allocator_type(rhs), + baseAllocator_(rhs.baseAllocator_) + { } + + template + StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT : + allocator_type(rhs), + baseAllocator_(rhs.baseAllocator_) + { } + + /* implicit */ + StdAllocator(const BaseAllocator& baseAllocator) RAPIDJSON_NOEXCEPT : + allocator_type(), + baseAllocator_(baseAllocator) + { } + + ~StdAllocator() RAPIDJSON_NOEXCEPT + { } + + template + struct rebind { + typedef StdAllocator other; + }; + + typedef typename allocator_type::value_type value_type; + +private: + template + friend class StdAllocator; // access to StdAllocator.* + + BaseAllocator baseAllocator_; +}; +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + RAPIDJSON_NAMESPACE_END #endif // RAPIDJSON_ENCODINGS_H_ diff --git a/third_party/rapidjson/include/rapidjson/cursorstreamwrapper.h b/third_party/rapidjson/include/rapidjson/cursorstreamwrapper.h new file mode 100644 index 000000000..fd6513db1 --- /dev/null +++ b/third_party/rapidjson/include/rapidjson/cursorstreamwrapper.h @@ -0,0 +1,78 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_CURSORSTREAMWRAPPER_H_ +#define RAPIDJSON_CURSORSTREAMWRAPPER_H_ + +#include "stream.h" + +#if defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4702) // unreachable code +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + + +//! Cursor stream wrapper for counting line and column number if error exists. +/*! + \tparam InputStream Any stream that implements Stream Concept +*/ +template > +class CursorStreamWrapper : public GenericStreamWrapper { +public: + typedef typename Encoding::Ch Ch; + + CursorStreamWrapper(InputStream& is): + GenericStreamWrapper(is), line_(1), col_(0) {} + + // counting line and column number + Ch Take() { + Ch ch = this->is_.Take(); + if(ch == '\n') { + line_ ++; + col_ = 0; + } else { + col_ ++; + } + return ch; + } + + //! Get the error line number, if error exists. + size_t GetLine() const { return line_; } + //! Get the error column number, if error exists. + size_t GetColumn() const { return col_; } + +private: + size_t line_; //!< Current Line + size_t col_; //!< Current Column +}; + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_POP +#endif + +#if defined(__GNUC__) +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_CURSORSTREAMWRAPPER_H_ diff --git a/third_party/rapidjson/include/rapidjson/document.h b/third_party/rapidjson/include/rapidjson/document.h index e3e20dfbd..2cd9a70a6 100644 --- a/third_party/rapidjson/include/rapidjson/document.h +++ b/third_party/rapidjson/include/rapidjson/document.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -24,32 +24,39 @@ #include "encodedstream.h" #include // placement new #include - -RAPIDJSON_DIAG_PUSH -#ifdef _MSC_VER -RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant -RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data +#ifdef __cpp_lib_three_way_comparison +#include #endif +RAPIDJSON_DIAG_PUSH #ifdef __clang__ RAPIDJSON_DIAG_OFF(padded) RAPIDJSON_DIAG_OFF(switch-enum) RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data #endif #ifdef __GNUC__ RAPIDJSON_DIAG_OFF(effc++) -#if __GNUC__ >= 6 -RAPIDJSON_DIAG_OFF(terminate) // ignore throwing RAPIDJSON_ASSERT in RAPIDJSON_NOEXCEPT functions -#endif #endif // __GNUC__ -#ifndef RAPIDJSON_NOMEMBERITERATORCLASS -#include // std::iterator, std::random_access_iterator_tag +#ifdef GetObject +// see https://github.com/Tencent/rapidjson/issues/1448 +// a former included windows.h might have defined a macro called GetObject, which affects +// GetObject defined here. This ensures the macro does not get applied +#pragma push_macro("GetObject") +#define RAPIDJSON_WINDOWS_GETOBJECT_WORKAROUND_APPLIED +#undef GetObject #endif -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS -#include // std::move +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS +#include // std::random_access_iterator_tag +#endif + +#if RAPIDJSON_USE_MEMBERSMAP +#include // std::multimap #endif RAPIDJSON_NAMESPACE_BEGIN @@ -61,6 +68,48 @@ class GenericValue; template class GenericDocument; +/*! \def RAPIDJSON_DEFAULT_ALLOCATOR + \ingroup RAPIDJSON_CONFIG + \brief Allows to choose default allocator. + + User can define this to use CrtAllocator or MemoryPoolAllocator. +*/ +#ifndef RAPIDJSON_DEFAULT_ALLOCATOR +#define RAPIDJSON_DEFAULT_ALLOCATOR ::RAPIDJSON_NAMESPACE::MemoryPoolAllocator<::RAPIDJSON_NAMESPACE::CrtAllocator> +#endif + +/*! \def RAPIDJSON_DEFAULT_STACK_ALLOCATOR + \ingroup RAPIDJSON_CONFIG + \brief Allows to choose default stack allocator for Document. + + User can define this to use CrtAllocator or MemoryPoolAllocator. +*/ +#ifndef RAPIDJSON_DEFAULT_STACK_ALLOCATOR +#define RAPIDJSON_DEFAULT_STACK_ALLOCATOR ::RAPIDJSON_NAMESPACE::CrtAllocator +#endif + +/*! \def RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User defined kDefaultObjectCapacity value. + + User can define this as any natural number. +*/ +#ifndef RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY +// number of objects that rapidjson::Value allocates memory for by default +#define RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY 16 +#endif + +/*! \def RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User defined kDefaultArrayCapacity value. + + User can define this as any natural number. +*/ +#ifndef RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY +// number of array elements that rapidjson::Value allocates memory for by default +#define RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY 16 +#endif + //! Name-value pair in a JSON object value. /*! This class was internal to GenericValue. It used to be a inner struct. @@ -68,9 +117,45 @@ class GenericDocument; https://code.google.com/p/rapidjson/issues/detail?id=64 */ template -struct GenericMember { +class GenericMember { +public: GenericValue name; //!< name of member (must be a string) GenericValue value; //!< value of member. + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericMember(GenericMember&& rhs) RAPIDJSON_NOEXCEPT + : name(std::move(rhs.name)), + value(std::move(rhs.value)) + { + } + + //! Move assignment in C++11 + GenericMember& operator=(GenericMember&& rhs) RAPIDJSON_NOEXCEPT { + return *this = static_cast(rhs); + } +#endif + + //! Assignment with move semantics. + /*! \param rhs Source of the assignment. Its name and value will become a null value after assignment. + */ + GenericMember& operator=(GenericMember& rhs) RAPIDJSON_NOEXCEPT { + if (RAPIDJSON_LIKELY(this != &rhs)) { + name = rhs.name; + value = rhs.value; + } + return *this; + } + + // swap() for std::sort() and other potential use in STL. + friend inline void swap(GenericMember& a, GenericMember& b) RAPIDJSON_NOEXCEPT { + a.name.Swap(b.name); + a.value.Swap(b.value); + } + +private: + //! Copy constructor is not permitted. + GenericMember(const GenericMember& rhs); }; /////////////////////////////////////////////////////////////////////////////// @@ -98,16 +183,13 @@ struct GenericMember { \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator */ template -class GenericMemberIterator - : public std::iterator >::Type> { +class GenericMemberIterator { friend class GenericValue; template friend class GenericMemberIterator; typedef GenericMember PlainType; typedef typename internal::MaybeAddConst::Type ValueType; - typedef std::iterator BaseType; public: //! Iterator type itself @@ -117,12 +199,21 @@ public: //! Non-constant iterator type typedef GenericMemberIterator NonConstIterator; + /** \name std::iterator_traits support */ + //@{ + typedef ValueType value_type; + typedef ValueType * pointer; + typedef ValueType & reference; + typedef std::ptrdiff_t difference_type; + typedef std::random_access_iterator_tag iterator_category; + //@} + //! Pointer to (const) GenericMember - typedef typename BaseType::pointer Pointer; + typedef pointer Pointer; //! Reference to (const) GenericMember - typedef typename BaseType::reference Reference; + typedef reference Reference; //! Signed integer type (e.g. \c ptrdiff_t) - typedef typename BaseType::difference_type DifferenceType; + typedef difference_type DifferenceType; //! Default constructor (singular value) /*! Creates an iterator pointing to no element. @@ -168,12 +259,16 @@ public: //! @name relations //@{ - bool operator==(ConstIterator that) const { return ptr_ == that.ptr_; } - bool operator!=(ConstIterator that) const { return ptr_ != that.ptr_; } - bool operator<=(ConstIterator that) const { return ptr_ <= that.ptr_; } - bool operator>=(ConstIterator that) const { return ptr_ >= that.ptr_; } - bool operator< (ConstIterator that) const { return ptr_ < that.ptr_; } - bool operator> (ConstIterator that) const { return ptr_ > that.ptr_; } + template bool operator==(const GenericMemberIterator& that) const { return ptr_ == that.ptr_; } + template bool operator!=(const GenericMemberIterator& that) const { return ptr_ != that.ptr_; } + template bool operator<=(const GenericMemberIterator& that) const { return ptr_ <= that.ptr_; } + template bool operator>=(const GenericMemberIterator& that) const { return ptr_ >= that.ptr_; } + template bool operator< (const GenericMemberIterator& that) const { return ptr_ < that.ptr_; } + template bool operator> (const GenericMemberIterator& that) const { return ptr_ > that.ptr_; } + +#ifdef __cpp_lib_three_way_comparison + template std::strong_ordering operator<=>(const GenericMemberIterator& that) const { return ptr_ <=> that.ptr_; } +#endif //@} //! @name dereference @@ -198,17 +293,19 @@ private: // class-based member iterator implementation disabled, use plain pointers template -struct GenericMemberIterator; +class GenericMemberIterator; //! non-const GenericMemberIterator template -struct GenericMemberIterator { +class GenericMemberIterator { +public: //! use plain pointer as iterator type typedef GenericMember* Iterator; }; //! const GenericMemberIterator template -struct GenericMemberIterator { +class GenericMemberIterator { +public: //! use plain const pointer as iterator type typedef const GenericMember* Iterator; }; @@ -300,7 +397,7 @@ struct GenericStringRef { */ #endif explicit GenericStringRef(const CharType* str) - : s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != 0); } + : s(str), length(NotNullStrLen(str)) {} //! Create constant string reference from pointer and length #ifndef __clang__ // -Wdocumentation @@ -312,12 +409,10 @@ struct GenericStringRef { */ #endif GenericStringRef(const CharType* str, SizeType len) - : s(str), length(len) { RAPIDJSON_ASSERT(s != 0); } + : s(RAPIDJSON_LIKELY(str) ? str : emptyString), length(len) { RAPIDJSON_ASSERT(str != 0 || len == 0u); } GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {} - GenericStringRef& operator=(const GenericStringRef& rhs) { s = rhs.s; length = rhs.length; } - //! implicit conversion to plain CharType pointer operator const Ch *() const { return s; } @@ -325,11 +420,24 @@ struct GenericStringRef { const SizeType length; //!< length of the string (excluding the trailing NULL terminator) private: + SizeType NotNullStrLen(const CharType* str) { + RAPIDJSON_ASSERT(str != 0); + return internal::StrLen(str); + } + + /// Empty string - used when passing in a NULL pointer + static const Ch emptyString[]; + //! Disallow construction from non-const array template GenericStringRef(CharType (&str)[N]) /* = delete */; + //! Copy assignment operator not permitted - immutable type + GenericStringRef& operator=(const GenericStringRef& rhs) /* = delete */; }; +template +const CharType GenericStringRef::emptyString[] = { CharType() }; + //! Mark a character pointer as constant string /*! Mark a plain character pointer as a "string literal". This function can be used to avoid copying a character string to be referenced as a @@ -344,7 +452,7 @@ private: */ template inline GenericStringRef StringRef(const CharType* str) { - return GenericStringRef(str, internal::StrLen(str)); + return GenericStringRef(str); } //! Mark a character pointer as constant string @@ -434,6 +542,26 @@ struct TypeHelper { static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); } }; +#ifdef _MSC_VER +RAPIDJSON_STATIC_ASSERT(sizeof(long) == sizeof(int)); +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt(); } + static long Get(const ValueType& v) { return v.GetInt(); } + static ValueType& Set(ValueType& v, long data) { return v.SetInt(data); } + static ValueType& Set(ValueType& v, long data, typename ValueType::AllocatorType&) { return v.SetInt(data); } +}; + +RAPIDJSON_STATIC_ASSERT(sizeof(unsigned long) == sizeof(unsigned)); +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint(); } + static unsigned long Get(const ValueType& v) { return v.GetUint(); } + static ValueType& Set(ValueType& v, unsigned long data) { return v.SetUint(data); } + static ValueType& Set(ValueType& v, unsigned long data, typename ValueType::AllocatorType&) { return v.SetUint(data); } +}; +#endif + template struct TypeHelper { static bool Is(const ValueType& v) { return v.IsInt64(); } @@ -507,7 +635,7 @@ struct TypeHelper { static bool Is(const ValueType& v) { return v.IsObject(); } static ObjectType Get(ValueType& v) { return v.GetObject(); } static ValueType& Set(ValueType& v, ObjectType data) { return v = data; } - static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { v = data; } + static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { return v = data; } }; template @@ -536,7 +664,7 @@ template class GenericObject; \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) \tparam Allocator Allocator type for allocating memory of object, array and string. */ -template > +template class GenericValue { public: //! Name-value pair in an object. @@ -590,11 +718,11 @@ public: \note Default content for number is zero. */ explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() { - static const uint16_t defaultFlags[7] = { + static const uint16_t defaultFlags[] = { kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, kNumberAnyFlag }; - RAPIDJSON_ASSERT(type <= kNumberType); + RAPIDJSON_NOEXCEPT_ASSERT(type >= kNullType && type <= kNumberType); data_.f.flags = defaultFlags[type]; // Use ShortString to store empty string. @@ -607,10 +735,40 @@ public: \tparam SourceAllocator allocator of \c rhs \param rhs Value to copy from (read-only) \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) \see CopyFrom() */ - template< typename SourceAllocator > - GenericValue(const GenericValue& rhs, Allocator & allocator); + template + GenericValue(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { + switch (rhs.GetType()) { + case kObjectType: + DoCopyMembers(rhs, allocator, copyConstStrings); + break; + case kArrayType: { + SizeType count = rhs.data_.a.size; + GenericValue* le = reinterpret_cast(allocator.Malloc(count * sizeof(GenericValue))); + const GenericValue* re = rhs.GetElementsPointer(); + for (SizeType i = 0; i < count; i++) + new (&le[i]) GenericValue(re[i], allocator, copyConstStrings); + data_.f.flags = kArrayFlag; + data_.a.size = data_.a.capacity = count; + SetElementsPointer(le); + } + break; + case kStringType: + if (rhs.data_.f.flags == kConstStringFlag && !copyConstStrings) { + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + } + else + SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); + break; + default: + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + break; + } + } //! Constructor for boolean value. /*! \param b Boolean value @@ -672,6 +830,9 @@ public: //! Constructor for double value. explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; } + //! Constructor for float value. + explicit GenericValue(float f) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = static_cast(f); data_.f.flags = kNumberDoubleFlag; } + //! Constructor for constant string (i.e. do not make a copy of string) GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); } @@ -717,25 +878,30 @@ public: /*! Need to destruct elements of array, members of object, or copy-string. */ ~GenericValue() { - if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + // With RAPIDJSON_USE_MEMBERSMAP, the maps need to be destroyed to release + // their Allocator if it's refcounted (e.g. MemoryPoolAllocator). + if (Allocator::kNeedFree || (RAPIDJSON_USE_MEMBERSMAP+0 && + internal::IsRefCounted::Value)) { switch(data_.f.flags) { case kArrayFlag: { GenericValue* e = GetElementsPointer(); for (GenericValue* v = e; v != e + data_.a.size; ++v) v->~GenericValue(); - Allocator::Free(e); + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + Allocator::Free(e); + } } break; case kObjectFlag: - for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) - m->~Member(); - Allocator::Free(GetMembersPointer()); + DoFreeMembers(); break; case kCopyStringFlag: - Allocator::Free(const_cast(GetStringPointer())); + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + Allocator::Free(const_cast(GetStringPointer())); + } break; default: @@ -753,9 +919,15 @@ public: /*! \param rhs Source of the assignment. It will become a null value after assignment. */ GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT { - RAPIDJSON_ASSERT(this != &rhs); - this->~GenericValue(); - RawAssign(rhs); + if (RAPIDJSON_LIKELY(this != &rhs)) { + // Can't destroy "this" before assigning "rhs", otherwise "rhs" + // could be used after free if it's an sub-Value of "this", + // hence the temporary danse. + GenericValue temp; + temp.RawAssign(rhs); + this->~GenericValue(); + RawAssign(temp); + } return *this; } @@ -800,12 +972,13 @@ public: \tparam SourceAllocator Allocator type of \c rhs \param rhs Value to copy from (read-only) \param allocator Allocator to use for copying + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) */ template - GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator) { + GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { RAPIDJSON_ASSERT(static_cast(this) != static_cast(&rhs)); this->~GenericValue(); - new (this) GenericValue(rhs, allocator); + new (this) GenericValue(rhs, allocator, copyConstStrings); return *this; } @@ -846,7 +1019,7 @@ public: //! Equal-to operator /*! \note If an object contains duplicated named member, comparing equality with any object is always \c false. - \note Linear time complexity (number of all values in the subtree and total lengths of all strings). + \note Complexity is quadratic in Object's member number and linear for the rest (number of all values in the subtree and total lengths of all strings). */ template bool operator==(const GenericValue& rhs) const { @@ -905,6 +1078,7 @@ public: */ template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr,internal::IsGenericValue >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } +#ifndef __cpp_impl_three_way_comparison //! Not-equal-to operator /*! \return !(*this == rhs) */ @@ -929,6 +1103,7 @@ public: */ template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } //@} +#endif //!@name Type //@{ @@ -955,14 +1130,14 @@ public: uint64_t u = GetUint64(); volatile double d = static_cast(u); return (d >= 0.0) - && (d < static_cast(std::numeric_limits::max())) + && (d < static_cast((std::numeric_limits::max)())) && (u == static_cast(d)); } if (IsInt64()) { int64_t i = GetInt64(); volatile double d = static_cast(i); - return (d >= static_cast(std::numeric_limits::min())) - && (d < static_cast(std::numeric_limits::max())) + return (d >= static_cast((std::numeric_limits::min)())) + && (d < static_cast((std::numeric_limits::max)())) && (i == static_cast(d)); } return true; // double, int, uint are always lossless @@ -979,8 +1154,8 @@ public: bool IsLosslessFloat() const { if (!IsNumber()) return false; double a = GetDouble(); - if (a < static_cast(-std::numeric_limits::max()) - || a > static_cast(std::numeric_limits::max())) + if (a < static_cast(-(std::numeric_limits::max)()) + || a > static_cast((std::numeric_limits::max)())) return false; double b = static_cast(static_cast(a)); return a >= b && a <= b; // Prevent -Wfloat-equal @@ -1015,6 +1190,9 @@ public: //! Get the number of members in the object. SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; } + //! Get the capacity of object. + SizeType MemberCapacity() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.capacity; } + //! Check whether the object is empty. bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; } @@ -1052,13 +1230,28 @@ public: else { RAPIDJSON_ASSERT(false); // see above note - // This will generate -Wexit-time-destructors in clang - // static GenericValue NullValue; - // return NullValue; - - // Use static buffer and placement-new to prevent destruction - static char buffer[sizeof(GenericValue)]; +#if RAPIDJSON_HAS_CXX11 + // Use thread-local storage to prevent races between threads. + // Use static buffer and placement-new to prevent destruction, with + // alignas() to ensure proper alignment. + alignas(GenericValue) thread_local static char buffer[sizeof(GenericValue)]; return *new (buffer) GenericValue(); +#elif defined(_MSC_VER) && _MSC_VER < 1900 + // There's no way to solve both thread locality and proper alignment + // simultaneously. + __declspec(thread) static char buffer[sizeof(GenericValue)]; + return *new (buffer) GenericValue(); +#elif defined(__GNUC__) || defined(__clang__) + // This will generate -Wexit-time-destructors in clang, but that's + // better than having under-alignment. + __thread static GenericValue buffer; + return buffer; +#else + // Don't know what compiler this is, so don't know how to ensure + // thread-locality. + static GenericValue buffer; + return buffer; +#endif } } template @@ -1083,6 +1276,18 @@ public: /*! \pre IsObject() == true */ MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); } + //! Request the object to have enough capacity to store members. + /*! \param newCapacity The capacity that the object at least need to have. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note Linear time complexity. + */ + GenericValue& MemberReserve(SizeType newCapacity, Allocator &allocator) { + RAPIDJSON_ASSERT(IsObject()); + DoReserveMembers(newCapacity, allocator); + return *this; + } + //! Check whether a member exists in the object. /*! \param name Member name to be searched. @@ -1153,11 +1358,7 @@ public: MemberIterator FindMember(const GenericValue& name) { RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(name.IsString()); - MemberIterator member = MemberBegin(); - for ( ; member != MemberEnd(); ++member) - if (name.StringEqual(member->name)) - break; - return member; + return DoFindMember(name); } template ConstMemberIterator FindMember(const GenericValue& name) const { return const_cast(*this).FindMember(name); } @@ -1186,23 +1387,7 @@ public: GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(name.IsString()); - - ObjectData& o = data_.o; - if (o.size >= o.capacity) { - if (o.capacity == 0) { - o.capacity = kDefaultObjectCapacity; - SetMembersPointer(reinterpret_cast(allocator.Malloc(o.capacity * sizeof(Member)))); - } - else { - SizeType oldCapacity = o.capacity; - o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5 - SetMembersPointer(reinterpret_cast(allocator.Realloc(GetMembersPointer(), oldCapacity * sizeof(Member), o.capacity * sizeof(Member)))); - } - } - Member* members = GetMembersPointer(); - members[o.size].name.RawAssign(name); - members[o.size].value.RawAssign(value); - o.size++; + DoAddMember(name, value, allocator); return *this; } @@ -1336,9 +1521,7 @@ public: */ void RemoveAllMembers() { RAPIDJSON_ASSERT(IsObject()); - for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) - m->~Member(); - data_.o.size = 0; + DoClearMembers(); } //! Remove a member in object by its name. @@ -1382,14 +1565,7 @@ public: RAPIDJSON_ASSERT(data_.o.size > 0); RAPIDJSON_ASSERT(GetMembersPointer() != 0); RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd()); - - MemberIterator last(GetMembersPointer() + (data_.o.size - 1)); - if (data_.o.size > 1 && m != last) - *m = *last; // Move the last one to this place - else - m->~Member(); // Only one left, just destroy - --data_.o.size; - return m; + return DoRemoveMember(m); } //! Remove a member from an object by iterator. @@ -1421,13 +1597,7 @@ public: RAPIDJSON_ASSERT(first >= MemberBegin()); RAPIDJSON_ASSERT(first <= last); RAPIDJSON_ASSERT(last <= MemberEnd()); - - MemberIterator pos = MemberBegin() + (first - MemberBegin()); - for (MemberIterator itr = pos; itr != last; ++itr) - itr->~Member(); - std::memmove(&*pos, &*last, static_cast(MemberEnd() - last) * sizeof(Member)); - data_.o.size -= static_cast(last - first); - return pos; + return DoEraseMembers(first, last); } //! Erase a member in object by its name. @@ -1456,7 +1626,9 @@ public: } Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); } + Object GetObj() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); } ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); } + ConstObject GetObj() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); } //@} @@ -1628,8 +1800,8 @@ public: RAPIDJSON_ASSERT(last <= End()); ValueIterator pos = Begin() + (first - Begin()); for (ValueIterator itr = pos; itr != last; ++itr) - itr->~GenericValue(); - std::memmove(pos, last, static_cast(End() - last) * sizeof(GenericValue)); + itr->~GenericValue(); + std::memmove(static_cast(pos), last, static_cast(End() - last) * sizeof(GenericValue)); data_.a.size -= static_cast(last - first); return pos; } @@ -1671,19 +1843,19 @@ public: GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; } GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; } GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; } - GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(f); return *this; } + GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(static_cast(f)); return *this; } //@} //!@name String //@{ - const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); } + const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return DataString(data_); } //! Get the length of string. /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength(). */ - SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((data_.f.flags & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); } + SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return DataStringLength(data_); } //! Set this value as a string without copying source string. /*! This version has better performance with supplied length, and also support string containing null character. @@ -1710,7 +1882,7 @@ public: \return The value itself for fluent API. \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length */ - GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(StringRef(s, length), allocator); return *this; } + GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { return SetString(StringRef(s, length), allocator); } //! Set this value as a string by copying from source string. /*! \param s source string. @@ -1718,7 +1890,15 @@ public: \return The value itself for fluent API. \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length */ - GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); } + GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(StringRef(s), allocator); } + + //! Set this value as a string by copying from source string. + /*! \param s source string reference + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(StringRefType s, Allocator& allocator) { this->~GenericValue(); SetStringRaw(s, allocator); return *this; } #if RAPIDJSON_HAS_STDSTRING //! Set this value as a string by copying from source string. @@ -1728,7 +1908,7 @@ public: \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size() \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. */ - GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(s.data(), SizeType(s.size()), allocator); } + GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(StringRef(s), allocator); } #endif //@} @@ -1786,7 +1966,7 @@ public: case kArrayType: if (RAPIDJSON_UNLIKELY(!handler.StartArray())) return false; - for (const GenericValue* v = Begin(); v != End(); ++v) + for (ConstValueIterator v = Begin(); v != End(); ++v) if (RAPIDJSON_UNLIKELY(!v->Accept(handler))) return false; return handler.EndArray(data_.a.size); @@ -1822,25 +2002,26 @@ private: // Initial flags of different types. kNullFlag = kNullType, - kTrueFlag = kTrueType | kBoolFlag, - kFalseFlag = kFalseType | kBoolFlag, - kNumberIntFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag, - kNumberUintFlag = kNumberType | kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag, - kNumberInt64Flag = kNumberType | kNumberFlag | kInt64Flag, - kNumberUint64Flag = kNumberType | kNumberFlag | kUint64Flag, - kNumberDoubleFlag = kNumberType | kNumberFlag | kDoubleFlag, - kNumberAnyFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag, - kConstStringFlag = kStringType | kStringFlag, - kCopyStringFlag = kStringType | kStringFlag | kCopyFlag, - kShortStringFlag = kStringType | kStringFlag | kCopyFlag | kInlineStrFlag, + // These casts are added to suppress the warning on MSVC about bitwise operations between enums of different types. + kTrueFlag = static_cast(kTrueType) | static_cast(kBoolFlag), + kFalseFlag = static_cast(kFalseType) | static_cast(kBoolFlag), + kNumberIntFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kIntFlag | kInt64Flag), + kNumberUintFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag), + kNumberInt64Flag = static_cast(kNumberType) | static_cast(kNumberFlag | kInt64Flag), + kNumberUint64Flag = static_cast(kNumberType) | static_cast(kNumberFlag | kUint64Flag), + kNumberDoubleFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kDoubleFlag), + kNumberAnyFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag), + kConstStringFlag = static_cast(kStringType) | static_cast(kStringFlag), + kCopyStringFlag = static_cast(kStringType) | static_cast(kStringFlag | kCopyFlag), + kShortStringFlag = static_cast(kStringType) | static_cast(kStringFlag | kCopyFlag | kInlineStrFlag), kObjectFlag = kObjectType, kArrayFlag = kArrayType, kTypeMask = 0x07 }; - static const SizeType kDefaultArrayCapacity = 16; - static const SizeType kDefaultObjectCapacity = 16; + static const SizeType kDefaultArrayCapacity = RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY; + static const SizeType kDefaultObjectCapacity = RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY; struct Flag { #if RAPIDJSON_48BITPOINTER_OPTIMIZATION @@ -1923,6 +2104,13 @@ private: Flag f; }; // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION + static RAPIDJSON_FORCEINLINE const Ch* DataString(const Data& data) { + return (data.f.flags & kInlineStrFlag) ? data.ss.str : RAPIDJSON_GETPOINTER(Ch, data.s.str); + } + static RAPIDJSON_FORCEINLINE SizeType DataStringLength(const Data& data) { + return (data.f.flags & kInlineStrFlag) ? data.ss.GetLength() : data.s.length; + } + RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); } RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); } RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); } @@ -1930,13 +2118,293 @@ private: RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); } RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); } +#if RAPIDJSON_USE_MEMBERSMAP + + struct MapTraits { + struct Less { + bool operator()(const Data& s1, const Data& s2) const { + SizeType n1 = DataStringLength(s1), n2 = DataStringLength(s2); + int cmp = std::memcmp(DataString(s1), DataString(s2), sizeof(Ch) * (n1 < n2 ? n1 : n2)); + return cmp < 0 || (cmp == 0 && n1 < n2); + } + }; + typedef std::pair Pair; + typedef std::multimap > Map; + typedef typename Map::iterator Iterator; + }; + typedef typename MapTraits::Map Map; + typedef typename MapTraits::Less MapLess; + typedef typename MapTraits::Pair MapPair; + typedef typename MapTraits::Iterator MapIterator; + + // + // Layout of the members' map/array, re(al)located according to the needed capacity: + // + // {Map*}<>{capacity}<>{Member[capacity]}<>{MapIterator[capacity]} + // + // (where <> stands for the RAPIDJSON_ALIGN-ment, if needed) + // + + static RAPIDJSON_FORCEINLINE size_t GetMapLayoutSize(SizeType capacity) { + return RAPIDJSON_ALIGN(sizeof(Map*)) + + RAPIDJSON_ALIGN(sizeof(SizeType)) + + RAPIDJSON_ALIGN(capacity * sizeof(Member)) + + capacity * sizeof(MapIterator); + } + + static RAPIDJSON_FORCEINLINE SizeType &GetMapCapacity(Map* &map) { + return *reinterpret_cast(reinterpret_cast(&map) + + RAPIDJSON_ALIGN(sizeof(Map*))); + } + + static RAPIDJSON_FORCEINLINE Member* GetMapMembers(Map* &map) { + return reinterpret_cast(reinterpret_cast(&map) + + RAPIDJSON_ALIGN(sizeof(Map*)) + + RAPIDJSON_ALIGN(sizeof(SizeType))); + } + + static RAPIDJSON_FORCEINLINE MapIterator* GetMapIterators(Map* &map) { + return reinterpret_cast(reinterpret_cast(&map) + + RAPIDJSON_ALIGN(sizeof(Map*)) + + RAPIDJSON_ALIGN(sizeof(SizeType)) + + RAPIDJSON_ALIGN(GetMapCapacity(map) * sizeof(Member))); + } + + static RAPIDJSON_FORCEINLINE Map* &GetMap(Member* members) { + RAPIDJSON_ASSERT(members != 0); + return *reinterpret_cast(reinterpret_cast(members) - + RAPIDJSON_ALIGN(sizeof(SizeType)) - + RAPIDJSON_ALIGN(sizeof(Map*))); + } + + // Some compilers' debug mechanisms want all iterators to be destroyed, for their accounting.. + RAPIDJSON_FORCEINLINE MapIterator DropMapIterator(MapIterator& rhs) { +#if RAPIDJSON_HAS_CXX11 + MapIterator ret = std::move(rhs); +#else + MapIterator ret = rhs; +#endif + rhs.~MapIterator(); + return ret; + } + + Map* &DoReallocMap(Map** oldMap, SizeType newCapacity, Allocator& allocator) { + Map **newMap = static_cast(allocator.Malloc(GetMapLayoutSize(newCapacity))); + GetMapCapacity(*newMap) = newCapacity; + if (!oldMap) { + *newMap = new (allocator.Malloc(sizeof(Map))) Map(MapLess(), allocator); + } + else { + *newMap = *oldMap; + size_t count = (*oldMap)->size(); + std::memcpy(static_cast(GetMapMembers(*newMap)), + static_cast(GetMapMembers(*oldMap)), + count * sizeof(Member)); + MapIterator *oldIt = GetMapIterators(*oldMap), + *newIt = GetMapIterators(*newMap); + while (count--) { + new (&newIt[count]) MapIterator(DropMapIterator(oldIt[count])); + } + Allocator::Free(oldMap); + } + return *newMap; + } + + RAPIDJSON_FORCEINLINE Member* DoAllocMembers(SizeType capacity, Allocator& allocator) { + return GetMapMembers(DoReallocMap(0, capacity, allocator)); + } + + void DoReserveMembers(SizeType newCapacity, Allocator& allocator) { + ObjectData& o = data_.o; + if (newCapacity > o.capacity) { + Member* oldMembers = GetMembersPointer(); + Map **oldMap = oldMembers ? &GetMap(oldMembers) : 0, + *&newMap = DoReallocMap(oldMap, newCapacity, allocator); + RAPIDJSON_SETPOINTER(Member, o.members, GetMapMembers(newMap)); + o.capacity = newCapacity; + } + } + + template + MemberIterator DoFindMember(const GenericValue& name) { + if (Member* members = GetMembersPointer()) { + Map* &map = GetMap(members); + MapIterator mit = map->find(reinterpret_cast(name.data_)); + if (mit != map->end()) { + return MemberIterator(&members[mit->second]); + } + } + return MemberEnd(); + } + + void DoClearMembers() { + if (Member* members = GetMembersPointer()) { + Map* &map = GetMap(members); + MapIterator* mit = GetMapIterators(map); + for (SizeType i = 0; i < data_.o.size; i++) { + map->erase(DropMapIterator(mit[i])); + members[i].~Member(); + } + data_.o.size = 0; + } + } + + void DoFreeMembers() { + if (Member* members = GetMembersPointer()) { + GetMap(members)->~Map(); + for (SizeType i = 0; i < data_.o.size; i++) { + members[i].~Member(); + } + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + Map** map = &GetMap(members); + Allocator::Free(*map); + Allocator::Free(map); + } + } + } + +#else // !RAPIDJSON_USE_MEMBERSMAP + + RAPIDJSON_FORCEINLINE Member* DoAllocMembers(SizeType capacity, Allocator& allocator) { + return Malloc(allocator, capacity); + } + + void DoReserveMembers(SizeType newCapacity, Allocator& allocator) { + ObjectData& o = data_.o; + if (newCapacity > o.capacity) { + Member* newMembers = Realloc(allocator, GetMembersPointer(), o.capacity, newCapacity); + RAPIDJSON_SETPOINTER(Member, o.members, newMembers); + o.capacity = newCapacity; + } + } + + template + MemberIterator DoFindMember(const GenericValue& name) { + MemberIterator member = MemberBegin(); + for ( ; member != MemberEnd(); ++member) + if (name.StringEqual(member->name)) + break; + return member; + } + + void DoClearMembers() { + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + data_.o.size = 0; + } + + void DoFreeMembers() { + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + Allocator::Free(GetMembersPointer()); + } + +#endif // !RAPIDJSON_USE_MEMBERSMAP + + void DoAddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { + ObjectData& o = data_.o; + if (o.size >= o.capacity) + DoReserveMembers(o.capacity ? (o.capacity + (o.capacity + 1) / 2) : kDefaultObjectCapacity, allocator); + Member* members = GetMembersPointer(); + Member* m = members + o.size; + m->name.RawAssign(name); + m->value.RawAssign(value); +#if RAPIDJSON_USE_MEMBERSMAP + Map* &map = GetMap(members); + MapIterator* mit = GetMapIterators(map); + new (&mit[o.size]) MapIterator(map->insert(MapPair(m->name.data_, o.size))); +#endif + ++o.size; + } + + MemberIterator DoRemoveMember(MemberIterator m) { + ObjectData& o = data_.o; + Member* members = GetMembersPointer(); +#if RAPIDJSON_USE_MEMBERSMAP + Map* &map = GetMap(members); + MapIterator* mit = GetMapIterators(map); + SizeType mpos = static_cast(&*m - members); + map->erase(DropMapIterator(mit[mpos])); +#endif + MemberIterator last(members + (o.size - 1)); + if (o.size > 1 && m != last) { +#if RAPIDJSON_USE_MEMBERSMAP + new (&mit[mpos]) MapIterator(DropMapIterator(mit[&*last - members])); + mit[mpos]->second = mpos; +#endif + *m = *last; // Move the last one to this place + } + else { + m->~Member(); // Only one left, just destroy + } + --o.size; + return m; + } + + MemberIterator DoEraseMembers(ConstMemberIterator first, ConstMemberIterator last) { + ObjectData& o = data_.o; + MemberIterator beg = MemberBegin(), + pos = beg + (first - beg), + end = MemberEnd(); +#if RAPIDJSON_USE_MEMBERSMAP + Map* &map = GetMap(GetMembersPointer()); + MapIterator* mit = GetMapIterators(map); +#endif + for (MemberIterator itr = pos; itr != last; ++itr) { +#if RAPIDJSON_USE_MEMBERSMAP + map->erase(DropMapIterator(mit[itr - beg])); +#endif + itr->~Member(); + } +#if RAPIDJSON_USE_MEMBERSMAP + if (first != last) { + // Move remaining members/iterators + MemberIterator next = pos + (last - first); + for (MemberIterator itr = pos; next != end; ++itr, ++next) { + std::memcpy(static_cast(&*itr), &*next, sizeof(Member)); + SizeType mpos = static_cast(itr - beg); + new (&mit[mpos]) MapIterator(DropMapIterator(mit[next - beg])); + mit[mpos]->second = mpos; + } + } +#else + std::memmove(static_cast(&*pos), &*last, + static_cast(end - last) * sizeof(Member)); +#endif + o.size -= static_cast(last - first); + return pos; + } + + template + void DoCopyMembers(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings) { + RAPIDJSON_ASSERT(rhs.GetType() == kObjectType); + + data_.f.flags = kObjectFlag; + SizeType count = rhs.data_.o.size; + Member* lm = DoAllocMembers(count, allocator); + const typename GenericValue::Member* rm = rhs.GetMembersPointer(); +#if RAPIDJSON_USE_MEMBERSMAP + Map* &map = GetMap(lm); + MapIterator* mit = GetMapIterators(map); +#endif + for (SizeType i = 0; i < count; i++) { + new (&lm[i].name) GenericValue(rm[i].name, allocator, copyConstStrings); + new (&lm[i].value) GenericValue(rm[i].value, allocator, copyConstStrings); +#if RAPIDJSON_USE_MEMBERSMAP + new (&mit[i]) MapIterator(map->insert(MapPair(lm[i].name.data_, i))); +#endif + } + data_.o.size = data_.o.capacity = count; + SetMembersPointer(lm); + } + // Initialize this value as array with initial data, without calling destructor. void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { data_.f.flags = kArrayFlag; if (count) { GenericValue* e = static_cast(allocator.Malloc(count * sizeof(GenericValue))); SetElementsPointer(e); - std::memcpy(e, values, count * sizeof(GenericValue)); + std::memcpy(static_cast(e), values, count * sizeof(GenericValue)); } else SetElementsPointer(0); @@ -1947,9 +2415,16 @@ private: void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) { data_.f.flags = kObjectFlag; if (count) { - Member* m = static_cast(allocator.Malloc(count * sizeof(Member))); + Member* m = DoAllocMembers(count, allocator); SetMembersPointer(m); - std::memcpy(m, members, count * sizeof(Member)); + std::memcpy(static_cast(m), members, count * sizeof(Member)); +#if RAPIDJSON_USE_MEMBERSMAP + Map* &map = GetMap(m); + MapIterator* mit = GetMapIterators(map); + for (SizeType i = 0; i < count; i++) { + new (&mit[i]) MapIterator(map->insert(MapPair(m[i].name.data_, i))); + } +#endif } else SetMembersPointer(0); @@ -2020,12 +2495,13 @@ typedef GenericValue > Value; \tparam StackAllocator Allocator for allocating memory for stack during parsing. \warning Although GenericDocument inherits from GenericValue, the API does \b not provide any virtual functions, especially no virtual destructor. To avoid memory leaks, do not \c delete a GenericDocument object via a pointer to a GenericValue. */ -template , typename StackAllocator = CrtAllocator> +template class GenericDocument : public GenericValue { public: typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. typedef GenericValue ValueType; //!< Value type of the document. typedef Allocator AllocatorType; //!< Allocator type from template parameter. + typedef StackAllocator StackAllocatorType; //!< StackAllocator type from template parameter. //! Constructor /*! Creates an empty document of specified type. @@ -2038,7 +2514,7 @@ public: GenericValue(type), allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() { if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); } //! Constructor @@ -2051,7 +2527,7 @@ public: allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() { if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS @@ -2070,6 +2546,13 @@ public: #endif ~GenericDocument() { + // Clear the ::ValueType before ownAllocator is destroyed, ~ValueType() + // runs last and may access its elements or members which would be freed + // with an allocator like MemoryPoolAllocator (CrtAllocator does not + // free its data when destroyed, but MemoryPoolAllocator does). + if (ownAllocator_) { + ValueType::SetNull(); + } Destroy(); } @@ -2112,6 +2595,10 @@ public: return *this; } + // Allow Swap with ValueType. + // Refer to Effective C++ 3rd Edition/Item 33: Avoid hiding inherited names. + using ValueType::Swap; + //! free-standing swap function helper /*! Helper function to enable support for common swap implementation pattern based on \c std::swap: @@ -2243,7 +2730,7 @@ public: template GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) { RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); - MemoryStream ms(static_cast(str), length * sizeof(typename SourceEncoding::Ch)); + MemoryStream ms(reinterpret_cast(str), length * sizeof(typename SourceEncoding::Ch)); EncodedInputStream is(ms); ParseStream(is); return *this; @@ -2280,7 +2767,7 @@ public: //!@name Handling parse errors //!@{ - //! Whether a parse error has occured in the last parsing. + //! Whether a parse error has occurred in the last parsing. bool HasParseError() const { return parseResult_.IsError(); } //! Get the \ref ParseErrorCode of last parsing. @@ -2401,34 +2888,6 @@ private: //! GenericDocument with UTF8 encoding typedef GenericDocument > Document; -// defined here due to the dependency on GenericDocument -template -template -inline -GenericValue::GenericValue(const GenericValue& rhs, Allocator& allocator) -{ - switch (rhs.GetType()) { - case kObjectType: - case kArrayType: { // perform deep copy via SAX Handler - GenericDocument d(&allocator); - rhs.Accept(d); - RawAssign(*d.stack_.template Pop(1)); - } - break; - case kStringType: - if (rhs.data_.f.flags == kConstStringFlag) { - data_.f.flags = rhs.data_.f.flags; - data_ = *reinterpret_cast(&rhs.data_); - } else { - SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); - } - break; - default: - data_.f.flags = rhs.data_.f.flags; - data_ = *reinterpret_cast(&rhs.data_); - break; - } -} //! Helper class for accessing Value of array type. /*! @@ -2454,6 +2913,7 @@ public: GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; } ~GenericArray() {} + operator ValueType&() const { return value_; } SizeType Size() const { return value_.Size(); } SizeType Capacity() const { return value_.Capacity(); } bool Empty() const { return value_.Empty(); } @@ -2509,7 +2969,9 @@ public: GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; } ~GenericObject() {} + operator ValueType&() const { return value_; } SizeType MemberCount() const { return value_.MemberCount(); } + SizeType MemberCapacity() const { return value_.MemberCapacity(); } bool ObjectEmpty() const { return value_.ObjectEmpty(); } template ValueType& operator[](T* name) const { return value_[name]; } template ValueType& operator[](const GenericValue& name) const { return value_[name]; } @@ -2518,6 +2980,7 @@ public: #endif MemberIterator MemberBegin() const { return value_.MemberBegin(); } MemberIterator MemberEnd() const { return value_.MemberEnd(); } + GenericObject MemberReserve(SizeType newCapacity, AllocatorType &allocator) const { value_.MemberReserve(newCapacity, allocator); return *this; } bool HasMember(const Ch* name) const { return value_.HasMember(name); } #if RAPIDJSON_HAS_STDSTRING bool HasMember(const std::basic_string& name) const { return value_.HasMember(name); } @@ -2543,7 +3006,7 @@ public: GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } - void RemoveAllMembers() { return value_.RemoveAllMembers(); } + void RemoveAllMembers() { value_.RemoveAllMembers(); } bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); } #if RAPIDJSON_HAS_STDSTRING bool RemoveMember(const std::basic_string& name) const { return value_.RemoveMember(name); } @@ -2572,4 +3035,9 @@ private: RAPIDJSON_NAMESPACE_END RAPIDJSON_DIAG_POP +#ifdef RAPIDJSON_WINDOWS_GETOBJECT_WORKAROUND_APPLIED +#pragma pop_macro("GetObject") +#undef RAPIDJSON_WINDOWS_GETOBJECT_WORKAROUND_APPLIED +#endif + #endif // RAPIDJSON_DOCUMENT_H_ diff --git a/third_party/rapidjson/include/rapidjson/encodedstream.h b/third_party/rapidjson/include/rapidjson/encodedstream.h index 145068386..cf046b892 100644 --- a/third_party/rapidjson/include/rapidjson/encodedstream.h +++ b/third_party/rapidjson/include/rapidjson/encodedstream.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -200,7 +200,7 @@ private: // xx xx xx xx UTF-8 if (!hasBOM_) { - unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); + int pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); switch (pattern) { case 0x08: type_ = kUTF32BE; break; case 0x0A: type_ = kUTF16BE; break; diff --git a/third_party/rapidjson/include/rapidjson/encodings.h b/third_party/rapidjson/include/rapidjson/encodings.h index baa7c2b17..50ad18bdc 100644 --- a/third_party/rapidjson/include/rapidjson/encodings.h +++ b/third_party/rapidjson/include/rapidjson/encodings.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -17,7 +17,7 @@ #include "rapidjson.h" -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__clang__) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data RAPIDJSON_DIAG_OFF(4702) // unreachable code @@ -144,9 +144,9 @@ struct UTF8 { template static bool Decode(InputStream& is, unsigned* codepoint) { -#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast(c) & 0x3Fu) -#define TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) -#define TAIL() COPY(); TRANS(0x70) +#define RAPIDJSON_COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast(c) & 0x3Fu) +#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) typename InputStream::Ch c = is.Take(); if (!(c & 0x80)) { *codepoint = static_cast(c); @@ -157,48 +157,48 @@ struct UTF8 { if (type >= 32) { *codepoint = 0; } else { - *codepoint = (0xFF >> type) & static_cast(c); + *codepoint = (0xFFu >> type) & static_cast(c); } bool result = true; switch (type) { - case 2: TAIL(); return result; - case 3: TAIL(); TAIL(); return result; - case 4: COPY(); TRANS(0x50); TAIL(); return result; - case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; - case 6: TAIL(); TAIL(); TAIL(); return result; - case 10: COPY(); TRANS(0x20); TAIL(); return result; - case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; + case 2: RAPIDJSON_TAIL(); return result; + case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; + case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; + case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; default: return false; } -#undef COPY -#undef TRANS -#undef TAIL +#undef RAPIDJSON_COPY +#undef RAPIDJSON_TRANS +#undef RAPIDJSON_TAIL } template static bool Validate(InputStream& is, OutputStream& os) { -#define COPY() os.Put(c = is.Take()) -#define TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) -#define TAIL() COPY(); TRANS(0x70) +#define RAPIDJSON_COPY() os.Put(c = is.Take()) +#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) Ch c; - COPY(); + RAPIDJSON_COPY(); if (!(c & 0x80)) return true; bool result = true; switch (GetRange(static_cast(c))) { - case 2: TAIL(); return result; - case 3: TAIL(); TAIL(); return result; - case 4: COPY(); TRANS(0x50); TAIL(); return result; - case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; - case 6: TAIL(); TAIL(); TAIL(); return result; - case 10: COPY(); TRANS(0x20); TAIL(); return result; - case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; + case 2: RAPIDJSON_TAIL(); return result; + case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; + case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; + case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; default: return false; } -#undef COPY -#undef TRANS -#undef TAIL +#undef RAPIDJSON_COPY +#undef RAPIDJSON_TRANS +#undef RAPIDJSON_TAIL } static unsigned char GetRange(unsigned char c) { @@ -283,7 +283,7 @@ struct UTF16 { RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); unsigned v = codepoint - 0x10000; os.Put(static_cast((v >> 10) | 0xD800)); - os.Put((v & 0x3FF) | 0xDC00); + os.Put(static_cast((v & 0x3FF) | 0xDC00)); } } @@ -299,7 +299,7 @@ struct UTF16 { RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); unsigned v = codepoint - 0x10000; PutUnsafe(os, static_cast((v >> 10) | 0xD800)); - PutUnsafe(os, (v & 0x3FF) | 0xDC00); + PutUnsafe(os, static_cast((v & 0x3FF) | 0xDC00)); } } @@ -384,7 +384,7 @@ struct UTF16BE : UTF16 { static CharType Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); unsigned c = static_cast(static_cast(is.Take())) << 8; - c |= static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())); return static_cast(c); } @@ -620,28 +620,28 @@ struct AutoUTF { #define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x template - RAPIDJSON_FORCEINLINE static void Encode(OutputStream& os, unsigned codepoint) { + static RAPIDJSON_FORCEINLINE void Encode(OutputStream& os, unsigned codepoint) { typedef void (*EncodeFunc)(OutputStream&, unsigned); static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) }; (*f[os.GetType()])(os, codepoint); } template - RAPIDJSON_FORCEINLINE static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + static RAPIDJSON_FORCEINLINE void EncodeUnsafe(OutputStream& os, unsigned codepoint) { typedef void (*EncodeFunc)(OutputStream&, unsigned); static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) }; (*f[os.GetType()])(os, codepoint); } template - RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) { + static RAPIDJSON_FORCEINLINE bool Decode(InputStream& is, unsigned* codepoint) { typedef bool (*DecodeFunc)(InputStream&, unsigned*); static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) }; return (*f[is.GetType()])(is, codepoint); } template - RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { typedef bool (*ValidateFunc)(InputStream&, OutputStream&); static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) }; return (*f[is.GetType()])(is, os); @@ -658,7 +658,7 @@ template struct Transcoder { //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. template - RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { unsigned codepoint; if (!SourceEncoding::Decode(is, &codepoint)) return false; @@ -667,7 +667,7 @@ struct Transcoder { } template - RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { unsigned codepoint; if (!SourceEncoding::Decode(is, &codepoint)) return false; @@ -677,7 +677,7 @@ struct Transcoder { //! Validate one Unicode codepoint from an encoded stream. template - RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { return Transcode(is, os); // Since source/target encoding is different, must transcode. } }; @@ -690,26 +690,26 @@ inline void PutUnsafe(Stream& stream, typename Stream::Ch c); template struct Transcoder { template - RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class. return true; } template - RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class. return true; } template - RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { return Encoding::Validate(is, os); // source/target encoding are the same } }; RAPIDJSON_NAMESPACE_END -#if defined(__GNUC__) || defined(_MSC_VER) +#if defined(__GNUC__) || (defined(_MSC_VER) && !defined(__clang__)) RAPIDJSON_DIAG_POP #endif diff --git a/third_party/rapidjson/include/rapidjson/error/en.h b/third_party/rapidjson/include/rapidjson/error/en.h index 2db838bff..c87b04eb1 100644 --- a/third_party/rapidjson/include/rapidjson/error/en.h +++ b/third_party/rapidjson/include/rapidjson/error/en.h @@ -1,15 +1,15 @@ // Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // -// 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 +// 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 RAPIDJSON_ERROR_EN_H_ @@ -39,13 +39,13 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErro case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty."); case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values."); - + case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value."); - + case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member."); case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member."); case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member."); - + case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element."); case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string."); @@ -65,6 +65,108 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErro } } +//! Maps error code of validation into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param validateErrorCode Error code obtained from validator. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ +inline const RAPIDJSON_ERROR_CHARTYPE* GetValidateError_En(ValidateErrorCode validateErrorCode) { + switch (validateErrorCode) { + case kValidateErrors: return RAPIDJSON_ERROR_STRING("One or more validation errors have occurred"); + case kValidateErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kValidateErrorMultipleOf: return RAPIDJSON_ERROR_STRING("Number '%actual' is not a multiple of the 'multipleOf' value '%expected'."); + case kValidateErrorMaximum: return RAPIDJSON_ERROR_STRING("Number '%actual' is greater than the 'maximum' value '%expected'."); + case kValidateErrorExclusiveMaximum: return RAPIDJSON_ERROR_STRING("Number '%actual' is greater than or equal to the 'exclusiveMaximum' value '%expected'."); + case kValidateErrorMinimum: return RAPIDJSON_ERROR_STRING("Number '%actual' is less than the 'minimum' value '%expected'."); + case kValidateErrorExclusiveMinimum: return RAPIDJSON_ERROR_STRING("Number '%actual' is less than or equal to the 'exclusiveMinimum' value '%expected'."); + + case kValidateErrorMaxLength: return RAPIDJSON_ERROR_STRING("String '%actual' is longer than the 'maxLength' value '%expected'."); + case kValidateErrorMinLength: return RAPIDJSON_ERROR_STRING("String '%actual' is shorter than the 'minLength' value '%expected'."); + case kValidateErrorPattern: return RAPIDJSON_ERROR_STRING("String '%actual' does not match the 'pattern' regular expression."); + + case kValidateErrorMaxItems: return RAPIDJSON_ERROR_STRING("Array of length '%actual' is longer than the 'maxItems' value '%expected'."); + case kValidateErrorMinItems: return RAPIDJSON_ERROR_STRING("Array of length '%actual' is shorter than the 'minItems' value '%expected'."); + case kValidateErrorUniqueItems: return RAPIDJSON_ERROR_STRING("Array has duplicate items at indices '%duplicates' but 'uniqueItems' is true."); + case kValidateErrorAdditionalItems: return RAPIDJSON_ERROR_STRING("Array has an additional item at index '%disallowed' that is not allowed by the schema."); + + case kValidateErrorMaxProperties: return RAPIDJSON_ERROR_STRING("Object has '%actual' members which is more than 'maxProperties' value '%expected'."); + case kValidateErrorMinProperties: return RAPIDJSON_ERROR_STRING("Object has '%actual' members which is less than 'minProperties' value '%expected'."); + case kValidateErrorRequired: return RAPIDJSON_ERROR_STRING("Object is missing the following members required by the schema: '%missing'."); + case kValidateErrorAdditionalProperties: return RAPIDJSON_ERROR_STRING("Object has an additional member '%disallowed' that is not allowed by the schema."); + case kValidateErrorPatternProperties: return RAPIDJSON_ERROR_STRING("Object has 'patternProperties' that are not allowed by the schema."); + case kValidateErrorDependencies: return RAPIDJSON_ERROR_STRING("Object has missing property or schema dependencies, refer to following errors."); + + case kValidateErrorEnum: return RAPIDJSON_ERROR_STRING("Property has a value that is not one of its allowed enumerated values."); + case kValidateErrorType: return RAPIDJSON_ERROR_STRING("Property has a type '%actual' that is not in the following list: '%expected'."); + + case kValidateErrorOneOf: return RAPIDJSON_ERROR_STRING("Property did not match any of the sub-schemas specified by 'oneOf', refer to following errors."); + case kValidateErrorOneOfMatch: return RAPIDJSON_ERROR_STRING("Property matched more than one of the sub-schemas specified by 'oneOf', indices '%matches'."); + case kValidateErrorAllOf: return RAPIDJSON_ERROR_STRING("Property did not match all of the sub-schemas specified by 'allOf', refer to following errors."); + case kValidateErrorAnyOf: return RAPIDJSON_ERROR_STRING("Property did not match any of the sub-schemas specified by 'anyOf', refer to following errors."); + case kValidateErrorNot: return RAPIDJSON_ERROR_STRING("Property matched the sub-schema specified by 'not'."); + + case kValidateErrorReadOnly: return RAPIDJSON_ERROR_STRING("Property is read-only but has been provided when validation is for writing."); + case kValidateErrorWriteOnly: return RAPIDJSON_ERROR_STRING("Property is write-only but has been provided when validation is for reading."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +//! Maps error code of schema document compilation into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param schemaErrorCode Error code obtained from compiling the schema document. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ + inline const RAPIDJSON_ERROR_CHARTYPE* GetSchemaError_En(SchemaErrorCode schemaErrorCode) { + switch (schemaErrorCode) { + case kSchemaErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kSchemaErrorStartUnknown: return RAPIDJSON_ERROR_STRING("Pointer '%value' to start of schema does not resolve to a location in the document."); + case kSchemaErrorRefPlainName: return RAPIDJSON_ERROR_STRING("$ref fragment '%value' must be a JSON pointer."); + case kSchemaErrorRefInvalid: return RAPIDJSON_ERROR_STRING("$ref must not be an empty string."); + case kSchemaErrorRefPointerInvalid: return RAPIDJSON_ERROR_STRING("$ref fragment '%value' is not a valid JSON pointer at offset '%offset'."); + case kSchemaErrorRefUnknown: return RAPIDJSON_ERROR_STRING("$ref '%value' does not resolve to a location in the target document."); + case kSchemaErrorRefCyclical: return RAPIDJSON_ERROR_STRING("$ref '%value' is cyclical."); + case kSchemaErrorRefNoRemoteProvider: return RAPIDJSON_ERROR_STRING("$ref is remote but there is no remote provider."); + case kSchemaErrorRefNoRemoteSchema: return RAPIDJSON_ERROR_STRING("$ref '%value' is remote but the remote provider did not return a schema."); + case kSchemaErrorRegexInvalid: return RAPIDJSON_ERROR_STRING("Invalid regular expression '%value' in 'pattern' or 'patternProperties'."); + case kSchemaErrorSpecUnknown: return RAPIDJSON_ERROR_STRING("JSON schema draft or OpenAPI version is not recognized."); + case kSchemaErrorSpecUnsupported: return RAPIDJSON_ERROR_STRING("JSON schema draft or OpenAPI version is not supported."); + case kSchemaErrorSpecIllegal: return RAPIDJSON_ERROR_STRING("Both JSON schema draft and OpenAPI version found in document."); + case kSchemaErrorReadOnlyAndWriteOnly: return RAPIDJSON_ERROR_STRING("Property must not be both 'readOnly' and 'writeOnly'."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } + } + +//! Maps error code of pointer parse into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param pointerParseErrorCode Error code obtained from pointer parse. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ +inline const RAPIDJSON_ERROR_CHARTYPE* GetPointerParseError_En(PointerParseErrorCode pointerParseErrorCode) { + switch (pointerParseErrorCode) { + case kPointerParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kPointerParseErrorTokenMustBeginWithSolidus: return RAPIDJSON_ERROR_STRING("A token must begin with a '/'."); + case kPointerParseErrorInvalidEscape: return RAPIDJSON_ERROR_STRING("Invalid escape."); + case kPointerParseErrorInvalidPercentEncoding: return RAPIDJSON_ERROR_STRING("Invalid percent encoding in URI fragment."); + case kPointerParseErrorCharacterMustPercentEncode: return RAPIDJSON_ERROR_STRING("A character must be percent encoded in a URI fragment."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + RAPIDJSON_NAMESPACE_END #ifdef __clang__ diff --git a/third_party/rapidjson/include/rapidjson/error/error.h b/third_party/rapidjson/include/rapidjson/error/error.h index 95cb31a72..cae345db3 100644 --- a/third_party/rapidjson/include/rapidjson/error/error.h +++ b/third_party/rapidjson/include/rapidjson/error/error.h @@ -1,15 +1,15 @@ // Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // -// 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 +// 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 RAPIDJSON_ERROR_ERROR_H_ @@ -42,7 +42,7 @@ RAPIDJSON_DIAG_OFF(padded) /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_ERROR_STRING -//! Macro for converting string literial to \ref RAPIDJSON_ERROR_CHARTYPE[]. +//! Macro for converting string literal to \ref RAPIDJSON_ERROR_CHARTYPE[]. /*! \ingroup RAPIDJSON_ERRORS By default this conversion macro does nothing. On Windows, user can define this macro as \c _T(x) for supporting both @@ -104,6 +104,8 @@ enum ParseErrorCode { \see GenericReader::Parse, GenericDocument::Parse */ struct ParseResult { + //!! Unspecified boolean type + typedef bool (ParseResult::*BooleanType)() const; public: //! Default constructor, no error. ParseResult() : code_(kParseErrorNone), offset_(0) {} @@ -115,8 +117,8 @@ public: //! Get the error offset, if \ref IsError(), 0 otherwise. size_t Offset() const { return offset_; } - //! Conversion to \c bool, returns \c true, iff !\ref IsError(). - operator bool() const { return !IsError(); } + //! Explicit conversion to \c bool, returns \c true, iff !\ref IsError(). + operator BooleanType() const { return !IsError() ? &ParseResult::IsError : NULL; } //! Whether the result is an error. bool IsError() const { return code_ != kParseErrorNone; } @@ -124,6 +126,10 @@ public: bool operator==(ParseErrorCode code) const { return code_ == code; } friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } + bool operator!=(const ParseResult& that) const { return !(*this == that); } + bool operator!=(ParseErrorCode code) const { return !(*this == code); } + friend bool operator!=(ParseErrorCode code, const ParseResult & err) { return err != code; } + //! Reset error code. void Clear() { Set(kParseErrorNone); } //! Update error code and offset. @@ -146,6 +152,130 @@ private: */ typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); +/////////////////////////////////////////////////////////////////////////////// +// ValidateErrorCode + +//! Error codes when validating. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericSchemaValidator +*/ +enum ValidateErrorCode { + kValidateErrors = -1, //!< Top level error code when kValidateContinueOnErrorsFlag set. + kValidateErrorNone = 0, //!< No error. + + kValidateErrorMultipleOf, //!< Number is not a multiple of the 'multipleOf' value. + kValidateErrorMaximum, //!< Number is greater than the 'maximum' value. + kValidateErrorExclusiveMaximum, //!< Number is greater than or equal to the 'maximum' value. + kValidateErrorMinimum, //!< Number is less than the 'minimum' value. + kValidateErrorExclusiveMinimum, //!< Number is less than or equal to the 'minimum' value. + + kValidateErrorMaxLength, //!< String is longer than the 'maxLength' value. + kValidateErrorMinLength, //!< String is longer than the 'maxLength' value. + kValidateErrorPattern, //!< String does not match the 'pattern' regular expression. + + kValidateErrorMaxItems, //!< Array is longer than the 'maxItems' value. + kValidateErrorMinItems, //!< Array is shorter than the 'minItems' value. + kValidateErrorUniqueItems, //!< Array has duplicate items but 'uniqueItems' is true. + kValidateErrorAdditionalItems, //!< Array has additional items that are not allowed by the schema. + + kValidateErrorMaxProperties, //!< Object has more members than 'maxProperties' value. + kValidateErrorMinProperties, //!< Object has less members than 'minProperties' value. + kValidateErrorRequired, //!< Object is missing one or more members required by the schema. + kValidateErrorAdditionalProperties, //!< Object has additional members that are not allowed by the schema. + kValidateErrorPatternProperties, //!< See other errors. + kValidateErrorDependencies, //!< Object has missing property or schema dependencies. + + kValidateErrorEnum, //!< Property has a value that is not one of its allowed enumerated values. + kValidateErrorType, //!< Property has a type that is not allowed by the schema. + + kValidateErrorOneOf, //!< Property did not match any of the sub-schemas specified by 'oneOf'. + kValidateErrorOneOfMatch, //!< Property matched more than one of the sub-schemas specified by 'oneOf'. + kValidateErrorAllOf, //!< Property did not match all of the sub-schemas specified by 'allOf'. + kValidateErrorAnyOf, //!< Property did not match any of the sub-schemas specified by 'anyOf'. + kValidateErrorNot, //!< Property matched the sub-schema specified by 'not'. + + kValidateErrorReadOnly, //!< Property is read-only but has been provided when validation is for writing + kValidateErrorWriteOnly //!< Property is write-only but has been provided when validation is for reading +}; + +//! Function pointer type of GetValidateError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetValidateError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetValidateErrorFunc GetValidateError = GetValidateError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetValidateError(validator.GetInvalidSchemaCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetValidateErrorFunc)(ValidateErrorCode); + +/////////////////////////////////////////////////////////////////////////////// +// SchemaErrorCode + +//! Error codes when validating. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericSchemaValidator +*/ +enum SchemaErrorCode { + kSchemaErrorNone = 0, //!< No error. + + kSchemaErrorStartUnknown, //!< Pointer to start of schema does not resolve to a location in the document + kSchemaErrorRefPlainName, //!< $ref fragment must be a JSON pointer + kSchemaErrorRefInvalid, //!< $ref must not be an empty string + kSchemaErrorRefPointerInvalid, //!< $ref fragment is not a valid JSON pointer at offset + kSchemaErrorRefUnknown, //!< $ref does not resolve to a location in the target document + kSchemaErrorRefCyclical, //!< $ref is cyclical + kSchemaErrorRefNoRemoteProvider, //!< $ref is remote but there is no remote provider + kSchemaErrorRefNoRemoteSchema, //!< $ref is remote but the remote provider did not return a schema + kSchemaErrorRegexInvalid, //!< Invalid regular expression in 'pattern' or 'patternProperties' + kSchemaErrorSpecUnknown, //!< JSON schema draft or OpenAPI version is not recognized + kSchemaErrorSpecUnsupported, //!< JSON schema draft or OpenAPI version is not supported + kSchemaErrorSpecIllegal, //!< Both JSON schema draft and OpenAPI version found in document + kSchemaErrorReadOnlyAndWriteOnly //!< Property must not be both 'readOnly' and 'writeOnly' +}; + +//! Function pointer type of GetSchemaError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetSchemaError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetSchemaErrorFunc GetSchemaError = GetSchemaError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetSchemaError(validator.GetInvalidSchemaCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetSchemaErrorFunc)(SchemaErrorCode); + +/////////////////////////////////////////////////////////////////////////////// +// PointerParseErrorCode + +//! Error code of JSON pointer parsing. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericPointer::GenericPointer, GenericPointer::GetParseErrorCode +*/ +enum PointerParseErrorCode { + kPointerParseErrorNone = 0, //!< The parse is successful + + kPointerParseErrorTokenMustBeginWithSolidus, //!< A token must begin with a '/' + kPointerParseErrorInvalidEscape, //!< Invalid escape + kPointerParseErrorInvalidPercentEncoding, //!< Invalid percent encoding in URI fragment + kPointerParseErrorCharacterMustPercentEncode //!< A character must percent encoded in URI fragment +}; + +//! Function pointer type of GetPointerParseError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetPointerParseError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetPointerParseErrorFunc GetPointerParseError = GetPointerParseError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetPointerParseError(pointer.GetParseErrorCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetPointerParseErrorFunc)(PointerParseErrorCode); + + RAPIDJSON_NAMESPACE_END #ifdef __clang__ diff --git a/third_party/rapidjson/include/rapidjson/filereadstream.h b/third_party/rapidjson/include/rapidjson/filereadstream.h index b56ea13b3..f8bb43cb0 100644 --- a/third_party/rapidjson/include/rapidjson/filereadstream.h +++ b/third_party/rapidjson/include/rapidjson/filereadstream.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -59,7 +59,7 @@ public: // For encoding detection only. const Ch* Peek4() const { - return (current_ + 4 <= bufferLast_) ? current_ : 0; + return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0; } private: @@ -68,7 +68,7 @@ private: ++current_; else if (!eof_) { count_ += readCount_; - readCount_ = fread(buffer_, 1, bufferSize_, fp_); + readCount_ = std::fread(buffer_, 1, bufferSize_, fp_); bufferLast_ = buffer_ + readCount_ - 1; current_ = buffer_; diff --git a/third_party/rapidjson/include/rapidjson/filewritestream.h b/third_party/rapidjson/include/rapidjson/filewritestream.h index 6378dd60e..5d89588c2 100644 --- a/third_party/rapidjson/include/rapidjson/filewritestream.h +++ b/third_party/rapidjson/include/rapidjson/filewritestream.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -25,7 +25,7 @@ RAPIDJSON_DIAG_OFF(unreachable-code) RAPIDJSON_NAMESPACE_BEGIN -//! Wrapper of C file stream for input using fread(). +//! Wrapper of C file stream for output using fwrite(). /*! \note implements Stream concept */ @@ -62,7 +62,7 @@ public: void Flush() { if (current_ != buffer_) { - size_t result = fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); + size_t result = std::fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); if (result < static_cast(current_ - buffer_)) { // failure deliberately ignored at this time // added to avoid warn_unused_result build errors diff --git a/third_party/rapidjson/include/rapidjson/fwd.h b/third_party/rapidjson/include/rapidjson/fwd.h index e8104e841..d62f77f0e 100644 --- a/third_party/rapidjson/include/rapidjson/fwd.h +++ b/third_party/rapidjson/include/rapidjson/fwd.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -102,7 +102,7 @@ class PrettyWriter; // document.h template -struct GenericMember; +class GenericMember; template class GenericMemberIterator; diff --git a/third_party/rapidjson/include/rapidjson/internal/biginteger.h b/third_party/rapidjson/include/rapidjson/internal/biginteger.h index 9d3e88c99..4930043dc 100644 --- a/third_party/rapidjson/include/rapidjson/internal/biginteger.h +++ b/third_party/rapidjson/include/rapidjson/internal/biginteger.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -17,9 +17,13 @@ #include "../rapidjson.h" -#if defined(_MSC_VER) && defined(_M_AMD64) +#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && defined(_M_AMD64) #include // for _umul128 +#if !defined(_ARM64EC_) #pragma intrinsic(_umul128) +#else +#pragma comment(lib,"softintrin") +#endif #endif RAPIDJSON_NAMESPACE_BEGIN @@ -37,7 +41,8 @@ public: digits_[0] = u; } - BigInteger(const char* decimals, size_t length) : count_(1) { + template + BigInteger(const Ch* decimals, size_t length) : count_(1) { RAPIDJSON_ASSERT(length > 0); digits_[0] = 0; size_t i = 0; @@ -133,7 +138,7 @@ public: RAPIDJSON_ASSERT(count_ + offset <= kCapacity); if (interShift == 0) { - std::memmove(&digits_[count_ - 1 + offset], &digits_[count_ - 1], count_ * sizeof(Type)); + std::memmove(digits_ + offset, digits_, count_ * sizeof(Type)); count_ += offset; } else { @@ -221,7 +226,8 @@ public: bool IsZero() const { return count_ == 1 && digits_[0] == 0; } private: - void AppendDecimal64(const char* begin, const char* end) { + template + void AppendDecimal64(const Ch* begin, const Ch* end) { uint64_t u = ParseUint64(begin, end); if (IsZero()) *this = u; @@ -236,11 +242,12 @@ private: digits_[count_++] = digit; } - static uint64_t ParseUint64(const char* begin, const char* end) { + template + static uint64_t ParseUint64(const Ch* begin, const Ch* end) { uint64_t r = 0; - for (const char* p = begin; p != end; ++p) { - RAPIDJSON_ASSERT(*p >= '0' && *p <= '9'); - r = r * 10u + static_cast(*p - '0'); + for (const Ch* p = begin; p != end; ++p) { + RAPIDJSON_ASSERT(*p >= Ch('0') && *p <= Ch('9')); + r = r * 10u + static_cast(*p - Ch('0')); } return r; } @@ -252,7 +259,7 @@ private: if (low < k) (*outHigh)++; return low; -#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) __extension__ typedef unsigned __int128 uint128; uint128 p = static_cast(a) * static_cast(b); p += k; diff --git a/third_party/rapidjson/include/rapidjson/internal/clzll.h b/third_party/rapidjson/include/rapidjson/internal/clzll.h new file mode 100644 index 000000000..8fc5118aa --- /dev/null +++ b/third_party/rapidjson/include/rapidjson/internal/clzll.h @@ -0,0 +1,71 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_CLZLL_H_ +#define RAPIDJSON_CLZLL_H_ + +#include "../rapidjson.h" + +#if defined(_MSC_VER) && !defined(UNDER_CE) +#include +#if defined(_WIN64) +#pragma intrinsic(_BitScanReverse64) +#else +#pragma intrinsic(_BitScanReverse) +#endif +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline uint32_t clzll(uint64_t x) { + // Passing 0 to __builtin_clzll is UB in GCC and results in an + // infinite loop in the software implementation. + RAPIDJSON_ASSERT(x != 0); + +#if defined(_MSC_VER) && !defined(UNDER_CE) + unsigned long r = 0; +#if defined(_WIN64) + _BitScanReverse64(&r, x); +#else + // Scan the high 32 bits. + if (_BitScanReverse(&r, static_cast(x >> 32))) + return 63 - (r + 32); + + // Scan the low 32 bits. + _BitScanReverse(&r, static_cast(x & 0xFFFFFFFF)); +#endif // _WIN64 + + return 63 - r; +#elif (defined(__GNUC__) && __GNUC__ >= 4) || RAPIDJSON_HAS_BUILTIN(__builtin_clzll) + // __builtin_clzll wrapper + return static_cast(__builtin_clzll(x)); +#else + // naive version + uint32_t r = 0; + while (!(x & (static_cast(1) << 63))) { + x <<= 1; + ++r; + } + + return r; +#endif // _MSC_VER +} + +#define RAPIDJSON_CLZLL RAPIDJSON_NAMESPACE::internal::clzll + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_CLZLL_H_ diff --git a/third_party/rapidjson/include/rapidjson/internal/diyfp.h b/third_party/rapidjson/include/rapidjson/internal/diyfp.h index c9fefdc61..1f60fb60c 100644 --- a/third_party/rapidjson/include/rapidjson/internal/diyfp.h +++ b/third_party/rapidjson/include/rapidjson/internal/diyfp.h @@ -1,15 +1,15 @@ // Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // -// 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 +// 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. // This is a C++ header-only implementation of Grisu2 algorithm from the publication: @@ -20,11 +20,16 @@ #define RAPIDJSON_DIYFP_H_ #include "../rapidjson.h" +#include "clzll.h" +#include -#if defined(_MSC_VER) && defined(_M_AMD64) +#if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER) #include -#pragma intrinsic(_BitScanReverse64) +#if !defined(_ARM64EC_) #pragma intrinsic(_umul128) +#else +#pragma comment(lib,"softintrin") +#endif #endif RAPIDJSON_NAMESPACE_BEGIN @@ -56,7 +61,7 @@ struct DiyFp { if (biased_e != 0) { f = significand + kDpHiddenBit; e = biased_e - kDpExponentBias; - } + } else { f = significand; e = kDpMinExponent + 1; @@ -74,7 +79,7 @@ struct DiyFp { if (l & (uint64_t(1) << 63)) // rounding h++; return DiyFp(h, e + rhs.e + 64); -#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) __extension__ typedef unsigned __int128 uint128; uint128 p = static_cast(f) * static_cast(rhs.f); uint64_t h = static_cast(p >> 64); @@ -99,21 +104,8 @@ struct DiyFp { } DiyFp Normalize() const { -#if defined(_MSC_VER) && defined(_M_AMD64) - unsigned long index; - _BitScanReverse64(&index, f); - return DiyFp(f << (63 - index), e - (63 - index)); -#elif defined(__GNUC__) && __GNUC__ >= 4 - int s = __builtin_clzll(f); + int s = static_cast(clzll(f)); return DiyFp(f << s, e - s); -#else - DiyFp res = *this; - while (!(res.f & (static_cast(1) << 63))) { - res.f <<= 1; - res.e--; - } - return res; -#endif } DiyFp NormalizeBoundary() const { @@ -141,7 +133,16 @@ struct DiyFp { double d; uint64_t u64; }u; - const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 : + RAPIDJSON_ASSERT(f <= kDpHiddenBit + kDpSignificandMask); + if (e < kDpDenormalExponent) { + // Underflow. + return 0.0; + } + if (e >= kDpMaxExponent) { + // Overflow. + return std::numeric_limits::infinity(); + } + const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 : static_cast(e + kDpExponentBias); u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize); return u.d; @@ -220,9 +221,10 @@ inline DiyFp GetCachedPowerByIndex(size_t index) { 641, 667, 694, 720, 747, 774, 800, 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066 }; + RAPIDJSON_ASSERT(index < 87); return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); } - + inline DiyFp GetCachedPower(int e, int* K) { //int k = static_cast(ceil((-61 - e) * 0.30102999566398114)) + 374; @@ -238,10 +240,11 @@ inline DiyFp GetCachedPower(int e, int* K) { } inline DiyFp GetCachedPower10(int exp, int *outExp) { - unsigned index = (static_cast(exp) + 348u) / 8u; - *outExp = -348 + static_cast(index) * 8; - return GetCachedPowerByIndex(index); - } + RAPIDJSON_ASSERT(exp >= -348); + unsigned index = static_cast(exp + 348) / 8u; + *outExp = -348 + static_cast(index) * 8; + return GetCachedPowerByIndex(index); +} #ifdef __GNUC__ RAPIDJSON_DIAG_POP diff --git a/third_party/rapidjson/include/rapidjson/internal/dtoa.h b/third_party/rapidjson/include/rapidjson/internal/dtoa.h index 8d6350e62..cd456721a 100644 --- a/third_party/rapidjson/include/rapidjson/internal/dtoa.h +++ b/third_party/rapidjson/include/rapidjson/internal/dtoa.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -41,7 +41,7 @@ inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uin } } -inline unsigned CountDecimalDigit32(uint32_t n) { +inline int CountDecimalDigit32(uint32_t n) { // Simple pure C++ implementation was faster than __builtin_clz version in this situation. if (n < 10) return 1; if (n < 100) return 2; @@ -58,12 +58,16 @@ inline unsigned CountDecimalDigit32(uint32_t n) { } inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) { - static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + static const uint64_t kPow10[] = { 1ULL, 10ULL, 100ULL, 1000ULL, 10000ULL, 100000ULL, 1000000ULL, 10000000ULL, 100000000ULL, + 1000000000ULL, 10000000000ULL, 100000000000ULL, 1000000000000ULL, + 10000000000000ULL, 100000000000000ULL, 1000000000000000ULL, + 10000000000000000ULL, 100000000000000000ULL, 1000000000000000000ULL, + 10000000000000000000ULL }; const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); const DiyFp wp_w = Mp - W; uint32_t p1 = static_cast(Mp.f >> -one.e); uint64_t p2 = Mp.f & (one.f - 1); - unsigned kappa = CountDecimalDigit32(p1); // kappa in [0, 9] + int kappa = CountDecimalDigit32(p1); // kappa in [0, 9] *len = 0; while (kappa > 0) { @@ -86,7 +90,7 @@ inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buff uint64_t tmp = (static_cast(p1) << -one.e) + p2; if (tmp <= delta) { *K += kappa; - GrisuRound(buffer, *len, delta, tmp, static_cast(kPow10[kappa]) << -one.e, wp_w.f); + GrisuRound(buffer, *len, delta, tmp, kPow10[kappa] << -one.e, wp_w.f); return; } } @@ -102,8 +106,8 @@ inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buff kappa--; if (p2 < delta) { *K += kappa; - int index = -static_cast(kappa); - GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[-static_cast(kappa)] : 0)); + int index = -kappa; + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 20 ? kPow10[index] : 0)); return; } } diff --git a/third_party/rapidjson/include/rapidjson/internal/ieee754.h b/third_party/rapidjson/include/rapidjson/internal/ieee754.h index 82bb0b99e..68c9e9664 100644 --- a/third_party/rapidjson/include/rapidjson/internal/ieee754.h +++ b/third_party/rapidjson/include/rapidjson/internal/ieee754.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -48,13 +48,13 @@ public: int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; } - static unsigned EffectiveSignificandSize(int order) { + static int EffectiveSignificandSize(int order) { if (order >= -1021) return 53; else if (order <= -1074) return 0; else - return static_cast(order) + 1074; + return order + 1074; } private: diff --git a/third_party/rapidjson/include/rapidjson/internal/itoa.h b/third_party/rapidjson/include/rapidjson/internal/itoa.h index 01a4e7e72..9fe8c932f 100644 --- a/third_party/rapidjson/include/rapidjson/internal/itoa.h +++ b/third_party/rapidjson/include/rapidjson/internal/itoa.h @@ -1,15 +1,15 @@ // Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // -// 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 +// 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 RAPIDJSON_ITOA_ @@ -37,12 +37,14 @@ inline const char* GetDigitsLut() { } inline char* u32toa(uint32_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); + const char* cDigitsLut = GetDigitsLut(); if (value < 10000) { const uint32_t d1 = (value / 100) << 1; const uint32_t d2 = (value % 100) << 1; - + if (value >= 1000) *buffer++ = cDigitsLut[d1]; if (value >= 100) @@ -55,13 +57,13 @@ inline char* u32toa(uint32_t value, char* buffer) { // value = bbbbcccc const uint32_t b = value / 10000; const uint32_t c = value % 10000; - + const uint32_t d1 = (b / 100) << 1; const uint32_t d2 = (b % 100) << 1; - + const uint32_t d3 = (c / 100) << 1; const uint32_t d4 = (c % 100) << 1; - + if (value >= 10000000) *buffer++ = cDigitsLut[d1]; if (value >= 1000000) @@ -69,7 +71,7 @@ inline char* u32toa(uint32_t value, char* buffer) { if (value >= 100000) *buffer++ = cDigitsLut[d2]; *buffer++ = cDigitsLut[d2 + 1]; - + *buffer++ = cDigitsLut[d3]; *buffer++ = cDigitsLut[d3 + 1]; *buffer++ = cDigitsLut[d4]; @@ -77,10 +79,10 @@ inline char* u32toa(uint32_t value, char* buffer) { } else { // value = aabbbbcccc in decimal - + const uint32_t a = value / 100000000; // 1 to 42 value %= 100000000; - + if (a >= 10) { const unsigned i = a << 1; *buffer++ = cDigitsLut[i]; @@ -91,13 +93,13 @@ inline char* u32toa(uint32_t value, char* buffer) { const uint32_t b = value / 10000; // 0 to 9999 const uint32_t c = value % 10000; // 0 to 9999 - + const uint32_t d1 = (b / 100) << 1; const uint32_t d2 = (b % 100) << 1; - + const uint32_t d3 = (c / 100) << 1; const uint32_t d4 = (c % 100) << 1; - + *buffer++ = cDigitsLut[d1]; *buffer++ = cDigitsLut[d1 + 1]; *buffer++ = cDigitsLut[d2]; @@ -111,6 +113,7 @@ inline char* u32toa(uint32_t value, char* buffer) { } inline char* i32toa(int32_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); uint32_t u = static_cast(value); if (value < 0) { *buffer++ = '-'; @@ -121,6 +124,7 @@ inline char* i32toa(int32_t value, char* buffer) { } inline char* u64toa(uint64_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); const char* cDigitsLut = GetDigitsLut(); const uint64_t kTen8 = 100000000; const uint64_t kTen9 = kTen8 * 10; @@ -131,13 +135,13 @@ inline char* u64toa(uint64_t value, char* buffer) { const uint64_t kTen14 = kTen8 * 1000000; const uint64_t kTen15 = kTen8 * 10000000; const uint64_t kTen16 = kTen8 * kTen8; - + if (value < kTen8) { uint32_t v = static_cast(value); if (v < 10000) { const uint32_t d1 = (v / 100) << 1; const uint32_t d2 = (v % 100) << 1; - + if (v >= 1000) *buffer++ = cDigitsLut[d1]; if (v >= 100) @@ -150,13 +154,13 @@ inline char* u64toa(uint64_t value, char* buffer) { // value = bbbbcccc const uint32_t b = v / 10000; const uint32_t c = v % 10000; - + const uint32_t d1 = (b / 100) << 1; const uint32_t d2 = (b % 100) << 1; - + const uint32_t d3 = (c / 100) << 1; const uint32_t d4 = (c % 100) << 1; - + if (value >= 10000000) *buffer++ = cDigitsLut[d1]; if (value >= 1000000) @@ -164,7 +168,7 @@ inline char* u64toa(uint64_t value, char* buffer) { if (value >= 100000) *buffer++ = cDigitsLut[d2]; *buffer++ = cDigitsLut[d2 + 1]; - + *buffer++ = cDigitsLut[d3]; *buffer++ = cDigitsLut[d3 + 1]; *buffer++ = cDigitsLut[d4]; @@ -174,22 +178,22 @@ inline char* u64toa(uint64_t value, char* buffer) { else if (value < kTen16) { const uint32_t v0 = static_cast(value / kTen8); const uint32_t v1 = static_cast(value % kTen8); - + const uint32_t b0 = v0 / 10000; const uint32_t c0 = v0 % 10000; - + const uint32_t d1 = (b0 / 100) << 1; const uint32_t d2 = (b0 % 100) << 1; - + const uint32_t d3 = (c0 / 100) << 1; const uint32_t d4 = (c0 % 100) << 1; const uint32_t b1 = v1 / 10000; const uint32_t c1 = v1 % 10000; - + const uint32_t d5 = (b1 / 100) << 1; const uint32_t d6 = (b1 % 100) << 1; - + const uint32_t d7 = (c1 / 100) << 1; const uint32_t d8 = (c1 % 100) << 1; @@ -207,9 +211,8 @@ inline char* u64toa(uint64_t value, char* buffer) { *buffer++ = cDigitsLut[d3 + 1]; if (value >= kTen9) *buffer++ = cDigitsLut[d4]; - if (value >= kTen8) - *buffer++ = cDigitsLut[d4 + 1]; - + + *buffer++ = cDigitsLut[d4 + 1]; *buffer++ = cDigitsLut[d5]; *buffer++ = cDigitsLut[d5 + 1]; *buffer++ = cDigitsLut[d6]; @@ -222,7 +225,7 @@ inline char* u64toa(uint64_t value, char* buffer) { else { const uint32_t a = static_cast(value / kTen16); // 1 to 1844 value %= kTen16; - + if (a < 10) *buffer++ = static_cast('0' + static_cast(a)); else if (a < 100) { @@ -232,7 +235,7 @@ inline char* u64toa(uint64_t value, char* buffer) { } else if (a < 1000) { *buffer++ = static_cast('0' + static_cast(a / 100)); - + const uint32_t i = (a % 100) << 1; *buffer++ = cDigitsLut[i]; *buffer++ = cDigitsLut[i + 1]; @@ -245,28 +248,28 @@ inline char* u64toa(uint64_t value, char* buffer) { *buffer++ = cDigitsLut[j]; *buffer++ = cDigitsLut[j + 1]; } - + const uint32_t v0 = static_cast(value / kTen8); const uint32_t v1 = static_cast(value % kTen8); - + const uint32_t b0 = v0 / 10000; const uint32_t c0 = v0 % 10000; - + const uint32_t d1 = (b0 / 100) << 1; const uint32_t d2 = (b0 % 100) << 1; - + const uint32_t d3 = (c0 / 100) << 1; const uint32_t d4 = (c0 % 100) << 1; - + const uint32_t b1 = v1 / 10000; const uint32_t c1 = v1 % 10000; - + const uint32_t d5 = (b1 / 100) << 1; const uint32_t d6 = (b1 % 100) << 1; - + const uint32_t d7 = (c1 / 100) << 1; const uint32_t d8 = (c1 % 100) << 1; - + *buffer++ = cDigitsLut[d1]; *buffer++ = cDigitsLut[d1 + 1]; *buffer++ = cDigitsLut[d2]; @@ -284,11 +287,12 @@ inline char* u64toa(uint64_t value, char* buffer) { *buffer++ = cDigitsLut[d8]; *buffer++ = cDigitsLut[d8 + 1]; } - + return buffer; } inline char* i64toa(int64_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); uint64_t u = static_cast(value); if (value < 0) { *buffer++ = '-'; diff --git a/third_party/rapidjson/include/rapidjson/internal/meta.h b/third_party/rapidjson/include/rapidjson/internal/meta.h index 5a9aaa428..27092dc0d 100644 --- a/third_party/rapidjson/include/rapidjson/internal/meta.h +++ b/third_party/rapidjson/include/rapidjson/internal/meta.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -21,7 +21,8 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif -#if defined(_MSC_VER) + +#if defined(_MSC_VER) && !defined(__clang__) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(6334) #endif @@ -174,7 +175,11 @@ template struct RemoveSfinaeTag { typedef T Type; RAPIDJSON_NAMESPACE_END //@endcond -#if defined(__GNUC__) || defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif diff --git a/third_party/rapidjson/include/rapidjson/internal/pow10.h b/third_party/rapidjson/include/rapidjson/internal/pow10.h index 02f475d70..eae1a43ed 100644 --- a/third_party/rapidjson/include/rapidjson/internal/pow10.h +++ b/third_party/rapidjson/include/rapidjson/internal/pow10.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/third_party/rapidjson/include/rapidjson/internal/regex.h b/third_party/rapidjson/include/rapidjson/internal/regex.h index 422a5240b..6446c403a 100644 --- a/third_party/rapidjson/include/rapidjson/internal/regex.h +++ b/third_party/rapidjson/include/rapidjson/internal/regex.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -23,7 +23,9 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(padded) RAPIDJSON_DIAG_OFF(switch-enum) -RAPIDJSON_DIAG_OFF(implicit-fallthrough) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated #endif #ifdef __GNUC__ @@ -31,11 +33,6 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif -#ifdef _MSC_VER -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated -#endif - #ifndef RAPIDJSON_REGEX_VERBOSE #define RAPIDJSON_REGEX_VERBOSE 0 #endif @@ -43,12 +40,40 @@ RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated RAPIDJSON_NAMESPACE_BEGIN namespace internal { +/////////////////////////////////////////////////////////////////////////////// +// DecodedStream + +template +class DecodedStream { +public: + DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); } + unsigned Peek() { return codepoint_; } + unsigned Take() { + unsigned c = codepoint_; + if (c) // No further decoding when '\0' + Decode(); + return c; + } + +private: + void Decode() { + if (!Encoding::Decode(ss_, &codepoint_)) + codepoint_ = 0; + } + + SourceStream& ss_; + unsigned codepoint_; +}; + /////////////////////////////////////////////////////////////////////////////// // GenericRegex static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1 static const SizeType kRegexInvalidRange = ~SizeType(0); +template +class GenericRegexSearch; + //! Regular expression engine with subset of ECMAscript grammar. /*! Supported regular expression syntax: @@ -84,45 +109,29 @@ static const SizeType kRegexInvalidRange = ~SizeType(0); template class GenericRegex { public: + typedef Encoding EncodingType; typedef typename Encoding::Ch Ch; + template friend class GenericRegexSearch; GenericRegex(const Ch* source, Allocator* allocator = 0) : - states_(allocator, 256), ranges_(allocator, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), - stateSet_(), state0_(allocator, 0), state1_(allocator, 0), anchorBegin_(), anchorEnd_() + ownAllocator_(allocator ? 0 : RAPIDJSON_NEW(Allocator)()), allocator_(allocator ? allocator : ownAllocator_), + states_(allocator_, 256), ranges_(allocator_, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), + anchorBegin_(), anchorEnd_() { GenericStringStream ss(source); - DecodedStream > ds(ss); + DecodedStream, Encoding> ds(ss); Parse(ds); } - ~GenericRegex() { - Allocator::Free(stateSet_); + ~GenericRegex() + { + RAPIDJSON_DELETE(ownAllocator_); } bool IsValid() const { return root_ != kRegexInvalidState; } - template - bool Match(InputStream& is) const { - return SearchWithAnchoring(is, true, true); - } - - bool Match(const Ch* s) const { - GenericStringStream is(s); - return Match(is); - } - - template - bool Search(InputStream& is) const { - return SearchWithAnchoring(is, anchorBegin_, anchorEnd_); - } - - bool Search(const Ch* s) const { - GenericStringStream is(s); - return Search(is); - } - private: enum Operator { kZeroOrOne, @@ -157,28 +166,6 @@ private: SizeType minIndex; }; - template - class DecodedStream { - public: - DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); } - unsigned Peek() { return codepoint_; } - unsigned Take() { - unsigned c = codepoint_; - if (c) // No further decoding when '\0' - Decode(); - return c; - } - - private: - void Decode() { - if (!Encoding::Decode(ss_, &codepoint_)) - codepoint_ = 0; - } - - SourceStream& ss_; - unsigned codepoint_; - }; - State& GetState(SizeType index) { RAPIDJSON_ASSERT(index < stateCount_); return states_.template Bottom()[index]; @@ -200,11 +187,10 @@ private: } template - void Parse(DecodedStream& ds) { - Allocator allocator; - Stack operandStack(&allocator, 256); // Frag - Stack operatorStack(&allocator, 256); // Operator - Stack atomCountStack(&allocator, 256); // unsigned (Atom per parenthesis) + void Parse(DecodedStream& ds) { + Stack operandStack(allocator_, 256); // Frag + Stack operatorStack(allocator_, 256); // Operator + Stack atomCountStack(allocator_, 256); // unsigned (Atom per parenthesis) *atomCountStack.template Push() = 0; @@ -301,6 +287,7 @@ private: if (!CharacterEscape(ds, &codepoint)) return; // Unsupported escape character // fall through to default + RAPIDJSON_DELIBERATE_FALLTHROUGH; default: // Pattern character PushOperand(operandStack, codepoint); @@ -327,14 +314,6 @@ private: printf("\n"); #endif } - - // Preallocate buffer for SearchWithAnchoring() - RAPIDJSON_ASSERT(stateSet_ == 0); - if (stateCount_ > 0) { - stateSet_ = static_cast(states_.GetAllocator().Malloc(GetStateSetSize())); - state0_.template Reserve(stateCount_); - state1_.template Reserve(stateCount_); - } } SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) { @@ -413,8 +392,7 @@ private: } return false; - default: - RAPIDJSON_ASSERT(op == kOneOrMore); + case kOneOrMore: if (operandStack.GetSize() >= sizeof(Frag)) { Frag e = *operandStack.template Pop(1); SizeType s = NewState(kRegexInvalidState, e.start, 0); @@ -423,6 +401,10 @@ private: return true; } return false; + + default: + // syntax error (e.g. unclosed kLeftParenthesis) + return false; } } @@ -483,7 +465,7 @@ private: } template - bool ParseUnsigned(DecodedStream& ds, unsigned* u) { + bool ParseUnsigned(DecodedStream& ds, unsigned* u) { unsigned r = 0; if (ds.Peek() < '0' || ds.Peek() > '9') return false; @@ -497,7 +479,7 @@ private: } template - bool ParseRange(DecodedStream& ds, SizeType* range) { + bool ParseRange(DecodedStream& ds, SizeType* range) { bool isBegin = true; bool negate = false; int step = 0; @@ -535,6 +517,7 @@ private: else if (!CharacterEscape(ds, &codepoint)) return false; // fall through to default + RAPIDJSON_DELIBERATE_FALLTHROUGH; default: switch (step) { @@ -544,6 +527,7 @@ private: break; } // fall through to step 0 for other characters + RAPIDJSON_DELIBERATE_FALLTHROUGH; case 0: { @@ -575,7 +559,7 @@ private: } template - bool CharacterEscape(DecodedStream& ds, unsigned* escapedCodepoint) { + bool CharacterEscape(DecodedStream& ds, unsigned* escapedCodepoint) { unsigned codepoint; switch (codepoint = ds.Take()) { case '^': @@ -603,72 +587,8 @@ private: } } - template - bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) const { - RAPIDJSON_ASSERT(IsValid()); - DecodedStream ds(is); - - state0_.Clear(); - Stack *current = &state0_, *next = &state1_; - const size_t stateSetSize = GetStateSetSize(); - std::memset(stateSet_, 0, stateSetSize); - - bool matched = AddState(*current, root_); - unsigned codepoint; - while (!current->Empty() && (codepoint = ds.Take()) != 0) { - std::memset(stateSet_, 0, stateSetSize); - next->Clear(); - matched = false; - for (const SizeType* s = current->template Bottom(); s != current->template End(); ++s) { - const State& sr = GetState(*s); - if (sr.codepoint == codepoint || - sr.codepoint == kAnyCharacterClass || - (sr.codepoint == kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint))) - { - matched = AddState(*next, sr.out) || matched; - if (!anchorEnd && matched) - return true; - } - if (!anchorBegin) - AddState(*next, root_); - } - internal::Swap(current, next); - } - - return matched; - } - - size_t GetStateSetSize() const { - return (stateCount_ + 31) / 32 * 4; - } - - // Return whether the added states is a match state - bool AddState(Stack& l, SizeType index) const { - RAPIDJSON_ASSERT(index != kRegexInvalidState); - - const State& s = GetState(index); - if (s.out1 != kRegexInvalidState) { // Split - bool matched = AddState(l, s.out); - return AddState(l, s.out1) || matched; - } - else if (!(stateSet_[index >> 5] & (1 << (index & 31)))) { - stateSet_[index >> 5] |= (1 << (index & 31)); - *l.template PushUnsafe() = index; - } - return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation. - } - - bool MatchRange(SizeType rangeIndex, unsigned codepoint) const { - bool yes = (GetRange(rangeIndex).start & kRangeNegationFlag) == 0; - while (rangeIndex != kRegexInvalidRange) { - const Range& r = GetRange(rangeIndex); - if (codepoint >= (r.start & ~kRangeNegationFlag) && codepoint <= r.end) - return yes; - rangeIndex = r.next; - } - return !yes; - } - + Allocator* ownAllocator_; + Allocator* allocator_; Stack states_; Stack ranges_; SizeType root_; @@ -678,23 +598,141 @@ private: static const unsigned kInfinityQuantifier = ~0u; // For SearchWithAnchoring() - uint32_t* stateSet_; // allocated by states_.GetAllocator() - mutable Stack state0_; - mutable Stack state1_; bool anchorBegin_; bool anchorEnd_; }; +template +class GenericRegexSearch { +public: + typedef typename RegexType::EncodingType Encoding; + typedef typename Encoding::Ch Ch; + + GenericRegexSearch(const RegexType& regex, Allocator* allocator = 0) : + regex_(regex), allocator_(allocator), ownAllocator_(0), + state0_(allocator, 0), state1_(allocator, 0), stateSet_() + { + RAPIDJSON_ASSERT(regex_.IsValid()); + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + stateSet_ = static_cast(allocator_->Malloc(GetStateSetSize())); + state0_.template Reserve(regex_.stateCount_); + state1_.template Reserve(regex_.stateCount_); + } + + ~GenericRegexSearch() { + Allocator::Free(stateSet_); + RAPIDJSON_DELETE(ownAllocator_); + } + + template + bool Match(InputStream& is) { + return SearchWithAnchoring(is, true, true); + } + + bool Match(const Ch* s) { + GenericStringStream is(s); + return Match(is); + } + + template + bool Search(InputStream& is) { + return SearchWithAnchoring(is, regex_.anchorBegin_, regex_.anchorEnd_); + } + + bool Search(const Ch* s) { + GenericStringStream is(s); + return Search(is); + } + +private: + typedef typename RegexType::State State; + typedef typename RegexType::Range Range; + + template + bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) { + DecodedStream ds(is); + + state0_.Clear(); + Stack *current = &state0_, *next = &state1_; + const size_t stateSetSize = GetStateSetSize(); + std::memset(stateSet_, 0, stateSetSize); + + bool matched = AddState(*current, regex_.root_); + unsigned codepoint; + while (!current->Empty() && (codepoint = ds.Take()) != 0) { + std::memset(stateSet_, 0, stateSetSize); + next->Clear(); + matched = false; + for (const SizeType* s = current->template Bottom(); s != current->template End(); ++s) { + const State& sr = regex_.GetState(*s); + if (sr.codepoint == codepoint || + sr.codepoint == RegexType::kAnyCharacterClass || + (sr.codepoint == RegexType::kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint))) + { + matched = AddState(*next, sr.out) || matched; + if (!anchorEnd && matched) + return true; + } + if (!anchorBegin) + AddState(*next, regex_.root_); + } + internal::Swap(current, next); + } + + return matched; + } + + size_t GetStateSetSize() const { + return (regex_.stateCount_ + 31) / 32 * 4; + } + + // Return whether the added states is a match state + bool AddState(Stack& l, SizeType index) { + RAPIDJSON_ASSERT(index != kRegexInvalidState); + + const State& s = regex_.GetState(index); + if (s.out1 != kRegexInvalidState) { // Split + bool matched = AddState(l, s.out); + return AddState(l, s.out1) || matched; + } + else if (!(stateSet_[index >> 5] & (1u << (index & 31)))) { + stateSet_[index >> 5] |= (1u << (index & 31)); + *l.template PushUnsafe() = index; + } + return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation. + } + + bool MatchRange(SizeType rangeIndex, unsigned codepoint) const { + bool yes = (regex_.GetRange(rangeIndex).start & RegexType::kRangeNegationFlag) == 0; + while (rangeIndex != kRegexInvalidRange) { + const Range& r = regex_.GetRange(rangeIndex); + if (codepoint >= (r.start & ~RegexType::kRangeNegationFlag) && codepoint <= r.end) + return yes; + rangeIndex = r.next; + } + return !yes; + } + + const RegexType& regex_; + Allocator* allocator_; + Allocator* ownAllocator_; + Stack state0_; + Stack state1_; + uint32_t* stateSet_; +}; + typedef GenericRegex > Regex; +typedef GenericRegexSearch RegexSearch; } // namespace internal RAPIDJSON_NAMESPACE_END -#ifdef __clang__ +#ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif -#ifdef _MSC_VER +#if defined(__clang__) || defined(_MSC_VER) RAPIDJSON_DIAG_POP #endif diff --git a/third_party/rapidjson/include/rapidjson/internal/stack.h b/third_party/rapidjson/include/rapidjson/internal/stack.h index 022c9aab4..73abd706e 100644 --- a/third_party/rapidjson/include/rapidjson/internal/stack.h +++ b/third_party/rapidjson/include/rapidjson/internal/stack.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -17,6 +17,7 @@ #include "../allocators.h" #include "swap.h" +#include #if defined(__clang__) RAPIDJSON_DIAG_PUSH @@ -100,7 +101,7 @@ public: void ShrinkToFit() { if (Empty()) { // If the stack is empty, completely deallocate the memory. - Allocator::Free(stack_); + Allocator::Free(stack_); // NOLINT (+clang-analyzer-unix.Malloc) stack_ = 0; stackTop_ = 0; stackEnd_ = 0; @@ -114,7 +115,7 @@ public: template RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) { // Expand the stack if needed - if (RAPIDJSON_UNLIKELY(stackTop_ + sizeof(T) * count > stackEnd_)) + if (RAPIDJSON_UNLIKELY(static_cast(sizeof(T) * count) > (stackEnd_ - stackTop_))) Expand(count); } @@ -126,7 +127,8 @@ public: template RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) { - RAPIDJSON_ASSERT(stackTop_ + sizeof(T) * count <= stackEnd_); + RAPIDJSON_ASSERT(stackTop_); + RAPIDJSON_ASSERT(static_cast(sizeof(T) * count) <= (stackEnd_ - stackTop_)); T* ret = reinterpret_cast(stackTop_); stackTop_ += sizeof(T) * count; return ret; @@ -183,7 +185,7 @@ private: size_t newCapacity; if (stack_ == 0) { if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); newCapacity = initialCapacity_; } else { newCapacity = GetCapacity(); diff --git a/third_party/rapidjson/include/rapidjson/internal/strfunc.h b/third_party/rapidjson/include/rapidjson/internal/strfunc.h index 2edfae526..b698a8f43 100644 --- a/third_party/rapidjson/include/rapidjson/internal/strfunc.h +++ b/third_party/rapidjson/include/rapidjson/internal/strfunc.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -16,6 +16,7 @@ #define RAPIDJSON_INTERNAL_STRFUNC_H_ #include "../stream.h" +#include RAPIDJSON_NAMESPACE_BEGIN namespace internal { @@ -28,14 +29,41 @@ namespace internal { */ template inline SizeType StrLen(const Ch* s) { + RAPIDJSON_ASSERT(s != 0); const Ch* p = s; while (*p) ++p; return SizeType(p - s); } +template <> +inline SizeType StrLen(const char* s) { + return SizeType(std::strlen(s)); +} + +template <> +inline SizeType StrLen(const wchar_t* s) { + return SizeType(std::wcslen(s)); +} + +//! Custom strcmpn() which works on different character types. +/*! \tparam Ch Character type (e.g. char, wchar_t, short) + \param s1 Null-terminated input string. + \param s2 Null-terminated input string. + \return 0 if equal +*/ +template +inline int StrCmp(const Ch* s1, const Ch* s2) { + RAPIDJSON_ASSERT(s1 != 0); + RAPIDJSON_ASSERT(s2 != 0); + while(*s1 && (*s1 == *s2)) { s1++; s2++; } + return static_cast(*s1) < static_cast(*s2) ? -1 : static_cast(*s1) > static_cast(*s2); +} + //! Returns number of code points in a encoded string. template bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { + RAPIDJSON_ASSERT(s != 0); + RAPIDJSON_ASSERT(outCount != 0); GenericStringStream is(s); const typename Encoding::Ch* end = s + length; SizeType count = 0; diff --git a/third_party/rapidjson/include/rapidjson/internal/strtod.h b/third_party/rapidjson/include/rapidjson/internal/strtod.h index 289c413b0..55f0e380b 100644 --- a/third_party/rapidjson/include/rapidjson/internal/strtod.h +++ b/third_party/rapidjson/include/rapidjson/internal/strtod.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -19,6 +19,8 @@ #include "biginteger.h" #include "diyfp.h" #include "pow10.h" +#include +#include RAPIDJSON_NAMESPACE_BEGIN namespace internal { @@ -126,46 +128,47 @@ inline bool StrtodFast(double d, int p, double* result) { } // Compute an approximation and see if it is within 1/2 ULP -inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosition, int exp, double* result) { +template +inline bool StrtodDiyFp(const Ch* decimals, int dLen, int dExp, double* result) { uint64_t significand = 0; - size_t i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 - for (; i < length; i++) { + int i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 + for (; i < dLen; i++) { if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || - (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5')) + (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > Ch('5'))) break; - significand = significand * 10u + static_cast(decimals[i] - '0'); + significand = significand * 10u + static_cast(decimals[i] - Ch('0')); } - if (i < length && decimals[i] >= '5') // Rounding + if (i < dLen && decimals[i] >= Ch('5')) // Rounding significand++; - size_t remaining = length - i; - const unsigned kUlpShift = 3; - const unsigned kUlp = 1 << kUlpShift; + int remaining = dLen - i; + const int kUlpShift = 3; + const int kUlp = 1 << kUlpShift; int64_t error = (remaining == 0) ? 0 : kUlp / 2; DiyFp v(significand, 0); v = v.Normalize(); error <<= -v.e; - const int dExp = static_cast(decimalPosition) - static_cast(i) + exp; + dExp += remaining; int actualExp; DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); if (actualExp != dExp) { static const DiyFp kPow10[] = { - DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 00000000), -60), // 10^1 - DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 00000000), -57), // 10^2 - DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 00000000), -54), // 10^3 - DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 00000000), -50), // 10^4 - DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 00000000), -47), // 10^5 - DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44), // 10^6 - DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7 + DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 0x00000000), -60), // 10^1 + DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 0x00000000), -57), // 10^2 + DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 0x00000000), -54), // 10^3 + DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), -50), // 10^4 + DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 0x00000000), -47), // 10^5 + DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 0x00000000), -44), // 10^6 + DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 0x00000000), -40) // 10^7 }; - int adjustment = dExp - actualExp - 1; - RAPIDJSON_ASSERT(adjustment >= 0 && adjustment < 7); - v = v * kPow10[adjustment]; - if (length + static_cast(adjustment)> 19u) // has more digits than decimal digits in 64-bit + int adjustment = dExp - actualExp; + RAPIDJSON_ASSERT(adjustment >= 1 && adjustment < 8); + v = v * kPow10[adjustment - 1]; + if (dLen + adjustment > 19) // has more digits than decimal digits in 64-bit error += kUlp / 2; } @@ -177,17 +180,17 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit v = v.Normalize(); error <<= oldExp - v.e; - const unsigned effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); - unsigned precisionSize = 64 - effectiveSignificandSize; + const int effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); + int precisionSize = 64 - effectiveSignificandSize; if (precisionSize + kUlpShift >= 64) { - unsigned scaleExp = (precisionSize + kUlpShift) - 63; + int scaleExp = (precisionSize + kUlpShift) - 63; v.f >>= scaleExp; v.e += scaleExp; - error = (error >> scaleExp) + 1 + static_cast(kUlp); + error = (error >> scaleExp) + 1 + kUlp; precisionSize -= scaleExp; } - DiyFp rounded(v.f >> precisionSize, v.e + static_cast(precisionSize)); + DiyFp rounded(v.f >> precisionSize, v.e + precisionSize); const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp; const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp; if (precisionBits >= halfWay + static_cast(error)) { @@ -203,9 +206,10 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit return halfWay - static_cast(error) >= precisionBits || precisionBits >= halfWay + static_cast(error); } -inline double StrtodBigInteger(double approx, const char* decimals, size_t length, size_t decimalPosition, int exp) { - const BigInteger dInt(decimals, length); - const int dExp = static_cast(decimalPosition) - static_cast(length) + exp; +template +inline double StrtodBigInteger(double approx, const Ch* decimals, int dLen, int dExp) { + RAPIDJSON_ASSERT(dLen >= 0); + const BigInteger dInt(decimals, static_cast(dLen)); Double a(approx); int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp); if (cmp < 0) @@ -221,46 +225,66 @@ inline double StrtodBigInteger(double approx, const char* decimals, size_t lengt return a.NextPositiveDouble(); } -inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) { +template +inline double StrtodFullPrecision(double d, int p, const Ch* decimals, size_t length, size_t decimalPosition, int exp) { RAPIDJSON_ASSERT(d >= 0.0); RAPIDJSON_ASSERT(length >= 1); - double result; + double result = 0.0; if (StrtodFast(d, p, &result)) return result; + RAPIDJSON_ASSERT(length <= INT_MAX); + int dLen = static_cast(length); + + RAPIDJSON_ASSERT(length >= decimalPosition); + RAPIDJSON_ASSERT(length - decimalPosition <= INT_MAX); + int dExpAdjust = static_cast(length - decimalPosition); + + RAPIDJSON_ASSERT(exp >= INT_MIN + dExpAdjust); + int dExp = exp - dExpAdjust; + + // Make sure length+dExp does not overflow + RAPIDJSON_ASSERT(dExp <= INT_MAX - dLen); + // Trim leading zeros - while (*decimals == '0' && length > 1) { - length--; + while (dLen > 0 && *decimals == '0') { + dLen--; decimals++; - decimalPosition--; } // Trim trailing zeros - while (decimals[length - 1] == '0' && length > 1) { - length--; - decimalPosition--; - exp++; + while (dLen > 0 && decimals[dLen - 1] == '0') { + dLen--; + dExp++; + } + + if (dLen == 0) { // Buffer only contains zeros. + return 0.0; } // Trim right-most digits - const int kMaxDecimalDigit = 780; - if (static_cast(length) > kMaxDecimalDigit) { - int delta = (static_cast(length) - kMaxDecimalDigit); - exp += delta; - decimalPosition -= static_cast(delta); - length = kMaxDecimalDigit; + const int kMaxDecimalDigit = 767 + 1; + if (dLen > kMaxDecimalDigit) { + dExp += dLen - kMaxDecimalDigit; + dLen = kMaxDecimalDigit; } - // If too small, underflow to zero - if (int(length) + exp < -324) + // If too small, underflow to zero. + // Any x <= 10^-324 is interpreted as zero. + if (dLen + dExp <= -324) return 0.0; - if (StrtodDiyFp(decimals, length, decimalPosition, exp, &result)) + // If too large, overflow to infinity. + // Any x >= 10^309 is interpreted as +infinity. + if (dLen + dExp > 309) + return std::numeric_limits::infinity(); + + if (StrtodDiyFp(decimals, dLen, dExp, &result)) return result; // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison - return StrtodBigInteger(result, decimals, length, decimalPosition, exp); + return StrtodBigInteger(result, decimals, dLen, dExp); } } // namespace internal diff --git a/third_party/rapidjson/include/rapidjson/internal/swap.h b/third_party/rapidjson/include/rapidjson/internal/swap.h index 666e49f97..2cf92f93a 100644 --- a/third_party/rapidjson/include/rapidjson/internal/swap.h +++ b/third_party/rapidjson/include/rapidjson/internal/swap.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/third_party/rapidjson/include/rapidjson/istreamwrapper.h b/third_party/rapidjson/include/rapidjson/istreamwrapper.h index f5fe28977..01437ec01 100644 --- a/third_party/rapidjson/include/rapidjson/istreamwrapper.h +++ b/third_party/rapidjson/include/rapidjson/istreamwrapper.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -17,13 +17,12 @@ #include "stream.h" #include +#include #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(padded) -#endif - -#ifdef _MSC_VER +#elif defined(_MSC_VER) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized #endif @@ -50,57 +49,71 @@ template class BasicIStreamWrapper { public: typedef typename StreamType::char_type Ch; - BasicIStreamWrapper(StreamType& stream) : stream_(stream), count_(), peekBuffer_() {} - Ch Peek() const { - typename StreamType::int_type c = stream_.peek(); - return RAPIDJSON_LIKELY(c != StreamType::traits_type::eof()) ? static_cast(c) : '\0'; + //! Constructor. + /*! + \param stream stream opened for read. + */ + BasicIStreamWrapper(StreamType &stream) : stream_(stream), buffer_(peekBuffer_), bufferSize_(4), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + Read(); } - Ch Take() { - typename StreamType::int_type c = stream_.get(); - if (RAPIDJSON_LIKELY(c != StreamType::traits_type::eof())) { - count_++; - return static_cast(c); - } - else - return '\0'; + //! Constructor. + /*! + \param stream stream opened for read. + \param buffer user-supplied buffer. + \param bufferSize size of buffer in bytes. Must >=4 bytes. + */ + BasicIStreamWrapper(StreamType &stream, char* buffer, size_t bufferSize) : stream_(stream), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + RAPIDJSON_ASSERT(bufferSize >= 4); + Read(); } - // tellg() may return -1 when failed. So we count by ourself. - size_t Tell() const { return count_; } + Ch Peek() const { return *current_; } + Ch Take() { Ch c = *current_; Read(); return c; } + size_t Tell() const { return count_ + static_cast(current_ - buffer_); } - Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + // Not implemented void Put(Ch) { RAPIDJSON_ASSERT(false); } - void Flush() { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } // For encoding detection only. const Ch* Peek4() const { - RAPIDJSON_ASSERT(sizeof(Ch) == 1); // Only usable for byte stream. - int i; - bool hasError = false; - for (i = 0; i < 4; ++i) { - typename StreamType::int_type c = stream_.get(); - if (c == StreamType::traits_type::eof()) { - hasError = true; - stream_.clear(); - break; - } - peekBuffer_[i] = static_cast(c); - } - for (--i; i >= 0; --i) - stream_.putback(peekBuffer_[i]); - return !hasError ? peekBuffer_ : 0; + return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0; } private: + BasicIStreamWrapper(); BasicIStreamWrapper(const BasicIStreamWrapper&); BasicIStreamWrapper& operator=(const BasicIStreamWrapper&); - StreamType& stream_; - size_t count_; //!< Number of characters read. Note: - mutable Ch peekBuffer_[4]; + void Read() { + if (current_ < bufferLast_) + ++current_; + else if (!eof_) { + count_ += readCount_; + readCount_ = bufferSize_; + bufferLast_ = buffer_ + readCount_ - 1; + current_ = buffer_; + + if (!stream_.read(buffer_, static_cast(bufferSize_))) { + readCount_ = static_cast(stream_.gcount()); + *(bufferLast_ = buffer_ + readCount_) = '\0'; + eof_ = true; + } + } + } + + StreamType &stream_; + Ch peekBuffer_[4], *buffer_; + size_t bufferSize_; + Ch *bufferLast_; + Ch *current_; + size_t readCount_; + size_t count_; //!< Number of characters read + bool eof_; }; typedef BasicIStreamWrapper IStreamWrapper; diff --git a/third_party/rapidjson/include/rapidjson/memorybuffer.h b/third_party/rapidjson/include/rapidjson/memorybuffer.h index 39bee1dec..ffbc41ed1 100644 --- a/third_party/rapidjson/include/rapidjson/memorybuffer.h +++ b/third_party/rapidjson/include/rapidjson/memorybuffer.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/third_party/rapidjson/include/rapidjson/memorystream.h b/third_party/rapidjson/include/rapidjson/memorystream.h index 1d71d8a4f..77af6c999 100644 --- a/third_party/rapidjson/include/rapidjson/memorystream.h +++ b/third_party/rapidjson/include/rapidjson/memorystream.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/third_party/rapidjson/include/rapidjson/ostreamwrapper.h b/third_party/rapidjson/include/rapidjson/ostreamwrapper.h index 6f4667c08..11ed4d33f 100644 --- a/third_party/rapidjson/include/rapidjson/ostreamwrapper.h +++ b/third_party/rapidjson/include/rapidjson/ostreamwrapper.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/third_party/rapidjson/include/rapidjson/pointer.h b/third_party/rapidjson/include/rapidjson/pointer.h index 0206ac1c8..6f4ef3892 100644 --- a/third_party/rapidjson/include/rapidjson/pointer.h +++ b/third_party/rapidjson/include/rapidjson/pointer.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -16,14 +16,14 @@ #define RAPIDJSON_POINTER_H_ #include "document.h" +#include "uri.h" #include "internal/itoa.h" +#include "error/error.h" // PointerParseErrorCode #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(switch-enum) -#endif - -#ifdef _MSC_VER +#elif defined(_MSC_VER) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated #endif @@ -32,19 +32,6 @@ RAPIDJSON_NAMESPACE_BEGIN static const SizeType kPointerInvalidIndex = ~SizeType(0); //!< Represents an invalid index in GenericPointer::Token -//! Error code of parsing. -/*! \ingroup RAPIDJSON_ERRORS - \see GenericPointer::GenericPointer, GenericPointer::GetParseErrorCode -*/ -enum PointerParseErrorCode { - kPointerParseErrorNone = 0, //!< The parse is successful - - kPointerParseErrorTokenMustBeginWithSolidus, //!< A token must begin with a '/' - kPointerParseErrorInvalidEscape, //!< Invalid escape - kPointerParseErrorInvalidPercentEncoding, //!< Invalid percent encoding in URI fragment - kPointerParseErrorCharacterMustPercentEncode //!< A character must percent encoded in URI fragment -}; - /////////////////////////////////////////////////////////////////////////////// // GenericPointer @@ -70,10 +57,10 @@ enum PointerParseErrorCode { supplied tokens eliminates these. GenericPointer depends on GenericDocument and GenericValue. - + \tparam ValueType The value type of the DOM tree. E.g. GenericValue > \tparam Allocator The allocator type for allocating memory for internal representation. - + \note GenericPointer uses same encoding of ValueType. However, Allocator of GenericPointer is independent of Allocator of Value. */ @@ -82,8 +69,10 @@ class GenericPointer { public: typedef typename ValueType::EncodingType EncodingType; //!< Encoding type from Value typedef typename ValueType::Ch Ch; //!< Character type from Value + typedef GenericUri UriType; - //! A token is the basic units of internal representation. + + //! A token is the basic units of internal representation. /*! A JSON pointer string representation "/foo/123" is parsed to two tokens: "foo" and 123. 123 will be represented in both numeric form and string form. @@ -165,7 +154,12 @@ public: GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} //! Copy constructor. - GenericPointer(const GenericPointer& rhs, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + GenericPointer(const GenericPointer& rhs) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + *this = rhs; + } + + //! Copy constructor. + GenericPointer(const GenericPointer& rhs, Allocator* allocator) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { *this = rhs; } @@ -197,6 +191,36 @@ public: return *this; } + //! Swap the content of this pointer with an other. + /*! + \param other The pointer to swap with. + \note Constant complexity. + */ + GenericPointer& Swap(GenericPointer& other) RAPIDJSON_NOEXCEPT { + internal::Swap(allocator_, other.allocator_); + internal::Swap(ownAllocator_, other.ownAllocator_); + internal::Swap(nameBuffer_, other.nameBuffer_); + internal::Swap(tokens_, other.tokens_); + internal::Swap(tokenCount_, other.tokenCount_); + internal::Swap(parseErrorOffset_, other.parseErrorOffset_); + internal::Swap(parseErrorCode_, other.parseErrorCode_); + return *this; + } + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.pointer, b.pointer); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericPointer& a, GenericPointer& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + //@} //!@name Append token @@ -240,7 +264,7 @@ public: template RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >), (GenericPointer)) Append(T* name, Allocator* allocator = 0) const { - return Append(name, StrLen(name), allocator); + return Append(name, internal::StrLen(name), allocator); } #if RAPIDJSON_HAS_STDSTRING @@ -274,7 +298,7 @@ public: else { Ch name[21]; for (size_t i = 0; i <= length; i++) - name[i] = buffer[i]; + name[i] = static_cast(buffer[i]); Token token = { name, length, index }; return Append(token, allocator); } @@ -353,6 +377,33 @@ public: */ bool operator!=(const GenericPointer& rhs) const { return !(*this == rhs); } + //! Less than operator. + /*! + \note Invalid pointers are always greater than valid ones. + */ + bool operator<(const GenericPointer& rhs) const { + if (!IsValid()) + return false; + if (!rhs.IsValid()) + return true; + + if (tokenCount_ != rhs.tokenCount_) + return tokenCount_ < rhs.tokenCount_; + + for (size_t i = 0; i < tokenCount_; i++) { + if (tokens_[i].index != rhs.tokens_[i].index) + return tokens_[i].index < rhs.tokens_[i].index; + + if (tokens_[i].length != rhs.tokens_[i].length) + return tokens_[i].length < rhs.tokens_[i].length; + + if (int cmp = std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch) * tokens_[i].length)) + return cmp < 0; + } + + return false; + } + //@} //!@name Stringify @@ -428,10 +479,11 @@ public: v = &((*v)[t->index]); } else { - typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); + typename ValueType::MemberIterator m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); if (m == v->MemberEnd()) { v->AddMember(ValueType(t->name, t->length, allocator).Move(), ValueType().Move(), allocator); - v = &(--v->MemberEnd())->value; // Assumes AddMember() appends at the end + m = v->MemberEnd(); + v = &(--m)->value; // Assumes AddMember() appends at the end exist = false; } else @@ -459,6 +511,70 @@ public: //@} + //!@name Compute URI + //@{ + + //! Compute the in-scope URI for a subtree. + // For use with JSON pointers into JSON schema documents. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param rootUri Root URI + \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. + \param allocator Allocator for Uris + \return Uri if it can be resolved. Otherwise null. + + \note + There are only 3 situations when a URI cannot be resolved: + 1. A value in the path is not an array nor object. + 2. An object value does not contain the token. + 3. A token is out of range of an array value. + + Use unresolvedTokenIndex to retrieve the token index. + */ + UriType GetUri(ValueType& root, const UriType& rootUri, size_t* unresolvedTokenIndex = 0, Allocator* allocator = 0) const { + static const Ch kIdString[] = { 'i', 'd', '\0' }; + static const ValueType kIdValue(kIdString, 2); + UriType base = UriType(rootUri, allocator); + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + switch (v->GetType()) { + case kObjectType: + { + // See if we have an id, and if so resolve with the current base + typename ValueType::MemberIterator m = v->FindMember(kIdValue); + if (m != v->MemberEnd() && (m->value).IsString()) { + UriType here = UriType(m->value, allocator).Resolve(base, allocator); + base = here; + } + m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) + break; + v = &m->value; + } + continue; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + break; + v = &((*v)[t->index]); + continue; + default: + break; + } + + // Error: unresolved token + if (unresolvedTokenIndex) + *unresolvedTokenIndex = static_cast(t - tokens_); + return UriType(allocator); + } + return base; + } + + UriType GetUri(const ValueType& root, const UriType& rootUri, size_t* unresolvedTokenIndex = 0, Allocator* allocator = 0) const { + return GetUri(const_cast(root), rootUri, unresolvedTokenIndex, allocator); + } + + //!@name Query value //@{ @@ -483,7 +599,7 @@ public: switch (v->GetType()) { case kObjectType: { - typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); + typename ValueType::MemberIterator m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); if (m == v->MemberEnd()) break; v = &m->value; @@ -532,14 +648,14 @@ public: */ ValueType& GetWithDefault(ValueType& root, const ValueType& defaultValue, typename ValueType::AllocatorType& allocator) const { bool alreadyExist; - Value& v = Create(root, allocator, &alreadyExist); + ValueType& v = Create(root, allocator, &alreadyExist); return alreadyExist ? v : v.CopyFrom(defaultValue, allocator); } //! Query a value in a subtree with default null-terminated string. ValueType& GetWithDefault(ValueType& root, const Ch* defaultValue, typename ValueType::AllocatorType& allocator) const { bool alreadyExist; - Value& v = Create(root, allocator, &alreadyExist); + ValueType& v = Create(root, allocator, &alreadyExist); return alreadyExist ? v : v.SetString(defaultValue, allocator); } @@ -547,7 +663,7 @@ public: //! Query a value in a subtree with default std::basic_string. ValueType& GetWithDefault(ValueType& root, const std::basic_string& defaultValue, typename ValueType::AllocatorType& allocator) const { bool alreadyExist; - Value& v = Create(root, allocator, &alreadyExist); + ValueType& v = Create(root, allocator, &alreadyExist); return alreadyExist ? v : v.SetString(defaultValue, allocator); } #endif @@ -573,7 +689,7 @@ public: ValueType& GetWithDefault(GenericDocument& document, const Ch* defaultValue) const { return GetWithDefault(document, defaultValue, document.GetAllocator()); } - + #if RAPIDJSON_HAS_STDSTRING //! Query a value in a document with default std::basic_string. template @@ -719,7 +835,7 @@ public: switch (v->GetType()) { case kObjectType: { - typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); + typename ValueType::MemberIterator m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); if (m == v->MemberEnd()) return false; v = &m->value; @@ -758,7 +874,7 @@ private: */ Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) { if (!allocator_) // allocator is independently owned. - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t) @@ -774,10 +890,16 @@ private: std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch)); } - // Adjust pointers to name buffer - std::ptrdiff_t diff = nameBuffer_ - rhs.nameBuffer_; - for (Token *t = tokens_; t != tokens_ + rhs.tokenCount_; ++t) - t->name += diff; + // The names of each token point to a string in the nameBuffer_. The + // previous memcpy copied over string pointers into the rhs.nameBuffer_, + // but they should point to the strings in the new nameBuffer_. + for (size_t i = 0; i < rhs.tokenCount_; ++i) { + // The offset between the string address and the name buffer should + // still be constant, so we can just get this offset and set each new + // token name according the new buffer start + the known offset. + std::ptrdiff_t name_offset = rhs.tokens_[i].name - rhs.nameBuffer_; + tokens_[i].name = nameBuffer_ + name_offset; + } return nameBuffer_ + nameBufferSize; } @@ -806,7 +928,7 @@ private: // Create own allocator if user did not supply. if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); // Count number of '/' as tokenCount tokenCount_ = 0; @@ -867,7 +989,7 @@ private: } i++; - + // Escaping "~0" -> '~', "~1" -> '/' if (c == '~') { if (i < length) { @@ -1029,8 +1151,8 @@ private: unsigned char u = static_cast(c); static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; os_.Put('%'); - os_.Put(hexDigits[u >> 4]); - os_.Put(hexDigits[u & 15]); + os_.Put(static_cast(hexDigits[u >> 4])); + os_.Put(static_cast(hexDigits[u & 15])); } private: OutputStream& os_; @@ -1347,11 +1469,7 @@ bool EraseValueByPointer(T& root, const CharType(&source)[N]) { RAPIDJSON_NAMESPACE_END -#ifdef __clang__ -RAPIDJSON_DIAG_POP -#endif - -#ifdef _MSC_VER +#if defined(__clang__) || defined(_MSC_VER) RAPIDJSON_DIAG_POP #endif diff --git a/third_party/rapidjson/include/rapidjson/prettywriter.h b/third_party/rapidjson/include/rapidjson/prettywriter.h index 0dcb0fee9..fe45df1d1 100644 --- a/third_party/rapidjson/include/rapidjson/prettywriter.h +++ b/third_party/rapidjson/include/rapidjson/prettywriter.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -22,6 +22,11 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + RAPIDJSON_NAMESPACE_BEGIN //! Combination of PrettyWriter format flags. @@ -34,7 +39,7 @@ enum PrettyFormatOptions { //! Writer with indentation and spacing. /*! - \tparam OutputStream Type of ouptut os. + \tparam OutputStream Type of output os. \tparam SourceEncoding Encoding of source string. \tparam TargetEncoding Encoding of output stream. \tparam StackAllocator Type of allocator for allocating memory of stack. @@ -42,7 +47,7 @@ enum PrettyFormatOptions { template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> class PrettyWriter : public Writer { public: - typedef Writer Base; + typedef Writer Base; typedef typename Base::Ch Ch; //! Constructor @@ -55,7 +60,12 @@ public: explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : - Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {} + Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + PrettyWriter(PrettyWriter&& rhs) : + Base(std::forward(rhs)), indentChar_(rhs.indentChar_), indentCharCount_(rhs.indentCharCount_), formatOptions_(rhs.formatOptions_) {} +#endif //! Set custom indentation. /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r'). @@ -82,24 +92,26 @@ public: */ //@{ - bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); } - bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); } - bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); } - bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); } - bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); } - bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); } - bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); } + bool Null() { PrettyPrefix(kNullType); return Base::EndValue(Base::WriteNull()); } + bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::EndValue(Base::WriteBool(b)); } + bool Int(int i) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt(i)); } + bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint(u)); } + bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt64(i64)); } + bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint64(u64)); } + bool Double(double d) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteDouble(d)); } bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); (void)copy; PrettyPrefix(kNumberType); - return Base::WriteString(str, length); + return Base::EndValue(Base::WriteString(str, length)); } bool String(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); (void)copy; PrettyPrefix(kStringType); - return Base::WriteString(str, length); + return Base::EndValue(Base::WriteString(str, length)); } #if RAPIDJSON_HAS_STDSTRING @@ -124,19 +136,21 @@ public: bool EndObject(SizeType memberCount = 0) { (void)memberCount; - RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); - RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); // not inside an Object + RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == Base::level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; if (!empty) { Base::os_->Put('\n'); WriteIndent(); } - bool ret = Base::WriteEndObject(); + bool ret = Base::EndValue(Base::WriteEndObject()); (void)ret; RAPIDJSON_ASSERT(ret == true); if (Base::level_stack_.Empty()) // end of json text - Base::os_->Flush(); + Base::Flush(); return true; } @@ -156,11 +170,11 @@ public: Base::os_->Put('\n'); WriteIndent(); } - bool ret = Base::WriteEndArray(); + bool ret = Base::EndValue(Base::WriteEndArray()); (void)ret; RAPIDJSON_ASSERT(ret == true); if (Base::level_stack_.Empty()) // end of json text - Base::os_->Flush(); + Base::Flush(); return true; } @@ -184,7 +198,11 @@ public: \param type Type of the root of json. \note When using PrettyWriter::RawValue(), the result json may not be indented correctly. */ - bool RawValue(const Ch* json, size_t length, Type type) { PrettyPrefix(type); return Base::WriteRawValue(json, length); } + bool RawValue(const Ch* json, size_t length, Type type) { + RAPIDJSON_ASSERT(json != 0); + PrettyPrefix(type); + return Base::EndValue(Base::WriteRawValue(json, length)); + } protected: void PrettyPrefix(Type type) { @@ -233,7 +251,7 @@ protected: void WriteIndent() { size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; - PutN(*Base::os_, static_cast(indentChar_), count); + PutN(*Base::os_, static_cast(indentChar_), count); } Ch indentChar_; @@ -248,6 +266,10 @@ private: RAPIDJSON_NAMESPACE_END +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + #ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif diff --git a/third_party/rapidjson/include/rapidjson/rapidjson.h b/third_party/rapidjson/include/rapidjson/rapidjson.h index 053b2ce43..5ea694795 100644 --- a/third_party/rapidjson/include/rapidjson/rapidjson.h +++ b/third_party/rapidjson/include/rapidjson/rapidjson.h @@ -1,15 +1,15 @@ // Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // -// 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 +// 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 RAPIDJSON_RAPIDJSON_H_ @@ -17,7 +17,7 @@ /*!\file rapidjson.h \brief common definitions and configuration - + \see RAPIDJSON_CONFIG */ @@ -26,7 +26,7 @@ Some RapidJSON features are configurable to adapt the library to a wide variety of platforms, environments and usage scenarios. Most of the - features can be configured in terms of overriden or predefined + features can be configured in terms of overridden or predefined preprocessor macros at compile-time. Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs. @@ -49,6 +49,11 @@ // token stringification #define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x) #define RAPIDJSON_DO_STRINGIFY(x) #x + +// token concatenation +#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) +#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) +#define RAPIDJSON_DO_JOIN2(X, Y) X##Y //!@endcond /*! \def RAPIDJSON_MAJOR_VERSION @@ -119,6 +124,19 @@ #define RAPIDJSON_NAMESPACE_END } #endif +/////////////////////////////////////////////////////////////////////////////// +// __cplusplus macro + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN + +#if defined(_MSC_VER) +#define RAPIDJSON_CPLUSPLUS _MSVC_LANG +#else +#define RAPIDJSON_CPLUSPLUS __cplusplus +#endif + +//!@endcond + /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_HAS_STDSTRING @@ -144,6 +162,24 @@ #include #endif // RAPIDJSON_HAS_STDSTRING +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_USE_MEMBERSMAP + +/*! \def RAPIDJSON_USE_MEMBERSMAP + \ingroup RAPIDJSON_CONFIG + \brief Enable RapidJSON support for object members handling in a \c std::multimap + + By defining this preprocessor symbol to \c 1, \ref rapidjson::GenericValue object + members are stored in a \c std::multimap for faster lookup and deletion times, a + trade off with a slightly slower insertion time and a small object allocat(or)ed + memory overhead. + + \hideinitializer +*/ +#ifndef RAPIDJSON_USE_MEMBERSMAP +#define RAPIDJSON_USE_MEMBERSMAP 0 // not by default +#endif + /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_NO_INT64DEFINE @@ -159,7 +195,7 @@ */ #ifndef RAPIDJSON_NO_INT64DEFINE //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN -#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013 +#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013 #include "msinttypes/stdint.h" #include "msinttypes/inttypes.h" #else @@ -214,7 +250,7 @@ # elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ # define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN # else -# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. # endif // __BYTE_ORDER__ // Detect with GLIBC's endian.h # elif defined(__GLIBC__) @@ -224,7 +260,7 @@ # elif (__BYTE_ORDER == __BIG_ENDIAN) # define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN # else -# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. # endif // __GLIBC__ // Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro # elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) @@ -236,12 +272,12 @@ # define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN # elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__) # define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN -# elif defined(_MSC_VER) && defined(_M_ARM) +# elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64)) # define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN # elif defined(RAPIDJSON_DOXYGEN_RUNNING) # define RAPIDJSON_ENDIAN # else -# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. # endif #endif // RAPIDJSON_ENDIAN @@ -264,16 +300,11 @@ /*! \ingroup RAPIDJSON_CONFIG \param x pointer to align - Some machines require strict data alignment. Currently the default uses 4 bytes - alignment on 32-bit platforms and 8 bytes alignment for 64-bit platforms. + Some machines require strict data alignment. The default is 8 bytes. User can customize by defining the RAPIDJSON_ALIGN function macro. */ #ifndef RAPIDJSON_ALIGN -#if RAPIDJSON_64BIT == 1 -#define RAPIDJSON_ALIGN(x) (((x) + static_cast(7u)) & ~static_cast(7u)) -#else -#define RAPIDJSON_ALIGN(x) (((x) + 3u) & ~3u) -#endif +#define RAPIDJSON_ALIGN(x) (((x) + static_cast(7u)) & ~static_cast(7u)) #endif /////////////////////////////////////////////////////////////////////////////// @@ -320,17 +351,17 @@ #endif /////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_SIMD +// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_NEON/RAPIDJSON_SIMD /*! \def RAPIDJSON_SIMD \ingroup RAPIDJSON_CONFIG - \brief Enable SSE2/SSE4.2 optimization. + \brief Enable SSE2/SSE4.2/Neon optimization. RapidJSON supports optimized implementations for some parsing operations - based on the SSE2 or SSE4.2 SIMD extensions on modern Intel-compatible - processors. + based on the SSE2, SSE4.2 or NEon SIMD extensions on modern Intel + or ARM compatible processors. - To enable these optimizations, two different symbols can be defined; + To enable these optimizations, three different symbols can be defined; \code // Enable SSE2 optimization. #define RAPIDJSON_SSE2 @@ -339,13 +370,17 @@ #define RAPIDJSON_SSE42 \endcode - \c RAPIDJSON_SSE42 takes precedence, if both are defined. + // Enable ARM Neon optimization. + #define RAPIDJSON_NEON + \endcode + + \c RAPIDJSON_SSE42 takes precedence over SSE2, if both are defined. If any of these symbols is defined, RapidJSON defines the macro \c RAPIDJSON_SIMD to indicate the availability of the optimized code. */ #if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \ - || defined(RAPIDJSON_DOXYGEN_RUNNING) + || defined(RAPIDJSON_NEON) || defined(RAPIDJSON_DOXYGEN_RUNNING) #define RAPIDJSON_SIMD #endif @@ -405,7 +440,15 @@ RAPIDJSON_NAMESPACE_END /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_STATIC_ASSERT -// Adopt from boost +// Prefer C++11 static_assert, if available +#ifndef RAPIDJSON_STATIC_ASSERT +#if RAPIDJSON_CPLUSPLUS >= 201103L || ( defined(_MSC_VER) && _MSC_VER >= 1800 ) +#define RAPIDJSON_STATIC_ASSERT(x) \ + static_assert(x, RAPIDJSON_STRINGIFY(x)) +#endif // C++11 +#endif // RAPIDJSON_STATIC_ASSERT + +// Adopt C++03 implementation from boost #ifndef RAPIDJSON_STATIC_ASSERT #ifndef __clang__ //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN @@ -413,14 +456,10 @@ RAPIDJSON_NAMESPACE_END RAPIDJSON_NAMESPACE_BEGIN template struct STATIC_ASSERTION_FAILURE; template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; -template struct StaticAssertTest {}; +template struct StaticAssertTest {}; RAPIDJSON_NAMESPACE_END -#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) -#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) -#define RAPIDJSON_DO_JOIN2(X, Y) X##Y - -#if defined(__GNUC__) +#if defined(__GNUC__) || defined(__clang__) #define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) #else #define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE @@ -438,7 +477,7 @@ RAPIDJSON_NAMESPACE_END typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \ sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE)> \ RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE -#endif +#endif // RAPIDJSON_STATIC_ASSERT /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY @@ -474,7 +513,7 @@ RAPIDJSON_NAMESPACE_END //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN -#define RAPIDJSON_MULTILINEMACRO_BEGIN do { +#define RAPIDJSON_MULTILINEMACRO_BEGIN do { #define RAPIDJSON_MULTILINEMACRO_END \ } while((void)0, 0) @@ -482,6 +521,12 @@ RAPIDJSON_NAMESPACE_END #define RAPIDJSON_VERSION_CODE(x,y,z) \ (((x)*100000) + ((y)*100) + (z)) +#if defined(__has_builtin) +#define RAPIDJSON_HAS_BUILTIN(x) __has_builtin(x) +#else +#define RAPIDJSON_HAS_BUILTIN(x) 0 +#endif + /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF @@ -527,16 +572,23 @@ RAPIDJSON_NAMESPACE_END /////////////////////////////////////////////////////////////////////////////// // C++11 features +#ifndef RAPIDJSON_HAS_CXX11 +#define RAPIDJSON_HAS_CXX11 (RAPIDJSON_CPLUSPLUS >= 201103L) +#endif + #ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS -#if defined(__clang__) +#if RAPIDJSON_HAS_CXX11 +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#elif defined(__clang__) #if __has_feature(cxx_rvalue_references) && \ - (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306) + (defined(_MSC_VER) || defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306) #define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 #else #define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 #endif #elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ - (defined(_MSC_VER) && _MSC_VER >= 1600) + (defined(_MSC_VER) && _MSC_VER >= 1600) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) #define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 #else @@ -544,46 +596,120 @@ RAPIDJSON_NAMESPACE_END #endif #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + #ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT -#if defined(__clang__) +#if RAPIDJSON_HAS_CXX11 +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 +#elif defined(__clang__) #define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept) -#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) -// (defined(_MSC_VER) && _MSC_VER >= ????) // not yet supported +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1900) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) #define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 #else #define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 #endif #endif +#ifndef RAPIDJSON_NOEXCEPT #if RAPIDJSON_HAS_CXX11_NOEXCEPT #define RAPIDJSON_NOEXCEPT noexcept #else -#define RAPIDJSON_NOEXCEPT /* noexcept */ +#define RAPIDJSON_NOEXCEPT throw() #endif // RAPIDJSON_HAS_CXX11_NOEXCEPT +#endif // no automatic detection, yet #ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS +#if (defined(_MSC_VER) && _MSC_VER >= 1700) +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 1 +#else #define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 #endif +#endif #ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR #if defined(__clang__) #define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for) -#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ - (defined(_MSC_VER) && _MSC_VER >= 1700) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1700) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) #define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 #else #define RAPIDJSON_HAS_CXX11_RANGE_FOR 0 #endif #endif // RAPIDJSON_HAS_CXX11_RANGE_FOR +/////////////////////////////////////////////////////////////////////////////// +// C++17 features + +#ifndef RAPIDJSON_HAS_CXX17 +#define RAPIDJSON_HAS_CXX17 (RAPIDJSON_CPLUSPLUS >= 201703L) +#endif + +#if RAPIDJSON_HAS_CXX17 +# define RAPIDJSON_DELIBERATE_FALLTHROUGH [[fallthrough]] +#elif defined(__has_cpp_attribute) +# if __has_cpp_attribute(clang::fallthrough) +# define RAPIDJSON_DELIBERATE_FALLTHROUGH [[clang::fallthrough]] +# elif __has_cpp_attribute(fallthrough) +# define RAPIDJSON_DELIBERATE_FALLTHROUGH __attribute__((fallthrough)) +# else +# define RAPIDJSON_DELIBERATE_FALLTHROUGH +# endif +#else +# define RAPIDJSON_DELIBERATE_FALLTHROUGH +#endif + //!@endcond +//! Assertion (in non-throwing contexts). + /*! \ingroup RAPIDJSON_CONFIG + Some functions provide a \c noexcept guarantee, if the compiler supports it. + In these cases, the \ref RAPIDJSON_ASSERT macro cannot be overridden to + throw an exception. This macro adds a separate customization point for + such cases. + + Defaults to C \c assert() (as \ref RAPIDJSON_ASSERT), if \c noexcept is + supported, and to \ref RAPIDJSON_ASSERT otherwise. + */ + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NOEXCEPT_ASSERT + +#ifndef RAPIDJSON_NOEXCEPT_ASSERT +#ifdef RAPIDJSON_ASSERT_THROWS +#include +#define RAPIDJSON_NOEXCEPT_ASSERT(x) assert(x) +#else +#define RAPIDJSON_NOEXCEPT_ASSERT(x) RAPIDJSON_ASSERT(x) +#endif // RAPIDJSON_ASSERT_THROWS +#endif // RAPIDJSON_NOEXCEPT_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// malloc/realloc/free + +#ifndef RAPIDJSON_MALLOC +///! customization point for global \c malloc +#define RAPIDJSON_MALLOC(size) std::malloc(size) +#endif +#ifndef RAPIDJSON_REALLOC +///! customization point for global \c realloc +#define RAPIDJSON_REALLOC(ptr, new_size) std::realloc(ptr, new_size) +#endif +#ifndef RAPIDJSON_FREE +///! customization point for global \c free +#define RAPIDJSON_FREE(ptr) std::free(ptr) +#endif + /////////////////////////////////////////////////////////////////////////////// // new/delete #ifndef RAPIDJSON_NEW ///! customization point for global \c new -#define RAPIDJSON_NEW(x) new x +#define RAPIDJSON_NEW(TypeName) new TypeName #endif #ifndef RAPIDJSON_DELETE ///! customization point for global \c delete @@ -605,7 +731,7 @@ enum Type { kFalseType = 1, //!< false kTrueType = 2, //!< true kObjectType = 3, //!< object - kArrayType = 4, //!< array + kArrayType = 4, //!< array kStringType = 5, //!< string kNumberType = 6 //!< number }; diff --git a/third_party/rapidjson/include/rapidjson/reader.h b/third_party/rapidjson/include/rapidjson/reader.h index 19f8849b1..55546601e 100644 --- a/third_party/rapidjson/include/rapidjson/reader.h +++ b/third_party/rapidjson/include/rapidjson/reader.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -20,6 +20,7 @@ #include "allocators.h" #include "stream.h" #include "encodedstream.h" +#include "internal/clzll.h" #include "internal/meta.h" #include "internal/stack.h" #include "internal/strtod.h" @@ -33,12 +34,8 @@ #include #elif defined(RAPIDJSON_SSE2) #include -#endif - -#ifdef _MSC_VER -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant -RAPIDJSON_DIAG_OFF(4702) // unreachable code +#elif defined(RAPIDJSON_NEON) +#include #endif #ifdef __clang__ @@ -46,6 +43,10 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(old-style-cast) RAPIDJSON_DIAG_OFF(padded) RAPIDJSON_DIAG_OFF(switch-enum) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4702) // unreachable code #endif #ifdef __GNUC__ @@ -153,6 +154,7 @@ enum ParseFlag { kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings. kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays. kParseNanAndInfFlag = 256, //!< Allow parsing NaN, Inf, Infinity, -Inf and -Infinity as doubles. + kParseEscapedApostropheFlag = 512, //!< Allow escaped apostrophe in strings. kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS }; @@ -299,16 +301,9 @@ inline const char *SkipWhitespace_SIMD(const char* p) { for (;; p += 16) { const __m128i s = _mm_load_si128(reinterpret_cast(p)); - const int r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY)); - if (r != 0) { // some of characters is non-whitespace -#ifdef _MSC_VER // Find the index of first non-whitespace - unsigned long offset; - _BitScanForward(&offset, r); - return p + offset; -#else - return p + __builtin_ffs(r) - 1; -#endif - } + const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); + if (r != 16) // some of characters is non-whitespace + return p + r; } } @@ -325,16 +320,9 @@ inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { for (; p <= end - 16; p += 16) { const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); - const int r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY)); - if (r != 0) { // some of characters is non-whitespace -#ifdef _MSC_VER // Find the index of first non-whitespace - unsigned long offset; - _BitScanForward(&offset, r); - return p + offset; -#else - return p + __builtin_ffs(r) - 1; -#endif - } + const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); + if (r != 16) // some of characters is non-whitespace + return p + r; } return SkipWhitespace(p, end); @@ -425,7 +413,92 @@ inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { return SkipWhitespace(p, end); } -#endif // RAPIDJSON_SSE2 +#elif defined(RAPIDJSON_NEON) + +//! Skip whitespace with ARM Neon instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + return p + 8 + (lz >> 3); + } + } else { + uint32_t lz = internal::clzll(low); + return p + (lz >> 3); + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (; p <= end - 16; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + return p + 8 + (lz >> 3); + } + } else { + uint32_t lz = internal::clzll(low); + return p + (lz >> 3); + } + } + + return SkipWhitespace(p, end); +} + +#endif // RAPIDJSON_NEON #ifdef RAPIDJSON_SIMD //! Template function specialization for InsituStringStream @@ -471,7 +544,8 @@ public: /*! \param stackAllocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing) \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing) */ - GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(stackAllocator, stackCapacity), parseResult_() {} + GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : + stack_(stackAllocator, stackCapacity), parseResult_(), state_(IterativeParsingStartState) {} //! Parse JSON text. /*! \tparam parseFlags Combination of \ref ParseFlag. @@ -527,7 +601,84 @@ public: return Parse(is, handler); } - //! Whether a parse error has occured in the last parsing. + //! Initialize JSON text token-by-token parsing + /*! + */ + void IterativeParseInit() { + parseResult_.Clear(); + state_ = IterativeParsingStartState; + } + + //! Parse one token from JSON text + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + bool IterativeParseNext(InputStream& is, Handler& handler) { + while (RAPIDJSON_LIKELY(is.Peek() != '\0')) { + SkipWhitespaceAndComments(is); + + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state_, t); + IterativeParsingState d = Transit(state_, t, n, is, handler); + + // If we've finished or hit an error... + if (RAPIDJSON_UNLIKELY(IsIterativeParsingCompleteState(d))) { + // Report errors. + if (d == IterativeParsingErrorState) { + HandleError(state_, is); + return false; + } + + // Transition to the finish state. + RAPIDJSON_ASSERT(d == IterativeParsingFinishState); + state_ = d; + + // If StopWhenDone is not set... + if (!(parseFlags & kParseStopWhenDoneFlag)) { + // ... and extra non-whitespace data is found... + SkipWhitespaceAndComments(is); + if (is.Peek() != '\0') { + // ... this is considered an error. + HandleError(state_, is); + return false; + } + } + + // Success! We are done! + return true; + } + + // Transition to the new state. + state_ = d; + + // If we parsed anything other than a delimiter, we invoked the handler, so we can return true now. + if (!IsIterativeParsingDelimiterState(n)) + return true; + } + + // We reached the end of file. + stack_.Clear(); + + if (state_ != IterativeParsingFinishState) { + HandleError(state_, is); + return false; + } + + return true; + } + + //! Check if token-by-token parsing JSON text is complete + /*! \return Whether the JSON has been fully decoded. + */ + RAPIDJSON_FORCEINLINE bool IterativeParseComplete() const { + return IsIterativeParsingCompleteState(state_); + } + + //! Whether a parse error has occurred in the last parsing. bool HasParseError() const { return parseResult_.IsError(); } //! Get the \ref ParseErrorCode of last parsing. @@ -575,7 +726,7 @@ private: } } else if (RAPIDJSON_LIKELY(Consume(is, '/'))) - while (is.Peek() != '\0' && is.Take() != '\n'); + while (is.Peek() != '\0' && is.Take() != '\n') {} else RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); @@ -750,7 +901,7 @@ private: return false; } - // Helper function to parse four hexidecimal digits in \uXXXX in ParseString(). + // Helper function to parse four hexadecimal digits in \uXXXX in ParseString(). template unsigned ParseHex4(InputStream& is, size_t escapeOffset) { unsigned codepoint = 0; @@ -841,7 +992,7 @@ private: //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN #define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 static const char escape[256] = { - Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/', + Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '/', Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -857,26 +1008,38 @@ private: Ch c = is.Peek(); if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape - size_t escapeOffset = is.Tell(); // For invalid escaping, report the inital '\\' as error offset + size_t escapeOffset = is.Tell(); // For invalid escaping, report the initial '\\' as error offset is.Take(); Ch e = is.Peek(); if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast(e)])) { is.Take(); os.Put(static_cast(escape[static_cast(e)])); } + else if ((parseFlags & kParseEscapedApostropheFlag) && RAPIDJSON_LIKELY(e == '\'')) { // Allow escaped apostrophe + is.Take(); + os.Put('\''); + } else if (RAPIDJSON_LIKELY(e == 'u')) { // Unicode is.Take(); unsigned codepoint = ParseHex4(is, escapeOffset); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDBFF)) { - // Handle UTF-16 surrogate pair - if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u'))) + if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDFFF)) { + // high surrogate, check if followed by valid low surrogate + if (RAPIDJSON_LIKELY(codepoint <= 0xDBFF)) { + // Handle UTF-16 surrogate pair + if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u'))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + unsigned codepoint2 = ParseHex4(is, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF)) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; + } + // single low surrogate + else + { RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); - unsigned codepoint2 = ParseHex4(is, escapeOffset); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF)) - RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); - codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; + } } TEncoding::Encode(os, codepoint); } @@ -892,7 +1055,7 @@ private: if (c == '\0') RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell()); else - RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell()); + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); } else { size_t offset = is.Tell(); @@ -927,7 +1090,7 @@ private: // The rest of string using SIMD static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; - static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); @@ -936,7 +1099,7 @@ private: const __m128i s = _mm_load_si128(reinterpret_cast(p)); const __m128i t1 = _mm_cmpeq_epi8(s, dq); const __m128i t2 = _mm_cmpeq_epi8(s, bs); - const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); unsigned short r = static_cast(_mm_movemask_epi8(x)); if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped @@ -948,11 +1111,13 @@ private: #else length = static_cast(__builtin_ffs(r) - 1); #endif - char* q = reinterpret_cast(os.Push(length)); - for (size_t i = 0; i < length; i++) - q[i] = p[i]; + if (length != 0) { + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; - p += length; + p += length; + } break; } _mm_storeu_si128(reinterpret_cast<__m128i *>(os.Push(16)), s); @@ -988,7 +1153,7 @@ private: // The rest of string using SIMD static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; - static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); @@ -997,7 +1162,7 @@ private: const __m128i s = _mm_load_si128(reinterpret_cast(p)); const __m128i t1 = _mm_cmpeq_epi8(s, dq); const __m128i t2 = _mm_cmpeq_epi8(s, bs); - const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); unsigned short r = static_cast(_mm_movemask_epi8(x)); if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped @@ -1036,7 +1201,7 @@ private: // The rest of string using SIMD static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; - static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); @@ -1045,7 +1210,7 @@ private: const __m128i s = _mm_load_si128(reinterpret_cast(p)); const __m128i t1 = _mm_cmpeq_epi8(s, dq); const __m128i t2 = _mm_cmpeq_epi8(s, bs); - const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); unsigned short r = static_cast(_mm_movemask_epi8(x)); if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped @@ -1064,27 +1229,199 @@ private: is.src_ = is.dst_ = p; } -#endif +#elif defined(RAPIDJSON_NEON) + // StringStream -> StackStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { + const char* p = is.src_; - template + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + return; + } + else + os.Put(*p++); + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + length = 8 + (lz >> 3); + escaped = true; + } + } else { + uint32_t lz = internal::clzll(low); + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + if (length != 0) { + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + } + break; + } + vst1q_u8(reinterpret_cast(os.Push(16)), s); + } + + is.src_ = p; + } + + // InsituStringStream -> InsituStringStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { + RAPIDJSON_ASSERT(&is == &os); + (void)os; + + if (is.src_ == is.dst_) { + SkipUnescapedString(is); + return; + } + + char* p = is.src_; + char *q = is.dst_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + is.dst_ = q; + return; + } + else + *q++ = *p++; + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16, q += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + length = 8 + (lz >> 3); + escaped = true; + } + } else { + uint32_t lz = internal::clzll(low); + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + for (const char* pend = p + length; p != pend; ) { + *q++ = *p++; + } + break; + } + vst1q_u8(reinterpret_cast(q), s); + } + + is.src_ = p; + is.dst_ = q; + } + + // When read/write pointers are the same for insitu stream, just skip unescaped characters + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { + RAPIDJSON_ASSERT(is.src_ == is.dst_); + char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + for (; p != nextAligned; p++) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = is.dst_ = p; + return; + } + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + p += 8 + (lz >> 3); + break; + } + } else { + uint32_t lz = internal::clzll(low); + p += lz >> 3; + break; + } + } + + is.src_ = is.dst_ = p; + } +#endif // RAPIDJSON_NEON + + template class NumberStream; - template - class NumberStream { + template + class NumberStream { public: typedef typename InputStream::Ch Ch; NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; } - ~NumberStream() {} RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); } - RAPIDJSON_FORCEINLINE void Push(char) {} + RAPIDJSON_FORCEINLINE void Push(char) {} size_t Tell() { return is.Tell(); } size_t Length() { return 0; } - const char* Pop() { return 0; } + const StackCharacter* Pop() { return 0; } protected: NumberStream& operator=(const NumberStream&); @@ -1092,47 +1429,47 @@ private: InputStream& is; }; - template - class NumberStream : public NumberStream { - typedef NumberStream Base; + template + class NumberStream : public NumberStream { + typedef NumberStream Base; public: - NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is), stackStream(reader.stack_) {} - ~NumberStream() {} + NumberStream(GenericReader& reader, InputStream& s) : Base(reader, s), stackStream(reader.stack_) {} RAPIDJSON_FORCEINLINE Ch TakePush() { - stackStream.Put(static_cast(Base::is.Peek())); + stackStream.Put(static_cast(Base::is.Peek())); return Base::is.Take(); } - RAPIDJSON_FORCEINLINE void Push(char c) { + RAPIDJSON_FORCEINLINE void Push(StackCharacter c) { stackStream.Put(c); } size_t Length() { return stackStream.Length(); } - const char* Pop() { + const StackCharacter* Pop() { stackStream.Put('\0'); return stackStream.Pop(); } private: - StackStream stackStream; + StackStream stackStream; }; - template - class NumberStream : public NumberStream { - typedef NumberStream Base; + template + class NumberStream : public NumberStream { + typedef NumberStream Base; public: - NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is) {} - ~NumberStream() {} + NumberStream(GenericReader& reader, InputStream& s) : Base(reader, s) {} RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); } }; template void ParseNumber(InputStream& is, Handler& handler) { + typedef typename internal::SelectIf, typename TargetEncoding::Ch, char>::Type NumberCharacter; + internal::StreamLocalCopy copy(is); - NumberStream::quiet_NaN(); + if (Consume(s, 'N')) { + if (Consume(s, 'a') && Consume(s, 'N')) { + d = std::numeric_limits::quiet_NaN(); + useNanOrInf = true; + } } - else if (RAPIDJSON_LIKELY(Consume(s, 'I') && Consume(s, 'n') && Consume(s, 'f'))) { - d = (minus ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); - if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n') - && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + else if (RAPIDJSON_LIKELY(Consume(s, 'I'))) { + if (Consume(s, 'n') && Consume(s, 'f')) { + d = (minus ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); + useNanOrInf = true; + + if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n') + && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) { + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + } } - else + + if (RAPIDJSON_UNLIKELY(!useNanOrInf)) { RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } } else RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); @@ -1231,8 +1577,6 @@ private: // Force double for big integer if (useDouble) { while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { - if (RAPIDJSON_UNLIKELY(d >= 1.7976931348623157e307)) // DBL_MAX / 10.0 - RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); d = d * 10 + (s.TakePush() - '0'); } } @@ -1302,9 +1646,18 @@ private: if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { exp = static_cast(s.Take() - '0'); if (expMinus) { + // (exp + expFrac) must not underflow int => we're detecting when -exp gets + // dangerously close to INT_MIN (a pessimistic next digit 9 would push it into + // underflow territory): + // + // -(exp * 10 + 9) + expFrac >= INT_MIN + // <=> exp <= (expFrac - INT_MIN - 9) / 10 + RAPIDJSON_ASSERT(expFrac <= 0); + int maxExp = (expFrac + 2147483639) / 10; + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { exp = exp * 10 + static_cast(s.Take() - '0'); - if (exp >= 214748364) { // Issue #313: prevent overflow exponent + if (RAPIDJSON_UNLIKELY(exp > maxExp)) { while (RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9')) // Consume the rest of exponent s.Take(); } @@ -1341,10 +1694,10 @@ private: } else { SizeType numCharsToCopy = static_cast(s.Length()); - StringStream srcStream(s.Pop()); + GenericStringStream > srcStream(s.Pop()); StackStream dstStream(stack_); while (numCharsToCopy--) { - Transcoder, TargetEncoding>::Transcode(srcStream, dstStream); + Transcoder, TargetEncoding>::Transcode(srcStream, dstStream); } dstStream.Put('\0'); const typename TargetEncoding::Ch* str = dstStream.Pop(); @@ -1354,7 +1707,7 @@ private: } else { size_t length = s.Length(); - const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not. + const NumberCharacter* decimal = s.Pop(); // Pop stack no matter if it will be used or not. if (useDouble) { int p = exp + expFrac; @@ -1363,6 +1716,13 @@ private: else d = internal::StrtodNormalPrecision(d, p); + // Use > max, instead of == inf, to fix bogus warning -Wfloat-equal + if (d > (std::numeric_limits::max)()) { + // Overflow + // TODO: internal::StrtodX should report overflow (or underflow) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + } + cont = handler.Double(minus ? -d : d); } else if (useNanOrInf) { @@ -1408,29 +1768,31 @@ private: // States enum IterativeParsingState { - IterativeParsingStartState = 0, - IterativeParsingFinishState, - IterativeParsingErrorState, + IterativeParsingFinishState = 0, // sink states at top + IterativeParsingErrorState, // sink states at top + IterativeParsingStartState, // Object states IterativeParsingObjectInitialState, IterativeParsingMemberKeyState, - IterativeParsingKeyValueDelimiterState, IterativeParsingMemberValueState, - IterativeParsingMemberDelimiterState, IterativeParsingObjectFinishState, // Array states IterativeParsingArrayInitialState, IterativeParsingElementState, - IterativeParsingElementDelimiterState, IterativeParsingArrayFinishState, // Single value state - IterativeParsingValueState - }; + IterativeParsingValueState, - enum { cIterativeParsingStateCount = IterativeParsingValueState + 1 }; + // Delimiter states (at bottom) + IterativeParsingElementDelimiterState, + IterativeParsingMemberDelimiterState, + IterativeParsingKeyValueDelimiterState, + + cIterativeParsingStateCount + }; // Tokens enum Token { @@ -1452,7 +1814,7 @@ private: kTokenCount }; - RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) { + RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) const { //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN #define N NumberToken @@ -1479,9 +1841,21 @@ private: return NumberToken; } - RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) { + RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) const { // current state x one lookahead token -> new state static const char G[cIterativeParsingStateCount][kTokenCount] = { + // Finish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Error(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, // Start { IterativeParsingArrayInitialState, // Left bracket @@ -1496,18 +1870,6 @@ private: IterativeParsingValueState, // Null IterativeParsingValueState // Number }, - // Finish(sink state) - { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - }, - // Error(sink state) - { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - }, // ObjectInitial { IterativeParsingErrorState, // Left bracket @@ -1536,20 +1898,6 @@ private: IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, - // KeyValueDelimiter - { - IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) - IterativeParsingErrorState, // Right bracket - IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) - IterativeParsingErrorState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingMemberValueState, // String - IterativeParsingMemberValueState, // False - IterativeParsingMemberValueState, // True - IterativeParsingMemberValueState, // Null - IterativeParsingMemberValueState // Number - }, // MemberValue { IterativeParsingErrorState, // Left bracket @@ -1564,20 +1912,6 @@ private: IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, - // MemberDelimiter - { - IterativeParsingErrorState, // Left bracket - IterativeParsingErrorState, // Right bracket - IterativeParsingErrorState, // Left curly bracket - IterativeParsingObjectFinishState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingMemberKeyState, // String - IterativeParsingErrorState, // False - IterativeParsingErrorState, // True - IterativeParsingErrorState, // Null - IterativeParsingErrorState // Number - }, // ObjectFinish(sink state) { IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, @@ -1612,6 +1946,18 @@ private: IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, + // ArrayFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Single Value (sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, // ElementDelimiter { IterativeParsingArrayInitialState, // Left bracket(push Element state) @@ -1626,18 +1972,34 @@ private: IterativeParsingElementState, // Null IterativeParsingElementState // Number }, - // ArrayFinish(sink state) + // MemberDelimiter { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number }, - // Single Value (sink state) + // KeyValueDelimiter { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - } + IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberValueState, // String + IterativeParsingMemberValueState, // False + IterativeParsingMemberValueState, // True + IterativeParsingMemberValueState, // Null + IterativeParsingMemberValueState // Number + }, }; // End of G return static_cast(G[state][token]); @@ -1818,6 +2180,14 @@ private: } } + RAPIDJSON_FORCEINLINE bool IsIterativeParsingDelimiterState(IterativeParsingState s) const { + return s >= IterativeParsingElementDelimiterState; + } + + RAPIDJSON_FORCEINLINE bool IsIterativeParsingCompleteState(IterativeParsingState s) const { + return s <= IterativeParsingErrorState; + } + template ParseResult IterativeParse(InputStream& is, Handler& handler) { parseResult_.Clear(); @@ -1856,6 +2226,7 @@ private: static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. ParseResult parseResult_; + IterativeParsingState state_; }; // class GenericReader //! Reader with UTF8 encoding and default allocator. @@ -1863,7 +2234,7 @@ typedef GenericReader, UTF8<> > Reader; RAPIDJSON_NAMESPACE_END -#ifdef __clang__ +#if defined(__clang__) || defined(_MSC_VER) RAPIDJSON_DIAG_POP #endif @@ -1872,8 +2243,4 @@ RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_POP #endif -#ifdef _MSC_VER -RAPIDJSON_DIAG_POP -#endif - #endif // RAPIDJSON_READER_H_ diff --git a/third_party/rapidjson/include/rapidjson/schema.h b/third_party/rapidjson/include/rapidjson/schema.h index b182aa27f..973e935f1 100644 --- a/third_party/rapidjson/include/rapidjson/schema.h +++ b/third_party/rapidjson/include/rapidjson/schema.h @@ -1,5 +1,5 @@ // Tencent is pleased to support the open source community by making RapidJSON available-> -// +// // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved-> // // Licensed under the MIT License (the "License"); you may not use this file except @@ -7,9 +7,9 @@ // // http://opensource->org/licenses/MIT // -// 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 +// 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 RAPIDJSON_SCHEMA_H_ @@ -17,6 +17,9 @@ #include "document.h" #include "pointer.h" +#include "stringbuffer.h" +#include "error/en.h" +#include "uri.h" #include // abs, floor #if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX) @@ -25,7 +28,7 @@ #define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0 #endif -#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)) +#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)) #define RAPIDJSON_SCHEMA_USE_STDREGEX 1 #else #define RAPIDJSON_SCHEMA_USE_STDREGEX 0 @@ -47,10 +50,6 @@ #define RAPIDJSON_SCHEMA_VERBOSE 0 #endif -#if RAPIDJSON_SCHEMA_VERBOSE -#include "stringbuffer.h" -#endif - RAPIDJSON_DIAG_PUSH #if defined(__GNUC__) @@ -62,9 +61,7 @@ RAPIDJSON_DIAG_OFF(weak-vtables) RAPIDJSON_DIAG_OFF(exit-time-destructors) RAPIDJSON_DIAG_OFF(c++98-compat-pedantic) RAPIDJSON_DIAG_OFF(variadic-macros) -#endif - -#ifdef _MSC_VER +#elif defined(_MSC_VER) RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated #endif @@ -77,50 +74,161 @@ RAPIDJSON_NAMESPACE_BEGIN namespace internal { -inline void PrintInvalidKeyword(const char* keyword) { - printf("Fail keyword: %s\n", keyword); +inline void PrintInvalidKeywordData(const char* keyword) { + printf(" Fail keyword: '%s'\n", keyword); } -inline void PrintInvalidKeyword(const wchar_t* keyword) { - wprintf(L"Fail keyword: %ls\n", keyword); +inline void PrintInvalidKeywordData(const wchar_t* keyword) { + wprintf(L" Fail keyword: '%ls'\n", keyword); } -inline void PrintInvalidDocument(const char* document) { - printf("Fail document: %s\n\n", document); +inline void PrintInvalidDocumentData(const char* document) { + printf(" Fail document: '%s'\n", document); } -inline void PrintInvalidDocument(const wchar_t* document) { - wprintf(L"Fail document: %ls\n\n", document); +inline void PrintInvalidDocumentData(const wchar_t* document) { + wprintf(L" Fail document: '%ls'\n", document); } -inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) { - printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d); +inline void PrintValidatorPointersData(const char* s, const char* d, unsigned depth) { + printf(" Sch: %*s'%s'\n Doc: %*s'%s'\n", depth * 4, " ", s, depth * 4, " ", d); } -inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) { - wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d); +inline void PrintValidatorPointersData(const wchar_t* s, const wchar_t* d, unsigned depth) { + wprintf(L" Sch: %*ls'%ls'\n Doc: %*ls'%ls'\n", depth * 4, L" ", s, depth * 4, L" ", d); +} + +inline void PrintSchemaIdsData(const char* base, const char* local, const char* resolved) { + printf(" Resolving id: Base: '%s', Local: '%s', Resolved: '%s'\n", base, local, resolved); +} + +inline void PrintSchemaIdsData(const wchar_t* base, const wchar_t* local, const wchar_t* resolved) { + wprintf(L" Resolving id: Base: '%ls', Local: '%ls', Resolved: '%ls'\n", base, local, resolved); +} + +inline void PrintMethodData(const char* method) { + printf("%s\n", method); +} + +inline void PrintMethodData(const char* method, bool b) { + printf("%s, Data: '%s'\n", method, b ? "true" : "false"); +} + +inline void PrintMethodData(const char* method, int64_t i) { + printf("%s, Data: '%" PRId64 "'\n", method, i); +} + +inline void PrintMethodData(const char* method, uint64_t u) { + printf("%s, Data: '%" PRIu64 "'\n", method, u); +} + +inline void PrintMethodData(const char* method, double d) { + printf("%s, Data: '%lf'\n", method, d); +} + +inline void PrintMethodData(const char* method, const char* s) { + printf("%s, Data: '%s'\n", method, s); +} + +inline void PrintMethodData(const char* method, const wchar_t* s) { + wprintf(L"%hs, Data: '%ls'\n", method, s); +} + +inline void PrintMethodData(const char* method, const char* s1, const char* s2) { + printf("%s, Data: '%s', '%s'\n", method, s1, s2); +} + +inline void PrintMethodData(const char* method, const wchar_t* s1, const wchar_t* s2) { + wprintf(L"%hs, Data: '%ls', '%ls'\n", method, s1, s2); } } // namespace internal #endif // RAPIDJSON_SCHEMA_VERBOSE +#ifndef RAPIDJSON_SCHEMA_PRINT +#if RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_SCHEMA_PRINT(name, ...) internal::Print##name##Data(__VA_ARGS__) +#else +#define RAPIDJSON_SCHEMA_PRINT(name, ...) +#endif +#endif + /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_INVALID_KEYWORD_RETURN -#if RAPIDJSON_SCHEMA_VERBOSE -#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword) -#else -#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) -#endif - -#define RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\ +#define RAPIDJSON_INVALID_KEYWORD_RETURN(code)\ RAPIDJSON_MULTILINEMACRO_BEGIN\ - context.invalidKeyword = keyword.GetString();\ - RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\ + context.invalidCode = code;\ + context.invalidKeyword = SchemaType::GetValidateErrorKeyword(code).GetString();\ + RAPIDJSON_SCHEMA_PRINT(InvalidKeyword, context.invalidKeyword);\ return false;\ RAPIDJSON_MULTILINEMACRO_END +/////////////////////////////////////////////////////////////////////////////// +// ValidateFlag + +/*! \def RAPIDJSON_VALIDATE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kValidateDefaultFlags definition. + + User can define this as any \c ValidateFlag combinations. +*/ +#ifndef RAPIDJSON_VALIDATE_DEFAULT_FLAGS +#define RAPIDJSON_VALIDATE_DEFAULT_FLAGS kValidateNoFlags +#endif + +//! Combination of validate flags +enum ValidateFlag { + kValidateNoFlags = 0, //!< No flags are set. + kValidateContinueOnErrorFlag = 1, //!< Don't stop after first validation error. + kValidateReadFlag = 2, //!< Validation is for a read semantic. + kValidateWriteFlag = 4, //!< Validation is for a write semantic. + kValidateDefaultFlags = RAPIDJSON_VALIDATE_DEFAULT_FLAGS //!< Default validate flags. Can be customized by defining RAPIDJSON_VALIDATE_DEFAULT_FLAGS +}; + +/////////////////////////////////////////////////////////////////////////////// +// Specification +enum SchemaDraft { + kDraftUnknown = -1, + kDraftNone = 0, + kDraft03 = 3, + kDraftMin = 4, //!< Current minimum supported draft + kDraft04 = 4, + kDraft05 = 5, + kDraftMax = 5, //!< Current maximum supported draft + kDraft06 = 6, + kDraft07 = 7, + kDraft2019_09 = 8, + kDraft2020_12 = 9 +}; + +enum OpenApiVersion { + kVersionUnknown = -1, + kVersionNone = 0, + kVersionMin = 2, //!< Current minimum supported version + kVersion20 = 2, + kVersion30 = 3, + kVersionMax = 3, //!< Current maximum supported version + kVersion31 = 4, +}; + +struct Specification { + Specification(SchemaDraft d) : draft(d), oapi(kVersionNone) {} + Specification(OpenApiVersion o) : oapi(o) { + if (oapi == kVersion20) draft = kDraft04; + else if (oapi == kVersion30) draft = kDraft05; + else if (oapi == kVersion31) draft = kDraft2020_12; + else draft = kDraft04; + } + ~Specification() {} + bool IsSupported() const { + return ((draft >= kDraftMin && draft <= kDraftMax) && ((oapi == kVersionNone) || (oapi >= kVersionMin && oapi <= kVersionMax))); + } + SchemaDraft draft; + OpenApiVersion oapi; +}; + /////////////////////////////////////////////////////////////////////////////// // Forward declarations @@ -139,6 +247,8 @@ class ISchemaValidator { public: virtual ~ISchemaValidator() {} virtual bool IsValid() const = 0; + virtual void SetValidateFlags(unsigned flags) = 0; + virtual unsigned GetValidateFlags() const = 0; }; /////////////////////////////////////////////////////////////////////////////// @@ -148,7 +258,7 @@ template class ISchemaStateFactory { public: virtual ~ISchemaStateFactory() {} - virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0; + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&, const bool inheritContinueOnErrors) = 0; virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0; virtual void* CreateHasher() = 0; virtual uint64_t GetHashCode(void* hasher) = 0; @@ -157,6 +267,65 @@ public: virtual void FreeState(void* p) = 0; }; +/////////////////////////////////////////////////////////////////////////////// +// IValidationErrorHandler + +template +class IValidationErrorHandler { +public: + typedef typename SchemaType::Ch Ch; + typedef typename SchemaType::SValue SValue; + + virtual ~IValidationErrorHandler() {} + + virtual void NotMultipleOf(int64_t actual, const SValue& expected) = 0; + virtual void NotMultipleOf(uint64_t actual, const SValue& expected) = 0; + virtual void NotMultipleOf(double actual, const SValue& expected) = 0; + virtual void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void AboveMaximum(double actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(double actual, const SValue& expected, bool exclusive) = 0; + + virtual void TooLong(const Ch* str, SizeType length, SizeType expected) = 0; + virtual void TooShort(const Ch* str, SizeType length, SizeType expected) = 0; + virtual void DoesNotMatch(const Ch* str, SizeType length) = 0; + + virtual void DisallowedItem(SizeType index) = 0; + virtual void TooFewItems(SizeType actualCount, SizeType expectedCount) = 0; + virtual void TooManyItems(SizeType actualCount, SizeType expectedCount) = 0; + virtual void DuplicateItems(SizeType index1, SizeType index2) = 0; + + virtual void TooManyProperties(SizeType actualCount, SizeType expectedCount) = 0; + virtual void TooFewProperties(SizeType actualCount, SizeType expectedCount) = 0; + virtual void StartMissingProperties() = 0; + virtual void AddMissingProperty(const SValue& name) = 0; + virtual bool EndMissingProperties() = 0; + virtual void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void DisallowedProperty(const Ch* name, SizeType length) = 0; + + virtual void StartDependencyErrors() = 0; + virtual void StartMissingDependentProperties() = 0; + virtual void AddMissingDependentProperty(const SValue& targetName) = 0; + virtual void EndMissingDependentProperties(const SValue& sourceName) = 0; + virtual void AddDependencySchemaError(const SValue& souceName, ISchemaValidator* subvalidator) = 0; + virtual bool EndDependencyErrors() = 0; + + virtual void DisallowedValue(const ValidateErrorCode code) = 0; + virtual void StartDisallowedType() = 0; + virtual void AddExpectedType(const typename SchemaType::ValueType& expectedType) = 0; + virtual void EndDisallowedType(const typename SchemaType::ValueType& actualType) = 0; + virtual void NotAllOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void NoneOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void MultipleOneOf(SizeType index1, SizeType index2) = 0; + virtual void Disallowed() = 0; + virtual void DisallowedWhenWriting() = 0; + virtual void DisallowedWhenReading() = 0; +}; + + /////////////////////////////////////////////////////////////////////////////// // Hasher @@ -174,10 +343,10 @@ public: bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } - bool Double(double d) { - Number n; + bool Double(double d) { + Number n; if (d < 0) n.u.i = static_cast(d); - else n.u.u = static_cast(d); + else n.u.u = static_cast(d); n.d = d; return WriteNumber(n); } @@ -198,7 +367,9 @@ public: uint64_t h = Hash(0, kObjectType); uint64_t* kv = stack_.template Pop(memberCount * 2); for (SizeType i = 0; i < memberCount; i++) - h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive + // Issue #2205 + // Hasing the key to avoid key=value cases with bug-prone zero-value hash + h ^= Hash(Hash(0, kv[i * 2]), kv[i * 2 + 1]); // Use xor to achieve member order insensitive *stack_.template Push() = h; return true; } @@ -236,7 +407,7 @@ private: bool WriteBuffer(Type type, const void* data, size_t len) { // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/ - uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type); + uint64_t h = Hash(RAPIDJSON_UINT64_C2(0xcbf29ce4, 0x84222325), type); const unsigned char* d = static_cast(data); for (size_t i = 0; i < len; i++) h = Hash(h, d[i]); @@ -261,6 +432,7 @@ template struct SchemaValidationContext { typedef Schema SchemaType; typedef ISchemaStateFactory SchemaValidatorFactoryType; + typedef IValidationErrorHandler ErrorHandlerType; typedef typename SchemaType::ValueType ValueType; typedef typename ValueType::Ch Ch; @@ -270,11 +442,14 @@ struct SchemaValidationContext { kPatternValidatorWithAdditionalProperty }; - SchemaValidationContext(SchemaValidatorFactoryType& f, const SchemaType* s) : + SchemaValidationContext(SchemaValidatorFactoryType& f, ErrorHandlerType& eh, const SchemaType* s, unsigned fl = 0) : factory(f), + error_handler(eh), schema(s), + flags(fl), valueSchema(), invalidKeyword(), + invalidCode(), hasher(), arrayElementHashCodes(), validators(), @@ -295,13 +470,19 @@ struct SchemaValidationContext { if (hasher) factory.DestroryHasher(hasher); if (validators) { - for (SizeType i = 0; i < validatorCount; i++) - factory.DestroySchemaValidator(validators[i]); + for (SizeType i = 0; i < validatorCount; i++) { + if (validators[i]) { + factory.DestroySchemaValidator(validators[i]); + } + } factory.FreeState(validators); } if (patternPropertiesValidators) { - for (SizeType i = 0; i < patternPropertiesValidatorCount; i++) - factory.DestroySchemaValidator(patternPropertiesValidators[i]); + for (SizeType i = 0; i < patternPropertiesValidatorCount; i++) { + if (patternPropertiesValidators[i]) { + factory.DestroySchemaValidator(patternPropertiesValidators[i]); + } + } factory.FreeState(patternPropertiesValidators); } if (patternPropertiesSchemas) @@ -311,9 +492,12 @@ struct SchemaValidationContext { } SchemaValidatorFactoryType& factory; + ErrorHandlerType& error_handler; const SchemaType* schema; + unsigned flags; const SchemaType* valueSchema; const Ch* invalidKeyword; + ValidateErrorCode invalidCode; void* hasher; // Only validator access void* arrayElementHashCodes; // Only validator access this ISchemaValidator** validators; @@ -345,15 +529,23 @@ public: typedef SchemaValidationContext Context; typedef Schema SchemaType; typedef GenericValue SValue; + typedef IValidationErrorHandler ErrorHandler; + typedef GenericUri UriType; friend class GenericSchemaDocument; - Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) : + Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator, const UriType& id = UriType()) : allocator_(allocator), + uri_(schemaDocument->GetURI(), *allocator), + id_(id, allocator), + spec_(schemaDocument->GetSpecification()), + pointer_(p, allocator), + typeless_(schemaDocument->GetTypeless()), enum_(), enumCount_(), not_(), type_((1 << kTotalSchemaType) - 1), // typeless validatorCount_(), + notValidatorIndex_(), properties_(), additionalPropertiesSchema_(), patternProperties_(), @@ -377,15 +569,44 @@ public: minLength_(0), maxLength_(~SizeType(0)), exclusiveMinimum_(false), - exclusiveMaximum_(false) + exclusiveMaximum_(false), + defaultValueLength_(0), + readOnly_(false), + writeOnly_(false), + nullable_(false) { - typedef typename SchemaDocumentType::ValueType ValueType; + GenericStringBuffer sb; + p.StringifyUriFragment(sb); + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Schema", sb.GetString(), id.GetString()); + typedef typename ValueType::ConstValueIterator ConstValueIterator; typedef typename ValueType::ConstMemberIterator ConstMemberIterator; + // PR #1393 + // Early add this Schema and its $ref(s) in schemaDocument's map to avoid infinite + // recursion (with recursive schemas), since schemaDocument->getSchema() is always + // checked before creating a new one. Don't cache typeless_, though. + if (this != typeless_) { + typedef typename SchemaDocumentType::SchemaEntry SchemaEntry; + SchemaEntry *entry = schemaDocument->schemaMap_.template Push(); + new (entry) SchemaEntry(pointer_, this, true, allocator_); + schemaDocument->AddSchemaRefs(this); + } + if (!value.IsObject()) return; + // If we have an id property, resolve it with the in-scope id + // Not supported for open api 2.0 or 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) + if (const ValueType* v = GetMember(value, GetIdString())) { + if (v->IsString()) { + UriType local(*v, allocator); + id_ = local.Resolve(id_, allocator); + RAPIDJSON_SCHEMA_PRINT(SchemaIds, id.GetString(), v->GetString(), id_.GetString()); + } + } + if (const ValueType* v = GetMember(value, GetTypeString())) { type_ = 0; if (v->IsString()) @@ -395,29 +616,33 @@ public: AddType(*itr); } - if (const ValueType* v = GetMember(value, GetEnumString())) + if (const ValueType* v = GetMember(value, GetEnumString())) { if (v->IsArray() && v->Size() > 0) { enum_ = static_cast(allocator_->Malloc(sizeof(uint64_t) * v->Size())); for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) { - typedef Hasher > EnumHasherType; - char buffer[256 + 24]; - MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer)); + typedef Hasher > EnumHasherType; + char buffer[256u + 24]; + MemoryPoolAllocator hasherAllocator(buffer, sizeof(buffer)); EnumHasherType h(&hasherAllocator, 256); itr->Accept(h); enum_[enumCount_++] = h.GetHashCode(); } } - - if (schemaDocument) { - AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document); - AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document); - AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document); } - if (const ValueType* v = GetMember(value, GetNotString())) { - schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document); - notValidatorIndex_ = validatorCount_; - validatorCount_++; + if (schemaDocument) + AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document); + + // AnyOf, OneOf, Not not supported for open api 2.0 + if (schemaDocument && spec_.oapi != kVersion20) { + AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document); + AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document); + + if (const ValueType* v = GetMember(value, GetNotString())) { + schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document, id_); + notValidatorIndex_ = validatorCount_; + validatorCount_++; + } } // Object @@ -432,12 +657,14 @@ public: if (properties && properties->IsObject()) for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) AddUniqueElement(allProperties, itr->name); - + if (required && required->IsArray()) for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) if (itr->IsString()) AddUniqueElement(allProperties, *itr); + // Dependencies not supported for open api 2.0 and 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) if (dependencies && dependencies->IsObject()) for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { AddUniqueElement(allProperties, itr->name); @@ -453,7 +680,7 @@ public: for (SizeType i = 0; i < propertyCount_; i++) { new (&properties_[i]) Property(); properties_[i].name = allProperties[i]; - properties_[i].schema = GetTypeless(); + properties_[i].schema = typeless_; } } } @@ -463,10 +690,12 @@ public: for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) { SizeType index; if (FindPropertyIndex(itr->name, &index)) - schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document); + schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document, id_); } } + // PatternProperties not supported for open api 2.0 and 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) { PointerType q = p.Append(GetPatternPropertiesString(), allocator_); patternProperties_ = static_cast(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount())); @@ -474,8 +703,9 @@ public: for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) { new (&patternProperties_[patternPropertyCount_]) PatternProperty(); - patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name); - schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document); + PointerType r = q.Append(itr->name, allocator_); + patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name, schemaDocument, r); + schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, r, itr->value, document, id_); patternPropertyCount_++; } } @@ -490,6 +720,8 @@ public: } } + // Dependencies not supported for open api 2.0 and 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) if (dependencies && dependencies->IsObject()) { PointerType q = p.Append(GetDependenciesString(), allocator_); hasDependencies_ = true; @@ -507,7 +739,7 @@ public: } else if (itr->value.IsObject()) { hasSchemaDependencies_ = true; - schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document); + schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document, id_); properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_; validatorCount_++; } @@ -519,7 +751,7 @@ public: if (v->IsBool()) additionalProperties_ = v->GetBool(); else if (v->IsObject()) - schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document); + schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document, id_); } AssignIfExist(minProperties_, value, GetMinPropertiesString()); @@ -529,23 +761,25 @@ public: if (const ValueType* v = GetMember(value, GetItemsString())) { PointerType q = p.Append(GetItemsString(), allocator_); if (v->IsObject()) // List validation - schemaDocument->CreateSchema(&itemsList_, q, *v, document); + schemaDocument->CreateSchema(&itemsList_, q, *v, document, id_); else if (v->IsArray()) { // Tuple validation itemsTuple_ = static_cast(allocator_->Malloc(sizeof(const Schema*) * v->Size())); SizeType index = 0; for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++) - schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document); + schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document, id_); } } AssignIfExist(minItems_, value, GetMinItemsString()); AssignIfExist(maxItems_, value, GetMaxItemsString()); + // AdditionalItems not supported for openapi 2.0 and 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) { if (v->IsBool()) additionalItems_ = v->GetBool(); else if (v->IsObject()) - schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document); + schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document, id_); } AssignIfExist(uniqueItems_, value, GetUniqueItemsString()); @@ -555,7 +789,7 @@ public: AssignIfExist(maxLength_, value, GetMaxLengthString()); if (const ValueType* v = GetMember(value, GetPatternString())) - pattern_ = CreatePattern(*v); + pattern_ = CreatePattern(*v, schemaDocument, p.Append(GetPatternString(), allocator_)); // Number if (const ValueType* v = GetMember(value, GetMinimumString())) @@ -572,12 +806,33 @@ public: if (const ValueType* v = GetMember(value, GetMultipleOfString())) if (v->IsNumber() && v->GetDouble() > 0.0) multipleOf_.CopyFrom(*v, *allocator_); + + // Default + if (const ValueType* v = GetMember(value, GetDefaultValueString())) + if (v->IsString()) + defaultValueLength_ = v->GetStringLength(); + + // ReadOnly - open api only (until draft 7 supported) + // WriteOnly - open api 3 only (until draft 7 supported) + // Both can't be true + if (spec_.oapi != kVersionNone) + AssignIfExist(readOnly_, value, GetReadOnlyString()); + if (spec_.oapi >= kVersion30) + AssignIfExist(writeOnly_, value, GetWriteOnlyString()); + if (readOnly_ && writeOnly_) + schemaDocument->SchemaError(kSchemaErrorReadOnlyAndWriteOnly, p); + + // Nullable - open api 3 only + // If true add 'null' as allowable type + if (spec_.oapi >= kVersion30) { + AssignIfExist(nullable_, value, GetNullableString()); + if (nullable_) + AddType(GetNullString()); + } } ~Schema() { - if (allocator_) { - allocator_->Free(enum_); - } + AllocatorType::Free(enum_); if (properties_) { for (SizeType i = 0; i < propertyCount_; i++) properties_[i].~Property(); @@ -592,12 +847,29 @@ public: #if RAPIDJSON_SCHEMA_HAS_REGEX if (pattern_) { pattern_->~RegexType(); - allocator_->Free(pattern_); + AllocatorType::Free(pattern_); } #endif } + const SValue& GetURI() const { + return uri_; + } + + const UriType& GetId() const { + return id_; + } + + const Specification& GetSpecification() const { + return spec_; + } + + const PointerType& GetPointer() const { + return pointer_; + } + bool BeginValue(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::BeginValue"); if (context.inArray) { if (uniqueItems_) context.valueUniqueness = true; @@ -610,12 +882,18 @@ public: else if (additionalItemsSchema_) context.valueSchema = additionalItemsSchema_; else if (additionalItems_) - context.valueSchema = GetTypeless(); - else - RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString()); + context.valueSchema = typeless_; + else { + context.error_handler.DisallowedItem(context.arrayElementIndex); + // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error + context.valueSchema = typeless_; + // Must bump arrayElementIndex for when kValidateContinueOnErrorFlag is set + context.arrayElementIndex++; + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalItems); + } } else - context.valueSchema = GetTypeless(); + context.valueSchema = typeless_; context.arrayElementIndex++; } @@ -623,6 +901,8 @@ public: } RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndValue"); + // Only check pattern properties if we have validators if (context.patternPropertiesValidatorCount > 0) { bool otherValid = false; SizeType count = context.patternPropertiesValidatorCount; @@ -637,133 +917,178 @@ public: } if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) { - if (!patternValid) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + if (!patternValid) { + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties); + } } else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { - if (!patternValid || !otherValid) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + if (!patternValid || !otherValid) { + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties); + } + } + else if (!patternValid && !otherValid) { // kPatternValidatorWithAdditionalProperty) + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties); } - else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); } - if (enum_) { + // For enums only check if we have a hasher + if (enum_ && context.hasher) { const uint64_t h = context.factory.GetHashCode(context.hasher); for (SizeType i = 0; i < enumCount_; i++) if (enum_[i] == h) goto foundEnum; - RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString()); + context.error_handler.DisallowedValue(kValidateErrorEnum); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorEnum); foundEnum:; } - if (allOf_.schemas) - for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++) - if (!context.validators[i]->IsValid()) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString()); - - if (anyOf_.schemas) { - for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++) - if (context.validators[i]->IsValid()) - goto foundAny; - RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString()); - foundAny:; - } + // Only check allOf etc if we have validators + if (context.validatorCount > 0) { + if (allOf_.schemas) + for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++) + if (!context.validators[i]->IsValid()) { + context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAllOf); + } - if (oneOf_.schemas) { - bool oneValid = false; - for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++) - if (context.validators[i]->IsValid()) { - if (oneValid) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); - else - oneValid = true; + if (anyOf_.schemas) { + for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++) + if (context.validators[i]->IsValid()) + goto foundAny; + context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAnyOf); + foundAny:; + } + + if (oneOf_.schemas) { + bool oneValid = false; + SizeType firstMatch = 0; + for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++) + if (context.validators[i]->IsValid()) { + if (oneValid) { + context.error_handler.MultipleOneOf(firstMatch, i - oneOf_.begin); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOfMatch); + } else { + oneValid = true; + firstMatch = i - oneOf_.begin; + } + } + if (!oneValid) { + context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOf); } - if (!oneValid) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); - } + } - if (not_ && context.validators[notValidatorIndex_]->IsValid()) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString()); + if (not_ && context.validators[notValidatorIndex_]->IsValid()) { + context.error_handler.Disallowed(); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorNot); + } + } return true; } - bool Null(Context& context) const { - if (!(type_ & (1 << kNullSchemaType))) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + bool Null(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Null"); + if (!(type_ & (1 << kNullSchemaType))) { + DisallowedType(context, GetNullString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } return CreateParallelValidator(context); } - - bool Bool(Context& context, bool) const { - if (!(type_ & (1 << kBooleanSchemaType))) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + bool Bool(Context& context, bool b) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Bool", b); + if (!CheckBool(context, b)) + return false; return CreateParallelValidator(context); } bool Int(Context& context, int i) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Int", (int64_t)i); if (!CheckInt(context, i)) return false; return CreateParallelValidator(context); } bool Uint(Context& context, unsigned u) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Uint", (uint64_t)u); if (!CheckUint(context, u)) return false; return CreateParallelValidator(context); } bool Int64(Context& context, int64_t i) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Int64", i); if (!CheckInt(context, i)) return false; return CreateParallelValidator(context); } bool Uint64(Context& context, uint64_t u) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Uint64", u); if (!CheckUint(context, u)) return false; return CreateParallelValidator(context); } bool Double(Context& context, double d) const { - if (!(type_ & (1 << kNumberSchemaType))) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Double", d); + if (!(type_ & (1 << kNumberSchemaType))) { + DisallowedType(context, GetNumberString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d)) return false; if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d)) return false; - + if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d)) return false; - + return CreateParallelValidator(context); } - + bool String(Context& context, const Ch* str, SizeType length, bool) const { - if (!(type_ & (1 << kStringSchemaType))) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::String", str); + if (!(type_ & (1 << kStringSchemaType))) { + DisallowedType(context, GetStringString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } if (minLength_ != 0 || maxLength_ != SizeType(~0)) { SizeType count; if (internal::CountStringCodePoint(str, length, &count)) { - if (count < minLength_) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString()); - if (count > maxLength_) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString()); + if (count < minLength_) { + context.error_handler.TooShort(str, length, minLength_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinLength); + } + if (count > maxLength_) { + context.error_handler.TooLong(str, length, maxLength_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxLength); + } } } - if (pattern_ && !IsPatternMatch(pattern_, str, length)) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString()); + if (pattern_ && !IsPatternMatch(pattern_, str, length)) { + context.error_handler.DoesNotMatch(str, length); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPattern); + } return CreateParallelValidator(context); } - bool StartObject(Context& context) const { - if (!(type_ & (1 << kObjectSchemaType))) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + bool StartObject(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::StartObject"); + if (!(type_ & (1 << kObjectSchemaType))) { + DisallowedType(context, GetObjectString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } if (hasDependencies_ || hasRequired_) { context.propertyExist = static_cast(context.factory.MallocState(sizeof(bool) * propertyCount_)); @@ -779,20 +1104,24 @@ public: return CreateParallelValidator(context); } - + bool Key(Context& context, const Ch* str, SizeType len, bool) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Key", str); + if (patternProperties_) { context.patternPropertiesSchemaCount = 0; for (SizeType i = 0; i < patternPropertyCount_; i++) - if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) + if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) { context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema; + context.valueSchema = typeless_; + } } - SizeType index; + SizeType index = 0; if (FindPropertyIndex(ValueType(str, len).Move(), &index)) { if (context.patternPropertiesSchemaCount > 0) { context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema; - context.valueSchema = GetTypeless(); + context.valueSchema = typeless_; context.valuePatternValidatorType = Context::kPatternValidatorWithProperty; } else @@ -805,9 +1134,9 @@ public: } if (additionalPropertiesSchema_) { - if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) { + if (context.patternPropertiesSchemaCount > 0) { context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_; - context.valueSchema = GetTypeless(); + context.valueSchema = typeless_; context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty; } else @@ -815,73 +1144,144 @@ public: return true; } else if (additionalProperties_) { - context.valueSchema = GetTypeless(); + context.valueSchema = typeless_; return true; } - if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties - RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString()); + if (context.patternPropertiesSchemaCount == 0) { // patternProperties are not additional properties + // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error + context.valueSchema = typeless_; + context.error_handler.DisallowedProperty(str, len); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalProperties); + } return true; } bool EndObject(Context& context, SizeType memberCount) const { - if (hasRequired_) + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndObject"); + if (hasRequired_) { + context.error_handler.StartMissingProperties(); for (SizeType index = 0; index < propertyCount_; index++) - if (properties_[index].required) - if (!context.propertyExist[index]) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString()); + if (properties_[index].required && !context.propertyExist[index]) + if (properties_[index].schema->defaultValueLength_ == 0 ) + context.error_handler.AddMissingProperty(properties_[index].name); + if (context.error_handler.EndMissingProperties()) + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorRequired); + } - if (memberCount < minProperties_) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString()); + if (memberCount < minProperties_) { + context.error_handler.TooFewProperties(memberCount, minProperties_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinProperties); + } - if (memberCount > maxProperties_) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString()); + if (memberCount > maxProperties_) { + context.error_handler.TooManyProperties(memberCount, maxProperties_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxProperties); + } if (hasDependencies_) { - for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) + context.error_handler.StartDependencyErrors(); + for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) { + const Property& source = properties_[sourceIndex]; if (context.propertyExist[sourceIndex]) { - if (properties_[sourceIndex].dependencies) { + if (source.dependencies) { + context.error_handler.StartMissingDependentProperties(); for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) - if (properties_[sourceIndex].dependencies[targetIndex] && !context.propertyExist[targetIndex]) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); + if (source.dependencies[targetIndex] && !context.propertyExist[targetIndex]) + context.error_handler.AddMissingDependentProperty(properties_[targetIndex].name); + context.error_handler.EndMissingDependentProperties(source.name); + } + else if (source.dependenciesSchema) { + ISchemaValidator* dependenciesValidator = context.validators[source.dependenciesValidatorIndex]; + if (!dependenciesValidator->IsValid()) + context.error_handler.AddDependencySchemaError(source.name, dependenciesValidator); } - else if (properties_[sourceIndex].dependenciesSchema) - if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid()) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); } + } + if (context.error_handler.EndDependencyErrors()) + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorDependencies); } return true; } - bool StartArray(Context& context) const { - if (!(type_ & (1 << kArraySchemaType))) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); - + bool StartArray(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::StartArray"); context.arrayElementIndex = 0; - context.inArray = true; + context.inArray = true; // Ensure we note that we are in an array + + if (!(type_ & (1 << kArraySchemaType))) { + DisallowedType(context, GetArrayString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } return CreateParallelValidator(context); } - bool EndArray(Context& context, SizeType elementCount) const { + bool EndArray(Context& context, SizeType elementCount) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndArray"); context.inArray = false; - - if (elementCount < minItems_) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString()); - - if (elementCount > maxItems_) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString()); + + if (elementCount < minItems_) { + context.error_handler.TooFewItems(elementCount, minItems_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinItems); + } + + if (elementCount > maxItems_) { + context.error_handler.TooManyItems(elementCount, maxItems_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxItems); + } return true; } + static const ValueType& GetValidateErrorKeyword(ValidateErrorCode validateErrorCode) { + switch (validateErrorCode) { + case kValidateErrorMultipleOf: return GetMultipleOfString(); + case kValidateErrorMaximum: return GetMaximumString(); + case kValidateErrorExclusiveMaximum: return GetMaximumString(); // Same + case kValidateErrorMinimum: return GetMinimumString(); + case kValidateErrorExclusiveMinimum: return GetMinimumString(); // Same + + case kValidateErrorMaxLength: return GetMaxLengthString(); + case kValidateErrorMinLength: return GetMinLengthString(); + case kValidateErrorPattern: return GetPatternString(); + + case kValidateErrorMaxItems: return GetMaxItemsString(); + case kValidateErrorMinItems: return GetMinItemsString(); + case kValidateErrorUniqueItems: return GetUniqueItemsString(); + case kValidateErrorAdditionalItems: return GetAdditionalItemsString(); + + case kValidateErrorMaxProperties: return GetMaxPropertiesString(); + case kValidateErrorMinProperties: return GetMinPropertiesString(); + case kValidateErrorRequired: return GetRequiredString(); + case kValidateErrorAdditionalProperties: return GetAdditionalPropertiesString(); + case kValidateErrorPatternProperties: return GetPatternPropertiesString(); + case kValidateErrorDependencies: return GetDependenciesString(); + + case kValidateErrorEnum: return GetEnumString(); + case kValidateErrorType: return GetTypeString(); + + case kValidateErrorOneOf: return GetOneOfString(); + case kValidateErrorOneOfMatch: return GetOneOfString(); // Same + case kValidateErrorAllOf: return GetAllOfString(); + case kValidateErrorAnyOf: return GetAnyOfString(); + case kValidateErrorNot: return GetNotString(); + + case kValidateErrorReadOnly: return GetReadOnlyString(); + case kValidateErrorWriteOnly: return GetWriteOnlyString(); + + default: return GetNullString(); + } + } + + // Generate functions for string literal according to Ch #define RAPIDJSON_STRING_(name, ...) \ static const ValueType& Get##name##String() {\ static const Ch s[] = { __VA_ARGS__, '\0' };\ - static const ValueType v(s, sizeof(s) / sizeof(Ch) - 1);\ + static const ValueType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1));\ return v;\ } @@ -918,6 +1318,15 @@ public: RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm') RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm') RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f') + RAPIDJSON_STRING_(DefaultValue, 'd', 'e', 'f', 'a', 'u', 'l', 't') + RAPIDJSON_STRING_(Schema, '$', 's', 'c', 'h', 'e', 'm', 'a') + RAPIDJSON_STRING_(Ref, '$', 'r', 'e', 'f') + RAPIDJSON_STRING_(Id, 'i', 'd') + RAPIDJSON_STRING_(Swagger, 's', 'w', 'a', 'g', 'g', 'e', 'r') + RAPIDJSON_STRING_(OpenApi, 'o', 'p', 'e', 'n', 'a', 'p', 'i') + RAPIDJSON_STRING_(ReadOnly, 'r', 'e', 'a', 'd', 'O', 'n', 'l', 'y') + RAPIDJSON_STRING_(WriteOnly, 'w', 'r', 'i', 't', 'e', 'O', 'n', 'l', 'y') + RAPIDJSON_STRING_(Nullable, 'n', 'u', 'l', 'l', 'a', 'b', 'l', 'e') #undef RAPIDJSON_STRING_ @@ -934,7 +1343,7 @@ private: }; #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX - typedef internal::GenericRegex RegexType; + typedef internal::GenericRegex RegexType; #elif RAPIDJSON_SCHEMA_USE_STDREGEX typedef std::basic_regex RegexType; #else @@ -949,11 +1358,6 @@ private: SizeType count; }; - static const SchemaType* GetTypeless() { - static SchemaType typeless(0, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0); - return &typeless; - } - template void AddUniqueElement(V1& a, const V2& v) { for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr) @@ -988,7 +1392,7 @@ private: out.schemas = static_cast(allocator_->Malloc(out.count * sizeof(const Schema*))); memset(out.schemas, 0, sizeof(Schema*)* out.count); for (SizeType i = 0; i < out.count; i++) - schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document); + schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document, id_); out.begin = validatorCount_; validatorCount_ += out.count; } @@ -997,10 +1401,11 @@ private: #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX template - RegexType* CreatePattern(const ValueType& value) { + RegexType* CreatePattern(const ValueType& value, SchemaDocumentType* sd, const PointerType& p) { if (value.IsString()) { - RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString()); + RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), allocator_); if (!r->IsValid()) { + sd->SchemaErrorValue(kSchemaErrorRegexInvalid, p, value.GetString(), value.GetStringLength()); r->~RegexType(); AllocatorType::Free(r); r = 0; @@ -1011,17 +1416,22 @@ private: } static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) { - return pattern->Search(str); + GenericRegexSearch rs(*pattern); + return rs.Search(str); } #elif RAPIDJSON_SCHEMA_USE_STDREGEX template - RegexType* CreatePattern(const ValueType& value) { - if (value.IsString()) + RegexType* CreatePattern(const ValueType& value, SchemaDocumentType* sd, const PointerType& p) { + if (value.IsString()) { + RegexType *r = static_cast(allocator_->Malloc(sizeof(RegexType))); try { - return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); + return new (r) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); } - catch (const std::regex_error&) { + catch (const std::regex_error& e) { + sd->SchemaErrorValue(kSchemaErrorRegexInvalid, p, value.GetString(), value.GetStringLength()); + AllocatorType::Free(r); } + } return 0; } @@ -1031,7 +1441,9 @@ private: } #else template - RegexType* CreatePattern(const ValueType&) { return 0; } + RegexType* CreatePattern(const ValueType&) { + return 0; + } static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; } #endif // RAPIDJSON_SCHEMA_USE_STDREGEX @@ -1046,6 +1458,9 @@ private: else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType); } + // Creates parallel validators for allOf, anyOf, oneOf, not and schema dependencies, if required. + // Also creates a hasher for enums and array uniqueness, if required. + // Also a useful place to add type-independent error checks. bool CreateParallelValidator(Context& context) const { if (enum_ || context.arrayUniqueness) context.hasher = context.factory.CreateHasher(); @@ -1053,33 +1468,45 @@ private: if (validatorCount_) { RAPIDJSON_ASSERT(context.validators == 0); context.validators = static_cast(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_)); + std::memset(context.validators, 0, sizeof(ISchemaValidator*) * validatorCount_); context.validatorCount = validatorCount_; + // Always return after first failure for these sub-validators if (allOf_.schemas) - CreateSchemaValidators(context, allOf_); + CreateSchemaValidators(context, allOf_, false); if (anyOf_.schemas) - CreateSchemaValidators(context, anyOf_); - + CreateSchemaValidators(context, anyOf_, false); + if (oneOf_.schemas) - CreateSchemaValidators(context, oneOf_); - + CreateSchemaValidators(context, oneOf_, false); + if (not_) - context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_); - + context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_, false); + if (hasSchemaDependencies_) { for (SizeType i = 0; i < propertyCount_; i++) if (properties_[i].dependenciesSchema) - context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema); + context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema, false); } } + // Add any other type-independent checks here + if (readOnly_ && (context.flags & kValidateWriteFlag)) { + context.error_handler.DisallowedWhenWriting(); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorReadOnly); + } + if (writeOnly_ && (context.flags & kValidateReadFlag)) { + context.error_handler.DisallowedWhenReading(); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorWriteOnly); + } + return true; } - void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const { + void CreateSchemaValidators(Context& context, const SchemaArray& schemas, const bool inheritContinueOnErrors) const { for (SizeType i = 0; i < schemas.count; i++) - context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]); + context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i], inheritContinueOnErrors); } // O(n) @@ -1087,7 +1514,7 @@ private: SizeType len = name.GetStringLength(); const Ch* str = name.GetString(); for (SizeType index = 0; index < propertyCount_; index++) - if (properties_[index].name.GetStringLength() == len && + if (properties_[index].name.GetStringLength() == len && (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0)) { *outIndex = index; @@ -1096,17 +1523,30 @@ private: return false; } + bool CheckBool(Context& context, bool) const { + if (!(type_ & (1 << kBooleanSchemaType))) { + DisallowedType(context, GetBooleanString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + return true; + } + bool CheckInt(Context& context, int64_t i) const { - if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) { + DisallowedType(context, GetIntegerString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } if (!minimum_.IsNull()) { if (minimum_.IsInt64()) { - if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); + } } else if (minimum_.IsUint64()) { - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64() + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); // i <= max(int64_t) < minimum.GetUint64() } else if (!CheckDoubleMinimum(context, static_cast(i))) return false; @@ -1114,19 +1554,23 @@ private: if (!maximum_.IsNull()) { if (maximum_.IsInt64()) { - if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); + } } - else if (maximum_.IsUint64()) - /* do nothing */; // i <= max(int64_t) < maximum_.GetUint64() + else if (maximum_.IsUint64()) { } + /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64() else if (!CheckDoubleMaximum(context, static_cast(i))) return false; } if (!multipleOf_.IsNull()) { if (multipleOf_.IsUint64()) { - if (static_cast(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + if (static_cast(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) { + context.error_handler.NotMultipleOf(i, multipleOf_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf); + } } else if (!CheckDoubleMultipleOf(context, static_cast(i))) return false; @@ -1136,13 +1580,17 @@ private: } bool CheckUint(Context& context, uint64_t i) const { - if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) { + DisallowedType(context, GetIntegerString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } if (!minimum_.IsNull()) { if (minimum_.IsUint64()) { - if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); + } } else if (minimum_.IsInt64()) /* do nothing */; // i >= 0 > minimum.Getint64() @@ -1152,19 +1600,25 @@ private: if (!maximum_.IsNull()) { if (maximum_.IsUint64()) { - if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); + } + } + else if (maximum_.IsInt64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); // i >= 0 > maximum_ } - else if (maximum_.IsInt64()) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_ else if (!CheckDoubleMaximum(context, static_cast(i))) return false; } if (!multipleOf_.IsNull()) { if (multipleOf_.IsUint64()) { - if (i % multipleOf_.GetUint64() != 0) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + if (i % multipleOf_.GetUint64() != 0) { + context.error_handler.NotMultipleOf(i, multipleOf_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf); + } } else if (!CheckDoubleMultipleOf(context, static_cast(i))) return false; @@ -1174,14 +1628,18 @@ private: } bool CheckDoubleMinimum(Context& context, double d) const { - if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) { + context.error_handler.BelowMinimum(d, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); + } return true; } bool CheckDoubleMaximum(Context& context, double d) const { - if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) { + context.error_handler.AboveMaximum(d, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); + } return true; } @@ -1189,11 +1647,29 @@ private: double a = std::abs(d), b = std::abs(multipleOf_.GetDouble()); double q = std::floor(a / b); double r = a - q * b; - if (r > 0.0) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + if (r > 0.0) { + context.error_handler.NotMultipleOf(d, multipleOf_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf); + } return true; } + void DisallowedType(Context& context, const ValueType& actualType) const { + ErrorHandler& eh = context.error_handler; + eh.StartDisallowedType(); + + if (type_ & (1 << kNullSchemaType)) eh.AddExpectedType(GetNullString()); + if (type_ & (1 << kBooleanSchemaType)) eh.AddExpectedType(GetBooleanString()); + if (type_ & (1 << kObjectSchemaType)) eh.AddExpectedType(GetObjectString()); + if (type_ & (1 << kArraySchemaType)) eh.AddExpectedType(GetArrayString()); + if (type_ & (1 << kStringSchemaType)) eh.AddExpectedType(GetStringString()); + + if (type_ & (1 << kNumberSchemaType)) eh.AddExpectedType(GetNumberString()); + else if (type_ & (1 << kIntegerSchemaType)) eh.AddExpectedType(GetIntegerString()); + + eh.EndDisallowedType(actualType); + } + struct Property { Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {} ~Property() { AllocatorType::Free(dependencies); } @@ -1207,7 +1683,7 @@ private: struct PatternProperty { PatternProperty() : schema(), pattern() {} - ~PatternProperty() { + ~PatternProperty() { if (pattern) { pattern->~RegexType(); AllocatorType::Free(pattern); @@ -1218,6 +1694,11 @@ private: }; AllocatorType* allocator_; + SValue uri_; + UriType id_; + Specification spec_; + PointerType pointer_; + const SchemaType* typeless_; uint64_t* enum_; SizeType enumCount_; SchemaArray allOf_; @@ -1258,6 +1739,12 @@ private: SValue multipleOf_; bool exclusiveMinimum_; bool exclusiveMaximum_; + + SizeType defaultValueLength_; + + bool readOnly_; + bool writeOnly_; + bool nullable_; }; template @@ -1267,7 +1754,7 @@ struct TokenHelper { char buffer[21]; size_t length = static_cast((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer); for (size_t i = 0; i < length; i++) - *documentStack.template Push() = buffer[i]; + *documentStack.template Push() = static_cast(buffer[i]); } }; @@ -1299,9 +1786,18 @@ template class IGenericRemoteSchemaDocumentProvider { public: typedef typename SchemaDocumentType::Ch Ch; + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename SchemaDocumentType::AllocatorType AllocatorType; virtual ~IGenericRemoteSchemaDocumentProvider() {} virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0; + virtual const SchemaDocumentType* GetRemoteDocument(const GenericUri uri, Specification& spec) { + // Default implementation just calls through for compatibility + // Following line suppresses unused parameter warning + (void)spec; + // printf("GetRemoteDocument: %d %d\n", spec.draft, spec.oapi); + return GetRemoteDocument(uri.GetBaseString(), uri.GetBaseStringLength()); + } }; /////////////////////////////////////////////////////////////////////////////// @@ -1326,6 +1822,9 @@ public: typedef typename EncodingType::Ch Ch; typedef internal::Schema SchemaType; typedef GenericPointer PointerType; + typedef GenericValue GValue; + typedef GenericUri UriType; + typedef GenericStringRef StringRefType; friend class internal::Schema; template friend class GenericSchemaValidator; @@ -1335,37 +1834,57 @@ public: Compile a JSON document into schema document. \param document A JSON document as source. + \param uri The base URI of this schema document for purposes of violation reporting. + \param uriLength Length of \c name, in code points. \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null. \param allocator An optional allocator instance for allocating memory. Can be null. + \param pointer An optional JSON pointer to the start of the schema document + \param spec Optional schema draft or OpenAPI version. Used if no specification in document. Defaults to draft-04. */ - explicit GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) : + explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0, + IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0, + const PointerType& pointer = PointerType(), // PR #1393 + const Specification& spec = Specification(kDraft04)) : remoteProvider_(remoteProvider), allocator_(allocator), ownAllocator_(), root_(), + typeless_(), schemaMap_(allocator, kInitialSchemaMapSize), - schemaRef_(allocator, kInitialSchemaRefSize) + schemaRef_(allocator, kInitialSchemaRefSize), + spec_(spec), + error_(kObjectType), + currentError_() { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::GenericSchemaDocument"); if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + Ch noUri[1] = {0}; + uri_.SetString(uri ? uri : noUri, uriLength, *allocator_); + docId_ = UriType(uri_, allocator_); + + typeless_ = static_cast(allocator_->Malloc(sizeof(SchemaType))); + new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_, docId_); + + // Establish the schema draft or open api version. + // We only ever look for '$schema' or 'swagger' or 'openapi' at the root of the document. + SetSchemaSpecification(document); // Generate root schema, it will call CreateSchema() to create sub-schemas, - // And call AddRefSchema() if there are $ref. - CreateSchemaRecursive(&root_, PointerType(), document, document); - - // Resolve $ref - while (!schemaRef_.Empty()) { - SchemaRefEntry* refEntry = schemaRef_.template Pop(1); - if (const SchemaType* s = GetSchema(refEntry->target)) { - if (refEntry->schema) - *refEntry->schema = s; - - // Create entry in map if not exist - if (!GetSchema(refEntry->source)) { - new (schemaMap_.template Push()) SchemaEntry(refEntry->source, const_cast(s), false, allocator_); - } - } - refEntry->~SchemaRefEntry(); + // And call HandleRefSchema() if there are $ref. + // PR #1393 use input pointer if supplied + root_ = typeless_; + if (pointer.GetTokenCount() == 0) { + CreateSchemaRecursive(&root_, pointer, document, document, docId_); + } + else if (const ValueType* v = pointer.Get(document)) { + CreateSchema(&root_, pointer, *v, document, docId_); + } + else { + GenericStringBuffer sb; + pointer.StringifyUriFragment(sb); + SchemaErrorValue(kSchemaErrorStartUnknown, PointerType(), sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch))); } RAPIDJSON_ASSERT(root_ != 0); @@ -1380,12 +1899,19 @@ public: allocator_(rhs.allocator_), ownAllocator_(rhs.ownAllocator_), root_(rhs.root_), + typeless_(rhs.typeless_), schemaMap_(std::move(rhs.schemaMap_)), - schemaRef_(std::move(rhs.schemaRef_)) + schemaRef_(std::move(rhs.schemaRef_)), + uri_(std::move(rhs.uri_)), + docId_(std::move(rhs.docId_)), + spec_(rhs.spec_), + error_(std::move(rhs.error_)), + currentError_(std::move(rhs.currentError_)) { rhs.remoteProvider_ = 0; rhs.allocator_ = 0; rhs.ownAllocator_ = 0; + rhs.typeless_ = 0; } #endif @@ -1394,24 +1920,92 @@ public: while (!schemaMap_.Empty()) schemaMap_.template Pop(1)->~SchemaEntry(); + if (typeless_) { + typeless_->~SchemaType(); + Allocator::Free(typeless_); + } + + // these may contain some allocator data so clear before deleting ownAllocator_ + uri_.SetNull(); + error_.SetNull(); + currentError_.SetNull(); + RAPIDJSON_DELETE(ownAllocator_); } + const GValue& GetURI() const { return uri_; } + + const Specification& GetSpecification() const { return spec_; } + bool IsSupportedSpecification() const { return spec_.IsSupported(); } + + //! Static method to get the specification of any schema document + // Returns kDraftNone if document is silent + static const Specification GetSpecification(const ValueType& document) { + SchemaDraft draft = GetSchemaDraft(document); + if (draft != kDraftNone) + return Specification(draft); + else { + OpenApiVersion oapi = GetOpenApiVersion(document); + if (oapi != kVersionNone) + return Specification(oapi); + } + return Specification(kDraftNone); + } + //! Get the root schema. const SchemaType& GetRoot() const { return *root_; } -private: + //! Gets the error object. + GValue& GetError() { return error_; } + const GValue& GetError() const { return error_; } + + static const StringRefType& GetSchemaErrorKeyword(SchemaErrorCode schemaErrorCode) { + switch (schemaErrorCode) { + case kSchemaErrorStartUnknown: return GetStartUnknownString(); + case kSchemaErrorRefPlainName: return GetRefPlainNameString(); + case kSchemaErrorRefInvalid: return GetRefInvalidString(); + case kSchemaErrorRefPointerInvalid: return GetRefPointerInvalidString(); + case kSchemaErrorRefUnknown: return GetRefUnknownString(); + case kSchemaErrorRefCyclical: return GetRefCyclicalString(); + case kSchemaErrorRefNoRemoteProvider: return GetRefNoRemoteProviderString(); + case kSchemaErrorRefNoRemoteSchema: return GetRefNoRemoteSchemaString(); + case kSchemaErrorRegexInvalid: return GetRegexInvalidString(); + case kSchemaErrorSpecUnknown: return GetSpecUnknownString(); + case kSchemaErrorSpecUnsupported: return GetSpecUnsupportedString(); + case kSchemaErrorSpecIllegal: return GetSpecIllegalString(); + case kSchemaErrorReadOnlyAndWriteOnly: return GetReadOnlyAndWriteOnlyString(); + default: return GetNullString(); + } + } + + //! Default error method + void SchemaError(const SchemaErrorCode code, const PointerType& location) { + currentError_ = GValue(kObjectType); + AddCurrentError(code, location); + } + + //! Method for error with single string value insert + void SchemaErrorValue(const SchemaErrorCode code, const PointerType& location, const Ch* value, SizeType length) { + currentError_ = GValue(kObjectType); + currentError_.AddMember(GetValueString(), GValue(value, length, *allocator_).Move(), *allocator_); + AddCurrentError(code, location); + } + + //! Method for error with invalid pointer + void SchemaErrorPointer(const SchemaErrorCode code, const PointerType& location, const Ch* value, SizeType length, const PointerType& pointer) { + currentError_ = GValue(kObjectType); + currentError_.AddMember(GetValueString(), GValue(value, length, *allocator_).Move(), *allocator_); + currentError_.AddMember(GetOffsetString(), static_cast(pointer.GetParseErrorOffset() / sizeof(Ch)), *allocator_); + AddCurrentError(code, location); + } + + private: //! Prohibit copying GenericSchemaDocument(const GenericSchemaDocument&); //! Prohibit assignment GenericSchemaDocument& operator=(const GenericSchemaDocument&); - struct SchemaRefEntry { - SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {} - PointerType source; - PointerType target; - const SchemaType** schema; - }; + typedef const PointerType* SchemaRefPtr; // PR #1393 struct SchemaEntry { SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {} @@ -1426,78 +2020,361 @@ private: bool owned; }; - void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { - if (schema) - *schema = SchemaType::GetTypeless(); + void AddErrorInstanceLocation(GValue& result, const PointerType& location) { + GenericStringBuffer sb; + location.StringifyUriFragment(sb); + GValue instanceRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), *allocator_); + result.AddMember(GetInstanceRefString(), instanceRef, *allocator_); + } + void AddError(GValue& keyword, GValue& error) { + typename GValue::MemberIterator member = error_.FindMember(keyword); + if (member == error_.MemberEnd()) + error_.AddMember(keyword, error, *allocator_); + else { + if (member->value.IsObject()) { + GValue errors(kArrayType); + errors.PushBack(member->value, *allocator_); + member->value = errors; + } + member->value.PushBack(error, *allocator_); + } + } + + void AddCurrentError(const SchemaErrorCode code, const PointerType& location) { + RAPIDJSON_SCHEMA_PRINT(InvalidKeyword, GetSchemaErrorKeyword(code)); + currentError_.AddMember(GetErrorCodeString(), code, *allocator_); + AddErrorInstanceLocation(currentError_, location); + AddError(GValue(GetSchemaErrorKeyword(code)).Move(), currentError_); + } + +#define RAPIDJSON_STRING_(name, ...) \ + static const StringRefType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const StringRefType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1)); \ + return v;\ + } + + RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f') + RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e') + RAPIDJSON_STRING_(Value, 'v', 'a', 'l', 'u', 'e') + RAPIDJSON_STRING_(Offset, 'o', 'f', 'f', 's', 'e', 't') + + RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l') + RAPIDJSON_STRING_(SpecUnknown, 'S', 'p', 'e', 'c', 'U', 'n', 'k', 'n', 'o', 'w', 'n') + RAPIDJSON_STRING_(SpecUnsupported, 'S', 'p', 'e', 'c', 'U', 'n', 's', 'u', 'p', 'p', 'o', 'r', 't', 'e', 'd') + RAPIDJSON_STRING_(SpecIllegal, 'S', 'p', 'e', 'c', 'I', 'l', 'l', 'e', 'g', 'a', 'l') + RAPIDJSON_STRING_(StartUnknown, 'S', 't', 'a', 'r', 't', 'U', 'n', 'k', 'n', 'o', 'w', 'n') + RAPIDJSON_STRING_(RefPlainName, 'R', 'e', 'f', 'P', 'l', 'a', 'i', 'n', 'N', 'a', 'm', 'e') + RAPIDJSON_STRING_(RefInvalid, 'R', 'e', 'f', 'I', 'n', 'v', 'a', 'l', 'i', 'd') + RAPIDJSON_STRING_(RefPointerInvalid, 'R', 'e', 'f', 'P', 'o', 'i', 'n', 't', 'e', 'r', 'I', 'n', 'v', 'a', 'l', 'i', 'd') + RAPIDJSON_STRING_(RefUnknown, 'R', 'e', 'f', 'U', 'n', 'k', 'n', 'o', 'w', 'n') + RAPIDJSON_STRING_(RefCyclical, 'R', 'e', 'f', 'C', 'y', 'c', 'l', 'i', 'c', 'a', 'l') + RAPIDJSON_STRING_(RefNoRemoteProvider, 'R', 'e', 'f', 'N', 'o', 'R', 'e', 'm', 'o', 't', 'e', 'P', 'r', 'o', 'v', 'i', 'd', 'e', 'r') + RAPIDJSON_STRING_(RefNoRemoteSchema, 'R', 'e', 'f', 'N', 'o', 'R', 'e', 'm', 'o', 't', 'e', 'S', 'c', 'h', 'e', 'm', 'a') + RAPIDJSON_STRING_(ReadOnlyAndWriteOnly, 'R', 'e', 'a', 'd', 'O', 'n', 'l', 'y', 'A', 'n', 'd', 'W', 'r', 'i', 't', 'e', 'O', 'n', 'l', 'y') + RAPIDJSON_STRING_(RegexInvalid, 'R', 'e', 'g', 'e', 'x', 'I', 'n', 'v', 'a', 'l', 'i', 'd') + +#undef RAPIDJSON_STRING_ + + // Static method to get schema draft of any schema document + static SchemaDraft GetSchemaDraft(const ValueType& document) { + static const Ch kDraft03String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '3', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft04String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '4', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft05String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '5', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft06String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '6', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft07String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '7', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft2019_09String[] = { 'h', 't', 't', 'p', 's', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '/', '2', '0', '1', '9', '-', '0', '9', '/', 's', 'c', 'h', 'e', 'm', 'a', '\0' }; + static const Ch kDraft2020_12String[] = { 'h', 't', 't', 'p', 's', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '/', '2', '0', '2', '0', '-', '1', '2', '/', 's', 'c', 'h', 'e', 'm', 'a', '\0' }; + + if (!document.IsObject()) { + return kDraftNone; + } + + // Get the schema draft from the $schema keyword at the supplied location + typename ValueType::ConstMemberIterator itr = document.FindMember(SchemaType::GetSchemaString()); + if (itr != document.MemberEnd()) { + if (!itr->value.IsString()) return kDraftUnknown; + const UriType draftUri(itr->value); + // Check base uri for match + if (draftUri.Match(UriType(kDraft04String), false)) return kDraft04; + if (draftUri.Match(UriType(kDraft05String), false)) return kDraft05; + if (draftUri.Match(UriType(kDraft06String), false)) return kDraft06; + if (draftUri.Match(UriType(kDraft07String), false)) return kDraft07; + if (draftUri.Match(UriType(kDraft03String), false)) return kDraft03; + if (draftUri.Match(UriType(kDraft2019_09String), false)) return kDraft2019_09; + if (draftUri.Match(UriType(kDraft2020_12String), false)) return kDraft2020_12; + return kDraftUnknown; + } + // $schema not found + return kDraftNone; + } + + + // Get open api version of any schema document + static OpenApiVersion GetOpenApiVersion(const ValueType& document) { + static const Ch kVersion20String[] = { '2', '.', '0', '\0' }; + static const Ch kVersion30String[] = { '3', '.', '0', '.', '\0' }; // ignore patch level + static const Ch kVersion31String[] = { '3', '.', '1', '.', '\0' }; // ignore patch level + static SizeType len = internal::StrLen(kVersion30String); + + if (!document.IsObject()) { + return kVersionNone; + } + + // Get the open api version from the swagger / openapi keyword at the supplied location + typename ValueType::ConstMemberIterator itr = document.FindMember(SchemaType::GetSwaggerString()); + if (itr == document.MemberEnd()) itr = document.FindMember(SchemaType::GetOpenApiString()); + if (itr != document.MemberEnd()) { + if (!itr->value.IsString()) return kVersionUnknown; + const ValueType kVersion20Value(kVersion20String); + if (kVersion20Value == itr->value) return kVersion20; // must match 2.0 exactly + const ValueType kVersion30Value(kVersion30String); + if (itr->value.GetStringLength() > len && kVersion30Value == ValueType(itr->value.GetString(), len)) return kVersion30; // must match 3.0.x + const ValueType kVersion31Value(kVersion31String); + if (itr->value.GetStringLength() > len && kVersion31Value == ValueType(itr->value.GetString(), len)) return kVersion31; // must match 3.1.x + return kVersionUnknown; + } + // swagger or openapi not found + return kVersionNone; + } + + // Get the draft of the schema or the open api version (which implies the draft). + // Report an error if schema draft or open api version not supported or not recognized, or both in document, and carry on. + void SetSchemaSpecification(const ValueType& document) { + // Look for '$schema', 'swagger' or 'openapi' keyword at document root + SchemaDraft docDraft = GetSchemaDraft(document); + OpenApiVersion docOapi = GetOpenApiVersion(document); + // Error if both in document + if (docDraft != kDraftNone && docOapi != kVersionNone) + SchemaError(kSchemaErrorSpecIllegal, PointerType()); + // Use document draft or open api version if present or use spec from constructor + if (docDraft != kDraftNone) + spec_ = Specification(docDraft); + else if (docOapi != kVersionNone) + spec_ = Specification(docOapi); + // Error if draft or version unknown + if (spec_.draft == kDraftUnknown || spec_.oapi == kVersionUnknown) + SchemaError(kSchemaErrorSpecUnknown, PointerType()); + else if (!spec_.IsSupported()) + SchemaError(kSchemaErrorSpecUnsupported, PointerType()); + } + + // Changed by PR #1393 + void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) { if (v.GetType() == kObjectType) { - const SchemaType* s = GetSchema(pointer); - if (!s) - CreateSchema(schema, pointer, v, document); + UriType newid = UriType(CreateSchema(schema, pointer, v, document, id), allocator_); for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) - CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document); + CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document, newid); } else if (v.GetType() == kArrayType) for (SizeType i = 0; i < v.Size(); i++) - CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document); + CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document, id); } - void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { + // Changed by PR #1393 + const UriType& CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) { RAPIDJSON_ASSERT(pointer.IsValid()); + GenericStringBuffer sb; + pointer.StringifyUriFragment(sb); + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::CreateSchema", sb.GetString(), id.GetString()); if (v.IsObject()) { - if (!HandleRefSchema(pointer, schema, v, document)) { - SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_); - new (schemaMap_.template Push()) SchemaEntry(pointer, s, true, allocator_); + if (const SchemaType* sc = GetSchema(pointer)) { + if (schema) + *schema = sc; + AddSchemaRefs(const_cast(sc)); + } + else if (!HandleRefSchema(pointer, schema, v, document, id)) { + // The new schema constructor adds itself and its $ref(s) to schemaMap_ + SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_, id); if (schema) *schema = s; + return s->GetId(); } } + else { + if (schema) + *schema = typeless_; + AddSchemaRefs(typeless_); + } + return id; } - bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) { - static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' }; - static const ValueType kRefValue(kRefString, 4); - - typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue); + // Changed by PR #1393 + // TODO should this return a UriType& ? + bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document, const UriType& id) { + typename ValueType::ConstMemberIterator itr = v.FindMember(SchemaType::GetRefString()); if (itr == v.MemberEnd()) return false; + GenericStringBuffer sb; + source.StringifyUriFragment(sb); + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::HandleRefSchema", sb.GetString(), id.GetString()); + // Resolve the source pointer to the $ref'ed schema (finally) + new (schemaRef_.template Push()) SchemaRefPtr(&source); + if (itr->value.IsString()) { SizeType len = itr->value.GetStringLength(); - if (len > 0) { - const Ch* s = itr->value.GetString(); - SizeType i = 0; - while (i < len && s[i] != '#') // Find the first # - i++; - - if (i > 0) { // Remote reference, resolve immediately - if (remoteProvider_) { - if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i - 1)) { - PointerType pointer(&s[i], len - i, allocator_); - if (pointer.IsValid()) { - if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) { - if (schema) - *schema = sc; - return true; + if (len == 0) + SchemaError(kSchemaErrorRefInvalid, source); + else { + // First resolve $ref against the in-scope id + UriType scopeId = UriType(id, allocator_); + UriType ref = UriType(itr->value, allocator_).Resolve(scopeId, allocator_); + RAPIDJSON_SCHEMA_PRINT(SchemaIds, id.GetString(), itr->value.GetString(), ref.GetString()); + // See if the resolved $ref minus the fragment matches a resolved id in this document + // Search from the root. Returns the subschema in the document and its absolute JSON pointer. + PointerType basePointer = PointerType(); + const ValueType *base = FindId(document, ref, basePointer, docId_, false); + if (!base) { + // Remote reference - call the remote document provider + if (!remoteProvider_) + SchemaError(kSchemaErrorRefNoRemoteProvider, source); + else { + if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(ref, spec_)) { + const Ch* s = ref.GetFragString(); + len = ref.GetFragStringLength(); + if (len <= 1 || s[1] == '/') { + // JSON pointer fragment, absolute in the remote schema + const PointerType pointer(s, len, allocator_); + if (!pointer.IsValid()) + SchemaErrorPointer(kSchemaErrorRefPointerInvalid, source, s, len, pointer); + else { + // Get the subschema + if (const SchemaType *sc = remoteDocument->GetSchema(pointer)) { + if (schema) + *schema = sc; + AddSchemaRefs(const_cast(sc)); + return true; + } else + SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength()); } - } - } + } else + // Plain name fragment, not allowed in remote schema + SchemaErrorValue(kSchemaErrorRefPlainName, source, s, len); + } else + SchemaErrorValue(kSchemaErrorRefNoRemoteSchema, source, ref.GetString(), ref.GetStringLength()); } } - else if (s[i] == '#') { // Local reference, defer resolution - PointerType pointer(&s[i], len - i, allocator_); - if (pointer.IsValid()) { - if (const ValueType* nv = pointer.Get(document)) - if (HandleRefSchema(source, schema, *nv, document)) + else { // Local reference + const Ch* s = ref.GetFragString(); + len = ref.GetFragStringLength(); + if (len <= 1 || s[1] == '/') { + // JSON pointer fragment, relative to the resolved URI + const PointerType relPointer(s, len, allocator_); + if (!relPointer.IsValid()) + SchemaErrorPointer(kSchemaErrorRefPointerInvalid, source, s, len, relPointer); + else { + // Get the subschema + if (const ValueType *pv = relPointer.Get(*base)) { + // Now get the absolute JSON pointer by adding relative to base + PointerType pointer(basePointer, allocator_); + for (SizeType i = 0; i < relPointer.GetTokenCount(); i++) + pointer = pointer.Append(relPointer.GetTokens()[i], allocator_); + if (IsCyclicRef(pointer)) + SchemaErrorValue(kSchemaErrorRefCyclical, source, ref.GetString(), ref.GetStringLength()); + else { + // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there + // TODO: cache pointer <-> id mapping + size_t unresolvedTokenIndex; + scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_); + CreateSchema(schema, pointer, *pv, document, scopeId); + return true; + } + } else + SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength()); + } + } else { + // Plain name fragment, relative to the resolved URI + // Not supported in open api 2.0 and 3.0 + PointerType pointer(allocator_); + if (spec_.oapi == kVersion20 || spec_.oapi == kVersion30) + SchemaErrorValue(kSchemaErrorRefPlainName, source, s, len); + // See if the fragment matches an id in this document. + // Search from the base we just established. Returns the subschema in the document and its absolute JSON pointer. + else if (const ValueType *pv = FindId(*base, ref, pointer, UriType(ref.GetBaseString(), ref.GetBaseStringLength(), allocator_), true, basePointer)) { + if (IsCyclicRef(pointer)) + SchemaErrorValue(kSchemaErrorRefCyclical, source, ref.GetString(), ref.GetStringLength()); + else { + // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there + // TODO: cache pointer <-> id mapping + size_t unresolvedTokenIndex; + scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_); + CreateSchema(schema, pointer, *pv, document, scopeId); return true; - - new (schemaRef_.template Push()) SchemaRefEntry(source, pointer, schema, allocator_); - return true; + } + } else + SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength()); } } } } + + // Invalid/Unknown $ref + if (schema) + *schema = typeless_; + AddSchemaRefs(typeless_); + return true; + } + + //! Find the first subschema with a resolved 'id' that matches the specified URI. + // If full specified use all URI else ignore fragment. + // If found, return a pointer to the subschema and its JSON pointer. + // TODO cache pointer <-> id mapping + ValueType* FindId(const ValueType& doc, const UriType& finduri, PointerType& resptr, const UriType& baseuri, bool full, const PointerType& here = PointerType()) const { + SizeType i = 0; + ValueType* resval = 0; + UriType tempuri = UriType(finduri, allocator_); + UriType localuri = UriType(baseuri, allocator_); + if (doc.GetType() == kObjectType) { + // Establish the base URI of this object + typename ValueType::ConstMemberIterator m = doc.FindMember(SchemaType::GetIdString()); + if (m != doc.MemberEnd() && m->value.GetType() == kStringType) { + localuri = UriType(m->value, allocator_).Resolve(baseuri, allocator_); + } + // See if it matches + if (localuri.Match(finduri, full)) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::FindId (match)", full ? localuri.GetString() : localuri.GetBaseString()); + resval = const_cast(&doc); + resptr = here; + return resval; + } + // No match, continue looking + for (m = doc.MemberBegin(); m != doc.MemberEnd(); ++m) { + if (m->value.GetType() == kObjectType || m->value.GetType() == kArrayType) { + resval = FindId(m->value, finduri, resptr, localuri, full, here.Append(m->name.GetString(), m->name.GetStringLength(), allocator_)); + } + if (resval) break; + } + } else if (doc.GetType() == kArrayType) { + // Continue looking + for (typename ValueType::ConstValueIterator v = doc.Begin(); v != doc.End(); ++v) { + if (v->GetType() == kObjectType || v->GetType() == kArrayType) { + resval = FindId(*v, finduri, resptr, localuri, full, here.Append(i, allocator_)); + } + if (resval) break; + i++; + } + } + return resval; + } + + // Added by PR #1393 + void AddSchemaRefs(SchemaType* schema) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::AddSchemaRefs"); + while (!schemaRef_.Empty()) { + SchemaRefPtr *ref = schemaRef_.template Pop(1); + SchemaEntry *entry = schemaMap_.template Push(); + new (entry) SchemaEntry(**ref, schema, false, allocator_); + } + } + + // Added by PR #1393 + bool IsCyclicRef(const PointerType& pointer) const { + for (const SchemaRefPtr* ref = schemaRef_.template Bottom(); ref != schemaRef_.template End(); ++ref) + if (pointer == **ref) + return true; return false; } @@ -1515,6 +2392,8 @@ private: return PointerType(); } + const SchemaType* GetTypeless() const { return typeless_; } + static const size_t kInitialSchemaMapSize = 64; static const size_t kInitialSchemaRefSize = 64; @@ -1522,8 +2401,14 @@ private: Allocator *allocator_; Allocator *ownAllocator_; const SchemaType* root_; //!< Root schema. + SchemaType* typeless_; internal::Stack schemaMap_; // Stores created Pointer -> Schemas - internal::Stack schemaRef_; // Stores Pointer from $ref and schema which holds the $ref + internal::Stack schemaRef_; // Stores Pointer(s) from $ref(s) until resolved + GValue uri_; // Schema document URI + UriType docId_; + Specification spec_; + GValue error_; + GValue currentError_; }; //! GenericSchemaDocument using Value type. @@ -1552,13 +2437,16 @@ template < typename StateAllocator = CrtAllocator> class GenericSchemaValidator : public internal::ISchemaStateFactory, - public internal::ISchemaValidator -{ + public internal::ISchemaValidator, + public internal::IValidationErrorHandler { public: typedef typename SchemaDocumentType::SchemaType SchemaType; typedef typename SchemaDocumentType::PointerType PointerType; typedef typename SchemaType::EncodingType EncodingType; + typedef typename SchemaType::SValue SValue; typedef typename EncodingType::Ch Ch; + typedef GenericStringRef StringRefType; + typedef GenericValue ValueType; //! Constructor without output handler. /*! @@ -1575,16 +2463,19 @@ public: : schemaDocument_(&schemaDocument), root_(schemaDocument.GetRoot()), - outputHandler_(GetNullHandler()), stateAllocator_(allocator), ownStateAllocator_(0), schemaStack_(allocator, schemaStackCapacity), documentStack_(allocator, documentStackCapacity), - valid_(true) -#if RAPIDJSON_SCHEMA_VERBOSE - , depth_(0) -#endif + outputHandler_(0), + error_(kObjectType), + currentError_(), + missingDependents_(), + valid_(true), + flags_(kValidateDefaultFlags), + depth_(0) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator"); } //! Constructor with output handler. @@ -1603,16 +2494,19 @@ public: : schemaDocument_(&schemaDocument), root_(schemaDocument.GetRoot()), - outputHandler_(outputHandler), stateAllocator_(allocator), ownStateAllocator_(0), schemaStack_(allocator, schemaStackCapacity), documentStack_(allocator, documentStackCapacity), - valid_(true) -#if RAPIDJSON_SCHEMA_VERBOSE - , depth_(0) -#endif + outputHandler_(&outputHandler), + error_(kObjectType), + currentError_(), + missingDependents_(), + valid_(true), + flags_(kValidateDefaultFlags), + depth_(0) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator (output handler)"); } //! Destructor. @@ -1626,44 +2520,291 @@ public: while (!schemaStack_.Empty()) PopSchema(); documentStack_.Clear(); + ResetError(); + } + + //! Reset the error state. + void ResetError() { + error_.SetObject(); + currentError_.SetNull(); + missingDependents_.SetNull(); valid_ = true; } - //! Checks whether the current state is valid. - // Implementation of ISchemaValidator - virtual bool IsValid() const { return valid_; } + //! Implementation of ISchemaValidator + void SetValidateFlags(unsigned flags) { + flags_ = flags; + } + virtual unsigned GetValidateFlags() const { + return flags_; + } + + virtual bool IsValid() const { + if (!valid_) return false; + if (GetContinueOnErrors() && !error_.ObjectEmpty()) return false; + return true; + } + //! End of Implementation of ISchemaValidator + + //! Gets the error object. + ValueType& GetError() { return error_; } + const ValueType& GetError() const { return error_; } //! Gets the JSON pointer pointed to the invalid schema. + // If reporting all errors, the stack will be empty. PointerType GetInvalidSchemaPointer() const { - return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema()); + return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer(); } //! Gets the keyword of invalid schema. + // If reporting all errors, the stack will be empty, so return "errors". const Ch* GetInvalidSchemaKeyword() const { - return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword; + if (!schemaStack_.Empty()) return CurrentContext().invalidKeyword; + if (GetContinueOnErrors() && !error_.ObjectEmpty()) return static_cast(GetErrorsString()); + return 0; + } + + //! Gets the error code of invalid schema. + // If reporting all errors, the stack will be empty, so return kValidateErrors. + ValidateErrorCode GetInvalidSchemaCode() const { + if (!schemaStack_.Empty()) return CurrentContext().invalidCode; + if (GetContinueOnErrors() && !error_.ObjectEmpty()) return kValidateErrors; + return kValidateErrorNone; } //! Gets the JSON pointer pointed to the invalid value. + // If reporting all errors, the stack will be empty. PointerType GetInvalidDocumentPointer() const { - return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); + if (documentStack_.Empty()) { + return PointerType(); + } + else { + return PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); + } } -#if RAPIDJSON_SCHEMA_VERBOSE -#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \ -RAPIDJSON_MULTILINEMACRO_BEGIN\ - *documentStack_.template Push() = '\0';\ - documentStack_.template Pop(1);\ - internal::PrintInvalidDocument(documentStack_.template Bottom());\ -RAPIDJSON_MULTILINEMACRO_END -#else -#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() -#endif + void NotMultipleOf(int64_t actual, const SValue& expected) { + AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected); + } + void NotMultipleOf(uint64_t actual, const SValue& expected) { + AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected); + } + void NotMultipleOf(double actual, const SValue& expected) { + AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected); + } + void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void AboveMaximum(double actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + void BelowMinimum(double actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + + void TooLong(const Ch* str, SizeType length, SizeType expected) { + AddNumberError(kValidateErrorMaxLength, + ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); + } + void TooShort(const Ch* str, SizeType length, SizeType expected) { + AddNumberError(kValidateErrorMinLength, + ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); + } + void DoesNotMatch(const Ch* str, SizeType length) { + currentError_.SetObject(); + currentError_.AddMember(GetActualString(), ValueType(str, length, GetStateAllocator()).Move(), GetStateAllocator()); + AddCurrentError(kValidateErrorPattern); + } + + void DisallowedItem(SizeType index) { + currentError_.SetObject(); + currentError_.AddMember(GetDisallowedString(), ValueType(index).Move(), GetStateAllocator()); + AddCurrentError(kValidateErrorAdditionalItems, true); + } + void TooFewItems(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMinItems, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void TooManyItems(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMaxItems, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void DuplicateItems(SizeType index1, SizeType index2) { + ValueType duplicates(kArrayType); + duplicates.PushBack(index1, GetStateAllocator()); + duplicates.PushBack(index2, GetStateAllocator()); + currentError_.SetObject(); + currentError_.AddMember(GetDuplicatesString(), duplicates, GetStateAllocator()); + AddCurrentError(kValidateErrorUniqueItems, true); + } + + void TooManyProperties(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMaxProperties, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void TooFewProperties(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMinProperties, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void StartMissingProperties() { + currentError_.SetArray(); + } + void AddMissingProperty(const SValue& name) { + currentError_.PushBack(ValueType(name, GetStateAllocator()).Move(), GetStateAllocator()); + } + bool EndMissingProperties() { + if (currentError_.Empty()) + return false; + ValueType error(kObjectType); + error.AddMember(GetMissingString(), currentError_, GetStateAllocator()); + currentError_ = error; + AddCurrentError(kValidateErrorRequired); + return true; + } + void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) { + for (SizeType i = 0; i < count; ++i) + MergeError(static_cast(subvalidators[i])->GetError()); + } + void DisallowedProperty(const Ch* name, SizeType length) { + currentError_.SetObject(); + currentError_.AddMember(GetDisallowedString(), ValueType(name, length, GetStateAllocator()).Move(), GetStateAllocator()); + AddCurrentError(kValidateErrorAdditionalProperties, true); + } + + void StartDependencyErrors() { + currentError_.SetObject(); + } + void StartMissingDependentProperties() { + missingDependents_.SetArray(); + } + void AddMissingDependentProperty(const SValue& targetName) { + missingDependents_.PushBack(ValueType(targetName, GetStateAllocator()).Move(), GetStateAllocator()); + } + void EndMissingDependentProperties(const SValue& sourceName) { + if (!missingDependents_.Empty()) { + // Create equivalent 'required' error + ValueType error(kObjectType); + ValidateErrorCode code = kValidateErrorRequired; + error.AddMember(GetMissingString(), missingDependents_.Move(), GetStateAllocator()); + AddErrorCode(error, code); + AddErrorInstanceLocation(error, false); + // When appending to a pointer ensure its allocator is used + PointerType schemaRef = GetInvalidSchemaPointer().Append(SchemaType::GetValidateErrorKeyword(kValidateErrorDependencies), &GetInvalidSchemaPointer().GetAllocator()); + AddErrorSchemaLocation(error, schemaRef.Append(sourceName.GetString(), sourceName.GetStringLength(), &GetInvalidSchemaPointer().GetAllocator())); + ValueType wrapper(kObjectType); + wrapper.AddMember(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator()).Move(), error, GetStateAllocator()); + currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), wrapper, GetStateAllocator()); + } + } + void AddDependencySchemaError(const SValue& sourceName, ISchemaValidator* subvalidator) { + currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), + static_cast(subvalidator)->GetError(), GetStateAllocator()); + } + bool EndDependencyErrors() { + if (currentError_.ObjectEmpty()) + return false; + ValueType error(kObjectType); + error.AddMember(GetErrorsString(), currentError_, GetStateAllocator()); + currentError_ = error; + AddCurrentError(kValidateErrorDependencies); + return true; + } + + void DisallowedValue(const ValidateErrorCode code = kValidateErrorEnum) { + currentError_.SetObject(); + AddCurrentError(code); + } + void StartDisallowedType() { + currentError_.SetArray(); + } + void AddExpectedType(const typename SchemaType::ValueType& expectedType) { + currentError_.PushBack(ValueType(expectedType, GetStateAllocator()).Move(), GetStateAllocator()); + } + void EndDisallowedType(const typename SchemaType::ValueType& actualType) { + ValueType error(kObjectType); + error.AddMember(GetExpectedString(), currentError_, GetStateAllocator()); + error.AddMember(GetActualString(), ValueType(actualType, GetStateAllocator()).Move(), GetStateAllocator()); + currentError_ = error; + AddCurrentError(kValidateErrorType); + } + void NotAllOf(ISchemaValidator** subvalidators, SizeType count) { + // Treat allOf like oneOf and anyOf to match https://rapidjson.org/md_doc_schema.html#allOf-anyOf-oneOf + AddErrorArray(kValidateErrorAllOf, subvalidators, count); + //for (SizeType i = 0; i < count; ++i) { + // MergeError(static_cast(subvalidators[i])->GetError()); + //} + } + void NoneOf(ISchemaValidator** subvalidators, SizeType count) { + AddErrorArray(kValidateErrorAnyOf, subvalidators, count); + } + void NotOneOf(ISchemaValidator** subvalidators, SizeType count) { + AddErrorArray(kValidateErrorOneOf, subvalidators, count); + } + void MultipleOneOf(SizeType index1, SizeType index2) { + ValueType matches(kArrayType); + matches.PushBack(index1, GetStateAllocator()); + matches.PushBack(index2, GetStateAllocator()); + currentError_.SetObject(); + currentError_.AddMember(GetMatchesString(), matches, GetStateAllocator()); + AddCurrentError(kValidateErrorOneOfMatch); + } + void Disallowed() { + currentError_.SetObject(); + AddCurrentError(kValidateErrorNot); + } + void DisallowedWhenWriting() { + currentError_.SetObject(); + AddCurrentError(kValidateErrorReadOnly); + } + void DisallowedWhenReading() { + currentError_.SetObject(); + AddCurrentError(kValidateErrorWriteOnly); + } + +#define RAPIDJSON_STRING_(name, ...) \ + static const StringRefType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const StringRefType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1)); \ + return v;\ + } + + RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f') + RAPIDJSON_STRING_(SchemaRef, 's', 'c', 'h', 'e', 'm', 'a', 'R', 'e', 'f') + RAPIDJSON_STRING_(Expected, 'e', 'x', 'p', 'e', 'c', 't', 'e', 'd') + RAPIDJSON_STRING_(Actual, 'a', 'c', 't', 'u', 'a', 'l') + RAPIDJSON_STRING_(Disallowed, 'd', 'i', 's', 'a', 'l', 'l', 'o', 'w', 'e', 'd') + RAPIDJSON_STRING_(Missing, 'm', 'i', 's', 's', 'i', 'n', 'g') + RAPIDJSON_STRING_(Errors, 'e', 'r', 'r', 'o', 'r', 's') + RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e') + RAPIDJSON_STRING_(ErrorMessage, 'e', 'r', 'r', 'o', 'r', 'M', 'e', 's', 's', 'a', 'g', 'e') + RAPIDJSON_STRING_(Duplicates, 'd', 'u', 'p', 'l', 'i', 'c', 'a', 't', 'e', 's') + RAPIDJSON_STRING_(Matches, 'm', 'a', 't', 'c', 'h', 'e', 's') + +#undef RAPIDJSON_STRING_ #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\ if (!valid_) return false; \ - if (!BeginValue() || !CurrentSchema().method arg1) {\ - RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\ - return valid_ = false;\ + if ((!BeginValue() && !GetContinueOnErrors()) || (!CurrentSchema().method arg1 && !GetContinueOnErrors())) {\ + *documentStack_.template Push() = '\0';\ + documentStack_.template Pop(1);\ + RAPIDJSON_SCHEMA_PRINT(InvalidDocument, documentStack_.template Bottom());\ + valid_ = false;\ + return valid_;\ } #define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\ @@ -1679,14 +2820,15 @@ RAPIDJSON_MULTILINEMACRO_END } #define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\ - return valid_ = EndValue() && outputHandler_.method arg2 + valid_ = (EndValue() || GetContinueOnErrors()) && (!outputHandler_ || outputHandler_->method arg2);\ + return valid_; #define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \ RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\ RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\ RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2) - bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext() ), ( )); } + bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext()), ( )); } bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); } bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); } bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); } @@ -1699,51 +2841,69 @@ RAPIDJSON_MULTILINEMACRO_END { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } bool StartObject() { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::StartObject"); RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); - return valid_ = outputHandler_.StartObject(); + valid_ = !outputHandler_ || outputHandler_->StartObject(); + return valid_; } bool Key(const Ch* str, SizeType len, bool copy) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::Key", str); if (!valid_) return false; AppendToken(str, len); - if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false; + if (!CurrentSchema().Key(CurrentContext(), str, len, copy) && !GetContinueOnErrors()) { + valid_ = false; + return valid_; + } RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); - return valid_ = outputHandler_.Key(str, len, copy); + valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy); + return valid_; } - bool EndObject(SizeType memberCount) { + bool EndObject(SizeType memberCount) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndObject"); if (!valid_) return false; RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount)); - if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false; + if (!CurrentSchema().EndObject(CurrentContext(), memberCount) && !GetContinueOnErrors()) { + valid_ = false; + return valid_; + } RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount)); } bool StartArray() { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::StartArray"); RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); - return valid_ = outputHandler_.StartArray(); + valid_ = !outputHandler_ || outputHandler_->StartArray(); + return valid_; } bool EndArray(SizeType elementCount) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndArray"); if (!valid_) return false; RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount)); - if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false; + if (!CurrentSchema().EndArray(CurrentContext(), elementCount) && !GetContinueOnErrors()) { + valid_ = false; + return valid_; + } RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount)); } -#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_ #undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_ #undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_ #undef RAPIDJSON_SCHEMA_HANDLE_VALUE_ // Implementation of ISchemaStateFactory - virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) { - return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, -#if RAPIDJSON_SCHEMA_VERBOSE + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root, const bool inheritContinueOnErrors) { + *documentStack_.template Push() = '\0'; + documentStack_.template Pop(1); + ISchemaValidator* sv = new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom(), documentStack_.GetSize(), depth_ + 1, -#endif &GetStateAllocator()); + sv->SetValidateFlags(inheritContinueOnErrors ? GetValidateFlags() : GetValidateFlags() & ~static_cast(kValidateContinueOnErrorFlag)); + return sv; } virtual void DestroySchemaValidator(ISchemaValidator* validator) { @@ -1771,8 +2931,9 @@ RAPIDJSON_MULTILINEMACRO_END } virtual void FreeState(void* p) { - return StateAllocator::Free(p); + StateAllocator::Free(p); } + // End of implementation of ISchemaStateFactory private: typedef typename SchemaType::Context Context; @@ -1782,57 +2943,67 @@ private: GenericSchemaValidator( const SchemaDocumentType& schemaDocument, const SchemaType& root, -#if RAPIDJSON_SCHEMA_VERBOSE + const char* basePath, size_t basePathSize, unsigned depth, -#endif StateAllocator* allocator = 0, size_t schemaStackCapacity = kDefaultSchemaStackCapacity, size_t documentStackCapacity = kDefaultDocumentStackCapacity) : schemaDocument_(&schemaDocument), root_(root), - outputHandler_(GetNullHandler()), stateAllocator_(allocator), ownStateAllocator_(0), schemaStack_(allocator, schemaStackCapacity), documentStack_(allocator, documentStackCapacity), - valid_(true) -#if RAPIDJSON_SCHEMA_VERBOSE - , depth_(depth) -#endif + outputHandler_(0), + error_(kObjectType), + currentError_(), + missingDependents_(), + valid_(true), + flags_(kValidateDefaultFlags), + depth_(depth) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator (internal)", basePath && basePathSize ? basePath : ""); + if (basePath && basePathSize) + memcpy(documentStack_.template Push(basePathSize), basePath, basePathSize); } StateAllocator& GetStateAllocator() { if (!stateAllocator_) - stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator()); + stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)(); return *stateAllocator_; } + bool GetContinueOnErrors() const { + return flags_ & kValidateContinueOnErrorFlag; + } + bool BeginValue() { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::BeginValue"); if (schemaStack_.Empty()) PushSchema(root_); else { if (CurrentContext().inArray) internal::TokenHelper, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex); - if (!CurrentSchema().BeginValue(CurrentContext())) + if (!CurrentSchema().BeginValue(CurrentContext()) && !GetContinueOnErrors()) return false; SizeType count = CurrentContext().patternPropertiesSchemaCount; const SchemaType** sa = CurrentContext().patternPropertiesSchemas; typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType; bool valueUniqueness = CurrentContext().valueUniqueness; - if (CurrentContext().valueSchema) - PushSchema(*CurrentContext().valueSchema); + RAPIDJSON_ASSERT(CurrentContext().valueSchema); + PushSchema(*CurrentContext().valueSchema); if (count > 0) { CurrentContext().objectPatternValidatorType = patternValidatorType; ISchemaValidator**& va = CurrentContext().patternPropertiesValidators; SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount; va = static_cast(MallocState(sizeof(ISchemaValidator*) * count)); + std::memset(va, 0, sizeof(ISchemaValidator*) * count); for (SizeType i = 0; i < count; i++) - va[validatorCount++] = CreateSchemaValidator(*sa[i]); + va[validatorCount++] = CreateSchemaValidator(*sa[i], true); // Inherit continueOnError } CurrentContext().arrayUniqueness = valueUniqueness; @@ -1841,31 +3012,37 @@ private: } bool EndValue() { - if (!CurrentSchema().EndValue(CurrentContext())) + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndValue"); + if (!CurrentSchema().EndValue(CurrentContext()) && !GetContinueOnErrors()) return false; -#if RAPIDJSON_SCHEMA_VERBOSE GenericStringBuffer sb; - schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb); - + schemaDocument_->GetPointer(&CurrentSchema()).StringifyUriFragment(sb); *documentStack_.template Push() = '\0'; documentStack_.template Pop(1); - internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom()); -#endif - - uint64_t h = CurrentContext().arrayUniqueness ? static_cast(CurrentContext().hasher)->GetHashCode() : 0; + RAPIDJSON_SCHEMA_PRINT(ValidatorPointers, sb.GetString(), documentStack_.template Bottom(), depth_); + void* hasher = CurrentContext().hasher; + uint64_t h = hasher && CurrentContext().arrayUniqueness ? static_cast(hasher)->GetHashCode() : 0; PopSchema(); if (!schemaStack_.Empty()) { Context& context = CurrentContext(); - if (context.valueUniqueness) { + // Only check uniqueness if there is a hasher + if (hasher && context.valueUniqueness) { HashCodeArray* a = static_cast(context.arrayElementHashCodes); if (!a) CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType); for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr) - if (itr->GetUint64() == h) - RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString()); + if (itr->GetUint64() == h) { + DuplicateItems(static_cast(itr - a->Begin()), a->Size()); + // Cleanup before returning if continuing + if (GetContinueOnErrors()) { + a->PushBack(h, GetStateAllocator()); + while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/'); + } + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorUniqueItems); + } a->PushBack(h, GetStateAllocator()); } } @@ -1894,7 +3071,7 @@ private: } } - RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(*this, &schema); } + RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(*this, *this, &schema, flags_); } RAPIDJSON_FORCEINLINE void PopSchema() { Context* c = schemaStack_.template Pop(1); @@ -1905,28 +3082,98 @@ private: c->~Context(); } + void AddErrorInstanceLocation(ValueType& result, bool parent) { + GenericStringBuffer sb; + PointerType instancePointer = GetInvalidDocumentPointer(); + ((parent && instancePointer.GetTokenCount() > 0) + ? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1) + : instancePointer).StringifyUriFragment(sb); + ValueType instanceRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), + GetStateAllocator()); + result.AddMember(GetInstanceRefString(), instanceRef, GetStateAllocator()); + } + + void AddErrorSchemaLocation(ValueType& result, PointerType schema = PointerType()) { + GenericStringBuffer sb; + SizeType len = CurrentSchema().GetURI().GetStringLength(); + if (len) memcpy(sb.Push(len), CurrentSchema().GetURI().GetString(), len * sizeof(Ch)); + if (schema.GetTokenCount()) schema.StringifyUriFragment(sb); + else GetInvalidSchemaPointer().StringifyUriFragment(sb); + ValueType schemaRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), + GetStateAllocator()); + result.AddMember(GetSchemaRefString(), schemaRef, GetStateAllocator()); + } + + void AddErrorCode(ValueType& result, const ValidateErrorCode code) { + result.AddMember(GetErrorCodeString(), code, GetStateAllocator()); + } + + void AddError(ValueType& keyword, ValueType& error) { + typename ValueType::MemberIterator member = error_.FindMember(keyword); + if (member == error_.MemberEnd()) + error_.AddMember(keyword, error, GetStateAllocator()); + else { + if (member->value.IsObject()) { + ValueType errors(kArrayType); + errors.PushBack(member->value, GetStateAllocator()); + member->value = errors; + } + member->value.PushBack(error, GetStateAllocator()); + } + } + + void AddCurrentError(const ValidateErrorCode code, bool parent = false) { + AddErrorCode(currentError_, code); + AddErrorInstanceLocation(currentError_, parent); + AddErrorSchemaLocation(currentError_); + AddError(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator(), false).Move(), currentError_); + } + + void MergeError(ValueType& other) { + for (typename ValueType::MemberIterator it = other.MemberBegin(), end = other.MemberEnd(); it != end; ++it) { + AddError(it->name, it->value); + } + } + + void AddNumberError(const ValidateErrorCode code, ValueType& actual, const SValue& expected, + const typename SchemaType::ValueType& (*exclusive)() = 0) { + currentError_.SetObject(); + currentError_.AddMember(GetActualString(), actual, GetStateAllocator()); + currentError_.AddMember(GetExpectedString(), ValueType(expected, GetStateAllocator()).Move(), GetStateAllocator()); + if (exclusive) + currentError_.AddMember(ValueType(exclusive(), GetStateAllocator()).Move(), true, GetStateAllocator()); + AddCurrentError(code); + } + + void AddErrorArray(const ValidateErrorCode code, + ISchemaValidator** subvalidators, SizeType count) { + ValueType errors(kArrayType); + for (SizeType i = 0; i < count; ++i) + errors.PushBack(static_cast(subvalidators[i])->GetError(), GetStateAllocator()); + currentError_.SetObject(); + currentError_.AddMember(GetErrorsString(), errors, GetStateAllocator()); + AddCurrentError(code); + } + const SchemaType& CurrentSchema() const { return *schemaStack_.template Top()->schema; } Context& CurrentContext() { return *schemaStack_.template Top(); } const Context& CurrentContext() const { return *schemaStack_.template Top(); } - static OutputHandler& GetNullHandler() { - static OutputHandler nullHandler; - return nullHandler; - } - static const size_t kDefaultSchemaStackCapacity = 1024; static const size_t kDefaultDocumentStackCapacity = 256; const SchemaDocumentType* schemaDocument_; const SchemaType& root_; - OutputHandler& outputHandler_; StateAllocator* stateAllocator_; StateAllocator* ownStateAllocator_; internal::Stack schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) internal::Stack documentStack_; //!< stack to store the current path of validating document (Ch) + OutputHandler* outputHandler_; + ValueType error_; + ValueType currentError_; + ValueType missingDependents_; bool valid_; -#if RAPIDJSON_SCHEMA_VERBOSE + unsigned flags_; unsigned depth_; -#endif }; typedef GenericSchemaValidator SchemaValidator; @@ -1954,13 +3201,14 @@ class SchemaValidatingReader { public: typedef typename SchemaDocumentType::PointerType PointerType; typedef typename InputStream::Ch Ch; + typedef GenericValue ValueType; //! Constructor /*! \param is Input stream. \param sd Schema document. */ - SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {} + SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), invalidSchemaCode_(kValidateErrorNone), error_(kObjectType), isValid_(true) {} template bool operator()(Handler& handler) { @@ -1973,11 +3221,14 @@ public: invalidSchemaPointer_ = PointerType(); invalidSchemaKeyword_ = 0; invalidDocumentPointer_ = PointerType(); + error_.SetObject(); } else { invalidSchemaPointer_ = validator.GetInvalidSchemaPointer(); invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword(); + invalidSchemaCode_ = validator.GetInvalidSchemaCode(); invalidDocumentPointer_ = validator.GetInvalidDocumentPointer(); + error_.CopyFrom(validator.GetError(), allocator_); } return parseResult_; @@ -1988,6 +3239,8 @@ public: const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; } const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; } const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; } + const ValueType& GetError() const { return error_; } + ValidateErrorCode GetInvalidSchemaCode() const { return invalidSchemaCode_; } private: InputStream& is_; @@ -1997,6 +3250,9 @@ private: PointerType invalidSchemaPointer_; const Ch* invalidSchemaKeyword_; PointerType invalidDocumentPointer_; + ValidateErrorCode invalidSchemaCode_; + StackAllocator allocator_; + ValueType error_; bool isValid_; }; diff --git a/third_party/rapidjson/include/rapidjson/stream.h b/third_party/rapidjson/include/rapidjson/stream.h index fef82c252..1fd70915c 100644 --- a/third_party/rapidjson/include/rapidjson/stream.h +++ b/third_party/rapidjson/include/rapidjson/stream.h @@ -1,15 +1,15 @@ // Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // -// 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 +// 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 "rapidjson.h" @@ -100,6 +100,50 @@ inline void PutN(Stream& stream, Ch c, size_t n) { PutUnsafe(stream, c); } +/////////////////////////////////////////////////////////////////////////////// +// GenericStreamWrapper + +//! A Stream Wrapper +/*! \tThis string stream is a wrapper for any stream by just forwarding any + \treceived message to the origin stream. + \note implements Stream concept +*/ + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4702) // unreachable code +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +template > +class GenericStreamWrapper { +public: + typedef typename Encoding::Ch Ch; + GenericStreamWrapper(InputStream& is): is_(is) {} + + Ch Peek() const { return is_.Peek(); } + Ch Take() { return is_.Take(); } + size_t Tell() { return is_.Tell(); } + Ch* PutBegin() { return is_.PutBegin(); } + void Put(Ch ch) { is_.Put(ch); } + void Flush() { is_.Flush(); } + size_t PutEnd(Ch* ch) { return is_.PutEnd(ch); } + + // wrapper for MemoryStream + const Ch* Peek4() const { return is_.Peek4(); } + + // wrapper for AutoUTFInputStream + UTFType GetType() const { return is_.GetType(); } + bool HasBOM() const { return is_.HasBOM(); } + +protected: + InputStream& is_; +}; + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_POP +#endif + /////////////////////////////////////////////////////////////////////////////// // StringStream diff --git a/third_party/rapidjson/include/rapidjson/stringbuffer.h b/third_party/rapidjson/include/rapidjson/stringbuffer.h index 78f34d209..82ad3ca6b 100644 --- a/third_party/rapidjson/include/rapidjson/stringbuffer.h +++ b/third_party/rapidjson/include/rapidjson/stringbuffer.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -78,8 +78,12 @@ public: return stack_.template Bottom(); } + //! Get the size of string in bytes in the string buffer. size_t GetSize() const { return stack_.GetSize(); } + //! Get the length of string in Ch in the string buffer. + size_t GetLength() const { return stack_.GetSize() / sizeof(Ch); } + static const size_t kDefaultCapacity = 256; mutable internal::Stack stack_; diff --git a/third_party/rapidjson/include/rapidjson/uri.h b/third_party/rapidjson/include/rapidjson/uri.h new file mode 100644 index 000000000..f93e508a4 --- /dev/null +++ b/third_party/rapidjson/include/rapidjson/uri.h @@ -0,0 +1,481 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// (C) Copyright IBM Corporation 2021 +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_URI_H_ +#define RAPIDJSON_URI_H_ + +#include "internal/strfunc.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// GenericUri + +template +class GenericUri { +public: + typedef typename ValueType::Ch Ch; +#if RAPIDJSON_HAS_STDSTRING + typedef std::basic_string String; +#endif + + //! Constructors + GenericUri(Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + } + + GenericUri(const Ch* uri, SizeType len, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + Parse(uri, len); + } + + GenericUri(const Ch* uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + Parse(uri, internal::StrLen(uri)); + } + + // Use with specializations of GenericValue + template GenericUri(const T& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + const Ch* u = uri.template Get(); // TypeHelper from document.h + Parse(u, internal::StrLen(u)); + } + +#if RAPIDJSON_HAS_STDSTRING + GenericUri(const String& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + Parse(uri.c_str(), internal::StrLen(uri.c_str())); + } +#endif + + //! Copy constructor + GenericUri(const GenericUri& rhs) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(), ownAllocator_() { + *this = rhs; + } + + //! Copy constructor + GenericUri(const GenericUri& rhs, Allocator* allocator) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + *this = rhs; + } + + //! Destructor. + ~GenericUri() { + Free(); + RAPIDJSON_DELETE(ownAllocator_); + } + + //! Assignment operator + GenericUri& operator=(const GenericUri& rhs) { + if (this != &rhs) { + // Do not delete ownAllocator + Free(); + Allocate(rhs.GetStringLength()); + auth_ = CopyPart(scheme_, rhs.scheme_, rhs.GetSchemeStringLength()); + path_ = CopyPart(auth_, rhs.auth_, rhs.GetAuthStringLength()); + query_ = CopyPart(path_, rhs.path_, rhs.GetPathStringLength()); + frag_ = CopyPart(query_, rhs.query_, rhs.GetQueryStringLength()); + base_ = CopyPart(frag_, rhs.frag_, rhs.GetFragStringLength()); + uri_ = CopyPart(base_, rhs.base_, rhs.GetBaseStringLength()); + CopyPart(uri_, rhs.uri_, rhs.GetStringLength()); + } + return *this; + } + + //! Getters + // Use with specializations of GenericValue + template void Get(T& uri, Allocator& allocator) { + uri.template Set(this->GetString(), allocator); // TypeHelper from document.h + } + + const Ch* GetString() const { return uri_; } + SizeType GetStringLength() const { return uri_ == 0 ? 0 : internal::StrLen(uri_); } + const Ch* GetBaseString() const { return base_; } + SizeType GetBaseStringLength() const { return base_ == 0 ? 0 : internal::StrLen(base_); } + const Ch* GetSchemeString() const { return scheme_; } + SizeType GetSchemeStringLength() const { return scheme_ == 0 ? 0 : internal::StrLen(scheme_); } + const Ch* GetAuthString() const { return auth_; } + SizeType GetAuthStringLength() const { return auth_ == 0 ? 0 : internal::StrLen(auth_); } + const Ch* GetPathString() const { return path_; } + SizeType GetPathStringLength() const { return path_ == 0 ? 0 : internal::StrLen(path_); } + const Ch* GetQueryString() const { return query_; } + SizeType GetQueryStringLength() const { return query_ == 0 ? 0 : internal::StrLen(query_); } + const Ch* GetFragString() const { return frag_; } + SizeType GetFragStringLength() const { return frag_ == 0 ? 0 : internal::StrLen(frag_); } + +#if RAPIDJSON_HAS_STDSTRING + static String Get(const GenericUri& uri) { return String(uri.GetString(), uri.GetStringLength()); } + static String GetBase(const GenericUri& uri) { return String(uri.GetBaseString(), uri.GetBaseStringLength()); } + static String GetScheme(const GenericUri& uri) { return String(uri.GetSchemeString(), uri.GetSchemeStringLength()); } + static String GetAuth(const GenericUri& uri) { return String(uri.GetAuthString(), uri.GetAuthStringLength()); } + static String GetPath(const GenericUri& uri) { return String(uri.GetPathString(), uri.GetPathStringLength()); } + static String GetQuery(const GenericUri& uri) { return String(uri.GetQueryString(), uri.GetQueryStringLength()); } + static String GetFrag(const GenericUri& uri) { return String(uri.GetFragString(), uri.GetFragStringLength()); } +#endif + + //! Equality operators + bool operator==(const GenericUri& rhs) const { + return Match(rhs, true); + } + + bool operator!=(const GenericUri& rhs) const { + return !Match(rhs, true); + } + + bool Match(const GenericUri& uri, bool full = true) const { + Ch* s1; + Ch* s2; + if (full) { + s1 = uri_; + s2 = uri.uri_; + } else { + s1 = base_; + s2 = uri.base_; + } + if (s1 == s2) return true; + if (s1 == 0 || s2 == 0) return false; + return internal::StrCmp(s1, s2) == 0; + } + + //! Resolve this URI against another (base) URI in accordance with URI resolution rules. + // See https://tools.ietf.org/html/rfc3986 + // Use for resolving an id or $ref with an in-scope id. + // Returns a new GenericUri for the resolved URI. + GenericUri Resolve(const GenericUri& baseuri, Allocator* allocator = 0) { + GenericUri resuri; + resuri.allocator_ = allocator; + // Ensure enough space for combining paths + resuri.Allocate(GetStringLength() + baseuri.GetStringLength() + 1); // + 1 for joining slash + + if (!(GetSchemeStringLength() == 0)) { + // Use all of this URI + resuri.auth_ = CopyPart(resuri.scheme_, scheme_, GetSchemeStringLength()); + resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength()); + resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength()); + resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); + resuri.RemoveDotSegments(); + } else { + // Use the base scheme + resuri.auth_ = CopyPart(resuri.scheme_, baseuri.scheme_, baseuri.GetSchemeStringLength()); + if (!(GetAuthStringLength() == 0)) { + // Use this auth, path, query + resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength()); + resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength()); + resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); + resuri.RemoveDotSegments(); + } else { + // Use the base auth + resuri.path_ = CopyPart(resuri.auth_, baseuri.auth_, baseuri.GetAuthStringLength()); + if (GetPathStringLength() == 0) { + // Use the base path + resuri.query_ = CopyPart(resuri.path_, baseuri.path_, baseuri.GetPathStringLength()); + if (GetQueryStringLength() == 0) { + // Use the base query + resuri.frag_ = CopyPart(resuri.query_, baseuri.query_, baseuri.GetQueryStringLength()); + } else { + // Use this query + resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); + } + } else { + if (path_[0] == '/') { + // Absolute path - use all of this path + resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength()); + resuri.RemoveDotSegments(); + } else { + // Relative path - append this path to base path after base path's last slash + size_t pos = 0; + if (!(baseuri.GetAuthStringLength() == 0) && baseuri.GetPathStringLength() == 0) { + resuri.path_[pos] = '/'; + pos++; + } + size_t lastslashpos = baseuri.GetPathStringLength(); + while (lastslashpos > 0) { + if (baseuri.path_[lastslashpos - 1] == '/') break; + lastslashpos--; + } + std::memcpy(&resuri.path_[pos], baseuri.path_, lastslashpos * sizeof(Ch)); + pos += lastslashpos; + resuri.query_ = CopyPart(&resuri.path_[pos], path_, GetPathStringLength()); + resuri.RemoveDotSegments(); + } + // Use this query + resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); + } + } + } + // Always use this frag + resuri.base_ = CopyPart(resuri.frag_, frag_, GetFragStringLength()); + + // Re-constitute base_ and uri_ + resuri.SetBase(); + resuri.uri_ = resuri.base_ + resuri.GetBaseStringLength() + 1; + resuri.SetUri(); + return resuri; + } + + //! Get the allocator of this GenericUri. + Allocator& GetAllocator() { return *allocator_; } + +private: + // Allocate memory for a URI + // Returns total amount allocated + std::size_t Allocate(std::size_t len) { + // Create own allocator if user did not supply. + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + // Allocate one block containing each part of the URI (5) plus base plus full URI, all null terminated. + // Order: scheme, auth, path, query, frag, base, uri + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + size_t total = (3 * len + 7) * sizeof(Ch); + scheme_ = static_cast(allocator_->Malloc(total)); + *scheme_ = '\0'; + auth_ = scheme_; + auth_++; + *auth_ = '\0'; + path_ = auth_; + path_++; + *path_ = '\0'; + query_ = path_; + query_++; + *query_ = '\0'; + frag_ = query_; + frag_++; + *frag_ = '\0'; + base_ = frag_; + base_++; + *base_ = '\0'; + uri_ = base_; + uri_++; + *uri_ = '\0'; + return total; + } + + // Free memory for a URI + void Free() { + if (scheme_) { + Allocator::Free(scheme_); + scheme_ = 0; + } + } + + // Parse a URI into constituent scheme, authority, path, query, & fragment parts + // Supports URIs that match regex ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? as per + // https://tools.ietf.org/html/rfc3986 + void Parse(const Ch* uri, std::size_t len) { + std::size_t start = 0, pos1 = 0, pos2 = 0; + Allocate(len); + + // Look for scheme ([^:/?#]+):)? + if (start < len) { + while (pos1 < len) { + if (uri[pos1] == ':') break; + pos1++; + } + if (pos1 != len) { + while (pos2 < len) { + if (uri[pos2] == '/') break; + if (uri[pos2] == '?') break; + if (uri[pos2] == '#') break; + pos2++; + } + if (pos1 < pos2) { + pos1++; + std::memcpy(scheme_, &uri[start], pos1 * sizeof(Ch)); + scheme_[pos1] = '\0'; + start = pos1; + } + } + } + // Look for auth (//([^/?#]*))? + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + auth_ = scheme_ + GetSchemeStringLength(); + auth_++; + *auth_ = '\0'; + if (start < len - 1 && uri[start] == '/' && uri[start + 1] == '/') { + pos2 = start + 2; + while (pos2 < len) { + if (uri[pos2] == '/') break; + if (uri[pos2] == '?') break; + if (uri[pos2] == '#') break; + pos2++; + } + std::memcpy(auth_, &uri[start], (pos2 - start) * sizeof(Ch)); + auth_[pos2 - start] = '\0'; + start = pos2; + } + // Look for path ([^?#]*) + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + path_ = auth_ + GetAuthStringLength(); + path_++; + *path_ = '\0'; + if (start < len) { + pos2 = start; + while (pos2 < len) { + if (uri[pos2] == '?') break; + if (uri[pos2] == '#') break; + pos2++; + } + if (start != pos2) { + std::memcpy(path_, &uri[start], (pos2 - start) * sizeof(Ch)); + path_[pos2 - start] = '\0'; + if (path_[0] == '/') + RemoveDotSegments(); // absolute path - normalize + start = pos2; + } + } + // Look for query (\?([^#]*))? + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + query_ = path_ + GetPathStringLength(); + query_++; + *query_ = '\0'; + if (start < len && uri[start] == '?') { + pos2 = start + 1; + while (pos2 < len) { + if (uri[pos2] == '#') break; + pos2++; + } + if (start != pos2) { + std::memcpy(query_, &uri[start], (pos2 - start) * sizeof(Ch)); + query_[pos2 - start] = '\0'; + start = pos2; + } + } + // Look for fragment (#(.*))? + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + frag_ = query_ + GetQueryStringLength(); + frag_++; + *frag_ = '\0'; + if (start < len && uri[start] == '#') { + std::memcpy(frag_, &uri[start], (len - start) * sizeof(Ch)); + frag_[len - start] = '\0'; + } + + // Re-constitute base_ and uri_ + base_ = frag_ + GetFragStringLength() + 1; + SetBase(); + uri_ = base_ + GetBaseStringLength() + 1; + SetUri(); + } + + // Reconstitute base + void SetBase() { + Ch* next = base_; + std::memcpy(next, scheme_, GetSchemeStringLength() * sizeof(Ch)); + next+= GetSchemeStringLength(); + std::memcpy(next, auth_, GetAuthStringLength() * sizeof(Ch)); + next+= GetAuthStringLength(); + std::memcpy(next, path_, GetPathStringLength() * sizeof(Ch)); + next+= GetPathStringLength(); + std::memcpy(next, query_, GetQueryStringLength() * sizeof(Ch)); + next+= GetQueryStringLength(); + *next = '\0'; + } + + // Reconstitute uri + void SetUri() { + Ch* next = uri_; + std::memcpy(next, base_, GetBaseStringLength() * sizeof(Ch)); + next+= GetBaseStringLength(); + std::memcpy(next, frag_, GetFragStringLength() * sizeof(Ch)); + next+= GetFragStringLength(); + *next = '\0'; + } + + // Copy a part from one GenericUri to another + // Return the pointer to the next part to be copied to + Ch* CopyPart(Ch* to, Ch* from, std::size_t len) { + RAPIDJSON_ASSERT(to != 0); + RAPIDJSON_ASSERT(from != 0); + std::memcpy(to, from, len * sizeof(Ch)); + to[len] = '\0'; + Ch* next = to + len + 1; + return next; + } + + // Remove . and .. segments from the path_ member. + // https://tools.ietf.org/html/rfc3986 + // This is done in place as we are only removing segments. + void RemoveDotSegments() { + std::size_t pathlen = GetPathStringLength(); + std::size_t pathpos = 0; // Position in path_ + std::size_t newpos = 0; // Position in new path_ + + // Loop through each segment in original path_ + while (pathpos < pathlen) { + // Get next segment, bounded by '/' or end + size_t slashpos = 0; + while ((pathpos + slashpos) < pathlen) { + if (path_[pathpos + slashpos] == '/') break; + slashpos++; + } + // Check for .. and . segments + if (slashpos == 2 && path_[pathpos] == '.' && path_[pathpos + 1] == '.') { + // Backup a .. segment in the new path_ + // We expect to find a previously added slash at the end or nothing + RAPIDJSON_ASSERT(newpos == 0 || path_[newpos - 1] == '/'); + size_t lastslashpos = newpos; + // Make sure we don't go beyond the start segment + if (lastslashpos > 1) { + // Find the next to last slash and back up to it + lastslashpos--; + while (lastslashpos > 0) { + if (path_[lastslashpos - 1] == '/') break; + lastslashpos--; + } + // Set the new path_ position + newpos = lastslashpos; + } + } else if (slashpos == 1 && path_[pathpos] == '.') { + // Discard . segment, leaves new path_ unchanged + } else { + // Move any other kind of segment to the new path_ + RAPIDJSON_ASSERT(newpos <= pathpos); + std::memmove(&path_[newpos], &path_[pathpos], slashpos * sizeof(Ch)); + newpos += slashpos; + // Add slash if not at end + if ((pathpos + slashpos) < pathlen) { + path_[newpos] = '/'; + newpos++; + } + } + // Move to next segment + pathpos += slashpos + 1; + } + path_[newpos] = '\0'; + } + + Ch* uri_; // Everything + Ch* base_; // Everything except fragment + Ch* scheme_; // Includes the : + Ch* auth_; // Includes the // + Ch* path_; // Absolute if starts with / + Ch* query_; // Includes the ? + Ch* frag_; // Includes the # + + Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_. + Allocator* ownAllocator_; //!< Allocator owned by this Uri. +}; + +//! GenericUri for Value (UTF-8, default allocator). +typedef GenericUri Uri; + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_URI_H_ diff --git a/third_party/rapidjson/include/rapidjson/writer.h b/third_party/rapidjson/include/rapidjson/writer.h index 94f22dd5f..632e02ce7 100644 --- a/third_party/rapidjson/include/rapidjson/writer.h +++ b/third_party/rapidjson/include/rapidjson/writer.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -16,6 +16,8 @@ #define RAPIDJSON_WRITER_H_ #include "stream.h" +#include "internal/clzll.h" +#include "internal/meta.h" #include "internal/stack.h" #include "internal/strfunc.h" #include "internal/dtoa.h" @@ -31,17 +33,18 @@ #include #elif defined(RAPIDJSON_SSE2) #include -#endif - -#ifdef _MSC_VER -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +#elif defined(RAPIDJSON_NEON) +#include #endif #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(padded) RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant #endif RAPIDJSON_NAMESPACE_BEGIN @@ -64,6 +67,7 @@ enum WriteFlag { kWriteNoFlags = 0, //!< No flags are set. kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings. kWriteNanAndInfFlag = 2, //!< Allow writing of Infinity, -Infinity and NaN. + kWriteNanAndInfNullFlag = 4, //!< Allow writing of Infinity, -Infinity and NaN as null. kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS }; @@ -103,6 +107,13 @@ public: Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Writer(Writer&& rhs) : + os_(rhs.os_), level_stack_(std::move(rhs.level_stack_)), maxDecimalPlaces_(rhs.maxDecimalPlaces_), hasRoot_(rhs.hasRoot_) { + rhs.os_ = 0; + } +#endif + //! Reset the writer with a new stream. /*! This function reset the writer with a new stream and default settings, @@ -184,12 +195,14 @@ public: bool Double(double d) { Prefix(kNumberType); return EndValue(WriteDouble(d)); } bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); (void)copy; Prefix(kNumberType); return EndValue(WriteString(str, length)); } bool String(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); (void)copy; Prefix(kStringType); return EndValue(WriteString(str, length)); @@ -209,10 +222,18 @@ public: bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } +#if RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) + { + return Key(str.data(), SizeType(str.size())); + } +#endif + bool EndObject(SizeType memberCount = 0) { (void)memberCount; - RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); - RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); // not inside an Object + RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value level_stack_.template Pop(1); return EndValue(WriteEndObject()); } @@ -236,9 +257,9 @@ public: //@{ //! Simpler but slower overload. - bool String(const Ch* str) { return String(str, internal::StrLen(str)); } - bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } - + bool String(const Ch* const& str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* const& str) { return Key(str, internal::StrLen(str)); } + //@} //! Write a raw JSON value. @@ -249,7 +270,21 @@ public: \param length Length of the json. \param type Type of the root of json. */ - bool RawValue(const Ch* json, size_t length, Type type) { Prefix(type); return EndValue(WriteRawValue(json, length)); } + bool RawValue(const Ch* json, size_t length, Type type) { + RAPIDJSON_ASSERT(json != 0); + Prefix(type); + return EndValue(WriteRawValue(json, length)); + } + + //! Flush the output stream. + /*! + Allows the user to flush the output stream immediately. + */ + void Flush() { + os_->Flush(); + } + + static const size_t kDefaultLevelDepth = 32; protected: //! Information for each nested level @@ -259,8 +294,6 @@ protected: bool inArray; //!< true if in array, otherwise in object }; - static const size_t kDefaultLevelDepth = 32; - bool WriteNull() { PutReserve(*os_, 4); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true; @@ -283,7 +316,7 @@ protected: const char* end = internal::i32toa(i, buffer); PutReserve(*os_, static_cast(end - buffer)); for (const char* p = buffer; p != end; ++p) - PutUnsafe(*os_, static_cast(*p)); + PutUnsafe(*os_, static_cast(*p)); return true; } @@ -292,7 +325,7 @@ protected: const char* end = internal::u32toa(u, buffer); PutReserve(*os_, static_cast(end - buffer)); for (const char* p = buffer; p != end; ++p) - PutUnsafe(*os_, static_cast(*p)); + PutUnsafe(*os_, static_cast(*p)); return true; } @@ -301,7 +334,7 @@ protected: const char* end = internal::i64toa(i64, buffer); PutReserve(*os_, static_cast(end - buffer)); for (const char* p = buffer; p != end; ++p) - PutUnsafe(*os_, static_cast(*p)); + PutUnsafe(*os_, static_cast(*p)); return true; } @@ -310,14 +343,19 @@ protected: char* end = internal::u64toa(u64, buffer); PutReserve(*os_, static_cast(end - buffer)); for (char* p = buffer; p != end; ++p) - PutUnsafe(*os_, static_cast(*p)); + PutUnsafe(*os_, static_cast(*p)); return true; } bool WriteDouble(double d) { if (internal::Double(d).IsNanOrInf()) { - if (!(writeFlags & kWriteNanAndInfFlag)) + if (!(writeFlags & kWriteNanAndInfFlag) && !(writeFlags & kWriteNanAndInfNullFlag)) return false; + if (writeFlags & kWriteNanAndInfNullFlag) { + PutReserve(*os_, 4); + PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); + return true; + } if (internal::Double(d).IsNan()) { PutReserve(*os_, 3); PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); @@ -338,12 +376,12 @@ protected: char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); PutReserve(*os_, static_cast(end - buffer)); for (char* p = buffer; p != end; ++p) - PutUnsafe(*os_, static_cast(*p)); + PutUnsafe(*os_, static_cast(*p)); return true; } bool WriteString(const Ch* str, SizeType length) { - static const typename TargetEncoding::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + static const typename OutputStream::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; static const char escape[256] = { #define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 //0 1 2 3 4 5 6 7 8 9 A B C D E F @@ -399,7 +437,7 @@ protected: else if ((sizeof(Ch) == 1 || static_cast(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast(c)])) { is.Take(); PutUnsafe(*os_, '\\'); - PutUnsafe(*os_, static_cast(escape[static_cast(c)])); + PutUnsafe(*os_, static_cast(escape[static_cast(c)])); if (escape[static_cast(c)] == 'u') { PutUnsafe(*os_, '0'); PutUnsafe(*os_, '0'); @@ -427,9 +465,13 @@ protected: bool WriteRawValue(const Ch* json, size_t length) { PutReserve(*os_, length); - for (size_t i = 0; i < length; i++) { - RAPIDJSON_ASSERT(json[i] != '\0'); - PutUnsafe(*os_, json[i]); + GenericStringStream is(json); + while (RAPIDJSON_LIKELY(is.Tell() < length)) { + RAPIDJSON_ASSERT(is.Peek() != '\0'); + if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? + Transcoder::Validate(is, *os_) : + Transcoder::TranscodeUnsafe(is, *os_)))) + return false; } return true; } @@ -457,7 +499,7 @@ protected: // Flush the value if it is the top level one. bool EndValue(bool ret) { if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text - os_->Flush(); + Flush(); return ret; } @@ -512,6 +554,11 @@ inline bool Writer::WriteDouble(double d) { // Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag). if (!(kWriteDefaultFlags & kWriteNanAndInfFlag)) return false; + if (kWriteDefaultFlags & kWriteNanAndInfNullFlag) { + PutReserve(*os_, 4); + PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); + return true; + } if (internal::Double(d).IsNan()) { PutReserve(*os_, 3); PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); @@ -561,7 +608,7 @@ inline bool Writer::ScanWriteUnescapedString(StringStream& is, siz // The rest of string using SIMD static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; - static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); @@ -570,7 +617,7 @@ inline bool Writer::ScanWriteUnescapedString(StringStream& is, siz const __m128i s = _mm_load_si128(reinterpret_cast(p)); const __m128i t1 = _mm_cmpeq_epi8(s, dq); const __m128i t2 = _mm_cmpeq_epi8(s, bs); - const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); unsigned short r = static_cast(_mm_movemask_epi8(x)); if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped @@ -595,15 +642,79 @@ inline bool Writer::ScanWriteUnescapedString(StringStream& is, siz is.src_ = p; return RAPIDJSON_LIKELY(is.Tell() < length); } -#endif // defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) +#elif defined(RAPIDJSON_NEON) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return RAPIDJSON_LIKELY(is.Tell() < length); + + if (!RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (; p != endAligned; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + SizeType len = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + len = 8 + (lz >> 3); + escaped = true; + } + } else { + uint32_t lz = internal::clzll(low); + len = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + vst1q_u8(reinterpret_cast(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); +} +#endif // RAPIDJSON_NEON RAPIDJSON_NAMESPACE_END -#ifdef _MSC_VER -RAPIDJSON_DIAG_POP -#endif - -#ifdef __clang__ +#if defined(_MSC_VER) || defined(__clang__) RAPIDJSON_DIAG_POP #endif diff --git a/third_party/rapidjson/library.json b/third_party/rapidjson/library.json index 47fd352ac7efa35c343de9a2d74ee55ff19ba2eb..2210fcd61735c8cca63c19534e8df591eda60d58 100644 GIT binary patch delta 77 zcmdnV^q6Uaj6hjxQE_H|o|2VUS1lI+y44sp delta 58 zcmaFNw3BIq%tWIO;mo|`oYIt3B`XCb7<1yoOv&8Loczkn0{x=Ig3Oex;`}@%E(L{J Hu39btde#)3 diff --git a/third_party/rapidjson/package.json b/third_party/rapidjson/package.json index cc6087a5ca36cfd95aacccceb7c07d909f085f4d..129581a633512e5165e985041339c58f990efc24 100644 GIT binary patch delta 60 zcmdnUvXNy&CL?=DYF=_`UdiMF#uPaFHlvjcR9wF(u^=-gt2jSTxhOR;B{x-xtCkA@ Dmf#g| delta 60 zcmdnUvXNy&CL?=pW=?)(X2IkF#uPaFHlvjcR9wF(u^=-gt2jSTxhOR;B{x-xtCkA@ Dt%nu{ diff --git a/third_party/rapidjson/rapidjson.autopkg b/third_party/rapidjson/rapidjson.autopkg index 70eb0d8a0..fe72030a9 100644 --- a/third_party/rapidjson/rapidjson.autopkg +++ b/third_party/rapidjson/rapidjson.autopkg @@ -5,10 +5,10 @@ nuget { id = rapidjson; version : ${MYVERSION}; title: "rapidjson"; - authors: {"https://github.com/miloyip/rapidjson/releases/tag/v1.1.0"}; + authors: {"https://github.com/Tencent/rapidjson/releases/tag/v1.1.0"}; owners: {"@lsantos (github)"}; - licenseUrl: "https://github.com/miloyip/rapidjson/blob/master/license.txt"; - projectUrl: "https://github.com/miloyip/rapidjson/"; + licenseUrl: "https://github.com/Tencent/rapidjson/blob/master/license.txt"; + projectUrl: "https://github.com/Tencent/rapidjson/"; iconUrl: "https://cdn1.iconfinder.com/data/icons/fatcow/32x32/json.png"; requireLicenseAcceptance:false; summary: @"A fast JSON parser/generator for C++ with both SAX/DOM style API"; @@ -48,7 +48,7 @@ Changed dependencies { packages : { - //TODO: Add dependecies here in [pkg.name]/[version] form per newline + //TODO: Add dependencies here in [pkg.name]/[version] form per newline //zlib/[1.2.8], }; } @@ -71,5 +71,7 @@ Changed targets { // We're trying to be standard about these sorts of thing. (Will help with config.h later :D) //Defines += HAS_EQCORE; + // Fix creating the package with Raggles' fork of CoApp + Includes += "$(MSBuildThisFileDirectory)../..${d_include}"; }; } \ No newline at end of file diff --git a/third_party/rapidjson/readme.md b/third_party/rapidjson/readme.md index 4a1d64d0a..ac683b051 100644 --- a/third_party/rapidjson/readme.md +++ b/third_party/rapidjson/readme.md @@ -1,14 +1,14 @@ -![](doc/logo/rapidjson.png) +![RapidJSON logo](doc/logo/rapidjson.png) -![](https://img.shields.io/badge/release-v1.1.0-blue.png) +![Release version](https://img.shields.io/badge/release-v1.1.0-blue.svg) -## A fast JSON parser/generator for C++ with both SAX/DOM style API +## A fast JSON parser/generator for C++ with both SAX/DOM style API Tencent is pleased to support the open source community by making RapidJSON available. -Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -* [RapidJSON GitHub](https://github.com/miloyip/rapidjson/) +* [RapidJSON GitHub](https://github.com/Tencent/rapidjson/) * RapidJSON Documentation * [English](http://rapidjson.org/) * [简体中文](http://rapidjson.org/zh-cn/) @@ -20,12 +20,12 @@ Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights | :---------------: | :-----------------: | :-------------------: | | ![lin-badge] | ![win-badge] | ![cov-badge] | -[lin-badge]: https://travis-ci.org/miloyip/rapidjson.png?branch=master "Travis build status" -[lin-link]: https://travis-ci.org/miloyip/rapidjson "Travis build status" -[win-badge]: https://ci.appveyor.com/api/projects/status/u658dcuwxo14a8m9/branch/master "AppVeyor build status" -[win-link]: https://ci.appveyor.com/project/miloyip/rapidjson/branch/master "AppVeyor build status" -[cov-badge]: https://coveralls.io/repos/miloyip/rapidjson/badge.png?branch=master -[cov-link]: https://coveralls.io/r/miloyip/rapidjson?branch=master +[lin-badge]: https://travis-ci.org/Tencent/rapidjson.svg?branch=master "Travis build status" +[lin-link]: https://travis-ci.org/Tencent/rapidjson "Travis build status" +[win-badge]: https://ci.appveyor.com/api/projects/status/l6qulgqahcayidrf/branch/master?svg=true "AppVeyor build status" +[win-link]: https://ci.appveyor.com/project/miloyip/rapidjson-0fdqj/branch/master "AppVeyor build status" +[cov-badge]: https://coveralls.io/repos/Tencent/rapidjson/badge.svg?branch=master "Coveralls coverage" +[cov-link]: https://coveralls.io/r/Tencent/rapidjson?branch=master "Coveralls coverage" ## Introduction @@ -43,10 +43,10 @@ RapidJSON is a JSON parser and generator for C++. It was inspired by [RapidXml]( More features can be read [here](doc/features.md). -JSON(JavaScript Object Notation) is a light-weight data exchange format. RapidJSON should be in fully compliance with RFC7159/ECMA-404, with optional support of relaxed syntax. More information about JSON can be obtained at +JSON(JavaScript Object Notation) is a light-weight data exchange format. RapidJSON should be in full compliance with RFC7159/ECMA-404, with optional support of relaxed syntax. More information about JSON can be obtained at * [Introducing JSON](http://json.org/) -* [RFC7159: The JavaScript Object Notation (JSON) Data Interchange Format](http://www.ietf.org/rfc/rfc7159.txt) -* [Standard ECMA-404: The JSON Data Interchange Format](http://www.ecma-international.org/publications/standards/Ecma-404.htm) +* [RFC7159: The JavaScript Object Notation (JSON) Data Interchange Format](https://tools.ietf.org/html/rfc7159) +* [Standard ECMA-404: The JSON Data Interchange Format](https://www.ecma-international.org/publications/standards/Ecma-404.htm) ## Highlights in v1.1 (2016-8-25) @@ -72,10 +72,13 @@ Users can build and run the unit tests on their platform/compiler. RapidJSON is a header-only C++ library. Just copy the `include/rapidjson` folder to system or project's include path. +Alternatively, if you are using the [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager you can download and install rapidjson with CMake integration in a single command: +* vcpkg install rapidjson + RapidJSON uses following software as its dependencies: * [CMake](https://cmake.org/) as a general build tool -* (optional)[Doxygen](http://www.doxygen.org) to build documentation -* (optional)[googletest](https://github.com/google/googletest) for unit and performance testing +* (optional) [Doxygen](http://www.doxygen.org) to build documentation +* (optional) [googletest](https://github.com/google/googletest) for unit and performance testing To generate user documentation and run tests please proceed with the steps below: @@ -84,7 +87,7 @@ To generate user documentation and run tests please proceed with the steps below 3. Change to `build` directory and run `cmake ..` command to configure your build. Windows users can do the same with cmake-gui application. 4. On Windows, build the solution found in the build directory. On Linux, run `make` from the build directory. -On successfull build you will find compiled test and example binaries in `bin` +On successful build you will find compiled test and example binaries in `bin` directory. The generated documentation will be available in `doc/html` directory of the build tree. To run tests after finished build please run `make test` or `ctest` from your build tree. You can get detailed output using `ctest @@ -136,25 +139,72 @@ The following diagram shows the process. ![simpledom](doc/diagram/simpledom.png) -More [examples](https://github.com/miloyip/rapidjson/tree/master/example) are available: +More [examples](https://github.com/Tencent/rapidjson/tree/master/example) are available: * DOM API - * [tutorial](https://github.com/miloyip/rapidjson/blob/master/example/tutorial/tutorial.cpp): Basic usage of DOM API. + * [tutorial](https://github.com/Tencent/rapidjson/blob/master/example/tutorial/tutorial.cpp): Basic usage of DOM API. * SAX API - * [simplereader](https://github.com/miloyip/rapidjson/blob/master/example/simplereader/simplereader.cpp): Dumps all SAX events while parsing a JSON by `Reader`. - * [condense](https://github.com/miloyip/rapidjson/blob/master/example/condense/condense.cpp): A command line tool to rewrite a JSON, with all whitespaces removed. - * [pretty](https://github.com/miloyip/rapidjson/blob/master/example/pretty/pretty.cpp): A command line tool to rewrite a JSON with indents and newlines by `PrettyWriter`. - * [capitalize](https://github.com/miloyip/rapidjson/blob/master/example/capitalize/capitalize.cpp): A command line tool to capitalize strings in JSON. - * [messagereader](https://github.com/miloyip/rapidjson/blob/master/example/messagereader/messagereader.cpp): Parse a JSON message with SAX API. - * [serialize](https://github.com/miloyip/rapidjson/blob/master/example/serialize/serialize.cpp): Serialize a C++ object into JSON with SAX API. - * [jsonx](https://github.com/miloyip/rapidjson/blob/master/example/jsonx/jsonx.cpp): Implements a `JsonxWriter` which stringify SAX events into [JSONx](https://www-01.ibm.com/support/knowledgecenter/SS9H2Y_7.1.0/com.ibm.dp.doc/json_jsonx.html) (a kind of XML) format. The example is a command line tool which converts input JSON into JSONx format. + * [simplereader](https://github.com/Tencent/rapidjson/blob/master/example/simplereader/simplereader.cpp): Dumps all SAX events while parsing a JSON by `Reader`. + * [condense](https://github.com/Tencent/rapidjson/blob/master/example/condense/condense.cpp): A command line tool to rewrite a JSON, with all whitespaces removed. + * [pretty](https://github.com/Tencent/rapidjson/blob/master/example/pretty/pretty.cpp): A command line tool to rewrite a JSON with indents and newlines by `PrettyWriter`. + * [capitalize](https://github.com/Tencent/rapidjson/blob/master/example/capitalize/capitalize.cpp): A command line tool to capitalize strings in JSON. + * [messagereader](https://github.com/Tencent/rapidjson/blob/master/example/messagereader/messagereader.cpp): Parse a JSON message with SAX API. + * [serialize](https://github.com/Tencent/rapidjson/blob/master/example/serialize/serialize.cpp): Serialize a C++ object into JSON with SAX API. + * [jsonx](https://github.com/Tencent/rapidjson/blob/master/example/jsonx/jsonx.cpp): Implements a `JsonxWriter` which stringify SAX events into [JSONx](https://www-01.ibm.com/support/knowledgecenter/SS9H2Y_7.1.0/com.ibm.dp.doc/json_jsonx.html) (a kind of XML) format. The example is a command line tool which converts input JSON into JSONx format. * Schema - * [schemavalidator](https://github.com/miloyip/rapidjson/blob/master/example/schemavalidator/schemavalidator.cpp) : A command line tool to validate a JSON with a JSON schema. - + * [schemavalidator](https://github.com/Tencent/rapidjson/blob/master/example/schemavalidator/schemavalidator.cpp) : A command line tool to validate a JSON with a JSON schema. + * Advanced - * [prettyauto](https://github.com/miloyip/rapidjson/blob/master/example/prettyauto/prettyauto.cpp): A modified version of [pretty](https://github.com/miloyip/rapidjson/blob/master/example/pretty/pretty.cpp) to automatically handle JSON with any UTF encodings. - * [parsebyparts](https://github.com/miloyip/rapidjson/blob/master/example/parsebyparts/parsebyparts.cpp): Implements an `AsyncDocumentParser` which can parse JSON in parts, using C++11 thread. - * [filterkey](https://github.com/miloyip/rapidjson/blob/master/example/filterkey/filterkey.cpp): A command line tool to remove all values with user-specified key. - * [filterkeydom](https://github.com/miloyip/rapidjson/blob/master/example/filterkeydom/filterkeydom.cpp): Same tool as above, but it demonstrates how to use a generator to populate a `Document`. + * [prettyauto](https://github.com/Tencent/rapidjson/blob/master/example/prettyauto/prettyauto.cpp): A modified version of [pretty](https://github.com/Tencent/rapidjson/blob/master/example/pretty/pretty.cpp) to automatically handle JSON with any UTF encodings. + * [parsebyparts](https://github.com/Tencent/rapidjson/blob/master/example/parsebyparts/parsebyparts.cpp): Implements an `AsyncDocumentParser` which can parse JSON in parts, using C++11 thread. + * [filterkey](https://github.com/Tencent/rapidjson/blob/master/example/filterkey/filterkey.cpp): A command line tool to remove all values with user-specified key. + * [filterkeydom](https://github.com/Tencent/rapidjson/blob/master/example/filterkeydom/filterkeydom.cpp): Same tool as above, but it demonstrates how to use a generator to populate a `Document`. + +## Contributing + +RapidJSON welcomes contributions. When contributing, please follow the code below. + +### Issues + +Feel free to submit issues and enhancement requests. + +Please help us by providing **minimal reproducible examples**, because source code is easier to let other people understand what happens. +For crash problems on certain platforms, please bring stack dump content with the detail of the OS, compiler, etc. + +Please try breakpoint debugging first, tell us what you found, see if we can start exploring based on more information been prepared. + +### Workflow + +In general, we follow the "fork-and-pull" Git workflow. + + 1. **Fork** the repo on GitHub + 2. **Clone** the project to your own machine + 3. **Checkout** a new branch on your fork, start developing on the branch + 4. **Test** the change before commit, Make sure the changes pass all the tests, including `unittest` and `preftest`, please add test case for each new feature or bug-fix if needed. + 5. **Commit** changes to your own branch + 6. **Push** your work back up to your fork + 7. Submit a **Pull request** so that we can review your changes + +NOTE: Be sure to merge the latest from "upstream" before making a pull request! + +### Copyright and Licensing + +You can copy and paste the license summary from below. + +``` +Tencent is pleased to support the open source community by making RapidJSON available. + +Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. + +Licensed under the MIT License (the "License"); you may not use this file except +in compliance with the License. You may obtain a copy of the License at + +http://opensource.org/licenses/MIT + +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. +``` diff --git a/third_party/rapidjson/readme.zh-cn.md b/third_party/rapidjson/readme.zh-cn.md index 74d267c98..216802e1b 100644 --- a/third_party/rapidjson/readme.zh-cn.md +++ b/third_party/rapidjson/readme.zh-cn.md @@ -1,18 +1,18 @@ -![](doc/logo/rapidjson.png) +![RapidJSON logo](doc/logo/rapidjson.png) -![](https://img.shields.io/badge/release-v1.1.0-blue.png) +![Release version](https://img.shields.io/badge/release-v1.1.0-blue.svg) ## 高效的 C++ JSON 解析/生成器,提供 SAX 及 DOM 风格 API Tencent is pleased to support the open source community by making RapidJSON available. -Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -* [RapidJSON GitHub](https://github.com/miloyip/rapidjson/) +* [RapidJSON GitHub](https://github.com/Tencent/rapidjson/) * RapidJSON 文档 * [English](http://rapidjson.org/) * [简体中文](http://rapidjson.org/zh-cn/) - * [GitBook](https://www.gitbook.com/book/miloyip/rapidjson/) 可下载 PDF/EPUB/MOBI,但不含 API 参考手册。 + * [GitBook](https://www.gitbook.com/book/miloyip/rapidjson/details/zh-cn) 可下载 PDF/EPUB/MOBI,但不含 API 参考手册。 ## Build 状态 @@ -20,12 +20,12 @@ Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights | :---------------: | :-----------------: | :-------------------: | | ![lin-badge] | ![win-badge] | ![cov-badge] | -[lin-badge]: https://travis-ci.org/miloyip/rapidjson.png?branch=master "Travis build status" -[lin-link]: https://travis-ci.org/miloyip/rapidjson "Travis build status" -[win-badge]: https://ci.appveyor.com/api/projects/status/u658dcuwxo14a8m9/branch/master "AppVeyor build status" -[win-link]: https://ci.appveyor.com/project/miloyip/rapidjson/branch/master "AppVeyor build status" -[cov-badge]: https://coveralls.io/repos/miloyip/rapidjson/badge.png?branch=master -[cov-link]: https://coveralls.io/r/miloyip/rapidjson?branch=master +[lin-badge]: https://travis-ci.org/Tencent/rapidjson.svg?branch=master "Travis build status" +[lin-link]: https://travis-ci.org/Tencent/rapidjson "Travis build status" +[win-badge]: https://ci.appveyor.com/api/projects/status/l6qulgqahcayidrf/branch/master?svg=true "AppVeyor build status" +[win-link]: https://ci.appveyor.com/project/miloyip/rapidjson-0fdqj/branch/master "AppVeyor build status" +[cov-badge]: https://coveralls.io/repos/Tencent/rapidjson/badge.svg?branch=master "Coveralls coverage" +[cov-link]: https://coveralls.io/r/Tencent/rapidjson?branch=master "Coveralls coverage" ## 简介 @@ -45,8 +45,8 @@ RapidJSON 是一个 C++ 的 JSON 解析器及生成器。它的灵感来自 [Rap JSON(JavaScript Object Notation)是一个轻量的数据交换格式。RapidJSON 应该完全遵从 RFC7159/ECMA-404,并支持可选的放宽语法。 关于 JSON 的更多信息可参考: * [Introducing JSON](http://json.org/) -* [RFC7159: The JavaScript Object Notation (JSON) Data Interchange Format](http://www.ietf.org/rfc/rfc7159.txt) -* [Standard ECMA-404: The JSON Data Interchange Format](http://www.ecma-international.org/publications/standards/Ecma-404.htm) +* [RFC7159: The JavaScript Object Notation (JSON) Data Interchange Format](https://tools.ietf.org/html/rfc7159) +* [Standard ECMA-404: The JSON Data Interchange Format](https://www.ecma-international.org/publications/standards/Ecma-404.htm) ## v1.1 中的亮点 (2016-8-25) @@ -73,20 +73,20 @@ RapidJSON 是跨平台的。以下是一些曾测试的平台/编译器组合 RapidJSON 是只有头文件的 C++ 库。只需把 `include/rapidjson` 目录复制至系统或项目的 include 目录中。 RapidJSON 依赖于以下软件: -* [CMake](http://www.cmake.org) 作为通用生成工具 -* (optional)[Doxygen](http://www.doxygen.org) 用于生成文档 -* (optional)[googletest](https://code.google.com/p/googletest/) 用于单元及性能测试 +* [CMake](https://cmake.org/) 作为通用生成工具 +* (optional) [Doxygen](http://www.doxygen.org) 用于生成文档 +* (optional) [googletest](https://github.com/google/googletest) 用于单元及性能测试 生成测试及例子的步骤: 1. 执行 `git submodule update --init` 去获取 thirdparty submodules (google test)。 -2. 在 rapidjson 目渌下,建立一个 `build` 目录。 +2. 在 rapidjson 目录下,建立一个 `build` 目录。 3. 在 `build` 目录下执行 `cmake ..` 命令以设置生成。Windows 用户可使用 cmake-gui 应用程序。 4. 在 Windows 下,编译生成在 build 目录中的 solution。在 Linux 下,于 build 目录运行 `make`。 成功生成后,你会在 `bin` 的目录下找到编译后的测试及例子可执行文件。而生成的文档将位于 build 下的 `doc/html` 目录。要执行测试,请在 build 下执行 `make test` 或 `ctest`。使用 `ctest -V` 命令可获取详细的输出。 -我们也可以把程序库安装至全系统中,只要在具管理權限下从 build 目录执行 `make install` 命令。这样会按系统的偏好设置安装所有文件。当安装 RapidJSON 后,其他的 CMake 项目需要使用它时,可以通过在 `CMakeLists.txt` 加入一句 `find_package(RapidJSON)`。 +我们也可以把程序库安装至全系统中,只要在具管理权限下从 build 目录执行 `make install` 命令。这样会按系统的偏好设置安装所有文件。当安装 RapidJSON 后,其他的 CMake 项目需要使用它时,可以通过在 `CMakeLists.txt` 加入一句 `find_package(RapidJSON)`。 ## 用法一览 @@ -128,25 +128,25 @@ int main() { ![simpledom](doc/diagram/simpledom.png) -还有许多 [例子](https://github.com/miloyip/rapidjson/tree/master/example) 可供参考: +还有许多 [例子](https://github.com/Tencent/rapidjson/tree/master/example) 可供参考: * DOM API - * [tutorial](https://github.com/miloyip/rapidjson/blob/master/example/tutorial/tutorial.cpp): DOM API 的基本使用方法。 + * [tutorial](https://github.com/Tencent/rapidjson/blob/master/example/tutorial/tutorial.cpp): DOM API 的基本使用方法。 * SAX API - * [simplereader](https://github.com/miloyip/rapidjson/blob/master/example/simplereader/simplereader.cpp): 使用 `Reader` 解析 JSON 时,打印所有 SAX 事件。 - * [condense](https://github.com/miloyip/rapidjson/blob/master/example/condense/condense.cpp): 移除 JSON 中所有空白符的命令行工具。 - * [pretty](https://github.com/miloyip/rapidjson/blob/master/example/pretty/pretty.cpp): 为 JSON 加入缩进与换行的命令行工具,当中使用了 `PrettyWriter`。 - * [capitalize](https://github.com/miloyip/rapidjson/blob/master/example/capitalize/capitalize.cpp): 把 JSON 中所有字符串改为大写的命令行工具。 - * [messagereader](https://github.com/miloyip/rapidjson/blob/master/example/messagereader/messagereader.cpp): 使用 SAX API 去解析一个 JSON 报文。 - * [serialize](https://github.com/miloyip/rapidjson/blob/master/example/serialize/serialize.cpp): 使用 SAX API 去序列化 C++ 对象,生成 JSON。 - * [jsonx](https://github.com/miloyip/rapidjson/blob/master/example/jsonx/jsonx.cpp): 实现了一个 `JsonxWriter`,它能把 SAX 事件写成 [JSONx](https://www-01.ibm.com/support/knowledgecenter/SS9H2Y_7.1.0/com.ibm.dp.doc/json_jsonx.html)(一种 XML)格式。这个例子是把 JSON 输入转换成 JSONx 格式的命令行工具。 + * [simplereader](https://github.com/Tencent/rapidjson/blob/master/example/simplereader/simplereader.cpp): 使用 `Reader` 解析 JSON 时,打印所有 SAX 事件。 + * [condense](https://github.com/Tencent/rapidjson/blob/master/example/condense/condense.cpp): 移除 JSON 中所有空白符的命令行工具。 + * [pretty](https://github.com/Tencent/rapidjson/blob/master/example/pretty/pretty.cpp): 为 JSON 加入缩进与换行的命令行工具,当中使用了 `PrettyWriter`。 + * [capitalize](https://github.com/Tencent/rapidjson/blob/master/example/capitalize/capitalize.cpp): 把 JSON 中所有字符串改为大写的命令行工具。 + * [messagereader](https://github.com/Tencent/rapidjson/blob/master/example/messagereader/messagereader.cpp): 使用 SAX API 去解析一个 JSON 报文。 + * [serialize](https://github.com/Tencent/rapidjson/blob/master/example/serialize/serialize.cpp): 使用 SAX API 去序列化 C++ 对象,生成 JSON。 + * [jsonx](https://github.com/Tencent/rapidjson/blob/master/example/jsonx/jsonx.cpp): 实现了一个 `JsonxWriter`,它能把 SAX 事件写成 [JSONx](https://www-01.ibm.com/support/knowledgecenter/SS9H2Y_7.1.0/com.ibm.dp.doc/json_jsonx.html)(一种 XML)格式。这个例子是把 JSON 输入转换成 JSONx 格式的命令行工具。 * Schema API - * [schemavalidator](https://github.com/miloyip/rapidjson/blob/master/example/schemavalidator/schemavalidator.cpp): 使用 JSON Schema 去校验 JSON 的命令行工具。 - + * [schemavalidator](https://github.com/Tencent/rapidjson/blob/master/example/schemavalidator/schemavalidator.cpp): 使用 JSON Schema 去校验 JSON 的命令行工具。 + * 进阶 - * [prettyauto](https://github.com/miloyip/rapidjson/blob/master/example/prettyauto/prettyauto.cpp): [pretty](https://github.com/miloyip/rapidjson/blob/master/example/pretty/pretty.cpp) 的修改版本,可自动处理任何 UTF 编码的 JSON。 - * [parsebyparts](https://github.com/miloyip/rapidjson/blob/master/example/parsebyparts/parsebyparts.cpp): 这例子中的 `AsyncDocumentParser` 类使用 C++ 线程来逐段解析 JSON。 - * [filterkey](https://github.com/miloyip/rapidjson/blob/master/example/filterkey/filterkey.cpp): 移取使用者指定的键值的命令行工具。 - * [filterkeydom](https://github.com/miloyip/rapidjson/blob/master/example/filterkey/filterkey.cpp): 如上的工具,但展示如何使用生成器(generator)去填充一个 `Document`。 \ No newline at end of file + * [prettyauto](https://github.com/Tencent/rapidjson/blob/master/example/prettyauto/prettyauto.cpp): [pretty](https://github.com/Tencent/rapidjson/blob/master/example/pretty/pretty.cpp) 的修改版本,可自动处理任何 UTF 编码的 JSON。 + * [parsebyparts](https://github.com/Tencent/rapidjson/blob/master/example/parsebyparts/parsebyparts.cpp): 这例子中的 `AsyncDocumentParser` 类使用 C++ 线程来逐段解析 JSON。 + * [filterkey](https://github.com/Tencent/rapidjson/blob/master/example/filterkey/filterkey.cpp): 移取使用者指定的键值的命令行工具。 + * [filterkeydom](https://github.com/Tencent/rapidjson/blob/master/example/filterkey/filterkey.cpp): 如上的工具,但展示如何使用生成器(generator)去填充一个 `Document`。 diff --git a/third_party/rapidjson/test/perftest/CMakeLists.txt b/third_party/rapidjson/test/perftest/CMakeLists.txt index c33aae469..035e544d9 100644 --- a/third_party/rapidjson/test/perftest/CMakeLists.txt +++ b/third_party/rapidjson/test/perftest/CMakeLists.txt @@ -19,6 +19,8 @@ if(CCACHE_FOUND) endif() endif(CCACHE_FOUND) +set_property(DIRECTORY PROPERTY COMPILE_OPTIONS ${EXTRA_CXX_FLAGS}) + IF(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug")) add_test(NAME perftest COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/perftest diff --git a/third_party/rapidjson/test/perftest/misctest.cpp b/third_party/rapidjson/test/perftest/misctest.cpp index aac847784..f43b05018 100644 --- a/third_party/rapidjson/test/perftest/misctest.cpp +++ b/third_party/rapidjson/test/perftest/misctest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -432,7 +432,7 @@ bool Writer1::WriteUint(unsigned u) { return true; } -// Using digits LUT to reduce divsion/modulo +// Using digits LUT to reduce division/modulo template class Writer2 { public: @@ -616,7 +616,7 @@ inline bool Writer3::WriteUint64(uint64_t u) { return true; } -// Using digits LUT to reduce divsion/modulo, two passes +// Using digits LUT to reduce division/modulo, two passes template class Writer4 { public: diff --git a/third_party/rapidjson/test/perftest/perftest.cpp b/third_party/rapidjson/test/perftest/perftest.cpp index 4e79f1f51..b149a4c12 100644 --- a/third_party/rapidjson/test/perftest/perftest.cpp +++ b/third_party/rapidjson/test/perftest/perftest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/third_party/rapidjson/test/perftest/perftest.h b/third_party/rapidjson/test/perftest/perftest.h index b098e4147..31e3ca633 100644 --- a/third_party/rapidjson/test/perftest/perftest.h +++ b/third_party/rapidjson/test/perftest/perftest.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -24,10 +24,13 @@ // __SSE2__ and __SSE4_2__ are recognized by gcc, clang, and the Intel compiler. // We use -march=native with gmake to enable -msse2 and -msse4.2, if supported. +// Likewise, __ARM_NEON is used to detect Neon. #if defined(__SSE4_2__) # define RAPIDJSON_SSE42 #elif defined(__SSE2__) # define RAPIDJSON_SSE2 +#elif defined(__ARM_NEON) +# define RAPIDJSON_NEON #endif #define RAPIDJSON_HAS_STDSTRING 1 @@ -127,7 +130,8 @@ public: "integers.json", "mixed.json", "nulls.json", - "paragraphs.json" + "paragraphs.json", + "alotofkeys.json" }; for (size_t j = 0; j < sizeof(typesfilenames) / sizeof(typesfilenames[0]); j++) { @@ -155,7 +159,7 @@ public: free(whitespace_); json_ = 0; whitespace_ = 0; - for (size_t i = 0; i < 7; i++) { + for (size_t i = 0; i < 8; i++) { free(types_[i]); types_[i] = 0; } @@ -171,8 +175,8 @@ protected: size_t length_; char *whitespace_; size_t whitespace_length_; - char *types_[7]; - size_t typesLength_[7]; + char *types_[8]; + size_t typesLength_[8]; static const size_t kTrialCount = 1000; }; diff --git a/third_party/rapidjson/test/perftest/platformtest.cpp b/third_party/rapidjson/test/perftest/platformtest.cpp index bb905ca73..c490da7a8 100644 --- a/third_party/rapidjson/test/perftest/platformtest.cpp +++ b/third_party/rapidjson/test/perftest/platformtest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -76,7 +76,7 @@ TEST_F(Platform, strlen) { TEST_F(Platform, memcmp) { for (int i = 0; i < kTrialCount; i++) { - EXPECT_EQ(0, memcmp(temp_, json_, length_)); + EXPECT_EQ(0u, memcmp(temp_, json_, length_)); } } diff --git a/third_party/rapidjson/test/perftest/rapidjsontest.cpp b/third_party/rapidjson/test/perftest/rapidjsontest.cpp index 675db3182..ce41c109a 100644 --- a/third_party/rapidjson/test/perftest/rapidjsontest.cpp +++ b/third_party/rapidjson/test/perftest/rapidjsontest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -21,13 +21,19 @@ #include "rapidjson/prettywriter.h" #include "rapidjson/stringbuffer.h" #include "rapidjson/filereadstream.h" +#include "rapidjson/istreamwrapper.h" #include "rapidjson/encodedstream.h" #include "rapidjson/memorystream.h" +#include +#include + #ifdef RAPIDJSON_SSE2 #define SIMD_SUFFIX(name) name##_SSE2 #elif defined(RAPIDJSON_SSE42) #define SIMD_SUFFIX(name) name##_SSE42 +#elif defined(RAPIDJSON_NEON) +#define SIMD_SUFFIX(name) name##_NEON #else #define SIMD_SUFFIX(name) name #endif @@ -47,7 +53,7 @@ public: // Parse as a document EXPECT_FALSE(doc_.Parse(json_).HasParseError()); - for (size_t i = 0; i < 7; i++) + for (size_t i = 0; i < 8; i++) EXPECT_FALSE(typesDoc_[i].Parse(types_[i]).HasParseError()); } @@ -63,7 +69,7 @@ private: protected: char *temp_; Document doc_; - Document typesDoc_[7]; + Document typesDoc_[8]; }; TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseInsitu_DummyHandler)) { @@ -152,6 +158,35 @@ TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseIterativeInsitu_DummyHandler)) { } } +TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseIterativePull_DummyHandler)) { + for (size_t i = 0; i < kTrialCount; i++) { + StringStream s(json_); + BaseReaderHandler<> h; + Reader reader; + reader.IterativeParseInit(); + while (!reader.IterativeParseComplete()) { + if (!reader.IterativeParseNext(s, h)) + break; + } + EXPECT_FALSE(reader.HasParseError()); + } +} + +TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseIterativePullInsitu_DummyHandler)) { + for (size_t i = 0; i < kTrialCount; i++) { + memcpy(temp_, json_, length_ + 1); + InsituStringStream s(temp_); + BaseReaderHandler<> h; + Reader reader; + reader.IterativeParseInit(); + while (!reader.IterativeParseComplete()) { + if (!reader.IterativeParseNext(s, h)) + break; + } + EXPECT_FALSE(reader.HasParseError()); + } +} + TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler_ValidateEncoding)) { for (size_t i = 0; i < kTrialCount; i++) { StringStream s(json_); @@ -301,6 +336,23 @@ TEST_F(RapidJson, DocumentAccept) { } } +TEST_F(RapidJson, DocumentFind) { + typedef Document::ValueType ValueType; + typedef ValueType::ConstMemberIterator ConstMemberIterator; + const Document &doc = typesDoc_[7]; // alotofkeys.json + if (doc.IsObject()) { + std::vector keys; + for (ConstMemberIterator it = doc.MemberBegin(); it != doc.MemberEnd(); ++it) { + keys.push_back(&it->name); + } + for (size_t i = 0; i < kTrialCount; i++) { + for (size_t j = 0; j < keys.size(); j++) { + EXPECT_TRUE(doc.FindMember(*keys[j]) != doc.MemberEnd()); + } + } + } +} + struct NullStream { typedef char Ch; @@ -432,6 +484,77 @@ TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler_FileReadStream)) { } } +TEST_F(RapidJson, IStreamWrapper) { + for (size_t i = 0; i < kTrialCount; i++) { + std::ifstream is(filename_, std::ios::in | std::ios::binary); + char buffer[65536]; + IStreamWrapper isw(is, buffer, sizeof(buffer)); + while (isw.Take() != '\0') + ; + is.close(); + } +} + +TEST_F(RapidJson, IStreamWrapper_Unbuffered) { + for (size_t i = 0; i < kTrialCount; i++) { + std::ifstream is(filename_, std::ios::in | std::ios::binary); + IStreamWrapper isw(is); + while (isw.Take() != '\0') + ; + is.close(); + } +} + +TEST_F(RapidJson, IStreamWrapper_Setbuffered) { + for (size_t i = 0; i < kTrialCount; i++) { + std::ifstream is; + char buffer[65536]; + is.rdbuf()->pubsetbuf(buffer, sizeof(buffer)); + is.open(filename_, std::ios::in | std::ios::binary); + IStreamWrapper isw(is); + while (isw.Take() != '\0') + ; + is.close(); + } +} + +TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler_IStreamWrapper)) { + for (size_t i = 0; i < kTrialCount; i++) { + std::ifstream is(filename_, std::ios::in | std::ios::binary); + char buffer[65536]; + IStreamWrapper isw(is, buffer, sizeof(buffer)); + BaseReaderHandler<> h; + Reader reader; + reader.Parse(isw, h); + is.close(); + } +} + +TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler_IStreamWrapper_Unbuffered)) { + for (size_t i = 0; i < kTrialCount; i++) { + std::ifstream is(filename_, std::ios::in | std::ios::binary); + IStreamWrapper isw(is); + BaseReaderHandler<> h; + Reader reader; + reader.Parse(isw, h); + is.close(); + } +} + +TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler_IStreamWrapper_Setbuffered)) { + for (size_t i = 0; i < kTrialCount; i++) { + std::ifstream is; + char buffer[65536]; + is.rdbuf()->pubsetbuf(buffer, sizeof(buffer)); + is.open(filename_, std::ios::in | std::ios::binary); + IStreamWrapper isw(is); + BaseReaderHandler<> h; + Reader reader; + reader.Parse(isw, h); + is.close(); + } +} + TEST_F(RapidJson, StringBuffer) { StringBuffer sb; for (int i = 0; i < 32 * 1024 * 1024; i++) diff --git a/third_party/rapidjson/test/perftest/schematest.cpp b/third_party/rapidjson/test/perftest/schematest.cpp index 468f5fe6f..7d27344b5 100644 --- a/third_party/rapidjson/test/perftest/schematest.cpp +++ b/third_party/rapidjson/test/perftest/schematest.cpp @@ -11,6 +11,11 @@ using namespace rapidjson; +RAPIDJSON_DIAG_PUSH +#if defined(__GNUC__) && __GNUC__ >= 7 +RAPIDJSON_DIAG_OFF(format-overflow) +#endif + template static char* ReadFile(const char* filename, Allocator& allocator) { const char *paths[] = { @@ -42,6 +47,8 @@ static char* ReadFile(const char* filename, Allocator& allocator) { return json; } +RAPIDJSON_DIAG_POP + class Schema : public PerfTest { public: Schema() {} diff --git a/third_party/rapidjson/test/unittest/CMakeLists.txt b/third_party/rapidjson/test/unittest/CMakeLists.txt index b3204d6c8..565ed9823 100644 --- a/third_party/rapidjson/test/unittest/CMakeLists.txt +++ b/third_party/rapidjson/test/unittest/CMakeLists.txt @@ -3,6 +3,8 @@ include(CheckCXXCompilerFlag) set(UNITTEST_SOURCES allocatorstest.cpp bigintegertest.cpp + clzlltest.cpp + cursorstreamwrappertest.cpp documenttest.cpp dtoatest.cpp encodedstreamtest.cpp @@ -14,6 +16,7 @@ set(UNITTEST_SOURCES jsoncheckertest.cpp namespacetest.cpp pointertest.cpp + platformtest.cpp prettywritertest.cpp ostreamwrappertest.cpp readertest.cpp @@ -24,6 +27,7 @@ set(UNITTEST_SOURCES stringbuffertest.cpp strtodtest.cpp unittest.cpp + uritest.cpp valuetest.cpp writertest.cpp) @@ -36,10 +40,9 @@ if(CCACHE_FOUND) endif() endif(CCACHE_FOUND) -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Weffc++ -Wswitch-default -Wfloat-equal") -elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Weffc++ -Wswitch-default -Wfloat-equal -Wimplicit-fallthrough -Weverything") +set_property(DIRECTORY PROPERTY COMPILE_OPTIONS ${EXTRA_CXX_FLAGS}) + +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") # If the user is running a newer version of Clang that includes the # -Wdouble-promotion, we will ignore that warning. if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.7) @@ -80,7 +83,7 @@ add_test(NAME unittest if(NOT MSVC) # Not running SIMD.* unit test cases for Valgrind add_test(NAME valgrind_unittest - COMMAND valgrind --leak-check=full --error-exitcode=1 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest --gtest_filter=-SIMD.* + COMMAND valgrind --suppressions=${CMAKE_SOURCE_DIR}/test/valgrind.supp --leak-check=full --error-exitcode=1 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest --gtest_filter=-SIMD.* WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) if(CMAKE_BUILD_TYPE STREQUAL "Debug") diff --git a/third_party/rapidjson/test/unittest/allocatorstest.cpp b/third_party/rapidjson/test/unittest/allocatorstest.cpp index a5958de19..9a65b1690 100644 --- a/third_party/rapidjson/test/unittest/allocatorstest.cpp +++ b/third_party/rapidjson/test/unittest/allocatorstest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -16,6 +16,11 @@ #include "rapidjson/allocators.h" +#include +#include +#include +#include + using namespace rapidjson; template @@ -47,39 +52,224 @@ void TestAllocator(Allocator& a) { EXPECT_TRUE(a.Realloc(a.Malloc(1), 1, 0) == 0); } +struct TestStdAllocatorData { + TestStdAllocatorData(int &constructions, int &destructions) : + constructions_(&constructions), + destructions_(&destructions) + { + ++*constructions_; + } + TestStdAllocatorData(const TestStdAllocatorData& rhs) : + constructions_(rhs.constructions_), + destructions_(rhs.destructions_) + { + ++*constructions_; + } + TestStdAllocatorData& operator=(const TestStdAllocatorData& rhs) + { + this->~TestStdAllocatorData(); + constructions_ = rhs.constructions_; + destructions_ = rhs.destructions_; + ++*constructions_; + return *this; + } + ~TestStdAllocatorData() + { + ++*destructions_; + } +private: + TestStdAllocatorData(); + int *constructions_, + *destructions_; +}; + +template +void TestStdAllocator(const Allocator& a) { +#if RAPIDJSON_HAS_CXX17 + typedef StdAllocator BoolAllocator; +#else + typedef StdAllocator VoidAllocator; + typedef typename VoidAllocator::template rebind::other BoolAllocator; +#endif + BoolAllocator ba(a), ba2(a); + EXPECT_TRUE(ba == ba2); + EXPECT_FALSE(ba!= ba2); + ba.deallocate(ba.allocate()); + EXPECT_TRUE(ba == ba2); + EXPECT_FALSE(ba != ba2); + + unsigned long long ll = 0, *llp = ≪ + const unsigned long long cll = 0, *cllp = &cll; + StdAllocator lla(a); + EXPECT_EQ(lla.address(ll), llp); + EXPECT_EQ(lla.address(cll), cllp); + EXPECT_TRUE(lla.max_size() > 0 && lla.max_size() <= SIZE_MAX / sizeof(unsigned long long)); + + int *arr; + StdAllocator ia(a); + arr = ia.allocate(10 * sizeof(int)); + EXPECT_TRUE(arr != 0); + for (int i = 0; i < 10; ++i) { + arr[i] = 0x0f0f0f0f; + } + ia.deallocate(arr, 10); + arr = Malloc(ia, 10); + EXPECT_TRUE(arr != 0); + for (int i = 0; i < 10; ++i) { + arr[i] = 0x0f0f0f0f; + } + arr = Realloc(ia, arr, 10, 20); + EXPECT_TRUE(arr != 0); + for (int i = 0; i < 10; ++i) { + EXPECT_EQ(arr[i], 0x0f0f0f0f); + } + for (int i = 10; i < 20; i++) { + arr[i] = 0x0f0f0f0f; + } + Free(ia, arr, 20); + + int cons = 0, dest = 0; + StdAllocator da(a); + for (int i = 1; i < 10; i++) { + TestStdAllocatorData *d = da.allocate(); + EXPECT_TRUE(d != 0); + + da.destroy(new(d) TestStdAllocatorData(cons, dest)); + EXPECT_EQ(cons, i); + EXPECT_EQ(dest, i); + + da.deallocate(d); + } + + typedef StdAllocator CharAllocator; + typedef std::basic_string, CharAllocator> String; +#if RAPIDJSON_HAS_CXX11 + String s(CharAllocator{a}); +#else + CharAllocator ca(a); + String s(ca); +#endif + for (int i = 0; i < 26; i++) { + s.push_back(static_cast('A' + i)); + } + EXPECT_TRUE(s == "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + + typedef StdAllocator, Allocator> MapAllocator; + typedef std::map, MapAllocator> Map; +#if RAPIDJSON_HAS_CXX11 + Map map(std::less(), MapAllocator{a}); +#else + MapAllocator ma(a); + Map map(std::less(), ma); +#endif + for (int i = 0; i < 10; i++) { + map.insert(std::make_pair(i, (i % 2) == 0)); + } + EXPECT_TRUE(map.size() == 10); + for (int i = 0; i < 10; i++) { + typename Map::iterator it = map.find(i); + EXPECT_TRUE(it != map.end()); + EXPECT_TRUE(it->second == ((i % 2) == 0)); + } +} + TEST(Allocator, CrtAllocator) { CrtAllocator a; + TestAllocator(a); + TestStdAllocator(a); + + CrtAllocator a2; + EXPECT_TRUE(a == a2); + EXPECT_FALSE(a != a2); + a2.Free(a2.Malloc(1)); + EXPECT_TRUE(a == a2); + EXPECT_FALSE(a != a2); } TEST(Allocator, MemoryPoolAllocator) { - MemoryPoolAllocator<> a; + const size_t capacity = RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY; + MemoryPoolAllocator<> a(capacity); + + a.Clear(); // noop + EXPECT_EQ(a.Size(), 0u); + EXPECT_EQ(a.Capacity(), 0u); + EXPECT_EQ(a.Shared(), false); + { + MemoryPoolAllocator<> a2(a); + EXPECT_EQ(a2.Shared(), true); + EXPECT_EQ(a.Shared(), true); + EXPECT_TRUE(a == a2); + EXPECT_FALSE(a != a2); + a2.Free(a2.Malloc(1)); + EXPECT_TRUE(a == a2); + EXPECT_FALSE(a != a2); + } + EXPECT_EQ(a.Shared(), false); + EXPECT_EQ(a.Capacity(), capacity); + EXPECT_EQ(a.Size(), 8u); // aligned + a.Clear(); + EXPECT_EQ(a.Capacity(), 0u); + EXPECT_EQ(a.Size(), 0u); + TestAllocator(a); + TestStdAllocator(a); for (size_t i = 1; i < 1000; i++) { EXPECT_TRUE(a.Malloc(i) != 0); EXPECT_LE(a.Size(), a.Capacity()); } + + CrtAllocator baseAllocator; + a = MemoryPoolAllocator<>(capacity, &baseAllocator); + EXPECT_EQ(a.Capacity(), 0u); + EXPECT_EQ(a.Size(), 0u); + a.Free(a.Malloc(1)); + EXPECT_EQ(a.Capacity(), capacity); + EXPECT_EQ(a.Size(), 8u); // aligned + + { + a.Clear(); + const size_t bufSize = 1024; + char *buffer = static_cast(a.Malloc(bufSize)); + MemoryPoolAllocator<> aligned_a(buffer, bufSize); + EXPECT_TRUE(aligned_a.Capacity() > 0 && aligned_a.Capacity() <= bufSize); + EXPECT_EQ(aligned_a.Size(), 0u); + aligned_a.Free(aligned_a.Malloc(1)); + EXPECT_TRUE(aligned_a.Capacity() > 0 && aligned_a.Capacity() <= bufSize); + EXPECT_EQ(aligned_a.Size(), 8u); // aligned + } + + { + a.Clear(); + const size_t bufSize = 1024; + char *buffer = static_cast(a.Malloc(bufSize)); + RAPIDJSON_ASSERT(bufSize % sizeof(void*) == 0); + MemoryPoolAllocator<> unaligned_a(buffer + 1, bufSize - 1); + EXPECT_TRUE(unaligned_a.Capacity() > 0 && unaligned_a.Capacity() <= bufSize - sizeof(void*)); + EXPECT_EQ(unaligned_a.Size(), 0u); + unaligned_a.Free(unaligned_a.Malloc(1)); + EXPECT_TRUE(unaligned_a.Capacity() > 0 && unaligned_a.Capacity() <= bufSize - sizeof(void*)); + EXPECT_EQ(unaligned_a.Size(), 8u); // aligned + } } TEST(Allocator, Alignment) { -#if RAPIDJSON_64BIT == 1 - EXPECT_EQ(RAPIDJSON_UINT64_C2(0x00000000, 0x00000000), RAPIDJSON_ALIGN(0)); - for (uint64_t i = 1; i < 8; i++) { - EXPECT_EQ(RAPIDJSON_UINT64_C2(0x00000000, 0x00000008), RAPIDJSON_ALIGN(i)); - EXPECT_EQ(RAPIDJSON_UINT64_C2(0x00000000, 0x00000010), RAPIDJSON_ALIGN(RAPIDJSON_UINT64_C2(0x00000000, 0x00000008) + i)); - EXPECT_EQ(RAPIDJSON_UINT64_C2(0x00000001, 0x00000000), RAPIDJSON_ALIGN(RAPIDJSON_UINT64_C2(0x00000000, 0xFFFFFFF8) + i)); - EXPECT_EQ(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFF8), RAPIDJSON_ALIGN(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFF0) + i)); + if (sizeof(size_t) >= 8) { + EXPECT_EQ(RAPIDJSON_UINT64_C2(0x00000000, 0x00000000), RAPIDJSON_ALIGN(0)); + for (uint64_t i = 1; i < 8; i++) { + EXPECT_EQ(RAPIDJSON_UINT64_C2(0x00000000, 0x00000008), RAPIDJSON_ALIGN(i)); + EXPECT_EQ(RAPIDJSON_UINT64_C2(0x00000000, 0x00000010), RAPIDJSON_ALIGN(RAPIDJSON_UINT64_C2(0x00000000, 0x00000008) + i)); + EXPECT_EQ(RAPIDJSON_UINT64_C2(0x00000001, 0x00000000), RAPIDJSON_ALIGN(RAPIDJSON_UINT64_C2(0x00000000, 0xFFFFFFF8) + i)); + EXPECT_EQ(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFF8), RAPIDJSON_ALIGN(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFF0) + i)); + } } -#else + EXPECT_EQ(0u, RAPIDJSON_ALIGN(0u)); - for (uint32_t i = 1; i < 4; i++) { - EXPECT_EQ(4u, RAPIDJSON_ALIGN(i)); - EXPECT_EQ(8u, RAPIDJSON_ALIGN(4u + i)); - EXPECT_EQ(0xFFFFFFF8u, RAPIDJSON_ALIGN(0xFFFFFFF4u + i)); - EXPECT_EQ(0xFFFFFFFCu, RAPIDJSON_ALIGN(0xFFFFFFF8u + i)); + for (uint32_t i = 1; i < 8; i++) { + EXPECT_EQ(8u, RAPIDJSON_ALIGN(i)); + EXPECT_EQ(0xFFFFFFF8u, RAPIDJSON_ALIGN(0xFFFFFFF0u + i)); } -#endif } TEST(Allocator, Issue399) { diff --git a/third_party/rapidjson/test/unittest/bigintegertest.cpp b/third_party/rapidjson/test/unittest/bigintegertest.cpp index a68e14446..fad54382c 100644 --- a/third_party/rapidjson/test/unittest/bigintegertest.cpp +++ b/third_party/rapidjson/test/unittest/bigintegertest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -120,6 +120,11 @@ TEST(BigInteger, LeftShift) { EXPECT_TRUE(BIGINTEGER_LITERAL("4537899042132549697536") == a); a <<= 99; EXPECT_TRUE(BIGINTEGER_LITERAL("2876235222267216943024851750785644982682875244576768") == a); + + a = 1; + a <<= 64; // a.count_ != 1 + a <<= 256; // interShift == 0 + EXPECT_TRUE(BIGINTEGER_LITERAL("2135987035920910082395021706169552114602704522356652769947041607822219725780640550022962086936576") == a); } TEST(BigInteger, Compare) { diff --git a/third_party/rapidjson/test/unittest/clzlltest.cpp b/third_party/rapidjson/test/unittest/clzlltest.cpp new file mode 100644 index 000000000..ad465e1f3 --- /dev/null +++ b/third_party/rapidjson/test/unittest/clzlltest.cpp @@ -0,0 +1,34 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 "unittest.h" +#include "rapidjson/internal/clzll.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +#endif + +using namespace rapidjson::internal; + +TEST(clzll, normal) { + EXPECT_EQ(clzll(1), 63U); + EXPECT_EQ(clzll(2), 62U); + EXPECT_EQ(clzll(12), 60U); + EXPECT_EQ(clzll(0x0000000080000001UL), 32U); + EXPECT_EQ(clzll(0x8000000000000001UL), 0U); +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif diff --git a/third_party/rapidjson/test/unittest/cursorstreamwrappertest.cpp b/third_party/rapidjson/test/unittest/cursorstreamwrappertest.cpp new file mode 100644 index 000000000..49e3d5e54 --- /dev/null +++ b/third_party/rapidjson/test/unittest/cursorstreamwrappertest.cpp @@ -0,0 +1,115 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 "unittest.h" +#include "rapidjson/document.h" +#include "rapidjson/cursorstreamwrapper.h" + +using namespace rapidjson; + +// static const char json[] = "{\"string\"\n\n:\"my string\",\"array\"\n:[\"1\", \"2\", \"3\"]}"; + +static bool testJson(const char *json, size_t &line, size_t &col) { + StringStream ss(json); + CursorStreamWrapper csw(ss); + Document document; + document.ParseStream(csw); + bool ret = document.HasParseError(); + if (ret) { + col = csw.GetColumn(); + line = csw.GetLine(); + } + return ret; +} + +TEST(CursorStreamWrapper, MissingFirstBracket) { + const char json[] = "\"string\"\n\n:\"my string\",\"array\"\n:[\"1\", \"2\", \"3\"]}"; + size_t col, line; + bool ret = testJson(json, line, col); + EXPECT_TRUE(ret); + EXPECT_EQ(line, 3u); + EXPECT_EQ(col, 0u); +} + +TEST(CursorStreamWrapper, MissingQuotes) { + const char json[] = "{\"string\n\n:\"my string\",\"array\"\n:[\"1\", \"2\", \"3\"]}"; + size_t col, line; + bool ret = testJson(json, line, col); + EXPECT_TRUE(ret); + EXPECT_EQ(line, 1u); + EXPECT_EQ(col, 8u); +} + +TEST(CursorStreamWrapper, MissingColon) { + const char json[] = "{\"string\"\n\n\"my string\",\"array\"\n:[\"1\", \"2\", \"3\"]}"; + size_t col, line; + bool ret = testJson(json, line, col); + EXPECT_TRUE(ret); + EXPECT_EQ(line, 3u); + EXPECT_EQ(col, 0u); +} + +TEST(CursorStreamWrapper, MissingSecondQuotes) { + const char json[] = "{\"string\"\n\n:my string\",\"array\"\n:[\"1\", \"2\", \"3\"]}"; + size_t col, line; + bool ret = testJson(json, line, col); + EXPECT_TRUE(ret); + EXPECT_EQ(line, 3u); + EXPECT_EQ(col, 1u); +} + +TEST(CursorStreamWrapper, MissingComma) { + const char json[] = "{\"string\"\n\n:\"my string\"\"array\"\n:[\"1\", \"2\", \"3\"]}"; + size_t col, line; + bool ret = testJson(json, line, col); + EXPECT_TRUE(ret); + EXPECT_EQ(line, 3u); + EXPECT_EQ(col, 12u); +} + +TEST(CursorStreamWrapper, MissingArrayBracket) { + const char json[] = "{\"string\"\n\n:\"my string\",\"array\"\n:\"1\", \"2\", \"3\"]}"; + size_t col, line; + bool ret = testJson(json, line, col); + EXPECT_TRUE(ret); + EXPECT_EQ(line, 4u); + EXPECT_EQ(col, 9u); +} + +TEST(CursorStreamWrapper, MissingArrayComma) { + const char json[] = "{\"string\"\n\n:\"my string\",\"array\"\n:[\"1\" \"2\", \"3\"]}"; + size_t col, line; + bool ret = testJson(json, line, col); + EXPECT_TRUE(ret); + EXPECT_EQ(line, 4u); + EXPECT_EQ(col, 6u); +} + +TEST(CursorStreamWrapper, MissingLastArrayBracket) { + const char json8[] = "{\"string\"\n\n:\"my string\",\"array\"\n:[\"1\", \"2\", \"3\"}"; + size_t col, line; + bool ret = testJson(json8, line, col); + EXPECT_TRUE(ret); + EXPECT_EQ(line, 4u); + EXPECT_EQ(col, 15u); +} + +TEST(CursorStreamWrapper, MissingLastBracket) { + const char json9[] = "{\"string\"\n\n:\"my string\",\"array\"\n:[\"1\", \"2\", \"3\"]"; + size_t col, line; + bool ret = testJson(json9, line, col); + EXPECT_TRUE(ret); + EXPECT_EQ(line, 4u); + EXPECT_EQ(col, 16u); +} diff --git a/third_party/rapidjson/test/unittest/documenttest.cpp b/third_party/rapidjson/test/unittest/documenttest.cpp index ecd4b79bc..c3d1e484d 100644 --- a/third_party/rapidjson/test/unittest/documenttest.cpp +++ b/third_party/rapidjson/test/unittest/documenttest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -128,8 +128,14 @@ TEST(Document, UnchangedOnParseError) { Document doc; doc.SetArray().PushBack(0, doc.GetAllocator()); + ParseResult noError; + EXPECT_TRUE(noError); + ParseResult err = doc.Parse("{]"); EXPECT_TRUE(doc.HasParseError()); + EXPECT_NE(err, noError); + EXPECT_NE(err.Code(), noError); + EXPECT_NE(noError, doc.GetParseError()); EXPECT_EQ(err.Code(), doc.GetParseError()); EXPECT_EQ(err.Offset(), doc.GetErrorOffset()); EXPECT_TRUE(doc.IsArray()); @@ -138,6 +144,9 @@ TEST(Document, UnchangedOnParseError) { err = doc.Parse("{}"); EXPECT_FALSE(doc.HasParseError()); EXPECT_FALSE(err.IsError()); + EXPECT_TRUE(err); + EXPECT_EQ(err, noError); + EXPECT_EQ(err.Code(), noError); EXPECT_EQ(err.Code(), doc.GetParseError()); EXPECT_EQ(err.Offset(), doc.GetErrorOffset()); EXPECT_TRUE(doc.IsObject()); @@ -291,7 +300,14 @@ TEST(Document, Swap) { o.SetObject().AddMember("a", 1, a); // Swap between Document and Value - // d1.Swap(o); // doesn't compile + d1.Swap(o); + EXPECT_TRUE(d1.IsObject()); + EXPECT_TRUE(o.IsArray()); + + d1.Swap(o); + EXPECT_TRUE(d1.IsArray()); + EXPECT_TRUE(o.IsObject()); + o.Swap(d1); EXPECT_TRUE(d1.IsObject()); EXPECT_TRUE(o.IsArray()); @@ -309,6 +325,8 @@ TEST(Document, Swap) { EXPECT_TRUE(d1.IsNull()); // reset document, including allocator + // so clear o before so that it doesnt contain dangling elements + o.Clear(); Document().Swap(d2); EXPECT_TRUE(d2.IsNull()); EXPECT_NE(&d2.GetAllocator(), &a); @@ -488,15 +506,19 @@ TYPED_TEST(DocumentMove, MoveConstructorParseError) { a.Parse("{ 4 = 4]"); ParseResult error(a.GetParseError(), a.GetErrorOffset()); EXPECT_TRUE(a.HasParseError()); + EXPECT_NE(error, noError); + EXPECT_NE(error.Code(), noError); EXPECT_NE(error.Code(), noError.Code()); EXPECT_NE(error.Offset(), noError.Offset()); D b(std::move(a)); EXPECT_FALSE(a.HasParseError()); EXPECT_TRUE(b.HasParseError()); + EXPECT_EQ(a.GetParseError(), noError); EXPECT_EQ(a.GetParseError(), noError.Code()); - EXPECT_EQ(b.GetParseError(), error.Code()); EXPECT_EQ(a.GetErrorOffset(), noError.Offset()); + EXPECT_EQ(b.GetParseError(), error); + EXPECT_EQ(b.GetParseError(), error.Code()); EXPECT_EQ(b.GetErrorOffset(), error.Offset()); D c(std::move(b)); diff --git a/third_party/rapidjson/test/unittest/dtoatest.cpp b/third_party/rapidjson/test/unittest/dtoatest.cpp index afd76eb09..3ec898289 100644 --- a/third_party/rapidjson/test/unittest/dtoatest.cpp +++ b/third_party/rapidjson/test/unittest/dtoatest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -38,6 +38,7 @@ TEST(dtoa, normal) { TEST_DTOA(0.123456789012, "0.123456789012"); TEST_DTOA(1234567.8, "1234567.8"); TEST_DTOA(-79.39773355813419, "-79.39773355813419"); + TEST_DTOA(-36.973846435546875, "-36.973846435546875"); TEST_DTOA(0.000001, "0.000001"); TEST_DTOA(0.0000001, "1e-7"); TEST_DTOA(1e30, "1e30"); diff --git a/third_party/rapidjson/test/unittest/encodedstreamtest.cpp b/third_party/rapidjson/test/unittest/encodedstreamtest.cpp index bc234d3ba..1f0f0e764 100644 --- a/third_party/rapidjson/test/unittest/encodedstreamtest.cpp +++ b/third_party/rapidjson/test/unittest/encodedstreamtest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -113,8 +113,8 @@ protected: EXPECT_EQ(expected, actual); } EXPECT_EQ('\0', s.Peek()); - free(data); EXPECT_EQ(size, eis.Tell()); + free(data); } } diff --git a/third_party/rapidjson/test/unittest/encodingstest.cpp b/third_party/rapidjson/test/unittest/encodingstest.cpp index 67b0391ed..455881e7e 100644 --- a/third_party/rapidjson/test/unittest/encodingstest.cpp +++ b/third_party/rapidjson/test/unittest/encodingstest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -267,7 +267,7 @@ static unsigned inline decode(unsigned* state, unsigned* codep, unsigned byte) { *codep = (*state != UTF8_ACCEPT) ? (byte & 0x3fu) | (*codep << 6) : - (0xff >> type) & (byte); + (0xffu >> type) & (byte); *state = utf8d[256 + *state + type]; return *state; diff --git a/third_party/rapidjson/test/unittest/filestreamtest.cpp b/third_party/rapidjson/test/unittest/filestreamtest.cpp index a38133fa7..de0b4d1a4 100644 --- a/third_party/rapidjson/test/unittest/filestreamtest.cpp +++ b/third_party/rapidjson/test/unittest/filestreamtest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -21,7 +21,7 @@ using namespace rapidjson; class FileStreamTest : public ::testing::Test { public: - FileStreamTest() : filename_(), json_(), length_() {} + FileStreamTest() : filename_(), json_(), length_(), abcde_() {} virtual ~FileStreamTest(); virtual void SetUp() { @@ -49,6 +49,24 @@ public: size_t readLength = fread(json_, 1, length_, fp); json_[readLength] = '\0'; fclose(fp); + + const char *abcde_paths[] = { + "data/abcde.txt", + "bin/data/abcde.txt", + "../bin/data/abcde.txt", + "../../bin/data/abcde.txt", + "../../../bin/data/abcde.txt" + }; + fp = 0; + for (size_t i = 0; i < sizeof(abcde_paths) / sizeof(abcde_paths[0]); i++) { + fp = fopen(abcde_paths[i], "rb"); + if (fp) { + abcde_ = abcde_paths[i]; + break; + } + } + ASSERT_TRUE(fp != 0); + fclose(fp); } virtual void TearDown() { @@ -64,6 +82,7 @@ protected: const char* filename_; char *json_; size_t length_; + const char* abcde_; }; FileStreamTest::~FileStreamTest() {} @@ -86,6 +105,30 @@ TEST_F(FileStreamTest, FileReadStream) { fclose(fp); } +TEST_F(FileStreamTest, FileReadStream_Peek4) { + FILE *fp = fopen(abcde_, "rb"); + ASSERT_TRUE(fp != 0); + char buffer[4]; + FileReadStream s(fp, buffer, sizeof(buffer)); + + const char* c = s.Peek4(); + for (int i = 0; i < 4; i++) + EXPECT_EQ('a' + i, c[i]); + EXPECT_EQ(0u, s.Tell()); + + for (int i = 0; i < 5; i++) { + EXPECT_EQ(static_cast(i), s.Tell()); + EXPECT_EQ('a' + i, s.Peek()); + EXPECT_EQ('a' + i, s.Peek()); + EXPECT_EQ('a' + i, s.Take()); + } + EXPECT_EQ(5u, s.Tell()); + EXPECT_EQ(0, s.Peek()); + EXPECT_EQ(0, s.Take()); + + fclose(fp); +} + TEST_F(FileStreamTest, FileWriteStream) { char filename[L_tmpnam]; FILE* fp = TempFile(filename); diff --git a/third_party/rapidjson/test/unittest/fwdtest.cpp b/third_party/rapidjson/test/unittest/fwdtest.cpp index 4f3268461..e9c707805 100644 --- a/third_party/rapidjson/test/unittest/fwdtest.cpp +++ b/third_party/rapidjson/test/unittest/fwdtest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -100,6 +100,9 @@ struct Foo { #include "rapidjson/prettywriter.h" #include "rapidjson/schema.h" // -> pointer.h +typedef Transcoder, UTF8<> > TranscoderUtf8ToUtf8; +typedef BaseReaderHandler, void> BaseReaderHandlerUtf8Void; + Foo::Foo() : // encodings.h utf8(RAPIDJSON_NEW(UTF8<>)), @@ -111,40 +114,40 @@ Foo::Foo() : utf32le(RAPIDJSON_NEW(UTF32LE<>)), ascii(RAPIDJSON_NEW(ASCII<>)), autoutf(RAPIDJSON_NEW(AutoUTF)), - transcoder(RAPIDJSON_NEW((Transcoder, UTF8<> >))), + transcoder(RAPIDJSON_NEW(TranscoderUtf8ToUtf8)), // allocators.h crtallocator(RAPIDJSON_NEW(CrtAllocator)), memorypoolallocator(RAPIDJSON_NEW(MemoryPoolAllocator<>)), // stream.h - stringstream(RAPIDJSON_NEW(StringStream(0))), - insitustringstream(RAPIDJSON_NEW(InsituStringStream(0))), + stringstream(RAPIDJSON_NEW(StringStream)(NULL)), + insitustringstream(RAPIDJSON_NEW(InsituStringStream)(NULL)), // stringbuffer.h stringbuffer(RAPIDJSON_NEW(StringBuffer)), // // filereadstream.h - // filereadstream(RAPIDJSON_NEW(FileReadStream(stdout, buffer, sizeof(buffer)))), + // filereadstream(RAPIDJSON_NEW(FileReadStream)(stdout, buffer, sizeof(buffer))), // // filewritestream.h - // filewritestream(RAPIDJSON_NEW(FileWriteStream(stdout, buffer, sizeof(buffer)))), + // filewritestream(RAPIDJSON_NEW(FileWriteStream)(stdout, buffer, sizeof(buffer))), // memorybuffer.h memorybuffer(RAPIDJSON_NEW(MemoryBuffer)), // memorystream.h - memorystream(RAPIDJSON_NEW(MemoryStream(0, 0))), + memorystream(RAPIDJSON_NEW(MemoryStream)(NULL, 0)), // reader.h - basereaderhandler(RAPIDJSON_NEW((BaseReaderHandler, void>))), + basereaderhandler(RAPIDJSON_NEW(BaseReaderHandlerUtf8Void)), reader(RAPIDJSON_NEW(Reader)), // writer.h - writer(RAPIDJSON_NEW((Writer))), + writer(RAPIDJSON_NEW(Writer)), // prettywriter.h - prettywriter(RAPIDJSON_NEW((PrettyWriter))), + prettywriter(RAPIDJSON_NEW(PrettyWriter)), // document.h value(RAPIDJSON_NEW(Value)), @@ -154,8 +157,8 @@ Foo::Foo() : pointer(RAPIDJSON_NEW(Pointer)), // schema.h - schemadocument(RAPIDJSON_NEW(SchemaDocument(*document))), - schemavalidator(RAPIDJSON_NEW(SchemaValidator(*schemadocument))) + schemadocument(RAPIDJSON_NEW(SchemaDocument)(*document)), + schemavalidator(RAPIDJSON_NEW(SchemaValidator)(*schemadocument)) { } diff --git a/third_party/rapidjson/test/unittest/istreamwrappertest.cpp b/third_party/rapidjson/test/unittest/istreamwrappertest.cpp index 9d6fbcff0..f0cdb2d38 100644 --- a/third_party/rapidjson/test/unittest/istreamwrappertest.cpp +++ b/third_party/rapidjson/test/unittest/istreamwrappertest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -20,7 +20,7 @@ #include #include -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__clang__) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4702) // unreachable code #endif @@ -35,21 +35,21 @@ static void TestStringStream() { { StringStreamType iss; BasicIStreamWrapper is(iss); - EXPECT_EQ(0, is.Tell()); + EXPECT_EQ(0u, is.Tell()); if (sizeof(Ch) == 1) { EXPECT_EQ(0, is.Peek4()); - EXPECT_EQ(0, is.Tell()); + EXPECT_EQ(0u, is.Tell()); } EXPECT_EQ(0, is.Peek()); EXPECT_EQ(0, is.Take()); - EXPECT_EQ(0, is.Tell()); + EXPECT_EQ(0u, is.Tell()); } { Ch s[] = { 'A', 'B', 'C', '\0' }; StringStreamType iss(s); BasicIStreamWrapper is(iss); - EXPECT_EQ(0, is.Tell()); + EXPECT_EQ(0u, is.Tell()); if (sizeof(Ch) == 1) { EXPECT_EQ(0, is.Peek4()); // less than 4 bytes } @@ -59,7 +59,7 @@ static void TestStringStream() { EXPECT_EQ('A' + i, is.Peek()); EXPECT_EQ('A' + i, is.Take()); } - EXPECT_EQ(3, is.Tell()); + EXPECT_EQ(3u, is.Tell()); EXPECT_EQ(0, is.Peek()); EXPECT_EQ(0, is.Take()); } @@ -72,7 +72,7 @@ static void TestStringStream() { const Ch* c = is.Peek4(); for (int i = 0; i < 4; i++) EXPECT_EQ('A' + i, c[i]); - EXPECT_EQ(0, is.Tell()); + EXPECT_EQ(0u, is.Tell()); } for (int i = 0; i < 5; i++) { EXPECT_EQ(static_cast(i), is.Tell()); @@ -80,7 +80,7 @@ static void TestStringStream() { EXPECT_EQ('A' + i, is.Peek()); EXPECT_EQ('A' + i, is.Take()); } - EXPECT_EQ(5, is.Tell()); + EXPECT_EQ(5u, is.Tell()); EXPECT_EQ(0, is.Peek()); EXPECT_EQ(0, is.Take()); } @@ -129,7 +129,7 @@ TEST(IStreamWrapper, ifstream) { Document d; EXPECT_TRUE(!d.ParseStream(eis).HasParseError()); EXPECT_TRUE(d.IsObject()); - EXPECT_EQ(5, d.MemberCount()); + EXPECT_EQ(5u, d.MemberCount()); } TEST(IStreamWrapper, fstream) { @@ -140,7 +140,7 @@ TEST(IStreamWrapper, fstream) { Document d; EXPECT_TRUE(!d.ParseStream(eis).HasParseError()); EXPECT_TRUE(d.IsObject()); - EXPECT_EQ(5, d.MemberCount()); + EXPECT_EQ(5u, d.MemberCount()); } // wifstream/wfstream only works on C++11 with codecvt_utf16 @@ -176,6 +176,6 @@ TEST(IStreamWrapper, wfstream) { #endif -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__clang__) RAPIDJSON_DIAG_POP #endif diff --git a/third_party/rapidjson/test/unittest/itoatest.cpp b/third_party/rapidjson/test/unittest/itoatest.cpp index b752a6a26..4c834de37 100644 --- a/third_party/rapidjson/test/unittest/itoatest.cpp +++ b/third_party/rapidjson/test/unittest/itoatest.cpp @@ -1,15 +1,15 @@ // Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // -// 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 +// 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 "unittest.h" @@ -61,7 +61,7 @@ static void VerifyValue(T value, void(*f)(T, char*), char* (*g)(T, char*)) { f(value, buffer1); *g(value, buffer2) = '\0'; - + EXPECT_STREQ(buffer1, buffer2); } @@ -70,23 +70,23 @@ template static void Verify(void(*f)(T, char*), char* (*g)(T, char*)) { // Boundary cases VerifyValue(0, f, g); - VerifyValue(std::numeric_limits::min(), f, g); - VerifyValue(std::numeric_limits::max(), f, g); + VerifyValue((std::numeric_limits::min)(), f, g); + VerifyValue((std::numeric_limits::max)(), f, g); // 2^n - 1, 2^n, 10^n - 1, 10^n until overflow - for (uint32_t power = 2; power <= 10; power += 8) { + for (int power = 2; power <= 10; power += 8) { T i = 1, last; do { VerifyValue(i - 1, f, g); VerifyValue(i, f, g); - if (std::numeric_limits::min() < 0) { + if ((std::numeric_limits::min)() < 0) { VerifyValue(Traits::Negate(i), f, g); VerifyValue(Traits::Negate(i + 1), f, g); } last = i; - if (i > static_cast(std::numeric_limits::max() / static_cast(power))) + if (i > static_cast((std::numeric_limits::max)() / static_cast(power))) break; - i *= power; + i *= static_cast(power); } while (last < i); } } diff --git a/third_party/rapidjson/test/unittest/jsoncheckertest.cpp b/third_party/rapidjson/test/unittest/jsoncheckertest.cpp index bea788d26..19e1f1c47 100644 --- a/third_party/rapidjson/test/unittest/jsoncheckertest.cpp +++ b/third_party/rapidjson/test/unittest/jsoncheckertest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -48,6 +48,24 @@ static char* ReadFile(const char* filename, size_t& length) { return json; } +struct NoOpHandler { + bool Null() { return true; } + bool Bool(bool) { return true; } + bool Int(int) { return true; } + bool Uint(unsigned) { return true; } + bool Int64(int64_t) { return true; } + bool Uint64(uint64_t) { return true; } + bool Double(double) { return true; } + bool RawNumber(const char*, SizeType, bool) { return true; } + bool String(const char*, SizeType, bool) { return true; } + bool StartObject() { return true; } + bool Key(const char*, SizeType, bool) { return true; } + bool EndObject(SizeType) { return true; } + bool StartArray() { return true; } + bool EndArray(SizeType) { return true; } +}; + + TEST(JsonChecker, Reader) { char filename[256]; @@ -67,13 +85,26 @@ TEST(JsonChecker, Reader) { continue; } + // Test stack-based parsing. GenericDocument, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak) document.Parse(json); - EXPECT_TRUE(document.HasParseError()); + EXPECT_TRUE(document.HasParseError()) << filename; + // Test iterative parsing. document.Parse(json); - EXPECT_TRUE(document.HasParseError()); + EXPECT_TRUE(document.HasParseError()) << filename; + // Test iterative pull-parsing. + Reader reader; + StringStream ss(json); + NoOpHandler h; + reader.IterativeParseInit(); + while (!reader.IterativeParseComplete()) { + if (!reader.IterativeParseNext(ss, h)) + break; + } + EXPECT_TRUE(reader.HasParseError()) << filename; + free(json); } @@ -87,12 +118,25 @@ TEST(JsonChecker, Reader) { continue; } + // Test stack-based parsing. GenericDocument, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak) document.Parse(json); - EXPECT_FALSE(document.HasParseError()); + EXPECT_FALSE(document.HasParseError()) << filename; + // Test iterative parsing. document.Parse(json); - EXPECT_FALSE(document.HasParseError()); + EXPECT_FALSE(document.HasParseError()) << filename; + + // Test iterative pull-parsing. + Reader reader; + StringStream ss(json); + NoOpHandler h; + reader.IterativeParseInit(); + while (!reader.IterativeParseComplete()) { + if (!reader.IterativeParseNext(ss, h)) + break; + } + EXPECT_FALSE(reader.HasParseError()) << filename; free(json); } diff --git a/third_party/rapidjson/test/unittest/namespacetest.cpp b/third_party/rapidjson/test/unittest/namespacetest.cpp index 1814724ae..e33e6d5f5 100644 --- a/third_party/rapidjson/test/unittest/namespacetest.cpp +++ b/third_party/rapidjson/test/unittest/namespacetest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/third_party/rapidjson/test/unittest/ostreamwrappertest.cpp b/third_party/rapidjson/test/unittest/ostreamwrappertest.cpp index b1d1cd827..be9e429ca 100644 --- a/third_party/rapidjson/test/unittest/ostreamwrappertest.cpp +++ b/third_party/rapidjson/test/unittest/ostreamwrappertest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -69,14 +69,15 @@ static void TestFileStream() { const char* s = "Hello World!\n"; { - ofstream ofs(filename, ios::out | ios::binary); - BasicOStreamWrapper osw(ofs); + FileStreamType ofs(filename, ios::out | ios::binary); + BasicOStreamWrapper osw(ofs); for (const char* p = s; *p; p++) osw.Put(*p); osw.Flush(); } fp = fopen(filename, "r"); + ASSERT_TRUE( fp != NULL ); for (const char* p = s; *p; p++) EXPECT_EQ(*p, static_cast(fgetc(fp))); fclose(fp); diff --git a/third_party/rapidjson/test/unittest/platformtest.cpp b/third_party/rapidjson/test/unittest/platformtest.cpp new file mode 100644 index 000000000..05eba3f5b --- /dev/null +++ b/third_party/rapidjson/test/unittest/platformtest.cpp @@ -0,0 +1,40 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 "unittest.h" + +// see https://github.com/Tencent/rapidjson/issues/1448 +// including windows.h on purpose to provoke a compile time problem as GetObject is a +// macro that gets defined when windows.h is included +#ifdef _WIN32 +#include +#endif + +#include "rapidjson/document.h" +#undef GetObject + +using namespace rapidjson; + +TEST(Platform, GetObject) { + Document doc; + doc.Parse(" { \"object\" : { \"pi\": 3.1416} } "); + EXPECT_TRUE(doc.IsObject()); + EXPECT_TRUE(doc.HasMember("object")); + const Document::ValueType& o = doc["object"]; + EXPECT_TRUE(o.IsObject()); + Value::ConstObject sub = o.GetObject(); + EXPECT_TRUE(sub.HasMember("pi")); + Value::ConstObject sub2 = o.GetObj(); + EXPECT_TRUE(sub2.HasMember("pi")); +} diff --git a/third_party/rapidjson/test/unittest/pointertest.cpp b/third_party/rapidjson/test/unittest/pointertest.cpp index dbddbedee..c35fa8f53 100644 --- a/third_party/rapidjson/test/unittest/pointertest.cpp +++ b/third_party/rapidjson/test/unittest/pointertest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -15,7 +15,10 @@ #include "unittest.h" #include "rapidjson/pointer.h" #include "rapidjson/stringbuffer.h" +#include "rapidjson/ostreamwrapper.h" #include +#include +#include using namespace rapidjson; @@ -300,7 +303,7 @@ TEST(Pointer, Parse_URIFragment) { } { - // Decode UTF-8 perecent encoding to UTF-8 + // Decode UTF-8 percent encoding to UTF-8 Pointer p("#/%C2%A2"); EXPECT_TRUE(p.IsValid()); EXPECT_EQ(1u, p.GetTokenCount()); @@ -308,7 +311,7 @@ TEST(Pointer, Parse_URIFragment) { } { - // Decode UTF-8 perecent encoding to UTF-16 + // Decode UTF-8 percent encoding to UTF-16 GenericPointer > > p(L"#/%C2%A2"); EXPECT_TRUE(p.IsValid()); EXPECT_EQ(1u, p.GetTokenCount()); @@ -317,7 +320,7 @@ TEST(Pointer, Parse_URIFragment) { } { - // Decode UTF-8 perecent encoding to UTF-16 + // Decode UTF-8 percent encoding to UTF-16 GenericPointer > > p(L"#/%E2%82%AC"); EXPECT_TRUE(p.IsValid()); EXPECT_EQ(1u, p.GetTokenCount()); @@ -441,8 +444,8 @@ TEST(Pointer, Stringify) { } // Construct a Pointer with static tokens, no dynamic allocation involved. -#define NAME(s) { s, sizeof(s) / sizeof(s[0]) - 1, kPointerInvalidIndex } -#define INDEX(i) { #i, sizeof(#i) - 1, i } +#define NAME(s) { s, static_cast(sizeof(s) / sizeof(s[0]) - 1), kPointerInvalidIndex } +#define INDEX(i) { #i, static_cast(sizeof(#i) - 1), i } static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(0) }; // equivalent to "/foo/0" @@ -462,7 +465,8 @@ TEST(Pointer, ConstructorWithToken) { TEST(Pointer, CopyConstructor) { { - Pointer p("/foo/0"); + CrtAllocator allocator; + Pointer p("/foo/0", &allocator); Pointer q(p); EXPECT_TRUE(q.IsValid()); EXPECT_EQ(2u, q.GetTokenCount()); @@ -471,6 +475,9 @@ TEST(Pointer, CopyConstructor) { EXPECT_EQ(1u, q.GetTokens()[1].length); EXPECT_STREQ("0", q.GetTokens()[1].name); EXPECT_EQ(0u, q.GetTokens()[1].index); + + // Copied pointer needs to have its own allocator + EXPECT_NE(&p.GetAllocator(), &q.GetAllocator()); } // Static tokens @@ -489,7 +496,8 @@ TEST(Pointer, CopyConstructor) { TEST(Pointer, Assignment) { { - Pointer p("/foo/0"); + CrtAllocator allocator; + Pointer p("/foo/0", &allocator); Pointer q; q = p; EXPECT_TRUE(q.IsValid()); @@ -499,7 +507,8 @@ TEST(Pointer, Assignment) { EXPECT_EQ(1u, q.GetTokens()[1].length); EXPECT_STREQ("0", q.GetTokens()[1].name); EXPECT_EQ(0u, q.GetTokens()[1].index); - q = q; + EXPECT_NE(&p.GetAllocator(), &q.GetAllocator()); + q = static_cast(q); EXPECT_TRUE(q.IsValid()); EXPECT_EQ(2u, q.GetTokenCount()); EXPECT_EQ(3u, q.GetTokens()[0].length); @@ -507,6 +516,7 @@ TEST(Pointer, Assignment) { EXPECT_EQ(1u, q.GetTokens()[1].length); EXPECT_STREQ("0", q.GetTokens()[1].name); EXPECT_EQ(0u, q.GetTokens()[1].index); + EXPECT_NE(&p.GetAllocator(), &q.GetAllocator()); } // Static tokens @@ -524,6 +534,36 @@ TEST(Pointer, Assignment) { } } +TEST(Pointer, Swap) { + Pointer p("/foo/0"); + Pointer q(&p.GetAllocator()); + + q.Swap(p); + EXPECT_EQ(&q.GetAllocator(), &p.GetAllocator()); + EXPECT_TRUE(p.IsValid()); + EXPECT_TRUE(q.IsValid()); + EXPECT_EQ(0u, p.GetTokenCount()); + EXPECT_EQ(2u, q.GetTokenCount()); + EXPECT_EQ(3u, q.GetTokens()[0].length); + EXPECT_STREQ("foo", q.GetTokens()[0].name); + EXPECT_EQ(1u, q.GetTokens()[1].length); + EXPECT_STREQ("0", q.GetTokens()[1].name); + EXPECT_EQ(0u, q.GetTokens()[1].index); + + // std::swap compatibility + std::swap(p, q); + EXPECT_EQ(&p.GetAllocator(), &q.GetAllocator()); + EXPECT_TRUE(q.IsValid()); + EXPECT_TRUE(p.IsValid()); + EXPECT_EQ(0u, q.GetTokenCount()); + EXPECT_EQ(2u, p.GetTokenCount()); + EXPECT_EQ(3u, p.GetTokens()[0].length); + EXPECT_STREQ("foo", p.GetTokens()[0].name); + EXPECT_EQ(1u, p.GetTokens()[1].length); + EXPECT_STREQ("0", p.GetTokens()[1].name); + EXPECT_EQ(0u, p.GetTokens()[1].index); +} + TEST(Pointer, Append) { { Pointer p; @@ -610,6 +650,52 @@ TEST(Pointer, Create) { } } +static const char kJsonIds[] = "{\n" + " \"id\": \"/root/\"," + " \"foo\":[\"bar\", \"baz\", {\"id\": \"inarray\", \"child\": 1}],\n" + " \"int\" : 2,\n" + " \"str\" : \"val\",\n" + " \"obj\": {\"id\": \"inobj\", \"child\": 3},\n" + " \"jbo\": {\"id\": true, \"child\": 4}\n" + "}"; + + +TEST(Pointer, GetUri) { + CrtAllocator allocator; + Document d; + d.Parse(kJsonIds); + Pointer::UriType doc("http://doc"); + Pointer::UriType root("http://doc/root/"); + Pointer::UriType empty = Pointer::UriType(); + + EXPECT_TRUE(Pointer("").GetUri(d, doc) == doc); + EXPECT_TRUE(Pointer("/foo").GetUri(d, doc) == root); + EXPECT_TRUE(Pointer("/foo/0").GetUri(d, doc) == root); + EXPECT_TRUE(Pointer("/foo/2").GetUri(d, doc) == root); + EXPECT_TRUE(Pointer("/foo/2/child").GetUri(d, doc) == Pointer::UriType("http://doc/root/inarray")); + EXPECT_TRUE(Pointer("/int").GetUri(d, doc) == root); + EXPECT_TRUE(Pointer("/str").GetUri(d, doc) == root); + EXPECT_TRUE(Pointer("/obj").GetUri(d, doc) == root); + EXPECT_TRUE(Pointer("/obj/child").GetUri(d, doc) == Pointer::UriType("http://doc/root/inobj")); + EXPECT_TRUE(Pointer("/jbo").GetUri(d, doc) == root); + EXPECT_TRUE(Pointer("/jbo/child").GetUri(d, doc) == root); // id not string + + size_t unresolvedTokenIndex; + EXPECT_TRUE(Pointer("/abc").GetUri(d, doc, &unresolvedTokenIndex, &allocator) == empty); // Out of boundary + EXPECT_EQ(0u, unresolvedTokenIndex); + EXPECT_TRUE(Pointer("/foo/3").GetUri(d, doc, &unresolvedTokenIndex, &allocator) == empty); // Out of boundary + EXPECT_EQ(1u, unresolvedTokenIndex); + EXPECT_TRUE(Pointer("/foo/a").GetUri(d, doc, &unresolvedTokenIndex, &allocator) == empty); // "/foo" is an array, cannot query by "a" + EXPECT_EQ(1u, unresolvedTokenIndex); + EXPECT_TRUE(Pointer("/foo/0/0").GetUri(d, doc, &unresolvedTokenIndex, &allocator) == empty); // "/foo/0" is an string, cannot further query + EXPECT_EQ(2u, unresolvedTokenIndex); + EXPECT_TRUE(Pointer("/foo/0/a").GetUri(d, doc, &unresolvedTokenIndex, &allocator) == empty); // "/foo/0" is an string, cannot further query + EXPECT_EQ(2u, unresolvedTokenIndex); + + Pointer::Token tokens[] = { { "foo ...", 3, kPointerInvalidIndex } }; + EXPECT_TRUE(Pointer(tokens, 1).GetUri(d, doc) == root); +} + TEST(Pointer, Get) { Document d; d.Parse(kJson); @@ -626,16 +712,20 @@ TEST(Pointer, Get) { EXPECT_EQ(&d["k\"l"], Pointer("/k\"l").Get(d)); EXPECT_EQ(&d[" "], Pointer("/ ").Get(d)); EXPECT_EQ(&d["m~n"], Pointer("/m~0n").Get(d)); - EXPECT_TRUE(Pointer("/abc").Get(d) == 0); + + EXPECT_TRUE(Pointer("/abc").Get(d) == 0); // Out of boundary size_t unresolvedTokenIndex; EXPECT_TRUE(Pointer("/foo/2").Get(d, &unresolvedTokenIndex) == 0); // Out of boundary - EXPECT_EQ(1, unresolvedTokenIndex); + EXPECT_EQ(1u, unresolvedTokenIndex); EXPECT_TRUE(Pointer("/foo/a").Get(d, &unresolvedTokenIndex) == 0); // "/foo" is an array, cannot query by "a" - EXPECT_EQ(1, unresolvedTokenIndex); + EXPECT_EQ(1u, unresolvedTokenIndex); EXPECT_TRUE(Pointer("/foo/0/0").Get(d, &unresolvedTokenIndex) == 0); // "/foo/0" is an string, cannot further query - EXPECT_EQ(2, unresolvedTokenIndex); + EXPECT_EQ(2u, unresolvedTokenIndex); EXPECT_TRUE(Pointer("/foo/0/a").Get(d, &unresolvedTokenIndex) == 0); // "/foo/0" is an string, cannot further query - EXPECT_EQ(2, unresolvedTokenIndex); + EXPECT_EQ(2u, unresolvedTokenIndex); + + Pointer::Token tokens[] = { { "foo ...", 3, kPointerInvalidIndex } }; + EXPECT_EQ(&d["foo"], Pointer(tokens, 1).Get(d)); } TEST(Pointer, GetWithDefault) { @@ -862,7 +952,7 @@ TEST(Pointer, Set_NoAllocator) { #endif } -TEST(Pointer, Swap) { +TEST(Pointer, Swap_Value) { Document d; d.Parse(kJson); Document::AllocatorType& a = d.GetAllocator(); @@ -871,7 +961,7 @@ TEST(Pointer, Swap) { EXPECT_STREQ("bar", d["foo"][1].GetString()); } -TEST(Pointer, Swap_NoAllocator) { +TEST(Pointer, Swap_Value_NoAllocator) { Document d; d.Parse(kJson); Pointer("/foo/0").Swap(d, *Pointer("/foo/1").Get(d)); @@ -954,13 +1044,13 @@ TEST(Pointer, GetValueByPointer) { size_t unresolvedTokenIndex; EXPECT_TRUE(GetValueByPointer(d, "/foo/2", &unresolvedTokenIndex) == 0); // Out of boundary - EXPECT_EQ(1, unresolvedTokenIndex); + EXPECT_EQ(1u, unresolvedTokenIndex); EXPECT_TRUE(GetValueByPointer(d, "/foo/a", &unresolvedTokenIndex) == 0); // "/foo" is an array, cannot query by "a" - EXPECT_EQ(1, unresolvedTokenIndex); + EXPECT_EQ(1u, unresolvedTokenIndex); EXPECT_TRUE(GetValueByPointer(d, "/foo/0/0", &unresolvedTokenIndex) == 0); // "/foo/0" is an string, cannot further query - EXPECT_EQ(2, unresolvedTokenIndex); + EXPECT_EQ(2u, unresolvedTokenIndex); EXPECT_TRUE(GetValueByPointer(d, "/foo/0/a", &unresolvedTokenIndex) == 0); // "/foo/0" is an string, cannot further query - EXPECT_EQ(2, unresolvedTokenIndex); + EXPECT_EQ(2u, unresolvedTokenIndex); // const version const Value& v = d; @@ -968,13 +1058,13 @@ TEST(Pointer, GetValueByPointer) { EXPECT_EQ(&d["foo"][0], GetValueByPointer(v, "/foo/0")); EXPECT_TRUE(GetValueByPointer(v, "/foo/2", &unresolvedTokenIndex) == 0); // Out of boundary - EXPECT_EQ(1, unresolvedTokenIndex); + EXPECT_EQ(1u, unresolvedTokenIndex); EXPECT_TRUE(GetValueByPointer(v, "/foo/a", &unresolvedTokenIndex) == 0); // "/foo" is an array, cannot query by "a" - EXPECT_EQ(1, unresolvedTokenIndex); + EXPECT_EQ(1u, unresolvedTokenIndex); EXPECT_TRUE(GetValueByPointer(v, "/foo/0/0", &unresolvedTokenIndex) == 0); // "/foo/0" is an string, cannot further query - EXPECT_EQ(2, unresolvedTokenIndex); + EXPECT_EQ(2u, unresolvedTokenIndex); EXPECT_TRUE(GetValueByPointer(v, "/foo/0/a", &unresolvedTokenIndex) == 0); // "/foo/0" is an string, cannot further query - EXPECT_EQ(2, unresolvedTokenIndex); + EXPECT_EQ(2u, unresolvedTokenIndex); } @@ -1488,7 +1578,112 @@ TEST(Pointer, Ambiguity) { } } -// https://github.com/miloyip/rapidjson/issues/483 +TEST(Pointer, ResolveOnObject) { + Document d; + EXPECT_FALSE(d.Parse("{\"a\": 123}").HasParseError()); + + { + Value::ConstObject o = static_cast(d).GetObject(); + EXPECT_EQ(123, Pointer("/a").Get(o)->GetInt()); + } + + { + Value::Object o = d.GetObject(); + Pointer("/a").Set(o, 456, d.GetAllocator()); + EXPECT_EQ(456, Pointer("/a").Get(o)->GetInt()); + } +} + +TEST(Pointer, ResolveOnArray) { + Document d; + EXPECT_FALSE(d.Parse("[1, 2, 3]").HasParseError()); + + { + Value::ConstArray a = static_cast(d).GetArray(); + EXPECT_EQ(2, Pointer("/1").Get(a)->GetInt()); + } + + { + Value::Array a = d.GetArray(); + Pointer("/1").Set(a, 123, d.GetAllocator()); + EXPECT_EQ(123, Pointer("/1").Get(a)->GetInt()); + } +} + +TEST(Pointer, LessThan) { + static const struct { + const char *str; + bool valid; + } pointers[] = { + { "/a/b", true }, + { "/a", true }, + { "/d/1", true }, + { "/d/2/z", true }, + { "/d/2/3", true }, + { "/d/2", true }, + { "/a/c", true }, + { "/e/f~g", false }, + { "/d/2/zz", true }, + { "/d/1", true }, + { "/d/2/z", true }, + { "/e/f~~g", false }, + { "/e/f~0g", true }, + { "/e/f~1g", true }, + { "/e/f.g", true }, + { "", true } + }; + static const char *ordered_pointers[] = { + "", + "/a", + "/a/b", + "/a/c", + "/d/1", + "/d/1", + "/d/2", + "/e/f.g", + "/e/f~1g", + "/e/f~0g", + "/d/2/3", + "/d/2/z", + "/d/2/z", + "/d/2/zz", + NULL, // was invalid "/e/f~g" + NULL // was invalid "/e/f~~g" + }; + typedef MemoryPoolAllocator<> AllocatorType; + typedef GenericPointer PointerType; + typedef std::multimap PointerMap; + PointerMap map; + PointerMap::iterator it; + AllocatorType allocator; + size_t i; + + EXPECT_EQ(sizeof(pointers) / sizeof(pointers[0]), + sizeof(ordered_pointers) / sizeof(ordered_pointers[0])); + + for (i = 0; i < sizeof(pointers) / sizeof(pointers[0]); ++i) { + it = map.insert(PointerMap::value_type(PointerType(pointers[i].str, &allocator), i)); + if (!it->first.IsValid()) { + EXPECT_EQ(++it, map.end()); + } + } + + for (i = 0, it = map.begin(); it != map.end(); ++it, ++i) { + EXPECT_TRUE(it->second < sizeof(pointers) / sizeof(pointers[0])); + EXPECT_EQ(it->first.IsValid(), pointers[it->second].valid); + EXPECT_TRUE(i < sizeof(ordered_pointers) / sizeof(ordered_pointers[0])); + EXPECT_EQ(it->first.IsValid(), !!ordered_pointers[i]); + if (it->first.IsValid()) { + std::stringstream ss; + OStreamWrapper os(ss); + EXPECT_TRUE(it->first.Stringify(os)); + EXPECT_EQ(ss.str(), pointers[it->second].str); + EXPECT_EQ(ss.str(), ordered_pointers[i]); + } + } +} + +// https://github.com/Tencent/rapidjson/issues/483 namespace myjson { class MyAllocator @@ -1522,3 +1717,14 @@ TEST(Pointer, Issue483) { value.SetString(mystr.c_str(), static_cast(mystr.length()), document.GetAllocator()); myjson::Pointer(path.c_str()).Set(document, value, document.GetAllocator()); } + +TEST(Pointer, Issue1899) { + typedef GenericPointer > PointerType; + PointerType p; + PointerType q = p.Append("foo"); + EXPECT_TRUE(PointerType("/foo") == q); + q = q.Append(1234); + EXPECT_TRUE(PointerType("/foo/1234") == q); + q = q.Append(""); + EXPECT_TRUE(PointerType("/foo/1234/") == q); +} diff --git a/third_party/rapidjson/test/unittest/prettywritertest.cpp b/third_party/rapidjson/test/unittest/prettywritertest.cpp index a372f7986..0b7feef3b 100644 --- a/third_party/rapidjson/test/unittest/prettywritertest.cpp +++ b/third_party/rapidjson/test/unittest/prettywritertest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -18,6 +18,11 @@ #include "rapidjson/stringbuffer.h" #include "rapidjson/filewritestream.h" +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + using namespace rapidjson; static const char kJson[] = "{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3,-1],\"u64\":1234567890123456789,\"i64\":-1234567890123456789}"; @@ -162,6 +167,7 @@ TEST(PrettyWriter, OStreamWrapper) { TEST(PrettyWriter, FileWriteStream) { char filename[L_tmpnam]; FILE* fp = TempFile(filename); + ASSERT_TRUE(fp!=NULL); char buffer[16]; FileWriteStream os(fp, buffer, sizeof(buffer)); PrettyWriter writer(os); @@ -201,3 +207,167 @@ TEST(PrettyWriter, RawValue) { "}", buffer.GetString()); } + +TEST(PrettyWriter, InvalidEventSequence) { + // {] + { + StringBuffer buffer; + PrettyWriter writer(buffer); + writer.StartObject(); + EXPECT_THROW(writer.EndArray(), AssertException); + EXPECT_FALSE(writer.IsComplete()); + } + + // [} + { + StringBuffer buffer; + PrettyWriter writer(buffer); + writer.StartArray(); + EXPECT_THROW(writer.EndObject(), AssertException); + EXPECT_FALSE(writer.IsComplete()); + } + + // { 1: + { + StringBuffer buffer; + PrettyWriter writer(buffer); + writer.StartObject(); + EXPECT_THROW(writer.Int(1), AssertException); + EXPECT_FALSE(writer.IsComplete()); + } + + // { 'a' } + { + StringBuffer buffer; + PrettyWriter writer(buffer); + writer.StartObject(); + writer.Key("a"); + EXPECT_THROW(writer.EndObject(), AssertException); + EXPECT_FALSE(writer.IsComplete()); + } + + // { 'a':'b','c' } + { + StringBuffer buffer; + PrettyWriter writer(buffer); + writer.StartObject(); + writer.Key("a"); + writer.String("b"); + writer.Key("c"); + EXPECT_THROW(writer.EndObject(), AssertException); + EXPECT_FALSE(writer.IsComplete()); + } +} + +TEST(PrettyWriter, NaN) { + double nan = std::numeric_limits::quiet_NaN(); + + EXPECT_TRUE(internal::Double(nan).IsNan()); + StringBuffer buffer; + { + PrettyWriter writer(buffer); + EXPECT_FALSE(writer.Double(nan)); + } + { + PrettyWriter, UTF8<>, CrtAllocator, kWriteNanAndInfFlag> writer(buffer); + EXPECT_TRUE(writer.Double(nan)); + EXPECT_STREQ("NaN", buffer.GetString()); + } + GenericStringBuffer > buffer2; + PrettyWriter > > writer2(buffer2); + EXPECT_FALSE(writer2.Double(nan)); +} + +TEST(PrettyWriter, Inf) { + double inf = std::numeric_limits::infinity(); + + EXPECT_TRUE(internal::Double(inf).IsInf()); + StringBuffer buffer; + { + PrettyWriter writer(buffer); + EXPECT_FALSE(writer.Double(inf)); + } + { + PrettyWriter writer(buffer); + EXPECT_FALSE(writer.Double(-inf)); + } + { + PrettyWriter, UTF8<>, CrtAllocator, kWriteNanAndInfFlag> writer(buffer); + EXPECT_TRUE(writer.Double(inf)); + } + { + PrettyWriter, UTF8<>, CrtAllocator, kWriteNanAndInfFlag> writer(buffer); + EXPECT_TRUE(writer.Double(-inf)); + } + EXPECT_STREQ("Infinity-Infinity", buffer.GetString()); +} + +TEST(PrettyWriter, Issue_889) { + char buf[100] = "Hello"; + + StringBuffer buffer; + PrettyWriter writer(buffer); + writer.StartArray(); + writer.String(buf); + writer.EndArray(); + + EXPECT_STREQ("[\n \"Hello\"\n]", buffer.GetString()); + EXPECT_TRUE(writer.IsComplete()); \ +} + + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + +static PrettyWriter WriterGen(StringBuffer &target) { + PrettyWriter writer(target); + writer.StartObject(); + writer.Key("a"); + writer.Int(1); + return writer; +} + +TEST(PrettyWriter, MoveCtor) { + StringBuffer buffer; + PrettyWriter writer(WriterGen(buffer)); + writer.EndObject(); + EXPECT_TRUE(writer.IsComplete()); + EXPECT_STREQ( + "{\n" + " \"a\": 1\n" + "}", + buffer.GetString()); +} +#endif + +TEST(PrettyWriter, Issue_1336) { +#define T(meth, val, expected) \ + { \ + StringBuffer buffer; \ + PrettyWriter writer(buffer); \ + writer.meth(val); \ + \ + EXPECT_STREQ(expected, buffer.GetString()); \ + EXPECT_TRUE(writer.IsComplete()); \ + } + + T(Bool, false, "false"); + T(Bool, true, "true"); + T(Int, 0, "0"); + T(Uint, 0, "0"); + T(Int64, 0, "0"); + T(Uint64, 0, "0"); + T(Double, 0, "0.0"); + T(String, "Hello", "\"Hello\""); +#undef T + + StringBuffer buffer; + PrettyWriter writer(buffer); + writer.Null(); + + EXPECT_STREQ("null", buffer.GetString()); + EXPECT_TRUE(writer.IsComplete()); +} + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif diff --git a/third_party/rapidjson/test/unittest/readertest.cpp b/third_party/rapidjson/test/unittest/readertest.cpp index 64a1f9c3c..f828dbbe2 100644 --- a/third_party/rapidjson/test/unittest/readertest.cpp +++ b/third_party/rapidjson/test/unittest/readertest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -207,6 +207,7 @@ static void TestParseDouble() { TEST_DOUBLE(fullPrecision, "0.0", 0.0); TEST_DOUBLE(fullPrecision, "-0.0", -0.0); // For checking issue #289 + TEST_DOUBLE(fullPrecision, "0e100", 0.0); // For checking issue #1249 TEST_DOUBLE(fullPrecision, "1.0", 1.0); TEST_DOUBLE(fullPrecision, "-1.0", -1.0); TEST_DOUBLE(fullPrecision, "1.5", 1.5); @@ -233,7 +234,7 @@ static void TestParseDouble() { TEST_DOUBLE(fullPrecision, "1e-10000", 0.0); // must underflow TEST_DOUBLE(fullPrecision, "18446744073709551616", 18446744073709551616.0); // 2^64 (max of uint64_t + 1, force to use double) TEST_DOUBLE(fullPrecision, "-9223372036854775809", -9223372036854775809.0); // -2^63 - 1(min of int64_t + 1, force to use double) - TEST_DOUBLE(fullPrecision, "0.9868011474609375", 0.9868011474609375); // https://github.com/miloyip/rapidjson/issues/120 + TEST_DOUBLE(fullPrecision, "0.9868011474609375", 0.9868011474609375); // https://github.com/Tencent/rapidjson/issues/120 TEST_DOUBLE(fullPrecision, "123e34", 123e34); // Fast Path Cases In Disguise TEST_DOUBLE(fullPrecision, "45913141877270640000.0", 45913141877270640000.0); TEST_DOUBLE(fullPrecision, "2.2250738585072011e-308", 2.2250738585072011e-308); // http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/ @@ -242,16 +243,18 @@ static void TestParseDouble() { TEST_DOUBLE(fullPrecision, "1e-214748363", 0.0); // Maximum supported negative exponent TEST_DOUBLE(fullPrecision, "1e-214748364", 0.0); TEST_DOUBLE(fullPrecision, "1e-21474836311", 0.0); + TEST_DOUBLE(fullPrecision, "1.00000000001e-2147483638", 0.0); TEST_DOUBLE(fullPrecision, "0.017976931348623157e+310", 1.7976931348623157e+308); // Max double in another form + TEST_DOUBLE(fullPrecision, "128.74836467836484838364836483643636483648e-336", 0.0); // Issue #1251 // Since - // abs((2^-1022 - 2^-1074) - 2.2250738585072012e-308) = 3.109754131239141401123495768877590405345064751974375599... �� 10^-324 - // abs((2^-1022) - 2.2250738585072012e-308) = 1.830902327173324040642192159804623318305533274168872044... �� 10 ^ -324 + // abs((2^-1022 - 2^-1074) - 2.2250738585072012e-308) = 3.109754131239141401123495768877590405345064751974375599... x 10^-324 + // abs((2^-1022) - 2.2250738585072012e-308) = 1.830902327173324040642192159804623318305533274168872044... x 10 ^ -324 // So 2.2250738585072012e-308 should round to 2^-1022 = 2.2250738585072014e-308 TEST_DOUBLE(fullPrecision, "2.2250738585072012e-308", 2.2250738585072014e-308); // http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/ // More closer to normal/subnormal boundary - // boundary = 2^-1022 - 2^-1075 = 2.225073858507201136057409796709131975934819546351645648... �� 10^-308 + // boundary = 2^-1022 - 2^-1075 = 2.225073858507201136057409796709131975934819546351645648... x 10^-308 TEST_DOUBLE(fullPrecision, "2.22507385850720113605740979670913197593481954635164564e-308", 2.2250738585072009e-308); TEST_DOUBLE(fullPrecision, "2.22507385850720113605740979670913197593481954635164565e-308", 2.2250738585072014e-308); @@ -376,6 +379,208 @@ static void TestParseDouble() { d = d.Value() * 0.5; } } + + // Issue 1249 + TEST_DOUBLE(fullPrecision, "0e100", 0.0); + + // Issue 1251 + TEST_DOUBLE(fullPrecision, "128.74836467836484838364836483643636483648e-336", 0.0); + + // Issue 1256 + TEST_DOUBLE(fullPrecision, + "6223372036854775296.1701512723685473547372536854755293372036854685477" + "529752233737201701512337200972013723685473123372036872036854236854737" + "247372368372367752975258547752975254729752547372368737201701512354737" + "83723677529752585477247372368372368547354737253685475529752", + 6223372036854775808.0); + +#if 0 + // Test (length + exponent) overflow + TEST_DOUBLE(fullPrecision, "0e+2147483647", 0.0); + TEST_DOUBLE(fullPrecision, "0e-2147483648", 0.0); + TEST_DOUBLE(fullPrecision, "1e-2147483648", 0.0); + TEST_DOUBLE(fullPrecision, "0e+9223372036854775807", 0.0); + TEST_DOUBLE(fullPrecision, "0e-9223372036854775808", 0.0); +#endif + + if (fullPrecision) + { + TEST_DOUBLE(fullPrecision, "1e-325", 0.0); + TEST_DOUBLE(fullPrecision, "1e-324", 0.0); + TEST_DOUBLE(fullPrecision, "2e-324", 0.0); + TEST_DOUBLE(fullPrecision, "2.4703282292062327e-324", 0.0); + TEST_DOUBLE(fullPrecision, "2.4703282292062328e-324", 5e-324); + TEST_DOUBLE(fullPrecision, "2.48e-324",5e-324); + TEST_DOUBLE(fullPrecision, "2.5e-324", 5e-324); + + // Slightly above max-normal + TEST_DOUBLE(fullPrecision, "1.7976931348623158e+308", 1.7976931348623158e+308); + + TEST_DOUBLE(fullPrecision, + "17976931348623157081452742373170435679807056752584499659891747680315726" + "07800285387605895586327668781715404589535143824642343213268894641827684" + "67546703537516986049910576551282076245490090389328944075868508455133942" + "30458323690322294816580855933212334827479782620414472316873817718091929" + "9881250404026184124858368", + (std::numeric_limits::max)()); + + TEST_DOUBLE(fullPrecision, + "243546080556034731077856379609316893158278902575447060151047" + "212703405344938119816206067372775299130836050315842578309818" + "316450894337978612745889730079163798234256495613858256849283" + "467066859489192118352020514036083287319232435355752493038825" + "828481044358810649108367633313557305310641892225870327827273" + "41408256.000000", + 2.4354608055603473e+307); + // 9007199254740991 * 2^971 (max normal) + TEST_DOUBLE(fullPrecision, + "1.797693134862315708145274237317043567980705675258449965989174768031572607800285" + "38760589558632766878171540458953514382464234321326889464182768467546703537516986" + "04991057655128207624549009038932894407586850845513394230458323690322294816580855" + "9332123348274797826204144723168738177180919299881250404026184124858368e+308", + 1.797693134862315708e+308 // 0x1.fffffffffffffp1023 + ); +#if 0 + // TODO: + // Should work at least in full-precision mode... + TEST_DOUBLE(fullPrecision, + "0.00000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000024703282292062327208828439643411068618252" + "9901307162382212792841250337753635104375932649918180817996189" + "8982823477228588654633283551779698981993873980053909390631503" + "5659515570226392290858392449105184435931802849936536152500319" + "3704576782492193656236698636584807570015857692699037063119282" + "7955855133292783433840935197801553124659726357957462276646527" + "2827220056374006485499977096599470454020828166226237857393450" + "7363390079677619305775067401763246736009689513405355374585166" + "6113422376667860416215968046191446729184030053005753084904876" + "5391711386591646239524912623653881879636239373280423891018672" + "3484976682350898633885879256283027559956575244555072551893136" + "9083625477918694866799496832404970582102851318545139621383772" + "2826145437693412532098591327667236328125", + 0.0); +#endif + // 9007199254740991 * 2^-1074 = (2^53 - 1) * 2^-1074 + TEST_DOUBLE(fullPrecision, + "4.450147717014402272114819593418263951869639092703291296046852219449644444042153" + "89103305904781627017582829831782607924221374017287738918929105531441481564124348" + "67599762821265346585071045737627442980259622449029037796981144446145705102663115" + "10031828794952795966823603998647925096578034214163701381261333311989876551545144" + "03152612538132666529513060001849177663286607555958373922409899478075565940981010" + "21612198814605258742579179000071675999344145086087205681577915435923018910334964" + "86942061405218289243144579760516365090360651414037721744226256159024466852576737" + "24464300755133324500796506867194913776884780053099639677097589658441378944337966" + "21993967316936280457084866613206797017728916080020698679408551343728867675409720" + "757232455434770912461317493580281734466552734375e-308", + 4.450147717014402272e-308 // 0x1.fffffffffffffp-1022 + ); + // 9007199254740990 * 2^-1074 + TEST_DOUBLE(fullPrecision, + "4.450147717014401778049173752171719775300846224481918930987049605124880018456471" + "39035755177760751831052846195619008686241717547743167145836439860405887584484471" + "19639655002484083577939142623582164522087943959208000909794783876158397872163051" + "22622675229968408654350206725478309956546318828765627255022767720818849892988457" + "26333908582101604036318532842699932130356061901518261174396928478121372742040102" + "17446565569357687263889031732270082446958029584739170416643195242132750803227473" + "16608838720742955671061336566907126801014814608027120593609275183716632624844904" + "31985250929886016737037234388448352929102742708402644340627409931664203093081360" + "70794835812045179006047003875039546061891526346421705014598610179523165038319441" + "51446491086954182492263498716056346893310546875e-308", + 4.450147717014401778e-308 // 0x1.ffffffffffffep-1022 + ); + // half way between the two numbers above. + // round to nearest even. + TEST_DOUBLE(fullPrecision, + "4.450147717014402025081996672794991863585242658592605113516950912287262231249312" + "64069530541271189424317838013700808305231545782515453032382772695923684574304409" + "93619708911874715081505094180604803751173783204118519353387964161152051487413083" + "16327252012460602310586905362063117526562176521464664318142050516404363222266800" + "64743260560117135282915796422274554896821334728738317548403413978098469341510556" + "19529382191981473003234105366170879223151087335413188049110555339027884856781219" + "01775450062980622457102958163711745945687733011032421168917765671370549738710820" + "78224775842509670618916870627821633352993761380751142008862499795052791018709663" + "46394401564490729731565935244123171539810221213221201847003580761626016356864581" + "1358486831521563686919762403704226016998291015625e-308", + 4.450147717014401778e-308 // 0x1.ffffffffffffep-1022 + ); + TEST_DOUBLE(fullPrecision, + "4.450147717014402025081996672794991863585242658592605113516950912287262231249312" + "64069530541271189424317838013700808305231545782515453032382772695923684574304409" + "93619708911874715081505094180604803751173783204118519353387964161152051487413083" + "16327252012460602310586905362063117526562176521464664318142050516404363222266800" + "64743260560117135282915796422274554896821334728738317548403413978098469341510556" + "19529382191981473003234105366170879223151087335413188049110555339027884856781219" + "01775450062980622457102958163711745945687733011032421168917765671370549738710820" + "78224775842509670618916870627821633352993761380751142008862499795052791018709663" + "46394401564490729731565935244123171539810221213221201847003580761626016356864581" + "13584868315215636869197624037042260169982910156250000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000e-308", + 4.450147717014401778e-308 // 0x1.ffffffffffffep-1022 + ); +#if 0 + // ... round up + // TODO: + // Should work at least in full-precision mode... + TEST_DOUBLE(fullPrecision, + "4.450147717014402025081996672794991863585242658592605113516950912287262231249312" + "64069530541271189424317838013700808305231545782515453032382772695923684574304409" + "93619708911874715081505094180604803751173783204118519353387964161152051487413083" + "16327252012460602310586905362063117526562176521464664318142050516404363222266800" + "64743260560117135282915796422274554896821334728738317548403413978098469341510556" + "19529382191981473003234105366170879223151087335413188049110555339027884856781219" + "01775450062980622457102958163711745945687733011032421168917765671370549738710820" + "78224775842509670618916870627821633352993761380751142008862499795052791018709663" + "46394401564490729731565935244123171539810221213221201847003580761626016356864581" + "13584868315215636869197624037042260169982910156250000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000001e-308", + 4.450147717014402272e-308 // 0x1.fffffffffffffp-1022 + ); +#endif + // ... round down + TEST_DOUBLE(fullPrecision, + "4.450147717014402025081996672794991863585242658592605113516950912287262231249312" + "64069530541271189424317838013700808305231545782515453032382772695923684574304409" + "93619708911874715081505094180604803751173783204118519353387964161152051487413083" + "16327252012460602310586905362063117526562176521464664318142050516404363222266800" + "64743260560117135282915796422274554896821334728738317548403413978098469341510556" + "19529382191981473003234105366170879223151087335413188049110555339027884856781219" + "01775450062980622457102958163711745945687733011032421168917765671370549738710820" + "78224775842509670618916870627821633352993761380751142008862499795052791018709663" + "46394401564490729731565935244123171539810221213221201847003580761626016356864581" + "13584868315215636869197624037042260169982910156249999999999999999999999999999999" + "99999999999999999999999999999999999999999999999999999999999999999999999999999999" + "99999999999999999999999999999999999999999999999999999999999999999999999999999999e-308", + 4.450147717014401778e-308 // 0x1.ffffffffffffep-1022 + ); + // Slightly below half way between max-normal and infinity. + // Should round down. + TEST_DOUBLE(fullPrecision, + "1.797693134862315807937289714053034150799341327100378269361737789804449682927647" + "50946649017977587207096330286416692887910946555547851940402630657488671505820681" + "90890200070838367627385484581771153176447573027006985557136695962284291481986083" + "49364752927190741684443655107043427115596995080930428801779041744977919999999999" + "99999999999999999999999999999999999999999999999999999999999999999999999999999999" + "99999999999999999999999999999999999999999999999999999999999999999999999999999999" + "99999999999999999999999999999999999999999999999999999999999999999999999999999999" + "99999999999999999999999999999999999999999999999999999999999999999999999999999999" + "99999999999999999999999999999999999999999999999999999999999999999999999999999999" + "99999999999999999999999999999999999999999999999999999999999999999999999999999999" + "99999999999999999999999999999999999999999999999999999999999999999999999999999999" + "99999999999999999999999999999999999999999999999999999999999999999999999999999999" + "99999999999999999999999999999999999999999999999999999999999999999999999999999999" + "99999999999999999999999999999999999999999999999999999999999999999999999999999999" + "99999999999999999999999999999999999999999999999999999999999999999999999999999999" + "99999999999999999999999999999999999999999999999999999999999999999999999999999999e+308", + 1.797693134862315708e+308 // 0x1.fffffffffffffp1023 + ); + } + #undef TEST_DOUBLE } @@ -415,21 +620,23 @@ TEST(Reader, ParseNumber_NormalPrecisionError) { uint64_t bias1 = e.ToBias(); uint64_t bias2 = a.ToBias(); double ulp = static_cast(bias1 >= bias2 ? bias1 - bias2 : bias2 - bias1); - ulpMax = std::max(ulpMax, ulp); + ulpMax = (std::max)(ulpMax, ulp); ulpSum += ulp; } printf("ULP Average = %g, Max = %g \n", ulpSum / count, ulpMax); } -TEST(Reader, ParseNumber_Error) { +template +static void TestParseNumberError() { #define TEST_NUMBER_ERROR(errorCode, str, errorOffset, streamPos) \ { \ - char buffer[1001]; \ + char buffer[2048]; \ + ASSERT_LT(std::strlen(str), 2048u); \ sprintf(buffer, "%s", str); \ InsituStringStream s(buffer); \ BaseReaderHandler<> h; \ Reader reader; \ - EXPECT_FALSE(reader.Parse(s, h)); \ + EXPECT_FALSE(reader.Parse(s, h)); \ EXPECT_EQ(errorCode, reader.GetParseErrorCode());\ EXPECT_EQ(errorOffset, reader.GetErrorOffset());\ EXPECT_EQ(streamPos, s.Tell());\ @@ -442,21 +649,109 @@ TEST(Reader, ParseNumber_Error) { for (int i = 1; i < 310; i++) n1e309[i] = '0'; n1e309[310] = '\0'; - TEST_NUMBER_ERROR(kParseErrorNumberTooBig, n1e309, 0, 309); + TEST_NUMBER_ERROR(kParseErrorNumberTooBig, n1e309, 0u, 310u); } - TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1e309", 0, 5); + TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1e309", 0u, 5u); // Miss fraction part in number. - TEST_NUMBER_ERROR(kParseErrorNumberMissFraction, "1.", 2, 2); - TEST_NUMBER_ERROR(kParseErrorNumberMissFraction, "1.a", 2, 2); + TEST_NUMBER_ERROR(kParseErrorNumberMissFraction, "1.", 2u, 2u); + TEST_NUMBER_ERROR(kParseErrorNumberMissFraction, "1.a", 2u, 2u); // Miss exponent in number. - TEST_NUMBER_ERROR(kParseErrorNumberMissExponent, "1e", 2, 2); - TEST_NUMBER_ERROR(kParseErrorNumberMissExponent, "1e_", 2, 2); + TEST_NUMBER_ERROR(kParseErrorNumberMissExponent, "1e", 2u, 2u); + TEST_NUMBER_ERROR(kParseErrorNumberMissExponent, "1e_", 2u, 2u); + + // Issue 849 + TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1.8e308", 0u, 7u); + TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "5e308", 0u, 5u); + TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1e309", 0u, 5u); + TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1.0e310", 0u, 7u); + TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1.00e310", 0u, 8u); + TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "-1.8e308", 0u, 8u); + TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "-1e309", 0u, 6u); + + // Issue 1253 + TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "2e308", 0u, 5u); + + // Issue 1259 + TEST_NUMBER_ERROR(kParseErrorNumberTooBig, + "88474320368547737236837236775298547354737253685475547552933720368546854775297525" + "29337203685468547770151233720097201372368547312337203687203685423685123372036872" + "03685473724737236837236775297525854775297525472975254737236873720170151235473783" + "7236737247372368772473723683723456789012E66", 0u, 283u); + +#if 0 + // Test (length + exponent) overflow + TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1e+2147483647", 0u, 13u); + TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1e+9223372036854775807", 0u, 22u); + TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1e+10000", 0u, 8u); + TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1e+50000", 0u, 8u); +#endif + + // 9007199254740992 * 2^971 ("infinity") + TEST_NUMBER_ERROR(kParseErrorNumberTooBig, + "1.797693134862315907729305190789024733617976978942306572734300811577326758055009" + "63132708477322407536021120113879871393357658789768814416622492847430639474124377" + "76789342486548527630221960124609411945308295208500576883815068234246288147391311" + "0540827237163350510684586298239947245938479716304835356329624224137216e+308", 0u, 315u); + + // TODO: + // These tests (currently) fail in normal-precision mode + if (fullPrecision) + { + // Half way between max-normal and infinity + // Should round to infinity in nearest-even mode. + TEST_NUMBER_ERROR(kParseErrorNumberTooBig, + "1.797693134862315807937289714053034150799341327100378269361737789804449682927647" + "50946649017977587207096330286416692887910946555547851940402630657488671505820681" + "90890200070838367627385484581771153176447573027006985557136695962284291481986083" + "49364752927190741684443655107043427115596995080930428801779041744977920000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000e+308", 0u, 1125u); + // ...round up + TEST_NUMBER_ERROR(kParseErrorNumberTooBig, + "1.797693134862315807937289714053034150799341327100378269361737789804449682927647" + "50946649017977587207096330286416692887910946555547851940402630657488671505820681" + "90890200070838367627385484581771153176447573027006985557136695962284291481986083" + "49364752927190741684443655107043427115596995080930428801779041744977920000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000001e+308", 0u, 1205u); + } + + TEST_NUMBER_ERROR(kParseErrorNumberTooBig, + "10000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000001", 0u, 310u); #undef TEST_NUMBER_ERROR } +TEST(Reader, ParseNumberError_NormalPrecisionDouble) { + TestParseNumberError(); +} + +TEST(Reader, ParseNumberError_FullPrecisionDouble) { + TestParseNumberError(); +} + template struct ParseStringHandler : BaseReaderHandler > { ParseStringHandler() : str_(0), length_(0), copy_() {} @@ -636,21 +931,24 @@ TEST(Reader, ParseString_Error) { } // Invalid escape character in string. - TEST_STRING_ERROR(kParseErrorStringEscapeInvalid, "[\"\\a\"]", 2, 3); + TEST_STRING_ERROR(kParseErrorStringEscapeInvalid, "[\"\\a\"]", 2u, 3u); // Incorrect hex digit after \\u escape in string. - TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uABCG\"]", 2, 7); + TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uABCG\"]", 2u, 7u); // Quotation in \\u escape in string (Issue #288) - TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uaaa\"]", 2, 7); - TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uD800\\uFFF\"]", 2, 13); + TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uaaa\"]", 2u, 7u); + TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uD800\\uFFF\"]", 2u, 13u); // The surrogate pair in string is invalid. - TEST_STRING_ERROR(kParseErrorStringUnicodeSurrogateInvalid, "[\"\\uD800X\"]", 2, 8); - TEST_STRING_ERROR(kParseErrorStringUnicodeSurrogateInvalid, "[\"\\uD800\\uFFFF\"]", 2, 14); + TEST_STRING_ERROR(kParseErrorStringUnicodeSurrogateInvalid, "[\"\\uD800X\"]", 2u, 8u); + TEST_STRING_ERROR(kParseErrorStringUnicodeSurrogateInvalid, "[\"\\uD800\\uFFFF\"]", 2u, 14u); + + // Single low surrogate pair in string is invalid. + TEST_STRING_ERROR(kParseErrorStringUnicodeSurrogateInvalid, "[\"\\udc4d\"]", 2u, 8u); // Missing a closing quotation mark in string. - TEST_STRING_ERROR(kParseErrorStringMissQuotationMark, "[\"Test]", 7, 7); + TEST_STRING_ERROR(kParseErrorStringMissQuotationMark, "[\"Test]", 7u, 7u); // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt @@ -673,7 +971,7 @@ TEST(Reader, ParseString_Error) { char e[] = { '[', '\"', 0, ' ', '\"', ']', '\0' }; for (unsigned c = 0xC0u; c <= 0xFFu; c++) { e[2] = static_cast(c); - int streamPos; + unsigned streamPos; if (c <= 0xC1u) streamPos = 3; // 0xC0 - 0xC1 else if (c <= 0xDFu) @@ -684,7 +982,7 @@ TEST(Reader, ParseString_Error) { streamPos = 6; // 0xF0 - 0xF4 else streamPos = 3; // 0xF5 - 0xFF - TEST_STRING_ERROR(kParseErrorStringInvalidEncoding, e, 2, streamPos); + TEST_STRING_ERROR(kParseErrorStringInvalidEncoding, e, 2u, streamPos); } } @@ -725,6 +1023,8 @@ TEST(Reader, ParseString_Error) { // Malform ASCII sequence TEST_STRINGENCODING_ERROR(ASCII<>, UTF8<>, char, ARRAY('[', '\"', char(0x80u), '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(ASCII<>, UTF8<>, char, ARRAY('[', '\"', char(0x01u), '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(ASCII<>, UTF8<>, char, ARRAY('[', '\"', char(0x1Cu), '\"', ']', '\0')); #undef ARRAY #undef TEST_STRINGARRAY_ERROR @@ -765,7 +1065,7 @@ TEST(Reader, ParseArray) { TEST(Reader, ParseArray_Error) { #define TEST_ARRAY_ERROR(errorCode, str, errorOffset) \ { \ - int streamPos = errorOffset; \ + unsigned streamPos = errorOffset; \ char buffer[1001]; \ strncpy(buffer, str, 1000); \ InsituStringStream s(buffer); \ @@ -778,13 +1078,13 @@ TEST(Reader, ParseArray_Error) { } // Missing a comma or ']' after an array element. - TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1", 2); - TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1}", 2); - TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1 2]", 3); + TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1", 2u); + TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1}", 2u); + TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1 2]", 3u); // Array cannot have a trailing comma (without kParseTrailingCommasFlag); // a value must follow a comma - TEST_ARRAY_ERROR(kParseErrorValueInvalid, "[1,]", 3); + TEST_ARRAY_ERROR(kParseErrorValueInvalid, "[1,]", 3u); #undef TEST_ARRAY_ERROR } @@ -933,7 +1233,7 @@ TEST(Reader, ParseInsituIterative_MultipleRoot) { #define TEST_ERROR(errorCode, str, errorOffset) \ { \ - int streamPos = errorOffset; \ + unsigned streamPos = errorOffset; \ char buffer[1001]; \ strncpy(buffer, str, 1000); \ InsituStringStream s(buffer); \ @@ -947,48 +1247,48 @@ TEST(Reader, ParseInsituIterative_MultipleRoot) { TEST(Reader, ParseDocument_Error) { // The document is empty. - TEST_ERROR(kParseErrorDocumentEmpty, "", 0); - TEST_ERROR(kParseErrorDocumentEmpty, " ", 1); - TEST_ERROR(kParseErrorDocumentEmpty, " \n", 2); + TEST_ERROR(kParseErrorDocumentEmpty, "", 0u); + TEST_ERROR(kParseErrorDocumentEmpty, " ", 1u); + TEST_ERROR(kParseErrorDocumentEmpty, " \n", 2u); // The document root must not follow by other values. - TEST_ERROR(kParseErrorDocumentRootNotSingular, "[] 0", 3); - TEST_ERROR(kParseErrorDocumentRootNotSingular, "{} 0", 3); - TEST_ERROR(kParseErrorDocumentRootNotSingular, "null []", 5); - TEST_ERROR(kParseErrorDocumentRootNotSingular, "0 {}", 2); + TEST_ERROR(kParseErrorDocumentRootNotSingular, "[] 0", 3u); + TEST_ERROR(kParseErrorDocumentRootNotSingular, "{} 0", 3u); + TEST_ERROR(kParseErrorDocumentRootNotSingular, "null []", 5u); + TEST_ERROR(kParseErrorDocumentRootNotSingular, "0 {}", 2u); } TEST(Reader, ParseValue_Error) { // Invalid value. - TEST_ERROR(kParseErrorValueInvalid, "nulL", 3); - TEST_ERROR(kParseErrorValueInvalid, "truE", 3); - TEST_ERROR(kParseErrorValueInvalid, "falsE", 4); - TEST_ERROR(kParseErrorValueInvalid, "a]", 0); - TEST_ERROR(kParseErrorValueInvalid, ".1", 0); + TEST_ERROR(kParseErrorValueInvalid, "nulL", 3u); + TEST_ERROR(kParseErrorValueInvalid, "truE", 3u); + TEST_ERROR(kParseErrorValueInvalid, "falsE", 4u); + TEST_ERROR(kParseErrorValueInvalid, "a]", 0u); + TEST_ERROR(kParseErrorValueInvalid, ".1", 0u); } TEST(Reader, ParseObject_Error) { // Missing a name for object member. - TEST_ERROR(kParseErrorObjectMissName, "{1}", 1); - TEST_ERROR(kParseErrorObjectMissName, "{:1}", 1); - TEST_ERROR(kParseErrorObjectMissName, "{null:1}", 1); - TEST_ERROR(kParseErrorObjectMissName, "{true:1}", 1); - TEST_ERROR(kParseErrorObjectMissName, "{false:1}", 1); - TEST_ERROR(kParseErrorObjectMissName, "{1:1}", 1); - TEST_ERROR(kParseErrorObjectMissName, "{[]:1}", 1); - TEST_ERROR(kParseErrorObjectMissName, "{{}:1}", 1); - TEST_ERROR(kParseErrorObjectMissName, "{xyz:1}", 1); + TEST_ERROR(kParseErrorObjectMissName, "{1}", 1u); + TEST_ERROR(kParseErrorObjectMissName, "{:1}", 1u); + TEST_ERROR(kParseErrorObjectMissName, "{null:1}", 1u); + TEST_ERROR(kParseErrorObjectMissName, "{true:1}", 1u); + TEST_ERROR(kParseErrorObjectMissName, "{false:1}", 1u); + TEST_ERROR(kParseErrorObjectMissName, "{1:1}", 1u); + TEST_ERROR(kParseErrorObjectMissName, "{[]:1}", 1u); + TEST_ERROR(kParseErrorObjectMissName, "{{}:1}", 1u); + TEST_ERROR(kParseErrorObjectMissName, "{xyz:1}", 1u); // Missing a colon after a name of object member. - TEST_ERROR(kParseErrorObjectMissColon, "{\"a\" 1}", 5); - TEST_ERROR(kParseErrorObjectMissColon, "{\"a\",1}", 4); + TEST_ERROR(kParseErrorObjectMissColon, "{\"a\" 1}", 5u); + TEST_ERROR(kParseErrorObjectMissColon, "{\"a\",1}", 4u); // Must be a comma or '}' after an object member - TEST_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, "{\"a\":1]", 6); + TEST_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, "{\"a\":1]", 6u); // Object cannot have a trailing comma (without kParseTrailingCommasFlag); // an object member name must follow a comma - TEST_ERROR(kParseErrorObjectMissName, "{\"a\":1,}", 7); + TEST_ERROR(kParseErrorObjectMissName, "{\"a\":1,}", 7u); // This tests that MemoryStream is checking the length in Peek(). { @@ -1092,6 +1392,36 @@ private: std::istream& is_; }; +class WIStreamWrapper { +public: + typedef wchar_t Ch; + + WIStreamWrapper(std::wistream& is) : is_(is) {} + + Ch Peek() const { + unsigned c = is_.peek(); + return c == std::char_traits::eof() ? Ch('\0') : static_cast(c); + } + + Ch Take() { + unsigned c = is_.get(); + return c == std::char_traits::eof() ? Ch('\0') : static_cast(c); + } + + size_t Tell() const { return static_cast(is_.tellg()); } + + Ch* PutBegin() { assert(false); return 0; } + void Put(Ch) { assert(false); } + void Flush() { assert(false); } + size_t PutEnd(Ch*) { assert(false); return 0; } + +private: + WIStreamWrapper(const WIStreamWrapper&); + WIStreamWrapper& operator=(const WIStreamWrapper&); + + std::wistream& is_; +}; + TEST(Reader, Parse_IStreamWrapper_StringStream) { const char* json = "[1,2,3,4]"; @@ -1108,7 +1438,7 @@ TEST(Reader, Parse_IStreamWrapper_StringStream) { #define TESTERRORHANDLING(text, errorCode, offset)\ {\ - int streamPos = offset; \ + unsigned streamPos = offset; \ StringStream json(text); \ BaseReaderHandler<> handler; \ Reader reader; \ @@ -1157,22 +1487,22 @@ template > struct IterativeParsingReaderHandler { typedef typename Encoding::Ch Ch; - const static int LOG_NULL = -1; - const static int LOG_BOOL = -2; - const static int LOG_INT = -3; - const static int LOG_UINT = -4; - const static int LOG_INT64 = -5; - const static int LOG_UINT64 = -6; - const static int LOG_DOUBLE = -7; - const static int LOG_STRING = -8; - const static int LOG_STARTOBJECT = -9; - const static int LOG_KEY = -10; - const static int LOG_ENDOBJECT = -11; - const static int LOG_STARTARRAY = -12; - const static int LOG_ENDARRAY = -13; + const static uint32_t LOG_NULL = 0x10000000; + const static uint32_t LOG_BOOL = 0x20000000; + const static uint32_t LOG_INT = 0x30000000; + const static uint32_t LOG_UINT = 0x40000000; + const static uint32_t LOG_INT64 = 0x50000000; + const static uint32_t LOG_UINT64 = 0x60000000; + const static uint32_t LOG_DOUBLE = 0x70000000; + const static uint32_t LOG_STRING = 0x80000000; + const static uint32_t LOG_STARTOBJECT = 0x90000000; + const static uint32_t LOG_KEY = 0xA0000000; + const static uint32_t LOG_ENDOBJECT = 0xB0000000; + const static uint32_t LOG_STARTARRAY = 0xC0000000; + const static uint32_t LOG_ENDARRAY = 0xD0000000; const static size_t LogCapacity = 256; - int Logs[LogCapacity]; + uint32_t Logs[LogCapacity]; size_t LogCount; IterativeParsingReaderHandler() : LogCount(0) { @@ -1202,8 +1532,8 @@ struct IterativeParsingReaderHandler { bool EndObject(SizeType c) { RAPIDJSON_ASSERT(LogCount < LogCapacity); - Logs[LogCount++] = LOG_ENDOBJECT; - Logs[LogCount++] = static_cast(c); + RAPIDJSON_ASSERT((static_cast(c) & 0xF0000000) == 0); + Logs[LogCount++] = LOG_ENDOBJECT | static_cast(c); return true; } @@ -1211,8 +1541,8 @@ struct IterativeParsingReaderHandler { bool EndArray(SizeType c) { RAPIDJSON_ASSERT(LogCount < LogCapacity); - Logs[LogCount++] = LOG_ENDARRAY; - Logs[LogCount++] = static_cast(c); + RAPIDJSON_ASSERT((static_cast(c) & 0xF0000000) == 0); + Logs[LogCount++] = LOG_ENDARRAY | static_cast(c); return true; } }; @@ -1228,7 +1558,7 @@ TEST(Reader, IterativeParsing_General) { EXPECT_FALSE(r.IsError()); EXPECT_FALSE(reader.HasParseError()); - int e[] = { + uint32_t e[] = { handler.LOG_STARTARRAY, handler.LOG_INT, handler.LOG_STARTOBJECT, @@ -1236,14 +1566,14 @@ TEST(Reader, IterativeParsing_General) { handler.LOG_STARTARRAY, handler.LOG_INT, handler.LOG_INT, - handler.LOG_ENDARRAY, 2, - handler.LOG_ENDOBJECT, 1, + handler.LOG_ENDARRAY | 2, + handler.LOG_ENDOBJECT | 1, handler.LOG_NULL, handler.LOG_BOOL, handler.LOG_BOOL, handler.LOG_STRING, handler.LOG_DOUBLE, - handler.LOG_ENDARRAY, 7 + handler.LOG_ENDARRAY | 7 }; EXPECT_EQ(sizeof(e) / sizeof(int), handler.LogCount); @@ -1265,20 +1595,20 @@ TEST(Reader, IterativeParsing_Count) { EXPECT_FALSE(r.IsError()); EXPECT_FALSE(reader.HasParseError()); - int e[] = { + uint32_t e[] = { handler.LOG_STARTARRAY, handler.LOG_STARTOBJECT, - handler.LOG_ENDOBJECT, 0, + handler.LOG_ENDOBJECT | 0, handler.LOG_STARTOBJECT, handler.LOG_KEY, handler.LOG_INT, - handler.LOG_ENDOBJECT, 1, + handler.LOG_ENDOBJECT | 1, handler.LOG_STARTARRAY, handler.LOG_INT, - handler.LOG_ENDARRAY, 1, + handler.LOG_ENDARRAY | 1, handler.LOG_STARTARRAY, - handler.LOG_ENDARRAY, 0, - handler.LOG_ENDARRAY, 4 + handler.LOG_ENDARRAY | 0, + handler.LOG_ENDARRAY | 4 }; EXPECT_EQ(sizeof(e) / sizeof(int), handler.LogCount); @@ -1289,6 +1619,51 @@ TEST(Reader, IterativeParsing_Count) { } } +TEST(Reader, IterativePullParsing_General) { + { + IterativeParsingReaderHandler<> handler; + uint32_t e[] = { + handler.LOG_STARTARRAY, + handler.LOG_INT, + handler.LOG_STARTOBJECT, + handler.LOG_KEY, + handler.LOG_STARTARRAY, + handler.LOG_INT, + handler.LOG_INT, + handler.LOG_ENDARRAY | 2, + handler.LOG_ENDOBJECT | 1, + handler.LOG_NULL, + handler.LOG_BOOL, + handler.LOG_BOOL, + handler.LOG_STRING, + handler.LOG_DOUBLE, + handler.LOG_ENDARRAY | 7 + }; + + StringStream is("[1, {\"k\": [1, 2]}, null, false, true, \"string\", 1.2]"); + Reader reader; + + reader.IterativeParseInit(); + while (!reader.IterativeParseComplete()) { + size_t oldLogCount = handler.LogCount; + EXPECT_TRUE(oldLogCount < sizeof(e) / sizeof(int)) << "overrun"; + + EXPECT_TRUE(reader.IterativeParseNext(is, handler)) << "parse fail"; + EXPECT_EQ(handler.LogCount, oldLogCount + 1) << "handler should be invoked exactly once each time"; + EXPECT_EQ(e[oldLogCount], handler.Logs[oldLogCount]) << "wrong event returned"; + } + + EXPECT_FALSE(reader.HasParseError()); + EXPECT_EQ(sizeof(e) / sizeof(int), handler.LogCount) << "handler invoked wrong number of times"; + + // The handler should not be invoked when the JSON has been fully read, but it should not fail + size_t oldLogCount = handler.LogCount; + EXPECT_TRUE(reader.IterativeParseNext(is, handler)) << "parse-next past complete is allowed"; + EXPECT_EQ(handler.LogCount, oldLogCount) << "parse-next past complete should not invoke handler"; + EXPECT_FALSE(reader.HasParseError()) << "parse-next past complete should not generate parse error"; + } +} + // Test iterative parsing on kParseErrorTermination. struct HandlerTerminateAtStartObject : public IterativeParsingReaderHandler<> { bool StartObject() { return false; } @@ -1633,6 +2008,129 @@ TEST(Reader, NumbersAsStrings) { Reader reader; EXPECT_TRUE(reader.Parse(s, h)); } + { + char n1e319[321]; // '1' followed by 319 '0' + n1e319[0] = '1'; + for (int i = 1; i < 320; i++) + n1e319[i] = '0'; + n1e319[320] = '\0'; + StringStream s(n1e319); + NumbersAsStringsHandler h(n1e319); + Reader reader; + EXPECT_TRUE(reader.Parse(s, h)); + } +} + +struct NumbersAsStringsHandlerWChar_t { + bool Null() { return true; } + bool Bool(bool) { return true; } + bool Int(int) { return true; } + bool Uint(unsigned) { return true; } + bool Int64(int64_t) { return true; } + bool Uint64(uint64_t) { return true; } + bool Double(double) { return true; } + // 'str' is not null-terminated + bool RawNumber(const wchar_t* str, SizeType length, bool) { + EXPECT_TRUE(str != 0); + EXPECT_TRUE(expected_len_ == length); + EXPECT_TRUE(wcsncmp(str, expected_, length) == 0); + return true; + } + bool String(const wchar_t*, SizeType, bool) { return true; } + bool StartObject() { return true; } + bool Key(const wchar_t*, SizeType, bool) { return true; } + bool EndObject(SizeType) { return true; } + bool StartArray() { return true; } + bool EndArray(SizeType) { return true; } + + NumbersAsStringsHandlerWChar_t(const wchar_t* expected) + : expected_(expected) + , expected_len_(wcslen(expected)) {} + + const wchar_t* expected_; + size_t expected_len_; +}; + +TEST(Reader, NumbersAsStringsWChar_t) { + { + const wchar_t* json = L"{ \"pi\": 3.1416 } "; + GenericStringStream > s(json); + NumbersAsStringsHandlerWChar_t h(L"3.1416"); + GenericReader, UTF16<> > reader; + EXPECT_TRUE(reader.Parse(s, h)); + } + { + wchar_t* json = StrDup(L"{ \"pi\": 3.1416 } "); + GenericInsituStringStream > s(json); + NumbersAsStringsHandlerWChar_t h(L"3.1416"); + GenericReader, UTF16<> > reader; + EXPECT_TRUE(reader.Parse(s, h)); + free(json); + } + { + const wchar_t* json = L"{ \"gigabyte\": 1.0e9 } "; + GenericStringStream > s(json); + NumbersAsStringsHandlerWChar_t h(L"1.0e9"); + GenericReader, UTF16<> > reader; + EXPECT_TRUE(reader.Parse(s, h)); + } + { + wchar_t* json = StrDup(L"{ \"gigabyte\": 1.0e9 } "); + GenericInsituStringStream > s(json); + NumbersAsStringsHandlerWChar_t h(L"1.0e9"); + GenericReader, UTF16<> > reader; + EXPECT_TRUE(reader.Parse(s, h)); + free(json); + } + { + const wchar_t* json = L"{ \"pi\": 314.159e-2 } "; + GenericStringStream > s(json); + NumbersAsStringsHandlerWChar_t h(L"314.159e-2"); + GenericReader, UTF16<> > reader; + EXPECT_TRUE(reader.Parse(s, h)); + } + { + wchar_t* json = StrDup(L"{ \"gigabyte\": 314.159e-2 } "); + GenericInsituStringStream > s(json); + NumbersAsStringsHandlerWChar_t h(L"314.159e-2"); + GenericReader, UTF16<> > reader; + EXPECT_TRUE(reader.Parse(s, h)); + free(json); + } + { + const wchar_t* json = L"{ \"negative\": -1.54321 } "; + GenericStringStream > s(json); + NumbersAsStringsHandlerWChar_t h(L"-1.54321"); + GenericReader, UTF16<> > reader; + EXPECT_TRUE(reader.Parse(s, h)); + } + { + wchar_t* json = StrDup(L"{ \"negative\": -1.54321 } "); + GenericInsituStringStream > s(json); + NumbersAsStringsHandlerWChar_t h(L"-1.54321"); + GenericReader, UTF16<> > reader; + EXPECT_TRUE(reader.Parse(s, h)); + free(json); + } + { + const wchar_t* json = L"{ \"pi\": 314.159e-2 } "; + std::wstringstream ss(json); + WIStreamWrapper s(ss); + NumbersAsStringsHandlerWChar_t h(L"314.159e-2"); + GenericReader, UTF16<> > reader; + EXPECT_TRUE(reader.Parse(s, h)); + } + { + wchar_t n1e319[321]; // '1' followed by 319 '0' + n1e319[0] = L'1'; + for(int i = 1; i < 320; i++) + n1e319[i] = L'0'; + n1e319[320] = L'\0'; + GenericStringStream > s(n1e319); + NumbersAsStringsHandlerWChar_t h(n1e319); + GenericReader, UTF16<> > reader; + EXPECT_TRUE(reader.Parse(s, h)); + } } template @@ -1811,7 +2309,7 @@ TEST(Reader, ParseNanAndInfinity) { } #define TEST_NAN_INF_ERROR(errorCode, str, errorOffset) \ { \ - int streamPos = errorOffset; \ + unsigned streamPos = errorOffset; \ char buffer[1001]; \ strncpy(buffer, str, 1000); \ InsituStringStream s(buffer); \ @@ -1832,13 +2330,41 @@ TEST(Reader, ParseNanAndInfinity) { TEST_NAN_INF("Infinity", inf); TEST_NAN_INF("-Inf", -inf); TEST_NAN_INF("-Infinity", -inf); - TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "nan", 1); - TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "-nan", 1); - TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "NAN", 1); - TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "-Infinty", 6); + TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "NInf", 1u); + TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "NaInf", 2u); + TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "INan", 1u); + TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "InNan", 2u); + TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "nan", 1u); + TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "-nan", 1u); + TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "NAN", 1u); + TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "-Infinty", 6u); #undef TEST_NAN_INF_ERROR #undef TEST_NAN_INF } +TEST(Reader, EscapedApostrophe) { + const char json[] = " { \"foo\": \"bar\\'buzz\" } "; + + BaseReaderHandler<> h; + + { + StringStream s(json); + Reader reader; + ParseResult r = reader.Parse(s, h); + EXPECT_TRUE(reader.HasParseError()); + EXPECT_EQ(kParseErrorStringEscapeInvalid, r.Code()); + EXPECT_EQ(14u, r.Offset()); + } + + { + StringStream s(json); + Reader reader; + ParseResult r = reader.Parse(s, h); + EXPECT_FALSE(reader.HasParseError()); + EXPECT_EQ(kParseErrorNone, r.Code()); + EXPECT_EQ(0u, r.Offset()); + } +} + RAPIDJSON_DIAG_POP diff --git a/third_party/rapidjson/test/unittest/regextest.cpp b/third_party/rapidjson/test/unittest/regextest.cpp index 4fb5b222e..a288622bc 100644 --- a/third_party/rapidjson/test/unittest/regextest.cpp +++ b/third_party/rapidjson/test/unittest/regextest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -20,523 +20,569 @@ using namespace rapidjson::internal; TEST(Regex, Single) { Regex re("a"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("a")); - EXPECT_FALSE(re.Match("")); - EXPECT_FALSE(re.Match("b")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("a")); + EXPECT_FALSE(rs.Match("")); + EXPECT_FALSE(rs.Match("b")); } TEST(Regex, Concatenation) { Regex re("abc"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("abc")); - EXPECT_FALSE(re.Match("")); - EXPECT_FALSE(re.Match("a")); - EXPECT_FALSE(re.Match("b")); - EXPECT_FALSE(re.Match("ab")); - EXPECT_FALSE(re.Match("abcd")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("abc")); + EXPECT_FALSE(rs.Match("")); + EXPECT_FALSE(rs.Match("a")); + EXPECT_FALSE(rs.Match("b")); + EXPECT_FALSE(rs.Match("ab")); + EXPECT_FALSE(rs.Match("abcd")); } TEST(Regex, Alternation1) { Regex re("abab|abbb"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("abab")); - EXPECT_TRUE(re.Match("abbb")); - EXPECT_FALSE(re.Match("")); - EXPECT_FALSE(re.Match("ab")); - EXPECT_FALSE(re.Match("ababa")); - EXPECT_FALSE(re.Match("abb")); - EXPECT_FALSE(re.Match("abbbb")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("abab")); + EXPECT_TRUE(rs.Match("abbb")); + EXPECT_FALSE(rs.Match("")); + EXPECT_FALSE(rs.Match("ab")); + EXPECT_FALSE(rs.Match("ababa")); + EXPECT_FALSE(rs.Match("abb")); + EXPECT_FALSE(rs.Match("abbbb")); } TEST(Regex, Alternation2) { Regex re("a|b|c"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("a")); - EXPECT_TRUE(re.Match("b")); - EXPECT_TRUE(re.Match("c")); - EXPECT_FALSE(re.Match("")); - EXPECT_FALSE(re.Match("aa")); - EXPECT_FALSE(re.Match("ab")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("a")); + EXPECT_TRUE(rs.Match("b")); + EXPECT_TRUE(rs.Match("c")); + EXPECT_FALSE(rs.Match("")); + EXPECT_FALSE(rs.Match("aa")); + EXPECT_FALSE(rs.Match("ab")); } TEST(Regex, Parenthesis1) { Regex re("(ab)c"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("abc")); - EXPECT_FALSE(re.Match("")); - EXPECT_FALSE(re.Match("a")); - EXPECT_FALSE(re.Match("b")); - EXPECT_FALSE(re.Match("ab")); - EXPECT_FALSE(re.Match("abcd")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("abc")); + EXPECT_FALSE(rs.Match("")); + EXPECT_FALSE(rs.Match("a")); + EXPECT_FALSE(rs.Match("b")); + EXPECT_FALSE(rs.Match("ab")); + EXPECT_FALSE(rs.Match("abcd")); } TEST(Regex, Parenthesis2) { Regex re("a(bc)"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("abc")); - EXPECT_FALSE(re.Match("")); - EXPECT_FALSE(re.Match("a")); - EXPECT_FALSE(re.Match("b")); - EXPECT_FALSE(re.Match("ab")); - EXPECT_FALSE(re.Match("abcd")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("abc")); + EXPECT_FALSE(rs.Match("")); + EXPECT_FALSE(rs.Match("a")); + EXPECT_FALSE(rs.Match("b")); + EXPECT_FALSE(rs.Match("ab")); + EXPECT_FALSE(rs.Match("abcd")); } TEST(Regex, Parenthesis3) { Regex re("(a|b)(c|d)"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("ac")); - EXPECT_TRUE(re.Match("ad")); - EXPECT_TRUE(re.Match("bc")); - EXPECT_TRUE(re.Match("bd")); - EXPECT_FALSE(re.Match("")); - EXPECT_FALSE(re.Match("ab")); - EXPECT_FALSE(re.Match("cd")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("ac")); + EXPECT_TRUE(rs.Match("ad")); + EXPECT_TRUE(rs.Match("bc")); + EXPECT_TRUE(rs.Match("bd")); + EXPECT_FALSE(rs.Match("")); + EXPECT_FALSE(rs.Match("ab")); + EXPECT_FALSE(rs.Match("cd")); } TEST(Regex, ZeroOrOne1) { Regex re("a?"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("")); - EXPECT_TRUE(re.Match("a")); - EXPECT_FALSE(re.Match("aa")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("")); + EXPECT_TRUE(rs.Match("a")); + EXPECT_FALSE(rs.Match("aa")); } TEST(Regex, ZeroOrOne2) { Regex re("a?b"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("b")); - EXPECT_TRUE(re.Match("ab")); - EXPECT_FALSE(re.Match("a")); - EXPECT_FALSE(re.Match("aa")); - EXPECT_FALSE(re.Match("bb")); - EXPECT_FALSE(re.Match("ba")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("b")); + EXPECT_TRUE(rs.Match("ab")); + EXPECT_FALSE(rs.Match("a")); + EXPECT_FALSE(rs.Match("aa")); + EXPECT_FALSE(rs.Match("bb")); + EXPECT_FALSE(rs.Match("ba")); } TEST(Regex, ZeroOrOne3) { Regex re("ab?"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("a")); - EXPECT_TRUE(re.Match("ab")); - EXPECT_FALSE(re.Match("b")); - EXPECT_FALSE(re.Match("aa")); - EXPECT_FALSE(re.Match("bb")); - EXPECT_FALSE(re.Match("ba")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("a")); + EXPECT_TRUE(rs.Match("ab")); + EXPECT_FALSE(rs.Match("b")); + EXPECT_FALSE(rs.Match("aa")); + EXPECT_FALSE(rs.Match("bb")); + EXPECT_FALSE(rs.Match("ba")); } TEST(Regex, ZeroOrOne4) { Regex re("a?b?"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("")); - EXPECT_TRUE(re.Match("a")); - EXPECT_TRUE(re.Match("b")); - EXPECT_TRUE(re.Match("ab")); - EXPECT_FALSE(re.Match("aa")); - EXPECT_FALSE(re.Match("bb")); - EXPECT_FALSE(re.Match("ba")); - EXPECT_FALSE(re.Match("abc")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("")); + EXPECT_TRUE(rs.Match("a")); + EXPECT_TRUE(rs.Match("b")); + EXPECT_TRUE(rs.Match("ab")); + EXPECT_FALSE(rs.Match("aa")); + EXPECT_FALSE(rs.Match("bb")); + EXPECT_FALSE(rs.Match("ba")); + EXPECT_FALSE(rs.Match("abc")); } TEST(Regex, ZeroOrOne5) { Regex re("a(ab)?b"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("ab")); - EXPECT_TRUE(re.Match("aabb")); - EXPECT_FALSE(re.Match("aab")); - EXPECT_FALSE(re.Match("abb")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("ab")); + EXPECT_TRUE(rs.Match("aabb")); + EXPECT_FALSE(rs.Match("aab")); + EXPECT_FALSE(rs.Match("abb")); } TEST(Regex, ZeroOrMore1) { Regex re("a*"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("")); - EXPECT_TRUE(re.Match("a")); - EXPECT_TRUE(re.Match("aa")); - EXPECT_FALSE(re.Match("b")); - EXPECT_FALSE(re.Match("ab")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("")); + EXPECT_TRUE(rs.Match("a")); + EXPECT_TRUE(rs.Match("aa")); + EXPECT_FALSE(rs.Match("b")); + EXPECT_FALSE(rs.Match("ab")); } TEST(Regex, ZeroOrMore2) { Regex re("a*b"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("b")); - EXPECT_TRUE(re.Match("ab")); - EXPECT_TRUE(re.Match("aab")); - EXPECT_FALSE(re.Match("")); - EXPECT_FALSE(re.Match("bb")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("b")); + EXPECT_TRUE(rs.Match("ab")); + EXPECT_TRUE(rs.Match("aab")); + EXPECT_FALSE(rs.Match("")); + EXPECT_FALSE(rs.Match("bb")); } TEST(Regex, ZeroOrMore3) { Regex re("a*b*"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("")); - EXPECT_TRUE(re.Match("a")); - EXPECT_TRUE(re.Match("aa")); - EXPECT_TRUE(re.Match("b")); - EXPECT_TRUE(re.Match("bb")); - EXPECT_TRUE(re.Match("ab")); - EXPECT_TRUE(re.Match("aabb")); - EXPECT_FALSE(re.Match("ba")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("")); + EXPECT_TRUE(rs.Match("a")); + EXPECT_TRUE(rs.Match("aa")); + EXPECT_TRUE(rs.Match("b")); + EXPECT_TRUE(rs.Match("bb")); + EXPECT_TRUE(rs.Match("ab")); + EXPECT_TRUE(rs.Match("aabb")); + EXPECT_FALSE(rs.Match("ba")); } TEST(Regex, ZeroOrMore4) { Regex re("a(ab)*b"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("ab")); - EXPECT_TRUE(re.Match("aabb")); - EXPECT_TRUE(re.Match("aababb")); - EXPECT_FALSE(re.Match("")); - EXPECT_FALSE(re.Match("aa")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("ab")); + EXPECT_TRUE(rs.Match("aabb")); + EXPECT_TRUE(rs.Match("aababb")); + EXPECT_FALSE(rs.Match("")); + EXPECT_FALSE(rs.Match("aa")); } TEST(Regex, OneOrMore1) { Regex re("a+"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("a")); - EXPECT_TRUE(re.Match("aa")); - EXPECT_FALSE(re.Match("")); - EXPECT_FALSE(re.Match("b")); - EXPECT_FALSE(re.Match("ab")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("a")); + EXPECT_TRUE(rs.Match("aa")); + EXPECT_FALSE(rs.Match("")); + EXPECT_FALSE(rs.Match("b")); + EXPECT_FALSE(rs.Match("ab")); } TEST(Regex, OneOrMore2) { Regex re("a+b"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("ab")); - EXPECT_TRUE(re.Match("aab")); - EXPECT_FALSE(re.Match("")); - EXPECT_FALSE(re.Match("b")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("ab")); + EXPECT_TRUE(rs.Match("aab")); + EXPECT_FALSE(rs.Match("")); + EXPECT_FALSE(rs.Match("b")); } TEST(Regex, OneOrMore3) { Regex re("a+b+"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("ab")); - EXPECT_TRUE(re.Match("aab")); - EXPECT_TRUE(re.Match("abb")); - EXPECT_TRUE(re.Match("aabb")); - EXPECT_FALSE(re.Match("")); - EXPECT_FALSE(re.Match("b")); - EXPECT_FALSE(re.Match("ba")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("ab")); + EXPECT_TRUE(rs.Match("aab")); + EXPECT_TRUE(rs.Match("abb")); + EXPECT_TRUE(rs.Match("aabb")); + EXPECT_FALSE(rs.Match("")); + EXPECT_FALSE(rs.Match("b")); + EXPECT_FALSE(rs.Match("ba")); } TEST(Regex, OneOrMore4) { Regex re("a(ab)+b"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("aabb")); - EXPECT_TRUE(re.Match("aababb")); - EXPECT_FALSE(re.Match("")); - EXPECT_FALSE(re.Match("ab")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("aabb")); + EXPECT_TRUE(rs.Match("aababb")); + EXPECT_FALSE(rs.Match("")); + EXPECT_FALSE(rs.Match("ab")); } TEST(Regex, QuantifierExact1) { Regex re("ab{3}c"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("abbbc")); - EXPECT_FALSE(re.Match("ac")); - EXPECT_FALSE(re.Match("abc")); - EXPECT_FALSE(re.Match("abbc")); - EXPECT_FALSE(re.Match("abbbbc")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("abbbc")); + EXPECT_FALSE(rs.Match("ac")); + EXPECT_FALSE(rs.Match("abc")); + EXPECT_FALSE(rs.Match("abbc")); + EXPECT_FALSE(rs.Match("abbbbc")); } TEST(Regex, QuantifierExact2) { Regex re("a(bc){3}d"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("abcbcbcd")); - EXPECT_FALSE(re.Match("ad")); - EXPECT_FALSE(re.Match("abcd")); - EXPECT_FALSE(re.Match("abcbcd")); - EXPECT_FALSE(re.Match("abcbcbcbcd")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("abcbcbcd")); + EXPECT_FALSE(rs.Match("ad")); + EXPECT_FALSE(rs.Match("abcd")); + EXPECT_FALSE(rs.Match("abcbcd")); + EXPECT_FALSE(rs.Match("abcbcbcbcd")); } TEST(Regex, QuantifierExact3) { Regex re("a(b|c){3}d"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("abbbd")); - EXPECT_TRUE(re.Match("acccd")); - EXPECT_TRUE(re.Match("abcbd")); - EXPECT_FALSE(re.Match("ad")); - EXPECT_FALSE(re.Match("abbd")); - EXPECT_FALSE(re.Match("accccd")); - EXPECT_FALSE(re.Match("abbbbd")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("abbbd")); + EXPECT_TRUE(rs.Match("acccd")); + EXPECT_TRUE(rs.Match("abcbd")); + EXPECT_FALSE(rs.Match("ad")); + EXPECT_FALSE(rs.Match("abbd")); + EXPECT_FALSE(rs.Match("accccd")); + EXPECT_FALSE(rs.Match("abbbbd")); } TEST(Regex, QuantifierMin1) { Regex re("ab{3,}c"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("abbbc")); - EXPECT_TRUE(re.Match("abbbbc")); - EXPECT_TRUE(re.Match("abbbbbc")); - EXPECT_FALSE(re.Match("ac")); - EXPECT_FALSE(re.Match("abc")); - EXPECT_FALSE(re.Match("abbc")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("abbbc")); + EXPECT_TRUE(rs.Match("abbbbc")); + EXPECT_TRUE(rs.Match("abbbbbc")); + EXPECT_FALSE(rs.Match("ac")); + EXPECT_FALSE(rs.Match("abc")); + EXPECT_FALSE(rs.Match("abbc")); } TEST(Regex, QuantifierMin2) { Regex re("a(bc){3,}d"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("abcbcbcd")); - EXPECT_TRUE(re.Match("abcbcbcbcd")); - EXPECT_FALSE(re.Match("ad")); - EXPECT_FALSE(re.Match("abcd")); - EXPECT_FALSE(re.Match("abcbcd")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("abcbcbcd")); + EXPECT_TRUE(rs.Match("abcbcbcbcd")); + EXPECT_FALSE(rs.Match("ad")); + EXPECT_FALSE(rs.Match("abcd")); + EXPECT_FALSE(rs.Match("abcbcd")); } TEST(Regex, QuantifierMin3) { Regex re("a(b|c){3,}d"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("abbbd")); - EXPECT_TRUE(re.Match("acccd")); - EXPECT_TRUE(re.Match("abcbd")); - EXPECT_TRUE(re.Match("accccd")); - EXPECT_TRUE(re.Match("abbbbd")); - EXPECT_FALSE(re.Match("ad")); - EXPECT_FALSE(re.Match("abbd")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("abbbd")); + EXPECT_TRUE(rs.Match("acccd")); + EXPECT_TRUE(rs.Match("abcbd")); + EXPECT_TRUE(rs.Match("accccd")); + EXPECT_TRUE(rs.Match("abbbbd")); + EXPECT_FALSE(rs.Match("ad")); + EXPECT_FALSE(rs.Match("abbd")); } TEST(Regex, QuantifierMinMax1) { Regex re("ab{3,5}c"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("abbbc")); - EXPECT_TRUE(re.Match("abbbbc")); - EXPECT_TRUE(re.Match("abbbbbc")); - EXPECT_FALSE(re.Match("ac")); - EXPECT_FALSE(re.Match("abc")); - EXPECT_FALSE(re.Match("abbc")); - EXPECT_FALSE(re.Match("abbbbbbc")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("abbbc")); + EXPECT_TRUE(rs.Match("abbbbc")); + EXPECT_TRUE(rs.Match("abbbbbc")); + EXPECT_FALSE(rs.Match("ac")); + EXPECT_FALSE(rs.Match("abc")); + EXPECT_FALSE(rs.Match("abbc")); + EXPECT_FALSE(rs.Match("abbbbbbc")); } TEST(Regex, QuantifierMinMax2) { Regex re("a(bc){3,5}d"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("abcbcbcd")); - EXPECT_TRUE(re.Match("abcbcbcbcd")); - EXPECT_TRUE(re.Match("abcbcbcbcbcd")); - EXPECT_FALSE(re.Match("ad")); - EXPECT_FALSE(re.Match("abcd")); - EXPECT_FALSE(re.Match("abcbcd")); - EXPECT_FALSE(re.Match("abcbcbcbcbcbcd")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("abcbcbcd")); + EXPECT_TRUE(rs.Match("abcbcbcbcd")); + EXPECT_TRUE(rs.Match("abcbcbcbcbcd")); + EXPECT_FALSE(rs.Match("ad")); + EXPECT_FALSE(rs.Match("abcd")); + EXPECT_FALSE(rs.Match("abcbcd")); + EXPECT_FALSE(rs.Match("abcbcbcbcbcbcd")); } TEST(Regex, QuantifierMinMax3) { Regex re("a(b|c){3,5}d"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("abbbd")); - EXPECT_TRUE(re.Match("acccd")); - EXPECT_TRUE(re.Match("abcbd")); - EXPECT_TRUE(re.Match("accccd")); - EXPECT_TRUE(re.Match("abbbbd")); - EXPECT_TRUE(re.Match("acccccd")); - EXPECT_TRUE(re.Match("abbbbbd")); - EXPECT_FALSE(re.Match("ad")); - EXPECT_FALSE(re.Match("abbd")); - EXPECT_FALSE(re.Match("accccccd")); - EXPECT_FALSE(re.Match("abbbbbbd")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("abbbd")); + EXPECT_TRUE(rs.Match("acccd")); + EXPECT_TRUE(rs.Match("abcbd")); + EXPECT_TRUE(rs.Match("accccd")); + EXPECT_TRUE(rs.Match("abbbbd")); + EXPECT_TRUE(rs.Match("acccccd")); + EXPECT_TRUE(rs.Match("abbbbbd")); + EXPECT_FALSE(rs.Match("ad")); + EXPECT_FALSE(rs.Match("abbd")); + EXPECT_FALSE(rs.Match("accccccd")); + EXPECT_FALSE(rs.Match("abbbbbbd")); } // Issue538 TEST(Regex, QuantifierMinMax4) { Regex re("a(b|c){0,3}d"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("ad")); - EXPECT_TRUE(re.Match("abd")); - EXPECT_TRUE(re.Match("acd")); - EXPECT_TRUE(re.Match("abbd")); - EXPECT_TRUE(re.Match("accd")); - EXPECT_TRUE(re.Match("abcd")); - EXPECT_TRUE(re.Match("abbbd")); - EXPECT_TRUE(re.Match("acccd")); - EXPECT_FALSE(re.Match("abbbbd")); - EXPECT_FALSE(re.Match("add")); - EXPECT_FALSE(re.Match("accccd")); - EXPECT_FALSE(re.Match("abcbcd")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("ad")); + EXPECT_TRUE(rs.Match("abd")); + EXPECT_TRUE(rs.Match("acd")); + EXPECT_TRUE(rs.Match("abbd")); + EXPECT_TRUE(rs.Match("accd")); + EXPECT_TRUE(rs.Match("abcd")); + EXPECT_TRUE(rs.Match("abbbd")); + EXPECT_TRUE(rs.Match("acccd")); + EXPECT_FALSE(rs.Match("abbbbd")); + EXPECT_FALSE(rs.Match("add")); + EXPECT_FALSE(rs.Match("accccd")); + EXPECT_FALSE(rs.Match("abcbcd")); } // Issue538 TEST(Regex, QuantifierMinMax5) { Regex re("a(b|c){0,}d"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("ad")); - EXPECT_TRUE(re.Match("abd")); - EXPECT_TRUE(re.Match("acd")); - EXPECT_TRUE(re.Match("abbd")); - EXPECT_TRUE(re.Match("accd")); - EXPECT_TRUE(re.Match("abcd")); - EXPECT_TRUE(re.Match("abbbd")); - EXPECT_TRUE(re.Match("acccd")); - EXPECT_TRUE(re.Match("abbbbd")); - EXPECT_TRUE(re.Match("accccd")); - EXPECT_TRUE(re.Match("abcbcd")); - EXPECT_FALSE(re.Match("add")); - EXPECT_FALSE(re.Match("aad")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("ad")); + EXPECT_TRUE(rs.Match("abd")); + EXPECT_TRUE(rs.Match("acd")); + EXPECT_TRUE(rs.Match("abbd")); + EXPECT_TRUE(rs.Match("accd")); + EXPECT_TRUE(rs.Match("abcd")); + EXPECT_TRUE(rs.Match("abbbd")); + EXPECT_TRUE(rs.Match("acccd")); + EXPECT_TRUE(rs.Match("abbbbd")); + EXPECT_TRUE(rs.Match("accccd")); + EXPECT_TRUE(rs.Match("abcbcd")); + EXPECT_FALSE(rs.Match("add")); + EXPECT_FALSE(rs.Match("aad")); } -#define EURO "\xE2\x82\xAC" // "\xE2\x82\xAC" is UTF-8 sequence of Euro sign U+20AC +#define EURO "\xE2\x82\xAC" // "\xE2\x82\xAC" is UTF-8 rsquence of Euro sign U+20AC TEST(Regex, Unicode) { Regex re("a" EURO "+b"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("a" EURO "b")); - EXPECT_TRUE(re.Match("a" EURO EURO "b")); - EXPECT_FALSE(re.Match("a?b")); - EXPECT_FALSE(re.Match("a" EURO "\xAC" "b")); // unaware of UTF-8 will match + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("a" EURO "b")); + EXPECT_TRUE(rs.Match("a" EURO EURO "b")); + EXPECT_FALSE(rs.Match("a?b")); + EXPECT_FALSE(rs.Match("a" EURO "\xAC" "b")); // unaware of UTF-8 will match } TEST(Regex, AnyCharacter) { Regex re("."); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("a")); - EXPECT_TRUE(re.Match("b")); - EXPECT_TRUE(re.Match(EURO)); - EXPECT_FALSE(re.Match("")); - EXPECT_FALSE(re.Match("aa")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("a")); + EXPECT_TRUE(rs.Match("b")); + EXPECT_TRUE(rs.Match(EURO)); + EXPECT_FALSE(rs.Match("")); + EXPECT_FALSE(rs.Match("aa")); } TEST(Regex, CharacterRange1) { Regex re("[abc]"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("a")); - EXPECT_TRUE(re.Match("b")); - EXPECT_TRUE(re.Match("c")); - EXPECT_FALSE(re.Match("")); - EXPECT_FALSE(re.Match("`")); - EXPECT_FALSE(re.Match("d")); - EXPECT_FALSE(re.Match("aa")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("a")); + EXPECT_TRUE(rs.Match("b")); + EXPECT_TRUE(rs.Match("c")); + EXPECT_FALSE(rs.Match("")); + EXPECT_FALSE(rs.Match("`")); + EXPECT_FALSE(rs.Match("d")); + EXPECT_FALSE(rs.Match("aa")); } TEST(Regex, CharacterRange2) { Regex re("[^abc]"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("`")); - EXPECT_TRUE(re.Match("d")); - EXPECT_FALSE(re.Match("a")); - EXPECT_FALSE(re.Match("b")); - EXPECT_FALSE(re.Match("c")); - EXPECT_FALSE(re.Match("")); - EXPECT_FALSE(re.Match("aa")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("`")); + EXPECT_TRUE(rs.Match("d")); + EXPECT_FALSE(rs.Match("a")); + EXPECT_FALSE(rs.Match("b")); + EXPECT_FALSE(rs.Match("c")); + EXPECT_FALSE(rs.Match("")); + EXPECT_FALSE(rs.Match("aa")); } TEST(Regex, CharacterRange3) { Regex re("[a-c]"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("a")); - EXPECT_TRUE(re.Match("b")); - EXPECT_TRUE(re.Match("c")); - EXPECT_FALSE(re.Match("")); - EXPECT_FALSE(re.Match("`")); - EXPECT_FALSE(re.Match("d")); - EXPECT_FALSE(re.Match("aa")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("a")); + EXPECT_TRUE(rs.Match("b")); + EXPECT_TRUE(rs.Match("c")); + EXPECT_FALSE(rs.Match("")); + EXPECT_FALSE(rs.Match("`")); + EXPECT_FALSE(rs.Match("d")); + EXPECT_FALSE(rs.Match("aa")); } TEST(Regex, CharacterRange4) { Regex re("[^a-c]"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("`")); - EXPECT_TRUE(re.Match("d")); - EXPECT_FALSE(re.Match("a")); - EXPECT_FALSE(re.Match("b")); - EXPECT_FALSE(re.Match("c")); - EXPECT_FALSE(re.Match("")); - EXPECT_FALSE(re.Match("aa")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("`")); + EXPECT_TRUE(rs.Match("d")); + EXPECT_FALSE(rs.Match("a")); + EXPECT_FALSE(rs.Match("b")); + EXPECT_FALSE(rs.Match("c")); + EXPECT_FALSE(rs.Match("")); + EXPECT_FALSE(rs.Match("aa")); } TEST(Regex, CharacterRange5) { Regex re("[-]"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("-")); - EXPECT_FALSE(re.Match("")); - EXPECT_FALSE(re.Match("a")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("-")); + EXPECT_FALSE(rs.Match("")); + EXPECT_FALSE(rs.Match("a")); } TEST(Regex, CharacterRange6) { Regex re("[a-]"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("a")); - EXPECT_TRUE(re.Match("-")); - EXPECT_FALSE(re.Match("")); - EXPECT_FALSE(re.Match("`")); - EXPECT_FALSE(re.Match("b")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("a")); + EXPECT_TRUE(rs.Match("-")); + EXPECT_FALSE(rs.Match("")); + EXPECT_FALSE(rs.Match("`")); + EXPECT_FALSE(rs.Match("b")); } TEST(Regex, CharacterRange7) { Regex re("[-a]"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("a")); - EXPECT_TRUE(re.Match("-")); - EXPECT_FALSE(re.Match("")); - EXPECT_FALSE(re.Match("`")); - EXPECT_FALSE(re.Match("b")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("a")); + EXPECT_TRUE(rs.Match("-")); + EXPECT_FALSE(rs.Match("")); + EXPECT_FALSE(rs.Match("`")); + EXPECT_FALSE(rs.Match("b")); } TEST(Regex, CharacterRange8) { Regex re("[a-zA-Z0-9]*"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("Milo")); - EXPECT_TRUE(re.Match("MT19937")); - EXPECT_TRUE(re.Match("43")); - EXPECT_FALSE(re.Match("a_b")); - EXPECT_FALSE(re.Match("!")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("Milo")); + EXPECT_TRUE(rs.Match("MT19937")); + EXPECT_TRUE(rs.Match("43")); + EXPECT_FALSE(rs.Match("a_b")); + EXPECT_FALSE(rs.Match("!")); } TEST(Regex, Search) { Regex re("abc"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Search("abc")); - EXPECT_TRUE(re.Search("_abc")); - EXPECT_TRUE(re.Search("abc_")); - EXPECT_TRUE(re.Search("_abc_")); - EXPECT_TRUE(re.Search("__abc__")); - EXPECT_TRUE(re.Search("abcabc")); - EXPECT_FALSE(re.Search("a")); - EXPECT_FALSE(re.Search("ab")); - EXPECT_FALSE(re.Search("bc")); - EXPECT_FALSE(re.Search("cba")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Search("abc")); + EXPECT_TRUE(rs.Search("_abc")); + EXPECT_TRUE(rs.Search("abc_")); + EXPECT_TRUE(rs.Search("_abc_")); + EXPECT_TRUE(rs.Search("__abc__")); + EXPECT_TRUE(rs.Search("abcabc")); + EXPECT_FALSE(rs.Search("a")); + EXPECT_FALSE(rs.Search("ab")); + EXPECT_FALSE(rs.Search("bc")); + EXPECT_FALSE(rs.Search("cba")); } TEST(Regex, Search_BeginAnchor) { Regex re("^abc"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Search("abc")); - EXPECT_TRUE(re.Search("abc_")); - EXPECT_TRUE(re.Search("abcabc")); - EXPECT_FALSE(re.Search("_abc")); - EXPECT_FALSE(re.Search("_abc_")); - EXPECT_FALSE(re.Search("a")); - EXPECT_FALSE(re.Search("ab")); - EXPECT_FALSE(re.Search("bc")); - EXPECT_FALSE(re.Search("cba")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Search("abc")); + EXPECT_TRUE(rs.Search("abc_")); + EXPECT_TRUE(rs.Search("abcabc")); + EXPECT_FALSE(rs.Search("_abc")); + EXPECT_FALSE(rs.Search("_abc_")); + EXPECT_FALSE(rs.Search("a")); + EXPECT_FALSE(rs.Search("ab")); + EXPECT_FALSE(rs.Search("bc")); + EXPECT_FALSE(rs.Search("cba")); } TEST(Regex, Search_EndAnchor) { Regex re("abc$"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Search("abc")); - EXPECT_TRUE(re.Search("_abc")); - EXPECT_TRUE(re.Search("abcabc")); - EXPECT_FALSE(re.Search("abc_")); - EXPECT_FALSE(re.Search("_abc_")); - EXPECT_FALSE(re.Search("a")); - EXPECT_FALSE(re.Search("ab")); - EXPECT_FALSE(re.Search("bc")); - EXPECT_FALSE(re.Search("cba")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Search("abc")); + EXPECT_TRUE(rs.Search("_abc")); + EXPECT_TRUE(rs.Search("abcabc")); + EXPECT_FALSE(rs.Search("abc_")); + EXPECT_FALSE(rs.Search("_abc_")); + EXPECT_FALSE(rs.Search("a")); + EXPECT_FALSE(rs.Search("ab")); + EXPECT_FALSE(rs.Search("bc")); + EXPECT_FALSE(rs.Search("cba")); } TEST(Regex, Search_BothAnchor) { Regex re("^abc$"); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Search("abc")); - EXPECT_FALSE(re.Search("")); - EXPECT_FALSE(re.Search("a")); - EXPECT_FALSE(re.Search("b")); - EXPECT_FALSE(re.Search("ab")); - EXPECT_FALSE(re.Search("abcd")); + RegexSearch rs(re); + EXPECT_TRUE(rs.Search("abc")); + EXPECT_FALSE(rs.Search("")); + EXPECT_FALSE(rs.Search("a")); + EXPECT_FALSE(rs.Search("b")); + EXPECT_FALSE(rs.Search("ab")); + EXPECT_FALSE(rs.Search("abcd")); } TEST(Regex, Escape) { const char* s = "\\^\\$\\|\\(\\)\\?\\*\\+\\.\\[\\]\\{\\}\\\\\\f\\n\\r\\t\\v[\\b][\\[][\\]]"; Regex re(s); ASSERT_TRUE(re.IsValid()); - EXPECT_TRUE(re.Match("^$|()?*+.[]{}\\\x0C\n\r\t\x0B\b[]")); - EXPECT_FALSE(re.Match(s)); // Not escaping + RegexSearch rs(re); + EXPECT_TRUE(rs.Match("^$|()?*+.[]{}\\\x0C\n\r\t\x0B\b[]")); + EXPECT_FALSE(rs.Match(s)); // Not escaping } TEST(Regex, Invalid) { @@ -549,6 +595,7 @@ TEST(Regex, Invalid) { TEST_INVALID(""); TEST_INVALID("a|"); TEST_INVALID("()"); + TEST_INVALID("("); TEST_INVALID(")"); TEST_INVALID("(a))"); TEST_INVALID("(a|)"); diff --git a/third_party/rapidjson/test/unittest/schematest.cpp b/third_party/rapidjson/test/unittest/schematest.cpp index d75b1e593..dbc467ea3 100644 --- a/third_party/rapidjson/test/unittest/schematest.cpp +++ b/third_party/rapidjson/test/unittest/schematest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -12,14 +12,22 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. +#define RAPIDJSON_SCHEMA_VERBOSE 0 +#define RAPIDJSON_HAS_STDSTRING 1 + #include "unittest.h" #include "rapidjson/schema.h" #include "rapidjson/stringbuffer.h" #include "rapidjson/writer.h" +#include "rapidjson/error/error.h" +#include "rapidjson/error/en.h" #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(variadic-macros) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4822) // local class member function does not have a body #endif using namespace rapidjson; @@ -91,6 +99,9 @@ TEST(SchemaValidator, Hasher) { TEST_HASHER("{\"a\":1}", "{\"a\":1}", true); TEST_HASHER("{\"a\":1}", "{\"b\":1}", false); TEST_HASHER("{\"a\":1}", "{\"a\":2}", false); + TEST_HASHER("{\"a\":\"a\"}", "{\"b\":\"b\"}", false); // Key equals value hashing + TEST_HASHER("{\"a\":\"a\", \"b\":\"b\"}", "{\"c\":\"c\", \"d\":\"d\"}", false); + TEST_HASHER("{\"a\":\"a\"}", "{\"b\":\"b\", \"c\":\"c\"}", false); TEST_HASHER("{\"a\":1, \"b\":2}", "{\"b\":2, \"a\":1}", true); // Member order insensitive TEST_HASHER("{}", "null", false); TEST_HASHER("{}", "false", false); @@ -104,6 +115,13 @@ TEST(SchemaValidator, Hasher) { #define VALIDATE(schema, json, expected) \ {\ + VALIDATE_(schema, json, expected, true) \ +} + +#define VALIDATE_(schema, json, expected, expected2) \ +{\ + EXPECT_TRUE(expected2 == schema.GetError().ObjectEmpty());\ + EXPECT_TRUE(schema.IsSupportedSpecification());\ SchemaValidator validator(schema);\ Document d;\ /*printf("\n%s\n", json);*/\ @@ -111,27 +129,50 @@ TEST(SchemaValidator, Hasher) { EXPECT_FALSE(d.HasParseError());\ EXPECT_TRUE(expected == d.Accept(validator));\ EXPECT_TRUE(expected == validator.IsValid());\ + ValidateErrorCode code = validator.GetInvalidSchemaCode();\ + if (expected) {\ + EXPECT_TRUE(code == kValidateErrorNone);\ + EXPECT_TRUE(validator.GetInvalidSchemaKeyword() == 0);\ + }\ if ((expected) && !validator.IsValid()) {\ StringBuffer sb;\ validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);\ printf("Invalid schema: %s\n", sb.GetString());\ printf("Invalid keyword: %s\n", validator.GetInvalidSchemaKeyword());\ + printf("Invalid code: %d\n", code);\ + printf("Invalid message: %s\n", GetValidateError_En(code));\ sb.Clear();\ validator.GetInvalidDocumentPointer().StringifyUriFragment(sb);\ printf("Invalid document: %s\n", sb.GetString());\ + sb.Clear();\ + Writer w(sb);\ + validator.GetError().Accept(w);\ + printf("Validation error: %s\n", sb.GetString());\ }\ } -#define INVALIDATE(schema, json, invalidSchemaPointer, invalidSchemaKeyword, invalidDocumentPointer) \ +#define INVALIDATE(schema, json, invalidSchemaPointer, invalidSchemaKeyword, invalidDocumentPointer, error) \ {\ - SchemaValidator validator(schema);\ + INVALIDATE_(schema, json, invalidSchemaPointer, invalidSchemaKeyword, invalidDocumentPointer, error, kValidateDefaultFlags, SchemaValidator, Pointer) \ +} + +#define INVALIDATE_(schema, json, invalidSchemaPointer, invalidSchemaKeyword, invalidDocumentPointer, error, \ + flags, SchemaValidatorType, PointerType) \ +{\ + EXPECT_TRUE(schema.GetError().ObjectEmpty());\ + EXPECT_TRUE(schema.IsSupportedSpecification());\ + SchemaValidatorType validator(schema);\ + validator.SetValidateFlags(flags);\ Document d;\ /*printf("\n%s\n", json);*/\ d.Parse(json);\ EXPECT_FALSE(d.HasParseError());\ - EXPECT_FALSE(d.Accept(validator));\ + d.Accept(validator);\ EXPECT_FALSE(validator.IsValid());\ - if (validator.GetInvalidSchemaPointer() != Pointer(invalidSchemaPointer)) {\ + ValidateErrorCode code = validator.GetInvalidSchemaCode();\ + ASSERT_TRUE(code != kValidateErrorNone);\ + ASSERT_TRUE(strcmp(GetValidateError_En(code), "Unknown error.") != 0);\ + if (validator.GetInvalidSchemaPointer() != PointerType(invalidSchemaPointer)) {\ StringBuffer sb;\ validator.GetInvalidSchemaPointer().Stringify(sb);\ printf("GetInvalidSchemaPointer() Expected: %s Actual: %s\n", invalidSchemaPointer, sb.GetString());\ @@ -142,12 +183,35 @@ TEST(SchemaValidator, Hasher) { printf("GetInvalidSchemaKeyword() Expected: %s Actual %s\n", invalidSchemaKeyword, validator.GetInvalidSchemaKeyword());\ ADD_FAILURE();\ }\ - if (validator.GetInvalidDocumentPointer() != Pointer(invalidDocumentPointer)) {\ + if (validator.GetInvalidDocumentPointer() != PointerType(invalidDocumentPointer)) {\ StringBuffer sb;\ validator.GetInvalidDocumentPointer().Stringify(sb);\ printf("GetInvalidDocumentPointer() Expected: %s Actual: %s\n", invalidDocumentPointer, sb.GetString());\ ADD_FAILURE();\ }\ + Document e;\ + e.Parse(error);\ + if (validator.GetError() != e) {\ + StringBuffer sb;\ + Writer w(sb);\ + validator.GetError().Accept(w);\ + printf("GetError() Expected: %s Actual: %s\n", error, sb.GetString());\ + ADD_FAILURE();\ + }\ +} + +// Use for checking whether a compiled schema document contains errors +#define SCHEMAERROR(schema, error) \ +{\ + Document e;\ + e.Parse(error);\ + if (schema.GetError() != e) {\ + StringBuffer sb;\ + Writer w(sb);\ + schema.GetError().Accept(w);\ + printf("GetError() Expected: %s Actual: %s\n", error, sb.GetString());\ + ADD_FAILURE();\ + }\ } TEST(SchemaValidator, Typeless) { @@ -167,7 +231,12 @@ TEST(SchemaValidator, MultiType) { VALIDATE(s, "42", true); VALIDATE(s, "\"Life, the universe, and everything\"", true); - INVALIDATE(s, "[\"Life\", \"the universe\", \"and everything\"]", "", "type", ""); + INVALIDATE(s, "[\"Life\", \"the universe\", \"and everything\"]", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"string\", \"number\"], \"actual\": \"array\"" + "}}"); } TEST(SchemaValidator, Enum_Typed) { @@ -176,10 +245,11 @@ TEST(SchemaValidator, Enum_Typed) { SchemaDocument s(sd); VALIDATE(s, "\"red\"", true); - INVALIDATE(s, "\"blue\"", "", "enum", ""); + INVALIDATE(s, "\"blue\"", "", "enum", "", + "{ \"enum\": { \"errorCode\": 19, \"instanceRef\": \"#\", \"schemaRef\": \"#\" }}"); } -TEST(SchemaValidator, Enum_Typless) { +TEST(SchemaValidator, Enum_Typeless) { Document sd; sd.Parse("{ \"enum\": [\"red\", \"amber\", \"green\", null, 42] }"); SchemaDocument s(sd); @@ -187,7 +257,8 @@ TEST(SchemaValidator, Enum_Typless) { VALIDATE(s, "\"red\"", true); VALIDATE(s, "null", true); VALIDATE(s, "42", true); - INVALIDATE(s, "0", "", "enum", ""); + INVALIDATE(s, "0", "", "enum", "", + "{ \"enum\": { \"errorCode\": 19, \"instanceRef\": \"#\", \"schemaRef\": \"#\" }}"); } TEST(SchemaValidator, Enum_InvalidType) { @@ -196,7 +267,12 @@ TEST(SchemaValidator, Enum_InvalidType) { SchemaDocument s(sd); VALIDATE(s, "\"red\"", true); - INVALIDATE(s, "null", "", "type", ""); + INVALIDATE(s, "null", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"string\"], \"actual\": \"null\"" + "}}"); } TEST(SchemaValidator, AllOf) { @@ -206,7 +282,14 @@ TEST(SchemaValidator, AllOf) { SchemaDocument s(sd); VALIDATE(s, "\"ok\"", true); - INVALIDATE(s, "\"too long\"", "", "allOf", ""); + INVALIDATE(s, "\"too long\"", "", "allOf", "", + "{ \"allOf\": {" + " \"errors\": [" + " {}," + " {\"maxLength\": {\"errorCode\": 6, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/1\", \"expected\": 5, \"actual\": \"too long\"}}" + " ]," + " \"errorCode\": 23, \"instanceRef\": \"#\", \"schemaRef\": \"#\"" + "}}"); } { Document sd; @@ -214,7 +297,14 @@ TEST(SchemaValidator, AllOf) { SchemaDocument s(sd); VALIDATE(s, "\"No way\"", false); - INVALIDATE(s, "-1", "", "allOf", ""); + INVALIDATE(s, "-1", "", "allOf", "", + "{ \"allOf\": {" + " \"errors\": [" + " {\"type\": { \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/0\", \"errorCode\": 20, \"expected\": [\"string\"], \"actual\": \"integer\"}}," + " {}" + " ]," + " \"errorCode\": 23, \"instanceRef\": \"#\", \"schemaRef\": \"#\"" + "}}"); } } @@ -225,7 +315,23 @@ TEST(SchemaValidator, AnyOf) { VALIDATE(s, "\"Yes\"", true); VALIDATE(s, "42", true); - INVALIDATE(s, "{ \"Not a\": \"string or number\" }", "", "anyOf", ""); + INVALIDATE(s, "{ \"Not a\": \"string or number\" }", "", "anyOf", "", + "{ \"anyOf\": {" + " \"errorCode\": 24," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\", " + " \"errors\": [" + " { \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#/anyOf/0\"," + " \"expected\": [\"string\"], \"actual\": \"object\"" + " }}," + " { \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#/anyOf/1\"," + " \"expected\": [\"number\"], \"actual\": \"object\"" + " }}" + " ]" + "}}"); } TEST(SchemaValidator, OneOf) { @@ -235,8 +341,25 @@ TEST(SchemaValidator, OneOf) { VALIDATE(s, "10", true); VALIDATE(s, "9", true); - INVALIDATE(s, "2", "", "oneOf", ""); - INVALIDATE(s, "15", "", "oneOf", ""); + INVALIDATE(s, "2", "", "oneOf", "", + "{ \"oneOf\": {" + " \"errorCode\": 21," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"errors\": [" + " { \"multipleOf\": {" + " \"errorCode\": 1," + " \"instanceRef\": \"#\", \"schemaRef\": \"#/oneOf/0\"," + " \"expected\": 5, \"actual\": 2" + " }}," + " { \"multipleOf\": {" + " \"errorCode\": 1," + " \"instanceRef\": \"#\", \"schemaRef\": \"#/oneOf/1\"," + " \"expected\": 3, \"actual\": 2" + " }}" + " ]" + "}}"); + INVALIDATE(s, "15", "", "oneOf", "", + "{ \"oneOf\": { \"errorCode\": 22, \"instanceRef\": \"#\", \"schemaRef\": \"#\", \"matches\": [0,1]}}"); } TEST(SchemaValidator, Not) { @@ -246,7 +369,8 @@ TEST(SchemaValidator, Not) { VALIDATE(s, "42", true); VALIDATE(s, "{ \"key\": \"value\" }", true); - INVALIDATE(s, "\"I am a string\"", "", "not", ""); + INVALIDATE(s, "\"I am a string\"", "", "not", "", + "{ \"not\": { \"errorCode\": 25, \"instanceRef\": \"#\", \"schemaRef\": \"#\" }}"); } TEST(SchemaValidator, Ref) { @@ -310,7 +434,14 @@ TEST(SchemaValidator, Ref_AllOf) { "}"); SchemaDocument s(sd); - INVALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\"} }", "/properties/shipping_address", "allOf", "/shipping_address"); + INVALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\"} }", "/properties/shipping_address", "allOf", "/shipping_address", + "{ \"allOf\": {" + " \"errors\": [" + " {}," + " {\"required\": {\"errorCode\": 15, \"instanceRef\": \"#/shipping_address\", \"schemaRef\": \"#/properties/shipping_address/allOf/1\", \"missing\": [\"type\"]}}" + " ]," + " \"errorCode\":23,\"instanceRef\":\"#/shipping_address\",\"schemaRef\":\"#/properties/shipping_address\"" + "}}"); VALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\", \"type\": \"business\"} }", true); } @@ -320,11 +451,36 @@ TEST(SchemaValidator, String) { SchemaDocument s(sd); VALIDATE(s, "\"I'm a string\"", true); - INVALIDATE(s, "42", "", "type", ""); - INVALIDATE(s, "2147483648", "", "type", ""); // 2^31 can only be fit in unsigned - INVALIDATE(s, "-2147483649", "", "type", ""); // -2^31 - 1 can only be fit in int64_t - INVALIDATE(s, "4294967296", "", "type", ""); // 2^32 can only be fit in int64_t - INVALIDATE(s, "3.1415926", "", "type", ""); + INVALIDATE(s, "42", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"string\"], \"actual\": \"integer\"" + "}}"); + INVALIDATE(s, "2147483648", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"string\"], \"actual\": \"integer\"" + "}}"); // 2^31 can only be fit in unsigned + INVALIDATE(s, "-2147483649", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"string\"], \"actual\": \"integer\"" + "}}"); // -2^31 - 1 can only be fit in int64_t + INVALIDATE(s, "4294967296", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"string\"], \"actual\": \"integer\"" + "}}"); // 2^32 can only be fit in int64_t + INVALIDATE(s, "3.1415926", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"string\"], \"actual\": \"number\"" + "}}"); } TEST(SchemaValidator, String_LengthRange) { @@ -332,10 +488,20 @@ TEST(SchemaValidator, String_LengthRange) { sd.Parse("{\"type\":\"string\",\"minLength\":2,\"maxLength\":3}"); SchemaDocument s(sd); - INVALIDATE(s, "\"A\"", "", "minLength", ""); + INVALIDATE(s, "\"A\"", "", "minLength", "", + "{ \"minLength\": {" + " \"errorCode\": 7," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 2, \"actual\": \"A\"" + "}}"); VALIDATE(s, "\"AB\"", true); VALIDATE(s, "\"ABC\"", true); - INVALIDATE(s, "\"ABCD\"", "", "maxLength", ""); + INVALIDATE(s, "\"ABCD\"", "", "maxLength", "", + "{ \"maxLength\": {" + " \"errorCode\": 6," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 3, \"actual\": \"ABCD\"" + "}}"); } #if RAPIDJSON_SCHEMA_HAS_REGEX @@ -346,18 +512,29 @@ TEST(SchemaValidator, String_Pattern) { VALIDATE(s, "\"555-1212\"", true); VALIDATE(s, "\"(888)555-1212\"", true); - INVALIDATE(s, "\"(888)555-1212 ext. 532\"", "", "pattern", ""); - INVALIDATE(s, "\"(800)FLOWERS\"", "", "pattern", ""); + INVALIDATE(s, "\"(888)555-1212 ext. 532\"", "", "pattern", "", + "{ \"pattern\": {" + " \"errorCode\": 8," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"actual\": \"(888)555-1212 ext. 532\"" + "}}"); + INVALIDATE(s, "\"(800)FLOWERS\"", "", "pattern", "", + "{ \"pattern\": {" + " \"errorCode\": 8," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"actual\": \"(800)FLOWERS\"" + "}}"); } TEST(SchemaValidator, String_Pattern_Invalid) { Document sd; - sd.Parse("{\"type\":\"string\",\"pattern\":\"a{0}\"}"); // TODO: report regex is invalid somehow + sd.Parse("{\"type\":\"string\",\"pattern\":\"a{0}\"}"); SchemaDocument s(sd); + SCHEMAERROR(s, "{\"RegexInvalid\":{\"errorCode\":9,\"instanceRef\":\"#/pattern\",\"value\":\"a{0}\"}}"); - VALIDATE(s, "\"\"", true); - VALIDATE(s, "\"a\"", true); - VALIDATE(s, "\"aa\"", true); + VALIDATE_(s, "\"\"", true, false); + VALIDATE_(s, "\"a\"", true, false); + VALIDATE_(s, "\"aa\"", true, false); } #endif @@ -372,8 +549,18 @@ TEST(SchemaValidator, Integer) { VALIDATE(s, "-2147483649", true); // -2^31 - 1 can only be fit in int64_t VALIDATE(s, "2147483648", true); // 2^31 can only be fit in unsigned VALIDATE(s, "4294967296", true); // 2^32 can only be fit in int64_t - INVALIDATE(s, "3.1415926", "", "type", ""); - INVALIDATE(s, "\"42\"", "", "type", ""); + INVALIDATE(s, "3.1415926", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"integer\"], \"actual\": \"number\"" + "}}"); + INVALIDATE(s, "\"42\"", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"integer\"], \"actual\": \"string\"" + "}}"); } TEST(SchemaValidator, Integer_Range) { @@ -381,12 +568,27 @@ TEST(SchemaValidator, Integer_Range) { sd.Parse("{\"type\":\"integer\",\"minimum\":0,\"maximum\":100,\"exclusiveMaximum\":true}"); SchemaDocument s(sd); - INVALIDATE(s, "-1", "", "minimum", ""); + INVALIDATE(s, "-1", "", "minimum", "", + "{ \"minimum\": {" + " \"errorCode\": 4," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 0, \"actual\": -1" + "}}"); VALIDATE(s, "0", true); VALIDATE(s, "10", true); VALIDATE(s, "99", true); - INVALIDATE(s, "100", "", "maximum", ""); - INVALIDATE(s, "101", "", "maximum", ""); + INVALIDATE(s, "100", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 3," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 100, \"exclusiveMaximum\": true, \"actual\": 100" + "}}"); + INVALIDATE(s, "101", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 3," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 100, \"exclusiveMaximum\": true, \"actual\": 101" + "}}"); } TEST(SchemaValidator, Integer_Range64Boundary) { @@ -394,7 +596,12 @@ TEST(SchemaValidator, Integer_Range64Boundary) { sd.Parse("{\"type\":\"integer\",\"minimum\":-9223372036854775807,\"maximum\":9223372036854775806}"); SchemaDocument s(sd); - INVALIDATE(s, "-9223372036854775808", "", "minimum", ""); + INVALIDATE(s, "-9223372036854775808", "", "minimum", "", + "{ \"minimum\": {" + " \"errorCode\": 4," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": -9223372036854775807, \"actual\": -9223372036854775808" + "}}"); VALIDATE(s, "-9223372036854775807", true); VALIDATE(s, "-2147483648", true); // int min VALIDATE(s, "0", true); @@ -402,8 +609,18 @@ TEST(SchemaValidator, Integer_Range64Boundary) { VALIDATE(s, "2147483648", true); // unsigned first VALIDATE(s, "4294967295", true); // unsigned max VALIDATE(s, "9223372036854775806", true); - INVALIDATE(s, "9223372036854775807", "", "maximum", ""); - INVALIDATE(s, "18446744073709551615", "", "maximum", ""); // uint64_t max + INVALIDATE(s, "9223372036854775807", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 2," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 9223372036854775806, \"actual\": 9223372036854775807" + "}}"); + INVALIDATE(s, "18446744073709551615", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 2," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 9223372036854775806, \"actual\": 18446744073709551615" + "}}"); // uint64_t max } TEST(SchemaValidator, Integer_RangeU64Boundary) { @@ -411,16 +628,56 @@ TEST(SchemaValidator, Integer_RangeU64Boundary) { sd.Parse("{\"type\":\"integer\",\"minimum\":9223372036854775808,\"maximum\":18446744073709551614}"); SchemaDocument s(sd); - INVALIDATE(s, "-9223372036854775808", "", "minimum", ""); - INVALIDATE(s, "9223372036854775807", "", "minimum", ""); - INVALIDATE(s, "-2147483648", "", "minimum", ""); // int min - INVALIDATE(s, "0", "", "minimum", ""); - INVALIDATE(s, "2147483647", "", "minimum", ""); // int max - INVALIDATE(s, "2147483648", "", "minimum", ""); // unsigned first - INVALIDATE(s, "4294967295", "", "minimum", ""); // unsigned max + INVALIDATE(s, "-9223372036854775808", "", "minimum", "", + "{ \"minimum\": {" + " \"errorCode\": 4," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 9223372036854775808, \"actual\": -9223372036854775808" + "}}"); + INVALIDATE(s, "9223372036854775807", "", "minimum", "", + "{ \"minimum\": {" + " \"errorCode\": 4," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 9223372036854775808, \"actual\": 9223372036854775807" + "}}"); + INVALIDATE(s, "-2147483648", "", "minimum", "", + "{ \"minimum\": {" + " \"errorCode\": 4," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 9223372036854775808, \"actual\": -2147483648" + "}}"); // int min + INVALIDATE(s, "0", "", "minimum", "", + "{ \"minimum\": {" + " \"errorCode\": 4," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 9223372036854775808, \"actual\": 0" + "}}"); + INVALIDATE(s, "2147483647", "", "minimum", "", + "{ \"minimum\": {" + " \"errorCode\": 4," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 9223372036854775808, \"actual\": 2147483647" + "}}"); // int max + INVALIDATE(s, "2147483648", "", "minimum", "", + "{ \"minimum\": {" + " \"errorCode\": 4," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 9223372036854775808, \"actual\": 2147483648" + "}}"); // unsigned first + INVALIDATE(s, "4294967295", "", "minimum", "", + "{ \"minimum\": {" + " \"errorCode\": 4," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 9223372036854775808, \"actual\": 4294967295" + "}}"); // unsigned max VALIDATE(s, "9223372036854775808", true); VALIDATE(s, "18446744073709551614", true); - INVALIDATE(s, "18446744073709551615", "", "maximum", ""); + INVALIDATE(s, "18446744073709551615", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 2," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 18446744073709551614, \"actual\": 18446744073709551615" + "}}"); } TEST(SchemaValidator, Integer_Range64BoundaryExclusive) { @@ -428,10 +685,22 @@ TEST(SchemaValidator, Integer_Range64BoundaryExclusive) { sd.Parse("{\"type\":\"integer\",\"minimum\":-9223372036854775808,\"maximum\":18446744073709551615,\"exclusiveMinimum\":true,\"exclusiveMaximum\":true}"); SchemaDocument s(sd); - INVALIDATE(s, "-9223372036854775808", "", "minimum", ""); + INVALIDATE(s, "-9223372036854775808", "", "minimum", "", + "{ \"minimum\": {" + " \"errorCode\": 5," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": -9223372036854775808, \"exclusiveMinimum\": true, " + " \"actual\": -9223372036854775808" + "}}"); VALIDATE(s, "-9223372036854775807", true); VALIDATE(s, "18446744073709551614", true); - INVALIDATE(s, "18446744073709551615", "", "maximum", ""); + INVALIDATE(s, "18446744073709551615", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 3," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 18446744073709551615, \"exclusiveMaximum\": true, " + " \"actual\": 18446744073709551615" + "}}"); } TEST(SchemaValidator, Integer_MultipleOf) { @@ -443,8 +712,18 @@ TEST(SchemaValidator, Integer_MultipleOf) { VALIDATE(s, "10", true); VALIDATE(s, "-10", true); VALIDATE(s, "20", true); - INVALIDATE(s, "23", "", "multipleOf", ""); - INVALIDATE(s, "-23", "", "multipleOf", ""); + INVALIDATE(s, "23", "", "multipleOf", "", + "{ \"multipleOf\": {" + " \"errorCode\": 1," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 10, \"actual\": 23" + "}}"); + INVALIDATE(s, "-23", "", "multipleOf", "", + "{ \"multipleOf\": {" + " \"errorCode\": 1," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 10, \"actual\": -23" + "}}"); } TEST(SchemaValidator, Integer_MultipleOf64Boundary) { @@ -454,7 +733,12 @@ TEST(SchemaValidator, Integer_MultipleOf64Boundary) { VALIDATE(s, "0", true); VALIDATE(s, "18446744073709551615", true); - INVALIDATE(s, "18446744073709551614", "", "multipleOf", ""); + INVALIDATE(s, "18446744073709551614", "", "multipleOf", "", + "{ \"multipleOf\": {" + " \"errorCode\": 1," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 18446744073709551615, \"actual\": 18446744073709551614" + "}}"); } TEST(SchemaValidator, Number_Range) { @@ -462,15 +746,35 @@ TEST(SchemaValidator, Number_Range) { sd.Parse("{\"type\":\"number\",\"minimum\":0,\"maximum\":100,\"exclusiveMaximum\":true}"); SchemaDocument s(sd); - INVALIDATE(s, "-1", "", "minimum", ""); + INVALIDATE(s, "-1", "", "minimum", "", + "{ \"minimum\": {" + " \"errorCode\": 4," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 0, \"actual\": -1" + "}}"); VALIDATE(s, "0", true); VALIDATE(s, "0.1", true); VALIDATE(s, "10", true); VALIDATE(s, "99", true); VALIDATE(s, "99.9", true); - INVALIDATE(s, "100", "", "maximum", ""); - INVALIDATE(s, "100.0", "", "maximum", ""); - INVALIDATE(s, "101.5", "", "maximum", ""); + INVALIDATE(s, "100", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 3," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 100, \"exclusiveMaximum\": true, \"actual\": 100" + "}}"); + INVALIDATE(s, "100.0", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 3," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 100, \"exclusiveMaximum\": true, \"actual\": 100.0" + "}}"); + INVALIDATE(s, "101.5", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 3," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 100, \"exclusiveMaximum\": true, \"actual\": 101.5" + "}}"); } TEST(SchemaValidator, Number_RangeInt) { @@ -478,19 +782,74 @@ TEST(SchemaValidator, Number_RangeInt) { sd.Parse("{\"type\":\"number\",\"minimum\":-100,\"maximum\":-1,\"exclusiveMaximum\":true}"); SchemaDocument s(sd); - INVALIDATE(s, "-101", "", "minimum", ""); - INVALIDATE(s, "-100.1", "", "minimum", ""); + INVALIDATE(s, "-101", "", "minimum", "", + "{ \"minimum\": {" + " \"errorCode\": 4," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": -100, \"actual\": -101" + "}}"); + INVALIDATE(s, "-100.1", "", "minimum", "", + "{ \"minimum\": {" + " \"errorCode\": 4," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": -100, \"actual\": -100.1" + "}}"); VALIDATE(s, "-100", true); VALIDATE(s, "-2", true); - INVALIDATE(s, "-1", "", "maximum", ""); - INVALIDATE(s, "-0.9", "", "maximum", ""); - INVALIDATE(s, "0", "", "maximum", ""); - INVALIDATE(s, "2147483647", "", "maximum", ""); // int max - INVALIDATE(s, "2147483648", "", "maximum", ""); // unsigned first - INVALIDATE(s, "4294967295", "", "maximum", ""); // unsigned max - INVALIDATE(s, "9223372036854775808", "", "maximum", ""); - INVALIDATE(s, "18446744073709551614", "", "maximum", ""); - INVALIDATE(s, "18446744073709551615", "", "maximum", ""); + INVALIDATE(s, "-1", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 3," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": -1, \"exclusiveMaximum\": true, \"actual\": -1" + "}}"); + INVALIDATE(s, "-0.9", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 3," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": -1, \"exclusiveMaximum\": true, \"actual\": -0.9" + "}}"); + INVALIDATE(s, "0", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 3," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": -1, \"exclusiveMaximum\": true, \"actual\": 0" + "}}"); + INVALIDATE(s, "2147483647", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 3," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": -1, \"exclusiveMaximum\": true, \"actual\": 2147483647" + "}}"); // int max + INVALIDATE(s, "2147483648", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 3," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": -1, \"exclusiveMaximum\": true, \"actual\": 2147483648" + "}}"); // unsigned first + INVALIDATE(s, "4294967295", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 3," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": -1, \"exclusiveMaximum\": true, \"actual\": 4294967295" + "}}"); // unsigned max + INVALIDATE(s, "9223372036854775808", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 3," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": -1, \"exclusiveMaximum\": true, \"actual\": 9223372036854775808" + "}}"); + INVALIDATE(s, "18446744073709551614", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 3," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": -1, \"exclusiveMaximum\": true, \"actual\": 18446744073709551614" + "}}"); + INVALIDATE(s, "18446744073709551615", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 3," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": -1, \"exclusiveMaximum\": true, \"actual\": 18446744073709551615" + "}}"); } TEST(SchemaValidator, Number_RangeDouble) { @@ -498,23 +857,88 @@ TEST(SchemaValidator, Number_RangeDouble) { sd.Parse("{\"type\":\"number\",\"minimum\":0.1,\"maximum\":100.1,\"exclusiveMaximum\":true}"); SchemaDocument s(sd); - INVALIDATE(s, "-9223372036854775808", "", "minimum", ""); - INVALIDATE(s, "-2147483648", "", "minimum", ""); // int min - INVALIDATE(s, "-1", "", "minimum", ""); + INVALIDATE(s, "-9223372036854775808", "", "minimum", "", + "{ \"minimum\": {" + " \"errorCode\": 4," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 0.1, \"actual\": -9223372036854775808" + "}}"); + INVALIDATE(s, "-2147483648", "", "minimum", "", + "{ \"minimum\": {" + " \"errorCode\": 4," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 0.1, \"actual\": -2147483648" + "}}"); // int min + INVALIDATE(s, "-1", "", "minimum", "", + "{ \"minimum\": {" + " \"errorCode\": 4," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 0.1, \"actual\": -1" + "}}"); VALIDATE(s, "0.1", true); VALIDATE(s, "10", true); VALIDATE(s, "99", true); VALIDATE(s, "100", true); - INVALIDATE(s, "101", "", "maximum", ""); - INVALIDATE(s, "101.5", "", "maximum", ""); - INVALIDATE(s, "18446744073709551614", "", "maximum", ""); - INVALIDATE(s, "18446744073709551615", "", "maximum", ""); - INVALIDATE(s, "2147483647", "", "maximum", ""); // int max - INVALIDATE(s, "2147483648", "", "maximum", ""); // unsigned first - INVALIDATE(s, "4294967295", "", "maximum", ""); // unsigned max - INVALIDATE(s, "9223372036854775808", "", "maximum", ""); - INVALIDATE(s, "18446744073709551614", "", "maximum", ""); - INVALIDATE(s, "18446744073709551615", "", "maximum", ""); + INVALIDATE(s, "101", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 3," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 100.1, \"exclusiveMaximum\": true, \"actual\": 101" + "}}"); + INVALIDATE(s, "101.5", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 3," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 100.1, \"exclusiveMaximum\": true, \"actual\": 101.5" + "}}"); + INVALIDATE(s, "18446744073709551614", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 3," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 100.1, \"exclusiveMaximum\": true, \"actual\": 18446744073709551614" + "}}"); + INVALIDATE(s, "18446744073709551615", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 3," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 100.1, \"exclusiveMaximum\": true, \"actual\": 18446744073709551615" + "}}"); + INVALIDATE(s, "2147483647", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 3," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 100.1, \"exclusiveMaximum\": true, \"actual\": 2147483647" + "}}"); // int max + INVALIDATE(s, "2147483648", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 3," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 100.1, \"exclusiveMaximum\": true, \"actual\": 2147483648" + "}}"); // unsigned first + INVALIDATE(s, "4294967295", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 3," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 100.1, \"exclusiveMaximum\": true, \"actual\": 4294967295" + "}}"); // unsigned max + INVALIDATE(s, "9223372036854775808", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 3," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 100.1, \"exclusiveMaximum\": true, \"actual\": 9223372036854775808" + "}}"); + INVALIDATE(s, "18446744073709551614", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 3," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 100.1, \"exclusiveMaximum\": true, \"actual\": 18446744073709551614" + "}}"); + INVALIDATE(s, "18446744073709551615", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 3," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 100.1, \"exclusiveMaximum\": true, \"actual\": 18446744073709551615" + "}}"); } TEST(SchemaValidator, Number_RangeDoubleU64Boundary) { @@ -522,15 +946,50 @@ TEST(SchemaValidator, Number_RangeDoubleU64Boundary) { sd.Parse("{\"type\":\"number\",\"minimum\":9223372036854775808.0,\"maximum\":18446744073709550000.0}"); SchemaDocument s(sd); - INVALIDATE(s, "-9223372036854775808", "", "minimum", ""); - INVALIDATE(s, "-2147483648", "", "minimum", ""); // int min - INVALIDATE(s, "0", "", "minimum", ""); - INVALIDATE(s, "2147483647", "", "minimum", ""); // int max - INVALIDATE(s, "2147483648", "", "minimum", ""); // unsigned first - INVALIDATE(s, "4294967295", "", "minimum", ""); // unsigned max + INVALIDATE(s, "-9223372036854775808", "", "minimum", "", + "{ \"minimum\": {" + " \"errorCode\": 4," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 9223372036854775808.0, \"actual\": -9223372036854775808" + "}}"); + INVALIDATE(s, "-2147483648", "", "minimum", "", + "{ \"minimum\": {" + " \"errorCode\": 4," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 9223372036854775808.0, \"actual\": -2147483648" + "}}"); // int min + INVALIDATE(s, "0", "", "minimum", "", + "{ \"minimum\": {" + " \"errorCode\": 4," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 9223372036854775808.0, \"actual\": 0" + "}}"); + INVALIDATE(s, "2147483647", "", "minimum", "", + "{ \"minimum\": {" + " \"errorCode\": 4," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 9223372036854775808.0, \"actual\": 2147483647" + "}}"); // int max + INVALIDATE(s, "2147483648", "", "minimum", "", + "{ \"minimum\": {" + " \"errorCode\": 4," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 9223372036854775808.0, \"actual\": 2147483648" + "}}"); // unsigned first + INVALIDATE(s, "4294967295", "", "minimum", "", + "{ \"minimum\": {" + " \"errorCode\": 4," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 9223372036854775808.0, \"actual\": 4294967295" + "}}"); // unsigned max VALIDATE(s, "9223372036854775808", true); VALIDATE(s, "18446744073709540000", true); - INVALIDATE(s, "18446744073709551615", "", "maximum", ""); + INVALIDATE(s, "18446744073709551615", "", "maximum", "", + "{ \"maximum\": {" + " \"errorCode\": 2," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 18446744073709550000.0, \"actual\": 18446744073709551615" + "}}"); } TEST(SchemaValidator, Number_MultipleOf) { @@ -542,13 +1001,38 @@ TEST(SchemaValidator, Number_MultipleOf) { VALIDATE(s, "10", true); VALIDATE(s, "-10", true); VALIDATE(s, "20", true); - INVALIDATE(s, "23", "", "multipleOf", ""); - INVALIDATE(s, "-2147483648", "", "multipleOf", ""); // int min + INVALIDATE(s, "23", "", "multipleOf", "", + "{ \"multipleOf\": {" + " \"errorCode\": 1," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 10.0, \"actual\": 23" + "}}"); + INVALIDATE(s, "-2147483648", "", "multipleOf", "", + "{ \"multipleOf\": {" + " \"errorCode\": 1," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 10.0, \"actual\": -2147483648" + "}}"); // int min VALIDATE(s, "-2147483640", true); - INVALIDATE(s, "2147483647", "", "multipleOf", ""); // int max - INVALIDATE(s, "2147483648", "", "multipleOf", ""); // unsigned first + INVALIDATE(s, "2147483647", "", "multipleOf", "", + "{ \"multipleOf\": {" + " \"errorCode\": 1," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 10.0, \"actual\": 2147483647" + "}}"); // int max + INVALIDATE(s, "2147483648", "", "multipleOf", "", + "{ \"multipleOf\": {" + " \"errorCode\": 1," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 10.0, \"actual\": 2147483648" + "}}"); // unsigned first VALIDATE(s, "2147483650", true); - INVALIDATE(s, "4294967295", "", "multipleOf", ""); // unsigned max + INVALIDATE(s, "4294967295", "", "multipleOf", "", + "{ \"multipleOf\": {" + " \"errorCode\": 1," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 10.0, \"actual\": 4294967295" + "}}"); // unsigned max VALIDATE(s, "4294967300", true); } @@ -559,7 +1043,12 @@ TEST(SchemaValidator, Number_MultipleOfOne) { VALIDATE(s, "42", true); VALIDATE(s, "42.0", true); - INVALIDATE(s, "3.1415926", "", "multipleOf", ""); + INVALIDATE(s, "3.1415926", "", "multipleOf", "", + "{ \"multipleOf\": {" + " \"errorCode\": 1," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 1, \"actual\": 3.1415926" + "}}"); } TEST(SchemaValidator, Object) { @@ -569,8 +1058,18 @@ TEST(SchemaValidator, Object) { VALIDATE(s, "{\"key\":\"value\",\"another_key\":\"another_value\"}", true); VALIDATE(s, "{\"Sun\":1.9891e30,\"Jupiter\":1.8986e27,\"Saturn\":5.6846e26,\"Neptune\":10.243e25,\"Uranus\":8.6810e25,\"Earth\":5.9736e24,\"Venus\":4.8685e24,\"Mars\":6.4185e23,\"Mercury\":3.3022e23,\"Moon\":7.349e22,\"Pluto\":1.25e22}", true); - INVALIDATE(s, "[\"An\", \"array\", \"not\", \"an\", \"object\"]", "", "type", ""); - INVALIDATE(s, "\"Not an object\"", "", "type", ""); + INVALIDATE(s, "[\"An\", \"array\", \"not\", \"an\", \"object\"]", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"object\"], \"actual\": \"array\"" + "}}"); + INVALIDATE(s, "\"Not an object\"", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"object\"], \"actual\": \"string\"" + "}}"); } TEST(SchemaValidator, Object_Properties) { @@ -588,7 +1087,19 @@ TEST(SchemaValidator, Object_Properties) { SchemaDocument s(sd); VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", true); - INVALIDATE(s, "{ \"number\": \"1600\", \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", "/properties/number", "type", "/number"); + INVALIDATE(s, "{ \"number\": \"1600\", \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", "/properties/number", "type", "/number", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/number\", \"schemaRef\": \"#/properties/number\"," + " \"expected\": [\"number\"], \"actual\": \"string\"" + "}}"); + INVALIDATE(s, "{ \"number\": \"One\", \"street_name\": \"Microsoft\", \"street_type\": \"Way\" }", + "/properties/number", "type", "/number", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/number\", \"schemaRef\": \"#/properties/number\"," + " \"expected\": [\"number\"], \"actual\": \"string\"" + "}}"); // fail fast VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\" }", true); VALIDATE(s, "{}", true); VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"direction\": \"NW\" }", true); @@ -612,7 +1123,12 @@ TEST(SchemaValidator, Object_AdditionalPropertiesBoolean) { SchemaDocument s(sd); VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", true); - INVALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"direction\": \"NW\" }", "", "additionalProperties", "/direction"); + INVALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"direction\": \"NW\" }", "", "additionalProperties", "/direction", + "{ \"additionalProperties\": {" + " \"errorCode\": 16," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"disallowed\": \"direction\"" + "}}"); } TEST(SchemaValidator, Object_AdditionalPropertiesObject) { @@ -633,7 +1149,12 @@ TEST(SchemaValidator, Object_AdditionalPropertiesObject) { VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", true); VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"direction\": \"NW\" }", true); - INVALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"office_number\": 201 }", "/additionalProperties", "type", "/office_number"); + INVALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"office_number\": 201 }", "/additionalProperties", "type", "/office_number", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/office_number\", \"schemaRef\": \"#/additionalProperties\"," + " \"expected\": [\"string\"], \"actual\": \"integer\"" + "}}"); } TEST(SchemaValidator, Object_Required) { @@ -653,20 +1174,75 @@ TEST(SchemaValidator, Object_Required) { VALIDATE(s, "{ \"name\": \"William Shakespeare\", \"email\" : \"bill@stratford-upon-avon.co.uk\" }", true); VALIDATE(s, "{ \"name\": \"William Shakespeare\", \"email\" : \"bill@stratford-upon-avon.co.uk\", \"address\" : \"Henley Street, Stratford-upon-Avon, Warwickshire, England\", \"authorship\" : \"in question\"}", true); - INVALIDATE(s, "{ \"name\": \"William Shakespeare\", \"address\" : \"Henley Street, Stratford-upon-Avon, Warwickshire, England\" }", "", "required", ""); + INVALIDATE(s, "{ \"name\": \"William Shakespeare\", \"address\" : \"Henley Street, Stratford-upon-Avon, Warwickshire, England\" }", "", "required", "", + "{ \"required\": {" + " \"errorCode\": 15," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"missing\": [\"email\"]" + "}}"); + INVALIDATE(s, "{}", "", "required", "", + "{ \"required\": {" + " \"errorCode\": 15," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"missing\": [\"name\", \"email\"]" + "}}"); } +TEST(SchemaValidator, Object_Required_PassWithDefault) { + Document sd; + sd.Parse( + "{" + " \"type\": \"object\"," + " \"properties\" : {" + " \"name\": { \"type\": \"string\", \"default\": \"William Shakespeare\" }," + " \"email\" : { \"type\": \"string\", \"default\": \"\" }," + " \"address\" : { \"type\": \"string\" }," + " \"telephone\" : { \"type\": \"string\" }" + " }," + " \"required\":[\"name\", \"email\"]" + "}"); + SchemaDocument s(sd); + + VALIDATE(s, "{ \"email\" : \"bill@stratford-upon-avon.co.uk\", \"address\" : \"Henley Street, Stratford-upon-Avon, Warwickshire, England\", \"authorship\" : \"in question\"}", true); + INVALIDATE(s, "{ \"name\": \"William Shakespeare\", \"address\" : \"Henley Street, Stratford-upon-Avon, Warwickshire, England\" }", "", "required", "", + "{ \"required\": {" + " \"errorCode\": 15," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"missing\": [\"email\"]" + "}}"); + INVALIDATE(s, "{}", "", "required", "", + "{ \"required\": {" + " \"errorCode\": 15," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"missing\": [\"email\"]" + "}}"); +} TEST(SchemaValidator, Object_PropertiesRange) { Document sd; sd.Parse("{\"type\":\"object\", \"minProperties\":2, \"maxProperties\":3}"); SchemaDocument s(sd); - INVALIDATE(s, "{}", "", "minProperties", ""); - INVALIDATE(s, "{\"a\":0}", "", "minProperties", ""); + INVALIDATE(s, "{}", "", "minProperties", "", + "{ \"minProperties\": {" + " \"errorCode\": 14," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 2, \"actual\": 0" + "}}"); + INVALIDATE(s, "{\"a\":0}", "", "minProperties", "", + "{ \"minProperties\": {" + " \"errorCode\": 14," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 2, \"actual\": 1" + "}}"); VALIDATE(s, "{\"a\":0,\"b\":1}", true); VALIDATE(s, "{\"a\":0,\"b\":1,\"c\":2}", true); - INVALIDATE(s, "{\"a\":0,\"b\":1,\"c\":2,\"d\":3}", "", "maxProperties", ""); + INVALIDATE(s, "{\"a\":0,\"b\":1,\"c\":2,\"d\":3}", "", "maxProperties", "", + "{ \"maxProperties\": {" + " \"errorCode\": 13," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\", " + " \"expected\": 3, \"actual\": 4" + "}}"); } TEST(SchemaValidator, Object_PropertyDependencies) { @@ -677,19 +1253,32 @@ TEST(SchemaValidator, Object_PropertyDependencies) { " \"properties\": {" " \"name\": { \"type\": \"string\" }," " \"credit_card\": { \"type\": \"number\" }," + " \"cvv_code\": { \"type\": \"number\" }," " \"billing_address\": { \"type\": \"string\" }" " }," " \"required\": [\"name\"]," " \"dependencies\": {" - " \"credit_card\": [\"billing_address\"]" + " \"credit_card\": [\"cvv_code\", \"billing_address\"]" " }" "}"); SchemaDocument s(sd); - VALIDATE(s, "{ \"name\": \"John Doe\", \"credit_card\": 5555555555555555, \"billing_address\": \"555 Debtor's Lane\" }", true); - INVALIDATE(s, "{ \"name\": \"John Doe\", \"credit_card\": 5555555555555555 }", "", "dependencies", ""); + VALIDATE(s, "{ \"name\": \"John Doe\", \"credit_card\": 5555555555555555, \"cvv_code\": 777, " + "\"billing_address\": \"555 Debtor's Lane\" }", true); + INVALIDATE(s, "{ \"name\": \"John Doe\", \"credit_card\": 5555555555555555 }", "", "dependencies", "", + "{ \"dependencies\": {" + " \"errorCode\": 18," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"errors\": {" + " \"credit_card\": {" + " \"required\": {" + " \"errorCode\": 15," + " \"instanceRef\": \"#\", \"schemaRef\": \"#/dependencies/credit_card\"," + " \"missing\": [\"cvv_code\", \"billing_address\"]" + " } } }" + "}}"); VALIDATE(s, "{ \"name\": \"John Doe\"}", true); - VALIDATE(s, "{ \"name\": \"John Doe\", \"billing_address\": \"555 Debtor's Lane\" }", true); + VALIDATE(s, "{ \"name\": \"John Doe\", \"cvv_code\": 777, \"billing_address\": \"555 Debtor's Lane\" }", true); } TEST(SchemaValidator, Object_SchemaDependencies) { @@ -714,7 +1303,18 @@ TEST(SchemaValidator, Object_SchemaDependencies) { SchemaDocument s(sd); VALIDATE(s, "{\"name\": \"John Doe\", \"credit_card\" : 5555555555555555,\"billing_address\" : \"555 Debtor's Lane\"}", true); - INVALIDATE(s, "{\"name\": \"John Doe\", \"credit_card\" : 5555555555555555 }", "", "dependencies", ""); + INVALIDATE(s, "{\"name\": \"John Doe\", \"credit_card\" : 5555555555555555 }", "", "dependencies", "", + "{ \"dependencies\": {" + " \"errorCode\": 18," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"errors\": {" + " \"credit_card\": {" + " \"required\": {" + " \"errorCode\": 15," + " \"instanceRef\": \"#\", \"schemaRef\": \"#/dependencies/credit_card\"," + " \"missing\": [\"billing_address\"]" + " } } }" + "}}"); VALIDATE(s, "{\"name\": \"John Doe\", \"billing_address\" : \"555 Debtor's Lane\"}", true); } @@ -733,12 +1333,85 @@ TEST(SchemaValidator, Object_PatternProperties) { VALIDATE(s, "{ \"S_25\": \"This is a string\" }", true); VALIDATE(s, "{ \"I_0\": 42 }", true); - INVALIDATE(s, "{ \"S_0\": 42 }", "", "patternProperties", "/S_0"); - INVALIDATE(s, "{ \"I_42\": \"This is a string\" }", "", "patternProperties", "/I_42"); + INVALIDATE(s, "{ \"S_0\": 42 }", "", "patternProperties", "/S_0", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/S_0\", \"schemaRef\": \"#/patternProperties/%5ES_\"," + " \"expected\": [\"string\"], \"actual\": \"integer\"" + "}}"); + INVALIDATE(s, "{ \"I_42\": \"This is a string\" }", "", "patternProperties", "/I_42", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/I_42\", \"schemaRef\": \"#/patternProperties/%5EI_\"," + " \"expected\": [\"integer\"], \"actual\": \"string\"" + "}}"); VALIDATE(s, "{ \"keyword\": \"value\" }", true); } -TEST(SchemaValidator, Object_PatternProperties_AdditionalProperties) { +TEST(SchemaValidator, Object_PatternProperties_ErrorConflict) { + Document sd; + sd.Parse( + "{" + " \"type\": \"object\"," + " \"patternProperties\": {" + " \"^I_\": { \"multipleOf\": 5 }," + " \"30$\": { \"multipleOf\": 6 }" + " }" + "}"); + SchemaDocument s(sd); + + VALIDATE(s, "{ \"I_30\": 30 }", true); + INVALIDATE(s, "{ \"I_30\": 7 }", "", "patternProperties", "/I_30", + "{ \"multipleOf\": [" + " {" + " \"errorCode\": 1," + " \"instanceRef\": \"#/I_30\", \"schemaRef\": \"#/patternProperties/%5EI_\"," + " \"expected\": 5, \"actual\": 7" + " }, {" + " \"errorCode\": 1," + " \"instanceRef\": \"#/I_30\", \"schemaRef\": \"#/patternProperties/30%24\"," + " \"expected\": 6, \"actual\": 7" + " }" + "]}"); +} + +TEST(SchemaValidator, Object_Properties_PatternProperties) { + Document sd; + sd.Parse( + "{" + " \"type\": \"object\"," + " \"properties\": {" + " \"I_42\": { \"type\": \"integer\", \"minimum\": 73 }" + " }," + " \"patternProperties\": {" + " \"^I_\": { \"type\": \"integer\", \"multipleOf\": 6 }" + " }" + "}"); + SchemaDocument s(sd); + + VALIDATE(s, "{ \"I_6\": 6 }", true); + VALIDATE(s, "{ \"I_42\": 78 }", true); + INVALIDATE(s, "{ \"I_42\": 42 }", "", "patternProperties", "/I_42", + "{ \"minimum\": {" + " \"errorCode\": 4," + " \"instanceRef\": \"#/I_42\", \"schemaRef\": \"#/properties/I_42\"," + " \"expected\": 73, \"actual\": 42" + "}}"); + INVALIDATE(s, "{ \"I_42\": 7 }", "", "patternProperties", "/I_42", + "{ \"minimum\": {" + " \"errorCode\": 4," + " \"instanceRef\": \"#/I_42\", \"schemaRef\": \"#/properties/I_42\"," + " \"expected\": 73, \"actual\": 7" + " }," + " \"multipleOf\": {" + " \"errorCode\": 1," + " \"instanceRef\": \"#/I_42\", \"schemaRef\": \"#/patternProperties/%5EI_\"," + " \"expected\": 6, \"actual\": 7" + " }" + "}"); +} + +TEST(SchemaValidator, Object_PatternProperties_AdditionalPropertiesObject) { Document sd; sd.Parse( "{" @@ -756,7 +1429,36 @@ TEST(SchemaValidator, Object_PatternProperties_AdditionalProperties) { VALIDATE(s, "{ \"builtin\": 42 }", true); VALIDATE(s, "{ \"keyword\": \"value\" }", true); - INVALIDATE(s, "{ \"keyword\": 42 }", "/additionalProperties", "type", "/keyword"); + INVALIDATE(s, "{ \"keyword\": 42 }", "/additionalProperties", "type", "/keyword", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/keyword\", \"schemaRef\": \"#/additionalProperties\"," + " \"expected\": [\"string\"], \"actual\": \"integer\"" + "}}"); +} + +// Replaces test Issue285 and tests failure as well as success +TEST(SchemaValidator, Object_PatternProperties_AdditionalPropertiesBoolean) { + Document sd; + sd.Parse( + "{" + " \"type\": \"object\"," + " \"patternProperties\": {" + " \"^S_\": { \"type\": \"string\" }," + " \"^I_\": { \"type\": \"integer\" }" + " }," + " \"additionalProperties\": false" + "}"); + SchemaDocument s(sd); + + VALIDATE(s, "{ \"S_25\": \"This is a string\" }", true); + VALIDATE(s, "{ \"I_0\": 42 }", true); + INVALIDATE(s, "{ \"keyword\": \"value\" }", "", "additionalProperties", "/keyword", + "{ \"additionalProperties\": {" + " \"errorCode\": 16," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"disallowed\": \"keyword\"" + "}}"); } #endif @@ -767,7 +1469,12 @@ TEST(SchemaValidator, Array) { VALIDATE(s, "[1, 2, 3, 4, 5]", true); VALIDATE(s, "[3, \"different\", { \"types\" : \"of values\" }]", true); - INVALIDATE(s, "{\"Not\": \"an array\"}", "", "type", ""); + INVALIDATE(s, "{\"Not\": \"an array\"}", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"array\"], \"actual\": \"object\"" + "}}"); } TEST(SchemaValidator, Array_ItemsList) { @@ -782,7 +1489,12 @@ TEST(SchemaValidator, Array_ItemsList) { SchemaDocument s(sd); VALIDATE(s, "[1, 2, 3, 4, 5]", true); - INVALIDATE(s, "[1, 2, \"3\", 4, 5]", "/items", "type", "/2"); + INVALIDATE(s, "[1, 2, \"3\", 4, 5]", "/items", "type", "/2", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/2\", \"schemaRef\": \"#/items\"," + " \"expected\": [\"number\"], \"actual\": \"string\"" + "}}"); VALIDATE(s, "[]", true); } @@ -811,13 +1523,25 @@ TEST(SchemaValidator, Array_ItemsTuple) { SchemaDocument s(sd); VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\"]", true); - INVALIDATE(s, "[24, \"Sussex\", \"Drive\"]", "/items/2", "enum", "/2"); - INVALIDATE(s, "[\"Palais de l'Elysee\"]", "/items/0", "type", "/0"); + INVALIDATE(s, "[24, \"Sussex\", \"Drive\"]", "/items/2", "enum", "/2", + "{ \"enum\": { \"errorCode\": 19, \"instanceRef\": \"#/2\", \"schemaRef\": \"#/items/2\" }}"); + INVALIDATE(s, "[\"Palais de l'Elysee\"]", "/items/0", "type", "/0", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/0\", \"schemaRef\": \"#/items/0\"," + " \"expected\": [\"number\"], \"actual\": \"string\"" + "}}"); + INVALIDATE(s, "[\"Twenty-four\", \"Sussex\", \"Drive\"]", "/items/0", "type", "/0", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/0\", \"schemaRef\": \"#/items/0\"," + " \"expected\": [\"number\"], \"actual\": \"string\"" + "}}"); // fail fast VALIDATE(s, "[10, \"Downing\", \"Street\"]", true); VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\", \"Washington\"]", true); } -TEST(SchemaValidator, Array_AdditionalItmes) { +TEST(SchemaValidator, Array_AdditionalItems) { Document sd; sd.Parse( "{" @@ -844,7 +1568,12 @@ TEST(SchemaValidator, Array_AdditionalItmes) { VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\"]", true); VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\"]", true); - INVALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\", \"Washington\"]", "", "items", "/4"); + INVALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\", \"Washington\"]", "", "additionalItems", "/4", + "{ \"additionalItems\": {" + " \"errorCode\": 12," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"disallowed\": 4" + "}}"); } TEST(SchemaValidator, Array_ItemsRange) { @@ -852,11 +1581,26 @@ TEST(SchemaValidator, Array_ItemsRange) { sd.Parse("{\"type\": \"array\",\"minItems\": 2,\"maxItems\" : 3}"); SchemaDocument s(sd); - INVALIDATE(s, "[]", "", "minItems", ""); - INVALIDATE(s, "[1]", "", "minItems", ""); + INVALIDATE(s, "[]", "", "minItems", "", + "{ \"minItems\": {" + " \"errorCode\": 10," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 2, \"actual\": 0" + "}}"); + INVALIDATE(s, "[1]", "", "minItems", "", + "{ \"minItems\": {" + " \"errorCode\": 10," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 2, \"actual\": 1" + "}}"); VALIDATE(s, "[1, 2]", true); VALIDATE(s, "[1, 2, 3]", true); - INVALIDATE(s, "[1, 2, 3, 4]", "", "maxItems", ""); + INVALIDATE(s, "[1, 2, 3, 4]", "", "maxItems", "", + "{ \"maxItems\": {" + " \"errorCode\": 9," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 3, \"actual\": 4" + "}}"); } TEST(SchemaValidator, Array_UniqueItems) { @@ -865,7 +1609,18 @@ TEST(SchemaValidator, Array_UniqueItems) { SchemaDocument s(sd); VALIDATE(s, "[1, 2, 3, 4, 5]", true); - INVALIDATE(s, "[1, 2, 3, 3, 4]", "", "uniqueItems", "/3"); + INVALIDATE(s, "[1, 2, 3, 3, 4]", "", "uniqueItems", "/3", + "{ \"uniqueItems\": {" + " \"errorCode\": 11," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"duplicates\": [2, 3]" + "}}"); + INVALIDATE(s, "[1, 2, 3, 3, 3]", "", "uniqueItems", "/3", + "{ \"uniqueItems\": {" + " \"errorCode\": 11," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"duplicates\": [2, 3]" + "}}"); // fail fast VALIDATE(s, "[]", true); } @@ -876,8 +1631,18 @@ TEST(SchemaValidator, Boolean) { VALIDATE(s, "true", true); VALIDATE(s, "false", true); - INVALIDATE(s, "\"true\"", "", "type", ""); - INVALIDATE(s, "0", "", "type", ""); + INVALIDATE(s, "\"true\"", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"boolean\"], \"actual\": \"string\"" + "}}"); + INVALIDATE(s, "0", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"boolean\"], \"actual\": \"integer\"" + "}}"); } TEST(SchemaValidator, Null) { @@ -886,9 +1651,24 @@ TEST(SchemaValidator, Null) { SchemaDocument s(sd); VALIDATE(s, "null", true); - INVALIDATE(s, "false", "", "type", ""); - INVALIDATE(s, "0", "", "type", ""); - INVALIDATE(s, "\"\"", "", "type", ""); + INVALIDATE(s, "false", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"null\"], \"actual\": \"boolean\"" + "}}"); + INVALIDATE(s, "0", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"null\"], \"actual\": \"integer\"" + "}}"); + INVALIDATE(s, "\"\"", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"null\"], \"actual\": \"string\"" + "}}"); } // Additional tests @@ -899,8 +1679,18 @@ TEST(SchemaValidator, ObjectInArray) { SchemaDocument s(sd); VALIDATE(s, "[\"a\"]", true); - INVALIDATE(s, "[1]", "/items", "type", "/0"); - INVALIDATE(s, "[{}]", "/items", "type", "/0"); + INVALIDATE(s, "[1]", "/items", "type", "/0", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/0\", \"schemaRef\": \"#/items\"," + " \"expected\": [\"string\"], \"actual\": \"integer\"" + "}}"); + INVALIDATE(s, "[{}]", "/items", "type", "/0", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/0\", \"schemaRef\": \"#/items\"," + " \"expected\": [\"string\"], \"actual\": \"object\"" + "}}"); } TEST(SchemaValidator, MultiTypeInObject) { @@ -918,7 +1708,12 @@ TEST(SchemaValidator, MultiTypeInObject) { VALIDATE(s, "{ \"tel\": 999 }", true); VALIDATE(s, "{ \"tel\": \"123-456\" }", true); - INVALIDATE(s, "{ \"tel\": true }", "/properties/tel", "type", "/tel"); + INVALIDATE(s, "{ \"tel\": true }", "/properties/tel", "type", "/tel", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/tel\", \"schemaRef\": \"#/properties/tel\"," + " \"expected\": [\"string\", \"integer\"], \"actual\": \"boolean\"" + "}}"); } TEST(SchemaValidator, MultiTypeWithObject) { @@ -936,7 +1731,12 @@ TEST(SchemaValidator, MultiTypeWithObject) { VALIDATE(s, "\"Hello\"", true); VALIDATE(s, "{ \"tel\": 999 }", true); - INVALIDATE(s, "{ \"tel\": \"fail\" }", "/properties/tel", "type", "/tel"); + INVALIDATE(s, "{ \"tel\": \"fail\" }", "/properties/tel", "type", "/tel", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/tel\", \"schemaRef\": \"#/properties/tel\"," + " \"expected\": [\"integer\"], \"actual\": \"string\"" + "}}"); } TEST(SchemaValidator, AllOf_Nested) { @@ -953,11 +1753,72 @@ TEST(SchemaValidator, AllOf_Nested) { VALIDATE(s, "\"ok\"", true); VALIDATE(s, "\"OK\"", true); - INVALIDATE(s, "\"okay\"", "", "allOf", ""); - INVALIDATE(s, "\"o\"", "", "allOf", ""); - INVALIDATE(s, "\"n\"", "", "allOf", ""); - INVALIDATE(s, "\"too long\"", "", "allOf", ""); - INVALIDATE(s, "123", "", "allOf", ""); + INVALIDATE(s, "\"okay\"", "", "allOf", "", + "{ \"allOf\": {" + " \"errors\": [" + " {},{}," + " { \"allOf\": {" + " \"errors\": [" + " {}," + " { \"enum\": {\"errorCode\": 19, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/2/allOf/1\" }}" + " ]," + " \"errorCode\": 23, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/2\"" + " }}]," + " \"errorCode\": 23, \"instanceRef\": \"#\", \"schemaRef\": \"#\"" + "}}"); + INVALIDATE(s, "\"o\"", "", "allOf", "", + "{ \"allOf\": {" + " \"errors\": [" + " { \"minLength\": {\"actual\": \"o\", \"expected\": 2, \"errorCode\": 7, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/0\" }}," + " {},{}" + " ]," + " \"errorCode\": 23, \"instanceRef\": \"#\", \"schemaRef\": \"#\"" + "}}"); + INVALIDATE(s, "\"n\"", "", "allOf", "", + "{ \"allOf\": {" + " \"errors\": [" + " { \"minLength\": {\"actual\": \"n\", \"expected\": 2, \"errorCode\": 7, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/0\" }}," + " {}," + " { \"allOf\": {" + " \"errors\": [" + " { \"enum\": {\"errorCode\": 19 ,\"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/2/allOf/0\"}}," + " { \"enum\": {\"errorCode\": 19, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/2/allOf/1\"}}" + " ]," + " \"errorCode\": 23, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/2\"" + " }}" + " ]," + " \"errorCode\":23,\"instanceRef\":\"#\",\"schemaRef\":\"#\"" + "}}"); + INVALIDATE(s, "\"too long\"", "", "allOf", "", + "{ \"allOf\": {" + " \"errors\": [" + " {}," + " { \"maxLength\": {\"actual\": \"too long\", \"expected\": 5, \"errorCode\": 6, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/1\" }}," + " { \"allOf\": {" + " \"errors\": [" + " { \"enum\": {\"errorCode\": 19 ,\"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/2/allOf/0\"}}," + " { \"enum\": {\"errorCode\": 19, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/2/allOf/1\"}}" + " ]," + " \"errorCode\": 23, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/2\"" + " }}" + " ]," + " \"errorCode\":23,\"instanceRef\":\"#\",\"schemaRef\":\"#\"" + "}}"); + INVALIDATE(s, "123", "", "allOf", "", + "{ \"allOf\": {" + " \"errors\": [" + " {\"type\": {\"expected\": [\"string\"], \"actual\": \"integer\", \"errorCode\": 20, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/0\"}}," + " {\"type\": {\"expected\": [\"string\"], \"actual\": \"integer\", \"errorCode\": 20, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/1\"}}," + " { \"allOf\": {" + " \"errors\": [" + " { \"enum\": {\"errorCode\": 19 ,\"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/2/allOf/0\"}}," + " { \"enum\": {\"errorCode\": 19, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/2/allOf/1\"}}" + " ]," + " \"errorCode\": 23, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/2\"" + " }}" + " ]," + " \"errorCode\":23,\"instanceRef\":\"#\",\"schemaRef\":\"#\"" + "}}"); } TEST(SchemaValidator, EscapedPointer) { @@ -970,7 +1831,189 @@ TEST(SchemaValidator, EscapedPointer) { " }" "}"); SchemaDocument s(sd); - INVALIDATE(s, "{\"~/\":true}", "/properties/~0~1", "type", "/~0~1"); + INVALIDATE(s, "{\"~/\":true}", "/properties/~0~1", "type", "/~0~1", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/~0~1\", \"schemaRef\": \"#/properties/~0~1\"," + " \"expected\": [\"number\"], \"actual\": \"boolean\"" + "}}"); +} + +TEST(SchemaValidator, SchemaPointer) { + Document sd; + sd.Parse( + "{" + " \"swagger\": \"2.0\"," + " \"paths\": {" + " \"/some/path\": {" + " \"post\": {" + " \"parameters\": [" + " {" + " \"in\": \"body\"," + " \"name\": \"body\"," + " \"schema\": {" + " \"properties\": {" + " \"a\": {" + " \"$ref\": \"#/definitions/Prop_a\"" + " }," + " \"b\": {" + " \"type\": \"integer\"" + " }" + " }," + " \"type\": \"object\"" + " }" + " }" + " ]," + " \"responses\": {" + " \"200\": {" + " \"schema\": {" + " \"$ref\": \"#/definitions/Resp_200\"" + " }" + " }" + " }" + " }" + " }" + " }," + " \"definitions\": {" + " \"Prop_a\": {" + " \"properties\": {" + " \"c\": {" + " \"enum\": [" + " \"C1\"," + " \"C2\"," + " \"C3\"" + " ]," + " \"type\": \"string\"" + " }," + " \"d\": {" + " \"$ref\": \"#/definitions/Prop_d\"" + " }," + " \"s\": {" + " \"type\": \"string\"" + " }" + " }," + " \"required\": [\"c\"]," + " \"type\": \"object\"" + " }," + " \"Prop_d\": {" + " \"properties\": {" + " \"a\": {" + " \"$ref\": \"#/definitions/Prop_a\"" + " }," + " \"c\": {" + " \"$ref\": \"#/definitions/Prop_a/properties/c\"" + " }" + " }," + " \"type\": \"object\"" + " }," + " \"Resp_200\": {" + " \"properties\": {" + " \"e\": {" + " \"type\": \"string\"" + " }," + " \"f\": {" + " \"type\": \"boolean\"" + " }" + " }," + " \"type\": \"object\"" + " }" + " }" + "}"); + SchemaDocument s1(sd, NULL, 0, NULL, NULL, Pointer("#/paths/~1some~1path/post/parameters/0/schema")); + VALIDATE(s1, + "{" + " \"a\": {" + " \"c\": \"C1\"," + " \"d\": {" + " \"a\": {" + " \"c\": \"C2\"" + " }," + " \"c\": \"C3\"" + " }" + " }," + " \"b\": 123" + "}", + true); + INVALIDATE(s1, + "{" + " \"a\": {" + " \"c\": \"C1\"," + " \"d\": {" + " \"a\": {" + " \"c\": \"C2\"" + " }," + " \"c\": \"C3\"" + " }" + " }," + " \"b\": \"should be an int\"" + "}", + "#/paths/~1some~1path/post/parameters/0/schema/properties/b", "type", "#/b", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\":\"#/b\"," + " \"schemaRef\":\"#/paths/~1some~1path/post/parameters/0/schema/properties/b\"," + " \"expected\": [\"integer\"], \"actual\":\"string\"" + "}}"); + INVALIDATE(s1, + "{" + " \"a\": {" + " \"c\": \"C1\"," + " \"d\": {" + " \"a\": {" + " \"c\": \"should be within enum\"" + " }," + " \"c\": \"C3\"" + " }" + " }," + " \"b\": 123" + "}", + "#/definitions/Prop_a/properties/c", "enum", "#/a/d/a/c", + "{ \"enum\": {" + " \"errorCode\": 19," + " \"instanceRef\":\"#/a/d/a/c\"," + " \"schemaRef\":\"#/definitions/Prop_a/properties/c\"" + "}}"); + INVALIDATE(s1, + "{" + " \"a\": {" + " \"c\": \"C1\"," + " \"d\": {" + " \"a\": {" + " \"s\": \"required 'c' is missing\"" + " }" + " }" + " }," + " \"b\": 123" + "}", + "#/definitions/Prop_a", "required", "#/a/d/a", + "{ \"required\": {" + " \"errorCode\": 15," + " \"missing\":[\"c\"]," + " \"instanceRef\":\"#/a/d/a\"," + " \"schemaRef\":\"#/definitions/Prop_a\"" + "}}"); + SchemaDocument s2(sd, NULL, 0, NULL, NULL, Pointer("#/paths/~1some~1path/post/responses/200/schema")); + VALIDATE(s2, + "{ \"e\": \"some string\", \"f\": false }", + true); + INVALIDATE(s2, + "{ \"e\": true, \"f\": false }", + "#/definitions/Resp_200/properties/e", "type", "#/e", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\":\"#/e\"," + " \"schemaRef\":\"#/definitions/Resp_200/properties/e\"," + " \"expected\": [\"string\"], \"actual\":\"boolean\"" + "}}"); + INVALIDATE(s2, + "{ \"e\": \"some string\", \"f\": 123 }", + "#/definitions/Resp_200/properties/f", "type", "#/f", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\":\"#/f\"," + " \"schemaRef\":\"#/definitions/Resp_200/properties/f\"," + " \"expected\": [\"boolean\"], \"actual\":\"integer\"" + "}}"); } template @@ -1012,14 +2055,21 @@ TEST(SchemaValidator, ValidateMetaSchema) { ASSERT_FALSE(d.HasParseError()); SchemaDocument sd(d); SchemaValidator validator(sd); - if (!d.Accept(validator)) { + d.Accept(validator); + if (!validator.IsValid()) { StringBuffer sb; validator.GetInvalidSchemaPointer().StringifyUriFragment(sb); printf("Invalid schema: %s\n", sb.GetString()); printf("Invalid keyword: %s\n", validator.GetInvalidSchemaKeyword()); + printf("Invalid code: %d\n", validator.GetInvalidSchemaCode()); + printf("Invalid message: %s\n", GetValidateError_En(validator.GetInvalidSchemaCode())); sb.Clear(); validator.GetInvalidDocumentPointer().StringifyUriFragment(sb); printf("Invalid document: %s\n", sb.GetString()); + sb.Clear(); + Writer w(sb); + validator.GetError().Accept(w); + printf("Validation error: %s\n", sb.GetString()); ADD_FAILURE(); } CrtAllocator::Free(json); @@ -1039,7 +2089,8 @@ TEST(SchemaValidator, ValidateMetaSchema_UTF16) { ASSERT_FALSE(d.HasParseError()); SD sd(d); SV validator(sd); - if (!d.Accept(validator)) { + d.Accept(validator); + if (!validator.IsValid()) { GenericStringBuffer > sb; validator.GetInvalidSchemaPointer().StringifyUriFragment(sb); wprintf(L"Invalid schema: %ls\n", sb.GetString()); @@ -1047,6 +2098,10 @@ TEST(SchemaValidator, ValidateMetaSchema_UTF16) { sb.Clear(); validator.GetInvalidDocumentPointer().StringifyUriFragment(sb); wprintf(L"Invalid document: %ls\n", sb.GetString()); + sb.Clear(); + Writer >, UTF16<> > w(sb); + validator.GetError().Accept(w); + printf("Validation error: %ls\n", sb.GetString()); ADD_FAILURE(); } CrtAllocator::Free(json); @@ -1063,7 +2118,15 @@ public: "jsonschema/remotes/integer.json", "jsonschema/remotes/subSchemas.json", "jsonschema/remotes/folder/folderInteger.json", - "draft-04/schema" + "draft-04/schema", + "unittestschema/address.json" + }; + const char* uris[kCount] = { + "http://localhost:1234/integer.json", + "http://localhost:1234/subSchemas.json", + "http://localhost:1234/folder/folderInteger.json", + "http://json-schema.org/draft-04/schema", + "http://localhost:1234/address.json" }; for (size_t i = 0; i < kCount; i++) { @@ -1081,7 +2144,7 @@ public: MemoryPoolAllocator<> stackAllocator(stackBuffer, sizeof(stackBuffer)); DocumentType d(&documentAllocator_, 1024, &stackAllocator); d.Parse(json); - sd_[i] = new SchemaDocumentType(d, 0, &schemaAllocator_); + sd_[i] = new SchemaDocumentType(d, uris[i], static_cast(strlen(uris[i])), 0, &schemaAllocator_); MemoryPoolAllocator<>::Free(json); } }; @@ -1093,16 +2156,13 @@ public: } virtual const SchemaDocumentType* GetRemoteDocument(const char* uri, SizeType length) { - const char* uris[kCount] = { - "http://localhost:1234/integer.json", - "http://localhost:1234/subSchemas.json", - "http://localhost:1234/folder/folderInteger.json", - "http://json-schema.org/draft-04/schema" - }; - + //printf("GetRemoteDocument : %s\n", uri); for (size_t i = 0; i < kCount; i++) - if (strncmp(uri, uris[i], length) == 0) + if (typename SchemaDocumentType::GValue(uri, length) == sd_[i]->GetURI()) { + //printf("Matched document"); return sd_[i]; + } + //printf("No matched document"); return 0; } @@ -1112,12 +2172,12 @@ private: RemoteSchemaDocumentProvider(const RemoteSchemaDocumentProvider&); RemoteSchemaDocumentProvider& operator=(const RemoteSchemaDocumentProvider&); - static const size_t kCount = 4; + static const size_t kCount = 5; SchemaDocumentType* sd_[kCount]; typename DocumentType::AllocatorType documentAllocator_; typename SchemaDocumentType::AllocatorType schemaAllocator_; char documentBuffer_[16384]; - char schemaBuffer_[128 * 1024]; + char schemaBuffer_[128u * 1024]; }; TEST(SchemaValidator, TestSuite) { @@ -1181,6 +2241,7 @@ TEST(SchemaValidator, TestSuite) { ADD_FAILURE(); } else { + //printf("\njson test suite file %s parsed ok\n", filename); GenericDocument, MemoryPoolAllocator<>, MemoryPoolAllocator<> > d(&documentAllocator, 1024, &documentStackAllocator); d.Parse(json); if (d.HasParseError()) { @@ -1190,22 +2251,27 @@ TEST(SchemaValidator, TestSuite) { else { for (Value::ConstValueIterator schemaItr = d.Begin(); schemaItr != d.End(); ++schemaItr) { { - SchemaDocumentType schema((*schemaItr)["schema"], &provider, &schemaAllocator); - GenericSchemaValidator >, MemoryPoolAllocator<> > validator(schema, &validatorAllocator); const char* description1 = (*schemaItr)["description"].GetString(); + //printf("\ncompiling schema for json test %s \n", description1); + SchemaDocumentType schema((*schemaItr)["schema"], filenames[i], static_cast(strlen(filenames[i])), &provider, &schemaAllocator); + GenericSchemaValidator >, MemoryPoolAllocator<> > validator(schema, &validatorAllocator); const Value& tests = (*schemaItr)["tests"]; for (Value::ConstValueIterator testItr = tests.Begin(); testItr != tests.End(); ++testItr) { const char* description2 = (*testItr)["description"].GetString(); + //printf("running json test %s \n", description2); if (!onlyRunDescription || strcmp(description2, onlyRunDescription) == 0) { const Value& data = (*testItr)["data"]; bool expected = (*testItr)["valid"].GetBool(); testCount++; validator.Reset(); - bool actual = data.Accept(validator); + data.Accept(validator); + bool actual = validator.IsValid(); if (expected != actual) printf("Fail: %30s \"%s\" \"%s\"\n", filename, description1, description2); - else + else { + //printf("Passed: %30s \"%s\" \"%s\"\n", filename, description1, description2); passCount++; + } } } //printf("%zu %zu %zu\n", documentAllocator.Size(), schemaAllocator.Size(), validatorAllocator.Size()); @@ -1220,8 +2286,8 @@ TEST(SchemaValidator, TestSuite) { jsonAllocator.Clear(); } printf("%d / %d passed (%2d%%)\n", passCount, testCount, passCount * 100 / testCount); - // if (passCount != testCount) - // ADD_FAILURE(); + if (passCount != testCount) + ADD_FAILURE(); } TEST(SchemaValidatingReader, Simple) { @@ -1252,9 +2318,20 @@ TEST(SchemaValidatingReader, Invalid) { EXPECT_FALSE(reader.IsValid()); EXPECT_EQ(kParseErrorTermination, reader.GetParseResult().Code()); EXPECT_STREQ("maxLength", reader.GetInvalidSchemaKeyword()); + EXPECT_TRUE(reader.GetInvalidSchemaCode() == kValidateErrorMaxLength); EXPECT_TRUE(reader.GetInvalidSchemaPointer() == SchemaDocument::PointerType("")); EXPECT_TRUE(reader.GetInvalidDocumentPointer() == SchemaDocument::PointerType("")); EXPECT_TRUE(d.IsNull()); + Document e; + e.Parse( + "{ \"maxLength\": {" + " \"errorCode\": 6," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 3, \"actual\": \"ABCD\"" + "}}"); + if (e != reader.GetError()) { + ADD_FAILURE(); + } } TEST(SchemaValidatingWriter, Simple) { @@ -1279,6 +2356,21 @@ TEST(SchemaValidatingWriter, Simple) { EXPECT_FALSE(validator.IsValid()); EXPECT_TRUE(validator.GetInvalidSchemaPointer() == SchemaDocument::PointerType("")); EXPECT_TRUE(validator.GetInvalidDocumentPointer() == SchemaDocument::PointerType("")); + EXPECT_TRUE(validator.GetInvalidSchemaCode() == kValidateErrorMaxLength); + Document e; + e.Parse( + "{ \"maxLength\": {" +" \"errorCode\": 6," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": 3, \"actual\": \"ABCD\"" + "}}"); + EXPECT_EQ(e, validator.GetError()); +} + +TEST(Schema, Issue848) { + rapidjson::Document d; + rapidjson::SchemaDocument s(d); + rapidjson::GenericSchemaValidator v(s); } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS @@ -1294,7 +2386,12 @@ TEST(Schema, Issue552) { SchemaDocument s = ReturnSchemaDocument(); VALIDATE(s, "42", true); VALIDATE(s, "\"Life, the universe, and everything\"", true); - INVALIDATE(s, "[\"Life\", \"the universe\", \"and everything\"]", "", "type", ""); + INVALIDATE(s, "[\"Life\", \"the universe\", \"and everything\"]", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"string\", \"number\"], \"actual\": \"array\"" + "}}"); } #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS @@ -1305,9 +2402,1177 @@ TEST(SchemaValidator, Issue608) { SchemaDocument s(sd); VALIDATE(s, "{\"a\" : null, \"b\": null}", true); - INVALIDATE(s, "{\"a\" : null, \"a\" : null}", "", "required", ""); + INVALIDATE(s, "{\"a\" : null, \"a\" : null}", "", "required", "", + "{ \"required\": {" + " \"errorCode\": 15," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"missing\": [\"b\"]" + "}}"); } -#ifdef __clang__ +// Fail to resolve $ref in allOf causes crash in SchemaValidator::StartObject() +TEST(SchemaValidator, Issue728_AllOfRef) { + Document sd; + sd.Parse("{\"allOf\": [{\"$ref\": \"#/abc\"}]}"); + SchemaDocument s(sd); + SCHEMAERROR(s, "{\"RefUnknown\":{\"errorCode\":5,\"instanceRef\":\"#/allOf/0\",\"value\":\"#/abc\"}}"); + + VALIDATE_(s, "{\"key1\": \"abc\", \"key2\": \"def\"}", true, false); +} + +TEST(SchemaValidator, Issue1017_allOfHandler) { + Document sd; + sd.Parse("{\"allOf\": [{\"type\": \"object\",\"properties\": {\"cyanArray2\": {\"type\": \"array\",\"items\": { \"type\": \"string\" }}}},{\"type\": \"object\",\"properties\": {\"blackArray\": {\"type\": \"array\",\"items\": { \"type\": \"string\" }}},\"required\": [ \"blackArray\" ]}]}"); + SchemaDocument s(sd); + StringBuffer sb; + Writer writer(sb); + GenericSchemaValidator > validator(s, writer); + EXPECT_TRUE(validator.StartObject()); + EXPECT_TRUE(validator.Key("cyanArray2", 10, false)); + EXPECT_TRUE(validator.StartArray()); + EXPECT_TRUE(validator.EndArray(0)); + EXPECT_TRUE(validator.Key("blackArray", 10, false)); + EXPECT_TRUE(validator.StartArray()); + EXPECT_TRUE(validator.EndArray(0)); + EXPECT_TRUE(validator.EndObject(0)); + EXPECT_TRUE(validator.IsValid()); + EXPECT_STREQ("{\"cyanArray2\":[],\"blackArray\":[]}", sb.GetString()); +} + +TEST(SchemaValidator, Ref_remote) { + typedef GenericSchemaDocument > SchemaDocumentType; + RemoteSchemaDocumentProvider provider; + Document sd; + sd.Parse("{\"$ref\": \"http://localhost:1234/subSchemas.json#/integer\"}"); + SchemaDocumentType s(sd, 0, 0, &provider); + typedef GenericSchemaValidator >, MemoryPoolAllocator<> > SchemaValidatorType; + typedef GenericPointer > PointerType; + INVALIDATE_(s, "null", "/integer", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\"," + " \"schemaRef\": \"http://localhost:1234/subSchemas.json#/integer\"," + " \"expected\": [\"integer\"], \"actual\": \"null\"" + "}}", + kValidateDefaultFlags, SchemaValidatorType, PointerType); +} + +// Merge with id where $ref is full URI +TEST(SchemaValidator, Ref_remote_change_resolution_scope_uri) { + typedef GenericSchemaDocument > SchemaDocumentType; + RemoteSchemaDocumentProvider provider; + Document sd; + sd.Parse("{\"id\": \"http://ignore/blah#/ref\", \"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"http://localhost:1234/subSchemas.json#/integer\"}}}"); + SchemaDocumentType s(sd, 0, 0, &provider); + typedef GenericSchemaValidator >, MemoryPoolAllocator<> > SchemaValidatorType; + typedef GenericPointer > PointerType; + INVALIDATE_(s, "{\"myInt\": null}", "/integer", "type", "/myInt", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/myInt\"," + " \"schemaRef\": \"http://localhost:1234/subSchemas.json#/integer\"," + " \"expected\": [\"integer\"], \"actual\": \"null\"" + "}}", + kValidateDefaultFlags, SchemaValidatorType, PointerType); +} + +// Merge with id where $ref is a relative path +TEST(SchemaValidator, Ref_remote_change_resolution_scope_relative_path) { + typedef GenericSchemaDocument > SchemaDocumentType; + RemoteSchemaDocumentProvider provider; + Document sd; + sd.Parse("{\"id\": \"http://localhost:1234/\", \"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"subSchemas.json#/integer\"}}}"); + SchemaDocumentType s(sd, 0, 0, &provider); + typedef GenericSchemaValidator >, MemoryPoolAllocator<> > SchemaValidatorType; + typedef GenericPointer > PointerType; + INVALIDATE_(s, "{\"myInt\": null}", "/integer", "type", "/myInt", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/myInt\"," + " \"schemaRef\": \"http://localhost:1234/subSchemas.json#/integer\"," + " \"expected\": [\"integer\"], \"actual\": \"null\"" + "}}", + kValidateDefaultFlags, SchemaValidatorType, PointerType); +} + +// Merge with id where $ref is an absolute path +TEST(SchemaValidator, Ref_remote_change_resolution_scope_absolute_path) { + typedef GenericSchemaDocument > SchemaDocumentType; + RemoteSchemaDocumentProvider provider; + Document sd; + sd.Parse("{\"id\": \"http://localhost:1234/xxxx\", \"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"/subSchemas.json#/integer\"}}}"); + SchemaDocumentType s(sd, 0, 0, &provider); + typedef GenericSchemaValidator >, MemoryPoolAllocator<> > SchemaValidatorType; + typedef GenericPointer > PointerType; + INVALIDATE_(s, "{\"myInt\": null}", "/integer", "type", "/myInt", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/myInt\"," + " \"schemaRef\": \"http://localhost:1234/subSchemas.json#/integer\"," + " \"expected\": [\"integer\"], \"actual\": \"null\"" + "}}", + kValidateDefaultFlags, SchemaValidatorType, PointerType); +} + +// Merge with id where $ref is an absolute path, and the document has a base URI +TEST(SchemaValidator, Ref_remote_change_resolution_scope_absolute_path_document) { + typedef GenericSchemaDocument > SchemaDocumentType; + RemoteSchemaDocumentProvider provider; + Document sd; + sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"/subSchemas.json#/integer\"}}}"); + SchemaDocumentType s(sd, "http://localhost:1234/xxxx", 26, &provider); + typedef GenericSchemaValidator >, MemoryPoolAllocator<> > SchemaValidatorType; + typedef GenericPointer > PointerType; + INVALIDATE_(s, "{\"myInt\": null}", "/integer", "type", "/myInt", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/myInt\"," + " \"schemaRef\": \"http://localhost:1234/subSchemas.json#/integer\"," + " \"expected\": [\"integer\"], \"actual\": \"null\"" + "}}", + kValidateDefaultFlags, SchemaValidatorType, PointerType); +} + +// $ref is a non-JSON pointer fragment and there a matching id +TEST(SchemaValidator, Ref_internal_id_1) { + typedef GenericSchemaDocument > SchemaDocumentType; + Document sd; + sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt1\": {\"$ref\": \"#myId\"}, \"myStr\": {\"type\": \"string\", \"id\": \"#myStrId\"}, \"myInt2\": {\"type\": \"integer\", \"id\": \"#myId\"}}}"); + SchemaDocumentType s(sd); + typedef GenericSchemaValidator >, MemoryPoolAllocator<> > SchemaValidatorType; + typedef GenericPointer > PointerType; + INVALIDATE_(s, "{\"myInt1\": null}", "/properties/myInt2", "type", "/myInt1", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/myInt1\"," + " \"schemaRef\": \"#/properties/myInt2\"," + " \"expected\": [\"integer\"], \"actual\": \"null\"" + "}}", + kValidateDefaultFlags, SchemaValidatorType, PointerType); +} + +// $ref is a non-JSON pointer fragment and there are two matching ids so we take the first +TEST(SchemaValidator, Ref_internal_id_2) { + typedef GenericSchemaDocument > SchemaDocumentType; + Document sd; + sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt1\": {\"$ref\": \"#myId\"}, \"myInt2\": {\"type\": \"integer\", \"id\": \"#myId\"}, \"myStr\": {\"type\": \"string\", \"id\": \"#myId\"}}}"); + SchemaDocumentType s(sd); + typedef GenericSchemaValidator >, MemoryPoolAllocator<> > SchemaValidatorType; + typedef GenericPointer > PointerType; + INVALIDATE_(s, "{\"myInt1\": null}", "/properties/myInt2", "type", "/myInt1", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/myInt1\"," + " \"schemaRef\": \"#/properties/myInt2\"," + " \"expected\": [\"integer\"], \"actual\": \"null\"" + "}}", + kValidateDefaultFlags, SchemaValidatorType, PointerType); +} + +// $ref is a non-JSON pointer fragment and there is a matching id within array +TEST(SchemaValidator, Ref_internal_id_in_array) { + typedef GenericSchemaDocument > SchemaDocumentType; + Document sd; + sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt1\": {\"$ref\": \"#myId\"}, \"myInt2\": {\"anyOf\": [{\"type\": \"string\", \"id\": \"#myStrId\"}, {\"type\": \"integer\", \"id\": \"#myId\"}]}}}"); + SchemaDocumentType s(sd); + typedef GenericSchemaValidator >, MemoryPoolAllocator<> > SchemaValidatorType; + typedef GenericPointer > PointerType; + INVALIDATE_(s, "{\"myInt1\": null}", "/properties/myInt2/anyOf/1", "type", "/myInt1", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/myInt1\"," + " \"schemaRef\": \"#/properties/myInt2/anyOf/1\"," + " \"expected\": [\"integer\"], \"actual\": \"null\"" + "}}", + kValidateDefaultFlags, SchemaValidatorType, PointerType); +} + +// $ref is a non-JSON pointer fragment and there is a matching id, and the schema is embedded in the document +TEST(SchemaValidator, Ref_internal_id_and_schema_pointer) { + typedef GenericSchemaDocument > SchemaDocumentType; + Document sd; + sd.Parse("{ \"schema\": {\"type\": \"object\", \"properties\": {\"myInt1\": {\"$ref\": \"#myId\"}, \"myInt2\": {\"anyOf\": [{\"type\": \"integer\", \"id\": \"#myId\"}]}}}}"); + typedef GenericPointer > PointerType; + SchemaDocumentType s(sd, 0, 0, 0, 0, PointerType("/schema")); + typedef GenericSchemaValidator >, MemoryPoolAllocator<> > SchemaValidatorType; + INVALIDATE_(s, "{\"myInt1\": null}", "/schema/properties/myInt2/anyOf/0", "type", "/myInt1", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/myInt1\"," + " \"schemaRef\": \"#/schema/properties/myInt2/anyOf/0\"," + " \"expected\": [\"integer\"], \"actual\": \"null\"" + "}}", + kValidateDefaultFlags, SchemaValidatorType, PointerType); +} + +// Test that $refs are correctly resolved when intermediate multiple ids are present +// Includes $ref to a part of the document with a different in-scope id, which also contains $ref.. +TEST(SchemaValidator, Ref_internal_multiple_ids) { + typedef GenericSchemaDocument > SchemaDocumentType; + //RemoteSchemaDocumentProvider provider; + CrtAllocator allocator; + char* schema = ReadFile("unittestschema/idandref.json", allocator); + Document sd; + sd.Parse(schema); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocumentType s(sd, "http://xyz", 10/*, &provider*/); + typedef GenericSchemaValidator >, MemoryPoolAllocator<> > SchemaValidatorType; + typedef GenericPointer > PointerType; + INVALIDATE_(s, "{\"PA1\": \"s\", \"PA2\": \"t\", \"PA3\": \"r\", \"PX1\": 1, \"PX2Y\": 2, \"PX3Z\": 3, \"PX4\": 4, \"PX5\": 5, \"PX6\": 6, \"PX7W\": 7, \"PX8N\": { \"NX\": 8}}", "#", "errors", "#", + "{ \"type\": [" + " {\"errorCode\": 20, \"instanceRef\": \"#/PA1\", \"schemaRef\": \"http://xyz#/definitions/A\", \"expected\": [\"integer\"], \"actual\": \"string\"}," + " {\"errorCode\": 20, \"instanceRef\": \"#/PA2\", \"schemaRef\": \"http://xyz#/definitions/A\", \"expected\": [\"integer\"], \"actual\": \"string\"}," + " {\"errorCode\": 20, \"instanceRef\": \"#/PA3\", \"schemaRef\": \"http://xyz#/definitions/A\", \"expected\": [\"integer\"], \"actual\": \"string\"}," + " {\"errorCode\": 20, \"instanceRef\": \"#/PX1\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"}," + " {\"errorCode\": 20, \"instanceRef\": \"#/PX2Y\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"}," + " {\"errorCode\": 20, \"instanceRef\": \"#/PX3Z\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"}," + " {\"errorCode\": 20, \"instanceRef\": \"#/PX4\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"}," + " {\"errorCode\": 20, \"instanceRef\": \"#/PX5\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"}," + " {\"errorCode\": 20, \"instanceRef\": \"#/PX6\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"}," + " {\"errorCode\": 20, \"instanceRef\": \"#/PX7W\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"}," + " {\"errorCode\": 20, \"instanceRef\": \"#/PX8N/NX\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"}" + "]}", + kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidatorType, PointerType); + CrtAllocator::Free(schema); +} + +TEST(SchemaValidator, Ref_remote_issue1210) { + class SchemaDocumentProvider : public IRemoteSchemaDocumentProvider { + SchemaDocument** collection; + + // Dummy private copy constructor & assignment operator. + // Function bodies added so that they compile in MSVC 2019. + SchemaDocumentProvider(const SchemaDocumentProvider&) : collection(NULL) { + } + SchemaDocumentProvider& operator=(const SchemaDocumentProvider&) { + return *this; + } + + public: + SchemaDocumentProvider(SchemaDocument** collection) : collection(collection) { } + virtual const SchemaDocument* GetRemoteDocument(const char* uri, SizeType length) { + int i = 0; + while (collection[i] && SchemaDocument::GValue(uri, length) != collection[i]->GetURI()) ++i; + return collection[i]; + } + }; + SchemaDocument* collection[] = { 0, 0, 0 }; + SchemaDocumentProvider provider(collection); + + Document x, y, z; + x.Parse("{\"properties\":{\"country\":{\"$ref\":\"y.json#/definitions/country_remote\"}},\"type\":\"object\"}"); + y.Parse("{\"definitions\":{\"country_remote\":{\"$ref\":\"z.json#/definitions/country_list\"}}}"); + z.Parse("{\"definitions\":{\"country_list\":{\"enum\":[\"US\"]}}}"); + + SchemaDocument sz(z, "z.json", 6, &provider); + collection[0] = &sz; + SchemaDocument sy(y, "y.json", 6, &provider); + collection[1] = &sy; + SchemaDocument sx(x, "x.json", 6, &provider); + + VALIDATE(sx, "{\"country\":\"UK\"}", false); + VALIDATE(sx, "{\"country\":\"US\"}", true); +} + +// Test that when kValidateContinueOnErrorFlag is set, all errors are reported. +TEST(SchemaValidator, ContinueOnErrors) { + CrtAllocator allocator; + char* schema = ReadFile("unittestschema/address.json", allocator); + Document sd; + sd.Parse(schema); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + VALIDATE(s, "{\"version\": 1.0, \"address\": {\"number\": 24, \"street1\": \"The Woodlands\", \"street3\": \"Ham\", \"city\": \"Romsey\", \"area\": \"Kent\", \"country\": \"UK\", \"postcode\": \"SO51 0GP\"}, \"phones\": [\"0111-222333\", \"0777-666888\"], \"names\": [\"Fred\", \"Bloggs\"]}", true); + INVALIDATE_(s, "{\"version\": 1.01, \"address\": {\"number\": 0, \"street2\": false, \"street3\": \"Ham\", \"city\": \"RomseyTownFC\", \"area\": \"Narnia\", \"country\": \"USA\", \"postcode\": \"999ABC\"}, \"phones\": [], \"planet\": \"Earth\", \"extra\": {\"S_xxx\": 123}}", "#", "errors", "#", + "{ \"multipleOf\": {" + " \"errorCode\": 1, \"instanceRef\": \"#/version\", \"schemaRef\": \"#/definitions/decimal_type\", \"expected\": 1.0, \"actual\": 1.01" + " }," + " \"minimum\": {" + " \"errorCode\": 5, \"instanceRef\": \"#/address/number\", \"schemaRef\": \"#/definitions/positiveInt_type\", \"expected\": 0, \"actual\": 0, \"exclusiveMinimum\": true" + " }," + " \"type\": [" + " {\"expected\": [\"null\", \"string\"], \"actual\": \"boolean\", \"errorCode\": 20, \"instanceRef\": \"#/address/street2\", \"schemaRef\": \"#/definitions/address_type/properties/street2\"}," + " {\"expected\": [\"string\"], \"actual\": \"integer\", \"errorCode\": 20, \"instanceRef\": \"#/extra/S_xxx\", \"schemaRef\": \"#/properties/extra/patternProperties/%5ES_\"}" + " ]," + " \"maxLength\": {" + " \"actual\": \"RomseyTownFC\", \"expected\": 10, \"errorCode\": 6, \"instanceRef\": \"#/address/city\", \"schemaRef\": \"#/definitions/address_type/properties/city\"" + " }," + " \"anyOf\": {" + " \"errors\":[" + " {\"pattern\": {\"actual\": \"999ABC\", \"errorCode\": 8, \"instanceRef\": \"#/address/postcode\", \"schemaRef\": \"#/definitions/address_type/properties/postcode/anyOf/0\"}}," + " {\"pattern\": {\"actual\": \"999ABC\", \"errorCode\": 8, \"instanceRef\": \"#/address/postcode\", \"schemaRef\": \"#/definitions/address_type/properties/postcode/anyOf/1\"}}" + " ]," + " \"errorCode\": 24, \"instanceRef\": \"#/address/postcode\", \"schemaRef\": \"#/definitions/address_type/properties/postcode\"" + " }," + " \"allOf\": {" + " \"errors\":[" + " {\"enum\":{\"errorCode\":19,\"instanceRef\":\"#/address/country\",\"schemaRef\":\"#/definitions/country_type\"}}" + " ]," + " \"errorCode\":23,\"instanceRef\":\"#/address/country\",\"schemaRef\":\"#/definitions/address_type/properties/country\"" + " }," + " \"minItems\": {" + " \"actual\": 0, \"expected\": 1, \"errorCode\": 10, \"instanceRef\": \"#/phones\", \"schemaRef\": \"#/properties/phones\"" + " }," + " \"additionalProperties\": {" + " \"disallowed\": \"planet\", \"errorCode\": 16, \"instanceRef\": \"#\", \"schemaRef\": \"#\"" + " }," + " \"required\": {" + " \"missing\": [\"street1\"], \"errorCode\": 15, \"instanceRef\": \"#/address\", \"schemaRef\": \"#/definitions/address_type\"" + " }," + " \"oneOf\": {" + " \"matches\": [0, 1], \"errorCode\": 22, \"instanceRef\": \"#/address/area\", \"schemaRef\": \"#/definitions/address_type/properties/area\"" + " }" + "}", + kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer); + INVALIDATE_(s, "{\"address\": {\"number\": 200, \"street1\": {}, \"street3\": null, \"city\": \"Rom\", \"area\": \"Dorset\", \"postcode\": \"SO51 0GP\"}, \"phones\": [\"0111-222333\", \"0777-666888\", \"0777-666888\"], \"names\": [\"Fred\", \"S\", \"M\", \"Bloggs\"]}", "#", "errors", "#", + "{ \"maximum\": {" + " \"errorCode\": 3, \"instanceRef\": \"#/address/number\", \"schemaRef\": \"#/definitions/positiveInt_type\", \"expected\": 100, \"actual\": 200, \"exclusiveMaximum\": true" + " }," + " \"type\": {" + " \"expected\": [\"string\"], \"actual\": \"object\", \"errorCode\": 20, \"instanceRef\": \"#/address/street1\", \"schemaRef\": \"#/definitions/address_type/properties/street1\"" + " }," + " \"not\": {" + " \"errorCode\": 25, \"instanceRef\": \"#/address/street3\", \"schemaRef\": \"#/definitions/address_type/properties/street3\"" + " }," + " \"minLength\": {" + " \"actual\": \"Rom\", \"expected\": 4, \"errorCode\": 7, \"instanceRef\": \"#/address/city\", \"schemaRef\": \"#/definitions/address_type/properties/city\"" + " }," + " \"maxItems\": {" + " \"actual\": 3, \"expected\": 2, \"errorCode\": 9, \"instanceRef\": \"#/phones\", \"schemaRef\": \"#/properties/phones\"" + " }," + " \"uniqueItems\": {" + " \"duplicates\": [1, 2], \"errorCode\": 11, \"instanceRef\": \"#/phones\", \"schemaRef\": \"#/properties/phones\"" + " }," + " \"minProperties\": {\"actual\": 6, \"expected\": 7, \"errorCode\": 14, \"instanceRef\": \"#/address\", \"schemaRef\": \"#/definitions/address_type\"" + " }," + " \"additionalItems\": [" + " {\"disallowed\": 2, \"errorCode\": 12, \"instanceRef\": \"#/names\", \"schemaRef\": \"#/properties/names\"}," + " {\"disallowed\": 3, \"errorCode\": 12, \"instanceRef\": \"#/names\", \"schemaRef\": \"#/properties/names\"}" + " ]," + " \"dependencies\": {" + " \"errors\": {" + " \"address\": {\"required\": {\"missing\": [\"version\"], \"errorCode\": 15, \"instanceRef\": \"#\", \"schemaRef\": \"#/dependencies/address\"}}," + " \"names\": {\"required\": {\"missing\": [\"version\"], \"errorCode\": 15, \"instanceRef\": \"#\", \"schemaRef\": \"#/dependencies/names\"}}" + " }," + " \"errorCode\": 18, \"instanceRef\": \"#\", \"schemaRef\": \"#\"" + " }," + " \"oneOf\": {" + " \"errors\": [" + " {\"enum\": {\"errorCode\": 19, \"instanceRef\": \"#/address/area\", \"schemaRef\": \"#/definitions/county_type\"}}," + " {\"enum\": {\"errorCode\": 19, \"instanceRef\": \"#/address/area\", \"schemaRef\": \"#/definitions/province_type\"}}" + " ]," + " \"errorCode\": 21, \"instanceRef\": \"#/address/area\", \"schemaRef\": \"#/definitions/address_type/properties/area\"" + " }" + "}", + kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer); + + CrtAllocator::Free(schema); +} + +// Test that when kValidateContinueOnErrorFlag is set, it is not propagated to oneOf sub-validator so we only get the first error. +TEST(SchemaValidator, ContinueOnErrors_OneOf) { + typedef GenericSchemaDocument > SchemaDocumentType; + RemoteSchemaDocumentProvider provider; + CrtAllocator allocator; + char* schema = ReadFile("unittestschema/oneOf_address.json", allocator); + Document sd; + sd.Parse(schema); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocumentType s(sd, 0, 0, &provider); + typedef GenericSchemaValidator >, MemoryPoolAllocator<> > SchemaValidatorType; + typedef GenericPointer > PointerType; + INVALIDATE_(s, "{\"version\": 1.01, \"address\": {\"number\": 0, \"street2\": false, \"street3\": \"Ham\", \"city\": \"RomseyTownFC\", \"area\": \"BC\", \"country\": \"USA\", \"postcode\": \"999ABC\"}, \"phones\": [], \"planet\": \"Earth\", \"extra\": {\"S_xxx\": 123}}", "#", "errors", "#", + "{ \"oneOf\": {" + " \"errors\": [{" + " \"multipleOf\": {" + " \"errorCode\": 1, \"instanceRef\": \"#/version\", \"schemaRef\": \"http://localhost:1234/address.json#/definitions/decimal_type\", \"expected\": 1.0, \"actual\": 1.01" + " }" + " }]," + " \"errorCode\": 21, \"instanceRef\": \"#\", \"schemaRef\": \"#\"" + " }" + "}", + kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidatorType, PointerType); + CrtAllocator::Free(schema); +} + +// Test that when kValidateContinueOnErrorFlag is set, it is not propagated to allOf sub-validator so we only get the first error. +TEST(SchemaValidator, ContinueOnErrors_AllOf) { + typedef GenericSchemaDocument > SchemaDocumentType; + RemoteSchemaDocumentProvider provider; + CrtAllocator allocator; + char* schema = ReadFile("unittestschema/allOf_address.json", allocator); + Document sd; + sd.Parse(schema); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocumentType s(sd, 0, 0, &provider); + typedef GenericSchemaValidator >, MemoryPoolAllocator<> > SchemaValidatorType; + typedef GenericPointer > PointerType; + INVALIDATE_(s, "{\"version\": 1.01, \"address\": {\"number\": 0, \"street2\": false, \"street3\": \"Ham\", \"city\": \"RomseyTownFC\", \"area\": \"BC\", \"country\": \"USA\", \"postcode\": \"999ABC\"}, \"phones\": [], \"planet\": \"Earth\", \"extra\": {\"S_xxx\": 123}}", "#", "errors", "#", + "{ \"allOf\": {" + " \"errors\": [{" + " \"multipleOf\": {" + " \"errorCode\": 1, \"instanceRef\": \"#/version\", \"schemaRef\": \"http://localhost:1234/address.json#/definitions/decimal_type\", \"expected\": 1.0, \"actual\": 1.01" + " }" + " }]," + " \"errorCode\": 23, \"instanceRef\": \"#\", \"schemaRef\": \"#\"" + " }" + "}", + kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidatorType, PointerType); + CrtAllocator::Free(schema); +} + +// Test that when kValidateContinueOnErrorFlag is set, it is not propagated to anyOf sub-validator so we only get the first error. +TEST(SchemaValidator, ContinueOnErrors_AnyOf) { + typedef GenericSchemaDocument > SchemaDocumentType; + RemoteSchemaDocumentProvider provider; + CrtAllocator allocator; + char* schema = ReadFile("unittestschema/anyOf_address.json", allocator); + Document sd; + sd.Parse(schema); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocumentType s(sd, 0, 0, &provider); + typedef GenericSchemaValidator >, MemoryPoolAllocator<> > SchemaValidatorType; + typedef GenericPointer > PointerType; + INVALIDATE_(s, "{\"version\": 1.01, \"address\": {\"number\": 0, \"street2\": false, \"street3\": \"Ham\", \"city\": \"RomseyTownFC\", \"area\": \"BC\", \"country\": \"USA\", \"postcode\": \"999ABC\"}, \"phones\": [], \"planet\": \"Earth\", \"extra\": {\"S_xxx\": 123}}", "#", "errors", "#", + "{ \"anyOf\": {" + " \"errors\": [{" + " \"multipleOf\": {" + " \"errorCode\": 1, \"instanceRef\": \"#/version\", \"schemaRef\": \"http://localhost:1234/address.json#/definitions/decimal_type\", \"expected\": 1.0, \"actual\": 1.01" + " }" + " }]," + " \"errorCode\": 24, \"instanceRef\": \"#\", \"schemaRef\": \"#\"" + " }" + "}", + kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidatorType, PointerType); + + CrtAllocator::Free(schema); +} + +// Test that when kValidateContinueOnErrorFlag is set, arrays with uniqueItems:true are correctly processed when an item is invalid. +// This tests that we don't blow up if a hasher does not get created. +TEST(SchemaValidator, ContinueOnErrors_UniqueItems) { + CrtAllocator allocator; + char* schema = ReadFile("unittestschema/address.json", allocator); + Document sd; + sd.Parse(schema); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + VALIDATE(s, "{\"phones\":[\"12-34\",\"56-78\"]}", true); + INVALIDATE_(s, "{\"phones\":[\"12-34\",\"12-34\"]}", "#", "errors", "#", + "{\"uniqueItems\": {\"duplicates\": [0,1], \"errorCode\": 11, \"instanceRef\": \"#/phones\", \"schemaRef\": \"#/properties/phones\"}}", + kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer); + INVALIDATE_(s, "{\"phones\":[\"ab-34\",\"cd-78\"]}", "#", "errors", "#", + "{\"pattern\": [" + " {\"actual\": \"ab-34\", \"errorCode\": 8, \"instanceRef\": \"#/phones/0\", \"schemaRef\": \"#/definitions/phone_type\"}," + " {\"actual\": \"cd-78\", \"errorCode\": 8, \"instanceRef\": \"#/phones/1\", \"schemaRef\": \"#/definitions/phone_type\"}" + "]}", + kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer); + CrtAllocator::Free(schema); +} + +// Test that when kValidateContinueOnErrorFlag is set, an enum field is correctly processed when it has an invalid value. +// This tests that we don't blow up if a hasher does not get created. +TEST(SchemaValidator, ContinueOnErrors_Enum) { + CrtAllocator allocator; + char* schema = ReadFile("unittestschema/address.json", allocator); + Document sd; + sd.Parse(schema); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + VALIDATE(s, "{\"gender\":\"M\"}", true); + INVALIDATE_(s, "{\"gender\":\"X\"}", "#", "errors", "#", + "{\"enum\": {\"errorCode\": 19, \"instanceRef\": \"#/gender\", \"schemaRef\": \"#/properties/gender\"}}", + kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer); + INVALIDATE_(s, "{\"gender\":1}", "#", "errors", "#", + "{\"type\": {\"expected\":[\"string\"], \"actual\": \"integer\", \"errorCode\": 20, \"instanceRef\": \"#/gender\", \"schemaRef\": \"#/properties/gender\"}}", + kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer); + CrtAllocator::Free(schema); +} + +// Test that when kValidateContinueOnErrorFlag is set, an array appearing for an object property is handled +// This tests that we don't blow up when there is a type mismatch. +TEST(SchemaValidator, ContinueOnErrors_RogueArray) { + CrtAllocator allocator; + char* schema = ReadFile("unittestschema/address.json", allocator); + Document sd; + sd.Parse(schema); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + INVALIDATE_(s, "{\"address\":[{\"number\": 0}]}", "#", "errors", "#", + "{\"type\": {\"expected\":[\"object\"], \"actual\": \"array\", \"errorCode\": 20, \"instanceRef\": \"#/address\", \"schemaRef\": \"#/definitions/address_type\"}," + " \"dependencies\": {" + " \"errors\": {" + " \"address\": {\"required\": {\"missing\": [\"version\"], \"errorCode\": 15, \"instanceRef\": \"#\", \"schemaRef\": \"#/dependencies/address\"}}" + " },\"errorCode\": 18, \"instanceRef\": \"#\", \"schemaRef\": \"#\"}}", + kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer); + CrtAllocator::Free(schema); +} + +// Test that when kValidateContinueOnErrorFlag is set, an object appearing for an array property is handled +// This tests that we don't blow up when there is a type mismatch. +TEST(SchemaValidator, ContinueOnErrors_RogueObject) { + CrtAllocator allocator; + char* schema = ReadFile("unittestschema/address.json", allocator); + Document sd; + sd.Parse(schema); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + INVALIDATE_(s, "{\"phones\":{\"number\": 0}}", "#", "errors", "#", + "{\"type\": {\"expected\":[\"array\"], \"actual\": \"object\", \"errorCode\": 20, \"instanceRef\": \"#/phones\", \"schemaRef\": \"#/properties/phones\"}}", + kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer); + CrtAllocator::Free(schema); +} + +// Test that when kValidateContinueOnErrorFlag is set, a string appearing for an array or object property is handled +// This tests that we don't blow up when there is a type mismatch. +TEST(SchemaValidator, ContinueOnErrors_RogueString) { + CrtAllocator allocator; + char* schema = ReadFile("unittestschema/address.json", allocator); + Document sd; + sd.Parse(schema); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + INVALIDATE_(s, "{\"address\":\"number\"}", "#", "errors", "#", + "{\"type\": {\"expected\":[\"object\"], \"actual\": \"string\", \"errorCode\": 20, \"instanceRef\": \"#/address\", \"schemaRef\": \"#/definitions/address_type\"}," + " \"dependencies\": {" + " \"errors\": {" + " \"address\": {\"required\": {\"missing\": [\"version\"], \"errorCode\": 15, \"instanceRef\": \"#\", \"schemaRef\": \"#/dependencies/address\"}}" + " },\"errorCode\": 18, \"instanceRef\": \"#\", \"schemaRef\": \"#\"}}", + kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer); + INVALIDATE_(s, "{\"phones\":\"number\"}", "#", "errors", "#", + "{\"type\": {\"expected\":[\"array\"], \"actual\": \"string\", \"errorCode\": 20, \"instanceRef\": \"#/phones\", \"schemaRef\": \"#/properties/phones\"}}", + kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer); + CrtAllocator::Free(schema); +} + +// Test that when kValidateContinueOnErrorFlag is set, an incorrect simple type with a sub-schema is handled correctly. +// This tests that we don't blow up when there is a type mismatch but there is a sub-schema present +TEST(SchemaValidator, ContinueOnErrors_BadSimpleType) { + Document sd; + sd.Parse("{\"type\":\"string\", \"anyOf\":[{\"maxLength\":2}]}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + VALIDATE(s, "\"AB\"", true); + INVALIDATE_(s, "\"ABC\"", "#", "errors", "#", + "{ \"anyOf\": {" + " \"errors\": [{" + " \"maxLength\": {" + " \"errorCode\": 6, \"instanceRef\": \"#\", \"schemaRef\": \"#/anyOf/0\", \"expected\": 2, \"actual\": \"ABC\"" + " }" + " }]," + " \"errorCode\": 24, \"instanceRef\": \"#\", \"schemaRef\": \"#\"" + " }" + "}", + kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer); + // Invalid type + INVALIDATE_(s, "333", "#", "errors", "#", + "{ \"type\": {" + " \"errorCode\": 20, \"instanceRef\": \"#\", \"schemaRef\": \"#\", \"expected\": [\"string\"], \"actual\": \"integer\"" + " }" + "}", + kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer); +} + + +TEST(SchemaValidator, UnknownValidationError) { + ASSERT_TRUE(SchemaValidator::SchemaType::GetValidateErrorKeyword(kValidateErrors).GetString() == std::string("null")); +} + +// The first occurrence of a duplicate keyword is taken +TEST(SchemaValidator, DuplicateKeyword) { + Document sd; + sd.Parse("{ \"title\": \"test\",\"type\": \"number\", \"type\": \"string\" }"); + EXPECT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + VALIDATE(s, "42", true); + INVALIDATE(s, "\"Life, the universe, and everything\"", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"number\"], \"actual\": \"string\"" + "}}"); +} + + +// SchemaDocument tests + +// Specification (schema draft, open api version) +TEST(SchemaValidator, Schema_SupportedNotObject) { + Document sd; + sd.Parse("true"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + ASSERT_TRUE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().draft == kDraft04); + ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone); + EXPECT_TRUE(s.GetError().ObjectEmpty()); +} + +TEST(SchemaValidator, Schema_SupportedNoSpec) { + Document sd; + sd.Parse("{\"type\": \"integer\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + ASSERT_TRUE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().draft == kDraft04); + ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone); + EXPECT_TRUE(s.GetError().ObjectEmpty()); +} + +TEST(SchemaValidator, Schema_SupportedNoSpecStatic) { + typedef GenericSchemaDocument > SchemaDocumentType; + Document sd; + sd.Parse("{\"type\": \"integer\"}"); + ASSERT_FALSE(sd.HasParseError()); + Specification spec = SchemaDocumentType::GetSpecification(sd); + ASSERT_FALSE(spec.IsSupported()); + ASSERT_TRUE(spec.draft == kDraftNone); + ASSERT_TRUE(spec.oapi == kVersionNone); +} + +TEST(SchemaValidator, Schema_SupportedDraft5Static) { + typedef GenericSchemaDocument > SchemaDocumentType; + Document sd; + sd.Parse("{\"$schema\":\"http://json-schema.org/draft-05/schema#\", \"type\": \"integer\"}"); + ASSERT_FALSE(sd.HasParseError()); + Specification spec = SchemaDocumentType::GetSpecification(sd); + ASSERT_TRUE(spec.IsSupported()); + ASSERT_TRUE(spec.draft == kDraft05); + ASSERT_TRUE(spec.oapi == kVersionNone); +} + +TEST(SchemaValidator, Schema_SupportedDraft4) { + Document sd; + sd.Parse("{\"$schema\":\"http://json-schema.org/draft-04/schema#\", \"type\": \"integer\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + ASSERT_TRUE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().draft == kDraft04); + ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone); + EXPECT_TRUE(s.GetError().ObjectEmpty()); +} + +TEST(SchemaValidator, Schema_SupportedDraft4NoFrag) { + Document sd; + sd.Parse("{\"$schema\":\"http://json-schema.org/draft-04/schema\", \"type\": \"integer\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + ASSERT_TRUE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().draft == kDraft04); + ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone); + EXPECT_TRUE(s.GetError().ObjectEmpty()); +} + +TEST(SchemaValidator, Schema_SupportedDraft5) { + Document sd; + sd.Parse("{\"$schema\":\"http://json-schema.org/draft-05/schema#\", \"type\": \"integer\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + ASSERT_TRUE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().draft == kDraft05); + ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone); + EXPECT_TRUE(s.GetError().ObjectEmpty()); +} + +TEST(SchemaValidator, Schema_SupportedDraft5NoFrag) { + Document sd; + sd.Parse("{\"$schema\":\"http://json-schema.org/draft-05/schema\", \"type\": \"integer\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + ASSERT_TRUE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().draft == kDraft05); + ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone); + EXPECT_TRUE(s.GetError().ObjectEmpty()); +} + +TEST(SchemaValidator, Schema_IgnoreDraftEmbedded) { + Document sd; + sd.Parse("{\"root\": {\"$schema\":\"http://json-schema.org/draft-05/schema#\", \"type\": \"integer\"}}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd, 0, 0, 0, 0, SchemaDocument::PointerType("/root")); + ASSERT_TRUE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().draft == kDraft04); + ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone); + EXPECT_TRUE(s.GetError().ObjectEmpty()); +} + +TEST(SchemaValidator, Schema_SupportedDraftOverride) { + Document sd; + sd.Parse("{\"type\": \"integer\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd, 0, 0, 0, 0, 0, Specification(kDraft04)); + ASSERT_TRUE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().draft == kDraft04); + ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone); + EXPECT_TRUE(s.GetError().ObjectEmpty()); +} + +TEST(SchemaValidator, Schema_UnknownDraftOverride) { + Document sd; + sd.Parse("{\"type\": \"integer\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd, 0, 0, 0, 0, 0, Specification(kDraftUnknown)); + ASSERT_FALSE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().draft == kDraftUnknown); + ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone); + SCHEMAERROR(s, "{\"SpecUnknown\":{\"errorCode\":10,\"instanceRef\":\"#\"}}"); +} + +TEST(SchemaValidator, Schema_UnsupportedDraftOverride) { + Document sd; + sd.Parse("{\"type\": \"integer\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd, 0, 0, 0, 0, 0, Specification(kDraft03)); + ASSERT_FALSE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().draft == kDraft03); + ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone); + SCHEMAERROR(s, "{\"SpecUnsupported\":{\"errorCode\":11,\"instanceRef\":\"#\"}}"); +} + +TEST(SchemaValidator, Schema_UnknownDraft) { + Document sd; + sd.Parse("{\"$schema\":\"http://json-schema.org/draft-xxx/schema#\", \"type\": \"integer\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + ASSERT_FALSE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().draft == kDraftUnknown); + ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone); + SCHEMAERROR(s, "{\"SpecUnknown\":{\"errorCode\":10,\"instanceRef\":\"#\"}}"); +} + +TEST(SchemaValidator, Schema_UnknownDraftNotString) { + Document sd; + sd.Parse("{\"$schema\": 4, \"type\": \"integer\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + ASSERT_FALSE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().draft == kDraftUnknown); + ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone); + SCHEMAERROR(s, "{\"SpecUnknown\":{\"errorCode\":10,\"instanceRef\":\"#\"}}"); +} + +TEST(SchemaValidator, Schema_UnsupportedDraft3) { + Document sd; + sd.Parse("{\"$schema\":\"http://json-schema.org/draft-03/schema#\", \"type\": \"integer\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + ASSERT_FALSE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().draft == kDraft03); + ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone); + SCHEMAERROR(s, "{\"SpecUnsupported\":{\"errorCode\":11,\"instanceRef\":\"#\"}}"); +} + +TEST(SchemaValidator, Schema_UnsupportedDraft6) { + Document sd; + sd.Parse("{\"$schema\":\"http://json-schema.org/draft-06/schema#\", \"type\": \"integer\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + ASSERT_FALSE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().draft == kDraft06); + SCHEMAERROR(s, "{\"SpecUnsupported\":{\"errorCode\":11,\"instanceRef\":\"#\"}}"); +} + +TEST(SchemaValidator, Schema_UnsupportedDraft7) { + Document sd; + sd.Parse("{\"$schema\":\"http://json-schema.org/draft-07/schema#\", \"type\": \"integer\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + ASSERT_FALSE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().draft == kDraft07); + ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone); + SCHEMAERROR(s, "{\"SpecUnsupported\":{\"errorCode\":11,\"instanceRef\":\"#\"}}"); +} + +TEST(SchemaValidator, Schema_UnsupportedDraft2019_09) { + Document sd; + sd.Parse("{\"$schema\":\"https://json-schema.org/draft/2019-09/schema\", \"type\": \"integer\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + ASSERT_FALSE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().draft == kDraft2019_09); + ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone); + SCHEMAERROR(s, "{\"SpecUnsupported\":{\"errorCode\":11,\"instanceRef\":\"#\"}}"); +} + +TEST(SchemaValidator, Schema_UnsupportedDraft2020_12) { + Document sd; + sd.Parse("{\"$schema\":\"https://json-schema.org/draft/2020-12/schema\", \"type\": \"integer\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + ASSERT_FALSE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().draft == kDraft2020_12); + ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone); + SCHEMAERROR(s, "{\"SpecUnsupported\":{\"errorCode\":11,\"instanceRef\":\"#\"}}"); +} + +TEST(SchemaValidator, Schema_SupportedVersion20Static) { + typedef GenericSchemaDocument > SchemaDocumentType; + Document sd; + sd.Parse("{\"swagger\":\"2.0\"}"); + ASSERT_FALSE(sd.HasParseError()); + Specification spec = SchemaDocumentType::GetSpecification(sd); + ASSERT_TRUE(spec.IsSupported()); + ASSERT_TRUE(spec.draft == kDraft04); + ASSERT_TRUE(spec.oapi == kVersion20); +} + +TEST(SchemaValidator, Schema_SupportedVersion20) { + Document sd; + sd.Parse("{\"swagger\":\"2.0\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + ASSERT_TRUE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().oapi == kVersion20); + ASSERT_TRUE(s.GetSpecification().draft == kDraft04); + EXPECT_TRUE(s.GetError().ObjectEmpty()); +} + +TEST(SchemaValidator, Schema_SupportedVersion30x) { + Document sd; + sd.Parse("{\"openapi\":\"3.0.0\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + ASSERT_TRUE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().oapi == kVersion30); + ASSERT_TRUE(s.GetSpecification().draft == kDraft05); + EXPECT_TRUE(s.GetError().ObjectEmpty()); +} + +TEST(SchemaValidator, Schema_SupportedVersionOverride) { + Document sd; + sd.Parse("{\"type\": \"integer\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd, 0, 0, 0, 0, 0, Specification(kVersion20)); + ASSERT_TRUE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().oapi == kVersion20); + ASSERT_TRUE(s.GetSpecification().draft == kDraft04); + EXPECT_TRUE(s.GetError().ObjectEmpty()); +} + +TEST(SchemaValidator, Schema_UnknownVersionOverride) { + Document sd; + sd.Parse("{\"type\": \"integer\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd, 0, 0, 0, 0, 0, Specification(kVersionUnknown)); + ASSERT_FALSE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().oapi == kVersionUnknown); + ASSERT_TRUE(s.GetSpecification().draft == kDraft04); + SCHEMAERROR(s, "{\"SpecUnknown\":{\"errorCode\":10,\"instanceRef\":\"#\"}}"); +} + +TEST(SchemaValidator, Schema_UnsupportedVersionOverride) { + Document sd; + sd.Parse("{\"type\": \"integer\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd, 0, 0, 0, 0, 0, Specification(kVersion31)); + ASSERT_FALSE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().oapi == kVersion31); + ASSERT_TRUE(s.GetSpecification().draft == kDraft2020_12); + SCHEMAERROR(s, "{\"SpecUnsupported\":{\"errorCode\":11,\"instanceRef\":\"#\"}}"); +} + +TEST(SchemaValidator, Schema_UnknownVersion) { + Document sd; + sd.Parse("{\"openapi\":\"1.0\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + ASSERT_FALSE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().oapi == kVersionUnknown); + ASSERT_TRUE(s.GetSpecification().draft == kDraft04); + SCHEMAERROR(s, "{\"SpecUnknown\":{\"errorCode\":10,\"instanceRef\":\"#\"}}"); +} + +TEST(SchemaValidator, Schema_UnknownVersionShort) { + Document sd; + sd.Parse("{\"openapi\":\"3.0.\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + ASSERT_FALSE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().oapi == kVersionUnknown); + ASSERT_TRUE(s.GetSpecification().draft == kDraft04); + SCHEMAERROR(s, "{\"SpecUnknown\":{\"errorCode\":10,\"instanceRef\":\"#\"}}"); +} + +TEST(SchemaValidator, Schema_UnknownVersionNotString) { + Document sd; + sd.Parse("{\"swagger\": 2}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + ASSERT_FALSE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().oapi == kVersionUnknown); + ASSERT_TRUE(s.GetSpecification().draft == kDraft04); + SCHEMAERROR(s, "{\"SpecUnknown\":{\"errorCode\":10,\"instanceRef\":\"#\"}}"); +} + +TEST(SchemaValidator, Schema_UnsupportedVersion31) { + Document sd; + sd.Parse("{\"openapi\":\"3.1.0\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + ASSERT_FALSE(s.IsSupportedSpecification()); + ASSERT_TRUE(s.GetSpecification().oapi == kVersion31); + ASSERT_TRUE(s.GetSpecification().draft == kDraft2020_12); + SCHEMAERROR(s, "{\"SpecUnsupported\":{\"errorCode\":11,\"instanceRef\":\"#\"}}"); +} + +TEST(SchemaValidator, Schema_DraftAndVersion) { + Document sd; + sd.Parse("{\"swagger\": \"2.0\", \"$schema\": \"http://json-schema.org/draft-04/schema#\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + ASSERT_TRUE(s.IsSupportedSpecification()); + SCHEMAERROR(s, "{\"SpecIllegal\":{\"errorCode\":12,\"instanceRef\":\"#\"}}"); +} + +TEST(SchemaValidator, Schema_StartUnknown) { + Document sd; + sd.Parse("{\"type\": \"integer\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd, 0, 0, 0, 0, SchemaDocument::PointerType("/nowhere")); + SCHEMAERROR(s, "{\"StartUnknown\":{\"errorCode\":1,\"instanceRef\":\"#\", \"value\":\"#/nowhere\"}}"); +} + +TEST(SchemaValidator, Schema_MultipleErrors) { + Document sd; + sd.Parse("{\"swagger\": \"foo\", \"$schema\": \"bar\"}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s(sd); + SCHEMAERROR(s, "{ \"SpecUnknown\": {\"errorCode\":10,\"instanceRef\":\"#\"}," + " \"SpecIllegal\": {\"errorCode\":12,\"instanceRef\":\"#\"}" + "}"); +} + +// $ref is a non-JSON pointer fragment - not allowed when OpenAPI +TEST(SchemaValidator, Schema_RefPlainNameOpenApi) { + typedef GenericSchemaDocument > SchemaDocumentType; + Document sd; + sd.Parse("{\"swagger\": \"2.0\", \"type\": \"object\", \"properties\": {\"myInt1\": {\"$ref\": \"#myId\"}, \"myStr\": {\"type\": \"string\", \"id\": \"#myStrId\"}, \"myInt2\": {\"type\": \"integer\", \"id\": \"#myId\"}}}"); + SchemaDocumentType s(sd); + SCHEMAERROR(s, "{\"RefPlainName\":{\"errorCode\":2,\"instanceRef\":\"#/properties/myInt1\",\"value\":\"#myId\"}}"); +} + +// $ref is a non-JSON pointer fragment - not allowed when remote document +TEST(SchemaValidator, Schema_RefPlainNameRemote) { + typedef GenericSchemaDocument > SchemaDocumentType; + RemoteSchemaDocumentProvider provider; + Document sd; + sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"/subSchemas.json#plainname\"}}}"); + SchemaDocumentType s(sd, "http://localhost:1234/xxxx", 26, &provider); + SCHEMAERROR(s, "{\"RefPlainName\":{\"errorCode\":2,\"instanceRef\":\"#/properties/myInt\",\"value\":\"#plainname\"}}"); +} + +// $ref is an empty string +TEST(SchemaValidator, Schema_RefEmptyString) { + typedef GenericSchemaDocument > SchemaDocumentType; + Document sd; + sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt1\": {\"$ref\": \"\"}}}"); + SchemaDocumentType s(sd); + SCHEMAERROR(s, "{\"RefInvalid\":{\"errorCode\":3,\"instanceRef\":\"#/properties/myInt1\"}}"); +} + +// $ref is remote but no provider +TEST(SchemaValidator, Schema_RefNoRemoteProvider) { + typedef GenericSchemaDocument > SchemaDocumentType; + Document sd; + sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"/subSchemas.json#plainname\"}}}"); + SchemaDocumentType s(sd, "http://localhost:1234/xxxx", 26, 0); + SCHEMAERROR(s, "{\"RefNoRemoteProvider\":{\"errorCode\":7,\"instanceRef\":\"#/properties/myInt\"}}"); +} + +// $ref is remote but no schema returned +TEST(SchemaValidator, Schema_RefNoRemoteSchema) { + typedef GenericSchemaDocument > SchemaDocumentType; + RemoteSchemaDocumentProvider provider; + Document sd; + sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"/will-not-resolve.json\"}}}"); + SchemaDocumentType s(sd, "http://localhost:1234/xxxx", 26, &provider); + SCHEMAERROR(s, "{\"RefNoRemoteSchema\":{\"errorCode\":8,\"instanceRef\":\"#/properties/myInt\",\"value\":\"http://localhost:1234/will-not-resolve.json\"}}"); +} + +// $ref pointer is invalid +TEST(SchemaValidator, Schema_RefPointerInvalid) { + typedef GenericSchemaDocument > SchemaDocumentType; + Document sd; + sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"#/&&&&&\"}}}"); + SchemaDocumentType s(sd); + SCHEMAERROR(s, "{\"RefPointerInvalid\":{\"errorCode\":4,\"instanceRef\":\"#/properties/myInt\",\"value\":\"#/&&&&&\",\"offset\":2}}"); +} + +// $ref is remote and pointer is invalid +TEST(SchemaValidator, Schema_RefPointerInvalidRemote) { + typedef GenericSchemaDocument > SchemaDocumentType; + RemoteSchemaDocumentProvider provider; + Document sd; + sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"/subSchemas.json#/abc&&&&&\"}}}"); + SchemaDocumentType s(sd, "http://localhost:1234/xxxx", 26, &provider); + SCHEMAERROR(s, "{\"RefPointerInvalid\":{\"errorCode\":4,\"instanceRef\":\"#/properties/myInt\",\"value\":\"#/abc&&&&&\",\"offset\":5}}"); +} + +// $ref is unknown non-pointer +TEST(SchemaValidator, Schema_RefUnknownPlainName) { + typedef GenericSchemaDocument > SchemaDocumentType; + Document sd; + sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"#plainname\"}}}"); + SchemaDocumentType s(sd); + SCHEMAERROR(s, "{\"RefUnknown\":{\"errorCode\":5,\"instanceRef\":\"#/properties/myInt\",\"value\":\"#plainname\"}}"); +} + +/// $ref is unknown pointer +TEST(SchemaValidator, Schema_RefUnknownPointer) { + typedef GenericSchemaDocument > SchemaDocumentType; + Document sd; + sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"#/a/b\"}}}"); + SchemaDocumentType s(sd); + SCHEMAERROR(s, "{\"RefUnknown\":{\"errorCode\":5,\"instanceRef\":\"#/properties/myInt\",\"value\":\"#/a/b\"}}"); +} + +// $ref is remote and unknown pointer +TEST(SchemaValidator, Schema_RefUnknownPointerRemote) { + typedef GenericSchemaDocument > SchemaDocumentType; + RemoteSchemaDocumentProvider provider; + Document sd; + sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"/subSchemas.json#/a/b\"}}}"); + SchemaDocumentType s(sd, "http://localhost:1234/xxxx", 26, &provider); + SCHEMAERROR(s, "{\"RefUnknown\":{\"errorCode\":5,\"instanceRef\":\"#/properties/myInt\",\"value\":\"http://localhost:1234/subSchemas.json#/a/b\"}}"); +} + +// $ref is cyclical +TEST(SchemaValidator, Schema_RefCyclical) { + typedef GenericSchemaDocument > SchemaDocumentType; + Document sd; + sd.Parse("{\"type\": \"object\", \"properties\": {" + " \"cyclic_source\": {" + " \"$ref\": \"#/properties/cyclic_target\"" + " }," + " \"cyclic_target\": {" + " \"$ref\": \"#/properties/cyclic_source\"" + " }" + "}}"); + SchemaDocumentType s(sd); + SCHEMAERROR(s, "{\"RefCyclical\":{\"errorCode\":6,\"instanceRef\":\"#/properties/cyclic_target\",\"value\":\"#/properties/cyclic_source\"}}"); +} + +TEST(SchemaValidator, Schema_ReadOnlyAndWriteOnly) { + Document sd; + sd.Parse("{\"type\": \"integer\", \"readOnly\": true, \"writeOnly\": true}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument s1(sd, 0, 0, 0, 0, 0, Specification(kDraft04)); + EXPECT_TRUE(s1.GetError().ObjectEmpty()); + SchemaDocument s2(sd, 0, 0, 0, 0, 0, Specification(kVersion30)); + SCHEMAERROR(s2, "{\"ReadOnlyAndWriteOnly\":{\"errorCode\":13,\"instanceRef\":\"#\"}}"); +} + +TEST(SchemaValidator, ReadOnlyWhenWriting) { + Document sd; + sd.Parse( + "{" + " \"type\":\"object\"," + " \"properties\": {" + " \"rprop\" : {" + " \"type\": \"string\"," + " \"readOnly\": true" + " }" + " }" + "}"); + SchemaDocument s(sd, 0, 0, 0, 0, 0, Specification(kVersion20)); + VALIDATE(s, "{ \"rprop\": \"hello\" }", true); + INVALIDATE_(s, "{ \"rprop\": \"hello\" }", "/properties/rprop", "readOnly", "/rprop", + "{ \"readOnly\": {" + " \"errorCode\": 26, \"instanceRef\": \"#/rprop\", \"schemaRef\": \"#/properties/rprop\"" + " }" + "}", + kValidateDefaultFlags | kValidateWriteFlag, SchemaValidator, Pointer); +} + +TEST(SchemaValidator, WriteOnlyWhenReading) { + Document sd; + sd.Parse( + "{" + " \"type\":\"object\"," + " \"properties\": {" + " \"wprop\" : {" + " \"type\": \"boolean\"," + " \"writeOnly\": true" + " }" + " }" + "}"); + SchemaDocument s(sd, 0, 0, 0, 0, 0, Specification(kVersion30)); + VALIDATE(s, "{ \"wprop\": true }", true); + INVALIDATE_(s, "{ \"wprop\": true }", "/properties/wprop", "writeOnly", "/wprop", + "{ \"writeOnly\": {" + " \"errorCode\": 27, \"instanceRef\": \"#/wprop\", \"schemaRef\": \"#/properties/wprop\"" + " }" + "}", + kValidateDefaultFlags | kValidateReadFlag, SchemaValidator, Pointer); +} + +TEST(SchemaValidator, NullableTrue) { + Document sd; + sd.Parse("{\"type\": \"string\", \"nullable\": true}"); + SchemaDocument s(sd, 0, 0, 0, 0, 0, kVersion20); + + VALIDATE(s, "\"hello\"", true); + INVALIDATE(s, "null", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"string\"], \"actual\": \"null\"" + "}}"); + INVALIDATE(s, "false", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"string\"], \"actual\": \"boolean\"" + "}}"); + + SchemaDocument s30(sd, 0, 0, 0, 0, 0, kVersion30); + + VALIDATE(s30, "\"hello\"", true); + VALIDATE(s30, "null", true); + INVALIDATE(s30, "false", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"null\", \"string\"], \"actual\": \"boolean\"" + "}}"); +} + +TEST(SchemaValidator, NullableFalse) { + Document sd; + sd.Parse("{\"type\": \"string\", \"nullable\": false}"); + SchemaDocument s(sd, 0, 0, 0, 0, 0, kVersion20); + + VALIDATE(s, "\"hello\"", true); + INVALIDATE(s, "null", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"string\"], \"actual\": \"null\"" + "}}"); + INVALIDATE(s, "false", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"string\"], \"actual\": \"boolean\"" + "}}"); + + SchemaDocument s30(sd, 0, 0, 0, 0, 0, kVersion30); + + VALIDATE(s30, "\"hello\"", true); + INVALIDATE(s, "null", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"string\"], \"actual\": \"null\"" + "}}"); + INVALIDATE(s30, "false", "", "type", "", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," + " \"expected\": [\"string\"], \"actual\": \"boolean\"" + "}}"); +} + +#if defined(_MSC_VER) || defined(__clang__) RAPIDJSON_DIAG_POP #endif diff --git a/third_party/rapidjson/test/unittest/simdtest.cpp b/third_party/rapidjson/test/unittest/simdtest.cpp index b01b559f4..570b08364 100644 --- a/third_party/rapidjson/test/unittest/simdtest.cpp +++ b/third_party/rapidjson/test/unittest/simdtest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -21,6 +21,8 @@ # define RAPIDJSON_SSE42 #elif defined(__SSE2__) # define RAPIDJSON_SSE2 +#elif defined(__ARM_NEON) +# define RAPIDJSON_NEON #endif #define RAPIDJSON_NAMESPACE rapidjson_simd @@ -41,14 +43,18 @@ using namespace rapidjson_simd; #define SIMD_SUFFIX(name) name##_SSE2 #elif defined(RAPIDJSON_SSE42) #define SIMD_SUFFIX(name) name##_SSE42 +#elif defined(RAPIDJSON_NEON) +#define SIMD_SUFFIX(name) name##_NEON #else #define SIMD_SUFFIX(name) name #endif +#define SIMD_SIZE_ALIGN(n) ((size_t(n) + 15) & ~size_t(15)) + template void TestSkipWhitespace() { for (size_t step = 1; step < 32; step++) { - char buffer[1025]; + char buffer[SIMD_SIZE_ALIGN(1025)]; for (size_t i = 0; i < 1024; i++) buffer[i] = " \t\r\n"[i % 4]; for (size_t i = 0; i < 1024; i += step) @@ -75,7 +81,7 @@ TEST(SIMD, SIMD_SUFFIX(SkipWhitespace)) { TEST(SIMD, SIMD_SUFFIX(SkipWhitespace_EncodedMemoryStream)) { for (size_t step = 1; step < 32; step++) { - char buffer[1024]; + char buffer[SIMD_SIZE_ALIGN(1024)]; for (size_t i = 0; i < 1024; i++) buffer[i] = " \t\r\n"[i % 4]; for (size_t i = 0; i < 1024; i += step) @@ -83,14 +89,12 @@ TEST(SIMD, SIMD_SUFFIX(SkipWhitespace_EncodedMemoryStream)) { MemoryStream ms(buffer, 1024); EncodedInputStream, MemoryStream> s(ms); - size_t i = 0; for (;;) { SkipWhitespace(s); if (s.Peek() == '\0') break; //EXPECT_EQ(i, s.Tell()); EXPECT_EQ('X', s.Take()); - i += step; } } } @@ -105,8 +109,8 @@ struct ScanCopyUnescapedStringHandler : BaseReaderHandler, ScanCopyUnesca template void TestScanCopyUnescapedString() { - char buffer[1024 + 5 + 32]; - char backup[1024 + 5 + 32]; + char buffer[SIMD_SIZE_ALIGN(1024u + 5 + 32)]; + char backup[SIMD_SIZE_ALIGN(1024u + 5 + 32)]; // Test "ABCDABCD...\\" for (size_t offset = 0; offset < 32; offset++) { @@ -163,7 +167,7 @@ TEST(SIMD, SIMD_SUFFIX(ScanCopyUnescapedString)) { } TEST(SIMD, SIMD_SUFFIX(ScanWriteUnescapedString)) { - char buffer[2048 + 1 + 32]; + char buffer[SIMD_SIZE_ALIGN(2048 + 1 + 32)]; for (size_t offset = 0; offset < 32; offset++) { for (size_t step = 0; step < 1024; step++) { char* s = buffer + offset; diff --git a/third_party/rapidjson/test/unittest/strfunctest.cpp b/third_party/rapidjson/test/unittest/strfunctest.cpp index cc1bb22f0..411269396 100644 --- a/third_party/rapidjson/test/unittest/strfunctest.cpp +++ b/third_party/rapidjson/test/unittest/strfunctest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/third_party/rapidjson/test/unittest/stringbuffertest.cpp b/third_party/rapidjson/test/unittest/stringbuffertest.cpp index ded513cdd..eaa29e715 100644 --- a/third_party/rapidjson/test/unittest/stringbuffertest.cpp +++ b/third_party/rapidjson/test/unittest/stringbuffertest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -26,6 +26,7 @@ using namespace rapidjson; TEST(StringBuffer, InitialSize) { StringBuffer buffer; EXPECT_EQ(0u, buffer.GetSize()); + EXPECT_EQ(0u, buffer.GetLength()); EXPECT_STREQ("", buffer.GetString()); } @@ -34,14 +35,17 @@ TEST(StringBuffer, Put) { buffer.Put('A'); EXPECT_EQ(1u, buffer.GetSize()); + EXPECT_EQ(1u, buffer.GetLength()); EXPECT_STREQ("A", buffer.GetString()); } TEST(StringBuffer, PutN_Issue672) { GenericStringBuffer, MemoryPoolAllocator<> > buffer; - EXPECT_EQ(0, buffer.GetSize()); + EXPECT_EQ(0u, buffer.GetSize()); + EXPECT_EQ(0u, buffer.GetLength()); rapidjson::PutN(buffer, ' ', 1); - EXPECT_EQ(1, buffer.GetSize()); + EXPECT_EQ(1u, buffer.GetSize()); + EXPECT_EQ(1u, buffer.GetLength()); } TEST(StringBuffer, Clear) { @@ -52,6 +56,7 @@ TEST(StringBuffer, Clear) { buffer.Clear(); EXPECT_EQ(0u, buffer.GetSize()); + EXPECT_EQ(0u, buffer.GetLength()); EXPECT_STREQ("", buffer.GetString()); } @@ -60,6 +65,7 @@ TEST(StringBuffer, Push) { buffer.Push(5); EXPECT_EQ(5u, buffer.GetSize()); + EXPECT_EQ(5u, buffer.GetLength()); // Causes sudden expansion to make the stack's capacity equal to size buffer.Push(65536u); @@ -76,9 +82,19 @@ TEST(StringBuffer, Pop) { buffer.Pop(3); EXPECT_EQ(2u, buffer.GetSize()); + EXPECT_EQ(2u, buffer.GetLength()); EXPECT_STREQ("AB", buffer.GetString()); } +TEST(StringBuffer, GetLength_Issue744) { + GenericStringBuffer > buffer; + buffer.Put('A'); + buffer.Put('B'); + buffer.Put('C'); + EXPECT_EQ(3u * sizeof(wchar_t), buffer.GetSize()); + EXPECT_EQ(3u, buffer.GetLength()); +} + #if RAPIDJSON_HAS_CXX11_RVALUE_REFS #if 0 // Many old compiler does not support these. Turn it off temporaily. @@ -130,18 +146,23 @@ TEST(StringBuffer, MoveConstructor) { x.Put('D'); EXPECT_EQ(4u, x.GetSize()); + EXPECT_EQ(4u, x.GetLength()); EXPECT_STREQ("ABCD", x.GetString()); // StringBuffer y(x); // does not compile (!is_copy_constructible) StringBuffer y(std::move(x)); EXPECT_EQ(0u, x.GetSize()); + EXPECT_EQ(0u, x.GetLength()); EXPECT_EQ(4u, y.GetSize()); + EXPECT_EQ(4u, y.GetLength()); EXPECT_STREQ("ABCD", y.GetString()); // StringBuffer z = y; // does not compile (!is_copy_assignable) StringBuffer z = std::move(y); EXPECT_EQ(0u, y.GetSize()); + EXPECT_EQ(0u, y.GetLength()); EXPECT_EQ(4u, z.GetSize()); + EXPECT_EQ(4u, z.GetLength()); EXPECT_STREQ("ABCD", z.GetString()); } @@ -153,13 +174,14 @@ TEST(StringBuffer, MoveAssignment) { x.Put('D'); EXPECT_EQ(4u, x.GetSize()); + EXPECT_EQ(4u, x.GetLength()); EXPECT_STREQ("ABCD", x.GetString()); StringBuffer y; // y = x; // does not compile (!is_copy_assignable) y = std::move(x); EXPECT_EQ(0u, x.GetSize()); - EXPECT_EQ(4u, y.GetSize()); + EXPECT_EQ(4u, y.GetLength()); EXPECT_STREQ("ABCD", y.GetString()); } diff --git a/third_party/rapidjson/test/unittest/strtodtest.cpp b/third_party/rapidjson/test/unittest/strtodtest.cpp index cde836c7e..66167a4a3 100644 --- a/third_party/rapidjson/test/unittest/strtodtest.cpp +++ b/third_party/rapidjson/test/unittest/strtodtest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -91,7 +91,7 @@ TEST(Strtod, CheckApproximationCase) { } // Remove common power of two factor from all three scaled values - int common_Exp2 = std::min(dS_Exp2, std::min(bS_Exp2, hS_Exp2)); + int common_Exp2 = (std::min)(dS_Exp2, (std::min)(bS_Exp2, hS_Exp2)); dS_Exp2 -= common_Exp2; bS_Exp2 -= common_Exp2; hS_Exp2 -= common_Exp2; diff --git a/third_party/rapidjson/test/unittest/unittest.cpp b/third_party/rapidjson/test/unittest/unittest.cpp index b754563ea..879976a78 100644 --- a/third_party/rapidjson/test/unittest/unittest.cpp +++ b/third_party/rapidjson/test/unittest/unittest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at diff --git a/third_party/rapidjson/test/unittest/unittest.h b/third_party/rapidjson/test/unittest/unittest.h index e125bf88d..0e64d3970 100644 --- a/third_party/rapidjson/test/unittest/unittest.h +++ b/third_party/rapidjson/test/unittest/unittest.h @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -78,7 +78,7 @@ inline Ch* StrDup(const Ch* str) { } inline FILE* TempFile(char *filename) { -#ifdef _MSC_VER +#if defined(__WIN32__) || defined(_MSC_VER) filename = tmpnam(filename); // For Visual Studio, tmpnam() adds a backslash in front. Remove it. @@ -117,7 +117,15 @@ public: #pragma GCC diagnostic pop #endif -#define RAPIDJSON_ASSERT(x) if (!(x)) throw AssertException(RAPIDJSON_STRINGIFY(x)) +// Not using noexcept for testing RAPIDJSON_ASSERT() +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 + +#ifndef RAPIDJSON_ASSERT +#define RAPIDJSON_ASSERT(x) (!(x) ? throw AssertException(RAPIDJSON_STRINGIFY(x)) : (void)0u) +#ifndef RAPIDJSON_ASSERT_THROWS +#define RAPIDJSON_ASSERT_THROWS +#endif +#endif class Random { public: diff --git a/third_party/rapidjson/test/unittest/uritest.cpp b/third_party/rapidjson/test/unittest/uritest.cpp new file mode 100644 index 000000000..789c9dd82 --- /dev/null +++ b/third_party/rapidjson/test/unittest/uritest.cpp @@ -0,0 +1,725 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// (C) Copyright IBM Corporation 2021 +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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. + +#define RAPIDJSON_SCHEMA_VERBOSE 0 +#define RAPIDJSON_HAS_STDSTRING 1 + +#include "unittest.h" +#include "rapidjson/document.h" +#include "rapidjson/uri.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(variadic-macros) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4822) // local class member function does not have a body +#endif + +using namespace rapidjson; + +TEST(Uri, DefaultConstructor) { + typedef GenericUri UriType; + UriType u; + EXPECT_TRUE(u.GetSchemeString() == 0); + EXPECT_TRUE(u.GetAuthString() == 0); + EXPECT_TRUE(u.GetPathString() == 0); + EXPECT_TRUE(u.GetBaseString() == 0); + EXPECT_TRUE(u.GetQueryString() == 0); + EXPECT_TRUE(u.GetFragString() == 0); + EXPECT_TRUE(u.GetString() == 0); + EXPECT_TRUE(u.GetSchemeStringLength() == 0); + EXPECT_TRUE(u.GetAuthStringLength() == 0); + EXPECT_TRUE(u.GetPathStringLength() == 0); + EXPECT_TRUE(u.GetBaseStringLength() == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); + EXPECT_TRUE(u.GetStringLength() == 0); +} + +TEST(Uri, Parse) { + typedef GenericUri > UriType; + MemoryPoolAllocator allocator; + Value v; + Value w; + + v.SetString("http://auth/path/xxx?query#frag", allocator); + UriType u = UriType(v, &allocator); + EXPECT_TRUE(StrCmp(u.GetSchemeString(), "http:") == 0); + EXPECT_TRUE(StrCmp(u.GetAuthString(), "//auth") == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), "/path/xxx") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), "http://auth/path/xxx?query") == 0); + EXPECT_TRUE(StrCmp(u.GetQueryString(), "?query") == 0); + EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag") == 0); + u.Get(w, allocator); + EXPECT_TRUE(*w.GetString() == *v.GetString()); + + v.SetString("urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f", allocator); + u = UriType(v, &allocator); + EXPECT_TRUE(StrCmp(u.GetSchemeString(), "urn:") == 0); + EXPECT_TRUE(u.GetAuthStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), "uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); + u.Get(w, allocator); + EXPECT_TRUE(*w.GetString() == *v.GetString()); + + v.SetString("", allocator); + u = UriType(v, &allocator); + EXPECT_TRUE(u.GetSchemeStringLength() == 0); + EXPECT_TRUE(u.GetAuthStringLength() == 0); + EXPECT_TRUE(u.GetPathStringLength() == 0); + EXPECT_TRUE(u.GetBaseStringLength() == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); + + v.SetString("http://auth/", allocator); + u = UriType(v, &allocator); + EXPECT_TRUE(StrCmp(u.GetSchemeString(), "http:") == 0); + EXPECT_TRUE(StrCmp(u.GetAuthString(), "//auth") == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), "/") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), "http://auth/") == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); + + u = UriType("/path/sub"); + EXPECT_TRUE(u.GetSchemeStringLength() == 0); + EXPECT_TRUE(u.GetAuthStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), "/path/sub") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), "/path/sub") == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); + + // absolute path gets normalized + u = UriType("/path/../sub/"); + EXPECT_TRUE(u.GetSchemeStringLength() == 0); + EXPECT_TRUE(u.GetAuthStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), "/sub/") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), "/sub/") == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); + + // relative path does not + u = UriType("path/../sub"); + EXPECT_TRUE(u.GetSchemeStringLength() == 0); + EXPECT_TRUE(u.GetAuthStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), "path/../sub") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), "path/../sub") == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); + + u = UriType("http://auth#frag/stuff"); + EXPECT_TRUE(StrCmp(u.GetSchemeString(), "http:") == 0); + EXPECT_TRUE(StrCmp(u.GetAuthString(), "//auth") == 0); + EXPECT_TRUE(u.GetPathStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), "http://auth") == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag/stuff") == 0); + EXPECT_TRUE(StrCmp(u.GetString(), "http://auth#frag/stuff") == 0); + + const Value::Ch c[] = { '#', 'f', 'r', 'a', 'g', '/', 's', 't', 'u', 'f', 'f', '\0'}; + SizeType len = internal::StrLen(c); + u = UriType(c, len); + EXPECT_TRUE(StrCmp(u.GetString(), "#frag/stuff") == 0); + EXPECT_TRUE(u.GetStringLength() == len); + EXPECT_TRUE(StrCmp(u.GetBaseString(), "") == 0); + EXPECT_TRUE(u.GetBaseStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag/stuff") == 0); + EXPECT_TRUE(u.GetFragStringLength() == len); + + u = UriType(c); + EXPECT_TRUE(StrCmp(u.GetString(), "#frag/stuff") == 0); + EXPECT_TRUE(u.GetStringLength() == len); + EXPECT_TRUE(StrCmp(u.GetBaseString(), "") == 0); + EXPECT_TRUE(u.GetBaseStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag/stuff") == 0); + EXPECT_TRUE(u.GetFragStringLength() == len); + + // Incomplete auth treated as path + u = UriType("http:/"); + EXPECT_TRUE(StrCmp(u.GetSchemeString(), "http:") == 0); + EXPECT_TRUE(u.GetAuthStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), "/") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), "http:/") == 0); +} + +TEST(Uri, Parse_UTF16) { + typedef GenericValue > Value16; + typedef GenericUri > UriType; + MemoryPoolAllocator allocator; + Value16 v; + Value16 w; + + v.SetString(L"http://auth/path/xxx?query#frag", allocator); + UriType u = UriType(v, &allocator); + EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"http:") == 0); + EXPECT_TRUE(StrCmp(u.GetAuthString(), L"//auth") == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), L"/path/xxx") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), L"http://auth/path/xxx?query") == 0); + EXPECT_TRUE(StrCmp(u.GetQueryString(), L"?query") == 0); + EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag") == 0); + u.Get(w, allocator); + EXPECT_TRUE(*w.GetString() == *v.GetString()); + + v.SetString(L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f", allocator); + u = UriType(v, &allocator); + EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"urn:") == 0); + EXPECT_TRUE(u.GetAuthStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), L"uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); + u.Get(w, allocator); + EXPECT_TRUE(*w.GetString() == *v.GetString()); + + v.SetString(L"", allocator); + u = UriType(v, &allocator); + EXPECT_TRUE(u.GetSchemeStringLength() == 0); + EXPECT_TRUE(u.GetAuthStringLength() == 0); + EXPECT_TRUE(u.GetPathStringLength() == 0); + EXPECT_TRUE(u.GetBaseStringLength() == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); + + v.SetString(L"http://auth/", allocator); + u = UriType(v, &allocator); + EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"http:") == 0); + EXPECT_TRUE(StrCmp(u.GetAuthString(), L"//auth") == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), L"/") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), L"http://auth/") == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); + + u = UriType(L"/path/sub"); + EXPECT_TRUE(u.GetSchemeStringLength() == 0); + EXPECT_TRUE(u.GetAuthStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), L"/path/sub") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), L"/path/sub") == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); + + // absolute path gets normalized + u = UriType(L"/path/../sub/"); + EXPECT_TRUE(u.GetSchemeStringLength() == 0); + EXPECT_TRUE(u.GetAuthStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), L"/sub/") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), L"/sub/") == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); + + // relative path does not + u = UriType(L"path/../sub"); + EXPECT_TRUE(u.GetSchemeStringLength() == 0); + EXPECT_TRUE(u.GetAuthStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), L"path/../sub") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), L"path/../sub") == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); + + u = UriType(L"http://auth#frag/stuff"); + EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"http:") == 0); + EXPECT_TRUE(StrCmp(u.GetAuthString(), L"//auth") == 0); + EXPECT_TRUE(u.GetPathStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), L"http://auth") == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag/stuff") == 0); + EXPECT_TRUE(StrCmp(u.GetString(), L"http://auth#frag/stuff") == 0); + + const Value16::Ch c[] = { '#', 'f', 'r', 'a', 'g', '/', 's', 't', 'u', 'f', 'f', '\0'}; + SizeType len = internal::StrLen(c); + u = UriType(c, len); + EXPECT_TRUE(StrCmp(u.GetString(), L"#frag/stuff") == 0); + EXPECT_TRUE(u.GetStringLength() == len); + EXPECT_TRUE(StrCmp(u.GetBaseString(), L"") == 0); + EXPECT_TRUE(u.GetBaseStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag/stuff") == 0); + EXPECT_TRUE(u.GetFragStringLength() == len); + + u = UriType(c); + EXPECT_TRUE(StrCmp(u.GetString(), L"#frag/stuff") == 0); + EXPECT_TRUE(u.GetStringLength() == len); + EXPECT_TRUE(StrCmp(u.GetBaseString(), L"") == 0); + EXPECT_TRUE(u.GetBaseStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag/stuff") == 0); + EXPECT_TRUE(u.GetFragStringLength() == len); + + // Incomplete auth treated as path + u = UriType(L"http:/"); + EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"http:") == 0); + EXPECT_TRUE(u.GetAuthStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), L"/") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), L"http:/") == 0); +} + +#if RAPIDJSON_HAS_STDSTRING +TEST(Uri, Parse_Std) { + typedef GenericUri > UriType; + MemoryPoolAllocator allocator; + typedef std::basic_string String; + + String str = "http://auth/path/xxx?query#frag"; + const UriType uri = UriType(str, &allocator); + EXPECT_TRUE(UriType::GetScheme(uri) == "http:"); + EXPECT_TRUE(UriType::GetAuth(uri) == "//auth"); + EXPECT_TRUE(UriType::GetPath(uri) == "/path/xxx"); + EXPECT_TRUE(UriType::GetBase(uri) == "http://auth/path/xxx?query"); + EXPECT_TRUE(UriType::GetQuery(uri) == "?query"); + EXPECT_TRUE(UriType::GetFrag(uri) == "#frag"); + EXPECT_TRUE(UriType::Get(uri) == str); +} + +TEST(Uri, Parse_UTF16_Std) { + typedef GenericValue > Value16; + typedef GenericUri > UriType; + MemoryPoolAllocator allocator; + typedef std::basic_string String; + + String str = L"http://auth/path/xxx?query#frag"; + const UriType uri = UriType(str, &allocator); + EXPECT_TRUE(UriType::GetScheme(uri) == L"http:"); + EXPECT_TRUE(UriType::GetAuth(uri) == L"//auth"); + EXPECT_TRUE(UriType::GetPath(uri) == L"/path/xxx"); + EXPECT_TRUE(UriType::GetBase(uri) == L"http://auth/path/xxx?query"); + EXPECT_TRUE(UriType::GetQuery(uri) == L"?query"); + EXPECT_TRUE(UriType::GetFrag(uri) == L"#frag"); + EXPECT_TRUE(UriType::Get(uri) == str); +} +#endif + +TEST(Uri, CopyConstructor) { + typedef GenericUri UriType; + CrtAllocator allocator; + + UriType u("http://auth/path/xxx?query#frag", &allocator); + UriType u2(u); + EXPECT_TRUE(u == u2); + EXPECT_NE(&u.GetAllocator(), &u2.GetAllocator()); +} + +TEST(Uri, Assignment) { + typedef GenericUri UriType; + CrtAllocator allocator; + + UriType u("http://auth/path/xxx?query#frag", &allocator); + UriType u2; + u2 = u; + EXPECT_TRUE(u == u2); + EXPECT_NE(&u.GetAllocator(), &u2.GetAllocator()); +} + +TEST(Uri, Resolve) { + typedef GenericUri UriType; + CrtAllocator allocator; + + // ref is full uri + UriType base = UriType("http://auth/path/#frag"); + UriType ref = UriType("http://newauth/newpath#newfrag"); + UriType res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://newauth/newpath#newfrag") == 0); + + base = UriType("/path/#frag", &allocator); + ref = UriType("http://newauth/newpath#newfrag", &allocator); + res = ref.Resolve(base, &allocator); + EXPECT_TRUE(StrCmp(res.GetString(), "http://newauth/newpath#newfrag") == 0); + + // ref is alternate uri + base = UriType("http://auth/path/#frag"); + ref = UriType("urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0); + + // ref is absolute path + base = UriType("http://auth/path/#"); + ref = UriType("/newpath#newfrag"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/newpath#newfrag") == 0); + + // ref is relative path + base = UriType("http://auth/path/file.json#frag"); + ref = UriType("newfile.json#"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/path/newfile.json#") == 0); + + base = UriType("http://auth/path/file.json#frag/stuff"); + ref = UriType("newfile.json#newfrag/newstuff"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/path/newfile.json#newfrag/newstuff") == 0); + + base = UriType("file.json", &allocator); + ref = UriType("newfile.json", &base.GetAllocator()); + res = ref.Resolve(base, &ref.GetAllocator()); + EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0); + + base = UriType("file.json", &allocator); + ref = UriType("./newfile.json", &allocator); + res = ref.Resolve(base, &allocator); + EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0); + + base = UriType("file.json"); + ref = UriType("parent/../newfile.json"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0); + + base = UriType("file.json"); + ref = UriType("parent/./newfile.json"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "parent/newfile.json") == 0); + + base = UriType("file.json"); + ref = UriType("../../parent/.././newfile.json"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0); + + // This adds a joining slash so resolved length is base length + ref length + 1 + base = UriType("http://auth"); + ref = UriType("newfile.json"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/newfile.json") == 0); + + // ref is fragment + base = UriType("#frag/stuff"); + ref = UriType("#newfrag/newstuff"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "#newfrag/newstuff") == 0); + + // test ref fragment always wins + base = UriType("/path#frag"); + ref = UriType(""); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "/path") == 0); + + // Examples from RFC3896 + base = UriType("http://a/b/c/d;p?q"); + ref = UriType("g:h"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "g:h") == 0); + ref = UriType("g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g") == 0); + ref = UriType("./g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g") == 0); + ref = UriType("g/"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g/") == 0); + ref = UriType("/g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0); + ref = UriType("//g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://g") == 0); + ref = UriType("?y"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/d;p?y") == 0); + ref = UriType("g?y"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g?y") == 0); + ref = UriType("#s"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/d;p?q#s") == 0); + ref = UriType("g#s"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g#s") == 0); + ref = UriType("g?y#s"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g?y#s") == 0); + ref = UriType(";x"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/;x") == 0); + ref = UriType("g;x"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g;x") == 0); + ref = UriType("g;x?y#s"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g;x?y#s") == 0); + ref = UriType(""); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/d;p?q") == 0); + ref = UriType("."); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/") == 0); + ref = UriType("./"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/") == 0); + ref = UriType(".."); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/") == 0); + ref = UriType("../"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/") == 0); + ref = UriType("../g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/g") == 0); + ref = UriType("../.."); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/") == 0); + ref = UriType("../../"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/") == 0); + ref = UriType("../../g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0); + ref = UriType("../../../g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0); + ref = UriType("../../../../g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0); + ref = UriType("/./g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0); + ref = UriType("/../g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0); + ref = UriType("g."); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g.") == 0); + ref = UriType(".g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/.g") == 0); + ref = UriType("g.."); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g..") == 0); + ref = UriType("..g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/..g") == 0); + ref = UriType("g#s/../x"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g#s/../x") == 0); +} + +TEST(Uri, Resolve_UTF16) { + typedef GenericValue > Value16; + typedef GenericUri UriType; + CrtAllocator allocator; + + // ref is full uri + UriType base = UriType(L"http://auth/path/#frag"); + UriType ref = UriType(L"http://newauth/newpath#newfrag"); + UriType res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://newauth/newpath#newfrag") == 0); + + base = UriType(L"/path/#frag"); + ref = UriType(L"http://newauth/newpath#newfrag"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://newauth/newpath#newfrag") == 0); + + // ref is alternate uri + base = UriType(L"http://auth/path/#frag"); + ref = UriType(L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0); + + // ref is absolute path + base = UriType(L"http://auth/path/#"); + ref = UriType(L"/newpath#newfrag"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/newpath#newfrag") == 0); + + // ref is relative path + base = UriType(L"http://auth/path/file.json#frag"); + ref = UriType(L"newfile.json#"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/path/newfile.json#") == 0); + + base = UriType(L"http://auth/path/file.json#frag/stuff"); + ref = UriType(L"newfile.json#newfrag/newstuff"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/path/newfile.json#newfrag/newstuff") == 0); + + base = UriType(L"file.json", &allocator); + ref = UriType(L"newfile.json", &base.GetAllocator()); + res = ref.Resolve(base, &ref.GetAllocator()); + EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0); + + base = UriType(L"file.json", &allocator); + ref = UriType(L"./newfile.json", &allocator); + res = ref.Resolve(base, &allocator); + EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0); + + base = UriType(L"file.json"); + ref = UriType(L"parent/../newfile.json"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0); + + base = UriType(L"file.json"); + ref = UriType(L"parent/./newfile.json"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"parent/newfile.json") == 0); + + base = UriType(L"file.json"); + ref = UriType(L"../../parent/.././newfile.json"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0); + + // This adds a joining slash so resolved length is base length + ref length + 1 + base = UriType(L"http://auth"); + ref = UriType(L"newfile.json"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/newfile.json") == 0); + + // ref is fragment + base = UriType(L"#frag/stuff"); + ref = UriType(L"#newfrag/newstuff"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"#newfrag/newstuff") == 0); + + // test ref fragment always wins + base = UriType(L"/path#frag"); + ref = UriType(L""); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"/path") == 0); + + // Examples from RFC3896 + base = UriType(L"http://a/b/c/d;p?q"); + ref = UriType(L"g:h"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"g:h") == 0); + ref = UriType(L"g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g") == 0); + ref = UriType(L"./g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g") == 0); + ref = UriType(L"g/"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g/") == 0); + ref = UriType(L"/g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0); + ref = UriType(L"//g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://g") == 0); + ref = UriType(L"?y"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/d;p?y") == 0); + ref = UriType(L"g?y"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g?y") == 0); + ref = UriType(L"#s"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/d;p?q#s") == 0); + ref = UriType(L"g#s"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g#s") == 0); + ref = UriType(L"g?y#s"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g?y#s") == 0); + ref = UriType(L";x"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/;x") == 0); + ref = UriType(L"g;x"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g;x") == 0); + ref = UriType(L"g;x?y#s"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g;x?y#s") == 0); + ref = UriType(L""); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/d;p?q") == 0); + ref = UriType(L"."); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/") == 0); + ref = UriType(L"./"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/") == 0); + ref = UriType(L".."); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/") == 0); + ref = UriType(L"../"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/") == 0); + ref = UriType(L"../g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/g") == 0); + ref = UriType(L"../.."); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/") == 0); + ref = UriType(L"../../"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/") == 0); + ref = UriType(L"../../g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0); + ref = UriType(L"../../../g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0); + ref = UriType(L"../../../../g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0); + ref = UriType(L"/./g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0); + ref = UriType(L"/../g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0); + ref = UriType(L"g."); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g.") == 0); + ref = UriType(L".g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/.g") == 0); + ref = UriType(L"g.."); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g..") == 0); + ref = UriType(L"..g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/..g") == 0); + ref = UriType(L"g#s/../x"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g#s/../x") == 0); +} + +TEST(Uri, Equals) { + typedef GenericUri UriType; + + UriType a = UriType("http://a/a#a"); + UriType b = UriType("http://a/a#b"); + UriType c = a; + + EXPECT_TRUE(a == a); + EXPECT_TRUE(a == c); + EXPECT_TRUE(a != b); +} + +TEST(Uri, Match) { + typedef GenericUri UriType; + + UriType a = UriType("http://a/a#a"); + UriType b = UriType("http://a/a#b"); + UriType c = a; + UriType d; + + EXPECT_TRUE(a.Match(a)); + EXPECT_TRUE(a.Match(c)); + EXPECT_FALSE(a.Match(b)); + EXPECT_FALSE(a.Match(b, true)); + EXPECT_TRUE(a.Match(b, false)); // Base Uri same + EXPECT_FALSE(a.Match(d)); + EXPECT_FALSE(d.Match(a)); +} + +TEST(Uri, Issue1899) { + typedef GenericUri > UriType; + + UriType base = UriType("http://auth/path/#frag"); + UriType ref = UriType("http://newauth/newpath#newfrag"); + UriType res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://newauth/newpath#newfrag") == 0); +} + +#if defined(_MSC_VER) || defined(__clang__) +RAPIDJSON_DIAG_POP +#endif diff --git a/third_party/rapidjson/test/unittest/valuetest.cpp b/third_party/rapidjson/test/unittest/valuetest.cpp index fefc001d4..aeaaf2fd8 100644 --- a/third_party/rapidjson/test/unittest/valuetest.cpp +++ b/third_party/rapidjson/test/unittest/valuetest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -26,11 +26,11 @@ using namespace rapidjson; TEST(Value, Size) { if (sizeof(SizeType) == 4) { #if RAPIDJSON_48BITPOINTER_OPTIMIZATION - EXPECT_EQ(16, sizeof(Value)); + EXPECT_EQ(16u, sizeof(Value)); #elif RAPIDJSON_64BIT - EXPECT_EQ(24, sizeof(Value)); + EXPECT_EQ(24u, sizeof(Value)); #else - EXPECT_EQ(16, sizeof(Value)); + EXPECT_EQ(16u, sizeof(Value)); #endif } } @@ -439,6 +439,17 @@ TEST(Value, Int) { EXPECT_EQ(5678, z.Get()); EXPECT_EQ(5679, z.Set(5679).Get()); EXPECT_EQ(5680, z.Set(5680).Get()); + +#ifdef _MSC_VER + // long as int on MSC platforms + RAPIDJSON_STATIC_ASSERT(sizeof(long) == sizeof(int)); + z.SetInt(2222); + EXPECT_TRUE(z.Is()); + EXPECT_EQ(2222l, z.Get()); + EXPECT_EQ(3333l, z.Set(3333l).Get()); + EXPECT_EQ(4444l, z.Set(4444l).Get()); + EXPECT_TRUE(z.IsInt()); +#endif } TEST(Value, Uint) { @@ -485,6 +496,17 @@ TEST(Value, Uint) { EXPECT_EQ(2147483648u, z.Get()); EXPECT_EQ(2147483649u, z.Set(2147483649u).Get()); EXPECT_EQ(2147483650u, z.Set(2147483650u).Get()); + +#ifdef _MSC_VER + // unsigned long as unsigned on MSC platforms + RAPIDJSON_STATIC_ASSERT(sizeof(unsigned long) == sizeof(unsigned)); + z.SetUint(2222); + EXPECT_TRUE(z.Is()); + EXPECT_EQ(2222ul, z.Get()); + EXPECT_EQ(3333ul, z.Set(3333ul).Get()); + EXPECT_EQ(4444ul, z.Set(4444ul).Get()); + EXPECT_TRUE(x.IsUint()); +#endif } TEST(Value, Int64) { @@ -857,9 +879,46 @@ TEST(Value, String) { } // Issue 226: Value of string type should not point to NULL -TEST(Value, SetStringNullException) { - Value v; - EXPECT_THROW(v.SetString(0, 0), AssertException); +TEST(Value, SetStringNull) { + + MemoryPoolAllocator<> allocator; + const char* nullPtr = 0; + { + // Construction with string type creates empty string + Value v(kStringType); + EXPECT_NE(v.GetString(), nullPtr); // non-null string returned + EXPECT_EQ(v.GetStringLength(), 0u); + + // Construction from/setting to null without length not allowed + EXPECT_THROW(Value(StringRef(nullPtr)), AssertException); + EXPECT_THROW(Value(StringRef(nullPtr), allocator), AssertException); + EXPECT_THROW(v.SetString(nullPtr, allocator), AssertException); + + // Non-empty length with null string is not allowed + EXPECT_THROW(v.SetString(nullPtr, 17u), AssertException); + EXPECT_THROW(v.SetString(nullPtr, 42u, allocator), AssertException); + + // Setting to null string with empty length is allowed + v.SetString(nullPtr, 0u); + EXPECT_NE(v.GetString(), nullPtr); // non-null string returned + EXPECT_EQ(v.GetStringLength(), 0u); + + v.SetNull(); + v.SetString(nullPtr, 0u, allocator); + EXPECT_NE(v.GetString(), nullPtr); // non-null string returned + EXPECT_EQ(v.GetStringLength(), 0u); + } + // Construction with null string and empty length is allowed + { + Value v(nullPtr,0u); + EXPECT_NE(v.GetString(), nullPtr); // non-null string returned + EXPECT_EQ(v.GetStringLength(), 0u); + } + { + Value v(nullPtr, 0u, allocator); + EXPECT_NE(v.GetString(), nullPtr); // non-null string returned + EXPECT_EQ(v.GetStringLength(), 0u); + } } template @@ -1001,7 +1060,7 @@ static void TestArray(T& x, Allocator& allocator) { x.Clear(); for (unsigned i = 0; i < n; i++) x.PushBack(Value(kArrayType).PushBack(i, allocator).Move(), allocator); - + itr = x.Erase(x.Begin() + first, x.Begin() + last); if (last == n) EXPECT_EQ(x.End(), itr); @@ -1019,9 +1078,9 @@ static void TestArray(T& x, Allocator& allocator) { } TEST(Value, Array) { + Value::AllocatorType allocator; Value x(kArrayType); const Value& y = x; - Value::AllocatorType allocator; EXPECT_EQ(kArrayType, x.GetType()); EXPECT_TRUE(x.IsArray()); @@ -1060,6 +1119,16 @@ TEST(Value, Array) { z.SetArray(); EXPECT_TRUE(z.IsArray()); EXPECT_TRUE(z.Empty()); + + // PR #1503: assign from inner Value + { + CrtAllocator a; // Free() is not a noop + GenericValue, CrtAllocator> nullValue; + GenericValue, CrtAllocator> arrayValue(kArrayType); + arrayValue.PushBack(nullValue, a); + arrayValue = arrayValue[0]; // shouldn't crash (use after free) + EXPECT_TRUE(arrayValue.IsNull()); + } } TEST(Value, ArrayHelper) { @@ -1076,10 +1145,10 @@ TEST(Value, ArrayHelper) { a.PushBack(1, allocator); Value::Array a2(a); // copy constructor - EXPECT_EQ(1, a2.Size()); + EXPECT_EQ(1u, a2.Size()); Value::Array a3 = a; - EXPECT_EQ(1, a3.Size()); + EXPECT_EQ(1u, a3.Size()); Value::ConstArray y = static_cast(x).GetArray(); (void)y; @@ -1116,7 +1185,7 @@ TEST(Value, ArrayHelper) { y.PushBack(123, allocator); x.PushBack(y.GetArray(), allocator); // Implicit constructor to convert Array to GenericValue - EXPECT_EQ(1, x.Size()); + EXPECT_EQ(1u, x.Size()); EXPECT_EQ(123, x[0][0].GetInt()); EXPECT_TRUE(y.IsArray()); EXPECT_TRUE(y.Empty()); @@ -1365,7 +1434,7 @@ static void TestObject(T& x, Allocator& allocator) { for (; itr != x.MemberEnd(); ++itr) { size_t i = static_cast((itr - x.MemberBegin())) + 1; EXPECT_STREQ(itr->name.GetString(), keys[i]); - EXPECT_EQ(i, itr->value[0].GetInt()); + EXPECT_EQ(static_cast(i), itr->value[0].GetInt()); } // Erase the last @@ -1376,7 +1445,7 @@ static void TestObject(T& x, Allocator& allocator) { for (; itr != x.MemberEnd(); ++itr) { size_t i = static_cast(itr - x.MemberBegin()) + 1; EXPECT_STREQ(itr->name.GetString(), keys[i]); - EXPECT_EQ(i, itr->value[0].GetInt()); + EXPECT_EQ(static_cast(i), itr->value[0].GetInt()); } // Erase the middle @@ -1388,7 +1457,7 @@ static void TestObject(T& x, Allocator& allocator) { size_t i = static_cast(itr - x.MemberBegin()); i += (i < 4) ? 1 : 2; EXPECT_STREQ(itr->name.GetString(), keys[i]); - EXPECT_EQ(i, itr->value[0].GetInt()); + EXPECT_EQ(static_cast(i), itr->value[0].GetInt()); } // EraseMember(ConstMemberIterator, ConstMemberIterator) @@ -1422,9 +1491,9 @@ static void TestObject(T& x, Allocator& allocator) { } TEST(Value, Object) { + Value::AllocatorType allocator; Value x(kObjectType); const Value& y = x; // const version - Value::AllocatorType allocator; EXPECT_EQ(kObjectType, x.GetType()); EXPECT_TRUE(x.IsObject()); @@ -1457,10 +1526,10 @@ TEST(Value, ObjectHelper) { o.AddMember("1", 1, allocator); Value::Object o2(o); // copy constructor - EXPECT_EQ(1, o2.MemberCount()); + EXPECT_EQ(1u, o2.MemberCount()); Value::Object o3 = o; - EXPECT_EQ(1, o3.MemberCount()); + EXPECT_EQ(1u, o3.MemberCount()); Value::ConstObject y = static_cast(x).GetObject(); (void)y; @@ -1487,7 +1556,7 @@ TEST(Value, ObjectHelper) { EXPECT_STREQ("apple", y["a"].GetString()); EXPECT_TRUE(x.IsObject()); // Invariant } - + { Value x(kObjectType); x.AddMember("a", "apple", allocator); @@ -1512,7 +1581,7 @@ TEST(Value, ObjectHelperRangeFor) { { int i = 0; for (auto& m : x.GetObject()) { - char name[10]; + char name[11]; sprintf(name, "%d", i); EXPECT_STREQ(name, m.name.GetString()); EXPECT_EQ(i, m.value.GetInt()); @@ -1523,7 +1592,7 @@ TEST(Value, ObjectHelperRangeFor) { { int i = 0; for (const auto& m : const_cast(x).GetObject()) { - char name[10]; + char name[11]; sprintf(name, "%d", i); EXPECT_STREQ(name, m.name.GetString()); EXPECT_EQ(i, m.value.GetInt()); @@ -1580,10 +1649,11 @@ TEST(Value, BigNestedObject) { MemoryPoolAllocator<> allocator; Value x(kObjectType); static const SizeType n = 200; + const char* format = std::numeric_limits::is_signed ? "%d" : "%u"; for (SizeType i = 0; i < n; i++) { char name1[10]; - sprintf(name1, "%d", i); + sprintf(name1, format, i); // Value name(name1); // should not compile Value name(name1, static_cast(strlen(name1)), allocator); @@ -1591,7 +1661,7 @@ TEST(Value, BigNestedObject) { for (SizeType j = 0; j < n; j++) { char name2[10]; - sprintf(name2, "%d", j); + sprintf(name2, format, j); Value name3(name2, static_cast(strlen(name2)), allocator); Value number(static_cast(i * n + j)); @@ -1604,11 +1674,11 @@ TEST(Value, BigNestedObject) { for (SizeType i = 0; i < n; i++) { char name1[10]; - sprintf(name1, "%d", i); - + sprintf(name1, format, i); + for (SizeType j = 0; j < n; j++) { char name2[10]; - sprintf(name2, "%d", j); + sprintf(name2, format, j); x[name1]; EXPECT_EQ(static_cast(i * n + j), x[name1][name2].GetInt()); } @@ -1620,8 +1690,8 @@ TEST(Value, BigNestedObject) { TEST(Value, RemoveLastElement) { rapidjson::Document doc; rapidjson::Document::AllocatorType& allocator = doc.GetAllocator(); - rapidjson::Value objVal(rapidjson::kObjectType); - objVal.AddMember("var1", 123, allocator); + rapidjson::Value objVal(rapidjson::kObjectType); + objVal.AddMember("var1", 123, allocator); objVal.AddMember("var2", "444", allocator); objVal.AddMember("var3", 555, allocator); EXPECT_TRUE(objVal.HasMember("var3")); @@ -1643,22 +1713,22 @@ TEST(Document, CrtAllocator) { static void TestShortStringOptimization(const char* str) { const rapidjson::SizeType len = static_cast(strlen(str)); - + rapidjson::Document doc; rapidjson::Value val; val.SetString(str, len, doc.GetAllocator()); - - EXPECT_EQ(val.GetStringLength(), len); - EXPECT_STREQ(val.GetString(), str); + + EXPECT_EQ(val.GetStringLength(), len); + EXPECT_STREQ(val.GetString(), str); } TEST(Value, AllocateShortString) { - TestShortStringOptimization(""); // edge case: empty string - TestShortStringOptimization("12345678"); // regular case for short strings: 8 chars - TestShortStringOptimization("12345678901"); // edge case: 11 chars in 32-bit mode (=> short string) - TestShortStringOptimization("123456789012"); // edge case: 12 chars in 32-bit mode (=> regular string) - TestShortStringOptimization("123456789012345"); // edge case: 15 chars in 64-bit mode (=> short string) - TestShortStringOptimization("1234567890123456"); // edge case: 16 chars in 64-bit mode (=> regular string) + TestShortStringOptimization(""); // edge case: empty string + TestShortStringOptimization("12345678"); // regular case for short strings: 8 chars + TestShortStringOptimization("12345678901"); // edge case: 11 chars in 32-bit mode (=> short string) + TestShortStringOptimization("123456789012"); // edge case: 12 chars in 32-bit mode (=> regular string) + TestShortStringOptimization("123456789012345"); // edge case: 15 chars in 64-bit mode (=> short string) + TestShortStringOptimization("1234567890123456"); // edge case: 16 chars in 64-bit mode (=> regular string) } template @@ -1733,7 +1803,7 @@ static void MergeDuplicateKey(Value& v, Value::AllocatorType& a) { // Convert all key:value into key:[value] for (Value::MemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) itr->value = Value(kArrayType).Move().PushBack(itr->value, a); - + // Merge arrays if key is duplicated for (Value::MemberIterator itr = v.MemberBegin(); itr != v.MemberEnd();) { Value::MemberIterator itr2 = v.FindMember(itr->name); diff --git a/third_party/rapidjson/test/unittest/writertest.cpp b/third_party/rapidjson/test/unittest/writertest.cpp index 29f762609..4c2412109 100644 --- a/third_party/rapidjson/test/unittest/writertest.cpp +++ b/third_party/rapidjson/test/unittest/writertest.cpp @@ -1,6 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -20,6 +20,11 @@ #include "rapidjson/stringbuffer.h" #include "rapidjson/memorybuffer.h" +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + using namespace rapidjson; TEST(Writer, Compact) { @@ -95,6 +100,19 @@ TEST(Writer, String) { #endif } +TEST(Writer, Issue_889) { + char buf[100] = "Hello"; + + StringBuffer buffer; + Writer writer(buffer); + writer.StartArray(); + writer.String(buf); + writer.EndArray(); + + EXPECT_STREQ("[\"Hello\"]", buffer.GetString()); + EXPECT_TRUE(writer.IsComplete()); \ +} + TEST(Writer, ScanWriteUnescapedString) { const char json[] = "[\" \\\"0123456789ABCDEF\"]"; // ^ scanning stops here. @@ -394,8 +412,10 @@ TEST(Writer, ValidateEncoding) { EXPECT_TRUE(writer.String("\xC2\xA2")); // Cents sign U+00A2 EXPECT_TRUE(writer.String("\xE2\x82\xAC")); // Euro sign U+20AC EXPECT_TRUE(writer.String("\xF0\x9D\x84\x9E")); // G clef sign U+1D11E + EXPECT_TRUE(writer.String("\x01")); // SOH control U+0001 + EXPECT_TRUE(writer.String("\x1B")); // Escape control U+001B writer.EndArray(); - EXPECT_STREQ("[\"\x24\",\"\xC2\xA2\",\"\xE2\x82\xAC\",\"\xF0\x9D\x84\x9E\"]", buffer.GetString()); + EXPECT_STREQ("[\"\x24\",\"\xC2\xA2\",\"\xE2\x82\xAC\",\"\xF0\x9D\x84\x9E\",\"\\u0001\",\"\\u001B\"]", buffer.GetString()); } // Fail in decoding invalid UTF-8 sequence http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt @@ -437,6 +457,28 @@ TEST(Writer, InvalidEventSequence) { EXPECT_THROW(writer.Int(1), AssertException); EXPECT_FALSE(writer.IsComplete()); } + + // { 'a' } + { + StringBuffer buffer; + Writer writer(buffer); + writer.StartObject(); + writer.Key("a"); + EXPECT_THROW(writer.EndObject(), AssertException); + EXPECT_FALSE(writer.IsComplete()); + } + + // { 'a':'b','c' } + { + StringBuffer buffer; + Writer writer(buffer); + writer.StartObject(); + writer.Key("a"); + writer.String("b"); + writer.Key("c"); + EXPECT_THROW(writer.EndObject(), AssertException); + EXPECT_FALSE(writer.IsComplete()); + } } TEST(Writer, NaN) { @@ -458,6 +500,18 @@ TEST(Writer, NaN) { EXPECT_FALSE(writer2.Double(nan)); } +TEST(Writer, NaNToNull) { + double nan = std::numeric_limits::quiet_NaN(); + + EXPECT_TRUE(internal::Double(nan).IsNan()); + { + StringBuffer buffer; + Writer, UTF8<>, CrtAllocator, kWriteNanAndInfNullFlag> writer(buffer); + EXPECT_TRUE(writer.Double(nan)); + EXPECT_STREQ("null", buffer.GetString()); + } +} + TEST(Writer, Inf) { double inf = std::numeric_limits::infinity(); @@ -482,6 +536,24 @@ TEST(Writer, Inf) { EXPECT_STREQ("Infinity-Infinity", buffer.GetString()); } +TEST(Writer, InfToNull) { + double inf = std::numeric_limits::infinity(); + + EXPECT_TRUE(internal::Double(inf).IsInf()); + { + StringBuffer buffer; + Writer, UTF8<>, CrtAllocator, kWriteNanAndInfNullFlag> writer(buffer); + EXPECT_TRUE(writer.Double(inf)); + EXPECT_STREQ("null", buffer.GetString()); + } + { + StringBuffer buffer; + Writer, UTF8<>, CrtAllocator, kWriteNanAndInfNullFlag> writer(buffer); + EXPECT_TRUE(writer.Double(-inf)); + EXPECT_STREQ("null", buffer.GetString()); + } +} + TEST(Writer, RawValue) { StringBuffer buffer; Writer writer(buffer); @@ -495,3 +567,62 @@ TEST(Writer, RawValue) { EXPECT_TRUE(writer.IsComplete()); EXPECT_STREQ("{\"a\":1,\"raw\":[\"Hello\\nWorld\", 123.456]}", buffer.GetString()); } + +TEST(Write, RawValue_Issue1152) { + { + GenericStringBuffer > sb; + Writer >, UTF8<>, UTF32<> > writer(sb); + writer.RawValue("null", 4, kNullType); + EXPECT_TRUE(writer.IsComplete()); + const unsigned *out = sb.GetString(); + EXPECT_EQ(static_cast('n'), out[0]); + EXPECT_EQ(static_cast('u'), out[1]); + EXPECT_EQ(static_cast('l'), out[2]); + EXPECT_EQ(static_cast('l'), out[3]); + EXPECT_EQ(static_cast(0 ), out[4]); + } + + { + GenericStringBuffer > sb; + Writer >, UTF16<>, UTF8<> > writer(sb); + writer.RawValue(L"null", 4, kNullType); + EXPECT_TRUE(writer.IsComplete()); + EXPECT_STREQ("null", sb.GetString()); + } + + { + // Fail in transcoding + GenericStringBuffer > buffer; + Writer >, UTF8<>, UTF16<> > writer(buffer); + EXPECT_FALSE(writer.RawValue("\"\xfe\"", 3, kStringType)); + } + + { + // Fail in encoding validation + StringBuffer buffer; + Writer, UTF8<>, CrtAllocator, kWriteValidateEncodingFlag> writer(buffer); + EXPECT_FALSE(writer.RawValue("\"\xfe\"", 3, kStringType)); + } +} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +static Writer WriterGen(StringBuffer &target) { + Writer writer(target); + writer.StartObject(); + writer.Key("a"); + writer.Int(1); + return writer; +} + +TEST(Writer, MoveCtor) { + StringBuffer buffer; + Writer writer(WriterGen(buffer)); + writer.EndObject(); + EXPECT_TRUE(writer.IsComplete()); + EXPECT_STREQ("{\"a\":1}", buffer.GetString()); +} +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif diff --git a/third_party/rapidjson/test/valgrind.supp b/third_party/rapidjson/test/valgrind.supp new file mode 100644 index 000000000..c9d3d2265 --- /dev/null +++ b/third_party/rapidjson/test/valgrind.supp @@ -0,0 +1,26 @@ +{ + Suppress wcslen valgrind report 1 + Memcheck:Cond + fun:__wcslen_sse2 +} + +{ + Suppress wcslen valgrind report 2 + Memcheck:Addr8 + fun:__wcslen_sse2 +} + +{ + Suppress wcslen valgrind report 3 + Memcheck:Value8 + fun:__wcslen_sse2 +} + +{ + Suppress wmemcmp valgrind report 4 + Memcheck:Addr32 + fun:__wmemcmp_avx2_movbe + ... + fun:*Uri*Parse_UTF16_Std* +} + diff --git a/third_party/rapidjson/travis-doxygen.sh b/third_party/rapidjson/travis-doxygen.sh index 31a50cfa9..cf18dc343 100755 --- a/third_party/rapidjson/travis-doxygen.sh +++ b/third_party/rapidjson/travis-doxygen.sh @@ -4,12 +4,10 @@ set -e -DOXYGEN_VER=doxygen-1.8.7 -DOXYGEN_TAR=${DOXYGEN_VER}.linux.bin.tar.gz -DOXYGEN_URL="http://ftp.stack.nl/pub/users/dimitri/${DOXYGEN_TAR}" -DOXYGEN_BIN="/usr/local/bin/doxygen" +DOXYGEN_VER=1_8_16 +DOXYGEN_URL="https://codeload.github.com/doxygen/doxygen/tar.gz/Release_${DOXYGEN_VER}" -: ${GITHUB_REPO:="miloyip/rapidjson"} +: ${GITHUB_REPO:="Tencent/rapidjson"} GITHUB_HOST="github.com" GITHUB_CLONE="git://${GITHUB_HOST}/${GITHUB_REPO}" GITHUB_URL="https://${GITHUB_HOST}/${GITHUB_PUSH-${GITHUB_REPO}}" @@ -48,9 +46,17 @@ abort() { # install doxygen binary distribution doxygen_install() { - wget -O - "${DOXYGEN_URL}" | \ - tar xz -C ${TMPDIR-/tmp} ${DOXYGEN_VER}/bin/doxygen - export PATH="${TMPDIR-/tmp}/${DOXYGEN_VER}/bin:$PATH" + cd ${TMPDIR-/tmp} + curl ${DOXYGEN_URL} -o doxygen.tar.gz + tar zxvf doxygen.tar.gz + mkdir doxygen_build + cd doxygen_build + cmake ../doxygen-Release_${DOXYGEN_VER}/ + make + + export PATH="${TMPDIR-/tmp}/doxygen_build/bin:$PATH" + + cd ../../ } doxygen_run() @@ -66,7 +72,7 @@ gh_pages_prepare() [ ! -d "html" ] || \ abort "Doxygen target directory already exists." git --version - git clone -b gh-pages "${GITHUB_CLONE}" html + git clone --single-branch -b gh-pages "${GITHUB_CLONE}" html cd html # setup git config (with defaults) git config user.name "${GIT_NAME-travis}" From 1a6f4c44e7ddabaceb3a000adb2f1b65b285fd06 Mon Sep 17 00:00:00 2001 From: Siarhei Fedartsou Date: Mon, 27 May 2024 08:33:05 +0200 Subject: [PATCH 08/28] Fix performance-type-promotion-in-math-fn clang-tidy warning (#6908) --- .clang-tidy | 1 - include/engine/api/route_api.hpp | 33 ++++++++++++++++---------------- src/engine/plugins/tile.cpp | 8 ++++---- src/extractor/raster_source.cpp | 4 ++-- unit_tests/library/route.cpp | 8 ++++---- 5 files changed, 26 insertions(+), 28 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index c724cc0c4..89a5afdc4 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -54,7 +54,6 @@ Checks: > performance-*, -performance-noexcept-move-constructor, -performance-no-int-to-ptr, - -performance-type-promotion-in-math-fn, readability-*, -readability-avoid-const-params-in-decls, -readability-braces-around-statements, diff --git a/include/engine/api/route_api.hpp b/include/engine/api/route_api.hpp index cbff9ce3b..3b7c4f17c 100644 --- a/include/engine/api/route_api.hpp +++ b/include/engine/api/route_api.hpp @@ -443,23 +443,22 @@ class RouteAPI : public BaseAPI if (requested_annotations & RouteParameters::AnnotationsType::Speed) { double prev_speed = 0; - speed = - GetAnnotations(fb_result, - leg_geometry, - [&prev_speed](const guidance::LegGeometry::Annotation &anno) - { - if (anno.duration < std::numeric_limits::min()) - { - return prev_speed; - } - else - { - auto speed = - round(anno.distance / anno.duration * 10.) / 10.; - prev_speed = speed; - return util::json::clamp_float(speed); - } - }); + speed = GetAnnotations( + fb_result, + leg_geometry, + [&prev_speed](const guidance::LegGeometry::Annotation &anno) + { + if (anno.duration < std::numeric_limits::min()) + { + return prev_speed; + } + else + { + auto speed = std::round(anno.distance / anno.duration * 10.) / 10.; + prev_speed = speed; + return util::json::clamp_float(speed); + } + }); } flatbuffers::Offset> duration; diff --git a/src/engine/plugins/tile.cpp b/src/engine/plugins/tile.cpp index 17f896b6d..6bf9254eb 100644 --- a/src/engine/plugins/tile.cpp +++ b/src/engine/plugins/tile.cpp @@ -497,13 +497,13 @@ void encodeVectorTile(const DataFacadeBase &facade, { // Calculate the speed for this line std::uint32_t speed_kmh_idx = static_cast( - round(length / from_alias(forward_duration) * 10 * 3.6)); + std::round(length / from_alias(forward_duration) * 10 * 3.6)); // Rate values are in meters per weight-unit - and similar to speeds, we // present 1 decimal place of precision (these values are added as // double/10) lower down std::uint32_t forward_rate = static_cast( - round(length / from_alias(forward_weight) * 10.)); + std::round(length / from_alias(forward_weight) * 10.)); auto tile_line = coordinatesToTileLine(a, b, tile_bbox); if (!tile_line.empty()) @@ -531,13 +531,13 @@ void encodeVectorTile(const DataFacadeBase &facade, { // Calculate the speed for this line std::uint32_t speed_kmh_idx = static_cast( - round(length / from_alias(reverse_duration) * 10 * 3.6)); + std::round(length / from_alias(reverse_duration) * 10 * 3.6)); // Rate values are in meters per weight-unit - and similar to speeds, we // present 1 decimal place of precision (these values are added as // double/10) lower down std::uint32_t reverse_rate = static_cast( - round(length / from_alias(reverse_weight) * 10.)); + std::round(length / from_alias(reverse_weight) * 10.)); auto tile_line = coordinatesToTileLine(b, a, tile_bbox); if (!tile_line.empty()) diff --git a/src/extractor/raster_source.cpp b/src/extractor/raster_source.cpp index 72e9f8f5b..cda4f78f5 100644 --- a/src/extractor/raster_source.cpp +++ b/src/extractor/raster_source.cpp @@ -40,8 +40,8 @@ RasterDatum RasterSource::GetRasterData(const int lon, const int lat) const return {}; } - const std::size_t xth = static_cast(round((lon - xmin) / xstep)); - const std::size_t yth = static_cast(round((ymax - lat) / ystep)); + const std::size_t xth = static_cast(std::round((lon - xmin) / xstep)); + const std::size_t yth = static_cast(std::round((ymax - lat) / ystep)); return {raster_data(xth, yth)}; } diff --git a/unit_tests/library/route.cpp b/unit_tests/library/route.cpp index cc823816d..1e2f427a7 100644 --- a/unit_tests/library/route.cpp +++ b/unit_tests/library/route.cpp @@ -54,8 +54,8 @@ void test_route_same_coordinates_fixture(bool use_json_only_api) itr.get().values["hint"] = ""; // Round value to 6 decimal places for double comparison later - itr.get().values["distance"] = - round(itr.get().values["distance"].get().value * 1000000); + itr.get().values["distance"] = std::round( + itr.get().values["distance"].get().value * 1000000); } const auto location = json::Array{{{7.437070}, {43.749248}}}; @@ -65,11 +65,11 @@ void test_route_same_coordinates_fixture(bool use_json_only_api) {"waypoints", json::Array{{json::Object{{{"name", "Boulevard du Larvotto"}, {"location", location}, - {"distance", round(0.137249 * 1000000)}, + {"distance", std::round(0.137249 * 1000000)}, {"hint", ""}}}, json::Object{{{"name", "Boulevard du Larvotto"}, {"location", location}, - {"distance", round(0.137249 * 1000000)}, + {"distance", std::round(0.137249 * 1000000)}, {"hint", ""}}}}}}, {"routes", json::Array{{json::Object{ From 9aaab7a53f5c0ad21bc599ac4084b2ba9b839c70 Mon Sep 17 00:00:00 2001 From: Siarhei Fedartsou Date: Mon, 27 May 2024 08:33:26 +0200 Subject: [PATCH 09/28] Enable readability-container-contains clang-tidy check (#6909) --- .clang-tidy | 1 - include/extractor/traffic_signals.hpp | 6 +++--- src/engine/guidance/lane_processing.cpp | 8 ++++---- .../routing_algorithms/alternative_path_mld.cpp | 6 +++--- src/engine/routing_algorithms/many_to_many_mld.cpp | 2 +- src/engine/routing_algorithms/tile_turns.cpp | 6 +++--- src/extractor/edge_based_graph_factory.cpp | 8 ++++---- src/extractor/graph_compressor.cpp | 2 +- .../intersection/intersection_analysis.cpp | 2 +- src/extractor/suffix_table.cpp | 2 +- src/extractor/way_restriction_map.cpp | 2 +- src/guidance/roundabout_handler.cpp | 6 +++--- src/guidance/turn_lane_handler.cpp | 2 +- src/partitioner/dinic_max_flow.cpp | 13 +++++++------ 14 files changed, 33 insertions(+), 33 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 89a5afdc4..229e2e69a 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -82,7 +82,6 @@ Checks: > -readability-make-member-function-const, -readability-redundant-string-init, -readability-non-const-parameter, - -readability-container-contains, -readability-static-accessed-through-instance WarningsAsErrors: '*' diff --git a/include/extractor/traffic_signals.hpp b/include/extractor/traffic_signals.hpp index febb5d25d..af0d3daeb 100644 --- a/include/extractor/traffic_signals.hpp +++ b/include/extractor/traffic_signals.hpp @@ -17,18 +17,18 @@ struct TrafficSignals inline bool HasSignal(NodeID from, NodeID to) const { - return bidirectional_nodes.count(to) > 0 || unidirectional_segments.count({from, to}) > 0; + return bidirectional_nodes.contains(to) || unidirectional_segments.contains({from, to}); } void Compress(NodeID from, NodeID via, NodeID to) { bidirectional_nodes.erase(via); - if (unidirectional_segments.count({via, to})) + if (unidirectional_segments.contains({via, to})) { unidirectional_segments.erase({via, to}); unidirectional_segments.insert({from, to}); } - if (unidirectional_segments.count({via, from})) + if (unidirectional_segments.contains({via, from})) { unidirectional_segments.erase({via, from}); unidirectional_segments.insert({to, from}); diff --git a/src/engine/guidance/lane_processing.cpp b/src/engine/guidance/lane_processing.cpp index fb2aadfb1..8dde68127 100644 --- a/src/engine/guidance/lane_processing.cpp +++ b/src/engine/guidance/lane_processing.cpp @@ -125,9 +125,9 @@ std::vector anticipateLaneChange(std::vector steps, if (previous_is_straight) { - if (isLeftTurn(current_inst) || is_straight_left.count(¤t) > 0) + if (isLeftTurn(current_inst) || is_straight_left.contains(¤t)) is_straight_left.insert(&previous); - else if (isRightTurn(current_inst) || is_straight_right.count(¤t) > 0) + else if (isRightTurn(current_inst) || is_straight_right.contains(¤t)) is_straight_right.insert(&previous); } @@ -190,9 +190,9 @@ std::vector anticipateLaneChange(std::vector steps, // // coming from right, going to left (in direction of way) -> handle as left turn - if (is_straight_left.count(¤t) > 0) + if (is_straight_left.contains(¤t)) anticipate_for_left_turn(); - else if (is_straight_right.count(¤t) > 0) + else if (is_straight_right.contains(¤t)) anticipate_for_right_turn(); else // FIXME: right-sided driving anticipate_for_right_turn(); diff --git a/src/engine/routing_algorithms/alternative_path_mld.cpp b/src/engine/routing_algorithms/alternative_path_mld.cpp index 25d0d94e9..27bd0a4d5 100644 --- a/src/engine/routing_algorithms/alternative_path_mld.cpp +++ b/src/engine/routing_algorithms/alternative_path_mld.cpp @@ -248,7 +248,7 @@ filterViaCandidatesByViaNotOnPath(const WeightedViaNodePackedPath &path, RandIt for (const auto &edge : path.path) nodes.insert(std::get<1>(edge)); - const auto via_on_path = [&](const auto via) { return nodes.count(via.node) > 0; }; + const auto via_on_path = [&](const auto via) { return nodes.contains(via.node); }; return std::remove_if(first, last, via_on_path); } @@ -308,7 +308,7 @@ RandIt filterPackedPathsByCellSharing(RandIt first, { const auto source_cell = get_cell(std::get<0>(edge)); const auto target_cell = get_cell(std::get<1>(edge)); - return cells.count(source_cell) < 1 && cells.count(target_cell) < 1; + return !cells.contains(source_cell) && !cells.contains(target_cell); }; const auto different = std::count_if(begin(packed.path), end(packed.path), not_seen); @@ -491,7 +491,7 @@ RandIt filterUnpackedPathsBySharing(RandIt first, { auto node_duration = facade.GetNodeDuration(node); total_duration += node_duration; - if (nodes.count(node) > 0) + if (nodes.contains(node)) { return duration + node_duration; } diff --git a/src/engine/routing_algorithms/many_to_many_mld.cpp b/src/engine/routing_algorithms/many_to_many_mld.cpp index e40c889ba..0949c7488 100644 --- a/src/engine/routing_algorithms/many_to_many_mld.cpp +++ b/src/engine/routing_algorithms/many_to_many_mld.cpp @@ -325,7 +325,7 @@ oneToManySearch(SearchEngineData &engine_working_data, EdgeDuration initial_duration, EdgeDistance initial_distance) { - if (target_nodes_index.count(node)) + if (target_nodes_index.contains(node)) { // Source and target on the same edge node. If target is not reachable directly via // the node (e.g destination is before source on oneway segment) we want to allow diff --git a/src/engine/routing_algorithms/tile_turns.cpp b/src/engine/routing_algorithms/tile_turns.cpp index e2d133930..90538aee5 100644 --- a/src/engine/routing_algorithms/tile_turns.cpp +++ b/src/engine/routing_algorithms/tile_turns.cpp @@ -49,7 +49,7 @@ std::vector generateTurns(const datafacade &facade, { // operator[] will construct an empty vector at [edge.u] if there is no value. directed_graph[edge.u].push_back({edge.v, edge.forward_segment_id.id}); - if (edge_based_node_info.count(edge.forward_segment_id.id) == 0) + if (!edge_based_node_info.contains(edge.forward_segment_id.id)) { edge_based_node_info[edge.forward_segment_id.id] = {true, get_geometry_id(edge)}; } @@ -64,7 +64,7 @@ std::vector generateTurns(const datafacade &facade, if (edge.reverse_segment_id.enabled) { directed_graph[edge.v].push_back({edge.u, edge.reverse_segment_id.id}); - if (edge_based_node_info.count(edge.reverse_segment_id.id) == 0) + if (!edge_based_node_info.contains(edge.reverse_segment_id.id)) { edge_based_node_info[edge.reverse_segment_id.id] = {false, get_geometry_id(edge)}; } @@ -106,7 +106,7 @@ std::vector generateTurns(const datafacade &facade, { // If the target of this edge doesn't exist in our directed // graph, it's probably outside the tile, so we can skip it - if (directed_graph.count(approachedge.target_node) == 0) + if (!directed_graph.contains(approachedge.target_node)) continue; // For each of the outgoing edges from our target coordinate diff --git a/src/extractor/edge_based_graph_factory.cpp b/src/extractor/edge_based_graph_factory.cpp index bc50a031e..ae46d58af 100644 --- a/src/extractor/edge_based_graph_factory.cpp +++ b/src/extractor/edge_based_graph_factory.cpp @@ -167,7 +167,7 @@ NBGToEBG EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const N m_edge_based_node_container.nodes[nbe_to_ebn_mapping[edge_id_1]].annotation_id = forward_data.annotation_data; m_edge_based_node_container.nodes[nbe_to_ebn_mapping[edge_id_1]].segregated = - segregated_edges.count(edge_id_1) > 0; + segregated_edges.contains(edge_id_1); if (nbe_to_ebn_mapping[edge_id_2] != SPECIAL_EDGEID) { @@ -176,7 +176,7 @@ NBGToEBG EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const N m_edge_based_node_container.nodes[nbe_to_ebn_mapping[edge_id_2]].annotation_id = reverse_data.annotation_data; m_edge_based_node_container.nodes[nbe_to_ebn_mapping[edge_id_2]].segregated = - segregated_edges.count(edge_id_2) > 0; + segregated_edges.contains(edge_id_2); } // Add segments of edge-based nodes @@ -382,7 +382,7 @@ EdgeBasedGraphFactory::GenerateEdgeExpandedNodes(const WayRestrictionMap &way_re m_edge_based_node_container.nodes[edge_based_node_id].annotation_id = edge_data.annotation_data; m_edge_based_node_container.nodes[edge_based_node_id].segregated = - segregated_edges.count(eid) > 0; + segregated_edges.contains(eid); const auto ebn_weight = m_edge_based_node_weights[nbe_to_ebn_mapping[eid]]; BOOST_ASSERT((ebn_weight & EdgeWeight{0x7fffffff}) == edge_data.weight); @@ -942,7 +942,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( const auto turn_nodes = NodeBasedTurn{ incoming_edge.node, intersection_node, outgoing_edge_target}; - const auto is_maneuver_turn = unresolved_turns.count(turn_nodes) > 0; + const auto is_maneuver_turn = unresolved_turns.contains(turn_nodes); if (is_maneuver_turn) { diff --git a/src/extractor/graph_compressor.cpp b/src/extractor/graph_compressor.cpp index ba1e8bebc..931668f26 100644 --- a/src/extractor/graph_compressor.cpp +++ b/src/extractor/graph_compressor.cpp @@ -83,7 +83,7 @@ void GraphCompressor::Compress(const std::unordered_set &barrier_nodes, } // check if v is an entry/exit via node for a turn restriction - if (incompressible_via_nodes.count(node_v) > 0) + if (incompressible_via_nodes.contains(node_v)) { continue; } diff --git a/src/extractor/intersection/intersection_analysis.cpp b/src/extractor/intersection/intersection_analysis.cpp index 98c8ef8a7..4ea3985ec 100644 --- a/src/extractor/intersection/intersection_analysis.cpp +++ b/src/extractor/intersection/intersection_analysis.cpp @@ -622,7 +622,7 @@ IntersectionView convertToIntersectionView(const util::NodeBasedDynamicGraph &gr for (const auto &outgoing_edge : outgoing_edges) { const auto edge_it = findEdge(edge_geometries, outgoing_edge.edge); - const auto is_merged = merged_edges.count(outgoing_edge.edge) != 0; + const auto is_merged = merged_edges.contains(outgoing_edge.edge); const auto is_turn_allowed = intersection::isTurnAllowed(graph, node_data_container, restriction_map, diff --git a/src/extractor/suffix_table.cpp b/src/extractor/suffix_table.cpp index 10b421a5c..f1177cc61 100644 --- a/src/extractor/suffix_table.cpp +++ b/src/extractor/suffix_table.cpp @@ -28,7 +28,7 @@ bool SuffixTable::isSuffix(const std::string &possible_suffix) const bool SuffixTable::isSuffix(std::string_view possible_suffix) const { - return suffix_set.count(possible_suffix) > 0; + return suffix_set.contains(possible_suffix); } } // namespace osrm::extractor diff --git a/src/extractor/way_restriction_map.cpp b/src/extractor/way_restriction_map.cpp index d49bec87b..890d58031 100644 --- a/src/extractor/way_restriction_map.cpp +++ b/src/extractor/way_restriction_map.cpp @@ -18,7 +18,7 @@ std::size_t WayRestrictionMap::NumberOfDuplicatedNodes() const bool WayRestrictionMap::IsViaWayEdge(const NodeID from, const NodeID to) const { - return restriction_graph.via_edge_to_node.count({from, to}) > 0; + return restriction_graph.via_edge_to_node.contains({from, to}); } std::vector WayRestrictionMap::DuplicatedNodeIDs(const NodeID from, diff --git a/src/guidance/roundabout_handler.cpp b/src/guidance/roundabout_handler.cpp index 29f7173f5..82998a4e1 100644 --- a/src/guidance/roundabout_handler.cpp +++ b/src/guidance/roundabout_handler.cpp @@ -302,7 +302,7 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const if (roundabout == circular) return RoundaboutType::None; - while (0 == roundabout_nodes.count(last_node)) + while (!roundabout_nodes.contains(last_node)) { // only count exits/entry locations if (node_based_graph.GetOutDegree(last_node) > 2) @@ -345,8 +345,8 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const // used with a reference and without. This will be fixed automatically // when we handle references separately or if the useage is more consistent const auto is_rotary = (1 == roundabout_name_ids.size()) && - (circular || // - ((0 == connected_names.count(*roundabout_name_ids.begin())) && // + (circular || // + ((!connected_names.contains(*roundabout_name_ids.begin())) && // (radius > MAX_ROUNDABOUT_RADIUS))); if (is_rotary) diff --git a/src/guidance/turn_lane_handler.cpp b/src/guidance/turn_lane_handler.cpp index 98938525d..9b9bcbace 100644 --- a/src/guidance/turn_lane_handler.cpp +++ b/src/guidance/turn_lane_handler.cpp @@ -508,7 +508,7 @@ bool TurnLaneHandler::isSimpleIntersection(const LaneDataVector &lane_data, }(); BOOST_ASSERT(best_match != intersection.end()); std::size_t match_index = std::distance(intersection.begin(), best_match); - all_simple &= (matched_indices.count(match_index) == 0); + all_simple &= (!matched_indices.contains(match_index)); matched_indices.insert(match_index); // in case of u-turns, we might need to activate them first all_simple &= (best_match->entry_allowed || diff --git a/src/partitioner/dinic_max_flow.cpp b/src/partitioner/dinic_max_flow.cpp index ae1765b6d..4038e4b25 100644 --- a/src/partitioner/dinic_max_flow.cpp +++ b/src/partitioner/dinic_max_flow.cpp @@ -22,7 +22,7 @@ auto makeHasNeighborNotInCheck(const DinicMaxFlow::SourceSinkNodes &set, return [&](const NodeID nid) { const auto is_not_contained = [&set](const BisectionEdge &edge) - { return set.count(edge.target) == 0; }; + { return !set.contains(edge.target); }; return view.EndEdges(nid) != std::find_if(view.BeginEdges(nid), view.EndEdges(nid), is_not_contained); }; @@ -128,7 +128,7 @@ DinicMaxFlow::ComputeLevelGraph(const BisectionGraphView &view, levels[node_id] = 0; level_queue.push(node_id); for (const auto &edge : view.Edges(node_id)) - if (source_nodes.count(edge.target)) + if (source_nodes.contains(edge.target)) levels[edge.target] = 0; } // check if there is flow present on an edge @@ -139,7 +139,7 @@ DinicMaxFlow::ComputeLevelGraph(const BisectionGraphView &view, const auto relax_node = [&](const NodeID node_id) { // don't relax sink nodes - if (sink_nodes.count(node_id)) + if (sink_nodes.contains(node_id)) return; const auto level = levels[node_id] + 1; @@ -264,7 +264,7 @@ std::vector DinicMaxFlow::GetAugmentingPath(LevelGraph &levels, dfs_stack.top().edge_iterator++; // check if the edge is valid - const auto has_capacity = flow[target].count(path.back()) == 0; + const auto has_capacity = !flow[target].contains(path.back()); const auto descends_level_graph = levels[target] + 1 == levels[path.back()]; if (has_capacity && descends_level_graph) @@ -300,8 +300,9 @@ bool DinicMaxFlow::Validate(const BisectionGraphView &view, // sink and source cannot share a common node const auto separated = std::find_if(source_nodes.begin(), source_nodes.end(), - [&sink_nodes](const auto node) - { return sink_nodes.count(node); }) == source_nodes.end(); + [&sink_nodes](const auto node) { + return sink_nodes.contains(node); + }) == source_nodes.end(); const auto invalid_id = [&view](const NodeID nid) { return nid >= view.NumberOfNodes(); }; const auto in_range_source = From d4dc297f75b574f0236eb8ea08670082d2493719 Mon Sep 17 00:00:00 2001 From: Siarhei Fedartsou Date: Mon, 27 May 2024 09:31:13 +0200 Subject: [PATCH 10/28] Add CI job with GCC 14 (#6905) --- .github/workflows/osrm-backend.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/osrm-backend.yml b/.github/workflows/osrm-backend.yml index 090e9fea7..1b92a2c8a 100644 --- a/.github/workflows/osrm-backend.yml +++ b/.github/workflows/osrm-backend.yml @@ -245,6 +245,16 @@ jobs: CXXCOMPILER: clang++-15 ENABLE_CONAN: ON + - name: gcc-14-release + continue-on-error: false + node: 20 + runs-on: ubuntu-24.04 + BUILD_TOOLS: ON + BUILD_TYPE: Release + CCOMPILER: gcc-14 + CXXCOMPILER: g++-14 + CXXFLAGS: '-Wno-array-bounds -Wno-uninitialized' + - name: gcc-13-release continue-on-error: false node: 20 From 01b1673c8ab2176539203303c6f766da8667d5d7 Mon Sep 17 00:00:00 2001 From: Siarhei Fedartsou Date: Tue, 28 May 2024 08:43:23 +0200 Subject: [PATCH 11/28] Fix CCache usage in GitHub Actions (#6911) --- .github/workflows/osrm-backend.yml | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/.github/workflows/osrm-backend.yml b/.github/workflows/osrm-backend.yml index 1b92a2c8a..01aec1748 100644 --- a/.github/workflows/osrm-backend.yml +++ b/.github/workflows/osrm-backend.yml @@ -15,7 +15,6 @@ env: CCACHE_TEMPDIR: /tmp/.ccache-temp CCACHE_COMPRESS: 1 CASHER_TIME_OUT: 599 # one second less than 10m to avoid 10m timeout error: https://github.com/Project-OSRM/osrm-backend/issues/2742 - CCACHE_VERSION: 3.3.1 CMAKE_VERSION: 3.21.2 ENABLE_NODE_BINDINGS: "ON" @@ -390,6 +389,9 @@ jobs: - name: Prepare environment run: | + echo "CCACHE_DIR=$HOME/.ccache" >> $GITHUB_ENV + mkdir -p $HOME/.ccache + PACKAGE_JSON_VERSION=$(node -e "console.log(require('./package.json').version)") echo PUBLISH=$([[ "${GITHUB_REF:-}" == "refs/tags/v${PACKAGE_JSON_VERSION}" ]] && echo "On" || echo "Off") >> $GITHUB_ENV echo "OSRM_INSTALL_DIR=${GITHUB_WORKSPACE}/install-osrm" >> $GITHUB_ENV @@ -498,7 +500,7 @@ jobs: echo "Using ${JOBS} jobs" pushd ${OSRM_BUILD_DIR} - + ccache --zero-stats cmake .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ -DENABLE_CONAN=${ENABLE_CONAN:-OFF} \ -DENABLE_ASSERTIONS=${ENABLE_ASSERTIONS:-OFF} \ @@ -515,7 +517,6 @@ jobs: if [[ "${NODE_PACKAGE_TESTS_ONLY}" != "ON" ]]; then make tests --jobs=${JOBS} make benchmarks --jobs=${JOBS} - ccache -s sudo make install if [[ "${RUNNER_OS}" == "Linux" ]]; then echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${OSRM_INSTALL_DIR}/lib" >> $GITHUB_ENV @@ -618,7 +619,10 @@ jobs: omitNameDuringUpdate: true replacesArtifacts: true token: ${{ secrets.GITHUB_TOKEN }} - + - name: Show CCache statistics + run: | + ccache -p + ccache -s benchmarks: if: github.event_name == 'pull_request' @@ -652,7 +656,16 @@ jobs: with: ref: ${{ github.head_ref }} path: pr - - run: python3 -m pip install "conan<2.0.0" "requests==2.31.0" + - name: Install dependencies + run: | + python3 -m pip install "conan<2.0.0" "requests==2.31.0" + sudo apt-get update -y && sudo apt-get install ccache + - name: Prepare environment + run: | + echo "CCACHE_DIR=$HOME/.ccache" >> $GITHUB_ENV + mkdir -p $HOME/.ccache + ccache --zero-stats + ccache --max-size=256M - name: Build PR Branch run: | mkdir -p pr/build @@ -682,6 +695,10 @@ jobs: - name: Post Benchmark Results run: | python3 pr/scripts/ci/post_benchmark_results.py base_results pr_results + - name: Show CCache statistics + run: | + ccache -p + ccache -s ci-complete: runs-on: ubuntu-22.04 From c1ed73126dd467171dc7adb4ad07864909bcb90f Mon Sep 17 00:00:00 2001 From: Siarhei Fedartsou Date: Tue, 28 May 2024 18:52:49 +0200 Subject: [PATCH 12/28] Use std::variant instead of mapbox::util::variant (#6903) --- .github/workflows/osrm-backend.yml | 9 - CHANGELOG.md | 1 + CMakeLists.txt | 3 - example/example.cpp | 14 +- include/engine/api/base_result.hpp | 5 +- include/engine/api/json_factory.hpp | 4 +- include/engine/api/match_api.hpp | 6 +- include/engine/api/nearest_api.hpp | 6 +- include/engine/api/route_api.hpp | 19 +- include/engine/api/table_api.hpp | 32 +- include/engine/api/trip_api.hpp | 6 +- include/engine/plugins/plugin_base.hpp | 2 +- include/extractor/internal_extractor_edge.hpp | 2 +- include/extractor/maneuver_override.hpp | 2 +- include/extractor/restriction.hpp | 4 +- include/extractor/turn_path.hpp | 78 +- include/nodejs/json_v8_renderer.hpp | 4 +- include/nodejs/node_osrm_support.hpp | 11 +- include/server/service/base_service.hpp | 2 +- include/util/geojson_debug_logger.hpp | 2 +- include/util/geojson_debug_policies.hpp | 2 +- include/util/geojson_debug_policy_toolkit.hpp | 8 +- include/util/json_container.hpp | 11 +- include/util/json_deep_compare.hpp | 22 +- include/util/json_renderer.hpp | 4 +- scripts/update_dependencies.sh | 5 +- src/benchmarks/json_render.cpp | 2 +- src/benchmarks/match.cpp | 4 +- src/benchmarks/route.cpp | 2 +- src/engine/api/json_factory.cpp | 4 +- src/engine/plugins/tile.cpp | 2 +- .../routing_algorithms/routing_base_mld.cpp | 60 + src/nodejs/node_osrm.cpp | 8 +- src/osrm/osrm.cpp | 12 +- src/partitioner/partitioner.cpp | 1 - src/server/request_handler.cpp | 21 +- src/server/service/match_service.cpp | 2 +- src/server/service/nearest_service.cpp | 2 +- src/server/service/route_service.cpp | 2 +- src/server/service/table_service.cpp | 2 +- src/server/service/tile_service.cpp | 4 +- src/server/service/trip_service.cpp | 4 +- src/server/service_handler.cpp | 4 +- src/util/geojson_debug_policies.cpp | 2 +- third_party/variant/.clang-format | 89 - third_party/variant/.gitignore | 9 - third_party/variant/.gitmodules | 3 - third_party/variant/.mason | 1 - third_party/variant/.travis.yml | 159 - third_party/variant/.ycm_extra_conf.py | 40 - third_party/variant/CHANGELOG.md | 420 - third_party/variant/Jamroot | 62 - third_party/variant/LICENSE | 25 - third_party/variant/LICENSE_1_0.txt | 23 - third_party/variant/Makefile | 157 - third_party/variant/README.md | 248 - third_party/variant/appveyor.yml | 17 - third_party/variant/common.gypi | 143 - .../variant/doc/other_implementations.md | 15 - third_party/variant/doc/standards_effort.md | 28 - .../variant/include/mapbox/optional.hpp | 74 - .../include/mapbox/recursive_wrapper.hpp | 122 - .../variant/include/mapbox/variant.hpp | 1053 -- .../variant/include/mapbox/variant_cast.hpp | 85 - .../variant/include/mapbox/variant_io.hpp | 45 - .../include/mapbox/variant_visitor.hpp | 43 - third_party/variant/package.json | 10 - .../variant/scripts/build-appveyor.bat | 32 - third_party/variant/scripts/build-local.bat | 7 - .../scripts/run_compilation_failure_tests.sh | 49 - third_party/variant/test/bench_variant.cpp | 184 - .../variant/test/binary_visitor_test.cpp | 138 - .../test/boost_variant_hello_world.cpp | 20 - .../default_constructor.cpp | 21 - .../compilation_failure/empty_typelist.cpp | 10 - .../test/compilation_failure/equality.cpp | 10 - .../test/compilation_failure/get_type.cpp | 9 - .../test/compilation_failure/is_type.cpp | 9 - .../mutating_visitor_on_const.cpp | 23 - .../test/compilation_failure/no-reference.cpp | 8 - third_party/variant/test/hashable_test.cpp | 158 - .../variant/test/include/auto_cpu_timer.hpp | 16 - third_party/variant/test/include/catch.hpp | 11422 ---------------- .../variant/test/lambda_overload_test.cpp | 282 - .../variant/test/our_variant_hello_world.cpp | 20 - .../variant/test/recursive_wrapper_test.cpp | 124 - .../variant/test/reference_wrapper_test.cpp | 79 - .../variant/test/t/binary_visitor_1.cpp | 7 - .../variant/test/t/binary_visitor_2.cpp | 7 - .../variant/test/t/binary_visitor_3.cpp | 7 - .../variant/test/t/binary_visitor_4.cpp | 7 - .../variant/test/t/binary_visitor_5.cpp | 7 - .../variant/test/t/binary_visitor_6.cpp | 7 - .../variant/test/t/binary_visitor_impl.hpp | 204 - third_party/variant/test/t/issue122.cpp | 20 - third_party/variant/test/t/issue21.cpp | 52 - .../variant/test/t/mutating_visitor.cpp | 36 - third_party/variant/test/t/nothrow_move.cpp | 66 - third_party/variant/test/t/optional.cpp | 103 - .../variant/test/t/recursive_wrapper.cpp | 185 - third_party/variant/test/t/sizeof.cpp | 51 - third_party/variant/test/t/unary_visitor.cpp | 127 - third_party/variant/test/t/variant.cpp | 551 - .../variant/test/t/variant_alternative.cpp | 31 - .../variant/test/t/visitor_result_type.cpp | 52 - third_party/variant/test/unique_ptr_test.cpp | 126 - third_party/variant/test/unit.cpp | 2 - third_party/variant/variant.gyp | 35 - unit_tests/library/json.cpp | 20 +- unit_tests/library/limits.cpp | 24 +- unit_tests/library/match.cpp | 50 +- unit_tests/library/nearest.cpp | 36 +- unit_tests/library/route.cpp | 192 +- unit_tests/library/table.cpp | 58 +- unit_tests/library/tile.cpp | 2 +- unit_tests/library/trip.cpp | 102 +- unit_tests/library/waypoint_check.hpp | 12 +- 117 files changed, 472 insertions(+), 17601 deletions(-) create mode 100644 src/engine/routing_algorithms/routing_base_mld.cpp delete mode 100644 third_party/variant/.clang-format delete mode 100644 third_party/variant/.gitignore delete mode 100644 third_party/variant/.gitmodules delete mode 160000 third_party/variant/.mason delete mode 100644 third_party/variant/.travis.yml delete mode 100644 third_party/variant/.ycm_extra_conf.py delete mode 100644 third_party/variant/CHANGELOG.md delete mode 100644 third_party/variant/Jamroot delete mode 100644 third_party/variant/LICENSE delete mode 100644 third_party/variant/LICENSE_1_0.txt delete mode 100644 third_party/variant/Makefile delete mode 100644 third_party/variant/README.md delete mode 100644 third_party/variant/appveyor.yml delete mode 100644 third_party/variant/common.gypi delete mode 100644 third_party/variant/doc/other_implementations.md delete mode 100644 third_party/variant/doc/standards_effort.md delete mode 100644 third_party/variant/include/mapbox/optional.hpp delete mode 100644 third_party/variant/include/mapbox/recursive_wrapper.hpp delete mode 100644 third_party/variant/include/mapbox/variant.hpp delete mode 100644 third_party/variant/include/mapbox/variant_cast.hpp delete mode 100644 third_party/variant/include/mapbox/variant_io.hpp delete mode 100644 third_party/variant/include/mapbox/variant_visitor.hpp delete mode 100644 third_party/variant/package.json delete mode 100644 third_party/variant/scripts/build-appveyor.bat delete mode 100644 third_party/variant/scripts/build-local.bat delete mode 100755 third_party/variant/scripts/run_compilation_failure_tests.sh delete mode 100644 third_party/variant/test/bench_variant.cpp delete mode 100644 third_party/variant/test/binary_visitor_test.cpp delete mode 100644 third_party/variant/test/boost_variant_hello_world.cpp delete mode 100644 third_party/variant/test/compilation_failure/default_constructor.cpp delete mode 100644 third_party/variant/test/compilation_failure/empty_typelist.cpp delete mode 100644 third_party/variant/test/compilation_failure/equality.cpp delete mode 100644 third_party/variant/test/compilation_failure/get_type.cpp delete mode 100644 third_party/variant/test/compilation_failure/is_type.cpp delete mode 100644 third_party/variant/test/compilation_failure/mutating_visitor_on_const.cpp delete mode 100644 third_party/variant/test/compilation_failure/no-reference.cpp delete mode 100644 third_party/variant/test/hashable_test.cpp delete mode 100644 third_party/variant/test/include/auto_cpu_timer.hpp delete mode 100644 third_party/variant/test/include/catch.hpp delete mode 100644 third_party/variant/test/lambda_overload_test.cpp delete mode 100644 third_party/variant/test/our_variant_hello_world.cpp delete mode 100644 third_party/variant/test/recursive_wrapper_test.cpp delete mode 100644 third_party/variant/test/reference_wrapper_test.cpp delete mode 100644 third_party/variant/test/t/binary_visitor_1.cpp delete mode 100644 third_party/variant/test/t/binary_visitor_2.cpp delete mode 100644 third_party/variant/test/t/binary_visitor_3.cpp delete mode 100644 third_party/variant/test/t/binary_visitor_4.cpp delete mode 100644 third_party/variant/test/t/binary_visitor_5.cpp delete mode 100644 third_party/variant/test/t/binary_visitor_6.cpp delete mode 100644 third_party/variant/test/t/binary_visitor_impl.hpp delete mode 100644 third_party/variant/test/t/issue122.cpp delete mode 100644 third_party/variant/test/t/issue21.cpp delete mode 100644 third_party/variant/test/t/mutating_visitor.cpp delete mode 100644 third_party/variant/test/t/nothrow_move.cpp delete mode 100644 third_party/variant/test/t/optional.cpp delete mode 100644 third_party/variant/test/t/recursive_wrapper.cpp delete mode 100644 third_party/variant/test/t/sizeof.cpp delete mode 100644 third_party/variant/test/t/unary_visitor.cpp delete mode 100644 third_party/variant/test/t/variant.cpp delete mode 100644 third_party/variant/test/t/variant_alternative.cpp delete mode 100644 third_party/variant/test/t/visitor_result_type.cpp delete mode 100644 third_party/variant/test/unique_ptr_test.cpp delete mode 100644 third_party/variant/test/unit.cpp delete mode 100644 third_party/variant/variant.gyp diff --git a/.github/workflows/osrm-backend.yml b/.github/workflows/osrm-backend.yml index 01aec1748..9a35cd04a 100644 --- a/.github/workflows/osrm-backend.yml +++ b/.github/workflows/osrm-backend.yml @@ -274,15 +274,6 @@ jobs: CXXCOMPILER: g++-12 CXXFLAGS: '-Wno-array-bounds -Wno-uninitialized' - - name: gcc-11-release - continue-on-error: false - node: 20 - runs-on: ubuntu-22.04 - BUILD_TOOLS: ON - BUILD_TYPE: Release - CCOMPILER: gcc-11 - CXXCOMPILER: g++-11 - - name: conan-linux-release-node build_node_package: true continue-on-error: false diff --git a/CHANGELOG.md b/CHANGELOG.md index c19a18d90..a22841b00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - NodeJS: - CHANGED: Use node-api instead of NAN. [#6452](https://github.com/Project-OSRM/osrm-backend/pull/6452) - Misc: + - CHANGED: Use std::variant instead of mapbox::util::variant. [#6903](https://github.com/Project-OSRM/osrm-backend/pull/6903) - CHANGED: Bump rapidjson to version f9d53419e912910fd8fa57d5705fa41425428c35 [#6906](https://github.com/Project-OSRM/osrm-backend/pull/6906) - CHANGED: Bump mapbox/variant to version 1.2.0 [#6898](https://github.com/Project-OSRM/osrm-backend/pull/6898) - CHANGED: Avoid copy of std::function-based callback in path unpacking [#6895](https://github.com/Project-OSRM/osrm-backend/pull/6895) diff --git a/CMakeLists.txt b/CMakeLists.txt index fb135888b..5aff11255 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,7 +121,6 @@ endif() include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}/include/) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/include/) include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/third_party/sol2-3.3.0/include) -include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/third_party/variant/include) set(BOOST_COMPONENTS date_time chrono filesystem iostreams program_options regex system thread unit_test_framework) @@ -607,7 +606,6 @@ if (BUILD_ROUTED) set_property(TARGET osrm-routed PROPERTY INSTALL_RPATH_USE_LINK_PATH TRUE) endif() -file(GLOB VariantGlob third_party/variant/include/mapbox/*.hpp) file(GLOB FlatbuffersGlob third_party/flatbuffers/include/flatbuffers/*.h) file(GLOB LibraryGlob include/osrm/*.hpp) file(GLOB ParametersGlob include/engine/api/*_parameters.hpp) @@ -627,7 +625,6 @@ install(FILES ${ContractorHeader} DESTINATION include/osrm/contractor) install(FILES ${LibraryGlob} DESTINATION include/osrm) install(FILES ${ParametersGlob} DESTINATION include/osrm/engine/api) install(FILES ${ApiHeader} DESTINATION include/osrm/engine/api) -install(FILES ${VariantGlob} DESTINATION include/mapbox) install(FILES ${FlatbuffersGlob} DESTINATION include/flatbuffers) install(TARGETS osrm-extract DESTINATION bin) install(TARGETS osrm-partition DESTINATION bin) diff --git a/example/example.cpp b/example/example.cpp index 108fc622e..1d08eb9fc 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -57,15 +57,15 @@ int main(int argc, const char *argv[]) // Execute routing request, this does the heavy lifting const auto status = osrm.Route(params, result); - auto &json_result = result.get(); + auto &json_result = std::get(result); if (status == Status::Ok) { - auto &routes = json_result.values["routes"].get(); + auto &routes = std::get(json_result.values["routes"]); // Let's just use the first route - auto &route = routes.values.at(0).get(); - const auto distance = route.values["distance"].get().value; - const auto duration = route.values["duration"].get().value; + auto &route = std::get(routes.values.at(0)); + const auto distance = std::get(route.values["distance"]).value; + const auto duration = std::get(route.values["duration"]).value; // Warn users if extract does not contain the default coordinates from above if (distance == 0 || duration == 0) @@ -80,8 +80,8 @@ int main(int argc, const char *argv[]) } else if (status == Status::Error) { - const auto code = json_result.values["code"].get().value; - const auto message = json_result.values["message"].get().value; + const auto code = std::get(json_result.values["code"]).value; + const auto message = std::get(json_result.values["message"]).value; std::cout << "Code: " << code << "\n"; std::cout << "Message: " << code << "\n"; diff --git a/include/engine/api/base_result.hpp b/include/engine/api/base_result.hpp index 328871595..76d083d6f 100644 --- a/include/engine/api/base_result.hpp +++ b/include/engine/api/base_result.hpp @@ -2,7 +2,7 @@ #define ENGINE_API_BASE_RESULT_HPP #include -#include +#include #include @@ -10,8 +10,7 @@ namespace osrm::engine::api { -using ResultT = - mapbox::util::variant; +using ResultT = std::variant; } // namespace osrm::engine::api #endif diff --git a/include/engine/api/json_factory.hpp b/include/engine/api/json_factory.hpp index a3f2b0954..1175dcd7e 100644 --- a/include/engine/api/json_factory.hpp +++ b/include/engine/api/json_factory.hpp @@ -41,7 +41,7 @@ inline bool hasValidLanes(const guidance::IntermediateIntersection &intersection return intersection.lanes.lanes_in_turn > 0; } -util::json::Array coordinateToLonLat(const util::Coordinate &coordinate); +util::json::Value coordinateToLonLat(const util::Coordinate &coordinate); /** * Ensures that a bearing value is a whole number, and clamped to the range 0-359 @@ -79,7 +79,7 @@ util::json::Object makeGeoJSONGeometry(ForwardIter begin, ForwardIter end) coordinates.values.push_back(location); coordinates.values.push_back(location); } - geojson.values["coordinates"] = std::move(coordinates); + geojson.values["coordinates"] = util::json::Value{std::move(coordinates)}; return geojson; } diff --git a/include/engine/api/match_api.hpp b/include/engine/api/match_api.hpp index 1a6172abf..012c01176 100644 --- a/include/engine/api/match_api.hpp +++ b/include/engine/api/match_api.hpp @@ -30,14 +30,14 @@ class MatchAPI final : public RouteAPI osrm::engine::api::ResultT &response) const { BOOST_ASSERT(sub_matchings.size() == sub_routes.size()); - if (response.is()) + if (std::holds_alternative(response)) { - auto &fb_result = response.get(); + auto &fb_result = std::get(response); MakeResponse(sub_matchings, sub_routes, fb_result); } else { - auto &json_result = response.get(); + auto &json_result = std::get(response); MakeResponse(sub_matchings, sub_routes, json_result); } } diff --git a/include/engine/api/nearest_api.hpp b/include/engine/api/nearest_api.hpp index 53ea1fd43..32a5898d3 100644 --- a/include/engine/api/nearest_api.hpp +++ b/include/engine/api/nearest_api.hpp @@ -29,14 +29,14 @@ class NearestAPI final : public BaseAPI BOOST_ASSERT(phantom_nodes.size() == 1); BOOST_ASSERT(parameters.coordinates.size() == 1); - if (response.is()) + if (std::holds_alternative(response)) { - auto &fb_result = response.get(); + auto &fb_result = std::get(response); MakeResponse(phantom_nodes, fb_result); } else { - auto &json_result = response.get(); + auto &json_result = std::get(response); MakeResponse(phantom_nodes, json_result); } } diff --git a/include/engine/api/route_api.hpp b/include/engine/api/route_api.hpp index 3b7c4f17c..f833d603c 100644 --- a/include/engine/api/route_api.hpp +++ b/include/engine/api/route_api.hpp @@ -50,14 +50,14 @@ class RouteAPI : public BaseAPI { BOOST_ASSERT(!raw_routes.routes.empty()); - if (response.is()) + if (std::holds_alternative(response)) { - auto &fb_result = response.get(); + auto &fb_result = std::get(response); MakeResponse(raw_routes, waypoint_candidates, fb_result); } else { - auto &json_result = response.get(); + auto &json_result = std::get(response); MakeResponse(raw_routes, waypoint_candidates, json_result); } } @@ -158,8 +158,8 @@ class RouteAPI : public BaseAPI } template - mapbox::util::variant, - flatbuffers::Offset>> + std::variant, + flatbuffers::Offset>> MakeGeometry(flatbuffers::FlatBufferBuilder &builder, ForwardIter begin, ForwardIter end) const { if (parameters.geometries == RouteParameters::GeometriesType::Polyline) @@ -408,8 +408,8 @@ class RouteAPI : public BaseAPI // Fill geometry auto overview = MakeOverview(leg_geometries); - mapbox::util::variant, - flatbuffers::Offset>> + std::variant, + flatbuffers::Offset>> geometry; if (overview) { @@ -426,8 +426,7 @@ class RouteAPI : public BaseAPI routeObject.add_legs(legs_vector); if (overview) { - mapbox::util::apply_visitor(GeometryVisitor(routeObject), - geometry); + std::visit(GeometryVisitor(routeObject), geometry); } return routeObject.Finish(); @@ -644,7 +643,7 @@ class RouteAPI : public BaseAPI stepBuilder.add_rotary_pronunciation(rotary_pronunciation_string); stepBuilder.add_intersections(intersections_vector); stepBuilder.add_maneuver(maneuver_buffer); - mapbox::util::apply_visitor(GeometryVisitor(stepBuilder), geometry); + std::visit(GeometryVisitor(stepBuilder), geometry); return stepBuilder.Finish(); }; diff --git a/include/engine/api/table_api.hpp b/include/engine/api/table_api.hpp index 20d6a4a23..10b57f985 100644 --- a/include/engine/api/table_api.hpp +++ b/include/engine/api/table_api.hpp @@ -50,14 +50,14 @@ class TableAPI final : public BaseAPI const std::vector &fallback_speed_cells, osrm::engine::api::ResultT &response) const { - if (response.is()) + if (std::holds_alternative(response)) { - auto &fb_result = response.get(); + auto &fb_result = std::get(response); MakeResponse(tables, candidates, fallback_speed_cells, fb_result); } else { - auto &json_result = response.get(); + auto &json_result = std::get(response); MakeResponse(tables, candidates, fallback_speed_cells, json_result); } } @@ -377,7 +377,8 @@ class TableAPI final : public BaseAPI return util::json::Value( util::json::Number(from_alias(duration) / 10.)); }); - json_table.values.push_back(std::move(json_row)); + + json_table.values.push_back(util::json::Value{json_row}); } return json_table; } @@ -406,7 +407,7 @@ class TableAPI final : public BaseAPI return util::json::Value(util::json::Number( std::round(from_alias(distance) * 10) / 10.)); }); - json_table.values.push_back(std::move(json_row)); + json_table.values.push_back(util::json::Value{json_row}); } return json_table; } @@ -415,15 +416,18 @@ class TableAPI final : public BaseAPI MakeEstimatesTable(const std::vector &fallback_speed_cells) const { util::json::Array json_table; - std::for_each(fallback_speed_cells.begin(), - fallback_speed_cells.end(), - [&](const auto &cell) - { - util::json::Array row; - row.values.push_back(util::json::Number(cell.row)); - row.values.push_back(util::json::Number(cell.column)); - json_table.values.push_back(std::move(row)); - }); + std::for_each( + fallback_speed_cells.begin(), + fallback_speed_cells.end(), + [&](const auto &cell) + { + util::json::Array row; + util::json::Value jCellRow{util::json::Number(static_cast(cell.row))}; + util::json::Value jCellColumn{util::json::Number(static_cast(cell.column))}; + row.values.push_back(jCellRow); + row.values.push_back(jCellColumn); + json_table.values.push_back(util::json::Value{row}); + }); return json_table; } diff --git a/include/engine/api/trip_api.hpp b/include/engine/api/trip_api.hpp index d7a4ab69c..2f821cc1b 100644 --- a/include/engine/api/trip_api.hpp +++ b/include/engine/api/trip_api.hpp @@ -27,14 +27,14 @@ class TripAPI final : public RouteAPI { BOOST_ASSERT(sub_trips.size() == sub_routes.size()); - if (response.is()) + if (std::holds_alternative(response)) { - auto &fb_result = response.get(); + auto &fb_result = std::get(response); MakeResponse(sub_trips, sub_routes, candidates, fb_result); } else { - auto &json_result = response.get(); + auto &json_result = std::get(response); MakeResponse(sub_trips, sub_routes, candidates, json_result); } } diff --git a/include/engine/plugins/plugin_base.hpp b/include/engine/plugins/plugin_base.hpp index 5fb0ffc58..3a98022e9 100644 --- a/include/engine/plugins/plugin_base.hpp +++ b/include/engine/plugins/plugin_base.hpp @@ -95,7 +95,7 @@ class BasePlugin const std::string &message, osrm::engine::api::ResultT &result) const { - mapbox::util::apply_visitor(ErrorRenderer(code, message), result); + std::visit(ErrorRenderer(code, message), result); return Status::Error; } diff --git a/include/extractor/internal_extractor_edge.hpp b/include/extractor/internal_extractor_edge.hpp index 32242252a..c9e072d60 100644 --- a/include/extractor/internal_extractor_edge.hpp +++ b/include/extractor/internal_extractor_edge.hpp @@ -7,8 +7,8 @@ #include "util/typedefs.hpp" #include -#include #include +#include namespace osrm::extractor { diff --git a/include/extractor/maneuver_override.hpp b/include/extractor/maneuver_override.hpp index 49668f2c1..78eb31021 100644 --- a/include/extractor/maneuver_override.hpp +++ b/include/extractor/maneuver_override.hpp @@ -11,7 +11,7 @@ #include "util/std_hash.hpp" #include "util/vector_view.hpp" -#include +#include #include diff --git a/include/extractor/restriction.hpp b/include/extractor/restriction.hpp index 5e0cc5d72..cfd3e1759 100644 --- a/include/extractor/restriction.hpp +++ b/include/extractor/restriction.hpp @@ -1,12 +1,10 @@ #ifndef RESTRICTION_HPP #define RESTRICTION_HPP +#include "turn_path.hpp" #include "util/coordinate.hpp" #include "util/opening_hours.hpp" #include "util/typedefs.hpp" - -#include "mapbox/variant.hpp" -#include "turn_path.hpp" #include namespace osrm::extractor diff --git a/include/extractor/turn_path.hpp b/include/extractor/turn_path.hpp index 004036d59..5747f620c 100644 --- a/include/extractor/turn_path.hpp +++ b/include/extractor/turn_path.hpp @@ -4,7 +4,7 @@ #include "util/typedefs.hpp" #include -#include +#include #include namespace osrm::extractor @@ -61,50 +61,50 @@ struct InputViaWayPath struct InputTurnPath { - mapbox::util::variant node_or_way; + std::variant node_or_way; TurnPathType Type() const { - BOOST_ASSERT(node_or_way.which() < TurnPathType::NUM_TURN_PATH_TYPES); - return static_cast(node_or_way.which()); + BOOST_ASSERT(node_or_way.index() < TurnPathType::NUM_TURN_PATH_TYPES); + return static_cast(node_or_way.index()); } OSMWayID From() const { - return node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH - ? mapbox::util::get(node_or_way).from - : mapbox::util::get(node_or_way).from; + return node_or_way.index() == TurnPathType::VIA_NODE_TURN_PATH + ? std::get(node_or_way).from + : std::get(node_or_way).from; } OSMWayID To() const { - return node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH - ? mapbox::util::get(node_or_way).to - : mapbox::util::get(node_or_way).to; + return node_or_way.index() == TurnPathType::VIA_NODE_TURN_PATH + ? std::get(node_or_way).to + : std::get(node_or_way).to; } InputViaWayPath &AsViaWayPath() { - BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_WAY_TURN_PATH); - return mapbox::util::get(node_or_way); + BOOST_ASSERT(node_or_way.index() == TurnPathType::VIA_WAY_TURN_PATH); + return std::get(node_or_way); } const InputViaWayPath &AsViaWayPath() const { - BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_WAY_TURN_PATH); - return mapbox::util::get(node_or_way); + BOOST_ASSERT(node_or_way.index() == TurnPathType::VIA_WAY_TURN_PATH); + return std::get(node_or_way); } InputViaNodePath &AsViaNodePath() { - BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH); - return mapbox::util::get(node_or_way); + BOOST_ASSERT(node_or_way.index() == TurnPathType::VIA_NODE_TURN_PATH); + return std::get(node_or_way); } const InputViaNodePath &AsViaNodePath() const { - BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH); - return mapbox::util::get(node_or_way); + BOOST_ASSERT(node_or_way.index() == TurnPathType::VIA_NODE_TURN_PATH); + return std::get(node_or_way); } }; @@ -175,63 +175,63 @@ struct ViaWayPath // between node/way paths struct TurnPath { - mapbox::util::variant node_or_way; + std::variant node_or_way; NodeID To() const { - return node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH - ? mapbox::util::get(node_or_way).to - : mapbox::util::get(node_or_way).to; + return node_or_way.index() == TurnPathType::VIA_NODE_TURN_PATH + ? std::get(node_or_way).to + : std::get(node_or_way).to; } NodeID From() const { - return node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH - ? mapbox::util::get(node_or_way).from - : mapbox::util::get(node_or_way).from; + return node_or_way.index() == TurnPathType::VIA_NODE_TURN_PATH + ? std::get(node_or_way).from + : std::get(node_or_way).from; } NodeID FirstVia() const { - if (node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH) + if (node_or_way.index() == TurnPathType::VIA_NODE_TURN_PATH) { - return mapbox::util::get(node_or_way).via; + return std::get(node_or_way).via; } else { - BOOST_ASSERT(!mapbox::util::get(node_or_way).via.empty()); - return mapbox::util::get(node_or_way).via[0]; + BOOST_ASSERT(!std::get(node_or_way).via.empty()); + return std::get(node_or_way).via[0]; } } ViaWayPath &AsViaWayPath() { - BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_WAY_TURN_PATH); - return mapbox::util::get(node_or_way); + BOOST_ASSERT(node_or_way.index() == TurnPathType::VIA_WAY_TURN_PATH); + return std::get(node_or_way); } const ViaWayPath &AsViaWayPath() const { - BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_WAY_TURN_PATH); - return mapbox::util::get(node_or_way); + BOOST_ASSERT(node_or_way.index() == TurnPathType::VIA_WAY_TURN_PATH); + return std::get(node_or_way); } ViaNodePath &AsViaNodePath() { - BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH); - return mapbox::util::get(node_or_way); + BOOST_ASSERT(node_or_way.index() == TurnPathType::VIA_NODE_TURN_PATH); + return std::get(node_or_way); } const ViaNodePath &AsViaNodePath() const { - BOOST_ASSERT(node_or_way.which() == TurnPathType::VIA_NODE_TURN_PATH); - return mapbox::util::get(node_or_way); + BOOST_ASSERT(node_or_way.index() == TurnPathType::VIA_NODE_TURN_PATH); + return std::get(node_or_way); } TurnPathType Type() const { - BOOST_ASSERT(node_or_way.which() < TurnPathType::NUM_TURN_PATH_TYPES); - return static_cast(node_or_way.which()); + BOOST_ASSERT(node_or_way.index() < TurnPathType::NUM_TURN_PATH_TYPES); + return static_cast(node_or_way.index()); } bool operator==(const TurnPath &other) const diff --git a/include/nodejs/json_v8_renderer.hpp b/include/nodejs/json_v8_renderer.hpp index 4b8bbd193..9572744c2 100644 --- a/include/nodejs/json_v8_renderer.hpp +++ b/include/nodejs/json_v8_renderer.hpp @@ -29,7 +29,7 @@ struct V8Renderer for (const auto &keyValue : object.values) { Napi::Value child; - mapbox::util::apply_visitor(V8Renderer(env, child), keyValue.second); + std::visit(V8Renderer(env, child), keyValue.second); obj.Set(keyValue.first, child); } out = obj; @@ -41,7 +41,7 @@ struct V8Renderer for (auto i = 0u; i < array.values.size(); ++i) { Napi::Value child; - mapbox::util::apply_visitor(V8Renderer(env, child), array.values[i]); + std::visit(V8Renderer(env, child), array.values[i]); a.Set(i, child); } out = a; diff --git a/include/nodejs/node_osrm_support.hpp b/include/nodejs/node_osrm_support.hpp index f69d8e813..18fb20bac 100644 --- a/include/nodejs/node_osrm_support.hpp +++ b/include/nodejs/node_osrm_support.hpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -50,7 +51,7 @@ struct PluginParameters bool renderToBuffer = false; }; -using ObjectOrString = typename mapbox::util::variant; +using ObjectOrString = typename std::variant; template inline Napi::Value render(const Napi::Env &env, const ResultT &result); @@ -61,18 +62,18 @@ template <> Napi::Value inline render(const Napi::Env &env, const std::string &r template <> Napi::Value inline render(const Napi::Env &env, const ObjectOrString &result) { - if (result.is()) + if (std::holds_alternative(result)) { // Convert osrm::json object tree into matching v8 object tree Napi::Value value; - renderToV8(env, value, result.get()); + renderToV8(env, value, std::get(result)); return value; } else { // Return the string object as a node Buffer return Napi::Buffer::Copy( - env, result.get().data(), result.get().size()); + env, std::get(result).data(), std::get(result).size()); } } @@ -95,7 +96,7 @@ inline void ParseResult(const osrm::Status &result_status, osrm::json::Object &r if (result_status == osrm::Status::Error) { - throw std::logic_error(code_iter->second.get().value.c_str()); + throw std::logic_error(std::get(code_iter->second).value.c_str()); } result.values.erase(code_iter); diff --git a/include/server/service/base_service.hpp b/include/server/service/base_service.hpp index 4a5db6db1..5e52a2e09 100644 --- a/include/server/service/base_service.hpp +++ b/include/server/service/base_service.hpp @@ -5,7 +5,7 @@ #include "osrm/osrm.hpp" #include "util/coordinate.hpp" -#include +#include #include #include diff --git a/include/util/geojson_debug_logger.hpp b/include/util/geojson_debug_logger.hpp index ea86535d5..99f751391 100644 --- a/include/util/geojson_debug_logger.hpp +++ b/include/util/geojson_debug_logger.hpp @@ -51,7 +51,7 @@ class GeojsonLogger if (!first) ofs << ",\n\t\t"; - util::json::render(ofs, object.get()); + util::json::render(ofs, std::get(object)); first = false; } diff --git a/include/util/geojson_debug_policies.hpp b/include/util/geojson_debug_policies.hpp index 0f25badd0..8e81bb474 100644 --- a/include/util/geojson_debug_policies.hpp +++ b/include/util/geojson_debug_policies.hpp @@ -51,4 +51,4 @@ struct CoordinateVectorToMultiPoint } // namespace osrm::util -#endif /* OSRM_GEOJSON_DEBUG_POLICIES */ +#endif /* OSRM_GEOJSON_DEBUG_POLICIES */ \ No newline at end of file diff --git a/include/util/geojson_debug_policy_toolkit.hpp b/include/util/geojson_debug_policy_toolkit.hpp index fcddc21a1..8d2bef58f 100644 --- a/include/util/geojson_debug_policy_toolkit.hpp +++ b/include/util/geojson_debug_policy_toolkit.hpp @@ -55,12 +55,12 @@ inline util::json::Object makeStyle(const GeojsonStyleSize size_type, struct CoordinateToJsonArray { - util::json::Array operator()(const util::Coordinate coordinate) + util::json::Value operator()(const util::Coordinate coordinate) { util::json::Array json_coordinate; json_coordinate.values.push_back(static_cast(toFloating(coordinate.lon))); json_coordinate.values.push_back(static_cast(toFloating(coordinate.lat))); - return json_coordinate; + return util::json::Value{json_coordinate}; } }; @@ -73,7 +73,7 @@ struct NodeIdToCoordinate const std::vector &node_coordinates; - util::json::Array operator()(const NodeID nid) + util::json::Value operator()(const NodeID nid) { auto coordinate = node_coordinates[nid]; CoordinateToJsonArray converter; @@ -108,4 +108,4 @@ inline util::json::Array makeJsonArray(const std::vector &inpu } } // namespace osrm::util -#endif /* OSRM_GEOJSON_DEBUG_POLICY_TOOLKIT_HPP */ +#endif /* OSRM_GEOJSON_DEBUG_POLICY_TOOLKIT_HPP */ \ No newline at end of file diff --git a/include/util/json_container.hpp b/include/util/json_container.hpp index 27436b7f8..14ca9d52f 100644 --- a/include/util/json_container.hpp +++ b/include/util/json_container.hpp @@ -31,11 +31,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef JSON_CONTAINER_HPP #define JSON_CONTAINER_HPP -#include - #include #include #include +#include #include namespace osrm::util::json @@ -96,13 +95,7 @@ struct Null * * Dispatch on its type by either by using apply_visitor or its get function. */ -using Value = mapbox::util::variant, - mapbox::util::recursive_wrapper, - True, - False, - Null>; +using Value = std::variant; /** * Typed Object. diff --git a/include/util/json_deep_compare.hpp b/include/util/json_deep_compare.hpp index 810dcdfad..24b226ca7 100644 --- a/include/util/json_deep_compare.hpp +++ b/include/util/json_deep_compare.hpp @@ -81,10 +81,10 @@ struct Comparator const auto &rhs_child = rhs.values.find(key)->second; const auto &lhs_child = lhs.values.find(key)->second; - auto is_same = mapbox::util::apply_visitor( - Comparator(reason, lhs_path + "." + key, rhs_path + "." + key), - lhs_child, - rhs_child); + auto is_same = + std::visit(Comparator(reason, lhs_path + "." + key, rhs_path + "." + key), + lhs_child, + rhs_child); if (!is_same) { return false; @@ -104,12 +104,11 @@ struct Comparator for (auto i = 0UL; i < lhs.values.size(); ++i) { - auto is_same = - mapbox::util::apply_visitor(Comparator(reason, - lhs_path + "[" + std::to_string(i) + "]", - rhs_path + "[" + std::to_string(i) + "]"), - lhs.values[i], - rhs.values[i]); + auto is_same = std::visit(Comparator(reason, + lhs_path + "[" + std::to_string(i) + "]", + rhs_path + "[" + std::to_string(i) + "]"), + lhs.values[i], + rhs.values[i]); if (!is_same) { return false; @@ -151,8 +150,7 @@ struct Comparator inline bool compare(const Value &reference, const Value &result, std::string &reason) { - return mapbox::util::apply_visitor( - Comparator(reason, "reference", "result"), reference, result); + return std::visit(Comparator(reason, "reference", "result"), reference, result); } } // namespace osrm::util::json diff --git a/include/util/json_renderer.hpp b/include/util/json_renderer.hpp index aba5c2e4e..d1adfcce6 100644 --- a/include/util/json_renderer.hpp +++ b/include/util/json_renderer.hpp @@ -67,7 +67,7 @@ template struct Renderer write('\"'); write(it->first); write<>("\":"); - mapbox::util::apply_visitor(Renderer(out), it->second); + std::visit(Renderer(out), it->second); if (++it != end) { write(','); @@ -81,7 +81,7 @@ template struct Renderer write('['); for (auto it = array.values.cbegin(), end = array.values.cend(); it != end;) { - mapbox::util::apply_visitor(Renderer(out), *it); + std::visit(Renderer(out), *it); if (++it != end) { write(','); diff --git a/scripts/update_dependencies.sh b/scripts/update_dependencies.sh index 9e8e231bf..f16b175ca 100755 --- a/scripts/update_dependencies.sh +++ b/scripts/update_dependencies.sh @@ -12,9 +12,6 @@ set -o nounset OSMIUM_PATH="osmcode/libosmium" OSMIUM_TAG=v2.14.0 -VARIANT_PATH="mapbox/variant" -VARIANT_TAG=v1.2.0 - SOL_PATH="ThePhD/sol2" SOL_TAG=v2.17.5 @@ -56,6 +53,6 @@ function update_subtree () { } ## Update dependencies -for dep in osmium variant sol rapidjson microtar protozero vtzero fmt; do +for dep in osmium sol rapidjson microtar protozero vtzero fmt; do update_subtree $dep done diff --git a/src/benchmarks/json_render.cpp b/src/benchmarks/json_render.cpp index d9d3c3cac..d2c00b51f 100644 --- a/src/benchmarks/json_render.cpp +++ b/src/benchmarks/json_render.cpp @@ -86,7 +86,7 @@ json::Object load(const char *filename) json::Value result; convert(document, result); - return result.get(); + return std::get(result); } } // namespace diff --git a/src/benchmarks/match.cpp b/src/benchmarks/match.cpp index cece7960b..47a9b12b4 100644 --- a/src/benchmarks/match.cpp +++ b/src/benchmarks/match.cpp @@ -232,9 +232,9 @@ try { engine::api::ResultT result = json::Object(); const auto rc = osrm.Match(params, result); - auto &json_result = result.get(); + auto &json_result = std::get(result); if (rc != Status::Ok || - json_result.values.at("matchings").get().values.size() != 1) + std::get(json_result.values.at("matchings")).values.size() != 1) { throw std::runtime_error{"Couldn't match"}; } diff --git a/src/benchmarks/route.cpp b/src/benchmarks/route.cpp index ea95d06fb..def90c175 100644 --- a/src/benchmarks/route.cpp +++ b/src/benchmarks/route.cpp @@ -76,7 +76,7 @@ try { engine::api::ResultT result = json::Object(); const auto rc = osrm.Route(params, result); - auto &json_result = result.get(); + auto &json_result = std::get(result); if (rc != Status::Ok || json_result.values.find("routes") == json_result.values.end()) { throw std::runtime_error{"Couldn't route"}; diff --git a/src/engine/api/json_factory.cpp b/src/engine/api/json_factory.cpp index 988ca08e8..673de0496 100644 --- a/src/engine/api/json_factory.cpp +++ b/src/engine/api/json_factory.cpp @@ -74,12 +74,12 @@ std::string waypointTypeToString(const guidance::WaypointType waypoint_type) return waypoint_type_names[static_cast(waypoint_type)]; } -util::json::Array coordinateToLonLat(const util::Coordinate &coordinate) +util::json::Value coordinateToLonLat(const util::Coordinate &coordinate) { util::json::Array array; array.values.push_back(static_cast(util::toFloating(coordinate.lon))); array.values.push_back(static_cast(util::toFloating(coordinate.lat))); - return array; + return util::json::Value{std::move(array)}; } } // namespace detail diff --git a/src/engine/plugins/tile.cpp b/src/engine/plugins/tile.cpp index 6bf9254eb..ae6a8ec78 100644 --- a/src/engine/plugins/tile.cpp +++ b/src/engine/plugins/tile.cpp @@ -663,7 +663,7 @@ Status TilePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms, { BOOST_ASSERT(parameters.IsValid()); - auto &pbf_buffer = result.get(); + auto &pbf_buffer = std::get(result); const auto &facade = algorithms.GetFacade(); auto edges = getEdges(facade, parameters.x, parameters.y, parameters.z); auto segregated_nodes = getSegregatedNodes(facade, edges); diff --git a/src/engine/routing_algorithms/routing_base_mld.cpp b/src/engine/routing_algorithms/routing_base_mld.cpp new file mode 100644 index 000000000..f5babeb84 --- /dev/null +++ b/src/engine/routing_algorithms/routing_base_mld.cpp @@ -0,0 +1,60 @@ +#include "engine/routing_algorithms/routing_base_mld.hpp" + +namespace osrm::engine::routing_algorithms::mld +{ +double getNetworkDistance(SearchEngineData &engine_working_data, + const DataFacade &facade, + typename SearchEngineData::QueryHeap &forward_heap, + typename SearchEngineData::QueryHeap &reverse_heap, + const PhantomNode &source_phantom, + const PhantomNode &target_phantom, + EdgeWeight weight_upper_bound) +{ + forward_heap.Clear(); + reverse_heap.Clear(); + + const PhantomEndpoints endpoints{source_phantom, target_phantom}; + insertNodesInHeaps(forward_heap, reverse_heap, endpoints); + + auto [weight, unpacked_nodes, unpacked_edges] = search( + engine_working_data, facade, forward_heap, reverse_heap, {}, weight_upper_bound, endpoints); + + if (weight == INVALID_EDGE_WEIGHT) + { + return std::numeric_limits::max(); + } + + BOOST_ASSERT(unpacked_nodes.size() >= 1); + + EdgeDistance distance = {0.0}; + + if (source_phantom.forward_segment_id.id == unpacked_nodes.front()) + { + BOOST_ASSERT(source_phantom.forward_segment_id.enabled); + distance = EdgeDistance{0} - source_phantom.GetForwardDistance(); + } + else if (source_phantom.reverse_segment_id.id == unpacked_nodes.front()) + { + BOOST_ASSERT(source_phantom.reverse_segment_id.enabled); + distance = EdgeDistance{0} - source_phantom.GetReverseDistance(); + } + + for (size_t index = 0; index < unpacked_nodes.size() - 1; ++index) + { + distance += facade.GetNodeDistance(unpacked_nodes[index]); + } + + if (target_phantom.forward_segment_id.id == unpacked_nodes.back()) + { + BOOST_ASSERT(target_phantom.forward_segment_id.enabled); + distance += target_phantom.GetForwardDistance(); + } + else if (target_phantom.reverse_segment_id.id == unpacked_nodes.back()) + { + BOOST_ASSERT(target_phantom.reverse_segment_id.enabled); + distance += target_phantom.GetReverseDistance(); + } + + return from_alias(distance); +} +} // namespace osrm::engine::routing_algorithms::mld diff --git a/src/nodejs/node_osrm.cpp b/src/nodejs/node_osrm.cpp index 320aaa849..65585abac 100644 --- a/src/nodejs/node_osrm.cpp +++ b/src/nodejs/node_osrm.cpp @@ -147,7 +147,7 @@ inline void async(const Napi::CallbackInfo &info, osrm::engine::api::ResultT r; r = osrm::util::json::Object(); const auto status = ((*osrm).*(service))(*params, r); - auto &json_result = r.get(); + auto &json_result = std::get(r); ParseResult(status, json_result); if (pluginParams.renderToBuffer) { @@ -165,7 +165,7 @@ inline void async(const Napi::CallbackInfo &info, { osrm::engine::api::ResultT r = flatbuffers::FlatBufferBuilder(); const auto status = ((*osrm).*(service))(*params, r); - const auto &fbs_result = r.get(); + const auto &fbs_result = std::get(r); ParseResult(status, fbs_result); BOOST_ASSERT(pluginParams.renderToBuffer); std::string result_str( @@ -240,7 +240,7 @@ inline void asyncForTiles(const Napi::CallbackInfo &info, { result = std::string(); const auto status = ((*osrm).*(service))(*params, result); - auto str_result = result.get(); + auto str_result = std::get(result); ParseResult(status, str_result); } catch (const std::exception &e) @@ -252,7 +252,7 @@ inline void asyncForTiles(const Napi::CallbackInfo &info, { Napi::HandleScope scope{Env()}; - Callback().Call({Env().Null(), render(Env(), result.get())}); + Callback().Call({Env().Null(), render(Env(), std::get(result))}); } // Keeps the OSRM object alive even after shutdown until we're done with callback diff --git a/src/osrm/osrm.cpp b/src/osrm/osrm.cpp index 83e1076f7..cf961d5fb 100644 --- a/src/osrm/osrm.cpp +++ b/src/osrm/osrm.cpp @@ -65,7 +65,7 @@ Status OSRM::Route(const engine::api::RouteParameters ¶ms, json::Object &jso { osrm::engine::api::ResultT result = json::Object(); auto status = engine_->Route(params, result); - json_result = std::move(result.get()); + json_result = std::move(std::get(result)); return status; } @@ -78,7 +78,7 @@ Status OSRM::Table(const engine::api::TableParameters ¶ms, json::Object &jso { osrm::engine::api::ResultT result = json::Object(); auto status = engine_->Table(params, result); - json_result = std::move(result.get()); + json_result = std::move(std::get(result)); return status; } @@ -91,7 +91,7 @@ Status OSRM::Nearest(const engine::api::NearestParameters ¶ms, json::Object { osrm::engine::api::ResultT result = json::Object(); auto status = engine_->Nearest(params, result); - json_result = std::move(result.get()); + json_result = std::move(std::get(result)); return status; } @@ -104,7 +104,7 @@ Status OSRM::Trip(const engine::api::TripParameters ¶ms, json::Object &json_ { osrm::engine::api::ResultT result = json::Object(); auto status = engine_->Trip(params, result); - json_result = std::move(result.get()); + json_result = std::move(std::get(result)); return status; } @@ -118,7 +118,7 @@ Status OSRM::Match(const engine::api::MatchParameters ¶ms, json::Object &jso { osrm::engine::api::ResultT result = json::Object(); auto status = engine_->Match(params, result); - json_result = std::move(result.get()); + json_result = std::move(std::get(result)); return status; } @@ -131,7 +131,7 @@ Status OSRM::Tile(const engine::api::TileParameters ¶ms, std::string &str_re { osrm::engine::api::ResultT result = std::string(); auto status = engine_->Tile(params, result); - str_result = std::move(result.get()); + str_result = std::move(std::get(result)); return status; } diff --git a/src/partitioner/partitioner.cpp b/src/partitioner/partitioner.cpp index e8544bf64..dbed86bb1 100644 --- a/src/partitioner/partitioner.cpp +++ b/src/partitioner/partitioner.cpp @@ -15,7 +15,6 @@ #include "util/coordinate.hpp" #include "util/geojson_debug_logger.hpp" -#include "util/geojson_debug_policies.hpp" #include "util/integer_range.hpp" #include "util/json_container.hpp" #include "util/log.hpp" diff --git a/src/server/request_handler.cpp b/src/server/request_handler.cpp index 78408865d..284df6fd2 100644 --- a/src/server/request_handler.cpp +++ b/src/server/request_handler.cpp @@ -21,6 +21,7 @@ #include #include #include +#include namespace osrm::server { @@ -38,17 +39,17 @@ void SendResponse(ServiceHandler::ResultT &result, http::reply ¤t_reply) current_reply.headers.emplace_back("Access-Control-Allow-Methods", "GET"); current_reply.headers.emplace_back("Access-Control-Allow-Headers", "X-Requested-With, Content-Type"); - if (result.is()) + if (std::holds_alternative(result)) { current_reply.headers.emplace_back("Content-Type", "application/json; charset=UTF-8"); current_reply.headers.emplace_back("Content-Disposition", "inline; filename=\"response.json\""); - util::json::render(current_reply.content, result.get()); + util::json::render(current_reply.content, std::get(result)); } - else if (result.is()) + else if (std::holds_alternative(result)) { - auto &buffer = result.get(); + auto &buffer = std::get(result); current_reply.content.resize(buffer.GetSize()); std::copy(buffer.GetBufferPointer(), buffer.GetBufferPointer() + buffer.GetSize(), @@ -59,10 +60,10 @@ void SendResponse(ServiceHandler::ResultT &result, http::reply ¤t_reply) } else { - BOOST_ASSERT(result.is()); - current_reply.content.resize(result.get().size()); - std::copy(result.get().cbegin(), - result.get().cend(), + BOOST_ASSERT(std::holds_alternative(result)); + current_reply.content.resize(std::get(result).size()); + std::copy(std::get(result).cbegin(), + std::get(result).cend(), current_reply.content.begin()); current_reply.headers.emplace_back("Content-Type", "application/x-protobuf"); @@ -127,7 +128,7 @@ void RequestHandler::HandleRequest(const http::request ¤t_request, http::r current_reply.status = http::reply::bad_request; result = util::json::Object(); - auto &json_result = result.get(); + auto &json_result = std::get(result); json_result.values["code"] = "InvalidUrl"; json_result.values["message"] = "URL string malformed close to position " + std::to_string(position) + ": \"" + context + "\""; @@ -174,7 +175,7 @@ void RequestHandler::HandleRequest(const http::request ¤t_request, http::r current_reply.status = http::reply::bad_request; ServiceHandler::ResultT result = util::json::Object(); - auto &json_result = result.get(); + auto &json_result = std::get(result); json_result.values["code"] = "DisabledDataset"; json_result.values["message"] = e.what(); SendResponse(result, current_reply); diff --git a/src/server/service/match_service.cpp b/src/server/service/match_service.cpp index 3a74ec90b..22b482454 100644 --- a/src/server/service/match_service.cpp +++ b/src/server/service/match_service.cpp @@ -42,7 +42,7 @@ engine::Status MatchService::RunQuery(std::size_t prefix_length, osrm::engine::api::ResultT &result) { result = util::json::Object(); - auto &json_result = result.get(); + auto &json_result = std::get(result); auto query_iterator = query.begin(); auto parameters = diff --git a/src/server/service/nearest_service.cpp b/src/server/service/nearest_service.cpp index 28aa75d69..8eb0d1ea0 100644 --- a/src/server/service/nearest_service.cpp +++ b/src/server/service/nearest_service.cpp @@ -36,7 +36,7 @@ engine::Status NearestService::RunQuery(std::size_t prefix_length, osrm::engine::api::ResultT &result) { result = util::json::Object(); - auto &json_result = result.get(); + auto &json_result = std::get(result); auto query_iterator = query.begin(); auto parameters = diff --git a/src/server/service/route_service.cpp b/src/server/service/route_service.cpp index f5b61e82a..a651ab9f7 100644 --- a/src/server/service/route_service.cpp +++ b/src/server/service/route_service.cpp @@ -40,7 +40,7 @@ engine::Status RouteService::RunQuery(std::size_t prefix_length, osrm::engine::api::ResultT &result) { result = util::json::Object(); - auto &json_result = result.get(); + auto &json_result = std::get(result); auto query_iterator = query.begin(); auto parameters = diff --git a/src/server/service/table_service.cpp b/src/server/service/table_service.cpp index 27341f6aa..719218bd0 100644 --- a/src/server/service/table_service.cpp +++ b/src/server/service/table_service.cpp @@ -71,7 +71,7 @@ engine::Status TableService::RunQuery(std::size_t prefix_length, osrm::engine::api::ResultT &result) { result = util::json::Object(); - auto &json_result = result.get(); + auto &json_result = std::get(result); auto query_iterator = query.begin(); auto parameters = diff --git a/src/server/service/tile_service.cpp b/src/server/service/tile_service.cpp index d90a0861d..ba256cdc6 100644 --- a/src/server/service/tile_service.cpp +++ b/src/server/service/tile_service.cpp @@ -22,7 +22,7 @@ engine::Status TileService::RunQuery(std::size_t prefix_length, { const auto position = std::distance(query.begin(), query_iterator); result = util::json::Object(); - auto &json_result = result.get(); + auto &json_result = std::get(result); json_result.values["code"] = "InvalidQuery"; json_result.values["message"] = "Query string malformed close to position " + std::to_string(prefix_length + position); @@ -33,7 +33,7 @@ engine::Status TileService::RunQuery(std::size_t prefix_length, if (!parameters->IsValid()) { result = util::json::Object(); - auto &json_result = result.get(); + auto &json_result = std::get(result); json_result.values["code"] = "InvalidOptions"; json_result.values["message"] = "Invalid coodinates. Only zoomlevel 12+ is supported"; return engine::Status::Error; diff --git a/src/server/service/trip_service.cpp b/src/server/service/trip_service.cpp index a9e44b896..633a8b606 100644 --- a/src/server/service/trip_service.cpp +++ b/src/server/service/trip_service.cpp @@ -42,7 +42,7 @@ engine::Status TripService::RunQuery(std::size_t prefix_length, osrm::engine::api::ResultT &result) { result = util::json::Object(); - auto &json_result = result.get(); + auto &json_result = std::get(result); auto query_iterator = query.begin(); auto parameters = @@ -51,7 +51,7 @@ engine::Status TripService::RunQuery(std::size_t prefix_length, { const auto position = std::distance(query.begin(), query_iterator); result = util::json::Object(); - auto &json_result = result.get(); + auto &json_result = std::get(result); json_result.values["code"] = "InvalidQuery"; json_result.values["message"] = "Query string malformed close to position " + std::to_string(prefix_length + position); diff --git a/src/server/service_handler.cpp b/src/server/service_handler.cpp index 675c3be09..d7ee91126 100644 --- a/src/server/service_handler.cpp +++ b/src/server/service_handler.cpp @@ -31,7 +31,7 @@ engine::Status ServiceHandler::RunQuery(api::ParsedURL parsed_url, if (service_iter == service_map.end()) { result = util::json::Object(); - auto &json_result = result.get(); + auto &json_result = std::get(result); json_result.values["code"] = "InvalidService"; json_result.values["message"] = "Service " + parsed_url.service + " not found!"; return engine::Status::Error; @@ -41,7 +41,7 @@ engine::Status ServiceHandler::RunQuery(api::ParsedURL parsed_url, if (service->GetVersion() != parsed_url.version) { result = util::json::Object(); - auto &json_result = result.get(); + auto &json_result = std::get(result); json_result.values["code"] = "InvalidVersion"; json_result.values["message"] = "Service " + parsed_url.service + " not found!"; return engine::Status::Error; diff --git a/src/util/geojson_debug_policies.cpp b/src/util/geojson_debug_policies.cpp index 9fd2ad60d..fda25819c 100644 --- a/src/util/geojson_debug_policies.cpp +++ b/src/util/geojson_debug_policies.cpp @@ -66,4 +66,4 @@ CoordinateVectorToLineString::operator()(const std::vector &in return makeFeature("LineString", std::move(coordinates), properties); } -} // namespace osrm::util +} // namespace osrm::util \ No newline at end of file diff --git a/third_party/variant/.clang-format b/third_party/variant/.clang-format deleted file mode 100644 index c1aca2a57..000000000 --- a/third_party/variant/.clang-format +++ /dev/null @@ -1,89 +0,0 @@ ---- -# Mapbox.Variant C/C+ style -Language: Cpp -AccessModifierOffset: -2 -AlignAfterOpenBracket: Align -AlignConsecutiveAssignments: false -AlignConsecutiveDeclarations: false -AlignEscapedNewlinesLeft: false -AlignOperands: true -AlignTrailingComments: true -AllowAllParametersOfDeclarationOnNextLine: true -AllowShortBlocksOnASingleLine: false -AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: All -AllowShortIfStatementsOnASingleLine: true -AllowShortLoopsOnASingleLine: true -AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: false -AlwaysBreakTemplateDeclarations: true -BinPackArguments: true -BinPackParameters: true -BraceWrapping: - AfterClass: true - AfterControlStatement: true - AfterEnum: true - AfterFunction: true - AfterNamespace: false - AfterObjCDeclaration: true - AfterStruct: true - AfterUnion: true - BeforeCatch: true - BeforeElse: true - IndentBraces: false -BreakBeforeBinaryOperators: None -BreakBeforeBraces: Custom -BreakBeforeTernaryOperators: true -BreakConstructorInitializersBeforeComma: false -ColumnLimit: 0 -CommentPragmas: '^ IWYU pragma:' -ConstructorInitializerAllOnOneLineOrOnePerLine: true -ConstructorInitializerIndentWidth: 4 -ContinuationIndentWidth: 4 -Cpp11BracedListStyle: true -DerivePointerAlignment: false -DisableFormat: false -ExperimentalAutoDetectBinPacking: false -ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] -IncludeCategories: - - Regex: '^"(llvm|llvm-c|clang|clang-c)/' - Priority: 2 - - Regex: '^(<|"(gtest|isl|json)/)' - Priority: 3 - - Regex: '.*' - Priority: 1 -IndentCaseLabels: false -IndentWidth: 4 -IndentWrappedFunctionNames: false -KeepEmptyLinesAtTheStartOfBlocks: true -MacroBlockBegin: '' -MacroBlockEnd: '' -MaxEmptyLinesToKeep: 1 -NamespaceIndentation: None -ObjCBlockIndentWidth: 2 -ObjCSpaceAfterProperty: false -ObjCSpaceBeforeProtocolList: true -PenaltyBreakBeforeFirstCallParameter: 19 -PenaltyBreakComment: 300 -PenaltyBreakFirstLessLess: 120 -PenaltyBreakString: 1000 -PenaltyExcessCharacter: 1000000 -PenaltyReturnTypeOnItsOwnLine: 60 -PointerAlignment: Left -ReflowComments: true -SortIncludes: true -SpaceAfterCStyleCast: false -SpaceBeforeAssignmentOperators: true -SpaceBeforeParens: ControlStatements -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 1 -SpacesInAngles: false -SpacesInContainerLiterals: true -SpacesInCStyleCastParentheses: false -SpacesInParentheses: false -SpacesInSquareBrackets: false -Standard: Cpp11 -TabWidth: 4 -UseTab: Never -... diff --git a/third_party/variant/.gitignore b/third_party/variant/.gitignore deleted file mode 100644 index e656256b6..000000000 --- a/third_party/variant/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -.DS_Store -out -profiling -build -deps -*.gcda -*.gcno -.ycm_extra_conf.pyc -mason_packages diff --git a/third_party/variant/.gitmodules b/third_party/variant/.gitmodules deleted file mode 100644 index 44fba9a2f..000000000 --- a/third_party/variant/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule ".mason"] - path = .mason - url = https://github.com/mapbox/mason.git diff --git a/third_party/variant/.mason b/third_party/variant/.mason deleted file mode 160000 index 6adb14016..000000000 --- a/third_party/variant/.mason +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6adb140160cb549400f73ea35c1d9eb5782210e0 diff --git a/third_party/variant/.travis.yml b/third_party/variant/.travis.yml deleted file mode 100644 index e45e137a9..000000000 --- a/third_party/variant/.travis.yml +++ /dev/null @@ -1,159 +0,0 @@ -language: generic - -sudo: false - -matrix: - include: - # clang++ 4.0 via mason with -flto and -fsanitize=cfi - - os: linux - compiler: "clang++-40-mason" - env: CXX=clang++-4.0 CXXFLAGS="-flto -fsanitize=cfi -fvisibility=hidden" LDFLAGS="-flto -fsanitize=cfi -fvisibility=hidden" - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test' ] - packages: [ 'libstdc++-4.9-dev' ] - before_install: - - git submodule update --init - - ./.mason/mason install clang++ 4.0.1 - - export PATH=$(./.mason/mason prefix clang++ 4.0.1)/bin:${PATH} - - ./.mason/mason install binutils 2.27 - - export PATH=$(./.mason/mason prefix binutils 2.27)/bin:${PATH} - # clang++ 4.0 via mason with -fsanitize=address - - os: linux - compiler: "clang++-40-mason" - env: CXX=clang++-4.0 CXXFLAGS="-fsanitize=address -fsanitize-address-use-after-scope -fno-omit-frame-pointer -fno-common" LDFLAGS="-fsanitize=address" ASAN_OPTIONS=check_initialization_order=1:detect_stack_use_after_return=1 - sudo: required - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test' ] - packages: [ 'libstdc++-4.9-dev' ] - before_install: - - git submodule update --init - - ./.mason/mason install clang++ 4.0.1 - - export PATH=$(./.mason/mason prefix clang++ 4.0.1)/bin:${PATH} - # clang++ 4.0 via mason with -fsanitize=undefined - - os: linux - compiler: "clang++-40-mason" - env: CXX=clang++-4.0 CXXFLAGS="-fsanitize=undefined" LDFLAGS="-fsanitize=undefined" - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test' ] - packages: [ 'libstdc++-4.9-dev' ] - before_install: - - git submodule update --init - - ./.mason/mason install clang++ 4.0.1 - - export PATH=$(./.mason/mason prefix clang++ 4.0.1)/bin:${PATH} - # clang++ 4.0 via mason with -fsanitize=integer - - os: linux - compiler: "clang++-40-mason" - env: CXX=clang++-4.0 CXXFLAGS="-fsanitize=integer" LDFLAGS="-fsanitize=integer" - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test' ] - packages: [ 'libstdc++-4.9-dev' ] - before_install: - - git submodule update --init - - ./.mason/mason install clang++ 4.0.1 - - export PATH=$(./.mason/mason prefix clang++ 4.0.1)/bin:${PATH} - # clang++ 4.0 via mason with -fsanitize=safe-stack - - os: linux - compiler: "clang++-40-mason" - env: CXX=clang++-4.0 CXXFLAGS="-fsanitize=safe-stack" LDFLAGS="-fsanitize=safe-stack" - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test' ] - packages: [ 'libstdc++-4.9-dev' ] - before_install: - - git submodule update --init - - ./.mason/mason install clang++ 4.0.1 - - export PATH=$(./.mason/mason prefix clang++ 4.0.1)/bin:${PATH} - - os: osx - osx_image: xcode8 - env: OSX_OLDEST_SUPPORTED=10.7 TEST_GYP_BUILD=True - compiler: clang - - os: osx - osx_image: xcode8 - env: OSX_OLDEST_SUPPORTED=10.12 - compiler: clang - - os: linux - compiler: "clang35" - env: CXX=clang++-3.5 COVERAGE=True - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.5' ] - packages: [ 'clang-3.5', 'libstdc++-4.9-dev' ] - - os: linux - compiler: "clang36" - env: CXX=clang++-3.6 - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6' ] - packages: [ 'clang-3.6' ] - - os: linux - compiler: "gcc48" - env: CXX=g++-4.8 - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test' ] - packages: [ 'g++-4.8' ] - - os: linux - compiler: "gcc49" - env: CXX=g++-4.9 - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test' ] - packages: [ 'g++-4.9' ] - - os: linux - compiler: "gcc5" - env: CXX=g++-5 - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test' ] - packages: [ 'g++-5' ] - - os: linux - compiler: "gcc6" - env: CXX=g++-6 CXX_STD=c++14 - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test' ] - packages: [ 'g++-6' ] - -install: - - echo ${CXX} - - if [[ $(uname -s) == 'Linux' ]]; then - export PYTHONPATH=$(pwd)/.local/lib/python2.7/site-packages; - else - export PYTHONPATH=$(pwd)/.local/lib/python/site-packages; - fi - - if [[ ${COVERAGE:-0} == 'True' ]]; then - PYTHONUSERBASE=$(pwd)/.local pip install --user cpp-coveralls; - fi - -script: - # Build in Release - - make - - make test - - make bench - - make sizes - - scripts/run_compilation_failure_tests.sh - - make clean; - # Build in Debug - - export BUILDTYPE=Debug - - make - - make test - - make bench - - make sizes - - scripts/run_compilation_failure_tests.sh - - if [[ ${TEST_GYP_BUILD:-0} == 'True' ]]; then - make clean; - make gyp; - fi - -after_script: - - if [[ ${COVERAGE:-0} == 'True' ]]; then - make clean; - make coverage; - ./out/cov-test; - cp unit*gc* test/; - ./.local/bin/cpp-coveralls --gcov /usr/bin/llvm-cov-3.5 --gcov-options '\-lp' -i optional.hpp -i recursive_wrapper.hpp -i variant.hpp -i variant_io.hpp variant_cast.hpp; - fi diff --git a/third_party/variant/.ycm_extra_conf.py b/third_party/variant/.ycm_extra_conf.py deleted file mode 100644 index ba746684b..000000000 --- a/third_party/variant/.ycm_extra_conf.py +++ /dev/null @@ -1,40 +0,0 @@ -#----------------------------------------------------------------------------- -# -# Configuration for YouCompleteMe Vim plugin -# -# http://valloric.github.io/YouCompleteMe/ -# -#----------------------------------------------------------------------------- - -from os.path import realpath, dirname - -basedir = dirname(realpath(__file__)) - -# some default flags -# for more information install clang-3.2-doc package and -# check UsersManual.html -flags = [ -'-Werror', -'-Wall', -'-Wextra', -'-pedantic', - -'-std=c++11', - -# '-x' and 'c++' also required -# use 'c' for C projects -'-x', -'c++', - -# include third party libraries -'-I.', -] - -# youcompleteme is calling this function to get flags -# You can also set database for flags. Check: JSONCompilationDatabase.html in -# clang-3.2-doc package -def FlagsForFile( filename ): - return { - 'flags': flags, - 'do_cache': True - } diff --git a/third_party/variant/CHANGELOG.md b/third_party/variant/CHANGELOG.md deleted file mode 100644 index f6c4ca044..000000000 --- a/third_party/variant/CHANGELOG.md +++ /dev/null @@ -1,420 +0,0 @@ -# Variant changelog - -## 1.2.0 - -Released: July 3, 2020 - -(82f9561) - -* Use perfect forwarding for internal value types deductions (#178) (#180) -* Implement support for "moving" values out (#142) (#178) (#180) -* Preserve ability to specify explicit `return_type` in visitors (#181) -* Add self-assignment checks in copy and move assignment operator= (#164) -* Add relevant tests - -## 1.1.6 - -Released: April 25, 2019 - -(a4f87dc) - -* make type used for `type_index` configurable via `type_index_t` typdef + use `unsigned int` by default. This addresses `sizeof` discrepancies between boost/std/mapbox variants (ref #19) [view commit](http://github.com/mapbox/variant/commit/9eec1fd48947d81af3debb82686c593b15f79aad) -* use `mapbox::util::type_index_t` (#19) [view commit](http://github.com/mapbox/variant/commit/05ee9aca16c3968e34db3b241c44eecb981344e0) -* Ensure internal index type is capable of holding all alternatives (ref #138) [view commit](http://github.com/mapbox/variant/commit/fa8e124a2367abc9c06f7e83926691085eed45c0) -* Add compile time check to disallow array types as alternatives. [view commit](http://github.com/mapbox/variant/commit/3f6fd131ef07a091338cec81ec8d23d6ca44528d) -* Make `type_index_t` configurable at compile time via `MAPBOX_VARIANT_MINIMIZE_SIZE` and `MAPBOX_VARIANT_OPTIMIZE_FOR_SPEED`. Default is `unsigned int`. (ref #138) [view commit](http://github.com/mapbox/variant/commit/35487cd39400b9a4bd30a18e6dfbf9cb1aaa80cd) -* add missing [view commit](http://github.com/mapbox/variant/commit/c839c666c324306a252b0fbcadd31e80e02e2898) -* Adds a test for polymorphic lambdas in match, resolves #140 [view commit](http://github.com/mapbox/variant/commit/9ac8978f5125182ac1bd9a1b68533bf9695f7289) -* Merge pull request #138 from mapbox/sizeof [view commit](http://github.com/mapbox/variant/commit/3d807d31621d52a8b27494c8f80aa50f6464f17d) -* Merge pull request #141 from mapbox/match-otherwise [view commit](http://github.com/mapbox/variant/commit/916139a2e51e125816efce6e19d428385601273f) -* Update bundled Catch to v1.9.0 [view commit](http://github.com/mapbox/variant/commit/f9c265d7e7a188aa4437f534d4c0648af0118b51) -* REQUIRE_THROWS etc take an expression not a block [view commit](http://github.com/mapbox/variant/commit/a064940e2ce7e40ef5e4db5710b399c68a71be4b) -* Merge pull request #143 from tomhughes/catch [view commit](http://github.com/mapbox/variant/commit/550ac2f159ca883d360c196149b466955c77a573) -* Add static_variant_cast, dynamic_variant_cast [view commit](http://github.com/mapbox/variant/commit/51fccd755b05ee9d3dec9da239acd15d0823c381) -* Merge pull request #144 from narizhny/Casts [view commit](http://github.com/mapbox/variant/commit/291121f6ac682c6cc5a7a69f253492f03ca7324f) -* recursive_wrapper fail to compile when used with 2 classes which are base and derived #146 [view commit](http://github.com/mapbox/variant/commit/7a541ba10d2eb9a9da0f11bb27319cd125d43a3d) -* recursive_wrapper test - avoid constructing new functor in recursive calls, call itself via `this` pointer. [view commit](http://github.com/mapbox/variant/commit/ea106db54b167b8dce7c0b3b9b59bb06b209db33) -* Merge branch 'master' of https://github.com/BlueSolei/variant into BlueSolei-master [view commit](http://github.com/mapbox/variant/commit/195367cfc19e1e08933487fa7cc56cb7f6d25cc8) -* Merge branch 'BlueSolei-master' [view commit](http://github.com/mapbox/variant/commit/e01b7bf3334e788fb99f4a510d5bc87a4a581342) -* add test for ref #147 + https://github.com/mapbox/variant/pull/147 [view commit](http://github.com/mapbox/variant/commit/6247207595902dbf898a430346335af2a3485c74) -* - Add a project mapbox_variant. - Use of the 'os' module to capture CXX_STD. - Common configs moved to project. - Built targets moved to 'out' directory. [view commit](http://github.com/mapbox/variant/commit/b2471ffc74c163194943b17b2b2c5758c59655ca) -* - Remove the use of boost libraries. - Add default build. [view commit](http://github.com/mapbox/variant/commit/561a09dd005468f9cdef651030471a1215f1885f) -* - Use of the module 'os' to get BOOST_DIR. - Add macro SINGLE_THREADED to single threading mode. - Define single threading mode as default. - Add lambda_overload_test and hashable_test. [view commit](http://github.com/mapbox/variant/commit/bd0a2d559724b8daa7d1ff33df90976e26a595aa) -* - Add auxiliar rule exe-test. [view commit](http://github.com/mapbox/variant/commit/04a6797a6aa2a86dd3eb6517893255c010f6e524) -* Merge pull request #153 from ricardocosme/boost-build [view commit](http://github.com/mapbox/variant/commit/266f68d9f1c3ad65e6d6c264f0130bc4c652618a) -* Use forwarding reference in make_visitor and visitor [view commit](http://github.com/mapbox/variant/commit/9f991da78d3146d32be67695a89c2b1197c826b2) -* Add copy assignment and move assignment operators. [view commit](http://github.com/mapbox/variant/commit/f0b50062b4fd2bf2b86aeada2efa8e36cfa6cb1c) -* Merge pull request #154 from ricardocosme/forwarding_reference_make_visitor [view commit](http://github.com/mapbox/variant/commit/b78b51548743737357e5b3bbe296f465d5f4fdae) -* add CHANGELOG.md skeleton [view commit](http://github.com/mapbox/variant/commit/555436f715e5e0929a13664f0911ecc4931356d1) -* add to CHANGELOG entries. [view commit](http://github.com/mapbox/variant/commit/6497bce683e6e8edf80f80bc4fed65235690b335) -* update CHANGELOG (git log ... --pretty=format:'* %s [view commit](http://github.com/mapbox/variant/commit/%H)' --reverse) [view commit](http://github.com/mapbox/variant/commit/75bb549d233eb94c74d2730dc3a8d8ed35c87f3d) -* use full sha1 [view commit](http://github.com/mapbox/variant/commit/ba3085a5eb6e874d43432dc75f3392092e1e7214) -* add intial `variant_alternative` implementation (#161 http://en.cppreference.com/w/cpp/utility/variant/variant_alternative) [view commit](http://github.com/mapbox/variant/commit/43357808cc93e69d2975e31e68986137ac5e88c9) -* add lost test check + remove stderr [view commit](http://github.com/mapbox/variant/commit/4b98c485cf7d74691f7921145054641daa66936e) -* alternative implementation of `variant_alternative` [view commit](http://github.com/mapbox/variant/commit/3449d00cf525d8ef98cee0f4a276e2928398a8f9) -* add `variant_alternative_t` [view commit](http://github.com/mapbox/variant/commit/3ffef950b005f31961f167242911b2f97d2634c3) -* add optimized 'variant_alternative' implementation usinh built-in `__type_pack_element` when available (clang++) [view commit](http://github.com/mapbox/variant/commit/ae193141379c1706e17098c67c5b4e4f48b19c48) -* add compile index in range check for __type_pack_element branch. [view commit](http://github.com/mapbox/variant/commit/8b1de314711bff2f9f3c748ac0ed7cd7d6400331) -* fix preprocessor logic [view commit](http://github.com/mapbox/variant/commit/30560e19e60c23227b29bc3434a163d2343333d3) -* add `variant_size` helper [view commit](http://github.com/mapbox/variant/commit/835ebc19321c6a9696a2072b7fbd5ca3de818860) -* Merge pull request #162 from mapbox/variant_alternative [view commit](http://github.com/mapbox/variant/commit/237f83cad2c76b1717ba4076c30aca32339336a8) -* Removes deprecated static_visitor to avoid msvc C4996 compiler warning [view commit](http://github.com/mapbox/variant/commit/215d64585ef92e16f18f5da81195b0279f53f599) -* Merge pull request #163 from MaxRis/master [view commit](http://github.com/mapbox/variant/commit/859a8c933a0c2ab18941acb9dcf834799c0de46c) -* Fix README.md issues [view commit](http://github.com/mapbox/variant/commit/0888a8e92df4c1cfd85419b05910355b2d78013b) -* Merge pull request #165 from nick70/master [view commit](http://github.com/mapbox/variant/commit/5eee328d69aaa805bd0b43bdaede12a8eb4632eb) -* Fix the noexcept specifications for move assignment and conversion. [view commit](http://github.com/mapbox/variant/commit/9c81bef8cf285d9fb45f1eaf9b29eba4fee08d1b) -* Merge pull request #160 from mlogan/master [view commit](http://github.com/mapbox/variant/commit/256ddd55582bb7c06c342315dbacc6a42fee4b34) -* report actual file size not allocated size. [view commit](http://github.com/mapbox/variant/commit/ef3856c85f389d4be7feb6555336168c4adcfa0e) -* use `ls -lah` as `du -h --apparent-size` is not universally supported. [view commit](http://github.com/mapbox/variant/commit/a64062576d9af09469183a94b9770c8e7f877a93) -* fix Makefile [view commit](http://github.com/mapbox/variant/commit/502e32b8bade6e19d7fe511f4634e7033f61235f) -* try fixing travis via upgrading clang++ from 3.9.1 -> 4.0.1 [view commit](http://github.com/mapbox/variant/commit/f31bcfb4bc97cf4c5e89b01bbfa7df4cd553f576) -* steady .. downgrade clang++ to 4.0.0 [view commit](http://github.com/mapbox/variant/commit/11a36a9f12adc30ac47afc9681ec8aa1a2d68c28) -* update mason [view commit](http://github.com/mapbox/variant/commit/fe0a0666fc734033f6a9cd2256226eec5943138a) -* update mason + update clang++ to 4.0.1 [view commit](http://github.com/mapbox/variant/commit/c1a14e7d9e9a10ea7d793d83043d9a18b974ca8e) -* Run ASAN builda in isolated VM via `sudo : required` [view commit](http://github.com/mapbox/variant/commit/5a5ecca5bef02072109a714bab840a36ea772f01) -* Merge pull request #167 from mapbox/clang++4 [view commit](http://github.com/mapbox/variant/commit/0f734f01e685a298e3756d30044a4164786c58c5) -* Moved to in-class initialization [view commit](http://github.com/mapbox/variant/commit/2fef61f08e44bcc99b1acc21ea78554b08d8f6e7) -* Merge pull request #171 from mapbox/jrex-mute-clang-analyzer [view commit](http://github.com/mapbox/variant/commit/0305fdb2a462ca39db7b8cce189561bed17b48 - -## 1.1.5 - -Released: January 7, 2017 - -(d2588a8f1d6b5d480d228e6d8a906ce634bdea9a) - -* add package.json for publishing to npm [view commit](http://github.com/mapbox/variant/commit/cb5635ba2556d76aaba97e4d0fc14b82b48f8b61) -* test with clang 3.9 and g++-6 [view commit](http://github.com/mapbox/variant/commit/efa75df2735d3f5a5fa2646528d4006bf9b5b3dc) -* travis: fix addons [view commit](http://github.com/mapbox/variant/commit/ce2eea64499cd37eec7932ddf82f72b9f1a1b79e) -* makefile improvements [view commit](http://github.com/mapbox/variant/commit/c81b475b40797d503f18ddad4c065f6b1694d341) -* upgrade boost to 1.62.0 [view commit](http://github.com/mapbox/variant/commit/b9c58d631a22e97f4069a819765f5c157525df6a) -* upgrade mason [view commit](http://github.com/mapbox/variant/commit/a760cea8dab53e587498470861b364f1256a5e0d) -* test clang++ via mason [view commit](http://github.com/mapbox/variant/commit/84eeb54c9408297db4bc57e3ea5a1b3d9f075a66) -* fix clang++ PATH [view commit](http://github.com/mapbox/variant/commit/e07a533a8f451fe541db683fc713eb0012730115) -* disable clang++ 3.9, will work on getting working in a branch [view commit](http://github.com/mapbox/variant/commit/702826365d1ea13710b82949c254af5920c92999) -* test with clang++ sanitizers and flto [view commit](http://github.com/mapbox/variant/commit/9b2de45460f4df3a22c6607043d50df730dd42af) -* fix LDFLAGS [view commit](http://github.com/mapbox/variant/commit/20d693ed9a04cc6b689f4c6a1ed58c96f964b08a) -* -fsanitize=cfi and -fsanitize=safe-stack [view commit](http://github.com/mapbox/variant/commit/d1bb6e54608c6bfe3970b2653971da49b3c15ef8) -* more sanitizer options [view commit](http://github.com/mapbox/variant/commit/4fe5ced5db2cb46b23a6e326b5bf9292d95c0642) -* re-enable older compilers, trim excess [view commit](http://github.com/mapbox/variant/commit/6317a0b7406395729139327a83a71cfa14513fac) -* avoid expensive instantiation of tuple constructor in noexcept [view commit](http://github.com/mapbox/variant/commit/4febf973c2a0fc297869d78fc82368a16ee7f4fb) -* Merge pull request #132 from lightmare/avoid-tuple-instantiation [view commit](http://github.com/mapbox/variant/commit/18919174da93165a79ca5fdbfde4b172182c6156) -* enable -Werror, suppress warnings from non variant headers using isystem [view commit](http://github.com/mapbox/variant/commit/253047f53549c3fb1df441859cfd42aecc9f3a8f) -* fix conversion warnings [view commit](http://github.com/mapbox/variant/commit/539d712746d08a172e68a47f7aa73ffdda40b70b) -* build in both release and debug on travis [view commit](http://github.com/mapbox/variant/commit/cf9a534991b5a36f86674975768e0a1600c776be) -* fortification flags + -pthreads for linux where needed [view commit](http://github.com/mapbox/variant/commit/886377de081dd8099ef4a7869ae3ff5d6c850c8d) -* try without pthreads [view commit](http://github.com/mapbox/variant/commit/1023f2d9adcf0a14be7cb729abab8156ecd962c5) -* limit some flags to clang++ [view commit](http://github.com/mapbox/variant/commit/904dcaee6d0d872eae52578e38199577418f9a32) -* Add -pthread [view commit](http://github.com/mapbox/variant/commit/b433986199b7dfbe59fa498eb80791a49c625170) -* upgrade libstdc++ for coverage build [view commit](http://github.com/mapbox/variant/commit/7b409402c3cf4a0260aa36ee768ee474aa3683c1) -* drop -Wstack-protector which gives unhelpful warnings [view commit](http://github.com/mapbox/variant/commit/c8ec829ffb4498a0aea8ffa9b62ba4932cdefbdf) -* disable -Wparentheses for older gcc [view commit](http://github.com/mapbox/variant/commit/a80beaafc20c2c015ace5fb1e4c99f6de8c24d75) -* Merge pull request #133 from mapbox/Werror [view commit](http://github.com/mapbox/variant/commit/a9707c3de095c715f84a1855684a3dc8e37a594a) -* remove useless and/or dubious compiler flags [view commit](http://github.com/mapbox/variant/commit/5141d8d21a5b648e9fef879bfc12b29ffac7288d) -* Merge pull request #134 from lightmare/warnings [view commit](http://github.com/mapbox/variant/commit/18a8055fef0e14110c313ca358f0f7c88290bed0) -* osx: test that will support both latest (10.12) and oldest with c++11 support: 10.7 [view commit](http://github.com/mapbox/variant/commit/4923eb527c129060ba970d622d639ad2ada42497) -* fix gyp build [view commit](http://github.com/mapbox/variant/commit/5baa948fa73313091bc082b9f3d17c5b5f600cac) -* upgrade to llvm 3.9.1 [view commit](http://github.com/mapbox/variant/commit/f5fb4661ebf1ecd0167bce50b00a8339604395b0) -* upgrade mason [view commit](http://github.com/mapbox/variant/commit/61f8acea1b09de639b46c8af0c5aae29f51dd05c) -* Merge pull request #135 from mapbox/llvm-3.9.1 [view commit](http://github.com/mapbox/variant/commit/05b7612aa86c28f95d39ba786c7b611e811e4bf8) -* Trivial missing comma in README example code [view commit](http://github.com/mapbox/variant/commit/d2588a8f1d6b5d480d228e6d8a906ce634bdea9a) - -## 1.1.4 - -Released: December 21, 2016 - -(02bd1ac4c07e6db9fe0f01267853e43b41637b74) - -* Provides Convenient Lambda Overload Visitor Interface, resolves #113. [view commit](http://github.com/mapbox/variant/commit/d09188640b6d5a637f391108f849a962d02dbb40) -* Removes ::type Usage [view commit](http://github.com/mapbox/variant/commit/2275a61974aaf117fa8d08c08d640ffd05935db8) -* Adds C++14 SFINAE Test [view commit](http://github.com/mapbox/variant/commit/4d462f27b2d852f66a3769e5983691c0f9233c9e) -* Merge branch 'daniel-j-h-lambda-visitor' [view commit](http://github.com/mapbox/variant/commit/9a115c5eb3c09509c70a57b25b283b6e1cbba919) -* Makes variant hashable iff Ts... are hashable, closes #125 [view commit](http://github.com/mapbox/variant/commit/97d0379f0afc87cd74a10be56fbccfa04785f568) -* Implements Pattern Matching for Sum Types via `.match` Member Function. [view commit](http://github.com/mapbox/variant/commit/720c23736bb318937d0f53a413a617ff05040b73) -* Adds Documentation for Readme, resolves #98 [view commit](http://github.com/mapbox/variant/commit/d0266436b18ea3b1f15c8a244985b57a0a2b3770) -* Merge pull request #126 from daniel-j-h/hashable [view commit](http://github.com/mapbox/variant/commit/3c17c37aea0d7e3d9e860b746d54160ec820e6a2) -* Merge pull request #128 from daniel-j-h/match [view commit](http://github.com/mapbox/variant/commit/ed84def128ed99a4b3b1ebcde278be7f761e782e) -* Merge pull request #129 from daniel-j-h/docs [view commit](http://github.com/mapbox/variant/commit/02bd1ac4c07e6db9fe0f01267853e43b41637b74) - - -## 1.1.3 - -Released: October 24, 2016 - -(a5a79a594f39d705a7ef969f54a0743516f0bc6d) - -* use C++17 disjunction for no-references and one-convertible tests [view commit](http://github.com/mapbox/variant/commit/2c7ddecdb7ec3b1c1a6bc1797528375e513b7ab0) -* Merge pull request #116 from lightmare/disjunction [view commit](http://github.com/mapbox/variant/commit/8e2f6964157885f1655c1673d65f3aea9b90fe18) -* Update README [view commit](http://github.com/mapbox/variant/commit/aaddee9270e3956cee98cdd7d04aea848d69f5f0) -* expose `using types = std::tuple;` - useful for adapting variant to `boost::spirit` (QI,Karma,X3) [view commit](http://github.com/mapbox/variant/commit/e5818212a8f7ef89df0aa76d5244eca78b8dbb8d) -* add `struct adapted_variant_tag;` [view commit](http://github.com/mapbox/variant/commit/173a7457952d0f24e2c55d5eb3ea785ad41639fb) -* Merge pull request #120 from mapbox/types [view commit](http://github.com/mapbox/variant/commit/84a426a31ad3b63c4b8f8d189841e19af48cda40) -* nicer stderr [view commit](http://github.com/mapbox/variant/commit/9b46167f5c42a19a3b66cb92eb60418486b4e424) -* Fix #122 by adding an extra compile check in universal ctor (via @lightmare) + test case [view commit](http://github.com/mapbox/variant/commit/a5a79a594f39d705a7ef969f54a0743516f0bc6d) - - -## 1.1.2 - -Released: July 26, 2016 - -(388376ac9f0102feba2d2122873b08e15a66a879) - -* Re-implement type matching logic to reject ambigious conversions [view commit](http://github.com/mapbox/variant/commit/71ac8fdf96e547dca34fe58c5cd8d1dce2ef0dac) -* update tests [view commit](http://github.com/mapbox/variant/commit/8be6a2aa8f85e1455198eff31a577a1fb95e1d46) -* comment out code [view commit](http://github.com/mapbox/variant/commit/075d9636fdfe563b535fa3ba087409f940c018e4) -* Merge pull request #114 from mapbox/strict-conversions [view commit](http://github.com/mapbox/variant/commit/388376ac9f0102feba2d2122873b08e15a66a879) - - -## 1.1.1 - -Released: July 18, 2016 - -(c511b2f34d966c09e02a1b833db33a9a1f9b2196) - -## 1.1.0 - -Released: February 11, 2016 - -(5aab5df0dc899b484c04ce9c649645787ee0bc5c) - -* remove erroneous `;` ref #96 [view commit](http://github.com/mapbox/variant/commit/3f025adbf599d8dd9bfca02d45b37e49a2cae841) -* fix coverage to avoid warning: unit.gcno:version '402*', prefer '406*' [view commit](http://github.com/mapbox/variant/commit/b0ee4729bfc9ea649abe40f279de384df75b79d1) -* fix clang 3.8 compile, try 3.9 [view commit](http://github.com/mapbox/variant/commit/f034d5571de987d14b404dba94e7269ac41fa583) -* remove invalid option for llvm-cov [view commit](http://github.com/mapbox/variant/commit/5f6ed7149d737edf9f1f019beb54cbe5289c474c) -* run coverage with clang 3.5 - fix clang 3.8 build [view commit](http://github.com/mapbox/variant/commit/82bb901b6cc0de8c238a07292163dc7c28a26ce4) -* issue warning `-Wweak-vtables` so this issue is not forgotten (https://github.com/mapbox/variant/issues/95) [view commit](http://github.com/mapbox/variant/commit/35ca16c74f5712afb4f042f45ea64078fa0b630e) -* move headers into include/mapbox folder - closes #99 [view commit](http://github.com/mapbox/variant/commit/f00b24bf65e8af7fddc56ac4a3abe67ed974b0a5) -* Update README.md [view commit](http://github.com/mapbox/variant/commit/13c631a6297d9abc9677c1afc1a3907fec7c16b4) -* Add include directory [view commit](http://github.com/mapbox/variant/commit/7e4a01189bb4050524954b2a88c82de7cb82ea4a) -* Merge branch 'master' into include [view commit](http://github.com/mapbox/variant/commit/9bd902536f533305186aaf70edb2f0b9713f6b6b) -* fix typo [view commit](http://github.com/mapbox/variant/commit/a606e90243dcf145cf06d46e8e30c447f85af178) -* ammend include dir [view commit](http://github.com/mapbox/variant/commit/343831611e60e324e311d67f05da953e357df0a1) -* update remaining `` to `` [view commit](http://github.com/mapbox/variant/commit/bfe0f19dd14dedad9c0a6f1e211e81bd1233564e) -* fix compilation [view commit](http://github.com/mapbox/variant/commit/390229a59703d2467347d62f3e134e67ea6835cc) -* Merge pull request #101 from mapbox/include [view commit](http://github.com/mapbox/variant/commit/1bc46e525a9dec71af28f822e5fc031c1352ad2e) -* Remove Xcode 6 from CI matrix [view commit](http://github.com/mapbox/variant/commit/9b2fc858ccd84509fd7164a4847e7dc95e58d5a5) -* Install boost with mason; eliminate boost::timer dependency [view commit](http://github.com/mapbox/variant/commit/04dc3a46b02d6b8714d00280bb85c88716727862) -* `is()` - add specialisation for recursive_wrapper + update tests (ref #102) [view commit](http://github.com/mapbox/variant/commit/c6ae1ea0acf8c4392a806ad3abd5b11eb3b8a8ce) -* remove expected error string - current implementation emits compiler specific error message e.g [view commit](http://github.com/mapbox/variant/commit/4368d75292ae5149034b59b483fc3f8b3956a839) -* Jamroot - add missing include directory ./test/include for auto_cpu_timer.hpp [view commit](http://github.com/mapbox/variant/commit/7f7470fee6a42c3c68f1fa359a28cf762df385c3) -* Update README.md [view commit](http://github.com/mapbox/variant/commit/8bdad6b6d73844ef8437f004654c0745f0cec96a) -* Fix building with GCC (g++-5.2.0) on OS X (Darwin) (ref #108) [view commit](http://github.com/mapbox/variant/commit/55579f03fba747500b3a105ae73a7dfe6059cfc1) -* Update README.md [view commit](http://github.com/mapbox/variant/commit/33e27ec4c7cc5e1669f2181d13eacdfff15dfb61) -* Merge pull request #109 from mapbox/darwin-build-flags [view commit](http://github.com/mapbox/variant/commit/2f8a4a381f2ad8f9c2d3d068a757a3e0e9994495) -* Add get_unchecked() to enable use with exceptions disabled [view commit](http://github.com/mapbox/variant/commit/434dab048d52e4141146bb95fdabdf7aa62e799b) -* remove unused internal metafunctions [view commit](http://github.com/mapbox/variant/commit/48d60445cca1d71fbebb6456b5d45a18bb9cb3b8) -* add static which() function to get a contained types' which value [view commit](http://github.com/mapbox/variant/commit/74ce146d9b96081965d5fcdf53feefab6199468c) -* Merge branch 'master' into 111-which-constexpr [view commit](http://github.com/mapbox/variant/commit/dca3d967c165de1d0fb3bb5e1c2d6b4bcd76782f) -* Merge branch '111-which-constexpr' [view commit](http://github.com/mapbox/variant/commit/bb8c2d20317191f5228341a87b0c362c4d15be5b) -* variant - yield return type of mapbox::util::get automatically and make interface consistent (addresses #82) [view commit](http://github.com/mapbox/variant/commit/adf0e02bceb74339b8ccc3e9e3f316917cb3cc22) -* uncomment tests ref #82 [view commit](http://github.com/mapbox/variant/commit/37acc5a7caef10d2f52dbdcee71be53b79dda027) -* Merge pull request #110 from mapbox/110-get_unchecked [view commit](http://github.com/mapbox/variant/commit/20e44accb1edf84d944d44f91ed7401198368aae) -* c++ apply formatting [view commit](http://github.com/mapbox/variant/commit/372d7c88fe796a138d0e578328914ac80e5a949a) -* use local HAS_EXCEPTIONS #define (__EXCEPTIONS is g++/clang specific macro) [view commit](http://github.com/mapbox/variant/commit/eedafd31f9fdbaffcb605d8d34a3a3443a4f7a2d) -* update .mason pkgs [view commit](http://github.com/mapbox/variant/commit/b5728ad76e1402c130a9330aa44b6f4b655b13b4) -* fix value_traits to be able to match T, T& and T const& to the direct type stored in variant (ref #112) [view commit](http://github.com/mapbox/variant/commit/b3a002d185afac295486e2ebd6b84c78a2267ba0) -* add test for b3a002d185afac295486e2ebd6b84c78a2267ba0 (ref #112) [view commit](http://github.com/mapbox/variant/commit/c511b2f34d966c09e02a1b833db33a9a1f9b2196) - -## 1.0 - -Released: April 1, 2015 - -(bf485dfb59aef26f3ef2183d7c8c1111ad97062b) - -* Initial commit [view commit](http://github.com/mapbox/variant/commit/9b82890ea11742eafd686f44b8cc7075029dbd7b) -* initial import [view commit](http://github.com/mapbox/variant/commit/bb95645b86a8fe427df9af799ecd139e4fab38ef) -* remove unused boost::static_visitor [view commit](http://github.com/mapbox/variant/commit/2dbc79fdd28c2ad9623c6516069ae616af11cdc5) -* call reserve in boost::variant test and change order [view commit](http://github.com/mapbox/variant/commit/9c1d245388396ac289ccee238b549e48a7916f9e) -* add readme and makefile [view commit](http://github.com/mapbox/variant/commit/5afa5b2e1756dfb0e332f3e11bffdfb33cd31f75) -* makefile fixups [view commit](http://github.com/mapbox/variant/commit/c627c07afc972b3f1bab3ca219416d6e918cd39b) -* turn on more aggressive warnings and fix them where appropriate [view commit](http://github.com/mapbox/variant/commit/20ee8ffe2889f9e0f4974ba2aba028ccd3ce347a) -* -Wsign-compare [view commit](http://github.com/mapbox/variant/commit/12ef94a8c0edb897858b82d0064093e251ac4024) -* remove unneeded headers [view commit](http://github.com/mapbox/variant/commit/eeba005f2f2e287f23661e9dd555e57e0365d22c) -* include [view commit](http://github.com/mapbox/variant/commit/a7b562eb0ef665b57e3a29a2ff83721bc3f66a03) -* include headers for new/size_t/move [view commit](http://github.com/mapbox/variant/commit/c3963a96aab7d3d2e380652e4268c5d729778a59) -* add sizes target to check object sizes [view commit](http://github.com/mapbox/variant/commit/a1685041726cba13aa9fb728ec4963f3e7872200) -* add tests [view commit](http://github.com/mapbox/variant/commit/df1d00cc50d3b5a1cce203305d52f536f622663c) -* interleave test runs to ensure first run penalty does not murky the results [view commit](http://github.com/mapbox/variant/commit/969a346db6ab41adb66f7b3cbbcc92cd15c3b339) -* add to gitignore [view commit](http://github.com/mapbox/variant/commit/e98fc0736064d3131a25e899420b41276e801a45) -* fix build on ubuntu/g++-4.8 [view commit](http://github.com/mapbox/variant/commit/b8ab4b8c6beb2d0d4bb2afc9fda901c83588d153) -* debug/release builds + profiling [view commit](http://github.com/mapbox/variant/commit/a25c3597deb4d889085bc9492d2cf4f6df847a5a) -* pass release flags to make sizes [view commit](http://github.com/mapbox/variant/commit/23f112ae5e551a731939e8220a16a6c7a29d844b) -* force inlining to reduce final object size (yes **reduce**, with clang++ at least) [view commit](http://github.com/mapbox/variant/commit/c65aa114402e8f7b1d44062ff973f7c807741d06) -* optimization: avoid overhead of function call for invalid type check [view commit](http://github.com/mapbox/variant/commit/cebd6a31e54d84811cccab62991d142aa8aacbad) -* test threaded [view commit](http://github.com/mapbox/variant/commit/5a84ea2f40ccd2cadbc1091fae05b37b1d69c85c) -* 10 threads [view commit](http://github.com/mapbox/variant/commit/c06ebf15268925ef5e0f3ce763db13fd0c27ef2b) -* use perfect forwarding instead of move [view commit](http://github.com/mapbox/variant/commit/cd81aed73c9f1176e26090abc60a9d2467f5d5bf) -* const as const can. also reimplementation of operator= to allow for ADL in swap [view commit](http://github.com/mapbox/variant/commit/33603114129c387c5938287f1839f852a8f3a5f2) -* Merge pull request #1 from artemp/consts_and_fwds [view commit](http://github.com/mapbox/variant/commit/dff07459931e6d76b4eea6092041cbf247e01f64) -* implement comparison operators (==,<) implement operator<< [view commit](http://github.com/mapbox/variant/commit/cb9374b1ebb4c2da42e06af71ca738e9316b0582) -* make THREADS=4 ( we want to test variant not an operating system, better still make it config (TODO)) add simple test for operator== and operator<< ( define VARIANT_LOGICAL_TESTS to enable) [view commit](http://github.com/mapbox/variant/commit/68aa114c5d06a979b8fb31f0cdd2647833544cb1) -* c++ : better names (#2) [view commit](http://github.com/mapbox/variant/commit/48dd83308ebb5ab21c295499948d27d42801b306) -* c++ : better names (#2) [view commit](http://github.com/mapbox/variant/commit/46f40479fd02170b9d4bf48655a6a6a7845d77cc) -* Merge branch 'master' of github.com:artemp/variant [view commit](http://github.com/mapbox/variant/commit/41d5626bee25a4edd36c2e2d05bde46751417baa) -* Added const [view commit](http://github.com/mapbox/variant/commit/9d7afc7362e095acf032b5a3d2057d960157ffcb) -* Fixed var names [view commit](http://github.com/mapbox/variant/commit/7423b70b907b640b477c9675d79b43abb6bf21ed) -* changing const keyword position to 'T const &' [view commit](http://github.com/mapbox/variant/commit/f6677d163a1c1df6d960baa7d0d7983edac95cc3) -* Add -march=native to release build flags, implies -mtune=.. [view commit](http://github.com/mapbox/variant/commit/3ff69626f348da3f6827fc1573e014400d5f6813) -* Merge pull request #7 from artemp/const_correctness_part2 [view commit](http://github.com/mapbox/variant/commit/f8ff1da09fdb3902b39cb6893fda048b2760da68) -* Merge pull request #8 from artemp/architecture-optimizations [view commit](http://github.com/mapbox/variant/commit/4b325da289df3e91bc32d71d4adceb28d3cf8215) -* fix remaining names [view commit](http://github.com/mapbox/variant/commit/2de14db6a431689e07630f42ee18a30101ed11b3) -* add -march=native to Jamroot [view commit](http://github.com/mapbox/variant/commit/8c03239ed115ed51d181ea52a40f0e9e8ec73f42) -* more name fixing ref #2 [view commit](http://github.com/mapbox/variant/commit/f27bd4c7f514f9f646eb0766cfd8f4b7e8f0ed33) -* structs dont have private members [view commit](http://github.com/mapbox/variant/commit/3d0072d34600bbf2fc21bee3db65770cf6796402) -* add pgo option to Makefile [view commit](http://github.com/mapbox/variant/commit/7f4f85e93d09630dd78ba5eb113bb03fb9804979) -* Merge pull request #9 from artemp/classes_and_structs [view commit](http://github.com/mapbox/variant/commit/21ced9474d93a96bb5c7cd527efa5e0ba742e84a) -* Merge pull request #10 from artemp/profile_guided_optimization [view commit](http://github.com/mapbox/variant/commit/0fc8f6b3911b6b6c0dbe72d5c440c7c094add899) -* + implement binary visitation [view commit](http://github.com/mapbox/variant/commit/943b24689b918f13ee1bdeb43111cbdf49139797) -* more realistic test [view commit](http://github.com/mapbox/variant/commit/e481fdb58ac2fec43a18ba119715f539ba000007) -* fix return types [view commit](http://github.com/mapbox/variant/commit/b41b4f69c21e552198310ebaf17c4776e2b4dc1f) -* Return uint64_t [view commit](http://github.com/mapbox/variant/commit/de6db67da287a8f80b956c85a5a6bff2af6cd52f) -* recursive_wrapper init impl [view commit](http://github.com/mapbox/variant/commit/f1c12747d921763288435beb53fbf23eafb6100d) -* Merge branch 'master' of github.com:artemp/variant [view commit](http://github.com/mapbox/variant/commit/49274509129f7f5957cc785b82771ad772fb2478) -* + add static_visitor requirement to ease return_type deduction [view commit](http://github.com/mapbox/variant/commit/80d999a3471b5647a17c14217ef237f8e9f82543) -* fix binary visitor test [view commit](http://github.com/mapbox/variant/commit/87207d76fb81f87b0bbde00c1ed44ec6043103fa) -* use static_visitor as a base class for all visitors [view commit](http://github.com/mapbox/variant/commit/48f78bcd767b99575a5af4aeb0490342e8a2b7a9) -* recursive_wrapper test (work-in-progress) [view commit](http://github.com/mapbox/variant/commit/05af9c4f21eefc91c713bc1e34e0e04b0556519a) -* include recursive_wrapper [view commit](http://github.com/mapbox/variant/commit/cbd9e2cf91e58baebd79ce336ba4b20148f303af) -* update test (FIXME - inteface is very clunky atm and needs more work) [view commit](http://github.com/mapbox/variant/commit/d2cda9a88684d2e7fad3d3527e53f7c64c84378e) -* unwrap recursive_wrapper [view commit](http://github.com/mapbox/variant/commit/bc65cb8d7be23806dc63c2adf03fc8e9b5557f66) -* + const [view commit](http://github.com/mapbox/variant/commit/6e9a10f43eab476d68399d6dcc51a2464cd2f0b8) -* recursive variant test using std::unique_ptr move semantics [view commit](http://github.com/mapbox/variant/commit/5ebf86772daade4490652218e3437d5d0ca7d1de) -* add missing test file [view commit](http://github.com/mapbox/variant/commit/b4abfa58efb676aa86a3dd2dfee04f7e95aee7fd) -* update recursive_wrapper and unique_ptr tests to accept arg [view commit](http://github.com/mapbox/variant/commit/edcb444afc193784cb6237616d68973b36e7920e) -* make test -> make bench [view commit](http://github.com/mapbox/variant/commit/fc80297390285e83394983201473d01edfa17d4c) -* fix compile of test/variant [view commit](http://github.com/mapbox/variant/commit/da417016e6af968cdcea1b2ec9f2023d3ee77cad) -* recursive_wrapper.hpp depends on boost checked delete for the moment [view commit](http://github.com/mapbox/variant/commit/a8d019d470e11eb3a569581ce140397bb6f6e31d) -* shuffle code, all build targets in out/ directory [view commit](http://github.com/mapbox/variant/commit/8e5abd0f14e298b9cc93f7428c2cd21ce732fad9) -* all tests in ./test directory [view commit](http://github.com/mapbox/variant/commit/dadea1f2a7d9c08e1a5979cc5bb824779fb73380) -* add travis [view commit](http://github.com/mapbox/variant/commit/7e775d10774261b5461474013b840fdd899db023) -* fix travis targets [view commit](http://github.com/mapbox/variant/commit/c6bd4f8131f6a58d6d6dfaf3017c8793a0da9819) -* travis: upgrade to gcc-4.8 for c++11 support [view commit](http://github.com/mapbox/variant/commit/84fdc9e2c0701980c083ff3cb8701f3076f10fa6) -* fix a few -Wsign-compare warnings [view commit](http://github.com/mapbox/variant/commit/d3d0704c59dba67f925b707d6c72babadd607ce9) -* fix -Wshadow warning [view commit](http://github.com/mapbox/variant/commit/e19d0e5aca3b4e4a76e29e34352d634b7c495202) -* fix linux compile of binary_visitor_test.cpp [view commit](http://github.com/mapbox/variant/commit/d8df077f29428c55688910d5f96e79b6d351d5e1) -* qualify c++11 int types [view commit](http://github.com/mapbox/variant/commit/62e7165925bfce3e4c11a2fce1cad8d486825613) -* fix #12 [view commit](http://github.com/mapbox/variant/commit/c0593af2c3066d13f30bf15241da313539539f18) -* test with both gcc 4.7 and 4.8 [view commit](http://github.com/mapbox/variant/commit/916719bb38cec2e9f4ff7c3d2ef480734badbb7d) -* Add BSD license [view commit](http://github.com/mapbox/variant/commit/cf7f7bef518d7dd67d74e84bdbaf40a6c5826114) -* add unit tests [view commit](http://github.com/mapbox/variant/commit/0ef558f9607844fa88b1857e6ff993121dd84d71) -* port logical tests to test/unit.cpp - refs #15 [view commit](http://github.com/mapbox/variant/commit/7109df9c4f423b846839240d720de4a89be101c8) -* version, starting at 0.1.0 [view commit](http://github.com/mapbox/variant/commit/e5c89779e59bc22571ce5beb27a823b94b2be786) -* try building on windows with gyp [view commit](http://github.com/mapbox/variant/commit/b97876a2e5ddaf8931cecd36b5a649fb3e1eb420) -* call gyp_main.py [view commit](http://github.com/mapbox/variant/commit/24cddfbc76c713046b130158c72231bd827d4a23) -* be explicit about config and platform [view commit](http://github.com/mapbox/variant/commit/bc80b31f8c4afd469f65046cfc3fe18bba95fbd3) -* also test building with gyp on travis [view commit](http://github.com/mapbox/variant/commit/51eda4f439c24223a964fe25d077ff1efa27dce0) -* try 'any cpu' [view commit](http://github.com/mapbox/variant/commit/41056fa781bf3c6a79182db338595b44d6370c25) -* put msbuild on path [view commit](http://github.com/mapbox/variant/commit/948a6ccb89ae2b4a3682805519b585bf573763a9) -* appveyor: try building 32 bit [view commit](http://github.com/mapbox/variant/commit/1b89ab841021d28da193e3b1fab27269651bdc17) -* Update README.md [view commit](http://github.com/mapbox/variant/commit/011b5125a4bb139f794bb37080bd33cae6519f7c) -* + it breaks builds for me - I have 'using clang ..' in $(boost-dir)/tools/build/v2/user-config.jam where it should be. [view commit](http://github.com/mapbox/variant/commit/1b2fc49bbc7e76bbaf2c64ea44cb3f287d5948a5) -* ```apply_visitor``` interface not-compatible with boost::apply_visitor (by changing args order) wasn't smart [view commit](http://github.com/mapbox/variant/commit/67ac560672e5ea7bf99a4a823b5259ccd6d1bd5d) -* fix syntax [view commit](http://github.com/mapbox/variant/commit/002ccdcde4aacfcc6b67ad4981c05402514c09f1) -* windows support [view commit](http://github.com/mapbox/variant/commit/64f8fb4473f3ef6f7117dc02f02a20645e415b72) -* update readme [view commit](http://github.com/mapbox/variant/commit/20f26eceebef717a0ee759b42bfe939a95874807) -* appeveyor: try building for just x86 [view commit](http://github.com/mapbox/variant/commit/b3f8117d05071981106b7d1fe15405a318d3c1df) -* fix setting of msbuild_toolset for c++11 support [view commit](http://github.com/mapbox/variant/commit/d0603d41a8597089c0c1cd099ebb8bcf5e1414d3) -* Add vcbuild.bat [view commit](http://github.com/mapbox/variant/commit/bb12b87574fcdaa7bdca6c1529d442eab2d52b97) -* remove sizeof checks since they are implementation depedenent [view commit](http://github.com/mapbox/variant/commit/f5af06c20329a0c762af9420a3ae2e3d22c02c91) -* comment failing test on windows [view commit](http://github.com/mapbox/variant/commit/7d4dc68667659e7585d28f743509956e92759255) -* appveyor: re-enable matrix [view commit](http://github.com/mapbox/variant/commit/6dbc0546bdab9e8dc291eb08d2e6dce13a0dcfe8) -* add to be include order agnostic [view commit](http://github.com/mapbox/variant/commit/9be8c519c67ef2ec8d3b5a7b736f0a8b870b43ed) -* Merge pull request #16 from DennisOSRM/master [view commit](http://github.com/mapbox/variant/commit/b27a14e98d9eadf2aabd628a602f9e4a5fcb0a5f) -* move detail tests + add initial comments (@artemp, please review) [view commit](http://github.com/mapbox/variant/commit/ea25e84aeec16588857ac83c1241d34a5df4adac) -* fix typo in code comment [skip ci] [view commit](http://github.com/mapbox/variant/commit/b773421f5868315eccd504204f32ee8c02e78dc6) -* rename internal id to index, add tests [view commit](http://github.com/mapbox/variant/commit/390e2315b97bbc71aa575d98bacc1ed760d0aa4a) -* Merge pull request #18 from mapbox/type_index [view commit](http://github.com/mapbox/variant/commit/046296a95c9d34224c23c843bc8bd6d502f81b47) -* modify tests slightly to output num_iter ((3+2)-4) [view commit](http://github.com/mapbox/variant/commit/602eceb753751ae30cd5ca7a25d3337179ba6b5e) -* boost is uneeded for unit.cpp tests [view commit](http://github.com/mapbox/variant/commit/e1de3d78e8827d5b6819cc91b430429a46093c95) -* enable ctor's for valid types at compile time [view commit](http://github.com/mapbox/variant/commit/31e3fd96fe4f86d9ac5ac53d6e3e07605307eb5c) -* [travis] multi-os [view commit](http://github.com/mapbox/variant/commit/2f1e36d25f152c31cc3b2673946a8670dd189e74) -* fix path to boost/variant.hpp on linux [view commit](http://github.com/mapbox/variant/commit/b31579c99042d36672fa9eefb09bcdb01e1bcc0a) -* move variant and friends into mapbox namespace for easy integration [view commit](http://github.com/mapbox/variant/commit/df55ab6ef48d1a28c58e409d71b3ee4b416cdf31) -* fix namespace [view commit](http://github.com/mapbox/variant/commit/397ed9c90bc55bbf2f3331f43a707789869d064a) -* inline accessors/setters [view commit](http://github.com/mapbox/variant/commit/2ebabd9b6cb1b95605b01708899498ac70b84201) -* default ctor : initialise with default contructed first type in parameters pack [view commit](http://github.com/mapbox/variant/commit/4d038f1462698c0977e14eacfd9abeb8da95c852) -* add default ctor test [view commit](http://github.com/mapbox/variant/commit/d7fa62b52f771f0d194441b5889409e792eff740) -* c++11 : use type aliases instead of typedefs [view commit](http://github.com/mapbox/variant/commit/03eb4c7d287b72eb43d2b6456ef38437c8c0ac34) -* converting operator= [view commit](http://github.com/mapbox/variant/commit/0eed7c3c1477e0673d379336b0745606d2780d8f) -* avoid wrapped object copying [view commit](http://github.com/mapbox/variant/commit/f7648ba392aea548fe0d583ceee2048ad57e2f54) -* fix move ctor + housekeeping [view commit](http://github.com/mapbox/variant/commit/6aee8c4a7543e0d38f64a07d67aaf394bea704d9) -* add [view commit](http://github.com/mapbox/variant/commit/82cc6e2335b377b5e61e9a3bc974e619658ab515) -* remove unused header [view commit](http://github.com/mapbox/variant/commit/74425f135a6b7cdd49d86d2dd84cf9bdfe9154c2) -* uncomment to test single threaded [view commit](http://github.com/mapbox/variant/commit/7296d18458e2dbfc7ddb39f0fe5b96962c264dc6) -* fix bug : assign correct index (in reverse order of args) e.g first type is sizeof...(Types) - 1 [view commit](http://github.com/mapbox/variant/commit/7eb748eb65729c2d91a7c4f16e5bd56eb3038bdd) -* fix default ctor unit test [view commit](http://github.com/mapbox/variant/commit/03af9e421b36e17a9b873cb24347f6bad7c3dc6d) -* [gyp] fix typo in Windows release targets [view commit](http://github.com/mapbox/variant/commit/2d8ca78704f69c6498a0e689222588275c8f33aa) -* add non-const visitor interface (#22) [view commit](http://github.com/mapbox/variant/commit/54d07c9d336c989fe3c1231bbb7f6465ebc68da2) -* add unary_visitor test [view commit](http://github.com/mapbox/variant/commit/3b31c34368613c4cd101e6f7ee9677c2c7b6b75e) -* support implicit type convertions [view commit](http://github.com/mapbox/variant/commit/724a40baec3ded142c631b66521502840d8d183f) -* update to use recursive_wrapper -> T conversions [view commit](http://github.com/mapbox/variant/commit/7e609812f15cbf41fab978fd3222c6029a5b67eb) -* unit test : update to use latest variant impl [view commit](http://github.com/mapbox/variant/commit/d1392fa431b5b5dac99cdb8b937ec05e13d98847) -* Mapbox name without the inner uppercase B. [view commit](http://github.com/mapbox/variant/commit/4e11d41723af714895c90cb8b9798527c946b2b4) -* Fix typo in comment. [view commit](http://github.com/mapbox/variant/commit/01509c76c30bea7a6bfffd0e59f9bbdbae8f1026) -* Formatting fixes. [view commit](http://github.com/mapbox/variant/commit/cfd7d991b23b2ecc659d18f22a2f78a05074450c) -* Use formatting "TYPE const&". [view commit](http://github.com/mapbox/variant/commit/9253ffdda65bd41ed21f9328a20a335fa0f5d582) -* Remove superfluous and inconsistent whitespace. [view commit](http://github.com/mapbox/variant/commit/d855ba8672fc5f439594325663899564f0632c94) -* Add comments for closing namespaces. [view commit](http://github.com/mapbox/variant/commit/3c2d662abb352bd429777251ddc613e751e49123) -* Merge branch 'joto-master' [view commit](http://github.com/mapbox/variant/commit/49e9f351a7a8d5615f8fb5ae2d4840042fc95bcc) -* Fix typos, whitespace and test tags. [view commit](http://github.com/mapbox/variant/commit/a12326984f323d10648115d94ace0b510d097b06) -* Add tests for implicit conversion and exceptions for wrong types. [view commit](http://github.com/mapbox/variant/commit/12c70938b00215ce86e48657d60a650dbfd8966e) -* Add test for printer visitor. [view commit](http://github.com/mapbox/variant/commit/fd470a6fde0941407a74c3d544c009eac0579e4f) -* Add test case for issue #25. [view commit](http://github.com/mapbox/variant/commit/74f1c5d9b02788fb9524ae674d43fdf07671d9cc) -* fix appveyor link in readme [view commit](http://github.com/mapbox/variant/commit/d49f82efb7689b8bc67be30633dd7f41b54f2e5a) -* Remove the need for the static_visitor class. [view commit](http://github.com/mapbox/variant/commit/e9283622a33231500c8cc3d1f27358d431c1e8a2) -* msvs: also define _DEBUG for debug builds [view commit](http://github.com/mapbox/variant/commit/c4bb359f883b568a88d5be8b3e4ce5c45626d71f) -* [appveyor] more debug flags [view commit](http://github.com/mapbox/variant/commit/b852c8d386e59a145a6b684d036ee7e327d0efe2) -* [appveyor][gyp] correct msvs_settings usage [view commit](http://github.com/mapbox/variant/commit/1f707864e37c305779e3398006b20e2ee5f14866) -* customize release builds [view commit](http://github.com/mapbox/variant/commit/4661a3c06b3738c7564c18474577d4faece6613a) -* [gyp][windows] add exception handling/rtti [view commit](http://github.com/mapbox/variant/commit/02556c9c317834ad3f05aa88dacc072ad9a63c99) -* use numeric values in valid range to avoid overflowing [view commit](http://github.com/mapbox/variant/commit/854c5a7a115d92b67698f3654d915edd483456b9) -* Removed wrong comments. [view commit](http://github.com/mapbox/variant/commit/8b6c0b34b76144d697ede0f386f1ea2f9c6ae2d1) -* Removed test case. [view commit](http://github.com/mapbox/variant/commit/08a7e04e3cf3ebd54e289201b1bd3d85225248ee) -* allow explicit un-initilised variant ctor [view commit](http://github.com/mapbox/variant/commit/d99139d4dee3ae4fa2b7465000d08fc5f5907e82) -* add boost::variant compatible accessors [view commit](http://github.com/mapbox/variant/commit/2afd3415cc28b8b7184e2750ac90e739c078b335) -* more verbose output to test script [view commit](http://github.com/mapbox/variant/commit/0c8b6b736788718ed28a0676cdb0b390249d5540) -* variant : make operator<< stricter to avoid unexpected instantiations [view commit](http://github.com/mapbox/variant/commit/76027798360405d910f8af9d6ae873fa5905be13) -* Add details on advantages of Mapbox variant [view commit](http://github.com/mapbox/variant/commit/a5ee02b2aacb0109725c611beaafc27dce89c12d) -* remove unneeded header [view commit](http://github.com/mapbox/variant/commit/73da70737f0e55b2276173ca923eb91bd8ae4f32) -* less debug info in release mode [view commit](http://github.com/mapbox/variant/commit/607ed1344f8117000e03da0b8bfb9268a1f826ee) -* rough cut of an optional type [view commit](http://github.com/mapbox/variant/commit/9badbd0fa37863240cf678be61116003d345a20a) -* add operator* and a static assert against reference types [view commit](http://github.com/mapbox/variant/commit/1141292eeed4a95f3e54c85e98f9ded180f84f32) -* Merge pull request #30 from DennisOSRM/optional [view commit](http://github.com/mapbox/variant/commit/32f971794c643d11f5bf374caef44cee295cdf7d) -* remove some whitespace [view commit](http://github.com/mapbox/variant/commit/f4bcf3ff733acbd4a798f1e9a5f80d5422bc9b79) -* explicit operator bool() const noexcept [view commit](http://github.com/mapbox/variant/commit/c77d98b3e8d06714ed0b0c288cd9ad457f2708c4) -* rename none_t -> none_type, move to private member of optional [view commit](http://github.com/mapbox/variant/commit/43e2e9a943555c09ce6ffb9c825a64bff229a3a6) -* remove instantiation of none_type as per @kkaefer's suggestion [view commit](http://github.com/mapbox/variant/commit/7242396fb5468defba37afd03e7a633075fa51ce) -* remove none_type and its complicated typedef from detail namespace, add it as private member class to optional [view commit](http://github.com/mapbox/variant/commit/c5f720515ad21fe3913fc841ea427ebdd1fa4c68) -* guard against self-assignment [view commit](http://github.com/mapbox/variant/commit/10a039c78b58857f8793dae3b0359cf1d6fb11ef) -* add unit tests for optional type [view commit](http://github.com/mapbox/variant/commit/607c6332cb96261a43a16fa94aea5d034ae58aac) -* Merge pull request #31 from mapbox/optional [view commit](http://github.com/mapbox/variant/commit/4cdd805a04175248b221753a0974de15c8f5b397) -* reformat optional.hpp to be more legible and less linty [view commit](http://github.com/mapbox/variant/commit/54e1dfe7631207da3f6a2bbc895c5240592ea5ea) -* universal-references: template variant(T && val) {} it turned out we don't need a separate converting copy ctor (fixes windows compiler) [view commit](http://github.com/mapbox/variant/commit/82df4ed9cd2a8335c7e50b913f827216604dab07) -* Merge pull request #32 from mapbox/universal-references [view commit](http://github.com/mapbox/variant/commit/804fb6f370a35eaea3e273ebfcb2f92ef7379e91) -* use std::forward for perfect forwarding (via @DennisOSRM) [view commit](http://github.com/mapbox/variant/commit/eb995158362307cea98e36eb782dd4ae63593d0f) -* fix assignment of optionals, adds a unit test [view commit](http://github.com/mapbox/variant/commit/f69f317069523cb6994fa9d594240deeb537753c) -* Merge pull request #33 from mapbox/fix_assignment_operator [view commit](http://github.com/mapbox/variant/commit/c56af229af98b71ba8ef637f3237cab828f0ec14) -* readme improvements [view commit](http://github.com/mapbox/variant/commit/5b8972b6ebcd095f35b810c43639785235218bbd) -* Merge branch 'master' into result_of [view commit](http://github.com/mapbox/variant/commit/4900027d0f66a7c701e22c667e92ce7b7521a73c) -* cast lhs to rhs type to avoid signed/unsigned comparisons warnings [view commit](http://github.com/mapbox/variant/commit/17074a3de539256ccab3871033df33c9d387dcbf) -* attempting to fix travis [view commit](http://github.com/mapbox/variant/commit/4fb9bd65ac4a204f8721f9528df9c14907a151f9) -* Merge branch 'master' into result_of [view commit](http://github.com/mapbox/variant/commit/fbcc50b57e58460e0cd90a232673271eff72311c) -* make deriving from static_visitor and providing result_type an option [view commit](http://github.com/mapbox/variant/commit/76ab6d4aa54664d7f8849695896ca57f4984bed0) -* Merge branch 'result_of' [view commit](http://github.com/mapbox/variant/commit/3cc2d708e0552ba4ebce11d4d67f798813f15aa2) -* fix automatic return_type calculation - ref #35 [view commit](http://github.com/mapbox/variant/commit/00ab88117ed25f78cdca2faa00beea0061271e85) -* test either g++ or clang++ [view commit](http://github.com/mapbox/variant/commit/199b3eca054075319a1978f09bdd77f9ff42e681) -* try adding code coverage / coveralls upload [view commit](http://github.com/mapbox/variant/commit/11e65282e335ed4a50bf261d21b8194230787ed8) -* fix bash syntax [view commit](http://github.com/mapbox/variant/commit/2fbfcd33998992df4d77f329e7a75c864616d0ab) -* add recursive wrapper to coverage [view commit](http://github.com/mapbox/variant/commit/f87a1cf10d62814d3bf4e72ac260a92e483ffb25) -* move operator<< into separate header [view commit](http://github.com/mapbox/variant/commit/24dcab23c4f70e54838e4a32a228aba8045ae17b) -* add coverage report [view commit](http://github.com/mapbox/variant/commit/89f8a41a4da81c2a01d39f83cf52e61997ce76ba) -* clean up coverage files in test directory too [skip ci] [view commit](http://github.com/mapbox/variant/commit/7dfdfa271e43780aff640852a632e88443b6fe32) -* add get() overloads for when T is stored in recursive_wrapper also makes get() a compile time error where T is not in Types... (ref #24) [view commit](http://github.com/mapbox/variant/commit/36f1e12f66570f2a1f04535dc0ac0485d48e2bbe) -* update unit test to match c64c74775a80474f2012c1a49ab2865e3666107a [view commit](http://github.com/mapbox/variant/commit/c117592337cc20c238d4ce9f9d8847aff0cd55ab) -* add which() method returning zero based index of stored T in Types... for boost::variant() compatibility [view commit](http://github.com/mapbox/variant/commit/3b02ca0e3ab1a36dd6ec9138e7f93eb3176ae5f7) -* add reference_wrapper test [view commit](http://github.com/mapbox/variant/commit/5a2d5c5f292ae2d6128d02e7ee2b3b3d72facfc2) -* remove boost variant header [view commit](http://github.com/mapbox/variant/commit/c53422fb2d4e44b7e276fcde65498b603f429a2f) -* add support for 'unwrapping' std::reference_wrapper and accessing std::reference_wrapper::type through get() + update test [view commit](http://github.com/mapbox/variant/commit/587519521ae0d9a24f997ab2dff77f13309aa5d2) -* pass F (functor) by ref/const ref [view commit](http://github.com/mapbox/variant/commit/2e0ce4a86d0b9d0ff9855838349fb599b15a274a) -* pass by const ref in 'apply_const' [view commit](http://github.com/mapbox/variant/commit/a3014f54651b71e25d81fbeaf99f29d63c625703) -* Revert "pass by const ref in 'apply_const'" [view commit](http://github.com/mapbox/variant/commit/e031c53d0c876ecf2b9d4a6e0a5383bd4169df71) -* Revert "pass F (functor) by ref/const ref" [view commit](http://github.com/mapbox/variant/commit/bf485dfb59aef26f3ef2183d7c8c1111ad97062b) diff --git a/third_party/variant/Jamroot b/third_party/variant/Jamroot deleted file mode 100644 index 4e7a01f39..000000000 --- a/third_party/variant/Jamroot +++ /dev/null @@ -1,62 +0,0 @@ -# Unofficial and incomplete build file using Boost build system. -# You should use make unless you know what you are doing. - -import os ; - -local boost_dir = [ os.environ BOOST_DIR ] ; -if ! $(boost_dir) -{ - boost_dir = "/usr/local" ; -} - -#using clang : : ; - -local cxx_std = [ os.environ CXX_STD ] ; -if ! $(cxx_std) -{ - cxx_std = c++11 ; -} - -project mapbox_variant - : requirements - -std=$(cxx_std) - $(boost_dir)/include - include - test/include - release:-march=native - single:SINGLE_THREADED - : default-build - release - speed - single - ; - -rule exe-test ( name : reqs * : deps * ) -{ - exe $(name) - : test/$(name).cpp - : $(reqs) - : $(deps) - ; - explicit $(name) ; -} - -exe-test bench_variant - : release:-Wweak-vtables - ; - -exe-test binary_visitor_test ; -exe-test recursive_wrapper_test ; -exe-test unique_ptr_test ; -exe-test reference_wrapper_test ; -exe-test lambda_overload_test ; -exe-test hashable_test ; - -install out - : bench_variant - binary_visitor_test - unique_ptr_test - reference_wrapper_test - lambda_overload_test - hashable_test - ; diff --git a/third_party/variant/LICENSE b/third_party/variant/LICENSE deleted file mode 100644 index 6c4ce40d5..000000000 --- a/third_party/variant/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) MapBox -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 "MapBox" 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 HOLDER 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. \ No newline at end of file diff --git a/third_party/variant/LICENSE_1_0.txt b/third_party/variant/LICENSE_1_0.txt deleted file mode 100644 index 36b7cd93c..000000000 --- a/third_party/variant/LICENSE_1_0.txt +++ /dev/null @@ -1,23 +0,0 @@ -Boost Software License - Version 1.0 - August 17th, 2003 - -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/third_party/variant/Makefile b/third_party/variant/Makefile deleted file mode 100644 index 2559c0579..000000000 --- a/third_party/variant/Makefile +++ /dev/null @@ -1,157 +0,0 @@ -MASON = .mason/mason -BOOST_VERSION = 1.62.0 - -CXX := $(CXX) -CXX_STD ?= c++11 - -BOOST_ROOT = $(shell $(MASON) prefix boost $(BOOST_VERSION)) -BOOST_FLAGS = -isystem $(BOOST_ROOT)/include/ -RELEASE_FLAGS = -O3 -DNDEBUG -march=native -DSINGLE_THREADED -fvisibility-inlines-hidden -fvisibility=hidden -DEBUG_FLAGS = -O0 -g -DDEBUG -fno-inline-functions -fno-omit-frame-pointer -fPIE -WARNING_FLAGS = -Werror -Wall -Wextra -pedantic \ - -Wformat=2 -Wsign-conversion -Wshadow -Wunused-parameter - -COMMON_FLAGS = -std=$(CXX_STD) -COMMON_FLAGS += $(WARNING_FLAGS) - -CXXFLAGS := $(CXXFLAGS) -LDFLAGS := $(LDFLAGS) - -export BUILDTYPE ?= Release - -OS := $(shell uname -s) -ifeq ($(OS), Linux) - EXTRA_FLAGS = -pthread -endif -ifeq ($(OS), Darwin) - OSX_OLDEST_SUPPORTED ?= 10.7 - # we need to explicitly ask for libc++ otherwise the - # default will flip back to libstdc++ for mmacosx-version-min < 10.9 - EXTRA_FLAGS = -stdlib=libc++ -mmacosx-version-min=$(OSX_OLDEST_SUPPORTED) -endif - - -ifeq ($(BUILDTYPE),Release) - FINAL_CXXFLAGS := $(COMMON_FLAGS) $(RELEASE_FLAGS) $(CXXFLAGS) $(EXTRA_FLAGS) -else - FINAL_CXXFLAGS := $(COMMON_FLAGS) $(DEBUG_FLAGS) $(CXXFLAGS) $(EXTRA_FLAGS) -endif - - - -ALL_HEADERS = $(shell find include/mapbox/ '(' -name '*.hpp' ')') - -all: out/bench-variant out/unique_ptr_test out/unique_ptr_test out/recursive_wrapper_test out/binary_visitor_test out/lambda_overload_test out/hashable_test - -$(MASON): - git submodule update --init .mason - -mason_packages/headers/boost: $(MASON) - $(MASON) install boost $(BOOST_VERSION) - -./deps/gyp: - git clone --depth 1 https://chromium.googlesource.com/external/gyp.git ./deps/gyp - -gyp: ./deps/gyp - deps/gyp/gyp --depth=. -Goutput_dir=./ --generator-output=./out -f make - make V=1 -C ./out tests - ./out/$(BUILDTYPE)/tests - -out/bench-variant-debug: Makefile mason_packages/headers/boost test/bench_variant.cpp - mkdir -p ./out - $(CXX) -o out/bench-variant-debug test/bench_variant.cpp -I./include -isystem test/include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) - -out/bench-variant: Makefile mason_packages/headers/boost test/bench_variant.cpp - mkdir -p ./out - $(CXX) -o out/bench-variant test/bench_variant.cpp -I./include -isystem test/include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) - -out/unique_ptr_test: Makefile mason_packages/headers/boost test/unique_ptr_test.cpp - mkdir -p ./out - $(CXX) -o out/unique_ptr_test test/unique_ptr_test.cpp -I./include -isystem test/include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) - -out/recursive_wrapper_test: Makefile mason_packages/headers/boost test/recursive_wrapper_test.cpp - mkdir -p ./out - $(CXX) -o out/recursive_wrapper_test test/recursive_wrapper_test.cpp -I./include -isystem test/include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) - -out/binary_visitor_test: Makefile mason_packages/headers/boost test/binary_visitor_test.cpp - mkdir -p ./out - $(CXX) -o out/binary_visitor_test test/binary_visitor_test.cpp -I./include -isystem test/include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) - -out/lambda_overload_test: Makefile mason_packages/headers/boost test/lambda_overload_test.cpp - mkdir -p ./out - $(CXX) -o out/lambda_overload_test test/lambda_overload_test.cpp -I./include -isystem test/include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) - -out/hashable_test: Makefile mason_packages/headers/boost test/hashable_test.cpp - mkdir -p ./out - $(CXX) -o out/hashable_test test/hashable_test.cpp -I./include -isystem test/include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) - -bench: out/bench-variant out/unique_ptr_test out/unique_ptr_test out/recursive_wrapper_test out/binary_visitor_test - ./out/bench-variant 100000 - ./out/unique_ptr_test 100000 - ./out/recursive_wrapper_test 100000 - ./out/binary_visitor_test 100000 - -out/unit.o: Makefile test/unit.cpp - mkdir -p ./out - $(CXX) -c -o $@ test/unit.cpp -isystem test/include $(FINAL_CXXFLAGS) - -out/%.o: test/t/%.cpp Makefile $(ALL_HEADERS) - mkdir -p ./out - $(CXX) -c -o $@ $< -Iinclude -isystem test/include $(FINAL_CXXFLAGS) - -out/unit: out/unit.o \ - out/binary_visitor_1.o \ - out/binary_visitor_2.o \ - out/binary_visitor_3.o \ - out/binary_visitor_4.o \ - out/binary_visitor_5.o \ - out/binary_visitor_6.o \ - out/issue21.o \ - out/issue122.o \ - out/mutating_visitor.o \ - out/optional.o \ - out/recursive_wrapper.o \ - out/sizeof.o \ - out/unary_visitor.o \ - out/variant.o \ - out/variant_alternative.o \ - out/nothrow_move.o \ - out/visitor_result_type.o \ - - mkdir -p ./out - $(CXX) -o $@ $^ $(LDFLAGS) - -test: out/unit - ./out/unit - -coverage: - mkdir -p ./out - $(CXX) -o out/cov-test --coverage test/unit.cpp test/t/*.cpp -I./include -isystem test/include $(FINAL_CXXFLAGS) $(LDFLAGS) - -sizes: Makefile - mkdir -p ./out - @$(CXX) -o ./out/our_variant_hello_world.out include/mapbox/variant.hpp -I./include $(FINAL_CXXFLAGS) && ls -lah ./out/our_variant_hello_world.out - @$(CXX) -o ./out/boost_variant_hello_world.out $(BOOST_ROOT)/include/boost/variant.hpp -I./include $(FINAL_CXXFLAGS) $(BOOST_FLAGS) && ls -lah ./out/boost_variant_hello_world.out - @$(CXX) -o ./out/our_variant_hello_world ./test/our_variant_hello_world.cpp -I./include $(FINAL_CXXFLAGS) && ls -lah ./out/our_variant_hello_world - @$(CXX) -o ./out/boost_variant_hello_world ./test/boost_variant_hello_world.cpp -I./include $(FINAL_CXXFLAGS) $(BOOST_FLAGS) && ls -lah ./out/boost_variant_hello_world - -profile: out/bench-variant-debug - mkdir -p profiling/ - rm -rf profiling/* - iprofiler -timeprofiler -d profiling/ ./out/bench-variant-debug 500000 - -clean: - rm -rf ./out - rm -rf *.dSYM - rm -f unit.gc* - rm -f *gcov - rm -f test/unit.gc* - rm -f test/*gcov - rm -f *.gcda *.gcno - -pgo: out Makefile - $(CXX) -o out/bench-variant test/bench_variant.cpp -I./include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) -pg -fprofile-generate - ./test-variant 500000 >/dev/null 2>/dev/null - $(CXX) -o out/bench-variant test/bench_variant.cpp -I./include $(FINAL_CXXFLAGS) $(LDFLAGS) $(BOOST_FLAGS) -fprofile-use - -.PHONY: sizes test diff --git a/third_party/variant/README.md b/third_party/variant/README.md deleted file mode 100644 index 6bf97f0c4..000000000 --- a/third_party/variant/README.md +++ /dev/null @@ -1,248 +0,0 @@ -# Mapbox Variant - -An header-only alternative to `boost::variant` for C++11 and C++14 - -[![Build Status](https://secure.travis-ci.org/mapbox/variant.svg)](https://travis-ci.org/mapbox/variant) -[![Build status](https://ci.appveyor.com/api/projects/status/v9tatx21j1k0fcgy)](https://ci.appveyor.com/project/Mapbox/variant) -[![Coverage Status](https://coveralls.io/repos/mapbox/variant/badge.svg?branch=master&service=github)](https://coveralls.io/r/mapbox/variant?branch=master) - -## Introduction - -Variant's basic building blocks are: - -- `variant` - a type-safe representation for sum-types / discriminated unions -- `recursive_wrapper` - a helper type to represent recursive "tree-like" variants -- `apply_visitor(visitor, myVariant)` - to invoke a custom visitor on the variant's underlying type -- `get()` - a function to directly unwrap a variant's underlying type -- `.match([](Type){})` - a variant convenience member function taking an arbitrary number of lambdas creating a visitor behind the scenes and applying it to the variant - -### Basic Usage - HTTP API Example - -Suppose you want to represent a HTTP API response which is either a JSON result or an error: - -```c++ -struct Result { - Json object; -}; - -struct Error { - int32_t code; - string message; -}; -``` - -You can represent this at type level using a variant which is either an `Error` or a `Result`: - -```c++ -using Response = variant; - -Response makeRequest() { - return Error{501, "Not Implemented"}; -} - -Response ret = makeRequest(); -``` - -To see which type the `Response` holds you pattern match on the variant unwrapping the underlying value: - -```c++ -ret.match([] (Result r) { print(r.object); }, - [] (Error e) { print(e.message); }); -``` - -Instead of using the variant's convenience `.match` pattern matching function you can create a type visitor functor and use `apply_visitor` manually: - -```c++ -struct ResponseVisitor { - void operator()(Result r) const { - print(r.object); - } - - void operator()(Error e) const { - print(e.message); - } -}; - -ResponseVisitor visitor; -apply_visitor(visitor, ret); -``` - -In both cases the compiler makes sure you handle all types the variant can represent at compile. - -### Recursive Variants - JSON Example - -[JSON](http://www.json.org/) consists of types `String`, `Number`, `True`, `False`, `Null`, `Array` and `Object`. - -```c++ -struct String { string value; }; -struct Number { double value; }; -struct True { }; -struct False { }; -struct Null { }; -struct Array { vector values; }; -struct Object { unordered_map values; }; -``` - -This works for primitive types but how do we represent recursive types such as `Array` which can hold multiple elements and `Array` itself, too? - -For these use cases Variant provides a `recursive_wrapper` helper type which lets you express recursive Variants. - -```c++ -struct String { string value; }; -struct Number { double value; }; -struct True { }; -struct False { }; -struct Null { }; - -// Forward declarations only -struct Array; -struct Object; - -using Value = variant, recursive_wrapper>; - -struct Array { - vector values; -}; - -struct Object { - unordered_map values; -}; -``` - -For walking the JSON representation you can again either create a `JSONVisitor`: - -```c++ -struct JSONVisitor { - - void operator()(Null) const { - print("null"); - } - - // same for all other JSON types -}; - -JSONVisitor visitor; -apply_visitor(visitor, json); -``` - -Or use the convenience `.match` pattern matching function: - -```c++ -json.match([] (Null) { print("null"); }, - ...); -``` - -To summarize: use `recursive_wrapper` to represent recursive "tree-like" representations: - -```c++ -struct Empty { }; -struct Node; - -using Tree = variant>; - -struct Node { - uint64_t value; -} -``` - -### Advanced Usage Tips - -Creating type aliases for variants is a great way to reduce repetition. -Keep in mind those type aliases are not checked at type level, though. -We recommend creating a new type for all but basic variant usage: - -```c++ -// the compiler can't tell the following two apart -using APIResult = variant; -using FilesystemResult = variant; - -// new type -struct APIResult : variant { - using Base = variant; - using Base::Base; -} -``` - -## Why use Mapbox Variant? - -Mapbox variant has the same speedy performance of `boost::variant` but is -faster to compile, results in smaller binaries, and has no dependencies. - -For example on OS X 10.9 with clang++ and libc++: - -Test | Mapbox Variant | Boost Variant ----- | -------------- | ------------- -Size of pre-compiled header (release / debug) | 2.8/2.8 MB | 12/15 MB -Size of simple program linking variant (release / debug) | 8/24 K | 12/40 K -Time to compile header | 185 ms | 675 ms - -(Numbers from an older version of Mapbox variant.) - -## Goals - -Mapbox `variant` has been a very valuable, lightweight alternative for apps -that can use c++11 or c++14 but that do not want a boost dependency. -Mapbox `variant` has also been useful in apps that do depend on boost, like -mapnik, to help (slightly) with compile times and to majorly lessen dependence -on boost in core headers. The original goal and near term goal is to maintain -external API compatibility with `boost::variant` such that Mapbox `variant` -can be a "drop in". At the same time the goal is to stay minimal: Only -implement the features that are actually needed in existing software. So being -an "incomplete" implementation is just fine. - -Currently Mapbox variant doesn't try to be API compatible with the upcoming -variant standard, because the standard is not finished and it would be too much -work. But we'll revisit this decision in the future if needed. - -If Mapbox variant is not for you, have a look at [these other -implementations](doc/other_implementations.md). - -Want to know more about the upcoming standard? Have a look at our -[overview](doc/standards_effort.md). - -Most modern high-level languages provide ways to express sum types directly. -If you're curious have a look at Haskell's pattern matching or Rust's and Swift's enums. - -## Depends - -- Compiler supporting `-std=c++11` or `-std=c++14` - -Tested with: - -- g++-4.7 -- g++-4.8 -- g++-4.9 -- g++-5.2 -- clang++-3.5 -- clang++-3.6 -- clang++-3.7 -- clang++-3.8 -- clang++-3.9 -- Visual Studio 2015 - -## Unit Tests - -On Unix systems compile and run the unit tests with `make test`. - -On Windows run `scripts/build-local.bat`. - -## Limitations - -- The `variant` can not hold references (something like `variant` is - not possible). You might want to try `std::reference_wrapper` instead. - -## Deprecations - -- The included implementation of `optional` is deprecated and will be removed - in a future version. See [issue #64](https://github.com/mapbox/variant/issues/64). -- Old versions of the code needed visitors to derive from `static_visitor`. - This is not needed any more and marked as deprecated. The `static_visitor` - class will be removed in future versions. - -## Benchmarks - - make bench - -## Check object sizes - - make sizes /path/to/boost/variant.hpp diff --git a/third_party/variant/appveyor.yml b/third_party/variant/appveyor.yml deleted file mode 100644 index 050ad25dd..000000000 --- a/third_party/variant/appveyor.yml +++ /dev/null @@ -1,17 +0,0 @@ - -platform: - - x64 - - x86 - -configuration: - - Debug - - Release - -os: Visual Studio 2015 - -install: - - CALL scripts\build-appveyor.bat - -build: off -test: off -deploy: off diff --git a/third_party/variant/common.gypi b/third_party/variant/common.gypi deleted file mode 100644 index d4f09f880..000000000 --- a/third_party/variant/common.gypi +++ /dev/null @@ -1,143 +0,0 @@ -{ - "conditions": [ - ["OS=='win'", { - "target_defaults": { - "default_configuration": "Release_x64", - "msbuild_toolset":"v140", - "msvs_settings": { - "VCCLCompilerTool": { - "ExceptionHandling": 1, # /EHsc - "RuntimeTypeInfo": "true" # /GR - } - }, - "configurations": { - "Debug_Win32": { - "msvs_configuration_platform": "Win32", - "defines": [ "DEBUG","_DEBUG"], - "msvs_settings": { - "VCCLCompilerTool": { - "RuntimeLibrary": "1", # static debug /MTd - "Optimization": 0, # /Od, no optimization - "MinimalRebuild": "false", - "OmitFramePointers": "false", - "BasicRuntimeChecks": 3 # /RTC1 - } - } - }, - "Debug_x64": { - "msvs_configuration_platform": "x64", - "defines": [ "DEBUG","_DEBUG"], - "msvs_settings": { - "VCCLCompilerTool": { - "RuntimeLibrary": "1", # static debug /MTd - "Optimization": 0, # /Od, no optimization - "MinimalRebuild": "false", - "OmitFramePointers": "false", - "BasicRuntimeChecks": 3 # /RTC1 - } - } - }, - "Release_Win32": { - "msvs_configuration_platform": "Win32", - "defines": [ "NDEBUG"], - "msvs_settings": { - "VCCLCompilerTool": { - "RuntimeLibrary": 0, # static release - "Optimization": 3, # /Ox, full optimization - "FavorSizeOrSpeed": 1, # /Ot, favour speed over size - "InlineFunctionExpansion": 2, # /Ob2, inline anything eligible - "WholeProgramOptimization": "true", # /GL, whole program optimization, needed for LTCG - "OmitFramePointers": "true", - "EnableFunctionLevelLinking": "true", - "EnableIntrinsicFunctions": "true", - "AdditionalOptions": [ - "/MP", # compile across multiple CPUs - ], - "DebugInformationFormat": "0" - }, - "VCLibrarianTool": { - "AdditionalOptions": [ - "/LTCG" # link time code generation - ], - }, - "VCLinkerTool": { - "LinkTimeCodeGeneration": 1, # link-time code generation - "OptimizeReferences": 2, # /OPT:REF - "EnableCOMDATFolding": 2, # /OPT:ICF - "LinkIncremental": 1, # disable incremental linking - "GenerateDebugInformation": "false" - } - } - }, - "Release_x64": { - "msvs_configuration_platform": "x64", - "defines": [ "NDEBUG"], - "msvs_settings": { - "VCCLCompilerTool": { - "RuntimeLibrary": 0, # static release - "Optimization": 3, # /Ox, full optimization - "FavorSizeOrSpeed": 1, # /Ot, favour speed over size - "InlineFunctionExpansion": 2, # /Ob2, inline anything eligible - "WholeProgramOptimization": "true", # /GL, whole program optimization, needed for LTCG - "OmitFramePointers": "true", - "EnableFunctionLevelLinking": "true", - "EnableIntrinsicFunctions": "true", - "AdditionalOptions": [ - "/MP", # compile across multiple CPUs - ], - "DebugInformationFormat": "0" - }, - "VCLibrarianTool": { - "AdditionalOptions": [ - "/LTCG" # link time code generation - ], - }, - "VCLinkerTool": { - "LinkTimeCodeGeneration": 1, # link-time code generation - "OptimizeReferences": 2, # /OPT:REF - "EnableCOMDATFolding": 2, # /OPT:ICF - "LinkIncremental": 1, # disable incremental linking - "GenerateDebugInformation": "false" - } - } - } - } - } - }, { - "target_defaults": { - "default_configuration": "Release", - "xcode_settings": { - "CLANG_CXX_LIBRARY": "libc++", - "CLANG_CXX_LANGUAGE_STANDARD":"c++11", - "GCC_VERSION": "com.apple.compilers.llvm.clang.1_0", - }, - "cflags_cc": ["-std=c++11"], - "configurations": { - "Debug": { - "defines": [ - "DEBUG" - ], - "xcode_settings": { - "GCC_OPTIMIZATION_LEVEL": "0", - "GCC_GENERATE_DEBUGGING_SYMBOLS": "YES", - "OTHER_CPLUSPLUSFLAGS": [ "-Wall", "-Wextra", "-pedantic", "-g", "-O0" ] - } - }, - "Release": { - "defines": [ - "NDEBUG" - ], - "xcode_settings": { - "GCC_OPTIMIZATION_LEVEL": "3", - "GCC_GENERATE_DEBUGGING_SYMBOLS": "NO", - "DEAD_CODE_STRIPPING": "YES", - "GCC_INLINES_ARE_PRIVATE_EXTERN": "YES", - "OTHER_CPLUSPLUSFLAGS": [ "-Wall", "-Wextra", "-pedantic", "-O3" ] - } - } - } - } - }] - ] -} - diff --git a/third_party/variant/doc/other_implementations.md b/third_party/variant/doc/other_implementations.md deleted file mode 100644 index a0d8b9b3f..000000000 --- a/third_party/variant/doc/other_implementations.md +++ /dev/null @@ -1,15 +0,0 @@ - -# Other implementations of variant and optional - -These are some other implementations of `variant` and/or `optional` types. -They are not necessarily compatible with this implementation. This is an -incomplete list. - -* [Boost Variant](http://www.boost.org/doc/libs/1_59_0/doc/html/variant.html) and [Boost Optional](http://www.boost.org/doc/libs/1_59_0/libs/optional/doc/html/index.html) -* [Eggs Variant](http://eggs-cpp.github.io/variant/) by [Agustín Bergé](http://talesofcpp.fusionfenix.com/) -* [anthonyw/variant](https://bitbucket.org/anthonyw/variant) (implementation of P0110R0) -* [JasonL9000/cppcon14](https://github.com/JasonL9000/cppcon14) -* [tomilov/variant](https://github.com/tomilov/variant) -* [akrzemi1/Optional](https://github.com/akrzemi1/Optional) -* [mpark/variant](https://github.com/mpark/variant) - diff --git a/third_party/variant/doc/standards_effort.md b/third_party/variant/doc/standards_effort.md deleted file mode 100644 index d2df488f0..000000000 --- a/third_party/variant/doc/standards_effort.md +++ /dev/null @@ -1,28 +0,0 @@ - -# Standards efforts - -A `variant` type is on planned for inclusion in the C++ Standard, probably in -C++17. Current working papers are (list extracted from [2015 working group -papers](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/)): - -* 2015-09-28: Variant design review. [P0086R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0086r0.pdf) -* 2015-09-28: Variant: a type-safe union without undefined behavior (v2) [P0087R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0087r0.pdf) -* 2015-09-27: Variant: a type-safe union that is rarely invalid (v5) [P0088R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0088r0.pdf) -* 2015-09-24: Simply a Strong Variant [P0093R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0093r0.html) -* 2015-09-24: Simply a Basic Variant [P0094R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0094r0.html) -* 2015-09-24: The Case for a Language Based Variant [P0096R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0095r0.html) -* 2015-09-25: Implementing the strong guarantee for variant<> assignment [P0110R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0110r0.html) -* 2015-09-24: Homogeneous interface for variant, any and optional (Revision 1) [P0032R1](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0032r1.pdf) - -Last state can be seen from -[The Variant Saga: A happy ending?](https://isocpp.org/blog/2015/11/the-variant-saga-a-happy-ending). - -The `optional` type is also on the way into the standard. The papers are: -* 2013-10-03: A proposal to add a utility class to represent optional objects (Revision 5) [N3793](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3793.html) -* 2014-01-18: Working Draft, Technical Specification on C++ Extensions for Library Fundamentals [N3848](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3848.html) - -## Older Papers - -* Older working drafts are: N4218 (rev 1), N4516 (rev 2), N4450 (rev 3), and [N4542](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4542.pdf) (rev 4). They have been split into P0086 (general design discussions) and P0087 and P0088 (containing two competing? specs). -* 2015-07-28: Variant: Discriminated Union with Value Semantics [P0080R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0080r0.pdf) An alternative proposal to N4542. - diff --git a/third_party/variant/include/mapbox/optional.hpp b/third_party/variant/include/mapbox/optional.hpp deleted file mode 100644 index d84705c1a..000000000 --- a/third_party/variant/include/mapbox/optional.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef MAPBOX_UTIL_OPTIONAL_HPP -#define MAPBOX_UTIL_OPTIONAL_HPP - -#pragma message("This implementation of optional is deprecated. See https://github.com/mapbox/variant/issues/64.") - -#include -#include - -#include - -namespace mapbox { -namespace util { - -template -class optional -{ - static_assert(!std::is_reference::value, "optional doesn't support references"); - - struct none_type - { - }; - - variant variant_; - -public: - optional() = default; - - optional(optional const& rhs) - { - if (this != &rhs) - { // protect against invalid self-assignment - variant_ = rhs.variant_; - } - } - - optional(T const& v) { variant_ = v; } - - explicit operator bool() const noexcept { return variant_.template is(); } - - T const& get() const { return variant_.template get(); } - T& get() { return variant_.template get(); } - - T const& operator*() const { return this->get(); } - T operator*() { return this->get(); } - - optional& operator=(T const& v) - { - variant_ = v; - return *this; - } - - optional& operator=(optional const& rhs) - { - if (this != &rhs) - { - variant_ = rhs.variant_; - } - return *this; - } - - template - void emplace(Args&&... args) - { - variant_ = T{std::forward(args)...}; - } - - void reset() { variant_ = none_type{}; } - -}; // class optional - -} // namespace util -} // namespace mapbox - -#endif // MAPBOX_UTIL_OPTIONAL_HPP diff --git a/third_party/variant/include/mapbox/recursive_wrapper.hpp b/third_party/variant/include/mapbox/recursive_wrapper.hpp deleted file mode 100644 index 4ffcbd7c9..000000000 --- a/third_party/variant/include/mapbox/recursive_wrapper.hpp +++ /dev/null @@ -1,122 +0,0 @@ -#ifndef MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP -#define MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP - -// Based on variant/recursive_wrapper.hpp from boost. -// -// Original license: -// -// Copyright (c) 2002-2003 -// Eric Friedman, Itay Maman -// -// Distributed under the Boost Software License, Version 1.0. (See -// accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#include -#include - -namespace mapbox { -namespace util { - -template -class recursive_wrapper -{ - - T* p_; - - void assign(T const& rhs) - { - this->get() = rhs; - } - -public: - using type = T; - - /** - * Default constructor default initializes the internally stored value. - * For POD types this means nothing is done and the storage is - * uninitialized. - * - * @throws std::bad_alloc if there is insufficient memory for an object - * of type T. - * @throws any exception thrown by the default constructur of T. - */ - recursive_wrapper() - : p_(new T){} - - ~recursive_wrapper() noexcept { delete p_; } - - recursive_wrapper(recursive_wrapper const& operand) - : p_(new T(operand.get())) {} - - recursive_wrapper(T const& operand) - : p_(new T(operand)) {} - - recursive_wrapper(recursive_wrapper&& operand) - : p_(new T(std::move(operand.get()))) {} - - recursive_wrapper(T&& operand) - : p_(new T(std::move(operand))) {} - - inline recursive_wrapper& operator=(recursive_wrapper const& rhs) - { - assign(rhs.get()); - return *this; - } - - inline recursive_wrapper& operator=(T const& rhs) - { - assign(rhs); - return *this; - } - - inline void swap(recursive_wrapper& operand) noexcept - { - T* temp = operand.p_; - operand.p_ = p_; - p_ = temp; - } - - recursive_wrapper& operator=(recursive_wrapper&& rhs) noexcept - { - swap(rhs); - return *this; - } - - recursive_wrapper& operator=(T&& rhs) - { - get() = std::move(rhs); - return *this; - } - - T& get() - { - assert(p_); - return *get_pointer(); - } - - T const& get() const - { - assert(p_); - return *get_pointer(); - } - - T* get_pointer() { return p_; } - - const T* get_pointer() const { return p_; } - - operator T const&() const { return this->get(); } - - operator T&() { return this->get(); } - -}; // class recursive_wrapper - -template -inline void swap(recursive_wrapper& lhs, recursive_wrapper& rhs) noexcept -{ - lhs.swap(rhs); -} -} // namespace util -} // namespace mapbox - -#endif // MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP diff --git a/third_party/variant/include/mapbox/variant.hpp b/third_party/variant/include/mapbox/variant.hpp deleted file mode 100644 index 06a46abe5..000000000 --- a/third_party/variant/include/mapbox/variant.hpp +++ /dev/null @@ -1,1053 +0,0 @@ -#ifndef MAPBOX_UTIL_VARIANT_HPP -#define MAPBOX_UTIL_VARIANT_HPP - -#include -#include // size_t -#include // operator new -#include // runtime_error -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -// clang-format off -// [[deprecated]] is only available in C++14, use this for the time being -#if __cplusplus <= 201103L -# ifdef __GNUC__ -# define MAPBOX_VARIANT_DEPRECATED __attribute__((deprecated)) -# elif defined(_MSC_VER) -# define MAPBOX_VARIANT_DEPRECATED __declspec(deprecated) -# else -# define MAPBOX_VARIANT_DEPRECATED -# endif -#else -# define MAPBOX_VARIANT_DEPRECATED [[deprecated]] -#endif - - -#ifdef _MSC_VER -// https://msdn.microsoft.com/en-us/library/bw1hbe6y.aspx -# ifdef NDEBUG -# define VARIANT_INLINE __forceinline -# else -# define VARIANT_INLINE //__declspec(noinline) -# endif -#else -# ifdef NDEBUG -# define VARIANT_INLINE //inline __attribute__((always_inline)) -# else -# define VARIANT_INLINE __attribute__((noinline)) -# endif -#endif -// clang-format on - -// Exceptions -#if defined( __EXCEPTIONS) || defined( _MSC_VER) -#define HAS_EXCEPTIONS -#endif - -#define VARIANT_MAJOR_VERSION 1 -#define VARIANT_MINOR_VERSION 1 -#define VARIANT_PATCH_VERSION 0 - -#define VARIANT_VERSION (VARIANT_MAJOR_VERSION * 100000) + (VARIANT_MINOR_VERSION * 100) + (VARIANT_PATCH_VERSION) - -namespace mapbox { -namespace util { - -// XXX This should derive from std::logic_error instead of std::runtime_error. -// See https://github.com/mapbox/variant/issues/48 for details. -class bad_variant_access : public std::runtime_error -{ - -public: - explicit bad_variant_access(const std::string& what_arg) - : runtime_error(what_arg) {} - - explicit bad_variant_access(const char* what_arg) - : runtime_error(what_arg) {} - -}; // class bad_variant_access - -#if !defined(MAPBOX_VARIANT_MINIMIZE_SIZE) -using type_index_t = unsigned int; -#else -#if defined(MAPBOX_VARIANT_OPTIMIZE_FOR_SPEED) -using type_index_t = std::uint_fast8_t; -#else -using type_index_t = std::uint_least8_t; -#endif -#endif - -namespace detail { - -static constexpr type_index_t invalid_value = type_index_t(-1); - -template -struct direct_type; - -template -struct direct_type -{ - static constexpr type_index_t index = std::is_same::value - ? sizeof...(Types) - : direct_type::index; -}; - -template -struct direct_type -{ - static constexpr type_index_t index = invalid_value; -}; - -#if __cpp_lib_logical_traits >= 201510L - -using std::conjunction; -using std::disjunction; - -#else - -template -struct conjunction : std::true_type {}; - -template -struct conjunction : B1 {}; - -template -struct conjunction : std::conditional::type {}; - -template -struct conjunction : std::conditional, B1>::type {}; - -template -struct disjunction : std::false_type {}; - -template -struct disjunction : B1 {}; - -template -struct disjunction : std::conditional::type {}; - -template -struct disjunction : std::conditional>::type {}; - -#endif - -template -struct convertible_type; - -template -struct convertible_type -{ - static constexpr type_index_t index = std::is_convertible::value - ? disjunction...>::value ? invalid_value : sizeof...(Types) - : convertible_type::index; -}; - -template -struct convertible_type -{ - static constexpr type_index_t index = invalid_value; -}; - -template -struct value_traits -{ - using value_type = typename std::remove_const::type>::type; - using value_type_wrapper = recursive_wrapper; - static constexpr type_index_t direct_index = direct_type::index; - static constexpr bool is_direct = direct_index != invalid_value; - static constexpr type_index_t index_direct_or_wrapper = is_direct ? direct_index : direct_type::index; - static constexpr bool is_direct_or_wrapper = index_direct_or_wrapper != invalid_value; - static constexpr type_index_t index = is_direct_or_wrapper ? index_direct_or_wrapper : convertible_type::index; - static constexpr bool is_valid = index != invalid_value; - static constexpr type_index_t tindex = is_valid ? sizeof...(Types)-index : 0; - using target_type = typename std::tuple_element>::type; -}; - -template -struct copy_cvref -{ - using type = Dest; -}; - -template -struct copy_cvref -{ - using type = Dest const&; -}; - -template -struct copy_cvref -{ - using type = Dest&; -}; - -template -struct copy_cvref -{ - using type = Dest&&; -}; - -template -struct deduced_result_type -{}; - -template -struct deduced_result_type()(std::declval()...))> -{ - using type = decltype(std::declval()(std::declval()...)); -}; - -template -struct visitor_result_type : deduced_result_type -{}; - -// specialization for explicit result_type member in visitor class -template -struct visitor_result_type::type::result_type>())> -{ - using type = typename std::decay::type::result_type; -}; - -template -using result_of_unary_visit = typename visitor_result_type::type; - -template -using result_of_binary_visit = typename visitor_result_type::type; - -template -struct static_max; - -template -struct static_max -{ - static const type_index_t value = arg; -}; - -template -struct static_max -{ - static const type_index_t value = arg1 >= arg2 ? static_max::value : static_max::value; -}; - -template -struct variant_helper; - -template -struct variant_helper -{ - VARIANT_INLINE static void destroy(const type_index_t type_index, void* data) - { - if (type_index == sizeof...(Types)) - { - reinterpret_cast(data)->~T(); - } - else - { - variant_helper::destroy(type_index, data); - } - } - - VARIANT_INLINE static void move(const type_index_t old_type_index, void* old_value, void* new_value) - { - if (old_type_index == sizeof...(Types)) - { - new (new_value) T(std::move(*reinterpret_cast(old_value))); - } - else - { - variant_helper::move(old_type_index, old_value, new_value); - } - } - - VARIANT_INLINE static void copy(const type_index_t old_type_index, const void* old_value, void* new_value) - { - if (old_type_index == sizeof...(Types)) - { - new (new_value) T(*reinterpret_cast(old_value)); - } - else - { - variant_helper::copy(old_type_index, old_value, new_value); - } - } -}; - -template <> -struct variant_helper<> -{ - VARIANT_INLINE static void destroy(const type_index_t, void*) {} - VARIANT_INLINE static void move(const type_index_t, void*, void*) {} - VARIANT_INLINE static void copy(const type_index_t, const void*, void*) {} -}; - -template -struct unwrapper -{ - using value_type = T; - - template - static auto apply(typename std::remove_reference::type& var) - -> typename std::enable_if::value, - decltype(var.template get_unchecked())>::type - { - return var.template get_unchecked(); - } - - template - static auto apply(typename std::remove_reference::type& var) - -> typename std::enable_if::value, - decltype(std::move(var.template get_unchecked()))>::type - { - return std::move(var.template get_unchecked()); - } -}; - -template -struct unwrapper> : unwrapper -{}; - -template -struct unwrapper> : unwrapper -{}; - -template -struct dispatcher; - -template -struct dispatcher -{ - template - VARIANT_INLINE static R apply(V&& v, F&& f) - { - if (v.template is()) - { - return std::forward(f)(unwrapper::template apply(v)); - } - else - { - return dispatcher::apply(std::forward(v), std::forward(f)); - } - } -}; - -template -struct dispatcher -{ - template - VARIANT_INLINE static R apply(V&& v, F&& f) - { - return std::forward(f)(unwrapper::template apply(v)); - } -}; - -template -struct binary_dispatcher_rhs; - -template -struct binary_dispatcher_rhs -{ - template - VARIANT_INLINE static R apply(V&& lhs, V&& rhs, F&& f) - { - if (rhs.template is()) // call binary functor - { - return std::forward(f)(unwrapper::template apply(lhs), - unwrapper::template apply(rhs)); - } - else - { - return binary_dispatcher_rhs::apply(std::forward(lhs), - std::forward(rhs), - std::forward(f)); - } - } -}; - -template -struct binary_dispatcher_rhs -{ - template - VARIANT_INLINE static R apply(V&& lhs, V&& rhs, F&& f) - { - return std::forward(f)(unwrapper::template apply(lhs), - unwrapper::template apply(rhs)); - } -}; - -template -struct binary_dispatcher_lhs; - -template -struct binary_dispatcher_lhs -{ - template - VARIANT_INLINE static R apply(V&& lhs, V&& rhs, F&& f) - { - if (lhs.template is()) // call binary functor - { - return std::forward(f)(unwrapper::template apply(lhs), - unwrapper::template apply(rhs)); - } - else - { - return binary_dispatcher_lhs::apply(std::forward(lhs), - std::forward(rhs), - std::forward(f)); - } - } -}; - -template -struct binary_dispatcher_lhs -{ - template - VARIANT_INLINE static R apply(V&& lhs, V&& rhs, F&& f) - { - return std::forward(f)(unwrapper::template apply(lhs), - unwrapper::template apply(rhs)); - } -}; - -template -struct binary_dispatcher; - -template -struct binary_dispatcher -{ - template - VARIANT_INLINE static R apply(V&& v0, V&& v1, F&& f) - { - if (v0.template is()) - { - if (v1.template is()) - { - return std::forward(f)(unwrapper::template apply(v0), - unwrapper::template apply(v1)); // call binary functor - } - else - { - return binary_dispatcher_rhs::apply(std::forward(v0), - std::forward(v1), - std::forward(f)); - } - } - else if (v1.template is()) - { - return binary_dispatcher_lhs::apply(std::forward(v0), - std::forward(v1), - std::forward(f)); - } - return binary_dispatcher::apply(std::forward(v0), - std::forward(v1), - std::forward(f)); - } -}; - -template -struct binary_dispatcher -{ - template - VARIANT_INLINE static R apply(V&& v0, V&& v1, F&& f) - { - return std::forward(f)(unwrapper::template apply(v0), - unwrapper::template apply(v1)); // call binary functor - } -}; - -// comparator functors -struct equal_comp -{ - template - bool operator()(T const& lhs, T const& rhs) const - { - return lhs == rhs; - } -}; - -struct less_comp -{ - template - bool operator()(T const& lhs, T const& rhs) const - { - return lhs < rhs; - } -}; - -template -class comparer -{ -public: - explicit comparer(Variant const& lhs) noexcept - : lhs_(lhs) {} - comparer& operator=(comparer const&) = delete; - // visitor - template - bool operator()(T const& rhs_content) const - { - T const& lhs_content = lhs_.template get_unchecked(); - return Comp()(lhs_content, rhs_content); - } - -private: - Variant const& lhs_; -}; - -// hashing visitor -struct hasher -{ - template - std::size_t operator()(const T& hashable) const - { - return std::hash{}(hashable); - } -}; - -} // namespace detail - -struct no_init {}; - -template -class variant -{ - static_assert(sizeof...(Types) > 0, "Template parameter type list of variant can not be empty."); - static_assert(!detail::disjunction...>::value, "Variant can not hold reference types. Maybe use std::reference_wrapper?"); - static_assert(!detail::disjunction...>::value, "Variant can not hold array types."); - static_assert(sizeof...(Types) < std::numeric_limits::max(), "Internal index type must be able to accommodate all alternatives."); -private: - static const std::size_t data_size = detail::static_max::value; - static const std::size_t data_align = detail::static_max::value; -public: - struct adapted_variant_tag; - using types = std::tuple; -private: - using first_type = typename std::tuple_element<0, types>::type; - using unwrap_first_type = typename detail::unwrapper::value_type; - using data_type = typename std::aligned_storage::type; - using helper_type = detail::variant_helper; - - template - using alternative_ref = typename detail::copy_cvref::type; - - type_index_t type_index; -#ifdef __clang_analyzer__ - data_type data {}; -#else - data_type data; -#endif - -public: - VARIANT_INLINE variant() noexcept(std::is_nothrow_default_constructible::value) - : type_index(sizeof...(Types)-1) - { - static_assert(std::is_default_constructible::value, "First type in variant must be default constructible to allow default construction of variant."); - new (&data) first_type(); - } - - VARIANT_INLINE variant(no_init) noexcept - : type_index(detail::invalid_value) {} - - // http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers - template , - typename Enable = typename std::enable_if, typename Traits::value_type>::value>::type > - VARIANT_INLINE variant(T&& val) noexcept(std::is_nothrow_constructible::value) - : type_index(Traits::index) - { - new (&data) typename Traits::target_type(std::forward(val)); - } - - VARIANT_INLINE variant(variant const& old) - : type_index(old.type_index) - { - helper_type::copy(old.type_index, &old.data, &data); - } - - VARIANT_INLINE variant(variant&& old) - noexcept(detail::conjunction...>::value) - : type_index(old.type_index) - { - helper_type::move(old.type_index, &old.data, &data); - } - -private: - VARIANT_INLINE void copy_assign(variant const& rhs) - { - helper_type::destroy(type_index, &data); - type_index = detail::invalid_value; - helper_type::copy(rhs.type_index, &rhs.data, &data); - type_index = rhs.type_index; - } - - VARIANT_INLINE void move_assign(variant&& rhs) - { - helper_type::destroy(type_index, &data); - type_index = detail::invalid_value; - helper_type::move(rhs.type_index, &rhs.data, &data); - type_index = rhs.type_index; - } - -public: - VARIANT_INLINE variant& operator=(variant&& other) - // note we check for nothrow-constructible, not nothrow-assignable, since - // move_assign uses move-construction via placement new. - noexcept(detail::conjunction...>::value) - { - if (this == &other) { // playing safe in release mode, hit assertion in debug. - assert(false); - return *this; - } - move_assign(std::move(other)); - return *this; - } - - VARIANT_INLINE variant& operator=(variant const& other) - { - if (this != &other) - copy_assign(other); - return *this; - } - - // conversions - // move-assign - template , - typename Enable = typename std::enable_if, typename Traits::value_type>::value>::type > - VARIANT_INLINE variant& operator=(T&& rhs) - // not that we check is_nothrow_constructible, not is_nothrow_move_assignable, - // since we construct a temporary - noexcept(std::is_nothrow_constructible::value - && std::is_nothrow_move_assignable>::value) - { - variant temp(std::forward(rhs)); - move_assign(std::move(temp)); - return *this; - } - - // copy-assign - template - VARIANT_INLINE variant& operator=(T const& rhs) - { - variant temp(rhs); - copy_assign(temp); - return *this; - } - - template ::index != detail::invalid_value)>::type* = nullptr> - VARIANT_INLINE bool is() const - { - return type_index == detail::direct_type::index; - } - - template , Types...>::index != detail::invalid_value)>::type* = nullptr> - VARIANT_INLINE bool is() const - { - return type_index == detail::direct_type, Types...>::index; - } - - VARIANT_INLINE bool valid() const - { - return type_index != detail::invalid_value; - } - - template - VARIANT_INLINE void set(Args&&... args) - { - helper_type::destroy(type_index, &data); - type_index = detail::invalid_value; - new (&data) T(std::forward(args)...); - type_index = detail::direct_type::index; - } - - // get_unchecked() - template ::index != detail::invalid_value)>::type* = nullptr> - VARIANT_INLINE T& get_unchecked() - { - return *reinterpret_cast(&data); - } - -#ifdef HAS_EXCEPTIONS - // get() - template ::index != detail::invalid_value)>::type* = nullptr> - VARIANT_INLINE T& get() - { - if (type_index == detail::direct_type::index) - { - return *reinterpret_cast(&data); - } - else - { - throw bad_variant_access("in get()"); - } - } -#endif - - template ::index != detail::invalid_value)>::type* = nullptr> - VARIANT_INLINE T const& get_unchecked() const - { - return *reinterpret_cast(&data); - } - -#ifdef HAS_EXCEPTIONS - template ::index != detail::invalid_value)>::type* = nullptr> - VARIANT_INLINE T const& get() const - { - if (type_index == detail::direct_type::index) - { - return *reinterpret_cast(&data); - } - else - { - throw bad_variant_access("in get()"); - } - } -#endif - - // get_unchecked() - T stored as recursive_wrapper - template , Types...>::index != detail::invalid_value)>::type* = nullptr> - VARIANT_INLINE T& get_unchecked() - { - return (*reinterpret_cast*>(&data)).get(); - } - -#ifdef HAS_EXCEPTIONS - // get() - T stored as recursive_wrapper - template , Types...>::index != detail::invalid_value)>::type* = nullptr> - VARIANT_INLINE T& get() - { - if (type_index == detail::direct_type, Types...>::index) - { - return (*reinterpret_cast*>(&data)).get(); - } - else - { - throw bad_variant_access("in get()"); - } - } -#endif - - template , Types...>::index != detail::invalid_value)>::type* = nullptr> - VARIANT_INLINE T const& get_unchecked() const - { - return (*reinterpret_cast const*>(&data)).get(); - } - -#ifdef HAS_EXCEPTIONS - template , Types...>::index != detail::invalid_value)>::type* = nullptr> - VARIANT_INLINE T const& get() const - { - if (type_index == detail::direct_type, Types...>::index) - { - return (*reinterpret_cast const*>(&data)).get(); - } - else - { - throw bad_variant_access("in get()"); - } - } -#endif - - // get_unchecked() - T stored as std::reference_wrapper - template , Types...>::index != detail::invalid_value)>::type* = nullptr> - VARIANT_INLINE T& get_unchecked() - { - return (*reinterpret_cast*>(&data)).get(); - } - -#ifdef HAS_EXCEPTIONS - // get() - T stored as std::reference_wrapper - template , Types...>::index != detail::invalid_value)>::type* = nullptr> - VARIANT_INLINE T& get() - { - if (type_index == detail::direct_type, Types...>::index) - { - return (*reinterpret_cast*>(&data)).get(); - } - else - { - throw bad_variant_access("in get()"); - } - } -#endif - - template , Types...>::index != detail::invalid_value)>::type* = nullptr> - VARIANT_INLINE T const& get_unchecked() const - { - return (*reinterpret_cast const*>(&data)).get(); - } - -#ifdef HAS_EXCEPTIONS - template , Types...>::index != detail::invalid_value)>::type* = nullptr> - VARIANT_INLINE T const& get() const - { - if (type_index == detail::direct_type, Types...>::index) - { - return (*reinterpret_cast const*>(&data)).get(); - } - else - { - throw bad_variant_access("in get()"); - } - } -#endif - - // This function is deprecated because it returns an internal index field. - // Use which() instead. - MAPBOX_VARIANT_DEPRECATED VARIANT_INLINE type_index_t get_type_index() const - { - return type_index; - } - - VARIANT_INLINE int which() const noexcept - { - return static_cast(sizeof...(Types) - type_index - 1); - } - - template ::index != detail::invalid_value)>::type* = nullptr> - VARIANT_INLINE static constexpr int which() noexcept - { - return static_cast(sizeof...(Types)-detail::direct_type::index - 1); - } - - // visitor - // unary - template , - typename R = detail::result_of_unary_visit> - VARIANT_INLINE static R visit(V&& v, F&& f) - { - return detail::dispatcher::apply(std::forward(v), std::forward(f)); - } - - // binary - template , - typename R = detail::result_of_binary_visit> - VARIANT_INLINE static R binary_visit(V&& v0, V&& v1, F&& f) - { - return detail::binary_dispatcher::apply(std::forward(v0), - std::forward(v1), - std::forward(f)); - } - - // match - // unary - template - auto VARIANT_INLINE match(Fs&&... fs) const& - -> decltype(variant::visit(*this, ::mapbox::util::make_visitor(std::forward(fs)...))) - { - return variant::visit(*this, ::mapbox::util::make_visitor(std::forward(fs)...)); - } - // non-const - template - auto VARIANT_INLINE match(Fs&&... fs) & - -> decltype(variant::visit(*this, ::mapbox::util::make_visitor(std::forward(fs)...))) - { - return variant::visit(*this, ::mapbox::util::make_visitor(std::forward(fs)...)); - } - template - auto VARIANT_INLINE match(Fs&&... fs) && - -> decltype(variant::visit(std::move(*this), ::mapbox::util::make_visitor(std::forward(fs)...))) - { - return variant::visit(std::move(*this), ::mapbox::util::make_visitor(std::forward(fs)...)); - } - - ~variant() noexcept // no-throw destructor - { - helper_type::destroy(type_index, &data); - } - - // comparison operators - // equality - VARIANT_INLINE bool operator==(variant const& rhs) const - { - assert(valid() && rhs.valid()); - if (this->which() != rhs.which()) - { - return false; - } - detail::comparer visitor(*this); - return visit(rhs, visitor); - } - - VARIANT_INLINE bool operator!=(variant const& rhs) const - { - return !(*this == rhs); - } - - // less than - VARIANT_INLINE bool operator<(variant const& rhs) const - { - assert(valid() && rhs.valid()); - if (this->which() != rhs.which()) - { - return this->which() < rhs.which(); - } - detail::comparer visitor(*this); - return visit(rhs, visitor); - } - VARIANT_INLINE bool operator>(variant const& rhs) const - { - return rhs < *this; - } - VARIANT_INLINE bool operator<=(variant const& rhs) const - { - return !(*this > rhs); - } - VARIANT_INLINE bool operator>=(variant const& rhs) const - { - return !(*this < rhs); - } -}; - -// unary visitor interface -template -auto VARIANT_INLINE apply_visitor(F&& f, V&& v) - -> decltype(v.visit(std::forward(v), std::forward(f))) -{ - return v.visit(std::forward(v), std::forward(f)); -} - -// binary visitor interface -template -auto VARIANT_INLINE apply_visitor(F&& f, V&& v0, V&& v1) - -> decltype(v0.binary_visit(std::forward(v0), std::forward(v1), std::forward(f))) -{ - return v0.binary_visit(std::forward(v0), std::forward(v1), std::forward(f)); -} - -// getter interface - -#ifdef HAS_EXCEPTIONS -template -auto get(T& var)->decltype(var.template get()) -{ - return var.template get(); -} -#endif - -template -ResultType& get_unchecked(T& var) -{ - return var.template get_unchecked(); -} - -#ifdef HAS_EXCEPTIONS -template -auto get(T const& var)->decltype(var.template get()) -{ - return var.template get(); -} -#endif - -template -ResultType const& get_unchecked(T const& var) -{ - return var.template get_unchecked(); -} -// variant_size -template -struct variant_size; - -//variable templates is c++14 -//template -//constexpr std::size_t variant_size_v = variant_size::value; - -template -struct variant_size - : variant_size {}; - -template -struct variant_size - : variant_size {}; - -template -struct variant_size - : variant_size {}; - -template -struct variant_size> - : std::integral_constant {}; - -// variant_alternative -template -struct variant_alternative; - -#if defined(__clang__) -#if __has_builtin(__type_pack_element) -#define has_type_pack_element -#endif -#endif - -#if defined(has_type_pack_element) -template -struct variant_alternative> -{ - static_assert(sizeof...(Types) > Index , "Index out of range"); - using type = __type_pack_element; -}; -#else -template -struct variant_alternative> - : variant_alternative> -{ - static_assert(sizeof...(Types) > Index -1 , "Index out of range"); -}; - -template -struct variant_alternative<0, variant> -{ - using type = First; -}; - -#endif - -template -using variant_alternative_t = typename variant_alternative::type; - -template -struct variant_alternative - : std::add_const> {}; - -template -struct variant_alternative - : std::add_volatile> {}; - -template -struct variant_alternative - : std::add_cv> {}; - -} // namespace util -} // namespace mapbox - -// hashable iff underlying types are hashable -namespace std { -template -struct hash< ::mapbox::util::variant> { - std::size_t operator()(const ::mapbox::util::variant& v) const noexcept - { - return ::mapbox::util::apply_visitor(::mapbox::util::detail::hasher{}, v); - } -}; - -} - -#endif // MAPBOX_UTIL_VARIANT_HPP diff --git a/third_party/variant/include/mapbox/variant_cast.hpp b/third_party/variant/include/mapbox/variant_cast.hpp deleted file mode 100644 index fe1ab3543..000000000 --- a/third_party/variant/include/mapbox/variant_cast.hpp +++ /dev/null @@ -1,85 +0,0 @@ -#ifndef VARIANT_CAST_HPP -#define VARIANT_CAST_HPP - -#include - -namespace mapbox { -namespace util { - -namespace detail { - -template -class static_caster -{ -public: - template - T& operator()(V& v) const - { - return static_cast(v); - } -}; - -template -class dynamic_caster -{ -public: - using result_type = T&; - template - T& operator()(V& v, typename std::enable_if::value>::type* = nullptr) const - { - throw std::bad_cast(); - } - template - T& operator()(V& v, typename std::enable_if::value>::type* = nullptr) const - { - return dynamic_cast(v); - } -}; - -template -class dynamic_caster -{ -public: - using result_type = T*; - template - T* operator()(V& v, typename std::enable_if::value>::type* = nullptr) const - { - return nullptr; - } - template - T* operator()(V& v, typename std::enable_if::value>::type* = nullptr) const - { - return dynamic_cast(&v); - } -}; -} - -template -typename detail::dynamic_caster::result_type -dynamic_variant_cast(V& v) -{ - return mapbox::util::apply_visitor(detail::dynamic_caster(), v); -} - -template -typename detail::dynamic_caster::result_type -dynamic_variant_cast(const V& v) -{ - return mapbox::util::apply_visitor(detail::dynamic_caster(), v); -} - -template -T& static_variant_cast(V& v) -{ - return mapbox::util::apply_visitor(detail::static_caster(), v); -} - -template -const T& static_variant_cast(const V& v) -{ - return mapbox::util::apply_visitor(detail::static_caster(), v); -} -} -} - -#endif // VARIANT_CAST_HPP diff --git a/third_party/variant/include/mapbox/variant_io.hpp b/third_party/variant/include/mapbox/variant_io.hpp deleted file mode 100644 index 1456cc5ab..000000000 --- a/third_party/variant/include/mapbox/variant_io.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef MAPBOX_UTIL_VARIANT_IO_HPP -#define MAPBOX_UTIL_VARIANT_IO_HPP - -#include - -#include - -namespace mapbox { -namespace util { - -namespace detail { -// operator<< helper -template -class printer -{ -public: - explicit printer(Out& out) - : out_(out) {} - printer& operator=(printer const&) = delete; - - // visitor - template - void operator()(T const& operand) const - { - out_ << operand; - } - -private: - Out& out_; -}; -} - -// operator<< -template -VARIANT_INLINE std::basic_ostream& -operator<<(std::basic_ostream& out, variant const& rhs) -{ - detail::printer> visitor(out); - apply_visitor(visitor, rhs); - return out; -} -} // namespace util -} // namespace mapbox - -#endif // MAPBOX_UTIL_VARIANT_IO_HPP diff --git a/third_party/variant/include/mapbox/variant_visitor.hpp b/third_party/variant/include/mapbox/variant_visitor.hpp deleted file mode 100644 index 54ddba0e1..000000000 --- a/third_party/variant/include/mapbox/variant_visitor.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef MAPBOX_UTIL_VARIANT_VISITOR_HPP -#define MAPBOX_UTIL_VARIANT_VISITOR_HPP - -#include - -namespace mapbox { -namespace util { - -template -struct visitor; - -template -struct visitor : Fn -{ - using Fn::operator(); - - template - visitor(T&& fn) : Fn(std::forward(fn)) {} -}; - -template -struct visitor : Fn, visitor -{ - using Fn::operator(); - using visitor::operator(); - - template - visitor(T&& fn, Ts&&... fns) - : Fn(std::forward(fn)) - , visitor(std::forward(fns)...) {} -}; - -template -visitor::type...> make_visitor(Fns&&... fns) -{ - return visitor::type...> - (std::forward(fns)...); -} - -} // namespace util -} // namespace mapbox - -#endif // MAPBOX_UTIL_VARIANT_VISITOR_HPP diff --git a/third_party/variant/package.json b/third_party/variant/package.json deleted file mode 100644 index abca950f8..000000000 --- a/third_party/variant/package.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "variant", - "version": "1.1.6", - "description": "C++11/C++14 variant", - "main": "./package.json", - "repository" : { - "type" : "git", - "url" : "git://github.com/mapbox/variant.git" - } -} diff --git a/third_party/variant/scripts/build-appveyor.bat b/third_party/variant/scripts/build-appveyor.bat deleted file mode 100644 index bdee7ea8d..000000000 --- a/third_party/variant/scripts/build-appveyor.bat +++ /dev/null @@ -1,32 +0,0 @@ -@ECHO OFF -SETLOCAL - -SET PATH=c:\python27;%PATH% - -ECHO activating VS command prompt -IF /I "%PLATFORM"=="x64" ( - CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 -) ELSE ( - CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86 -) - -IF NOT EXIST deps\gyp git clone --quiet --depth 1 https://chromium.googlesource.com/external/gyp.git deps/gyp - -CALL deps\gyp\gyp.bat variant.gyp --depth=. ^ --f msvs ^ --G msvs_version=2015 ^ ---generator-output=build - -SET MSBUILD_PLATFORM=%platform% -IF /I "%MSBUILD_PLATFORM%" == "x86" SET MSBUILD_PLATFORM=Win32 - - -msbuild ^ -build\variant.sln ^ -/nologo ^ -/toolsversion:14.0 ^ -/p:PlatformToolset=v140 ^ -/p:Configuration=%configuration% ^ -/p:Platform=%MSBUILD_PLATFORM% - -build\"%configuration%"\tests.exe diff --git a/third_party/variant/scripts/build-local.bat b/third_party/variant/scripts/build-local.bat deleted file mode 100644 index 34a2e6112..000000000 --- a/third_party/variant/scripts/build-local.bat +++ /dev/null @@ -1,7 +0,0 @@ -@ECHO OFF -SETLOCAL - -SET platform=x64 -SET configuration=Release - -CALL scripts\build-appveyor.bat \ No newline at end of file diff --git a/third_party/variant/scripts/run_compilation_failure_tests.sh b/third_party/variant/scripts/run_compilation_failure_tests.sh deleted file mode 100755 index 5a1406187..000000000 --- a/third_party/variant/scripts/run_compilation_failure_tests.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/sh -# -# Try to compile all programs in the test/compilation_failure directory. -# Compilation must fail and the error message must match the pattern in the -# corresponding .pattern file. -# - -DIR="test/compilation_failure" -CXX=${CXX:-clang++} - -if [ `uname -s` = "Darwin" ]; then - CXXFLAGS="$CXXFLAGS -stdlib=libc++" -fi - -error_msg() { - if [ ! -z "$1" ]; then - printf 'output was:\n=======\n%s\n=======\n' "$1" - fi -} - -exit_code=0 -for test_code in $DIR/*.cpp; do - name=`basename $test_code .cpp` - - result=`${CXX} -std=c++11 -c -o /dev/null -I./include ${CXXFLAGS} ${test_code} 2>&1` - status=$? - - if [ $status = 1 ]; then - expected=`sed -n -e '/@EXPECTED/s/.*: \+//p' ${test_code}` - if echo $result | grep -q "$expected"; then - echo "$name [OK]" - else - echo "$name [FAILED - wrong error message]" - echo "Expected error message: $expected" - error_msg "$result" - exit_code=1 - fi - elif [ $status = 0 ]; then - echo "$name [FAILED - compile was successful]" - error_msg "$result" - exit_code=1 - else - echo "$name [FAILED - unknown error in compile]" - error_msg "$result" - exit_code=1 - fi -done - -exit ${exit_code} diff --git a/third_party/variant/test/bench_variant.cpp b/third_party/variant/test/bench_variant.cpp deleted file mode 100644 index 567bfe499..000000000 --- a/third_party/variant/test/bench_variant.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "auto_cpu_timer.hpp" - -#include -#include - -#define TEXT_SHORT "Test" -#define TEXT_LONG "Testing various variant implementations with a longish string ........................................." -#define NUM_SAMPLES 3 -//#define BOOST_VARIANT_MINIMIZE_SIZE - -using namespace mapbox; - -namespace test { - -template -struct Holder -{ - typedef V value_type; - std::vector data; - - template - void append_move(T&& obj) - { - data.emplace_back(std::forward(obj)); - } - - template - void append(T const& obj) - { - data.push_back(obj); - } -}; - -} // namespace test - -struct print -{ - template - void operator()(T const& val) const - { - std::cerr << val << ":" << typeid(T).name() << std::endl; - } -}; - -template -struct dummy : boost::static_visitor<> -{ - dummy(V& v) - : v_(v) {} - - template - void operator()(T&& val) const - { - v_ = std::move(val); - } - V& v_; -}; - -template -struct dummy2 -{ - dummy2(V& v) - : v_(v) {} - - template - void operator()(T&& val) const - { - v_ = std::move(val); - } - V& v_; -}; - -void run_boost_test(std::size_t runs) -{ - test::Holder> h; - h.data.reserve(runs); - for (std::size_t i = 0; i < runs; ++i) - { - h.append_move(std::string(TEXT_SHORT)); - h.append_move(std::string(TEXT_LONG)); - h.append_move(123); - h.append_move(3.14159); - } - - boost::variant v; - for (auto const& v2 : h.data) - { - dummy> d(v); - boost::apply_visitor(d, v2); - } -} - -void run_variant_test(std::size_t runs) -{ - test::Holder> h; - h.data.reserve(runs); - for (std::size_t i = 0; i < runs; ++i) - { - h.append_move(std::string(TEXT_SHORT)); - h.append_move(std::string(TEXT_LONG)); - h.append_move(123); - h.append_move(3.14159); - } - - util::variant v; - for (auto const& v2 : h.data) - { - dummy2> d(v); - util::apply_visitor(d, v2); - } -} - -int main(int argc, char** argv) -{ - if (argc != 2) - { - std::cerr << "Usage:" << argv[0] << " " << std::endl; - return 1; - } - -#ifndef SINGLE_THREADED - const std::size_t THREADS = 4; -#endif - const std::size_t NUM_RUNS = static_cast(std::stol(argv[1])); - -#ifdef SINGLE_THREADED - - for (std::size_t j = 0; j < NUM_SAMPLES; ++j) - { - - { - std::cerr << "custom variant: "; - auto_cpu_timer t; - run_variant_test(NUM_RUNS); - } - { - std::cerr << "boost variant: "; - auto_cpu_timer t; - run_boost_test(NUM_RUNS); - } - } - -#else - for (std::size_t j = 0; j < NUM_SAMPLES; ++j) - { - { - typedef std::vector> thread_group; - typedef thread_group::value_type value_type; - thread_group tg; - std::cerr << "custom variant: "; - auto_cpu_timer timer; - for (std::size_t i = 0; i < THREADS; ++i) - { - tg.emplace_back(new std::thread(run_variant_test, NUM_RUNS)); - } - std::for_each(tg.begin(), tg.end(), [](value_type& t) {if (t->joinable()) t->join(); }); - } - - { - typedef std::vector> thread_group; - typedef thread_group::value_type value_type; - thread_group tg; - std::cerr << "boost variant: "; - auto_cpu_timer timer; - for (std::size_t i = 0; i < THREADS; ++i) - { - tg.emplace_back(new std::thread(run_boost_test, NUM_RUNS)); - } - std::for_each(tg.begin(), tg.end(), [](value_type& t) {if (t->joinable()) t->join(); }); - } - } -#endif - - return EXIT_SUCCESS; -} diff --git a/third_party/variant/test/binary_visitor_test.cpp b/third_party/variant/test/binary_visitor_test.cpp deleted file mode 100644 index 8a7359267..000000000 --- a/third_party/variant/test/binary_visitor_test.cpp +++ /dev/null @@ -1,138 +0,0 @@ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -using namespace mapbox; - -namespace test { - -template -struct string_to_number -{ -}; - -template <> -struct string_to_number -{ - double operator()(std::string const& str) const - { - return std::stod(str); - } -}; - -template <> -struct string_to_number -{ - std::int64_t operator()(std::string const& str) const - { - return std::stoll(str); - } -}; - -template <> -struct string_to_number -{ - std::uint64_t operator()(std::string const& str) const - { - return std::stoull(str); - } -}; - -template <> -struct string_to_number -{ - bool operator()(std::string const& str) const - { - bool result; - std::istringstream(str) >> std::boolalpha >> result; - return result; - } -}; - -struct javascript_equal_visitor -{ - template - bool operator()(T lhs, T rhs) const - { - return lhs == rhs; - } - - template ::value>::type> - bool operator()(T lhs, std::string const& rhs) const - { - return lhs == string_to_number()(rhs); - } - - template ::value>::type> - bool operator()(std::string const& lhs, T rhs) const - { - return string_to_number()(lhs) == rhs; - } - - template - bool operator()(T0 lhs, T1 rhs) const - { - return lhs == static_cast(rhs); - } -}; - -template -struct javascript_equal -{ - javascript_equal(T const& lhs) - : lhs_(lhs) {} - - bool operator()(T const& rhs) const - { - return util::apply_visitor(test::javascript_equal_visitor(), lhs_, rhs); - } - T const& lhs_; -}; - -} // namespace test - -int main() -{ - typedef util::variant variant_type; - variant_type v0(3.14159); - variant_type v1(std::string("3.14159")); - variant_type v2(std::uint64_t(1)); - - std::cerr << v0 << " == " << v1 << " -> " - << std::boolalpha << util::apply_visitor(test::javascript_equal_visitor(), v0, v1) << std::endl; - - std::vector vec; - - vec.emplace_back(std::string("1")); - vec.push_back(variant_type(std::uint64_t(2))); - vec.push_back(variant_type(std::uint64_t(3))); - vec.push_back(std::string("3.14159")); - vec.emplace_back(3.14159); - - //auto itr = std::find_if(vec.begin(), vec.end(), [&v0](variant_type const& val) { - // return util::apply_visitor(test::javascript_equal_visitor(), v0, val); - // }); - - auto itr = std::find_if(vec.begin(), vec.end(), test::javascript_equal(v2)); - - if (itr != std::end(vec)) - { - std::cout << "found " << *itr << std::endl; - } - else - { - std::cout << "can't find " << v2 << '\n'; - } - - return EXIT_SUCCESS; -} diff --git a/third_party/variant/test/boost_variant_hello_world.cpp b/third_party/variant/test/boost_variant_hello_world.cpp deleted file mode 100644 index fdb200e73..000000000 --- a/third_party/variant/test/boost_variant_hello_world.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include - -#include - -struct check : boost::static_visitor<> -{ - template - void operator()(T const& val) const - { - if (val != 0) throw std::runtime_error("invalid"); - } -}; - -int main() -{ - typedef boost::variant variant_type; - variant_type v(0); - boost::apply_visitor(check(), v); - return 0; -} diff --git a/third_party/variant/test/compilation_failure/default_constructor.cpp b/third_party/variant/test/compilation_failure/default_constructor.cpp deleted file mode 100644 index b4e83d42b..000000000 --- a/third_party/variant/test/compilation_failure/default_constructor.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// @EXPECTED: First type in variant must be default constructible to allow default construction of variant - -#include - -// Checks that the first type in a variant must be default constructible to -// make the variant default constructible. - -struct no_def_constructor -{ - - int value; - - no_def_constructor() = delete; - - no_def_constructor(int v) : value(v) {} -}; - -int main() -{ - mapbox::util::variant x; -} diff --git a/third_party/variant/test/compilation_failure/empty_typelist.cpp b/third_party/variant/test/compilation_failure/empty_typelist.cpp deleted file mode 100644 index 9b8a12571..000000000 --- a/third_party/variant/test/compilation_failure/empty_typelist.cpp +++ /dev/null @@ -1,10 +0,0 @@ -// @EXPECTED: Template parameter type list of variant can not be empty - -#include - -// Empty type list should not work. - -int main() -{ - mapbox::util::variant<> x; -} diff --git a/third_party/variant/test/compilation_failure/equality.cpp b/third_party/variant/test/compilation_failure/equality.cpp deleted file mode 100644 index 36a199006..000000000 --- a/third_party/variant/test/compilation_failure/equality.cpp +++ /dev/null @@ -1,10 +0,0 @@ -// @EXPECTED: - -#include - -int main() -{ - mapbox::util::variant x; - mapbox::util::variant y; - x == y; -} diff --git a/third_party/variant/test/compilation_failure/get_type.cpp b/third_party/variant/test/compilation_failure/get_type.cpp deleted file mode 100644 index 744602fd5..000000000 --- a/third_party/variant/test/compilation_failure/get_type.cpp +++ /dev/null @@ -1,9 +0,0 @@ -// @EXPECTED: no matching .*\ - -#include - -int main() -{ - mapbox::util::variant x; - x.get(); -} diff --git a/third_party/variant/test/compilation_failure/is_type.cpp b/third_party/variant/test/compilation_failure/is_type.cpp deleted file mode 100644 index 95ccff47e..000000000 --- a/third_party/variant/test/compilation_failure/is_type.cpp +++ /dev/null @@ -1,9 +0,0 @@ -// @EXPECTED: - -#include - -int main() -{ - mapbox::util::variant x; - x.is(); -} diff --git a/third_party/variant/test/compilation_failure/mutating_visitor_on_const.cpp b/third_party/variant/test/compilation_failure/mutating_visitor_on_const.cpp deleted file mode 100644 index eb0ed18ee..000000000 --- a/third_party/variant/test/compilation_failure/mutating_visitor_on_const.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// @EXPECTED: no matching function for call to .*\ - -#include - -struct mutating_visitor -{ - mutating_visitor(int val) - : val_(val) {} - - void operator()(int& val) const - { - val = val_; - } - - int val_; -}; - -int main() -{ - const mapbox::util::variant var(123); - const mutating_visitor visitor(456); - mapbox::util::apply_visitor(visitor, var); -} diff --git a/third_party/variant/test/compilation_failure/no-reference.cpp b/third_party/variant/test/compilation_failure/no-reference.cpp deleted file mode 100644 index 2f547fc1f..000000000 --- a/third_party/variant/test/compilation_failure/no-reference.cpp +++ /dev/null @@ -1,8 +0,0 @@ -// @EXPECTED: Variant can not hold reference types - -#include - -int main() -{ - mapbox::util::variant x{mapbox::util::no_init()}; -} diff --git a/third_party/variant/test/hashable_test.cpp b/third_party/variant/test/hashable_test.cpp deleted file mode 100644 index f3393a9ed..000000000 --- a/third_party/variant/test/hashable_test.cpp +++ /dev/null @@ -1,158 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include - -using namespace mapbox::util; - -void test_singleton() -{ - using V = variant; - - V singleton = 5; - - if (std::hash{}(singleton) != std::hash{}(5)) - { - std::cerr << "Expected variant hash to be the same as hash of its value\n"; - std::exit(EXIT_FAILURE); - } -} - -void test_default_hashable() -{ - using V = variant; - - V var; - - // Check int hashes - var = 1; - - if (std::hash{}(var) != std::hash{}(1)) - { - std::cerr << "Expected variant hash to be the same as hash of its value\n"; - std::exit(EXIT_FAILURE); - } - - // Check double hashes - var = 23.4; - - if (std::hash{}(var) != std::hash{}(23.4)) - { - std::cerr << "Expected variant hash to be the same as hash of its value\n"; - std::exit(EXIT_FAILURE); - } - - // Check string hashes - var = std::string{"Hello, World!"}; - - if (std::hash{}(var) != std::hash{}("Hello, World!")) - { - std::cerr << "Expected variant hash to be the same as hash of its value\n"; - std::exit(EXIT_FAILURE); - } -} - -struct Hashable -{ - static const constexpr auto const_hash = 5; -}; - -namespace std { -template <> -struct hash -{ - std::size_t operator()(const Hashable&) const noexcept - { - return Hashable::const_hash; - } -}; -} - -void test_custom_hasher() -{ - using V = variant; - - V var; - - var = Hashable{}; - - if (std::hash{}(var) != Hashable::const_hash) - { - std::cerr << "Expected variant hash to be the same as hash of its value\n"; - std::exit(EXIT_FAILURE); - } -} - -void test_hashable_in_container() -{ - using V = variant; - - // won't compile if V is not Hashable - std::unordered_set vs; - - vs.insert(1); - vs.insert(2.3); - vs.insert("4"); -} - -struct Empty -{ -}; - -struct Node; - -using Tree = variant>; - -struct Node -{ - Node(Tree left_, Tree right_) : left(std::move(left_)), right(std::move(right_)) {} - - Tree left = Empty{}; - Tree right = Empty{}; -}; - -namespace std { -template <> -struct hash -{ - std::size_t operator()(const Empty&) const noexcept - { - return 3; - } -}; - -template <> -struct hash -{ - std::size_t operator()(const Node& n) const noexcept - { - return 5 + std::hash{}(n.left) + std::hash{}(n.right); - } -}; -} - -void test_recursive_hashable() -{ - - Tree tree = Node{Node{Empty{}, Empty{}}, Empty{}}; - - if (std::hash{}(tree) != ((5 + (5 + (3 + 3))) + 3)) - { - std::cerr << "Expected variant hash to be the same as hash of its value\n"; - std::exit(EXIT_FAILURE); - } -} - -int main() -{ - test_singleton(); - test_default_hashable(); - test_custom_hasher(); - test_hashable_in_container(); - test_recursive_hashable(); -} diff --git a/third_party/variant/test/include/auto_cpu_timer.hpp b/third_party/variant/test/include/auto_cpu_timer.hpp deleted file mode 100644 index b41935fb1..000000000 --- a/third_party/variant/test/include/auto_cpu_timer.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include -#include - -struct auto_cpu_timer { - std::chrono::time_point start; - auto_cpu_timer() : start(std::chrono::high_resolution_clock::now()) { - } - ~auto_cpu_timer() { - auto end = std::chrono::high_resolution_clock::now(); - std::chrono::microseconds elapsed = - std::chrono::duration_cast(end - start); - std::cerr << elapsed.count() << "us" << std::endl; - } -}; diff --git a/third_party/variant/test/include/catch.hpp b/third_party/variant/test/include/catch.hpp deleted file mode 100644 index f6c44e427..000000000 --- a/third_party/variant/test/include/catch.hpp +++ /dev/null @@ -1,11422 +0,0 @@ -/* - * Catch v1.9.0 - * Generated: 2017-04-07 22:51:48.249456 - * ---------------------------------------------------------- - * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED -#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED - -#define TWOBLUECUBES_CATCH_HPP_INCLUDED - -#ifdef __clang__ -# pragma clang system_header -#elif defined __GNUC__ -# pragma GCC system_header -#endif - -// #included from: internal/catch_suppress_warnings.h - -#ifdef __clang__ -# ifdef __ICC // icpc defines the __clang__ macro -# pragma warning(push) -# pragma warning(disable: 161 1682) -# else // __ICC -# pragma clang diagnostic ignored "-Wglobal-constructors" -# pragma clang diagnostic ignored "-Wvariadic-macros" -# pragma clang diagnostic ignored "-Wc99-extensions" -# pragma clang diagnostic ignored "-Wunused-variable" -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wpadded" -# pragma clang diagnostic ignored "-Wc++98-compat" -# pragma clang diagnostic ignored "-Wc++98-compat-pedantic" -# pragma clang diagnostic ignored "-Wswitch-enum" -# pragma clang diagnostic ignored "-Wcovered-switch-default" -# endif -#elif defined __GNUC__ -# pragma GCC diagnostic ignored "-Wvariadic-macros" -# pragma GCC diagnostic ignored "-Wunused-variable" -# pragma GCC diagnostic ignored "-Wparentheses" - -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wpadded" -#endif -#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) -# define CATCH_IMPL -#endif - -#ifdef CATCH_IMPL -# ifndef CLARA_CONFIG_MAIN -# define CLARA_CONFIG_MAIN_NOT_DEFINED -# define CLARA_CONFIG_MAIN -# endif -#endif - -// #included from: internal/catch_notimplemented_exception.h -#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED - -// #included from: catch_common.h -#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED - -// #included from: catch_compiler_capabilities.h -#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED - -// Detect a number of compiler features - mostly C++11/14 conformance - by compiler -// The following features are defined: -// -// CATCH_CONFIG_CPP11_NULLPTR : is nullptr supported? -// CATCH_CONFIG_CPP11_NOEXCEPT : is noexcept supported? -// CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods -// CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported? -// CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported -// CATCH_CONFIG_CPP11_LONG_LONG : is long long supported? -// CATCH_CONFIG_CPP11_OVERRIDE : is override supported? -// CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) -// CATCH_CONFIG_CPP11_SHUFFLE : is std::shuffle supported? -// CATCH_CONFIG_CPP11_TYPE_TRAITS : are type_traits and enable_if supported? - -// CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported? - -// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported? -// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? -// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? -// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? -// **************** -// Note to maintainers: if new toggles are added please document them -// in configuration.md, too -// **************** - -// In general each macro has a _NO_ form -// (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature. -// Many features, at point of detection, define an _INTERNAL_ macro, so they -// can be combined, en-mass, with the _NO_ forms later. - -// All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11 - -#ifdef __cplusplus - -# if __cplusplus >= 201103L -# define CATCH_CPP11_OR_GREATER -# endif - -# if __cplusplus >= 201402L -# define CATCH_CPP14_OR_GREATER -# endif - -#endif - -#ifdef __clang__ - -# if __has_feature(cxx_nullptr) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# endif - -# if __has_feature(cxx_noexcept) -# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -# endif - -# if defined(CATCH_CPP11_OR_GREATER) -# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) -# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ - _Pragma( "clang diagnostic pop" ) - -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) -# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic pop" ) -# endif - -#endif // __clang__ - -//////////////////////////////////////////////////////////////////////////////// -// Cygwin -#ifdef __CYGWIN__ - -# if !defined(CATCH_CONFIG_POSIX_SIGNALS) -# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -# endif - -// Required for some versions of Cygwin to declare gettimeofday -// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin -# define _BSD_SOURCE - -#endif // __CYGWIN__ - -//////////////////////////////////////////////////////////////////////////////// -// Borland -#ifdef __BORLANDC__ - -#endif // __BORLANDC__ - -//////////////////////////////////////////////////////////////////////////////// -// EDG -#ifdef __EDG_VERSION__ - -#endif // __EDG_VERSION__ - -//////////////////////////////////////////////////////////////////////////////// -// Digital Mars -#ifdef __DMC__ - -#endif // __DMC__ - -//////////////////////////////////////////////////////////////////////////////// -// GCC -#ifdef __GNUC__ - -# if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# endif - -// - otherwise more recent versions define __cplusplus >= 201103L -// and will get picked up below - -#endif // __GNUC__ - -//////////////////////////////////////////////////////////////////////////////// -// Visual C++ -#ifdef _MSC_VER - -#define CATCH_INTERNAL_CONFIG_WINDOWS_SEH - -#if (_MSC_VER >= 1600) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -#endif - -#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) -#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE -#define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS -#endif - -#endif // _MSC_VER - -//////////////////////////////////////////////////////////////////////////////// - -// Use variadic macros if the compiler supports them -#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \ - ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \ - ( defined __GNUC__ && __GNUC__ >= 3 ) || \ - ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L ) - -#define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS - -#endif - -// Use __COUNTER__ if the compiler supports it -#if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \ - ( defined __GNUC__ && __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 ) || \ - ( defined __clang__ && __clang_major__ >= 3 ) - -#define CATCH_INTERNAL_CONFIG_COUNTER - -#endif - -//////////////////////////////////////////////////////////////////////////////// -// C++ language feature support - -// catch all support for C++11 -#if defined(CATCH_CPP11_OR_GREATER) - -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -# define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM -# define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE -# define CATCH_INTERNAL_CONFIG_CPP11_TUPLE -# endif - -# ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS -# define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS -# endif - -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) -# define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG -# endif - -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) -# define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE -# endif -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) -# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -# endif -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) -# define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE -# endif -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) -# define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS -# endif - -#endif // __cplusplus >= 201103L - -// Now set the actual defines based on the above + anything the user has configured -#if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_NULLPTR -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_NOEXCEPT -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_GENERATED_METHODS -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_IS_ENUM -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_TUPLE -#endif -#if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS) -# define CATCH_CONFIG_VARIADIC_MACROS -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_LONG_LONG -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_OVERRIDE -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_UNIQUE_PTR -#endif -// Use of __COUNTER__ is suppressed if __JETBRAINS_IDE__ is #defined (meaning we're being parsed by a JetBrains IDE for -// analytics) because, at time of writing, __COUNTER__ is not properly handled by it. -// This does not affect compilation -#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) && !defined(__JETBRAINS_IDE__) -# define CATCH_CONFIG_COUNTER -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_NO_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_SHUFFLE -#endif -# if defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_NO_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_TYPE_TRAITS -# endif -#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) -# define CATCH_CONFIG_WINDOWS_SEH -#endif -// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. -#if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) -# define CATCH_CONFIG_POSIX_SIGNALS -#endif - -#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS -# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS -# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS -#endif - -// noexcept support: -#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT) -# define CATCH_NOEXCEPT noexcept -# define CATCH_NOEXCEPT_IS(x) noexcept(x) -#else -# define CATCH_NOEXCEPT throw() -# define CATCH_NOEXCEPT_IS(x) -#endif - -// nullptr support -#ifdef CATCH_CONFIG_CPP11_NULLPTR -# define CATCH_NULL nullptr -#else -# define CATCH_NULL NULL -#endif - -// override support -#ifdef CATCH_CONFIG_CPP11_OVERRIDE -# define CATCH_OVERRIDE override -#else -# define CATCH_OVERRIDE -#endif - -// unique_ptr support -#ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR -# define CATCH_AUTO_PTR( T ) std::unique_ptr -#else -# define CATCH_AUTO_PTR( T ) std::auto_ptr -#endif - -#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line -#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) -#ifdef CATCH_CONFIG_COUNTER -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) -#else -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) -#endif - -#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr -#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) - -#include -#include - -namespace Catch { - - struct IConfig; - - struct CaseSensitive { enum Choice { - Yes, - No - }; }; - - class NonCopyable { -#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - NonCopyable( NonCopyable const& ) = delete; - NonCopyable( NonCopyable && ) = delete; - NonCopyable& operator = ( NonCopyable const& ) = delete; - NonCopyable& operator = ( NonCopyable && ) = delete; -#else - NonCopyable( NonCopyable const& info ); - NonCopyable& operator = ( NonCopyable const& ); -#endif - - protected: - NonCopyable() {} - virtual ~NonCopyable(); - }; - - class SafeBool { - public: - typedef void (SafeBool::*type)() const; - - static type makeSafe( bool value ) { - return value ? &SafeBool::trueValue : 0; - } - private: - void trueValue() const {} - }; - - template - inline void deleteAll( ContainerT& container ) { - typename ContainerT::const_iterator it = container.begin(); - typename ContainerT::const_iterator itEnd = container.end(); - for(; it != itEnd; ++it ) - delete *it; - } - template - inline void deleteAllValues( AssociativeContainerT& container ) { - typename AssociativeContainerT::const_iterator it = container.begin(); - typename AssociativeContainerT::const_iterator itEnd = container.end(); - for(; it != itEnd; ++it ) - delete it->second; - } - - bool startsWith( std::string const& s, std::string const& prefix ); - bool startsWith( std::string const& s, char prefix ); - bool endsWith( std::string const& s, std::string const& suffix ); - bool endsWith( std::string const& s, char suffix ); - bool contains( std::string const& s, std::string const& infix ); - void toLowerInPlace( std::string& s ); - std::string toLower( std::string const& s ); - std::string trim( std::string const& str ); - bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); - - struct pluralise { - pluralise( std::size_t count, std::string const& label ); - - friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); - - std::size_t m_count; - std::string m_label; - }; - - struct SourceLineInfo { - - SourceLineInfo(); - SourceLineInfo( char const* _file, std::size_t _line ); -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - SourceLineInfo(SourceLineInfo const& other) = default; - SourceLineInfo( SourceLineInfo && ) = default; - SourceLineInfo& operator = ( SourceLineInfo const& ) = default; - SourceLineInfo& operator = ( SourceLineInfo && ) = default; -# endif - bool empty() const; - bool operator == ( SourceLineInfo const& other ) const; - bool operator < ( SourceLineInfo const& other ) const; - - char const* file; - std::size_t line; - }; - - std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); - - // This is just here to avoid compiler warnings with macro constants and boolean literals - inline bool isTrue( bool value ){ return value; } - inline bool alwaysTrue() { return true; } - inline bool alwaysFalse() { return false; } - - void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ); - - void seedRng( IConfig const& config ); - unsigned int rngSeed(); - - // Use this in variadic streaming macros to allow - // >> +StreamEndStop - // as well as - // >> stuff +StreamEndStop - struct StreamEndStop { - std::string operator+() { - return std::string(); - } - }; - template - T const& operator + ( T const& value, StreamEndStop ) { - return value; - } -} - -#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) -#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO ); - -namespace Catch { - - class NotImplementedException : public std::exception - { - public: - NotImplementedException( SourceLineInfo const& lineInfo ); - NotImplementedException( NotImplementedException const& ) {} - - virtual ~NotImplementedException() CATCH_NOEXCEPT {} - - virtual const char* what() const CATCH_NOEXCEPT; - - private: - std::string m_what; - SourceLineInfo m_lineInfo; - }; - -} // end namespace Catch - -/////////////////////////////////////////////////////////////////////////////// -#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO ) - -// #included from: internal/catch_context.h -#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED - -// #included from: catch_interfaces_generators.h -#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED - -#include - -namespace Catch { - - struct IGeneratorInfo { - virtual ~IGeneratorInfo(); - virtual bool moveNext() = 0; - virtual std::size_t getCurrentIndex() const = 0; - }; - - struct IGeneratorsForTest { - virtual ~IGeneratorsForTest(); - - virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0; - virtual bool moveNext() = 0; - }; - - IGeneratorsForTest* createGeneratorsForTest(); - -} // end namespace Catch - -// #included from: catch_ptr.hpp -#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -#endif - -namespace Catch { - - // An intrusive reference counting smart pointer. - // T must implement addRef() and release() methods - // typically implementing the IShared interface - template - class Ptr { - public: - Ptr() : m_p( CATCH_NULL ){} - Ptr( T* p ) : m_p( p ){ - if( m_p ) - m_p->addRef(); - } - Ptr( Ptr const& other ) : m_p( other.m_p ){ - if( m_p ) - m_p->addRef(); - } - ~Ptr(){ - if( m_p ) - m_p->release(); - } - void reset() { - if( m_p ) - m_p->release(); - m_p = CATCH_NULL; - } - Ptr& operator = ( T* p ){ - Ptr temp( p ); - swap( temp ); - return *this; - } - Ptr& operator = ( Ptr const& other ){ - Ptr temp( other ); - swap( temp ); - return *this; - } - void swap( Ptr& other ) { std::swap( m_p, other.m_p ); } - T* get() const{ return m_p; } - T& operator*() const { return *m_p; } - T* operator->() const { return m_p; } - bool operator !() const { return m_p == CATCH_NULL; } - operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_NULL ); } - - private: - T* m_p; - }; - - struct IShared : NonCopyable { - virtual ~IShared(); - virtual void addRef() const = 0; - virtual void release() const = 0; - }; - - template - struct SharedImpl : T { - - SharedImpl() : m_rc( 0 ){} - - virtual void addRef() const { - ++m_rc; - } - virtual void release() const { - if( --m_rc == 0 ) - delete this; - } - - mutable unsigned int m_rc; - }; - -} // end namespace Catch - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -namespace Catch { - - class TestCase; - class Stream; - struct IResultCapture; - struct IRunner; - struct IGeneratorsForTest; - struct IConfig; - - struct IContext - { - virtual ~IContext(); - - virtual IResultCapture* getResultCapture() = 0; - virtual IRunner* getRunner() = 0; - virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0; - virtual bool advanceGeneratorsForCurrentTest() = 0; - virtual Ptr getConfig() const = 0; - }; - - struct IMutableContext : IContext - { - virtual ~IMutableContext(); - virtual void setResultCapture( IResultCapture* resultCapture ) = 0; - virtual void setRunner( IRunner* runner ) = 0; - virtual void setConfig( Ptr const& config ) = 0; - }; - - IContext& getCurrentContext(); - IMutableContext& getCurrentMutableContext(); - void cleanUpContext(); - Stream createStream( std::string const& streamName ); - -} - -// #included from: internal/catch_test_registry.hpp -#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED - -// #included from: catch_interfaces_testcase.h -#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED - -#include - -namespace Catch { - - class TestSpec; - - struct ITestCase : IShared { - virtual void invoke () const = 0; - protected: - virtual ~ITestCase(); - }; - - class TestCase; - struct IConfig; - - struct ITestCaseRegistry { - virtual ~ITestCaseRegistry(); - virtual std::vector const& getAllTests() const = 0; - virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; - }; - - bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); - std::vector const& getAllTestCasesSorted( IConfig const& config ); - -} - -namespace Catch { - -template -class MethodTestCase : public SharedImpl { - -public: - MethodTestCase( void (C::*method)() ) : m_method( method ) {} - - virtual void invoke() const { - C obj; - (obj.*m_method)(); - } - -private: - virtual ~MethodTestCase() {} - - void (C::*m_method)(); -}; - -typedef void(*TestFunction)(); - -struct NameAndDesc { - NameAndDesc( const char* _name = "", const char* _description= "" ) - : name( _name ), description( _description ) - {} - - const char* name; - const char* description; -}; - -void registerTestCase - ( ITestCase* testCase, - char const* className, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo ); - -struct AutoReg { - - AutoReg - ( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ); - - template - AutoReg - ( void (C::*method)(), - char const* className, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo ) { - - registerTestCase - ( new MethodTestCase( method ), - className, - nameAndDesc, - lineInfo ); - } - - ~AutoReg(); - -private: - AutoReg( AutoReg const& ); - void operator= ( AutoReg const& ); -}; - -void registerTestCaseFunction - ( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ); - -} // end namespace Catch - -#ifdef CATCH_CONFIG_VARIADIC_MACROS - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ - static void TestName(); \ - CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); } \ - CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ - static void TestName() - #define INTERNAL_CATCH_TESTCASE( ... ) \ - INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ ) - - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ - CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } \ - CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS - - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ - CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - namespace{ \ - struct TestName : ClassName{ \ - void test(); \ - }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \ - } \ - CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ - void TestName::test() - #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ - INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ ) - - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ - CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); \ - CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS - -#else - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \ - static void TestName(); \ - CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\ - CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ - static void TestName() - #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ - INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc ) - - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \ - CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } \ - CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS - - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\ - CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - namespace{ \ - struct TestCaseName : ClassName{ \ - void test(); \ - }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \ - } \ - CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ - void TestCaseName::test() - #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ - INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc ) - - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \ - CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); \ - CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS - -#endif - -// #included from: internal/catch_capture.hpp -#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED - -// #included from: catch_result_builder.h -#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED - -// #included from: catch_result_type.h -#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED - -namespace Catch { - - // ResultWas::OfType enum - struct ResultWas { enum OfType { - Unknown = -1, - Ok = 0, - Info = 1, - Warning = 2, - - FailureBit = 0x10, - - ExpressionFailed = FailureBit | 1, - ExplicitFailure = FailureBit | 2, - - Exception = 0x100 | FailureBit, - - ThrewException = Exception | 1, - DidntThrowException = Exception | 2, - - FatalErrorCondition = 0x200 | FailureBit - - }; }; - - inline bool isOk( ResultWas::OfType resultType ) { - return ( resultType & ResultWas::FailureBit ) == 0; - } - inline bool isJustInfo( int flags ) { - return flags == ResultWas::Info; - } - - // ResultDisposition::Flags enum - struct ResultDisposition { enum Flags { - Normal = 0x01, - - ContinueOnFailure = 0x02, // Failures fail test, but execution continues - FalseTest = 0x04, // Prefix expression with ! - SuppressFail = 0x08 // Failures are reported but do not fail the test - }; }; - - inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { - return static_cast( static_cast( lhs ) | static_cast( rhs ) ); - } - - inline bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } - inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } - inline bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } - -} // end namespace Catch - -// #included from: catch_assertionresult.h -#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED - -#include - -namespace Catch { - - struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; - - struct DecomposedExpression - { - virtual ~DecomposedExpression() {} - virtual bool isBinaryExpression() const { - return false; - } - virtual void reconstructExpression( std::string& dest ) const = 0; - - // Only simple binary comparisons can be decomposed. - // If more complex check is required then wrap sub-expressions in parentheses. - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( T const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( T const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( T const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( T const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& ); - - private: - DecomposedExpression& operator = (DecomposedExpression const&); - }; - - struct AssertionInfo - { - AssertionInfo() {} - AssertionInfo( std::string const& _macroName, - SourceLineInfo const& _lineInfo, - std::string const& _capturedExpression, - ResultDisposition::Flags _resultDisposition ); - - std::string macroName; - SourceLineInfo lineInfo; - std::string capturedExpression; - ResultDisposition::Flags resultDisposition; - }; - - struct AssertionResultData - { - AssertionResultData() : decomposedExpression( CATCH_NULL ) - , resultType( ResultWas::Unknown ) - , negated( false ) - , parenthesized( false ) {} - - void negate( bool parenthesize ) { - negated = !negated; - parenthesized = parenthesize; - if( resultType == ResultWas::Ok ) - resultType = ResultWas::ExpressionFailed; - else if( resultType == ResultWas::ExpressionFailed ) - resultType = ResultWas::Ok; - } - - std::string const& reconstructExpression() const { - if( decomposedExpression != CATCH_NULL ) { - decomposedExpression->reconstructExpression( reconstructedExpression ); - if( parenthesized ) { - reconstructedExpression.insert( 0, 1, '(' ); - reconstructedExpression.append( 1, ')' ); - } - if( negated ) { - reconstructedExpression.insert( 0, 1, '!' ); - } - decomposedExpression = CATCH_NULL; - } - return reconstructedExpression; - } - - mutable DecomposedExpression const* decomposedExpression; - mutable std::string reconstructedExpression; - std::string message; - ResultWas::OfType resultType; - bool negated; - bool parenthesized; - }; - - class AssertionResult { - public: - AssertionResult(); - AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); - ~AssertionResult(); -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - AssertionResult( AssertionResult const& ) = default; - AssertionResult( AssertionResult && ) = default; - AssertionResult& operator = ( AssertionResult const& ) = default; - AssertionResult& operator = ( AssertionResult && ) = default; -# endif - - bool isOk() const; - bool succeeded() const; - ResultWas::OfType getResultType() const; - bool hasExpression() const; - bool hasMessage() const; - std::string getExpression() const; - std::string getExpressionInMacro() const; - bool hasExpandedExpression() const; - std::string getExpandedExpression() const; - std::string getMessage() const; - SourceLineInfo getSourceInfo() const; - std::string getTestMacroName() const; - void discardDecomposedExpression() const; - void expandDecomposedExpression() const; - - protected: - AssertionInfo m_info; - AssertionResultData m_resultData; - }; - -} // end namespace Catch - -// #included from: catch_matchers.hpp -#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED - -namespace Catch { -namespace Matchers { - namespace Impl { - - template struct MatchAllOf; - template struct MatchAnyOf; - template struct MatchNotOf; - - class MatcherUntypedBase { - public: - std::string toString() const { - if( m_cachedToString.empty() ) - m_cachedToString = describe(); - return m_cachedToString; - } - - protected: - virtual ~MatcherUntypedBase(); - virtual std::string describe() const = 0; - mutable std::string m_cachedToString; - private: - MatcherUntypedBase& operator = ( MatcherUntypedBase const& ); - }; - - template - struct MatcherMethod { - virtual bool match( ObjectT const& arg ) const = 0; - }; - template - struct MatcherMethod { - virtual bool match( PtrT* arg ) const = 0; - }; - - template - struct MatcherBase : MatcherUntypedBase, MatcherMethod { - - MatchAllOf operator && ( MatcherBase const& other ) const; - MatchAnyOf operator || ( MatcherBase const& other ) const; - MatchNotOf operator ! () const; - }; - - template - struct MatchAllOf : MatcherBase { - virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { - for( std::size_t i = 0; i < m_matchers.size(); ++i ) { - if (!m_matchers[i]->match(arg)) - return false; - } - return true; - } - virtual std::string describe() const CATCH_OVERRIDE { - std::string description; - description.reserve( 4 + m_matchers.size()*32 ); - description += "( "; - for( std::size_t i = 0; i < m_matchers.size(); ++i ) { - if( i != 0 ) - description += " and "; - description += m_matchers[i]->toString(); - } - description += " )"; - return description; - } - - MatchAllOf& operator && ( MatcherBase const& other ) { - m_matchers.push_back( &other ); - return *this; - } - - std::vector const*> m_matchers; - }; - template - struct MatchAnyOf : MatcherBase { - - virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { - for( std::size_t i = 0; i < m_matchers.size(); ++i ) { - if (m_matchers[i]->match(arg)) - return true; - } - return false; - } - virtual std::string describe() const CATCH_OVERRIDE { - std::string description; - description.reserve( 4 + m_matchers.size()*32 ); - description += "( "; - for( std::size_t i = 0; i < m_matchers.size(); ++i ) { - if( i != 0 ) - description += " or "; - description += m_matchers[i]->toString(); - } - description += " )"; - return description; - } - - MatchAnyOf& operator || ( MatcherBase const& other ) { - m_matchers.push_back( &other ); - return *this; - } - - std::vector const*> m_matchers; - }; - - template - struct MatchNotOf : MatcherBase { - - MatchNotOf( MatcherBase const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {} - - virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { - return !m_underlyingMatcher.match( arg ); - } - - virtual std::string describe() const CATCH_OVERRIDE { - return "not " + m_underlyingMatcher.toString(); - } - MatcherBase const& m_underlyingMatcher; - }; - - template - MatchAllOf MatcherBase::operator && ( MatcherBase const& other ) const { - return MatchAllOf() && *this && other; - } - template - MatchAnyOf MatcherBase::operator || ( MatcherBase const& other ) const { - return MatchAnyOf() || *this || other; - } - template - MatchNotOf MatcherBase::operator ! () const { - return MatchNotOf( *this ); - } - - } // namespace Impl - - // The following functions create the actual matcher objects. - // This allows the types to be inferred - // - deprecated: prefer ||, && and ! - template - inline Impl::MatchNotOf Not( Impl::MatcherBase const& underlyingMatcher ) { - return Impl::MatchNotOf( underlyingMatcher ); - } - template - inline Impl::MatchAllOf AllOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2 ) { - return Impl::MatchAllOf() && m1 && m2; - } - template - inline Impl::MatchAllOf AllOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2, Impl::MatcherBase const& m3 ) { - return Impl::MatchAllOf() && m1 && m2 && m3; - } - template - inline Impl::MatchAnyOf AnyOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2 ) { - return Impl::MatchAnyOf() || m1 || m2; - } - template - inline Impl::MatchAnyOf AnyOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2, Impl::MatcherBase const& m3 ) { - return Impl::MatchAnyOf() || m1 || m2 || m3; - } - -} // namespace Matchers - -using namespace Matchers; -using Matchers::Impl::MatcherBase; - -} // namespace Catch - -namespace Catch { - - struct TestFailureException{}; - - template class ExpressionLhs; - - struct CopyableStream { - CopyableStream() {} - CopyableStream( CopyableStream const& other ) { - oss << other.oss.str(); - } - CopyableStream& operator=( CopyableStream const& other ) { - oss.str(std::string()); - oss << other.oss.str(); - return *this; - } - std::ostringstream oss; - }; - - class ResultBuilder : public DecomposedExpression { - public: - ResultBuilder( char const* macroName, - SourceLineInfo const& lineInfo, - char const* capturedExpression, - ResultDisposition::Flags resultDisposition, - char const* secondArg = "" ); - ~ResultBuilder(); - - template - ExpressionLhs operator <= ( T const& operand ); - ExpressionLhs operator <= ( bool value ); - - template - ResultBuilder& operator << ( T const& value ) { - m_stream.oss << value; - return *this; - } - - ResultBuilder& setResultType( ResultWas::OfType result ); - ResultBuilder& setResultType( bool result ); - - void endExpression( DecomposedExpression const& expr ); - - virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE; - - AssertionResult build() const; - AssertionResult build( DecomposedExpression const& expr ) const; - - void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal ); - void captureResult( ResultWas::OfType resultType ); - void captureExpression(); - void captureExpectedException( std::string const& expectedMessage ); - void captureExpectedException( Matchers::Impl::MatcherBase const& matcher ); - void handleResult( AssertionResult const& result ); - void react(); - bool shouldDebugBreak() const; - bool allowThrows() const; - - template - void captureMatch( ArgT const& arg, MatcherT const& matcher, char const* matcherString ); - - void setExceptionGuard(); - void unsetExceptionGuard(); - - private: - AssertionInfo m_assertionInfo; - AssertionResultData m_data; - CopyableStream m_stream; - - bool m_shouldDebugBreak; - bool m_shouldThrow; - bool m_guardException; - }; - -} // namespace Catch - -// Include after due to circular dependency: -// #included from: catch_expression_lhs.hpp -#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED - -// #included from: catch_evaluate.hpp -#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4389) // '==' : signed/unsigned mismatch -#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) -#endif - -#include - -namespace Catch { -namespace Internal { - - enum Operator { - IsEqualTo, - IsNotEqualTo, - IsLessThan, - IsGreaterThan, - IsLessThanOrEqualTo, - IsGreaterThanOrEqualTo - }; - - template struct OperatorTraits { static const char* getName(){ return "*error*"; } }; - template<> struct OperatorTraits { static const char* getName(){ return "=="; } }; - template<> struct OperatorTraits { static const char* getName(){ return "!="; } }; - template<> struct OperatorTraits { static const char* getName(){ return "<"; } }; - template<> struct OperatorTraits { static const char* getName(){ return ">"; } }; - template<> struct OperatorTraits { static const char* getName(){ return "<="; } }; - template<> struct OperatorTraits{ static const char* getName(){ return ">="; } }; - - template - inline T& opCast(T const& t) { return const_cast(t); } - -// nullptr_t support based on pull request #154 from Konstantin Baumann -#ifdef CATCH_CONFIG_CPP11_NULLPTR - inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; } -#endif // CATCH_CONFIG_CPP11_NULLPTR - - // So the compare overloads can be operator agnostic we convey the operator as a template - // enum, which is used to specialise an Evaluator for doing the comparison. - template - class Evaluator{}; - - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs) { - return bool( opCast( lhs ) == opCast( rhs ) ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return bool( opCast( lhs ) != opCast( rhs ) ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return bool( opCast( lhs ) < opCast( rhs ) ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return bool( opCast( lhs ) > opCast( rhs ) ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return bool( opCast( lhs ) >= opCast( rhs ) ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return bool( opCast( lhs ) <= opCast( rhs ) ); - } - }; - - template - bool applyEvaluator( T1 const& lhs, T2 const& rhs ) { - return Evaluator::evaluate( lhs, rhs ); - } - - // This level of indirection allows us to specialise for integer types - // to avoid signed/ unsigned warnings - - // "base" overload - template - bool compare( T1 const& lhs, T2 const& rhs ) { - return Evaluator::evaluate( lhs, rhs ); - } - - // unsigned X to int - template bool compare( unsigned int lhs, int rhs ) { - return applyEvaluator( lhs, static_cast( rhs ) ); - } - template bool compare( unsigned long lhs, int rhs ) { - return applyEvaluator( lhs, static_cast( rhs ) ); - } - template bool compare( unsigned char lhs, int rhs ) { - return applyEvaluator( lhs, static_cast( rhs ) ); - } - - // unsigned X to long - template bool compare( unsigned int lhs, long rhs ) { - return applyEvaluator( lhs, static_cast( rhs ) ); - } - template bool compare( unsigned long lhs, long rhs ) { - return applyEvaluator( lhs, static_cast( rhs ) ); - } - template bool compare( unsigned char lhs, long rhs ) { - return applyEvaluator( lhs, static_cast( rhs ) ); - } - - // int to unsigned X - template bool compare( int lhs, unsigned int rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( int lhs, unsigned long rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( int lhs, unsigned char rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - - // long to unsigned X - template bool compare( long lhs, unsigned int rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( long lhs, unsigned long rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( long lhs, unsigned char rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - - // pointer to long (when comparing against NULL) - template bool compare( long lhs, T* rhs ) { - return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); - } - template bool compare( T* lhs, long rhs ) { - return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); - } - - // pointer to int (when comparing against NULL) - template bool compare( int lhs, T* rhs ) { - return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); - } - template bool compare( T* lhs, int rhs ) { - return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); - } - -#ifdef CATCH_CONFIG_CPP11_LONG_LONG - // long long to unsigned X - template bool compare( long long lhs, unsigned int rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( long long lhs, unsigned long rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( long long lhs, unsigned long long rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( long long lhs, unsigned char rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - - // unsigned long long to X - template bool compare( unsigned long long lhs, int rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( unsigned long long lhs, long rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( unsigned long long lhs, long long rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( unsigned long long lhs, char rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - - // pointer to long long (when comparing against NULL) - template bool compare( long long lhs, T* rhs ) { - return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); - } - template bool compare( T* lhs, long long rhs ) { - return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); - } -#endif // CATCH_CONFIG_CPP11_LONG_LONG - -#ifdef CATCH_CONFIG_CPP11_NULLPTR - // pointer to nullptr_t (when comparing against nullptr) - template bool compare( std::nullptr_t, T* rhs ) { - return Evaluator::evaluate( nullptr, rhs ); - } - template bool compare( T* lhs, std::nullptr_t ) { - return Evaluator::evaluate( lhs, nullptr ); - } -#endif // CATCH_CONFIG_CPP11_NULLPTR - -} // end of namespace Internal -} // end of namespace Catch - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -// #included from: catch_tostring.h -#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED - -#include -#include -#include -#include -#include - -#ifdef __OBJC__ -// #included from: catch_objc_arc.hpp -#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED - -#import - -#ifdef __has_feature -#define CATCH_ARC_ENABLED __has_feature(objc_arc) -#else -#define CATCH_ARC_ENABLED 0 -#endif - -void arcSafeRelease( NSObject* obj ); -id performOptionalSelector( id obj, SEL sel ); - -#if !CATCH_ARC_ENABLED -inline void arcSafeRelease( NSObject* obj ) { - [obj release]; -} -inline id performOptionalSelector( id obj, SEL sel ) { - if( [obj respondsToSelector: sel] ) - return [obj performSelector: sel]; - return nil; -} -#define CATCH_UNSAFE_UNRETAINED -#define CATCH_ARC_STRONG -#else -inline void arcSafeRelease( NSObject* ){} -inline id performOptionalSelector( id obj, SEL sel ) { -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Warc-performSelector-leaks" -#endif - if( [obj respondsToSelector: sel] ) - return [obj performSelector: sel]; -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - return nil; -} -#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained -#define CATCH_ARC_STRONG __strong -#endif - -#endif - -#ifdef CATCH_CONFIG_CPP11_TUPLE -#include -#endif - -#ifdef CATCH_CONFIG_CPP11_IS_ENUM -#include -#endif - -namespace Catch { - -// Why we're here. -template -std::string toString( T const& value ); - -// Built in overloads - -std::string toString( std::string const& value ); -std::string toString( std::wstring const& value ); -std::string toString( const char* const value ); -std::string toString( char* const value ); -std::string toString( const wchar_t* const value ); -std::string toString( wchar_t* const value ); -std::string toString( int value ); -std::string toString( unsigned long value ); -std::string toString( unsigned int value ); -std::string toString( const double value ); -std::string toString( const float value ); -std::string toString( bool value ); -std::string toString( char value ); -std::string toString( signed char value ); -std::string toString( unsigned char value ); - -#ifdef CATCH_CONFIG_CPP11_LONG_LONG -std::string toString( long long value ); -std::string toString( unsigned long long value ); -#endif - -#ifdef CATCH_CONFIG_CPP11_NULLPTR -std::string toString( std::nullptr_t ); -#endif - -#ifdef __OBJC__ - std::string toString( NSString const * const& nsstring ); - std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ); - std::string toString( NSObject* const& nsObject ); -#endif - -namespace Detail { - - extern const std::string unprintableString; - - #if !defined(CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK) - struct BorgType { - template BorgType( T const& ); - }; - - struct TrueType { char sizer[1]; }; - struct FalseType { char sizer[2]; }; - - TrueType& testStreamable( std::ostream& ); - FalseType testStreamable( FalseType ); - - FalseType operator<<( std::ostream const&, BorgType const& ); - - template - struct IsStreamInsertable { - static std::ostream &s; - static T const&t; - enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) }; - }; -#else - template - class IsStreamInsertable { - template - static auto test(int) - -> decltype( std::declval() << std::declval(), std::true_type() ); - - template - static auto test(...) -> std::false_type; - - public: - static const bool value = decltype(test(0))::value; - }; -#endif - -#if defined(CATCH_CONFIG_CPP11_IS_ENUM) - template::value - > - struct EnumStringMaker - { - static std::string convert( T const& ) { return unprintableString; } - }; - - template - struct EnumStringMaker - { - static std::string convert( T const& v ) - { - return ::Catch::toString( - static_cast::type>(v) - ); - } - }; -#endif - template - struct StringMakerBase { -#if defined(CATCH_CONFIG_CPP11_IS_ENUM) - template - static std::string convert( T const& v ) - { - return EnumStringMaker::convert( v ); - } -#else - template - static std::string convert( T const& ) { return unprintableString; } -#endif - }; - - template<> - struct StringMakerBase { - template - static std::string convert( T const& _value ) { - std::ostringstream oss; - oss << _value; - return oss.str(); - } - }; - - std::string rawMemoryToString( const void *object, std::size_t size ); - - template - inline std::string rawMemoryToString( const T& object ) { - return rawMemoryToString( &object, sizeof(object) ); - } - -} // end namespace Detail - -template -struct StringMaker : - Detail::StringMakerBase::value> {}; - -template -struct StringMaker { - template - static std::string convert( U* p ) { - if( !p ) - return "NULL"; - else - return Detail::rawMemoryToString( p ); - } -}; - -template -struct StringMaker { - static std::string convert( R C::* p ) { - if( !p ) - return "NULL"; - else - return Detail::rawMemoryToString( p ); - } -}; - -namespace Detail { - template - std::string rangeToString( InputIterator first, InputIterator last ); -} - -//template -//struct StringMaker > { -// static std::string convert( std::vector const& v ) { -// return Detail::rangeToString( v.begin(), v.end() ); -// } -//}; - -template -std::string toString( std::vector const& v ) { - return Detail::rangeToString( v.begin(), v.end() ); -} - -#ifdef CATCH_CONFIG_CPP11_TUPLE - -// toString for tuples -namespace TupleDetail { - template< - typename Tuple, - std::size_t N = 0, - bool = (N < std::tuple_size::value) - > - struct ElementPrinter { - static void print( const Tuple& tuple, std::ostream& os ) - { - os << ( N ? ", " : " " ) - << Catch::toString(std::get(tuple)); - ElementPrinter::print(tuple,os); - } - }; - - template< - typename Tuple, - std::size_t N - > - struct ElementPrinter { - static void print( const Tuple&, std::ostream& ) {} - }; - -} - -template -struct StringMaker> { - - static std::string convert( const std::tuple& tuple ) - { - std::ostringstream os; - os << '{'; - TupleDetail::ElementPrinter>::print( tuple, os ); - os << " }"; - return os.str(); - } -}; -#endif // CATCH_CONFIG_CPP11_TUPLE - -namespace Detail { - template - std::string makeString( T const& value ) { - return StringMaker::convert( value ); - } -} // end namespace Detail - -/// \brief converts any type to a string -/// -/// The default template forwards on to ostringstream - except when an -/// ostringstream overload does not exist - in which case it attempts to detect -/// that and writes {?}. -/// Overload (not specialise) this template for custom typs that you don't want -/// to provide an ostream overload for. -template -std::string toString( T const& value ) { - return StringMaker::convert( value ); -} - - namespace Detail { - template - std::string rangeToString( InputIterator first, InputIterator last ) { - std::ostringstream oss; - oss << "{ "; - if( first != last ) { - oss << Catch::toString( *first ); - for( ++first ; first != last ; ++first ) - oss << ", " << Catch::toString( *first ); - } - oss << " }"; - return oss.str(); - } -} - -} // end namespace Catch - -namespace Catch { - -template -class BinaryExpression; - -template -class MatchExpression; - -// Wraps the LHS of an expression and overloads comparison operators -// for also capturing those and RHS (if any) -template -class ExpressionLhs : public DecomposedExpression { -public: - ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ), m_truthy(false) {} - - ExpressionLhs& operator = ( const ExpressionLhs& ); - - template - BinaryExpression - operator == ( RhsT const& rhs ) { - return captureExpression( rhs ); - } - - template - BinaryExpression - operator != ( RhsT const& rhs ) { - return captureExpression( rhs ); - } - - template - BinaryExpression - operator < ( RhsT const& rhs ) { - return captureExpression( rhs ); - } - - template - BinaryExpression - operator > ( RhsT const& rhs ) { - return captureExpression( rhs ); - } - - template - BinaryExpression - operator <= ( RhsT const& rhs ) { - return captureExpression( rhs ); - } - - template - BinaryExpression - operator >= ( RhsT const& rhs ) { - return captureExpression( rhs ); - } - - BinaryExpression operator == ( bool rhs ) { - return captureExpression( rhs ); - } - - BinaryExpression operator != ( bool rhs ) { - return captureExpression( rhs ); - } - - void endExpression() { - m_truthy = m_lhs ? true : false; - m_rb - .setResultType( m_truthy ) - .endExpression( *this ); - } - - virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { - dest = Catch::toString( m_truthy ); - } - -private: - template - BinaryExpression captureExpression( RhsT& rhs ) const { - return BinaryExpression( m_rb, m_lhs, rhs ); - } - - template - BinaryExpression captureExpression( bool rhs ) const { - return BinaryExpression( m_rb, m_lhs, rhs ); - } - -private: - ResultBuilder& m_rb; - T m_lhs; - bool m_truthy; -}; - -template -class BinaryExpression : public DecomposedExpression { -public: - BinaryExpression( ResultBuilder& rb, LhsT lhs, RhsT rhs ) - : m_rb( rb ), m_lhs( lhs ), m_rhs( rhs ) {} - - BinaryExpression& operator = ( BinaryExpression& ); - - void endExpression() const { - m_rb - .setResultType( Internal::compare( m_lhs, m_rhs ) ) - .endExpression( *this ); - } - - virtual bool isBinaryExpression() const CATCH_OVERRIDE { - return true; - } - - virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { - std::string lhs = Catch::toString( m_lhs ); - std::string rhs = Catch::toString( m_rhs ); - char delim = lhs.size() + rhs.size() < 40 && - lhs.find('\n') == std::string::npos && - rhs.find('\n') == std::string::npos ? ' ' : '\n'; - dest.reserve( 7 + lhs.size() + rhs.size() ); - // 2 for spaces around operator - // 2 for operator - // 2 for parentheses (conditionally added later) - // 1 for negation (conditionally added later) - dest = lhs; - dest += delim; - dest += Internal::OperatorTraits::getName(); - dest += delim; - dest += rhs; - } - -private: - ResultBuilder& m_rb; - LhsT m_lhs; - RhsT m_rhs; -}; - -template -class MatchExpression : public DecomposedExpression { -public: - MatchExpression( ArgT arg, MatcherT matcher, char const* matcherString ) - : m_arg( arg ), m_matcher( matcher ), m_matcherString( matcherString ) {} - - virtual bool isBinaryExpression() const CATCH_OVERRIDE { - return true; - } - - virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { - std::string matcherAsString = m_matcher.toString(); - dest = Catch::toString( m_arg ); - dest += ' '; - if( matcherAsString == Detail::unprintableString ) - dest += m_matcherString; - else - dest += matcherAsString; - } - -private: - ArgT m_arg; - MatcherT m_matcher; - char const* m_matcherString; -}; - -} // end namespace Catch - - -namespace Catch { - - template - inline ExpressionLhs ResultBuilder::operator <= ( T const& operand ) { - return ExpressionLhs( *this, operand ); - } - - inline ExpressionLhs ResultBuilder::operator <= ( bool value ) { - return ExpressionLhs( *this, value ); - } - - template - inline void ResultBuilder::captureMatch( ArgT const& arg, MatcherT const& matcher, - char const* matcherString ) { - MatchExpression expr( arg, matcher, matcherString ); - setResultType( matcher.match( arg ) ); - endExpression( expr ); - } - -} // namespace Catch - -// #included from: catch_message.h -#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED - -#include - -namespace Catch { - - struct MessageInfo { - MessageInfo( std::string const& _macroName, - SourceLineInfo const& _lineInfo, - ResultWas::OfType _type ); - - std::string macroName; - SourceLineInfo lineInfo; - ResultWas::OfType type; - std::string message; - unsigned int sequence; - - bool operator == ( MessageInfo const& other ) const { - return sequence == other.sequence; - } - bool operator < ( MessageInfo const& other ) const { - return sequence < other.sequence; - } - private: - static unsigned int globalCount; - }; - - struct MessageBuilder { - MessageBuilder( std::string const& macroName, - SourceLineInfo const& lineInfo, - ResultWas::OfType type ) - : m_info( macroName, lineInfo, type ) - {} - - template - MessageBuilder& operator << ( T const& value ) { - m_stream << value; - return *this; - } - - MessageInfo m_info; - std::ostringstream m_stream; - }; - - class ScopedMessage { - public: - ScopedMessage( MessageBuilder const& builder ); - ScopedMessage( ScopedMessage const& other ); - ~ScopedMessage(); - - MessageInfo m_info; - }; - -} // end namespace Catch - -// #included from: catch_interfaces_capture.h -#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED - -#include - -namespace Catch { - - class TestCase; - class AssertionResult; - struct AssertionInfo; - struct SectionInfo; - struct SectionEndInfo; - struct MessageInfo; - class ScopedMessageBuilder; - struct Counts; - - struct IResultCapture { - - virtual ~IResultCapture(); - - virtual void assertionEnded( AssertionResult const& result ) = 0; - virtual bool sectionStarted( SectionInfo const& sectionInfo, - Counts& assertions ) = 0; - virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; - virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; - virtual void pushScopedMessage( MessageInfo const& message ) = 0; - virtual void popScopedMessage( MessageInfo const& message ) = 0; - - virtual std::string getCurrentTestName() const = 0; - virtual const AssertionResult* getLastResult() const = 0; - - virtual void exceptionEarlyReported() = 0; - - virtual void handleFatalErrorCondition( std::string const& message ) = 0; - }; - - IResultCapture& getResultCapture(); -} - -// #included from: catch_debugger.h -#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED - -// #included from: catch_platform.h -#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED - -#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) -# define CATCH_PLATFORM_MAC -#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) -# define CATCH_PLATFORM_IPHONE -#elif defined(linux) || defined(__linux) || defined(__linux__) -# define CATCH_PLATFORM_LINUX -#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) -# define CATCH_PLATFORM_WINDOWS -# if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) -# define CATCH_DEFINES_NOMINMAX -# endif -# if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) -# define CATCH_DEFINES_WIN32_LEAN_AND_MEAN -# endif -#endif - -#include - -namespace Catch{ - - bool isDebuggerActive(); - void writeToDebugConsole( std::string const& text ); -} - -#ifdef CATCH_PLATFORM_MAC - - // The following code snippet based on: - // http://cocoawithlove.com/2008/03/break-into-debugger.html - #if defined(__ppc64__) || defined(__ppc__) - #define CATCH_TRAP() \ - __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ - : : : "memory","r0","r3","r4" ) - #else - #define CATCH_TRAP() __asm__("int $3\n" : : ) - #endif - -#elif defined(CATCH_PLATFORM_LINUX) - // If we can use inline assembler, do it because this allows us to break - // directly at the location of the failing check instead of breaking inside - // raise() called from it, i.e. one stack frame below. - #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) - #define CATCH_TRAP() asm volatile ("int $3") - #else // Fall back to the generic way. - #include - - #define CATCH_TRAP() raise(SIGTRAP) - #endif -#elif defined(_MSC_VER) - #define CATCH_TRAP() __debugbreak() -#elif defined(__MINGW32__) - extern "C" __declspec(dllimport) void __stdcall DebugBreak(); - #define CATCH_TRAP() DebugBreak() -#endif - -#ifdef CATCH_TRAP - #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } -#else - #define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); -#endif - -// #included from: catch_interfaces_runner.h -#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED - -namespace Catch { - class TestCase; - - struct IRunner { - virtual ~IRunner(); - virtual bool aborting() const = 0; - }; -} - -#if defined(CATCH_CONFIG_FAST_COMPILE) -/////////////////////////////////////////////////////////////////////////////// -// We can speedup compilation significantly by breaking into debugger lower in -// the callstack, because then we don't have to expand CATCH_BREAK_INTO_DEBUGGER -// macro in each assertion -#define INTERNAL_CATCH_REACT( resultBuilder ) \ - resultBuilder.react(); - -/////////////////////////////////////////////////////////////////////////////// -// Another way to speed-up compilation is to omit local try-catch for REQUIRE* -// macros. -// This can potentially cause false negative, if the test code catches -// the exception before it propagates back up to the runner. -#define INTERNAL_CATCH_TEST_NO_TRY( macroName, resultDisposition, expr ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ - __catchResult.setExceptionGuard(); \ - CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - ( __catchResult <= expr ).endExpression(); \ - CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ - __catchResult.unsetExceptionGuard(); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::isTrue( false && static_cast( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look -// The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. - -#define INTERNAL_CHECK_THAT_NO_TRY( macroName, matcher, resultDisposition, arg ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \ - __catchResult.setExceptionGuard(); \ - __catchResult.captureMatch( arg, matcher, #matcher ); \ - __catchResult.unsetExceptionGuard(); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) - -#else -/////////////////////////////////////////////////////////////////////////////// -// In the event of a failure works out if the debugger needs to be invoked -// and/or an exception thrown and takes appropriate action. -// This needs to be done as a macro so the debugger will stop in the user -// source code rather than in Catch library code -#define INTERNAL_CATCH_REACT( resultBuilder ) \ - if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ - resultBuilder.react(); -#endif - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ - try { \ - CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - ( __catchResult <= expr ).endExpression(); \ - CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ - } \ - catch( ... ) { \ - __catchResult.useActiveException( resultDisposition ); \ - } \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::isTrue( false && static_cast( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look - // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_IF( macroName, resultDisposition, expr ) \ - INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \ - if( Catch::getResultCapture().getLastResult()->succeeded() ) - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, expr ) \ - INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \ - if( !Catch::getResultCapture().getLastResult()->succeeded() ) - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, expr ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ - try { \ - static_cast(expr); \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ - } \ - catch( ... ) { \ - __catchResult.useActiveException( resultDisposition ); \ - } \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, matcher, expr ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \ - if( __catchResult.allowThrows() ) \ - try { \ - static_cast(expr); \ - __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ - } \ - catch( ... ) { \ - __catchResult.captureExpectedException( matcher ); \ - } \ - else \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr ", " #exceptionType, resultDisposition ); \ - if( __catchResult.allowThrows() ) \ - try { \ - static_cast(expr); \ - __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ - } \ - catch( exceptionType ) { \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ - } \ - catch( ... ) { \ - __catchResult.useActiveException( resultDisposition ); \ - } \ - else \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) - -/////////////////////////////////////////////////////////////////////////////// -#ifdef CATCH_CONFIG_VARIADIC_MACROS - #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ - __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \ - __catchResult.captureResult( messageType ); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) -#else - #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ - __catchResult << log + ::Catch::StreamEndStop(); \ - __catchResult.captureResult( messageType ); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) -#endif - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_INFO( macroName, log ) \ - Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log; - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \ - try { \ - __catchResult.captureMatch( arg, matcher, #matcher ); \ - } catch( ... ) { \ - __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \ - } \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) - -// #included from: internal/catch_section.h -#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED - -// #included from: catch_section_info.h -#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED - -// #included from: catch_totals.hpp -#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED - -#include - -namespace Catch { - - struct Counts { - Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {} - - Counts operator - ( Counts const& other ) const { - Counts diff; - diff.passed = passed - other.passed; - diff.failed = failed - other.failed; - diff.failedButOk = failedButOk - other.failedButOk; - return diff; - } - Counts& operator += ( Counts const& other ) { - passed += other.passed; - failed += other.failed; - failedButOk += other.failedButOk; - return *this; - } - - std::size_t total() const { - return passed + failed + failedButOk; - } - bool allPassed() const { - return failed == 0 && failedButOk == 0; - } - bool allOk() const { - return failed == 0; - } - - std::size_t passed; - std::size_t failed; - std::size_t failedButOk; - }; - - struct Totals { - - Totals operator - ( Totals const& other ) const { - Totals diff; - diff.assertions = assertions - other.assertions; - diff.testCases = testCases - other.testCases; - return diff; - } - - Totals delta( Totals const& prevTotals ) const { - Totals diff = *this - prevTotals; - if( diff.assertions.failed > 0 ) - ++diff.testCases.failed; - else if( diff.assertions.failedButOk > 0 ) - ++diff.testCases.failedButOk; - else - ++diff.testCases.passed; - return diff; - } - - Totals& operator += ( Totals const& other ) { - assertions += other.assertions; - testCases += other.testCases; - return *this; - } - - Counts assertions; - Counts testCases; - }; -} - -#include - -namespace Catch { - - struct SectionInfo { - SectionInfo - ( SourceLineInfo const& _lineInfo, - std::string const& _name, - std::string const& _description = std::string() ); - - std::string name; - std::string description; - SourceLineInfo lineInfo; - }; - - struct SectionEndInfo { - SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ) - : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) - {} - - SectionInfo sectionInfo; - Counts prevAssertions; - double durationInSeconds; - }; - -} // end namespace Catch - -// #included from: catch_timer.h -#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED - -#ifdef CATCH_PLATFORM_WINDOWS -typedef unsigned long long uint64_t; -#else -#include -#endif - -namespace Catch { - - class Timer { - public: - Timer() : m_ticks( 0 ) {} - void start(); - unsigned int getElapsedMicroseconds() const; - unsigned int getElapsedMilliseconds() const; - double getElapsedSeconds() const; - - private: - uint64_t m_ticks; - }; - -} // namespace Catch - -#include - -namespace Catch { - - class Section : NonCopyable { - public: - Section( SectionInfo const& info ); - ~Section(); - - // This indicates whether the section should be executed or not - operator bool() const; - - private: - SectionInfo m_info; - - std::string m_name; - Counts m_assertions; - bool m_sectionIncluded; - Timer m_timer; - }; - -} // end namespace Catch - -#ifdef CATCH_CONFIG_VARIADIC_MACROS - #define INTERNAL_CATCH_SECTION( ... ) \ - if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) -#else - #define INTERNAL_CATCH_SECTION( name, desc ) \ - if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) ) -#endif - -// #included from: internal/catch_generators.hpp -#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED - -#include -#include -#include - -namespace Catch { - -template -struct IGenerator { - virtual ~IGenerator() {} - virtual T getValue( std::size_t index ) const = 0; - virtual std::size_t size () const = 0; -}; - -template -class BetweenGenerator : public IGenerator { -public: - BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){} - - virtual T getValue( std::size_t index ) const { - return m_from+static_cast( index ); - } - - virtual std::size_t size() const { - return static_cast( 1+m_to-m_from ); - } - -private: - - T m_from; - T m_to; -}; - -template -class ValuesGenerator : public IGenerator { -public: - ValuesGenerator(){} - - void add( T value ) { - m_values.push_back( value ); - } - - virtual T getValue( std::size_t index ) const { - return m_values[index]; - } - - virtual std::size_t size() const { - return m_values.size(); - } - -private: - std::vector m_values; -}; - -template -class CompositeGenerator { -public: - CompositeGenerator() : m_totalSize( 0 ) {} - - // *** Move semantics, similar to auto_ptr *** - CompositeGenerator( CompositeGenerator& other ) - : m_fileInfo( other.m_fileInfo ), - m_totalSize( 0 ) - { - move( other ); - } - - CompositeGenerator& setFileInfo( const char* fileInfo ) { - m_fileInfo = fileInfo; - return *this; - } - - ~CompositeGenerator() { - deleteAll( m_composed ); - } - - operator T () const { - size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize ); - - typename std::vector*>::const_iterator it = m_composed.begin(); - typename std::vector*>::const_iterator itEnd = m_composed.end(); - for( size_t index = 0; it != itEnd; ++it ) - { - const IGenerator* generator = *it; - if( overallIndex >= index && overallIndex < index + generator->size() ) - { - return generator->getValue( overallIndex-index ); - } - index += generator->size(); - } - CATCH_INTERNAL_ERROR( "Indexed past end of generated range" ); - return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so - } - - void add( const IGenerator* generator ) { - m_totalSize += generator->size(); - m_composed.push_back( generator ); - } - - CompositeGenerator& then( CompositeGenerator& other ) { - move( other ); - return *this; - } - - CompositeGenerator& then( T value ) { - ValuesGenerator* valuesGen = new ValuesGenerator(); - valuesGen->add( value ); - add( valuesGen ); - return *this; - } - -private: - - void move( CompositeGenerator& other ) { - m_composed.insert( m_composed.end(), other.m_composed.begin(), other.m_composed.end() ); - m_totalSize += other.m_totalSize; - other.m_composed.clear(); - } - - std::vector*> m_composed; - std::string m_fileInfo; - size_t m_totalSize; -}; - -namespace Generators -{ - template - CompositeGenerator between( T from, T to ) { - CompositeGenerator generators; - generators.add( new BetweenGenerator( from, to ) ); - return generators; - } - - template - CompositeGenerator values( T val1, T val2 ) { - CompositeGenerator generators; - ValuesGenerator* valuesGen = new ValuesGenerator(); - valuesGen->add( val1 ); - valuesGen->add( val2 ); - generators.add( valuesGen ); - return generators; - } - - template - CompositeGenerator values( T val1, T val2, T val3 ){ - CompositeGenerator generators; - ValuesGenerator* valuesGen = new ValuesGenerator(); - valuesGen->add( val1 ); - valuesGen->add( val2 ); - valuesGen->add( val3 ); - generators.add( valuesGen ); - return generators; - } - - template - CompositeGenerator values( T val1, T val2, T val3, T val4 ) { - CompositeGenerator generators; - ValuesGenerator* valuesGen = new ValuesGenerator(); - valuesGen->add( val1 ); - valuesGen->add( val2 ); - valuesGen->add( val3 ); - valuesGen->add( val4 ); - generators.add( valuesGen ); - return generators; - } - -} // end namespace Generators - -using namespace Generators; - -} // end namespace Catch - -#define INTERNAL_CATCH_LINESTR2( line ) #line -#define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line ) - -#define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" ) - -// #included from: internal/catch_interfaces_exception.h -#define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED - -#include -#include - -// #included from: catch_interfaces_registry_hub.h -#define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED - -#include - -namespace Catch { - - class TestCase; - struct ITestCaseRegistry; - struct IExceptionTranslatorRegistry; - struct IExceptionTranslator; - struct IReporterRegistry; - struct IReporterFactory; - struct ITagAliasRegistry; - - struct IRegistryHub { - virtual ~IRegistryHub(); - - virtual IReporterRegistry const& getReporterRegistry() const = 0; - virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; - virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; - - virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; - }; - - struct IMutableRegistryHub { - virtual ~IMutableRegistryHub(); - virtual void registerReporter( std::string const& name, Ptr const& factory ) = 0; - virtual void registerListener( Ptr const& factory ) = 0; - virtual void registerTest( TestCase const& testInfo ) = 0; - virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; - virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; - }; - - IRegistryHub& getRegistryHub(); - IMutableRegistryHub& getMutableRegistryHub(); - void cleanUp(); - std::string translateActiveException(); - -} - -namespace Catch { - - typedef std::string(*exceptionTranslateFunction)(); - - struct IExceptionTranslator; - typedef std::vector ExceptionTranslators; - - struct IExceptionTranslator { - virtual ~IExceptionTranslator(); - virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0; - }; - - struct IExceptionTranslatorRegistry { - virtual ~IExceptionTranslatorRegistry(); - - virtual std::string translateActiveException() const = 0; - }; - - class ExceptionTranslatorRegistrar { - template - class ExceptionTranslator : public IExceptionTranslator { - public: - - ExceptionTranslator( std::string(*translateFunction)( T& ) ) - : m_translateFunction( translateFunction ) - {} - - virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const CATCH_OVERRIDE { - try { - if( it == itEnd ) - throw; - else - return (*it)->translate( it+1, itEnd ); - } - catch( T& ex ) { - return m_translateFunction( ex ); - } - } - - protected: - std::string(*m_translateFunction)( T& ); - }; - - public: - template - ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) { - getMutableRegistryHub().registerTranslator - ( new ExceptionTranslator( translateFunction ) ); - } - }; -} - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \ - static std::string translatorName( signature ); \ - namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\ - static std::string translatorName( signature ) - -#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) - -// #included from: internal/catch_approx.hpp -#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED - -#include -#include - -#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) -#include -#endif - -namespace Catch { -namespace Detail { - - class Approx { - public: - explicit Approx ( double value ) - : m_epsilon( std::numeric_limits::epsilon()*100 ), - m_margin( 0.0 ), - m_scale( 1.0 ), - m_value( value ) - {} - - Approx( Approx const& other ) - : m_epsilon( other.m_epsilon ), - m_margin( other.m_margin ), - m_scale( other.m_scale ), - m_value( other.m_value ) - {} - - static Approx custom() { - return Approx( 0 ); - } - - Approx operator()( double value ) { - Approx approx( value ); - approx.epsilon( m_epsilon ); - approx.margin( m_margin ); - approx.scale( m_scale ); - return approx; - } - -#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) - - template ::value>::type> - explicit Approx( T value ): Approx(static_cast(value)) - {} - - template ::value>::type> - friend bool operator == ( const T& lhs, Approx const& rhs ) { - // Thanks to Richard Harris for his help refining this formula - auto lhs_v = double(lhs); - bool relativeOK = std::fabs(lhs_v - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + (std::max)(std::fabs(lhs_v), std::fabs(rhs.m_value))); - if (relativeOK) { - return true; - } - return std::fabs(lhs_v - rhs.m_value) < rhs.m_margin; - } - - template ::value>::type> - friend bool operator == ( Approx const& lhs, const T& rhs ) { - return operator==( rhs, lhs ); - } - - template ::value>::type> - friend bool operator != ( T lhs, Approx const& rhs ) { - return !operator==( lhs, rhs ); - } - - template ::value>::type> - friend bool operator != ( Approx const& lhs, T rhs ) { - return !operator==( rhs, lhs ); - } - - template ::value>::type> - friend bool operator <= ( T lhs, Approx const& rhs ) { - return double(lhs) < rhs.m_value || lhs == rhs; - } - - template ::value>::type> - friend bool operator <= ( Approx const& lhs, T rhs ) { - return lhs.m_value < double(rhs) || lhs == rhs; - } - - template ::value>::type> - friend bool operator >= ( T lhs, Approx const& rhs ) { - return double(lhs) > rhs.m_value || lhs == rhs; - } - - template ::value>::type> - friend bool operator >= ( Approx const& lhs, T rhs ) { - return lhs.m_value > double(rhs) || lhs == rhs; - } -#else - friend bool operator == ( double lhs, Approx const& rhs ) { - // Thanks to Richard Harris for his help refining this formula - bool relativeOK = std::fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs), std::fabs(rhs.m_value) ) ); - if (relativeOK) { - return true; - } - return std::fabs(lhs - rhs.m_value) < rhs.m_margin; - } - - friend bool operator == ( Approx const& lhs, double rhs ) { - return operator==( rhs, lhs ); - } - - friend bool operator != ( double lhs, Approx const& rhs ) { - return !operator==( lhs, rhs ); - } - - friend bool operator != ( Approx const& lhs, double rhs ) { - return !operator==( rhs, lhs ); - } - - friend bool operator <= ( double lhs, Approx const& rhs ) { - return lhs < rhs.m_value || lhs == rhs; - } - - friend bool operator <= ( Approx const& lhs, double rhs ) { - return lhs.m_value < rhs || lhs == rhs; - } - - friend bool operator >= ( double lhs, Approx const& rhs ) { - return lhs > rhs.m_value || lhs == rhs; - } - - friend bool operator >= ( Approx const& lhs, double rhs ) { - return lhs.m_value > rhs || lhs == rhs; - } -#endif - - Approx& epsilon( double newEpsilon ) { - m_epsilon = newEpsilon; - return *this; - } - - Approx& margin( double newMargin ) { - m_margin = newMargin; - return *this; - } - - Approx& scale( double newScale ) { - m_scale = newScale; - return *this; - } - - std::string toString() const { - std::ostringstream oss; - oss << "Approx( " << Catch::toString( m_value ) << " )"; - return oss.str(); - } - - private: - double m_epsilon; - double m_margin; - double m_scale; - double m_value; - }; -} - -template<> -inline std::string toString( Detail::Approx const& value ) { - return value.toString(); -} - -} // end namespace Catch - -// #included from: internal/catch_matchers_string.h -#define TWOBLUECUBES_CATCH_MATCHERS_STRING_H_INCLUDED - -namespace Catch { -namespace Matchers { - - namespace StdString { - - struct CasedString - { - CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ); - std::string adjustString( std::string const& str ) const; - std::string caseSensitivitySuffix() const; - - CaseSensitive::Choice m_caseSensitivity; - std::string m_str; - }; - - struct StringMatcherBase : MatcherBase { - StringMatcherBase( std::string const& operation, CasedString const& comparator ); - virtual std::string describe() const CATCH_OVERRIDE; - - CasedString m_comparator; - std::string m_operation; - }; - - struct EqualsMatcher : StringMatcherBase { - EqualsMatcher( CasedString const& comparator ); - virtual bool match( std::string const& source ) const CATCH_OVERRIDE; - }; - struct ContainsMatcher : StringMatcherBase { - ContainsMatcher( CasedString const& comparator ); - virtual bool match( std::string const& source ) const CATCH_OVERRIDE; - }; - struct StartsWithMatcher : StringMatcherBase { - StartsWithMatcher( CasedString const& comparator ); - virtual bool match( std::string const& source ) const CATCH_OVERRIDE; - }; - struct EndsWithMatcher : StringMatcherBase { - EndsWithMatcher( CasedString const& comparator ); - virtual bool match( std::string const& source ) const CATCH_OVERRIDE; - }; - - } // namespace StdString - - // The following functions create the actual matcher objects. - // This allows the types to be inferred - - StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); - StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); - StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); - StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); - -} // namespace Matchers -} // namespace Catch - -// #included from: internal/catch_matchers_vector.h -#define TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED - -namespace Catch { -namespace Matchers { - - namespace Vector { - - template - struct ContainsElementMatcher : MatcherBase, T> { - - ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} - - bool match(std::vector const &v) const CATCH_OVERRIDE { - return std::find(v.begin(), v.end(), m_comparator) != v.end(); - } - - virtual std::string describe() const CATCH_OVERRIDE { - return "Contains: " + Catch::toString( m_comparator ); - } - - T const& m_comparator; - }; - - template - struct ContainsMatcher : MatcherBase, std::vector > { - - ContainsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} - - bool match(std::vector const &v) const CATCH_OVERRIDE { - // !TBD: see note in EqualsMatcher - if (m_comparator.size() > v.size()) - return false; - for (size_t i = 0; i < m_comparator.size(); ++i) - if (std::find(v.begin(), v.end(), m_comparator[i]) == v.end()) - return false; - return true; - } - virtual std::string describe() const CATCH_OVERRIDE { - return "Contains: " + Catch::toString( m_comparator ); - } - - std::vector const& m_comparator; - }; - - template - struct EqualsMatcher : MatcherBase, std::vector > { - - EqualsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} - - bool match(std::vector const &v) const CATCH_OVERRIDE { - // !TBD: This currently works if all elements can be compared using != - // - a more general approach would be via a compare template that defaults - // to using !=. but could be specialised for, e.g. std::vector etc - // - then just call that directly - if (m_comparator.size() != v.size()) - return false; - for (size_t i = 0; i < v.size(); ++i) - if (m_comparator[i] != v[i]) - return false; - return true; - } - virtual std::string describe() const CATCH_OVERRIDE { - return "Equals: " + Catch::toString( m_comparator ); - } - std::vector const& m_comparator; - }; - - } // namespace Vector - - // The following functions create the actual matcher objects. - // This allows the types to be inferred - - template - Vector::ContainsMatcher Contains( std::vector const& comparator ) { - return Vector::ContainsMatcher( comparator ); - } - - template - Vector::ContainsElementMatcher VectorContains( T const& comparator ) { - return Vector::ContainsElementMatcher( comparator ); - } - - template - Vector::EqualsMatcher Equals( std::vector const& comparator ) { - return Vector::EqualsMatcher( comparator ); - } - -} // namespace Matchers -} // namespace Catch - -// #included from: internal/catch_interfaces_tag_alias_registry.h -#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED - -// #included from: catch_tag_alias.h -#define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED - -#include - -namespace Catch { - - struct TagAlias { - TagAlias( std::string const& _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {} - - std::string tag; - SourceLineInfo lineInfo; - }; - - struct RegistrarForTagAliases { - RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); - }; - -} // end namespace Catch - -#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } -// #included from: catch_option.hpp -#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED - -namespace Catch { - - // An optional type - template - class Option { - public: - Option() : nullableValue( CATCH_NULL ) {} - Option( T const& _value ) - : nullableValue( new( storage ) T( _value ) ) - {} - Option( Option const& _other ) - : nullableValue( _other ? new( storage ) T( *_other ) : CATCH_NULL ) - {} - - ~Option() { - reset(); - } - - Option& operator= ( Option const& _other ) { - if( &_other != this ) { - reset(); - if( _other ) - nullableValue = new( storage ) T( *_other ); - } - return *this; - } - Option& operator = ( T const& _value ) { - reset(); - nullableValue = new( storage ) T( _value ); - return *this; - } - - void reset() { - if( nullableValue ) - nullableValue->~T(); - nullableValue = CATCH_NULL; - } - - T& operator*() { return *nullableValue; } - T const& operator*() const { return *nullableValue; } - T* operator->() { return nullableValue; } - const T* operator->() const { return nullableValue; } - - T valueOr( T const& defaultValue ) const { - return nullableValue ? *nullableValue : defaultValue; - } - - bool some() const { return nullableValue != CATCH_NULL; } - bool none() const { return nullableValue == CATCH_NULL; } - - bool operator !() const { return nullableValue == CATCH_NULL; } - operator SafeBool::type() const { - return SafeBool::makeSafe( some() ); - } - - private: - T* nullableValue; - char storage[sizeof(T)]; - }; - -} // end namespace Catch - -namespace Catch { - - struct ITagAliasRegistry { - virtual ~ITagAliasRegistry(); - virtual Option find( std::string const& alias ) const = 0; - virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; - - static ITagAliasRegistry const& get(); - }; - -} // end namespace Catch - -// These files are included here so the single_include script doesn't put them -// in the conditionally compiled sections -// #included from: internal/catch_test_case_info.h -#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED - -#include -#include - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -#endif - -namespace Catch { - - struct ITestCase; - - struct TestCaseInfo { - enum SpecialProperties{ - None = 0, - IsHidden = 1 << 1, - ShouldFail = 1 << 2, - MayFail = 1 << 3, - Throws = 1 << 4, - NonPortable = 1 << 5 - }; - - TestCaseInfo( std::string const& _name, - std::string const& _className, - std::string const& _description, - std::set const& _tags, - SourceLineInfo const& _lineInfo ); - - TestCaseInfo( TestCaseInfo const& other ); - - friend void setTags( TestCaseInfo& testCaseInfo, std::set const& tags ); - - bool isHidden() const; - bool throws() const; - bool okToFail() const; - bool expectedToFail() const; - - std::string name; - std::string className; - std::string description; - std::set tags; - std::set lcaseTags; - std::string tagsAsString; - SourceLineInfo lineInfo; - SpecialProperties properties; - }; - - class TestCase : public TestCaseInfo { - public: - - TestCase( ITestCase* testCase, TestCaseInfo const& info ); - TestCase( TestCase const& other ); - - TestCase withName( std::string const& _newName ) const; - - void invoke() const; - - TestCaseInfo const& getTestCaseInfo() const; - - void swap( TestCase& other ); - bool operator == ( TestCase const& other ) const; - bool operator < ( TestCase const& other ) const; - TestCase& operator = ( TestCase const& other ); - - private: - Ptr test; - }; - - TestCase makeTestCase( ITestCase* testCase, - std::string const& className, - std::string const& name, - std::string const& description, - SourceLineInfo const& lineInfo ); -} - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - - -#ifdef __OBJC__ -// #included from: internal/catch_objc.hpp -#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED - -#import - -#include - -// NB. Any general catch headers included here must be included -// in catch.hpp first to make sure they are included by the single -// header for non obj-usage - -/////////////////////////////////////////////////////////////////////////////// -// This protocol is really only here for (self) documenting purposes, since -// all its methods are optional. -@protocol OcFixture - -@optional - --(void) setUp; --(void) tearDown; - -@end - -namespace Catch { - - class OcMethod : public SharedImpl { - - public: - OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} - - virtual void invoke() const { - id obj = [[m_cls alloc] init]; - - performOptionalSelector( obj, @selector(setUp) ); - performOptionalSelector( obj, m_sel ); - performOptionalSelector( obj, @selector(tearDown) ); - - arcSafeRelease( obj ); - } - private: - virtual ~OcMethod() {} - - Class m_cls; - SEL m_sel; - }; - - namespace Detail{ - - inline std::string getAnnotation( Class cls, - std::string const& annotationName, - std::string const& testCaseName ) { - NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; - SEL sel = NSSelectorFromString( selStr ); - arcSafeRelease( selStr ); - id value = performOptionalSelector( cls, sel ); - if( value ) - return [(NSString*)value UTF8String]; - return ""; - } - } - - inline size_t registerTestMethods() { - size_t noTestMethods = 0; - int noClasses = objc_getClassList( CATCH_NULL, 0 ); - - Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); - objc_getClassList( classes, noClasses ); - - for( int c = 0; c < noClasses; c++ ) { - Class cls = classes[c]; - { - u_int count; - Method* methods = class_copyMethodList( cls, &count ); - for( u_int m = 0; m < count ; m++ ) { - SEL selector = method_getName(methods[m]); - std::string methodName = sel_getName(selector); - if( startsWith( methodName, "Catch_TestCase_" ) ) { - std::string testCaseName = methodName.substr( 15 ); - std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); - std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); - const char* className = class_getName( cls ); - - getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) ); - noTestMethods++; - } - } - free(methods); - } - } - return noTestMethods; - } - - namespace Matchers { - namespace Impl { - namespace NSStringMatchers { - - struct StringHolder : MatcherBase{ - StringHolder( NSString* substr ) : m_substr( [substr copy] ){} - StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} - StringHolder() { - arcSafeRelease( m_substr ); - } - - virtual bool match( NSString* arg ) const CATCH_OVERRIDE { - return false; - } - - NSString* m_substr; - }; - - struct Equals : StringHolder { - Equals( NSString* substr ) : StringHolder( substr ){} - - virtual bool match( NSString* str ) const CATCH_OVERRIDE { - return (str != nil || m_substr == nil ) && - [str isEqualToString:m_substr]; - } - - virtual std::string describe() const CATCH_OVERRIDE { - return "equals string: " + Catch::toString( m_substr ); - } - }; - - struct Contains : StringHolder { - Contains( NSString* substr ) : StringHolder( substr ){} - - virtual bool match( NSString* str ) const { - return (str != nil || m_substr == nil ) && - [str rangeOfString:m_substr].location != NSNotFound; - } - - virtual std::string describe() const CATCH_OVERRIDE { - return "contains string: " + Catch::toString( m_substr ); - } - }; - - struct StartsWith : StringHolder { - StartsWith( NSString* substr ) : StringHolder( substr ){} - - virtual bool match( NSString* str ) const { - return (str != nil || m_substr == nil ) && - [str rangeOfString:m_substr].location == 0; - } - - virtual std::string describe() const CATCH_OVERRIDE { - return "starts with: " + Catch::toString( m_substr ); - } - }; - struct EndsWith : StringHolder { - EndsWith( NSString* substr ) : StringHolder( substr ){} - - virtual bool match( NSString* str ) const { - return (str != nil || m_substr == nil ) && - [str rangeOfString:m_substr].location == [str length] - [m_substr length]; - } - - virtual std::string describe() const CATCH_OVERRIDE { - return "ends with: " + Catch::toString( m_substr ); - } - }; - - } // namespace NSStringMatchers - } // namespace Impl - - inline Impl::NSStringMatchers::Equals - Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); } - - inline Impl::NSStringMatchers::Contains - Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); } - - inline Impl::NSStringMatchers::StartsWith - StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); } - - inline Impl::NSStringMatchers::EndsWith - EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); } - - } // namespace Matchers - - using namespace Matchers; - -} // namespace Catch - -/////////////////////////////////////////////////////////////////////////////// -#define OC_TEST_CASE( name, desc )\ -+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \ -{\ -return @ name; \ -}\ -+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \ -{ \ -return @ desc; \ -} \ --(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test ) - -#endif - -#ifdef CATCH_IMPL - -// !TBD: Move the leak detector code into a separate header -#ifdef CATCH_CONFIG_WINDOWS_CRTDBG -#include -class LeakDetector { -public: - LeakDetector() { - int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); - flag |= _CRTDBG_LEAK_CHECK_DF; - flag |= _CRTDBG_ALLOC_MEM_DF; - _CrtSetDbgFlag(flag); - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); - _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); - // Change this to leaking allocation's number to break there - _CrtSetBreakAlloc(-1); - } -}; -#else -class LeakDetector {}; -#endif - -LeakDetector leakDetector; - -// #included from: internal/catch_impl.hpp -#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED - -// Collect all the implementation files together here -// These are the equivalent of what would usually be cpp files - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wweak-vtables" -#endif - -// #included from: ../catch_session.hpp -#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED - -// #included from: internal/catch_commandline.hpp -#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED - -// #included from: catch_config.hpp -#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED - -// #included from: catch_test_spec_parser.hpp -#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -#endif - -// #included from: catch_test_spec.hpp -#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -#endif - -// #included from: catch_wildcard_pattern.hpp -#define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED - -#include - -namespace Catch -{ - class WildcardPattern { - enum WildcardPosition { - NoWildcard = 0, - WildcardAtStart = 1, - WildcardAtEnd = 2, - WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd - }; - - public: - - WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ) - : m_caseSensitivity( caseSensitivity ), - m_wildcard( NoWildcard ), - m_pattern( adjustCase( pattern ) ) - { - if( startsWith( m_pattern, '*' ) ) { - m_pattern = m_pattern.substr( 1 ); - m_wildcard = WildcardAtStart; - } - if( endsWith( m_pattern, '*' ) ) { - m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); - m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); - } - } - virtual ~WildcardPattern(); - virtual bool matches( std::string const& str ) const { - switch( m_wildcard ) { - case NoWildcard: - return m_pattern == adjustCase( str ); - case WildcardAtStart: - return endsWith( adjustCase( str ), m_pattern ); - case WildcardAtEnd: - return startsWith( adjustCase( str ), m_pattern ); - case WildcardAtBothEnds: - return contains( adjustCase( str ), m_pattern ); - } - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunreachable-code" -#endif - throw std::logic_error( "Unknown enum" ); -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - } - private: - std::string adjustCase( std::string const& str ) const { - return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; - } - CaseSensitive::Choice m_caseSensitivity; - WildcardPosition m_wildcard; - std::string m_pattern; - }; -} - -#include -#include - -namespace Catch { - - class TestSpec { - struct Pattern : SharedImpl<> { - virtual ~Pattern(); - virtual bool matches( TestCaseInfo const& testCase ) const = 0; - }; - class NamePattern : public Pattern { - public: - NamePattern( std::string const& name ) - : m_wildcardPattern( toLower( name ), CaseSensitive::No ) - {} - virtual ~NamePattern(); - virtual bool matches( TestCaseInfo const& testCase ) const { - return m_wildcardPattern.matches( toLower( testCase.name ) ); - } - private: - WildcardPattern m_wildcardPattern; - }; - - class TagPattern : public Pattern { - public: - TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} - virtual ~TagPattern(); - virtual bool matches( TestCaseInfo const& testCase ) const { - return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end(); - } - private: - std::string m_tag; - }; - - class ExcludedPattern : public Pattern { - public: - ExcludedPattern( Ptr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} - virtual ~ExcludedPattern(); - virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } - private: - Ptr m_underlyingPattern; - }; - - struct Filter { - std::vector > m_patterns; - - bool matches( TestCaseInfo const& testCase ) const { - // All patterns in a filter must match for the filter to be a match - for( std::vector >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) { - if( !(*it)->matches( testCase ) ) - return false; - } - return true; - } - }; - - public: - bool hasFilters() const { - return !m_filters.empty(); - } - bool matches( TestCaseInfo const& testCase ) const { - // A TestSpec matches if any filter matches - for( std::vector::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it ) - if( it->matches( testCase ) ) - return true; - return false; - } - - private: - std::vector m_filters; - - friend class TestSpecParser; - }; -} - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -namespace Catch { - - class TestSpecParser { - enum Mode{ None, Name, QuotedName, Tag, EscapedName }; - Mode m_mode; - bool m_exclusion; - std::size_t m_start, m_pos; - std::string m_arg; - std::vector m_escapeChars; - TestSpec::Filter m_currentFilter; - TestSpec m_testSpec; - ITagAliasRegistry const* m_tagAliases; - - public: - TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} - - TestSpecParser& parse( std::string const& arg ) { - m_mode = None; - m_exclusion = false; - m_start = std::string::npos; - m_arg = m_tagAliases->expandAliases( arg ); - m_escapeChars.clear(); - for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) - visitChar( m_arg[m_pos] ); - if( m_mode == Name ) - addPattern(); - return *this; - } - TestSpec testSpec() { - addFilter(); - return m_testSpec; - } - private: - void visitChar( char c ) { - if( m_mode == None ) { - switch( c ) { - case ' ': return; - case '~': m_exclusion = true; return; - case '[': return startNewMode( Tag, ++m_pos ); - case '"': return startNewMode( QuotedName, ++m_pos ); - case '\\': return escape(); - default: startNewMode( Name, m_pos ); break; - } - } - if( m_mode == Name ) { - if( c == ',' ) { - addPattern(); - addFilter(); - } - else if( c == '[' ) { - if( subString() == "exclude:" ) - m_exclusion = true; - else - addPattern(); - startNewMode( Tag, ++m_pos ); - } - else if( c == '\\' ) - escape(); - } - else if( m_mode == EscapedName ) - m_mode = Name; - else if( m_mode == QuotedName && c == '"' ) - addPattern(); - else if( m_mode == Tag && c == ']' ) - addPattern(); - } - void startNewMode( Mode mode, std::size_t start ) { - m_mode = mode; - m_start = start; - } - void escape() { - if( m_mode == None ) - m_start = m_pos; - m_mode = EscapedName; - m_escapeChars.push_back( m_pos ); - } - std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); } - template - void addPattern() { - std::string token = subString(); - for( size_t i = 0; i < m_escapeChars.size(); ++i ) - token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 ); - m_escapeChars.clear(); - if( startsWith( token, "exclude:" ) ) { - m_exclusion = true; - token = token.substr( 8 ); - } - if( !token.empty() ) { - Ptr pattern = new T( token ); - if( m_exclusion ) - pattern = new TestSpec::ExcludedPattern( pattern ); - m_currentFilter.m_patterns.push_back( pattern ); - } - m_exclusion = false; - m_mode = None; - } - void addFilter() { - if( !m_currentFilter.m_patterns.empty() ) { - m_testSpec.m_filters.push_back( m_currentFilter ); - m_currentFilter = TestSpec::Filter(); - } - } - }; - inline TestSpec parseTestSpec( std::string const& arg ) { - return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); - } - -} // namespace Catch - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -// #included from: catch_interfaces_config.h -#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED - -#include -#include -#include - -namespace Catch { - - struct Verbosity { enum Level { - NoOutput = 0, - Quiet, - Normal - }; }; - - struct WarnAbout { enum What { - Nothing = 0x00, - NoAssertions = 0x01 - }; }; - - struct ShowDurations { enum OrNot { - DefaultForReporter, - Always, - Never - }; }; - struct RunTests { enum InWhatOrder { - InDeclarationOrder, - InLexicographicalOrder, - InRandomOrder - }; }; - struct UseColour { enum YesOrNo { - Auto, - Yes, - No - }; }; - - class TestSpec; - - struct IConfig : IShared { - - virtual ~IConfig(); - - virtual bool allowThrows() const = 0; - virtual std::ostream& stream() const = 0; - virtual std::string name() const = 0; - virtual bool includeSuccessfulResults() const = 0; - virtual bool shouldDebugBreak() const = 0; - virtual bool warnAboutMissingAssertions() const = 0; - virtual int abortAfter() const = 0; - virtual bool showInvisibles() const = 0; - virtual ShowDurations::OrNot showDurations() const = 0; - virtual TestSpec const& testSpec() const = 0; - virtual RunTests::InWhatOrder runOrder() const = 0; - virtual unsigned int rngSeed() const = 0; - virtual UseColour::YesOrNo useColour() const = 0; - virtual std::vector const& getSectionsToRun() const = 0; - - }; -} - -// #included from: catch_stream.h -#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED - -// #included from: catch_streambuf.h -#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED - -#include - -namespace Catch { - - class StreamBufBase : public std::streambuf { - public: - virtual ~StreamBufBase() CATCH_NOEXCEPT; - }; -} - -#include -#include -#include -#include - -namespace Catch { - - std::ostream& cout(); - std::ostream& cerr(); - - struct IStream { - virtual ~IStream() CATCH_NOEXCEPT; - virtual std::ostream& stream() const = 0; - }; - - class FileStream : public IStream { - mutable std::ofstream m_ofs; - public: - FileStream( std::string const& filename ); - virtual ~FileStream() CATCH_NOEXCEPT; - public: // IStream - virtual std::ostream& stream() const CATCH_OVERRIDE; - }; - - class CoutStream : public IStream { - mutable std::ostream m_os; - public: - CoutStream(); - virtual ~CoutStream() CATCH_NOEXCEPT; - - public: // IStream - virtual std::ostream& stream() const CATCH_OVERRIDE; - }; - - class DebugOutStream : public IStream { - CATCH_AUTO_PTR( StreamBufBase ) m_streamBuf; - mutable std::ostream m_os; - public: - DebugOutStream(); - virtual ~DebugOutStream() CATCH_NOEXCEPT; - - public: // IStream - virtual std::ostream& stream() const CATCH_OVERRIDE; - }; -} - -#include -#include -#include -#include - -#ifndef CATCH_CONFIG_CONSOLE_WIDTH -#define CATCH_CONFIG_CONSOLE_WIDTH 80 -#endif - -namespace Catch { - - struct ConfigData { - - ConfigData() - : listTests( false ), - listTags( false ), - listReporters( false ), - listTestNamesOnly( false ), - showSuccessfulTests( false ), - shouldDebugBreak( false ), - noThrow( false ), - showHelp( false ), - showInvisibles( false ), - filenamesAsTags( false ), - abortAfter( -1 ), - rngSeed( 0 ), - verbosity( Verbosity::Normal ), - warnings( WarnAbout::Nothing ), - showDurations( ShowDurations::DefaultForReporter ), - runOrder( RunTests::InDeclarationOrder ), - useColour( UseColour::Auto ) - {} - - bool listTests; - bool listTags; - bool listReporters; - bool listTestNamesOnly; - - bool showSuccessfulTests; - bool shouldDebugBreak; - bool noThrow; - bool showHelp; - bool showInvisibles; - bool filenamesAsTags; - - int abortAfter; - unsigned int rngSeed; - - Verbosity::Level verbosity; - WarnAbout::What warnings; - ShowDurations::OrNot showDurations; - RunTests::InWhatOrder runOrder; - UseColour::YesOrNo useColour; - - std::string outputFilename; - std::string name; - std::string processName; - - std::vector reporterNames; - std::vector testsOrTags; - std::vector sectionsToRun; - }; - - class Config : public SharedImpl { - private: - Config( Config const& other ); - Config& operator = ( Config const& other ); - virtual void dummy(); - public: - - Config() - {} - - Config( ConfigData const& data ) - : m_data( data ), - m_stream( openStream() ) - { - if( !data.testsOrTags.empty() ) { - TestSpecParser parser( ITagAliasRegistry::get() ); - for( std::size_t i = 0; i < data.testsOrTags.size(); ++i ) - parser.parse( data.testsOrTags[i] ); - m_testSpec = parser.testSpec(); - } - } - - virtual ~Config() {} - - std::string const& getFilename() const { - return m_data.outputFilename ; - } - - bool listTests() const { return m_data.listTests; } - bool listTestNamesOnly() const { return m_data.listTestNamesOnly; } - bool listTags() const { return m_data.listTags; } - bool listReporters() const { return m_data.listReporters; } - - std::string getProcessName() const { return m_data.processName; } - - std::vector const& getReporterNames() const { return m_data.reporterNames; } - std::vector const& getSectionsToRun() const CATCH_OVERRIDE { return m_data.sectionsToRun; } - - virtual TestSpec const& testSpec() const CATCH_OVERRIDE { return m_testSpec; } - - bool showHelp() const { return m_data.showHelp; } - - // IConfig interface - virtual bool allowThrows() const CATCH_OVERRIDE { return !m_data.noThrow; } - virtual std::ostream& stream() const CATCH_OVERRIDE { return m_stream->stream(); } - virtual std::string name() const CATCH_OVERRIDE { return m_data.name.empty() ? m_data.processName : m_data.name; } - virtual bool includeSuccessfulResults() const CATCH_OVERRIDE { return m_data.showSuccessfulTests; } - virtual bool warnAboutMissingAssertions() const CATCH_OVERRIDE { return m_data.warnings & WarnAbout::NoAssertions; } - virtual ShowDurations::OrNot showDurations() const CATCH_OVERRIDE { return m_data.showDurations; } - virtual RunTests::InWhatOrder runOrder() const CATCH_OVERRIDE { return m_data.runOrder; } - virtual unsigned int rngSeed() const CATCH_OVERRIDE { return m_data.rngSeed; } - virtual UseColour::YesOrNo useColour() const CATCH_OVERRIDE { return m_data.useColour; } - virtual bool shouldDebugBreak() const CATCH_OVERRIDE { return m_data.shouldDebugBreak; } - virtual int abortAfter() const CATCH_OVERRIDE { return m_data.abortAfter; } - virtual bool showInvisibles() const CATCH_OVERRIDE { return m_data.showInvisibles; } - - private: - - IStream const* openStream() { - if( m_data.outputFilename.empty() ) - return new CoutStream(); - else if( m_data.outputFilename[0] == '%' ) { - if( m_data.outputFilename == "%debug" ) - return new DebugOutStream(); - else - throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename ); - } - else - return new FileStream( m_data.outputFilename ); - } - ConfigData m_data; - - CATCH_AUTO_PTR( IStream const ) m_stream; - TestSpec m_testSpec; - }; - -} // end namespace Catch - -// #included from: catch_clara.h -#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED - -// Use Catch's value for console width (store Clara's off to the side, if present) -#ifdef CLARA_CONFIG_CONSOLE_WIDTH -#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH -#undef CLARA_CONFIG_CONSOLE_WIDTH -#endif -#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH - -// Declare Clara inside the Catch namespace -#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch { -// #included from: ../external/clara.h - -// Version 0.0.2.4 - -// Only use header guard if we are not using an outer namespace -#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE) - -#ifndef STITCH_CLARA_OPEN_NAMESPACE -#define TWOBLUECUBES_CLARA_H_INCLUDED -#define STITCH_CLARA_OPEN_NAMESPACE -#define STITCH_CLARA_CLOSE_NAMESPACE -#else -#define STITCH_CLARA_CLOSE_NAMESPACE } -#endif - -#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE - -// ----------- #included from tbc_text_format.h ----------- - -// Only use header guard if we are not using an outer namespace -#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE) -#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE -#define TBC_TEXT_FORMAT_H_INCLUDED -#endif - -#include -#include -#include -#include -#include - -// Use optional outer namespace -#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE -namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE { -#endif - -namespace Tbc { - -#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH - const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; -#else - const unsigned int consoleWidth = 80; -#endif - - struct TextAttributes { - TextAttributes() - : initialIndent( std::string::npos ), - indent( 0 ), - width( consoleWidth-1 ), - tabChar( '\t' ) - {} - - TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } - TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } - TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } - TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } - - std::size_t initialIndent; // indent of first line, or npos - std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos - std::size_t width; // maximum width of text, including indent. Longer text will wrap - char tabChar; // If this char is seen the indent is changed to current pos - }; - - class Text { - public: - Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) - : attr( _attr ) - { - std::string wrappableChars = " [({.,/|\\-"; - std::size_t indent = _attr.initialIndent != std::string::npos - ? _attr.initialIndent - : _attr.indent; - std::string remainder = _str; - - while( !remainder.empty() ) { - if( lines.size() >= 1000 ) { - lines.push_back( "... message truncated due to excessive size" ); - return; - } - std::size_t tabPos = std::string::npos; - std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); - std::size_t pos = remainder.find_first_of( '\n' ); - if( pos <= width ) { - width = pos; - } - pos = remainder.find_last_of( _attr.tabChar, width ); - if( pos != std::string::npos ) { - tabPos = pos; - if( remainder[width] == '\n' ) - width--; - remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); - } - - if( width == remainder.size() ) { - spliceLine( indent, remainder, width ); - } - else if( remainder[width] == '\n' ) { - spliceLine( indent, remainder, width ); - if( width <= 1 || remainder.size() != 1 ) - remainder = remainder.substr( 1 ); - indent = _attr.indent; - } - else { - pos = remainder.find_last_of( wrappableChars, width ); - if( pos != std::string::npos && pos > 0 ) { - spliceLine( indent, remainder, pos ); - if( remainder[0] == ' ' ) - remainder = remainder.substr( 1 ); - } - else { - spliceLine( indent, remainder, width-1 ); - lines.back() += "-"; - } - if( lines.size() == 1 ) - indent = _attr.indent; - if( tabPos != std::string::npos ) - indent += tabPos; - } - } - } - - void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { - lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); - _remainder = _remainder.substr( _pos ); - } - - typedef std::vector::const_iterator const_iterator; - - const_iterator begin() const { return lines.begin(); } - const_iterator end() const { return lines.end(); } - std::string const& last() const { return lines.back(); } - std::size_t size() const { return lines.size(); } - std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } - std::string toString() const { - std::ostringstream oss; - oss << *this; - return oss.str(); - } - - inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { - for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); - it != itEnd; ++it ) { - if( it != _text.begin() ) - _stream << "\n"; - _stream << *it; - } - return _stream; - } - - private: - std::string str; - TextAttributes attr; - std::vector lines; - }; - -} // end namespace Tbc - -#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE -} // end outer namespace -#endif - -#endif // TBC_TEXT_FORMAT_H_INCLUDED - -// ----------- end of #include from tbc_text_format.h ----------- -// ........... back in clara.h - -#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE - -// ----------- #included from clara_compilers.h ----------- - -#ifndef TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED -#define TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED - -// Detect a number of compiler features - mostly C++11/14 conformance - by compiler -// The following features are defined: -// -// CLARA_CONFIG_CPP11_NULLPTR : is nullptr supported? -// CLARA_CONFIG_CPP11_NOEXCEPT : is noexcept supported? -// CLARA_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods -// CLARA_CONFIG_CPP11_OVERRIDE : is override supported? -// CLARA_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) - -// CLARA_CONFIG_CPP11_OR_GREATER : Is C++11 supported? - -// CLARA_CONFIG_VARIADIC_MACROS : are variadic macros supported? - -// In general each macro has a _NO_ form -// (e.g. CLARA_CONFIG_CPP11_NO_NULLPTR) which disables the feature. -// Many features, at point of detection, define an _INTERNAL_ macro, so they -// can be combined, en-mass, with the _NO_ forms later. - -// All the C++11 features can be disabled with CLARA_CONFIG_NO_CPP11 - -#ifdef __clang__ - -#if __has_feature(cxx_nullptr) -#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR -#endif - -#if __has_feature(cxx_noexcept) -#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT -#endif - -#endif // __clang__ - -//////////////////////////////////////////////////////////////////////////////// -// GCC -#ifdef __GNUC__ - -#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) -#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR -#endif - -// - otherwise more recent versions define __cplusplus >= 201103L -// and will get picked up below - -#endif // __GNUC__ - -//////////////////////////////////////////////////////////////////////////////// -// Visual C++ -#ifdef _MSC_VER - -#if (_MSC_VER >= 1600) -#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR -#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -#endif - -#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) -#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT -#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#endif - -#endif // _MSC_VER - -//////////////////////////////////////////////////////////////////////////////// -// C++ language feature support - -// catch all support for C++11 -#if defined(__cplusplus) && __cplusplus >= 201103L - -#define CLARA_CPP11_OR_GREATER - -#if !defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) -#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR -#endif - -#ifndef CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT -#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT -#endif - -#ifndef CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#endif - -#if !defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) -#define CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE -#endif -#if !defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) -#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -#endif - -#endif // __cplusplus >= 201103L - -// Now set the actual defines based on the above + anything the user has configured -#if defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NO_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_NULLPTR -#endif -#if defined(CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_NOEXCEPT -#endif -#if defined(CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_GENERATED_METHODS -#endif -#if defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_OVERRIDE) && !defined(CLARA_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_OVERRIDE -#endif -#if defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_UNIQUE_PTR) && !defined(CLARA_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_UNIQUE_PTR -#endif - -// noexcept support: -#if defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_NOEXCEPT) -#define CLARA_NOEXCEPT noexcept -# define CLARA_NOEXCEPT_IS(x) noexcept(x) -#else -#define CLARA_NOEXCEPT throw() -# define CLARA_NOEXCEPT_IS(x) -#endif - -// nullptr support -#ifdef CLARA_CONFIG_CPP11_NULLPTR -#define CLARA_NULL nullptr -#else -#define CLARA_NULL NULL -#endif - -// override support -#ifdef CLARA_CONFIG_CPP11_OVERRIDE -#define CLARA_OVERRIDE override -#else -#define CLARA_OVERRIDE -#endif - -// unique_ptr support -#ifdef CLARA_CONFIG_CPP11_UNIQUE_PTR -# define CLARA_AUTO_PTR( T ) std::unique_ptr -#else -# define CLARA_AUTO_PTR( T ) std::auto_ptr -#endif - -#endif // TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED - -// ----------- end of #include from clara_compilers.h ----------- -// ........... back in clara.h - -#include -#include -#include - -#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) -#define CLARA_PLATFORM_WINDOWS -#endif - -// Use optional outer namespace -#ifdef STITCH_CLARA_OPEN_NAMESPACE -STITCH_CLARA_OPEN_NAMESPACE -#endif - -namespace Clara { - - struct UnpositionalTag {}; - - extern UnpositionalTag _; - -#ifdef CLARA_CONFIG_MAIN - UnpositionalTag _; -#endif - - namespace Detail { - -#ifdef CLARA_CONSOLE_WIDTH - const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; -#else - const unsigned int consoleWidth = 80; -#endif - - using namespace Tbc; - - inline bool startsWith( std::string const& str, std::string const& prefix ) { - return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix; - } - - template struct RemoveConstRef{ typedef T type; }; - template struct RemoveConstRef{ typedef T type; }; - template struct RemoveConstRef{ typedef T type; }; - template struct RemoveConstRef{ typedef T type; }; - - template struct IsBool { static const bool value = false; }; - template<> struct IsBool { static const bool value = true; }; - - template - void convertInto( std::string const& _source, T& _dest ) { - std::stringstream ss; - ss << _source; - ss >> _dest; - if( ss.fail() ) - throw std::runtime_error( "Unable to convert " + _source + " to destination type" ); - } - inline void convertInto( std::string const& _source, std::string& _dest ) { - _dest = _source; - } - char toLowerCh(char c) { - return static_cast( std::tolower( c ) ); - } - inline void convertInto( std::string const& _source, bool& _dest ) { - std::string sourceLC = _source; - std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), toLowerCh ); - if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" ) - _dest = true; - else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" ) - _dest = false; - else - throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" ); - } - - template - struct IArgFunction { - virtual ~IArgFunction() {} -#ifdef CLARA_CONFIG_CPP11_GENERATED_METHODS - IArgFunction() = default; - IArgFunction( IArgFunction const& ) = default; -#endif - virtual void set( ConfigT& config, std::string const& value ) const = 0; - virtual bool takesArg() const = 0; - virtual IArgFunction* clone() const = 0; - }; - - template - class BoundArgFunction { - public: - BoundArgFunction() : functionObj( CLARA_NULL ) {} - BoundArgFunction( IArgFunction* _functionObj ) : functionObj( _functionObj ) {} - BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CLARA_NULL ) {} - BoundArgFunction& operator = ( BoundArgFunction const& other ) { - IArgFunction* newFunctionObj = other.functionObj ? other.functionObj->clone() : CLARA_NULL; - delete functionObj; - functionObj = newFunctionObj; - return *this; - } - ~BoundArgFunction() { delete functionObj; } - - void set( ConfigT& config, std::string const& value ) const { - functionObj->set( config, value ); - } - bool takesArg() const { return functionObj->takesArg(); } - - bool isSet() const { - return functionObj != CLARA_NULL; - } - private: - IArgFunction* functionObj; - }; - - template - struct NullBinder : IArgFunction{ - virtual void set( C&, std::string const& ) const {} - virtual bool takesArg() const { return true; } - virtual IArgFunction* clone() const { return new NullBinder( *this ); } - }; - - template - struct BoundDataMember : IArgFunction{ - BoundDataMember( M C::* _member ) : member( _member ) {} - virtual void set( C& p, std::string const& stringValue ) const { - convertInto( stringValue, p.*member ); - } - virtual bool takesArg() const { return !IsBool::value; } - virtual IArgFunction* clone() const { return new BoundDataMember( *this ); } - M C::* member; - }; - template - struct BoundUnaryMethod : IArgFunction{ - BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {} - virtual void set( C& p, std::string const& stringValue ) const { - typename RemoveConstRef::type value; - convertInto( stringValue, value ); - (p.*member)( value ); - } - virtual bool takesArg() const { return !IsBool::value; } - virtual IArgFunction* clone() const { return new BoundUnaryMethod( *this ); } - void (C::*member)( M ); - }; - template - struct BoundNullaryMethod : IArgFunction{ - BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {} - virtual void set( C& p, std::string const& stringValue ) const { - bool value; - convertInto( stringValue, value ); - if( value ) - (p.*member)(); - } - virtual bool takesArg() const { return false; } - virtual IArgFunction* clone() const { return new BoundNullaryMethod( *this ); } - void (C::*member)(); - }; - - template - struct BoundUnaryFunction : IArgFunction{ - BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {} - virtual void set( C& obj, std::string const& stringValue ) const { - bool value; - convertInto( stringValue, value ); - if( value ) - function( obj ); - } - virtual bool takesArg() const { return false; } - virtual IArgFunction* clone() const { return new BoundUnaryFunction( *this ); } - void (*function)( C& ); - }; - - template - struct BoundBinaryFunction : IArgFunction{ - BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {} - virtual void set( C& obj, std::string const& stringValue ) const { - typename RemoveConstRef::type value; - convertInto( stringValue, value ); - function( obj, value ); - } - virtual bool takesArg() const { return !IsBool::value; } - virtual IArgFunction* clone() const { return new BoundBinaryFunction( *this ); } - void (*function)( C&, T ); - }; - - } // namespace Detail - - inline std::vector argsToVector( int argc, char const* const* const argv ) { - std::vector args( static_cast( argc ) ); - for( std::size_t i = 0; i < static_cast( argc ); ++i ) - args[i] = argv[i]; - - return args; - } - - class Parser { - enum Mode { None, MaybeShortOpt, SlashOpt, ShortOpt, LongOpt, Positional }; - Mode mode; - std::size_t from; - bool inQuotes; - public: - - struct Token { - enum Type { Positional, ShortOpt, LongOpt }; - Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {} - Type type; - std::string data; - }; - - Parser() : mode( None ), from( 0 ), inQuotes( false ){} - - void parseIntoTokens( std::vector const& args, std::vector& tokens ) { - const std::string doubleDash = "--"; - for( std::size_t i = 1; i < args.size() && args[i] != doubleDash; ++i ) - parseIntoTokens( args[i], tokens); - } - - void parseIntoTokens( std::string const& arg, std::vector& tokens ) { - for( std::size_t i = 0; i < arg.size(); ++i ) { - char c = arg[i]; - if( c == '"' ) - inQuotes = !inQuotes; - mode = handleMode( i, c, arg, tokens ); - } - mode = handleMode( arg.size(), '\0', arg, tokens ); - } - Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { - switch( mode ) { - case None: return handleNone( i, c ); - case MaybeShortOpt: return handleMaybeShortOpt( i, c ); - case ShortOpt: - case LongOpt: - case SlashOpt: return handleOpt( i, c, arg, tokens ); - case Positional: return handlePositional( i, c, arg, tokens ); - default: throw std::logic_error( "Unknown mode" ); - } - } - - Mode handleNone( std::size_t i, char c ) { - if( inQuotes ) { - from = i; - return Positional; - } - switch( c ) { - case '-': return MaybeShortOpt; -#ifdef CLARA_PLATFORM_WINDOWS - case '/': from = i+1; return SlashOpt; -#endif - default: from = i; return Positional; - } - } - Mode handleMaybeShortOpt( std::size_t i, char c ) { - switch( c ) { - case '-': from = i+1; return LongOpt; - default: from = i; return ShortOpt; - } - } - - Mode handleOpt( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { - if( std::string( ":=\0", 3 ).find( c ) == std::string::npos ) - return mode; - - std::string optName = arg.substr( from, i-from ); - if( mode == ShortOpt ) - for( std::size_t j = 0; j < optName.size(); ++j ) - tokens.push_back( Token( Token::ShortOpt, optName.substr( j, 1 ) ) ); - else if( mode == SlashOpt && optName.size() == 1 ) - tokens.push_back( Token( Token::ShortOpt, optName ) ); - else - tokens.push_back( Token( Token::LongOpt, optName ) ); - return None; - } - Mode handlePositional( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { - if( inQuotes || std::string( "\0", 1 ).find( c ) == std::string::npos ) - return mode; - - std::string data = arg.substr( from, i-from ); - tokens.push_back( Token( Token::Positional, data ) ); - return None; - } - }; - - template - struct CommonArgProperties { - CommonArgProperties() {} - CommonArgProperties( Detail::BoundArgFunction const& _boundField ) : boundField( _boundField ) {} - - Detail::BoundArgFunction boundField; - std::string description; - std::string detail; - std::string placeholder; // Only value if boundField takes an arg - - bool takesArg() const { - return !placeholder.empty(); - } - void validate() const { - if( !boundField.isSet() ) - throw std::logic_error( "option not bound" ); - } - }; - struct OptionArgProperties { - std::vector shortNames; - std::string longName; - - bool hasShortName( std::string const& shortName ) const { - return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end(); - } - bool hasLongName( std::string const& _longName ) const { - return _longName == longName; - } - }; - struct PositionalArgProperties { - PositionalArgProperties() : position( -1 ) {} - int position; // -1 means non-positional (floating) - - bool isFixedPositional() const { - return position != -1; - } - }; - - template - class CommandLine { - - struct Arg : CommonArgProperties, OptionArgProperties, PositionalArgProperties { - Arg() {} - Arg( Detail::BoundArgFunction const& _boundField ) : CommonArgProperties( _boundField ) {} - - using CommonArgProperties::placeholder; // !TBD - - std::string dbgName() const { - if( !longName.empty() ) - return "--" + longName; - if( !shortNames.empty() ) - return "-" + shortNames[0]; - return "positional args"; - } - std::string commands() const { - std::ostringstream oss; - bool first = true; - std::vector::const_iterator it = shortNames.begin(), itEnd = shortNames.end(); - for(; it != itEnd; ++it ) { - if( first ) - first = false; - else - oss << ", "; - oss << "-" << *it; - } - if( !longName.empty() ) { - if( !first ) - oss << ", "; - oss << "--" << longName; - } - if( !placeholder.empty() ) - oss << " <" << placeholder << ">"; - return oss.str(); - } - }; - - typedef CLARA_AUTO_PTR( Arg ) ArgAutoPtr; - - friend void addOptName( Arg& arg, std::string const& optName ) - { - if( optName.empty() ) - return; - if( Detail::startsWith( optName, "--" ) ) { - if( !arg.longName.empty() ) - throw std::logic_error( "Only one long opt may be specified. '" - + arg.longName - + "' already specified, now attempting to add '" - + optName + "'" ); - arg.longName = optName.substr( 2 ); - } - else if( Detail::startsWith( optName, "-" ) ) - arg.shortNames.push_back( optName.substr( 1 ) ); - else - throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" ); - } - friend void setPositionalArg( Arg& arg, int position ) - { - arg.position = position; - } - - class ArgBuilder { - public: - ArgBuilder( Arg* arg ) : m_arg( arg ) {} - - // Bind a non-boolean data member (requires placeholder string) - template - void bind( M C::* field, std::string const& placeholder ) { - m_arg->boundField = new Detail::BoundDataMember( field ); - m_arg->placeholder = placeholder; - } - // Bind a boolean data member (no placeholder required) - template - void bind( bool C::* field ) { - m_arg->boundField = new Detail::BoundDataMember( field ); - } - - // Bind a method taking a single, non-boolean argument (requires a placeholder string) - template - void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) { - m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); - m_arg->placeholder = placeholder; - } - - // Bind a method taking a single, boolean argument (no placeholder string required) - template - void bind( void (C::* unaryMethod)( bool ) ) { - m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); - } - - // Bind a method that takes no arguments (will be called if opt is present) - template - void bind( void (C::* nullaryMethod)() ) { - m_arg->boundField = new Detail::BoundNullaryMethod( nullaryMethod ); - } - - // Bind a free function taking a single argument - the object to operate on (no placeholder string required) - template - void bind( void (* unaryFunction)( C& ) ) { - m_arg->boundField = new Detail::BoundUnaryFunction( unaryFunction ); - } - - // Bind a free function taking a single argument - the object to operate on (requires a placeholder string) - template - void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) { - m_arg->boundField = new Detail::BoundBinaryFunction( binaryFunction ); - m_arg->placeholder = placeholder; - } - - ArgBuilder& describe( std::string const& description ) { - m_arg->description = description; - return *this; - } - ArgBuilder& detail( std::string const& detail ) { - m_arg->detail = detail; - return *this; - } - - protected: - Arg* m_arg; - }; - - class OptBuilder : public ArgBuilder { - public: - OptBuilder( Arg* arg ) : ArgBuilder( arg ) {} - OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {} - - OptBuilder& operator[]( std::string const& optName ) { - addOptName( *ArgBuilder::m_arg, optName ); - return *this; - } - }; - - public: - - CommandLine() - : m_boundProcessName( new Detail::NullBinder() ), - m_highestSpecifiedArgPosition( 0 ), - m_throwOnUnrecognisedTokens( false ) - {} - CommandLine( CommandLine const& other ) - : m_boundProcessName( other.m_boundProcessName ), - m_options ( other.m_options ), - m_positionalArgs( other.m_positionalArgs ), - m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ), - m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens ) - { - if( other.m_floatingArg.get() ) - m_floatingArg.reset( new Arg( *other.m_floatingArg ) ); - } - - CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) { - m_throwOnUnrecognisedTokens = shouldThrow; - return *this; - } - - OptBuilder operator[]( std::string const& optName ) { - m_options.push_back( Arg() ); - addOptName( m_options.back(), optName ); - OptBuilder builder( &m_options.back() ); - return builder; - } - - ArgBuilder operator[]( int position ) { - m_positionalArgs.insert( std::make_pair( position, Arg() ) ); - if( position > m_highestSpecifiedArgPosition ) - m_highestSpecifiedArgPosition = position; - setPositionalArg( m_positionalArgs[position], position ); - ArgBuilder builder( &m_positionalArgs[position] ); - return builder; - } - - // Invoke this with the _ instance - ArgBuilder operator[]( UnpositionalTag ) { - if( m_floatingArg.get() ) - throw std::logic_error( "Only one unpositional argument can be added" ); - m_floatingArg.reset( new Arg() ); - ArgBuilder builder( m_floatingArg.get() ); - return builder; - } - - template - void bindProcessName( M C::* field ) { - m_boundProcessName = new Detail::BoundDataMember( field ); - } - template - void bindProcessName( void (C::*_unaryMethod)( M ) ) { - m_boundProcessName = new Detail::BoundUnaryMethod( _unaryMethod ); - } - - void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const { - typename std::vector::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it; - std::size_t maxWidth = 0; - for( it = itBegin; it != itEnd; ++it ) - maxWidth = (std::max)( maxWidth, it->commands().size() ); - - for( it = itBegin; it != itEnd; ++it ) { - Detail::Text usage( it->commands(), Detail::TextAttributes() - .setWidth( maxWidth+indent ) - .setIndent( indent ) ); - Detail::Text desc( it->description, Detail::TextAttributes() - .setWidth( width - maxWidth - 3 ) ); - - for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) { - std::string usageCol = i < usage.size() ? usage[i] : ""; - os << usageCol; - - if( i < desc.size() && !desc[i].empty() ) - os << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' ) - << desc[i]; - os << "\n"; - } - } - } - std::string optUsage() const { - std::ostringstream oss; - optUsage( oss ); - return oss.str(); - } - - void argSynopsis( std::ostream& os ) const { - for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) { - if( i > 1 ) - os << " "; - typename std::map::const_iterator it = m_positionalArgs.find( i ); - if( it != m_positionalArgs.end() ) - os << "<" << it->second.placeholder << ">"; - else if( m_floatingArg.get() ) - os << "<" << m_floatingArg->placeholder << ">"; - else - throw std::logic_error( "non consecutive positional arguments with no floating args" ); - } - // !TBD No indication of mandatory args - if( m_floatingArg.get() ) { - if( m_highestSpecifiedArgPosition > 1 ) - os << " "; - os << "[<" << m_floatingArg->placeholder << "> ...]"; - } - } - std::string argSynopsis() const { - std::ostringstream oss; - argSynopsis( oss ); - return oss.str(); - } - - void usage( std::ostream& os, std::string const& procName ) const { - validate(); - os << "usage:\n " << procName << " "; - argSynopsis( os ); - if( !m_options.empty() ) { - os << " [options]\n\nwhere options are: \n"; - optUsage( os, 2 ); - } - os << "\n"; - } - std::string usage( std::string const& procName ) const { - std::ostringstream oss; - usage( oss, procName ); - return oss.str(); - } - - ConfigT parse( std::vector const& args ) const { - ConfigT config; - parseInto( args, config ); - return config; - } - - std::vector parseInto( std::vector const& args, ConfigT& config ) const { - std::string processName = args.empty() ? std::string() : args[0]; - std::size_t lastSlash = processName.find_last_of( "/\\" ); - if( lastSlash != std::string::npos ) - processName = processName.substr( lastSlash+1 ); - m_boundProcessName.set( config, processName ); - std::vector tokens; - Parser parser; - parser.parseIntoTokens( args, tokens ); - return populate( tokens, config ); - } - - std::vector populate( std::vector const& tokens, ConfigT& config ) const { - validate(); - std::vector unusedTokens = populateOptions( tokens, config ); - unusedTokens = populateFixedArgs( unusedTokens, config ); - unusedTokens = populateFloatingArgs( unusedTokens, config ); - return unusedTokens; - } - - std::vector populateOptions( std::vector const& tokens, ConfigT& config ) const { - std::vector unusedTokens; - std::vector errors; - for( std::size_t i = 0; i < tokens.size(); ++i ) { - Parser::Token const& token = tokens[i]; - typename std::vector::const_iterator it = m_options.begin(), itEnd = m_options.end(); - for(; it != itEnd; ++it ) { - Arg const& arg = *it; - - try { - if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) || - ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) { - if( arg.takesArg() ) { - if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional ) - errors.push_back( "Expected argument to option: " + token.data ); - else - arg.boundField.set( config, tokens[++i].data ); - } - else { - arg.boundField.set( config, "true" ); - } - break; - } - } - catch( std::exception& ex ) { - errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" ); - } - } - if( it == itEnd ) { - if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens ) - unusedTokens.push_back( token ); - else if( errors.empty() && m_throwOnUnrecognisedTokens ) - errors.push_back( "unrecognised option: " + token.data ); - } - } - if( !errors.empty() ) { - std::ostringstream oss; - for( std::vector::const_iterator it = errors.begin(), itEnd = errors.end(); - it != itEnd; - ++it ) { - if( it != errors.begin() ) - oss << "\n"; - oss << *it; - } - throw std::runtime_error( oss.str() ); - } - return unusedTokens; - } - std::vector populateFixedArgs( std::vector const& tokens, ConfigT& config ) const { - std::vector unusedTokens; - int position = 1; - for( std::size_t i = 0; i < tokens.size(); ++i ) { - Parser::Token const& token = tokens[i]; - typename std::map::const_iterator it = m_positionalArgs.find( position ); - if( it != m_positionalArgs.end() ) - it->second.boundField.set( config, token.data ); - else - unusedTokens.push_back( token ); - if( token.type == Parser::Token::Positional ) - position++; - } - return unusedTokens; - } - std::vector populateFloatingArgs( std::vector const& tokens, ConfigT& config ) const { - if( !m_floatingArg.get() ) - return tokens; - std::vector unusedTokens; - for( std::size_t i = 0; i < tokens.size(); ++i ) { - Parser::Token const& token = tokens[i]; - if( token.type == Parser::Token::Positional ) - m_floatingArg->boundField.set( config, token.data ); - else - unusedTokens.push_back( token ); - } - return unusedTokens; - } - - void validate() const - { - if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() ) - throw std::logic_error( "No options or arguments specified" ); - - for( typename std::vector::const_iterator it = m_options.begin(), - itEnd = m_options.end(); - it != itEnd; ++it ) - it->validate(); - } - - private: - Detail::BoundArgFunction m_boundProcessName; - std::vector m_options; - std::map m_positionalArgs; - ArgAutoPtr m_floatingArg; - int m_highestSpecifiedArgPosition; - bool m_throwOnUnrecognisedTokens; - }; - -} // end namespace Clara - -STITCH_CLARA_CLOSE_NAMESPACE -#undef STITCH_CLARA_OPEN_NAMESPACE -#undef STITCH_CLARA_CLOSE_NAMESPACE - -#endif // TWOBLUECUBES_CLARA_H_INCLUDED -#undef STITCH_CLARA_OPEN_NAMESPACE - -// Restore Clara's value for console width, if present -#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH -#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH -#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH -#endif - -#include -#include - -namespace Catch { - - inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; } - inline void abortAfterX( ConfigData& config, int x ) { - if( x < 1 ) - throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" ); - config.abortAfter = x; - } - inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } - inline void addSectionToRun( ConfigData& config, std::string const& sectionName ) { config.sectionsToRun.push_back( sectionName ); } - inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); } - - inline void addWarning( ConfigData& config, std::string const& _warning ) { - if( _warning == "NoAssertions" ) - config.warnings = static_cast( config.warnings | WarnAbout::NoAssertions ); - else - throw std::runtime_error( "Unrecognised warning: '" + _warning + '\'' ); - } - inline void setOrder( ConfigData& config, std::string const& order ) { - if( startsWith( "declared", order ) ) - config.runOrder = RunTests::InDeclarationOrder; - else if( startsWith( "lexical", order ) ) - config.runOrder = RunTests::InLexicographicalOrder; - else if( startsWith( "random", order ) ) - config.runOrder = RunTests::InRandomOrder; - else - throw std::runtime_error( "Unrecognised ordering: '" + order + '\'' ); - } - inline void setRngSeed( ConfigData& config, std::string const& seed ) { - if( seed == "time" ) { - config.rngSeed = static_cast( std::time(0) ); - } - else { - std::stringstream ss; - ss << seed; - ss >> config.rngSeed; - if( ss.fail() ) - throw std::runtime_error( "Argument to --rng-seed should be the word 'time' or a number" ); - } - } - inline void setVerbosity( ConfigData& config, int level ) { - // !TBD: accept strings? - config.verbosity = static_cast( level ); - } - inline void setShowDurations( ConfigData& config, bool _showDurations ) { - config.showDurations = _showDurations - ? ShowDurations::Always - : ShowDurations::Never; - } - inline void setUseColour( ConfigData& config, std::string const& value ) { - std::string mode = toLower( value ); - - if( mode == "yes" ) - config.useColour = UseColour::Yes; - else if( mode == "no" ) - config.useColour = UseColour::No; - else if( mode == "auto" ) - config.useColour = UseColour::Auto; - else - throw std::runtime_error( "colour mode must be one of: auto, yes or no" ); - } - inline void forceColour( ConfigData& config ) { - config.useColour = UseColour::Yes; - } - inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) { - std::ifstream f( _filename.c_str() ); - if( !f.is_open() ) - throw std::domain_error( "Unable to load input file: " + _filename ); - - std::string line; - while( std::getline( f, line ) ) { - line = trim(line); - if( !line.empty() && !startsWith( line, '#' ) ) { - if( !startsWith( line, '"' ) ) - line = '"' + line + '"'; - addTestOrTags( config, line + ',' ); - } - } - } - - inline Clara::CommandLine makeCommandLineParser() { - - using namespace Clara; - CommandLine cli; - - cli.bindProcessName( &ConfigData::processName ); - - cli["-?"]["-h"]["--help"] - .describe( "display usage information" ) - .bind( &ConfigData::showHelp ); - - cli["-l"]["--list-tests"] - .describe( "list all/matching test cases" ) - .bind( &ConfigData::listTests ); - - cli["-t"]["--list-tags"] - .describe( "list all/matching tags" ) - .bind( &ConfigData::listTags ); - - cli["-s"]["--success"] - .describe( "include successful tests in output" ) - .bind( &ConfigData::showSuccessfulTests ); - - cli["-b"]["--break"] - .describe( "break into debugger on failure" ) - .bind( &ConfigData::shouldDebugBreak ); - - cli["-e"]["--nothrow"] - .describe( "skip exception tests" ) - .bind( &ConfigData::noThrow ); - - cli["-i"]["--invisibles"] - .describe( "show invisibles (tabs, newlines)" ) - .bind( &ConfigData::showInvisibles ); - - cli["-o"]["--out"] - .describe( "output filename" ) - .bind( &ConfigData::outputFilename, "filename" ); - - cli["-r"]["--reporter"] -// .placeholder( "name[:filename]" ) - .describe( "reporter to use (defaults to console)" ) - .bind( &addReporterName, "name" ); - - cli["-n"]["--name"] - .describe( "suite name" ) - .bind( &ConfigData::name, "name" ); - - cli["-a"]["--abort"] - .describe( "abort at first failure" ) - .bind( &abortAfterFirst ); - - cli["-x"]["--abortx"] - .describe( "abort after x failures" ) - .bind( &abortAfterX, "no. failures" ); - - cli["-w"]["--warn"] - .describe( "enable warnings" ) - .bind( &addWarning, "warning name" ); - -// - needs updating if reinstated -// cli.into( &setVerbosity ) -// .describe( "level of verbosity (0=no output)" ) -// .shortOpt( "v") -// .longOpt( "verbosity" ) -// .placeholder( "level" ); - - cli[_] - .describe( "which test or tests to use" ) - .bind( &addTestOrTags, "test name, pattern or tags" ); - - cli["-d"]["--durations"] - .describe( "show test durations" ) - .bind( &setShowDurations, "yes|no" ); - - cli["-f"]["--input-file"] - .describe( "load test names to run from a file" ) - .bind( &loadTestNamesFromFile, "filename" ); - - cli["-#"]["--filenames-as-tags"] - .describe( "adds a tag for the filename" ) - .bind( &ConfigData::filenamesAsTags ); - - cli["-c"]["--section"] - .describe( "specify section to run" ) - .bind( &addSectionToRun, "section name" ); - - // Less common commands which don't have a short form - cli["--list-test-names-only"] - .describe( "list all/matching test cases names only" ) - .bind( &ConfigData::listTestNamesOnly ); - - cli["--list-reporters"] - .describe( "list all reporters" ) - .bind( &ConfigData::listReporters ); - - cli["--order"] - .describe( "test case order (defaults to decl)" ) - .bind( &setOrder, "decl|lex|rand" ); - - cli["--rng-seed"] - .describe( "set a specific seed for random numbers" ) - .bind( &setRngSeed, "'time'|number" ); - - cli["--force-colour"] - .describe( "force colourised output (deprecated)" ) - .bind( &forceColour ); - - cli["--use-colour"] - .describe( "should output be colourised" ) - .bind( &setUseColour, "yes|no" ); - - return cli; - } - -} // end namespace Catch - -// #included from: internal/catch_list.hpp -#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED - -// #included from: catch_text.h -#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED - -#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH - -#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch -// #included from: ../external/tbc_text_format.h -// Only use header guard if we are not using an outer namespace -#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE -# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED -# ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -# define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -# endif -# else -# define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED -# endif -#endif -#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -#include -#include -#include - -// Use optional outer namespace -#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE -namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE { -#endif - -namespace Tbc { - -#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH - const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; -#else - const unsigned int consoleWidth = 80; -#endif - - struct TextAttributes { - TextAttributes() - : initialIndent( std::string::npos ), - indent( 0 ), - width( consoleWidth-1 ) - {} - - TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } - TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } - TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } - - std::size_t initialIndent; // indent of first line, or npos - std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos - std::size_t width; // maximum width of text, including indent. Longer text will wrap - }; - - class Text { - public: - Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) - : attr( _attr ) - { - const std::string wrappableBeforeChars = "[({<\t"; - const std::string wrappableAfterChars = "])}>-,./|\\"; - const std::string wrappableInsteadOfChars = " \n\r"; - std::string indent = _attr.initialIndent != std::string::npos - ? std::string( _attr.initialIndent, ' ' ) - : std::string( _attr.indent, ' ' ); - - typedef std::string::const_iterator iterator; - iterator it = _str.begin(); - const iterator strEnd = _str.end(); - - while( it != strEnd ) { - - if( lines.size() >= 1000 ) { - lines.push_back( "... message truncated due to excessive size" ); - return; - } - - std::string suffix; - std::size_t width = (std::min)( static_cast( strEnd-it ), _attr.width-static_cast( indent.size() ) ); - iterator itEnd = it+width; - iterator itNext = _str.end(); - - iterator itNewLine = std::find( it, itEnd, '\n' ); - if( itNewLine != itEnd ) - itEnd = itNewLine; - - if( itEnd != strEnd ) { - bool foundWrapPoint = false; - iterator findIt = itEnd; - do { - if( wrappableAfterChars.find( *findIt ) != std::string::npos && findIt != itEnd ) { - itEnd = findIt+1; - itNext = findIt+1; - foundWrapPoint = true; - } - else if( findIt > it && wrappableBeforeChars.find( *findIt ) != std::string::npos ) { - itEnd = findIt; - itNext = findIt; - foundWrapPoint = true; - } - else if( wrappableInsteadOfChars.find( *findIt ) != std::string::npos ) { - itNext = findIt+1; - itEnd = findIt; - foundWrapPoint = true; - } - if( findIt == it ) - break; - else - --findIt; - } - while( !foundWrapPoint ); - - if( !foundWrapPoint ) { - // No good wrap char, so we'll break mid word and add a hyphen - --itEnd; - itNext = itEnd; - suffix = "-"; - } - else { - while( itEnd > it && wrappableInsteadOfChars.find( *(itEnd-1) ) != std::string::npos ) - --itEnd; - } - } - lines.push_back( indent + std::string( it, itEnd ) + suffix ); - - if( indent.size() != _attr.indent ) - indent = std::string( _attr.indent, ' ' ); - it = itNext; - } - } - - typedef std::vector::const_iterator const_iterator; - - const_iterator begin() const { return lines.begin(); } - const_iterator end() const { return lines.end(); } - std::string const& last() const { return lines.back(); } - std::size_t size() const { return lines.size(); } - std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } - std::string toString() const { - std::ostringstream oss; - oss << *this; - return oss.str(); - } - - inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { - for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); - it != itEnd; ++it ) { - if( it != _text.begin() ) - _stream << "\n"; - _stream << *it; - } - return _stream; - } - - private: - std::string str; - TextAttributes attr; - std::vector lines; - }; - -} // end namespace Tbc - -#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE -} // end outer namespace -#endif - -#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE - -namespace Catch { - using Tbc::Text; - using Tbc::TextAttributes; -} - -// #included from: catch_console_colour.hpp -#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED - -namespace Catch { - - struct Colour { - enum Code { - None = 0, - - White, - Red, - Green, - Blue, - Cyan, - Yellow, - Grey, - - Bright = 0x10, - - BrightRed = Bright | Red, - BrightGreen = Bright | Green, - LightGrey = Bright | Grey, - BrightWhite = Bright | White, - - // By intention - FileName = LightGrey, - Warning = Yellow, - ResultError = BrightRed, - ResultSuccess = BrightGreen, - ResultExpectedFailure = Warning, - - Error = BrightRed, - Success = Green, - - OriginalExpression = Cyan, - ReconstructedExpression = Yellow, - - SecondaryText = LightGrey, - Headers = White - }; - - // Use constructed object for RAII guard - Colour( Code _colourCode ); - Colour( Colour const& other ); - ~Colour(); - - // Use static method for one-shot changes - static void use( Code _colourCode ); - - private: - bool m_moved; - }; - - inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; } - -} // end namespace Catch - -// #included from: catch_interfaces_reporter.h -#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED - -#include -#include -#include - -namespace Catch -{ - struct ReporterConfig { - explicit ReporterConfig( Ptr const& _fullConfig ) - : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} - - ReporterConfig( Ptr const& _fullConfig, std::ostream& _stream ) - : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} - - std::ostream& stream() const { return *m_stream; } - Ptr fullConfig() const { return m_fullConfig; } - - private: - std::ostream* m_stream; - Ptr m_fullConfig; - }; - - struct ReporterPreferences { - ReporterPreferences() - : shouldRedirectStdOut( false ) - {} - - bool shouldRedirectStdOut; - }; - - template - struct LazyStat : Option { - LazyStat() : used( false ) {} - LazyStat& operator=( T const& _value ) { - Option::operator=( _value ); - used = false; - return *this; - } - void reset() { - Option::reset(); - used = false; - } - bool used; - }; - - struct TestRunInfo { - TestRunInfo( std::string const& _name ) : name( _name ) {} - std::string name; - }; - struct GroupInfo { - GroupInfo( std::string const& _name, - std::size_t _groupIndex, - std::size_t _groupsCount ) - : name( _name ), - groupIndex( _groupIndex ), - groupsCounts( _groupsCount ) - {} - - std::string name; - std::size_t groupIndex; - std::size_t groupsCounts; - }; - - struct AssertionStats { - AssertionStats( AssertionResult const& _assertionResult, - std::vector const& _infoMessages, - Totals const& _totals ) - : assertionResult( _assertionResult ), - infoMessages( _infoMessages ), - totals( _totals ) - { - if( assertionResult.hasMessage() ) { - // Copy message into messages list. - // !TBD This should have been done earlier, somewhere - MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); - builder << assertionResult.getMessage(); - builder.m_info.message = builder.m_stream.str(); - - infoMessages.push_back( builder.m_info ); - } - } - virtual ~AssertionStats(); - -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - AssertionStats( AssertionStats const& ) = default; - AssertionStats( AssertionStats && ) = default; - AssertionStats& operator = ( AssertionStats const& ) = default; - AssertionStats& operator = ( AssertionStats && ) = default; -# endif - - AssertionResult assertionResult; - std::vector infoMessages; - Totals totals; - }; - - struct SectionStats { - SectionStats( SectionInfo const& _sectionInfo, - Counts const& _assertions, - double _durationInSeconds, - bool _missingAssertions ) - : sectionInfo( _sectionInfo ), - assertions( _assertions ), - durationInSeconds( _durationInSeconds ), - missingAssertions( _missingAssertions ) - {} - virtual ~SectionStats(); -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - SectionStats( SectionStats const& ) = default; - SectionStats( SectionStats && ) = default; - SectionStats& operator = ( SectionStats const& ) = default; - SectionStats& operator = ( SectionStats && ) = default; -# endif - - SectionInfo sectionInfo; - Counts assertions; - double durationInSeconds; - bool missingAssertions; - }; - - struct TestCaseStats { - TestCaseStats( TestCaseInfo const& _testInfo, - Totals const& _totals, - std::string const& _stdOut, - std::string const& _stdErr, - bool _aborting ) - : testInfo( _testInfo ), - totals( _totals ), - stdOut( _stdOut ), - stdErr( _stdErr ), - aborting( _aborting ) - {} - virtual ~TestCaseStats(); - -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - TestCaseStats( TestCaseStats const& ) = default; - TestCaseStats( TestCaseStats && ) = default; - TestCaseStats& operator = ( TestCaseStats const& ) = default; - TestCaseStats& operator = ( TestCaseStats && ) = default; -# endif - - TestCaseInfo testInfo; - Totals totals; - std::string stdOut; - std::string stdErr; - bool aborting; - }; - - struct TestGroupStats { - TestGroupStats( GroupInfo const& _groupInfo, - Totals const& _totals, - bool _aborting ) - : groupInfo( _groupInfo ), - totals( _totals ), - aborting( _aborting ) - {} - TestGroupStats( GroupInfo const& _groupInfo ) - : groupInfo( _groupInfo ), - aborting( false ) - {} - virtual ~TestGroupStats(); - -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - TestGroupStats( TestGroupStats const& ) = default; - TestGroupStats( TestGroupStats && ) = default; - TestGroupStats& operator = ( TestGroupStats const& ) = default; - TestGroupStats& operator = ( TestGroupStats && ) = default; -# endif - - GroupInfo groupInfo; - Totals totals; - bool aborting; - }; - - struct TestRunStats { - TestRunStats( TestRunInfo const& _runInfo, - Totals const& _totals, - bool _aborting ) - : runInfo( _runInfo ), - totals( _totals ), - aborting( _aborting ) - {} - virtual ~TestRunStats(); - -# ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS - TestRunStats( TestRunStats const& _other ) - : runInfo( _other.runInfo ), - totals( _other.totals ), - aborting( _other.aborting ) - {} -# else - TestRunStats( TestRunStats const& ) = default; - TestRunStats( TestRunStats && ) = default; - TestRunStats& operator = ( TestRunStats const& ) = default; - TestRunStats& operator = ( TestRunStats && ) = default; -# endif - - TestRunInfo runInfo; - Totals totals; - bool aborting; - }; - - class MultipleReporters; - - struct IStreamingReporter : IShared { - virtual ~IStreamingReporter(); - - // Implementing class must also provide the following static method: - // static std::string getDescription(); - - virtual ReporterPreferences getPreferences() const = 0; - - virtual void noMatchingTestCases( std::string const& spec ) = 0; - - virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; - virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; - - virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; - virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; - - virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; - - // The return value indicates if the messages buffer should be cleared: - virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; - - virtual void sectionEnded( SectionStats const& sectionStats ) = 0; - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; - virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; - - virtual void skipTest( TestCaseInfo const& testInfo ) = 0; - - virtual MultipleReporters* tryAsMulti() { return CATCH_NULL; } - }; - - struct IReporterFactory : IShared { - virtual ~IReporterFactory(); - virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0; - virtual std::string getDescription() const = 0; - }; - - struct IReporterRegistry { - typedef std::map > FactoryMap; - typedef std::vector > Listeners; - - virtual ~IReporterRegistry(); - virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const = 0; - virtual FactoryMap const& getFactories() const = 0; - virtual Listeners const& getListeners() const = 0; - }; - - Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ); - -} - -#include -#include - -namespace Catch { - - inline std::size_t listTests( Config const& config ) { - - TestSpec testSpec = config.testSpec(); - if( config.testSpec().hasFilters() ) - Catch::cout() << "Matching test cases:\n"; - else { - Catch::cout() << "All available test cases:\n"; - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); - } - - std::size_t matchedTests = 0; - TextAttributes nameAttr, tagsAttr; - nameAttr.setInitialIndent( 2 ).setIndent( 4 ); - tagsAttr.setIndent( 6 ); - - std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); - for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); - it != itEnd; - ++it ) { - matchedTests++; - TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - Colour::Code colour = testCaseInfo.isHidden() - ? Colour::SecondaryText - : Colour::None; - Colour colourGuard( colour ); - - Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl; - if( !testCaseInfo.tags.empty() ) - Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl; - } - - if( !config.testSpec().hasFilters() ) - Catch::cout() << pluralise( matchedTests, "test case" ) << '\n' << std::endl; - else - Catch::cout() << pluralise( matchedTests, "matching test case" ) << '\n' << std::endl; - return matchedTests; - } - - inline std::size_t listTestsNamesOnly( Config const& config ) { - TestSpec testSpec = config.testSpec(); - if( !config.testSpec().hasFilters() ) - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); - std::size_t matchedTests = 0; - std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); - for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); - it != itEnd; - ++it ) { - matchedTests++; - TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - if( startsWith( testCaseInfo.name, '#' ) ) - Catch::cout() << '"' << testCaseInfo.name << '"' << std::endl; - else - Catch::cout() << testCaseInfo.name << std::endl; - } - return matchedTests; - } - - struct TagInfo { - TagInfo() : count ( 0 ) {} - void add( std::string const& spelling ) { - ++count; - spellings.insert( spelling ); - } - std::string all() const { - std::string out; - for( std::set::const_iterator it = spellings.begin(), itEnd = spellings.end(); - it != itEnd; - ++it ) - out += "[" + *it + "]"; - return out; - } - std::set spellings; - std::size_t count; - }; - - inline std::size_t listTags( Config const& config ) { - TestSpec testSpec = config.testSpec(); - if( config.testSpec().hasFilters() ) - Catch::cout() << "Tags for matching test cases:\n"; - else { - Catch::cout() << "All available tags:\n"; - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); - } - - std::map tagCounts; - - std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); - for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); - it != itEnd; - ++it ) { - for( std::set::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), - tagItEnd = it->getTestCaseInfo().tags.end(); - tagIt != tagItEnd; - ++tagIt ) { - std::string tagName = *tagIt; - std::string lcaseTagName = toLower( tagName ); - std::map::iterator countIt = tagCounts.find( lcaseTagName ); - if( countIt == tagCounts.end() ) - countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; - countIt->second.add( tagName ); - } - } - - for( std::map::const_iterator countIt = tagCounts.begin(), - countItEnd = tagCounts.end(); - countIt != countItEnd; - ++countIt ) { - std::ostringstream oss; - oss << " " << std::setw(2) << countIt->second.count << " "; - Text wrapper( countIt->second.all(), TextAttributes() - .setInitialIndent( 0 ) - .setIndent( oss.str().size() ) - .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) ); - Catch::cout() << oss.str() << wrapper << '\n'; - } - Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; - return tagCounts.size(); - } - - inline std::size_t listReporters( Config const& /*config*/ ) { - Catch::cout() << "Available reporters:\n"; - IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); - IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it; - std::size_t maxNameLen = 0; - for(it = itBegin; it != itEnd; ++it ) - maxNameLen = (std::max)( maxNameLen, it->first.size() ); - - for(it = itBegin; it != itEnd; ++it ) { - Text wrapper( it->second->getDescription(), TextAttributes() - .setInitialIndent( 0 ) - .setIndent( 7+maxNameLen ) - .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) ); - Catch::cout() << " " - << it->first - << ':' - << std::string( maxNameLen - it->first.size() + 2, ' ' ) - << wrapper << '\n'; - } - Catch::cout() << std::endl; - return factories.size(); - } - - inline Option list( Config const& config ) { - Option listedCount; - if( config.listTests() ) - listedCount = listedCount.valueOr(0) + listTests( config ); - if( config.listTestNamesOnly() ) - listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); - if( config.listTags() ) - listedCount = listedCount.valueOr(0) + listTags( config ); - if( config.listReporters() ) - listedCount = listedCount.valueOr(0) + listReporters( config ); - return listedCount; - } - -} // end namespace Catch - -// #included from: internal/catch_run_context.hpp -#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED - -// #included from: catch_test_case_tracker.hpp -#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED - -#include -#include -#include -#include -#include - -CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS - -namespace Catch { -namespace TestCaseTracking { - - struct NameAndLocation { - std::string name; - SourceLineInfo location; - - NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) - : name( _name ), - location( _location ) - {} - }; - - struct ITracker : SharedImpl<> { - virtual ~ITracker(); - - // static queries - virtual NameAndLocation const& nameAndLocation() const = 0; - - // dynamic queries - virtual bool isComplete() const = 0; // Successfully completed or failed - virtual bool isSuccessfullyCompleted() const = 0; - virtual bool isOpen() const = 0; // Started but not complete - virtual bool hasChildren() const = 0; - - virtual ITracker& parent() = 0; - - // actions - virtual void close() = 0; // Successfully complete - virtual void fail() = 0; - virtual void markAsNeedingAnotherRun() = 0; - - virtual void addChild( Ptr const& child ) = 0; - virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) = 0; - virtual void openChild() = 0; - - // Debug/ checking - virtual bool isSectionTracker() const = 0; - virtual bool isIndexTracker() const = 0; - }; - - class TrackerContext { - - enum RunState { - NotStarted, - Executing, - CompletedCycle - }; - - Ptr m_rootTracker; - ITracker* m_currentTracker; - RunState m_runState; - - public: - - static TrackerContext& instance() { - static TrackerContext s_instance; - return s_instance; - } - - TrackerContext() - : m_currentTracker( CATCH_NULL ), - m_runState( NotStarted ) - {} - - ITracker& startRun(); - - void endRun() { - m_rootTracker.reset(); - m_currentTracker = CATCH_NULL; - m_runState = NotStarted; - } - - void startCycle() { - m_currentTracker = m_rootTracker.get(); - m_runState = Executing; - } - void completeCycle() { - m_runState = CompletedCycle; - } - - bool completedCycle() const { - return m_runState == CompletedCycle; - } - ITracker& currentTracker() { - return *m_currentTracker; - } - void setCurrentTracker( ITracker* tracker ) { - m_currentTracker = tracker; - } - }; - - class TrackerBase : public ITracker { - protected: - enum CycleState { - NotStarted, - Executing, - ExecutingChildren, - NeedsAnotherRun, - CompletedSuccessfully, - Failed - }; - class TrackerHasName { - NameAndLocation m_nameAndLocation; - public: - TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {} - bool operator ()( Ptr const& tracker ) { - return - tracker->nameAndLocation().name == m_nameAndLocation.name && - tracker->nameAndLocation().location == m_nameAndLocation.location; - } - }; - typedef std::vector > Children; - NameAndLocation m_nameAndLocation; - TrackerContext& m_ctx; - ITracker* m_parent; - Children m_children; - CycleState m_runState; - public: - TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) - : m_nameAndLocation( nameAndLocation ), - m_ctx( ctx ), - m_parent( parent ), - m_runState( NotStarted ) - {} - virtual ~TrackerBase(); - - virtual NameAndLocation const& nameAndLocation() const CATCH_OVERRIDE { - return m_nameAndLocation; - } - virtual bool isComplete() const CATCH_OVERRIDE { - return m_runState == CompletedSuccessfully || m_runState == Failed; - } - virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE { - return m_runState == CompletedSuccessfully; - } - virtual bool isOpen() const CATCH_OVERRIDE { - return m_runState != NotStarted && !isComplete(); - } - virtual bool hasChildren() const CATCH_OVERRIDE { - return !m_children.empty(); - } - - virtual void addChild( Ptr const& child ) CATCH_OVERRIDE { - m_children.push_back( child ); - } - - virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) CATCH_OVERRIDE { - Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) ); - return( it != m_children.end() ) - ? it->get() - : CATCH_NULL; - } - virtual ITracker& parent() CATCH_OVERRIDE { - assert( m_parent ); // Should always be non-null except for root - return *m_parent; - } - - virtual void openChild() CATCH_OVERRIDE { - if( m_runState != ExecutingChildren ) { - m_runState = ExecutingChildren; - if( m_parent ) - m_parent->openChild(); - } - } - - virtual bool isSectionTracker() const CATCH_OVERRIDE { return false; } - virtual bool isIndexTracker() const CATCH_OVERRIDE { return false; } - - void open() { - m_runState = Executing; - moveToThis(); - if( m_parent ) - m_parent->openChild(); - } - - virtual void close() CATCH_OVERRIDE { - - // Close any still open children (e.g. generators) - while( &m_ctx.currentTracker() != this ) - m_ctx.currentTracker().close(); - - switch( m_runState ) { - case NotStarted: - case CompletedSuccessfully: - case Failed: - throw std::logic_error( "Illogical state" ); - - case NeedsAnotherRun: - break;; - - case Executing: - m_runState = CompletedSuccessfully; - break; - case ExecutingChildren: - if( m_children.empty() || m_children.back()->isComplete() ) - m_runState = CompletedSuccessfully; - break; - - default: - throw std::logic_error( "Unexpected state" ); - } - moveToParent(); - m_ctx.completeCycle(); - } - virtual void fail() CATCH_OVERRIDE { - m_runState = Failed; - if( m_parent ) - m_parent->markAsNeedingAnotherRun(); - moveToParent(); - m_ctx.completeCycle(); - } - virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE { - m_runState = NeedsAnotherRun; - } - private: - void moveToParent() { - assert( m_parent ); - m_ctx.setCurrentTracker( m_parent ); - } - void moveToThis() { - m_ctx.setCurrentTracker( this ); - } - }; - - class SectionTracker : public TrackerBase { - std::vector m_filters; - public: - SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) - : TrackerBase( nameAndLocation, ctx, parent ) - { - if( parent ) { - while( !parent->isSectionTracker() ) - parent = &parent->parent(); - - SectionTracker& parentSection = static_cast( *parent ); - addNextFilters( parentSection.m_filters ); - } - } - virtual ~SectionTracker(); - - virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; } - - static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { - SectionTracker* section = CATCH_NULL; - - ITracker& currentTracker = ctx.currentTracker(); - if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { - assert( childTracker ); - assert( childTracker->isSectionTracker() ); - section = static_cast( childTracker ); - } - else { - section = new SectionTracker( nameAndLocation, ctx, ¤tTracker ); - currentTracker.addChild( section ); - } - if( !ctx.completedCycle() ) - section->tryOpen(); - return *section; - } - - void tryOpen() { - if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) - open(); - } - - void addInitialFilters( std::vector const& filters ) { - if( !filters.empty() ) { - m_filters.push_back(""); // Root - should never be consulted - m_filters.push_back(""); // Test Case - not a section filter - m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); - } - } - void addNextFilters( std::vector const& filters ) { - if( filters.size() > 1 ) - m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); - } - }; - - class IndexTracker : public TrackerBase { - int m_size; - int m_index; - public: - IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) - : TrackerBase( nameAndLocation, ctx, parent ), - m_size( size ), - m_index( -1 ) - {} - virtual ~IndexTracker(); - - virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; } - - static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { - IndexTracker* tracker = CATCH_NULL; - - ITracker& currentTracker = ctx.currentTracker(); - if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { - assert( childTracker ); - assert( childTracker->isIndexTracker() ); - tracker = static_cast( childTracker ); - } - else { - tracker = new IndexTracker( nameAndLocation, ctx, ¤tTracker, size ); - currentTracker.addChild( tracker ); - } - - if( !ctx.completedCycle() && !tracker->isComplete() ) { - if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) - tracker->moveNext(); - tracker->open(); - } - - return *tracker; - } - - int index() const { return m_index; } - - void moveNext() { - m_index++; - m_children.clear(); - } - - virtual void close() CATCH_OVERRIDE { - TrackerBase::close(); - if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) - m_runState = Executing; - } - }; - - inline ITracker& TrackerContext::startRun() { - m_rootTracker = new SectionTracker( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, CATCH_NULL ); - m_currentTracker = CATCH_NULL; - m_runState = Executing; - return *m_rootTracker; - } - -} // namespace TestCaseTracking - -using TestCaseTracking::ITracker; -using TestCaseTracking::TrackerContext; -using TestCaseTracking::SectionTracker; -using TestCaseTracking::IndexTracker; - -} // namespace Catch - -CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS - -// #included from: catch_fatal_condition.hpp -#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED - -namespace Catch { - - // Report the error condition - inline void reportFatal( std::string const& message ) { - IContext& context = Catch::getCurrentContext(); - IResultCapture* resultCapture = context.getResultCapture(); - resultCapture->handleFatalErrorCondition( message ); - } - -} // namespace Catch - -#if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// -// #included from: catch_windows_h_proxy.h - -#define TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED - -#ifdef CATCH_DEFINES_NOMINMAX -# define NOMINMAX -#endif -#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -#endif - -#ifdef __AFXDLL -#include -#else -#include -#endif - -#ifdef CATCH_DEFINES_NOMINMAX -# undef NOMINMAX -#endif -#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN -# undef WIN32_LEAN_AND_MEAN -#endif - - -# if !defined ( CATCH_CONFIG_WINDOWS_SEH ) - -namespace Catch { - struct FatalConditionHandler { - void reset() {} - }; -} - -# else // CATCH_CONFIG_WINDOWS_SEH is defined - -namespace Catch { - - struct SignalDefs { DWORD id; const char* name; }; - extern SignalDefs signalDefs[]; - // There is no 1-1 mapping between signals and windows exceptions. - // Windows can easily distinguish between SO and SigSegV, - // but SigInt, SigTerm, etc are handled differently. - SignalDefs signalDefs[] = { - { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, - { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, - { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, - { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, - }; - - struct FatalConditionHandler { - - static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { - for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { - if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) { - reportFatal(signalDefs[i].name); - } - } - // If its not an exception we care about, pass it along. - // This stops us from eating debugger breaks etc. - return EXCEPTION_CONTINUE_SEARCH; - } - - FatalConditionHandler() { - isSet = true; - // 32k seems enough for Catch to handle stack overflow, - // but the value was found experimentally, so there is no strong guarantee - guaranteeSize = 32 * 1024; - exceptionHandlerHandle = CATCH_NULL; - // Register as first handler in current chain - exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); - // Pass in guarantee size to be filled - SetThreadStackGuarantee(&guaranteeSize); - } - - static void reset() { - if (isSet) { - // Unregister handler and restore the old guarantee - RemoveVectoredExceptionHandler(exceptionHandlerHandle); - SetThreadStackGuarantee(&guaranteeSize); - exceptionHandlerHandle = CATCH_NULL; - isSet = false; - } - } - - ~FatalConditionHandler() { - reset(); - } - private: - static bool isSet; - static ULONG guaranteeSize; - static PVOID exceptionHandlerHandle; - }; - - bool FatalConditionHandler::isSet = false; - ULONG FatalConditionHandler::guaranteeSize = 0; - PVOID FatalConditionHandler::exceptionHandlerHandle = CATCH_NULL; - -} // namespace Catch - -# endif // CATCH_CONFIG_WINDOWS_SEH - -#else // Not Windows - assumed to be POSIX compatible ////////////////////////// - -# if !defined(CATCH_CONFIG_POSIX_SIGNALS) - -namespace Catch { - struct FatalConditionHandler { - void reset() {} - }; -} - -# else // CATCH_CONFIG_POSIX_SIGNALS is defined - -#include - -namespace Catch { - - struct SignalDefs { - int id; - const char* name; - }; - extern SignalDefs signalDefs[]; - SignalDefs signalDefs[] = { - { SIGINT, "SIGINT - Terminal interrupt signal" }, - { SIGILL, "SIGILL - Illegal instruction signal" }, - { SIGFPE, "SIGFPE - Floating point error signal" }, - { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, - { SIGTERM, "SIGTERM - Termination request signal" }, - { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } - }; - - struct FatalConditionHandler { - - static bool isSet; - static struct sigaction oldSigActions [sizeof(signalDefs)/sizeof(SignalDefs)]; - static stack_t oldSigStack; - static char altStackMem[SIGSTKSZ]; - - static void handleSignal( int sig ) { - std::string name = ""; - for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { - SignalDefs &def = signalDefs[i]; - if (sig == def.id) { - name = def.name; - break; - } - } - reset(); - reportFatal(name); - raise( sig ); - } - - FatalConditionHandler() { - isSet = true; - stack_t sigStack; - sigStack.ss_sp = altStackMem; - sigStack.ss_size = SIGSTKSZ; - sigStack.ss_flags = 0; - sigaltstack(&sigStack, &oldSigStack); - struct sigaction sa = { 0 }; - - sa.sa_handler = handleSignal; - sa.sa_flags = SA_ONSTACK; - for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { - sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); - } - } - - ~FatalConditionHandler() { - reset(); - } - static void reset() { - if( isSet ) { - // Set signals back to previous values -- hopefully nobody overwrote them in the meantime - for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { - sigaction(signalDefs[i].id, &oldSigActions[i], CATCH_NULL); - } - // Return the old stack - sigaltstack(&oldSigStack, CATCH_NULL); - isSet = false; - } - } - }; - - bool FatalConditionHandler::isSet = false; - struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; - stack_t FatalConditionHandler::oldSigStack = {}; - char FatalConditionHandler::altStackMem[SIGSTKSZ] = {}; - -} // namespace Catch - -# endif // CATCH_CONFIG_POSIX_SIGNALS - -#endif // not Windows - -#include -#include - -namespace Catch { - - class StreamRedirect { - - public: - StreamRedirect( std::ostream& stream, std::string& targetString ) - : m_stream( stream ), - m_prevBuf( stream.rdbuf() ), - m_targetString( targetString ) - { - stream.rdbuf( m_oss.rdbuf() ); - } - - ~StreamRedirect() { - m_targetString += m_oss.str(); - m_stream.rdbuf( m_prevBuf ); - } - - private: - std::ostream& m_stream; - std::streambuf* m_prevBuf; - std::ostringstream m_oss; - std::string& m_targetString; - }; - - /////////////////////////////////////////////////////////////////////////// - - class RunContext : public IResultCapture, public IRunner { - - RunContext( RunContext const& ); - void operator =( RunContext const& ); - - public: - - explicit RunContext( Ptr const& _config, Ptr const& reporter ) - : m_runInfo( _config->name() ), - m_context( getCurrentMutableContext() ), - m_activeTestCase( CATCH_NULL ), - m_config( _config ), - m_reporter( reporter ), - m_shouldReportUnexpected ( true ) - { - m_context.setRunner( this ); - m_context.setConfig( m_config ); - m_context.setResultCapture( this ); - m_reporter->testRunStarting( m_runInfo ); - } - - virtual ~RunContext() { - m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) ); - } - - void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) { - m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) ); - } - void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) { - m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) ); - } - - Totals runTest( TestCase const& testCase ) { - Totals prevTotals = m_totals; - - std::string redirectedCout; - std::string redirectedCerr; - - TestCaseInfo testInfo = testCase.getTestCaseInfo(); - - m_reporter->testCaseStarting( testInfo ); - - m_activeTestCase = &testCase; - - do { - ITracker& rootTracker = m_trackerContext.startRun(); - assert( rootTracker.isSectionTracker() ); - static_cast( rootTracker ).addInitialFilters( m_config->getSectionsToRun() ); - do { - m_trackerContext.startCycle(); - m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( testInfo.name, testInfo.lineInfo ) ); - runCurrentTest( redirectedCout, redirectedCerr ); - } - while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() ); - } - // !TBD: deprecated - this will be replaced by indexed trackers - while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() ); - - Totals deltaTotals = m_totals.delta( prevTotals ); - if( testInfo.expectedToFail() && deltaTotals.testCases.passed > 0 ) { - deltaTotals.assertions.failed++; - deltaTotals.testCases.passed--; - deltaTotals.testCases.failed++; - } - m_totals.testCases += deltaTotals.testCases; - m_reporter->testCaseEnded( TestCaseStats( testInfo, - deltaTotals, - redirectedCout, - redirectedCerr, - aborting() ) ); - - m_activeTestCase = CATCH_NULL; - m_testCaseTracker = CATCH_NULL; - - return deltaTotals; - } - - Ptr config() const { - return m_config; - } - - private: // IResultCapture - - virtual void assertionEnded( AssertionResult const& result ) { - if( result.getResultType() == ResultWas::Ok ) { - m_totals.assertions.passed++; - } - else if( !result.isOk() ) { - m_totals.assertions.failed++; - } - - if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) ) - m_messages.clear(); - - // Reset working state - m_lastAssertionInfo = AssertionInfo( std::string(), m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition ); - m_lastResult = result; - } - - virtual bool sectionStarted ( - SectionInfo const& sectionInfo, - Counts& assertions - ) - { - ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( sectionInfo.name, sectionInfo.lineInfo ) ); - if( !sectionTracker.isOpen() ) - return false; - m_activeSections.push_back( §ionTracker ); - - m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; - - m_reporter->sectionStarting( sectionInfo ); - - assertions = m_totals.assertions; - - return true; - } - bool testForMissingAssertions( Counts& assertions ) { - if( assertions.total() != 0 ) - return false; - if( !m_config->warnAboutMissingAssertions() ) - return false; - if( m_trackerContext.currentTracker().hasChildren() ) - return false; - m_totals.assertions.failed++; - assertions.failed++; - return true; - } - - virtual void sectionEnded( SectionEndInfo const& endInfo ) { - Counts assertions = m_totals.assertions - endInfo.prevAssertions; - bool missingAssertions = testForMissingAssertions( assertions ); - - if( !m_activeSections.empty() ) { - m_activeSections.back()->close(); - m_activeSections.pop_back(); - } - - m_reporter->sectionEnded( SectionStats( endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions ) ); - m_messages.clear(); - } - - virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) { - if( m_unfinishedSections.empty() ) - m_activeSections.back()->fail(); - else - m_activeSections.back()->close(); - m_activeSections.pop_back(); - - m_unfinishedSections.push_back( endInfo ); - } - - virtual void pushScopedMessage( MessageInfo const& message ) { - m_messages.push_back( message ); - } - - virtual void popScopedMessage( MessageInfo const& message ) { - m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() ); - } - - virtual std::string getCurrentTestName() const { - return m_activeTestCase - ? m_activeTestCase->getTestCaseInfo().name - : std::string(); - } - - virtual const AssertionResult* getLastResult() const { - return &m_lastResult; - } - - virtual void exceptionEarlyReported() { - m_shouldReportUnexpected = false; - } - - virtual void handleFatalErrorCondition( std::string const& message ) { - // Don't rebuild the result -- the stringification itself can cause more fatal errors - // Instead, fake a result data. - AssertionResultData tempResult; - tempResult.resultType = ResultWas::FatalErrorCondition; - tempResult.message = message; - AssertionResult result(m_lastAssertionInfo, tempResult); - - getResultCapture().assertionEnded(result); - - handleUnfinishedSections(); - - // Recreate section for test case (as we will lose the one that was in scope) - TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); - SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); - - Counts assertions; - assertions.failed = 1; - SectionStats testCaseSectionStats( testCaseSection, assertions, 0, false ); - m_reporter->sectionEnded( testCaseSectionStats ); - - TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo(); - - Totals deltaTotals; - deltaTotals.testCases.failed = 1; - m_reporter->testCaseEnded( TestCaseStats( testInfo, - deltaTotals, - std::string(), - std::string(), - false ) ); - m_totals.testCases.failed++; - testGroupEnded( std::string(), m_totals, 1, 1 ); - m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) ); - } - - public: - // !TBD We need to do this another way! - bool aborting() const { - return m_totals.assertions.failed == static_cast( m_config->abortAfter() ); - } - - private: - - void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) { - TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); - SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); - m_reporter->sectionStarting( testCaseSection ); - Counts prevAssertions = m_totals.assertions; - double duration = 0; - m_shouldReportUnexpected = true; - try { - m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, std::string(), ResultDisposition::Normal ); - - seedRng( *m_config ); - - Timer timer; - timer.start(); - if( m_reporter->getPreferences().shouldRedirectStdOut ) { - StreamRedirect coutRedir( Catch::cout(), redirectedCout ); - StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr ); - invokeActiveTestCase(); - } - else { - invokeActiveTestCase(); - } - duration = timer.getElapsedSeconds(); - } - catch( TestFailureException& ) { - // This just means the test was aborted due to failure - } - catch(...) { - // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions - // are reported without translation at the point of origin. -#ifdef CATCH_CONFIG_FAST_COMPILE - if (m_shouldReportUnexpected) { - makeUnexpectedResultBuilder().useActiveException(); - } -#endif - } - m_testCaseTracker->close(); - handleUnfinishedSections(); - m_messages.clear(); - - Counts assertions = m_totals.assertions - prevAssertions; - bool missingAssertions = testForMissingAssertions( assertions ); - - if( testCaseInfo.okToFail() ) { - std::swap( assertions.failedButOk, assertions.failed ); - m_totals.assertions.failed -= assertions.failedButOk; - m_totals.assertions.failedButOk += assertions.failedButOk; - } - - SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions ); - m_reporter->sectionEnded( testCaseSectionStats ); - } - - void invokeActiveTestCase() { - FatalConditionHandler fatalConditionHandler; // Handle signals - m_activeTestCase->invoke(); - fatalConditionHandler.reset(); - } - - private: - - ResultBuilder makeUnexpectedResultBuilder() const { - return ResultBuilder( m_lastAssertionInfo.macroName.c_str(), - m_lastAssertionInfo.lineInfo, - m_lastAssertionInfo.capturedExpression.c_str(), - m_lastAssertionInfo.resultDisposition ); - } - - void handleUnfinishedSections() { - // If sections ended prematurely due to an exception we stored their - // infos here so we can tear them down outside the unwind process. - for( std::vector::const_reverse_iterator it = m_unfinishedSections.rbegin(), - itEnd = m_unfinishedSections.rend(); - it != itEnd; - ++it ) - sectionEnded( *it ); - m_unfinishedSections.clear(); - } - - TestRunInfo m_runInfo; - IMutableContext& m_context; - TestCase const* m_activeTestCase; - ITracker* m_testCaseTracker; - ITracker* m_currentSectionTracker; - AssertionResult m_lastResult; - - Ptr m_config; - Totals m_totals; - Ptr m_reporter; - std::vector m_messages; - AssertionInfo m_lastAssertionInfo; - std::vector m_unfinishedSections; - std::vector m_activeSections; - TrackerContext m_trackerContext; - bool m_shouldReportUnexpected; - }; - - IResultCapture& getResultCapture() { - if( IResultCapture* capture = getCurrentContext().getResultCapture() ) - return *capture; - else - throw std::logic_error( "No result capture instance" ); - } - -} // end namespace Catch - -// #included from: internal/catch_version.h -#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED - -namespace Catch { - - // Versioning information - struct Version { - Version( unsigned int _majorVersion, - unsigned int _minorVersion, - unsigned int _patchNumber, - char const * const _branchName, - unsigned int _buildNumber ); - - unsigned int const majorVersion; - unsigned int const minorVersion; - unsigned int const patchNumber; - - // buildNumber is only used if branchName is not null - char const * const branchName; - unsigned int const buildNumber; - - friend std::ostream& operator << ( std::ostream& os, Version const& version ); - - private: - void operator=( Version const& ); - }; - - inline Version libraryVersion(); -} - -#include -#include -#include - -namespace Catch { - - Ptr createReporter( std::string const& reporterName, Ptr const& config ) { - Ptr reporter = getRegistryHub().getReporterRegistry().create( reporterName, config.get() ); - if( !reporter ) { - std::ostringstream oss; - oss << "No reporter registered with name: '" << reporterName << "'"; - throw std::domain_error( oss.str() ); - } - return reporter; - } - - Ptr makeReporter( Ptr const& config ) { - std::vector reporters = config->getReporterNames(); - if( reporters.empty() ) - reporters.push_back( "console" ); - - Ptr reporter; - for( std::vector::const_iterator it = reporters.begin(), itEnd = reporters.end(); - it != itEnd; - ++it ) - reporter = addReporter( reporter, createReporter( *it, config ) ); - return reporter; - } - Ptr addListeners( Ptr const& config, Ptr reporters ) { - IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners(); - for( IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end(); - it != itEnd; - ++it ) - reporters = addReporter(reporters, (*it)->create( ReporterConfig( config ) ) ); - return reporters; - } - - Totals runTests( Ptr const& config ) { - - Ptr iconfig = config.get(); - - Ptr reporter = makeReporter( config ); - reporter = addListeners( iconfig, reporter ); - - RunContext context( iconfig, reporter ); - - Totals totals; - - context.testGroupStarting( config->name(), 1, 1 ); - - TestSpec testSpec = config->testSpec(); - if( !testSpec.hasFilters() ) - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests - - std::vector const& allTestCases = getAllTestCasesSorted( *iconfig ); - for( std::vector::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end(); - it != itEnd; - ++it ) { - if( !context.aborting() && matchTest( *it, testSpec, *iconfig ) ) - totals += context.runTest( *it ); - else - reporter->skipTest( *it ); - } - - context.testGroupEnded( iconfig->name(), totals, 1, 1 ); - return totals; - } - - void applyFilenamesAsTags( IConfig const& config ) { - std::vector const& tests = getAllTestCasesSorted( config ); - for(std::size_t i = 0; i < tests.size(); ++i ) { - TestCase& test = const_cast( tests[i] ); - std::set tags = test.tags; - - std::string filename = test.lineInfo.file; - std::string::size_type lastSlash = filename.find_last_of( "\\/" ); - if( lastSlash != std::string::npos ) - filename = filename.substr( lastSlash+1 ); - - std::string::size_type lastDot = filename.find_last_of( "." ); - if( lastDot != std::string::npos ) - filename = filename.substr( 0, lastDot ); - - tags.insert( "#" + filename ); - setTags( test, tags ); - } - } - - class Session : NonCopyable { - static bool alreadyInstantiated; - - public: - - struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; }; - - Session() - : m_cli( makeCommandLineParser() ) { - if( alreadyInstantiated ) { - std::string msg = "Only one instance of Catch::Session can ever be used"; - Catch::cerr() << msg << std::endl; - throw std::logic_error( msg ); - } - alreadyInstantiated = true; - } - ~Session() { - Catch::cleanUp(); - } - - void showHelp( std::string const& processName ) { - Catch::cout() << "\nCatch v" << libraryVersion() << "\n"; - - m_cli.usage( Catch::cout(), processName ); - Catch::cout() << "For more detail usage please see the project docs\n" << std::endl; - } - - int applyCommandLine( int argc, char const* const* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { - try { - m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail ); - m_unusedTokens = m_cli.parseInto( Clara::argsToVector( argc, argv ), m_configData ); - if( m_configData.showHelp ) - showHelp( m_configData.processName ); - m_config.reset(); - } - catch( std::exception& ex ) { - { - Colour colourGuard( Colour::Red ); - Catch::cerr() - << "\nError(s) in input:\n" - << Text( ex.what(), TextAttributes().setIndent(2) ) - << "\n\n"; - } - m_cli.usage( Catch::cout(), m_configData.processName ); - return (std::numeric_limits::max)(); - } - return 0; - } - - void useConfigData( ConfigData const& _configData ) { - m_configData = _configData; - m_config.reset(); - } - - int run( int argc, char const* const* const argv ) { - - int returnCode = applyCommandLine( argc, argv ); - if( returnCode == 0 ) - returnCode = run(); - return returnCode; - } - - int run() { - if( m_configData.showHelp ) - return 0; - - try - { - config(); // Force config to be constructed - - seedRng( *m_config ); - - if( m_configData.filenamesAsTags ) - applyFilenamesAsTags( *m_config ); - - // Handle list request - if( Option listed = list( config() ) ) - return static_cast( *listed ); - - return static_cast( runTests( m_config ).assertions.failed ); - } - catch( std::exception& ex ) { - Catch::cerr() << ex.what() << std::endl; - return (std::numeric_limits::max)(); - } - } - - Clara::CommandLine const& cli() const { - return m_cli; - } - std::vector const& unusedTokens() const { - return m_unusedTokens; - } - ConfigData& configData() { - return m_configData; - } - Config& config() { - if( !m_config ) - m_config = new Config( m_configData ); - return *m_config; - } - private: - Clara::CommandLine m_cli; - std::vector m_unusedTokens; - ConfigData m_configData; - Ptr m_config; - }; - - bool Session::alreadyInstantiated = false; - -} // end namespace Catch - -// #included from: catch_registry_hub.hpp -#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED - -// #included from: catch_test_case_registry_impl.hpp -#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED - -#include -#include -#include -#include - -namespace Catch { - - struct RandomNumberGenerator { - typedef std::ptrdiff_t result_type; - - result_type operator()( result_type n ) const { return std::rand() % n; } - -#ifdef CATCH_CONFIG_CPP11_SHUFFLE - static constexpr result_type min() { return 0; } - static constexpr result_type max() { return 1000000; } - result_type operator()() const { return std::rand() % max(); } -#endif - template - static void shuffle( V& vector ) { - RandomNumberGenerator rng; -#ifdef CATCH_CONFIG_CPP11_SHUFFLE - std::shuffle( vector.begin(), vector.end(), rng ); -#else - std::random_shuffle( vector.begin(), vector.end(), rng ); -#endif - } - }; - - inline std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { - - std::vector sorted = unsortedTestCases; - - switch( config.runOrder() ) { - case RunTests::InLexicographicalOrder: - std::sort( sorted.begin(), sorted.end() ); - break; - case RunTests::InRandomOrder: - { - seedRng( config ); - RandomNumberGenerator::shuffle( sorted ); - } - break; - case RunTests::InDeclarationOrder: - // already in declaration order - break; - } - return sorted; - } - bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { - return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); - } - - void enforceNoDuplicateTestCases( std::vector const& functions ) { - std::set seenFunctions; - for( std::vector::const_iterator it = functions.begin(), itEnd = functions.end(); - it != itEnd; - ++it ) { - std::pair::const_iterator, bool> prev = seenFunctions.insert( *it ); - if( !prev.second ) { - std::ostringstream ss; - - ss << Colour( Colour::Red ) - << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" - << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << '\n' - << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; - - throw std::runtime_error(ss.str()); - } - } - } - - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { - std::vector filtered; - filtered.reserve( testCases.size() ); - for( std::vector::const_iterator it = testCases.begin(), itEnd = testCases.end(); - it != itEnd; - ++it ) - if( matchTest( *it, testSpec, config ) ) - filtered.push_back( *it ); - return filtered; - } - std::vector const& getAllTestCasesSorted( IConfig const& config ) { - return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); - } - - class TestRegistry : public ITestCaseRegistry { - public: - TestRegistry() - : m_currentSortOrder( RunTests::InDeclarationOrder ), - m_unnamedCount( 0 ) - {} - virtual ~TestRegistry(); - - virtual void registerTest( TestCase const& testCase ) { - std::string name = testCase.getTestCaseInfo().name; - if( name.empty() ) { - std::ostringstream oss; - oss << "Anonymous test case " << ++m_unnamedCount; - return registerTest( testCase.withName( oss.str() ) ); - } - m_functions.push_back( testCase ); - } - - virtual std::vector const& getAllTests() const { - return m_functions; - } - virtual std::vector const& getAllTestsSorted( IConfig const& config ) const { - if( m_sortedFunctions.empty() ) - enforceNoDuplicateTestCases( m_functions ); - - if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { - m_sortedFunctions = sortTests( config, m_functions ); - m_currentSortOrder = config.runOrder(); - } - return m_sortedFunctions; - } - - private: - std::vector m_functions; - mutable RunTests::InWhatOrder m_currentSortOrder; - mutable std::vector m_sortedFunctions; - size_t m_unnamedCount; - std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised - }; - - /////////////////////////////////////////////////////////////////////////// - - class FreeFunctionTestCase : public SharedImpl { - public: - - FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {} - - virtual void invoke() const { - m_fun(); - } - - private: - virtual ~FreeFunctionTestCase(); - - TestFunction m_fun; - }; - - inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) { - std::string className = classOrQualifiedMethodName; - if( startsWith( className, '&' ) ) - { - std::size_t lastColons = className.rfind( "::" ); - std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); - if( penultimateColons == std::string::npos ) - penultimateColons = 1; - className = className.substr( penultimateColons, lastColons-penultimateColons ); - } - return className; - } - - void registerTestCase - ( ITestCase* testCase, - char const* classOrQualifiedMethodName, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo ) { - - getMutableRegistryHub().registerTest - ( makeTestCase - ( testCase, - extractClassName( classOrQualifiedMethodName ), - nameAndDesc.name, - nameAndDesc.description, - lineInfo ) ); - } - void registerTestCaseFunction - ( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ) { - registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo ); - } - - /////////////////////////////////////////////////////////////////////////// - - AutoReg::AutoReg - ( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ) { - registerTestCaseFunction( function, lineInfo, nameAndDesc ); - } - - AutoReg::~AutoReg() {} - -} // end namespace Catch - -// #included from: catch_reporter_registry.hpp -#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED - -#include - -namespace Catch { - - class ReporterRegistry : public IReporterRegistry { - - public: - - virtual ~ReporterRegistry() CATCH_OVERRIDE {} - - virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const CATCH_OVERRIDE { - FactoryMap::const_iterator it = m_factories.find( name ); - if( it == m_factories.end() ) - return CATCH_NULL; - return it->second->create( ReporterConfig( config ) ); - } - - void registerReporter( std::string const& name, Ptr const& factory ) { - m_factories.insert( std::make_pair( name, factory ) ); - } - void registerListener( Ptr const& factory ) { - m_listeners.push_back( factory ); - } - - virtual FactoryMap const& getFactories() const CATCH_OVERRIDE { - return m_factories; - } - virtual Listeners const& getListeners() const CATCH_OVERRIDE { - return m_listeners; - } - - private: - FactoryMap m_factories; - Listeners m_listeners; - }; -} - -// #included from: catch_exception_translator_registry.hpp -#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED - -#ifdef __OBJC__ -#import "Foundation/Foundation.h" -#endif - -namespace Catch { - - class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { - public: - ~ExceptionTranslatorRegistry() { - deleteAll( m_translators ); - } - - virtual void registerTranslator( const IExceptionTranslator* translator ) { - m_translators.push_back( translator ); - } - - virtual std::string translateActiveException() const { - try { -#ifdef __OBJC__ - // In Objective-C try objective-c exceptions first - @try { - return tryTranslators(); - } - @catch (NSException *exception) { - return Catch::toString( [exception description] ); - } -#else - return tryTranslators(); -#endif - } - catch( TestFailureException& ) { - throw; - } - catch( std::exception& ex ) { - return ex.what(); - } - catch( std::string& msg ) { - return msg; - } - catch( const char* msg ) { - return msg; - } - catch(...) { - return "Unknown exception"; - } - } - - std::string tryTranslators() const { - if( m_translators.empty() ) - throw; - else - return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() ); - } - - private: - std::vector m_translators; - }; -} - -// #included from: catch_tag_alias_registry.h -#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED - -#include - -namespace Catch { - - class TagAliasRegistry : public ITagAliasRegistry { - public: - virtual ~TagAliasRegistry(); - virtual Option find( std::string const& alias ) const; - virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const; - void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ); - - private: - std::map m_registry; - }; - -} // end namespace Catch - -namespace Catch { - - namespace { - - class RegistryHub : public IRegistryHub, public IMutableRegistryHub { - - RegistryHub( RegistryHub const& ); - void operator=( RegistryHub const& ); - - public: // IRegistryHub - RegistryHub() { - } - virtual IReporterRegistry const& getReporterRegistry() const CATCH_OVERRIDE { - return m_reporterRegistry; - } - virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE { - return m_testCaseRegistry; - } - virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE { - return m_exceptionTranslatorRegistry; - } - virtual ITagAliasRegistry const& getTagAliasRegistry() const CATCH_OVERRIDE { - return m_tagAliasRegistry; - } - - public: // IMutableRegistryHub - virtual void registerReporter( std::string const& name, Ptr const& factory ) CATCH_OVERRIDE { - m_reporterRegistry.registerReporter( name, factory ); - } - virtual void registerListener( Ptr const& factory ) CATCH_OVERRIDE { - m_reporterRegistry.registerListener( factory ); - } - virtual void registerTest( TestCase const& testInfo ) CATCH_OVERRIDE { - m_testCaseRegistry.registerTest( testInfo ); - } - virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE { - m_exceptionTranslatorRegistry.registerTranslator( translator ); - } - virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) CATCH_OVERRIDE { - m_tagAliasRegistry.add( alias, tag, lineInfo ); - } - - private: - TestRegistry m_testCaseRegistry; - ReporterRegistry m_reporterRegistry; - ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; - TagAliasRegistry m_tagAliasRegistry; - }; - - // Single, global, instance - inline RegistryHub*& getTheRegistryHub() { - static RegistryHub* theRegistryHub = CATCH_NULL; - if( !theRegistryHub ) - theRegistryHub = new RegistryHub(); - return theRegistryHub; - } - } - - IRegistryHub& getRegistryHub() { - return *getTheRegistryHub(); - } - IMutableRegistryHub& getMutableRegistryHub() { - return *getTheRegistryHub(); - } - void cleanUp() { - delete getTheRegistryHub(); - getTheRegistryHub() = CATCH_NULL; - cleanUpContext(); - } - std::string translateActiveException() { - return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); - } - -} // end namespace Catch - -// #included from: catch_notimplemented_exception.hpp -#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED - -#include - -namespace Catch { - - NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo ) - : m_lineInfo( lineInfo ) { - std::ostringstream oss; - oss << lineInfo << ": function "; - oss << "not implemented"; - m_what = oss.str(); - } - - const char* NotImplementedException::what() const CATCH_NOEXCEPT { - return m_what.c_str(); - } - -} // end namespace Catch - -// #included from: catch_context_impl.hpp -#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED - -// #included from: catch_stream.hpp -#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED - -#include -#include -#include - -namespace Catch { - - template - class StreamBufImpl : public StreamBufBase { - char data[bufferSize]; - WriterF m_writer; - - public: - StreamBufImpl() { - setp( data, data + sizeof(data) ); - } - - ~StreamBufImpl() CATCH_NOEXCEPT { - sync(); - } - - private: - int overflow( int c ) { - sync(); - - if( c != EOF ) { - if( pbase() == epptr() ) - m_writer( std::string( 1, static_cast( c ) ) ); - else - sputc( static_cast( c ) ); - } - return 0; - } - - int sync() { - if( pbase() != pptr() ) { - m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); - setp( pbase(), epptr() ); - } - return 0; - } - }; - - /////////////////////////////////////////////////////////////////////////// - - FileStream::FileStream( std::string const& filename ) { - m_ofs.open( filename.c_str() ); - if( m_ofs.fail() ) { - std::ostringstream oss; - oss << "Unable to open file: '" << filename << '\''; - throw std::domain_error( oss.str() ); - } - } - - std::ostream& FileStream::stream() const { - return m_ofs; - } - - struct OutputDebugWriter { - - void operator()( std::string const&str ) { - writeToDebugConsole( str ); - } - }; - - DebugOutStream::DebugOutStream() - : m_streamBuf( new StreamBufImpl() ), - m_os( m_streamBuf.get() ) - {} - - std::ostream& DebugOutStream::stream() const { - return m_os; - } - - // Store the streambuf from cout up-front because - // cout may get redirected when running tests - CoutStream::CoutStream() - : m_os( Catch::cout().rdbuf() ) - {} - - std::ostream& CoutStream::stream() const { - return m_os; - } - -#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions - std::ostream& cout() { - return std::cout; - } - std::ostream& cerr() { - return std::cerr; - } -#endif -} - -namespace Catch { - - class Context : public IMutableContext { - - Context() : m_config( CATCH_NULL ), m_runner( CATCH_NULL ), m_resultCapture( CATCH_NULL ) {} - Context( Context const& ); - void operator=( Context const& ); - - public: - virtual ~Context() { - deleteAllValues( m_generatorsByTestName ); - } - - public: // IContext - virtual IResultCapture* getResultCapture() { - return m_resultCapture; - } - virtual IRunner* getRunner() { - return m_runner; - } - virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) { - return getGeneratorsForCurrentTest() - .getGeneratorInfo( fileInfo, totalSize ) - .getCurrentIndex(); - } - virtual bool advanceGeneratorsForCurrentTest() { - IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); - return generators && generators->moveNext(); - } - - virtual Ptr getConfig() const { - return m_config; - } - - public: // IMutableContext - virtual void setResultCapture( IResultCapture* resultCapture ) { - m_resultCapture = resultCapture; - } - virtual void setRunner( IRunner* runner ) { - m_runner = runner; - } - virtual void setConfig( Ptr const& config ) { - m_config = config; - } - - friend IMutableContext& getCurrentMutableContext(); - - private: - IGeneratorsForTest* findGeneratorsForCurrentTest() { - std::string testName = getResultCapture()->getCurrentTestName(); - - std::map::const_iterator it = - m_generatorsByTestName.find( testName ); - return it != m_generatorsByTestName.end() - ? it->second - : CATCH_NULL; - } - - IGeneratorsForTest& getGeneratorsForCurrentTest() { - IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); - if( !generators ) { - std::string testName = getResultCapture()->getCurrentTestName(); - generators = createGeneratorsForTest(); - m_generatorsByTestName.insert( std::make_pair( testName, generators ) ); - } - return *generators; - } - - private: - Ptr m_config; - IRunner* m_runner; - IResultCapture* m_resultCapture; - std::map m_generatorsByTestName; - }; - - namespace { - Context* currentContext = CATCH_NULL; - } - IMutableContext& getCurrentMutableContext() { - if( !currentContext ) - currentContext = new Context(); - return *currentContext; - } - IContext& getCurrentContext() { - return getCurrentMutableContext(); - } - - void cleanUpContext() { - delete currentContext; - currentContext = CATCH_NULL; - } -} - -// #included from: catch_console_colour_impl.hpp -#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED - -// #included from: catch_errno_guard.hpp -#define TWOBLUECUBES_CATCH_ERRNO_GUARD_HPP_INCLUDED - -#include - -namespace Catch { - - class ErrnoGuard { - public: - ErrnoGuard():m_oldErrno(errno){} - ~ErrnoGuard() { errno = m_oldErrno; } - private: - int m_oldErrno; - }; - -} - -namespace Catch { - namespace { - - struct IColourImpl { - virtual ~IColourImpl() {} - virtual void use( Colour::Code _colourCode ) = 0; - }; - - struct NoColourImpl : IColourImpl { - void use( Colour::Code ) {} - - static IColourImpl* instance() { - static NoColourImpl s_instance; - return &s_instance; - } - }; - - } // anon namespace -} // namespace Catch - -#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI ) -# ifdef CATCH_PLATFORM_WINDOWS -# define CATCH_CONFIG_COLOUR_WINDOWS -# else -# define CATCH_CONFIG_COLOUR_ANSI -# endif -#endif - -#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// - -namespace Catch { -namespace { - - class Win32ColourImpl : public IColourImpl { - public: - Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) ) - { - CONSOLE_SCREEN_BUFFER_INFO csbiInfo; - GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); - originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY ); - originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); - } - - virtual void use( Colour::Code _colourCode ) { - switch( _colourCode ) { - case Colour::None: return setTextAttribute( originalForegroundAttributes ); - case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); - case Colour::Red: return setTextAttribute( FOREGROUND_RED ); - case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); - case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); - case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); - case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); - case Colour::Grey: return setTextAttribute( 0 ); - - case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); - case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); - case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); - case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); - - case Colour::Bright: throw std::logic_error( "not a colour" ); - } - } - - private: - void setTextAttribute( WORD _textAttribute ) { - SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes ); - } - HANDLE stdoutHandle; - WORD originalForegroundAttributes; - WORD originalBackgroundAttributes; - }; - - IColourImpl* platformColourInstance() { - static Win32ColourImpl s_instance; - - Ptr config = getCurrentContext().getConfig(); - UseColour::YesOrNo colourMode = config - ? config->useColour() - : UseColour::Auto; - if( colourMode == UseColour::Auto ) - colourMode = !isDebuggerActive() - ? UseColour::Yes - : UseColour::No; - return colourMode == UseColour::Yes - ? &s_instance - : NoColourImpl::instance(); - } - -} // end anon namespace -} // end namespace Catch - -#elif defined( CATCH_CONFIG_COLOUR_ANSI ) ////////////////////////////////////// - -#include - -namespace Catch { -namespace { - - // use POSIX/ ANSI console terminal codes - // Thanks to Adam Strzelecki for original contribution - // (http://github.com/nanoant) - // https://github.com/philsquared/Catch/pull/131 - class PosixColourImpl : public IColourImpl { - public: - virtual void use( Colour::Code _colourCode ) { - switch( _colourCode ) { - case Colour::None: - case Colour::White: return setColour( "[0m" ); - case Colour::Red: return setColour( "[0;31m" ); - case Colour::Green: return setColour( "[0;32m" ); - case Colour::Blue: return setColour( "[0;34m" ); - case Colour::Cyan: return setColour( "[0;36m" ); - case Colour::Yellow: return setColour( "[0;33m" ); - case Colour::Grey: return setColour( "[1;30m" ); - - case Colour::LightGrey: return setColour( "[0;37m" ); - case Colour::BrightRed: return setColour( "[1;31m" ); - case Colour::BrightGreen: return setColour( "[1;32m" ); - case Colour::BrightWhite: return setColour( "[1;37m" ); - - case Colour::Bright: throw std::logic_error( "not a colour" ); - } - } - static IColourImpl* instance() { - static PosixColourImpl s_instance; - return &s_instance; - } - - private: - void setColour( const char* _escapeCode ) { - Catch::cout() << '\033' << _escapeCode; - } - }; - - IColourImpl* platformColourInstance() { - ErrnoGuard guard; - Ptr config = getCurrentContext().getConfig(); - UseColour::YesOrNo colourMode = config - ? config->useColour() - : UseColour::Auto; - if( colourMode == UseColour::Auto ) - colourMode = (!isDebuggerActive() && isatty(STDOUT_FILENO) ) - ? UseColour::Yes - : UseColour::No; - return colourMode == UseColour::Yes - ? PosixColourImpl::instance() - : NoColourImpl::instance(); - } - -} // end anon namespace -} // end namespace Catch - -#else // not Windows or ANSI /////////////////////////////////////////////// - -namespace Catch { - - static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); } - -} // end namespace Catch - -#endif // Windows/ ANSI/ None - -namespace Catch { - - Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); } - Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast( _other ).m_moved = true; } - Colour::~Colour(){ if( !m_moved ) use( None ); } - - void Colour::use( Code _colourCode ) { - static IColourImpl* impl = platformColourInstance(); - impl->use( _colourCode ); - } - -} // end namespace Catch - -// #included from: catch_generators_impl.hpp -#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED - -#include -#include -#include - -namespace Catch { - - struct GeneratorInfo : IGeneratorInfo { - - GeneratorInfo( std::size_t size ) - : m_size( size ), - m_currentIndex( 0 ) - {} - - bool moveNext() { - if( ++m_currentIndex == m_size ) { - m_currentIndex = 0; - return false; - } - return true; - } - - std::size_t getCurrentIndex() const { - return m_currentIndex; - } - - std::size_t m_size; - std::size_t m_currentIndex; - }; - - /////////////////////////////////////////////////////////////////////////// - - class GeneratorsForTest : public IGeneratorsForTest { - - public: - ~GeneratorsForTest() { - deleteAll( m_generatorsInOrder ); - } - - IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) { - std::map::const_iterator it = m_generatorsByName.find( fileInfo ); - if( it == m_generatorsByName.end() ) { - IGeneratorInfo* info = new GeneratorInfo( size ); - m_generatorsByName.insert( std::make_pair( fileInfo, info ) ); - m_generatorsInOrder.push_back( info ); - return *info; - } - return *it->second; - } - - bool moveNext() { - std::vector::const_iterator it = m_generatorsInOrder.begin(); - std::vector::const_iterator itEnd = m_generatorsInOrder.end(); - for(; it != itEnd; ++it ) { - if( (*it)->moveNext() ) - return true; - } - return false; - } - - private: - std::map m_generatorsByName; - std::vector m_generatorsInOrder; - }; - - IGeneratorsForTest* createGeneratorsForTest() - { - return new GeneratorsForTest(); - } - -} // end namespace Catch - -// #included from: catch_assertionresult.hpp -#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED - -namespace Catch { - - AssertionInfo::AssertionInfo( std::string const& _macroName, - SourceLineInfo const& _lineInfo, - std::string const& _capturedExpression, - ResultDisposition::Flags _resultDisposition ) - : macroName( _macroName ), - lineInfo( _lineInfo ), - capturedExpression( _capturedExpression ), - resultDisposition( _resultDisposition ) - {} - - AssertionResult::AssertionResult() {} - - AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) - : m_info( info ), - m_resultData( data ) - {} - - AssertionResult::~AssertionResult() {} - - // Result was a success - bool AssertionResult::succeeded() const { - return Catch::isOk( m_resultData.resultType ); - } - - // Result was a success, or failure is suppressed - bool AssertionResult::isOk() const { - return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); - } - - ResultWas::OfType AssertionResult::getResultType() const { - return m_resultData.resultType; - } - - bool AssertionResult::hasExpression() const { - return !m_info.capturedExpression.empty(); - } - - bool AssertionResult::hasMessage() const { - return !m_resultData.message.empty(); - } - - std::string AssertionResult::getExpression() const { - if( isFalseTest( m_info.resultDisposition ) ) - return '!' + m_info.capturedExpression; - else - return m_info.capturedExpression; - } - std::string AssertionResult::getExpressionInMacro() const { - if( m_info.macroName.empty() ) - return m_info.capturedExpression; - else - return m_info.macroName + "( " + m_info.capturedExpression + " )"; - } - - bool AssertionResult::hasExpandedExpression() const { - return hasExpression() && getExpandedExpression() != getExpression(); - } - - std::string AssertionResult::getExpandedExpression() const { - return m_resultData.reconstructExpression(); - } - - std::string AssertionResult::getMessage() const { - return m_resultData.message; - } - SourceLineInfo AssertionResult::getSourceInfo() const { - return m_info.lineInfo; - } - - std::string AssertionResult::getTestMacroName() const { - return m_info.macroName; - } - - void AssertionResult::discardDecomposedExpression() const { - m_resultData.decomposedExpression = CATCH_NULL; - } - - void AssertionResult::expandDecomposedExpression() const { - m_resultData.reconstructExpression(); - } - -} // end namespace Catch - -// #included from: catch_test_case_info.hpp -#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED - -#include - -namespace Catch { - - inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { - if( startsWith( tag, '.' ) || - tag == "hide" || - tag == "!hide" ) - return TestCaseInfo::IsHidden; - else if( tag == "!throws" ) - return TestCaseInfo::Throws; - else if( tag == "!shouldfail" ) - return TestCaseInfo::ShouldFail; - else if( tag == "!mayfail" ) - return TestCaseInfo::MayFail; - else if( tag == "!nonportable" ) - return TestCaseInfo::NonPortable; - else - return TestCaseInfo::None; - } - inline bool isReservedTag( std::string const& tag ) { - return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] ); - } - inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { - if( isReservedTag( tag ) ) { - std::ostringstream ss; - ss << Colour(Colour::Red) - << "Tag name [" << tag << "] not allowed.\n" - << "Tag names starting with non alpha-numeric characters are reserved\n" - << Colour(Colour::FileName) - << _lineInfo << '\n'; - throw std::runtime_error(ss.str()); - } - } - - TestCase makeTestCase( ITestCase* _testCase, - std::string const& _className, - std::string const& _name, - std::string const& _descOrTags, - SourceLineInfo const& _lineInfo ) - { - bool isHidden( startsWith( _name, "./" ) ); // Legacy support - - // Parse out tags - std::set tags; - std::string desc, tag; - bool inTag = false; - for( std::size_t i = 0; i < _descOrTags.size(); ++i ) { - char c = _descOrTags[i]; - if( !inTag ) { - if( c == '[' ) - inTag = true; - else - desc += c; - } - else { - if( c == ']' ) { - TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); - if( prop == TestCaseInfo::IsHidden ) - isHidden = true; - else if( prop == TestCaseInfo::None ) - enforceNotReservedTag( tag, _lineInfo ); - - tags.insert( tag ); - tag.clear(); - inTag = false; - } - else - tag += c; - } - } - if( isHidden ) { - tags.insert( "hide" ); - tags.insert( "." ); - } - - TestCaseInfo info( _name, _className, desc, tags, _lineInfo ); - return TestCase( _testCase, info ); - } - - void setTags( TestCaseInfo& testCaseInfo, std::set const& tags ) - { - testCaseInfo.tags = tags; - testCaseInfo.lcaseTags.clear(); - - std::ostringstream oss; - for( std::set::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) { - oss << '[' << *it << ']'; - std::string lcaseTag = toLower( *it ); - testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); - testCaseInfo.lcaseTags.insert( lcaseTag ); - } - testCaseInfo.tagsAsString = oss.str(); - } - - TestCaseInfo::TestCaseInfo( std::string const& _name, - std::string const& _className, - std::string const& _description, - std::set const& _tags, - SourceLineInfo const& _lineInfo ) - : name( _name ), - className( _className ), - description( _description ), - lineInfo( _lineInfo ), - properties( None ) - { - setTags( *this, _tags ); - } - - TestCaseInfo::TestCaseInfo( TestCaseInfo const& other ) - : name( other.name ), - className( other.className ), - description( other.description ), - tags( other.tags ), - lcaseTags( other.lcaseTags ), - tagsAsString( other.tagsAsString ), - lineInfo( other.lineInfo ), - properties( other.properties ) - {} - - bool TestCaseInfo::isHidden() const { - return ( properties & IsHidden ) != 0; - } - bool TestCaseInfo::throws() const { - return ( properties & Throws ) != 0; - } - bool TestCaseInfo::okToFail() const { - return ( properties & (ShouldFail | MayFail ) ) != 0; - } - bool TestCaseInfo::expectedToFail() const { - return ( properties & (ShouldFail ) ) != 0; - } - - TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} - - TestCase::TestCase( TestCase const& other ) - : TestCaseInfo( other ), - test( other.test ) - {} - - TestCase TestCase::withName( std::string const& _newName ) const { - TestCase other( *this ); - other.name = _newName; - return other; - } - - void TestCase::swap( TestCase& other ) { - test.swap( other.test ); - name.swap( other.name ); - className.swap( other.className ); - description.swap( other.description ); - tags.swap( other.tags ); - lcaseTags.swap( other.lcaseTags ); - tagsAsString.swap( other.tagsAsString ); - std::swap( TestCaseInfo::properties, static_cast( other ).properties ); - std::swap( lineInfo, other.lineInfo ); - } - - void TestCase::invoke() const { - test->invoke(); - } - - bool TestCase::operator == ( TestCase const& other ) const { - return test.get() == other.test.get() && - name == other.name && - className == other.className; - } - - bool TestCase::operator < ( TestCase const& other ) const { - return name < other.name; - } - TestCase& TestCase::operator = ( TestCase const& other ) { - TestCase temp( other ); - swap( temp ); - return *this; - } - - TestCaseInfo const& TestCase::getTestCaseInfo() const - { - return *this; - } - -} // end namespace Catch - -// #included from: catch_version.hpp -#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED - -namespace Catch { - - Version::Version - ( unsigned int _majorVersion, - unsigned int _minorVersion, - unsigned int _patchNumber, - char const * const _branchName, - unsigned int _buildNumber ) - : majorVersion( _majorVersion ), - minorVersion( _minorVersion ), - patchNumber( _patchNumber ), - branchName( _branchName ), - buildNumber( _buildNumber ) - {} - - std::ostream& operator << ( std::ostream& os, Version const& version ) { - os << version.majorVersion << '.' - << version.minorVersion << '.' - << version.patchNumber; - // branchName is never null -> 0th char is \0 if it is empty - if (version.branchName[0]) { - os << '-' << version.branchName - << '.' << version.buildNumber; - } - return os; - } - - inline Version libraryVersion() { - static Version version( 1, 9, 0, "", 0 ); - return version; - } - -} - -// #included from: catch_message.hpp -#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED - -namespace Catch { - - MessageInfo::MessageInfo( std::string const& _macroName, - SourceLineInfo const& _lineInfo, - ResultWas::OfType _type ) - : macroName( _macroName ), - lineInfo( _lineInfo ), - type( _type ), - sequence( ++globalCount ) - {} - - // This may need protecting if threading support is added - unsigned int MessageInfo::globalCount = 0; - - //////////////////////////////////////////////////////////////////////////// - - ScopedMessage::ScopedMessage( MessageBuilder const& builder ) - : m_info( builder.m_info ) - { - m_info.message = builder.m_stream.str(); - getResultCapture().pushScopedMessage( m_info ); - } - ScopedMessage::ScopedMessage( ScopedMessage const& other ) - : m_info( other.m_info ) - {} - - ScopedMessage::~ScopedMessage() { - if ( !std::uncaught_exception() ){ - getResultCapture().popScopedMessage(m_info); - } - } - -} // end namespace Catch - -// #included from: catch_legacy_reporter_adapter.hpp -#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED - -// #included from: catch_legacy_reporter_adapter.h -#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED - -namespace Catch -{ - // Deprecated - struct IReporter : IShared { - virtual ~IReporter(); - - virtual bool shouldRedirectStdout() const = 0; - - virtual void StartTesting() = 0; - virtual void EndTesting( Totals const& totals ) = 0; - virtual void StartGroup( std::string const& groupName ) = 0; - virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0; - virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0; - virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0; - virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0; - virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0; - virtual void NoAssertionsInSection( std::string const& sectionName ) = 0; - virtual void NoAssertionsInTestCase( std::string const& testName ) = 0; - virtual void Aborted() = 0; - virtual void Result( AssertionResult const& result ) = 0; - }; - - class LegacyReporterAdapter : public SharedImpl - { - public: - LegacyReporterAdapter( Ptr const& legacyReporter ); - virtual ~LegacyReporterAdapter(); - - virtual ReporterPreferences getPreferences() const; - virtual void noMatchingTestCases( std::string const& ); - virtual void testRunStarting( TestRunInfo const& ); - virtual void testGroupStarting( GroupInfo const& groupInfo ); - virtual void testCaseStarting( TestCaseInfo const& testInfo ); - virtual void sectionStarting( SectionInfo const& sectionInfo ); - virtual void assertionStarting( AssertionInfo const& ); - virtual bool assertionEnded( AssertionStats const& assertionStats ); - virtual void sectionEnded( SectionStats const& sectionStats ); - virtual void testCaseEnded( TestCaseStats const& testCaseStats ); - virtual void testGroupEnded( TestGroupStats const& testGroupStats ); - virtual void testRunEnded( TestRunStats const& testRunStats ); - virtual void skipTest( TestCaseInfo const& ); - - private: - Ptr m_legacyReporter; - }; -} - -namespace Catch -{ - LegacyReporterAdapter::LegacyReporterAdapter( Ptr const& legacyReporter ) - : m_legacyReporter( legacyReporter ) - {} - LegacyReporterAdapter::~LegacyReporterAdapter() {} - - ReporterPreferences LegacyReporterAdapter::getPreferences() const { - ReporterPreferences prefs; - prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout(); - return prefs; - } - - void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {} - void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) { - m_legacyReporter->StartTesting(); - } - void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) { - m_legacyReporter->StartGroup( groupInfo.name ); - } - void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) { - m_legacyReporter->StartTestCase( testInfo ); - } - void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) { - m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description ); - } - void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) { - // Not on legacy interface - } - - bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) { - if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { - for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); - it != itEnd; - ++it ) { - if( it->type == ResultWas::Info ) { - ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal ); - rb << it->message; - rb.setResultType( ResultWas::Info ); - AssertionResult result = rb.build(); - m_legacyReporter->Result( result ); - } - } - } - m_legacyReporter->Result( assertionStats.assertionResult ); - return true; - } - void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) { - if( sectionStats.missingAssertions ) - m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name ); - m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions ); - } - void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) { - m_legacyReporter->EndTestCase - ( testCaseStats.testInfo, - testCaseStats.totals, - testCaseStats.stdOut, - testCaseStats.stdErr ); - } - void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) { - if( testGroupStats.aborting ) - m_legacyReporter->Aborted(); - m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals ); - } - void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) { - m_legacyReporter->EndTesting( testRunStats.totals ); - } - void LegacyReporterAdapter::skipTest( TestCaseInfo const& ) { - } -} - -// #included from: catch_timer.hpp - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wc++11-long-long" -#endif - -#ifdef CATCH_PLATFORM_WINDOWS - -#else - -#include - -#endif - -namespace Catch { - - namespace { -#ifdef CATCH_PLATFORM_WINDOWS - uint64_t getCurrentTicks() { - static uint64_t hz=0, hzo=0; - if (!hz) { - QueryPerformanceFrequency( reinterpret_cast( &hz ) ); - QueryPerformanceCounter( reinterpret_cast( &hzo ) ); - } - uint64_t t; - QueryPerformanceCounter( reinterpret_cast( &t ) ); - return ((t-hzo)*1000000)/hz; - } -#else - uint64_t getCurrentTicks() { - timeval t; - gettimeofday(&t,CATCH_NULL); - return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); - } -#endif - } - - void Timer::start() { - m_ticks = getCurrentTicks(); - } - unsigned int Timer::getElapsedMicroseconds() const { - return static_cast(getCurrentTicks() - m_ticks); - } - unsigned int Timer::getElapsedMilliseconds() const { - return static_cast(getElapsedMicroseconds()/1000); - } - double Timer::getElapsedSeconds() const { - return getElapsedMicroseconds()/1000000.0; - } - -} // namespace Catch - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif -// #included from: catch_common.hpp -#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED - -#include -#include - -namespace Catch { - - bool startsWith( std::string const& s, std::string const& prefix ) { - return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); - } - bool startsWith( std::string const& s, char prefix ) { - return !s.empty() && s[0] == prefix; - } - bool endsWith( std::string const& s, std::string const& suffix ) { - return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); - } - bool endsWith( std::string const& s, char suffix ) { - return !s.empty() && s[s.size()-1] == suffix; - } - bool contains( std::string const& s, std::string const& infix ) { - return s.find( infix ) != std::string::npos; - } - char toLowerCh(char c) { - return static_cast( std::tolower( c ) ); - } - void toLowerInPlace( std::string& s ) { - std::transform( s.begin(), s.end(), s.begin(), toLowerCh ); - } - std::string toLower( std::string const& s ) { - std::string lc = s; - toLowerInPlace( lc ); - return lc; - } - std::string trim( std::string const& str ) { - static char const* whitespaceChars = "\n\r\t "; - std::string::size_type start = str.find_first_not_of( whitespaceChars ); - std::string::size_type end = str.find_last_not_of( whitespaceChars ); - - return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string(); - } - - bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { - bool replaced = false; - std::size_t i = str.find( replaceThis ); - while( i != std::string::npos ) { - replaced = true; - str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() ); - if( i < str.size()-withThis.size() ) - i = str.find( replaceThis, i+withThis.size() ); - else - i = std::string::npos; - } - return replaced; - } - - pluralise::pluralise( std::size_t count, std::string const& label ) - : m_count( count ), - m_label( label ) - {} - - std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { - os << pluraliser.m_count << ' ' << pluraliser.m_label; - if( pluraliser.m_count != 1 ) - os << 's'; - return os; - } - - SourceLineInfo::SourceLineInfo() : file(""), line( 0 ){} - SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) - : file( _file ), - line( _line ) - {} - bool SourceLineInfo::empty() const { - return file[0] == '\0'; - } - bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const { - return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); - } - bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const { - return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0)); - } - - void seedRng( IConfig const& config ) { - if( config.rngSeed() != 0 ) - std::srand( config.rngSeed() ); - } - unsigned int rngSeed() { - return getCurrentContext().getConfig()->rngSeed(); - } - - std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { -#ifndef __GNUG__ - os << info.file << '(' << info.line << ')'; -#else - os << info.file << ':' << info.line; -#endif - return os; - } - - void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) { - std::ostringstream oss; - oss << locationInfo << ": Internal Catch error: '" << message << '\''; - if( alwaysTrue() ) - throw std::logic_error( oss.str() ); - } -} - -// #included from: catch_section.hpp -#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED - -namespace Catch { - - SectionInfo::SectionInfo - ( SourceLineInfo const& _lineInfo, - std::string const& _name, - std::string const& _description ) - : name( _name ), - description( _description ), - lineInfo( _lineInfo ) - {} - - Section::Section( SectionInfo const& info ) - : m_info( info ), - m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) - { - m_timer.start(); - } - - Section::~Section() { - if( m_sectionIncluded ) { - SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); - if( std::uncaught_exception() ) - getResultCapture().sectionEndedEarly( endInfo ); - else - getResultCapture().sectionEnded( endInfo ); - } - } - - // This indicates whether the section should be executed or not - Section::operator bool() const { - return m_sectionIncluded; - } - -} // end namespace Catch - -// #included from: catch_debugger.hpp -#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED - -#ifdef CATCH_PLATFORM_MAC - - #include - #include - #include - #include - #include - - namespace Catch{ - - // The following function is taken directly from the following technical note: - // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html - - // Returns true if the current process is being debugged (either - // running under the debugger or has a debugger attached post facto). - bool isDebuggerActive(){ - - int mib[4]; - struct kinfo_proc info; - size_t size; - - // Initialize the flags so that, if sysctl fails for some bizarre - // reason, we get a predictable result. - - info.kp_proc.p_flag = 0; - - // Initialize mib, which tells sysctl the info we want, in this case - // we're looking for information about a specific process ID. - - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; - mib[3] = getpid(); - - // Call sysctl. - - size = sizeof(info); - if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, CATCH_NULL, 0) != 0 ) { - Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; - return false; - } - - // We're being debugged if the P_TRACED flag is set. - - return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); - } - } // namespace Catch - -#elif defined(CATCH_PLATFORM_LINUX) - #include - #include - - namespace Catch{ - // The standard POSIX way of detecting a debugger is to attempt to - // ptrace() the process, but this needs to be done from a child and not - // this process itself to still allow attaching to this process later - // if wanted, so is rather heavy. Under Linux we have the PID of the - // "debugger" (which doesn't need to be gdb, of course, it could also - // be strace, for example) in /proc/$PID/status, so just get it from - // there instead. - bool isDebuggerActive(){ - // Libstdc++ has a bug, where std::ifstream sets errno to 0 - // This way our users can properly assert over errno values - ErrnoGuard guard; - std::ifstream in("/proc/self/status"); - for( std::string line; std::getline(in, line); ) { - static const int PREFIX_LEN = 11; - if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { - // We're traced if the PID is not 0 and no other PID starts - // with 0 digit, so it's enough to check for just a single - // character. - return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; - } - } - - return false; - } - } // namespace Catch -#elif defined(_MSC_VER) - extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); - namespace Catch { - bool isDebuggerActive() { - return IsDebuggerPresent() != 0; - } - } -#elif defined(__MINGW32__) - extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); - namespace Catch { - bool isDebuggerActive() { - return IsDebuggerPresent() != 0; - } - } -#else - namespace Catch { - inline bool isDebuggerActive() { return false; } - } -#endif // Platform - -#ifdef CATCH_PLATFORM_WINDOWS - - namespace Catch { - void writeToDebugConsole( std::string const& text ) { - ::OutputDebugStringA( text.c_str() ); - } - } -#else - namespace Catch { - void writeToDebugConsole( std::string const& text ) { - // !TBD: Need a version for Mac/ XCode and other IDEs - Catch::cout() << text; - } - } -#endif // Platform - -// #included from: catch_tostring.hpp -#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED - -namespace Catch { - -namespace Detail { - - const std::string unprintableString = "{?}"; - - namespace { - const int hexThreshold = 255; - - struct Endianness { - enum Arch { Big, Little }; - - static Arch which() { - union _{ - int asInt; - char asChar[sizeof (int)]; - } u; - - u.asInt = 1; - return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little; - } - }; - } - - std::string rawMemoryToString( const void *object, std::size_t size ) - { - // Reverse order for little endian architectures - int i = 0, end = static_cast( size ), inc = 1; - if( Endianness::which() == Endianness::Little ) { - i = end-1; - end = inc = -1; - } - - unsigned char const *bytes = static_cast(object); - std::ostringstream os; - os << "0x" << std::setfill('0') << std::hex; - for( ; i != end; i += inc ) - os << std::setw(2) << static_cast(bytes[i]); - return os.str(); - } -} - -std::string toString( std::string const& value ) { - std::string s = value; - if( getCurrentContext().getConfig()->showInvisibles() ) { - for(size_t i = 0; i < s.size(); ++i ) { - std::string subs; - switch( s[i] ) { - case '\n': subs = "\\n"; break; - case '\t': subs = "\\t"; break; - default: break; - } - if( !subs.empty() ) { - s = s.substr( 0, i ) + subs + s.substr( i+1 ); - ++i; - } - } - } - return '"' + s + '"'; -} -std::string toString( std::wstring const& value ) { - - std::string s; - s.reserve( value.size() ); - for(size_t i = 0; i < value.size(); ++i ) - s += value[i] <= 0xff ? static_cast( value[i] ) : '?'; - return Catch::toString( s ); -} - -std::string toString( const char* const value ) { - return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" ); -} - -std::string toString( char* const value ) { - return Catch::toString( static_cast( value ) ); -} - -std::string toString( const wchar_t* const value ) -{ - return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" ); -} - -std::string toString( wchar_t* const value ) -{ - return Catch::toString( static_cast( value ) ); -} - -std::string toString( int value ) { - std::ostringstream oss; - oss << value; - if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ')'; - return oss.str(); -} - -std::string toString( unsigned long value ) { - std::ostringstream oss; - oss << value; - if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ')'; - return oss.str(); -} - -std::string toString( unsigned int value ) { - return Catch::toString( static_cast( value ) ); -} - -template -std::string fpToString( T value, int precision ) { - std::ostringstream oss; - oss << std::setprecision( precision ) - << std::fixed - << value; - std::string d = oss.str(); - std::size_t i = d.find_last_not_of( '0' ); - if( i != std::string::npos && i != d.size()-1 ) { - if( d[i] == '.' ) - i++; - d = d.substr( 0, i+1 ); - } - return d; -} - -std::string toString( const double value ) { - return fpToString( value, 10 ); -} -std::string toString( const float value ) { - return fpToString( value, 5 ) + 'f'; -} - -std::string toString( bool value ) { - return value ? "true" : "false"; -} - -std::string toString( char value ) { - if ( value == '\r' ) - return "'\\r'"; - if ( value == '\f' ) - return "'\\f'"; - if ( value == '\n' ) - return "'\\n'"; - if ( value == '\t' ) - return "'\\t'"; - if ( '\0' <= value && value < ' ' ) - return toString( static_cast( value ) ); - char chstr[] = "' '"; - chstr[1] = value; - return chstr; -} - -std::string toString( signed char value ) { - return toString( static_cast( value ) ); -} - -std::string toString( unsigned char value ) { - return toString( static_cast( value ) ); -} - -#ifdef CATCH_CONFIG_CPP11_LONG_LONG -std::string toString( long long value ) { - std::ostringstream oss; - oss << value; - if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ')'; - return oss.str(); -} -std::string toString( unsigned long long value ) { - std::ostringstream oss; - oss << value; - if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ')'; - return oss.str(); -} -#endif - -#ifdef CATCH_CONFIG_CPP11_NULLPTR -std::string toString( std::nullptr_t ) { - return "nullptr"; -} -#endif - -#ifdef __OBJC__ - std::string toString( NSString const * const& nsstring ) { - if( !nsstring ) - return "nil"; - return "@" + toString([nsstring UTF8String]); - } - std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) { - if( !nsstring ) - return "nil"; - return "@" + toString([nsstring UTF8String]); - } - std::string toString( NSObject* const& nsObject ) { - return toString( [nsObject description] ); - } -#endif - -} // end namespace Catch - -// #included from: catch_result_builder.hpp -#define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED - -namespace Catch { - - std::string capturedExpressionWithSecondArgument( std::string const& capturedExpression, std::string const& secondArg ) { - return secondArg.empty() || secondArg == "\"\"" - ? capturedExpression - : capturedExpression + ", " + secondArg; - } - ResultBuilder::ResultBuilder( char const* macroName, - SourceLineInfo const& lineInfo, - char const* capturedExpression, - ResultDisposition::Flags resultDisposition, - char const* secondArg ) - : m_assertionInfo( macroName, lineInfo, capturedExpressionWithSecondArgument( capturedExpression, secondArg ), resultDisposition ), - m_shouldDebugBreak( false ), - m_shouldThrow( false ), - m_guardException( false ) - {} - - ResultBuilder::~ResultBuilder() { -#if defined(CATCH_CONFIG_FAST_COMPILE) - if ( m_guardException ) { - m_stream.oss << "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; - captureResult( ResultWas::ThrewException ); - getCurrentContext().getResultCapture()->exceptionEarlyReported(); - } -#endif - } - - ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) { - m_data.resultType = result; - return *this; - } - ResultBuilder& ResultBuilder::setResultType( bool result ) { - m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed; - return *this; - } - - void ResultBuilder::endExpression( DecomposedExpression const& expr ) { - AssertionResult result = build( expr ); - handleResult( result ); - } - - void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) { - m_assertionInfo.resultDisposition = resultDisposition; - m_stream.oss << Catch::translateActiveException(); - captureResult( ResultWas::ThrewException ); - } - - void ResultBuilder::captureResult( ResultWas::OfType resultType ) { - setResultType( resultType ); - captureExpression(); - } - - void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) { - if( expectedMessage.empty() ) - captureExpectedException( Matchers::Impl::MatchAllOf() ); - else - captureExpectedException( Matchers::Equals( expectedMessage ) ); - } - - void ResultBuilder::captureExpectedException( Matchers::Impl::MatcherBase const& matcher ) { - - assert( !isFalseTest( m_assertionInfo.resultDisposition ) ); - AssertionResultData data = m_data; - data.resultType = ResultWas::Ok; - data.reconstructedExpression = m_assertionInfo.capturedExpression; - - std::string actualMessage = Catch::translateActiveException(); - if( !matcher.match( actualMessage ) ) { - data.resultType = ResultWas::ExpressionFailed; - data.reconstructedExpression = actualMessage; - } - AssertionResult result( m_assertionInfo, data ); - handleResult( result ); - } - - void ResultBuilder::captureExpression() { - AssertionResult result = build(); - handleResult( result ); - } - - void ResultBuilder::handleResult( AssertionResult const& result ) - { - getResultCapture().assertionEnded( result ); - - if( !result.isOk() ) { - if( getCurrentContext().getConfig()->shouldDebugBreak() ) - m_shouldDebugBreak = true; - if( getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal) ) - m_shouldThrow = true; - } - } - - void ResultBuilder::react() { -#if defined(CATCH_CONFIG_FAST_COMPILE) - if (m_shouldDebugBreak) { - /////////////////////////////////////////////////////////////////// - // To inspect the state during test, you need to go one level up the callstack - // To go back to the test and change execution, jump over the throw statement - /////////////////////////////////////////////////////////////////// - CATCH_BREAK_INTO_DEBUGGER(); - } -#endif - if( m_shouldThrow ) - throw Catch::TestFailureException(); - } - - bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; } - bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); } - - AssertionResult ResultBuilder::build() const - { - return build( *this ); - } - - // CAVEAT: The returned AssertionResult stores a pointer to the argument expr, - // a temporary DecomposedExpression, which in turn holds references to - // operands, possibly temporary as well. - // It should immediately be passed to handleResult; if the expression - // needs to be reported, its string expansion must be composed before - // the temporaries are destroyed. - AssertionResult ResultBuilder::build( DecomposedExpression const& expr ) const - { - assert( m_data.resultType != ResultWas::Unknown ); - AssertionResultData data = m_data; - - // Flip bool results if FalseTest flag is set - if( isFalseTest( m_assertionInfo.resultDisposition ) ) { - data.negate( expr.isBinaryExpression() ); - } - - data.message = m_stream.oss.str(); - data.decomposedExpression = &expr; // for lazy reconstruction - return AssertionResult( m_assertionInfo, data ); - } - - void ResultBuilder::reconstructExpression( std::string& dest ) const { - dest = m_assertionInfo.capturedExpression; - } - - void ResultBuilder::setExceptionGuard() { - m_guardException = true; - } - void ResultBuilder::unsetExceptionGuard() { - m_guardException = false; - } - -} // end namespace Catch - -// #included from: catch_tag_alias_registry.hpp -#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED - -namespace Catch { - - TagAliasRegistry::~TagAliasRegistry() {} - - Option TagAliasRegistry::find( std::string const& alias ) const { - std::map::const_iterator it = m_registry.find( alias ); - if( it != m_registry.end() ) - return it->second; - else - return Option(); - } - - std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { - std::string expandedTestSpec = unexpandedTestSpec; - for( std::map::const_iterator it = m_registry.begin(), itEnd = m_registry.end(); - it != itEnd; - ++it ) { - std::size_t pos = expandedTestSpec.find( it->first ); - if( pos != std::string::npos ) { - expandedTestSpec = expandedTestSpec.substr( 0, pos ) + - it->second.tag + - expandedTestSpec.substr( pos + it->first.size() ); - } - } - return expandedTestSpec; - } - - void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { - - if( !startsWith( alias, "[@" ) || !endsWith( alias, ']' ) ) { - std::ostringstream oss; - oss << Colour( Colour::Red ) - << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" - << Colour( Colour::FileName ) - << lineInfo << '\n'; - throw std::domain_error( oss.str().c_str() ); - } - if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) { - std::ostringstream oss; - oss << Colour( Colour::Red ) - << "error: tag alias, \"" << alias << "\" already registered.\n" - << "\tFirst seen at " - << Colour( Colour::Red ) << find(alias)->lineInfo << '\n' - << Colour( Colour::Red ) << "\tRedefined at " - << Colour( Colour::FileName) << lineInfo << '\n'; - throw std::domain_error( oss.str().c_str() ); - } - } - - ITagAliasRegistry::~ITagAliasRegistry() {} - - ITagAliasRegistry const& ITagAliasRegistry::get() { - return getRegistryHub().getTagAliasRegistry(); - } - - RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { - getMutableRegistryHub().registerTagAlias( alias, tag, lineInfo ); - } - -} // end namespace Catch - -// #included from: catch_matchers_string.hpp - -namespace Catch { -namespace Matchers { - - namespace StdString { - - CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) - : m_caseSensitivity( caseSensitivity ), - m_str( adjustString( str ) ) - {} - std::string CasedString::adjustString( std::string const& str ) const { - return m_caseSensitivity == CaseSensitive::No - ? toLower( str ) - : str; - } - std::string CasedString::caseSensitivitySuffix() const { - return m_caseSensitivity == CaseSensitive::No - ? " (case insensitive)" - : std::string(); - } - - StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator ) - : m_comparator( comparator ), - m_operation( operation ) { - } - - std::string StringMatcherBase::describe() const { - std::string description; - description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + - m_comparator.caseSensitivitySuffix().size()); - description += m_operation; - description += ": \""; - description += m_comparator.m_str; - description += "\""; - description += m_comparator.caseSensitivitySuffix(); - return description; - } - - EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {} - - bool EqualsMatcher::match( std::string const& source ) const { - return m_comparator.adjustString( source ) == m_comparator.m_str; - } - - ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {} - - bool ContainsMatcher::match( std::string const& source ) const { - return contains( m_comparator.adjustString( source ), m_comparator.m_str ); - } - - StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {} - - bool StartsWithMatcher::match( std::string const& source ) const { - return startsWith( m_comparator.adjustString( source ), m_comparator.m_str ); - } - - EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {} - - bool EndsWithMatcher::match( std::string const& source ) const { - return endsWith( m_comparator.adjustString( source ), m_comparator.m_str ); - } - - } // namespace StdString - - StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) { - return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) ); - } - StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) { - return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) ); - } - StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { - return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) ); - } - StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { - return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) ); - } - -} // namespace Matchers -} // namespace Catch -// #included from: ../reporters/catch_reporter_multi.hpp -#define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED - -namespace Catch { - -class MultipleReporters : public SharedImpl { - typedef std::vector > Reporters; - Reporters m_reporters; - -public: - void add( Ptr const& reporter ) { - m_reporters.push_back( reporter ); - } - -public: // IStreamingReporter - - virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { - return m_reporters[0]->getPreferences(); - } - - virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->noMatchingTestCases( spec ); - } - - virtual void testRunStarting( TestRunInfo const& testRunInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testRunStarting( testRunInfo ); - } - - virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testGroupStarting( groupInfo ); - } - - virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testCaseStarting( testInfo ); - } - - virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->sectionStarting( sectionInfo ); - } - - virtual void assertionStarting( AssertionInfo const& assertionInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->assertionStarting( assertionInfo ); - } - - // The return value indicates if the messages buffer should be cleared: - virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - bool clearBuffer = false; - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - clearBuffer |= (*it)->assertionEnded( assertionStats ); - return clearBuffer; - } - - virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->sectionEnded( sectionStats ); - } - - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testCaseEnded( testCaseStats ); - } - - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testGroupEnded( testGroupStats ); - } - - virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testRunEnded( testRunStats ); - } - - virtual void skipTest( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->skipTest( testInfo ); - } - - virtual MultipleReporters* tryAsMulti() CATCH_OVERRIDE { - return this; - } - -}; - -Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ) { - Ptr resultingReporter; - - if( existingReporter ) { - MultipleReporters* multi = existingReporter->tryAsMulti(); - if( !multi ) { - multi = new MultipleReporters; - resultingReporter = Ptr( multi ); - if( existingReporter ) - multi->add( existingReporter ); - } - else - resultingReporter = existingReporter; - multi->add( additionalReporter ); - } - else - resultingReporter = additionalReporter; - - return resultingReporter; -} - -} // end namespace Catch - -// #included from: ../reporters/catch_reporter_xml.hpp -#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED - -// #included from: catch_reporter_bases.hpp -#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED - -#include -#include -#include -#include - -namespace Catch { - - namespace { - // Because formatting using c++ streams is stateful, drop down to C is required - // Alternatively we could use stringstream, but its performance is... not good. - std::string getFormattedDuration( double duration ) { - // Max exponent + 1 is required to represent the whole part - // + 1 for decimal point - // + 3 for the 3 decimal places - // + 1 for null terminator - const size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; - char buffer[maxDoubleSize]; - - // Save previous errno, to prevent sprintf from overwriting it - ErrnoGuard guard; -#ifdef _MSC_VER - sprintf_s(buffer, "%.3f", duration); -#else - sprintf(buffer, "%.3f", duration); -#endif - return std::string(buffer); - } - } - - struct StreamingReporterBase : SharedImpl { - - StreamingReporterBase( ReporterConfig const& _config ) - : m_config( _config.fullConfig() ), - stream( _config.stream() ) - { - m_reporterPrefs.shouldRedirectStdOut = false; - } - - virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { - return m_reporterPrefs; - } - - virtual ~StreamingReporterBase() CATCH_OVERRIDE; - - virtual void noMatchingTestCases( std::string const& ) CATCH_OVERRIDE {} - - virtual void testRunStarting( TestRunInfo const& _testRunInfo ) CATCH_OVERRIDE { - currentTestRunInfo = _testRunInfo; - } - virtual void testGroupStarting( GroupInfo const& _groupInfo ) CATCH_OVERRIDE { - currentGroupInfo = _groupInfo; - } - - virtual void testCaseStarting( TestCaseInfo const& _testInfo ) CATCH_OVERRIDE { - currentTestCaseInfo = _testInfo; - } - virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { - m_sectionStack.push_back( _sectionInfo ); - } - - virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) CATCH_OVERRIDE { - m_sectionStack.pop_back(); - } - virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) CATCH_OVERRIDE { - currentTestCaseInfo.reset(); - } - virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) CATCH_OVERRIDE { - currentGroupInfo.reset(); - } - virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) CATCH_OVERRIDE { - currentTestCaseInfo.reset(); - currentGroupInfo.reset(); - currentTestRunInfo.reset(); - } - - virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE { - // Don't do anything with this by default. - // It can optionally be overridden in the derived class. - } - - Ptr m_config; - std::ostream& stream; - - LazyStat currentTestRunInfo; - LazyStat currentGroupInfo; - LazyStat currentTestCaseInfo; - - std::vector m_sectionStack; - ReporterPreferences m_reporterPrefs; - }; - - struct CumulativeReporterBase : SharedImpl { - template - struct Node : SharedImpl<> { - explicit Node( T const& _value ) : value( _value ) {} - virtual ~Node() {} - - typedef std::vector > ChildNodes; - T value; - ChildNodes children; - }; - struct SectionNode : SharedImpl<> { - explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {} - virtual ~SectionNode(); - - bool operator == ( SectionNode const& other ) const { - return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; - } - bool operator == ( Ptr const& other ) const { - return operator==( *other ); - } - - SectionStats stats; - typedef std::vector > ChildSections; - typedef std::vector Assertions; - ChildSections childSections; - Assertions assertions; - std::string stdOut; - std::string stdErr; - }; - - struct BySectionInfo { - BySectionInfo( SectionInfo const& other ) : m_other( other ) {} - BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} - bool operator() ( Ptr const& node ) const { - return node->stats.sectionInfo.lineInfo == m_other.lineInfo; - } - private: - void operator=( BySectionInfo const& ); - SectionInfo const& m_other; - }; - - typedef Node TestCaseNode; - typedef Node TestGroupNode; - typedef Node TestRunNode; - - CumulativeReporterBase( ReporterConfig const& _config ) - : m_config( _config.fullConfig() ), - stream( _config.stream() ) - { - m_reporterPrefs.shouldRedirectStdOut = false; - } - ~CumulativeReporterBase(); - - virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { - return m_reporterPrefs; - } - - virtual void testRunStarting( TestRunInfo const& ) CATCH_OVERRIDE {} - virtual void testGroupStarting( GroupInfo const& ) CATCH_OVERRIDE {} - - virtual void testCaseStarting( TestCaseInfo const& ) CATCH_OVERRIDE {} - - virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { - SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); - Ptr node; - if( m_sectionStack.empty() ) { - if( !m_rootSection ) - m_rootSection = new SectionNode( incompleteStats ); - node = m_rootSection; - } - else { - SectionNode& parentNode = *m_sectionStack.back(); - SectionNode::ChildSections::const_iterator it = - std::find_if( parentNode.childSections.begin(), - parentNode.childSections.end(), - BySectionInfo( sectionInfo ) ); - if( it == parentNode.childSections.end() ) { - node = new SectionNode( incompleteStats ); - parentNode.childSections.push_back( node ); - } - else - node = *it; - } - m_sectionStack.push_back( node ); - m_deepestSection = node; - } - - virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} - - virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - assert( !m_sectionStack.empty() ); - SectionNode& sectionNode = *m_sectionStack.back(); - sectionNode.assertions.push_back( assertionStats ); - // AssertionResult holds a pointer to a temporary DecomposedExpression, - // which getExpandedExpression() calls to build the expression string. - // Our section stack copy of the assertionResult will likely outlive the - // temporary, so it must be expanded or discarded now to avoid calling - // a destroyed object later. - prepareExpandedExpression( sectionNode.assertions.back().assertionResult ); - return true; - } - virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { - assert( !m_sectionStack.empty() ); - SectionNode& node = *m_sectionStack.back(); - node.stats = sectionStats; - m_sectionStack.pop_back(); - } - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { - Ptr node = new TestCaseNode( testCaseStats ); - assert( m_sectionStack.size() == 0 ); - node->children.push_back( m_rootSection ); - m_testCases.push_back( node ); - m_rootSection.reset(); - - assert( m_deepestSection ); - m_deepestSection->stdOut = testCaseStats.stdOut; - m_deepestSection->stdErr = testCaseStats.stdErr; - } - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { - Ptr node = new TestGroupNode( testGroupStats ); - node->children.swap( m_testCases ); - m_testGroups.push_back( node ); - } - virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { - Ptr node = new TestRunNode( testRunStats ); - node->children.swap( m_testGroups ); - m_testRuns.push_back( node ); - testRunEndedCumulative(); - } - virtual void testRunEndedCumulative() = 0; - - virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {} - - virtual void prepareExpandedExpression( AssertionResult& result ) const { - if( result.isOk() ) - result.discardDecomposedExpression(); - else - result.expandDecomposedExpression(); - } - - Ptr m_config; - std::ostream& stream; - std::vector m_assertions; - std::vector > > m_sections; - std::vector > m_testCases; - std::vector > m_testGroups; - - std::vector > m_testRuns; - - Ptr m_rootSection; - Ptr m_deepestSection; - std::vector > m_sectionStack; - ReporterPreferences m_reporterPrefs; - - }; - - template - char const* getLineOfChars() { - static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; - if( !*line ) { - std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); - line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; - } - return line; - } - - struct TestEventListenerBase : StreamingReporterBase { - TestEventListenerBase( ReporterConfig const& _config ) - : StreamingReporterBase( _config ) - {} - - virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} - virtual bool assertionEnded( AssertionStats const& ) CATCH_OVERRIDE { - return false; - } - }; - -} // end namespace Catch - -// #included from: ../internal/catch_reporter_registrars.hpp -#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED - -namespace Catch { - - template - class LegacyReporterRegistrar { - - class ReporterFactory : public IReporterFactory { - virtual IStreamingReporter* create( ReporterConfig const& config ) const { - return new LegacyReporterAdapter( new T( config ) ); - } - - virtual std::string getDescription() const { - return T::getDescription(); - } - }; - - public: - - LegacyReporterRegistrar( std::string const& name ) { - getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); - } - }; - - template - class ReporterRegistrar { - - class ReporterFactory : public SharedImpl { - - // *** Please Note ***: - // - If you end up here looking at a compiler error because it's trying to register - // your custom reporter class be aware that the native reporter interface has changed - // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via - // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter. - // However please consider updating to the new interface as the old one is now - // deprecated and will probably be removed quite soon! - // Please contact me via github if you have any questions at all about this. - // In fact, ideally, please contact me anyway to let me know you've hit this - as I have - // no idea who is actually using custom reporters at all (possibly no-one!). - // The new interface is designed to minimise exposure to interface changes in the future. - virtual IStreamingReporter* create( ReporterConfig const& config ) const { - return new T( config ); - } - - virtual std::string getDescription() const { - return T::getDescription(); - } - }; - - public: - - ReporterRegistrar( std::string const& name ) { - getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); - } - }; - - template - class ListenerRegistrar { - - class ListenerFactory : public SharedImpl { - - virtual IStreamingReporter* create( ReporterConfig const& config ) const { - return new T( config ); - } - virtual std::string getDescription() const { - return std::string(); - } - }; - - public: - - ListenerRegistrar() { - getMutableRegistryHub().registerListener( new ListenerFactory() ); - } - }; -} - -#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \ - namespace{ Catch::LegacyReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } - -#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \ - namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } - -// Deprecated - use the form without INTERNAL_ -#define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \ - namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } - -#define CATCH_REGISTER_LISTENER( listenerType ) \ - namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } - -// #included from: ../internal/catch_xmlwriter.hpp -#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED - -#include -#include -#include -#include - -namespace Catch { - - class XmlEncode { - public: - enum ForWhat { ForTextNodes, ForAttributes }; - - XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ) - : m_str( str ), - m_forWhat( forWhat ) - {} - - void encodeTo( std::ostream& os ) const { - - // Apostrophe escaping not necessary if we always use " to write attributes - // (see: http://www.w3.org/TR/xml/#syntax) - - for( std::size_t i = 0; i < m_str.size(); ++ i ) { - char c = m_str[i]; - switch( c ) { - case '<': os << "<"; break; - case '&': os << "&"; break; - - case '>': - // See: http://www.w3.org/TR/xml/#syntax - if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' ) - os << ">"; - else - os << c; - break; - - case '\"': - if( m_forWhat == ForAttributes ) - os << """; - else - os << c; - break; - - default: - // Escape control chars - based on contribution by @espenalb in PR #465 and - // by @mrpi PR #588 - if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) { - // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 - os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) - << static_cast( c ); - } - else - os << c; - } - } - } - - friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { - xmlEncode.encodeTo( os ); - return os; - } - - private: - std::string m_str; - ForWhat m_forWhat; - }; - - class XmlWriter { - public: - - class ScopedElement { - public: - ScopedElement( XmlWriter* writer ) - : m_writer( writer ) - {} - - ScopedElement( ScopedElement const& other ) - : m_writer( other.m_writer ){ - other.m_writer = CATCH_NULL; - } - - ~ScopedElement() { - if( m_writer ) - m_writer->endElement(); - } - - ScopedElement& writeText( std::string const& text, bool indent = true ) { - m_writer->writeText( text, indent ); - return *this; - } - - template - ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { - m_writer->writeAttribute( name, attribute ); - return *this; - } - - private: - mutable XmlWriter* m_writer; - }; - - XmlWriter() - : m_tagIsOpen( false ), - m_needsNewline( false ), - m_os( Catch::cout() ) - { - writeDeclaration(); - } - - XmlWriter( std::ostream& os ) - : m_tagIsOpen( false ), - m_needsNewline( false ), - m_os( os ) - { - writeDeclaration(); - } - - ~XmlWriter() { - while( !m_tags.empty() ) - endElement(); - } - - XmlWriter& startElement( std::string const& name ) { - ensureTagClosed(); - newlineIfNecessary(); - m_os << m_indent << '<' << name; - m_tags.push_back( name ); - m_indent += " "; - m_tagIsOpen = true; - return *this; - } - - ScopedElement scopedElement( std::string const& name ) { - ScopedElement scoped( this ); - startElement( name ); - return scoped; - } - - XmlWriter& endElement() { - newlineIfNecessary(); - m_indent = m_indent.substr( 0, m_indent.size()-2 ); - if( m_tagIsOpen ) { - m_os << "/>"; - m_tagIsOpen = false; - } - else { - m_os << m_indent << ""; - } - m_os << std::endl; - m_tags.pop_back(); - return *this; - } - - XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) { - if( !name.empty() && !attribute.empty() ) - m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; - return *this; - } - - XmlWriter& writeAttribute( std::string const& name, bool attribute ) { - m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; - return *this; - } - - template - XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { - std::ostringstream oss; - oss << attribute; - return writeAttribute( name, oss.str() ); - } - - XmlWriter& writeText( std::string const& text, bool indent = true ) { - if( !text.empty() ){ - bool tagWasOpen = m_tagIsOpen; - ensureTagClosed(); - if( tagWasOpen && indent ) - m_os << m_indent; - m_os << XmlEncode( text ); - m_needsNewline = true; - } - return *this; - } - - XmlWriter& writeComment( std::string const& text ) { - ensureTagClosed(); - m_os << m_indent << ""; - m_needsNewline = true; - return *this; - } - - void writeStylesheetRef( std::string const& url ) { - m_os << "\n"; - } - - XmlWriter& writeBlankLine() { - ensureTagClosed(); - m_os << '\n'; - return *this; - } - - void ensureTagClosed() { - if( m_tagIsOpen ) { - m_os << ">" << std::endl; - m_tagIsOpen = false; - } - } - - private: - XmlWriter( XmlWriter const& ); - void operator=( XmlWriter const& ); - - void writeDeclaration() { - m_os << "\n"; - } - - void newlineIfNecessary() { - if( m_needsNewline ) { - m_os << std::endl; - m_needsNewline = false; - } - } - - bool m_tagIsOpen; - bool m_needsNewline; - std::vector m_tags; - std::string m_indent; - std::ostream& m_os; - }; - -} -// #included from: catch_reenable_warnings.h - -#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED - -#ifdef __clang__ -# ifdef __ICC // icpc defines the __clang__ macro -# pragma warning(pop) -# else -# pragma clang diagnostic pop -# endif -#elif defined __GNUC__ -# pragma GCC diagnostic pop -#endif - - -namespace Catch { - class XmlReporter : public StreamingReporterBase { - public: - XmlReporter( ReporterConfig const& _config ) - : StreamingReporterBase( _config ), - m_xml(_config.stream()), - m_sectionDepth( 0 ) - { - m_reporterPrefs.shouldRedirectStdOut = true; - } - - virtual ~XmlReporter() CATCH_OVERRIDE; - - static std::string getDescription() { - return "Reports test results as an XML document"; - } - - virtual std::string getStylesheetRef() const { - return std::string(); - } - - void writeSourceInfo( SourceLineInfo const& sourceInfo ) { - m_xml - .writeAttribute( "filename", sourceInfo.file ) - .writeAttribute( "line", sourceInfo.line ); - } - - public: // StreamingReporterBase - - virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE { - StreamingReporterBase::noMatchingTestCases( s ); - } - - virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE { - StreamingReporterBase::testRunStarting( testInfo ); - std::string stylesheetRef = getStylesheetRef(); - if( !stylesheetRef.empty() ) - m_xml.writeStylesheetRef( stylesheetRef ); - m_xml.startElement( "Catch" ); - if( !m_config->name().empty() ) - m_xml.writeAttribute( "name", m_config->name() ); - } - - virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { - StreamingReporterBase::testGroupStarting( groupInfo ); - m_xml.startElement( "Group" ) - .writeAttribute( "name", groupInfo.name ); - } - - virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { - StreamingReporterBase::testCaseStarting(testInfo); - m_xml.startElement( "TestCase" ) - .writeAttribute( "name", trim( testInfo.name ) ) - .writeAttribute( "description", testInfo.description ) - .writeAttribute( "tags", testInfo.tagsAsString ); - - writeSourceInfo( testInfo.lineInfo ); - - if ( m_config->showDurations() == ShowDurations::Always ) - m_testCaseTimer.start(); - m_xml.ensureTagClosed(); - } - - virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { - StreamingReporterBase::sectionStarting( sectionInfo ); - if( m_sectionDepth++ > 0 ) { - m_xml.startElement( "Section" ) - .writeAttribute( "name", trim( sectionInfo.name ) ) - .writeAttribute( "description", sectionInfo.description ); - writeSourceInfo( sectionInfo.lineInfo ); - m_xml.ensureTagClosed(); - } - } - - virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { } - - virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - - AssertionResult const& result = assertionStats.assertionResult; - - bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); - - if( includeResults ) { - // Print any info messages in tags. - for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); - it != itEnd; - ++it ) { - if( it->type == ResultWas::Info ) { - m_xml.scopedElement( "Info" ) - .writeText( it->message ); - } else if ( it->type == ResultWas::Warning ) { - m_xml.scopedElement( "Warning" ) - .writeText( it->message ); - } - } - } - - // Drop out if result was successful but we're not printing them. - if( !includeResults && result.getResultType() != ResultWas::Warning ) - return true; - - // Print the expression if there is one. - if( result.hasExpression() ) { - m_xml.startElement( "Expression" ) - .writeAttribute( "success", result.succeeded() ) - .writeAttribute( "type", result.getTestMacroName() ); - - writeSourceInfo( result.getSourceInfo() ); - - m_xml.scopedElement( "Original" ) - .writeText( result.getExpression() ); - m_xml.scopedElement( "Expanded" ) - .writeText( result.getExpandedExpression() ); - } - - // And... Print a result applicable to each result type. - switch( result.getResultType() ) { - case ResultWas::ThrewException: - m_xml.startElement( "Exception" ); - writeSourceInfo( result.getSourceInfo() ); - m_xml.writeText( result.getMessage() ); - m_xml.endElement(); - break; - case ResultWas::FatalErrorCondition: - m_xml.startElement( "FatalErrorCondition" ); - writeSourceInfo( result.getSourceInfo() ); - m_xml.writeText( result.getMessage() ); - m_xml.endElement(); - break; - case ResultWas::Info: - m_xml.scopedElement( "Info" ) - .writeText( result.getMessage() ); - break; - case ResultWas::Warning: - // Warning will already have been written - break; - case ResultWas::ExplicitFailure: - m_xml.startElement( "Failure" ); - writeSourceInfo( result.getSourceInfo() ); - m_xml.writeText( result.getMessage() ); - m_xml.endElement(); - break; - default: - break; - } - - if( result.hasExpression() ) - m_xml.endElement(); - - return true; - } - - virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { - StreamingReporterBase::sectionEnded( sectionStats ); - if( --m_sectionDepth > 0 ) { - XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); - e.writeAttribute( "successes", sectionStats.assertions.passed ); - e.writeAttribute( "failures", sectionStats.assertions.failed ); - e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); - - if ( m_config->showDurations() == ShowDurations::Always ) - e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); - - m_xml.endElement(); - } - } - - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { - StreamingReporterBase::testCaseEnded( testCaseStats ); - XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); - e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); - - if ( m_config->showDurations() == ShowDurations::Always ) - e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); - - if( !testCaseStats.stdOut.empty() ) - m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false ); - if( !testCaseStats.stdErr.empty() ) - m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false ); - - m_xml.endElement(); - } - - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { - StreamingReporterBase::testGroupEnded( testGroupStats ); - // TODO: Check testGroupStats.aborting and act accordingly. - m_xml.scopedElement( "OverallResults" ) - .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) - .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) - .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); - m_xml.endElement(); - } - - virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { - StreamingReporterBase::testRunEnded( testRunStats ); - m_xml.scopedElement( "OverallResults" ) - .writeAttribute( "successes", testRunStats.totals.assertions.passed ) - .writeAttribute( "failures", testRunStats.totals.assertions.failed ) - .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); - m_xml.endElement(); - } - - private: - Timer m_testCaseTimer; - XmlWriter m_xml; - int m_sectionDepth; - }; - - INTERNAL_CATCH_REGISTER_REPORTER( "xml", XmlReporter ) - -} // end namespace Catch - -// #included from: ../reporters/catch_reporter_junit.hpp -#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED - -#include - -namespace Catch { - - namespace { - std::string getCurrentTimestamp() { - // Beware, this is not reentrant because of backward compatibility issues - // Also, UTC only, again because of backward compatibility (%z is C++11) - time_t rawtime; - std::time(&rawtime); - const size_t timeStampSize = sizeof("2017-01-16T17:06:45Z"); - -#ifdef _MSC_VER - std::tm timeInfo = {}; - gmtime_s(&timeInfo, &rawtime); -#else - std::tm* timeInfo; - timeInfo = std::gmtime(&rawtime); -#endif - - char timeStamp[timeStampSize]; - const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; - -#ifdef _MSC_VER - std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); -#else - std::strftime(timeStamp, timeStampSize, fmt, timeInfo); -#endif - return std::string(timeStamp); - } - - } - - class JunitReporter : public CumulativeReporterBase { - public: - JunitReporter( ReporterConfig const& _config ) - : CumulativeReporterBase( _config ), - xml( _config.stream() ) - { - m_reporterPrefs.shouldRedirectStdOut = true; - } - - virtual ~JunitReporter() CATCH_OVERRIDE; - - static std::string getDescription() { - return "Reports test results in an XML format that looks like Ant's junitreport target"; - } - - virtual void noMatchingTestCases( std::string const& /*spec*/ ) CATCH_OVERRIDE {} - - virtual void testRunStarting( TestRunInfo const& runInfo ) CATCH_OVERRIDE { - CumulativeReporterBase::testRunStarting( runInfo ); - xml.startElement( "testsuites" ); - } - - virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { - suiteTimer.start(); - stdOutForSuite.str(""); - stdErrForSuite.str(""); - unexpectedExceptions = 0; - CumulativeReporterBase::testGroupStarting( groupInfo ); - } - - virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException ) - unexpectedExceptions++; - return CumulativeReporterBase::assertionEnded( assertionStats ); - } - - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { - stdOutForSuite << testCaseStats.stdOut; - stdErrForSuite << testCaseStats.stdErr; - CumulativeReporterBase::testCaseEnded( testCaseStats ); - } - - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { - double suiteTime = suiteTimer.getElapsedSeconds(); - CumulativeReporterBase::testGroupEnded( testGroupStats ); - writeGroup( *m_testGroups.back(), suiteTime ); - } - - virtual void testRunEndedCumulative() CATCH_OVERRIDE { - xml.endElement(); - } - - void writeGroup( TestGroupNode const& groupNode, double suiteTime ) { - XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); - TestGroupStats const& stats = groupNode.value; - xml.writeAttribute( "name", stats.groupInfo.name ); - xml.writeAttribute( "errors", unexpectedExceptions ); - xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); - xml.writeAttribute( "tests", stats.totals.assertions.total() ); - xml.writeAttribute( "hostname", "tbd" ); // !TBD - if( m_config->showDurations() == ShowDurations::Never ) - xml.writeAttribute( "time", "" ); - else - xml.writeAttribute( "time", suiteTime ); - xml.writeAttribute( "timestamp", getCurrentTimestamp() ); - - // Write test cases - for( TestGroupNode::ChildNodes::const_iterator - it = groupNode.children.begin(), itEnd = groupNode.children.end(); - it != itEnd; - ++it ) - writeTestCase( **it ); - - xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false ); - xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false ); - } - - void writeTestCase( TestCaseNode const& testCaseNode ) { - TestCaseStats const& stats = testCaseNode.value; - - // All test cases have exactly one section - which represents the - // test case itself. That section may have 0-n nested sections - assert( testCaseNode.children.size() == 1 ); - SectionNode const& rootSection = *testCaseNode.children.front(); - - std::string className = stats.testInfo.className; - - if( className.empty() ) { - if( rootSection.childSections.empty() ) - className = "global"; - } - writeSection( className, "", rootSection ); - } - - void writeSection( std::string const& className, - std::string const& rootName, - SectionNode const& sectionNode ) { - std::string name = trim( sectionNode.stats.sectionInfo.name ); - if( !rootName.empty() ) - name = rootName + '/' + name; - - if( !sectionNode.assertions.empty() || - !sectionNode.stdOut.empty() || - !sectionNode.stdErr.empty() ) { - XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); - if( className.empty() ) { - xml.writeAttribute( "classname", name ); - xml.writeAttribute( "name", "root" ); - } - else { - xml.writeAttribute( "classname", className ); - xml.writeAttribute( "name", name ); - } - xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) ); - - writeAssertions( sectionNode ); - - if( !sectionNode.stdOut.empty() ) - xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); - if( !sectionNode.stdErr.empty() ) - xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); - } - for( SectionNode::ChildSections::const_iterator - it = sectionNode.childSections.begin(), - itEnd = sectionNode.childSections.end(); - it != itEnd; - ++it ) - if( className.empty() ) - writeSection( name, "", **it ); - else - writeSection( className, name, **it ); - } - - void writeAssertions( SectionNode const& sectionNode ) { - for( SectionNode::Assertions::const_iterator - it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end(); - it != itEnd; - ++it ) - writeAssertion( *it ); - } - void writeAssertion( AssertionStats const& stats ) { - AssertionResult const& result = stats.assertionResult; - if( !result.isOk() ) { - std::string elementName; - switch( result.getResultType() ) { - case ResultWas::ThrewException: - case ResultWas::FatalErrorCondition: - elementName = "error"; - break; - case ResultWas::ExplicitFailure: - elementName = "failure"; - break; - case ResultWas::ExpressionFailed: - elementName = "failure"; - break; - case ResultWas::DidntThrowException: - elementName = "failure"; - break; - - // We should never see these here: - case ResultWas::Info: - case ResultWas::Warning: - case ResultWas::Ok: - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: - elementName = "internalError"; - break; - } - - XmlWriter::ScopedElement e = xml.scopedElement( elementName ); - - xml.writeAttribute( "message", result.getExpandedExpression() ); - xml.writeAttribute( "type", result.getTestMacroName() ); - - std::ostringstream oss; - if( !result.getMessage().empty() ) - oss << result.getMessage() << '\n'; - for( std::vector::const_iterator - it = stats.infoMessages.begin(), - itEnd = stats.infoMessages.end(); - it != itEnd; - ++it ) - if( it->type == ResultWas::Info ) - oss << it->message << '\n'; - - oss << "at " << result.getSourceInfo(); - xml.writeText( oss.str(), false ); - } - } - - XmlWriter xml; - Timer suiteTimer; - std::ostringstream stdOutForSuite; - std::ostringstream stdErrForSuite; - unsigned int unexpectedExceptions; - }; - - INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter ) - -} // end namespace Catch - -// #included from: ../reporters/catch_reporter_console.hpp -#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED - -#include -#include - -namespace Catch { - - struct ConsoleReporter : StreamingReporterBase { - ConsoleReporter( ReporterConfig const& _config ) - : StreamingReporterBase( _config ), - m_headerPrinted( false ) - {} - - virtual ~ConsoleReporter() CATCH_OVERRIDE; - static std::string getDescription() { - return "Reports test results as plain lines of text"; - } - - virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { - stream << "No test cases matched '" << spec << '\'' << std::endl; - } - - virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { - } - - virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE { - AssertionResult const& result = _assertionStats.assertionResult; - - bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); - - // Drop out if result was successful but we're not printing them. - if( !includeResults && result.getResultType() != ResultWas::Warning ) - return false; - - lazyPrint(); - - AssertionPrinter printer( stream, _assertionStats, includeResults ); - printer.print(); - stream << std::endl; - return true; - } - - virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { - m_headerPrinted = false; - StreamingReporterBase::sectionStarting( _sectionInfo ); - } - virtual void sectionEnded( SectionStats const& _sectionStats ) CATCH_OVERRIDE { - if( _sectionStats.missingAssertions ) { - lazyPrint(); - Colour colour( Colour::ResultError ); - if( m_sectionStack.size() > 1 ) - stream << "\nNo assertions in section"; - else - stream << "\nNo assertions in test case"; - stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; - } - if( m_config->showDurations() == ShowDurations::Always ) { - stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; - } - if( m_headerPrinted ) { - m_headerPrinted = false; - } - StreamingReporterBase::sectionEnded( _sectionStats ); - } - - virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) CATCH_OVERRIDE { - StreamingReporterBase::testCaseEnded( _testCaseStats ); - m_headerPrinted = false; - } - virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) CATCH_OVERRIDE { - if( currentGroupInfo.used ) { - printSummaryDivider(); - stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; - printTotals( _testGroupStats.totals ); - stream << '\n' << std::endl; - } - StreamingReporterBase::testGroupEnded( _testGroupStats ); - } - virtual void testRunEnded( TestRunStats const& _testRunStats ) CATCH_OVERRIDE { - printTotalsDivider( _testRunStats.totals ); - printTotals( _testRunStats.totals ); - stream << std::endl; - StreamingReporterBase::testRunEnded( _testRunStats ); - } - - private: - - class AssertionPrinter { - void operator= ( AssertionPrinter const& ); - public: - AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) - : stream( _stream ), - stats( _stats ), - result( _stats.assertionResult ), - colour( Colour::None ), - message( result.getMessage() ), - messages( _stats.infoMessages ), - printInfoMessages( _printInfoMessages ) - { - switch( result.getResultType() ) { - case ResultWas::Ok: - colour = Colour::Success; - passOrFail = "PASSED"; - //if( result.hasMessage() ) - if( _stats.infoMessages.size() == 1 ) - messageLabel = "with message"; - if( _stats.infoMessages.size() > 1 ) - messageLabel = "with messages"; - break; - case ResultWas::ExpressionFailed: - if( result.isOk() ) { - colour = Colour::Success; - passOrFail = "FAILED - but was ok"; - } - else { - colour = Colour::Error; - passOrFail = "FAILED"; - } - if( _stats.infoMessages.size() == 1 ) - messageLabel = "with message"; - if( _stats.infoMessages.size() > 1 ) - messageLabel = "with messages"; - break; - case ResultWas::ThrewException: - colour = Colour::Error; - passOrFail = "FAILED"; - messageLabel = "due to unexpected exception with "; - if (_stats.infoMessages.size() == 1) - messageLabel += "message"; - if (_stats.infoMessages.size() > 1) - messageLabel += "messages"; - break; - case ResultWas::FatalErrorCondition: - colour = Colour::Error; - passOrFail = "FAILED"; - messageLabel = "due to a fatal error condition"; - break; - case ResultWas::DidntThrowException: - colour = Colour::Error; - passOrFail = "FAILED"; - messageLabel = "because no exception was thrown where one was expected"; - break; - case ResultWas::Info: - messageLabel = "info"; - break; - case ResultWas::Warning: - messageLabel = "warning"; - break; - case ResultWas::ExplicitFailure: - passOrFail = "FAILED"; - colour = Colour::Error; - if( _stats.infoMessages.size() == 1 ) - messageLabel = "explicitly with message"; - if( _stats.infoMessages.size() > 1 ) - messageLabel = "explicitly with messages"; - break; - // These cases are here to prevent compiler warnings - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: - passOrFail = "** internal error **"; - colour = Colour::Error; - break; - } - } - - void print() const { - printSourceInfo(); - if( stats.totals.assertions.total() > 0 ) { - if( result.isOk() ) - stream << '\n'; - printResultType(); - printOriginalExpression(); - printReconstructedExpression(); - } - else { - stream << '\n'; - } - printMessage(); - } - - private: - void printResultType() const { - if( !passOrFail.empty() ) { - Colour colourGuard( colour ); - stream << passOrFail << ":\n"; - } - } - void printOriginalExpression() const { - if( result.hasExpression() ) { - Colour colourGuard( Colour::OriginalExpression ); - stream << " "; - stream << result.getExpressionInMacro(); - stream << '\n'; - } - } - void printReconstructedExpression() const { - if( result.hasExpandedExpression() ) { - stream << "with expansion:\n"; - Colour colourGuard( Colour::ReconstructedExpression ); - stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << '\n'; - } - } - void printMessage() const { - if( !messageLabel.empty() ) - stream << messageLabel << ':' << '\n'; - for( std::vector::const_iterator it = messages.begin(), itEnd = messages.end(); - it != itEnd; - ++it ) { - // If this assertion is a warning ignore any INFO messages - if( printInfoMessages || it->type != ResultWas::Info ) - stream << Text( it->message, TextAttributes().setIndent(2) ) << '\n'; - } - } - void printSourceInfo() const { - Colour colourGuard( Colour::FileName ); - stream << result.getSourceInfo() << ": "; - } - - std::ostream& stream; - AssertionStats const& stats; - AssertionResult const& result; - Colour::Code colour; - std::string passOrFail; - std::string messageLabel; - std::string message; - std::vector messages; - bool printInfoMessages; - }; - - void lazyPrint() { - - if( !currentTestRunInfo.used ) - lazyPrintRunInfo(); - if( !currentGroupInfo.used ) - lazyPrintGroupInfo(); - - if( !m_headerPrinted ) { - printTestCaseAndSectionHeader(); - m_headerPrinted = true; - } - } - void lazyPrintRunInfo() { - stream << '\n' << getLineOfChars<'~'>() << '\n'; - Colour colour( Colour::SecondaryText ); - stream << currentTestRunInfo->name - << " is a Catch v" << libraryVersion() << " host application.\n" - << "Run with -? for options\n\n"; - - if( m_config->rngSeed() != 0 ) - stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; - - currentTestRunInfo.used = true; - } - void lazyPrintGroupInfo() { - if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) { - printClosedHeader( "Group: " + currentGroupInfo->name ); - currentGroupInfo.used = true; - } - } - void printTestCaseAndSectionHeader() { - assert( !m_sectionStack.empty() ); - printOpenHeader( currentTestCaseInfo->name ); - - if( m_sectionStack.size() > 1 ) { - Colour colourGuard( Colour::Headers ); - - std::vector::const_iterator - it = m_sectionStack.begin()+1, // Skip first section (test case) - itEnd = m_sectionStack.end(); - for( ; it != itEnd; ++it ) - printHeaderString( it->name, 2 ); - } - - SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; - - if( !lineInfo.empty() ){ - stream << getLineOfChars<'-'>() << '\n'; - Colour colourGuard( Colour::FileName ); - stream << lineInfo << '\n'; - } - stream << getLineOfChars<'.'>() << '\n' << std::endl; - } - - void printClosedHeader( std::string const& _name ) { - printOpenHeader( _name ); - stream << getLineOfChars<'.'>() << '\n'; - } - void printOpenHeader( std::string const& _name ) { - stream << getLineOfChars<'-'>() << '\n'; - { - Colour colourGuard( Colour::Headers ); - printHeaderString( _name ); - } - } - - // if string has a : in first line will set indent to follow it on - // subsequent lines - void printHeaderString( std::string const& _string, std::size_t indent = 0 ) { - std::size_t i = _string.find( ": " ); - if( i != std::string::npos ) - i+=2; - else - i = 0; - stream << Text( _string, TextAttributes() - .setIndent( indent+i) - .setInitialIndent( indent ) ) << '\n'; - } - - struct SummaryColumn { - - SummaryColumn( std::string const& _label, Colour::Code _colour ) - : label( _label ), - colour( _colour ) - {} - SummaryColumn addRow( std::size_t count ) { - std::ostringstream oss; - oss << count; - std::string row = oss.str(); - for( std::vector::iterator it = rows.begin(); it != rows.end(); ++it ) { - while( it->size() < row.size() ) - *it = ' ' + *it; - while( it->size() > row.size() ) - row = ' ' + row; - } - rows.push_back( row ); - return *this; - } - - std::string label; - Colour::Code colour; - std::vector rows; - - }; - - void printTotals( Totals const& totals ) { - if( totals.testCases.total() == 0 ) { - stream << Colour( Colour::Warning ) << "No tests ran\n"; - } - else if( totals.assertions.total() > 0 && totals.testCases.allPassed() ) { - stream << Colour( Colour::ResultSuccess ) << "All tests passed"; - stream << " (" - << pluralise( totals.assertions.passed, "assertion" ) << " in " - << pluralise( totals.testCases.passed, "test case" ) << ')' - << '\n'; - } - else { - - std::vector columns; - columns.push_back( SummaryColumn( "", Colour::None ) - .addRow( totals.testCases.total() ) - .addRow( totals.assertions.total() ) ); - columns.push_back( SummaryColumn( "passed", Colour::Success ) - .addRow( totals.testCases.passed ) - .addRow( totals.assertions.passed ) ); - columns.push_back( SummaryColumn( "failed", Colour::ResultError ) - .addRow( totals.testCases.failed ) - .addRow( totals.assertions.failed ) ); - columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure ) - .addRow( totals.testCases.failedButOk ) - .addRow( totals.assertions.failedButOk ) ); - - printSummaryRow( "test cases", columns, 0 ); - printSummaryRow( "assertions", columns, 1 ); - } - } - void printSummaryRow( std::string const& label, std::vector const& cols, std::size_t row ) { - for( std::vector::const_iterator it = cols.begin(); it != cols.end(); ++it ) { - std::string value = it->rows[row]; - if( it->label.empty() ) { - stream << label << ": "; - if( value != "0" ) - stream << value; - else - stream << Colour( Colour::Warning ) << "- none -"; - } - else if( value != "0" ) { - stream << Colour( Colour::LightGrey ) << " | "; - stream << Colour( it->colour ) - << value << ' ' << it->label; - } - } - stream << '\n'; - } - - static std::size_t makeRatio( std::size_t number, std::size_t total ) { - std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0; - return ( ratio == 0 && number > 0 ) ? 1 : ratio; - } - static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) { - if( i > j && i > k ) - return i; - else if( j > k ) - return j; - else - return k; - } - - void printTotalsDivider( Totals const& totals ) { - if( totals.testCases.total() > 0 ) { - std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() ); - std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() ); - std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() ); - while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 ) - findMax( failedRatio, failedButOkRatio, passedRatio )++; - while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 ) - findMax( failedRatio, failedButOkRatio, passedRatio )--; - - stream << Colour( Colour::Error ) << std::string( failedRatio, '=' ); - stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' ); - if( totals.testCases.allPassed() ) - stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' ); - else - stream << Colour( Colour::Success ) << std::string( passedRatio, '=' ); - } - else { - stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' ); - } - stream << '\n'; - } - void printSummaryDivider() { - stream << getLineOfChars<'-'>() << '\n'; - } - - private: - bool m_headerPrinted; - }; - - INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter ) - -} // end namespace Catch - -// #included from: ../reporters/catch_reporter_compact.hpp -#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED - -namespace Catch { - - struct CompactReporter : StreamingReporterBase { - - CompactReporter( ReporterConfig const& _config ) - : StreamingReporterBase( _config ) - {} - - virtual ~CompactReporter(); - - static std::string getDescription() { - return "Reports test results on a single line, suitable for IDEs"; - } - - virtual ReporterPreferences getPreferences() const { - ReporterPreferences prefs; - prefs.shouldRedirectStdOut = false; - return prefs; - } - - virtual void noMatchingTestCases( std::string const& spec ) { - stream << "No test cases matched '" << spec << '\'' << std::endl; - } - - virtual void assertionStarting( AssertionInfo const& ) {} - - virtual bool assertionEnded( AssertionStats const& _assertionStats ) { - AssertionResult const& result = _assertionStats.assertionResult; - - bool printInfoMessages = true; - - // Drop out if result was successful and we're not printing those - if( !m_config->includeSuccessfulResults() && result.isOk() ) { - if( result.getResultType() != ResultWas::Warning ) - return false; - printInfoMessages = false; - } - - AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); - printer.print(); - - stream << std::endl; - return true; - } - - virtual void sectionEnded(SectionStats const& _sectionStats) CATCH_OVERRIDE { - if (m_config->showDurations() == ShowDurations::Always) { - stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; - } - } - - virtual void testRunEnded( TestRunStats const& _testRunStats ) { - printTotals( _testRunStats.totals ); - stream << '\n' << std::endl; - StreamingReporterBase::testRunEnded( _testRunStats ); - } - - private: - class AssertionPrinter { - void operator= ( AssertionPrinter const& ); - public: - AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) - : stream( _stream ) - , stats( _stats ) - , result( _stats.assertionResult ) - , messages( _stats.infoMessages ) - , itMessage( _stats.infoMessages.begin() ) - , printInfoMessages( _printInfoMessages ) - {} - - void print() { - printSourceInfo(); - - itMessage = messages.begin(); - - switch( result.getResultType() ) { - case ResultWas::Ok: - printResultType( Colour::ResultSuccess, passedString() ); - printOriginalExpression(); - printReconstructedExpression(); - if ( ! result.hasExpression() ) - printRemainingMessages( Colour::None ); - else - printRemainingMessages(); - break; - case ResultWas::ExpressionFailed: - if( result.isOk() ) - printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) ); - else - printResultType( Colour::Error, failedString() ); - printOriginalExpression(); - printReconstructedExpression(); - printRemainingMessages(); - break; - case ResultWas::ThrewException: - printResultType( Colour::Error, failedString() ); - printIssue( "unexpected exception with message:" ); - printMessage(); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::FatalErrorCondition: - printResultType( Colour::Error, failedString() ); - printIssue( "fatal error condition with message:" ); - printMessage(); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::DidntThrowException: - printResultType( Colour::Error, failedString() ); - printIssue( "expected exception, got none" ); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::Info: - printResultType( Colour::None, "info" ); - printMessage(); - printRemainingMessages(); - break; - case ResultWas::Warning: - printResultType( Colour::None, "warning" ); - printMessage(); - printRemainingMessages(); - break; - case ResultWas::ExplicitFailure: - printResultType( Colour::Error, failedString() ); - printIssue( "explicitly" ); - printRemainingMessages( Colour::None ); - break; - // These cases are here to prevent compiler warnings - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: - printResultType( Colour::Error, "** internal error **" ); - break; - } - } - - private: - // Colour::LightGrey - - static Colour::Code dimColour() { return Colour::FileName; } - -#ifdef CATCH_PLATFORM_MAC - static const char* failedString() { return "FAILED"; } - static const char* passedString() { return "PASSED"; } -#else - static const char* failedString() { return "failed"; } - static const char* passedString() { return "passed"; } -#endif - - void printSourceInfo() const { - Colour colourGuard( Colour::FileName ); - stream << result.getSourceInfo() << ':'; - } - - void printResultType( Colour::Code colour, std::string const& passOrFail ) const { - if( !passOrFail.empty() ) { - { - Colour colourGuard( colour ); - stream << ' ' << passOrFail; - } - stream << ':'; - } - } - - void printIssue( std::string const& issue ) const { - stream << ' ' << issue; - } - - void printExpressionWas() { - if( result.hasExpression() ) { - stream << ';'; - { - Colour colour( dimColour() ); - stream << " expression was:"; - } - printOriginalExpression(); - } - } - - void printOriginalExpression() const { - if( result.hasExpression() ) { - stream << ' ' << result.getExpression(); - } - } - - void printReconstructedExpression() const { - if( result.hasExpandedExpression() ) { - { - Colour colour( dimColour() ); - stream << " for: "; - } - stream << result.getExpandedExpression(); - } - } - - void printMessage() { - if ( itMessage != messages.end() ) { - stream << " '" << itMessage->message << '\''; - ++itMessage; - } - } - - void printRemainingMessages( Colour::Code colour = dimColour() ) { - if ( itMessage == messages.end() ) - return; - - // using messages.end() directly yields compilation error: - std::vector::const_iterator itEnd = messages.end(); - const std::size_t N = static_cast( std::distance( itMessage, itEnd ) ); - - { - Colour colourGuard( colour ); - stream << " with " << pluralise( N, "message" ) << ':'; - } - - for(; itMessage != itEnd; ) { - // If this assertion is a warning ignore any INFO messages - if( printInfoMessages || itMessage->type != ResultWas::Info ) { - stream << " '" << itMessage->message << '\''; - if ( ++itMessage != itEnd ) { - Colour colourGuard( dimColour() ); - stream << " and"; - } - } - } - } - - private: - std::ostream& stream; - AssertionStats const& stats; - AssertionResult const& result; - std::vector messages; - std::vector::const_iterator itMessage; - bool printInfoMessages; - }; - - // Colour, message variants: - // - white: No tests ran. - // - red: Failed [both/all] N test cases, failed [both/all] M assertions. - // - white: Passed [both/all] N test cases (no assertions). - // - red: Failed N tests cases, failed M assertions. - // - green: Passed [both/all] N tests cases with M assertions. - - std::string bothOrAll( std::size_t count ) const { - return count == 1 ? std::string() : count == 2 ? "both " : "all " ; - } - - void printTotals( const Totals& totals ) const { - if( totals.testCases.total() == 0 ) { - stream << "No tests ran."; - } - else if( totals.testCases.failed == totals.testCases.total() ) { - Colour colour( Colour::ResultError ); - const std::string qualify_assertions_failed = - totals.assertions.failed == totals.assertions.total() ? - bothOrAll( totals.assertions.failed ) : std::string(); - stream << - "Failed " << bothOrAll( totals.testCases.failed ) - << pluralise( totals.testCases.failed, "test case" ) << ", " - "failed " << qualify_assertions_failed << - pluralise( totals.assertions.failed, "assertion" ) << '.'; - } - else if( totals.assertions.total() == 0 ) { - stream << - "Passed " << bothOrAll( totals.testCases.total() ) - << pluralise( totals.testCases.total(), "test case" ) - << " (no assertions)."; - } - else if( totals.assertions.failed ) { - Colour colour( Colour::ResultError ); - stream << - "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " - "failed " << pluralise( totals.assertions.failed, "assertion" ) << '.'; - } - else { - Colour colour( Colour::ResultSuccess ); - stream << - "Passed " << bothOrAll( totals.testCases.passed ) - << pluralise( totals.testCases.passed, "test case" ) << - " with " << pluralise( totals.assertions.passed, "assertion" ) << '.'; - } - } - }; - - INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter ) - -} // end namespace Catch - -namespace Catch { - // These are all here to avoid warnings about not having any out of line - // virtual methods - NonCopyable::~NonCopyable() {} - IShared::~IShared() {} - IStream::~IStream() CATCH_NOEXCEPT {} - FileStream::~FileStream() CATCH_NOEXCEPT {} - CoutStream::~CoutStream() CATCH_NOEXCEPT {} - DebugOutStream::~DebugOutStream() CATCH_NOEXCEPT {} - StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {} - IContext::~IContext() {} - IResultCapture::~IResultCapture() {} - ITestCase::~ITestCase() {} - ITestCaseRegistry::~ITestCaseRegistry() {} - IRegistryHub::~IRegistryHub() {} - IMutableRegistryHub::~IMutableRegistryHub() {} - IExceptionTranslator::~IExceptionTranslator() {} - IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {} - IReporter::~IReporter() {} - IReporterFactory::~IReporterFactory() {} - IReporterRegistry::~IReporterRegistry() {} - IStreamingReporter::~IStreamingReporter() {} - AssertionStats::~AssertionStats() {} - SectionStats::~SectionStats() {} - TestCaseStats::~TestCaseStats() {} - TestGroupStats::~TestGroupStats() {} - TestRunStats::~TestRunStats() {} - CumulativeReporterBase::SectionNode::~SectionNode() {} - CumulativeReporterBase::~CumulativeReporterBase() {} - - StreamingReporterBase::~StreamingReporterBase() {} - ConsoleReporter::~ConsoleReporter() {} - CompactReporter::~CompactReporter() {} - IRunner::~IRunner() {} - IMutableContext::~IMutableContext() {} - IConfig::~IConfig() {} - XmlReporter::~XmlReporter() {} - JunitReporter::~JunitReporter() {} - TestRegistry::~TestRegistry() {} - FreeFunctionTestCase::~FreeFunctionTestCase() {} - IGeneratorInfo::~IGeneratorInfo() {} - IGeneratorsForTest::~IGeneratorsForTest() {} - WildcardPattern::~WildcardPattern() {} - TestSpec::Pattern::~Pattern() {} - TestSpec::NamePattern::~NamePattern() {} - TestSpec::TagPattern::~TagPattern() {} - TestSpec::ExcludedPattern::~ExcludedPattern() {} - Matchers::Impl::MatcherUntypedBase::~MatcherUntypedBase() {} - - void Config::dummy() {} - - namespace TestCaseTracking { - ITracker::~ITracker() {} - TrackerBase::~TrackerBase() {} - SectionTracker::~SectionTracker() {} - IndexTracker::~IndexTracker() {} - } -} - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#endif - -#ifdef CATCH_CONFIG_MAIN -// #included from: internal/catch_default_main.hpp -#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED - -#ifndef __OBJC__ - -// Standard C/C++ main entry point -int main (int argc, char * argv[]) { - int result = Catch::Session().run( argc, argv ); - return ( result < 0xff ? result : 0xff ); -} - -#else // __OBJC__ - -// Objective-C entry point -int main (int argc, char * const argv[]) { -#if !CATCH_ARC_ENABLED - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; -#endif - - Catch::registerTestMethods(); - int result = Catch::Session().run( argc, (char* const*)argv ); - -#if !CATCH_ARC_ENABLED - [pool drain]; -#endif - - return ( result < 0xff ? result : 0xff ); -} - -#endif // __OBJC__ - -#endif - -#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED -# undef CLARA_CONFIG_MAIN -#endif - -////// - -// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ -#ifdef CATCH_CONFIG_PREFIX_ALL - -#if defined(CATCH_CONFIG_FAST_COMPILE) -#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr ) -#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) -#else -#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr ) -#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) -#endif - -#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr ) -#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) -#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) -#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr ) - -#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr ) -#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr ) - -#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr ) -#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) -#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr ) - -#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) - -#if defined(CATCH_CONFIG_FAST_COMPILE) -#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) -#else -#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) -#endif - -#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) -#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) -#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) -#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) ) -#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) ) - -#ifdef CATCH_CONFIG_VARIADIC_MACROS - #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) - #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) - #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) - #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) - #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) - #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ ) - #define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) - #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) -#else - #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) - #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) - #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) - #define CATCH_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, name, description ) - #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) - #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg ) - #define CATCH_FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg ) - #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg ) -#endif -#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) - -#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) -#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) - -#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) - -// "BDD-style" convenience wrappers -#ifdef CATCH_CONFIG_VARIADIC_MACROS -#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) -#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) -#else -#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags ) -#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) -#endif -#define CATCH_GIVEN( desc ) CATCH_SECTION( std::string( "Given: ") + desc, "" ) -#define CATCH_WHEN( desc ) CATCH_SECTION( std::string( " When: ") + desc, "" ) -#define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" ) -#define CATCH_THEN( desc ) CATCH_SECTION( std::string( " Then: ") + desc, "" ) -#define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" ) - -// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required -#else - -#if defined(CATCH_CONFIG_FAST_COMPILE) -#define REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE", Catch::ResultDisposition::Normal, expr ) -#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) - -#else -#define REQUIRE( expr ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, expr ) -#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) -#endif - -#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr ) -#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) -#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) -#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr ) - -#define CHECK( expr ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr ) -#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr ) - -#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr ) -#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) -#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr ) - -#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) - -#if defined(CATCH_CONFIG_FAST_COMPILE) -#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) -#else -#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) -#endif - -#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) -#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) -#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) -#define CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) ) -#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) ) - -#ifdef CATCH_CONFIG_VARIADIC_MACROS -#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) -#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) -#define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) -#define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) -#define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) -#define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) -#define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) -#define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) -#else -#define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) - #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) - #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) - #define REGISTER_TEST_CASE( method, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( method, name, description ) - #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) - #define FAIL( msg ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg ) - #define FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg ) - #define SUCCEED( msg ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg ) -#endif -#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) - -#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) -#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) - -#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) - -#endif - -#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) - -// "BDD-style" convenience wrappers -#ifdef CATCH_CONFIG_VARIADIC_MACROS -#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) -#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) -#else -#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags ) -#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) -#endif -#define GIVEN( desc ) SECTION( std::string(" Given: ") + desc, "" ) -#define WHEN( desc ) SECTION( std::string(" When: ") + desc, "" ) -#define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc, "" ) -#define THEN( desc ) SECTION( std::string(" Then: ") + desc, "" ) -#define AND_THEN( desc ) SECTION( std::string(" And: ") + desc, "" ) - -using Catch::Detail::Approx; - -#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED - diff --git a/third_party/variant/test/lambda_overload_test.cpp b/third_party/variant/test/lambda_overload_test.cpp deleted file mode 100644 index 764921e63..000000000 --- a/third_party/variant/test/lambda_overload_test.cpp +++ /dev/null @@ -1,282 +0,0 @@ -#include -#include - -#include -#include - -#if __cplusplus >= 201402L -#define HAS_CPP14_SUPPORT -#endif - -using namespace mapbox::util; - -template -struct tag -{ - static void dump(const char* prefix) - { - std::cout << prefix << ": " << typeid(tag).name() << std::endl; - } -}; - -template -using Either = mapbox::util::variant; - -struct Response -{ -}; - -struct Error -{ -}; - -void test_lambda_overloads() -{ - Either rv; - - rv = Response{}; - - auto visitor = make_visitor([](Response) { std::cout << "Response\n"; }, // - [](Error) { std::cout << "Error\n"; }); // - apply_visitor(visitor, rv); -} - -void test_lambda_overloads_capture() -{ - Either rv; - - rv = Error{}; - - int ok = 0; - int err = 0; - - auto visitor = make_visitor([&](Response) { ok += 1; }, // - [&](Error) { err += 1; }); // - apply_visitor(visitor, rv); - - std::cout << "Got " << ok << " ok, " << err << " err" << std::endl; -} - -void test_singleton_variant() -{ - - variant singleton; - apply_visitor(make_visitor([](int) {}), singleton); -} - -// See #180 -struct test_call_nonconst_member_visitor -{ - template - void operator() (T & obj) const - { - tag::dump("test_call_nonconst_member: visitor"); - obj.foo(); - } -}; - -void test_call_nonconst_member() -{ - struct object - { - void foo() { val = 42;} - int val = 0; - }; - - variant v = object{}; - apply_visitor(test_call_nonconst_member_visitor{}, v); - -#ifdef HAS_CPP14_SUPPORT - apply_visitor([](auto& obj) - { - tag::dump("test_call_nonconst_member: lambda"); - obj.foo(); - }, v); -#endif -} - -void test_lambda_overloads_sfinae() -#ifdef HAS_CPP14_SUPPORT -{ - variant> var; - - auto visitor = make_visitor([](auto range) -> decltype(std::begin(range), void()) { - for (auto each : range) - std::cout << each << ' '; }, - [](auto x) -> decltype(std::cout << x, void()) { - std::cout << x << std::endl; - }); - - var = 1; - apply_visitor(visitor, var); - - var = 2.f; - apply_visitor(visitor, var); - - var = std::vector{4, 5, 6}; - apply_visitor(visitor, var); -} -#else -{ -} -#endif - -void test_match_singleton() -{ - variant singleton = 5; - singleton.match([](int) {}); - - auto lambda = [](int) {}; - singleton.match(lambda); -} - -void test_match_overloads() -{ - Either rv; - - rv = Response{}; - - rv.match([](Response) { std::cout << "Response\n"; }, // - [](Error) { std::cout << "Error\n"; }); // -} - -void test_match_overloads_capture() -{ - Either rv; - - rv = Error{}; - - int ok = 0; - int err = 0; - - rv.match([&](Response) { ok += 1; }, // - [&](Error) { err += 1; }); // - - std::cout << "Got " << ok << " ok, " << err << " err" << std::endl; -} - -struct MovableOnly -{ - MovableOnly() = default; - - MovableOnly(MovableOnly&&) = default; - MovableOnly& operator=(MovableOnly&&) = default; -}; - -struct MovableCopyable -{ - MovableCopyable() = default; - - MovableCopyable(MovableCopyable&&) = default; - MovableCopyable& operator=(MovableCopyable&&) = default; - - MovableCopyable(const MovableCopyable&) = default; - MovableCopyable& operator=(const MovableCopyable&) = default; -}; - -void test_match_overloads_init_capture() -#ifdef HAS_CPP14_SUPPORT -{ - Either rv; - - rv = Error{}; - - rv.match([p = MovableOnly{}](auto&&) {}); - { - auto lambda = [p = MovableCopyable{}](auto&&) {}; - rv.match(lambda); - - rv.match([p = MovableOnly{}](Response) { std::cout << "Response\n"; }, - [p = MovableOnly{}](Error) { std::cout << "Error\n"; }); - } - { - auto lambda = [](Error) { std::cout << "Error\n"; }; - rv.match([p = MovableOnly{}](Response) { std::cout << "Response\n"; }, - lambda); - rv.match(lambda, - [p = MovableOnly{}](Response) { std::cout << "Response\n"; }); - } -} -#else -{ -} -#endif - -// See #140 -void test_match_overloads_otherwise() -#ifdef HAS_CPP14_SUPPORT -{ - - struct Center - { - }; - struct Indent - { - }; - struct Justify - { - }; - struct None - { - }; - - using Properties = mapbox::util::variant; - - Properties props = Justify{}; - - props.match([&](Center) { std::cout << "Center\n"; }, // - [&](Indent) { std::cout << "Indent\n"; }, // - [&](auto&&) { std::cout << "Otherwise\n"; }); // -} -#else -{ -} -#endif - -template -struct Moveable -{ - Moveable() = default; // Default constructible - - Moveable(const Moveable&) = delete; // Disable copy ctor - Moveable& operator=(const Moveable&) = delete; // Disable copy assign op - - Moveable(Moveable&&) = default; // Enable move ctor - Moveable& operator=(Moveable&&) = default; // Enable move assign op -}; - -void test_match_move_out_of_variant() -{ - // Distinguishable at type level - using T1 = Moveable; - using T2 = Moveable; - using T3 = mapbox::util::recursive_wrapper; - - mapbox::util::variant v = T1{}; - - std::move(v).match([](T1&&) {}, // Consume T1 by value - [](T2&&) {}); // Consume T2 by value - - mapbox::util::variant w = T2{}; - - std::move(w).match([](int&&) {}, // Consume unwrapped int - [](T2&&) {}); // Consume T2 by value -} - -int main() -{ - test_lambda_overloads(); - test_singleton_variant(); - test_call_nonconst_member(); - test_lambda_overloads_capture(); - test_lambda_overloads_sfinae(); - - test_match_singleton(); - test_match_overloads(); - test_match_overloads_capture(); - test_match_overloads_init_capture(); - test_match_overloads_otherwise(); - test_match_move_out_of_variant(); -} - -#undef HAS_CPP14_SUPPORT diff --git a/third_party/variant/test/our_variant_hello_world.cpp b/third_party/variant/test/our_variant_hello_world.cpp deleted file mode 100644 index 07aeb6241..000000000 --- a/third_party/variant/test/our_variant_hello_world.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include - -#include - -struct check -{ - template - void operator()(T const& val) const - { - if (val != 0) throw std::runtime_error("invalid"); - } -}; - -int main() -{ - typedef mapbox::util::variant variant_type; - variant_type v(0); - mapbox::util::apply_visitor(check(), v); - return 0; -} diff --git a/third_party/variant/test/recursive_wrapper_test.cpp b/third_party/variant/test/recursive_wrapper_test.cpp deleted file mode 100644 index 2d9f5f9d5..000000000 --- a/third_party/variant/test/recursive_wrapper_test.cpp +++ /dev/null @@ -1,124 +0,0 @@ -#include -#include -#include -#include -#include - -#include "auto_cpu_timer.hpp" - -#include - -using namespace mapbox; - -namespace test { - -struct add; -struct sub; - -template -struct binary_op; - -typedef util::variant>, - util::recursive_wrapper>> - expression; - -template -struct binary_op -{ - expression left; // variant instantiated here... - expression right; - - binary_op(expression&& lhs, expression&& rhs) - : left(std::move(lhs)), right(std::move(rhs)) - { - } -}; - -struct print -{ - template - void operator()(T const& val) const - { - std::cerr << val << ":" << typeid(T).name() << std::endl; - } -}; - -struct test -{ - template - std::string operator()(T const& obj) const - { - return std::string("TYPE_ID=") + typeid(obj).name(); - } -}; - -struct calculator -{ - public: - int operator()(int value) const - { - return value; - } - - int operator()(binary_op const& binary) const - { - return util::apply_visitor(*this, binary.left) + util::apply_visitor(*this, binary.right); - } - - int operator()(binary_op const& binary) const - { - return util::apply_visitor(*this, binary.left) - util::apply_visitor(*this, binary.right); - } -}; - -struct to_string -{ - public: - std::string operator()(int value) const - { - return std::to_string(value); - } - - std::string operator()(binary_op const& binary) const - { - return util::apply_visitor(*this, binary.left) + std::string("+") + util::apply_visitor(*this, binary.right); - } - - std::string operator()(binary_op const& binary) const - { - return util::apply_visitor(*this, binary.left) + std::string("-") + util::apply_visitor(*this, binary.right); - } -}; - -} // namespace test - -int main(int argc, char** argv) -{ - if (argc != 2) - { - std::cerr << "Usage" << argv[0] << " " << std::endl; - return EXIT_FAILURE; - } - - const std::size_t NUM_ITER = static_cast(std::stol(argv[1])); - - test::expression sum(test::binary_op(2, 3)); - test::expression result(test::binary_op(std::move(sum), 4)); - - std::cerr << "TYPE OF RESULT-> " << util::apply_visitor(test::test(), result) << std::endl; - - int total = 0; - { - auto_cpu_timer t; - for (std::size_t i = 0; i < NUM_ITER; ++i) - { - total += util::apply_visitor(test::calculator(), result); - } - } - std::cerr << "total=" << total << std::endl; - - std::cerr << util::apply_visitor(test::to_string(), result) << "=" << util::apply_visitor(test::calculator(), result) << std::endl; - - return EXIT_SUCCESS; -} diff --git a/third_party/variant/test/reference_wrapper_test.cpp b/third_party/variant/test/reference_wrapper_test.cpp deleted file mode 100644 index 3b2085a7a..000000000 --- a/third_party/variant/test/reference_wrapper_test.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include - -using namespace mapbox; - -namespace test { - -struct point -{ - public: - point(double x_, double y_) - : x(x_), y(y_) {} - double x; - double y; -}; - -struct line_string : std::vector -{ -}; -struct polygon : std::vector -{ -}; -using variant = util::variant, - std::reference_wrapper, - std::reference_wrapper>; - -struct print -{ - using result_type = void; - void operator()(point const& pt) const - { - std::cerr << "Point(" << pt.x << "," << pt.y << ")" << std::endl; - } - void operator()(line_string const& line) const - { - std::cerr << "Line("; - bool first = true; - for (auto const& pt : line) - { - if (!first) std::cerr << ","; - std::cerr << pt.x << " " << pt.y; - if (first) first = false; - } - std::cerr << ")" << std::endl; - } - template - void operator()(T const& val) const - { - std::cerr << typeid(T).name() << std::endl; - } -}; -} - -int main() -{ - std::cerr << sizeof(test::polygon) << std::endl; - std::cerr << sizeof(test::variant) << std::endl; - test::point pt(123, 456); - test::variant var = std::cref(pt); - util::apply_visitor(test::print(), var); - test::line_string line; - line.push_back(pt); - line.push_back(pt); - line.push_back(test::point(999, 333)); - var = std::cref(line); - util::apply_visitor(test::print(), var); - std::cerr << "Is line (cref) ? " << var.is>() << std::endl; - auto const& line2 = var.get(); // accessing underlying type of std::reference_wrapper - test::print printer; - printer(line2); - return EXIT_SUCCESS; -} diff --git a/third_party/variant/test/t/binary_visitor_1.cpp b/third_party/variant/test/t/binary_visitor_1.cpp deleted file mode 100644 index 357922675..000000000 --- a/third_party/variant/test/t/binary_visitor_1.cpp +++ /dev/null @@ -1,7 +0,0 @@ - -#include - -#define NAME_EXT " i-d" -using variant_type = mapbox::util::variant; - -#include "binary_visitor_impl.hpp" diff --git a/third_party/variant/test/t/binary_visitor_2.cpp b/third_party/variant/test/t/binary_visitor_2.cpp deleted file mode 100644 index 9863ac5a8..000000000 --- a/third_party/variant/test/t/binary_visitor_2.cpp +++ /dev/null @@ -1,7 +0,0 @@ - -#include - -#define NAME_EXT " b-i-d" -using variant_type = mapbox::util::variant; - -#include "binary_visitor_impl.hpp" diff --git a/third_party/variant/test/t/binary_visitor_3.cpp b/third_party/variant/test/t/binary_visitor_3.cpp deleted file mode 100644 index a1c367eb0..000000000 --- a/third_party/variant/test/t/binary_visitor_3.cpp +++ /dev/null @@ -1,7 +0,0 @@ - -#include - -#define NAME_EXT " i-d-b" -using variant_type = mapbox::util::variant; - -#include "binary_visitor_impl.hpp" diff --git a/third_party/variant/test/t/binary_visitor_4.cpp b/third_party/variant/test/t/binary_visitor_4.cpp deleted file mode 100644 index 8cc66ab59..000000000 --- a/third_party/variant/test/t/binary_visitor_4.cpp +++ /dev/null @@ -1,7 +0,0 @@ - -#include - -#define NAME_EXT " b-i-d-c" -using variant_type = mapbox::util::variant; - -#include "binary_visitor_impl.hpp" diff --git a/third_party/variant/test/t/binary_visitor_5.cpp b/third_party/variant/test/t/binary_visitor_5.cpp deleted file mode 100644 index f6673c887..000000000 --- a/third_party/variant/test/t/binary_visitor_5.cpp +++ /dev/null @@ -1,7 +0,0 @@ - -#include - -#define NAME_EXT " b-i-c-d-i" -using variant_type = mapbox::util::variant; - -#include "binary_visitor_impl.hpp" diff --git a/third_party/variant/test/t/binary_visitor_6.cpp b/third_party/variant/test/t/binary_visitor_6.cpp deleted file mode 100644 index d259d7396..000000000 --- a/third_party/variant/test/t/binary_visitor_6.cpp +++ /dev/null @@ -1,7 +0,0 @@ - -#include - -#define NAME_EXT " b-i-i-d-c-u" -using variant_type = mapbox::util::variant; - -#include "binary_visitor_impl.hpp" diff --git a/third_party/variant/test/t/binary_visitor_impl.hpp b/third_party/variant/test/t/binary_visitor_impl.hpp deleted file mode 100644 index 4ee1f08e7..000000000 --- a/third_party/variant/test/t/binary_visitor_impl.hpp +++ /dev/null @@ -1,204 +0,0 @@ - -#include - -#include "catch.hpp" - -#include - -struct add_visitor -{ - add_visitor() {} - - template - double operator()(A a, B b) const - { - return a + b; - } -}; - -TEST_CASE("const binary visitor works on const variants" NAME_EXT, "[visitor][binary visitor]") -{ - const variant_type a{7}; - const variant_type b = 3; - const variant_type c{7.1}; - const variant_type d = 2.9; - - const add_visitor v; - - REQUIRE(mapbox::util::apply_visitor(v, a, b) == Approx(10)); - REQUIRE(mapbox::util::apply_visitor(v, c, d) == Approx(10)); - REQUIRE(mapbox::util::apply_visitor(v, a, c) == Approx(14.1)); - REQUIRE(mapbox::util::apply_visitor(v, a, d) == Approx(9.9)); - - REQUIRE(mapbox::util::apply_visitor(v, b, a) == Approx(10)); - REQUIRE(mapbox::util::apply_visitor(v, d, c) == Approx(10)); - REQUIRE(mapbox::util::apply_visitor(v, c, a) == Approx(14.1)); - REQUIRE(mapbox::util::apply_visitor(v, d, a) == Approx(9.9)); -} - -TEST_CASE("non-const binary visitor works on const variants" NAME_EXT, "[visitor][binary visitor]") -{ - const variant_type a = 7; - const variant_type b = 3; - const variant_type c = 7.1; - const variant_type d = 2.9; - - add_visitor v; - - REQUIRE(mapbox::util::apply_visitor(v, a, b) == Approx(10)); - REQUIRE(mapbox::util::apply_visitor(v, c, d) == Approx(10)); - REQUIRE(mapbox::util::apply_visitor(v, a, c) == Approx(14.1)); - REQUIRE(mapbox::util::apply_visitor(v, a, d) == Approx(9.9)); - - REQUIRE(mapbox::util::apply_visitor(v, b, a) == Approx(10)); - REQUIRE(mapbox::util::apply_visitor(v, d, c) == Approx(10)); - REQUIRE(mapbox::util::apply_visitor(v, c, a) == Approx(14.1)); - REQUIRE(mapbox::util::apply_visitor(v, d, a) == Approx(9.9)); -} - -TEST_CASE("const binary visitor works on non-const variants" NAME_EXT, "[visitor][binary visitor]") -{ - variant_type a = 7; - variant_type b = 3; - variant_type c = 7.1; - variant_type d = 2.9; - - const add_visitor v; - - REQUIRE(mapbox::util::apply_visitor(v, a, b) == Approx(10)); - REQUIRE(mapbox::util::apply_visitor(v, c, d) == Approx(10)); - REQUIRE(mapbox::util::apply_visitor(v, a, c) == Approx(14.1)); - REQUIRE(mapbox::util::apply_visitor(v, a, d) == Approx(9.9)); - - REQUIRE(mapbox::util::apply_visitor(v, b, a) == Approx(10)); - REQUIRE(mapbox::util::apply_visitor(v, d, c) == Approx(10)); - REQUIRE(mapbox::util::apply_visitor(v, c, a) == Approx(14.1)); - REQUIRE(mapbox::util::apply_visitor(v, d, a) == Approx(9.9)); -} - -TEST_CASE("non-const binary visitor works on non-const variants" NAME_EXT, "[visitor][binary visitor]") -{ - variant_type a = 7; - variant_type b = 3; - variant_type c = 7.1; - variant_type d = 2.9; - - add_visitor v; - - REQUIRE(mapbox::util::apply_visitor(v, a, b) == Approx(10)); - REQUIRE(mapbox::util::apply_visitor(v, c, d) == Approx(10)); - REQUIRE(mapbox::util::apply_visitor(v, a, c) == Approx(14.1)); - REQUIRE(mapbox::util::apply_visitor(v, a, d) == Approx(9.9)); - - REQUIRE(mapbox::util::apply_visitor(v, b, a) == Approx(10)); - REQUIRE(mapbox::util::apply_visitor(v, d, c) == Approx(10)); - REQUIRE(mapbox::util::apply_visitor(v, c, a) == Approx(14.1)); - REQUIRE(mapbox::util::apply_visitor(v, d, a) == Approx(9.9)); -} - -TEST_CASE("rvalue binary visitor works on const variants" NAME_EXT, "[visitor][binary visitor]") -{ - const variant_type a = 7; - const variant_type b = 3; - const variant_type c = 7.1; - const variant_type d = 2.9; - - REQUIRE(mapbox::util::apply_visitor(add_visitor{}, a, b) == Approx(10)); - REQUIRE(mapbox::util::apply_visitor(add_visitor{}, c, d) == Approx(10)); - REQUIRE(mapbox::util::apply_visitor(add_visitor{}, a, c) == Approx(14.1)); - REQUIRE(mapbox::util::apply_visitor(add_visitor{}, a, d) == Approx(9.9)); - - REQUIRE(mapbox::util::apply_visitor(add_visitor{}, b, a) == Approx(10)); - REQUIRE(mapbox::util::apply_visitor(add_visitor{}, d, c) == Approx(10)); - REQUIRE(mapbox::util::apply_visitor(add_visitor{}, c, a) == Approx(14.1)); - REQUIRE(mapbox::util::apply_visitor(add_visitor{}, d, a) == Approx(9.9)); -} - -struct sum_mul_visitor -{ - double sum; - - sum_mul_visitor() : sum(0.0) {} - - template - double operator()(A a, B b) - { - double m = a * b; - sum += m; - return m; - } -}; - -TEST_CASE("mutable binary visitor works" NAME_EXT, "[visitor][binary visitor]") -{ - const variant_type a = 2; - const variant_type b = 3; - const variant_type c = 0.1; - const variant_type d = 0.2; - - sum_mul_visitor v; - - REQUIRE(mapbox::util::apply_visitor(v, a, b) == Approx(6)); - REQUIRE(mapbox::util::apply_visitor(v, c, d) == Approx(0.02)); - REQUIRE(mapbox::util::apply_visitor(v, a, c) == Approx(0.2)); - REQUIRE(mapbox::util::apply_visitor(v, a, d) == Approx(0.4)); - - REQUIRE(v.sum == Approx(6.62)); - - REQUIRE(mapbox::util::apply_visitor(v, b, a) == Approx(6)); - REQUIRE(mapbox::util::apply_visitor(v, d, c) == Approx(0.02)); - REQUIRE(mapbox::util::apply_visitor(v, c, a) == Approx(0.2)); - REQUIRE(mapbox::util::apply_visitor(v, d, a) == Approx(0.4)); -} - -struct swap_visitor -{ - swap_visitor(){}; - - template - void operator()(A& a, B& b) const - { - using T = typename std::common_type::type; - T tmp = a; - a = static_cast(b); - b = static_cast(tmp); - } -}; - -TEST_CASE("static mutating visitor on mutable variants works" NAME_EXT, "[visitor][binary visitor]") -{ - variant_type a = 2; - variant_type b = 3; - variant_type c = 0.1; - variant_type d = 0.2; - - const swap_visitor v; - - SECTION("swap a and b") - { - mapbox::util::apply_visitor(v, a, b); - REQUIRE(a.get() == 3); - REQUIRE(b.get() == 2); - } - - SECTION("swap c and d") - { - mapbox::util::apply_visitor(v, c, d); - REQUIRE(c.get() == Approx(0.2)); - REQUIRE(d.get() == Approx(0.1)); - } - - SECTION("swap a and c") - { - mapbox::util::apply_visitor(v, a, c); - REQUIRE(a.get() == 0); - REQUIRE(c.get() == Approx(2.0)); - } - - SECTION("swap c and a") - { - mapbox::util::apply_visitor(v, c, a); - REQUIRE(a.get() == 0); - REQUIRE(c.get() == Approx(2.0)); - } -} diff --git a/third_party/variant/test/t/issue122.cpp b/third_party/variant/test/t/issue122.cpp deleted file mode 100644 index 12de7d54e..000000000 --- a/third_party/variant/test/t/issue122.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "catch.hpp" - -#include -#include - -// https://github.com/mapbox/variant/issues/122 - -struct X -{ - template - X(const ValueType&) {} -}; - - -TEST_CASE("Correctly choose appropriate constructor", "[variant]") -{ - mapbox::util::variant a{123}; - decltype(a) b(a); - REQUIRE(a.which() == b.which()); -} diff --git a/third_party/variant/test/t/issue21.cpp b/third_party/variant/test/t/issue21.cpp deleted file mode 100644 index 4e6be8a6f..000000000 --- a/third_party/variant/test/t/issue21.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "catch.hpp" - -#include -#include - -// https://github.com/mapbox/variant/issues/21 - -static int count; - -struct t1 -{ - int value; - - t1(t1 const& rhs) - : value(rhs.value) - { - ++count; - } - t1(int v) : value(v) - { - ++count; - } - ~t1() - { - --count; - } -}; - -struct t2 -{ - int value; - t2(int v) : value(v) - { // constructor fails - throw std::runtime_error("fail"); - } -}; - -TEST_CASE("set() works cleanly even if the constructor throws ", "[variant]") -{ - - using variant_type = mapbox::util::variant; - - count = 0; - { - t1 obj{42}; - variant_type v = obj; - REQUIRE(v.is()); - REQUIRE(v.get().value == 42); - REQUIRE_THROWS(v.set(13)); - } - REQUIRE(count == 0); -} diff --git a/third_party/variant/test/t/mutating_visitor.cpp b/third_party/variant/test/t/mutating_visitor.cpp deleted file mode 100644 index cd4f162eb..000000000 --- a/third_party/variant/test/t/mutating_visitor.cpp +++ /dev/null @@ -1,36 +0,0 @@ - -#include "catch.hpp" - -#include -#include - -#include - -template -struct mutating_visitor -{ - mutating_visitor(T& val) - : val_(val) {} - - void operator()(T& val) const - { - val = val_; - } - - template - void operator()(T1&) const - { - } // no-op - - T& val_; -}; - -TEST_CASE("variant visitation", "[visitor][unary visitor]") -{ - mapbox::util::variant var(123); - REQUIRE(var.get() == 123); - int val = 456; - const mutating_visitor visitor(val); - mapbox::util::apply_visitor(visitor, var); - REQUIRE(var.get() == 456); -} diff --git a/third_party/variant/test/t/nothrow_move.cpp b/third_party/variant/test/t/nothrow_move.cpp deleted file mode 100644 index b41c8fe9b..000000000 --- a/third_party/variant/test/t/nothrow_move.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include -#include - -#include - -using namespace mapbox; - -namespace test { - -struct t_noexcept_true_1 { - t_noexcept_true_1(t_noexcept_true_1&&) noexcept = default; - t_noexcept_true_1& operator=(t_noexcept_true_1&&) noexcept = default; -}; - -struct t_noexcept_true_2 { - t_noexcept_true_2(t_noexcept_true_2&&) noexcept = default; - t_noexcept_true_2& operator=(t_noexcept_true_2&&) noexcept = default; -}; - -struct t_noexcept_false_1 { - t_noexcept_false_1(t_noexcept_false_1&&) noexcept(false) {} - t_noexcept_false_1& operator=(t_noexcept_false_1&&) noexcept(false) { return *this; } -}; - -using should_be_no_throw_copyable = util::variant; -static_assert(std::is_nothrow_move_assignable::value, - "variants with no-throw move assignable types should be " - "no-throw move nothrow assignable"); - -using should_be_no_throw_assignable = util::variant; -static_assert(std::is_nothrow_move_constructible::value, - "variants with no-throw move assignable types should be " - "no-throw move nothrow assignable"); - -using should_not_be_no_throw_copyable = util::variant; -static_assert(not std::is_nothrow_move_assignable::value, - "variants with no-throw move assignable types should be " - "no-throw move nothrow assignable"); - -using should_not_be_no_throw_assignable = util::variant; -static_assert(not std::is_nothrow_move_constructible::value, - "variants with no-throw move assignable types should be " - "no-throw move nothrow assignable"); - - -// this type cannot be nothrow converted from either of its types, even the nothrow moveable one, -// because the conversion operator moves the whole variant. -using convertable_test_type = util::variant; - -// this type can be nothrow converted from either of its types. -using convertable_test_type_2 = util::variant; - -static_assert(not std::is_nothrow_assignable::value, - "variants with noexcept(true) move constructible types should be nothrow-convertible " - "from those types only IF the variant itself is nothrow_move_assignable"); - -static_assert(not std::is_nothrow_assignable::value, - "variants with noexcept(false) move constructible types should not be nothrow-convertible " - "from those types"); - -static_assert(std::is_nothrow_assignable::value, - "variants with noexcept(true) move constructible types should be nothrow-convertible " - "from those types only IF the variant itself is nothrow_move_assignable"); - - -} // namespace test diff --git a/third_party/variant/test/t/optional.cpp b/third_party/variant/test/t/optional.cpp deleted file mode 100644 index 4f77a0af9..000000000 --- a/third_party/variant/test/t/optional.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include "catch.hpp" - -#include - -struct dummy -{ - dummy(int _m_1, int _m_2) : m_1(_m_1), m_2(_m_2) {} - int m_1; - int m_2; -}; - -TEST_CASE("optional can be instantiated with a POD type", "[optional]") -{ - mapbox::util::optional dbl_opt; - - REQUIRE(!dbl_opt); - dbl_opt = 3; - REQUIRE(dbl_opt); - - REQUIRE(dbl_opt.get() == 3); - REQUIRE(*dbl_opt == 3); -} - -TEST_CASE("copy c'tor", "[optional]") -{ - mapbox::util::optional dbl_opt; - - REQUIRE(!dbl_opt); - dbl_opt = 3; - REQUIRE(dbl_opt); - - mapbox::util::optional other = dbl_opt; - - REQUIRE(other.get() == 3); - REQUIRE(*other == 3); -} - -TEST_CASE("const operator*, const get()", "[optional]") -{ - const mapbox::util::optional dbl_opt = 3; - - REQUIRE(dbl_opt); - - auto pi1 = dbl_opt.get(); - auto pi2 = *dbl_opt; - - REQUIRE(pi1 == 3); - REQUIRE(pi2 == 3); -} - -TEST_CASE("non-const operator*, non-const get()", "[optional]") -{ - mapbox::util::optional dbl_opt = 3; - - REQUIRE(dbl_opt); - - auto pi1 = dbl_opt.get(); - auto pi2 = *dbl_opt; - - REQUIRE(pi1 == 3); - REQUIRE(pi2 == 3); -} - -TEST_CASE("emplace initialization, reset", "[optional]") -{ - mapbox::util::optional dummy_opt; - REQUIRE(!dummy_opt); - - // rvalues, baby! - dummy_opt.emplace(1, 2); - REQUIRE(dummy_opt); - REQUIRE(dummy_opt.get().m_1 == 1); - REQUIRE((*dummy_opt).m_2 == 2); - - dummy_opt.reset(); - REQUIRE(!dummy_opt); -} - -TEST_CASE("assignment", "[optional]") -{ - mapbox::util::optional a; - mapbox::util::optional b; - - a = 1; - b = 3; - REQUIRE(a.get() == 1); - REQUIRE(b.get() == 3); - b = a; - REQUIRE(a.get() == b.get()); - REQUIRE(b.get() == 1); -} - -TEST_CASE("self assignment", "[optional]") -{ - mapbox::util::optional a; - - a = 1; - REQUIRE(a.get() == 1); -#if !defined(__clang__) - a = a; - REQUIRE(a.get() == 1); -#endif -} diff --git a/third_party/variant/test/t/recursive_wrapper.cpp b/third_party/variant/test/t/recursive_wrapper.cpp deleted file mode 100644 index 0a6848da8..000000000 --- a/third_party/variant/test/t/recursive_wrapper.cpp +++ /dev/null @@ -1,185 +0,0 @@ -#include "catch.hpp" - -#include -#include - -#include -#include - -using rwi = mapbox::util::recursive_wrapper; -using rwp = mapbox::util::recursive_wrapper>; - -static_assert(std::is_same::value, "type check failed"); - -TEST_CASE("recursive wrapper of int") -{ - - SECTION("construct with value") - { - rwi a{7}; - - REQUIRE(a.get() == 7); - REQUIRE(*a.get_pointer() == 7); - - a = 8; - REQUIRE(a.get() == 8); - - rwi b{a}; - REQUIRE(b.get() == 8); - - rwi c; - c = b; - REQUIRE(b.get() == 8); - REQUIRE(c.get() == 8); - - c = 9; - REQUIRE(c.get() == 9); - - int x = 10; - c = x; - REQUIRE(c.get() == 10); - - b = std::move(c); - REQUIRE(b.get() == 10); - } - - SECTION("construct with const reference") - { - int i = 7; - rwi a{i}; - - REQUIRE(a.get() == 7); - } - - SECTION("implicit conversion to reference of underlying type") - { - - SECTION("const") - { - rwi const a{7}; - REQUIRE(a.get() == 7); - REQUIRE(*a.get_pointer() == 7); - - rwi::type const& underlying = a; - REQUIRE(underlying == 7); - } - - SECTION("non const") - { - rwi a{7}; - REQUIRE(a.get() == 7); - REQUIRE(*a.get_pointer() == 7); - - rwi::type& underlying = a; - REQUIRE(underlying == 7); - a = 8; - REQUIRE(underlying == 8); - } - } -} - -TEST_CASE("move of recursive wrapper") -{ - rwi a{1}; - - SECTION("move constructor") - { - rwi b{std::move(a)}; - REQUIRE(b.get() == 1); - } - - SECTION("operator= on rvalue") - { - rwi b{2}; - b = std::move(a); - REQUIRE(b.get() == 1); - } -} - -TEST_CASE("swap") -{ - rwi a{1}; - rwi b{2}; - - REQUIRE(a.get() == 1); - REQUIRE(b.get() == 2); - - using std::swap; - swap(a, b); - - REQUIRE(a.get() == 2); - REQUIRE(b.get() == 1); -} - -TEST_CASE("recursive wrapper of pair") -{ - - SECTION("default constructed") - { - rwp a; - REQUIRE(a.get().first == 0); - REQUIRE(a.get().second == 0); - } - - SECTION("construct with value") - { - rwp a{std::make_pair(1, 2)}; - - REQUIRE(a.get().first == 1); - REQUIRE(a.get().second == 2); - - REQUIRE(a.get_pointer()->first == 1); - REQUIRE(a.get_pointer()->second == 2); - - a = {3, 4}; - REQUIRE(a.get().first == 3); - REQUIRE(a.get().second == 4); - - rwp b{a}; - REQUIRE(b.get().first == 3); - REQUIRE(b.get().second == 4); - - rwp c; - c = b; - REQUIRE(b.get().first == 3); - REQUIRE(b.get().second == 4); - REQUIRE(c.get().first == 3); - REQUIRE(c.get().second == 4); - - c = {5, 6}; - REQUIRE(c.get().first == 5); - REQUIRE(c.get().second == 6); - - b = std::move(c); - REQUIRE(b.get().first == 5); - REQUIRE(b.get().second == 6); - //REQUIRE(c.get_pointer() == nullptr); - } - - SECTION("Multiple recurssive wrappers of polymorphic types") - { - // https://github.com/mapbox/variant/issues/146 - // (Visual Studio 2015 update 3) - using namespace mapbox::util; - struct Base; - struct Derived; - using Variant = variant, recursive_wrapper>; - struct Base { }; - struct Derived : public Base { }; - { - Base base; - Derived derived; - Variant v; - v = base; - v = derived; // compile error prior https://github.com/mapbox/variant/pull/147 - CHECK(v.is()); - } - { - Derived derived; - Variant v(derived); // compile error prior https://github.com/mapbox/variant/pull/147 - CHECK(v.is()); - } - - - } -} diff --git a/third_party/variant/test/t/sizeof.cpp b/third_party/variant/test/t/sizeof.cpp deleted file mode 100644 index 72314e2b8..000000000 --- a/third_party/variant/test/t/sizeof.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include -#include - -#include "catch.hpp" - -#include -#include - -struct some_struct -{ - int a; - bool b; - std::string c; -}; - -using variant_internal_index_type = mapbox::util::type_index_t; - -TEST_CASE("size of variants") -{ - constexpr const auto min_overhead = sizeof(variant_internal_index_type); - - using namespace std; // workaround for bug in GCC <= 4.8 where max_align_t is not in std - constexpr const auto max_overhead = alignof(max_align_t) + min_overhead; - - using v1 = mapbox::util::variant; - using v2 = mapbox::util::variant; - using v3 = mapbox::util::variant; - using v4 = mapbox::util::variant; - using v5 = mapbox::util::variant; - - constexpr const auto si = sizeof(int); - constexpr const auto sb = sizeof(bool); - constexpr const auto si64 = sizeof(int64_t); - constexpr const auto sd = sizeof(double); - constexpr const auto sstr = sizeof(std::string); - constexpr const auto spi = sizeof(std::pair); - constexpr const auto ss = sizeof(some_struct); - - REQUIRE(sizeof(v1) <= max_overhead + si); - REQUIRE(sizeof(v2) <= max_overhead + std::max({si, sb, si64})); - REQUIRE(sizeof(v3) <= max_overhead + std::max({si, sstr})); - REQUIRE(sizeof(v4) <= max_overhead + sstr); - REQUIRE(sizeof(v5) <= max_overhead + ss); - - REQUIRE(sizeof(v1) >= min_overhead + si); - REQUIRE(sizeof(v2) >= min_overhead + std::max({si, sb, si64})); - REQUIRE(sizeof(v3) >= min_overhead + std::max({si, sstr})); - REQUIRE(sizeof(v4) >= min_overhead + sstr); - REQUIRE(sizeof(v5) >= min_overhead + ss); -} diff --git a/third_party/variant/test/t/unary_visitor.cpp b/third_party/variant/test/t/unary_visitor.cpp deleted file mode 100644 index e447bfa42..000000000 --- a/third_party/variant/test/t/unary_visitor.cpp +++ /dev/null @@ -1,127 +0,0 @@ - -#include "catch.hpp" - -#include -#include - -#include - -struct some_visitor -{ - int var_; - - some_visitor(int init) - : var_(init) {} - - int operator()(int val) const - { - return var_ + val; - } - - int operator()(double val) const - { - return var_ + int(val); - } - - int operator()(const std::string&) const - { - return 0; - } -}; - -TEST_CASE("non-const visitor works on const variants", "[visitor][unary visitor]") -{ - using variant_type = const mapbox::util::variant; - variant_type var1(123); - variant_type var2(3.2); - variant_type var3("foo"); - REQUIRE(var1.get() == 123); - REQUIRE(var2.get() == Approx(3.2)); - REQUIRE(var3.get() == "foo"); - - some_visitor visitor{1}; - - REQUIRE(mapbox::util::apply_visitor(visitor, var1) == 124); - REQUIRE(mapbox::util::apply_visitor(visitor, var2) == 4); - REQUIRE(mapbox::util::apply_visitor(visitor, var3) == 0); -} - -TEST_CASE("const visitor works on const variants", "[visitor][unary visitor]") -{ - using variant_type = const mapbox::util::variant; - variant_type var1(123); - variant_type var2(3.2); - variant_type var3("foo"); - REQUIRE(var1.get() == 123); - REQUIRE(var2.get() == Approx(3.2)); - REQUIRE(var3.get() == "foo"); - - const some_visitor visitor{1}; - - REQUIRE(mapbox::util::apply_visitor(visitor, var1) == 124); - REQUIRE(mapbox::util::apply_visitor(visitor, var2) == 4); - REQUIRE(mapbox::util::apply_visitor(visitor, var3) == 0); -} - -TEST_CASE("rvalue visitor works on const variants", "[visitor][unary visitor]") -{ - using variant_type = const mapbox::util::variant; - variant_type var1(123); - variant_type var2(3.2); - variant_type var3("foo"); - REQUIRE(var1.get() == 123); - REQUIRE(var2.get() == Approx(3.2)); - REQUIRE(var3.get() == "foo"); - - REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, var1) == 124); - REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, var2) == 4); - REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, var3) == 0); -} - -TEST_CASE("visitor works on rvalue variants", "[visitor][unary visitor]") -{ - using variant_type = const mapbox::util::variant; - - REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, variant_type{123}) == 124); - REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, variant_type{3.2}) == 4); - REQUIRE(mapbox::util::apply_visitor(some_visitor{1}, variant_type{"foo"}) == 0); -} - -struct total_sizeof -{ - total_sizeof() : total_(0) {} - - template - int operator()(const Value&) const - { - total_ += int(sizeof(Value)); - return total_; - } - - int result() const - { - return total_; - } - - mutable int total_; - -}; // total_sizeof - -TEST_CASE("changes in visitor should be visible", "[visitor][unary visitor]") -{ - using variant_type = mapbox::util::variant; - variant_type v; - total_sizeof ts; - v = 5.9; - REQUIRE(mapbox::util::apply_visitor(ts, v) == sizeof(double)); - REQUIRE(ts.result() == sizeof(double)); -} - -TEST_CASE("changes in const visitor (with mutable internals) should be visible", "[visitor][unary visitor]") -{ - using variant_type = const mapbox::util::variant; - variant_type v{"foo"}; - const total_sizeof ts; - REQUIRE(mapbox::util::apply_visitor(ts, v) == sizeof(std::string)); - REQUIRE(ts.result() == sizeof(std::string)); -} diff --git a/third_party/variant/test/t/variant.cpp b/third_party/variant/test/t/variant.cpp deleted file mode 100644 index 2c6d6669d..000000000 --- a/third_party/variant/test/t/variant.cpp +++ /dev/null @@ -1,551 +0,0 @@ -#include "catch.hpp" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Hack to make nullptr work with Catch -namespace std { - -template -std::basic_ostream& operator<<(std::basic_ostream& os, std::nullptr_t) -{ - return os << (void*)nullptr; -} -} - -TEST_CASE("variant can be moved into vector", "[variant]") -{ - using variant_type = mapbox::util::variant; - variant_type v(std::string("test")); - std::vector vec; - vec.emplace_back(std::move(v)); - REQUIRE(v.get() != std::string("test")); - REQUIRE(vec.at(0).get() == std::string("test")); -} - -TEST_CASE("variant should support built-in types", "[variant]") -{ - SECTION("bool") - { - mapbox::util::variant v(true); - REQUIRE(v.valid()); - REQUIRE(v.is()); - REQUIRE(v.which() == 0); - REQUIRE(v.get() == true); - v.set(false); - REQUIRE(v.get() == false); - v = true; - REQUIRE(v == mapbox::util::variant(true)); - } - SECTION("nullptr") - { - using value_type = std::nullptr_t; - mapbox::util::variant v(nullptr); - REQUIRE(v.valid()); - REQUIRE(v.is()); - REQUIRE(v.which() == 0); - REQUIRE(v.get() == nullptr); - REQUIRE(v == mapbox::util::variant(nullptr)); - } - SECTION("unique_ptr") - { - using value_type = std::unique_ptr; - mapbox::util::variant v(value_type(new std::string("hello"))); - REQUIRE(v.valid()); - REQUIRE(v.is()); - REQUIRE(v.which() == 0); - REQUIRE(*v.get().get() == *value_type(new std::string("hello")).get()); - REQUIRE(*v.get() == "hello"); - } - SECTION("string") - { - using value_type = std::string; - mapbox::util::variant v(value_type("hello")); - REQUIRE(v.valid()); - REQUIRE(v.is()); - REQUIRE(v.which() == 0); - REQUIRE(v.get() == value_type("hello")); - v.set(value_type("there")); - REQUIRE(v.get() == value_type("there")); - v = value_type("variant"); - REQUIRE(v == mapbox::util::variant(value_type("variant"))); - } - SECTION("size_t") - { - using value_type = std::size_t; - mapbox::util::variant v(std::numeric_limits::max()); - REQUIRE(v.valid()); - REQUIRE(v.is()); - REQUIRE(v.which() == 0); - REQUIRE(v.get() == std::numeric_limits::max()); - v.set(value_type(0)); - REQUIRE(v.get() == value_type(0)); - v = value_type(1); - REQUIRE(v == mapbox::util::variant(value_type(1))); - } - SECTION("int8_t") - { - using value_type = std::int8_t; - mapbox::util::variant v(std::numeric_limits::max()); - REQUIRE(v.valid()); - REQUIRE(v.is()); - REQUIRE(v.which() == 0); - REQUIRE(v.get() == std::numeric_limits::max()); - v.set(0); - REQUIRE(v.get() == value_type(0)); - v = value_type(1); - REQUIRE(v == mapbox::util::variant(value_type(1))); - } - SECTION("int16_t") - { - using value_type = std::int16_t; - mapbox::util::variant v(std::numeric_limits::max()); - REQUIRE(v.valid()); - REQUIRE(v.is()); - REQUIRE(v.which() == 0); - REQUIRE(v.get() == std::numeric_limits::max()); - v.set(0); - REQUIRE(v.get() == value_type(0)); - v = value_type(1); - REQUIRE(v == mapbox::util::variant(value_type(1))); - } - SECTION("int32_t") - { - using value_type = std::int32_t; - mapbox::util::variant v(std::numeric_limits::max()); - REQUIRE(v.valid()); - REQUIRE(v.is()); - REQUIRE(v.which() == 0); - REQUIRE(v.get() == std::numeric_limits::max()); - v.set(0); - REQUIRE(v.get() == value_type(0)); - v = value_type(1); - REQUIRE(v == mapbox::util::variant(value_type(1))); - } - SECTION("int64_t") - { - using value_type = std::int64_t; - mapbox::util::variant v(std::numeric_limits::max()); - REQUIRE(v.valid()); - REQUIRE(v.is()); - REQUIRE(v.which() == 0); - REQUIRE(v.get() == std::numeric_limits::max()); - v.set(0); - REQUIRE(v.get() == value_type(0)); - v = value_type(1); - REQUIRE(v == mapbox::util::variant(value_type(1))); - } -} - -struct MissionInteger -{ - using value_type = uint64_t; - value_type val_; - - public: - MissionInteger(uint64_t val) : val_(val) {} - - bool operator==(MissionInteger const& rhs) const - { - return (val_ == rhs.get()); - } - - uint64_t get() const - { - return val_; - } -}; - -std::ostream& operator<<(std::ostream& os, MissionInteger const& rhs) -{ - os << rhs.get(); - return os; -} - -TEST_CASE("variant should support custom types", "[variant]") -{ - // http://www.missionintegers.com/integer/34838300 - mapbox::util::variant v(MissionInteger(34838300)); - REQUIRE(v.valid()); - REQUIRE(v.is()); - REQUIRE(v.which() == 0); - REQUIRE(v.get() == MissionInteger(34838300)); - REQUIRE(v.get().get() == MissionInteger::value_type(34838300)); - // TODO: should both of the set usages below compile? - v.set(MissionInteger::value_type(0)); - v.set(MissionInteger(0)); - REQUIRE(v.get().get() == MissionInteger::value_type(0)); - v = MissionInteger(1); - REQUIRE(v == mapbox::util::variant(MissionInteger(1))); -} - -TEST_CASE("variant::which() returns zero based index of stored type", "[variant]") -{ - using variant_type = mapbox::util::variant; - // which() returns index in forward order - REQUIRE(0 == variant_type(true).which()); - REQUIRE(1 == variant_type(std::string("test")).which()); - REQUIRE(2 == variant_type(std::uint64_t(0)).which()); - REQUIRE(3 == variant_type(std::int64_t(0)).which()); - REQUIRE(4 == variant_type(double(0.0)).which()); - REQUIRE(5 == variant_type(float(0.0)).which()); -} - -TEST_CASE("get with wrong type (here: double) should throw", "[variant]") -{ - using variant_type = mapbox::util::variant; - variant_type var = 5; - REQUIRE(var.is()); - REQUIRE_FALSE(var.is()); - REQUIRE(var.get() == 5); - REQUIRE_THROWS_AS(var.get(), - mapbox::util::bad_variant_access&); -} - -TEST_CASE("get with wrong type (here: int) should throw", "[variant]") -{ - using variant_type = mapbox::util::variant; - variant_type var = 5.0; - REQUIRE(var.is()); - REQUIRE_FALSE(var.is()); - REQUIRE(var.get() == 5.0); - REQUIRE(mapbox::util::get(var) == 5.0); - REQUIRE_THROWS_AS(var.get(), - mapbox::util::bad_variant_access&); - REQUIRE_THROWS_AS(mapbox::util::get(var), - mapbox::util::bad_variant_access&); -} - -TEST_CASE("get on const varint with wrong type (here: int) should throw", "[variant]") -{ - using variant_type = mapbox::util::variant; - const variant_type var = 5.0; - REQUIRE(var.is()); - REQUIRE_FALSE(var.is()); - REQUIRE(var.get() == 5.0); - REQUIRE(mapbox::util::get(var) == 5.0); - REQUIRE_THROWS_AS(var.get(), - mapbox::util::bad_variant_access&); - REQUIRE_THROWS_AS(mapbox::util::get(var), - mapbox::util::bad_variant_access&); -} - -TEST_CASE("get with any type should throw if not initialized", "[variant]") -{ - mapbox::util::variant var{mapbox::util::no_init()}; - REQUIRE_THROWS_AS(var.get(), - mapbox::util::bad_variant_access&); - REQUIRE_THROWS_AS(var.get(), - mapbox::util::bad_variant_access&); -} - -TEST_CASE("no_init variant can be copied and moved from", "[variant]") -{ - using variant_type = mapbox::util::variant; - - variant_type v1{mapbox::util::no_init()}; - variant_type v2{42}; - variant_type v3{23}; - - REQUIRE(v2.get() == 42); - v2 = v1; - REQUIRE_THROWS_AS(v2.get(), - mapbox::util::bad_variant_access&); - - REQUIRE(v3.get() == 23); - v3 = std::move(v1); - REQUIRE_THROWS_AS(v3.get(), - mapbox::util::bad_variant_access&); -} - -TEST_CASE("no_init variant can be copied and moved to", "[variant]") -{ - using variant_type = mapbox::util::variant; - - variant_type v1{42}; - variant_type v2{mapbox::util::no_init()}; - variant_type v3{mapbox::util::no_init()}; - - REQUIRE_THROWS_AS(v2.get(), - mapbox::util::bad_variant_access&); - - REQUIRE(v1.get() == 42); - v2 = v1; - REQUIRE(v2.get() == 42); - REQUIRE(v1.get() == 42); - - REQUIRE_THROWS_AS(v3.get(), - mapbox::util::bad_variant_access&); - - v3 = std::move(v1); - REQUIRE(v3.get() == 42); -} - -TEST_CASE("implicit conversion", "[variant][implicit conversion]") -{ - using variant_type = mapbox::util::variant; - variant_type var(5.0); // converted to int - REQUIRE(var.get() == 5); - var = 6.0; // works for operator=, too - REQUIRE(var.get() == 6); -} - -TEST_CASE("implicit conversion to first type in variant type list", "[variant][implicit conversion]") -{ - using variant_type = mapbox::util::variant; - variant_type var = 5l; // converted to long - REQUIRE(var.get() == 5); - REQUIRE_THROWS_AS(var.get(), - mapbox::util::bad_variant_access&); -} - -TEST_CASE("implicit conversion to unsigned char", "[variant][implicit conversion]") -{ - using variant_type = mapbox::util::variant; - variant_type var = 100.0; - CHECK(var.get() == static_cast(100.0)); - CHECK(var.get() == static_cast(static_cast(100.0))); -} - -struct dummy -{ -}; - -TEST_CASE("implicit conversion to a suitable type", "[variant][implicit conversion]") -{ - using mapbox::util::variant; - CHECK_NOTHROW((variant(123)).get()); - CHECK_NOTHROW((variant("foo")).get()); -} - -TEST_CASE("value_traits for non-convertible type", "[variant::detail]") -{ - namespace detail = mapbox::util::detail; - using target_type = detail::value_traits::target_type; - CHECK((std::is_same::value) == true); -} - -TEST_CASE("Type indexing should work with variants with duplicated types", "[variant::detail]") -{ - // Index is in reverse order - REQUIRE((mapbox::util::detail::value_traits::index == 3)); - REQUIRE((mapbox::util::detail::value_traits::index == 3)); - REQUIRE((mapbox::util::detail::value_traits::index == 2)); - REQUIRE((mapbox::util::detail::value_traits::index == 2)); - REQUIRE((mapbox::util::detail::value_traits::index == 1)); - REQUIRE((mapbox::util::detail::value_traits::index == 3)); - REQUIRE((mapbox::util::detail::value_traits::index == 0)); - REQUIRE((mapbox::util::detail::value_traits::index == mapbox::util::detail::invalid_value)); - REQUIRE((mapbox::util::detail::value_traits, bool, int, double, std::string>::index == mapbox::util::detail::invalid_value)); -} - -TEST_CASE("variant default constructor", "[variant][default constructor]") -{ - // By default variant is initialised with (default constructed) first type in template parameters pack - // As a result first type in Types... must be default constructable - // NOTE: index in reverse order -> index = N - 1 - using variant_type = mapbox::util::variant; - REQUIRE(variant_type{}.which() == 0); - REQUIRE(variant_type{}.valid()); - REQUIRE_FALSE(variant_type{mapbox::util::no_init()}.valid()); -} - -TEST_CASE("variant printer", "[visitor][unary visitor][printer]") -{ - using variant_type = mapbox::util::variant; - std::vector var = {2.1, 123, "foo", 456}; - std::stringstream out; - std::copy(var.begin(), var.end(), std::ostream_iterator(out, ",")); - out << var[2]; - REQUIRE(out.str() == "2.1,123,foo,456,foo"); -} - -TEST_CASE("swapping variants should do the right thing", "[variant]") -{ - using variant_type = mapbox::util::variant; - variant_type a = 7; - variant_type b = 3; - variant_type c = 3.141; - variant_type d = "foo"; - variant_type e = "a long string that is longer than small string optimization"; - - using std::swap; - swap(a, b); - REQUIRE(a.get() == 3); - REQUIRE(a.which() == 0); - REQUIRE(b.get() == 7); - REQUIRE(b.which() == 0); - - swap(b, c); - REQUIRE(b.get() == Approx(3.141)); - REQUIRE(b.which() == 1); - REQUIRE(c.get() == 7); - REQUIRE(c.which() == 0); - - swap(b, d); - REQUIRE(b.get() == "foo"); - REQUIRE(b.which() == 2); - REQUIRE(d.get() == Approx(3.141)); - REQUIRE(d.which() == 1); - - swap(b, e); - REQUIRE(b.get() == "a long string that is longer than small string optimization"); - REQUIRE(b.which() == 2); - REQUIRE(e.get() == "foo"); - REQUIRE(e.which() == 2); -} - -TEST_CASE("variant should work with equality operators") -{ - using variant_type = mapbox::util::variant; - - variant_type a{1}; - variant_type b{1}; - variant_type c{2}; - variant_type s{"foo"}; - - REQUIRE(a == a); - REQUIRE(a == b); - REQUIRE_FALSE(a == c); - REQUIRE_FALSE(a == s); - REQUIRE_FALSE(c == s); - - REQUIRE_FALSE(a != a); - REQUIRE_FALSE(a != b); - REQUIRE(a != c); - REQUIRE(a != s); - REQUIRE(c != s); -} - -TEST_CASE("variant should work with comparison operators") -{ - using variant_type = mapbox::util::variant; - - variant_type a{1}; - variant_type b{1}; - variant_type c{2}; - variant_type s{"foo"}; - variant_type t{"bar"}; - - REQUIRE_FALSE(a < a); - REQUIRE_FALSE(a < b); - REQUIRE(a < c); - REQUIRE(a < s); - REQUIRE(c < s); - REQUIRE(t < s); - - REQUIRE_FALSE(a > a); - REQUIRE_FALSE(a > b); - REQUIRE_FALSE(a > c); - REQUIRE_FALSE(a > s); - REQUIRE_FALSE(c > s); - REQUIRE_FALSE(t > s); - - REQUIRE(a <= a); - REQUIRE(a <= b); - REQUIRE(a <= c); - REQUIRE(a <= s); - REQUIRE(c <= s); - REQUIRE(t <= s); - - REQUIRE(a >= a); - REQUIRE(a >= b); - REQUIRE_FALSE(a >= c); - REQUIRE_FALSE(a >= s); - REQUIRE_FALSE(c >= s); - REQUIRE_FALSE(t >= s); -} - -TEST_CASE("storing reference wrappers works") -{ - using variant_type = mapbox::util::variant, std::reference_wrapper>; - - int a = 1; - variant_type v{std::ref(a)}; - REQUIRE(v.get() == 1); - REQUIRE(mapbox::util::get(v) == 1); - REQUIRE_THROWS_AS(v.get(), - mapbox::util::bad_variant_access&); - REQUIRE_THROWS_AS(mapbox::util::get(v), - mapbox::util::bad_variant_access&); - a = 2; - REQUIRE(v.get() == 2); - v.get() = 3; - REQUIRE(a == 3); - - double b = 3.141; - v = std::ref(b); - REQUIRE(v.get() == Approx(3.141)); - REQUIRE(mapbox::util::get(v) == Approx(3.141)); - REQUIRE_THROWS_AS(v.get(), - mapbox::util::bad_variant_access&); - REQUIRE_THROWS_AS(mapbox::util::get(v), - mapbox::util::bad_variant_access&); - b = 2.718; - REQUIRE(v.get() == Approx(2.718)); - a = 3; - REQUIRE(v.get() == Approx(2.718)); - v.get() = 4.1; - REQUIRE(b == Approx(4.1)); - - REQUIRE_THROWS_AS(v.get() = 4, - mapbox::util::bad_variant_access&); -} - -TEST_CASE("storing reference wrappers to consts works") -{ - using variant_type = mapbox::util::variant, std::reference_wrapper>; - - int a = 1; - variant_type v{std::cref(a)}; - REQUIRE(v.get() == 1); - REQUIRE(v.get() == 1); - REQUIRE(mapbox::util::get(v) == 1); - REQUIRE(mapbox::util::get(v) == 1); - REQUIRE_THROWS_AS(v.get(), - mapbox::util::bad_variant_access&); - REQUIRE_THROWS_AS(mapbox::util::get(v), - mapbox::util::bad_variant_access&); - - double b = 3.141; - v = std::cref(b); - REQUIRE(v.get() == Approx(3.141)); - REQUIRE(mapbox::util::get(v) == Approx(3.141)); - REQUIRE_THROWS_AS(v.get(), - mapbox::util::bad_variant_access&); - REQUIRE_THROWS_AS(mapbox::util::get(v), - mapbox::util::bad_variant_access&); -} - -TEST_CASE("recursive wrapper") -{ - using variant_type = mapbox::util::variant>; - variant_type v(1); - REQUIRE(v.is()); - REQUIRE(v.get() == 1); -} - - -TEST_CASE("variant : direct_type helper should match T, references (T&) and const references (T const&) to the original type T)") -{ - using value = mapbox::util::variant; - - std::uint64_t u(1234); - REQUIRE(value(u).is()); // matches T - - std::uint64_t& ur(u); - REQUIRE(value(ur).is()); // matches T& - - std::uint64_t const& ucr(u); - REQUIRE(value(ucr).is()); // matches T const& -} diff --git a/third_party/variant/test/t/variant_alternative.cpp b/third_party/variant/test/t/variant_alternative.cpp deleted file mode 100644 index eedfe5cfc..000000000 --- a/third_party/variant/test/t/variant_alternative.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "catch.hpp" - -#include -#include - -#include - -TEST_CASE("variant_alternative", "[types]") -{ - using variant_type = mapbox::util::variant; - using type_0 = mapbox::util::variant_alternative<0, variant_type>::type; - using type_1 = mapbox::util::variant_alternative<1, variant_type>::type; - using type_2 = mapbox::util::variant_alternative<2, variant_type>::type; - //using type_3 = mapbox::util::variant_alternative<3, variant_type>::type; // compile error - constexpr bool check_0 = std::is_same::value; - constexpr bool check_1 = std::is_same::value; - constexpr bool check_2 = std::is_same::value; - CHECK(check_0); - CHECK(check_1); - CHECK(check_2); -} - -TEST_CASE("variant_size", "[types]") -{ - constexpr auto value_0 = mapbox::util::variant_size>::value; - constexpr auto value_1 = mapbox::util::variant_size>::value; - constexpr auto value_2 = mapbox::util::variant_size>::value; - CHECK(value_0 == 0); - CHECK(value_1 == 1); - CHECK(value_2 == 2); -} diff --git a/third_party/variant/test/t/visitor_result_type.cpp b/third_party/variant/test/t/visitor_result_type.cpp deleted file mode 100644 index f673ae4cb..000000000 --- a/third_party/variant/test/t/visitor_result_type.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include - -using namespace mapbox::util; - -namespace { - -template -struct tag {}; - -struct deduced_result_visitor -{ - template - tag operator() (T); - - template - tag operator() (T) const; - - template - tag operator() (T, U); - - template - tag operator() (T, U) const; -}; - -struct explicit_result_visitor : deduced_result_visitor -{ - using result_type = tag; -}; - -// Doing this compile-time test via assignment to typed tag objects gives -// more useful error messages when something goes wrong, than std::is_same -// in a static_assert would. Here if result_of_unary_visit returns anything -// other than the expected type on the left hand side, the conversion error -// message will tell you exactly what it was. - -#ifdef __clang__ -# pragma clang diagnostic ignored "-Wunused-variable" -#endif - -tag d1m = detail::result_of_unary_visit{}; -tag d1c = detail::result_of_unary_visit{}; - -tag e1m = detail::result_of_unary_visit{}; -tag e1c = detail::result_of_unary_visit{}; - -tag d2m = detail::result_of_binary_visit{}; -tag d2c = detail::result_of_binary_visit{}; - -tag e2m = detail::result_of_binary_visit{}; -tag e2c = detail::result_of_binary_visit{}; - -} // namespace diff --git a/third_party/variant/test/unique_ptr_test.cpp b/third_party/variant/test/unique_ptr_test.cpp deleted file mode 100644 index f07590003..000000000 --- a/third_party/variant/test/unique_ptr_test.cpp +++ /dev/null @@ -1,126 +0,0 @@ - -#include -#include -#include -#include -#include -#include - -#include "auto_cpu_timer.hpp" - -#include - -using namespace mapbox; - -namespace test { - -struct add; -struct sub; - -template -struct binary_op; - -typedef util::variant>, - std::unique_ptr>> - expression; - -template -struct binary_op -{ - expression left; // variant instantiated here... - expression right; - - binary_op(expression&& lhs, expression&& rhs) - : left(std::move(lhs)), right(std::move(rhs)) - { - } -}; - -struct print -{ - template - void operator()(T const& val) const - { - std::cerr << val << ":" << typeid(T).name() << std::endl; - } -}; - -struct test -{ - template - std::string operator()(T const& obj) const - { - return std::string("TYPE_ID=") + typeid(obj).name(); - } -}; - -struct calculator -{ - public: - int operator()(int value) const - { - return value; - } - - int operator()(std::unique_ptr> const& binary) const - { - return util::apply_visitor(calculator(), binary->left) + util::apply_visitor(calculator(), binary->right); - } - - int operator()(std::unique_ptr> const& binary) const - { - return util::apply_visitor(calculator(), binary->left) - util::apply_visitor(calculator(), binary->right); - } -}; - -struct to_string -{ - public: - std::string operator()(int value) const - { - return std::to_string(value); - } - - std::string operator()(std::unique_ptr> const& binary) const - { - return util::apply_visitor(to_string(), binary->left) + std::string("+") + util::apply_visitor(to_string(), binary->right); - } - - std::string operator()(std::unique_ptr> const& binary) const - { - return util::apply_visitor(to_string(), binary->left) + std::string("-") + util::apply_visitor(to_string(), binary->right); - } -}; - -} // namespace test - -int main(int argc, char** argv) -{ - if (argc != 2) - { - std::cerr << "Usage" << argv[0] << " " << std::endl; - return EXIT_FAILURE; - } - - const std::size_t NUM_ITER = static_cast(std::stol(argv[1])); - - test::expression sum(std::unique_ptr>(new test::binary_op(2, 3))); - test::expression result(std::unique_ptr>(new test::binary_op(std::move(sum), 4))); - - std::cerr << "TYPE OF RESULT-> " << util::apply_visitor(test::test(), result) << std::endl; - - int total = 0; - { - auto_cpu_timer t; - for (std::size_t i = 0; i < NUM_ITER; ++i) - { - total += util::apply_visitor(test::calculator(), result); - } - } - std::cerr << "total=" << total << std::endl; - - std::cerr << util::apply_visitor(test::to_string(), result) << "=" << util::apply_visitor(test::calculator(), result) << std::endl; - - return EXIT_SUCCESS; -} diff --git a/third_party/variant/test/unit.cpp b/third_party/variant/test/unit.cpp deleted file mode 100644 index 0c7c351f4..000000000 --- a/third_party/variant/test/unit.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#define CATCH_CONFIG_MAIN -#include "catch.hpp" diff --git a/third_party/variant/variant.gyp b/third_party/variant/variant.gyp deleted file mode 100644 index 7d7dca8df..000000000 --- a/third_party/variant/variant.gyp +++ /dev/null @@ -1,35 +0,0 @@ -{ - "includes": [ - "common.gypi" - ], - "targets": [ - { - "target_name": "tests", - "type": "executable", - "sources": [ - "test/unit.cpp", - "test/t/binary_visitor_1.cpp", - "test/t/binary_visitor_2.cpp", - "test/t/binary_visitor_3.cpp", - "test/t/binary_visitor_4.cpp", - "test/t/binary_visitor_5.cpp", - "test/t/binary_visitor_6.cpp", - "test/t/issue21.cpp", - "test/t/mutating_visitor.cpp", - "test/t/optional.cpp", - "test/t/recursive_wrapper.cpp", - "test/t/sizeof.cpp", - "test/t/unary_visitor.cpp", - "test/t/variant.cpp" - ], - "xcode_settings": { - "SDKROOT": "macosx", - "SUPPORTED_PLATFORMS":["macosx"] - }, - "include_dirs": [ - "./include", - "test/include" - ] - } - ] -} diff --git a/unit_tests/library/json.cpp b/unit_tests/library/json.cpp index 1b0b36a86..83d585497 100644 --- a/unit_tests/library/json.cpp +++ b/unit_tests/library/json.cpp @@ -20,19 +20,19 @@ BOOST_AUTO_TEST_CASE(test_json_linestring) auto geom = engine::api::json::makeGeoJSONGeometry(begin(locations), end(locations)); - const auto type = geom.values["type"].get().value; + const auto type = std::get(geom.values["type"]).value; BOOST_CHECK_EQUAL(type, "LineString"); - const auto coords = geom.values["coordinates"].get().values; + const auto coords = std::get(geom.values["coordinates"]).values; BOOST_CHECK_EQUAL(coords.size(), 3); // array of three location arrays for (const auto &each : coords) { - const auto loc = each.get().values; + const auto loc = std::get(each).values; BOOST_CHECK_EQUAL(loc.size(), 2); - const auto lon = loc[0].get().value; - const auto lat = loc[1].get().value; + const auto lon = std::get(loc[0]).value; + const auto lat = std::get(loc[1]).value; (void)lon; (void)lat; @@ -46,19 +46,19 @@ BOOST_AUTO_TEST_CASE(test_json_single_point) auto geom = engine::api::json::makeGeoJSONGeometry(begin(locations), end(locations)); - const auto type = geom.values["type"].get().value; + const auto type = std::get(geom.values["type"]).value; BOOST_CHECK_EQUAL(type, "LineString"); - const auto coords = geom.values["coordinates"].get().values; + const auto coords = std::get(geom.values["coordinates"]).values; BOOST_CHECK_EQUAL(coords.size(), 2); // array of two location arrays for (const auto &each : coords) { - const auto loc = each.get().values; + const auto loc = std::get(each).values; BOOST_CHECK_EQUAL(loc.size(), 2); - const auto lon = loc[0].get().value; - const auto lat = loc[1].get().value; + const auto lon = std::get(loc[0]).value; + const auto lat = std::get(loc[1]).value; (void)lon; (void)lat; diff --git a/unit_tests/library/limits.cpp b/unit_tests/library/limits.cpp index 8db7ff24b..cedbef4cb 100644 --- a/unit_tests/library/limits.cpp +++ b/unit_tests/library/limits.cpp @@ -45,8 +45,8 @@ BOOST_AUTO_TEST_CASE(test_trip_limits) BOOST_CHECK(rc == Status::Error); // Make sure we're not accidentally hitting a guard code path before - auto &json_result = result.get(); - const auto code = json_result.values["code"].get().value; + auto &json_result = std::get(result); + const auto code = std::get(json_result.values["code"]).value; BOOST_CHECK(code == "TooBig"); // per the New-Server API spec } @@ -73,8 +73,8 @@ BOOST_AUTO_TEST_CASE(test_route_limits) BOOST_CHECK(rc == Status::Error); // Make sure we're not accidentally hitting a guard code path before - auto &json_result = result.get(); - const auto code = json_result.values["code"].get().value; + auto &json_result = std::get(result); + const auto code = std::get(json_result.values["code"]).value; BOOST_CHECK(code == "TooBig"); // per the New-Server API spec } @@ -101,8 +101,8 @@ BOOST_AUTO_TEST_CASE(test_table_limits) BOOST_CHECK(rc == Status::Error); // Make sure we're not accidentally hitting a guard code path before - auto &json_result = result.get(); - const auto code = json_result.values["code"].get().value; + auto &json_result = std::get(result); + const auto code = std::get(json_result.values["code"]).value; BOOST_CHECK(code == "TooBig"); // per the New-Server API spec } @@ -129,8 +129,8 @@ BOOST_AUTO_TEST_CASE(test_match_coordinate_limits) BOOST_CHECK(rc == Status::Error); // Make sure we're not accidentally hitting a guard code path before - auto &json_result = result.get(); - const auto code = json_result.values["code"].get().value; + auto &json_result = std::get(result); + const auto code = std::get(json_result.values["code"]).value; BOOST_CHECK(code == "TooBig"); // per the New-Server API spec } @@ -162,8 +162,8 @@ BOOST_AUTO_TEST_CASE(test_match_radiuses_limits) BOOST_CHECK(rc == Status::Error); // Make sure we're not accidentally hitting a guard code path before - auto &json_result = result.get(); - const auto code = json_result.values["code"].get().value; + auto &json_result = std::get(result); + const auto code = std::get(json_result.values["code"]).value; BOOST_CHECK(code == "TooBig"); // per the New-Server API spec } @@ -189,8 +189,8 @@ BOOST_AUTO_TEST_CASE(test_nearest_limits) BOOST_CHECK(rc == Status::Error); // Make sure we're not accidentally hitting a guard code path before - auto &json_result = result.get(); - const auto code = json_result.values["code"].get().value; + auto &json_result = std::get(result); + const auto code = std::get(json_result.values["code"]).value; BOOST_CHECK(code == "TooBig"); // per the New-Server API spec } diff --git a/unit_tests/library/match.cpp b/unit_tests/library/match.cpp index 741e8e4fd..73dfb03ea 100644 --- a/unit_tests/library/match.cpp +++ b/unit_tests/library/match.cpp @@ -1,4 +1,5 @@ #include +#include #include "coordinates.hpp" #include "fixture.hpp" @@ -24,7 +25,7 @@ osrm::Status run_match_json(const osrm::OSRM &osrm, } engine::api::ResultT result = json::Object(); auto rc = osrm.Match(params, result); - json_result = result.get(); + json_result = std::get(result); return rc; } @@ -45,35 +46,34 @@ void test_match(bool use_json_only_api) const auto rc = run_match_json(osrm, params, json_result, use_json_only_api); BOOST_CHECK(rc == Status::Ok || rc == Status::Error); - const auto code = json_result.values.at("code").get().value; + const auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "Ok"); - const auto &tracepoints = json_result.values.at("tracepoints").get().values; + const auto &tracepoints = std::get(json_result.values.at("tracepoints")).values; BOOST_CHECK_EQUAL(tracepoints.size(), params.coordinates.size()); - const auto &matchings = json_result.values.at("matchings").get().values; + const auto &matchings = std::get(json_result.values.at("matchings")).values; const auto &number_of_matchings = matchings.size(); for (const auto &waypoint : tracepoints) { - if (waypoint.is>()) + if (std::holds_alternative(waypoint)) { BOOST_CHECK(waypoint_check(waypoint)); - const auto &waypoint_object = waypoint.get(); + const auto &waypoint_object = std::get(waypoint); const auto matchings_index = - waypoint_object.values.at("matchings_index").get().value; + std::get(waypoint_object.values.at("matchings_index")).value; const auto waypoint_index = - waypoint_object.values.at("waypoint_index").get().value; - const auto &route_legs = matchings[matchings_index] - .get() - .values.at("legs") - .get() - .values; + std::get(waypoint_object.values.at("waypoint_index")).value; + const auto &route_legs = + std::get( + std::get(matchings[matchings_index]).values.at("legs")) + .values; BOOST_CHECK_LT(waypoint_index, route_legs.size() + 1); BOOST_CHECK_LT(matchings_index, number_of_matchings); } else { - BOOST_CHECK(waypoint.is()); + BOOST_CHECK(std::holds_alternative(waypoint)); } } } @@ -96,7 +96,7 @@ void test_match_skip_waypoints(bool use_json_only_api) const auto rc = run_match_json(osrm, params, json_result, use_json_only_api); BOOST_CHECK(rc == Status::Ok || rc == Status::Error); - const auto code = json_result.values.at("code").get().value; + const auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "Ok"); BOOST_CHECK(json_result.values.find("tracepoints") == json_result.values.end()); @@ -118,26 +118,26 @@ void test_match_split(bool use_json_only_api) const auto rc = run_match_json(osrm, params, json_result, use_json_only_api); BOOST_CHECK(rc == Status::Ok || rc == Status::Error); - const auto code = json_result.values.at("code").get().value; + const auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "Ok"); - const auto &tracepoints = json_result.values.at("tracepoints").get().values; + const auto &tracepoints = std::get(json_result.values.at("tracepoints")).values; BOOST_CHECK_EQUAL(tracepoints.size(), params.coordinates.size()); - const auto &matchings = json_result.values.at("matchings").get().values; + const auto &matchings = std::get(json_result.values.at("matchings")).values; const auto &number_of_matchings = matchings.size(); BOOST_CHECK_EQUAL(number_of_matchings, 2); std::size_t current_matchings_index = 0, expected_waypoint_index = 0; for (const auto &waypoint : tracepoints) { - if (waypoint.is>()) + if (std::holds_alternative(waypoint)) { BOOST_CHECK(waypoint_check(waypoint)); - const auto &waypoint_object = waypoint.get(); + const auto &waypoint_object = std::get(waypoint); const auto matchings_index = - waypoint_object.values.at("matchings_index").get().value; + std::get(waypoint_object.values.at("matchings_index")).value; const auto waypoint_index = - waypoint_object.values.at("waypoint_index").get().value; + std::get(waypoint_object.values.at("waypoint_index")).value; BOOST_CHECK_LT(matchings_index, number_of_matchings); @@ -150,7 +150,7 @@ void test_match_split(bool use_json_only_api) } else { - BOOST_CHECK(waypoint.is()); + BOOST_CHECK(std::holds_alternative(waypoint)); } } } @@ -173,7 +173,7 @@ BOOST_AUTO_TEST_CASE(test_match_fb_serialization) const auto rc = osrm.Match(params, result); BOOST_CHECK(rc == Status::Ok); - auto &fb_result = result.get(); + auto &fb_result = std::get(result); auto fb = engine::api::fbresult::GetFBResult(fb_result.GetBufferPointer()); BOOST_CHECK(!fb->error()); @@ -215,7 +215,7 @@ BOOST_AUTO_TEST_CASE(test_match_fb_serialization_skip_waypoints) const auto rc = osrm.Match(params, result); BOOST_CHECK(rc == Status::Ok); - auto &fb_result = result.get(); + auto &fb_result = std::get(result); auto fb = engine::api::fbresult::GetFBResult(fb_result.GetBufferPointer()); BOOST_CHECK(!fb->error()); diff --git a/unit_tests/library/nearest.cpp b/unit_tests/library/nearest.cpp index 249e19387..e3d12936a 100644 --- a/unit_tests/library/nearest.cpp +++ b/unit_tests/library/nearest.cpp @@ -22,7 +22,7 @@ osrm::Status run_nearest_json(const osrm::OSRM &osrm, } osrm::engine::api::ResultT result = osrm::json::Object(); auto rc = osrm.Nearest(params, result); - json_result = result.get(); + json_result = std::get(result); return rc; } @@ -41,16 +41,16 @@ void test_nearest_response(bool use_json_only_api) const auto rc = run_nearest_json(osrm, params, json_result, use_json_only_api); BOOST_REQUIRE(rc == Status::Ok); - const auto code = json_result.values.at("code").get().value; + const auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "Ok"); - const auto &waypoints = json_result.values.at("waypoints").get().values; + const auto &waypoints = std::get(json_result.values.at("waypoints")).values; BOOST_CHECK(!waypoints.empty()); // the dataset has at least one nearest coordinate for (const auto &waypoint : waypoints) { - const auto &waypoint_object = waypoint.get(); - const auto distance = waypoint_object.values.at("distance").get().value; + const auto &waypoint_object = std::get(waypoint); + const auto distance = std::get(waypoint_object.values.at("distance")).value; BOOST_CHECK(distance >= 0); } } @@ -71,7 +71,7 @@ void test_nearest_response_skip_waypoints(bool use_json_only_api) const auto rc = run_nearest_json(osrm, params, json_result, use_json_only_api); BOOST_REQUIRE(rc == Status::Ok); - const auto code = json_result.values.at("code").get().value; + const auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "Ok"); BOOST_CHECK(json_result.values.find("waypoints") == json_result.values.end()); @@ -97,7 +97,7 @@ void test_nearest_response_no_coordinates(bool use_json_only_api) const auto rc = run_nearest_json(osrm, params, json_result, use_json_only_api); BOOST_REQUIRE(rc == Status::Error); - const auto code = json_result.values.at("code").get().value; + const auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "InvalidOptions"); } BOOST_AUTO_TEST_CASE(test_nearest_response_no_coordinates_old_api) @@ -123,7 +123,7 @@ void test_nearest_response_multiple_coordinates(bool use_json_only_api) const auto rc = run_nearest_json(osrm, params, json_result, use_json_only_api); BOOST_REQUIRE(rc == Status::Error); - const auto code = json_result.values.at("code").get().value; + const auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "InvalidOptions"); } BOOST_AUTO_TEST_CASE(test_nearest_response_multiple_coordinates_old_api) @@ -151,25 +151,25 @@ void test_nearest_response_for_location_in_small_component(bool use_json_only_ap const auto rc = run_nearest_json(osrm, params, json_result, use_json_only_api); BOOST_REQUIRE(rc == Status::Ok); - const auto code = json_result.values.at("code").get().value; + const auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "Ok"); - const auto &waypoints = json_result.values.at("waypoints").get().values; + const auto &waypoints = std::get(json_result.values.at("waypoints")).values; BOOST_CHECK(!waypoints.empty()); for (const auto &waypoint : waypoints) { - const auto &waypoint_object = waypoint.get(); + const auto &waypoint_object = std::get(waypoint); // Everything within ~20m (actually more) is still in small component. // Nearest service should snap to road network without considering components. - const auto distance = waypoint_object.values.at("distance").get().value; + const auto distance = std::get(waypoint_object.values.at("distance")).value; BOOST_CHECK_LT(distance, 20); - const auto &nodes = waypoint_object.values.at("nodes").get().values; + const auto &nodes = std::get(waypoint_object.values.at("nodes")).values; BOOST_CHECK(nodes.size() == 2); - BOOST_CHECK(nodes[0].get().value != 0); - BOOST_CHECK(nodes[1].get().value != 0); + BOOST_CHECK(std::get(nodes[0]).value != 0); + BOOST_CHECK(std::get(nodes[1]).value != 0); } } BOOST_AUTO_TEST_CASE(test_nearest_response_for_location_in_small_component_old_api) @@ -194,7 +194,7 @@ BOOST_AUTO_TEST_CASE(test_nearest_fb_serialization) const auto rc = osrm.Nearest(params, result); BOOST_REQUIRE(rc == Status::Ok); - auto &fb_result = result.get(); + auto &fb_result = std::get(result); auto fb = engine::api::fbresult::GetFBResult(fb_result.GetBufferPointer()); BOOST_CHECK(!fb->error()); @@ -224,7 +224,7 @@ BOOST_AUTO_TEST_CASE(test_nearest_fb_serialization_skip_waypoints) const auto rc = osrm.Nearest(params, result); BOOST_REQUIRE(rc == Status::Ok); - auto &fb_result = result.get(); + auto &fb_result = std::get(result); auto fb = engine::api::fbresult::GetFBResult(fb_result.GetBufferPointer()); BOOST_CHECK(!fb->error()); @@ -243,7 +243,7 @@ BOOST_AUTO_TEST_CASE(test_nearest_fb_error) const auto rc = osrm.Nearest(params, result); BOOST_REQUIRE(rc == Status::Error); - auto &fb_result = result.get(); + auto &fb_result = std::get(result); auto fb = engine::api::fbresult::GetFBResult(fb_result.GetBufferPointer()); BOOST_CHECK(fb->error()); BOOST_CHECK_EQUAL(fb->code()->code()->str(), "InvalidOptions"); diff --git a/unit_tests/library/route.cpp b/unit_tests/library/route.cpp index 1e2f427a7..8773db6b7 100644 --- a/unit_tests/library/route.cpp +++ b/unit_tests/library/route.cpp @@ -26,7 +26,7 @@ osrm::Status run_route_json(const osrm::OSRM &osrm, } osrm::engine::api::ResultT result = osrm::json::Object(); auto rc = osrm.Route(params, result); - json_result = result.get(); + json_result = std::get(result); return rc; } @@ -48,14 +48,14 @@ void test_route_same_coordinates_fixture(bool use_json_only_api) BOOST_CHECK(rc == Status::Ok); // unset snapping dependent hint - for (auto &itr : json_result.values["waypoints"].get().values) + for (auto &itr : std::get(json_result.values["waypoints"]).values) { // Hint values aren't stable, so blank it out - itr.get().values["hint"] = ""; + std::get(itr).values["hint"] = ""; // Round value to 6 decimal places for double comparison later - itr.get().values["distance"] = std::round( - itr.get().values["distance"].get().value * 1000000); + std::get(itr).values["distance"] = std::round( + std::get(std::get(itr).values["distance"]).value * 1000000); } const auto location = json::Array{{{7.437070}, {43.749248}}}; @@ -154,127 +154,133 @@ void test_route_same_coordinates(bool use_json_only_api) const auto rc = run_route_json(osrm, params, json_result, use_json_only_api); BOOST_CHECK(rc == Status::Ok); - const auto code = json_result.values.at("code").get().value; + const auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "Ok"); - const auto &waypoints = json_result.values.at("waypoints").get().values; + const auto &waypoints = std::get(json_result.values.at("waypoints")).values; BOOST_CHECK(waypoints.size() == params.coordinates.size()); for (const auto &waypoint : waypoints) { - const auto &waypoint_object = waypoint.get(); + const auto &waypoint_object = std::get(waypoint); // nothing can be said about name, empty or contains name of the street - const auto name = waypoint_object.values.at("name").get().value; + const auto name = std::get(waypoint_object.values.at("name")).value; BOOST_CHECK(((void)name, true)); - const auto location = waypoint_object.values.at("location").get().values; - const auto longitude = location[0].get().value; - const auto latitude = location[1].get().value; + const auto location = std::get(waypoint_object.values.at("location")).values; + const auto longitude = std::get(location[0]).value; + const auto latitude = std::get(location[1]).value; BOOST_CHECK(longitude >= -180. && longitude <= 180.); BOOST_CHECK(latitude >= -90. && latitude <= 90.); - const auto hint = waypoint_object.values.at("hint").get().value; + const auto hint = std::get(waypoint_object.values.at("hint")).value; BOOST_CHECK(!hint.empty()); } - const auto &routes = json_result.values.at("routes").get().values; + const auto &routes = std::get(json_result.values.at("routes")).values; BOOST_REQUIRE_GT(routes.size(), 0); for (const auto &route : routes) { - const auto &route_object = route.get(); + const auto &route_object = std::get(route); - const auto distance = route_object.values.at("distance").get().value; + const auto distance = std::get(route_object.values.at("distance")).value; BOOST_CHECK_EQUAL(distance, 0); - const auto duration = route_object.values.at("duration").get().value; + const auto duration = std::get(route_object.values.at("duration")).value; BOOST_CHECK_EQUAL(duration, 0); // geometries=polyline by default - const auto geometry = route_object.values.at("geometry").get().value; + const auto geometry = std::get(route_object.values.at("geometry")).value; BOOST_CHECK(!geometry.empty()); - const auto &legs = route_object.values.at("legs").get().values; + const auto &legs = std::get(route_object.values.at("legs")).values; BOOST_CHECK(!legs.empty()); for (const auto &leg : legs) { - const auto &leg_object = leg.get(); + const auto &leg_object = std::get(leg); - const auto distance = leg_object.values.at("distance").get().value; + const auto distance = std::get(leg_object.values.at("distance")).value; BOOST_CHECK_EQUAL(distance, 0); - const auto duration = leg_object.values.at("duration").get().value; + const auto duration = std::get(leg_object.values.at("duration")).value; BOOST_CHECK_EQUAL(duration, 0); // nothing can be said about summary, empty or contains human readable summary - const auto summary = leg_object.values.at("summary").get().value; + const auto summary = std::get(leg_object.values.at("summary")).value; BOOST_CHECK(((void)summary, true)); - const auto &steps = leg_object.values.at("steps").get().values; + const auto &steps = std::get(leg_object.values.at("steps")).values; BOOST_CHECK(!steps.empty()); std::size_t step_count = 0; for (const auto &step : steps) { - const auto &step_object = step.get(); + const auto &step_object = std::get(step); - const auto distance = step_object.values.at("distance").get().value; + const auto distance = + std::get(step_object.values.at("distance")).value; BOOST_CHECK_EQUAL(distance, 0); - const auto duration = step_object.values.at("duration").get().value; + const auto duration = + std::get(step_object.values.at("duration")).value; BOOST_CHECK_EQUAL(duration, 0); // geometries=polyline by default - const auto geometry = step_object.values.at("geometry").get().value; + const auto geometry = + std::get(step_object.values.at("geometry")).value; BOOST_CHECK(!geometry.empty()); // nothing can be said about name, empty or contains way name - const auto name = step_object.values.at("name").get().value; + const auto name = std::get(step_object.values.at("name")).value; BOOST_CHECK(((void)name, true)); // nothing can be said about mode, contains mode of transportation - const auto mode = step_object.values.at("mode").get().value; + const auto mode = std::get(step_object.values.at("mode")).value; BOOST_CHECK(!name.empty()); - const auto &maneuver = step_object.values.at("maneuver").get().values; + const auto &maneuver = + std::get(step_object.values.at("maneuver")).values; - const auto type = maneuver.at("type").get().value; + const auto type = std::get(maneuver.at("type")).value; BOOST_CHECK(!type.empty()); const auto &intersections = - step_object.values.at("intersections").get().values; + std::get(step_object.values.at("intersections")).values; for (auto &intersection : intersections) { - const auto &intersection_object = intersection.get().values; + const auto &intersection_object = std::get(intersection).values; const auto location = - intersection_object.at("location").get().values; - const auto longitude = location[0].get().value; - const auto latitude = location[1].get().value; + std::get(intersection_object.at("location")).values; + const auto longitude = std::get(location[0]).value; + const auto latitude = std::get(location[1]).value; BOOST_CHECK(longitude >= -180. && longitude <= 180.); BOOST_CHECK(latitude >= -90. && latitude <= 90.); const auto &bearings = - intersection_object.at("bearings").get().values; + std::get(intersection_object.at("bearings")).values; BOOST_CHECK(!bearings.empty()); - const auto &entries = intersection_object.at("entry").get().values; + const auto &entries = + std::get(intersection_object.at("entry")).values; BOOST_CHECK(bearings.size() == entries.size()); for (const auto &bearing : bearings) - BOOST_CHECK(0. <= bearing.get().value && - bearing.get().value <= 360.); + BOOST_CHECK(0. <= std::get(bearing).value && + std::get(bearing).value <= 360.); if (step_count > 0) { - const auto in = intersection_object.at("in").get().value; + const auto in = std::get(intersection_object.at("in")).value; BOOST_CHECK(in < bearings.size()); } if (step_count + 1 < steps.size()) { - const auto out = intersection_object.at("out").get().value; + const auto out = + std::get(intersection_object.at("out")).value; BOOST_CHECK(out < bearings.size()); } } @@ -309,29 +315,29 @@ void test_route_same_coordinates_no_waypoints(bool use_json_only_api) const auto rc = run_route_json(osrm, params, json_result, use_json_only_api); BOOST_CHECK(rc == Status::Ok); - const auto code = json_result.values.at("code").get().value; + const auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "Ok"); BOOST_CHECK(json_result.values.find("waypoints") == json_result.values.end()); - const auto &routes = json_result.values.at("routes").get().values; + const auto &routes = std::get(json_result.values.at("routes")).values; BOOST_REQUIRE_GT(routes.size(), 0); for (const auto &route : routes) { - const auto &route_object = route.get(); + const auto &route_object = std::get(route); - const auto distance = route_object.values.at("distance").get().value; + const auto distance = std::get(route_object.values.at("distance")).value; BOOST_CHECK_EQUAL(distance, 0); - const auto duration = route_object.values.at("duration").get().value; + const auto duration = std::get(route_object.values.at("duration")).value; BOOST_CHECK_EQUAL(duration, 0); // geometries=polyline by default - const auto geometry = route_object.values.at("geometry").get().value; + const auto geometry = std::get(route_object.values.at("geometry")).value; BOOST_CHECK(!geometry.empty()); - const auto &legs = route_object.values.at("legs").get().values; + const auto &legs = std::get(route_object.values.at("legs")).values; BOOST_CHECK(!legs.empty()); // The rest of legs contents is verified by test_route_same_coordinates @@ -363,19 +369,19 @@ void test_route_response_for_locations_in_small_component(bool use_json_only_api const auto rc = run_route_json(osrm, params, json_result, use_json_only_api); BOOST_CHECK(rc == Status::Ok); - const auto code = json_result.values.at("code").get().value; + const auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "Ok"); - const auto &waypoints = json_result.values.at("waypoints").get().values; + const auto &waypoints = std::get(json_result.values.at("waypoints")).values; BOOST_CHECK_EQUAL(waypoints.size(), params.coordinates.size()); for (const auto &waypoint : waypoints) { - const auto &waypoint_object = waypoint.get(); + const auto &waypoint_object = std::get(waypoint); - const auto location = waypoint_object.values.at("location").get().values; - const auto longitude = location[0].get().value; - const auto latitude = location[1].get().value; + const auto location = std::get(waypoint_object.values.at("location")).values; + const auto longitude = std::get(location[0]).value; + const auto latitude = std::get(location[1]).value; BOOST_CHECK(longitude >= -180. && longitude <= 180.); BOOST_CHECK(latitude >= -90. && latitude <= 90.); } @@ -406,19 +412,19 @@ void test_route_response_for_locations_in_big_component(bool use_json_only_api) const auto rc = run_route_json(osrm, params, json_result, use_json_only_api); BOOST_CHECK(rc == Status::Ok); - const auto code = json_result.values.at("code").get().value; + const auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "Ok"); - const auto &waypoints = json_result.values.at("waypoints").get().values; + const auto &waypoints = std::get(json_result.values.at("waypoints")).values; BOOST_CHECK_EQUAL(waypoints.size(), params.coordinates.size()); for (const auto &waypoint : waypoints) { - const auto &waypoint_object = waypoint.get(); + const auto &waypoint_object = std::get(waypoint); - const auto location = waypoint_object.values.at("location").get().values; - const auto longitude = location[0].get().value; - const auto latitude = location[1].get().value; + const auto location = std::get(waypoint_object.values.at("location")).values; + const auto longitude = std::get(location[0]).value; + const auto latitude = std::get(location[1]).value; BOOST_CHECK(longitude >= -180. && longitude <= 180.); BOOST_CHECK(latitude >= -90. && latitude <= 90.); } @@ -451,19 +457,19 @@ void test_route_response_for_locations_across_components(bool use_json_only_api) const auto rc = run_route_json(osrm, params, json_result, use_json_only_api); BOOST_CHECK(rc == Status::Ok); - const auto code = json_result.values.at("code").get().value; + const auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "Ok"); - const auto &waypoints = json_result.values.at("waypoints").get().values; + const auto &waypoints = std::get(json_result.values.at("waypoints")).values; BOOST_CHECK_EQUAL(waypoints.size(), params.coordinates.size()); for (const auto &waypoint : waypoints) { - const auto &waypoint_object = waypoint.get(); + const auto &waypoint_object = std::get(waypoint); - const auto location = waypoint_object.values.at("location").get().values; - const auto longitude = location[0].get().value; - const auto latitude = location[1].get().value; + const auto location = std::get(waypoint_object.values.at("location")).values; + const auto longitude = std::get(location[0]).value; + const auto latitude = std::get(location[1]).value; BOOST_CHECK(longitude >= -180. && longitude <= 180.); BOOST_CHECK(latitude >= -90. && latitude <= 90.); } @@ -493,8 +499,8 @@ void test_route_user_disables_generating_hints(bool use_json_only_api) const auto rc = run_route_json(osrm, params, json_result, use_json_only_api); BOOST_CHECK(rc == Status::Ok); - for (auto waypoint : json_result.values["waypoints"].get().values) - BOOST_CHECK_EQUAL(waypoint.get().values.count("hint"), 0); + for (auto waypoint : std::get(json_result.values["waypoints"]).values) + BOOST_CHECK_EQUAL(std::get(waypoint).values.count("hint"), 0); } BOOST_AUTO_TEST_CASE(test_route_user_disables_generating_hints_old_api) { @@ -522,21 +528,22 @@ void speed_annotation_matches_duration_and_distance(bool use_json_only_api) const auto rc = run_route_json(osrm, params, json_result, use_json_only_api); BOOST_CHECK(rc == Status::Ok); - const auto &routes = json_result.values["routes"].get().values; - const auto &legs = routes[0].get().values.at("legs").get().values; + const auto &routes = std::get(json_result.values["routes"]).values; + const auto &legs = + std::get(std::get(routes[0]).values.at("legs")).values; const auto &annotation = - legs[0].get().values.at("annotation").get(); - const auto &speeds = annotation.values.at("speed").get().values; - const auto &durations = annotation.values.at("duration").get().values; - const auto &distances = annotation.values.at("distance").get().values; + std::get(std::get(legs[0]).values.at("annotation")); + const auto &speeds = std::get(annotation.values.at("speed")).values; + const auto &durations = std::get(annotation.values.at("duration")).values; + const auto &distances = std::get(annotation.values.at("distance")).values; int length = speeds.size(); BOOST_CHECK_EQUAL(length, 1); for (int i = 0; i < length; i++) { - auto speed = speeds[i].get().value; - auto duration = durations[i].get().value; - auto distance = distances[i].get().value; + auto speed = std::get(speeds[i]).value; + auto duration = std::get(durations[i]).value; + auto distance = std::get(distances[i]).value; auto calc = std::round(distance / duration * 10.) / 10.; BOOST_CHECK_EQUAL(speed, std::isnan(calc) ? 0 : calc); @@ -570,20 +577,19 @@ void test_manual_setting_of_annotations_property(bool use_json_only_api) const auto rc = run_route_json(osrm, params, json_result, use_json_only_api); BOOST_CHECK(rc == Status::Ok); - const auto code = json_result.values.at("code").get().value; + const auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "Ok"); - auto annotations = json_result.values["routes"] - .get() - .values[0] - .get() - .values["legs"] - .get() - .values[0] - .get() - .values["annotation"] - .get() - .values; + auto annotations = + std::get( + std::get( + std::get( + std::get( + std::get(json_result.values["routes"]).values[0]) + .values["legs"]) + .values[0]) + .values["annotation"]) + .values; BOOST_CHECK_EQUAL(annotations.size(), 7); } BOOST_AUTO_TEST_CASE(test_manual_setting_of_annotations_property_old_api) @@ -611,7 +617,7 @@ BOOST_AUTO_TEST_CASE(test_route_serialize_fb) const auto rc = osrm.Route(params, result); BOOST_CHECK(rc == Status::Ok); - auto &fb_result = result.get(); + auto &fb_result = std::get(result); auto fb = engine::api::fbresult::GetFBResult(fb_result.GetBufferPointer()); BOOST_CHECK(!fb->error()); @@ -710,7 +716,7 @@ BOOST_AUTO_TEST_CASE(test_route_serialize_fb_skip_waypoints) const auto rc = osrm.Route(params, result); BOOST_CHECK(rc == Status::Ok); - auto &fb_result = result.get(); + auto &fb_result = std::get(result); auto fb = engine::api::fbresult::GetFBResult(fb_result.GetBufferPointer()); BOOST_CHECK(!fb->error()); diff --git a/unit_tests/library/table.cpp b/unit_tests/library/table.cpp index 0fa27d9a1..e1caafdae 100644 --- a/unit_tests/library/table.cpp +++ b/unit_tests/library/table.cpp @@ -22,7 +22,7 @@ osrm::Status run_table_json(const osrm::OSRM &osrm, } osrm::engine::api::ResultT result = osrm::json::Object(); auto rc = osrm.Table(params, result); - json_result = result.get(); + json_result = std::get(result); return rc; } @@ -46,41 +46,41 @@ void test_table_three_coords_one_source_one_dest_matrix(bool use_json_only_api) const auto rc = run_table_json(osrm, params, json_result, use_json_only_api); BOOST_CHECK(rc == Status::Ok || rc == Status::Error); - const auto code = json_result.values.at("code").get().value; + const auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "Ok"); // check that returned durations error is expected size and proportions // this test expects a 1x1 matrix - const auto &durations_array = json_result.values.at("durations").get().values; + const auto &durations_array = std::get(json_result.values.at("durations")).values; BOOST_CHECK_EQUAL(durations_array.size(), params.sources.size()); for (unsigned int i = 0; i < durations_array.size(); i++) { - const auto durations_matrix = durations_array[i].get().values; + const auto durations_matrix = std::get(durations_array[i]).values; BOOST_CHECK_EQUAL(durations_matrix.size(), params.sources.size() * params.destinations.size()); } // check that returned distances error is expected size and proportions // this test expects a 1x1 matrix - const auto &distances_array = json_result.values.at("distances").get().values; + const auto &distances_array = std::get(json_result.values.at("distances")).values; BOOST_CHECK_EQUAL(distances_array.size(), params.sources.size()); for (unsigned int i = 0; i < distances_array.size(); i++) { - const auto distances_matrix = distances_array[i].get().values; + const auto distances_matrix = std::get(distances_array[i]).values; BOOST_CHECK_EQUAL(distances_matrix.size(), params.sources.size() * params.destinations.size()); } // check destinations array of waypoint objects const auto &destinations_array = - json_result.values.at("destinations").get().values; + std::get(json_result.values.at("destinations")).values; BOOST_CHECK_EQUAL(destinations_array.size(), params.destinations.size()); for (const auto &destination : destinations_array) { BOOST_CHECK(waypoint_check(destination)); } // check sources array of waypoint objects - const auto &sources_array = json_result.values.at("sources").get().values; + const auto &sources_array = std::get(json_result.values.at("sources")).values; BOOST_CHECK_EQUAL(sources_array.size(), params.sources.size()); for (const auto &source : sources_array) { @@ -115,27 +115,27 @@ void test_table_three_coords_one_source_one_dest_matrix_no_waypoints(bool use_js const auto rc = run_table_json(osrm, params, json_result, use_json_only_api); BOOST_CHECK(rc == Status::Ok || rc == Status::Error); - const auto code = json_result.values.at("code").get().value; + const auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "Ok"); // check that returned durations error is expected size and proportions // this test expects a 1x1 matrix - const auto &durations_array = json_result.values.at("durations").get().values; + const auto &durations_array = std::get(json_result.values.at("durations")).values; BOOST_CHECK_EQUAL(durations_array.size(), params.sources.size()); for (unsigned int i = 0; i < durations_array.size(); i++) { - const auto durations_matrix = durations_array[i].get().values; + const auto durations_matrix = std::get(durations_array[i]).values; BOOST_CHECK_EQUAL(durations_matrix.size(), params.sources.size() * params.destinations.size()); } // check that returned distances error is expected size and proportions // this test expects a 1x1 matrix - const auto &distances_array = json_result.values.at("distances").get().values; + const auto &distances_array = std::get(json_result.values.at("distances")).values; BOOST_CHECK_EQUAL(distances_array.size(), params.sources.size()); for (unsigned int i = 0; i < distances_array.size(); i++) { - const auto distances_matrix = distances_array[i].get().values; + const auto distances_matrix = std::get(distances_array[i]).values; BOOST_CHECK_EQUAL(distances_matrix.size(), params.sources.size() * params.destinations.size()); } @@ -170,30 +170,30 @@ void test_table_three_coords_one_source_matrix(bool use_json_only_api) const auto rc = run_table_json(osrm, params, json_result, use_json_only_api); BOOST_CHECK(rc == Status::Ok || rc == Status::Error); - const auto code = json_result.values.at("code").get().value; + const auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "Ok"); // check that returned durations error is expected size and proportions // this test expects a 1x3 matrix - const auto &durations_array = json_result.values.at("durations").get().values; + const auto &durations_array = std::get(json_result.values.at("durations")).values; BOOST_CHECK_EQUAL(durations_array.size(), params.sources.size()); for (unsigned int i = 0; i < durations_array.size(); i++) { - const auto durations_matrix = durations_array[i].get().values; - BOOST_CHECK_EQUAL(durations_matrix[i].get().value, 0); + const auto durations_matrix = std::get(durations_array[i]).values; + BOOST_CHECK_EQUAL(std::get(durations_matrix[i]).value, 0); BOOST_CHECK_EQUAL(durations_matrix.size(), params.sources.size() * params.coordinates.size()); } // check destinations array of waypoint objects const auto &destinations_array = - json_result.values.at("destinations").get().values; + std::get(json_result.values.at("destinations")).values; BOOST_CHECK_EQUAL(destinations_array.size(), params.coordinates.size()); for (const auto &destination : destinations_array) { BOOST_CHECK(waypoint_check(destination)); } // check sources array of waypoint objects - const auto &sources_array = json_result.values.at("sources").get().values; + const auto &sources_array = std::get(json_result.values.at("sources")).values; BOOST_CHECK_EQUAL(sources_array.size(), params.sources.size()); for (const auto &source : sources_array) { @@ -225,26 +225,26 @@ void test_table_three_coordinates_matrix(bool use_json_only_api) const auto rc = run_table_json(osrm, params, json_result, use_json_only_api); BOOST_CHECK(rc == Status::Ok || rc == Status::Error); - const auto code = json_result.values.at("code").get().value; + const auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "Ok"); // check that returned durations error is expected size and proportions // this test expects a 3x3 matrix - const auto &durations_array = json_result.values.at("durations").get().values; + const auto &durations_array = std::get(json_result.values.at("durations")).values; BOOST_CHECK_EQUAL(durations_array.size(), params.coordinates.size()); for (unsigned int i = 0; i < durations_array.size(); i++) { - const auto durations_matrix = durations_array[i].get().values; - BOOST_CHECK_EQUAL(durations_matrix[i].get().value, 0); + const auto durations_matrix = std::get(durations_array[i]).values; + BOOST_CHECK_EQUAL(std::get(durations_matrix[i]).value, 0); BOOST_CHECK_EQUAL(durations_matrix.size(), params.coordinates.size()); } const auto &destinations_array = - json_result.values.at("destinations").get().values; + std::get(json_result.values.at("destinations")).values; for (const auto &destination : destinations_array) { BOOST_CHECK(waypoint_check(destination)); } - const auto &sources_array = json_result.values.at("sources").get().values; + const auto &sources_array = std::get(json_result.values.at("sources")).values; BOOST_CHECK_EQUAL(sources_array.size(), params.coordinates.size()); for (const auto &source : sources_array) { @@ -278,9 +278,9 @@ void test_table_no_segment_for_some_coordinates(bool use_json_only_api) const auto rc = run_table_json(osrm, params, json_result, use_json_only_api); BOOST_CHECK(rc == Status::Error); - const auto code = json_result.values.at("code").get().value; + const auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "NoSegment"); - const auto message = json_result.values.at("message").get().value; + const auto message = std::get(json_result.values.at("message")).value; BOOST_CHECK_EQUAL(message, "Could not find a matching segment for coordinate 0"); } BOOST_AUTO_TEST_CASE(test_table_no_segment_for_some_coordinates_old_api) @@ -312,7 +312,7 @@ BOOST_AUTO_TEST_CASE(test_table_serialiaze_fb) BOOST_CHECK(rc == Status::Ok || rc == Status::Error); - auto &fb_result = result.get(); + auto &fb_result = std::get(result); auto fb = engine::api::fbresult::GetFBResult(fb_result.GetBufferPointer()); BOOST_CHECK(!fb->error()); BOOST_CHECK(fb->table() != nullptr); @@ -366,7 +366,7 @@ BOOST_AUTO_TEST_CASE(test_table_serialiaze_fb_no_waypoints) BOOST_CHECK(rc == Status::Ok || rc == Status::Error); - auto &fb_result = result.get(); + auto &fb_result = std::get(result); auto fb = engine::api::fbresult::GetFBResult(fb_result.GetBufferPointer()); BOOST_CHECK(!fb->error()); BOOST_CHECK(fb->table() != nullptr); diff --git a/unit_tests/library/tile.cpp b/unit_tests/library/tile.cpp index 43b74bada..703e7755b 100644 --- a/unit_tests/library/tile.cpp +++ b/unit_tests/library/tile.cpp @@ -29,7 +29,7 @@ osrm::Status run_tile(const osrm::OSRM &osrm, } osrm::engine::api::ResultT result = std::string(); auto rc = osrm.Tile(params, result); - string_result = result.get(); + string_result = std::get(result); return rc; } diff --git a/unit_tests/library/trip.cpp b/unit_tests/library/trip.cpp index 52308f2e0..b286a1060 100644 --- a/unit_tests/library/trip.cpp +++ b/unit_tests/library/trip.cpp @@ -22,7 +22,7 @@ osrm::Status run_trip_json(const osrm::OSRM &osrm, } osrm::engine::api::ResultT result = osrm::json::Object(); auto rc = osrm.Trip(params, result); - json_result = result.get(); + json_result = std::get(result); return rc; } @@ -44,27 +44,27 @@ void test_roundtrip_response_for_locations_in_small_component(bool use_json_only const auto rc = run_trip_json(osrm, params, json_result, use_json_only_api); BOOST_CHECK(rc == Status::Ok); - const auto code = json_result.values.at("code").get().value; + const auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "Ok"); - const auto &waypoints = json_result.values.at("waypoints").get().values; + const auto &waypoints = std::get(json_result.values.at("waypoints")).values; BOOST_CHECK_EQUAL(waypoints.size(), params.coordinates.size()); - const auto &trips = json_result.values.at("trips").get().values; + const auto &trips = std::get(json_result.values.at("trips")).values; BOOST_CHECK_EQUAL(trips.size(), 1); for (const auto &waypoint : waypoints) { - const auto &waypoint_object = waypoint.get(); + const auto &waypoint_object = std::get(waypoint); - const auto location = waypoint_object.values.at("location").get().values; - const auto longitude = location[0].get().value; - const auto latitude = location[1].get().value; + const auto location = std::get(waypoint_object.values.at("location")).values; + const auto longitude = std::get(location[0]).value; + const auto latitude = std::get(location[1]).value; BOOST_CHECK(longitude >= -180. && longitude <= 180.); BOOST_CHECK(latitude >= -90. && latitude <= 90.); - const auto trip = waypoint_object.values.at("trips_index").get().value; - const auto pos = waypoint_object.values.at("waypoint_index").get().value; + const auto trip = std::get(waypoint_object.values.at("trips_index")).value; + const auto pos = std::get(waypoint_object.values.at("waypoint_index")).value; BOOST_CHECK(trip >= 0 && trip < trips.size()); BOOST_CHECK(pos >= 0 && pos < waypoints.size()); } @@ -95,7 +95,7 @@ void test_roundtrip_response_for_locations_in_small_component_skip_waypoints(boo const auto rc = run_trip_json(osrm, params, json_result, use_json_only_api); BOOST_CHECK(rc == Status::Ok); - const auto code = json_result.values.at("code").get().value; + const auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "Ok"); BOOST_CHECK(json_result.values.find("waypoints") == json_result.values.end()); @@ -127,27 +127,27 @@ void test_roundtrip_response_for_locations_in_big_component(bool use_json_only_a const auto rc = run_trip_json(osrm, params, json_result, use_json_only_api); BOOST_CHECK(rc == Status::Ok); - const auto code = json_result.values.at("code").get().value; + const auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "Ok"); - const auto &waypoints = json_result.values.at("waypoints").get().values; + const auto &waypoints = std::get(json_result.values.at("waypoints")).values; BOOST_CHECK_EQUAL(waypoints.size(), params.coordinates.size()); - const auto &trips = json_result.values.at("trips").get().values; + const auto &trips = std::get(json_result.values.at("trips")).values; BOOST_CHECK_EQUAL(trips.size(), 1); for (const auto &waypoint : waypoints) { - const auto &waypoint_object = waypoint.get(); + const auto &waypoint_object = std::get(waypoint); - const auto location = waypoint_object.values.at("location").get().values; - const auto longitude = location[0].get().value; - const auto latitude = location[1].get().value; + const auto location = std::get(waypoint_object.values.at("location")).values; + const auto longitude = std::get(location[0]).value; + const auto latitude = std::get(location[1]).value; BOOST_CHECK(longitude >= -180. && longitude <= 180.); BOOST_CHECK(latitude >= -90. && latitude <= 90.); - const auto trip = waypoint_object.values.at("trips_index").get().value; - const auto pos = waypoint_object.values.at("waypoint_index").get().value; + const auto trip = std::get(waypoint_object.values.at("trips_index")).value; + const auto pos = std::get(waypoint_object.values.at("waypoint_index")).value; BOOST_CHECK(trip >= 0 && trip < trips.size()); BOOST_CHECK(pos >= 0 && pos < waypoints.size()); } @@ -179,29 +179,29 @@ void test_roundtrip_response_for_locations_across_components(bool use_json_only_ const auto rc = run_trip_json(osrm, params, json_result, use_json_only_api); BOOST_CHECK(rc == Status::Ok); - const auto code = json_result.values.at("code").get().value; + const auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "Ok"); - const auto &waypoints = json_result.values.at("waypoints").get().values; + const auto &waypoints = std::get(json_result.values.at("waypoints")).values; BOOST_CHECK_EQUAL(waypoints.size(), params.coordinates.size()); - const auto &trips = json_result.values.at("trips").get().values; + const auto &trips = std::get(json_result.values.at("trips")).values; BOOST_CHECK_EQUAL(trips.size(), 1); // ^ First snapping, then SCC decomposition (see plugins/trip.cpp). Therefore only a single // trip. for (const auto &waypoint : waypoints) { - const auto &waypoint_object = waypoint.get(); + const auto &waypoint_object = std::get(waypoint); - const auto location = waypoint_object.values.at("location").get().values; - const auto longitude = location[0].get().value; - const auto latitude = location[1].get().value; + const auto location = std::get(waypoint_object.values.at("location")).values; + const auto longitude = std::get(location[0]).value; + const auto latitude = std::get(location[1]).value; BOOST_CHECK(longitude >= -180. && longitude <= 180.); BOOST_CHECK(latitude >= -90. && latitude <= 90.); - const auto trip = waypoint_object.values.at("trips_index").get().value; - const auto pos = waypoint_object.values.at("waypoint_index").get().value; + const auto trip = std::get(waypoint_object.values.at("trips_index")).value; + const auto pos = std::get(waypoint_object.values.at("waypoint_index")).value; BOOST_CHECK(trip >= 0 && trip < trips.size()); BOOST_CHECK(pos >= 0 && pos < waypoints.size()); } @@ -235,27 +235,27 @@ void test_tfse_1(bool use_json_only_api) const auto rc = run_trip_json(osrm, params, json_result, use_json_only_api); BOOST_CHECK(rc == Status::Ok); - const auto code = json_result.values.at("code").get().value; + const auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "Ok"); - const auto &waypoints = json_result.values.at("waypoints").get().values; + const auto &waypoints = std::get(json_result.values.at("waypoints")).values; BOOST_CHECK_EQUAL(waypoints.size(), params.coordinates.size()); - const auto &trips = json_result.values.at("trips").get().values; + const auto &trips = std::get(json_result.values.at("trips")).values; BOOST_CHECK_EQUAL(trips.size(), 1); for (const auto &waypoint : waypoints) { - const auto &waypoint_object = waypoint.get(); + const auto &waypoint_object = std::get(waypoint); - const auto location = waypoint_object.values.at("location").get().values; - const auto longitude = location[0].get().value; - const auto latitude = location[1].get().value; + const auto location = std::get(waypoint_object.values.at("location")).values; + const auto longitude = std::get(location[0]).value; + const auto latitude = std::get(location[1]).value; BOOST_CHECK(longitude >= -180. && longitude <= 180.); BOOST_CHECK(latitude >= -90. && latitude <= 90.); - const auto trip = waypoint_object.values.at("trips_index").get().value; - const auto pos = waypoint_object.values.at("waypoint_index").get().value; + const auto trip = std::get(waypoint_object.values.at("trips_index")).value; + const auto pos = std::get(waypoint_object.values.at("waypoint_index")).value; BOOST_CHECK(trip >= 0 && trip < trips.size()); BOOST_CHECK(pos >= 0 && pos < waypoints.size()); } @@ -283,27 +283,27 @@ void test_tfse_2(bool use_json_only_api) const auto rc = run_trip_json(osrm, params, json_result, use_json_only_api); BOOST_CHECK(rc == Status::Ok); - const auto code = json_result.values.at("code").get().value; + const auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "Ok"); - const auto &waypoints = json_result.values.at("waypoints").get().values; + const auto &waypoints = std::get(json_result.values.at("waypoints")).values; BOOST_CHECK_EQUAL(waypoints.size(), params.coordinates.size()); - const auto &trips = json_result.values.at("trips").get().values; + const auto &trips = std::get(json_result.values.at("trips")).values; BOOST_CHECK_EQUAL(trips.size(), 1); for (const auto &waypoint : waypoints) { - const auto &waypoint_object = waypoint.get(); + const auto &waypoint_object = std::get(waypoint); - const auto location = waypoint_object.values.at("location").get().values; - const auto longitude = location[0].get().value; - const auto latitude = location[1].get().value; + const auto location = std::get(waypoint_object.values.at("location")).values; + const auto longitude = std::get(location[0]).value; + const auto latitude = std::get(location[1]).value; BOOST_CHECK(longitude >= -180. && longitude <= 180.); BOOST_CHECK(latitude >= -90. && latitude <= 90.); - const auto trip = waypoint_object.values.at("trips_index").get().value; - const auto pos = waypoint_object.values.at("waypoint_index").get().value; + const auto trip = std::get(waypoint_object.values.at("trips_index")).value; + const auto pos = std::get(waypoint_object.values.at("waypoint_index")).value; BOOST_CHECK(trip >= 0 && trip < trips.size()); BOOST_CHECK(pos >= 0 && pos < waypoints.size()); } @@ -326,7 +326,7 @@ void CheckNotImplemented(const osrm::OSRM &osrm, json::Object json_result; const auto rc = run_trip_json(osrm, params, json_result, use_json_only_api); BOOST_REQUIRE(rc == osrm::Status::Error); - auto code = json_result.values.at("code").get().value; + auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "NotImplemented"); } @@ -336,7 +336,7 @@ void CheckOk(const osrm::OSRM &osrm, osrm::TripParameters ¶ms, bool use_json json::Object json_result; const auto rc = run_trip_json(osrm, params, json_result, use_json_only_api); BOOST_REQUIRE(rc == osrm::Status::Ok); - auto code = json_result.values.at("code").get().value; + auto code = std::get(json_result.values.at("code")).value; BOOST_CHECK_EQUAL(code, "Ok"); } @@ -512,7 +512,7 @@ BOOST_AUTO_TEST_CASE(test_roundtrip_response_fb_serialization) const auto rc = osrm.Trip(params, result); BOOST_CHECK(rc == Status::Ok); - auto &fb_result = result.get(); + auto &fb_result = std::get(result); auto fb = engine::api::fbresult::GetFBResult(fb_result.GetBufferPointer()); BOOST_CHECK(!fb->error()); @@ -556,7 +556,7 @@ BOOST_AUTO_TEST_CASE(test_roundtrip_response_fb_serialization_skip_waypoints) const auto rc = osrm.Trip(params, result); BOOST_CHECK(rc == Status::Ok); - auto &fb_result = result.get(); + auto &fb_result = std::get(result); auto fb = engine::api::fbresult::GetFBResult(fb_result.GetBufferPointer()); BOOST_CHECK(!fb->error()); diff --git a/unit_tests/library/waypoint_check.hpp b/unit_tests/library/waypoint_check.hpp index 76e4524f3..bda7dfc8c 100644 --- a/unit_tests/library/waypoint_check.hpp +++ b/unit_tests/library/waypoint_check.hpp @@ -5,19 +5,21 @@ #include "osrm/coordinate.hpp" #include "osrm/json_container.hpp" #include "util/exception.hpp" +#include inline bool waypoint_check(osrm::json::Value waypoint) { using namespace osrm; - if (!waypoint.is>()) + if (!std::holds_alternative(waypoint)) { throw util::exception("Must pass in a waypoint object"); } - const auto waypoint_object = waypoint.get(); - const auto waypoint_location = waypoint_object.values.at("location").get().values; - util::FloatLongitude lon{waypoint_location[0].get().value}; - util::FloatLatitude lat{waypoint_location[1].get().value}; + const auto waypoint_object = std::get(waypoint); + const auto waypoint_location = + std::get(waypoint_object.values.at("location")).values; + util::FloatLongitude lon{std::get(waypoint_location[0]).value}; + util::FloatLatitude lat{std::get(waypoint_location[1]).value}; util::Coordinate location_coordinate(lon, lat); return location_coordinate.IsValid(); } From 42cbca0ff06f16110fc88935fa75bf148e7808f1 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Tue, 28 May 2024 21:23:51 +0200 Subject: [PATCH 13/28] Move Sol2 to canonical path (#6912) --- CMakeLists.txt | 2 +- scripts/update_dependencies.sh | 2 +- third_party/{sol2-3.3.0 => sol2}/include/sol/config.hpp | 0 third_party/{sol2-3.3.0 => sol2}/include/sol/forward.hpp | 0 third_party/{sol2-3.3.0 => sol2}/include/sol/sol.hpp | 0 5 files changed, 2 insertions(+), 2 deletions(-) rename third_party/{sol2-3.3.0 => sol2}/include/sol/config.hpp (100%) rename third_party/{sol2-3.3.0 => sol2}/include/sol/forward.hpp (100%) rename third_party/{sol2-3.3.0 => sol2}/include/sol/sol.hpp (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5aff11255..1686da493 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -120,7 +120,7 @@ endif() include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}/include/) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/include/) -include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/third_party/sol2-3.3.0/include) +include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/third_party/sol2/include) set(BOOST_COMPONENTS date_time chrono filesystem iostreams program_options regex system thread unit_test_framework) diff --git a/scripts/update_dependencies.sh b/scripts/update_dependencies.sh index f16b175ca..ce9e48af0 100755 --- a/scripts/update_dependencies.sh +++ b/scripts/update_dependencies.sh @@ -13,7 +13,7 @@ OSMIUM_PATH="osmcode/libosmium" OSMIUM_TAG=v2.14.0 SOL_PATH="ThePhD/sol2" -SOL_TAG=v2.17.5 +SOL_TAG=v3.3.0 RAPIDJSON_PATH="Tencent/rapidjson" RAPIDJSON_TAG=f9d53419e912910fd8fa57d5705fa41425428c35 diff --git a/third_party/sol2-3.3.0/include/sol/config.hpp b/third_party/sol2/include/sol/config.hpp similarity index 100% rename from third_party/sol2-3.3.0/include/sol/config.hpp rename to third_party/sol2/include/sol/config.hpp diff --git a/third_party/sol2-3.3.0/include/sol/forward.hpp b/third_party/sol2/include/sol/forward.hpp similarity index 100% rename from third_party/sol2-3.3.0/include/sol/forward.hpp rename to third_party/sol2/include/sol/forward.hpp diff --git a/third_party/sol2-3.3.0/include/sol/sol.hpp b/third_party/sol2/include/sol/sol.hpp similarity index 100% rename from third_party/sol2-3.3.0/include/sol/sol.hpp rename to third_party/sol2/include/sol/sol.hpp From 0ea757ed0285dd5aa2b9e13b39568b1305ef3641 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Wed, 29 May 2024 09:13:13 +0200 Subject: [PATCH 14/28] Remove unused header file (#6913) --- include/util/matrix_graph_wrapper.hpp | 49 --------------------------- include/util/string_util.hpp | 47 ------------------------- unit_tests/util/string_util.cpp | 18 +--------- 3 files changed, 1 insertion(+), 113 deletions(-) delete mode 100644 include/util/matrix_graph_wrapper.hpp diff --git a/include/util/matrix_graph_wrapper.hpp b/include/util/matrix_graph_wrapper.hpp deleted file mode 100644 index c40fd131a..000000000 --- a/include/util/matrix_graph_wrapper.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef MATRIX_GRAPH_WRAPPER_H -#define MATRIX_GRAPH_WRAPPER_H - -#include -#include -#include - -#include "util/typedefs.hpp" - -namespace osrm::util -{ - -// This Wrapper provides all methods that are needed for util::TarjanSCC, when the graph is -// given in a matrix representation (e.g. as output from a distance table call) - -template class MatrixGraphWrapper -{ - public: - MatrixGraphWrapper(std::vector table, const std::size_t number_of_nodes) - : table_(std::move(table)), number_of_nodes_(number_of_nodes) - { - } - - std::size_t GetNumberOfNodes() const { return number_of_nodes_; } - - std::vector GetAdjacentEdgeRange(const NodeID node) const - { - - std::vector edges; - // find all valid adjacent edges and move to vector `edges` - for (std::size_t i = 0; i < number_of_nodes_; ++i) - { - if (*(std::begin(table_) + node * number_of_nodes_ + i) != INVALID_EDGE_WEIGHT) - { - edges.push_back(i); - } - } - return edges; - } - - EdgeWeight GetTarget(const EdgeWeight edge) const { return edge; } - - private: - const std::vector table_; - const std::size_t number_of_nodes_; -}; -} // namespace osrm::util - -#endif // MATRIX_GRAPH_WRAPPER_H diff --git a/include/util/string_util.hpp b/include/util/string_util.hpp index ae881c514..874a1d284 100644 --- a/include/util/string_util.hpp +++ b/include/util/string_util.hpp @@ -10,53 +10,6 @@ namespace osrm::util { -// precision: position after decimal point -// length: maximum number of digits including comma and decimals -// work with negative values to prevent overflowing when taking -value -template char *printInt(char *buffer, int value) -{ - static_assert(length > 0, "length must be positive"); - static_assert(precision > 0, "precision must be positive"); - - const bool minus = [&value] - { - if (value >= 0) - { - value = -value; - return false; - } - return true; - }(); - - buffer += length - 1; - for (int i = 0; i < precision; ++i) - { - *buffer = '0' - (value % 10); - value /= 10; - --buffer; - } - *buffer = '.'; - --buffer; - - for (int i = precision + 1; i < length; ++i) - { - *buffer = '0' - (value % 10); - value /= 10; - if (value == 0) - { - break; - } - --buffer; - } - - if (minus) - { - --buffer; - *buffer = '-'; - } - return buffer; -} - inline bool RequiresJSONStringEscaping(const std::string &string) { for (const char letter : string) diff --git a/unit_tests/util/string_util.cpp b/unit_tests/util/string_util.cpp index f027ca8e1..b3470644a 100644 --- a/unit_tests/util/string_util.cpp +++ b/unit_tests/util/string_util.cpp @@ -2,7 +2,7 @@ #include -#include +#include BOOST_AUTO_TEST_SUITE(string_util) @@ -27,20 +27,4 @@ BOOST_AUTO_TEST_CASE(json_escaping) BOOST_CHECK(!RequiresJSONStringEscaping("Aleja Solidarnosci")); } -BOOST_AUTO_TEST_CASE(print_int) -{ - const std::string input{"\b\\"}; - char buffer[12]; - buffer[11] = 0; // zero termination - std::string output = printInt<11, 8>(buffer, 314158976); - BOOST_CHECK_EQUAL(output, "3.14158976"); - - buffer[11] = 0; - output = printInt<11, 8>(buffer, 0); - BOOST_CHECK_EQUAL(output, "0.00000000"); - - output = printInt<11, 8>(buffer, -314158976); - BOOST_CHECK_EQUAL(output, "-3.14158976"); -} - BOOST_AUTO_TEST_SUITE_END() From 1ff096ac5c3aa11f1e98cd375e1fdf117f1b8ccc Mon Sep 17 00:00:00 2001 From: Siarhei Fedartsou Date: Thu, 30 May 2024 15:12:42 +0200 Subject: [PATCH 15/28] Make constants in PackedVector constexpr (#6917) --- CHANGELOG.md | 1 + include/util/packed_vector.hpp | 49 ++++++++++------------------------ 2 files changed, 15 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a22841b00..cbeaf91df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - NodeJS: - CHANGED: Use node-api instead of NAN. [#6452](https://github.com/Project-OSRM/osrm-backend/pull/6452) - Misc: + - CHANGED: Make constants in PackedVector constexpr. [#6917](https://github.com/Project-OSRM/osrm-backend/pull/6917) - CHANGED: Use std::variant instead of mapbox::util::variant. [#6903](https://github.com/Project-OSRM/osrm-backend/pull/6903) - CHANGED: Bump rapidjson to version f9d53419e912910fd8fa57d5705fa41425428c35 [#6906](https://github.com/Project-OSRM/osrm-backend/pull/6906) - CHANGED: Bump mapbox/variant to version 1.2.0 [#6898](https://github.com/Project-OSRM/osrm-backend/pull/6898) diff --git a/include/util/packed_vector.hpp b/include/util/packed_vector.hpp index 243f423e5..6e03421ee 100644 --- a/include/util/packed_vector.hpp +++ b/include/util/packed_vector.hpp @@ -153,8 +153,7 @@ template class Pack // number of words per block static constexpr std::size_t BLOCK_WORDS = (Bits * BLOCK_ELEMENTS) / WORD_BITS; - // C++14 does not allow operator[] to be constexpr, this is fixed in C++17. - static /* constexpr */ std::array initialize_lower_mask() + static constexpr std::array initialize_lower_mask() { std::array lower_mask; @@ -170,7 +169,7 @@ template class Pack return lower_mask; } - static /* constexpr */ std::array initialize_upper_mask() + static constexpr std::array initialize_upper_mask() { std::array upper_mask; @@ -194,7 +193,7 @@ template class Pack return upper_mask; } - static /* constexpr */ std::array initialize_lower_offset() + static constexpr std::array initialize_lower_offset() { std::array lower_offset; @@ -209,7 +208,7 @@ template class Pack return lower_offset; } - static /* constexpr */ std::array initialize_upper_offset() + static constexpr std::array initialize_upper_offset() { std::array upper_offset; @@ -232,7 +231,7 @@ template class Pack return upper_offset; } - static /* constexpr */ std::array initialize_word_offset() + static constexpr std::array initialize_word_offset() { std::array word_offset; @@ -246,28 +245,15 @@ template class Pack return word_offset; } - // For now we need to call these on object creation - void initialize() - { - lower_mask = initialize_lower_mask(); - upper_mask = initialize_upper_mask(); - lower_offset = initialize_lower_offset(); - upper_offset = initialize_upper_offset(); - word_offset = initialize_word_offset(); - } - // mask for the lower/upper word of a record - // TODO: With C++17 these could be constexpr - /* static constexpr */ std::array - lower_mask /* = initialize_lower_mask()*/; - /* static constexpr */ std::array - upper_mask /* = initialize_upper_mask()*/; - /* static constexpr */ std::array - lower_offset /* = initialize_lower_offset()*/; - /* static constexpr */ std::array - upper_offset /* = initialize_upper_offset()*/; + static constexpr std::array lower_mask = initialize_lower_mask(); + static constexpr std::array upper_mask = initialize_upper_mask(); + static constexpr std::array lower_offset = + initialize_lower_offset(); + static constexpr std::array upper_offset = + initialize_upper_offset(); // in which word of the block is the element - /* static constexpr */ std::array word_offset = + static constexpr std::array word_offset = initialize_word_offset(); struct InternalIndex @@ -378,27 +364,21 @@ template class Pack PackedVector(std::initializer_list list) { - initialize(); reserve(list.size()); for (const auto value : list) push_back(value); } - PackedVector() { initialize(); }; + PackedVector(){}; PackedVector(const PackedVector &) = default; PackedVector(PackedVector &&) = default; PackedVector &operator=(const PackedVector &) = default; PackedVector &operator=(PackedVector &&) = default; - PackedVector(std::size_t size) - { - initialize(); - resize(size); - } + PackedVector(std::size_t size) { resize(size); } PackedVector(std::size_t size, T initial_value) { - initialize(); resize(size); fill(initial_value); } @@ -406,7 +386,6 @@ template class Pack PackedVector(util::ViewOrVector vec_, std::size_t num_elements) : vec(std::move(vec_)), num_elements(num_elements) { - initialize(); } // forces the efficient read-only lookup From a9b1bd88d3073d2a23503fe4f43f615f870555e7 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Thu, 30 May 2024 17:13:44 +0200 Subject: [PATCH 16/28] Remove all core-CH left-overs (#6920) * Remove all core-CH left-overs * Fix formatting * Update CHANGELOG.md --- CHANGELOG.md | 1 + docs/nodejs/api.md | 2 +- include/engine/engine_config.hpp | 7 +----- include/engine/search_engine_data.hpp | 1 - include/nodejs/node_osrm_support.hpp | 9 ++----- scripts/ci/windows-build.bat | 6 ----- src/nodejs/node_osrm.cpp | 2 +- src/osrm/osrm.cpp | 7 ------ src/tools/routed.cpp | 4 +-- test/data/Makefile | 17 ++----------- test/nodejs/constants.js | 2 -- test/nodejs/index.js | 21 +++------------- test/nodejs/route.js | 27 -------------------- unit_tests/library/algorithm.cpp | 8 ------ unit_tests/library/options.cpp | 10 -------- unit_tests/library/tile.cpp | 36 --------------------------- 16 files changed, 13 insertions(+), 147 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cbeaf91df..965c2a3d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Unreleased - Changes from 5.27.1 - Features + - REMOVED: Remove all core-CH left-overs [#6920](https://github.com/Project-OSRM/osrm-backend/pull/6920) - ADDED: Add support for a keepalive_timeout flag. [#6674](https://github.com/Project-OSRM/osrm-backend/pull/6674) - ADDED: Add support for a default_radius flag. [#6575](https://github.com/Project-OSRM/osrm-backend/pull/6575) - ADDED: Add support for disabling feature datasets. [#6666](https://github.com/Project-OSRM/osrm-backend/pull/6666) diff --git a/docs/nodejs/api.md b/docs/nodejs/api.md index 9dbfe912c..a87452cbc 100644 --- a/docs/nodejs/api.md +++ b/docs/nodejs/api.md @@ -21,7 +21,7 @@ var osrm = new OSRM('network.osrm'); **Parameters** - `options` **([Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object) \| [String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String))** Options for creating an OSRM object or string to the `.osrm` file. (optional, default `{shared_memory:true}`) - - `options.algorithm` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** The algorithm to use for routing. Can be 'CH', 'CoreCH' or 'MLD'. Default is 'CH'. + - `options.algorithm` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** The algorithm to use for routing. Can be 'CH', or 'MLD'. Default is 'CH'. Make sure you prepared the dataset with the correct toolchain. - `options.shared_memory` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** Connects to the persistent shared memory datastore. This requires you to run `osrm-datastore` prior to creating an `OSRM` object. diff --git a/include/engine/engine_config.hpp b/include/engine/engine_config.hpp index c7c7eb06f..cb986be9e 100644 --- a/include/engine/engine_config.hpp +++ b/include/engine/engine_config.hpp @@ -54,14 +54,10 @@ namespace osrm::engine * * In addition, shared memory can be used for datasets loaded with osrm-datastore. * - * You can chose between three algorithms: + * You can chose between two algorithms: * - Algorithm::CH * Contraction Hierarchies, extremely fast queries but slow pre-processing. The default right * now. - * - Algorithm::CoreCH - * Deprecated, to be removed in v6.0 - * Contraction Hierachies with partial contraction for faster pre-processing but slower - * queries. * - Algorithm::MLD * Multi Level Dijkstra, moderately fast in both pre-processing and query. * @@ -74,7 +70,6 @@ struct EngineConfig final enum class Algorithm { CH, - CoreCH, // Deprecated, will be removed in v6.0 MLD }; diff --git a/include/engine/search_engine_data.hpp b/include/engine/search_engine_data.hpp index 4060ab6b2..16ffa25b1 100644 --- a/include/engine/search_engine_data.hpp +++ b/include/engine/search_engine_data.hpp @@ -12,7 +12,6 @@ namespace osrm::engine // Algorithm-dependent heaps // - CH algorithms use CH heaps -// - CoreCH algorithms use CH // - MLD algorithms use MLD heaps template struct SearchEngineData diff --git a/include/nodejs/node_osrm_support.hpp b/include/nodejs/node_osrm_support.hpp index 18fb20bac..cd0043bb4 100644 --- a/include/nodejs/node_osrm_support.hpp +++ b/include/nodejs/node_osrm_support.hpp @@ -296,24 +296,19 @@ inline engine_config_ptr argumentsToEngineConfig(const Napi::CallbackInfo &args) { engine_config->algorithm = osrm::EngineConfig::Algorithm::CH; } - else if (algorithm_str == "CoreCH") - { - engine_config->algorithm = osrm::EngineConfig::Algorithm::CH; - } else if (algorithm_str == "MLD") { engine_config->algorithm = osrm::EngineConfig::Algorithm::MLD; } else { - ThrowError(args.Env(), "algorithm option must be one of 'CH', 'CoreCH', or 'MLD'."); + ThrowError(args.Env(), "algorithm option must be one of 'CH', or 'MLD'."); return engine_config_ptr(); } } else if (!algorithm.IsUndefined()) { - ThrowError(args.Env(), - "algorithm option must be a string and one of 'CH', 'CoreCH', or 'MLD'."); + ThrowError(args.Env(), "algorithm option must be a string and one of 'CH', or 'MLD'."); return engine_config_ptr(); } diff --git a/scripts/ci/windows-build.bat b/scripts/ci/windows-build.bat index 71c9bfc71..bc1fc0a8f 100644 --- a/scripts/ci/windows-build.bat +++ b/scripts/ci/windows-build.bat @@ -58,7 +58,6 @@ IF %ERRORLEVEL% NEQ 0 GOTO ERROR SET test_region=monaco SET test_region_ch=ch\monaco -SET test_region_corech=corech\monaco SET test_region_mld=mld\monaco SET test_osm=%test_region%.osm.pbf COPY %PROJECT_DIR%\test\data\%test_region%.osm.pbf %test_osm% @@ -68,18 +67,13 @@ IF %ERRORLEVEL% NEQ 0 GOTO ERROR MKDIR ch XCOPY %test_region%.osrm.* ch\ XCOPY %test_region%.osrm ch\ -MKDIR corech -XCOPY %test_region%.osrm.* corech\ -XCOPY %test_region%.osrm corech\ MKDIR mld XCOPY %test_region%.osrm.* mld\ XCOPY %test_region%.osrm mld\ %CONFIGURATION%\osrm-contract.exe %test_region_ch%.osrm -%CONFIGURATION%\osrm-contract.exe --core 0.8 %test_region_corech%.osrm %CONFIGURATION%\osrm-partition.exe %test_region_mld%.osrm %CONFIGURATION%\osrm-customize.exe %test_region_mld%.osrm XCOPY /Y ch\*.* ..\test\data\ch\ -XCOPY /Y corech\*.* ..\test\data\corech\ XCOPY /Y mld\*.* ..\test\data\mld\ unit_tests\%CONFIGURATION%\library-tests.exe IF %ERRORLEVEL% NEQ 0 GOTO ERROR diff --git a/src/nodejs/node_osrm.cpp b/src/nodejs/node_osrm.cpp index 65585abac..5971fec1c 100644 --- a/src/nodejs/node_osrm.cpp +++ b/src/nodejs/node_osrm.cpp @@ -64,7 +64,7 @@ Napi::Object Engine::Init(Napi::Env env, Napi::Object exports) * ``` * * @param {Object|String} [options={shared_memory: true}] Options for creating an OSRM object or string to the `.osrm` file. - * @param {String} [options.algorithm] The algorithm to use for routing. Can be 'CH', 'CoreCH' or 'MLD'. Default is 'CH'. + * @param {String} [options.algorithm] The algorithm to use for routing. Can be 'CH', or 'MLD'. Default is 'CH'. * Make sure you prepared the dataset with the correct toolchain. * @param {Boolean} [options.shared_memory] Connects to the persistent shared memory datastore. * This requires you to run `osrm-datastore` prior to creating an `OSRM` object. diff --git a/src/osrm/osrm.cpp b/src/osrm/osrm.cpp index cf961d5fb..7f0ab1034 100644 --- a/src/osrm/osrm.cpp +++ b/src/osrm/osrm.cpp @@ -36,13 +36,6 @@ OSRM::OSRM(engine::EngineConfig &config) // Now, check that the algorithm requested can be used with the data // that's available. - - if (config.algorithm == EngineConfig::Algorithm::CoreCH) - { - util::Log(logWARNING) << "Using CoreCH is deprecated. Falling back to CH"; - config.algorithm = EngineConfig::Algorithm::CH; - } - switch (config.algorithm) { case EngineConfig::Algorithm::CH: diff --git a/src/tools/routed.cpp b/src/tools/routed.cpp index 4f2fae5ef..f4c229cba 100644 --- a/src/tools/routed.cpp +++ b/src/tools/routed.cpp @@ -61,7 +61,7 @@ std::istream &operator>>(std::istream &in, EngineConfig::Algorithm &algorithm) in >> token; boost::to_lower(token); - if (token == "ch" || token == "corech") + if (token == "ch") algorithm = EngineConfig::Algorithm::CH; else if (token == "mld") algorithm = EngineConfig::Algorithm::MLD; @@ -159,7 +159,7 @@ inline unsigned generateServerProgramOptions(const int argc, ("algorithm,a", value(&config.algorithm) ->default_value(EngineConfig::Algorithm::CH, "CH"), - "Algorithm to use for the data. Can be CH, CoreCH, MLD.") // + "Algorithm to use for the data. Can be CH, MLD.") // ("disable-feature-dataset", value>(&config.disable_feature_dataset)->multitoken(), "Disables a feature dataset from being loaded into memory if not needed. Options: " diff --git a/test/data/Makefile b/test/data/Makefile index 14c586e93..2c617405b 100755 --- a/test/data/Makefile +++ b/test/data/Makefile @@ -14,20 +14,16 @@ PROFILE:=$(PROFILE_ROOT)/car.lua all: data -data: ch/$(DATA_NAME).osrm.hsgr corech/$(DATA_NAME).osrm.hsgr mld/$(DATA_NAME).osrm.partition +data: ch/$(DATA_NAME).osrm.hsgr mld/$(DATA_NAME).osrm.partition clean: -rm -r $(DATA_NAME).* - -rm -r ch corech mld + -rm -r ch mld ch/$(DATA_NAME).osrm: $(DATA_NAME).osrm mkdir -p ch cp $(DATA_NAME).osrm.* ch/ -corech/$(DATA_NAME).osrm: $(DATA_NAME).osrm - mkdir -p corech - cp $(DATA_NAME).osrm.* corech/ - mld/$(DATA_NAME).osrm: $(DATA_NAME).osrm mkdir -p mld cp $(DATA_NAME).osrm.* mld/ @@ -42,10 +38,6 @@ ch/$(DATA_NAME).osrm.hsgr: ch/$(DATA_NAME).osrm $(PROFILE) $(OSRM_CONTRACT) @echo "Running osrm-contract..." $(TIMER) "osrm-contract\t$@" $(OSRM_CONTRACT) $< -corech/$(DATA_NAME).osrm.hsgr: corech/$(DATA_NAME).osrm $(PROFILE) $(OSRM_CONTRACT) - @echo "Running osrm-contract..." - $(TIMER) "osrm-contract\t$@" $(OSRM_CONTRACT) --core=0.5 $< - mld/$(DATA_NAME).osrm.partition: mld/$(DATA_NAME).osrm $(PROFILE) $(OSRM_PARTITION) @echo "Running osrm-partition..." $(TIMER) "osrm-partition\t$@" $(OSRM_PARTITION) $< @@ -61,11 +53,6 @@ benchmark: data $(DATA_NAME).requests $(TIMER) "queries\tCH" "cat $(DATA_NAME).requests | xargs curl &> /dev/null" @cat osrm-routed.pid | xargs kill @rm osrm-routed.pid - @/bin/sh -c '$(OSRM_ROUTED) --algorithm=CoreCH corech/$(DATA_NAME).osrm > /dev/null & echo "$$!" > osrm-routed.pid' - @sleep 1 - $(TIMER) "queries\tCoreCH" "cat $(DATA_NAME).requests | xargs curl &> /dev/null" - @cat osrm-routed.pid | xargs kill - @rm osrm-routed.pid @/bin/sh -c '$(OSRM_ROUTED) --algorithm=MLD mld/$(DATA_NAME).osrm > /dev/null & echo "$$!" > osrm-routed.pid' @sleep 1 $(TIMER) "queries\tMLD" "cat $(DATA_NAME).requests | xargs curl &> /dev/null" diff --git a/test/nodejs/constants.js b/test/nodejs/constants.js index e56742e0c..e62b46254 100644 --- a/test/nodejs/constants.js +++ b/test/nodejs/constants.js @@ -16,12 +16,10 @@ exports.test_tile = {'at': [17059, 11948, 15], 'size': 159125}; if (process.env.OSRM_DATA_PATH !== undefined) { exports.data_path = path.join(path.resolve(process.env.OSRM_DATA_PATH), "ch/monaco.osrm"); exports.mld_data_path = path.join(path.resolve(process.env.OSRM_DATA_PATH), "mld/monaco.osrm"); - exports.corech_data_path = path.join(path.resolve(process.env.OSRM_DATA_PATH), "corech/monaco.osrm"); exports.test_memory_path = path.join(path.resolve(process.env.OSRM_DATA_PATH), "test_memory"); console.log('Setting custom data path to ' + exports.data_path); } else { exports.data_path = path.resolve(path.join(__dirname, "../data/ch/monaco.osrm")); exports.mld_data_path = path.resolve(path.join(__dirname, "../data/mld/monaco.osrm")); - exports.corech_data_path = path.resolve(path.join(__dirname, "../data/corech/monaco.osrm")); exports.test_memory_path = path.resolve(path.join(__dirname, "../data/test_memory")); } diff --git a/test/nodejs/index.js b/test/nodejs/index.js index 73667cb8c..88a516384 100644 --- a/test/nodejs/index.js +++ b/test/nodejs/index.js @@ -3,7 +3,6 @@ var test = require('tape'); var monaco_path = require('./constants').data_path; var test_memory_file = require('./constants').test_memory_file; var monaco_mld_path = require('./constants').mld_data_path; -var monaco_corech_path = require('./constants').corech_data_path; test('constructor: throws if new keyword is not used', function(assert) { assert.plan(1); @@ -65,13 +64,13 @@ test('constructor: throws if given a non-string/obj argument', function(assert) test('constructor: throws if given an unkown algorithm', function(assert) { assert.plan(1); assert.throws(function() { new OSRM({algorithm: 'Foo', shared_memory: true}); }, - /algorithm option must be one of 'CH', 'CoreCH', or 'MLD'/); + /algorithm option must be one of 'CH', or 'MLD'/); }); test('constructor: throws if given an invalid algorithm', function(assert) { assert.plan(1); assert.throws(function() { new OSRM({algorithm: 3, shared_memory: true}); }, - /algorithm option must be a string and one of 'CH', 'CoreCH', or 'MLD'/); + /algorithm option must be a string and one of 'CH', or 'MLD'/); }); test('constructor: loads MLD if given as algorithm', function(assert) { @@ -86,22 +85,8 @@ test('constructor: loads CH if given as algorithm', function(assert) { assert.ok(osrm); }); -test('constructor: loads CoreCH if given as algorithm', function(assert) { - assert.plan(1); - var osrm = new OSRM({algorithm: 'CoreCH', path: monaco_corech_path}); - assert.ok(osrm); -}); - -test('constructor: autoswitches to CoreCH for a CH dataset if capable', function(assert) { - assert.plan(1); - var osrm = new OSRM({algorithm: 'CH', path: monaco_corech_path}); - assert.ok(osrm); -}); - test('constructor: throws if data doesn\'t match algorithm', function(assert) { - assert.plan(3); - assert.throws(function() { new OSRM({algorithm: 'CoreCH', path: monaco_mld_path}); }, /Could not find any metrics for CH/, 'CoreCH with MLD data'); - assert.ok(new OSRM({algorithm: 'CoreCH', path: monaco_path}), 'CoreCH with CH data'); + assert.plan(1); assert.throws(function() { new OSRM({algorithm: 'MLD', path: monaco_path}); }, /Could not find any metrics for MLD/, 'MLD with CH data'); }); diff --git a/test/nodejs/route.js b/test/nodejs/route.js index f84ced4e0..8678cb12a 100644 --- a/test/nodejs/route.js +++ b/test/nodejs/route.js @@ -2,7 +2,6 @@ var OSRM = require('../../'); var test = require('tape'); var monaco_path = require('./constants').data_path; var monaco_mld_path = require('./constants').mld_data_path; -var monaco_corech_path = require('./constants').corech_data_path; var three_test_coordinates = require('./constants').three_test_coordinates; var two_test_coordinates = require('./constants').two_test_coordinates; const flatbuffers = require('../../features/support/flatbuffers').flatbuffers; @@ -76,32 +75,6 @@ test('route: routes Monaco on MLD', function(assert) { }); }); -test('route: routes Monaco on CoreCH', function(assert) { - assert.plan(5); - var osrm = new OSRM({path: monaco_corech_path, algorithm: 'CoreCH'}); - osrm.route({coordinates: [[13.43864,52.51993],[13.415852,52.513191]]}, function(err, route) { - assert.ifError(err); - assert.ok(route.waypoints); - assert.ok(route.routes); - assert.ok(route.routes.length); - assert.ok(route.routes[0].geometry); - }); -}); - -test('route: routes Monaco and returns a JSON buffer', function(assert) { - assert.plan(6); - var osrm = new OSRM({path: monaco_corech_path, algorithm: 'CoreCH'}); - osrm.route({coordinates: [[13.43864,52.51993],[13.415852,52.513191]]}, { format: 'json_buffer'}, function(err, result) { - assert.ifError(err); - assert.ok(result instanceof Buffer); - const route = JSON.parse(result); - assert.ok(route.waypoints); - assert.ok(route.routes); - assert.ok(route.routes.length); - assert.ok(route.routes[0].geometry); - }); -}); - test('route: throws with too few or invalid args', function(assert) { assert.plan(4); var osrm = new OSRM(monaco_path); diff --git a/unit_tests/library/algorithm.cpp b/unit_tests/library/algorithm.cpp index b22cbfba6..31798ffb3 100644 --- a/unit_tests/library/algorithm.cpp +++ b/unit_tests/library/algorithm.cpp @@ -14,14 +14,6 @@ BOOST_AUTO_TEST_CASE(test_incompatible_with_mld) osrm::exception); } -BOOST_AUTO_TEST_CASE(test_compatible_with_corech_fallback) -{ - // Note - this tests that given the CoreCH algorithm config option, configuration falls back to - // CH and is compatible with CH data - BOOST_CHECK_NO_THROW( - getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm", osrm::EngineConfig::Algorithm::CoreCH)); -} - BOOST_AUTO_TEST_CASE(test_incompatible_with_ch) { // Can't use the CH algorithm with MLD data diff --git a/unit_tests/library/options.cpp b/unit_tests/library/options.cpp index 944a62fc5..2d28e474a 100644 --- a/unit_tests/library/options.cpp +++ b/unit_tests/library/options.cpp @@ -18,16 +18,6 @@ BOOST_AUTO_TEST_CASE(test_ch) OSRM osrm{config}; } -BOOST_AUTO_TEST_CASE(test_corech) -{ - using namespace osrm; - EngineConfig config; - config.use_shared_memory = false; - config.storage_config = storage::StorageConfig(OSRM_TEST_DATA_DIR "/corech/monaco.osrm"); - config.algorithm = EngineConfig::Algorithm::CoreCH; - OSRM osrm{config}; -} - BOOST_AUTO_TEST_CASE(test_mld) { using namespace osrm; diff --git a/unit_tests/library/tile.cpp b/unit_tests/library/tile.cpp index 703e7755b..89d7f1680 100644 --- a/unit_tests/library/tile.cpp +++ b/unit_tests/library/tile.cpp @@ -198,18 +198,6 @@ void test_tile_ch(bool use_string_only_api) BOOST_AUTO_TEST_CASE(test_tile_ch_old_api) { test_tile_ch(true); } BOOST_AUTO_TEST_CASE(test_tile_ch_new_api) { test_tile_ch(false); } -void test_tile_corech(bool use_string_only_api) -{ - // Note: this tests that given the CoreCH algorithm config option, configuration falls back to - // CH and is compatible with CH data - using namespace osrm; - auto osrm = - getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm", osrm::EngineConfig::Algorithm::CoreCH); - validate_tile(osrm, use_string_only_api); -} -BOOST_AUTO_TEST_CASE(test_tile_corech_old_api) { test_tile_corech(true); } -BOOST_AUTO_TEST_CASE(test_tile_corech_new_api) { test_tile_corech(false); } - void test_tile_mld(bool use_string_only_api) { using namespace osrm; @@ -347,14 +335,6 @@ BOOST_AUTO_TEST_CASE(test_tile_turns_ch_new_api) { test_tile_turns_ch(osrm::EngineConfig::Algorithm::CH, false); } -BOOST_AUTO_TEST_CASE(test_tile_turns_corech_old_api) -{ - test_tile_turns_ch(osrm::EngineConfig::Algorithm::CoreCH, true); -} -BOOST_AUTO_TEST_CASE(test_tile_turns_corech_new_api) -{ - test_tile_turns_ch(osrm::EngineConfig::Algorithm::CoreCH, false); -} void test_tile_turns_mld(bool use_string_only_api) { @@ -432,14 +412,6 @@ BOOST_AUTO_TEST_CASE(test_tile_speeds_ch_new_api) { test_tile_speeds_ch(osrm::EngineConfig::Algorithm::CH, false); } -BOOST_AUTO_TEST_CASE(test_tile_speeds_corech_old_api) -{ - test_tile_speeds_ch(osrm::EngineConfig::Algorithm::CoreCH, true); -} -BOOST_AUTO_TEST_CASE(test_tile_speeds_corech_new_api) -{ - test_tile_speeds_ch(osrm::EngineConfig::Algorithm::CoreCH, false); -} void test_tile_speeds_mld(bool use_string_only_api) { @@ -501,14 +473,6 @@ BOOST_AUTO_TEST_CASE(test_tile_node_ch_new_api) { test_tile_nodes_ch(osrm::EngineConfig::Algorithm::CH, false); } -BOOST_AUTO_TEST_CASE(test_tile_node_corech_old_api) -{ - test_tile_nodes_ch(osrm::EngineConfig::Algorithm::CoreCH, true); -} -BOOST_AUTO_TEST_CASE(test_tile_node_corech_new_api) -{ - test_tile_nodes_ch(osrm::EngineConfig::Algorithm::CoreCH, false); -} void test_tile_nodes_mld(bool use_string_only_api) { From fb9d1cefcc334990f3a23e5aca40a75e0c82dd5a Mon Sep 17 00:00:00 2001 From: Siarhei Fedartsou Date: Thu, 30 May 2024 19:40:56 +0200 Subject: [PATCH 17/28] Get rid of boost::math::constants::* and M_PI in favor of std::numbers (#6916) --- CHANGELOG.md | 1 + CMakeLists.txt | 1 - .../engine/map_matching/bayes_classifier.hpp | 6 ++--- .../map_matching/hidden_markov_model.hpp | 4 +-- .../map_matching/matching_confidence.hpp | 2 +- include/util/cheap_ruler.hpp | 3 ++- include/util/coordinate_calculation.hpp | 14 +++------- include/util/trigonometry_table.hpp | 26 ++++++++----------- include/util/web_mercator.hpp | 8 +++--- .../intersection/intersection_analysis.cpp | 9 ++++--- .../intersection/mergable_road_detector.cpp | 4 +-- src/guidance/roundabout_handler.cpp | 2 +- src/util/coordinate_calculation.cpp | 3 +-- 13 files changed, 35 insertions(+), 48 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 965c2a3d5..d03f7a755 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ - NodeJS: - CHANGED: Use node-api instead of NAN. [#6452](https://github.com/Project-OSRM/osrm-backend/pull/6452) - Misc: + - CHANGED: Get rid of boost::math::constants::* and M_PI in favor of std::numbers. [#6916](https://github.com/Project-OSRM/osrm-backend/pull/6916) - CHANGED: Make constants in PackedVector constexpr. [#6917](https://github.com/Project-OSRM/osrm-backend/pull/6917) - CHANGED: Use std::variant instead of mapbox::util::variant. [#6903](https://github.com/Project-OSRM/osrm-backend/pull/6903) - CHANGED: Bump rapidjson to version f9d53419e912910fd8fa57d5705fa41425428c35 [#6906](https://github.com/Project-OSRM/osrm-backend/pull/6906) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1686da493..1876d6b85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -266,7 +266,6 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") add_dependency_defines(-DBOOST_LIB_DIAGNOSTIC) add_dependency_defines(-D_CRT_SECURE_NO_WARNINGS) add_dependency_defines(-DNOMINMAX) # avoid min and max macros that can break compilation - add_dependency_defines(-D_USE_MATH_DEFINES) #needed for M_PI with cmath.h add_dependency_defines(-D_WIN32_WINNT=0x0501) add_dependency_defines(-DXML_STATIC) find_library(ws2_32_LIBRARY_PATH ws2_32) diff --git a/include/engine/map_matching/bayes_classifier.hpp b/include/engine/map_matching/bayes_classifier.hpp index 5dd5813c3..7d89e3d64 100644 --- a/include/engine/map_matching/bayes_classifier.hpp +++ b/include/engine/map_matching/bayes_classifier.hpp @@ -6,7 +6,7 @@ #include #include -#include +#include namespace osrm::engine::map_matching { @@ -21,10 +21,8 @@ struct NormalDistribution // FIXME implement log-probability version since it's faster double Density(const double val) const { - using namespace boost::math::constants; - const double x = val - mean; - return 1.0 / (std::sqrt(two_pi()) * standard_deviation) * + return 1.0 / (std::sqrt(2 * std::numbers::pi) * standard_deviation) * std::exp(-x * x / (standard_deviation * standard_deviation)); } diff --git a/include/engine/map_matching/hidden_markov_model.hpp b/include/engine/map_matching/hidden_markov_model.hpp index 54505c8ec..f719b076e 100644 --- a/include/engine/map_matching/hidden_markov_model.hpp +++ b/include/engine/map_matching/hidden_markov_model.hpp @@ -4,7 +4,7 @@ #include "util/integer_range.hpp" #include -#include +#include #include @@ -14,7 +14,7 @@ namespace osrm::engine::map_matching { -static const double log_2_pi = std::log(2. * boost::math::constants::pi()); +static const double log_2_pi = std::log(2. * std::numbers::pi); static const double IMPOSSIBLE_LOG_PROB = -std::numeric_limits::infinity(); static const double MINIMAL_LOG_PROB = std::numeric_limits::lowest(); static const std::size_t INVALID_STATE = std::numeric_limits::max(); diff --git a/include/engine/map_matching/matching_confidence.hpp b/include/engine/map_matching/matching_confidence.hpp index 4b6b00a83..41d4a731d 100644 --- a/include/engine/map_matching/matching_confidence.hpp +++ b/include/engine/map_matching/matching_confidence.hpp @@ -2,7 +2,7 @@ #define ENGINE_MAP_MATCHING_CONFIDENCE_HPP #include "engine/map_matching/bayes_classifier.hpp" - +#include #include namespace osrm::engine::map_matching diff --git a/include/util/cheap_ruler.hpp b/include/util/cheap_ruler.hpp index 67df8d6f2..386a1785d 100644 --- a/include/util/cheap_ruler.hpp +++ b/include/util/cheap_ruler.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -37,7 +38,7 @@ class CheapRuler static constexpr double FE = 1.0 / 298.257223563; // flattening static constexpr double E2 = FE * (2 - FE); - static constexpr double RAD = M_PI / 180.0; + static constexpr double RAD = std::numbers::pi / 180.0; public: explicit CheapRuler(double latitude) diff --git a/include/util/coordinate_calculation.hpp b/include/util/coordinate_calculation.hpp index 1013555ed..48a5753e2 100644 --- a/include/util/coordinate_calculation.hpp +++ b/include/util/coordinate_calculation.hpp @@ -3,7 +3,7 @@ #include "util/coordinate.hpp" -#include +#include #include #include @@ -23,17 +23,9 @@ const constexpr double RAD_TO_DEGREE = 1. / DEGREE_TO_RAD; // The IUGG value for the equatorial radius is 6378.137 km (3963.19 miles) const constexpr long double EARTH_RADIUS = 6372797.560856; -inline double degToRad(const double degree) -{ - using namespace boost::math::constants; - return degree * (pi() / 180.0); -} +inline double degToRad(const double degree) { return degree * (std::numbers::pi / 180.0); } -inline double radToDeg(const double radian) -{ - using namespace boost::math::constants; - return radian * (180.0 * (1. / pi())); -} +inline double radToDeg(const double radian) { return radian * (180.0 * std::numbers::inv_pi); } } // namespace detail const constexpr static double METERS_PER_DEGREE_LAT = 110567.0; diff --git a/include/util/trigonometry_table.hpp b/include/util/trigonometry_table.hpp index 412674960..3186df831 100644 --- a/include/util/trigonometry_table.hpp +++ b/include/util/trigonometry_table.hpp @@ -6,7 +6,7 @@ #include -#include +#include namespace osrm::util { @@ -356,25 +356,21 @@ constexpr unsigned short atan_table[4096] = { 0xffe0, 0xffea, 0xfff4, 0xffff}; // max value is pi/4 -#ifdef _MSC_VER // TODO: remove as soon as boost allows C++14 features with Visual Studio -const constexpr double SCALING_FACTOR = 4. / M_PI * 0xFFFF; -#else -const constexpr double SCALING_FACTOR = 4. / boost::math::constants::pi() * 0xFFFF; -#endif +const constexpr double SCALING_FACTOR = 4. * std::numbers::inv_pi * 0xFFFF; inline double atan2_lookup(double y, double x) { - using namespace boost::math::constants; + static constexpr auto half_pi = std::numbers::pi * 0.5; if (std::abs(x) < std::numeric_limits::epsilon()) { if (y >= 0.) { - return half_pi(); + return half_pi; } else { - return -half_pi(); + return -half_pi; } } @@ -405,25 +401,25 @@ inline double atan2_lookup(double y, double x) case 0: break; case 1: - angle = pi() - angle; + angle = std::numbers::pi - angle; break; case 2: angle = -angle; break; case 3: - angle = -pi() + angle; + angle = -std::numbers::pi + angle; break; case 4: - angle = half_pi() - angle; + angle = half_pi - angle; break; case 5: - angle = half_pi() + angle; + angle = half_pi + angle; break; case 6: - angle = -half_pi() + angle; + angle = -half_pi + angle; break; case 7: - angle = -half_pi() - angle; + angle = -half_pi - angle; break; } return angle; diff --git a/include/util/web_mercator.hpp b/include/util/web_mercator.hpp index 0d428486b..9fcfa07c3 100644 --- a/include/util/web_mercator.hpp +++ b/include/util/web_mercator.hpp @@ -3,7 +3,7 @@ #include "util/coordinate.hpp" -#include +#include namespace osrm::util::web_mercator { @@ -14,7 +14,7 @@ const constexpr double RAD_TO_DEGREE = 1. / DEGREE_TO_RAD; // radius used by WGS84 const constexpr double EARTH_RADIUS_WGS84 = 6378137.0; // earth circumference devided by 2 -const constexpr double MAXEXTENT = EARTH_RADIUS_WGS84 * boost::math::constants::pi(); +const constexpr double MAXEXTENT = EARTH_RADIUS_WGS84 * std::numbers::pi; // ^ math functions are not constexpr since they have side-effects (setting errno) :( const constexpr double EPSG3857_MAX_LATITUDE = 85.051128779806592378; // 90(4*atan(exp(pi))/pi-1) const constexpr double MAX_LONGITUDE = 180.0; @@ -103,8 +103,8 @@ inline void pixelToDegree(const double shift, double &x, double &y) const double b = shift / 2.0; x = (x - b) / shift * 360.0; // FIXME needs to be simplified - const double g = (y - b) / -(shift / (2 * M_PI)) / detail::DEGREE_TO_RAD; - static_assert(detail::DEGREE_TO_RAD / (2 * M_PI) - 1 / 360. < 0.0001, ""); + const double g = (y - b) / -(shift * 0.5 * std::numbers::inv_pi) / detail::DEGREE_TO_RAD; + static_assert(detail::DEGREE_TO_RAD * 0.5 * std::numbers::inv_pi - 1 / 360. < 0.0001, ""); y = static_cast(yToLat(g)); } diff --git a/src/extractor/intersection/intersection_analysis.cpp b/src/extractor/intersection/intersection_analysis.cpp index 4ea3985ec..7ee296f11 100644 --- a/src/extractor/intersection/intersection_analysis.cpp +++ b/src/extractor/intersection/intersection_analysis.cpp @@ -6,6 +6,7 @@ #include "util/coordinate_calculation.hpp" #include +#include namespace osrm::extractor::intersection { @@ -79,11 +80,11 @@ namespace { double findAngleBisector(double alpha, double beta) { - alpha *= M_PI / 180.; - beta *= M_PI / 180.; + alpha *= std::numbers::pi / 180.; + beta *= std::numbers::pi / 180.; const auto average = - 180. * std::atan2(std::sin(alpha) + std::sin(beta), std::cos(alpha) + std::cos(beta)) / - M_PI; + 180. * std::atan2(std::sin(alpha) + std::sin(beta), std::cos(alpha) + std::cos(beta)) * + std::numbers::inv_pi; return std::fmod(average + 360., 360.); } diff --git a/src/extractor/intersection/mergable_road_detector.cpp b/src/extractor/intersection/mergable_road_detector.cpp index 449a15e9e..adfb601cb 100644 --- a/src/extractor/intersection/mergable_road_detector.cpp +++ b/src/extractor/intersection/mergable_road_detector.cpp @@ -351,7 +351,7 @@ bool MergableRoadDetector::IsCircularShape(const NodeID intersection_node, // ---- ---- // \ / // ------- - const auto constexpr CIRCULAR_POLYGON_ISOPERIMETRIC_LOWER_BOUND = 0.85 / (4 * M_PI); + const auto constexpr CIRCULAR_POLYGON_ISOPERIMETRIC_LOWER_BOUND = 0.85 / (4 * std::numbers::pi); if (connect_again && coordinates_to_the_left.front() == coordinates_to_the_left.back()) { // if the left and right roads connect again and are closed polygons ... const auto area = util::coordinate_calculation::computeArea(coordinates_to_the_left); @@ -359,7 +359,7 @@ bool MergableRoadDetector::IsCircularShape(const NodeID intersection_node, const auto area_to_squared_perimeter_ratio = std::abs(area) / (perimeter * perimeter); // then don't merge roads if A/L² is greater than the lower bound - BOOST_ASSERT(area_to_squared_perimeter_ratio <= 1. / (4 * M_PI)); + BOOST_ASSERT(area_to_squared_perimeter_ratio <= 1. / (4 * std::numbers::pi)); if (area_to_squared_perimeter_ratio >= CIRCULAR_POLYGON_ISOPERIMETRIC_LOWER_BOUND) return true; } diff --git a/src/guidance/roundabout_handler.cpp b/src/guidance/roundabout_handler.cpp index 82998a4e1..451ebf7bf 100644 --- a/src/guidance/roundabout_handler.cpp +++ b/src/guidance/roundabout_handler.cpp @@ -337,7 +337,7 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const return RoundaboutType::RoundaboutIntersection; } - const double radius = roundabout_length / (2 * M_PI); + const double radius = roundabout_length * 0.5 * std::numbers::inv_pi; // Looks like a rotary: large roundabout with dedicated name // do we have a dedicated name for the rotary, if not its a roundabout diff --git a/src/util/coordinate_calculation.cpp b/src/util/coordinate_calculation.cpp index 00c5efbf5..2e43d2abd 100644 --- a/src/util/coordinate_calculation.cpp +++ b/src/util/coordinate_calculation.cpp @@ -146,7 +146,6 @@ double bearing(const Coordinate coordinate_1, const Coordinate coordinate_2) double computeAngle(const Coordinate first, const Coordinate second, const Coordinate third) { - using namespace boost::math::constants; using namespace coordinate_calculation; if (first == second || second == third) @@ -163,7 +162,7 @@ double computeAngle(const Coordinate first, const Coordinate second, const Coord const double v2y = web_mercator::latToY(toFloating(third.lat)) - web_mercator::latToY(toFloating(second.lat)); - double angle = (atan2_lookup(v2y, v2x) - atan2_lookup(v1y, v1x)) * 180. / pi(); + double angle = (atan2_lookup(v2y, v2x) - atan2_lookup(v1y, v1x)) * 180. * std::numbers::inv_pi; while (angle < 0.) { From c8de759cd6919a6e08e8644e5e811492dea6adca Mon Sep 17 00:00:00 2001 From: Siarhei Fedartsou Date: Thu, 30 May 2024 21:55:25 +0200 Subject: [PATCH 18/28] Use std::string_view::starts_with instead of boost::starts_with (#6918) --- CHANGELOG.md | 1 + include/util/guidance/name_announcements.hpp | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d03f7a755..f6012273f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ - NodeJS: - CHANGED: Use node-api instead of NAN. [#6452](https://github.com/Project-OSRM/osrm-backend/pull/6452) - Misc: + - CHANGED: Use std::string_view::starts_with instead of boost::starts_with. [#6918](https://github.com/Project-OSRM/osrm-backend/pull/6918) - CHANGED: Get rid of boost::math::constants::* and M_PI in favor of std::numbers. [#6916](https://github.com/Project-OSRM/osrm-backend/pull/6916) - CHANGED: Make constants in PackedVector constexpr. [#6917](https://github.com/Project-OSRM/osrm-backend/pull/6917) - CHANGED: Use std::variant instead of mapbox::util::variant. [#6903](https://github.com/Project-OSRM/osrm-backend/pull/6903) diff --git a/include/util/guidance/name_announcements.hpp b/include/util/guidance/name_announcements.hpp index 7cdd3a76d..9be2bbfaf 100644 --- a/include/util/guidance/name_announcements.hpp +++ b/include/util/guidance/name_announcements.hpp @@ -8,8 +8,6 @@ #include "util/typedefs.hpp" -#include - #include #include #include @@ -126,8 +124,7 @@ inline bool requiresNameAnnounced(const StringView &from_name, // check similarity of names const auto names_are_empty = from_name.empty() && to_name.empty(); - const auto name_is_contained = - boost::starts_with(from_name, to_name) || boost::starts_with(to_name, from_name); + const auto name_is_contained = from_name.starts_with(to_name) || to_name.starts_with(from_name); const auto checkForPrefixOrSuffixChange = [](const std::string_view first, const std::string_view second, From 42fafdcdfebfdf08178907939f9cf785d05bb74d Mon Sep 17 00:00:00 2001 From: Siarhei Fedartsou Date: Fri, 31 May 2024 07:06:58 +0200 Subject: [PATCH 19/28] Use custom struct instead of std::pair in QueryHeap (#6921) --- CHANGELOG.md | 1 + include/util/query_heap.hpp | 29 +++++++++++++++++++++-------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6012273f..9a8c566d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ - NodeJS: - CHANGED: Use node-api instead of NAN. [#6452](https://github.com/Project-OSRM/osrm-backend/pull/6452) - Misc: + - CHANGED: Use custom struct instead of std::pair in QueryHeap. [#6921](https://github.com/Project-OSRM/osrm-backend/pull/6921) - CHANGED: Use std::string_view::starts_with instead of boost::starts_with. [#6918](https://github.com/Project-OSRM/osrm-backend/pull/6918) - CHANGED: Get rid of boost::math::constants::* and M_PI in favor of std::numbers. [#6916](https://github.com/Project-OSRM/osrm-backend/pull/6916) - CHANGED: Make constants in PackedVector constexpr. [#6917](https://github.com/Project-OSRM/osrm-backend/pull/6917) diff --git a/include/util/query_heap.hpp b/include/util/query_heap.hpp index c9f1fc6c5..9ded36c9e 100644 --- a/include/util/query_heap.hpp +++ b/include/util/query_heap.hpp @@ -193,7 +193,20 @@ template ; + struct HeapData + { + Weight weight; + Key index; + + bool operator>(const HeapData &other) const + { + if (weight == other.weight) + { + return index > other.index; + } + return weight > other.weight; + } + }; using HeapContainer = boost::heap::d_ary_heap, boost::heap::mutable_, @@ -232,7 +245,7 @@ class QueryHeap { BOOST_ASSERT(node < std::numeric_limits::max()); const auto index = static_cast(inserted_nodes.size()); - const auto handle = heap.push(std::make_pair(weight, index)); + const auto handle = heap.emplace(HeapData{weight, index}); inserted_nodes.emplace_back(HeapNode{handle, node, weight, data}); node_index[node] = index; } @@ -315,19 +328,19 @@ class QueryHeap NodeID Min() const { BOOST_ASSERT(!heap.empty()); - return inserted_nodes[heap.top().second].node; + return inserted_nodes[heap.top().index].node; } Weight MinKey() const { BOOST_ASSERT(!heap.empty()); - return heap.top().first; + return heap.top().weight; } NodeID DeleteMin() { BOOST_ASSERT(!heap.empty()); - const Key removedIndex = heap.top().second; + const Key removedIndex = heap.top().index; heap.pop(); inserted_nodes[removedIndex].handle = heap.s_handle_from_iterator(heap.end()); return inserted_nodes[removedIndex].node; @@ -336,7 +349,7 @@ class QueryHeap HeapNode &DeleteMinGetHeapNode() { BOOST_ASSERT(!heap.empty()); - const Key removedIndex = heap.top().second; + const Key removedIndex = heap.top().index; heap.pop(); inserted_nodes[removedIndex].handle = heap.s_handle_from_iterator(heap.end()); return inserted_nodes[removedIndex]; @@ -357,13 +370,13 @@ class QueryHeap const auto index = node_index.peek_index(node); auto &reference = inserted_nodes[index]; reference.weight = weight; - heap.increase(reference.handle, std::make_pair(weight, index)); + heap.increase(reference.handle, HeapData{weight, static_cast(index)}); } void DecreaseKey(const HeapNode &heapNode) { BOOST_ASSERT(!WasRemoved(heapNode.node)); - heap.increase(heapNode.handle, std::make_pair(heapNode.weight, (*heapNode.handle).second)); + heap.increase(heapNode.handle, HeapData{heapNode.weight, (*heapNode.handle).index}); } private: From 2725202771f079f0f04c5a55cc572410f72f0645 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Fri, 31 May 2024 19:58:57 +0200 Subject: [PATCH 20/28] Use Lemire's fast check whether to escape a JSON string (#6923) --- include/util/string_util.hpp | 39 ++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/include/util/string_util.hpp b/include/util/string_util.hpp index 874a1d284..4e6351137 100644 --- a/include/util/string_util.hpp +++ b/include/util/string_util.hpp @@ -1,8 +1,9 @@ #ifndef STRING_UTIL_HPP #define STRING_UTIL_HPP +#include #include - +#include #include #include #include @@ -10,26 +11,30 @@ namespace osrm::util { +// implements Lemire's table-based escape needs check +// cf. https://lemire.me/blog/2024/05/31/quickly-checking-whether-a-string-needs-escaping/ +inline static constexpr std::array json_quotable_character = []() constexpr +{ + std::array result{}; + for (auto i = 0; i < 32; i++) + { + result[i] = 1; + } + for (auto i : {'"', '\\'}) + { + result[i] = 1; + } + return result; +}(); + inline bool RequiresJSONStringEscaping(const std::string &string) { - for (const char letter : string) + uint8_t needs = 0; + for (uint8_t c : string) { - switch (letter) - { - case '\\': - case '"': - case '/': - case '\b': - case '\f': - case '\n': - case '\r': - case '\t': - return true; - default: - continue; - } + needs |= json_quotable_character[c]; } - return false; + return needs; } inline void EscapeJSONString(const std::string &input, std::string &output) From c7ee1a59eb2003a5cb96846ccf38378e52ecf25f Mon Sep 17 00:00:00 2001 From: Siarhei Fedartsou Date: Wed, 5 Jun 2024 21:39:10 +0200 Subject: [PATCH 21/28] Implement end to end benchmark (#6910) --- .github/workflows/osrm-backend.yml | 23 ++++- scripts/ci/download_gps_traces.py | 91 ++++++++++++++++++ scripts/ci/locustfile.py | 74 ++++++++++++++ scripts/ci/post_benchmark_results.py | 15 ++- .../ci/process_locust_benchmark_results.py | 31 ++++++ scripts/ci/run_benchmarks.sh | 37 ++++++- test/data/berlin_gps_traces.csv.gz | Bin 0 -> 530329 bytes 7 files changed, 261 insertions(+), 10 deletions(-) create mode 100644 scripts/ci/download_gps_traces.py create mode 100644 scripts/ci/locustfile.py create mode 100644 scripts/ci/process_locust_benchmark_results.py create mode 100644 test/data/berlin_gps_traces.csv.gz diff --git a/.github/workflows/osrm-backend.yml b/.github/workflows/osrm-backend.yml index 9a35cd04a..d8eff0581 100644 --- a/.github/workflows/osrm-backend.yml +++ b/.github/workflows/osrm-backend.yml @@ -377,12 +377,11 @@ jobs: key: v4-test-${{ matrix.name }}-${{ github.sha }} restore-keys: | v4-test-${{ matrix.name }}- - - name: Prepare environment run: | echo "CCACHE_DIR=$HOME/.ccache" >> $GITHUB_ENV mkdir -p $HOME/.ccache - + PACKAGE_JSON_VERSION=$(node -e "console.log(require('./package.json').version)") echo PUBLISH=$([[ "${GITHUB_REF:-}" == "refs/tags/v${PACKAGE_JSON_VERSION}" ]] && echo "On" || echo "Off") >> $GITHUB_ENV echo "OSRM_INSTALL_DIR=${GITHUB_WORKSPACE}/install-osrm" >> $GITHUB_ENV @@ -490,7 +489,7 @@ jobs: run: | echo "Using ${JOBS} jobs" pushd ${OSRM_BUILD_DIR} - + ccache --zero-stats cmake .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ -DENABLE_CONAN=${ENABLE_CONAN:-OFF} \ @@ -508,6 +507,7 @@ jobs: if [[ "${NODE_PACKAGE_TESTS_ONLY}" != "ON" ]]; then make tests --jobs=${JOBS} make benchmarks --jobs=${JOBS} + sudo make install if [[ "${RUNNER_OS}" == "Linux" ]]; then echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${OSRM_INSTALL_DIR}/lib" >> $GITHUB_ENV @@ -628,6 +628,13 @@ jobs: PR_NUMBER: ${{ github.event.pull_request.number }} GITHUB_REPOSITORY: ${{ github.repository }} steps: + - name: Enable data.osm.pbf cache + uses: actions/cache@v4 + with: + path: ~/data.osm.pbf + key: v1-data-osm-pbf + restore-keys: | + v1-data-osm-pbf - name: Enable compiler cache uses: actions/cache@v4 with: @@ -648,9 +655,15 @@ jobs: ref: ${{ github.head_ref }} path: pr - name: Install dependencies - run: | - python3 -m pip install "conan<2.0.0" "requests==2.31.0" + run: | + python3 -m pip install "conan<2.0.0" "requests==2.31.0" "locust==2.28.0" sudo apt-get update -y && sudo apt-get install ccache + - name: Prepare data + run: | + if [ ! -f "~/data.osm.pbf" ]; then + wget http://download.geofabrik.de/europe/germany/berlin-latest.osm.pbf -O ~/data.osm.pbf + fi + gunzip -c ./pr/test/data/berlin_gps_traces.csv.gz > ~/gps_traces.csv - name: Prepare environment run: | echo "CCACHE_DIR=$HOME/.ccache" >> $GITHUB_ENV diff --git a/scripts/ci/download_gps_traces.py b/scripts/ci/download_gps_traces.py new file mode 100644 index 000000000..961acd532 --- /dev/null +++ b/scripts/ci/download_gps_traces.py @@ -0,0 +1,91 @@ +import requests +import xml.etree.ElementTree as ET +import csv +import sys +import argparse + +def get_osm_gps_traces(min_lon, min_lat, max_lon, max_lat): + url = 'https://api.openstreetmap.org/api/0.6/trackpoints' + traces = [] + + lon_step = 0.25 + lat_step = 0.25 + + current_min_lon = min_lon + + while current_min_lon < max_lon: + current_max_lon = min(current_min_lon + lon_step, max_lon) + + current_min_lat = min_lat + while current_min_lat < max_lat: + current_max_lat = min(current_min_lat + lat_step, max_lat) + + bbox = f'{current_min_lon},{current_min_lat},{current_max_lon},{current_max_lat}' + print(f"Requesting bbox: {bbox}", file=sys.stderr) + + params = { + 'bbox': bbox, + 'page': 0 + } + headers = { + 'Accept': 'application/xml' + } + + response = requests.get(url, params=params, headers=headers) + if response.status_code == 200: + traces.append(response.content) + else: + print(f"Error fetching data for bbox {bbox}: {response.status_code} {response.text}", file=sys.stderr) + + current_min_lat += lat_step + current_min_lon += lon_step + + return traces + +def parse_gpx_data(gpx_data): + try: + root = ET.fromstring(gpx_data) + except ET.ParseError as e: + print(f"Error parsing GPX data: {e}", file=sys.stderr) + return [] + namespace = {'gpx': 'http://www.topografix.com/GPX/1/0'} + + tracks = [] + for trk in root.findall('.//gpx:trk', namespace): + track_data = [] + for trkseg in trk.findall('.//gpx:trkseg', namespace): + for trkpt in trkseg.findall('gpx:trkpt', namespace): + lat = trkpt.get('lat') + lon = trkpt.get('lon') + time = trkpt.find('time').text if trkpt.find('time') is not None else '' + track_data.append([lat, lon, time]) + tracks.append(track_data) + return tracks + +def save_to_csv(data, file): + writer = csv.writer(file) + writer.writerow(['TrackID', 'Latitude', 'Longitude', 'Time']) + writer.writerows(data) + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Fetch and output OSM GPS traces for a given bounding box.') + parser.add_argument('min_lon', type=float, help='Minimum longitude of the bounding box') + parser.add_argument('min_lat', type=float, help='Minimum latitude of the bounding box') + parser.add_argument('max_lon', type=float, help='Maximum longitude of the bounding box') + parser.add_argument('max_lat', type=float, help='Maximum latitude of the bounding box') + + args = parser.parse_args() + + gpx_data_traces = get_osm_gps_traces(args.min_lon, args.min_lat, args.max_lon, args.max_lat) + print(f"Collected {len(gpx_data_traces)} trace segments", file=sys.stderr) + + all_data = [] + track_id = 0 + for gpx_data in gpx_data_traces: + for track in parse_gpx_data(gpx_data): + for point in track: + all_data.append([track_id] + point) + track_id += 1 + + # Output all data to stdout + save_to_csv(all_data, sys.stdout) diff --git a/scripts/ci/locustfile.py b/scripts/ci/locustfile.py new file mode 100644 index 000000000..cd46aaba9 --- /dev/null +++ b/scripts/ci/locustfile.py @@ -0,0 +1,74 @@ +from locust import HttpUser, TaskSet, task, between +import csv +import random +from collections import defaultdict +import os + +class OSRMTasks(TaskSet): + def on_start(self): + random.seed(42) + + self.coordinates = [] + self.tracks = defaultdict(list) + + gps_traces_file_path = os.path.expanduser('~/gps_traces.csv') + with open(gps_traces_file_path, 'r') as file: + reader = csv.DictReader(file) + for row in reader: + coord = (float(row['Latitude']), float(row['Longitude'])) + self.coordinates.append(coord) + self.tracks[row['TrackID']].append(coord) + self.track_ids = list(self.tracks.keys()) + + @task + def get_route(self): + start = random.choice(self.coordinates) + end = random.choice(self.coordinates) + + start_coord = f"{start[1]:.6f},{start[0]:.6f}" + end_coord = f"{end[1]:.6f},{end[0]:.6f}" + + self.client.get(f"/route/v1/driving/{start_coord};{end_coord}?overview=full&steps=true", name="route") + + @task + def get_table(self): + num_coords = random.randint(3, 100) + selected_coords = random.sample(self.coordinates, num_coords) + coords_str = ";".join([f"{coord[1]:.6f},{coord[0]:.6f}" for coord in selected_coords]) + + self.client.get(f"/table/v1/driving/{coords_str}", name="table") + + @task + def get_match(self): + num_coords = random.randint(50, 100) + track_id = random.choice(self.track_ids) + track_coords = self.tracks[track_id][:num_coords] + coords_str = ";".join([f"{coord[1]:.6f},{coord[0]:.6f}" for coord in track_coords]) + radiues_str = ";".join([f"{random.randint(5, 20)}" for _ in range(len(track_coords))]) + + with self.client.get(f"/match/v1/driving/{coords_str}?steps=true&radiuses={radiues_str}", name="match", catch_response=True) as response: + if response.status_code == 400: + j = response.json() + # it is expected that some of requests will fail with such error: map matching fails sometimes + if j['code'] == 'NoSegment' or j['code'] == 'NoMatch': + response.success() + + @task + def get_nearest(self): + coord = random.choice(self.coordinates) + coord_str = f"{coord[1]:.6f},{coord[0]:.6f}" + + self.client.get(f"/nearest/v1/driving/{coord_str}", name="nearest") + + @task + def get_trip(self): + num_coords = random.randint(2, 10) + selected_coords = random.sample(self.coordinates, num_coords) + coords_str = ";".join([f"{coord[1]:.6f},{coord[0]:.6f}" for coord in selected_coords]) + + self.client.get(f"/trip/v1/driving/{coords_str}?steps=true", name="trip") + +class OSRMUser(HttpUser): + tasks = [OSRMTasks] + # random wait time between requests to not load server for 100% + wait_time = between(0.05, 0.5) diff --git a/scripts/ci/post_benchmark_results.py b/scripts/ci/post_benchmark_results.py index a5dc38aa5..339534a19 100644 --- a/scripts/ci/post_benchmark_results.py +++ b/scripts/ci/post_benchmark_results.py @@ -16,8 +16,10 @@ def create_markdown_table(results): rows = [] for result in results: name = result['name'] - base = result['base'].replace('\n', '
') - pr = result['pr'].replace('\n', '
') + base = result['base'] or '' + base = base.replace('\n', '
') + pr = result['pr'] or '' + pr = pr.replace('\n', '
') row = f"| {name} | {base} | {pr} |" rows.append(row) return f"{header}\n" + "\n".join(rows) @@ -75,7 +77,14 @@ def main(): pr_body = pr_details.get('body', '') or '' markdown_table = create_markdown_table(benchmark_results) - new_benchmark_section = f"\n## Benchmark Results\n{markdown_table}\n" + new_benchmark_section = f""" + +

Benchmark Results

+ +{markdown_table} +
+ +""" if re.search(r'.*', pr_body, re.DOTALL): updated_body = re.sub( diff --git a/scripts/ci/process_locust_benchmark_results.py b/scripts/ci/process_locust_benchmark_results.py new file mode 100644 index 000000000..ad71daf7f --- /dev/null +++ b/scripts/ci/process_locust_benchmark_results.py @@ -0,0 +1,31 @@ +import sys +import csv + +def main(locust_csv_base_name, suffix, output_folder): + with open(f"{locust_csv_base_name}_stats.csv", 'r') as file: + reader = csv.DictReader(file) + for row in reader: + name = row['Name'] + if name == 'Aggregated': continue + + statistics = f''' +requests: {row['Request Count']} +failures: {row['Failure Count']} +req/s: {float(row['Requests/s']):.3f}req/s +avg: {float(row['Average Response Time']):.3f}ms +50%: {row['50%']}ms +75%: {row['75%']}ms +95%: {row['95%']}ms +98%: {row['98%']}ms +99%: {row['99%']}ms +min: {float(row['Min Response Time']):.3f}ms +max: {float(row['Max Response Time']):.3f}ms +''' + with open(f"{output_folder}/e2e_{name}_{suffix}.bench", 'w') as f: + f.write(statistics) + +if __name__ == '__main__': + if len(sys.argv) != 4: + print(f"Usage: {sys.argv[0]} ") + sys.exit(1) + main(sys.argv[1], sys.argv[2], sys.argv[3]) \ No newline at end of file diff --git a/scripts/ci/run_benchmarks.sh b/scripts/ci/run_benchmarks.sh index 6aea4e089..51ced3d53 100755 --- a/scripts/ci/run_benchmarks.sh +++ b/scripts/ci/run_benchmarks.sh @@ -13,12 +13,45 @@ function run_benchmarks_for_folder { ./$BENCHMARKS_FOLDER/match-bench "./$FOLDER/test/data/mld/monaco.osrm" mld > "$RESULTS_FOLDER/match_mld.bench" ./$BENCHMARKS_FOLDER/match-bench "./$FOLDER/test/data/ch/monaco.osrm" ch > "$RESULTS_FOLDER/match_ch.bench" - ./$BENCHMARKS_FOLDER/route-bench "./$FOLDER/test/data/mld/monaco.osrm" mld > "$RESULTS_FOLDER/route_mld.bench" || true # TODO: remove `true` when this benchmark will be merged to master - ./$BENCHMARKS_FOLDER/route-bench "./$FOLDER/test/data/ch/monaco.osrm" ch > "$RESULTS_FOLDER/route_ch.bench" || true # TODO: remove `true` when this benchmark will be merged to master + ./$BENCHMARKS_FOLDER/route-bench "./$FOLDER/test/data/mld/monaco.osrm" mld > "$RESULTS_FOLDER/route_mld.bench" + ./$BENCHMARKS_FOLDER/route-bench "./$FOLDER/test/data/ch/monaco.osrm" ch > "$RESULTS_FOLDER/route_ch.bench" ./$BENCHMARKS_FOLDER/alias-bench > "$RESULTS_FOLDER/alias.bench" ./$BENCHMARKS_FOLDER/json-render-bench "./$FOLDER/src/benchmarks/portugal_to_korea.json" > "$RESULTS_FOLDER/json-render.bench" ./$BENCHMARKS_FOLDER/packedvector-bench > "$RESULTS_FOLDER/packedvector.bench" ./$BENCHMARKS_FOLDER/rtree-bench "./$FOLDER/test/data/monaco.osrm.ramIndex" "./$FOLDER/test/data/monaco.osrm.fileIndex" "./$FOLDER/test/data/monaco.osrm.nbg_nodes" > "$RESULTS_FOLDER/rtree.bench" + + BINARIES_FOLDER="$FOLDER/build" + + cp ~/data.osm.pbf $FOLDER + $BINARIES_FOLDER/osrm-extract -p $FOLDER/profiles/car.lua $FOLDER/data.osm.pbf + $BINARIES_FOLDER/osrm-partition $FOLDER/data.osrm + $BINARIES_FOLDER/osrm-customize $FOLDER/data.osrm + $BINARIES_FOLDER/osrm-contract $FOLDER/data.osrm + + if [ -f "$FOLDER/scripts/ci/locustfile.py" ]; then + for ALGORITHM in mld ch; do + $BINARIES_FOLDER/osrm-routed --algorithm $ALGORITHM $FOLDER/data.osrm & + OSRM_ROUTED_PID=$! + + # wait for osrm-routed to start + curl --retry-delay 3 --retry 10 --retry-all-errors "http://127.0.0.1:5000/route/v1/driving/13.388860,52.517037;13.385983,52.496891?steps=true" + locust -f $FOLDER/scripts/ci/locustfile.py \ + --headless \ + --processes -1 \ + --users 10 \ + --spawn-rate 1 \ + --host http://localhost:5000 \ + --run-time 1m \ + --csv=locust_results_$ALGORITHM \ + --loglevel ERROR + + python3 $FOLDER/scripts/ci/process_locust_benchmark_results.py locust_results_$ALGORITHM $ALGORITHM $RESULTS_FOLDER + + + kill -0 $OSRM_ROUTED_PID + done + fi + } run_benchmarks_for_folder $1 "${1}_results" diff --git a/test/data/berlin_gps_traces.csv.gz b/test/data/berlin_gps_traces.csv.gz new file mode 100644 index 0000000000000000000000000000000000000000..842d1cc82bd746e0fdcd46a5af50a68ad1367b6c GIT binary patch literal 530329 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�L?}XpETa`*^2TOlqy0TNUT#>C) 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

RZ4+Vfi}qj;4; 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

2&p`BC;G#$^&V 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 z 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

%*WTBv=p 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�Ci;vLmI7=)zr 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Ş!%w0X*v?^hT7> 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�+ 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 z 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�w9RB> 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