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_forward_declare crow::Request 45 46 namespace redfish 47 { 48 49 namespace json_util 50 { 51 52 /** 53 * @brief Processes request to extract JSON from its body. If it fails, adds 54 * MalformedJSON message to response and ends it. 55 * 56 * @param[io] res Response object 57 * @param[in] req Request object 58 * @param[out] reqJson JSON object extracted from request's body 59 * 60 * @return true if JSON is valid, false when JSON is invalid and response has 61 * been filled with message and ended. 62 */ 63 bool processJsonFromRequest(crow::Response& res, const crow::Request& req, 64 nlohmann::json& reqJson); 65 namespace details 66 { 67 68 template <typename Type> 69 struct IsOptional : std::false_type 70 {}; 71 72 template <typename Type> 73 struct IsOptional<std::optional<Type>> : std::true_type 74 {}; 75 76 template <typename Type> 77 struct IsVector : std::false_type 78 {}; 79 80 template <typename Type> 81 struct IsVector<std::vector<Type>> : std::true_type 82 {}; 83 84 template <typename Type> 85 struct IsStdArray : std::false_type 86 {}; 87 88 template <typename Type, std::size_t size> 89 struct IsStdArray<std::array<Type, size>> : std::true_type 90 {}; 91 92 template <typename Type> 93 struct IsVariant : std::false_type 94 {}; 95 96 template <typename... Types> 97 struct IsVariant<std::variant<Types...>> : std::true_type 98 {}; 99 100 enum class UnpackErrorCode 101 { 102 success, 103 invalidType, 104 outOfRange 105 }; 106 107 template <typename ToType, typename FromType> 108 bool checkRange(const FromType& from [[maybe_unused]], 109 std::string_view key [[maybe_unused]]) 110 { 111 if constexpr (std::is_floating_point_v<ToType>) 112 { 113 if (std::isnan(from)) 114 { 115 BMCWEB_LOG_DEBUG("Value for key {} was NAN", key); 116 return false; 117 } 118 } 119 if constexpr (std::numeric_limits<ToType>::max() < 120 std::numeric_limits<FromType>::max()) 121 { 122 if (from > std::numeric_limits<ToType>::max()) 123 { 124 BMCWEB_LOG_DEBUG("Value for key {} was greater than max {}", key, 125 std::numeric_limits<FromType>::max()); 126 return false; 127 } 128 } 129 if constexpr (std::numeric_limits<ToType>::lowest() > 130 std::numeric_limits<FromType>::lowest()) 131 { 132 if (from < std::numeric_limits<ToType>::lowest()) 133 { 134 BMCWEB_LOG_DEBUG("Value for key {} was less than min {}", key, 135 std::numeric_limits<FromType>::lowest()); 136 return false; 137 } 138 } 139 140 return true; 141 } 142 143 template <typename Type> 144 UnpackErrorCode unpackValueWithErrorCode(nlohmann::json& jsonValue, 145 std::string_view key, Type& value); 146 147 template <std::size_t Index = 0, typename... Args> 148 UnpackErrorCode unpackValueVariant(nlohmann::json& j, std::string_view key, 149 std::variant<Args...>& v) 150 { 151 if constexpr (Index < std::variant_size_v<std::variant<Args...>>) 152 { 153 std::variant_alternative_t<Index, std::variant<Args...>> type{}; 154 UnpackErrorCode unpack = unpackValueWithErrorCode(j, key, type); 155 if (unpack == UnpackErrorCode::success) 156 { 157 v = std::move(type); 158 return unpack; 159 } 160 161 return unpackValueVariant<Index + 1, Args...>(j, key, v); 162 } 163 return UnpackErrorCode::invalidType; 164 } 165 166 template <typename Type> 167 UnpackErrorCode unpackValueWithErrorCode(nlohmann::json& jsonValue, 168 std::string_view key, Type& value) 169 { 170 UnpackErrorCode ret = UnpackErrorCode::success; 171 172 if constexpr (std::is_floating_point_v<Type>) 173 { 174 double helper = 0; 175 double* jsonPtr = jsonValue.get_ptr<double*>(); 176 177 if (jsonPtr == nullptr) 178 { 179 int64_t* intPtr = jsonValue.get_ptr<int64_t*>(); 180 if (intPtr != nullptr) 181 { 182 helper = static_cast<double>(*intPtr); 183 jsonPtr = &helper; 184 } 185 } 186 if (jsonPtr == nullptr) 187 { 188 return UnpackErrorCode::invalidType; 189 } 190 if (!checkRange<Type>(*jsonPtr, key)) 191 { 192 return UnpackErrorCode::outOfRange; 193 } 194 value = static_cast<Type>(*jsonPtr); 195 } 196 197 else if constexpr (std::is_signed_v<Type>) 198 { 199 int64_t* jsonPtr = jsonValue.get_ptr<int64_t*>(); 200 if (jsonPtr == nullptr) 201 { 202 return UnpackErrorCode::invalidType; 203 } 204 if (!checkRange<Type>(*jsonPtr, key)) 205 { 206 return UnpackErrorCode::outOfRange; 207 } 208 value = static_cast<Type>(*jsonPtr); 209 } 210 211 else if constexpr ((std::is_unsigned_v<Type>) && 212 (!std::is_same_v<bool, Type>)) 213 { 214 uint64_t* jsonPtr = jsonValue.get_ptr<uint64_t*>(); 215 if (jsonPtr == nullptr) 216 { 217 return UnpackErrorCode::invalidType; 218 } 219 if (!checkRange<Type>(*jsonPtr, key)) 220 { 221 return UnpackErrorCode::outOfRange; 222 } 223 value = static_cast<Type>(*jsonPtr); 224 } 225 226 else if constexpr (std::is_same_v<nlohmann::json, Type>) 227 { 228 value = std::move(jsonValue); 229 } 230 else if constexpr (std::is_same_v<std::nullptr_t, Type>) 231 { 232 if (!jsonValue.is_null()) 233 { 234 return UnpackErrorCode::invalidType; 235 } 236 } 237 else if constexpr (IsVector<Type>::value) 238 { 239 nlohmann::json::object_t* obj = 240 jsonValue.get_ptr<nlohmann::json::object_t*>(); 241 if (obj == nullptr) 242 { 243 return UnpackErrorCode::invalidType; 244 } 245 246 for (const auto& val : *obj) 247 { 248 value.emplace_back(); 249 ret = unpackValueWithErrorCode<typename Type::value_type>( 250 val, key, value.back()) && 251 ret; 252 } 253 } 254 else 255 { 256 using JsonType = std::add_const_t<std::add_pointer_t<Type>>; 257 JsonType jsonPtr = jsonValue.get_ptr<JsonType>(); 258 if (jsonPtr == nullptr) 259 { 260 BMCWEB_LOG_DEBUG("Value for key {} was incorrect type: {}", key, 261 jsonValue.type_name()); 262 return UnpackErrorCode::invalidType; 263 } 264 value = std::move(*jsonPtr); 265 } 266 return ret; 267 } 268 269 template <typename Type> 270 bool unpackValue(nlohmann::json& jsonValue, std::string_view key, 271 crow::Response& res, Type& value) 272 { 273 bool ret = true; 274 275 if constexpr (IsOptional<Type>::value) 276 { 277 value.emplace(); 278 ret = unpackValue<typename Type::value_type>(jsonValue, key, res, 279 *value) && 280 ret; 281 } 282 else if constexpr (IsStdArray<Type>::value) 283 { 284 nlohmann::json::array_t* arr = 285 jsonValue.get_ptr<nlohmann::json::array_t*>(); 286 if (arr == nullptr) 287 { 288 messages::propertyValueTypeError(res, res.jsonValue, key); 289 return false; 290 } 291 if (jsonValue.size() != value.size()) 292 { 293 messages::propertyValueTypeError(res, res.jsonValue, key); 294 return false; 295 } 296 size_t index = 0; 297 for (auto& val : *arr) 298 { 299 ret = unpackValue<typename Type::value_type>(val, key, res, 300 value[index++]) && 301 ret; 302 } 303 } 304 else if constexpr (IsVector<Type>::value) 305 { 306 nlohmann::json::array_t* arr = 307 jsonValue.get_ptr<nlohmann::json::array_t*>(); 308 if (arr == nullptr) 309 { 310 messages::propertyValueTypeError(res, res.jsonValue, key); 311 return false; 312 } 313 314 for (auto& val : *arr) 315 { 316 value.emplace_back(); 317 ret = unpackValue<typename Type::value_type>(val, key, res, 318 value.back()) && 319 ret; 320 } 321 } 322 else if constexpr (IsVariant<Type>::value) 323 { 324 UnpackErrorCode ec = unpackValueVariant(jsonValue, key, value); 325 if (ec != UnpackErrorCode::success) 326 { 327 if (ec == UnpackErrorCode::invalidType) 328 { 329 messages::propertyValueTypeError(res, jsonValue, key); 330 } 331 else if (ec == UnpackErrorCode::outOfRange) 332 { 333 messages::propertyValueNotInList(res, jsonValue, key); 334 } 335 return false; 336 } 337 } 338 else 339 { 340 UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value); 341 if (ec != UnpackErrorCode::success) 342 { 343 if (ec == UnpackErrorCode::invalidType) 344 { 345 messages::propertyValueTypeError(res, jsonValue, key); 346 } 347 else if (ec == UnpackErrorCode::outOfRange) 348 { 349 messages::propertyValueNotInList(res, jsonValue, key); 350 } 351 return false; 352 } 353 } 354 355 return ret; 356 } 357 358 template <typename Type> 359 bool unpackValue(nlohmann::json& jsonValue, std::string_view key, Type& value) 360 { 361 bool ret = true; 362 if constexpr (IsOptional<Type>::value) 363 { 364 value.emplace(); 365 ret = unpackValue<typename Type::value_type>(jsonValue, key, *value) && 366 ret; 367 } 368 else if constexpr (IsStdArray<Type>::value) 369 { 370 nlohmann::json::array_t* arr = 371 jsonValue.get_ptr<nlohmann::json::array_t*>(); 372 if (arr == nullptr) 373 { 374 return false; 375 } 376 if (jsonValue.size() != value.size()) 377 { 378 return false; 379 } 380 size_t index = 0; 381 for (const auto& val : *arr) 382 { 383 ret = unpackValue<typename Type::value_type>(val, key, 384 value[index++]) && 385 ret; 386 } 387 } 388 else if constexpr (IsVector<Type>::value) 389 { 390 nlohmann::json::array_t* arr = 391 jsonValue.get_ptr<nlohmann::json::array_t*>(); 392 if (arr == nullptr) 393 { 394 return false; 395 } 396 397 for (const auto& val : *arr) 398 { 399 value.emplace_back(); 400 ret = unpackValue<typename Type::value_type>(val, key, 401 value.back()) && 402 ret; 403 } 404 } 405 else 406 { 407 UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value); 408 if (ec != UnpackErrorCode::success) 409 { 410 return false; 411 } 412 } 413 414 return ret; 415 } 416 } // namespace details 417 418 // clang-format off 419 using UnpackVariant = std::variant< 420 uint8_t*, 421 uint16_t*, 422 int16_t*, 423 uint32_t*, 424 int32_t*, 425 uint64_t*, 426 int64_t*, 427 bool*, 428 double*, 429 std::string*, 430 nlohmann::json::object_t*, 431 std::variant<std::string, std::nullptr_t>*, 432 std::variant<uint8_t, std::nullptr_t>*, 433 std::variant<int16_t, std::nullptr_t>*, 434 std::variant<uint16_t, std::nullptr_t>*, 435 std::variant<int32_t, std::nullptr_t>*, 436 std::variant<uint32_t, std::nullptr_t>*, 437 std::variant<int64_t, std::nullptr_t>*, 438 std::variant<uint64_t, std::nullptr_t>*, 439 std::variant<double, std::nullptr_t>*, 440 std::variant<bool, std::nullptr_t>*, 441 std::vector<uint8_t>*, 442 std::vector<uint16_t>*, 443 std::vector<int16_t>*, 444 std::vector<uint32_t>*, 445 std::vector<int32_t>*, 446 std::vector<uint64_t>*, 447 std::vector<int64_t>*, 448 //std::vector<bool>*, 449 std::vector<double>*, 450 std::vector<std::string>*, 451 std::vector<nlohmann::json::object_t>*, 452 std::optional<uint8_t>*, 453 std::optional<uint16_t>*, 454 std::optional<int16_t>*, 455 std::optional<uint32_t>*, 456 std::optional<int32_t>*, 457 std::optional<uint64_t>*, 458 std::optional<int64_t>*, 459 std::optional<bool>*, 460 std::optional<double>*, 461 std::optional<std::string>*, 462 std::optional<nlohmann::json::object_t>*, 463 std::optional<std::vector<uint8_t>>*, 464 std::optional<std::vector<uint16_t>>*, 465 std::optional<std::vector<int16_t>>*, 466 std::optional<std::vector<uint32_t>>*, 467 std::optional<std::vector<int32_t>>*, 468 std::optional<std::vector<uint64_t>>*, 469 std::optional<std::vector<int64_t>>*, 470 //std::optional<std::vector<bool>>*, 471 std::optional<std::vector<double>>*, 472 std::optional<std::vector<std::string>>*, 473 std::optional<std::vector<nlohmann::json::object_t>>*, 474 std::optional<std::variant<std::string, std::nullptr_t>>*, 475 std::optional<std::variant<uint8_t, std::nullptr_t>>*, 476 std::optional<std::variant<int16_t, std::nullptr_t>>*, 477 std::optional<std::variant<uint16_t, std::nullptr_t>>*, 478 std::optional<std::variant<int32_t, std::nullptr_t>>*, 479 std::optional<std::variant<uint32_t, std::nullptr_t>>*, 480 std::optional<std::variant<int64_t, std::nullptr_t>>*, 481 std::optional<std::variant<uint64_t, std::nullptr_t>>*, 482 std::optional<std::variant<double, std::nullptr_t>>*, 483 std::optional<std::variant<bool, std::nullptr_t>>*, 484 std::optional<std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>>*, 485 std::optional<std::vector<std::variant<std::string, nlohmann::json::object_t, std::nullptr_t>>>*, 486 487 // Note, these types are kept for historical completeness, but should not be used, 488 // As they do not provide object type safety. Instead, rely on nlohmann::json::object_t 489 // Will be removed Q2 2025 490 nlohmann::json*, 491 std::optional<std::vector<nlohmann::json>>*, 492 std::vector<nlohmann::json>*, 493 std::optional<nlohmann::json>* 494 >; 495 // clang-format on 496 497 struct PerUnpack 498 { 499 std::string_view key; 500 UnpackVariant value; 501 bool complete = false; 502 }; 503 504 inline bool readJsonHelper(nlohmann::json& jsonRequest, crow::Response& res, 505 std::span<PerUnpack> toUnpack); 506 507 inline bool readJsonHelperObject(nlohmann::json::object_t& obj, 508 crow::Response& res, 509 std::span<PerUnpack> toUnpack) 510 { 511 bool result = true; 512 for (auto& item : obj) 513 { 514 size_t unpackIndex = 0; 515 for (; unpackIndex < toUnpack.size(); unpackIndex++) 516 { 517 PerUnpack& unpackSpec = toUnpack[unpackIndex]; 518 std::string_view key = unpackSpec.key; 519 size_t keysplitIndex = key.find('/'); 520 std::string_view leftover; 521 if (keysplitIndex != std::string_view::npos) 522 { 523 leftover = key.substr(keysplitIndex + 1); 524 key = key.substr(0, keysplitIndex); 525 } 526 527 if (key != item.first || unpackSpec.complete) 528 { 529 continue; 530 } 531 532 // Sublevel key 533 if (!leftover.empty()) 534 { 535 // Include the slash in the key so we can compare later 536 key = unpackSpec.key.substr(0, keysplitIndex + 1); 537 nlohmann::json j; 538 result = details::unpackValue<nlohmann::json>(item.second, key, 539 res, j) && 540 result; 541 if (!result) 542 { 543 return result; 544 } 545 546 std::vector<PerUnpack> nextLevel; 547 for (PerUnpack& p : toUnpack) 548 { 549 if (!p.key.starts_with(key)) 550 { 551 continue; 552 } 553 std::string_view thisLeftover = p.key.substr(key.size()); 554 nextLevel.push_back({thisLeftover, p.value, false}); 555 p.complete = true; 556 } 557 558 result = readJsonHelper(j, res, nextLevel) && result; 559 break; 560 } 561 562 result = 563 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, std::forward<FirstType>(first), 644 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