Orthanc/OrthancFramework/Sources/Toolbox.h
2025-06-23 19:07:37 +05:30

488 lines
15 KiB
C++

/**
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
* Copyright (C) 2017-2023 Osimis S.A., Belgium
* Copyright (C) 2024-2025 Orthanc Team SRL, Belgium
* Copyright (C) 2021-2025 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/>.
**/
#pragma once
#include "Enumerations.h"
#include "OrthancFramework.h"
#include <stdint.h>
#include <vector>
#include <string>
#include <json/value.h>
#if !defined(ORTHANC_ENABLE_BASE64)
# error The macro ORTHANC_ENABLE_BASE64 must be defined
#endif
#if !defined(ORTHANC_ENABLE_LOCALE)
# error The macro ORTHANC_ENABLE_LOCALE must be defined
#endif
#if !defined(ORTHANC_ENABLE_MD5)
# error The macro ORTHANC_ENABLE_MD5 must be defined
#endif
#if !defined(ORTHANC_ENABLE_PUGIXML)
# error The macro ORTHANC_ENABLE_PUGIXML must be defined
#endif
#if !defined(ORTHANC_ENABLE_SSL)
# error The macro ORTHANC_ENABLE_SSL must be defined
#endif
/**
* NOTE: GUID vs. UUID
* The simple answer is: no difference, they are the same thing. Treat
* them as a 16 byte (128 bits) value that is used as a unique
* value. In Microsoft-speak they are called GUIDs, but call them
* UUIDs when not using Microsoft-speak.
* http://stackoverflow.com/questions/246930/is-there-any-difference-between-a-guid-and-a-uuid
**/
#if ORTHANC_ENABLE_PUGIXML == 1
# include <pugixml.hpp>
#endif
#include <boost/date_time/posix_time/posix_time.hpp>
namespace Orthanc
{
typedef std::vector<std::string> UriComponents;
class NullType
{
};
class ORTHANC_PUBLIC Toolbox
{
public:
#if ORTHANC_ENABLE_MD5 == 1
class ORTHANC_PUBLIC MD5Context : public boost::noncopyable
{
private:
class PImpl;
boost::shared_ptr<PImpl> pimpl_;
public:
MD5Context();
void Append(const void* data,
size_t size);
void Append(const std::string& source);
void Export(std::string& target);
};
#endif
class ORTHANC_PUBLIC LinesIterator : public boost::noncopyable
{
private:
const std::string& content_;
size_t lineStart_;
size_t lineEnd_;
void FindEndOfLine();
public:
explicit LinesIterator(const std::string& content);
bool GetLine(std::string& target) const;
void Next();
};
static void ToUpperCase(std::string& s); // Inplace version
static void ToLowerCase(std::string& s); // Inplace version
static void ToUpperCase(std::string& result,
const std::string& source);
static void ToLowerCase(std::string& result,
const std::string& source);
static void SplitUriComponents(UriComponents& components,
const std::string& uri);
static void TruncateUri(UriComponents& target,
const UriComponents& source,
size_t fromLevel);
static bool IsChildUri(const UriComponents& baseUri,
const UriComponents& testedUri);
static std::string FlattenUri(const UriComponents& components,
size_t fromLevel = 0);
static std::string JoinUri(const std::string& base, const std::string& uri);
#if ORTHANC_ENABLE_MD5 == 1
static void ComputeMD5(std::string& result,
const std::string& data);
static void ComputeMD5(std::string& result,
const void* data,
size_t size);
static void ComputeMD5(std::string& result,
const std::set<std::string>& data);
#endif
static void ComputeSHA1(std::string& result,
const std::string& data);
static void ComputeSHA1(std::string& result,
const void* data,
size_t size);
static bool IsSHA1(const void* str,
size_t size);
static bool IsSHA1(const std::string& s);
#if ORTHANC_ENABLE_BASE64 == 1
static void DecodeBase64(std::string& result,
const std::string& data);
static void EncodeBase64(std::string& result,
const std::string& data);
static bool DecodeDataUriScheme(std::string& mime,
std::string& content,
const std::string& source);
static void EncodeDataUriScheme(std::string& result,
const std::string& mime,
const std::string& content);
#endif
#if ORTHANC_ENABLE_LOCALE == 1
static std::string ConvertToUtf8(const std::string& source,
Encoding sourceEncoding,
bool hasCodeExtensions);
static std::string ConvertFromUtf8(const std::string& source,
Encoding targetEncoding);
#endif
static bool IsAsciiString(const void* data,
size_t size);
static bool IsAsciiString(const std::string& s);
static std::string ConvertToAscii(const std::string& source);
static std::string StripSpaces(const std::string& source);
// In-place percent-decoding for URL
static void UrlDecode(std::string& s);
static Endianness DetectEndianness();
static std::string WildcardToRegularExpression(const std::string& s);
// TokenizeString result might contain empty strings (not SplitString)
static void TokenizeString(std::vector<std::string>& result,
const std::string& source,
char separator);
// SplitString result won't contain empty strings (compared to TokenizeString)
static void SplitString(std::vector<std::string>& result,
const std::string& source,
char separator);
// SplitString result won't contain empty strings (compared to TokenizeString)
static void SplitString(std::set<std::string>& result,
const std::string& source,
char separator);
static void JoinStrings(std::string& result,
const std::set<std::string>& source,
const char* separator);
static void JoinStrings(std::string& result,
const std::vector<std::string>& source,
const char* separator);
// returns true if all element of 'needles' are found in 'haystack'
template <typename T> static bool IsSetInSet(const std::set<T>& needles, const std::set<T>& haystack)
{
for (typename std::set<T>::const_iterator it = needles.begin();
it != needles.end(); ++it)
{
if (haystack.count(*it) == 0)
{
return false;
}
}
return true;
}
// returns the set of elements from 'needles' that are not in 'haystack'
template <typename T> static size_t GetMissingsFromSet(std::set<T>& missings, const std::set<T>& needles, const std::set<T>& haystack)
{
missings.clear();
for (typename std::set<T>::const_iterator it = needles.begin();
it != needles.end(); ++it)
{
if (haystack.count(*it) == 0)
{
missings.insert(*it);
}
}
return missings.size();
}
template <typename T> static void AppendSets(std::set<T>& target, const std::set<T>& toAppend)
{
for (typename std::set<T>::const_iterator it = toAppend.begin();
it != toAppend.end(); ++it)
{
target.insert(*it);
}
}
template <typename T> static void RemoveSets(std::set<T>& target, const std::set<T>& toRemove)
{
for (typename std::set<T>::const_iterator it = toRemove.begin();
it != toRemove.end(); ++it)
{
target.erase(*it);
}
}
// returns the elements that are both in a and b
template <typename T> static void GetIntersection(std::set<T>& target, const std::set<T>& a, const std::set<T>& b)
{
target.clear();
for (typename std::set<T>::const_iterator it = a.begin();
it != a.end(); ++it)
{
if (b.count(*it) > 0)
{
target.insert(*it);
}
}
}
#if ORTHANC_ENABLE_PUGIXML == 1
static void JsonToXml(std::string& target,
const Json::Value& source,
const std::string& rootElement = "root",
const std::string& arrayElement = "item");
#endif
#if ORTHANC_ENABLE_PUGIXML == 1
static void XmlToString(std::string& target,
const pugi::xml_document& source);
#endif
static bool IsInteger(const std::string& str);
static void CopyJsonWithoutComments(Json::Value& target,
const Json::Value& source);
static bool StartsWith(const std::string& str,
const std::string& prefix);
static void UriEncode(std::string& target,
const std::string& source);
static std::string GetJsonStringField(const ::Json::Value& json,
const std::string& key,
const std::string& defaultValue);
static bool GetJsonBooleanField(const ::Json::Value& json,
const std::string& key,
bool defaultValue);
static int GetJsonIntegerField(const ::Json::Value& json,
const std::string& key,
int defaultValue);
static unsigned int GetJsonUnsignedIntegerField(const ::Json::Value& json,
const std::string& key,
unsigned int defaultValue);
static bool IsUuid(const std::string& str);
static bool StartsWithUuid(const std::string& str);
#if ORTHANC_ENABLE_LOCALE == 1
static void InitializeGlobalLocale(const char* locale);
static void FinalizeGlobalLocale();
static std::string ToUpperCaseWithAccents(const std::string& source);
#endif
static void InitializeOpenSsl();
static void FinalizeOpenSsl();
static std::string GenerateUuid();
static std::string SubstituteVariables(const std::string& source,
const std::map<std::string, std::string>& dictionary);
static void RemoveIso2022EscapeSequences(std::string& dest,
const std::string& src);
static void Utf8ToUnicodeCharacter(uint32_t& unicode,
size_t& utf8Length,
const std::string& utf8,
size_t position);
static std::string LargeHexadecimalToDecimal(const std::string& hex);
// http://dicom.nema.org/medical/dicom/2019a/output/chtml/part05/sect_B.2.html
static std::string GenerateDicomPrivateUniqueIdentifier();
static void SimplifyDicomAsJson(Json::Value& target,
const Json::Value& source,
DicomToJsonFormat format);
static bool ReadJson(Json::Value& target,
const std::string& source);
static bool ReadJson(Json::Value& target,
const void* buffer,
size_t size);
static bool ReadJsonWithoutComments(Json::Value& target,
const std::string& source);
static bool ReadJsonWithoutComments(Json::Value& target,
const void* buffer,
size_t size);
static void WriteFastJson(std::string& target,
const Json::Value& source);
static void WriteStyledJson(std::string& target,
const Json::Value& source);
static void RemoveSurroundingQuotes(std::string& value);
class ORTHANC_PUBLIC ElapsedTimer : public boost::noncopyable
{
private:
boost::posix_time::ptime start_;
public:
ElapsedTimer();
uint64_t GetElapsedMilliseconds();
uint64_t GetElapsedMicroseconds();
uint64_t GetElapsedNanoseconds();
std::string GetHumanElapsedDuration();
std::string GetHumanTransferSpeed(bool full, uint64_t sizeInBytes);
void Restart();
};
// This is a helper class to measure and log time spend e.g in a method.
// This should be used only during debugging and should likely not ever be used in a release.
// By default, you should use it as a RAII but you may force Restart/StopAndLog manually if needed.
class ORTHANC_PUBLIC DebugElapsedTimeLogger : public boost::noncopyable
{
private:
ElapsedTimer timer_;
const std::string message_;
bool logged_;
public:
explicit DebugElapsedTimeLogger(const std::string& message);
~DebugElapsedTimeLogger();
void Restart();
void StopAndLog();
};
// This variant logs the same message when entering the method and when exiting (with the elapsed time).
// Logs goes to verbose-http.
class ORTHANC_PUBLIC ApiElapsedTimeLogger : public boost::noncopyable
{
private:
ElapsedTimer timer_;
const std::string message_;
public:
explicit ApiElapsedTimeLogger(const std::string& message);
~ApiElapsedTimeLogger();
};
static std::string GetHumanFileSize(uint64_t sizeInBytes);
static std::string GetHumanDuration(uint64_t durationInNanoseconds);
static std::string GetHumanTransferSpeed(bool full, uint64_t sizeInBytes, uint64_t durationInNanoseconds);
static bool ParseVersion(unsigned int& major,
unsigned int& minor,
unsigned int& revision,
const char* version);
static bool IsVersionAbove(const char* version,
unsigned int major,
unsigned int minor,
unsigned int revision);
};
}
/**
* The plain C, opaque data structure "OrthancLinesIterator" is a thin
* wrapper around Orthanc::Toolbox::LinesIterator, and is only used by
* "../Resources/Patches/dcmtk-dcdict_orthanc.cc", in order to avoid
* code duplication
**/
struct OrthancLinesIterator;
OrthancLinesIterator* OrthancLinesIterator_Create(const std::string& content);
bool OrthancLinesIterator_GetLine(std::string& target,
const OrthancLinesIterator* iterator);
void OrthancLinesIterator_Next(OrthancLinesIterator* iterator);
void OrthancLinesIterator_Free(OrthancLinesIterator* iterator);