changeset 4775:add0337b928a

refactoring parsing of numbers
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 30 Aug 2021 10:24:36 +0200
parents 9f207131c7f4
children 79d4e155592b
files OrthancFramework/Sources/DicomFormat/DicomValue.cpp OrthancFramework/Sources/SerializationToolbox.cpp OrthancFramework/Sources/SerializationToolbox.h OrthancFramework/UnitTestsSources/JobsTests.cpp
diffstat 4 files changed, 403 insertions(+), 118 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancFramework/Sources/DicomFormat/DicomValue.cpp	Wed Aug 25 17:20:21 2021 +0200
+++ b/OrthancFramework/Sources/DicomFormat/DicomValue.cpp	Mon Aug 30 10:24:36 2021 +0200
@@ -104,102 +104,110 @@
   }
 #endif
 
-  // same as ParseValue but in case the value actually contains a sequence,
-  // it will return the first value
-  // this has been introduced to support invalid "width/height" DICOM tags in some US
-  // images where the width is stored as "800\0" !
-  template <typename T,
-            bool allowSigned>
-  static bool ParseFirstValue(T& result,
-                              const DicomValue& source)
+  bool DicomValue::ParseInteger32(int32_t& result) const
+  {
+    if (IsBinary() ||
+        IsNull())
+    {
+      return false;
+    }
+    else
+    {
+      return SerializationToolbox::ParseInteger32(result, GetContent());
+    }
+  }
+
+  bool DicomValue::ParseInteger64(int64_t& result) const
   {
-    if (source.IsBinary() ||
-        source.IsNull())
+    if (IsBinary() ||
+        IsNull())
+    {
+      return false;
+    }
+    else
+    {
+      return SerializationToolbox::ParseInteger64(result, GetContent());
+    }
+  }
+
+  bool DicomValue::ParseUnsignedInteger32(uint32_t& result) const
+  {
+    if (IsBinary() ||
+        IsNull())
+    {
+      return false;
+    }
+    else
+    {
+      return SerializationToolbox::ParseUnsignedInteger32(result, GetContent());
+    }
+  }
+
+  bool DicomValue::ParseUnsignedInteger64(uint64_t& result) const
+  {
+    if (IsBinary() ||
+        IsNull())
     {
       return false;
     }
-
-    try
+    else
     {
-      std::string value = Toolbox::StripSpaces(source.GetContent());
-      if (value.empty())
-      {
-        return false;
-      }
-
-      if (!allowSigned &&
-          value[0] == '-')
-      {
-        return false;
-      }
+      return SerializationToolbox::ParseUnsignedInteger64(result, GetContent());
+    }
+  }
 
-      if (value.find("\\") == std::string::npos)
-      {
-        result = boost::lexical_cast<T>(value);
-        return true;
-      }
-      else
-      {
-        std::vector<std::string> tokens;
-        Toolbox::TokenizeString(tokens, value, '\\');
-
-        if (tokens.size() >= 1)
-        {
-          result = boost::lexical_cast<T>(tokens[0]);
-          return true;
-        }
-
-        return false;
-      }
-    }
-    catch (boost::bad_lexical_cast&)
+  bool DicomValue::ParseFloat(float& result) const
+  {
+    if (IsBinary() ||
+        IsNull())
     {
       return false;
     }
+    else
+    {
+      return SerializationToolbox::ParseFloat(result, GetContent());
+    }
   }
 
+  bool DicomValue::ParseDouble(double& result) const
+  {
+    if (IsBinary() ||
+        IsNull())
+    {
+      return false;
+    }
+    else
+    {
+      return SerializationToolbox::ParseDouble(result, GetContent());
+    }
+  }
 
-  template <typename T,
-            bool allowSigned>
-  static bool ParseValue(T& result,
-                         const DicomValue& source)
+  bool DicomValue::ParseFirstFloat(float& result) const
   {
-    if (source.IsBinary() ||
-        source.IsNull())
+    if (IsBinary() ||
+        IsNull())
     {
       return false;
     }
-    
-    try
+    else
     {
-      std::string value = Toolbox::StripSpaces(source.GetContent());
-      if (value.empty())
-      {
-        return false;
-      }
+      return SerializationToolbox::ParseFirstFloat(result, GetContent());
+    }
+  }
 
