Bug 911283 - Introduce nsTArray::SetLengthAndRetainStorage which unlike SetLength does not deallocate/reallocate the internal storage. Use it in NS_FillArray. r=bsmedberg
authorMats Palmgren <matspal@gmail.com>
Sun, 08 Sep 2013 02:05:02 +0000
changeset 158981 6f837a184a9066f43f7bb962d97236cc9d5614dd
parent 158980 29bbbd1de60b5cc3eea4d031ba95a597d59e6e52
child 158982 69e78915fc92cb08b22f29eeaaa87971787a291f
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbsmedberg
bugs911283
milestone26.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 911283 - Introduce nsTArray::SetLengthAndRetainStorage which unlike SetLength does not deallocate/reallocate the internal storage. Use it in NS_FillArray. r=bsmedberg
xpcom/glue/nsTArray.h
xpcom/io/nsStreamUtils.cpp
xpcom/tests/TestTArray.cpp
--- a/xpcom/glue/nsTArray.h
+++ b/xpcom/glue/nsTArray.h
@@ -1039,28 +1039,48 @@ public:
     return BinaryIndexOf(item, nsDefaultComparator<elem_type, Item>());
   }
 
   //
   // Mutation methods
   //
   // This method call the destructor on each element of the array, empties it,
   // but does not shrink the array's capacity.
-  //
+  // See also SetLengthAndRetainStorage.
   // Make sure to call Compact() if needed to avoid keeping a huge array
   // around.
   void ClearAndRetainStorage() {
     if (base_type::mHdr == EmptyHdr()) {
       return;
     }
 
     DestructRange(0, Length());
     base_type::mHdr->mLength = 0;
   }
 
+  // This method modifies the length of the array, but unlike SetLength
+  // it doesn't deallocate/reallocate the current internal storage.
+  // The new length MUST be shorter than or equal to the current capacity.
+  // If the new length is larger than the existing length of the array,
+  // then new elements will be constructed using elem_type's default
+  // constructor.  If shorter, elements will be destructed and removed.
+  // See also ClearAndRetainStorage.
+  // @param newLen  The desired length of this array.
+  void SetLengthAndRetainStorage(size_type newLen) {
+    MOZ_ASSERT(newLen <= base_type::Capacity());
+    size_type oldLen = Length();
+    if (newLen > oldLen) {
+      InsertElementsAt(oldLen, newLen - oldLen);
+      return;
+    }
+    if (newLen < oldLen) {
+      DestructRange(newLen, oldLen - newLen);
+      base_type::mHdr->mLength = newLen;
+    }
+  }
 
   // This method replaces a range of elements in this array.
   // @param start     The starting index of the elements to replace.
   // @param count     The number of elements to replace.  This may be zero to
   //                  insert elements without removing any existing elements.
   // @param array     The values to copy into this array.  Must be non-null,
   //                  and these elements must not already exist in the array
   //                  being modified.
--- a/xpcom/io/nsStreamUtils.cpp
+++ b/xpcom/io/nsStreamUtils.cpp
@@ -788,14 +788,15 @@ NS_FillArray(FallibleTArray<char>& aDest
   }
 
   nsresult rv =
     aInput->Read(aBuffer + aKeep, aDest.Capacity() - aKeep, aNewBytes);
   if (NS_FAILED(rv)) {
     *aNewBytes = 0;
   }
   // NOTE: we rely on the fact that the new slots are NOT initialized by
-  // SetLength here, see nsTArrayElementTraits::Construct() in nsTArray.h:
-  aDest.SetLength(aKeep + *aNewBytes);
+  // SetLengthAndRetainStorage here, see nsTArrayElementTraits::Construct()
+  // in nsTArray.h:
+  aDest.SetLengthAndRetainStorage(aKeep + *aNewBytes);
 
   MOZ_ASSERT(aDest.Length() <= aDest.Capacity(), "buffer overflow");
   return rv;
 }
--- a/xpcom/tests/TestTArray.cpp
+++ b/xpcom/tests/TestTArray.cpp
@@ -920,16 +920,102 @@ static bool test_conversion_operator() {
   CHECK_ARRAY_CAST(InfallibleTArray);
   CHECK_ARRAY_CAST(nsTArray);
 
 #undef CHECK_ARRAY_CAST
 
   return true;
 }
 
