Bug 1329564 - Option to process empty items in StringListRange - r=jya
authorGerald Squelart <gsquelart@mozilla.com>
Mon, 28 Nov 2016 22:45:58 +1100
changeset 374167 195a58e1ad5e39439d0e4ebb444afaba1ad7bf7c
parent 374166 5566e83980ed65494a1f61e93ba7590240c9f5fc
child 374168 d3da2d43ac5d31a362744c0a0c2b4c069a8c85d6
push id6996
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 20:48:21 +0000
treeherdermozilla-beta@d89512dab048 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjya
bugs1329564
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1329564 - Option to process empty items in StringListRange - r=jya By default StringListRange skips empty items. Two new template options allow handling empty items: - ProcessEmptyItems: Process all, *except* if string is empty. - ProcessAll: Process all, including 1 empty item in an empty string. MozReview-Commit-ID: WNRHU5iCHt
dom/media/VideoUtils.h
dom/media/gtest/TestVideoUtils.cpp
--- a/dom/media/VideoUtils.h
+++ b/dom/media/VideoUtils.h
@@ -357,24 +357,38 @@ CreateTrackInfoWithMIMEType(const nsACSt
 
 // Try and create a TrackInfo with a given codec MIME type, and optional extra
 // parameters from a content type (its MIME type and codecs are ignored).
 UniquePtr<TrackInfo>
 CreateTrackInfoWithMIMETypeAndContentTypeExtraParameters(
   const nsACString& aCodecMIMEType,
   const MediaContentType& aContentType);
 
-template <typename String>
+enum class StringListRangeEmptyItems
+{
+  // Skip all empty items (empty string will process nothing)
+  // E.g.: "a,,b" -> ["a", "b"], "" -> nothing
+  Skip,
+  // Process all, except if string is empty
+  // E.g.: "a,,b" -> ["a", "", "b"], "" -> nothing
+  ProcessEmptyItems,
+  // Process all, including 1 empty item in an empty string
+  // E.g.: "a,,b" -> ["a", "", "b"], "" -> [""]
+  ProcessAll
+};
+
+template <typename String,
+          StringListRangeEmptyItems empties = StringListRangeEmptyItems::Skip>
 class StringListRange
 {
   typedef typename String::char_type CharType;
   typedef const CharType* Pointer;
 
 public:
-  // Iterator into range, trims items and skips empty items.
+  // Iterator into range, trims items and optionally skips empty items.
   class Iterator
   {
   public:
     bool operator!=(const Iterator& a) const
     {
       return mStart != a.mStart || mEnd != a.mEnd;
     }
     Iterator& operator++()
@@ -394,22 +408,31 @@ public:
     {
       SearchItemAt(aRangeStart);
     }
     void SearchItemAt(Pointer start)
     {
       // First, skip leading whitespace.
       for (Pointer p = start; ; ++p) {
         if (p >= mRangeEnd) {
-          mStart = mEnd = mComma = mRangeEnd;
+          if (p > mRangeEnd
+                  + (empties != StringListRangeEmptyItems::Skip ? 1 : 0)) {
+            p = mRangeEnd
+                + (empties != StringListRangeEmptyItems::Skip ? 1 : 0);
+          }
+          mStart = mEnd = mComma = p;
           return;
         }
         auto c = *p;
         if (c == CharType(',')) {
-          // Comma -> Empty item -> Skip.
+          // Comma -> Empty item -> Skip or process?
+          if (empties != StringListRangeEmptyItems::Skip) {
+            mStart = mEnd = mComma = p;
+            return;
+          }
         } else if (c != CharType(' ')) {
           mStart = p;
           break;
         }
       }
       // Find comma, recording start of trailing space.
       Pointer trailingWhitespace = nullptr;
       for (Pointer p = mStart + 1; ; ++p) {
@@ -441,38 +464,45 @@ public:
     Pointer mStart;
     Pointer mEnd;
     Pointer mComma;
   };
 
   explicit StringListRange(const String& aList) : mList(aList) {}
   Iterator begin()
   {
-    return Iterator(mList.Data(), mList.Length());
+    return Iterator(mList.Data()
+                    + ((empties == StringListRangeEmptyItems::ProcessEmptyItems
+                        && mList.Length() == 0) ? 1 : 0),
+                    mList.Length());
   }
   Iterator end()
   {
-    return Iterator(mList.Data() + mList.Length(), 0);
+    return Iterator(mList.Data() + mList.Length()
+                    + (empties != StringListRangeEmptyItems::Skip ? 1 : 0),
+                    0);
   }
 private:
   const String& mList;
 };
 
-template <typename String>
-StringListRange<String>
+template <StringListRangeEmptyItems empties = StringListRangeEmptyItems::Skip,
+          typename String>
+StringListRange<String, empties>
 MakeStringListRange(const String& aList)
 {
-  return StringListRange<String>(aList);
+  return StringListRange<String, empties>(aList);
 }
 
-template <typename ListString, typename ItemString>
+template <StringListRangeEmptyItems empties = StringListRangeEmptyItems::Skip,
+          typename ListString, typename ItemString>
 static bool
 StringListContains(const ListString& aList, const ItemString& aItem)
 {
-  for (const auto& listItem : MakeStringListRange(aList)) {
+  for (const auto& listItem : MakeStringListRange<empties>(aList)) {
     if (listItem.Equals(aItem)) {
       return true;
     }
   }
   return false;
 }
 
 } // end namespace mozilla
--- a/dom/media/gtest/TestVideoUtils.cpp
+++ b/dom/media/gtest/TestVideoUtils.cpp
@@ -9,72 +9,107 @@
 
 using namespace mozilla;
 
 TEST(StringListRange, MakeStringListRange)
 {
   static const struct
   {
     const char* mList;
-    const char* mExpected;
+    const char* mExpectedSkipEmpties;
+    const char* mExpectedProcessAll;
+    const char* mExpectedProcessEmpties;
   } tests[] =
-  {
-    { "", "" },
-    { " ", "" },
-    { ",", "" },
-    { " , ", "" },
-    { "a", "a|" },
-    { "  a  ", "a|" },
-    { "aa,bb", "aa|bb|" },
-    { " a a ,  b b  ", "a a|b b|" },
-    { " , ,a 1,,  ,b  2,", "a 1|b  2|" }
+  { // string              skip         all               empties
+    { "",                  "",          "|",              "" },
+    { " ",                 "",          "|",              "|" },
+    { ",",                 "",          "||",             "||" },
+    { " , ",               "",          "||",             "||" },
+    { "a",                 "a|",        "a|",             "a|" },
+    { "  a  ",             "a|",        "a|",             "a|" },
+    { "a,",                "a|",        "a||",            "a||" },
+    { "a, ",               "a|",        "a||",            "a||" },
+    { ",a",                "a|",        "|a|",            "|a|" },
+    { " ,a",               "a|",        "|a|",            "|a|" },
+    { "aa,bb",             "aa|bb|",    "aa|bb|",         "aa|bb|" },
+    { " a a ,  b b  ",     "a a|b b|",  "a a|b b|",       "a a|b b|" },
+    { " , ,a 1,,  ,b  2,", "a 1|b  2|", "||a 1|||b  2||", "||a 1|||b  2||" }
   };
 
   for (const auto& test : tests) {
     nsCString list(test.mList);
     nsCString out;
     for (const auto& item : MakeStringListRange(list)) {
       out += item;
       out += "|";
     }
-    EXPECT_STREQ(test.mExpected, out.Data());
+    EXPECT_STREQ(test.mExpectedSkipEmpties, out.Data());
+    out.SetLength(0);
+
+    for (const auto& item :
+         MakeStringListRange<StringListRangeEmptyItems::ProcessAll>(list)) {
+      out += item;
+      out += "|";
+    }
+    EXPECT_STREQ(test.mExpectedProcessAll, out.Data());
+    out.SetLength(0);
+
+    for (const auto& item :
+         MakeStringListRange<StringListRangeEmptyItems::ProcessEmptyItems>(list)) {
+      out += item;
+      out += "|";
+    }
+    EXPECT_STREQ(test.mExpectedProcessEmpties, out.Data());
   }
 }
 
 TEST(StringListRange, StringListContains)
 {
   static const struct
   {
     const char* mList;
     const char* mItemToSearch;
-    bool mExpected;
+    bool mExpectedSkipEmpties;
+    bool mExpectedProcessAll;
+    bool mExpectedProcessEmpties;
   } tests[] =
-  {
-    { "", "", false },
-    { "", "a", false },
-    { " ", "a", false },
-    { ",", "a", false },
-    { " , ", "", false },
-    { " , ", "a", false },
-    { "a", "a", true },
-    { "a", "b", false },
-    { "  a  ", "a", true },
-    { "aa,bb", "aa", true },
-    { "aa,bb", "bb", true },
-    { "aa,bb", "cc", false },
-    { "aa,bb", " aa ", false },
-    { " a a ,  b b  ", "a a", true },
-    { " , ,a 1,,  ,b  2,", "a 1", true },
-    { " , ,a 1,,  ,b  2,", "b  2", true },
-    { " , ,a 1,,  ,b  2,", "", false },
-    { " , ,a 1,,  ,b  2,", " ", false },
-    { " , ,a 1,,  ,b  2,", "A 1", false },
-    { " , ,A 1,,  ,b  2,", "a 1", false }
+  { // haystack            needle  skip   all    empties
+    { "",                  "",     false, true,  false },
+    { " ",                 "",     false, true,  true  },
+    { "",                  "a",    false, false, false },
+    { " ",                 "a",    false, false, false },
+    { ",",                 "a",    false, false, false },
+    { " , ",               "",     false, true,  true  },
+    { " , ",               "a",    false, false, false },
+    { "a",                 "a",    true,  true,  true  },
+    { "a",                 "b",    false, false, false },
+    { "  a  ",             "a",    true,  true,  true  },
+    { "aa,bb",             "aa",   true,  true,  true  },
+    { "aa,bb",             "bb",   true,  true,  true  },
+    { "aa,bb",             "cc",   false, false, false },
+    { "aa,bb",             " aa ", false, false, false },
+    { " a a ,  b b  ",     "a a",  true,  true,  true  },
+    { " , ,a 1,,  ,b  2,", "a 1",  true,  true,  true  },
+    { " , ,a 1,,  ,b  2,", "b  2", true,  true,  true  },
+    { " , ,a 1,,  ,b  2,", "",     false, true,  true  },
+    { " , ,a 1,,  ,b  2,", " ",    false, false, false },
+    { " , ,a 1,,  ,b  2,", "A 1",  false, false, false },
+    { " , ,A 1,,  ,b  2,", "a 1",  false, false, false }
   };
 
   for (const auto& test : tests) {
     nsCString list(test.mList);
     nsCString itemToSearch(test.mItemToSearch);
-    EXPECT_EQ(test.mExpected, StringListContains(list, itemToSearch))
+    EXPECT_EQ(test.mExpectedSkipEmpties, StringListContains(list, itemToSearch))
+      << "trying to find \"" << itemToSearch.Data()
+      << "\" in \"" << list.Data() << "\" (skipping empties)";
+    EXPECT_EQ(test.mExpectedProcessAll,
+              StringListContains<StringListRangeEmptyItems::ProcessAll>
+                                (list, itemToSearch))
       << "trying to find \"" << itemToSearch.Data()
-      << "\" in \"" << list.Data() << "\"";
+      << "\" in \"" << list.Data() << "\" (processing everything)";
+    EXPECT_EQ(test.mExpectedProcessEmpties,
+              StringListContains<StringListRangeEmptyItems::ProcessEmptyItems>
+                                (list, itemToSearch))
+      << "trying to find \"" << itemToSearch.Data()
+      << "\" in \"" << list.Data() << "\" (processing empties)";
   }
 }