New strings no longer relocatable
authorNicholas Ormrod <njormrod@fb.com>
Thu, 31 Mar 2016 22:09:14 +0000 (15:09 -0700)
committerFacebook Github Bot 2 <facebook-github-bot-2-bot@fb.com>
Thu, 31 Mar 2016 22:20:21 +0000 (15:20 -0700)
Summary:gcc-5.0 introduced non-relocatable strings in libgcc. Only treat gcc < 5 strings as relocatable. Enable relocation for fbstrings via typedef.

Re https://github.com/facebook/folly/issues/316, thanks tomhughes for reporting this.

Reviewed By: snarkmaster, ot

Differential Revision: D3115580

fb-gh-sync-id: d8aff947d55a0a0c57f6b997f651a190e05f2779
fbshipit-source-id: d8aff947d55a0a0c57f6b997f651a190e05f2779

folly/FBString.h
folly/Traits.h
folly/test/TraitsTest.cpp

index dc455991e32c95df5319b9e9551aee47a6592b8b..2f025154422ca7400eb84af76917d439709b2b8a 100644 (file)
@@ -975,6 +975,7 @@ public:
                                 > const_reverse_iterator;
 
   static const size_type npos;                     // = size_type(-1)
+  typedef std::true_type IsRelocatable;
 
 private:
   static void procrustes(size_type& n, size_type nmax) {
index 76120c3287556b99646fed1390b2448bfe97ec3a..2d8c52dd73392da1cd4b0f5d81a058a37e5441e3 100644 (file)
@@ -386,7 +386,10 @@ constexpr construct_in_place_t construct_in_place{};
 
 } // namespace folly
 
+// gcc-5.0 changed string's implementation in libgcc to be non-relocatable
+#if __GNUC__ < 5
 FOLLY_ASSUME_FBVECTOR_COMPATIBLE_3(std::basic_string);
+#endif
 FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(std::vector);
 FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(std::list);
 FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(std::deque);
index c76f1daa4e5628e3a15cb27f9e272494287de2a9..82cded6739d5883a82eb6131ec090a6c4fd3526f 100644 (file)
 
 #include <folly/Traits.h>
 
+#include <cstring>
+#include <string>
+#include <utility>
+
+#include <folly/ScopeGuard.h>
 #include <gtest/gtest.h>
 
 using namespace folly;
@@ -108,6 +113,42 @@ TEST(Traits, relational) {
   EXPECT_FALSE((folly::greater_than<uint8_t, 255u, uint8_t>(254u)));
 }
 
+template <typename T, typename... Args>
+void testIsRelocatable(Args&&... args) {
+  if (!IsRelocatable<T>::value) return;
+
+  // We use placement new on zeroed memory to avoid garbage subsections
+  char vsrc[sizeof(T)] = { 0 };
+  char vdst[sizeof(T)] = { 0 };
+  char vcpy[sizeof(T)];
+
+  T* src = new (vsrc) T(std::forward<Args>(args)...);
+  SCOPE_EXIT { src->~T(); };
+  std::memcpy(vcpy, vsrc, sizeof(T));
+  T deep(*src);
+  T* dst = new (vdst) T(std::move(*src));
+  SCOPE_EXIT { dst->~T(); };
+
+  EXPECT_EQ(deep, *dst);
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-aliasing"
+  EXPECT_EQ(deep, *reinterpret_cast<T*>(vcpy));
+#pragma GCC diagnostic pop
+
+  // This test could technically fail; however, this is what relocation
+  // almost always means, so it's a good test to have
+  EXPECT_EQ(std::memcmp(vcpy, vdst, sizeof(T)), 0);
+}
+
+TEST(Traits, actuallyRelocatable) {
+  // Ensure that we test stack and heap allocation for strings with in-situ
+  // capacity
+  testIsRelocatable<std::string>("1");
+  testIsRelocatable<std::string>(sizeof(std::string) + 1, 'x');
+
+  testIsRelocatable<std::vector<char>>(5, 'g');
+}
+
 struct membership_no {};
 struct membership_yes { using x = void; };
 FOLLY_CREATE_HAS_MEMBER_TYPE_TRAITS(has_member_type_x, x);