-      if (!allowSigned &&
-          value[0] == '-')
-      {
-        return false;
-      }
-      
-      result = boost::lexical_cast<T>(value);
-      return true;
-    }
-    catch (boost::bad_lexical_cast&)
+  bool DicomValue::ParseFirstUnsignedInteger(unsigned int& result) const
+  {
+    uint64_t value;
+
+    if (IsBinary() ||
+        IsNull())
     {
       return false;
     }
-  }
-
-  bool DicomValue::ParseInteger32(int32_t& result) const
-  {
-    int64_t tmp;
-    if (ParseValue<int64_t, true>(tmp, *this))
+    else if (SerializationToolbox::ParseFirstUnsignedInteger64(value, GetContent()))
     {
-      result = static_cast<int32_t>(tmp);
-      return (tmp == static_cast<int64_t>(result));  // Check no overflow occurs
+      result = static_cast<unsigned int>(value);
+      return (static_cast<uint64_t>(result) == value);   // Check no overflow
     }
     else
     {
@@ -207,50 +215,6 @@
     }
   }
 
-  bool DicomValue::ParseInteger64(int64_t& result) const
-  {
-    return ParseValue<int64_t, true>(result, *this);
-  }
-
-  bool DicomValue::ParseUnsignedInteger32(uint32_t& result) const
-  {
-    uint64_t tmp;
-    if (ParseValue<uint64_t, false>(tmp, *this))
-    {
-      result = static_cast<uint32_t>(tmp);
-      return (tmp == static_cast<uint64_t>(result));  // Check no overflow occurs
-    }
-    else
-    {
-      return false;
-    }
-  }
-
-  bool DicomValue::ParseUnsignedInteger64(uint64_t& result) const
-  {
-    return ParseValue<uint64_t, false>(result, *this);
-  }
-
-  bool DicomValue::ParseFloat(float& result) const
-  {
-    return ParseValue<float, true>(result, *this);
-  }
-
-  bool DicomValue::ParseDouble(double& result) const
-  {
-    return ParseValue<double, true>(result, *this);
-  }
-
-  bool DicomValue::ParseFirstFloat(float& result) const
-  {
-    return ParseFirstValue<float, true>(result, *this);
-  }
-
-  bool DicomValue::ParseFirstUnsignedInteger(unsigned int& result) const
-  {
-    return ParseFirstValue<unsigned int, true>(result, *this);
-  }
-
   bool DicomValue::CopyToString(std::string& result,
                                 bool allowBinary) const
   {
--- a/OrthancFramework/Sources/SerializationToolbox.cpp	Wed Aug 25 17:20:21 2021 +0200
+++ b/OrthancFramework/Sources/SerializationToolbox.cpp	Mon Aug 30 10:24:36 2021 +0200
@@ -24,11 +24,15 @@
 #include "SerializationToolbox.h"
 
 #include "OrthancException.h"
+#include "Toolbox.h"
 
 #if ORTHANC_ENABLE_DCMTK == 1
 #  include "DicomParsing/FromDcmtkBridge.h"
 #endif
 
+#include <boost/lexical_cast.hpp>
+
+
 namespace Orthanc
 {
   static bool ParseTagInternal(DicomTag& tag,
@@ -445,4 +449,202 @@
       value[it->first.Format()] = it->second;
     }
   }
+
+
+  template <typename T,
+            bool allowSigned>
+  static bool ParseValue(T& target,
+                         const std::string& source)
+  {
+    try
+    {
+      std::string value = Toolbox::StripSpaces(source);
+      if (value.empty())
+      {
+        return false;
+      }
+      else if (!allowSigned &&
+               value[0] == '-')
+      {
+        return false;
+      }
+      else
+      {
+        target = boost::lexical_cast<T>(value);
+        return true;
+      }
+    }
+    catch (boost::bad_lexical_cast&)
+    {
+      return false;
+    }
+  }
+
+
+  bool SerializationToolbox::ParseInteger32(int32_t& target,
+                                            const std::string& source)
+  {
+    int64_t tmp;
+    if (ParseValue<int64_t, true>(tmp, source))
+    {
+      target = static_cast<int32_t>(tmp);
+      return (tmp == static_cast<int64_t>(target));  // Check no overflow occurs
+    }
+    else
+    {
+      return false;
+    }
+  }
+  
+
+  bool SerializationToolbox::ParseInteger64(int64_t& target,
+                                            const std::string& source)
+  {
+    return ParseValue<int64_t, true>(target, source);
+  }
+  
+
+  bool SerializationToolbox::ParseUnsignedInteger32(uint32_t& target,
+                                                    const std::string& source)
+  {
+    uint64_t tmp;
+    if (ParseValue<uint64_t, false>(tmp, source))
+    {
+      target = static_cast<uint32_t>(tmp);
+      return (tmp == static_cast<uint64_t>(target));  // Check no overflow occurs
+    }
+    else
+    {
+      return false;
+    }
+  }
+  
+
+  bool SerializationToolbox::ParseUnsignedInteger64(uint64_t& target,
+                                                    const std::string& source)
+  {
+    return ParseValue<uint64_t, false>(target, source);
+  }
+  
+
+  bool SerializationToolbox::ParseFloat(float& target,
+                                        const std::string& source)
+  {
+    return ParseValue<float, true>(target, source);
+  }
+         
+
+  bool SerializationToolbox::ParseDouble(double& target,
+                                         const std::string& source)
+  {
+    return ParseValue<double, true>(target, source);
+  }
+
+
+  static bool GetFirstItem(std::string& target,
+                           const std::string& source)
+  {
+    std::vector<std::string> tokens;
+    Toolbox::TokenizeString(tokens, source, '\\');
+
+    if (tokens.empty())
+    {
+      return false;
+    }
+    else
+    {
+      target = tokens[0];
+      return true;
+    }
+  }
+  
+
+  bool SerializationToolbox::ParseFirstInteger32(int32_t& target,
+                                                 const std::string& source)
+  {
+    std::string first;
+    if (GetFirstItem(first, source))
+    {
+      return ParseInteger32(target, first);
+    }
+    else
+    {
+      return false;
+    }
+  }
+  
+
+  bool SerializationToolbox::ParseFirstInteger64(int64_t& target,
+                                                 const std::string& source)
+  {
+    std::string first;
+    if (GetFirstItem(first, source))
+    {
+      return ParseInteger64(target, first);
+    }
+    else
+    {
+      return false;
+    }
+  }
+  
+
+  bool SerializationToolbox::ParseFirstUnsignedInteger32(uint32_t& target,
+                                                         const std::string& source)
+  {
+    std::string first;
+    if (GetFirstItem(first, source))
+    {
+      return ParseUnsignedInteger32(target, first);
+    }
+    else
+    {
+      return false;
+    }
+  }
+  
+
+  bool SerializationToolbox::ParseFirstUnsignedInteger64(uint64_t& target,
+                                                         const std::string& source)
+  {
+    std::string first;
+    if (GetFirstItem(first, source))
+    {
+      return ParseUnsignedInteger64(target, first);
+    }
+    else
+    {
+      return false;
+    }
+  }
+  
+
+  bool SerializationToolbox::ParseFirstFloat(float& target,
+                                             const std::string& source)
+  {
+    std::string first;
+    if (GetFirstItem(first, source))
+    {
+      return ParseFloat(target, first);
+    }
+    else
+    {
+      return false;
+    }
+  }
+  
+
+  bool SerializationToolbox::ParseFirstDouble(double& target,
+                                              const std::string& source)
+  {
+    std::string first;
+    if (GetFirstItem(first, source))
+    {
+      return ParseDouble(target, first);
+    }
+    else
+    {
+      return false;
+    }
+  }
 }
