123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 |
- /*
- * Copyright 2017-present Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #pragma once
- #include <string>
- #include <type_traits>
- #include <vector>
- namespace {
- // This struct is different in every translation unit. We use template
- // instantiations to define inline freestanding methods. Since the
- // methods are inline it is fine to define them in multiple translation
- // units, but the instantiation itself would be an ODR violation if it is
- // present in the program more than once. By tagging the instantiations
- // with this struct, we avoid ODR problems for the instantiation while
- // allowing the resulting methods to be inline-able. If you think that
- // seems hacky keep reading...
- struct FollyMemoryDetailTranslationUnitTag {};
- } // namespace
- namespace folly {
- namespace detail {
- void unsafeStringSetLargerSize(std::string& s, std::size_t n);
- template <typename T>
- void unsafeVectorSetLargerSize(std::vector<T>& v, std::size_t n);
- } // namespace detail
- /*
- * This file provides helper functions resizeWithoutInitialization()
- * that can resize std::string or std::vector without constructing or
- * initializing new elements.
- *
- * IMPORTANT: These functions can be unsafe if used improperly. If you
- * don't write to an element with index >= oldSize and < newSize, reading
- * the element can expose arbitrary memory contents to the world, including
- * the contents of old strings. If you're lucky you'll get a segfault,
- * because the kernel is only required to fault in new pages on write
- * access. MSAN should be able to catch problems in the common case that
- * the string or vector wasn't previously shrunk.
- *
- * Pay extra attention to your failure paths. For example, if you try
- * to read directly into a caller-provided string, make sure to clear
- * the string when you get an I/O error.
- *
- * You should only use this if you have profiling data from production
- * that shows that this is not a premature optimization. This code is
- * designed for retroactively optimizing code where touching every element
- * twice (or touching never-used elements once) shows up in profiling,
- * and where restructuring the code to use fixed-length arrays or IOBuf-s
- * would be difficult.
- *
- * NOTE: Just because .resize() shows up in your profile (probably
- * via one of the intrinsic memset implementations) doesn't mean that
- * these functions will make your program faster. A lot of the cost
- * of memset comes from cache misses, so avoiding the memset can mean
- * that the cache miss cost just gets pushed to the following code.
- * resizeWithoutInitialization can be a win when the contents are bigger
- * than a cache level, because the second access isn't free in that case.
- * It can be a win when the memory is already cached, so touching it
- * doesn't help later code. It can also be a win if the final length
- * of the string or vector isn't actually known, so the suffix will be
- * chopped off with a second call to .resize().
- */
- /**
- * Like calling s.resize(n), but when growing the string does not
- * initialize new elements. It is undefined behavior to read from
- * any element added to the string by this method unless it has been
- * written to by an operation that follows this call.
- *
- * IMPORTANT: Read the warning at the top of this header file.
- */
- inline void resizeWithoutInitialization(std::string& s, std::size_t n) {
- if (n <= s.size()) {
- s.resize(n);
- } else {
- // careful not to call reserve unless necessary, as it causes
- // shrink_to_fit on many platforms
- if (n > s.capacity()) {
- s.reserve(n);
- }
- detail::unsafeStringSetLargerSize(s, n);
- }
- }
- /**
- * Like calling v.resize(n), but when growing the vector does not construct
- * or initialize new elements. It is undefined behavior to read from any
- * element added to the vector by this method unless it has been written
- * to by an operation that follows this call.
- *
- * Use the FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT(T) macro to
- * declare (and inline define) the internals required to call
- * resizeWithoutInitialization for a std::vector<T>. This must
- * be done exactly once in each translation unit that wants to call
- * resizeWithoutInitialization(std::vector<T>&,size_t). char and unsigned
- * char are provided by default. If you don't do this you will get linker
- * errors about folly::detail::unsafeVectorSetLargerSize. Requiring that
- * T be trivially_destructible is only an approximation of the property
- * required of T. In fact what is required is that any random sequence of
- * bytes may be safely reinterpreted as a T and passed to T's destructor.
- *
- * std::vector<bool> has specialized internals and is not supported.
- *
- * IMPORTANT: Read the warning at the top of this header file.
- */
- template <
- typename T,
- typename = typename std::enable_if<
- std::is_trivially_destructible<T>::value &&
- !std::is_same<T, bool>::value>::type>
- void resizeWithoutInitialization(std::vector<T>& v, std::size_t n) {
- if (n <= v.size()) {
- v.resize(n);
- } else {
- if (n > v.capacity()) {
- v.reserve(n);
- }
- detail::unsafeVectorSetLargerSize(v, n);
- }
- }
- namespace detail {
- #if defined(_LIBCPP_STRING)
- // libc++
- } // namespace detail
- } // namespace folly
- template void std::string::__set_size(std::size_t);
- namespace folly {
- namespace detail {
- template <typename Tag, typename T, typename A, A Ptr__set_size>
- struct MakeUnsafeStringSetLargerSize {
- friend void unsafeStringSetLargerSize(
- std::basic_string<T>& s,
- std::size_t n) {
- // s.__set_size(n);
- (s.*Ptr__set_size)(n);
- (&s[0])[n] = '\0';
- }
- };
- template struct MakeUnsafeStringSetLargerSize<
- FollyMemoryDetailTranslationUnitTag,
- char,
- void (std::string::*)(std::size_t),
- &std::string::__set_size>;
- #elif defined(_GLIBCXX_USE_FB)
- // FBString
- template <typename Tag, typename T, typename A, A Ptrstore_>
- struct MakeUnsafeStringSetLargerSize {
- friend void unsafeStringSetLargerSize(
- std::basic_string<T>& s,
- std::size_t n) {
- // s.store_.expandNoinit(n - s.size(), false);
- (s.*Ptrstore_).expandNoinit(n - s.size(), false);
- }
- };
- template struct MakeUnsafeStringSetLargerSize<
- FollyMemoryDetailTranslationUnitTag,
- char,
- std::fbstring_core<char>(std::string::*),
- &std::string::store_>;
- #elif defined(_GLIBCXX_STRING) && _GLIBCXX_USE_CXX11_ABI
- // libstdc++ new implementation with SSO
- } // namespace detail
- } // namespace folly
- template void std::string::_M_set_length(std::size_t);
- namespace folly {
- namespace detail {
- template <typename Tag, typename T, typename A, A Ptr_M_set_length>
- struct MakeUnsafeStringSetLargerSize {
- friend void unsafeStringSetLargerSize(
- std::basic_string<T>& s,
- std::size_t n) {
- // s._M_set_length(n);
- (s.*Ptr_M_set_length)(n);
- }
- };
- template struct MakeUnsafeStringSetLargerSize<
- FollyMemoryDetailTranslationUnitTag,
- char,
- void (std::string::*)(std::size_t),
- &std::string::_M_set_length>;
- #elif defined(_GLIBCXX_STRING)
- // libstdc++ old implementation
- } // namespace detail
- } // namespace folly
- template std::string::_Rep* std::string::_M_rep() const;
- template void std::string::_Rep::_M_set_length_and_sharable(std::size_t);
- namespace folly {
- namespace detail {
- template <
- typename Tag,
- typename T,
- typename A,
- A Ptr_M_rep,
- typename B,
- B Ptr_M_set_length_and_sharable>
- struct MakeUnsafeStringSetLargerSize {
- friend void unsafeStringSetLargerSize(
- std::basic_string<T>& s,
- std::size_t n) {
- // s._M_rep()->_M_set_length_and_sharable(n);
- auto rep = (s.*Ptr_M_rep)();
- (rep->*Ptr_M_set_length_and_sharable)(n);
- }
- };
- template struct MakeUnsafeStringSetLargerSize<
- FollyMemoryDetailTranslationUnitTag,
- char,
- std::string::_Rep* (std::string::*)() const,
- &std::string::_M_rep,
- void (std::string::_Rep::*)(std::size_t),
- &std::string::_Rep::_M_set_length_and_sharable>;
- #elif defined(_MSC_VER)
- // MSVC
- inline void unsafeStringSetLargerSize(std::string& s, std::size_t n) {
- s._Eos(n);
- }
- #else
- #warning "No implementation for resizeWithoutInitialization of std::string"
- #endif
- // This machinery bridges template expansion and macro expansion
- #define FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT_IMPL(TYPE) \
- namespace folly { \
- namespace detail { \
- void unsafeVectorSetLargerSizeImpl(std::vector<TYPE>& v, std::size_t); \
- template <> \
- inline void unsafeVectorSetLargerSize<TYPE>( \
- std::vector<TYPE> & v, \
- std::size_t n) { \
- unsafeVectorSetLargerSizeImpl(v, n); \
- } \
- } \
- }
- #if defined(_LIBCPP_VECTOR)
- // libc++
- template <typename Tag, typename T, typename A, A Ptr__end_>
- struct MakeUnsafeVectorSetLargerSize {
- friend void unsafeVectorSetLargerSizeImpl(std::vector<T>& v, std::size_t n) {
- // v.__end_ += (n - v.size());
- using Base = std::__vector_base<T, std::allocator<T>>;
- static_assert(
- std::is_standard_layout<std::vector<T>>::value &&
- sizeof(std::vector<T>) == sizeof(Base),
- "reinterpret_cast safety conditions not met");
- reinterpret_cast<Base&>(v).*Ptr__end_ += (n - v.size());
- }
- };
- #define FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT(TYPE) \
- template struct folly::detail::MakeUnsafeVectorSetLargerSize< \
- FollyMemoryDetailTranslationUnitTag, \
- TYPE, \
- TYPE*(std::__vector_base<TYPE, std::allocator<TYPE>>::*), \
- &std::vector<TYPE>::__end_>; \
- FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT_IMPL(TYPE)
- #elif defined(_GLIBCXX_VECTOR)
- // libstdc++
- template <
- typename Tag,
- typename T,
- typename A,
- A Ptr_M_impl,
- typename B,
- B Ptr_M_finish>
- struct MakeUnsafeVectorSetLargerSize : std::vector<T> {
- friend void unsafeVectorSetLargerSizeImpl(std::vector<T>& v, std::size_t n) {
- // v._M_impl._M_finish += (n - v.size());
- (v.*Ptr_M_impl).*Ptr_M_finish += (n - v.size());
- }
- };
- #define FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT(TYPE) \
- template struct folly::detail::MakeUnsafeVectorSetLargerSize< \
- FollyMemoryDetailTranslationUnitTag, \
- TYPE, \
- std::vector<TYPE>::_Vector_impl( \
- std::_Vector_base<TYPE, std::allocator<TYPE>>::*), \
- &std::vector<TYPE>::_M_impl, \
- TYPE*(std::vector<TYPE>::_Vector_impl::*), \
- &std::vector<TYPE>::_Vector_impl::_M_finish>; \
- FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT_IMPL(TYPE)
- #elif defined(_MSC_VER)
- // MSVC
- #define FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT(TYPE) \
- extern inline void unsafeVectorSetLargerSizeImpl( \
- std::vector<TYPE>& v, std::size_t n) { \
- v._Mylast() += (n - v.size()); \
- } \
- FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT_IMPL(TYPE)
- #else
- #warning "No implementation for resizeWithoutInitialization of std::vector"
- #endif
- } // namespace detail
- } // namespace folly
- #if defined(FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT)
- FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT(char)
- FOLLY_DECLARE_VECTOR_RESIZE_WITHOUT_INIT(unsigned char)
- #endif
|