[dali_2.3.19] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / visual-url.cpp
index c3c675d..2f16427 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
 #include <dali-toolkit/internal/visuals/visual-url.h>
 
 // EXTERNAL HEADERS
+#include <dali/devel-api/common/hash.h>
 #include <cstring> // for toupper()
 
 namespace Dali
@@ -26,102 +27,159 @@ namespace Toolkit
 {
 namespace Internal
 {
-
 namespace
 {
+constexpr uint32_t URL_ELLIPSED_LENGTH = 20u;
 
-VisualUrl::Location ResolveLocation( const std::string& url)
-{
-  const char FTP[] = { 'f', 't', 'p', ':', '/', '/' };
-  const char SSH[] = { 's', 's', 'h', ':', '/', '/' };
-  const char HTTP[] = { 'h', 't', 't', 'p', ':', '/', '/' };
-  const char HTTPS[] = { 'h', 't', 't', 'p', 's', ':', '/', '/' };
-
-  const int MATCH_FTP = 0x01;
-  const int MATCH_SSH = 0x02;
-  const int MATCH_HTTP = 0x04;
-  const int MATCH_HTTPS = 0x08;
+static_assert(URL_ELLIPSED_LENGTH < URL_ELLIPSED_LENGTH + 3u); ///< Guard overflow cases. (for svace)
 
-  const char* urlCStr = url.c_str();
-  if( url.size() > 6 )
+VisualUrl::ProtocolType ResolveLocation(const std::string& url)
+{
+  const char*    urlCStr = url.c_str();
+  const uint32_t length  = url.size();
+  if((length > 7) && urlCStr[5] == ':' && urlCStr[6] == '/' && urlCStr[7] == '/')
   {
-    if( urlCStr[3] == ':' || urlCStr[4] == ':' || urlCStr[5] == ':' )
+    // https:// or enbuf://
+    const char hOre = tolower(urlCStr[0]);
+    const char tOrn = tolower(urlCStr[1]);
+    const char tOrb = tolower(urlCStr[2]);
+    const char pOru = tolower(urlCStr[3]);
+    const char sOrf = tolower(urlCStr[4]);
+    if(('h' == hOre) &&
+       ('t' == tOrn) &&
+       ('t' == tOrb) &&
+       ('p' == pOru) &&
+       ('s' == sOrf))
     {
-      int flags = 0x0F;
-      for( unsigned int i=0; i < sizeof(HTTPS); ++i )
-      {
-        char c = tolower( urlCStr[i] );
-        if( i < sizeof(FTP) && (flags & MATCH_FTP) && c != FTP[i] )
-        {
-          flags &= ~MATCH_FTP;
-        }
-        if( i < sizeof(SSH) && (flags & MATCH_SSH) && c != SSH[i] )
-        {
-          flags &= ~MATCH_SSH;
-        }
-        if( i < sizeof(HTTP) && (flags & MATCH_HTTP) && c != HTTP[i] )
-        {
-          flags &= ~MATCH_HTTP;
-        }
-        if( i < sizeof(HTTPS) && (flags & MATCH_HTTPS) && c != HTTPS[i] )
-        {
-          flags &= ~MATCH_HTTPS;
-        }
-
-        if( (flags & (MATCH_FTP | MATCH_SSH | MATCH_HTTP | MATCH_HTTPS )) == 0 )
-        {
-          break;
-        }
-      }
-
-      if( flags )
-      {
-        return VisualUrl::REMOTE;
-      }
+      return VisualUrl::REMOTE;
+    }
+    if(('e' == hOre) &&
+       ('n' == tOrn) &&
+       ('b' == tOrb) &&
+       ('u' == pOru) &&
+       ('f' == sOrf))
+    {
+      return VisualUrl::BUFFER;
+    }
+  }
+  else if((length > 6) && urlCStr[4] == ':' && urlCStr[5] == '/' && urlCStr[6] == '/')
+  {
+    // http:// or dali://
+    const char hOrd = tolower(urlCStr[0]);
+    const char tOra = tolower(urlCStr[1]);
+    const char tOrl = tolower(urlCStr[2]);
+    const char pOri = tolower(urlCStr[3]);
+    if(('h' == hOrd) &&
+       ('t' == tOra) &&
+       ('t' == tOrl) &&
+       ('p' == pOri))
+    {
+      return VisualUrl::REMOTE;
+    }
+    if(('d' == hOrd) &&
+       ('a' == tOra) &&
+       ('l' == tOrl) &&
+       ('i' == pOri))
+    {
+      return VisualUrl::TEXTURE;
+    }
+  }
+  else if((length > 5) && urlCStr[3] == ':' && urlCStr[4] == '/' && urlCStr[5] == '/')
+  {
+    // ftp:// or ssh://
+    const char fOrs = tolower(urlCStr[0]);
+    const char tOrs = tolower(urlCStr[1]);
+    const char pOrh = tolower(urlCStr[2]);
+    if(('f' == fOrs) &&
+       ('t' == tOrs) &&
+       ('p' == pOrh))
+    {
+      return VisualUrl::REMOTE;
+    }
+    if(('s' == fOrs) &&
+       ('s' == tOrs) &&
+       ('h' == pOrh))
+    {
+      return VisualUrl::REMOTE;
     }
   }
   return VisualUrl::LOCAL;
 }
 
-
-VisualUrl::Type ResolveType( const std::string& url )
+VisualUrl::Type ResolveType(const std::string& url)
 {
   // if only one char in string, can only be regular image
-  const std::size_t count = url.size();
-  if( count > 0 )
+  const std::size_t count      = url.size();
+  VisualUrl::Type   returnType = VisualUrl::REGULAR_IMAGE;
+  if(count > 0)
   {
     // parsing from the end for better chance of early outs
-    enum { SUFFIX, HASH, HASH_DOT } state = SUFFIX;
-    char SVG[ 4 ] = { 'g', 'v', 's', '.' };
-    char GIF[ 4 ] = { 'f', 'i', 'g', '.' };
-    unsigned int svgScore = 0;
-    unsigned int gifScore = 0;
-    int index = count;
-    while( --index >= 0 )
+    enum
     {
-      const char currentChar = url[ index ];
+      SUFFIX,
+      HASH,
+      HASH_DOT
+    } state                = SUFFIX;
+    char         SVG[4]    = {'g', 'v', 's', '.'};
+    char         GIF[4]    = {'f', 'i', 'g', '.'};
+    char         WEBP[5]   = {'p', 'b', 'e', 'w', '.'};
+    char         JSON[5]   = {'n', 'o', 's', 'j', '.'};
+    char         TVG[4]    = {'g', 'v', 't', '.'};
+    unsigned int svgScore  = 0;
+    unsigned int tvgScore  = 0;
+    unsigned int gifScore  = 0;
+    unsigned int webpScore = 0;
+    unsigned int jsonScore = 0;
+    int          index     = count;
+    while(--index >= 0)
+    {
+      const char        currentChar   = tolower(url[index]);
       const std::size_t offsetFromEnd = count - index - 1u;
-      if( ( offsetFromEnd < sizeof(SVG) )&&( tolower( currentChar ) == SVG[ offsetFromEnd ] ) )
+      if((offsetFromEnd < sizeof(SVG)) && (currentChar == SVG[offsetFromEnd]))
       {
         // early out if SVG as can't be used in N patch for now
-        if( ++svgScore == sizeof(SVG) )
+        if(++svgScore == sizeof(SVG))
         {
           return VisualUrl::SVG;
         }
       }
-      if( ( offsetFromEnd < sizeof(GIF) )&&( tolower( currentChar ) == GIF[ offsetFromEnd ] ) )
+      if((offsetFromEnd < sizeof(TVG)) && (currentChar == TVG[offsetFromEnd]))
+      {
+        // early out if TVG as can't be used in N patch for now
+        if(++tvgScore == sizeof(TVG))
+        {
+          return VisualUrl::TVG;
+        }
+      }
+      if((offsetFromEnd < sizeof(GIF)) && (currentChar == GIF[offsetFromEnd]))
       {
-        // early out if GIF
-        if( ++gifScore == sizeof(GIF) )
+        //find type, but need to be check used in N patch
+        if(++gifScore == sizeof(GIF))
         {
-          return VisualUrl::GIF;
+          returnType = VisualUrl::GIF;
         }
       }
-      switch( state )
+      if((offsetFromEnd < sizeof(WEBP)) && (currentChar == WEBP[offsetFromEnd]))
+      {
+        if(++webpScore == sizeof(WEBP))
+        {
+          //find type, but need to be check used in N patch
+          returnType = VisualUrl::WEBP;
+        }
+      }
+      if((offsetFromEnd < sizeof(JSON)) && (currentChar == JSON[offsetFromEnd]))
+      {
+        // early out if JSON as can't be used in N patch for now
+        if(++jsonScore == sizeof(JSON))
+        {
+          return VisualUrl::JSON;
+        }
+      }
+      switch(state)
       {
         case SUFFIX:
         {
-          if( '.' == currentChar )
+          if('.' == currentChar)
           {
             state = HASH;
           }
@@ -129,27 +187,27 @@ VisualUrl::Type ResolveType( const std::string& url )
         }
         case HASH:
         {
-          if( ( '#' == currentChar ) || ( '9' == currentChar ) )
+          if(('#' == currentChar) || ('9' == currentChar))
           {
             state = HASH_DOT;
           }
           else
           {
             // early out, not a valid N/9-patch URL
-            return VisualUrl::REGULAR_IMAGE;
+            return returnType;
           }
           break;
         }
         case HASH_DOT:
         {
-          if( '.' == currentChar )
+          if('.' == currentChar)
           {
             return VisualUrl::N_PATCH;
           }
           else
           {
             // early out, not a valid N/9-patch URL
-            return VisualUrl::REGULAR_IMAGE;
+            return returnType;
           }
           break;
         }
@@ -157,45 +215,79 @@ VisualUrl::Type ResolveType( const std::string& url )
     }
   }
   // if we got here it is a regular image
-  return VisualUrl::REGULAR_IMAGE;
-}
-
+  return returnType;
 }
 
+} // namespace
 
 VisualUrl::VisualUrl()
 : mUrl(),
-  mType( VisualUrl::REGULAR_IMAGE ),
-  mLocation( VisualUrl::LOCAL )
+  mType(VisualUrl::REGULAR_IMAGE),
+  mLocation(VisualUrl::LOCAL),
+  mUrlHash(0ull)
 {
 }
 
-VisualUrl::VisualUrl( const std::string& url )
-: mUrl( url ),
-  mType( VisualUrl::REGULAR_IMAGE ),
-  mLocation( VisualUrl::LOCAL )
+VisualUrl::VisualUrl(const std::string& url)
+: mUrl(url),
+  mType(VisualUrl::REGULAR_IMAGE),
+  mLocation(VisualUrl::LOCAL),
+  mUrlHash(0ull)
 {
-  if( ! url.empty() )
+  if(!url.empty())
   {
-    mLocation = ResolveLocation( url );
-    mType = ResolveType( url );
+    mLocation = ResolveLocation(url);
+    if(VisualUrl::TEXTURE != mLocation)
+    {
+      // TEXTURE location url doesn't need type resolving, REGULAR_IMAGE is fine
+      mType = ResolveType(url);
+    }
   }
 }
 
-VisualUrl::VisualUrl( const VisualUrl& url )
-: mUrl( url.mUrl ),
-  mType( url.mType ),
-  mLocation( url.mLocation )
+VisualUrl::VisualUrl(const VisualUrl& url)
+: mUrl(url.mUrl),
+  mType(url.mType),
+  mLocation(url.mLocation),
+  mUrlHash(url.mUrlHash)
+{
+}
+
+VisualUrl::VisualUrl(VisualUrl&& url) noexcept
+: mUrl(std::move(url.mUrl)),
+  mType(std::move(url.mType)),
+  mLocation(std::move(url.mLocation)),
+  mUrlHash(std::move(url.mUrlHash))
+{
+  url.mUrlHash = 0ull;
+}
+
+VisualUrl::~VisualUrl()
 {
 }
 
-VisualUrl& VisualUrl::operator=( const VisualUrl& url )
+VisualUrl& VisualUrl::operator=(const VisualUrl& url)
 {
-  if( &url != this )
+  if(&url != this)
   {
-    mUrl = url.mUrl;
-    mType = url.mType;
+    mUrl      = url.mUrl;
+    mType     = url.mType;
     mLocation = url.mLocation;
+    mUrlHash  = url.mUrlHash;
+  }
+  return *this;
+}
+
+VisualUrl& VisualUrl::operator=(VisualUrl&& url) noexcept
+{
+  if(&url != this)
+  {
+    mUrl      = std::move(url.mUrl);
+    mType     = std::move(url.mType);
+    mLocation = std::move(url.mLocation);
+    mUrlHash  = std::move(url.mUrlHash);
+
+    url.mUrlHash = 0ull;
   }
   return *this;
 }
@@ -205,12 +297,28 @@ const std::string& VisualUrl::GetUrl() const
   return mUrl;
 }
 
+std::string VisualUrl::GetEllipsedUrl() const
+{
+  if(mUrl.size() > URL_ELLIPSED_LENGTH + 3)
+  {
+    std::string ellipsedUrl = "...";
+    ellipsedUrl += mUrl.substr(mUrl.size() - URL_ELLIPSED_LENGTH);
+    return ellipsedUrl;
+  }
+  return mUrl;
+}
+
+std::uint64_t VisualUrl::GetUrlHash() const
+{
+  return DALI_UNLIKELY(mUrlHash == 0) ? (mUrlHash = Dali::CalculateHash(mUrl)) : mUrlHash;
+}
+
 VisualUrl::Type VisualUrl::GetType() const
 {
   return mType;
 }
 
-VisualUrl::Location VisualUrl::GetLocation() const
+VisualUrl::ProtocolType VisualUrl::GetProtocolType() const
 {
   return mLocation;
 }
@@ -220,13 +328,65 @@ bool VisualUrl::IsValid() const
   return mUrl.size() > 0u;
 }
 
-bool VisualUrl::IsLocal() const
+bool VisualUrl::IsLocalResource() const
 {
   return mLocation == VisualUrl::LOCAL;
 }
 
+bool VisualUrl::IsBufferResource() const
+{
+  return mLocation == VisualUrl::BUFFER;
+}
+
+std::string VisualUrl::GetLocation() const
+{
+  return GetLocation(mUrl);
+}
+
+std::string VisualUrl::GetLocationWithoutExtension() const
+{
+  return GetLocationWithoutExtension(mUrl);
+}
+
+std::string VisualUrl::CreateTextureUrl(const std::string& location)
+{
+  return "dali://" + location;
+}
+
+std::string VisualUrl::CreateBufferUrl(const std::string& location, const std::string_view& extension)
+{
+  return "enbuf://" + location + std::string(extension);
+}
+
+VisualUrl::ProtocolType VisualUrl::GetProtocolType(const std::string& url)
+{
+  return ResolveLocation(url);
+}
+
+std::string VisualUrl::GetLocation(const std::string& url)
+{
+  const auto location = url.find("://");
+  if(std::string::npos != location)
+  {
+    return url.substr(location + 3u); // 3 characters forwards from the start of ://
+  }
+  return url;
+}
+
+std::string VisualUrl::GetLocationWithoutExtension(const std::string& url)
+{
+  const auto location = url.find("://");
+  if(std::string::npos != location)
+  {
+    const auto extension      = url.find_last_of("."); // Find last position of '.' keyword.
+    const auto locationLength = extension != std::string::npos ? extension - (location + 3u) : std::string::npos;
+    return url.substr(location + 3u, locationLength); // 3 characters forwards from the start of ://, and end of last '.' keyword.
+  }
+  return url;
+}
+
+} // namespace Internal
 
+} // namespace Toolkit
 
-} // Internal
-} // Toolkit
-} // Dali
+} // namespace Dali