--- a/OrthancFramework/Sources/SerializationToolbox.h	Wed Aug 25 17:20:21 2021 +0200
+++ b/OrthancFramework/Sources/SerializationToolbox.h	Mon Aug 30 10:24:36 2021 +0200
@@ -101,5 +101,41 @@
     static void WriteMapOfTags(Json::Value& target,
                                const std::map<DicomTag, std::string>& values,
                                const std::string& field);
+
+    static bool ParseInteger32(int32_t& result,
+                               const std::string& value);
+
+    static bool ParseInteger64(int64_t& result,
+                               const std::string& value);
+
+    static bool ParseUnsignedInteger32(uint32_t& result,
+                                       const std::string& value);
+
+    static bool ParseUnsignedInteger64(uint64_t& result,
+                                       const std::string& value);
+
+    static bool ParseFloat(float& result,
+                           const std::string& value);
+
+    static bool ParseDouble(double& result,
+                            const std::string& value);
+
+    static bool ParseFirstInteger32(int32_t& result,
+                                    const std::string& value);
+
+    static bool ParseFirstInteger64(int64_t& result,
+                                    const std::string& value);
+
+    static bool ParseFirstUnsignedInteger32(uint32_t& result,
+                                            const std::string& value);
+
+    static bool ParseFirstUnsignedInteger64(uint64_t& result,
+                                            const std::string& value);
+
+    static bool ParseFirstFloat(float& result,
+                                const std::string& value);
+
+    static bool ParseFirstDouble(double& result,
+                                 const std::string& value);
   };
 }
