Mercurial > hg > orthanc
comparison OrthancFramework/Sources/Toolbox.cpp @ 5807:8279eaab0d1d attach-custom-data
merged default -> attach-custom-data
author | Alain Mazy <am@orthanc.team> |
---|---|
date | Tue, 24 Sep 2024 11:39:52 +0200 |
parents | 16df20505710 |
children |
comparison
equal
deleted
inserted
replaced
5085:79f98ee4f04b | 5807:8279eaab0d1d |
---|---|
1 /** | 1 /** |
2 * Orthanc - A Lightweight, RESTful DICOM Store | 2 * Orthanc - A Lightweight, RESTful DICOM Store |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | 3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics |
4 * Department, University Hospital of Liege, Belgium | 4 * Department, University Hospital of Liege, Belgium |
5 * Copyright (C) 2017-2022 Osimis S.A., Belgium | 5 * Copyright (C) 2017-2023 Osimis S.A., Belgium |
6 * Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium | 6 * Copyright (C) 2024-2024 Orthanc Team SRL, Belgium |
7 * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium | |
7 * | 8 * |
8 * This program is free software: you can redistribute it and/or | 9 * This program is free software: you can redistribute it and/or |
9 * modify it under the terms of the GNU Lesser General Public License | 10 * modify it under the terms of the GNU Lesser General Public License |
10 * as published by the Free Software Foundation, either version 3 of | 11 * as published by the Free Software Foundation, either version 3 of |
11 * the License, or (at your option) any later version. | 12 * the License, or (at your option) any later version. |
34 | 35 |
35 #if !defined(JSONCPP_VERSION_MAJOR) || !defined(JSONCPP_VERSION_MINOR) | 36 #if !defined(JSONCPP_VERSION_MAJOR) || !defined(JSONCPP_VERSION_MINOR) |
36 # error Cannot access the version of JsonCpp | 37 # error Cannot access the version of JsonCpp |
37 #endif | 38 #endif |
38 | 39 |
40 #if !defined(ORTHANC_ENABLE_ICU) | |
41 # define ORTHANC_ENABLE_ICU 1 | |
42 #endif | |
43 | |
39 | 44 |
40 /** | 45 /** |
41 * We use deprecated "Json::Reader", "Json::StyledWriter" and | 46 * We use deprecated "Json::Reader", "Json::StyledWriter" and |
42 * "Json::FastWriter" if JsonCpp < 1.7.0. This choice is rather | 47 * "Json::FastWriter" if JsonCpp < 1.7.0. This choice is rather |
43 * arbitrary, but if Json >= 1.9.0, gcc generates explicit deprecation | 48 * arbitrary, but if Json >= 1.9.0, gcc generates explicit deprecation |
141 #endif | 146 #endif |
142 } | 147 } |
143 | 148 |
144 | 149 |
145 #if defined(ORTHANC_STATIC_ICU) | 150 #if defined(ORTHANC_STATIC_ICU) |
151 | |
152 # if (ORTHANC_STATIC_ICU == 1) && (ORTHANC_ENABLE_ICU == 1) | |
153 # if !defined(ORTHANC_FRAMEWORK_INCLUDE_RESOURCES) || (ORTHANC_FRAMEWORK_INCLUDE_RESOURCES == 1) | |
154 # include <OrthancFrameworkResources.h> | |
155 # endif | |
156 # endif | |
157 | |
146 # if (ORTHANC_STATIC_ICU == 1 && ORTHANC_ENABLE_LOCALE == 1) | 158 # if (ORTHANC_STATIC_ICU == 1 && ORTHANC_ENABLE_LOCALE == 1) |
147 # include <OrthancFrameworkResources.h> | |
148 # include <unicode/udata.h> | 159 # include <unicode/udata.h> |
149 # include <unicode/uloc.h> | 160 # include <unicode/uloc.h> |
150 # include "Compression/GzipCompressor.h" | 161 # include "Compression/GzipCompressor.h" |
151 | 162 |
152 static std::string globalIcuData_; | 163 static std::string globalIcuData_; |
412 | 423 |
413 return r; | 424 return r; |
414 } | 425 } |
415 } | 426 } |
416 | 427 |
428 std::string Toolbox::JoinUri(const std::string& base, const std::string& uri) | |
429 { | |
430 if (uri.size() > 0 && base.size() > 0) | |
431 { | |
432 if (base[base.size() - 1] == '/' && uri[0] == '/') | |
433 { | |
434 return base + uri.substr(1, uri.size() - 1); | |
435 } | |
436 else if (base[base.size() - 1] != '/' && uri[0] != '/') | |
437 { | |
438 return base + "/" + uri; | |
439 } | |
440 } | |
441 | |
442 return base + uri; | |
443 } | |
444 | |
417 | 445 |
418 #if ORTHANC_ENABLE_MD5 == 1 | 446 #if ORTHANC_ENABLE_MD5 == 1 |
419 static char GetHexadecimalCharacter(uint8_t value) | 447 static char GetHexadecimalCharacter(uint8_t value) |
420 { | 448 { |
421 assert(value < 16); | 449 assert(value < 16); |
467 { | 495 { |
468 result[2 * i] = GetHexadecimalCharacter(static_cast<uint8_t>(actualHash[i] / 16)); | 496 result[2 * i] = GetHexadecimalCharacter(static_cast<uint8_t>(actualHash[i] / 16)); |
469 result[2 * i + 1] = GetHexadecimalCharacter(static_cast<uint8_t>(actualHash[i] % 16)); | 497 result[2 * i + 1] = GetHexadecimalCharacter(static_cast<uint8_t>(actualHash[i] % 16)); |
470 } | 498 } |
471 } | 499 } |
500 | |
501 void Toolbox::ComputeMD5(std::string& result, | |
502 const std::set<std::string>& data) | |
503 { | |
504 std::string s; | |
505 | |
506 for (std::set<std::string>::const_iterator it = data.begin(); it != data.end(); ++it) | |
507 { | |
508 s += *it; | |
509 } | |
510 | |
511 ComputeMD5(result, s); | |
512 } | |
513 | |
472 #endif | 514 #endif |
473 | 515 |
474 | 516 |
475 #if ORTHANC_ENABLE_BASE64 == 1 | 517 #if ORTHANC_ENABLE_BASE64 == 1 |
476 void Toolbox::EncodeBase64(std::string& result, | 518 void Toolbox::EncodeBase64(std::string& result, |
607 std::string Toolbox::ConvertToUtf8(const std::string& source, | 649 std::string Toolbox::ConvertToUtf8(const std::string& source, |
608 Encoding sourceEncoding, | 650 Encoding sourceEncoding, |
609 bool hasCodeExtensions) | 651 bool hasCodeExtensions) |
610 { | 652 { |
611 #if ORTHANC_STATIC_ICU == 1 | 653 #if ORTHANC_STATIC_ICU == 1 |
654 # if ORTHANC_ENABLE_ICU == 0 | |
655 throw OrthancException(ErrorCode_NotImplemented, "ICU is disabled for this target"); | |
656 # else | |
612 if (globalIcuData_.empty()) | 657 if (globalIcuData_.empty()) |
613 { | 658 { |
614 throw OrthancException(ErrorCode_BadSequenceOfCalls, | 659 throw OrthancException(ErrorCode_BadSequenceOfCalls, |
615 "Call Toolbox::InitializeGlobalLocale()"); | 660 "Call Toolbox::InitializeGlobalLocale()"); |
616 } | 661 } |
662 # endif | |
617 #endif | 663 #endif |
618 | 664 |
619 // The "::skip" flag makes boost skip invalid UTF-8 | 665 // The "::skip" flag makes boost skip invalid UTF-8 |
620 // characters. This can occur in badly-encoded DICOM files. | 666 // characters. This can occur in badly-encoded DICOM files. |
621 | 667 |
666 #if ORTHANC_ENABLE_LOCALE == 1 | 712 #if ORTHANC_ENABLE_LOCALE == 1 |
667 std::string Toolbox::ConvertFromUtf8(const std::string& source, | 713 std::string Toolbox::ConvertFromUtf8(const std::string& source, |
668 Encoding targetEncoding) | 714 Encoding targetEncoding) |
669 { | 715 { |
670 #if ORTHANC_STATIC_ICU == 1 | 716 #if ORTHANC_STATIC_ICU == 1 |
717 # if ORTHANC_ENABLE_ICU == 0 | |
718 throw OrthancException(ErrorCode_NotImplemented, "ICU is disabled for this target"); | |
719 # else | |
671 if (globalIcuData_.empty()) | 720 if (globalIcuData_.empty()) |
672 { | 721 { |
673 throw OrthancException(ErrorCode_BadSequenceOfCalls, | 722 throw OrthancException(ErrorCode_BadSequenceOfCalls, |
674 "Call Toolbox::InitializeGlobalLocale()"); | 723 "Call Toolbox::InitializeGlobalLocale()"); |
675 } | 724 } |
725 # endif | |
676 #endif | 726 #endif |
677 | 727 |
678 // The "::skip" flag makes boost skip invalid UTF-8 | 728 // The "::skip" flag makes boost skip invalid UTF-8 |
679 // characters. This can occur in badly-encoded DICOM files. | 729 // characters. This can occur in badly-encoded DICOM files. |
680 | 730 |
749 } | 799 } |
750 | 800 |
751 return result; | 801 return result; |
752 } | 802 } |
753 | 803 |
754 | |
755 void Toolbox::ComputeSHA1(std::string& result, | 804 void Toolbox::ComputeSHA1(std::string& result, |
756 const void* data, | 805 const void* data, |
757 size_t size) | 806 size_t size) |
758 { | 807 { |
759 boost::uuids::detail::sha1 sha1; | 808 boost::uuids::detail::sha1 sha1; |
762 { | 811 { |
763 sha1.process_bytes(data, size); | 812 sha1.process_bytes(data, size); |
764 } | 813 } |
765 | 814 |
766 unsigned int digest[5]; | 815 unsigned int digest[5]; |
767 | |
768 // Sanity check for the memory layout: A SHA-1 digest is 160 bits wide | 816 // Sanity check for the memory layout: A SHA-1 digest is 160 bits wide |
769 assert(sizeof(unsigned int) == 4 && sizeof(digest) == (160 / 8)); | 817 assert(sizeof(unsigned int) == 4 && sizeof(digest) == (160 / 8)); |
818 assert(sizeof(boost::uuids::detail::sha1::digest_type) == 20); | |
770 | 819 |
771 sha1.get_digest(digest); | 820 // From Boost 1.86, digest_type is "unsigned char[20]" while it was "unsigned int[5]"" in previous versions. |
821 // Always perform the cast even if it is useless for Boost < 1.86 | |
822 sha1.get_digest(*(reinterpret_cast<boost::uuids::detail::sha1::digest_type*>(digest))); | |
772 | 823 |
773 result.resize(8 * 5 + 4); | 824 result.resize(8 * 5 + 4); |
774 sprintf(&result[0], "%08x-%08x-%08x-%08x-%08x", | 825 sprintf(&result[0], "%08x-%08x-%08x-%08x-%08x", |
775 digest[0], | 826 digest[0], |
776 digest[1], | 827 digest[1], |
998 boost::replace_all(result, "*", ".*"); | 1049 boost::replace_all(result, "*", ".*"); |
999 | 1050 |
1000 return result; | 1051 return result; |
1001 } | 1052 } |
1002 | 1053 |
1054 static void TokenizeStringInternal(std::vector<std::string>& result, | |
1055 const std::string& value, | |
1056 char separator, | |
1057 bool includeEmptyStrings) | |
1058 { | |
1059 size_t countSeparators = 0; | |
1060 | |
1061 for (size_t i = 0; i < value.size(); i++) | |
1062 { | |
1063 if (value[i] == separator) | |
1064 { | |
1065 countSeparators++; | |
1066 } | |
1067 } | |
1068 | |
1069 result.clear(); | |
1070 result.reserve(countSeparators + 1); | |
1071 | |
1072 std::string currentItem; | |
1073 | |
1074 for (size_t i = 0; i < value.size(); i++) | |
1075 { | |
1076 if (value[i] == separator) | |
1077 { | |
1078 result.push_back(currentItem); | |
1079 currentItem.clear(); | |
1080 } | |
1081 else | |
1082 { | |
1083 currentItem.push_back(value[i]); | |
1084 } | |
1085 } | |
1086 | |
1087 if (includeEmptyStrings || !currentItem.empty()) | |
1088 { | |
1089 result.push_back(currentItem); | |
1090 } | |
1091 } | |
1092 | |
1003 | 1093 |
1004 void Toolbox::TokenizeString(std::vector<std::string>& result, | 1094 void Toolbox::TokenizeString(std::vector<std::string>& result, |
1005 const std::string& value, | 1095 const std::string& value, |
1006 char separator) | 1096 char separator) |
1007 { | 1097 { |
1008 size_t countSeparators = 0; | 1098 TokenizeStringInternal(result, value, separator, true); |
1009 | 1099 } |
1010 for (size_t i = 0; i < value.size(); i++) | 1100 |
1011 { | 1101 |
1012 if (value[i] == separator) | 1102 void Toolbox::SplitString(std::set<std::string>& result, |
1013 { | 1103 const std::string& value, |
1014 countSeparators++; | 1104 char separator) |
1015 } | 1105 { |
1016 } | |
1017 | |
1018 result.clear(); | 1106 result.clear(); |
1019 result.reserve(countSeparators + 1); | 1107 |
1020 | 1108 std::vector<std::string> temp; |
1021 std::string currentItem; | 1109 TokenizeStringInternal(temp, value, separator, false); |
1022 | 1110 for (size_t i = 0; i < temp.size(); ++i) |
1023 for (size_t i = 0; i < value.size(); i++) | 1111 { |
1024 { | 1112 result.insert(temp[i]); |
1025 if (value[i] == separator) | 1113 } |
1026 { | 1114 } |
1027 result.push_back(currentItem); | 1115 |
1028 currentItem.clear(); | 1116 |
1029 } | 1117 void Toolbox::SplitString(std::vector<std::string>& result, |
1030 else | 1118 const std::string& value, |
1031 { | 1119 char separator) |
1032 currentItem.push_back(value[i]); | 1120 { |
1033 } | 1121 TokenizeStringInternal(result, value, separator, false); |
1034 } | |
1035 | |
1036 result.push_back(currentItem); | |
1037 } | 1122 } |
1038 | 1123 |
1039 | 1124 |
1040 void Toolbox::JoinStrings(std::string& result, | 1125 void Toolbox::JoinStrings(std::string& result, |
1041 std::set<std::string>& source, | 1126 const std::set<std::string>& source, |
1042 const char* separator) | 1127 const char* separator) |
1043 { | 1128 { |
1044 result = boost::algorithm::join(source, separator); | 1129 result = boost::algorithm::join(source, separator); |
1045 } | 1130 } |
1046 | 1131 |
1047 void JoinStrings(std::string& result, | 1132 void Toolbox::JoinStrings(std::string& result, |
1048 std::vector<std::string>& source, | 1133 const std::vector<std::string>& source, |
1049 const char* separator) | 1134 const char* separator) |
1050 { | 1135 { |
1051 result = boost::algorithm::join(source, separator); | 1136 result = boost::algorithm::join(source, separator); |
1052 } | 1137 } |
1053 | 1138 |
1054 | 1139 |
1506 } | 1591 } |
1507 | 1592 |
1508 | 1593 |
1509 static void InitializeIcu() | 1594 static void InitializeIcu() |
1510 { | 1595 { |
1511 #if ORTHANC_STATIC_ICU == 1 | 1596 #if (ORTHANC_STATIC_ICU == 1) && (ORTHANC_ENABLE_ICU == 1) |
1512 if (globalIcuData_.empty()) | 1597 if (globalIcuData_.empty()) |
1513 { | 1598 { |
1514 LOG(INFO) << "Setting up the ICU common data"; | 1599 LOG(INFO) << "Setting up the ICU common data"; |
1515 | 1600 |
1516 GzipCompressor compressor; | 1601 GzipCompressor compressor; |
1674 std::string Toolbox::ToUpperCaseWithAccents(const std::string& source) | 1759 std::string Toolbox::ToUpperCaseWithAccents(const std::string& source) |
1675 { | 1760 { |
1676 bool error = (globalLocale_.get() == NULL); | 1761 bool error = (globalLocale_.get() == NULL); |
1677 | 1762 |
1678 #if ORTHANC_STATIC_ICU == 1 | 1763 #if ORTHANC_STATIC_ICU == 1 |
1764 # if ORTHANC_ENABLE_ICU == 0 | |
1765 throw OrthancException(ErrorCode_NotImplemented, "ICU is disabled for this target"); | |
1766 # else | |
1679 if (globalIcuData_.empty()) | 1767 if (globalIcuData_.empty()) |
1680 { | 1768 { |
1681 error = true; | 1769 error = true; |
1682 } | 1770 } |
1771 # endif | |
1683 #endif | 1772 #endif |
1684 | 1773 |
1685 if (error) | 1774 if (error) |
1686 { | 1775 { |
1687 throw OrthancException(ErrorCode_BadSequenceOfCalls, | 1776 throw OrthancException(ErrorCode_BadSequenceOfCalls, |
1795 | 1884 |
1796 | 1885 |
1797 | 1886 |
1798 std::string Toolbox::GenerateUuid() | 1887 std::string Toolbox::GenerateUuid() |
1799 { | 1888 { |
1800 #ifdef WIN32 | 1889 #ifdef _WIN32 |
1801 UUID uuid; | 1890 UUID uuid; |
1802 UuidCreate ( &uuid ); | 1891 UuidCreate ( &uuid ); |
1803 | 1892 |
1804 unsigned char * str; | 1893 unsigned char * str; |
1805 UuidToStringA ( &uuid, &str ); | 1894 UuidToStringA ( &uuid, &str ); |
2424 value[value.size() - 1] == '\"') | 2513 value[value.size() - 1] == '\"') |
2425 { | 2514 { |
2426 value = value.substr(1, value.size() - 2); | 2515 value = value.substr(1, value.size() - 2); |
2427 } | 2516 } |
2428 } | 2517 } |
2518 | |
2519 Toolbox::ElapsedTimer::ElapsedTimer() | |
2520 { | |
2521 Restart(); | |
2522 } | |
2523 | |
2524 void Toolbox::ElapsedTimer::Restart() | |
2525 { | |
2526 start_ = boost::posix_time::microsec_clock::universal_time(); | |
2527 } | |
2528 | |
2529 uint64_t Toolbox::ElapsedTimer::GetElapsedMilliseconds() | |
2530 { | |
2531 return GetElapsedNanoseconds() / 1000000; | |
2532 } | |
2533 | |
2534 uint64_t Toolbox::ElapsedTimer::GetElapsedMicroseconds() | |
2535 { | |
2536 return GetElapsedNanoseconds() / 1000; | |
2537 } | |
2538 | |
2539 uint64_t Toolbox::ElapsedTimer::GetElapsedNanoseconds() | |
2540 { | |
2541 boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time(); | |
2542 boost::posix_time::time_duration diff = now - start_; | |
2543 return static_cast<uint64_t>(diff.total_nanoseconds()); | |
2544 } | |
2545 | |
2546 std::string Toolbox::ElapsedTimer::GetHumanElapsedDuration() | |
2547 { | |
2548 return Toolbox::GetHumanDuration(GetElapsedNanoseconds()); | |
2549 } | |
2550 | |
2551 // in "full" mode, returns " 26.45MB in 2.25s = 94.04Mbps" | |
2552 // else, returns "94.04Mbps" | |
2553 std::string Toolbox::ElapsedTimer::GetHumanTransferSpeed(bool full, uint64_t sizeInBytes) | |
2554 { | |
2555 return Toolbox::GetHumanTransferSpeed(full, sizeInBytes, GetElapsedNanoseconds()); | |
2556 } | |
2557 | |
2558 Toolbox::ElapsedTimeLogger::ElapsedTimeLogger(const std::string& message) | |
2559 : message_(message), | |
2560 logged_(false) | |
2561 { | |
2562 Restart(); | |
2563 } | |
2564 | |
2565 Toolbox::ElapsedTimeLogger::~ElapsedTimeLogger() | |
2566 { | |
2567 if (!logged_) | |
2568 { | |
2569 StopAndLog(); | |
2570 } | |
2571 } | |
2572 | |
2573 void Toolbox::ElapsedTimeLogger::Restart() | |
2574 { | |
2575 timer_.Restart(); | |
2576 } | |
2577 | |
2578 void Toolbox::ElapsedTimeLogger::StopAndLog() | |
2579 { | |
2580 LOG(WARNING) << "ELAPSED TIMER: " << message_ << " (" << timer_.GetElapsedMicroseconds() << " us)"; | |
2581 logged_ = true; | |
2582 } | |
2583 | |
2584 std::string Toolbox::GetHumanFileSize(uint64_t sizeInBytes) | |
2585 { | |
2586 if (sizeInBytes < 1024) | |
2587 { | |
2588 std::ostringstream oss; | |
2589 oss << sizeInBytes << "bytes"; | |
2590 return oss.str(); | |
2591 } | |
2592 else | |
2593 { | |
2594 static const char* suffixes[] = {"KB", "MB", "GB", "TB"}; | |
2595 static const int suffixesCount = sizeof(suffixes) / sizeof(suffixes[0]); | |
2596 | |
2597 int i = 0; | |
2598 double size = static_cast<double>(sizeInBytes)/1024.0; | |
2599 | |
2600 while (size >= 1024.0 && i < suffixesCount - 1) | |
2601 { | |
2602 size /= 1024.0; | |
2603 i++; | |
2604 } | |
2605 | |
2606 std::ostringstream oss; | |
2607 oss << std::fixed << std::setprecision(2) << size << suffixes[i]; | |
2608 return oss.str(); | |
2609 } | |
2610 } | |
2611 | |
2612 std::string Toolbox::GetHumanDuration(uint64_t durationInNanoseconds) | |
2613 { | |
2614 if (durationInNanoseconds < 1024) | |
2615 { | |
2616 std::ostringstream oss; | |
2617 oss << durationInNanoseconds << "ns"; | |
2618 return oss.str(); | |
2619 } | |
2620 else | |
2621 { | |
2622 static const char* suffixes[] = {"ns", "us", "ms", "s"}; | |
2623 static const int suffixesCount = sizeof(suffixes) / sizeof(suffixes[0]); | |
2624 | |
2625 int i = 0; | |
2626 double duration = static_cast<double>(durationInNanoseconds); | |
2627 | |
2628 while (duration >= 1000.0 && i < suffixesCount - 1) | |
2629 { | |
2630 duration /= 1000.0; | |
2631 i++; | |
2632 } | |
2633 | |
2634 std::ostringstream oss; | |
2635 oss << std::fixed << std::setprecision(2) << duration << suffixes[i]; | |
2636 return oss.str(); | |
2637 } | |
2638 } | |
2639 | |
2640 std::string Toolbox::GetHumanTransferSpeed(bool full, uint64_t sizeInBytes, uint64_t durationInNanoseconds) | |
2641 { | |
2642 // in "full" mode, returns " 26.45MB in 2.25s = 94.04Mbps" | |
2643 // else, return "94.04Mbps" | |
2644 | |
2645 if (full) | |
2646 { | |
2647 std::ostringstream oss; | |
2648 oss << Toolbox::GetHumanFileSize(sizeInBytes) << " in " << Toolbox::GetHumanDuration(durationInNanoseconds) << " = " << GetHumanTransferSpeed(false, sizeInBytes, durationInNanoseconds); | |
2649 return oss.str(); | |
2650 } | |
2651 | |
2652 double throughputInBps = 8.0 * 1000000000.0 * static_cast<double>(sizeInBytes) / static_cast<double>(durationInNanoseconds); | |
2653 | |
2654 if (throughputInBps < 1000.0) | |
2655 { | |
2656 std::ostringstream oss; | |
2657 oss << throughputInBps << "bps"; | |
2658 return oss.str(); | |
2659 } | |
2660 else | |
2661 { | |
2662 throughputInBps /= 1000.0; | |
2663 static const char* suffixes[] = {"kbps", "Mbps", "Gbps"}; | |
2664 static const int suffixesCount = sizeof(suffixes) / sizeof(suffixes[0]); | |
2665 | |
2666 int i = 0; | |
2667 | |
2668 while (throughputInBps >= 1000.0 && i < suffixesCount - 1) | |
2669 { | |
2670 throughputInBps /= 1000.0; | |
2671 i++; | |
2672 } | |
2673 | |
2674 std::ostringstream oss; | |
2675 oss << std::fixed << std::setprecision(2) << throughputInBps << suffixes[i]; | |
2676 return oss.str(); | |
2677 } | |
2678 } | |
2679 | |
2680 | |
2681 bool Toolbox::ParseVersion(unsigned int& major, | |
2682 unsigned int& minor, | |
2683 unsigned int& revision, | |
2684 const char* version) | |
2685 { | |
2686 if (version == NULL) | |
2687 { | |
2688 throw OrthancException(ErrorCode_NullPointer); | |
2689 } | |
2690 | |
2691 #ifdef _MSC_VER | |
2692 #define ORTHANC_SCANF sscanf_s | |
2693 #else | |
2694 #define ORTHANC_SCANF sscanf | |
2695 #endif | |
2696 | |
2697 int a, b, c; | |
2698 if (ORTHANC_SCANF(version, "%4d.%4d.%4d", &a, &b, &c) == 3) | |
2699 { | |
2700 if (a >= 0 && | |
2701 b >= 0 && | |
2702 c >= 0) | |
2703 { | |
2704 major = static_cast<unsigned int>(a); | |
2705 minor = static_cast<unsigned int>(b); | |
2706 revision = static_cast<unsigned int>(c); | |
2707 return true; | |
2708 } | |
2709 else | |
2710 { | |
2711 return false; | |
2712 } | |
2713 } | |
2714 else if (ORTHANC_SCANF(version, "%4d.%4d", &a, &b) == 2) | |
2715 { | |
2716 if (a >= 0 && | |
2717 b >= 0) | |
2718 { | |
2719 major = static_cast<unsigned int>(a); | |
2720 minor = static_cast<unsigned int>(b); | |
2721 revision = 0; | |
2722 return true; | |
2723 } | |
2724 else | |
2725 { | |
2726 return false; | |
2727 } | |
2728 } | |
2729 else if (ORTHANC_SCANF(version, "%4d", &a) == 1 && | |
2730 a >= 0) | |
2731 { | |
2732 if (a >= 0) | |
2733 { | |
2734 major = static_cast<unsigned int>(a); | |
2735 minor = 0; | |
2736 revision = 0; | |
2737 return true; | |
2738 } | |
2739 else | |
2740 { | |
2741 return false; | |
2742 } | |
2743 } | |
2744 else | |
2745 { | |
2746 return false; | |
2747 } | |
2748 } | |
2749 | |
2750 | |
2751 bool Toolbox::IsVersionAbove(const char* version, | |
2752 unsigned int major, | |
2753 unsigned int minor, | |
2754 unsigned int revision) | |
2755 { | |
2756 /** | |
2757 * Note: Similar standalone functions are implemented in | |
2758 * "OrthancCPlugin.h" and "OrthancPluginCppWrapper.cpp". | |
2759 **/ | |
2760 | |
2761 unsigned int actualMajor, actualMinor, actualRevision; | |
2762 | |
2763 if (version == NULL) | |
2764 { | |
2765 throw OrthancException(ErrorCode_NullPointer); | |
2766 } | |
2767 else if (!strcmp(version, "mainline")) | |
2768 { | |
2769 // Assume compatibility with the mainline | |
2770 return true; | |
2771 } | |
2772 else if (ParseVersion(actualMajor, actualMinor, actualRevision, version)) | |
2773 { | |
2774 if (actualMajor > major) | |
2775 { | |
2776 return true; | |
2777 } | |
2778 | |
2779 if (actualMajor < major) | |
2780 { | |
2781 return false; | |
2782 } | |
2783 | |
2784 // Check the minor version number | |
2785 assert(actualMajor == major); | |
2786 | |
2787 if (actualMinor > minor) | |
2788 { | |
2789 return true; | |
2790 } | |
2791 | |
2792 if (actualMinor < minor) | |
2793 { | |
2794 return false; | |
2795 } | |
2796 | |
2797 // Check the patch level version number | |
2798 assert(actualMajor == major); | |
2799 | |
2800 if (actualRevision >= revision) | |
2801 { | |
2802 return true; | |
2803 } | |
2804 else | |
2805 { | |
2806 return false; | |
2807 } | |
2808 } | |
2809 else | |
2810 { | |
2811 throw OrthancException(ErrorCode_ParameterOutOfRange, "Not a valid version: " + std::string(version)); | |
2812 } | |
2813 } | |
2429 } | 2814 } |
2430 | 2815 |
2431 | 2816 |
2432 | 2817 |
2433 OrthancLinesIterator* OrthancLinesIterator_Create(const std::string& content) | 2818 OrthancLinesIterator* OrthancLinesIterator_Create(const std::string& content) |