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 nlohmann::json::array_t* arr = 286 jsonValue.get_ptr<nlohmann::json::array_t*>(); 287 if (arr == nullptr) 288 { 289 messages::propertyValueTypeError(res, res.jsonValue, key); 290 return false; 291 } 292 if (jsonValue.size() != value.size()) 293 { 294 messages::propertyValueTypeError(res, res.jsonValue, key); 295 return false; 296 } 297 size_t index = 0; 298 for (auto& val : *arr) 299 { 300 ret = unpackValue<typename Type::value_type>(val, key, res, 301 value[index++]) && 302 ret; 303 } 304 } 305 else if constexpr (IsVector<Type>::value) 306 { 307 nlohmann::json::array_t* arr = 308 jsonValue.get_ptr<nlohmann::json::array_t*>(); 309 if (arr == nullptr) 310 { 311 messages::propertyValueTypeError(res, res.jsonValue, key); 312 return false; 313 } 314 315 for (auto& val : *arr) 316 { 317 value.emplace_back(); 318 ret = unpackValue<typename Type::value_type>(val, key, res, 319 value.back()) && 320 ret; 321 } 322 } 323 else if constexpr (IsVariant<Type>::value) 324 { 325 UnpackErrorCode ec = unpackValueVariant(jsonValue, key, value); 326 if (ec != UnpackErrorCode::success) 327 { 328 if (ec == UnpackErrorCode::invalidType) 329 { 330 messages::propertyValueTypeError(res, jsonValue, key); 331 } 332 else if (ec == UnpackErrorCode::outOfRange) 333 { 334 messages::propertyValueNotInList(res, jsonValue, key); 335 } 336 return false; 337 } 338 } 339 else 340 { 341 UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value); 342 if (ec != UnpackErrorCode::success) 343 { 344 if (ec == UnpackErrorCode::invalidType) 345 { 346 messages::propertyValueTypeError(res, jsonValue, key); 347 } 348 else if (ec == UnpackErrorCode::outOfRange) 349 { 350 messages::propertyValueNotInList(res, jsonValue, key); 351 } 352 return false; 353 } 354 } 355 356 return ret; 357 } 358 359 template <typename Type> 360 bool unpackValue(nlohmann::json& jsonValue, std::string_view key, Type& value) 361 { 362 bool ret = true; 363 if constexpr (IsOptional<Type>::value) 364 { 365 value.emplace(); 366 ret = unpackValue<typename Type::value_type>(jsonValue, key, *value) && 367 ret; 368 } 369 else if constexpr (IsStdArray<Type>::value) 370 { 371 nlohmann::json::array_t* arr = 372 jsonValue.get_ptr<nlohmann::json::array_t*>(); 373 if (arr == nullptr) 374 { 375 return false; 376 } 377 if (jsonValue.size() != value.size()) 378 { 379 return false; 380 } 381 size_t index = 0; 382 for (const auto& val : *arr) 383 { 384 ret = unpackValue<typename Type::value_type>(val, key, 385 value[index++]) && 386 ret; 387 } 388 } 389 else if constexpr (IsVector<Type>::value) 390 { 391 nlohmann::json::array_t* arr = 392 jsonValue.get_ptr<nlohmann::json::array_t*>(); 393 if (arr == nullptr) 394 { 395 return false; 396 } 397 398 for (const auto& val : *arr) 399 { 400 value.emplace_back(); 401 ret = unpackValue<typename Type::value_type>(val, key, 402 value.back()) && 403 ret; 404 } 405 } 406 else 407 { 408 UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value); 409 if (ec != UnpackErrorCode::success) 410 { 411 return false; 412 } 413 } 414 415 return ret; 416 } 417 } // namespace details 418 419 // clang-format off 420 using UnpackVariant = std::variant< 421 uint8_t*, 422 uint16_t*, 423 int16_t*, 424 uint32_t*, 425 int32_t*, 426 uint64_t*, 427 int64_t*, 428 bool*, 429 double*, 430 std::string*, 431 nlohmann::json::object_t*, 432 std::variant<std::string, std::nullptr_t>*, 433 std::variant<uint8_t, std::nullptr_t>*, 434 std::variant<int16_t, std::nullptr_t>*, 435 std::variant<uint16_t, std::nullptr_t>*, 436 std::variant<int32_t, std::nullptr_t>*, 437 std::variant<uint32_t, std::nullptr_t>*, 438 std::variant<int64_t, std::nullptr_t>*, 439 std::variant<uint64_t, std::nullptr_t>*, 440 std::variant<double, std::nullptr_t>*, 441 std::variant<bool, std::nullptr_t>*, 442 std::vector<uint8_t>*, 443 std::vector<uint16_t>*, 444 std::vector<int16_t>*, 445 std::vector<uint32_t>*, 446 std::vector<int32_t>*, 447 std::vector<uint64_t>*, 448 std::vector<int64_t>*, 449 //std::vector<bool>*, 450 std::vector<double>*, 451 std::vector<std::string>*, 452 std::vector<nlohmann::json::object_t>*, 453 std::optional<uint8_t>*, 454 std::optional<uint16_t>*, 455 std::optional<int16_t>*, 456 std::optional<uint32_t>*, 457 std::optional<int32_t>*, 458 std::optional<uint64_t>*, 459 std::optional<int64_t>*, 460 std::optional<bool>*, 461 std::optional<double>*, 462 std::optional<std::string>*, 463 std::optional<nlohmann::json::object_t>*, 464 std::optional<std::vector<uint8_t>>*, 465 std::optional<std::vector<uint16_t>>*, 466 std::optional<std::vector<int16_t>>*, 467 std::optional<std::vector<uint32_t>>*, 468 std::optional<std::vector<int32_t>>*, 469 std::optional<std::vector<uint64_t>>*, 470 std::optional<std::vector<int64_t>>*, 471 //std::optional<std::vector<bool>>*, 472 std::optional<std::vector<double>>*, 473 std::optional<std::vector<std::string>>*, 474 std::optional<std::vector<nlohmann::json::object_t>>*, 475 std::optional<std::variant<std::string, std::nullptr_t>>*, 476 std::optional<std::variant<uint8_t, std::nullptr_t>>*, 477 std::optional<std::variant<int16_t, std::nullptr_t>>*, 478 std::optional<std::variant<uint16_t, std::nullptr_t>>*, 479 std::optional<std::variant<int32_t, std::nullptr_t>>*, 480 std::optional<std::variant<uint32_t, std::nullptr_t>>*, 481 std::optional<std::variant<int64_t, std::nullptr_t>>*, 482 std::optional<std::variant<uint64_t, std::nullptr_t>>*, 483 std::optional<std::variant<double, std::nullptr_t>>*, 484 std::optional<std::variant<bool, std::nullptr_t>>*, 485 std::optional<std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>>*, 486 std::optional<std::vector<std::variant<std::string, nlohmann::json::object_t, std::nullptr_t>>>*, 487 488 // Note, these types are kept for historical completeness, but should not be used, 489 // As they do not provide object type safety. Instead, rely on nlohmann::json::object_t 490 // Will be removed Q2 2025 491 nlohmann::json*, 492 std::optional<std::vector<nlohmann::json>>*, 493 std::vector<nlohmann::json>*, 494 std::optional<nlohmann::json>* 495 >; 496 // clang-format on 497 498 struct PerUnpack 499 { 500 std::string_view key; 501 UnpackVariant value; 502 bool complete = false; 503 }; 504 505 inline bool readJsonHelper(nlohmann::json& jsonRequest, crow::Response& res, 506 std::span<PerUnpack> toUnpack); 507 508 inline bool readJsonHelperObject(nlohmann::json::object_t& obj, 509 crow::Response& res, 510 std::span<PerUnpack> toUnpack) 511 { 512 bool result = true; 513 for (auto& item : obj) 514 { 515 size_t unpackIndex = 0; 516 for (; unpackIndex < toUnpack.size(); unpackIndex++) 517 { 518 PerUnpack& unpackSpec = toUnpack[unpackIndex]; 519 std::string_view key = unpackSpec.key; 520 size_t keysplitIndex = key.find('/'); 521 std::string_view leftover; 522 if (keysplitIndex != std::string_view::npos) 523 { 524 leftover = key.substr(keysplitIndex + 1); 525 key = key.substr(0, keysplitIndex); 526 } 527 528 if (key != item.first || unpackSpec.complete) 529 { 530 continue; 531 } 532 533 // Sublevel key 534 if (!leftover.empty()) 535 { 536 // Include the slash in the key so we can compare later 537 key = unpackSpec.key.substr(0, keysplitIndex + 1); 538 nlohmann::json j; 539 result = details::unpackValue<nlohmann::json>(item.second, key, 540 res, j) && 541 result; 542 if (!result) 543 { 544 return result; 545 } 546 547 std::vector<PerUnpack> nextLevel; 548 for (PerUnpack& p : toUnpack) 549 { 550 if (!p.key.starts_with(key)) 551 { 552 continue; 553 } 554 std::string_view thisLeftover = p.key.substr(key.size()); 555 nextLevel.push_back({thisLeftover, p.value, false}); 556 p.complete = true; 557 } 558 559 result = readJsonHelper(j, res, nextLevel) && result; 560 break; 561 } 562 563 result = std::visit( 564 [&item, &unpackSpec, &res](auto&& val) { 565 using ContainedT = 566 std::remove_pointer_t<std::decay_t<decltype(val)>>; 567 return details::unpackValue<ContainedT>( 568 item.second, unpackSpec.key, res, *val); 569 }, 570 unpackSpec.value) && 571 result; 572 573 unpackSpec.complete = true; 574 break; 575 } 576 577 if (unpackIndex == toUnpack.size()) 578 { 579 messages::propertyUnknown(res, item.first); 580 result = false; 581 } 582 } 583 584 for (PerUnpack& perUnpack : toUnpack) 585 { 586 if (!perUnpack.complete) 587 { 588 bool isOptional = std::visit( 589 [](auto&& val) { 590 using ContainedType = 591 std::remove_pointer_t<std::decay_t<decltype(val)>>; 592 return details::IsOptional<ContainedType>::value; 593 }, 594 perUnpack.value); 595 if (isOptional) 596 { 597 continue; 598 } 599 messages::propertyMissing(res, perUnpack.key); 600 result = false; 601 } 602 } 603 return result; 604 } 605 606 inline bool readJsonHelper(nlohmann::json& jsonRequest, crow::Response& res, 607 std::span<PerUnpack> toUnpack) 608 { 609 nlohmann::json::object_t* obj = 610 jsonRequest.get_ptr<nlohmann::json::object_t*>(); 611 if (obj == nullptr) 612 { 613 BMCWEB_LOG_DEBUG("Json value is not an object"); 614 messages::unrecognizedRequestBody(res); 615 return false; 616 } 617 return readJsonHelperObject(*obj, res, toUnpack); 618 } 619 620 inline void packVariant(std::span<PerUnpack> /*toPack*/) {} 621 622 template <typename FirstType, typename... UnpackTypes> 623 void packVariant(std::span<PerUnpack> toPack, std::string_view key, 624 FirstType& first, UnpackTypes&&... in) 625 { 626 if (toPack.empty()) 627 { 628 return; 629 } 630 toPack[0].key = key; 631 toPack[0].value = &first; 632 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) 633 packVariant(toPack.subspan(1), std::forward<UnpackTypes&&>(in)...); 634 } 635 636 template <typename FirstType, typename... UnpackTypes> 637 bool readJsonObject(nlohmann::json::object_t& jsonRequest, crow::Response& res, 638 std::string_view key, FirstType&& first, 639 UnpackTypes&&... in) 640 { 641 const std::size_t n = sizeof...(UnpackTypes) + 2; 642 std::array<PerUnpack, n / 2> toUnpack2; 643 packVariant(toUnpack2, key, first, std::forward<UnpackTypes&&>(in)...); 644 return readJsonHelperObject(jsonRequest, res, toUnpack2); 645 } 646 647 template <typename FirstType, typename... UnpackTypes> 648 bool readJson(nlohmann::json& jsonRequest, crow::Response& res, 649 std::string_view key, FirstType&& first, UnpackTypes&&... in) 650 { 651 nlohmann::json::object_t* obj = 652 jsonRequest.get_ptr<nlohmann::json::object_t*>(); 653 if (obj == nullptr) 654 { 655 BMCWEB_LOG_DEBUG("Json value is not an object"); 656 messages::unrecognizedRequestBody(res); 657 return false; 658 } 659 return readJsonObject(*obj, res, key, std::forward<FirstType>(first), 660 std::forward<UnpackTypes&&>(in)...); 661 } 662 663 inline std::optional<nlohmann::json::object_t> 664 readJsonPatchHelper(const crow::Request& req, crow::Response& res) 665 { 666 nlohmann::json jsonRequest; 667 if (!json_util::processJsonFromRequest(res, req, jsonRequest)) 668 { 669 BMCWEB_LOG_DEBUG("Json value not readable"); 670 return std::nullopt; 671 } 672 nlohmann::json::object_t* object = 673 jsonRequest.get_ptr<nlohmann::json::object_t*>(); 674 if (object == nullptr || object->empty()) 675 { 676 BMCWEB_LOG_DEBUG("Json value is empty"); 677 messages::emptyJSON(res); 678 return std::nullopt; 679 } 680 std::erase_if(*object, 681 [](const std::pair<std::string, nlohmann::json>& item) { 682 return item.first.starts_with("@odata."); 683 }); 684 if (object->empty()) 685 { 686 // If the update request only contains OData annotations, the service 687 // should return the HTTP 400 Bad Request status code with the 688 // NoOperation message from the Base Message Registry, ... 689 messages::noOperation(res); 690 return std::nullopt; 691 } 692 693 return {std::move(*object)}; 694 } 695 696 template <typename... UnpackTypes> 697 bool readJsonPatch(const crow::Request& req, crow::Response& res, 698 std::string_view key, UnpackTypes&&... in) 699 { 700 std::optional<nlohmann::json::object_t> jsonRequest = 701 readJsonPatchHelper(req, res); 702 if (!jsonRequest) 703 { 704 return false; 705 } 706 if (jsonRequest->empty()) 707 { 708 messages::emptyJSON(res); 709 return false; 710 } 711 712 return readJsonObject(*jsonRequest, res, key, 713 std::forward<UnpackTypes&&>(in)...); 714 } 715 716 template <typename... UnpackTypes> 717 bool readJsonAction(const crow::Request& req, crow::Response& res, 718 const char* key, UnpackTypes&&... in) 719 { 720 nlohmann::json jsonRequest; 721 if (!json_util::processJsonFromRequest(res, req, jsonRequest)) 722 { 723 BMCWEB_LOG_DEBUG("Json value not readable"); 724 return false; 725 } 726 nlohmann::json::object_t* object = 727 jsonRequest.get_ptr<nlohmann::json::object_t*>(); 728 if (object == nullptr) 729 { 730 BMCWEB_LOG_DEBUG("Json value is empty"); 731 messages::emptyJSON(res); 732 return false; 733 } 734 return readJsonObject(*object, res, key, 735 std::forward<UnpackTypes&&>(in)...); 736 } 737 738 // Determines if two json objects are less, based on the presence of the 739 // @odata.id key 740 inline int odataObjectCmp(const nlohmann::json& a, const nlohmann::json& b) 741 { 742 using object_t = nlohmann::json::object_t; 743 const object_t* aObj = a.get_ptr<const object_t*>(); 744 const object_t* bObj = b.get_ptr<const object_t*>(); 745 746 if (aObj == nullptr) 747 { 748 if (bObj == nullptr) 749 { 750 return 0; 751 } 752 return -1; 753 } 754 if (bObj == nullptr) 755 { 756 return 1; 757 } 758 object_t::const_iterator aIt = aObj->find("@odata.id"); 759 object_t::const_iterator bIt = bObj->find("@odata.id"); 760 // If either object doesn't have the key, they get "sorted" to the end. 761 if (aIt == aObj->end()) 762 { 763 if (bIt == bObj->end()) 764 { 765 return 0; 766 } 767 return -1; 768 } 769 if (bIt == bObj->end()) 770 { 771 return 1; 772 } 773 const nlohmann::json::string_t* nameA = 774 aIt->second.get_ptr<const std::string*>(); 775 const nlohmann::json::string_t* nameB = 776 bIt->second.get_ptr<const std::string*>(); 777 // If either object doesn't have a string as the key, they get "sorted" to 778 // the end. 779 if (nameA == nullptr) 780 { 781 if (nameB == nullptr) 782 { 783 return 0; 784 } 785 return -1; 786 } 787 if (nameB == nullptr) 788 { 789 return 1; 790 } 791 boost::urls::url_view aUrl(*nameA); 792 boost::urls::url_view bUrl(*nameB); 793 auto segmentsAIt = aUrl.segments().begin(); 794 auto segmentsBIt = bUrl.segments().begin(); 795 796 while (true) 797 { 798 if (segmentsAIt == aUrl.segments().end()) 799 { 800 if (segmentsBIt == bUrl.segments().end()) 801 { 802 return 0; 803 } 804 return -1; 805 } 806 if (segmentsBIt == bUrl.segments().end()) 807 { 808 return 1; 809 } 810 int res = alphanumComp(*segmentsAIt, *segmentsBIt); 811 if (res != 0) 812 { 813 return res; 814 } 815 816 segmentsAIt++; 817 segmentsBIt++; 818 } 819 }; 820 821 struct ODataObjectLess 822 { 823 bool operator()(const nlohmann::json& left, 824 const nlohmann::json& right) const 825 { 826 return odataObjectCmp(left, right) < 0; 827 } 828 }; 829 830 // Sort the JSON array by |element[key]|. 831 // Elements without |key| or type of |element[key]| is not string are smaller 832 // those whose |element[key]| is string. 833 inline void sortJsonArrayByOData(nlohmann::json::array_t& array) 834 { 835 std::ranges::sort(array, ODataObjectLess()); 836 } 837 838 // Returns the estimated size of the JSON value 839 // The implementation walks through every key and every value, accumulates the 840 // total size of keys and values. 841 // Ideally, we should use a custom allocator that nlohmann JSON supports. 842 843 // Assumption made: 844 // 1. number: 8 characters 845 // 2. boolean: 5 characters (False) 846 // 3. string: len(str) + 2 characters (quote) 847 // 4. bytes: len(bytes) characters 848 // 5. null: 4 characters (null) 849 uint64_t getEstimatedJsonSize(const nlohmann::json& root); 850 851 } // namespace json_util 852 } // namespace redfish 853