--- a/OrthancFramework/UnitTestsSources/JobsTests.cpp	Wed Aug 25 17:20:21 2021 +0200
+++ b/OrthancFramework/UnitTestsSources/JobsTests.cpp	Mon Aug 30 10:24:36 2021 +0200
@@ -1575,3 +1575,86 @@
     ASSERT_FALSE(b.IsRemoteCertificateRequired());
   }  
 }
+
+
+TEST(SerializationToolbox, Numbers)
+{
+  {
+    int32_t i;
+    ASSERT_FALSE(SerializationToolbox::ParseInteger32(i, ""));
+    ASSERT_FALSE(SerializationToolbox::ParseInteger32(i, "ee"));
+    ASSERT_TRUE(SerializationToolbox::ParseInteger32(i, "42"));  ASSERT_EQ(42, i);
+    ASSERT_TRUE(SerializationToolbox::ParseInteger32(i, "-42"));  ASSERT_EQ(-42, i);
+    ASSERT_TRUE(SerializationToolbox::ParseInteger32(i, "-2147483648")); ASSERT_EQ(-2147483648, i);
+    ASSERT_TRUE(SerializationToolbox::ParseInteger32(i, "2147483647")); ASSERT_EQ(2147483647, i);
+    ASSERT_FALSE(SerializationToolbox::ParseInteger32(i, "-2147483649"));
+    ASSERT_FALSE(SerializationToolbox::ParseInteger32(i, "2147483648"));
+    ASSERT_FALSE(SerializationToolbox::ParseInteger32(i, "-2\\-3\\-4"));
+    ASSERT_TRUE(SerializationToolbox::ParseFirstInteger32(i, "-2\\-3\\-4"));  ASSERT_EQ(-2, i);
+  }
+
+  {
+    uint32_t i;
+    ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger32(i, ""));
+    ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger32(i, "ee"));
+    ASSERT_TRUE(SerializationToolbox::ParseUnsignedInteger32(i, "42"));  ASSERT_EQ(42u, i);
+    ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger32(i, "-42"));
+    ASSERT_TRUE(SerializationToolbox::ParseUnsignedInteger32(i, "4294967295")); ASSERT_EQ(4294967295u, i);
+    ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger32(i, "4294967296"));
+    ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger32(i, "2\\3\\4"));
+    ASSERT_TRUE(SerializationToolbox::ParseFirstUnsignedInteger32(i, "2\\3\\4"));  ASSERT_EQ(2u, i);
+  }
+
+  {
+    int64_t i;
+    ASSERT_FALSE(SerializationToolbox::ParseInteger64(i, ""));
+    ASSERT_FALSE(SerializationToolbox::ParseInteger64(i, "ee"));
+    ASSERT_TRUE(SerializationToolbox::ParseInteger64(i, "42"));  ASSERT_EQ(42, i);
+    ASSERT_TRUE(SerializationToolbox::ParseInteger64(i, "-42"));  ASSERT_EQ(-42, i);
+    ASSERT_TRUE(SerializationToolbox::ParseInteger64(i, "-2147483649")); ASSERT_EQ(-2147483649ll, i);
+    ASSERT_TRUE(SerializationToolbox::ParseInteger64(i, "2147483648")); ASSERT_EQ(2147483648ll, i);
+    ASSERT_FALSE(SerializationToolbox::ParseInteger64(i, "-2\\-3\\-4"));
+    ASSERT_TRUE(SerializationToolbox::ParseFirstInteger64(i, "-2\\-3\\-4"));  ASSERT_EQ(-2, i);
+  }
+
+  {
+    uint64_t i;
+    ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger64(i, ""));
+    ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger64(i, "ee"));
+    ASSERT_TRUE(SerializationToolbox::ParseUnsignedInteger64(i, "42"));  ASSERT_EQ(42u, i);
+    ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger64(i, "-42"));
+    ASSERT_TRUE(SerializationToolbox::ParseUnsignedInteger64(i, "4294967296")); ASSERT_EQ(4294967296llu, i);
+    ASSERT_FALSE(SerializationToolbox::ParseUnsignedInteger64(i, "2\\3\\4"));
+    ASSERT_TRUE(SerializationToolbox::ParseFirstUnsignedInteger64(i, "2\\3\\4"));  ASSERT_EQ(2u, i);
+  }
+
+  {
+    float i;
+    ASSERT_FALSE(SerializationToolbox::ParseFloat(i, ""));
+    ASSERT_FALSE(SerializationToolbox::ParseFloat(i, "ee"));
+    ASSERT_TRUE(SerializationToolbox::ParseFloat(i, "42"));  ASSERT_FLOAT_EQ(42.0f, i);
+    ASSERT_TRUE(SerializationToolbox::ParseFloat(i, "-42"));  ASSERT_FLOAT_EQ(-42.0f, i);
+    ASSERT_FALSE(SerializationToolbox::ParseFloat(i, "2\\3\\4"));
+    ASSERT_TRUE(SerializationToolbox::ParseFirstFloat(i, "1.367\\2.367\\3.367"));  ASSERT_FLOAT_EQ(1.367f, i);
+
+    ASSERT_TRUE(SerializationToolbox::ParseFloat(i, "1.2"));  ASSERT_FLOAT_EQ(1.2f, i);
+    ASSERT_TRUE(SerializationToolbox::ParseFloat(i, "-1.2e+2"));  ASSERT_FLOAT_EQ(-120.0f, i);
+    ASSERT_TRUE(SerializationToolbox::ParseFloat(i, "-1e-2"));  ASSERT_FLOAT_EQ(-0.01f, i);
+    ASSERT_TRUE(SerializationToolbox::ParseFloat(i, "1.3671875"));  ASSERT_FLOAT_EQ(1.3671875f, i);
+  }
+
+  {
+    double i;
+    ASSERT_FALSE(SerializationToolbox::ParseDouble(i, ""));
+    ASSERT_FALSE(SerializationToolbox::ParseDouble(i, "ee"));
+    ASSERT_TRUE(SerializationToolbox::ParseDouble(i, "42"));  ASSERT_DOUBLE_EQ(42.0, i);
+    ASSERT_TRUE(SerializationToolbox::ParseDouble(i, "-42"));  ASSERT_DOUBLE_EQ(-42.0, i);
+    ASSERT_FALSE(SerializationToolbox::ParseDouble(i, "2\\3\\4"));
+    ASSERT_TRUE(SerializationToolbox::ParseFirstDouble(i, "1.367\\2.367\\3.367"));  ASSERT_DOUBLE_EQ(1.367, i);
+
+    ASSERT_TRUE(SerializationToolbox::ParseDouble(i, "1.2"));  ASSERT_DOUBLE_EQ(1.2, i);
+    ASSERT_TRUE(SerializationToolbox::ParseDouble(i, "-1.2e+2"));  ASSERT_DOUBLE_EQ(-120.0, i);
+    ASSERT_TRUE(SerializationToolbox::ParseDouble(i, "-1e-2"));  ASSERT_DOUBLE_EQ(-0.01, i);
+    ASSERT_TRUE(SerializationToolbox::ParseDouble(i, "1.3671875"));  ASSERT_DOUBLE_EQ(1.3671875, i);
+  }
+}