1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 #pragma once 17 18 #include "error_messages.hpp" 19 #include "http_connection.hpp" 20 #include "http_request.hpp" 21 #include "http_response.hpp" 22 #include "human_sort.hpp" 23 #include "logging.hpp" 24 25 #include <nlohmann/json.hpp> 26 27 #include <algorithm> 28 #include <array> 29 #include <cmath> 30 #include <cstddef> 31 #include <cstdint> 32 #include <limits> 33 #include <map> 34 #include <optional> 35 #include <ranges> 36 #include <span> 37 #include <string> 38 #include <string_view> 39 #include <type_traits> 40 #include <utility> 41 #include <variant> 42 #include <vector> 43 44 // IWYU pragma: no_include <stdint.h> 45 // IWYU pragma: no_forward_declare crow::Request 46 47 namespace redfish 48 { 49 50 namespace json_util 51 { 52 53 /** 54 * @brief Processes request to extract JSON from its body. If it fails, adds 55 * MalformedJSON message to response and ends it. 56 * 57 * @param[io] res Response object 58 * @param[in] req Request object 59 * @param[out] reqJson JSON object extracted from request's body 60 * 61 * @return true if JSON is valid, false when JSON is invalid and response has 62 * been filled with message and ended. 63 */ 64 bool processJsonFromRequest(crow::Response& res, const crow::Request& req, 65 nlohmann::json& reqJson); 66 namespace details 67 { 68 69 template <typename Type> 70 struct IsOptional : std::false_type 71 {}; 72 73 template <typename Type> 74 struct IsOptional<std::optional<Type>> : std::true_type 75 {}; 76 77 template <typename Type> 78 struct IsVector : std::false_type 79 {}; 80 81 template <typename Type> 82 struct IsVector<std::vector<Type>> : std::true_type 83 {}; 84 85 template <typename Type> 86 struct IsStdArray : std::false_type 87 {}; 88 89 template <typename Type, std::size_t size> 90 struct IsStdArray<std::array<Type, size>> : std::true_type 91 {}; 92 93 template <typename Type> 94 struct IsVariant : std::false_type 95 {}; 96 97 template <typename... Types> 98 struct IsVariant<std::variant<Types...>> : std::true_type 99 {}; 100 101 enum class UnpackErrorCode 102 { 103 success, 104 invalidType, 105 outOfRange 106 }; 107 108 template <typename ToType, typename FromType> 109 bool checkRange(const FromType& from [[maybe_unused]], 110 std::string_view key [[maybe_unused]]) 111 { 112 if constexpr (std::is_floating_point_v<ToType>) 113 { 114 if (std::isnan(from)) 115 { 116 BMCWEB_LOG_DEBUG("Value for key {} was NAN", key); 117 return false; 118 } 119 } 120 if constexpr (std::numeric_limits<ToType>::max() < 121 std::numeric_limits<FromType>::max()) 122 { 123 if (from > std::numeric_limits<ToType>::max()) 124 { 125 BMCWEB_LOG_DEBUG("Value for key {} was greater than max {}", key, 126 std::numeric_limits<FromType>::max()); 127 return false; 128 } 129 } 130 if constexpr (std::numeric_limits<ToType>::lowest() > 131 std::numeric_limits<FromType>::lowest()) 132 { 133 if (from < std::numeric_limits<ToType>::lowest()) 134 { 135 BMCWEB_LOG_DEBUG("Value for key {} was less than min {}", key, 136 std::numeric_limits<FromType>::lowest()); 137 return false; 138 } 139 } 140 141 return true; 142 } 143 144 template <typename Type> 145 UnpackErrorCode unpackValueWithErrorCode(nlohmann::json& jsonValue, 146 std::string_view key, Type& value); 147 148 template <std::size_t Index = 0, typename... Args> 149 UnpackErrorCode unpackValueVariant(nlohmann::json& j, std::string_view key, 150 std::variant<Args...>& v) 151 { 152 if constexpr (Index < std::variant_size_v<std::variant<Args...>>) 153 { 154 std::variant_alternative_t<Index, std::variant<Args...>> type{}; 155 UnpackErrorCode unpack = unpackValueWithErrorCode(j, key, type); 156 if (unpack == UnpackErrorCode::success) 157 { 158 v = std::move(type); 159 return unpack; 160 } 161 162 return unpackValueVariant<Index + 1, Args...>(j, key, v); 163 } 164 return UnpackErrorCode::invalidType; 165 } 166 167 template <typename Type> 168 UnpackErrorCode unpackValueWithErrorCode(nlohmann::json& jsonValue, 169 std::string_view key, Type& value) 170 { 171 UnpackErrorCode ret = UnpackErrorCode::success; 172 173 if constexpr (std::is_floating_point_v<Type>) 174 { 175 double helper = 0; 176 double* jsonPtr = jsonValue.get_ptr<double*>(); 177 178 if (jsonPtr == nullptr) 179 { 180 int64_t* intPtr = jsonValue.get_ptr<int64_t*>(); 181 if (intPtr != nullptr) 182 { 183 helper = static_cast<double>(*intPtr); 184 jsonPtr = &helper; 185 } 186 } 187 if (jsonPtr == nullptr) 188 { 189 return UnpackErrorCode::invalidType; 190 } 191 if (!checkRange<Type>(*jsonPtr, key)) 192 { 193 return UnpackErrorCode::outOfRange; 194 } 195 value = static_cast<Type>(*jsonPtr); 196 } 197 198 else if constexpr (std::is_signed_v<Type>) 199 { 200 int64_t* jsonPtr = jsonValue.get_ptr<int64_t*>(); 201 if (jsonPtr == nullptr) 202 { 203 return UnpackErrorCode::invalidType; 204 } 205 if (!checkRange<Type>(*jsonPtr, key)) 206 { 207 return UnpackErrorCode::outOfRange; 208 } 209 value = static_cast<Type>(*jsonPtr); 210 } 211 212 else if constexpr ((std::is_unsigned_v<Type>)&&( 213 !std::is_same_v<bool, Type>)) 214 { 215 uint64_t* jsonPtr = jsonValue.get_ptr<uint64_t*>(); 216 if (jsonPtr == nullptr) 217 { 218 return UnpackErrorCode::invalidType; 219 } 220 if (!checkRange<Type>(*jsonPtr, key)) 221 { 222 return UnpackErrorCode::outOfRange; 223 } 224 value = static_cast<Type>(*jsonPtr); 225 } 226 227 else if constexpr (std::is_same_v<nlohmann::json, Type>) 228 { 229 value = std::move(jsonValue); 230 } 231 else if constexpr (std::is_same_v<std::nullptr_t, Type>) 232 { 233 if (!jsonValue.is_null()) 234 { 235 return UnpackErrorCode::invalidType; 236 } 237 } 238 else if constexpr (IsVector<Type>::value) 239 { 240 nlohmann::json::object_t* obj = 241 jsonValue.get_ptr<nlohmann::json::object_t*>(); 242 if (obj == nullptr) 243 { 244 return UnpackErrorCode::invalidType; 245 } 246 247 for (const auto& val : *obj) 248 { 249 value.emplace_back(); 250 ret = unpackValueWithErrorCode<typename Type::value_type>( 251 val, key, value.back()) && 252 ret; 253 } 254 } 255 else 256 { 257 using JsonType = std::add_const_t<std::add_pointer_t<Type>>; 258 JsonType jsonPtr = jsonValue.get_ptr<JsonType>(); 259 if (jsonPtr == nullptr) 260 { 261 BMCWEB_LOG_DEBUG("Value for key {} was incorrect type: {}", key, 262 jsonValue.type_name()); 263 return UnpackErrorCode::invalidType; 264 } 265 value = std::move(*jsonPtr); 266 } 267 return ret; 268 } 269 270 template <typename Type> 271 bool unpackValue(nlohmann::json& jsonValue, std::string_view key, 272 crow::Response& res, Type& value) 273 { 274 bool ret = true; 275 276 if constexpr (IsOptional<Type>::value) 277 { 278 value.emplace(); 279 ret = unpackValue<typename Type::value_type>(jsonValue, key, res, 280 *value) && 281 ret; 282 } 283 else if constexpr (IsStdArray<Type>::value) 284 { 285 if (!jsonValue.is_array()) 286 { 287 messages::propertyValueTypeError(res, res.jsonValue, key); 288 return false; 289 } 290 if (jsonValue.size() != value.size()) 291 { 292 messages::propertyValueTypeError(res, res.jsonValue, key); 293 return false; 294 } 295 size_t index = 0; 296 for (const auto& val : jsonValue.items()) 297 { 298 ret = unpackValue<typename Type::value_type>(val.value(), key, res, 299 value[index++]) && 300 ret; 301 } 302 } 303 else if constexpr (IsVector<Type>::value) 304 { 305 if (!jsonValue.is_array()) 306 { 307 messages::propertyValueTypeError(res, res.jsonValue, key); 308 return false; 309 } 310 311 for (const auto& val : jsonValue.items()) 312 { 313 value.emplace_back(); 314 ret = unpackValue<typename Type::value_type>(val.value(), key, res, 315 value.back()) && 316 ret; 317 } 318 } 319 else if constexpr (IsVariant<Type>::value) 320 { 321 UnpackErrorCode ec = unpackValueVariant(jsonValue, key, value); 322 if (ec != UnpackErrorCode::success) 323 { 324 if (ec == UnpackErrorCode::invalidType) 325 { 326 messages::propertyValueTypeError(res, jsonValue, key); 327 } 328 else if (ec == UnpackErrorCode::outOfRange) 329 { 330 messages::propertyValueNotInList(res, jsonValue, key); 331 } 332 return false; 333 } 334 } 335 else 336 { 337 UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value); 338 if (ec != UnpackErrorCode::success) 339 { 340 if (ec == UnpackErrorCode::invalidType) 341 { 342 messages::propertyValueTypeError(res, jsonValue, key); 343 } 344 else if (ec == UnpackErrorCode::outOfRange) 345 { 346 messages::propertyValueNotInList(res, jsonValue, key); 347 } 348 return false; 349 } 350 } 351 352 return ret; 353 } 354 355 template <typename Type> 356 bool unpackValue(nlohmann::json& jsonValue, std::string_view key, Type& value) 357 { 358 bool ret = true; 359 if constexpr (IsOptional<Type>::value) 360 { 361 value.emplace(); 362 ret = unpackValue<typename Type::value_type>(jsonValue, key, *value) && 363 ret; 364 } 365 else if constexpr (IsStdArray<Type>::value) 366 { 367 if (!jsonValue.is_array()) 368 { 369 return false; 370 } 371 if (jsonValue.size() != value.size()) 372 { 373 return false; 374 } 375 size_t index = 0; 376 for (const auto& val : jsonValue.items()) 377 { 378 ret = unpackValue<typename Type::value_type>(val.value(), key, 379 value[index++]) && 380 ret; 381 } 382 } 383 else if constexpr (IsVector<Type>::value) 384 { 385 if (!jsonValue.is_array()) 386 { 387 return false; 388 } 389 390 for (const auto& val : jsonValue.items()) 391 { 392 value.emplace_back(); 393 ret = unpackValue<typename Type::value_type>(val.value(), key, 394 value.back()) && 395 ret; 396 } 397 } 398 else 399 { 400 UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value); 401 if (ec != UnpackErrorCode::success) 402 { 403 return false; 404 } 405 } 406 407 return ret; 408 } 409 } // namespace details 410 411 // clang-format off 412 using UnpackVariant = std::variant< 413 uint8_t*, 414 uint16_t*, 415 int16_t*, 416 uint32_t*, 417 int32_t*, 418 uint64_t*, 419 int64_t*, 420 bool*, 421 double*, 422 std::string*, 423 nlohmann::json::object_t*, 424 std::variant<std::string, std::nullptr_t>*, 425 std::variant<uint8_t, std::nullptr_t>*, 426 std::variant<int16_t, std::nullptr_t>*, 427 std::variant<uint16_t, std::nullptr_t>*, 428 std::variant<int32_t, std::nullptr_t>*, 429 std::variant<uint32_t, std::nullptr_t>*, 430 std::variant<int64_t, std::nullptr_t>*, 431 std::variant<uint64_t, std::nullptr_t>*, 432 std::variant<double, std::nullptr_t>*, 433 std::variant<bool, std::nullptr_t>*, 434 std::vector<uint8_t>*, 435 std::vector<uint16_t>*, 436 std::vector<int16_t>*, 437 std::vector<uint32_t>*, 438 std::vector<int32_t>*, 439 std::vector<uint64_t>*, 440 std::vector<int64_t>*, 441 //std::vector<bool>*, 442 std::vector<double>*, 443 std::vector<std::string>*, 444 std::vector<nlohmann::json::object_t>*, 445 std::optional<uint8_t>*, 446 std::optional<uint16_t>*, 447 std::optional<int16_t>*, 448 std::optional<uint32_t>*, 449 std::optional<int32_t>*, 450 std::optional<uint64_t>*, 451 std::optional<int64_t>*, 452 std::optional<bool>*, 453 std::optional<double>*, 454 std::optional<std::string>*, 455 std::optional<nlohmann::json::object_t>*, 456 std::optional<std::vector<uint8_t>>*, 457 std::optional<std::vector<uint16_t>>*, 458 std::optional<std::vector<int16_t>>*, 459 std::optional<std::vector<uint32_t>>*, 460 std::optional<std::vector<int32_t>>*, 461 std::optional<std::vector<uint64_t>>*, 462 std::optional<std::vector<int64_t>>*, 463 //std::optional<std::vector<bool>>*, 464 std::optional<std::vector<double>>*, 465 std::optional<std::vector<std::string>>*, 466 std::optional<std::vector<nlohmann::json::object_t>>*, 467 std::optional<std::variant<std::string, std::nullptr_t>>*, 468 std::optional<std::variant<uint8_t, std::nullptr_t>>*, 469 std::optional<std::variant<int16_t, std::nullptr_t>>*, 470 std::optional<std::variant<uint16_t, std::nullptr_t>>*, 471 std::optional<std::variant<int32_t, std::nullptr_t>>*, 472 std::optional<std::variant<uint32_t, std::nullptr_t>>*, 473 std::optional<std::variant<int64_t, std::nullptr_t>>*, 474 std::optional<std::variant<uint64_t, std::nullptr_t>>*, 475 std::optional<std::variant<double, std::nullptr_t>>*, 476 std::optional<std::variant<bool, std::nullptr_t>>*, 477 std::optional<std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>>*, 478 std::optional<std::vector<std::variant<std::string, nlohmann::json::object_t, std::nullptr_t>>>*, 479 480 // Note, these types are kept for historical completeness, but should not be used, 481 // As they do not provide object type safety. Instead, rely on nlohmann::json::object_t 482 // Will be removed Q2 2025 483 nlohmann::json*, 484 std::optional<std::vector<nlohmann::json>>*, 485 std::vector<nlohmann::json>*, 486 std::optional<nlohmann::json>* 487 >; 488 // clang-format on 489 490 struct PerUnpack 491 { 492 std::string_view key; 493 UnpackVariant value; 494 bool complete = false; 495 }; 496 497 inline bool readJsonHelper(nlohmann::json& jsonRequest, crow::Response& res, 498 std::span<PerUnpack> toUnpack); 499 500 inline bool readJsonHelperObject(nlohmann::json::object_t& obj, 501 crow::Response& res, 502 std::span<PerUnpack> toUnpack) 503 { 504 bool result = true; 505 for (auto& item : obj) 506 { 507 size_t unpackIndex = 0; 508 for (; unpackIndex < toUnpack.size(); unpackIndex++) 509 { 510 PerUnpack& unpackSpec = toUnpack[unpackIndex]; 511 std::string_view key = unpackSpec.key; 512 size_t keysplitIndex = key.find('/'); 513 std::string_view leftover; 514 if (keysplitIndex != std::string_view::npos) 515 { 516 leftover = key.substr(keysplitIndex + 1); 517 key = key.substr(0, keysplitIndex); 518 } 519 520 if (key != item.first || unpackSpec.complete) 521 { 522 continue; 523 } 524 525 // Sublevel key 526 if (!leftover.empty()) 527 { 528 // Include the slash in the key so we can compare later 529 key = unpackSpec.key.substr(0, keysplitIndex + 1); 530 nlohmann::json j; 531 result = details::unpackValue<nlohmann::json>(item.second, key, 532 res, j) && 533 result; 534 if (!result) 535 { 536 return result; 537 } 538 539 std::vector<PerUnpack> nextLevel; 540 for (PerUnpack& p : toUnpack) 541 { 542 if (!p.key.starts_with(key)) 543 { 544 continue; 545 } 546 std::string_view thisLeftover = p.key.substr(key.size()); 547 nextLevel.push_back({thisLeftover, p.value, false}); 548 p.complete = true; 549 } 550 551 result = readJsonHelper(j, res, nextLevel) && result; 552 break; 553 } 554 555 result = std::visit( 556 [&item, &unpackSpec, &res](auto&& val) { 557 using ContainedT = 558 std::remove_pointer_t<std::decay_t<decltype(val)>>; 559 return details::unpackValue<ContainedT>( 560 item.second, unpackSpec.key, res, *val); 561 }, 562 unpackSpec.value) && 563 result; 564 565 unpackSpec.complete = true; 566 break; 567 } 568 569 if (unpackIndex == toUnpack.size()) 570 { 571 messages::propertyUnknown(res, item.first); 572 result = false; 573 } 574 } 575 576 for (PerUnpack& perUnpack : toUnpack) 577 { 578 if (!perUnpack.complete) 579 { 580 bool isOptional = std::visit( 581 [](auto&& val) { 582 using ContainedType = 583 std::remove_pointer_t<std::decay_t<decltype(val)>>; 584 return details::IsOptional<ContainedType>::value; 585 }, 586 perUnpack.value); 587 if (isOptional) 588 { 589 continue; 590 } 591 messages::propertyMissing(res, perUnpack.key); 592 result = false; 593 } 594 } 595 return result; 596 } 597 598 inline bool readJsonHelper(nlohmann::json& jsonRequest, crow::Response& res, 599 std::span<PerUnpack> toUnpack) 600 { 601 nlohmann::json::object_t* obj = 602 jsonRequest.get_ptr<nlohmann::json::object_t*>(); 603 if (obj == nullptr) 604 { 605 BMCWEB_LOG_DEBUG("Json value is not an object"); 606 messages::unrecognizedRequestBody(res); 607 return false; 608 } 609 return readJsonHelperObject(*obj, res, toUnpack); 610 } 611 612 inline void packVariant(std::span<PerUnpack> /*toPack*/) {} 613 614 template <typename FirstType, typename... UnpackTypes> 615 void packVariant(std::span<PerUnpack> toPack, std::string_view key, 616 FirstType& first, UnpackTypes&&... in) 617 { 618 if (toPack.empty()) 619 { 620 return; 621 } 622 toPack[0].key = key; 623 toPack[0].value = &first; 624 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) 625 packVariant(toPack.subspan(1), std::forward<UnpackTypes&&>(in)...); 626 } 627 628 template <typename FirstType, typename... UnpackTypes> 629 bool readJsonObject(nlohmann::json::object_t& jsonRequest, crow::Response& res, 630 std::string_view key, FirstType&& first, 631 UnpackTypes&&... in) 632 { 633 const std::size_t n = sizeof...(UnpackTypes) + 2; 634 std::array<PerUnpack, n / 2> toUnpack2; 635 packVariant(toUnpack2, key, first, std::forward<UnpackTypes&&>(in)...); 636 return readJsonHelperObject(jsonRequest, res, toUnpack2); 637 } 638 639 template <typename FirstType, typename... UnpackTypes> 640 bool readJson(nlohmann::json& jsonRequest, crow::Response& res, 641 std::string_view key, FirstType&& first, UnpackTypes&&... in) 642 { 643 nlohmann::json::object_t* obj = 644 jsonRequest.get_ptr<nlohmann::json::object_t*>(); 645 if (obj == nullptr) 646 { 647 BMCWEB_LOG_DEBUG("Json value is not an object"); 648 messages::unrecognizedRequestBody(res); 649 return false; 650 } 651 return readJsonObject(*obj, res, key, std::forward<FirstType>(first), 652 std::forward<UnpackTypes&&>(in)...); 653 } 654 655 inline std::optional<nlohmann::json::object_t> 656 readJsonPatchHelper(const crow::Request& req, crow::Response& res) 657 { 658 nlohmann::json jsonRequest; 659 if (!json_util::processJsonFromRequest(res, req, jsonRequest)) 660 { 661 BMCWEB_LOG_DEBUG("Json value not readable"); 662 return std::nullopt; 663 } 664 nlohmann::json::object_t* object = 665 jsonRequest.get_ptr<nlohmann::json::object_t*>(); 666 if (object == nullptr || object->empty()) 667 { 668 BMCWEB_LOG_DEBUG("Json value is empty"); 669 messages::emptyJSON(res); 670 return std::nullopt; 671 } 672 std::erase_if(*object, 673 [](const std::pair<std::string, nlohmann::json>& item) { 674 return item.first.starts_with("@odata."); 675 }); 676 if (object->empty()) 677 { 678 // If the update request only contains OData annotations, the service 679 // should return the HTTP 400 Bad Request status code with the 680 // NoOperation message from the Base Message Registry, ... 681 messages::noOperation(res); 682 return std::nullopt; 683 } 684 685 return {std::move(*object)}; 686 } 687 688 template <typename... UnpackTypes> 689 bool readJsonPatch(const crow::Request& req, crow::Response& res, 690 std::string_view key, UnpackTypes&&... in) 691 { 692 std::optional<nlohmann::json::object_t> jsonRequest = 693 readJsonPatchHelper(req, res); 694 if (!jsonRequest) 695 { 696 return false; 697 } 698 if (jsonRequest->empty()) 699 { 700 messages::emptyJSON(res); 701 return false; 702 } 703 704 return readJsonObject(*jsonRequest, res, key, 705 std::forward<UnpackTypes&&>(in)...); 706 } 707 708 template <typename... UnpackTypes> 709 bool readJsonAction(const crow::Request& req, crow::Response& res, 710 const char* key, UnpackTypes&&... in) 711 { 712 nlohmann::json jsonRequest; 713 if (!json_util::processJsonFromRequest(res, req, jsonRequest)) 714 { 715 BMCWEB_LOG_DEBUG("Json value not readable"); 716 return false; 717 } 718 nlohmann::json::object_t* object = 719 jsonRequest.get_ptr<nlohmann::json::object_t*>(); 720 if (object == nullptr) 721 { 722 BMCWEB_LOG_DEBUG("Json value is empty"); 723 messages::emptyJSON(res); 724 return false; 725 } 726 return readJsonObject(*object, res, key, 727 std::forward<UnpackTypes&&>(in)...); 728 } 729 730 // Determines if two json objects are less, based on the presence of the 731 // @odata.id key 732 inline int odataObjectCmp(const nlohmann::json& a, const nlohmann::json& b) 733 { 734 using object_t = nlohmann::json::object_t; 735 const object_t* aObj = a.get_ptr<const object_t*>(); 736 const object_t* bObj = b.get_ptr<const object_t*>(); 737 738 if (aObj == nullptr) 739 { 740 if (bObj == nullptr) 741 { 742 return 0; 743 } 744 return -1; 745 } 746 if (bObj == nullptr) 747 { 748 return 1; 749 } 750 object_t::const_iterator aIt = aObj->find("@odata.id"); 751 object_t::const_iterator bIt = bObj->find("@odata.id"); 752 // If either object doesn't have the key, they get "sorted" to the end. 753 if (aIt == aObj->end()) 754 { 755 if (bIt == bObj->end()) 756 { 757 return 0; 758 } 759 return -1; 760 } 761 if (bIt == bObj->end()) 762 { 763 return 1; 764 } 765 const nlohmann::json::string_t* nameA = 766 aIt->second.get_ptr<const std::string*>(); 767 const nlohmann::json::string_t* nameB = 768 bIt->second.get_ptr<const std::string*>(); 769 // If either object doesn't have a string as the key, they get "sorted" to 770 // the end. 771 if (nameA == nullptr) 772 { 773 if (nameB == nullptr) 774 { 775 return 0; 776 } 777 return -1; 778 } 779 if (nameB == nullptr) 780 { 781 return 1; 782 } 783 boost::urls::url_view aUrl(*nameA); 784 boost::urls::url_view bUrl(*nameB); 785 auto segmentsAIt = aUrl.segments().begin(); 786 auto segmentsBIt = bUrl.segments().begin(); 787 788 while (true) 789 { 790 if (segmentsAIt == aUrl.segments().end()) 791 { 792 if (segmentsBIt == bUrl.segments().end()) 793 { 794 return 0; 795 } 796 return -1; 797 } 798 if (segmentsBIt == bUrl.segments().end()) 799 { 800 return 1; 801 } 802 int res = alphanumComp(*segmentsAIt, *segmentsBIt); 803 if (res != 0) 804 { 805 return res; 806 } 807 808 segmentsAIt++; 809 segmentsBIt++; 810 } 811 }; 812 813 struct ODataObjectLess 814 { 815 bool operator()(const nlohmann::json& left, 816 const nlohmann::json& right) const 817 { 818 return odataObjectCmp(left, right) < 0; 819 } 820 }; 821 822 // Sort the JSON array by |element[key]|. 823 // Elements without |key| or type of |element[key]| is not string are smaller 824 // those whose |element[key]| is string. 825 inline void sortJsonArrayByOData(nlohmann::json::array_t& array) 826 { 827 std::ranges::sort(array, ODataObjectLess()); 828 } 829 830 // Returns the estimated size of the JSON value 831 // The implementation walks through every key and every value, accumulates the 832 // total size of keys and values. 833 // Ideally, we should use a custom allocator that nlohmann JSON supports. 834 835 // Assumption made: 836 // 1. number: 8 characters 837 // 2. boolean: 5 characters (False) 838 // 3. string: len(str) + 2 characters (quote) 839 // 4. bytes: len(bytes) characters 840 // 5. null: 4 characters (null) 841 uint64_t getEstimatedJsonSize(const nlohmann::json& root); 842 843 } // namespace json_util 844 } // namespace redfish 845