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 = 564 std::visit( 565 [&item, &unpackSpec, &res](auto&& val) { 566 using ContainedT = 567 std::remove_pointer_t<std::decay_t<decltype(val)>>; 568 return details::unpackValue<ContainedT>( 569 item.second, unpackSpec.key, res, *val); 570 }, 571 unpackSpec.value) && 572 result; 573 574 unpackSpec.complete = true; 575 break; 576 } 577 578 if (unpackIndex == toUnpack.size()) 579 { 580 messages::propertyUnknown(res, item.first); 581 result = false; 582 } 583 } 584 585 for (PerUnpack& perUnpack : toUnpack) 586 { 587 if (!perUnpack.complete) 588 { 589 bool isOptional = std::visit( 590 [](auto&& val) { 591 using ContainedType = 592 std::remove_pointer_t<std::decay_t<decltype(val)>>; 593 return details::IsOptional<ContainedType>::value; 594 }, 595 perUnpack.value); 596 if (isOptional) 597 { 598 continue; 599 } 600 messages::propertyMissing(res, perUnpack.key); 601 result = false; 602 } 603 } 604 return result; 605 } 606 607 inline bool readJsonHelper(nlohmann::json& jsonRequest, crow::Response& res, 608 std::span<PerUnpack> toUnpack) 609 { 610 nlohmann::json::object_t* obj = 611 jsonRequest.get_ptr<nlohmann::json::object_t*>(); 612 if (obj == nullptr) 613 { 614 BMCWEB_LOG_DEBUG("Json value is not an object"); 615 messages::unrecognizedRequestBody(res); 616 return false; 617 } 618 return readJsonHelperObject(*obj, res, toUnpack); 619 } 620 621 inline void packVariant(std::span<PerUnpack> /*toPack*/) {} 622 623 template <typename FirstType, typename... UnpackTypes> 624 void packVariant(std::span<PerUnpack> toPack, std::string_view key, 625 FirstType& first, UnpackTypes&&... in) 626 { 627 if (toPack.empty()) 628 { 629 return; 630 } 631 toPack[0].key = key; 632 toPack[0].value = &first; 633 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) 634 packVariant(toPack.subspan(1), std::forward<UnpackTypes&&>(in)...); 635 } 636 637 template <typename FirstType, typename... UnpackTypes> 638 bool readJsonObject(nlohmann::json::object_t& jsonRequest, crow::Response& res, 639 std::string_view key, FirstType&& first, 640 UnpackTypes&&... in) 641 { 642 const std::size_t n = sizeof...(UnpackTypes) + 2; 643 std::array<PerUnpack, n / 2> toUnpack2; 644 packVariant(toUnpack2, key, first, std::forward<UnpackTypes&&>(in)...); 645 return readJsonHelperObject(jsonRequest, res, toUnpack2); 646 } 647 648 template <typename FirstType, typename... UnpackTypes> 649 bool readJson(nlohmann::json& jsonRequest, crow::Response& res, 650 std::string_view key, FirstType&& first, UnpackTypes&&... in) 651 { 652 nlohmann::json::object_t* obj = 653 jsonRequest.get_ptr<nlohmann::json::object_t*>(); 654 if (obj == nullptr) 655 { 656 BMCWEB_LOG_DEBUG("Json value is not an object"); 657 messages::unrecognizedRequestBody(res); 658 return false; 659 } 660 return readJsonObject(*obj, res, key, std::forward<FirstType>(first), 661 std::forward<UnpackTypes&&>(in)...); 662 } 663 664 inline std::optional<nlohmann::json::object_t> 665 readJsonPatchHelper(const crow::Request& req, crow::Response& res) 666 { 667 nlohmann::json jsonRequest; 668 if (!json_util::processJsonFromRequest(res, req, jsonRequest)) 669 { 670 BMCWEB_LOG_DEBUG("Json value not readable"); 671 return std::nullopt; 672 } 673 nlohmann::json::object_t* object = 674 jsonRequest.get_ptr<nlohmann::json::object_t*>(); 675 if (object == nullptr || object->empty()) 676 { 677 BMCWEB_LOG_DEBUG("Json value is empty"); 678 messages::emptyJSON(res); 679 return std::nullopt; 680 } 681 std::erase_if(*object, 682 [](const std::pair<std::string, nlohmann::json>& item) { 683 return item.first.starts_with("@odata."); 684 }); 685 if (object->empty()) 686 { 687 // If the update request only contains OData annotations, the service 688 // should return the HTTP 400 Bad Request status code with the 689 // NoOperation message from the Base Message Registry, ... 690 messages::noOperation(res); 691 return std::nullopt; 692 } 693 694 return {std::move(*object)}; 695 } 696 697 template <typename... UnpackTypes> 698 bool readJsonPatch(const crow::Request& req, crow::Response& res, 699 std::string_view key, UnpackTypes&&... in) 700 { 701 std::optional<nlohmann::json::object_t> jsonRequest = 702 readJsonPatchHelper(req, res); 703 if (!jsonRequest) 704 { 705 return false; 706 } 707 if (jsonRequest->empty()) 708 { 709 messages::emptyJSON(res); 710 return false; 711 } 712 713 return readJsonObject(*jsonRequest, res, key, 714 std::forward<UnpackTypes&&>(in)...); 715 } 716 717 template <typename... UnpackTypes> 718 bool readJsonAction(const crow::Request& req, crow::Response& res, 719 const char* key, UnpackTypes&&... in) 720 { 721 nlohmann::json jsonRequest; 722 if (!json_util::processJsonFromRequest(res, req, jsonRequest)) 723 { 724 BMCWEB_LOG_DEBUG("Json value not readable"); 725 return false; 726 } 727 nlohmann::json::object_t* object = 728 jsonRequest.get_ptr<nlohmann::json::object_t*>(); 729 if (object == nullptr) 730 { 731 BMCWEB_LOG_DEBUG("Json value is empty"); 732 messages::emptyJSON(res); 733 return false; 734 } 735 return readJsonObject(*object, res, key, 736 std::forward<UnpackTypes&&>(in)...); 737 } 738 739 // Determines if two json objects are less, based on the presence of the 740 // @odata.id key 741 inline int odataObjectCmp(const nlohmann::json& a, const nlohmann::json& b) 742 { 743 using object_t = nlohmann::json::object_t; 744 const object_t* aObj = a.get_ptr<const object_t*>(); 745 const object_t* bObj = b.get_ptr<const object_t*>(); 746 747 if (aObj == nullptr) 748 { 749 if (bObj == nullptr) 750 { 751 return 0; 752 } 753 return -1; 754 } 755 if (bObj == nullptr) 756 { 757 return 1; 758 } 759 object_t::const_iterator aIt = aObj->find("@odata.id"); 760 object_t::const_iterator bIt = bObj->find("@odata.id"); 761 // If either object doesn't have the key, they get "sorted" to the end. 762 if (aIt == aObj->end()) 763 { 764 if (bIt == bObj->end()) 765 { 766 return 0; 767 } 768 return -1; 769 } 770 if (bIt == bObj->end()) 771 { 772 return 1; 773 } 774 const nlohmann::json::string_t* nameA = 775 aIt->second.get_ptr<const std::string*>(); 776 const nlohmann::json::string_t* nameB = 777 bIt->second.get_ptr<const std::string*>(); 778 // If either object doesn't have a string as the key, they get "sorted" to 779 // the end. 780 if (nameA == nullptr) 781 { 782 if (nameB == nullptr) 783 { 784 return 0; 785 } 786 return -1; 787 } 788 if (nameB == nullptr) 789 { 790 return 1; 791 } 792 boost::urls::url_view aUrl(*nameA); 793 boost::urls::url_view bUrl(*nameB); 794 auto segmentsAIt = aUrl.segments().begin(); 795 auto segmentsBIt = bUrl.segments().begin(); 796 797 while (true) 798 { 799 if (segmentsAIt == aUrl.segments().end()) 800 { 801 if (segmentsBIt == bUrl.segments().end()) 802 { 803 return 0; 804 } 805 return -1; 806 } 807 if (segmentsBIt == bUrl.segments().end()) 808 { 809 return 1; 810 } 811 int res = alphanumComp(*segmentsAIt, *segmentsBIt); 812 if (res != 0) 813 { 814 return res; 815 } 816 817 segmentsAIt++; 818 segmentsBIt++; 819 } 820 }; 821 822 struct ODataObjectLess 823 { 824 bool operator()(const nlohmann::json& left, 825 const nlohmann::json& right) const 826 { 827 return odataObjectCmp(left, right) < 0; 828 } 829 }; 830 831 // Sort the JSON array by |element[key]|. 832 // Elements without |key| or type of |element[key]| is not string are smaller 833 // those whose |element[key]| is string. 834 inline void sortJsonArrayByOData(nlohmann::json::array_t& array) 835 { 836 std::ranges::sort(array, ODataObjectLess()); 837 } 838 839 // Returns the estimated size of the JSON value 840 // The implementation walks through every key and every value, accumulates the 841 // total size of keys and values. 842 // Ideally, we should use a custom allocator that nlohmann JSON supports. 843 844 // Assumption made: 845 // 1. number: 8 characters 846 // 2. boolean: 5 characters (False) 847 // 3. string: len(str) + 2 characters (quote) 848 // 4. bytes: len(bytes) characters 849 // 5. null: 4 characters (null) 850 uint64_t getEstimatedJsonSize(const nlohmann::json& root); 851 852 } // namespace json_util 853 } // namespace redfish 854