+template<class T>
+struct BufAccessor : public T
+{
+  void* GetHdr() { return T::mHdr; }
+};
+
+static bool test_SetLengthAndRetainStorage_no_ctor() {
+  // 1050 because sizeof(int)*1050 is more than a page typically.
+  const int N = 1050;
+  FallibleTArray<int> f;
+  AutoFallibleTArray<int, N> fauto;
+
+  InfallibleTArray<int> i;
+  AutoInfallibleTArray<int, N> iauto;
+
+  nsTArray<int> t;
+  nsAutoTArray<int, N> tauto;
+
+#define LPAREN (
+#define RPAREN )
+#define FOR_EACH(pre, post)                                    \
+  do {                                                         \
+    pre f post;                                                \
+    pre fauto post;                                            \
+    pre i post;                                                \
+    pre iauto post;                                            \
+    pre t post;                                                \
+    pre tauto post;                                            \
+  } while (0)
+  
+  // Setup test arrays.
+  FOR_EACH(;, .SetLength(N));
+  for (int n = 0; n < N; ++n) {
+    FOR_EACH(;, [n] = n);
+  }
+
+  void* initial_Hdrs[] = {
+    static_cast<BufAccessor<FallibleTArray<int> >&>(f).GetHdr(),
+    static_cast<BufAccessor<AutoFallibleTArray<int, N> >&>(fauto).GetHdr(),
+    static_cast<BufAccessor<InfallibleTArray<int> >&>(i).GetHdr(),
+    static_cast<BufAccessor<AutoInfallibleTArray<int, N> >&>(iauto).GetHdr(),
+    static_cast<BufAccessor<nsTArray<int> >&>(t).GetHdr(),
+    static_cast<BufAccessor<nsAutoTArray<int, N> >&>(tauto).GetHdr(),
+    nullptr
+  };
+
+  // SetLengthAndRetainStorage(n), should NOT overwrite memory when T hasn't
+  // a default constructor.
+  FOR_EACH(;, .SetLengthAndRetainStorage(8));
+  FOR_EACH(;, .SetLengthAndRetainStorage(12));
+  for (int n = 0; n < 12; ++n) {
+    FOR_EACH(if LPAREN, [n] != n RPAREN return false);
+  }
+  FOR_EACH(;, .SetLengthAndRetainStorage(0));
+  FOR_EACH(;, .SetLengthAndRetainStorage(N));
+  for (int n = 0; n < N; ++n) {
+    FOR_EACH(if LPAREN, [n] != n RPAREN return false);
+  }
+
+  void* current_Hdrs[] = {
+    static_cast<BufAccessor<FallibleTArray<int> >&>(f).GetHdr(),
+    static_cast<BufAccessor<AutoFallibleTArray<int, N> >&>(fauto).GetHdr(),
+    static_cast<BufAccessor<InfallibleTArray<int> >&>(i).GetHdr(),
+    static_cast<BufAccessor<AutoInfallibleTArray<int, N> >&>(iauto).GetHdr(),
+    static_cast<BufAccessor<nsTArray<int> >&>(t).GetHdr(),
+    static_cast<BufAccessor<nsAutoTArray<int, N> >&>(tauto).GetHdr(),
+    nullptr
+  };
+
+  // SetLengthAndRetainStorage(n) should NOT have reallocated the internal
+  // memory.
+  if (sizeof(initial_Hdrs) != sizeof(current_Hdrs)) return false;
+  for (size_t n = 0; n < sizeof(current_Hdrs) / sizeof(current_Hdrs[0]); ++n) {
+    if (current_Hdrs[n] != initial_Hdrs[n]) {
+      return false;
+    }
+  }
+
+
+#undef FOR_EACH
+#undef LPAREN
+#undef RPAREN
+
+  return true;
+}
+
 //----
 
 typedef bool (*TestFunc)();
 #define DECL_TEST(name) { #name, name }
 
 static const struct Test {
   const char* name;
   TestFunc    func;
@@ -946,16 +1032,17 @@ static const struct Test {
 #ifdef DEBUG
   DECL_TEST(test_autoarray),
 #endif
   DECL_TEST(test_indexof),
   DECL_TEST(test_heap),
   DECL_TEST(test_swap),
   DECL_TEST(test_fallible),
   DECL_TEST(test_conversion_operator),
+  DECL_TEST(test_SetLengthAndRetainStorage_no_ctor),
   { nullptr, nullptr }
 };
 
 }
 
 using namespace TestTArray;
 
 int main(int argc, char **argv) {