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