diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..6faaada81 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "hphp/submodules/folly"] + path = hphp/submodules/folly + url = git://github.com/facebook/folly.git diff --git a/CMake/FollySetup.cmake b/CMake/FollySetup.cmake new file mode 100644 index 000000000..66a08db23 --- /dev/null +++ b/CMake/FollySetup.cmake @@ -0,0 +1,31 @@ +# folly-config.h is a generated file from autotools +# We need to do the equivalent checks here and use +# add_definitions as needed +add_definitions(-DFOLLY_NO_CONFIG=1) + +INCLUDE(CheckCXXSourceCompiles) +CHECK_CXX_SOURCE_COMPILES(" +extern \"C\" void (*test_ifunc(void))() { return 0; } +void func() __attribute__((ifunc(\"test_ifunc\"))); +" FOLLY_IFUNC) +if (FOLLY_IFUNC) + add_definitions("-DHAVE_IFUNC=1") +endif() + +set(CMAKE_REQUIRED_LIBRARIES rt) +CHECK_CXX_SOURCE_COMPILES("#include +int main() { + clock_gettime((clockid_t)0, NULL); + return 0; +}" HAVE_CLOCK_GETTIME) + +if (HAVE_CLOCK_GETTIME) + add_definitions("-DFOLLY_HAVE_CLOCK_GETTIME=1") +endif() +set(CMAKE_REQUIRED_LIBRARIES) + +find_path(FEATURES_H_INCLUDE_DIR NAMES features.h) +if (FEATURES_H_INCLUDE_DIR) + include_directories("${FEATURES_H_INCLUDE_DIR}") + add_definitions("-DFOLLY_HAVE_FEATURES_H=1") +endif() diff --git a/hphp/CMakeLists.txt b/hphp/CMakeLists.txt index 1a4c99344..0bd1ae759 100644 --- a/hphp/CMakeLists.txt +++ b/hphp/CMakeLists.txt @@ -16,6 +16,7 @@ # include(HPHPSetup) +include(FollySetup) # HHVM Build SET(USE_HHVM TRUE) diff --git a/hphp/submodules/folly b/hphp/submodules/folly new file mode 160000 index 000000000..78d1c04a4 --- /dev/null +++ b/hphp/submodules/folly @@ -0,0 +1 @@ +Subproject commit 78d1c04a40275040789dce473603ab6096497f81 diff --git a/hphp/third_party/folly/CMakeLists.txt b/hphp/third_party/folly/CMakeLists.txt index bc7989707..e52cbd2c9 100644 --- a/hphp/third_party/folly/CMakeLists.txt +++ b/hphp/third_party/folly/CMakeLists.txt @@ -1,10 +1,9 @@ -# folly-config.h is a generated file from autotools -# We need to do the equivalent checks here and use -# add_definitions as needed -add_definitions(-DFOLLY_NO_CONFIG=1) - set(FOLLY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/folly") +if (NOT EXISTS "${FOLLY_DIR}/Portability.h") + message(FATAL_ERROR "${FOLLY_DIR}/Portability.h missing, did you forget to run `git submodule init && git submodule update`?") +endif() + # Generated files from folly/build/generate_*.py auto_sources(genfiles "*.cpp" "RECURSE" "${CMAKE_CURRENT_SOURCE_DIR}/gen") @@ -20,11 +19,15 @@ foreach (file ${files}) endforeach() list(REMOVE_ITEM files ${FOLLY_DIR}/Benchmark.cpp + ${FOLLY_DIR}/build/GenerateFingerprintTables.cpp + ${FOLLY_DIR}/detail/Clock.cpp ${FOLLY_DIR}/experimental/File.cpp ${FOLLY_DIR}/experimental/exception_tracer/ExceptionTracer.cpp + ${FOLLY_DIR}/experimental/exception_tracer/ExceptionTracerBenchmark.cpp ${FOLLY_DIR}/experimental/exception_tracer/ExceptionTracerLib.cpp ${FOLLY_DIR}/experimental/exception_tracer/ExceptionTracerTest.cpp ${FOLLY_DIR}/experimental/io/AsyncIO.cpp + ${FOLLY_DIR}/experimental/io/HugePageUtil.cpp ${FOLLY_DIR}/experimental/symbolizer/SymbolizerTest.cpp ${FOLLY_DIR}/experimental/symbolizer/ElfUtil.cpp ) @@ -40,6 +43,11 @@ endif() # and some other folly pieces we're not including yet # For now, that's not actually a requirement, so skip it list(REMOVE_ITEM files ${FOLLY_DIR}/Subprocess.cpp) + +# io/Compression requires snappy library +# Don't add dep until we need it +list(REMOVE_ITEM files ${FOLLY_DIR}/io/Compression.cpp) + add_library(folly STATIC ${files} ${genfiles} ${cfiles} ) find_package(Boost 1.48.0 COMPONENTS system program_options filesystem regex REQUIRED) @@ -52,15 +60,6 @@ include_directories(${LIBGLOG_INCLUDE_DIR}) find_package(PThread REQUIRED) include_directories(${LIBPTHREAD_INCLUDE_DIRS}) -INCLUDE(CheckCXXSourceCompiles) -CHECK_CXX_SOURCE_COMPILES(" -extern \"C\" void (*test_ifunc(void))() { return 0; } -void func() __attribute__((ifunc(\"test_ifunc\"))); -" FOLLY_IFUNC) -if (FOLLY_IFUNC) - add_definitions("-DHAVE_IFUNC=1") -endif() - target_link_libraries(folly ${Boost_LIBRARIES} ${LIBGLOG_LIBRARY} ${LIBPTHREAD_LIBRARIES}) diff --git a/hphp/third_party/folly/folly b/hphp/third_party/folly/folly new file mode 120000 index 000000000..6adacb24b --- /dev/null +++ b/hphp/third_party/folly/folly @@ -0,0 +1 @@ +../../submodules/folly/folly \ No newline at end of file diff --git a/hphp/third_party/folly/folly/ApplyTuple.h b/hphp/third_party/folly/folly/ApplyTuple.h deleted file mode 100644 index 2e49c78af..000000000 --- a/hphp/third_party/folly/folly/ApplyTuple.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2013 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. - */ - -/* - * Defines a function folly::applyTuple, which takes a function and a - * std::tuple of arguments and calls the function with those - * arguments. - * - * Example: - * - * int x = folly::applyTuple(std::plus(), std::make_tuple(12, 12)); - * ASSERT(x == 24); - */ - -#ifndef FOLLY_APPLYTUPLE_H_ -#define FOLLY_APPLYTUPLE_H_ - -#include -#include -#include - -namespace folly { - -////////////////////////////////////////////////////////////////////// - -namespace detail { - -// This is to allow using this with pointers to member functions, -// where the first argument in the tuple will be the this pointer. -template F& makeCallable(F& f) { return f; } -template -auto makeCallable(R (C::*d)(A...)) -> decltype(std::mem_fn(d)) { - return std::mem_fn(d); -} - -template -struct DerefSize - : std::tuple_size::type> -{}; - -// CallTuple recursively unpacks tuple arguments so we can forward -// them into the function. -template -struct CallTuple { - template - static typename std::enable_if< - (sizeof...(Unpacked) < DerefSize::value), - Ret - >::type call(const F& f, Tuple&& t, Unpacked&&... unp) { - typedef typename std::tuple_element< - sizeof...(Unpacked), - typename std::remove_reference::type - >::type ElementType; - return CallTuple::call(f, std::forward(t), - std::forward(unp)..., - std::forward(std::get(t)) - ); - } - - template - static typename std::enable_if< - (sizeof...(Unpacked) == DerefSize::value), - Ret - >::type call(const F& f, Tuple&& t, Unpacked&&... unp) { - return makeCallable(f)(std::forward(unp)...); - } -}; - -// The point of this meta function is to extract the contents of the -// tuple as a parameter pack so we can pass it into std::result_of<>. -template struct ReturnValue {}; -template -struct ReturnValue> { - typedef typename std::result_of::type type; -}; - -} - -////////////////////////////////////////////////////////////////////// - -template -typename detail::ReturnValue< - typename std::decay::type, - typename std::remove_reference::type ->::type -applyTuple(const Callable& c, Tuple&& t) { - typedef typename detail::ReturnValue< - typename std::decay::type, - typename std::remove_reference::type - >::type RetT; - return detail::CallTuple::call(c, std::forward(t)); -} - -////////////////////////////////////////////////////////////////////// - -} - -#endif diff --git a/hphp/third_party/folly/folly/Arena-inl.h b/hphp/third_party/folly/folly/Arena-inl.h deleted file mode 100644 index d97ea6973..000000000 --- a/hphp/third_party/folly/folly/Arena-inl.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_ARENA_H_ -#error This file may only be included from Arena.h -#endif - -// Implementation of Arena.h functions - -namespace folly { - -template -std::pair::Block*, size_t> -Arena::Block::allocate(Alloc& alloc, size_t size, bool allowSlack) { - size_t allocSize = sizeof(Block) + size; - if (allowSlack) { - allocSize = ArenaAllocatorTraits::goodSize(alloc, allocSize); - } - - void* mem = alloc.allocate(allocSize); - assert(isAligned(mem)); - return std::make_pair(new (mem) Block(), allocSize - sizeof(Block)); -} - -template -void Arena::Block::deallocate(Alloc& alloc) { - this->~Block(); - alloc.deallocate(this); -} - -template -void* Arena::allocateSlow(size_t size) { - std::pair p; - char* start; - - - size_t allocSize = std::max(size, minBlockSize()) + sizeof(Block); - if(sizeLimit_ && allocSize > sizeLimit_ - totalAllocatedSize_) { - throw std::bad_alloc(); - } - - if (size > minBlockSize()) { - // Allocate a large block for this chunk only, put it at the back of the - // list so it doesn't get used for small allocations; don't change ptr_ - // and end_, let them point into a normal block (or none, if they're - // null) - p = Block::allocate(alloc(), size, false); - start = p.first->start(); - blocks_.push_back(*p.first); - } else { - // Allocate a normal sized block and carve out size bytes from it - p = Block::allocate(alloc(), minBlockSize(), true); - start = p.first->start(); - blocks_.push_front(*p.first); - ptr_ = start + size; - end_ = start + p.second; - } - - assert(p.second >= size); - totalAllocatedSize_ += p.second + sizeof(Block); - return start; -} - -template -void Arena::merge(Arena&& other) { - blocks_.splice_after(blocks_.before_begin(), other.blocks_); - other.blocks_.clear(); - other.ptr_ = other.end_ = nullptr; - totalAllocatedSize_ += other.totalAllocatedSize_; - other.totalAllocatedSize_ = 0; -} - -template -Arena::~Arena() { - auto disposer = [this] (Block* b) { b->deallocate(this->alloc()); }; - while (!blocks_.empty()) { - blocks_.pop_front_and_dispose(disposer); - } -} - -} // namespace folly diff --git a/hphp/third_party/folly/folly/Arena.h b/hphp/third_party/folly/folly/Arena.h deleted file mode 100644 index a8d8d7f80..000000000 --- a/hphp/third_party/folly/folly/Arena.h +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_ARENA_H_ -#define FOLLY_ARENA_H_ - -#include -#include -#include -#include - -#include "folly/Likely.h" -#include "folly/Malloc.h" - -namespace folly { - -/** - * Simple arena: allocate memory which gets freed when the arena gets - * destroyed. - * - * The arena itself allocates memory using a custom allocator which provides - * the following interface (same as required by StlAllocator in StlAllocator.h) - * - * void* allocate(size_t size); - * Allocate a block of size bytes, properly aligned to the maximum - * alignment required on your system; throw std::bad_alloc if the - * allocation can't be satisfied. - * - * void deallocate(void* ptr); - * Deallocate a previously allocated block. - * - * You may also specialize ArenaAllocatorTraits for your allocator type to - * provide: - * - * size_t goodSize(const Allocator& alloc, size_t size) const; - * Return a size (>= the provided size) that is considered "good" for your - * allocator (for example, if your allocator allocates memory in 4MB - * chunks, size should be rounded up to 4MB). The provided value is - * guaranteed to be rounded up to a multiple of the maximum alignment - * required on your system; the returned value must be also. - * - * An implementation that uses malloc() / free() is defined below, see - * SysAlloc / SysArena. - */ -template struct ArenaAllocatorTraits; -template -class Arena { - public: - explicit Arena(const Alloc& alloc, - size_t minBlockSize = kDefaultMinBlockSize, - size_t sizeLimit = 0) - : allocAndSize_(alloc, minBlockSize) - , ptr_(nullptr) - , end_(nullptr) - , totalAllocatedSize_(0) - , bytesUsed_(0) - , sizeLimit_(sizeLimit) { - } - - ~Arena(); - - void* allocate(size_t size) { - size = roundUp(size); - bytesUsed_ += size; - - if (LIKELY(end_ - ptr_ >= size)) { - // Fast path: there's enough room in the current block - char* r = ptr_; - ptr_ += size; - assert(isAligned(r)); - return r; - } - - // Not enough room in the current block - void* r = allocateSlow(size); - assert(isAligned(r)); - return r; - } - - void deallocate(void* p) { - // Deallocate? Never! - } - - // Transfer ownership of all memory allocated from "other" to "this". - void merge(Arena&& other); - - // Gets the total memory used by the arena - size_t totalSize() const { - return totalAllocatedSize_ + sizeof(Arena); - } - - // Gets the total number of "used" bytes, i.e. bytes that the arena users - // allocated via the calls to `allocate`. Doesn't include fragmentation, e.g. - // if block size is 4KB and you allocate 2 objects of 3KB in size, - // `bytesUsed()` will be 6KB, while `totalSize()` will be 8KB+. - size_t bytesUsed() const { - return bytesUsed_; - } - - private: - // not copyable - Arena(const Arena&) = delete; - Arena& operator=(const Arena&) = delete; - - // movable - Arena(Arena&&) = default; - Arena& operator=(Arena&&) = default; - - struct Block; - typedef boost::intrusive::slist_member_hook< - boost::intrusive::tag> BlockLink; - - struct Block { - BlockLink link; - - // Allocate a block with at least size bytes of storage. - // If allowSlack is true, allocate more than size bytes if convenient - // (via ArenaAllocatorTraits::goodSize()) as we'll try to pack small - // allocations in this block. - static std::pair allocate( - Alloc& alloc, size_t size, bool allowSlack); - void deallocate(Alloc& alloc); - - char* start() { - return reinterpret_cast(this + 1); - } - - private: - Block() { } - ~Block() { } - } __attribute__((aligned)); - // This should be alignas(std::max_align_t) but neither alignas nor - // max_align_t are supported by gcc 4.6.2. - - public: - static constexpr size_t kDefaultMinBlockSize = 4096 - sizeof(Block); - - private: - static constexpr size_t maxAlign = alignof(Block); - static constexpr bool isAligned(uintptr_t address) { - return (address & (maxAlign - 1)) == 0; - } - static bool isAligned(void* p) { - return isAligned(reinterpret_cast(p)); - } - - // Round up size so it's properly aligned - static constexpr size_t roundUp(size_t size) { - return (size + maxAlign - 1) & ~(maxAlign - 1); - } - - // cache_last makes the list keep a pointer to the last element, so we - // have push_back() and constant time splice_after() - typedef boost::intrusive::slist< - Block, - boost::intrusive::member_hook, - boost::intrusive::constant_time_size, - boost::intrusive::cache_last> BlockList; - - void* allocateSlow(size_t size); - - // Empty member optimization: package Alloc with a non-empty member - // in case Alloc is empty (as it is in the case of SysAlloc). - struct AllocAndSize : public Alloc { - explicit AllocAndSize(const Alloc& a, size_t s) - : Alloc(a), minBlockSize(s) { - } - - size_t minBlockSize; - }; - - size_t minBlockSize() const { - return allocAndSize_.minBlockSize; - } - Alloc& alloc() { return allocAndSize_; } - const Alloc& alloc() const { return allocAndSize_; } - - AllocAndSize allocAndSize_; - BlockList blocks_; - char* ptr_; - char* end_; - size_t totalAllocatedSize_; - size_t bytesUsed_; - size_t sizeLimit_; -}; - -/** - * By default, don't pad the given size. - */ -template -struct ArenaAllocatorTraits { - static size_t goodSize(const Alloc& alloc, size_t size) { - return size; - } -}; - -/** - * Arena-compatible allocator that calls malloc() and free(); see - * goodMallocSize() in Malloc.h for goodSize(). - */ -class SysAlloc { - public: - void* allocate(size_t size) { - return checkedMalloc(size); - } - - void deallocate(void* p) { - free(p); - } -}; - -template <> -struct ArenaAllocatorTraits { - static size_t goodSize(const SysAlloc& alloc, size_t size) { - return goodMallocSize(size); - } -}; - -/** - * Arena that uses the system allocator (malloc / free) - */ -class SysArena : public Arena { - public: - explicit SysArena( - size_t minBlockSize = kDefaultMinBlockSize, - size_t sizeLimit = 0) - : Arena(SysAlloc(), minBlockSize, sizeLimit) { - } -}; - -} // namespace folly - -#include "folly/Arena-inl.h" - -#endif /* FOLLY_ARENA_H_ */ diff --git a/hphp/third_party/folly/folly/AtomicHashArray-inl.h b/hphp/third_party/folly/folly/AtomicHashArray-inl.h deleted file mode 100644 index 236c7c223..000000000 --- a/hphp/third_party/folly/folly/AtomicHashArray-inl.h +++ /dev/null @@ -1,379 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_ATOMICHASHARRAY_H_ -#error "This should only be included by AtomicHashArray.h" -#endif - -#include "folly/Bits.h" -#include "folly/detail/AtomicHashUtils.h" - -namespace folly { - -// AtomicHashArray private constructor -- -template -AtomicHashArray:: -AtomicHashArray(size_t capacity, KeyT emptyKey, KeyT lockedKey, - KeyT erasedKey, double maxLoadFactor, size_t cacheSize) - : capacity_(capacity), maxEntries_(size_t(maxLoadFactor * capacity_ + 0.5)), - kEmptyKey_(emptyKey), kLockedKey_(lockedKey), kErasedKey_(erasedKey), - kAnchorMask_(nextPowTwo(capacity_) - 1), numEntries_(0, cacheSize), - numPendingEntries_(0, cacheSize), isFull_(0), numErases_(0) { -} - -/* - * findInternal -- - * - * Sets ret.second to value found and ret.index to index - * of key and returns true, or if key does not exist returns false and - * ret.index is set to capacity_. - */ -template -typename AtomicHashArray::SimpleRetT -AtomicHashArray:: -findInternal(const KeyT key_in) { - DCHECK_NE(key_in, kEmptyKey_); - DCHECK_NE(key_in, kLockedKey_); - DCHECK_NE(key_in, kErasedKey_); - for (size_t idx = keyToAnchorIdx(key_in), numProbes = 0; - ; - idx = probeNext(idx, numProbes)) { - const KeyT key = acquireLoadKey(cells_[idx]); - if (LIKELY(EqualFcn()(key, key_in))) { - return SimpleRetT(idx, true); - } - if (UNLIKELY(key == kEmptyKey_)) { - // if we hit an empty element, this key does not exist - return SimpleRetT(capacity_, false); - } - ++numProbes; - if (UNLIKELY(numProbes >= capacity_)) { - // probed every cell...fail - return SimpleRetT(capacity_, false); - } - } -} - -/* - * insertInternal -- - * - * Returns false on failure due to key collision or full. - * Also sets ret.index to the index of the key. If the map is full, sets - * ret.index = capacity_. Also sets ret.second to cell value, thus if insert - * successful this will be what we just inserted, if there is a key collision - * this will be the previously inserted value, and if the map is full it is - * default. - */ -template -template -typename AtomicHashArray::SimpleRetT -AtomicHashArray:: -insertInternal(KeyT key_in, T&& value) { - const short NO_NEW_INSERTS = 1; - const short NO_PENDING_INSERTS = 2; - CHECK_NE(key_in, kEmptyKey_); - CHECK_NE(key_in, kLockedKey_); - CHECK_NE(key_in, kErasedKey_); - - size_t idx = keyToAnchorIdx(key_in); - size_t numProbes = 0; - for (;;) { - DCHECK_LT(idx, capacity_); - value_type* cell = &cells_[idx]; - if (relaxedLoadKey(*cell) == kEmptyKey_) { - // NOTE: isFull_ is set based on numEntries_.readFast(), so it's - // possible to insert more than maxEntries_ entries. However, it's not - // possible to insert past capacity_. - ++numPendingEntries_; - if (isFull_.load(std::memory_order_acquire)) { - --numPendingEntries_; - - // Before deciding whether this insert succeeded, this thread needs to - // wait until no other thread can add a new entry. - - // Correctness assumes isFull_ is true at this point. If - // another thread now does ++numPendingEntries_, we expect it - // to pass the isFull_.load() test above. (It shouldn't insert - // a new entry.) - FOLLY_SPIN_WAIT( - isFull_.load(std::memory_order_acquire) != NO_PENDING_INSERTS - && numPendingEntries_.readFull() != 0 - ); - isFull_.store(NO_PENDING_INSERTS, std::memory_order_release); - - if (relaxedLoadKey(*cell) == kEmptyKey_) { - // Don't insert past max load factor - return SimpleRetT(capacity_, false); - } - } else { - // An unallocated cell. Try once to lock it. If we succeed, insert here. - // If we fail, fall through to comparison below; maybe the insert that - // just beat us was for this very key.... - if (tryLockCell(cell)) { - // Write the value - done before unlocking - try { - DCHECK(relaxedLoadKey(*cell) == kLockedKey_); - /* - * This happens using the copy constructor because we won't have - * constructed a lhs to use an assignment operator on when - * values are being set. - */ - new (&cell->second) ValueT(std::forward(value)); - unlockCell(cell, key_in); // Sets the new key - } catch (...) { - // Transition back to empty key---requires handling - // locked->empty below. - unlockCell(cell, kEmptyKey_); - --numPendingEntries_; - throw; - } - // Direct comparison rather than EqualFcn ok here - // (we just inserted it) - DCHECK(relaxedLoadKey(*cell) == key_in); - --numPendingEntries_; - ++numEntries_; // This is a thread cached atomic increment :) - if (numEntries_.readFast() >= maxEntries_) { - isFull_.store(NO_NEW_INSERTS, std::memory_order_relaxed); - } - return SimpleRetT(idx, true); - } - --numPendingEntries_; - } - } - DCHECK(relaxedLoadKey(*cell) != kEmptyKey_); - if (kLockedKey_ == acquireLoadKey(*cell)) { - FOLLY_SPIN_WAIT( - kLockedKey_ == acquireLoadKey(*cell) - ); - } - - const KeyT thisKey = acquireLoadKey(*cell); - if (EqualFcn()(thisKey, key_in)) { - // Found an existing entry for our key, but we don't overwrite the - // previous value. - return SimpleRetT(idx, false); - } else if (thisKey == kEmptyKey_ || thisKey == kLockedKey_) { - // We need to try again (i.e., don't increment numProbes or - // advance idx): this case can happen if the constructor for - // ValueT threw for this very cell (the rethrow block above). - continue; - } - - ++numProbes; - if (UNLIKELY(numProbes >= capacity_)) { - // probed every cell...fail - return SimpleRetT(capacity_, false); - } - - idx = probeNext(idx, numProbes); - } -} - - -/* - * erase -- - * - * This will attempt to erase the given key key_in if the key is found. It - * returns 1 iff the key was located and marked as erased, and 0 otherwise. - * - * Memory is not freed or reclaimed by erase, i.e. the cell containing the - * erased key will never be reused. If there's an associated value, we won't - * touch it either. - */ -template -size_t AtomicHashArray:: -erase(KeyT key_in) { - CHECK_NE(key_in, kEmptyKey_); - CHECK_NE(key_in, kLockedKey_); - CHECK_NE(key_in, kErasedKey_); - for (size_t idx = keyToAnchorIdx(key_in), numProbes = 0; - ; - idx = probeNext(idx, numProbes)) { - DCHECK_LT(idx, capacity_); - value_type* cell = &cells_[idx]; - KeyT currentKey = acquireLoadKey(*cell); - if (currentKey == kEmptyKey_ || currentKey == kLockedKey_) { - // If we hit an empty (or locked) element, this key does not exist. This - // is similar to how it's handled in find(). - return 0; - } - if (EqualFcn()(currentKey, key_in)) { - // Found an existing entry for our key, attempt to mark it erased. - // Some other thread may have erased our key, but this is ok. - KeyT expect = currentKey; - if (cellKeyPtr(*cell)->compare_exchange_strong(expect, kErasedKey_)) { - numErases_.fetch_add(1, std::memory_order_relaxed); - - // Even if there's a value in the cell, we won't delete (or even - // default construct) it because some other thread may be accessing it. - // Locking it meanwhile won't work either since another thread may be - // holding a pointer to it. - - // We found the key and successfully erased it. - return 1; - } - // If another thread succeeds in erasing our key, we'll stop our search. - return 0; - } - ++numProbes; - if (UNLIKELY(numProbes >= capacity_)) { - // probed every cell...fail - return 0; - } - } -} - -template -const typename AtomicHashArray::Config -AtomicHashArray::defaultConfig; - -template -typename AtomicHashArray::SmartPtr -AtomicHashArray:: -create(size_t maxSize, const Config& c) { - CHECK_LE(c.maxLoadFactor, 1.0); - CHECK_GT(c.maxLoadFactor, 0.0); - CHECK_NE(c.emptyKey, c.lockedKey); - size_t capacity = size_t(maxSize / c.maxLoadFactor); - size_t sz = sizeof(AtomicHashArray) + sizeof(value_type) * capacity; - - std::unique_ptr mem(malloc(sz), free); - new(mem.get()) AtomicHashArray(capacity, c.emptyKey, c.lockedKey, c.erasedKey, - c.maxLoadFactor, c.entryCountThreadCacheSize); - SmartPtr map(static_cast(mem.release())); - - /* - * Mark all cells as empty. - * - * Note: we're bending the rules a little here accessing the key - * element in our cells even though the cell object has not been - * constructed, and casting them to atomic objects (see cellKeyPtr). - * (Also, in fact we never actually invoke the value_type - * constructor.) This is in order to avoid needing to default - * construct a bunch of value_type when we first start up: if you - * have an expensive default constructor for the value type this can - * noticeably speed construction time for an AHA. - */ - FOR_EACH_RANGE(i, 0, map->capacity_) { - cellKeyPtr(map->cells_[i])->store(map->kEmptyKey_, - std::memory_order_relaxed); - } - return map; -} - -template -void AtomicHashArray:: -destroy(AtomicHashArray* p) { - assert(p); - FOR_EACH_RANGE(i, 0, p->capacity_) { - if (p->cells_[i].first != p->kEmptyKey_) { - p->cells_[i].~value_type(); - } - } - p->~AtomicHashArray(); - free(p); -} - -// clear -- clears all keys and values in the map and resets all counters -template -void AtomicHashArray:: -clear() { - FOR_EACH_RANGE(i, 0, capacity_) { - if (cells_[i].first != kEmptyKey_) { - cells_[i].~value_type(); - *const_cast(&cells_[i].first) = kEmptyKey_; - } - CHECK(cells_[i].first == kEmptyKey_); - } - numEntries_.set(0); - numPendingEntries_.set(0); - isFull_.store(0, std::memory_order_relaxed); - numErases_.store(0, std::memory_order_relaxed); -} - - -// Iterator implementation - -template -template -struct AtomicHashArray::aha_iterator - : boost::iterator_facade, - IterVal, - boost::forward_traversal_tag> -{ - explicit aha_iterator() : aha_(0) {} - - // Conversion ctor for interoperability between const_iterator and - // iterator. The enable_if<> magic keeps us well-behaved for - // is_convertible<> (v. the iterator_facade documentation). - template - aha_iterator(const aha_iterator& o, - typename std::enable_if< - std::is_convertible::value >::type* = 0) - : aha_(o.aha_) - , offset_(o.offset_) - {} - - explicit aha_iterator(ContT* array, size_t offset) - : aha_(array) - , offset_(offset) - { - advancePastEmpty(); - } - - // Returns unique index that can be used with findAt(). - // WARNING: The following function will fail silently for hashtable - // with capacity > 2^32 - uint32_t getIndex() const { return offset_; } - - private: - friend class AtomicHashArray; - friend class boost::iterator_core_access; - - void increment() { - ++offset_; - advancePastEmpty(); - } - - bool equal(const aha_iterator& o) const { - return aha_ == o.aha_ && offset_ == o.offset_; - } - - IterVal& dereference() const { - return aha_->cells_[offset_]; - } - - void advancePastEmpty() { - while (offset_ < aha_->capacity_ && !isValid()) { - ++offset_; - } - } - - bool isValid() const { - KeyT key = acquireLoadKey(aha_->cells_[offset_]); - return key != aha_->kEmptyKey_ && - key != aha_->kLockedKey_ && - key != aha_->kErasedKey_; - } - - private: - ContT* aha_; - size_t offset_; -}; // aha_iterator - -} // namespace folly - -#undef FOLLY_SPIN_WAIT diff --git a/hphp/third_party/folly/folly/AtomicHashArray.h b/hphp/third_party/folly/folly/AtomicHashArray.h deleted file mode 100644 index e6b6c7213..000000000 --- a/hphp/third_party/folly/folly/AtomicHashArray.h +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright 2013 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. - */ - -/** - * AtomicHashArray is the building block for AtomicHashMap. It provides the - * core lock-free functionality, but is limitted by the fact that it cannot - * grow past it's initialization size and is a little more awkward (no public - * constructor, for example). If you're confident that you won't run out of - * space, don't mind the awkardness, and really need bare-metal performance, - * feel free to use AHA directly. - * - * Check out AtomicHashMap.h for more thorough documentation on perf and - * general pros and cons relative to other hash maps. - * - * @author Spencer Ahrens - * @author Jordan DeLong - */ - -#ifndef FOLLY_ATOMICHASHARRAY_H_ -#define FOLLY_ATOMICHASHARRAY_H_ - -#include - -#include -#include - -#include "folly/Hash.h" -#include "folly/ThreadCachedInt.h" - -namespace folly { - -template , class EqualFcn = std::equal_to> -class AtomicHashMap; - -template , class EqualFcn = std::equal_to> -class AtomicHashArray : boost::noncopyable { - static_assert((std::is_convertible::value || - std::is_convertible::value || - std::is_convertible::value), - "You are trying to use AtomicHashArray with disallowed key " - "types. You must use atomically compare-and-swappable integer " - "keys, or a different container class."); - public: - typedef KeyT key_type; - typedef ValueT mapped_type; - typedef std::pair value_type; - typedef std::size_t size_type; - typedef std::ptrdiff_t difference_type; - typedef value_type& reference; - typedef const value_type& const_reference; - typedef value_type* pointer; - typedef const value_type* const_pointer; - - const size_t capacity_; - const size_t maxEntries_; - const KeyT kEmptyKey_; - const KeyT kLockedKey_; - const KeyT kErasedKey_; - - template - struct aha_iterator; - - typedef aha_iterator const_iterator; - typedef aha_iterator iterator; - - // You really shouldn't need this if you use the SmartPtr provided by create, - // but if you really want to do something crazy like stick the released - // pointer into a DescriminatedPtr or something, you'll need this to clean up - // after yourself. - static void destroy(AtomicHashArray*); - - private: - const size_t kAnchorMask_; - - struct Deleter { - void operator()(AtomicHashArray* ptr) { - AtomicHashArray::destroy(ptr); - } - }; - - public: - typedef std::unique_ptr SmartPtr; - - /* - * create -- - * - * Creates AtomicHashArray objects. Use instead of constructor/destructor. - * - * We do things this way in order to avoid the perf penalty of a second - * pointer indirection when composing these into AtomicHashMap, which needs - * to store an array of pointers so that it can perform atomic operations on - * them when growing. - * - * Instead of a mess of arguments, we take a max size and a Config struct to - * simulate named ctor parameters. The Config struct has sensible defaults - * for everything, but is overloaded - if you specify a positive capacity, - * that will be used directly instead of computing it based on - * maxLoadFactor. - * - * Create returns an AHA::SmartPtr which is a unique_ptr with a custom - * deleter to make sure everything is cleaned up properly. - */ - struct Config { - KeyT emptyKey; - KeyT lockedKey; - KeyT erasedKey; - double maxLoadFactor; - double growthFactor; - int entryCountThreadCacheSize; - size_t capacity; // if positive, overrides maxLoadFactor - - constexpr Config() : emptyKey((KeyT)-1), - lockedKey((KeyT)-2), - erasedKey((KeyT)-3), - maxLoadFactor(0.8), - growthFactor(-1), - entryCountThreadCacheSize(1000), - capacity(0) {} - }; - - static const Config defaultConfig; - static SmartPtr create(size_t maxSize, const Config& = defaultConfig); - - iterator find(KeyT k) { - return iterator(this, findInternal(k).idx); - } - const_iterator find(KeyT k) const { - return const_cast(this)->find(k); - } - - /* - * insert -- - * - * Returns a pair with iterator to the element at r.first and bool success. - * Retrieve the index with ret.first.getIndex(). - * - * Fails on key collision (does not overwrite) or if map becomes - * full, at which point no element is inserted, iterator is set to end(), - * and success is set false. On collisions, success is set false, but the - * iterator is set to the existing entry. - */ - std::pair insert(const value_type& r) { - SimpleRetT ret = insertInternal(r.first, r.second); - return std::make_pair(iterator(this, ret.idx), ret.success); - } - std::pair insert(value_type&& r) { - SimpleRetT ret = insertInternal(r.first, std::move(r.second)); - return std::make_pair(iterator(this, ret.idx), ret.success); - } - - // returns the number of elements erased - should never exceed 1 - size_t erase(KeyT k); - - // clears all keys and values in the map and resets all counters. Not thread - // safe. - void clear(); - - // Exact number of elements in the map - note that readFull() acquires a - // mutex. See folly/ThreadCachedInt.h for more details. - size_t size() const { - return numEntries_.readFull() - - numErases_.load(std::memory_order_relaxed); - } - - bool empty() const { return size() == 0; } - - iterator begin() { return iterator(this, 0); } - iterator end() { return iterator(this, capacity_); } - const_iterator begin() const { return const_iterator(this, 0); } - const_iterator end() const { return const_iterator(this, capacity_); } - - // See AtomicHashMap::findAt - access elements directly - // WARNING: The following 2 functions will fail silently for hashtable - // with capacity > 2^32 - iterator findAt(uint32_t idx) { - DCHECK_LT(idx, capacity_); - return iterator(this, idx); - } - const_iterator findAt(uint32_t idx) const { - return const_cast(this)->findAt(idx); - } - - iterator makeIter(size_t idx) { return iterator(this, idx); } - const_iterator makeIter(size_t idx) const { - return const_iterator(this, idx); - } - - // The max load factor allowed for this map - double maxLoadFactor() const { return ((double) maxEntries_) / capacity_; } - - void setEntryCountThreadCacheSize(uint32_t newSize) { - numEntries_.setCacheSize(newSize); - numPendingEntries_.setCacheSize(newSize); - } - - int getEntryCountThreadCacheSize() const { - return numEntries_.getCacheSize(); - } - - /* Private data and helper functions... */ - - private: - friend class AtomicHashMap; - - struct SimpleRetT { size_t idx; bool success; - SimpleRetT(size_t i, bool s) : idx(i), success(s) {} - SimpleRetT() {} - }; - - template - SimpleRetT insertInternal(KeyT key, T&& value); - - SimpleRetT findInternal(const KeyT key); - - static std::atomic* cellKeyPtr(const value_type& r) { - // We need some illegal casting here in order to actually store - // our value_type as a std::pair. But a little bit of - // undefined behavior never hurt anyone ... - static_assert(sizeof(std::atomic) == sizeof(KeyT), - "std::atomic is implemented in an unexpected way for AHM"); - return - const_cast*>( - reinterpret_cast const*>(&r.first)); - } - - static KeyT relaxedLoadKey(const value_type& r) { - return cellKeyPtr(r)->load(std::memory_order_relaxed); - } - - static KeyT acquireLoadKey(const value_type& r) { - return cellKeyPtr(r)->load(std::memory_order_acquire); - } - - // Fun with thread local storage - atomic increment is expensive - // (relatively), so we accumulate in the thread cache and periodically - // flush to the actual variable, and walk through the unflushed counts when - // reading the value, so be careful of calling size() too frequently. This - // increases insertion throughput several times over while keeping the count - // accurate. - ThreadCachedInt numEntries_; // Successful key inserts - ThreadCachedInt numPendingEntries_; // Used by insertInternal - std::atomic isFull_; // Used by insertInternal - std::atomic numErases_; // Successful key erases - - value_type cells_[0]; // This must be the last field of this class - - // Force constructor/destructor private since create/destroy should be - // used externally instead - AtomicHashArray(size_t capacity, KeyT emptyKey, KeyT lockedKey, - KeyT erasedKey, double maxLoadFactor, size_t cacheSize); - - ~AtomicHashArray() {} - - inline void unlockCell(value_type* const cell, KeyT newKey) { - cellKeyPtr(*cell)->store(newKey, std::memory_order_release); - } - - inline bool tryLockCell(value_type* const cell) { - KeyT expect = kEmptyKey_; - return cellKeyPtr(*cell)->compare_exchange_strong(expect, kLockedKey_, - std::memory_order_acq_rel); - } - - inline size_t keyToAnchorIdx(const KeyT k) const { - const size_t hashVal = HashFcn()(k); - const size_t probe = hashVal & kAnchorMask_; - return LIKELY(probe < capacity_) ? probe : hashVal % capacity_; - } - - inline size_t probeNext(size_t idx, size_t numProbes) { - //idx += numProbes; // quadratic probing - idx += 1; // linear probing - // Avoid modulus because it's slow - return LIKELY(idx < capacity_) ? idx : (idx - capacity_); - } -}; // AtomicHashArray - -} // namespace folly - -#include "AtomicHashArray-inl.h" - -#endif // FOLLY_ATOMICHASHARRAY_H_ diff --git a/hphp/third_party/folly/folly/AtomicHashMap-inl.h b/hphp/third_party/folly/folly/AtomicHashMap-inl.h deleted file mode 100644 index 1548b7f06..000000000 --- a/hphp/third_party/folly/folly/AtomicHashMap-inl.h +++ /dev/null @@ -1,413 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_ATOMICHASHMAP_H_ -#error "This should only be included by AtomicHashMap.h" -#endif - -#include "folly/detail/AtomicHashUtils.h" - -namespace folly { - -template -const typename AtomicHashMap::Config -AtomicHashMap::defaultConfig; - -// AtomicHashMap constructor -- Atomic wrapper that allows growth -// This class has a lot of overhead (184 Bytes) so only use for big maps -template -AtomicHashMap:: -AtomicHashMap(size_t size, const Config& config) - : kGrowthFrac_(config.growthFactor < 0 ? - 1.0 - config.maxLoadFactor : config.growthFactor) { - CHECK(config.maxLoadFactor > 0.0 && config.maxLoadFactor < 1.0); - subMaps_[0].store(SubMap::create(size, config).release(), - std::memory_order_relaxed); - auto numSubMaps = kNumSubMaps_; - FOR_EACH_RANGE(i, 1, numSubMaps) { - subMaps_[i].store(nullptr, std::memory_order_relaxed); - } - numMapsAllocated_.store(1, std::memory_order_relaxed); -} - -// insert -- -template -std::pair::iterator,bool> -AtomicHashMap:: -insert(key_type k, const mapped_type& v) { - SimpleRetT ret = insertInternal(k,v); - SubMap* subMap = subMaps_[ret.i].load(std::memory_order_relaxed); - return std::make_pair(iterator(this, ret.i, subMap->makeIter(ret.j)), - ret.success); -} - -template -std::pair::iterator,bool> -AtomicHashMap:: -insert(key_type k, mapped_type&& v) { - SimpleRetT ret = insertInternal(k, std::move(v)); - SubMap* subMap = subMaps_[ret.i].load(std::memory_order_relaxed); - return std::make_pair(iterator(this, ret.i, subMap->makeIter(ret.j)), - ret.success); -} - -// insertInternal -- Allocates new sub maps as existing ones fill up. -template -template -typename AtomicHashMap::SimpleRetT -AtomicHashMap:: -insertInternal(key_type key, T&& value) { - beginInsertInternal: - int nextMapIdx = // this maintains our state - numMapsAllocated_.load(std::memory_order_acquire); - typename SubMap::SimpleRetT ret; - FOR_EACH_RANGE(i, 0, nextMapIdx) { - // insert in each map successively. If one succeeds, we're done! - SubMap* subMap = subMaps_[i].load(std::memory_order_relaxed); - ret = subMap->insertInternal(key, std::forward(value)); - if (ret.idx == subMap->capacity_) { - continue; //map is full, so try the next one - } - // Either collision or success - insert in either case - return SimpleRetT(i, ret.idx, ret.success); - } - - // If we made it this far, all maps are full and we need to try to allocate - // the next one. - - SubMap* primarySubMap = subMaps_[0].load(std::memory_order_relaxed); - if (nextMapIdx >= kNumSubMaps_ || - primarySubMap->capacity_ * kGrowthFrac_ < 1.0) { - // Can't allocate any more sub maps. - throw AtomicHashMapFullError(); - } - - if (tryLockMap(nextMapIdx)) { - // Alloc a new map and shove it in. We can change whatever - // we want because other threads are waiting on us... - size_t numCellsAllocated = (size_t) - (primarySubMap->capacity_ * - std::pow(1.0 + kGrowthFrac_, nextMapIdx - 1)); - size_t newSize = (int) (numCellsAllocated * kGrowthFrac_); - DCHECK(subMaps_[nextMapIdx].load(std::memory_order_relaxed) == - (SubMap*)kLockedPtr_); - // create a new map using the settings stored in the first map - - Config config; - config.emptyKey = primarySubMap->kEmptyKey_; - config.lockedKey = primarySubMap->kLockedKey_; - config.erasedKey = primarySubMap->kErasedKey_; - config.maxLoadFactor = primarySubMap->maxLoadFactor(); - config.entryCountThreadCacheSize = - primarySubMap->getEntryCountThreadCacheSize(); - subMaps_[nextMapIdx].store(SubMap::create(newSize, config).release(), - std::memory_order_relaxed); - - // Publish the map to other threads. - numMapsAllocated_.fetch_add(1, std::memory_order_release); - DCHECK_EQ(nextMapIdx + 1, - numMapsAllocated_.load(std::memory_order_relaxed)); - } else { - // If we lost the race, we'll have to wait for the next map to get - // allocated before doing any insertion here. - FOLLY_SPIN_WAIT( - nextMapIdx >= numMapsAllocated_.load(std::memory_order_acquire) - ); - } - - // Relaxed is ok here because either we just created this map, or we - // just did a spin wait with an acquire load on numMapsAllocated_. - SubMap* loadedMap = subMaps_[nextMapIdx].load(std::memory_order_relaxed); - DCHECK(loadedMap && loadedMap != (SubMap*)kLockedPtr_); - ret = loadedMap->insertInternal(key, std::forward(value)); - if (ret.idx != loadedMap->capacity_) { - return SimpleRetT(nextMapIdx, ret.idx, ret.success); - } - // We took way too long and the new map is already full...try again from - // the top (this should pretty much never happen). - goto beginInsertInternal; -} - -// find -- -template -typename AtomicHashMap::iterator -AtomicHashMap:: -find(KeyT k) { - SimpleRetT ret = findInternal(k); - if (ret.i >= numMapsAllocated_.load(std::memory_order_acquire)) { - return end(); - } - SubMap* subMap = subMaps_[ret.i].load(std::memory_order_relaxed); - return iterator(this, ret.i, subMap->makeIter(ret.j)); -} - -template -typename AtomicHashMap::const_iterator -AtomicHashMap:: -find(KeyT k) const { - return const_cast(this)->find(k); -} - -// findInternal -- -template -typename AtomicHashMap::SimpleRetT -AtomicHashMap:: -findInternal(const KeyT k) const { - SubMap* const primaryMap = subMaps_[0].load(std::memory_order_relaxed); - typename SubMap::SimpleRetT ret = primaryMap->findInternal(k); - if (LIKELY(ret.idx != primaryMap->capacity_)) { - return SimpleRetT(0, ret.idx, ret.success); - } - int const numMaps = numMapsAllocated_.load(std::memory_order_acquire); - FOR_EACH_RANGE(i, 1, numMaps) { - // Check each map successively. If one succeeds, we're done! - SubMap* thisMap = subMaps_[i].load(std::memory_order_relaxed); - ret = thisMap->findInternal(k); - if (LIKELY(ret.idx != thisMap->capacity_)) { - return SimpleRetT(i, ret.idx, ret.success); - } - } - // Didn't find our key... - return SimpleRetT(numMaps, 0, false); -} - -// findAtInternal -- see encodeIndex() for details. -template -typename AtomicHashMap::SimpleRetT -AtomicHashMap:: -findAtInternal(uint32_t idx) const { - uint32_t subMapIdx, subMapOffset; - if (idx & kSecondaryMapBit_) { - // idx falls in a secondary map - idx &= ~kSecondaryMapBit_; // unset secondary bit - subMapIdx = idx >> kSubMapIndexShift_; - DCHECK_LT(subMapIdx, numMapsAllocated_.load(std::memory_order_relaxed)); - subMapOffset = idx & kSubMapIndexMask_; - } else { - // idx falls in primary map - subMapIdx = 0; - subMapOffset = idx; - } - return SimpleRetT(subMapIdx, subMapOffset, true); -} - -// erase -- -template -typename AtomicHashMap::size_type -AtomicHashMap:: -erase(const KeyT k) { - int const numMaps = numMapsAllocated_.load(std::memory_order_acquire); - FOR_EACH_RANGE(i, 0, numMaps) { - // Check each map successively. If one succeeds, we're done! - if (subMaps_[i].load(std::memory_order_relaxed)->erase(k)) { - return 1; - } - } - // Didn't find our key... - return 0; -} - -// capacity -- summation of capacities of all submaps -template -size_t AtomicHashMap:: -capacity() const { - size_t totalCap(0); - int const numMaps = numMapsAllocated_.load(std::memory_order_acquire); - FOR_EACH_RANGE(i, 0, numMaps) { - totalCap += subMaps_[i].load(std::memory_order_relaxed)->capacity_; - } - return totalCap; -} - -// spaceRemaining -- -// number of new insertions until current submaps are all at max load -template -size_t AtomicHashMap:: -spaceRemaining() const { - size_t spaceRem(0); - int const numMaps = numMapsAllocated_.load(std::memory_order_acquire); - FOR_EACH_RANGE(i, 0, numMaps) { - SubMap* thisMap = subMaps_[i].load(std::memory_order_relaxed); - spaceRem += std::max( - 0, - thisMap->maxEntries_ - &thisMap->numEntries_.readFull() - ); - } - return spaceRem; -} - -// clear -- Wipes all keys and values from primary map and destroys -// all secondary maps. Not thread safe. -template -void AtomicHashMap:: -clear() { - subMaps_[0].load(std::memory_order_relaxed)->clear(); - int const numMaps = numMapsAllocated_ - .load(std::memory_order_relaxed); - FOR_EACH_RANGE(i, 1, numMaps) { - SubMap* thisMap = subMaps_[i].load(std::memory_order_relaxed); - DCHECK(thisMap); - SubMap::destroy(thisMap); - subMaps_[i].store(nullptr, std::memory_order_relaxed); - } - numMapsAllocated_.store(1, std::memory_order_relaxed); -} - -// size -- -template -size_t AtomicHashMap:: -size() const { - size_t totalSize(0); - int const numMaps = numMapsAllocated_.load(std::memory_order_acquire); - FOR_EACH_RANGE(i, 0, numMaps) { - totalSize += subMaps_[i].load(std::memory_order_relaxed)->size(); - } - return totalSize; -} - -// encodeIndex -- Encode the submap index and offset into return. -// index_ret must be pre-populated with the submap offset. -// -// We leave index_ret untouched when referring to the primary map -// so it can be as large as possible (31 data bits). Max size of -// secondary maps is limited by what can fit in the low 27 bits. -// -// Returns the following bit-encoded data in index_ret: -// if subMap == 0 (primary map) => -// bit(s) value -// 31 0 -// 0-30 submap offset (index_ret input) -// -// if subMap > 0 (secondary maps) => -// bit(s) value -// 31 1 -// 27-30 which subMap -// 0-26 subMap offset (index_ret input) -template -inline uint32_t AtomicHashMap:: -encodeIndex(uint32_t subMap, uint32_t offset) { - DCHECK_EQ(offset & kSecondaryMapBit_, 0); // offset can't be too big - if (subMap == 0) return offset; - // Make sure subMap isn't too big - DCHECK_EQ(subMap >> kNumSubMapBits_, 0); - // Make sure subMap bits of offset are clear - DCHECK_EQ(offset & (~kSubMapIndexMask_ | kSecondaryMapBit_), 0); - - // Set high-order bits to encode which submap this index belongs to - return offset | (subMap << kSubMapIndexShift_) | kSecondaryMapBit_; -} - - -// Iterator implementation - -template -template -struct AtomicHashMap::ahm_iterator - : boost::iterator_facade, - IterVal, - boost::forward_traversal_tag> -{ - explicit ahm_iterator() : ahm_(0) {} - - // Conversion ctor for interoperability between const_iterator and - // iterator. The enable_if<> magic keeps us well-behaved for - // is_convertible<> (v. the iterator_facade documentation). - template - ahm_iterator(const ahm_iterator& o, - typename std::enable_if< - std::is_convertible::value >::type* = 0) - : ahm_(o.ahm_) - , subMap_(o.subMap_) - , subIt_(o.subIt_) - {} - - /* - * Returns the unique index that can be used for access directly - * into the data storage. - */ - uint32_t getIndex() const { - CHECK(!isEnd()); - return ahm_->encodeIndex(subMap_, subIt_.getIndex()); - } - - private: - friend class AtomicHashMap; - explicit ahm_iterator(ContT* ahm, - uint32_t subMap, - const SubIt& subIt) - : ahm_(ahm) - , subMap_(subMap) - , subIt_(subIt) - { - checkAdvanceToNextSubmap(); - } - - friend class boost::iterator_core_access; - - void increment() { - CHECK(!isEnd()); - ++subIt_; - checkAdvanceToNextSubmap(); - } - - bool equal(const ahm_iterator& other) const { - if (ahm_ != other.ahm_) { - return false; - } - - if (isEnd() || other.isEnd()) { - return isEnd() == other.isEnd(); - } - - return subMap_ == other.subMap_ && - subIt_ == other.subIt_; - } - - IterVal& dereference() const { - return *subIt_; - } - - bool isEnd() const { return ahm_ == nullptr; } - - void checkAdvanceToNextSubmap() { - if (isEnd()) { - return; - } - - SubMap* thisMap = ahm_->subMaps_[subMap_]. - load(std::memory_order_relaxed); - if (subIt_ == thisMap->end()) { - // This sub iterator is done, advance to next one - if (subMap_ + 1 < - ahm_->numMapsAllocated_.load(std::memory_order_acquire)) { - ++subMap_; - thisMap = ahm_->subMaps_[subMap_].load(std::memory_order_relaxed); - subIt_ = thisMap->begin(); - } else { - ahm_ = nullptr; - } - } - } - - private: - ContT* ahm_; - uint32_t subMap_; - SubIt subIt_; -}; // ahm_iterator - -} // namespace folly - -#undef FOLLY_SPIN_WAIT diff --git a/hphp/third_party/folly/folly/AtomicHashMap.h b/hphp/third_party/folly/folly/AtomicHashMap.h deleted file mode 100644 index 2738f3997..000000000 --- a/hphp/third_party/folly/folly/AtomicHashMap.h +++ /dev/null @@ -1,412 +0,0 @@ -/* - * Copyright 2013 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. - */ - -/* - * AtomicHashMap -- - * - * A high performance concurrent hash map with int32 or int64 keys. Supports - * insert, find(key), findAt(index), erase(key), size, and more. Memory cannot - * be freed or reclaimed by erase. Can grow to a maximum of about 18 times the - * initial capacity, but performance degrades linearly with growth. Can also be - * used as an object store with unique 32-bit references directly into the - * internal storage (retrieved with iterator::getIndex()). - * - * Advantages: - * - High performance (~2-4x tbb::concurrent_hash_map in heavily - * multi-threaded environments). - * - Efficient memory usage if initial capacity is not over estimated - * (especially for small keys and values). - * - Good fragmentation properties (only allocates in large slabs which can - * be reused with clear() and never move). - * - Can generate unique, long-lived 32-bit references for efficient lookup - * (see findAt()). - * - * Disadvantages: - * - Keys must be native int32 or int64, or explicitly converted. - * - Must be able to specify unique empty, locked, and erased keys - * - Performance degrades linearly as size grows beyond initialization - * capacity. - * - Max size limit of ~18x initial size (dependent on max load factor). - * - Memory is not freed or reclaimed by erase. - * - * Usage and Operation Details: - * Simple performance/memory tradeoff with maxLoadFactor. Higher load factors - * give better memory utilization but probe lengths increase, reducing - * performance. - * - * Implementation and Performance Details: - * AHArray is a fixed size contiguous block of value_type cells. When - * writing a cell, the key is locked while the rest of the record is - * written. Once done, the cell is unlocked by setting the key. find() - * is completely wait-free and doesn't require any non-relaxed atomic - * operations. AHA cannot grow beyond initialization capacity, but is - * faster because of reduced data indirection. - * - * AHMap is a wrapper around AHArray sub-maps that allows growth and provides - * an interface closer to the stl UnorderedAssociativeContainer concept. These - * sub-maps are allocated on the fly and are processed in series, so the more - * there are (from growing past initial capacity), the worse the performance. - * - * Insert returns false if there is a key collision and throws if the max size - * of the map is exceeded. - * - * Benchmark performance with 8 simultaneous threads processing 1 million - * unique entries on a 4-core, 2.5 GHz machine: - * - * Load Factor Mem Efficiency usec/Insert usec/Find - * 50% 50% 0.19 0.05 - * 85% 85% 0.20 0.06 - * 90% 90% 0.23 0.08 - * 95% 95% 0.27 0.10 - * - * See folly/tests/AtomicHashMapTest.cpp for more benchmarks. - * - * @author Spencer Ahrens - * @author Jordan DeLong - * - */ - -#ifndef FOLLY_ATOMICHASHMAP_H_ -#define FOLLY_ATOMICHASHMAP_H_ - -#include -#include -#include - -#include -#include -#include - -#include "folly/AtomicHashArray.h" -#include "folly/Foreach.h" -#include "folly/Hash.h" -#include "folly/Likely.h" -#include "folly/ThreadCachedInt.h" - -namespace folly { - -/* - * AtomicHashMap provides an interface somewhat similar to the - * UnorderedAssociativeContainer concept in C++. This does not - * exactly match this concept (or even the basic Container concept), - * because of some restrictions imposed by our datastructure. - * - * Specific differences (there are quite a few): - * - * - Efficiently thread safe for inserts (main point of this stuff), - * wait-free for lookups. - * - * - You can erase from this container, but the cell containing the key will - * not be free or reclaimed. - * - * - You can erase everything by calling clear() (and you must guarantee only - * one thread can be using the container to do that). - * - * - We aren't DefaultConstructible, CopyConstructible, Assignable, or - * EqualityComparable. (Most of these are probably not something - * you actually want to do with this anyway.) - * - * - We don't support the various bucket functions, rehash(), - * reserve(), or equal_range(). Also no constructors taking - * iterators, although this could change. - * - * - Several insertion functions, notably operator[], are not - * implemented. It is a little too easy to misuse these functions - * with this container, where part of the point is that when an - * insertion happens for a new key, it will atomically have the - * desired value. - * - * - The map has no templated insert() taking an iterator range, but - * we do provide an insert(key, value). The latter seems more - * frequently useful for this container (to avoid sprinkling - * make_pair everywhere), and providing both can lead to some gross - * template error messages. - * - * - Not Allocator-aware. - * - * - KeyT must be a 32 bit or 64 bit atomic integer type, and you must - * define special 'locked' and 'empty' key values in the ctor - * - * - We don't take the Hash function object as an instance in the - * constructor. - * - */ - -// Thrown when insertion fails due to running out of space for -// submaps. -struct AtomicHashMapFullError : std::runtime_error { - explicit AtomicHashMapFullError() - : std::runtime_error("AtomicHashMap is full") - {} -}; - -template -class AtomicHashMap : boost::noncopyable { - typedef AtomicHashArray SubMap; - - public: - typedef KeyT key_type; - typedef ValueT mapped_type; - typedef std::pair value_type; - typedef HashFcn hasher; - typedef EqualFcn key_equal; - typedef value_type* pointer; - typedef value_type& reference; - typedef const value_type& const_reference; - typedef std::ptrdiff_t difference_type; - typedef std::size_t size_type; - typedef typename SubMap::Config Config; - - template - struct ahm_iterator; - - typedef ahm_iterator - const_iterator; - typedef ahm_iterator - iterator; - - public: - const float kGrowthFrac_; // How much to grow when we run out of capacity. - - // The constructor takes a finalSizeEst which is the optimal - // number of elements to maximize space utilization and performance, - // and a Config object to specify more advanced options. - static const Config defaultConfig; - explicit AtomicHashMap(size_t finalSizeEst, const Config& = defaultConfig); - - ~AtomicHashMap() { - const int numMaps = numMapsAllocated_.load(std::memory_order_relaxed); - FOR_EACH_RANGE (i, 0, numMaps) { - SubMap* thisMap = subMaps_[i].load(std::memory_order_relaxed); - DCHECK(thisMap); - SubMap::destroy(thisMap); - } - } - - key_equal key_eq() const { return key_equal(); } - hasher hash_function() const { return hasher(); } - - // TODO: emplace() support would be nice. - - /* - * insert -- - * - * Returns a pair with iterator to the element at r.first and - * success. Retrieve the index with ret.first.getIndex(). - * - * Does not overwrite on key collision, but returns an iterator to - * the existing element (since this could due to a race with - * another thread, it is often important to check this return - * value). - * - * Allocates new sub maps as the existing ones become full. If - * all sub maps are full, no element is inserted, and - * AtomicHashMapFullError is thrown. - */ - std::pair insert(const value_type& r) { - return insert(r.first, r.second); - } - std::pair insert(key_type k, const mapped_type& v); - std::pair insert(value_type&& r) { - return insert(r.first, std::move(r.second)); - } - std::pair insert(key_type k, mapped_type&& v); - - /* - * find -- - * - * Returns an iterator into the map. - * - * If the key is not found, returns end(). - */ - iterator find(key_type k); - const_iterator find(key_type k) const; - - /* - * erase -- - * - * Erases key k from the map - * - * Returns 1 iff the key is found and erased, and 0 otherwise. - */ - size_type erase(key_type k); - - /* - * clear -- - * - * Wipes all keys and values from primary map and destroys all secondary - * maps. Primary map remains allocated and thus the memory can be reused - * in place. Not thread safe. - * - */ - void clear(); - - /* - * size -- - * - * Returns the exact size of the map. Note this is not as cheap as typical - * size() implementations because, for each AtomicHashArray in this AHM, we - * need to grab a lock and accumulate the values from all the thread local - * counters. See folly/ThreadCachedInt.h for more details. - */ - size_t size() const; - - bool empty() const { return size() == 0; } - - size_type count(key_type k) const { - return find(k) == end() ? 0 : 1; - } - - - /* - * findAt -- - * - * Returns an iterator into the map. - * - * idx should only be an unmodified value returned by calling getIndex() on - * a valid iterator returned by find() or insert(). If idx is invalid you - * have a bug and the process aborts. - */ - iterator findAt(uint32_t idx) { - SimpleRetT ret = findAtInternal(idx); - DCHECK_LT(ret.i, numSubMaps()); - return iterator(this, ret.i, - subMaps_[ret.i].load(std::memory_order_relaxed)->makeIter(ret.j)); - } - const_iterator findAt(uint32_t idx) const { - return const_cast(this)->findAt(idx); - } - - // Total capacity - summation of capacities of all submaps. - size_t capacity() const; - - // Number of new insertions until current submaps are all at max load factor. - size_t spaceRemaining() const; - - void setEntryCountThreadCacheSize(int32_t newSize) { - const int numMaps = numMapsAllocated_.load(std::memory_order_acquire); - for (int i = 0; i < numMaps; ++i) { - SubMap* map = subMaps_[i].load(std::memory_order_relaxed); - map->setEntryCountThreadCacheSize(newSize); - } - } - - // Number of sub maps allocated so far to implement this map. The more there - // are, the worse the performance. - int numSubMaps() const { - return numMapsAllocated_.load(std::memory_order_acquire); - } - - iterator begin() { - return iterator(this, 0, - subMaps_[0].load(std::memory_order_relaxed)->begin()); - } - - iterator end() { - return iterator(); - } - - const_iterator begin() const { - return const_iterator(this, 0, - subMaps_[0].load(std::memory_order_relaxed)->begin()); - } - - const_iterator end() const { - return const_iterator(); - } - - /* Advanced functions for direct access: */ - - inline uint32_t recToIdx(const value_type& r, bool mayInsert = true) { - SimpleRetT ret = mayInsert ? - insertInternal(r.first, r.second) : findInternal(r.first); - return encodeIndex(ret.i, ret.j); - } - - inline uint32_t recToIdx(value_type&& r, bool mayInsert = true) { - SimpleRetT ret = mayInsert ? - insertInternal(r.first, std::move(r.second)) : findInternal(r.first); - return encodeIndex(ret.i, ret.j); - } - - inline uint32_t recToIdx(key_type k, const mapped_type& v, - bool mayInsert = true) { - SimpleRetT ret = mayInsert ? insertInternal(k, v) : findInternal(k); - return encodeIndex(ret.i, ret.j); - } - - inline uint32_t recToIdx(key_type k, mapped_type&& v, bool mayInsert = true) { - SimpleRetT ret = mayInsert ? - insertInternal(k, std::move(v)) : findInternal(k); - return encodeIndex(ret.i, ret.j); - } - - inline uint32_t keyToIdx(const KeyT k, bool mayInsert = false) { - return recToIdx(value_type(k), mayInsert); - } - - inline const value_type& idxToRec(uint32_t idx) const { - SimpleRetT ret = findAtInternal(idx); - return subMaps_[ret.i].load(std::memory_order_relaxed)->idxToRec(ret.j); - } - - /* Private data and helper functions... */ - - private: - // This limits primary submap size to 2^31 ~= 2 billion, secondary submap - // size to 2^(32 - kNumSubMapBits_ - 1) = 2^27 ~= 130 million, and num subMaps - // to 2^kNumSubMapBits_ = 16. - static const uint32_t kNumSubMapBits_ = 4; - static const uint32_t kSecondaryMapBit_ = 1u << 31; // Highest bit - static const uint32_t kSubMapIndexShift_ = 32 - kNumSubMapBits_ - 1; - static const uint32_t kSubMapIndexMask_ = (1 << kSubMapIndexShift_) - 1; - static const uint32_t kNumSubMaps_ = 1 << kNumSubMapBits_; - static const uintptr_t kLockedPtr_ = 0x88ul << 48; // invalid pointer - - struct SimpleRetT { uint32_t i; size_t j; bool success; - SimpleRetT(uint32_t ii, size_t jj, bool s) : i(ii), j(jj), success(s) {} - SimpleRetT() {} - }; - - template - SimpleRetT insertInternal(KeyT key, T&& value); - - SimpleRetT findInternal(const KeyT k) const; - - SimpleRetT findAtInternal(uint32_t idx) const; - - std::atomic subMaps_[kNumSubMaps_]; - std::atomic numMapsAllocated_; - - inline bool tryLockMap(int idx) { - SubMap* val = nullptr; - return subMaps_[idx].compare_exchange_strong(val, (SubMap*)kLockedPtr_, - std::memory_order_acquire); - } - - static inline uint32_t encodeIndex(uint32_t subMap, uint32_t subMapIdx); - -}; // AtomicHashMap - -} // namespace folly - -#include "AtomicHashMap-inl.h" - -#endif // FOLLY_ATOMICHASHMAP_H_ diff --git a/hphp/third_party/folly/folly/Bits.cpp b/hphp/third_party/folly/folly/Bits.cpp deleted file mode 100644 index 15842b457..000000000 --- a/hphp/third_party/folly/folly/Bits.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#include "folly/Bits.h" - -#include "folly/CpuId.h" - -// None of this is necessary if we're compiling for a target that supports -// popcnt -#ifndef __POPCNT__ - -namespace { - -int popcount_builtin(unsigned int x) { - return __builtin_popcount(x); -} - -int popcountll_builtin(unsigned long long x) { - return __builtin_popcountll(x); -} - - -#if FOLLY_HAVE_IFUNC - -// Strictly speaking, these versions of popcount are usable without ifunc -// support. However, we would have to check, via CpuId, if the processor -// implements the popcnt instruction first, which is what we use ifunc for. -int popcount_inst(unsigned int x) { - int n; - asm ("popcntl %1, %0" : "=r" (n) : "r" (x)); - return n; -} - -int popcountll_inst(unsigned long long x) { - unsigned long long n; - asm ("popcntq %1, %0" : "=r" (n) : "r" (x)); - return n; -} - -typedef decltype(popcount_builtin) Type_popcount; -typedef decltype(popcountll_builtin) Type_popcountll; - -// This function is called on startup to resolve folly::detail::popcount -extern "C" Type_popcount* folly_popcount_ifunc() { - return folly::CpuId().popcnt() ? popcount_inst : popcount_builtin; -} - -// This function is called on startup to resolve folly::detail::popcountll -extern "C" Type_popcountll* folly_popcountll_ifunc() { - return folly::CpuId().popcnt() ? popcountll_inst : popcountll_builtin; -} - -#endif // FOLLY_HAVE_IFUNC - -} // namespace - -namespace folly { -namespace detail { - -// Call folly_popcount_ifunc on startup to resolve to either popcount_inst -// or popcount_builtin -int popcount(unsigned int x) -#if FOLLY_HAVE_IFUNC - __attribute__((ifunc("folly_popcount_ifunc"))); -#else -{ return popcount_builtin(x); } -#endif - -// Call folly_popcount_ifunc on startup to resolve to either popcountll_inst -// or popcountll_builtin -int popcountll(unsigned long long x) -#if FOLLY_HAVE_IFUNC - __attribute__((ifunc("folly_popcountll_ifunc"))); -#else -{ return popcountll_builtin(x); } -#endif - -} // namespace detail -} // namespace folly - -#endif /* !__POPCNT__ */ diff --git a/hphp/third_party/folly/folly/Bits.h b/hphp/third_party/folly/folly/Bits.h deleted file mode 100644 index 58833106e..000000000 --- a/hphp/third_party/folly/folly/Bits.h +++ /dev/null @@ -1,558 +0,0 @@ -/* - * Copyright 2013 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. - */ - -/** - * Various low-level, bit-manipulation routines. - * - * findFirstSet(x) [constexpr] - * find first (least significant) bit set in a value of an integral type, - * 1-based (like ffs()). 0 = no bits are set (x == 0) - * - * findLastSet(x) [constexpr] - * find last (most significant) bit set in a value of an integral type, - * 1-based. 0 = no bits are set (x == 0) - * for x != 0, findLastSet(x) == 1 + floor(log2(x)) - * - * nextPowTwo(x) [constexpr] - * Finds the next power of two >= x. - * - * isPowTwo(x) [constexpr] - * return true iff x is a power of two - * - * popcount(x) - * return the number of 1 bits in x - * - * Endian - * convert between native, big, and little endian representation - * Endian::big(x) big <-> native - * Endian::little(x) little <-> native - * Endian::swap(x) big <-> little - * - * BitIterator - * Wrapper around an iterator over an integral type that iterates - * over its underlying bits in MSb to LSb order - * - * findFirstSet(BitIterator begin, BitIterator end) - * return a BitIterator pointing to the first 1 bit in [begin, end), or - * end if all bits in [begin, end) are 0 - * - * @author Tudor Bosman (tudorb@fb.com) - */ - -#ifndef FOLLY_BITS_H_ -#define FOLLY_BITS_H_ - -#include "folly/Portability.h" - -#ifndef __GNUC__ -#error GCC required -#endif - -#ifndef __clang__ -#define FOLLY_INTRINSIC_CONSTEXPR constexpr -#else -// Unlike GCC, in Clang (as of 3.2) intrinsics aren't constexpr. -#define FOLLY_INTRINSIC_CONSTEXPR const -#endif - -#ifndef FOLLY_NO_CONFIG -#include "folly/folly-config.h" -#endif - -#include "folly/detail/BitsDetail.h" -#include "folly/detail/BitIteratorDetail.h" -#include "folly/Likely.h" - -#if FOLLY_HAVE_BYTESWAP_H -# include -#endif - -#include -#include -#include -#include -#include -#include -#include - -namespace folly { - -// Generate overloads for findFirstSet as wrappers around -// appropriate ffs, ffsl, ffsll gcc builtins -template -inline FOLLY_INTRINSIC_CONSTEXPR -typename std::enable_if< - (std::is_integral::value && - std::is_unsigned::value && - sizeof(T) <= sizeof(unsigned int)), - unsigned int>::type - findFirstSet(T x) { - return __builtin_ffs(x); -} - -template -inline FOLLY_INTRINSIC_CONSTEXPR -typename std::enable_if< - (std::is_integral::value && - std::is_unsigned::value && - sizeof(T) > sizeof(unsigned int) && - sizeof(T) <= sizeof(unsigned long)), - unsigned int>::type - findFirstSet(T x) { - return __builtin_ffsl(x); -} - -template -inline FOLLY_INTRINSIC_CONSTEXPR -typename std::enable_if< - (std::is_integral::value && - std::is_unsigned::value && - sizeof(T) > sizeof(unsigned long) && - sizeof(T) <= sizeof(unsigned long long)), - unsigned int>::type - findFirstSet(T x) { - return __builtin_ffsll(x); -} - -template -inline FOLLY_INTRINSIC_CONSTEXPR -typename std::enable_if< - (std::is_integral::value && std::is_signed::value), - unsigned int>::type - findFirstSet(T x) { - // Note that conversion from a signed type to the corresponding unsigned - // type is technically implementation-defined, but will likely work - // on any impementation that uses two's complement. - return findFirstSet(static_cast::type>(x)); -} - -// findLastSet: return the 1-based index of the highest bit set -// for x > 0, findLastSet(x) == 1 + floor(log2(x)) -template -inline FOLLY_INTRINSIC_CONSTEXPR -typename std::enable_if< - (std::is_integral::value && - std::is_unsigned::value && - sizeof(T) <= sizeof(unsigned int)), - unsigned int>::type - findLastSet(T x) { - return x ? 8 * sizeof(unsigned int) - __builtin_clz(x) : 0; -} - -template -inline FOLLY_INTRINSIC_CONSTEXPR -typename std::enable_if< - (std::is_integral::value && - std::is_unsigned::value && - sizeof(T) > sizeof(unsigned int) && - sizeof(T) <= sizeof(unsigned long)), - unsigned int>::type - findLastSet(T x) { - return x ? 8 * sizeof(unsigned long) - __builtin_clzl(x) : 0; -} - -template -inline FOLLY_INTRINSIC_CONSTEXPR -typename std::enable_if< - (std::is_integral::value && - std::is_unsigned::value && - sizeof(T) > sizeof(unsigned long) && - sizeof(T) <= sizeof(unsigned long long)), - unsigned int>::type - findLastSet(T x) { - return x ? 8 * sizeof(unsigned long long) - __builtin_clzll(x) : 0; -} - -template -inline FOLLY_INTRINSIC_CONSTEXPR -typename std::enable_if< - (std::is_integral::value && - std::is_signed::value), - unsigned int>::type - findLastSet(T x) { - return findLastSet(static_cast::type>(x)); -} - -template -inline FOLLY_INTRINSIC_CONSTEXPR -typename std::enable_if< - std::is_integral::value && std::is_unsigned::value, - T>::type -nextPowTwo(T v) { - return v ? (1ul << findLastSet(v - 1)) : 1; -} - -template -inline constexpr -typename std::enable_if< - std::is_integral::value && std::is_unsigned::value, - bool>::type -isPowTwo(T v) { - return (v != 0) && !(v & (v - 1)); -} - -/** - * Population count - */ -template -inline typename std::enable_if< - (std::is_integral::value && - std::is_unsigned::value && - sizeof(T) <= sizeof(unsigned int)), - size_t>::type - popcount(T x) { - return detail::popcount(x); -} - -template -inline typename std::enable_if< - (std::is_integral::value && - std::is_unsigned::value && - sizeof(T) > sizeof(unsigned int) && - sizeof(T) <= sizeof(unsigned long long)), - size_t>::type - popcount(T x) { - return detail::popcountll(x); -} - -/** - * Endianness detection and manipulation primitives. - */ -namespace detail { - -template -struct EndianIntBase { - public: - static T swap(T x); -}; - -/** - * If we have the bswap_16 macro from byteswap.h, use it; otherwise, provide our - * own definition. - */ -#ifdef bswap_16 -# define our_bswap16 bswap_16 -#else - -template -inline constexpr typename std::enable_if< - sizeof(Int16) == 2, - Int16>::type -our_bswap16(Int16 x) { - return ((x >> 8) & 0xff) | ((x & 0xff) << 8); -} -#endif - -#define FB_GEN(t, fn) \ -template<> inline t EndianIntBase::swap(t x) { return fn(x); } - -// fn(x) expands to (x) if the second argument is empty, which is exactly -// what we want for [u]int8_t. Also, gcc 4.7 on Intel doesn't have -// __builtin_bswap16 for some reason, so we have to provide our own. -FB_GEN( int8_t,) -FB_GEN(uint8_t,) -FB_GEN( int64_t, __builtin_bswap64) -FB_GEN(uint64_t, __builtin_bswap64) -FB_GEN( int32_t, __builtin_bswap32) -FB_GEN(uint32_t, __builtin_bswap32) -FB_GEN( int16_t, our_bswap16) -FB_GEN(uint16_t, our_bswap16) - -#undef FB_GEN - -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - -template -struct EndianInt : public detail::EndianIntBase { - public: - static T big(T x) { return EndianInt::swap(x); } - static T little(T x) { return x; } -}; - -#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ - -template -struct EndianInt : public detail::EndianIntBase { - public: - static T big(T x) { return x; } - static T little(T x) { return EndianInt::swap(x); } -}; - -#else -# error Your machine uses a weird endianness! -#endif /* __BYTE_ORDER__ */ - -} // namespace detail - -// big* convert between native and big-endian representations -// little* convert between native and little-endian representations -// swap* convert between big-endian and little-endian representations -// -// ntohs, htons == big16 -// ntohl, htonl == big32 -#define FB_GEN1(fn, t, sz) \ - static t fn##sz(t x) { return fn(x); } \ - -#define FB_GEN2(t, sz) \ - FB_GEN1(swap, t, sz) \ - FB_GEN1(big, t, sz) \ - FB_GEN1(little, t, sz) - -#define FB_GEN(sz) \ - FB_GEN2(uint##sz##_t, sz) \ - FB_GEN2(int##sz##_t, sz) - -class Endian { - public: - enum class Order : uint8_t { - LITTLE, - BIG - }; - - static constexpr Order order = -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - Order::LITTLE; -#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ - Order::BIG; -#else -# error Your machine uses a weird endianness! -#endif /* __BYTE_ORDER__ */ - - template static T swap(T x) { - return detail::EndianInt::swap(x); - } - template static T big(T x) { - return detail::EndianInt::big(x); - } - template static T little(T x) { - return detail::EndianInt::little(x); - } - - FB_GEN(64) - FB_GEN(32) - FB_GEN(16) - FB_GEN(8) -}; - -#undef FB_GEN -#undef FB_GEN2 -#undef FB_GEN1 - -/** - * Fast bit iteration facility. - */ - - -template class BitIterator; -template -BitIterator findFirstSet(BitIterator, - BitIterator); -/** - * Wrapper around an iterator over an integer type that iterates - * over its underlying bits in LSb to MSb order. - * - * BitIterator models the same iterator concepts as the base iterator. - */ -template -class BitIterator - : public bititerator_detail::BitIteratorBase::type { - public: - /** - * Return the number of bits in an element of the underlying iterator. - */ - static size_t bitsPerBlock() { - return std::numeric_limits< - typename std::make_unsigned< - typename std::iterator_traits::value_type - >::type - >::digits; - } - - /** - * Construct a BitIterator that points at a given bit offset (default 0) - * in iter. - */ - #pragma GCC diagnostic push // bitOffset shadows a member - #pragma GCC diagnostic ignored "-Wshadow" - explicit BitIterator(const BaseIter& iter, size_t bitOffset=0) - : bititerator_detail::BitIteratorBase::type(iter), - bitOffset_(bitOffset) { - assert(bitOffset_ < bitsPerBlock()); - } - #pragma GCC diagnostic pop - - size_t bitOffset() const { - return bitOffset_; - } - - void advanceToNextBlock() { - bitOffset_ = 0; - ++this->base_reference(); - } - - BitIterator& operator=(const BaseIter& other) { - this->~BitIterator(); - new (this) BitIterator(other); - return *this; - } - - private: - friend class boost::iterator_core_access; - friend BitIterator findFirstSet<>(BitIterator, BitIterator); - - typedef bititerator_detail::BitReference< - typename std::iterator_traits::reference, - typename std::iterator_traits::value_type - > BitRef; - - void advanceInBlock(size_t n) { - bitOffset_ += n; - assert(bitOffset_ < bitsPerBlock()); - } - - BitRef dereference() const { - return BitRef(*this->base_reference(), bitOffset_); - } - - void advance(ssize_t n) { - size_t bpb = bitsPerBlock(); - ssize_t blocks = n / bpb; - bitOffset_ += n % bpb; - if (bitOffset_ >= bpb) { - bitOffset_ -= bpb; - ++blocks; - } - this->base_reference() += blocks; - } - - void increment() { - if (++bitOffset_ == bitsPerBlock()) { - advanceToNextBlock(); - } - } - - void decrement() { - if (bitOffset_-- == 0) { - bitOffset_ = bitsPerBlock() - 1; - --this->base_reference(); - } - } - - bool equal(const BitIterator& other) const { - return (bitOffset_ == other.bitOffset_ && - this->base_reference() == other.base_reference()); - } - - ssize_t distance_to(const BitIterator& other) const { - return - (other.base_reference() - this->base_reference()) * bitsPerBlock() + - (other.bitOffset_ - bitOffset_); - } - - ssize_t bitOffset_; -}; - -/** - * Helper function, so you can write - * auto bi = makeBitIterator(container.begin()); - */ -template -BitIterator makeBitIterator(const BaseIter& iter) { - return BitIterator(iter); -} - - -/** - * Find first bit set in a range of bit iterators. - * 4.5x faster than the obvious std::find(begin, end, true); - */ -template -BitIterator findFirstSet(BitIterator begin, - BitIterator end) { - // shortcut to avoid ugly static_cast<> - static const typename BaseIter::value_type one = 1; - - while (begin.base() != end.base()) { - typename BaseIter::value_type v = *begin.base(); - // mask out the bits that don't matter (< begin.bitOffset) - v &= ~((one << begin.bitOffset()) - 1); - size_t firstSet = findFirstSet(v); - if (firstSet) { - --firstSet; // now it's 0-based - assert(firstSet >= begin.bitOffset()); - begin.advanceInBlock(firstSet - begin.bitOffset()); - return begin; - } - begin.advanceToNextBlock(); - } - - // now begin points to the same block as end - if (end.bitOffset() != 0) { // assume end is dereferenceable - typename BaseIter::value_type v = *begin.base(); - // mask out the bits that don't matter (< begin.bitOffset) - v &= ~((one << begin.bitOffset()) - 1); - // mask out the bits that don't matter (>= end.bitOffset) - v &= (one << end.bitOffset()) - 1; - size_t firstSet = findFirstSet(v); - if (firstSet) { - --firstSet; // now it's 0-based - assert(firstSet >= begin.bitOffset()); - begin.advanceInBlock(firstSet - begin.bitOffset()); - return begin; - } - } - - return end; -} - - -template struct Unaligned; - -/** - * Representation of an unaligned value of a POD type. - */ -template -struct Unaligned< - T, - typename std::enable_if::value>::type> { - Unaligned() = default; // uninitialized - /* implicit */ Unaligned(T v) : value(v) { } - T value; -} __attribute__((packed)); - -/** - * Read an unaligned value of type T and return it. - */ -template -inline T loadUnaligned(const void* p) { - static_assert(sizeof(Unaligned) == sizeof(T), "Invalid unaligned size"); - static_assert(alignof(Unaligned) == 1, "Invalid alignment"); - return static_cast*>(p)->value; -} - -/** - * Write an unaligned value of type T. - */ -template -inline void storeUnaligned(void* p, T value) { - static_assert(sizeof(Unaligned) == sizeof(T), "Invalid unaligned size"); - static_assert(alignof(Unaligned) == 1, "Invalid alignment"); - new (p) Unaligned(value); -} - -} // namespace folly - -#endif /* FOLLY_BITS_H_ */ diff --git a/hphp/third_party/folly/folly/Chrono.h b/hphp/third_party/folly/folly/Chrono.h deleted file mode 100644 index 6465098be..000000000 --- a/hphp/third_party/folly/folly/Chrono.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2013 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. - */ - -// Wrapper around that hides away some gcc 4.6 issues -#ifndef FOLLY_CHRONO_H_ -#define FOLLY_CHRONO_H_ - -#include -#include "folly/Portability.h" - -// gcc 4.6 uses an obsolete name for steady_clock, although the implementation -// is the same -#if __GNUC_PREREQ(4, 6) && !__GNUC_PREREQ(4, 7) -namespace std { namespace chrono { -typedef monotonic_clock steady_clock; -}} // namespaces -#endif - -#endif /* FOLLY_CHRONO_H_ */ - diff --git a/hphp/third_party/folly/folly/ConcurrentSkipList-inl.h b/hphp/third_party/folly/folly/ConcurrentSkipList-inl.h deleted file mode 100644 index 62fab5163..000000000 --- a/hphp/third_party/folly/folly/ConcurrentSkipList-inl.h +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright 2013 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. - */ - -// @author: Xin Liu - -#ifndef FOLLY_CONCURRENTSKIPLIST_INL_H_ -#define FOLLY_CONCURRENTSKIPLIST_INL_H_ - -#include -#include -#include -#include - -#include -#include "folly/SmallLocks.h" -#include "folly/ThreadLocal.h" - -namespace folly { namespace detail { - -template class csl_iterator; - -template -class SkipListNode : boost::noncopyable { - enum { - IS_HEAD_NODE = 1, - MARKED_FOR_REMOVAL = (1 << 1), - FULLY_LINKED = (1 << 2), - }; - public: - typedef T value_type; - - template::value>::type> - static SkipListNode* create(int height, U&& data, bool isHead = false) { - DCHECK(height >= 1 && height < 64) << height; - - size_t size = sizeof(SkipListNode) + - height * sizeof(std::atomic); - auto* node = static_cast(malloc(size)); - // do placement new - new (node) SkipListNode(height, std::forward(data), isHead); - return node; - } - - static void destroy(SkipListNode* node) { - node->~SkipListNode(); - free(node); - } - - // copy the head node to a new head node assuming lock acquired - SkipListNode* copyHead(SkipListNode* node) { - DCHECK(node != nullptr && height_ > node->height_); - setFlags(node->getFlags()); - for (int i = 0; i < node->height_; ++i) { - setSkip(i, node->skip(i)); - } - return this; - } - - inline SkipListNode* skip(int layer) const { - DCHECK_LT(layer, height_); - return skip_[layer].load(std::memory_order_consume); - } - - // next valid node as in the linked list - SkipListNode* next() { - SkipListNode* node; - for (node = skip(0); - (node != nullptr && node->markedForRemoval()); - node = node->skip(0)) {} - return node; - } - - void setSkip(uint8_t h, SkipListNode* next) { - DCHECK_LT(h, height_); - skip_[h].store(next, std::memory_order_release); - } - - value_type& data() { return data_; } - const value_type& data() const { return data_; } - int maxLayer() const { return height_ - 1; } - int height() const { return height_; } - - std::unique_lock acquireGuard() { - return std::unique_lock(spinLock_); - } - - bool fullyLinked() const { return getFlags() & FULLY_LINKED; } - bool markedForRemoval() const { return getFlags() & MARKED_FOR_REMOVAL; } - bool isHeadNode() const { return getFlags() & IS_HEAD_NODE; } - - void setIsHeadNode() { - setFlags(getFlags() | IS_HEAD_NODE); - } - void setFullyLinked() { - setFlags(getFlags() | FULLY_LINKED); - } - void setMarkedForRemoval() { - setFlags(getFlags() | MARKED_FOR_REMOVAL); - } - - private: - // Note! this can only be called from create() as a placement new. - template - SkipListNode(uint8_t height, U&& data, bool isHead) : - height_(height), data_(std::forward(data)) { - spinLock_.init(); - setFlags(0); - if (isHead) setIsHeadNode(); - // need to explicitly init the dynamic atomic pointer array - for (uint8_t i = 0; i < height_; ++i) { - new (&skip_[i]) std::atomic(nullptr); - } - } - - ~SkipListNode() { - for (uint8_t i = 0; i < height_; ++i) { - skip_[i].~atomic(); - } - } - - uint16_t getFlags() const { - return flags_.load(std::memory_order_consume); - } - void setFlags(uint16_t flags) { - flags_.store(flags, std::memory_order_release); - } - - // TODO(xliu): on x86_64, it's possible to squeeze these into - // skip_[0] to maybe save 8 bytes depending on the data alignments. - // NOTE: currently this is x86_64 only anyway, due to the - // MicroSpinLock. - std::atomic flags_; - const uint8_t height_; - MicroSpinLock spinLock_; - - value_type data_; - - std::atomic skip_[0]; -}; - -class SkipListRandomHeight { - enum { kMaxHeight = 64 }; - public: - // make it a singleton. - static SkipListRandomHeight *instance() { - static SkipListRandomHeight instance_; - return &instance_; - } - - int getHeight(int maxHeight) const { - DCHECK_LE(maxHeight, kMaxHeight) << "max height too big!"; - double p = randomProb(); - for (int i = 0; i < maxHeight; ++i) { - if (p < lookupTable_[i]) { - return i + 1; - } - } - return maxHeight; - } - - size_t getSizeLimit(int height) const { - DCHECK_LT(height, kMaxHeight); - return sizeLimitTable_[height]; - } - - private: - - SkipListRandomHeight() { initLookupTable(); } - - void initLookupTable() { - // set skip prob = 1/E - static const double kProbInv = exp(1); - static const double kProb = 1.0 / kProbInv; - static const size_t kMaxSizeLimit = std::numeric_limits::max(); - - double sizeLimit = 1; - double p = lookupTable_[0] = (1 - kProb); - sizeLimitTable_[0] = 1; - for (int i = 1; i < kMaxHeight - 1; ++i) { - p *= kProb; - sizeLimit *= kProbInv; - lookupTable_[i] = lookupTable_[i - 1] + p; - sizeLimitTable_[i] = sizeLimit > kMaxSizeLimit ? - kMaxSizeLimit : - static_cast(sizeLimit); - } - lookupTable_[kMaxHeight - 1] = 1; - sizeLimitTable_[kMaxHeight - 1] = kMaxSizeLimit; - } - - static double randomProb() { - static ThreadLocal rng_; - return (*rng_)(); - } - - double lookupTable_[kMaxHeight]; - size_t sizeLimitTable_[kMaxHeight]; -}; - -}} - -#endif // FOLLY_CONCURRENTSKIPLIST_INL_H_ diff --git a/hphp/third_party/folly/folly/ConcurrentSkipList.h b/hphp/third_party/folly/folly/ConcurrentSkipList.h deleted file mode 100644 index 6c2a50064..000000000 --- a/hphp/third_party/folly/folly/ConcurrentSkipList.h +++ /dev/null @@ -1,859 +0,0 @@ -/* - * Copyright 2013 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. - */ - -// @author: Xin Liu -// -// A concurrent skip list (CSL) implementation. -// Ref: http://www.cs.tau.ac.il/~shanir/nir-pubs-web/Papers/OPODIS2006-BA.pdf - -/* - -This implements a sorted associative container that supports only -unique keys. (Similar to std::set.) - -Features: - - 1. Small memory overhead: ~40% less memory overhead compared with - std::set (1.6 words per node versus 3). It has an minimum of 4 - words (7 words if there nodes got deleted) per-list overhead - though. - - 2. Read accesses (count, find iterator, skipper) are lock-free and - mostly wait-free (the only wait a reader may need to do is when - the node it is visiting is in a pending stage, i.e. deleting, - adding and not fully linked). Write accesses (remove, add) need - to acquire locks, but locks are local to the predecessor nodes - and/or successor nodes. - - 3. Good high contention performance, comparable single-thread - performance. In the multithreaded case (12 workers), CSL tested - 10x faster than a RWSpinLocked std::set for an averaged sized - list (1K - 1M nodes). - - Comparable read performance to std::set when single threaded, - especially when the list size is large, and scales better to - larger lists: when the size is small, CSL can be 20-50% slower on - find()/contains(). As the size gets large (> 1M elements), - find()/contains() can be 30% faster. - - Iterating through a skiplist is similar to iterating through a - linked list, thus is much (2-6x) faster than on a std::set - (tree-based). This is especially true for short lists due to - better cache locality. Based on that, it's also faster to - intersect two skiplists. - - 4. Lazy removal with GC support. The removed nodes get deleted when - the last Accessor to the skiplist is destroyed. - -Caveats: - - 1. Write operations are usually 30% slower than std::set in a single - threaded environment. - - 2. Need to have a head node for each list, which has a 4 word - overhead. - - 3. When the list is quite small (< 1000 elements), single threaded - benchmarks show CSL can be 10x slower than std:set. - - 4. The interface requires using an Accessor to access the skiplist. - (See below.) - - 5. Currently x64 only, due to use of MicroSpinLock. - - 6. Freed nodes will not be reclaimed as long as there are ongoing - uses of the list. - -Sample usage: - - typedef ConcurrentSkipList SkipListT; - shared_ptr sl(SkipListT::createInstance(init_head_height); - { - // It's usually good practice to hold an accessor only during - // its necessary life cycle (but not in a tight loop as - // Accessor creation incurs ref-counting overhead). - // - // Holding it longer delays garbage-collecting the deleted - // nodes in the list. - SkipListT::Accessor accessor(sl); - accessor.insert(23); - accessor.erase(2); - for (auto &elem : accessor) { - // use elem to access data - } - ... ... - } - - Another useful type is the Skipper accessor. This is useful if you - want to skip to locations in the way std::lower_bound() works, - i.e. it can be used for going through the list by skipping to the - node no less than a specified key. The Skipper keeps its location as - state, which makes it convenient for things like implementing - intersection of two sets efficiently, as it can start from the last - visited position. - - { - SkipListT::Accessor accessor(sl); - SkipListT::Skipper skipper(accessor); - skipper.to(30); - if (skipper) { - CHECK_LE(30, *skipper); - } - ... ... - // GC may happen when the accessor gets destructed. - } -*/ - -#ifndef FOLLY_CONCURRENT_SKIP_LIST_H_ -#define FOLLY_CONCURRENT_SKIP_LIST_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "folly/ConcurrentSkipList-inl.h" -#include "folly/Likely.h" -#include "folly/SmallLocks.h" - -namespace folly { - -template, int MAX_HEIGHT=24> -class ConcurrentSkipList { - // MAX_HEIGHT needs to be at least 2 to suppress compiler - // warnings/errors (Werror=uninitialized tiggered due to preds_[1] - // being treated as a scalar in the compiler). - static_assert(MAX_HEIGHT >= 2 && MAX_HEIGHT < 64, - "MAX_HEIGHT can only be in the range of [2, 64)"); - typedef std::unique_lock ScopedLocker; - typedef ConcurrentSkipList SkipListType; - - public: - typedef detail::SkipListNode NodeType; - typedef T value_type; - typedef T key_type; - - - typedef detail::csl_iterator iterator; - typedef detail::csl_iterator const_iterator; - - class Accessor; - class Skipper; - - // convenient function to get an Accessor to a new instance. - static Accessor create(int height=1) { - return Accessor(createInstance(height)); - } - - // create a shared_ptr skiplist object with initial head height. - static boost::shared_ptr createInstance(int height=1) { - return boost::shared_ptr(new SkipListType(height)); - } - - // create a unique_ptr skiplist object with initial head height. - static std::unique_ptr createRawInstance(int height=1) { - return std::unique_ptr(new SkipListType(height)); - } - - - //=================================================================== - // Below are implementation details. - // Please see ConcurrentSkipList::Accessor for stdlib-like APIs. - //=================================================================== - - ~ConcurrentSkipList() { - CHECK_EQ(recycler_.refs(), 0); - while (NodeType* current = head_.load(std::memory_order_relaxed)) { - NodeType* tmp = current->skip(0); - NodeType::destroy(current); - head_.store(tmp, std::memory_order_relaxed); - } - } - - private: - - static bool greater(const value_type &data, const NodeType *node) { - return node && Comp()(node->data(), data); - } - - static bool less(const value_type &data, const NodeType *node) { - return (node == nullptr) || Comp()(data, node->data()); - } - - static int findInsertionPoint(NodeType *cur, int cur_layer, - const value_type &data, - NodeType *preds[], NodeType *succs[]) { - int foundLayer = -1; - NodeType *pred = cur; - NodeType *foundNode = nullptr; - for (int layer = cur_layer; layer >= 0; --layer) { - NodeType *node = pred->skip(layer); - while (greater(data, node)) { - pred = node; - node = node->skip(layer); - } - if (foundLayer == -1 && !less(data, node)) { // the two keys equal - foundLayer = layer; - foundNode = node; - } - preds[layer] = pred; - - // if found, succs[0..foundLayer] need to point to the cached foundNode, - // as foundNode might be deleted at the same time thus pred->skip() can - // return NULL or another node. - succs[layer] = foundNode ? foundNode : node; - } - return foundLayer; - } - - struct Recycler : private boost::noncopyable { - Recycler() : refs_(0), dirty_(false) { lock_.init(); } - - ~Recycler() { - if (nodes_) { - for (auto& node : *nodes_) { - NodeType::destroy(node); - } - } - } - - void add(NodeType* node) { - std::lock_guard g(lock_); - if (nodes_.get() == nullptr) { - nodes_.reset(new std::vector(1, node)); - } else { - nodes_->push_back(node); - } - DCHECK_GT(refs(), 0); - dirty_.store(true, std::memory_order_relaxed); - } - - int refs() const { - return refs_.load(std::memory_order_relaxed); - } - - int addRef() { - return refs_.fetch_add(1, std::memory_order_relaxed); - } - - int release() { - // We don't expect to clean the recycler immediately everytime it is OK - // to do so. Here, it is possible that multiple accessors all release at - // the same time but nobody would clean the recycler here. If this - // happens, the recycler will usually still get cleaned when - // such a race doesn't happen. The worst case is the recycler will - // eventually get deleted along with the skiplist. - if (LIKELY(!dirty_.load(std::memory_order_relaxed) || refs() > 1)) { - return refs_.fetch_add(-1, std::memory_order_relaxed); - } - - boost::scoped_ptr > newNodes; - { - std::lock_guard g(lock_); - if (nodes_.get() == nullptr || refs() > 1) { - return refs_.fetch_add(-1, std::memory_order_relaxed); - } - // once refs_ reaches 1 and there is no other accessor, it is safe to - // remove all the current nodes in the recycler, as we already acquired - // the lock here so no more new nodes can be added, even though new - // accessors may be added after that. - newNodes.swap(nodes_); - dirty_.store(false, std::memory_order_relaxed); - } - - // TODO(xliu) should we spawn a thread to do this when there are large - // number of nodes in the recycler? - for (auto& node : *newNodes) { - NodeType::destroy(node); - } - - // decrease the ref count at the very end, to minimize the - // chance of other threads acquiring lock_ to clear the deleted - // nodes again. - return refs_.fetch_add(-1, std::memory_order_relaxed); - } - - private: - boost::scoped_ptr> nodes_; - std::atomic refs_; // current number of visitors to the list - std::atomic dirty_; // whether *nodes_ is non-empty - MicroSpinLock lock_; // protects access to *nodes_ - }; // class ConcurrentSkipList::Recycler - - explicit ConcurrentSkipList(int height) : - head_(NodeType::create(height, value_type(), true)), size_(0) {} - - size_t size() const { return size_.load(std::memory_order_relaxed); } - int height() const { - return head_.load(std::memory_order_consume)->height(); - } - int maxLayer() const { return height() - 1; } - - size_t incrementSize(int delta) { - return size_.fetch_add(delta, std::memory_order_relaxed) + delta; - } - - // Returns the node if found, nullptr otherwise. - NodeType* find(const value_type &data) { - auto ret = findNode(data); - if (ret.second && !ret.first->markedForRemoval()) return ret.first; - return nullptr; - } - - // lock all the necessary nodes for changing (adding or removing) the list. - // returns true if all the lock acquried successfully and the related nodes - // are all validate (not in certain pending states), false otherwise. - bool lockNodesForChange(int nodeHeight, - ScopedLocker guards[MAX_HEIGHT], - NodeType *preds[MAX_HEIGHT], - NodeType *succs[MAX_HEIGHT], - bool adding=true) { - NodeType *pred, *succ, *prevPred = nullptr; - bool valid = true; - for (int layer = 0; valid && layer < nodeHeight; ++layer) { - pred = preds[layer]; - DCHECK(pred != nullptr) << "layer=" << layer << " height=" << height() - << " nodeheight=" << nodeHeight; - succ = succs[layer]; - if (pred != prevPred) { - guards[layer] = pred->acquireGuard(); - prevPred = pred; - } - valid = !pred->markedForRemoval() && - pred->skip(layer) == succ; // check again after locking - - if (adding) { // when adding a node, the succ shouldn't be going away - valid = valid && (succ == nullptr || !succ->markedForRemoval()); - } - } - - return valid; - } - - // Returns a paired value: - // pair.first always stores the pointer to the node with the same input key. - // It could be either the newly added data, or the existed data in the - // list with the same key. - // pair.second stores whether the data is added successfully: - // 0 means not added, otherwise reutrns the new size. - template - std::pair addOrGetData(U &&data) { - NodeType *preds[MAX_HEIGHT], *succs[MAX_HEIGHT]; - NodeType *newNode; - size_t newSize; - while (true) { - int max_layer = 0; - int layer = findInsertionPointGetMaxLayer(data, preds, succs, &max_layer); - - if (layer >= 0) { - NodeType *nodeFound = succs[layer]; - DCHECK(nodeFound != nullptr); - if (nodeFound->markedForRemoval()) { - continue; // if it's getting deleted retry finding node. - } - // wait until fully linked. - while (UNLIKELY(!nodeFound->fullyLinked())) {} - return std::make_pair(nodeFound, 0); - } - - // need to capped at the original height -- the real height may have grown - int nodeHeight = detail::SkipListRandomHeight::instance()-> - getHeight(max_layer + 1); - - ScopedLocker guards[MAX_HEIGHT]; - if (!lockNodesForChange(nodeHeight, guards, preds, succs)) { - continue; // give up the locks and retry until all valid - } - - // locks acquired and all valid, need to modify the links under the locks. - newNode = NodeType::create(nodeHeight, std::forward(data)); - for (int layer = 0; layer < nodeHeight; ++layer) { - newNode->setSkip(layer, succs[layer]); - preds[layer]->setSkip(layer, newNode); - } - - newNode->setFullyLinked(); - newSize = incrementSize(1); - break; - } - - int hgt = height(); - size_t sizeLimit = - detail::SkipListRandomHeight::instance()->getSizeLimit(hgt); - - if (hgt < MAX_HEIGHT && newSize > sizeLimit) { - growHeight(hgt + 1); - } - CHECK_GT(newSize, 0); - return std::make_pair(newNode, newSize); - } - - bool remove(const value_type &data) { - NodeType *nodeToDelete = nullptr; - ScopedLocker nodeGuard; - bool isMarked = false; - int nodeHeight = 0; - NodeType* preds[MAX_HEIGHT], *succs[MAX_HEIGHT]; - - while (true) { - int max_layer = 0; - int layer = findInsertionPointGetMaxLayer(data, preds, succs, &max_layer); - if (!isMarked && (layer < 0 || !okToDelete(succs[layer], layer))) { - return false; - } - - if (!isMarked) { - nodeToDelete = succs[layer]; - nodeHeight = nodeToDelete->height(); - nodeGuard = nodeToDelete->acquireGuard(); - if (nodeToDelete->markedForRemoval()) return false; - nodeToDelete->setMarkedForRemoval(); - isMarked = true; - } - - // acquire pred locks from bottom layer up - ScopedLocker guards[MAX_HEIGHT]; - if (!lockNodesForChange(nodeHeight, guards, preds, succs, false)) { - continue; // this will unlock all the locks - } - - for (int layer = nodeHeight - 1; layer >= 0; --layer) { - preds[layer]->setSkip(layer, nodeToDelete->skip(layer)); - } - - incrementSize(-1); - break; - } - recycle(nodeToDelete); - return true; - } - - const value_type *first() const { - auto node = head_.load(std::memory_order_consume)->skip(0); - return node ? &node->data() : nullptr; - } - - const value_type *last() const { - NodeType *pred = head_.load(std::memory_order_consume); - NodeType *node = nullptr; - for (int layer = maxLayer(); layer >= 0; --layer) { - do { - node = pred->skip(layer); - if (node) pred = node; - } while (node != nullptr); - } - return pred == head_.load(std::memory_order_relaxed) - ? nullptr : &pred->data(); - } - - static bool okToDelete(NodeType *candidate, int layer) { - DCHECK(candidate != nullptr); - return candidate->fullyLinked() && - candidate->maxLayer() == layer && - !candidate->markedForRemoval(); - } - - // find node for insertion/deleting - int findInsertionPointGetMaxLayer(const value_type &data, - NodeType *preds[], NodeType *succs[], int *max_layer) const { - *max_layer = maxLayer(); - return findInsertionPoint(head_.load(std::memory_order_consume), - *max_layer, data, preds, succs); - } - - // Find node for access. Returns a paired values: - // pair.first = the first node that no-less than data value - // pair.second = 1 when the data value is founded, or 0 otherwise. - // This is like lower_bound, but not exact: we could have the node marked for - // removal so still need to check that. - std::pair findNode(const value_type &data) const { - return findNodeDownRight(data); - } - - // Find node by first stepping down then stepping right. Based on benchmark - // results, this is slightly faster than findNodeRightDown for better - // localality on the skipping pointers. - std::pair findNodeDownRight(const value_type &data) const { - NodeType *pred = head_.load(std::memory_order_consume); - int ht = pred->height(); - NodeType *node = nullptr; - - bool found = false; - while (!found) { - // stepping down - for (; ht > 0 && less(data, pred->skip(ht - 1)); --ht) {} - if (ht == 0) return std::make_pair(pred->skip(0), 0); // not found - - node = pred->skip(--ht); // node <= data now - // stepping right - while (greater(data, node)) { - pred = node; - node = node->skip(ht); - } - found = !less(data, node); - } - return std::make_pair(node, found); - } - - // find node by first stepping right then stepping down. - // We still keep this for reference purposes. - std::pair findNodeRightDown(const value_type &data) const { - NodeType *pred = head_.load(std::memory_order_consume); - NodeType *node = nullptr; - auto top = maxLayer(); - int found = 0; - for (int layer = top; !found && layer >= 0; --layer) { - node = pred->skip(layer); - while (greater(data, node)) { - pred = node; - node = node->skip(layer); - } - found = !less(data, node); - } - return std::make_pair(node, found); - } - - NodeType* lower_bound(const value_type &data) const { - auto node = findNode(data).first; - while (node != nullptr && node->markedForRemoval()) { - node = node->skip(0); - } - return node; - } - - void growHeight(int height) { - NodeType* oldHead = head_.load(std::memory_order_consume); - if (oldHead->height() >= height) { // someone else already did this - return; - } - - NodeType* newHead = NodeType::create(height, value_type(), true); - - { // need to guard the head node in case others are adding/removing - // nodes linked to the head. - ScopedLocker g = oldHead->acquireGuard(); - newHead->copyHead(oldHead); - NodeType* expected = oldHead; - if (!head_.compare_exchange_strong(expected, newHead, - std::memory_order_release)) { - // if someone has already done the swap, just return. - NodeType::destroy(newHead); - return; - } - oldHead->setMarkedForRemoval(); - } - recycle(oldHead); - } - - void recycle(NodeType *node) { - recycler_.add(node); - } - - std::atomic head_; - Recycler recycler_; - std::atomic size_; -}; - -template -class ConcurrentSkipList::Accessor { - typedef detail::SkipListNode NodeType; - typedef ConcurrentSkipList SkipListType; - public: - typedef T value_type; - typedef T key_type; - typedef T& reference; - typedef T* pointer; - typedef const T& const_reference; - typedef const T* const_pointer; - typedef size_t size_type; - typedef Comp key_compare; - typedef Comp value_compare; - - typedef typename SkipListType::iterator iterator; - typedef typename SkipListType::const_iterator const_iterator; - typedef typename SkipListType::Skipper Skipper; - - explicit Accessor(boost::shared_ptr skip_list) - : slHolder_(std::move(skip_list)) - { - sl_ = slHolder_.get(); - DCHECK(sl_ != nullptr); - sl_->recycler_.addRef(); - } - - // Unsafe initializer: the caller assumes the responsibility to keep - // skip_list valid during the whole life cycle of the Acessor. - explicit Accessor(ConcurrentSkipList *skip_list) : sl_(skip_list) { - DCHECK(sl_ != nullptr); - sl_->recycler_.addRef(); - } - - Accessor(const Accessor &accessor) : - sl_(accessor.sl_), - slHolder_(accessor.slHolder_) { - sl_->recycler_.addRef(); - } - - Accessor& operator=(const Accessor &accessor) { - if (this != &accessor) { - slHolder_ = accessor.slHolder_; - sl_->recycler_.release(); - sl_ = accessor.sl_; - sl_->recycler_.addRef(); - } - return *this; - } - - ~Accessor() { - sl_->recycler_.release(); - } - - bool empty() const { return sl_->size() == 0; } - size_t size() const { return sl_->size(); } - size_type max_size() const { return std::numeric_limits::max(); } - - // returns end() if the value is not in the list, otherwise returns an - // iterator pointing to the data, and it's guaranteed that the data is valid - // as far as the Accessor is hold. - iterator find(const key_type &value) { return iterator(sl_->find(value)); } - const_iterator find(const key_type &value) const { - return iterator(sl_->find(value)); - } - size_type count(const key_type &data) const { return contains(data); } - - iterator begin() const { - NodeType* head = sl_->head_.load(std::memory_order_consume); - return iterator(head->next()); - } - iterator end() const { return iterator(nullptr); } - const_iterator cbegin() const { return begin(); } - const_iterator cend() const { return end(); } - - template::value>::type> - std::pair insert(U&& data) { - auto ret = sl_->addOrGetData(std::forward(data)); - return std::make_pair(iterator(ret.first), ret.second); - } - size_t erase(const key_type &data) { return remove(data); } - - iterator lower_bound(const key_type &data) const { - return iterator(sl_->lower_bound(data)); - } - - size_t height() const { return sl_->height(); } - - // first() returns pointer to the first element in the skiplist, or - // nullptr if empty. - // - // last() returns the pointer to the last element in the skiplist, - // nullptr if list is empty. - // - // Note: As concurrent writing can happen, first() is not - // guaranteed to be the min_element() in the list. Similarly - // last() is not guaranteed to be the max_element(), and both of them can - // be invalid (i.e. nullptr), so we name them differently from front() and - // tail() here. - const key_type *first() const { return sl_->first(); } - const key_type *last() const { return sl_->last(); } - - // Try to remove the last element in the skip list. - // - // Returns true if we removed it, false if either the list is empty - // or a race condition happened (i.e. the used-to-be last element - // was already removed by another thread). - bool pop_back() { - auto last = sl_->last(); - return last ? sl_->remove(*last) : false; - } - - std::pair addOrGetData(const key_type &data) { - auto ret = sl_->addOrGetData(data); - return std::make_pair(&ret.first->data(), ret.second); - } - - SkipListType* skiplist() const { return sl_; } - - // legacy interfaces - // TODO:(xliu) remove these. - // Returns true if the node is added successfully, false if not, i.e. the - // node with the same key already existed in the list. - bool contains(const key_type &data) const { return sl_->find(data); } - bool add(const key_type &data) { return sl_->addOrGetData(data).second; } - bool remove(const key_type &data) { return sl_->remove(data); } - - private: - SkipListType *sl_; - boost::shared_ptr slHolder_; -}; - -// implements forward iterator concept. -template -class detail::csl_iterator : - public boost::iterator_facade, - ValT, boost::forward_traversal_tag> { - public: - typedef ValT value_type; - typedef value_type& reference; - typedef value_type* pointer; - typedef ptrdiff_t difference_type; - - explicit csl_iterator(NodeT* node = nullptr) : node_(node) {} - - template - csl_iterator(const csl_iterator &other, - typename std::enable_if::value>::type* - = 0) : node_(other.node_) {} - - size_t nodeSize() const { - return node_ == nullptr ? 0 : - node_->height() * sizeof(NodeT*) + sizeof(*this); - } - - bool good() const { return node_ != nullptr; } - - private: - friend class boost::iterator_core_access; - template friend class csl_iterator; - - void increment() { node_ = node_->next(); }; - bool equal(const csl_iterator& other) const { return node_ == other.node_; } - value_type& dereference() const { return node_->data(); } - - NodeT* node_; -}; - -// Skipper interface -template -class ConcurrentSkipList::Skipper { - typedef detail::SkipListNode NodeType; - typedef ConcurrentSkipList SkipListType; - typedef typename SkipListType::Accessor Accessor; - - public: - typedef T value_type; - typedef T& reference; - typedef T* pointer; - typedef ptrdiff_t difference_type; - - Skipper(const boost::shared_ptr& skipList) : - accessor_(skipList) { - init(); - } - - Skipper(const Accessor& accessor) : accessor_(accessor) { - init(); - } - - void init() { - // need to cache the head node - NodeType* head_node = head(); - headHeight_ = head_node->height(); - for (int i = 0; i < headHeight_; ++i) { - preds_[i] = head_node; - succs_[i] = head_node->skip(i); - } - int max_layer = maxLayer(); - for (int i = 0; i < max_layer; ++i) { - hints_[i] = i + 1; - } - hints_[max_layer] = max_layer; - } - - // advance to the next node in the list. - Skipper& operator ++() { - preds_[0] = succs_[0]; - succs_[0] = preds_[0]->skip(0); - int height = curHeight(); - for (int i = 1; i < height && preds_[0] == succs_[i]; ++i) { - preds_[i] = succs_[i]; - succs_[i] = preds_[i]->skip(i); - } - return *this; - } - - bool good() const { return succs_[0] != nullptr; } - - int maxLayer() const { return headHeight_ - 1; } - - int curHeight() const { - // need to cap the height to the cached head height, as the current node - // might be some newly inserted node and also during the time period the - // head height may have grown. - return succs_[0] ? std::min(headHeight_, succs_[0]->height()) : 0; - } - - const value_type &data() const { - DCHECK(succs_[0] != NULL); - return succs_[0]->data(); - } - - value_type &operator *() const { - DCHECK(succs_[0] != NULL); - return succs_[0]->data(); - } - - value_type *operator->() { - DCHECK(succs_[0] != NULL); - return &succs_[0]->data(); - } - - /* - * Skip to the position whose data is no less than the parameter. - * (I.e. the lower_bound). - * - * Returns true if the data is found, false otherwise. - */ - bool to(const value_type &data) { - int layer = curHeight() - 1; - if (layer < 0) return false; // reaches the end of the list - - int lyr = hints_[layer]; - int max_layer = maxLayer(); - while (SkipListType::greater(data, succs_[lyr]) && lyr < max_layer) { - ++lyr; - } - hints_[layer] = lyr; // update the hint - - int foundLayer = SkipListType:: - findInsertionPoint(preds_[lyr], lyr, data, preds_, succs_); - if (foundLayer < 0) return false; - - DCHECK(succs_[0] != NULL) << "lyr=" << lyr << "; max_layer=" << max_layer; - return !succs_[0]->markedForRemoval(); - } - - private: - NodeType* head() const { - return accessor_.skiplist()->head_.load(std::memory_order_consume); - } - - Accessor accessor_; - int headHeight_; - NodeType *succs_[MAX_HEIGHT], *preds_[MAX_HEIGHT]; - uint8_t hints_[MAX_HEIGHT]; -}; - -} // namespace folly - -#endif // FOLLY_CONCURRENT_SKIP_LIST_H_ diff --git a/hphp/third_party/folly/folly/Conv.cpp b/hphp/third_party/folly/folly/Conv.cpp deleted file mode 100644 index 6dfede7ce..000000000 --- a/hphp/third_party/folly/folly/Conv.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2013 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. - */ -#define FOLLY_CONV_INTERNAL -#include "folly/Conv.h" - -namespace folly { -namespace detail { - -extern const char digit1[101] = - "00000000001111111111222222222233333333334444444444" - "55555555556666666666777777777788888888889999999999"; -extern const char digit2[101] = - "01234567890123456789012345678901234567890123456789" - "01234567890123456789012345678901234567890123456789"; - -template <> const char *const MaxString::value = "true"; -template <> const char *const MaxString::value = "255"; -template <> const char *const MaxString::value = "65535"; -template <> const char *const MaxString::value = "4294967295"; -#if __SIZEOF_LONG__ == 4 -template <> const char *const MaxString::value = - "4294967295"; -#else -template <> const char *const MaxString::value = - "18446744073709551615"; -#endif -static_assert(sizeof(unsigned long) >= 4, - "Wrong value for MaxString::value," - " please update."); -template <> const char *const MaxString::value = - "18446744073709551615"; -static_assert(sizeof(unsigned long long) >= 8, - "Wrong value for MaxString::value" - ", please update."); - -inline bool bool_str_cmp(const char** b, size_t len, const char* value) { - // Can't use strncasecmp, since we want to ensure that the full value matches - const char* p = *b; - const char* e = *b + len; - const char* v = value; - while (*v != '\0') { - if (p == e || tolower(*p) != *v) { // value is already lowercase - return false; - } - ++p; - ++v; - } - - *b = p; - return true; -} - -bool str_to_bool(StringPiece* src) { - auto b = src->begin(), e = src->end(); - for (;; ++b) { - FOLLY_RANGE_CHECK(b < e, - "No non-whitespace characters found in input string"); - if (!isspace(*b)) break; - } - - bool result; - size_t len = e - b; - switch (*b) { - case '0': - case '1': { - // Attempt to parse the value as an integer - StringPiece tmp(*src); - uint8_t value = to(&tmp); - // Only accept 0 or 1 - FOLLY_RANGE_CHECK(value <= 1, - "Integer overflow when parsing bool: must be 0 or 1"); - b = tmp.begin(); - result = (value == 1); - break; - } - case 'y': - case 'Y': - result = true; - if (!bool_str_cmp(&b, len, "yes")) { - ++b; // accept the single 'y' character - } - break; - case 'n': - case 'N': - result = false; - if (!bool_str_cmp(&b, len, "no")) { - ++b; - } - break; - case 't': - case 'T': - result = true; - if (!bool_str_cmp(&b, len, "true")) { - ++b; - } - break; - case 'f': - case 'F': - result = false; - if (!bool_str_cmp(&b, len, "false")) { - ++b; - } - break; - case 'o': - case 'O': - if (bool_str_cmp(&b, len, "on")) { - result = true; - } else if (bool_str_cmp(&b, len, "off")) { - result = false; - } else { - FOLLY_RANGE_CHECK(false, "Invalid value for bool"); - } - break; - default: - FOLLY_RANGE_CHECK(false, "Invalid value for bool"); - } - - src->assign(b, e); - return result; -} - -} // namespace detail -} // namespace folly diff --git a/hphp/third_party/folly/folly/Conv.h b/hphp/third_party/folly/folly/Conv.h deleted file mode 100644 index f5cc23c5c..000000000 --- a/hphp/third_party/folly/folly/Conv.h +++ /dev/null @@ -1,1120 +0,0 @@ -/* - * Copyright 2013 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. - */ - -/** - * Converts anything to anything, with an emphasis on performance and - * safety. - * - * @author Andrei Alexandrescu (andrei.alexandrescu@fb.com) - */ - -#ifndef FOLLY_BASE_CONV_H_ -#define FOLLY_BASE_CONV_H_ - -#include "folly/FBString.h" -#include "folly/Likely.h" -#include "folly/Preprocessor.h" -#include "folly/Range.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "double-conversion.h" // V8 JavaScript implementation - -#define FOLLY_RANGE_CHECK(condition, message) \ - ((condition) ? (void)0 : throw std::range_error( \ - (__FILE__ "(" + std::to_string((long long int) __LINE__) + "): " \ - + (message)).c_str())) - -namespace folly { - -/******************************************************************************* - * Integral to integral - ******************************************************************************/ - -/** - * Checked conversion from integral to integral. The checks are only - * performed when meaningful, e.g. conversion from int to long goes - * unchecked. - */ -template -typename std::enable_if< - std::is_integral::value && std::is_integral::value, - Tgt>::type -to(const Src & value) { - /* static */ if (std::numeric_limits::max() - < std::numeric_limits::max()) { - FOLLY_RANGE_CHECK( - (!greater_than::max()>(value)), - "Overflow" - ); - } - /* static */ if (std::is_signed::value && - (!std::is_signed::value || sizeof(Src) > sizeof(Tgt))) { - FOLLY_RANGE_CHECK( - (!less_than::min()>(value)), - "Negative overflow" - ); - } - return static_cast(value); -} - -/******************************************************************************* - * Floating point to floating point - ******************************************************************************/ - -template -typename std::enable_if< - std::is_floating_point::value && std::is_floating_point::value, - Tgt>::type -to(const Src & value) { - /* static */ if (std::numeric_limits::max() < - std::numeric_limits::max()) { - FOLLY_RANGE_CHECK(value <= std::numeric_limits::max(), - "Overflow"); - FOLLY_RANGE_CHECK(value >= -std::numeric_limits::max(), - "Negative overflow"); - } - return boost::implicit_cast(value); -} - -/******************************************************************************* - * Anything to string - ******************************************************************************/ - -namespace detail { - -template struct IsSomeString { - enum { value = std::is_same::value - || std::is_same::value }; -}; - -template -const T& getLastElement(const T & v) { - return v; -} - -template -typename std::tuple_element< - sizeof...(Ts), - std::tuple >::type const& - getLastElement(const T& v, const Ts&... vs) { - return getLastElement(vs...); -} - -} // namespace detail - -/******************************************************************************* - * Conversions from integral types to string types. - ******************************************************************************/ - -#if FOLLY_HAVE_INT128_T -namespace detail { - -template -constexpr unsigned int -digitsEnough() { - return ceil((double(sizeof(IntegerType) * CHAR_BIT) * M_LN2) / M_LN10); -} - -inline unsigned int -unsafeTelescope128(char * buffer, unsigned int room, unsigned __int128 x) { - typedef unsigned __int128 Usrc; - unsigned int p = room - 1; - - while (x >= (Usrc(1) << 64)) { // Using 128-bit division while needed - const auto y = x / 10; - const auto digit = x % 10; - - buffer[p--] = '0' + digit; - x = y; - } - - uint64_t xx = x; // Moving to faster 64-bit division thereafter - - while (xx >= 10) { - const auto y = xx / 10ULL; - const auto digit = xx % 10ULL; - - buffer[p--] = '0' + digit; - xx = y; - } - - buffer[p] = '0' + xx; - - return p; -} - -} -#endif - -/** - * Returns the number of digits in the base 10 representation of an - * uint64_t. Useful for preallocating buffers and such. It's also used - * internally, see below. Measurements suggest that defining a - * separate overload for 32-bit integers is not worthwhile. - */ - -inline uint32_t digits10(uint64_t v) { - uint32_t result = 1; - for (;;) { - if (LIKELY(v < 10)) return result; - if (LIKELY(v < 100)) return result + 1; - if (LIKELY(v < 1000)) return result + 2; - if (LIKELY(v < 10000)) return result + 3; - // Skip ahead by 4 orders of magnitude - v /= 10000U; - result += 4; - } -} - -/** - * Copies the ASCII base 10 representation of v into buffer and - * returns the number of bytes written. Does NOT append a \0. Assumes - * the buffer points to digits10(v) bytes of valid memory. Note that - * uint64 needs at most 20 bytes, uint32_t needs at most 10 bytes, - * uint16_t needs at most 5 bytes, and so on. Measurements suggest - * that defining a separate overload for 32-bit integers is not - * worthwhile. - * - * This primitive is unsafe because it makes the size assumption and - * because it does not add a terminating \0. - */ - -inline uint32_t uint64ToBufferUnsafe(uint64_t v, char *const buffer) { - auto const result = digits10(v); - // WARNING: using size_t or pointer arithmetic for pos slows down - // the loop below 20x. This is because several 32-bit ops can be - // done in parallel, but only fewer 64-bit ones. - uint32_t pos = result - 1; - while (v >= 10) { - // Keep these together so a peephole optimization "sees" them and - // computes them in one shot. - auto const q = v / 10; - auto const r = static_cast(v % 10); - buffer[pos--] = '0' + r; - v = q; - } - // Last digit is trivial to handle - buffer[pos] = static_cast(v) + '0'; - return result; -} - -/** - * A single char gets appended. - */ -template -void toAppend(char value, Tgt * result) { - *result += value; -} - -/** - * Everything implicitly convertible to const char* gets appended. - */ -template -typename std::enable_if< - std::is_convertible::value - && detail::IsSomeString::value>::type -toAppend(Src value, Tgt * result) { - // Treat null pointers like an empty string, as in: - // operator<<(std::ostream&, const char*). - const char* c = value; - if (c) { - result->append(value); - } -} - -/** - * Strings get appended, too. - */ -template -typename std::enable_if< - detail::IsSomeString::value && detail::IsSomeString::value>::type -toAppend(const Src& value, Tgt * result) { - result->append(value); -} - -/** - * and StringPiece objects too - */ -template -typename std::enable_if< - detail::IsSomeString::value>::type -toAppend(StringPiece value, Tgt * result) { - result->append(value.data(), value.size()); -} - -/** - * There's no implicit conversion from fbstring to other string types, - * so make a specialization. - */ -template -typename std::enable_if< - detail::IsSomeString::value>::type -toAppend(const fbstring& value, Tgt * result) { - result->append(value.data(), value.size()); -} - -#if FOLLY_HAVE_INT128_T -/** - * Special handling for 128 bit integers. - */ - -template -void -toAppend(__int128 value, Tgt * result) { - typedef unsigned __int128 Usrc; - char buffer[detail::digitsEnough() + 1]; - unsigned int p; - - if (value < 0) { - p = detail::unsafeTelescope128(buffer, sizeof(buffer), Usrc(-value)); - buffer[--p] = '-'; - } else { - p = detail::unsafeTelescope128(buffer, sizeof(buffer), value); - } - - result->append(buffer + p, buffer + sizeof(buffer)); -} - -template -void -toAppend(unsigned __int128 value, Tgt * result) { - char buffer[detail::digitsEnough()]; - unsigned int p; - - p = detail::unsafeTelescope128(buffer, sizeof(buffer), value); - - result->append(buffer + p, buffer + sizeof(buffer)); -} - -#endif - -/** - * int32_t and int64_t to string (by appending) go through here. The - * result is APPENDED to a preexisting string passed as the second - * parameter. This should be efficient with fbstring because fbstring - * incurs no dynamic allocation below 23 bytes and no number has more - * than 22 bytes in its textual representation (20 for digits, one for - * sign, one for the terminating 0). - */ -template -typename std::enable_if< - std::is_integral::value && std::is_signed::value && - detail::IsSomeString::value && sizeof(Src) >= 4>::type -toAppend(Src value, Tgt * result) { - char buffer[20]; - if (value < 0) { - result->push_back('-'); - result->append(buffer, uint64ToBufferUnsafe(-uint64_t(value), buffer)); - } else { - result->append(buffer, uint64ToBufferUnsafe(value, buffer)); - } -} - -/** - * As above, but for uint32_t and uint64_t. - */ -template -typename std::enable_if< - std::is_integral::value && !std::is_signed::value - && detail::IsSomeString::value && sizeof(Src) >= 4>::type -toAppend(Src value, Tgt * result) { - char buffer[20]; - result->append(buffer, buffer + uint64ToBufferUnsafe(value, buffer)); -} - -/** - * All small signed and unsigned integers to string go through 32-bit - * types int32_t and uint32_t, respectively. - */ -template -typename std::enable_if< - std::is_integral::value - && detail::IsSomeString::value && sizeof(Src) < 4>::type -toAppend(Src value, Tgt * result) { - typedef typename - std::conditional::value, int64_t, uint64_t>::type - Intermediate; - toAppend(static_cast(value), result); -} - -#if defined(__GNUC__) && __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) -// std::underlying_type became available by gcc 4.7.0 - -/** - * Enumerated values get appended as integers. - */ -template -typename std::enable_if< - std::is_enum::value && detail::IsSomeString::value>::type -toAppend(Src value, Tgt * result) { - toAppend( - static_cast::type>(value), result); -} - -#else - -/** - * Enumerated values get appended as integers. - */ -template -typename std::enable_if< - std::is_enum::value && detail::IsSomeString::value>::type -toAppend(Src value, Tgt * result) { - /* static */ if (Src(-1) < 0) { - /* static */ if (sizeof(Src) <= sizeof(int)) { - toAppend(static_cast(value), result); - } else { - toAppend(static_cast(value), result); - } - } else { - /* static */ if (sizeof(Src) <= sizeof(int)) { - toAppend(static_cast(value), result); - } else { - toAppend(static_cast(value), result); - } - } -} - -#endif // gcc 4.7 onwards - -/******************************************************************************* - * Conversions from floating-point types to string types. - ******************************************************************************/ - -/** Wrapper around DoubleToStringConverter **/ -template -typename std::enable_if< - std::is_floating_point::value - && detail::IsSomeString::value>::type -toAppend( - Src value, - Tgt * result, - double_conversion::DoubleToStringConverter::DtoaMode mode, - unsigned int numDigits) { - using namespace double_conversion; - DoubleToStringConverter - conv(DoubleToStringConverter::NO_FLAGS, - "infinity", "NaN", 'E', - -6, // decimal in shortest low - 21, // decimal in shortest high - 6, // max leading padding zeros - 1); // max trailing padding zeros - char buffer[256]; - StringBuilder builder(buffer, sizeof(buffer)); - switch (mode) { - case DoubleToStringConverter::SHORTEST: - conv.ToShortest(value, &builder); - break; - case DoubleToStringConverter::FIXED: - conv.ToFixed(value, numDigits, &builder); - break; - default: - CHECK(mode == DoubleToStringConverter::PRECISION); - conv.ToPrecision(value, numDigits, &builder); - break; - } - const size_t length = builder.position(); - builder.Finalize(); - result->append(buffer, length); -} - -/** - * As above, but for floating point - */ -template -typename std::enable_if< - std::is_floating_point::value - && detail::IsSomeString::value>::type -toAppend(Src value, Tgt * result) { - toAppend( - value, result, double_conversion::DoubleToStringConverter::SHORTEST, 0); -} - -/** - * Variadic conversion to string. Appends each element in turn. - */ -template -typename std::enable_if= 2 - && detail::IsSomeString< - typename std::remove_pointer< - typename std::tuple_element< - sizeof...(Ts) - 1, std::tuple - >::type>::type>::value>::type -toAppend(const T& v, const Ts&... vs) { - toAppend(v, detail::getLastElement(vs...)); - toAppend(vs...); -} - -/** - * Variadic base case: do nothing. - */ -template -typename std::enable_if::value>::type -toAppend(Tgt* result) { -} - -/** - * Variadic base case: do nothing. - */ -template -typename std::enable_if::value>::type -toAppendDelim(const Delimiter& delim, Tgt* result) { -} - -/** - * 1 element: same as toAppend. - */ -template -typename std::enable_if::value>::type -toAppendDelim(const Delimiter& delim, const T& v, Tgt* tgt) { - toAppend(v, tgt); -} - -/** - * Append to string with a delimiter in between elements. - */ -template -typename std::enable_if= 2 - && detail::IsSomeString< - typename std::remove_pointer< - typename std::tuple_element< - sizeof...(Ts) - 1, std::tuple - >::type>::type>::value>::type -toAppendDelim(const Delimiter& delim, const T& v, const Ts&... vs) { - toAppend(v, delim, detail::getLastElement(vs...)); - toAppendDelim(delim, vs...); -} - -/** - * to(v1, v2, ...) uses toAppend() (see below) as back-end - * for all types. - */ -template -typename std::enable_if::value, Tgt>::type -to(const Ts&... vs) { - Tgt result; - toAppend(vs..., &result); - return result; -} - -/** - * toDelim(delim, v1, v2, ...) uses toAppendDelim() as - * back-end for all types. - */ -template -typename std::enable_if::value, Tgt>::type -toDelim(const Delim& delim, const Ts&... vs) { - Tgt result; - toAppendDelim(delim, vs..., &result); - return result; -} - -/******************************************************************************* - * Conversions from string types to integral types. - ******************************************************************************/ - -namespace detail { - -/** - * Finds the first non-digit in a string. The number of digits - * searched depends on the precision of the Tgt integral. Assumes the - * string starts with NO whitespace and NO sign. - * - * The semantics of the routine is: - * for (;; ++b) { - * if (b >= e || !isdigit(*b)) return b; - * } - * - * Complete unrolling marks bottom-line (i.e. entire conversion) - * improvements of 20%. - */ - template - const char* findFirstNonDigit(const char* b, const char* e) { - for (; b < e; ++b) { - auto const c = static_cast(*b) - '0'; - if (c >= 10) break; - } - return b; - } - - // Maximum value of number when represented as a string - template struct MaxString { - static const char*const value; - }; - - -/* - * Lookup tables that converts from a decimal character value to an integral - * binary value, shifted by a decimal "shift" multiplier. - * For all character values in the range '0'..'9', the table at those - * index locations returns the actual decimal value shifted by the multiplier. - * For all other values, the lookup table returns an invalid OOR value. - */ -// Out-of-range flag value, larger than the largest value that can fit in -// four decimal bytes (9999), but four of these added up together should -// still not overflow uint16_t. -constexpr int32_t OOR = 10000; - -__attribute__((aligned(16))) constexpr uint16_t shift1[] = { - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 30 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0, // 40 - 1, 2, 3, 4, 5, 6, 7, 8, 9, OOR, OOR, - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 60 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 70 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 80 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 90 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240 - OOR, OOR, OOR, OOR, OOR, OOR // 250 -}; - -__attribute__((aligned(16))) constexpr uint16_t shift10[] = { - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 30 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0, // 40 - 10, 20, 30, 40, 50, 60, 70, 80, 90, OOR, OOR, - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 60 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 70 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 80 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 90 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240 - OOR, OOR, OOR, OOR, OOR, OOR // 250 -}; - -__attribute__((aligned(16))) constexpr uint16_t shift100[] = { - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 30 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0, // 40 - 100, 200, 300, 400, 500, 600, 700, 800, 900, OOR, OOR, - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 60 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 70 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 80 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 90 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240 - OOR, OOR, OOR, OOR, OOR, OOR // 250 -}; - -__attribute__((aligned(16))) constexpr uint16_t shift1000[] = { - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 10 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 20 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 30 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0, // 40 - 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, OOR, OOR, - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 60 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 70 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 80 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 90 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230 - OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240 - OOR, OOR, OOR, OOR, OOR, OOR // 250 -}; - -/** - * String represented as a pair of pointers to char to unsigned - * integrals. Assumes NO whitespace before or after, and also that the - * string is composed entirely of digits. Tgt must be unsigned, and no - * sign is allowed in the string (even it's '+'). String may be empty, - * in which case digits_to throws. - */ - template - Tgt digits_to(const char * b, const char * e) { - - static_assert(!std::is_signed::value, "Unsigned type expected"); - assert(b <= e); - - const size_t size = e - b; - - /* Although the string is entirely made of digits, we still need to - * check for overflow. - */ - if (size >= std::numeric_limits::digits10 + 1) { - // Leading zeros? If so, recurse to keep things simple - if (b < e && *b == '0') { - for (++b;; ++b) { - if (b == e) return 0; // just zeros, e.g. "0000" - if (*b != '0') return digits_to(b, e); - } - } - FOLLY_RANGE_CHECK(size == std::numeric_limits::digits10 + 1 && - strncmp(b, detail::MaxString::value, size) <= 0, - "Numeric overflow upon conversion"); - } - - // Here we know that the number won't overflow when - // converted. Proceed without checks. - - Tgt result = 0; - - for (; e - b >= 4; b += 4) { - result *= 10000; - const int32_t r0 = shift1000[static_cast(b[0])]; - const int32_t r1 = shift100[static_cast(b[1])]; - const int32_t r2 = shift10[static_cast(b[2])]; - const int32_t r3 = shift1[static_cast(b[3])]; - const auto sum = r0 + r1 + r2 + r3; - assert(sum < OOR && "Assumption: string only has digits"); - result += sum; - } - - switch (e - b) { - case 3: { - const int32_t r0 = shift100[static_cast(b[0])]; - const int32_t r1 = shift10[static_cast(b[1])]; - const int32_t r2 = shift1[static_cast(b[2])]; - const auto sum = r0 + r1 + r2; - assert(sum < OOR && "Assumption: string only has digits"); - return result * 1000 + sum; - } - case 2: { - const int32_t r0 = shift10[static_cast(b[0])]; - const int32_t r1 = shift1[static_cast(b[1])]; - const auto sum = r0 + r1; - assert(sum < OOR && "Assumption: string only has digits"); - return result * 100 + sum; - } - case 1: { - const int32_t sum = shift1[static_cast(b[0])]; - assert(sum < OOR && "Assumption: string only has digits"); - return result * 10 + sum; - } - } - - assert(b == e); - FOLLY_RANGE_CHECK(size > 0, "Found no digits to convert in input"); - return result; - } - - - bool str_to_bool(StringPiece * src); - -} // namespace detail - -/** - * String represented as a pair of pointers to char to unsigned - * integrals. Assumes NO whitespace before or after. - */ -template -typename std::enable_if< - std::is_integral::value && !std::is_signed::value - && !std::is_same::type, bool>::value, - Tgt>::type -to(const char * b, const char * e) { - return detail::digits_to(b, e); -} - -/** - * String represented as a pair of pointers to char to signed - * integrals. Assumes NO whitespace before or after. Allows an - * optional leading sign. - */ -template -typename std::enable_if< - std::is_integral::value && std::is_signed::value, - Tgt>::type -to(const char * b, const char * e) { - FOLLY_RANGE_CHECK(b < e, "Empty input string in conversion to integral"); - if (!isdigit(*b)) { - if (*b == '-') { - Tgt result = -to::type>(b + 1, e); - FOLLY_RANGE_CHECK(result <= 0, "Negative overflow."); - return result; - } - FOLLY_RANGE_CHECK(*b == '+', "Invalid lead character"); - ++b; - } - Tgt result = to::type>(b, e); - FOLLY_RANGE_CHECK(result >= 0, "Overflow."); - return result; -} - -/** - * Parsing strings to integrals. These routines differ from - * to(string) in that they take a POINTER TO a StringPiece - * and alter that StringPiece to reflect progress information. - */ - -/** - * StringPiece to integrals, with progress information. Alters the - * StringPiece parameter to munch the already-parsed characters. - */ -template -typename std::enable_if< - std::is_integral::value - && !std::is_same::type, bool>::value, - Tgt>::type -to(StringPiece * src) { - - auto b = src->data(), past = src->data() + src->size(); - for (;; ++b) { - FOLLY_RANGE_CHECK(b < past, "No digits found in input string"); - if (!isspace(*b)) break; - } - - auto m = b; - - // First digit is customized because we test for sign - bool negative = false; - /* static */ if (std::is_signed::value) { - if (!isdigit(*m)) { - if (*m == '-') { - negative = true; - } else { - FOLLY_RANGE_CHECK(*m == '+', "Invalid leading character in conversion" - " to integral"); - } - ++b; - ++m; - } - } - FOLLY_RANGE_CHECK(m < past, "No digits found in input string"); - FOLLY_RANGE_CHECK(isdigit(*m), "Non-digit character found"); - m = detail::findFirstNonDigit(m + 1, past); - - Tgt result; - /* static */ if (!std::is_signed::value) { - result = detail::digits_to::type>(b, m); - } else { - auto t = detail::digits_to::type>(b, m); - if (negative) { - result = -t; - FOLLY_RANGE_CHECK(result <= 0, "Negative overflow"); - } else { - result = t; - FOLLY_RANGE_CHECK(result >= 0, "Overflow"); - } - } - src->advance(m - src->data()); - return result; -} - -/** - * StringPiece to bool, with progress information. Alters the - * StringPiece parameter to munch the already-parsed characters. - */ -template -typename std::enable_if< - std::is_same::type, bool>::value, - Tgt>::type -to(StringPiece * src) { - return detail::str_to_bool(src); -} - -namespace detail { - -/** - * Enforce that the suffix following a number is made up only of whitespace. - */ -inline void enforceWhitespace(const char* b, const char* e) { - for (; b != e; ++b) { - FOLLY_RANGE_CHECK(isspace(*b), to("Non-whitespace: ", *b)); - } -} - -} // namespace detail - -/** - * String or StringPiece to integrals. Accepts leading and trailing - * whitespace, but no non-space trailing characters. - */ -template -typename std::enable_if< - std::is_integral::value, - Tgt>::type -to(StringPiece src) { - Tgt result = to(&src); - detail::enforceWhitespace(src.data(), src.data() + src.size()); - return result; -} - -/******************************************************************************* - * Conversions from string types to floating-point types. - ******************************************************************************/ - -/** - * StringPiece to double, with progress information. Alters the - * StringPiece parameter to munch the already-parsed characters. - */ -template -inline typename std::enable_if< - std::is_floating_point::value, - Tgt>::type -to(StringPiece *const src) { - using namespace double_conversion; - static StringToDoubleConverter - conv(StringToDoubleConverter::ALLOW_TRAILING_JUNK - | StringToDoubleConverter::ALLOW_LEADING_SPACES, - 0.0, - // return this for junk input string - std::numeric_limits::quiet_NaN(), - nullptr, nullptr); - - FOLLY_RANGE_CHECK(!src->empty(), "No digits found in input string"); - - int length; - auto result = conv.StringToDouble(src->data(), src->size(), - &length); // processed char count - - if (!std::isnan(result)) { - src->advance(length); - return result; - } - - for (;; src->advance(1)) { - if (src->empty()) { - throw std::range_error("Unable to convert an empty string" - " to a floating point value."); - } - if (!isspace(src->front())) { - break; - } - } - - // Was that "inf[inity]"? - if (src->size() >= 3 && toupper((*src)[0]) == 'I' - && toupper((*src)[1]) == 'N' && toupper((*src)[2]) == 'F') { - if (src->size() >= 8 && - toupper((*src)[3]) == 'I' && - toupper((*src)[4]) == 'N' && - toupper((*src)[5]) == 'I' && - toupper((*src)[6]) == 'T' && - toupper((*src)[7]) == 'Y') { - src->advance(8); - } else { - src->advance(3); - } - return std::numeric_limits::infinity(); - } - - // Was that "-inf[inity]"? - if (src->size() >= 4 && toupper((*src)[0]) == '-' - && toupper((*src)[1]) == 'I' && toupper((*src)[2]) == 'N' - && toupper((*src)[3]) == 'F') { - if (src->size() >= 9 && - toupper((*src)[4]) == 'I' && - toupper((*src)[5]) == 'N' && - toupper((*src)[6]) == 'I' && - toupper((*src)[7]) == 'T' && - toupper((*src)[8]) == 'Y') { - src->advance(9); - } else { - src->advance(4); - } - return -std::numeric_limits::infinity(); - } - - // "nan"? - if (src->size() >= 3 && toupper((*src)[0]) == 'N' - && toupper((*src)[1]) == 'A' && toupper((*src)[2]) == 'N') { - src->advance(3); - return std::numeric_limits::quiet_NaN(); - } - - // "-nan"? - if (src->size() >= 4 && - toupper((*src)[0]) == '-' && - toupper((*src)[1]) == 'N' && - toupper((*src)[2]) == 'A' && - toupper((*src)[3]) == 'N') { - src->advance(4); - return -std::numeric_limits::quiet_NaN(); - } - - // All bets are off - throw std::range_error("Unable to convert \"" + src->toString() - + "\" to a floating point value."); -} - -/** - * Any string, const char*, or StringPiece to double. - */ -template -typename std::enable_if< - std::is_floating_point::value, - Tgt>::type -to(StringPiece src) { - Tgt result = to(&src); - detail::enforceWhitespace(src.data(), src.data() + src.size()); - return result; -} - -/******************************************************************************* - * Integral to floating point and back - ******************************************************************************/ - -/** - * Checked conversion from integral to flating point and back. The - * result must be convertible back to the source type without loss of - * precision. This seems Draconian but sometimes is what's needed, and - * complements existing routines nicely. For various rounding - * routines, see . - */ -template -typename std::enable_if< - (std::is_integral::value && std::is_floating_point::value) - || - (std::is_floating_point::value && std::is_integral::value), - Tgt>::type -to(const Src & value) { - Tgt result = value; - auto witness = static_cast(result); - if (value != witness) { - throw std::range_error( - to("to<>: loss of precision when converting ", value, - " to type ", typeid(Tgt).name()).c_str()); - } - return result; -} - -/******************************************************************************* - * Enum to anything and back - ******************************************************************************/ - -#if defined(__GNUC__) && __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) -// std::underlying_type became available by gcc 4.7.0 - -template -typename std::enable_if::value, Tgt>::type -to(const Src & value) { - return to(static_cast::type>(value)); -} - -template -typename std::enable_if::value, Tgt>::type -to(const Src & value) { - return static_cast(to::type>(value)); -} - -#else - -template -typename std::enable_if::value, Tgt>::type -to(const Src & value) { - /* static */ if (Src(-1) < 0) { - /* static */ if (sizeof(Src) <= sizeof(int)) { - return to(static_cast(value)); - } else { - return to(static_cast(value)); - } - } else { - /* static */ if (sizeof(Src) <= sizeof(int)) { - return to(static_cast(value)); - } else { - return to(static_cast(value)); - } - } -} - -template -typename std::enable_if::value, Tgt>::type -to(const Src & value) { - /* static */ if (Tgt(-1) < 0) { - /* static */ if (sizeof(Tgt) <= sizeof(int)) { - return static_cast(to(value)); - } else { - return static_cast(to(value)); - } - } else { - /* static */ if (sizeof(Tgt) <= sizeof(int)) { - return static_cast(to(value)); - } else { - return static_cast(to(value)); - } - } -} - -#endif // gcc 4.7 onwards - -} // namespace folly - -// FOLLY_CONV_INTERNAL is defined by Conv.cpp. Keep the FOLLY_RANGE_CHECK -// macro for use in Conv.cpp, but #undefine it everywhere else we are included, -// to avoid defining this global macro name in other files that include Conv.h. -#ifndef FOLLY_CONV_INTERNAL -#undef FOLLY_RANGE_CHECK -#endif - -#endif /* FOLLY_BASE_CONV_H_ */ diff --git a/hphp/third_party/folly/folly/CpuId.h b/hphp/third_party/folly/folly/CpuId.h deleted file mode 100644 index ab4eb27ff..000000000 --- a/hphp/third_party/folly/folly/CpuId.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_CPUID_H_ -#define FOLLY_CPUID_H_ - -#include - -namespace folly { - -/** - * Identification of an Intel CPU. - * Supports CPUID (EAX=1) feature flags. - * Values from http://www.intel.com/content/www/us/en/processors/processor-identification-cpuid-instruction-note.html - */ -class CpuId { - public: - CpuId() { -#if defined(__x86_64__) || defined(__i386__) - __asm__("cpuid" : "=c"(c_), "=d"(d_) : "a"(1) : "ebx"); -#else - // On non-Intel, none of these features exist; at least not in the same form - // as they do on Intel - c_ = 0; - d_ = 0; -#endif - } -#define X(name, r, bit) bool name() const { return r & (1U << bit); } -#define C(name, bit) X(name, c_, bit) -#define D(name, bit) X(name, d_, bit) - C(sse3, 0) - C(pclmuldq, 1) - C(dtes64, 2) - C(monitor, 3) - C(dscpl, 4) - C(vmx, 5) - C(smx, 6) - C(eist, 7) - C(tm2, 8) - C(ssse3, 9) - C(cnxtid, 10) - // 11 is reserved - C(fma, 12) - C(cx16, 13) - C(xtpr, 14) - C(pdcm, 15) - // 16 is reserved - C(pcid, 17) - C(dca, 18) - C(sse41, 19) - C(sse42, 20) - C(x2apic, 21) - C(movbe, 22) - C(popcnt, 23) - C(tscdeadline, 24) - C(aes, 25) - C(xsave, 26) - C(osxsave, 27) - C(avx, 28) - C(f16c, 29) - C(rdrand, 30) - // 31 is not used - D(fpu, 0) - D(vme, 1) - D(de, 2) - D(pse, 3) - D(tsc, 4) - D(msr, 5) - D(pae, 6) - D(mce, 7) - D(cx8, 8) - D(apic, 9) - // 10 is reserved - D(sep, 11) - D(mtrr, 12) - D(pge, 13) - D(mca, 14) - D(cmov, 15) - D(pat, 16) - D(pse36, 17) - D(psn, 18) - D(clfsh, 19) - // 20 is reserved - D(ds, 21) - D(acpi, 22) - D(mmx, 23) - D(fxsr, 24) - D(sse, 25) - D(sse2, 26) - D(ss, 27) - D(htt, 28) - D(tm, 29) - // 30 is reserved - D(pbe, 31) -#undef D -#undef C -#undef X - private: - uint32_t c_; // ECX - uint32_t d_; // EDX -}; - -} // namespace folly - -#endif /* FOLLY_CPUID_H_ */ - diff --git a/hphp/third_party/folly/folly/DiscriminatedPtr.h b/hphp/third_party/folly/folly/DiscriminatedPtr.h deleted file mode 100644 index 74f4150e8..000000000 --- a/hphp/third_party/folly/folly/DiscriminatedPtr.h +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright 2013 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. - */ - -/** - * Discriminated pointer: Type-safe pointer to one of several types. - * - * Similar to boost::variant, but has no space overhead over a raw pointer, as - * it relies on the fact that (on x86_64) there are 16 unused bits in a - * pointer. - * - * @author Tudor Bosman (tudorb@fb.com) - */ - -#ifndef FOLLY_DISCRIMINATEDPTR_H_ -#define FOLLY_DISCRIMINATEDPTR_H_ - -#include -#include -#include -#include "folly/Likely.h" -#include "folly/detail/DiscriminatedPtrDetail.h" - -#ifndef __x86_64__ -# error "DiscriminatedPtr is x64-specific code." -#endif - -namespace folly { - -/** - * Discriminated pointer. - * - * Given a list of types, a DiscriminatedPtr may point to an object - * of one of the given types, or may be empty. DiscriminatedPtr is type-safe: - * you may only get a pointer to the type that you put in, otherwise get - * throws an exception (and get_nothrow returns nullptr) - * - * This pointer does not do any kind of lifetime management -- it's not a - * "smart" pointer. You are responsible for deallocating any memory used - * to hold pointees, if necessary. - */ -template -class DiscriminatedPtr { - // <, not <=, as our indexes are 1-based (0 means "empty") - static_assert(sizeof...(Types) < std::numeric_limits::max(), - "too many types"); - - public: - /** - * Create an empty DiscriminatedPtr. - */ - DiscriminatedPtr() : data_(0) { - } - - /** - * Create a DiscriminatedPtr that points to an object of type T. - * Fails at compile time if T is not a valid type (listed in Types) - */ - template - explicit DiscriminatedPtr(T* ptr) { - set(ptr, typeIndex()); - } - - /** - * Set this DiscriminatedPtr to point to an object of type T. - * Fails at compile time if T is not a valid type (listed in Types) - */ - template - void set(T* ptr) { - set(ptr, typeIndex()); - } - - /** - * Get a pointer to the object that this DiscriminatedPtr points to, if it is - * of type T. Fails at compile time if T is not a valid type (listed in - * Types), and returns nullptr if this DiscriminatedPtr is empty or points to - * an object of a different type. - */ - template - T* get_nothrow() noexcept { - void* p = LIKELY(hasType()) ? ptr() : nullptr; - return static_cast(p); - } - - template - const T* get_nothrow() const noexcept { - const void* p = LIKELY(hasType()) ? ptr() : nullptr; - return static_cast(p); - } - - /** - * Get a pointer to the object that this DiscriminatedPtr points to, if it is - * of type T. Fails at compile time if T is not a valid type (listed in - * Types), and throws std::invalid_argument if this DiscriminatedPtr is empty - * or points to an object of a different type. - */ - template - T* get() { - if (UNLIKELY(!hasType())) { - throw std::invalid_argument("Invalid type"); - } - return static_cast(ptr()); - } - - template - const T* get() const { - if (UNLIKELY(!hasType())) { - throw std::invalid_argument("Invalid type"); - } - return static_cast(ptr()); - } - - /** - * Return true iff this DiscriminatedPtr is empty. - */ - bool empty() const { - return index() == 0; - } - - /** - * Return true iff the object pointed by this DiscriminatedPtr has type T, - * false otherwise. Fails at compile time if T is not a valid type (listed - * in Types...) - */ - template - bool hasType() const { - return index() == typeIndex(); - } - - /** - * Clear this DiscriminatedPtr, making it empty. - */ - void clear() { - data_ = 0; - } - - /** - * Assignment operator from a pointer of type T. - */ - template - DiscriminatedPtr& operator=(T* ptr) { - set(ptr); - return *this; - } - - /** - * Apply a visitor to this object, calling the appropriate overload for - * the type currently stored in DiscriminatedPtr. Throws invalid_argument - * if the DiscriminatedPtr is empty. - * - * The visitor must meet the following requirements: - * - * - The visitor must allow invocation as a function by overloading - * operator(), unambiguously accepting all values of type T* (or const T*) - * for all T in Types... - * - All operations of the function object on T* (or const T*) must - * return the same type (or a static_assert will fire). - */ - template - typename dptr_detail::VisitorResult::type apply(V&& visitor) { - size_t n = index(); - if (n == 0) throw std::invalid_argument("Empty DiscriminatedPtr"); - return dptr_detail::ApplyVisitor()( - n, std::forward(visitor), ptr()); - } - - template - typename dptr_detail::ConstVisitorResult::type apply(V&& visitor) - const { - size_t n = index(); - if (n == 0) throw std::invalid_argument("Empty DiscriminatedPtr"); - return dptr_detail::ApplyConstVisitor()( - n, std::forward(visitor), ptr()); - } - - private: - /** - * Get the 1-based type index of T in Types. - */ - template - size_t typeIndex() const { - return dptr_detail::GetTypeIndex::value; - } - - uint16_t index() const { return data_ >> 48; } - void* ptr() const { - return reinterpret_cast(data_ & ((1ULL << 48) - 1)); - } - - void set(void* p, uint16_t v) { - uintptr_t ip = reinterpret_cast(p); - CHECK(!(ip >> 48)); - ip |= static_cast(v) << 48; - data_ = ip; - } - - /** - * We store a pointer in the least significant 48 bits of data_, and a type - * index (0 = empty, or 1-based index in Types) in the most significant 16 - * bits. We rely on the fact that pointers have their most significant 16 - * bits clear on x86_64. - */ - uintptr_t data_; -}; - -} // namespace folly - -#endif /* FOLLY_DISCRIMINATEDPTR_H_ */ - diff --git a/hphp/third_party/folly/folly/DynamicConverter.h b/hphp/third_party/folly/folly/DynamicConverter.h deleted file mode 100644 index 2c3d2c1d9..000000000 --- a/hphp/third_party/folly/folly/DynamicConverter.h +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright 2013 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. - */ - -// @author Nicholas Ormrod - -#ifndef DYNAMIC_CONVERTER_H -#define DYNAMIC_CONVERTER_H - -#include "folly/dynamic.h" -namespace folly { - template T convertTo(const dynamic&); - template dynamic toDynamic(const T&); -} - -/** - * convertTo returns a well-typed representation of the input dynamic. - * - * Example: - * - * dynamic d = { { 1, 2, 3 }, { 4, 5 } }; // a vector of vector of int - * auto vvi = convertTo>>(d); - * - * See docs/DynamicConverter.md for supported types and customization - */ - - -#include -#include -#include -#include -#include "folly/Likely.h" - -namespace folly { - -/////////////////////////////////////////////////////////////////////////////// -// traits - -namespace dynamicconverter_detail { - -BOOST_MPL_HAS_XXX_TRAIT_DEF(value_type); -BOOST_MPL_HAS_XXX_TRAIT_DEF(iterator); -BOOST_MPL_HAS_XXX_TRAIT_DEF(mapped_type); - -template struct class_is_container { - typedef std::reverse_iterator some_iterator; - enum { value = has_value_type::value && - has_iterator::value && - std::is_constructible::value }; -}; - -template struct class_is_range { - enum { value = has_value_type::value && - has_iterator::value }; -}; - - -template struct is_container - : std::conditional< - std::is_class::value, - class_is_container, - std::false_type - >::type {}; - -template struct is_range - : std::conditional< - std::is_class::value, - class_is_range, - std::false_type - >::type {}; - -template struct is_associative_container - : std::integral_constant< - bool, - is_range::value && has_mapped_type::value - > {}; - -} // namespace dynamicconverter_detail - -/////////////////////////////////////////////////////////////////////////////// -// custom iterators - -/** - * We have iterators that dereference to dynamics, but need iterators - * that dereference to typename T. - * - * Implementation details: - * 1. We cache the value of the dereference operator. This is necessary - * because boost::iterator_adaptor requires *it to return a - * reference. - * 2. For const reasons, we cannot call operator= to refresh the - * cache: we must call the destructor then placement new. - */ - -namespace dynamicconverter_detail { - -template -struct Dereferencer { - static inline void - derefToCache(T* mem, const dynamic::const_item_iterator& it) { - throw TypeError("array", dynamic::Type::OBJECT); - } - - static inline void derefToCache(T* mem, const dynamic::const_iterator& it) { - new (mem) T(convertTo(*it)); - } -}; - -template -struct Dereferencer> { - static inline void - derefToCache(std::pair* mem, const dynamic::const_item_iterator& it) { - new (mem) std::pair( - convertTo(it->first), convertTo(it->second) - ); - } - - // Intentional duplication of the code in Dereferencer - template - static inline void derefToCache(T* mem, const dynamic::const_iterator& it) { - new (mem) T(convertTo(*it)); - } -}; - -template -class Transformer : public boost::iterator_adaptor< - Transformer, - It, - typename T::value_type - > { - friend class boost::iterator_core_access; - - typedef typename T::value_type ttype; - - mutable ttype cache_; - mutable bool valid_; - - void increment() { - ++this->base_reference(); - valid_ = false; - } - - ttype& dereference() const { - if (LIKELY(!valid_)) { - cache_.~ttype(); - Dereferencer::derefToCache(&cache_, this->base_reference()); - valid_ = true; - } - return cache_; - } - -public: - explicit Transformer(const It& it) - : Transformer::iterator_adaptor_(it), valid_(false) {} -}; - -// conversion factory -template -static inline std::move_iterator> -conversionIterator(const It& it) { - return std::make_move_iterator(Transformer(it)); -} - -} // namespace dynamicconverter_detail - -/////////////////////////////////////////////////////////////////////////////// -// DynamicConverter specializations - -template struct DynamicConverter; - -/** - * Each specialization of DynamicConverter has the function - * 'static T convert(const dynamic& d);' - */ - -// boolean -template <> -struct DynamicConverter { - static bool convert(const dynamic& d) { - return d.asBool(); - } -}; - -// integrals -template -struct DynamicConverter::value && - !std::is_same::value>::type> { - static T convert(const dynamic& d) { - return static_cast(d.asInt()); - } -}; - -// floating point -template -struct DynamicConverter::value>::type> { - static T convert(const dynamic& d) { - return static_cast(d.asDouble()); - } -}; - -// fbstring -template <> -struct DynamicConverter { - static folly::fbstring convert(const dynamic& d) { - return d.asString(); - } -}; - -// std::string -template <> -struct DynamicConverter { - static std::string convert(const dynamic& d) { - return d.asString().toStdString(); - } -}; - -// std::pair -template -struct DynamicConverter> { - static std::pair convert(const dynamic& d) { - if (d.isArray() && d.size() == 2) { - return std::make_pair(convertTo(d[0]), convertTo(d[1])); - } else if (d.isObject() && d.size() == 1) { - auto it = d.items().begin(); - return std::make_pair(convertTo(it->first), convertTo(it->second)); - } else { - throw TypeError("array (size 2) or object (size 1)", d.type()); - } - } -}; - -// containers -template -struct DynamicConverter::value>::type> { - static C convert(const dynamic& d) { - if (d.isArray()) { - return C(dynamicconverter_detail::conversionIterator(d.begin()), - dynamicconverter_detail::conversionIterator(d.end())); - } else if (d.isObject()) { - return C(dynamicconverter_detail::conversionIterator - (d.items().begin()), - dynamicconverter_detail::conversionIterator - (d.items().end())); - } else { - throw TypeError("object or array", d.type()); - } - } - -}; - -template -struct DynamicConstructor { - static dynamic construct(const C& x) { - return dynamic(x); - } -}; - -template -struct DynamicConstructor::value>::type> { - static dynamic construct(const C& x) { - dynamic d = dynamic::object; - for (auto& pair : x) { - d.insert(toDynamic(pair.first), toDynamic(pair.second)); - } - return d; - } -}; - -template -struct DynamicConstructor::value && - !std::is_constructible::value && - dynamicconverter_detail::is_range::value>::type> { - static dynamic construct(const C& x) { - dynamic d = {}; - for (auto& item : x) { - d.push_back(toDynamic(item)); - } - return d; - } -}; - -template -struct DynamicConstructor, void> { - static dynamic construct(const std::pair& x) { - dynamic d = {}; - d.push_back(toDynamic(x.first)); - d.push_back(toDynamic(x.second)); - return d; - } -}; - -/////////////////////////////////////////////////////////////////////////////// -// convertTo implementation - -template -T convertTo(const dynamic& d) { - return DynamicConverter::type>::convert(d); -} - -template -dynamic toDynamic(const T& x) { - return DynamicConstructor::type>::construct(x); -} - -} // namespace folly - -#endif // DYNAMIC_CONVERTER_H - diff --git a/hphp/third_party/folly/folly/Exception.h b/hphp/third_party/folly/folly/Exception.h deleted file mode 100644 index fad0b691b..000000000 --- a/hphp/third_party/folly/folly/Exception.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_EXCEPTION_H_ -#define FOLLY_EXCEPTION_H_ - -#include - -#include -#include -#include - -#include "folly/Conv.h" -#include "folly/FBString.h" -#include "folly/Likely.h" -#include "folly/Portability.h" - -namespace folly { - -// Various helpers to throw appropriate std::system_error exceptions from C -// library errors (returned in errno, as positive return values (many POSIX -// functions), or as negative return values (Linux syscalls)) -// -// The *Explicit functions take an explicit value for errno. - -// Helper to throw std::system_error -void throwSystemErrorExplicit(int err, const char*) FOLLY_NORETURN; -inline void throwSystemErrorExplicit(int err, const char* msg) { - throw std::system_error(err, std::system_category(), msg); -} - -template -void throwSystemErrorExplicit(int, Args&&... args) FOLLY_NORETURN; -template -void throwSystemErrorExplicit(int err, Args&&... args) { - throwSystemErrorExplicit( - err, to(std::forward(args)...).c_str()); -} - -// Helper to throw std::system_error from errno and components of a string -template -void throwSystemError(Args&&... args) FOLLY_NORETURN; -template -void throwSystemError(Args&&... args) { - throwSystemErrorExplicit(errno, std::forward(args)...); -} - -// Check a Posix return code (0 on success, error number on error), throw -// on error. -template -void checkPosixError(int err, Args&&... args) { - if (UNLIKELY(err != 0)) { - throwSystemErrorExplicit(err, std::forward(args)...); - } -} - -// Check a Linux kernel-style return code (>= 0 on success, negative error -// number on error), throw on error. -template -void checkKernelError(ssize_t ret, Args&&... args) { - if (UNLIKELY(ret < 0)) { - throwSystemErrorExplicit(-ret, std::forward(args)...); - } -} - -// Check a traditional Unix return code (-1 and sets errno on error), throw -// on error. -template -void checkUnixError(ssize_t ret, Args&&... args) { - if (UNLIKELY(ret == -1)) { - throwSystemError(std::forward(args)...); - } -} - -template -void checkUnixErrorExplicit(ssize_t ret, int savedErrno, Args&&... args) { - if (UNLIKELY(ret == -1)) { - throwSystemErrorExplicit(savedErrno, std::forward(args)...); - } -} - -// Check the return code from a fopen-style function (returns a non-nullptr -// FILE* on success, nullptr on error, sets errno). Works with fopen, fdopen, -// freopen, tmpfile, etc. -template -void checkFopenError(FILE* fp, Args&&... args) { - if (UNLIKELY(!fp)) { - throwSystemError(std::forward(args)...); - } -} - -template -void checkFopenErrorExplicit(FILE* fp, int savedErrno, Args&&... args) { - if (UNLIKELY(!fp)) { - throwSystemErrorExplicit(savedErrno, std::forward(args)...); - } -} - -} // namespace folly - -#endif /* FOLLY_EXCEPTION_H_ */ - diff --git a/hphp/third_party/folly/folly/FBString.h b/hphp/third_party/folly/folly/FBString.h deleted file mode 100644 index cc34fe668..000000000 --- a/hphp/third_party/folly/folly/FBString.h +++ /dev/null @@ -1,2398 +0,0 @@ -/* - * Copyright 2013 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. - */ - -// @author: Andrei Alexandrescu (aalexandre) -// String type. - -#ifndef FOLLY_BASE_FBSTRING_H_ -#define FOLLY_BASE_FBSTRING_H_ - -/** - fbstring's behavior can be configured via two macro definitions, as - follows. Normally, fbstring does not write a '\0' at the end of - each string whenever it changes the underlying characters. Instead, - it lazily writes the '\0' whenever either c_str() or data() - called. - - This is standard-compliant behavior and may save costs in some - circumstances. However, it may be surprising to some client code - because c_str() and data() are const member functions (fbstring - uses the "mutable" storage class for its own state). - - In order to appease client code that expects fbstring to be - zero-terminated at all times, if the preprocessor symbol - FBSTRING_CONSERVATIVE is defined, fbstring does exactly that, - i.e. it goes the extra mile to guarantee a '\0' is always planted - at the end of its data. - - On the contrary, if the desire is to debug faulty client code that - unduly assumes the '\0' is present, fbstring plants a '^' (i.e., - emphatically NOT a zero) at the end of each string if - FBSTRING_PERVERSE is defined. (Calling c_str() or data() still - writes the '\0', of course.) - - The preprocessor symbols FBSTRING_PERVERSE and - FBSTRING_CONSERVATIVE cannot be defined simultaneously. This is - enforced during preprocessing. -*/ - -//#define FBSTRING_PERVERSE -//#define FBSTRING_CONSERVATIVE - -#ifdef FBSTRING_PERVERSE -#ifdef FBSTRING_CONSERVATIVE -#error Cannot define both FBSTRING_PERVERSE and FBSTRING_CONSERVATIVE. -#endif -#endif - -// This file appears in two locations: inside fbcode and in the -// libstdc++ source code (when embedding fbstring as std::string). -// To aid in this schizophrenic use, two macros are defined in -// c++config.h: -// _LIBSTDCXX_FBSTRING - Set inside libstdc++. This is useful to -// gate use inside fbcode v. libstdc++ -#include - -#ifdef _LIBSTDCXX_FBSTRING - -#pragma GCC system_header - -// Handle the cases where the fbcode version (folly/Malloc.h) is included -// either before or after this inclusion. -#ifdef FOLLY_MALLOC_H_ -#undef FOLLY_MALLOC_H_ -#include "basic_fbstring_malloc.h" -#else -#include "basic_fbstring_malloc.h" -#undef FOLLY_MALLOC_H_ -#endif - -#else // !_LIBSTDCXX_FBSTRING - -#include -#include -#include - -#include "folly/Traits.h" -#include "folly/Malloc.h" -#include "folly/Hash.h" - -#endif - -// We defined these here rather than including Likely.h to avoid -// redefinition errors when fbstring is imported into libstdc++. -#define FBSTRING_LIKELY(x) (__builtin_expect((x), 1)) -#define FBSTRING_UNLIKELY(x) (__builtin_expect((x), 0)) - -#include -#include -#include - -// Ignore shadowing warnings within this file, so includers can use -Wshadow. -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wshadow" - -#ifdef _LIBSTDCXX_FBSTRING -namespace std _GLIBCXX_VISIBILITY(default) { -_GLIBCXX_BEGIN_NAMESPACE_VERSION -#else -namespace folly { -#endif - -namespace fbstring_detail { - -template -inline -OutIt copy_n(InIt b, - typename std::iterator_traits::difference_type n, - OutIt d) { - for (; n != 0; --n, ++b, ++d) { - assert((const void*)&*d != &*b); - *d = *b; - } - return d; -} - -template -inline void pod_fill(Pod* b, Pod* e, T c) { - assert(b && e && b <= e); - /*static*/ if (sizeof(T) == 1) { - memset(b, c, e - b); - } else { - auto const ee = b + ((e - b) & ~7u); - for (; b != ee; b += 8) { - b[0] = c; - b[1] = c; - b[2] = c; - b[3] = c; - b[4] = c; - b[5] = c; - b[6] = c; - b[7] = c; - } - // Leftovers - for (; b != e; ++b) { - *b = c; - } - } -} - -/* - * Lightly structured memcpy, simplifies copying PODs and introduces - * some asserts. Unfortunately using this function may cause - * measurable overhead (presumably because it adjusts from a begin/end - * convention to a pointer/size convention, so it does some extra - * arithmetic even though the caller might have done the inverse - * adaptation outside). - */ -template -inline void pod_copy(const Pod* b, const Pod* e, Pod* d) { - assert(e >= b); - assert(d >= e || d + (e - b) <= b); - memcpy(d, b, (e - b) * sizeof(Pod)); -} - -/* - * Lightly structured memmove, simplifies copying PODs and introduces - * some asserts - */ -template -inline void pod_move(const Pod* b, const Pod* e, Pod* d) { - assert(e >= b); - memmove(d, b, (e - b) * sizeof(*b)); -} - -} // namespace fbstring_detail - -/** - * Defines a special acquisition method for constructing fbstring - * objects. AcquireMallocatedString means that the user passes a - * pointer to a malloc-allocated string that the fbstring object will - * take into custody. - */ -enum class AcquireMallocatedString {}; - -/* - * fbstring_core_model is a mock-up type that defines all required - * signatures of a fbstring core. The fbstring class itself uses such - * a core object to implement all of the numerous member functions - * required by the standard. - * - * If you want to define a new core, copy the definition below and - * implement the primitives. Then plug the core into basic_fbstring as - * a template argument. - -template -class fbstring_core_model { -public: - fbstring_core_model(); - fbstring_core_model(const fbstring_core_model &); - ~fbstring_core_model(); - // Returns a pointer to string's buffer (currently only contiguous - // strings are supported). The pointer is guaranteed to be valid - // until the next call to a non-const member function. - const Char * data() const; - // Much like data(), except the string is prepared to support - // character-level changes. This call is a signal for - // e.g. reference-counted implementation to fork the data. The - // pointer is guaranteed to be valid until the next call to a - // non-const member function. - Char * mutable_data(); - // Returns a pointer to string's buffer and guarantees that a - // readable '\0' lies right after the buffer. The pointer is - // guaranteed to be valid until the next call to a non-const member - // function. - const Char * c_str() const; - // Shrinks the string by delta characters. Asserts that delta <= - // size(). - void shrink(size_t delta); - // Expands the string by delta characters (i.e. after this call - // size() will report the old size() plus delta) but without - // initializing the expanded region. Returns a pointer to the memory - // to be initialized (the beginning of the expanded portion). The - // caller is expected to fill the expanded area appropriately. - Char* expand_noinit(size_t delta); - // Expands the string by one character and sets the last character - // to c. - void push_back(Char c); - // Returns the string's size. - size_t size() const; - // Returns the string's capacity, i.e. maximum size that the string - // can grow to without reallocation. Note that for reference counted - // strings that's technically a lie - even assigning characters - // within the existing size would cause a reallocation. - size_t capacity() const; - // Returns true if the data underlying the string is actually shared - // across multiple strings (in a refcounted fashion). - bool isShared() const; - // Makes sure that at least minCapacity characters are available for - // the string without reallocation. For reference-counted strings, - // it should fork the data even if minCapacity < size(). - void reserve(size_t minCapacity); -private: - // Do not implement - fbstring_core_model& operator=(const fbstring_core_model &); -}; -*/ - -/** - * gcc-4.7 throws what appears to be some false positive uninitialized - * warnings for the members of the MediumLarge struct. So, mute them here. - */ -#if defined(__GNUC__) && !defined(__clang__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wuninitialized" -#endif - -/** - * This is the core of the string. The code should work on 32- and - * 64-bit architectures and with any Char size. Porting to big endian - * architectures would require some changes. - * - * The storage is selected as follows (assuming we store one-byte - * characters on a 64-bit machine): (a) "small" strings between 0 and - * 23 chars are stored in-situ without allocation (the rightmost byte - * stores the size); (b) "medium" strings from 24 through 254 chars - * are stored in malloc-allocated memory that is copied eagerly; (c) - * "large" strings of 255 chars and above are stored in a similar - * structure as medium arrays, except that the string is - * reference-counted and copied lazily. the reference count is - * allocated right before the character array. - * - * The discriminator between these three strategies sits in the two - * most significant bits of the rightmost char of the storage. If - * neither is set, then the string is small (and its length sits in - * the lower-order bits of that rightmost character). If the MSb is - * set, the string is medium width. If the second MSb is set, then the - * string is large. - */ -template class fbstring_core { -public: - fbstring_core() { - // Only initialize the tag, will set the MSBs (i.e. the small - // string size) to zero too - ml_.capacity_ = maxSmallSize << (8 * (sizeof(size_t) - sizeof(Char))); - // or: setSmallSize(0); - writeTerminator(); - assert(category() == isSmall && size() == 0); - } - - fbstring_core(const fbstring_core & rhs) { - assert(&rhs != this); - // Simplest case first: small strings are bitblitted - if (rhs.category() == isSmall) { - assert(offsetof(MediumLarge, data_) == 0); - assert(offsetof(MediumLarge, size_) == sizeof(ml_.data_)); - assert(offsetof(MediumLarge, capacity_) == 2 * sizeof(ml_.data_)); - const size_t size = rhs.smallSize(); - if (size == 0) { - ml_.capacity_ = rhs.ml_.capacity_; - writeTerminator(); - } else { - // Just write the whole thing, don't look at details. In - // particular we need to copy capacity anyway because we want - // to set the size (don't forget that the last character, - // which stores a short string's length, is shared with the - // ml_.capacity field). - ml_ = rhs.ml_; - } - assert(category() == isSmall && this->size() == rhs.size()); - } else if (rhs.category() == isLarge) { - // Large strings are just refcounted - ml_ = rhs.ml_; - RefCounted::incrementRefs(ml_.data_); - assert(category() == isLarge && size() == rhs.size()); - } else { - // Medium strings are copied eagerly. Don't forget to allocate - // one extra Char for the null terminator. - auto const allocSize = - goodMallocSize((1 + rhs.ml_.size_) * sizeof(Char)); - ml_.data_ = static_cast(checkedMalloc(allocSize)); - fbstring_detail::pod_copy(rhs.ml_.data_, - // 1 for terminator - rhs.ml_.data_ + rhs.ml_.size_ + 1, - ml_.data_); - // No need for writeTerminator() here, we copied one extra - // element just above. - ml_.size_ = rhs.ml_.size_; - ml_.capacity_ = (allocSize / sizeof(Char) - 1) | isMedium; - assert(category() == isMedium); - } - assert(size() == rhs.size()); - assert(memcmp(data(), rhs.data(), size() * sizeof(Char)) == 0); - } - - fbstring_core(fbstring_core&& goner) { - if (goner.category() == isSmall) { - // Just copy, leave the goner in peace - new(this) fbstring_core(goner.small_, goner.smallSize()); - } else { - // Take goner's guts - ml_ = goner.ml_; - // Clean goner's carcass - goner.setSmallSize(0); - } - } - - fbstring_core(const Char *const data, const size_t size) { - // Simplest case first: small strings are bitblitted - if (size <= maxSmallSize) { - // Layout is: Char* data_, size_t size_, size_t capacity_ - /*static_*/assert(sizeof(*this) == sizeof(Char*) + 2 * sizeof(size_t)); - /*static_*/assert(sizeof(Char*) == sizeof(size_t)); - // sizeof(size_t) must be a power of 2 - /*static_*/assert((sizeof(size_t) & (sizeof(size_t) - 1)) == 0); - - // If data is aligned, use fast word-wise copying. Otherwise, - // use conservative memcpy. - if (reinterpret_cast(data) & (sizeof(size_t) - 1)) { - fbstring_detail::pod_copy(data, data + size, small_); - } else { - // Copy one word (64 bits) at a time - const size_t byteSize = size * sizeof(Char); - if (byteSize > 2 * sizeof(size_t)) { - // Copy three words - ml_.capacity_ = reinterpret_cast(data)[2]; - copyTwo: - ml_.size_ = reinterpret_cast(data)[1]; - copyOne: - ml_.data_ = *reinterpret_cast(const_cast(data)); - } else if (byteSize > sizeof(size_t)) { - // Copy two words - goto copyTwo; - } else if (size > 0) { - // Copy one word - goto copyOne; - } - } - setSmallSize(size); - } else if (size <= maxMediumSize) { - // Medium strings are allocated normally. Don't forget to - // allocate one extra Char for the terminating null. - auto const allocSize = goodMallocSize((1 + size) * sizeof(Char)); - ml_.data_ = static_cast(checkedMalloc(allocSize)); - fbstring_detail::pod_copy(data, data + size, ml_.data_); - ml_.size_ = size; - ml_.capacity_ = (allocSize / sizeof(Char) - 1) | isMedium; - } else { - // Large strings are allocated differently - size_t effectiveCapacity = size; - auto const newRC = RefCounted::create(data, & effectiveCapacity); - ml_.data_ = newRC->data_; - ml_.size_ = size; - ml_.capacity_ = effectiveCapacity | isLarge; - } - writeTerminator(); - assert(this->size() == size); - assert(memcmp(this->data(), data, size * sizeof(Char)) == 0); - } - - ~fbstring_core() { - auto const c = category(); - if (c == isSmall) { - return; - } - if (c == isMedium) { - free(ml_.data_); - return; - } - RefCounted::decrementRefs(ml_.data_); - } - - // Snatches a previously mallocated string. The parameter "size" - // is the size of the string, and the parameter "capacity" is the size - // of the mallocated block. The string must be \0-terminated, so - // data[size] == '\0' and capacity >= size + 1. - // - // So if you want a 2-character string, pass malloc(3) as "data", pass 2 as - // "size", and pass 3 as "capacity". - fbstring_core(Char *const data, const size_t size, - const size_t capacity, - AcquireMallocatedString) { - if (size > 0) { - assert(capacity > size); - assert(data[size] == '\0'); - // Use the medium string storage - ml_.data_ = data; - ml_.size_ = size; - ml_.capacity_ = capacity | isMedium; - } else { - // No need for the memory - free(data); - setSmallSize(0); - } - } - - // swap below doesn't test whether &rhs == this (and instead - // potentially does extra work) on the premise that the rarity of - // that situation actually makes the check more expensive than is - // worth. - void swap(fbstring_core & rhs) { - auto const t = ml_; - ml_ = rhs.ml_; - rhs.ml_ = t; - } - - // In C++11 data() and c_str() are 100% equivalent. - const Char * data() const { - return c_str(); - } - - Char * mutable_data() { - auto const c = category(); - if (c == isSmall) { - return small_; - } - assert(c == isMedium || c == isLarge); - if (c == isLarge && RefCounted::refs(ml_.data_) > 1) { - // Ensure unique. - size_t effectiveCapacity = ml_.capacity(); - auto const newRC = RefCounted::create(& effectiveCapacity); - // If this fails, someone placed the wrong capacity in an - // fbstring. - assert(effectiveCapacity >= ml_.capacity()); - fbstring_detail::pod_copy(ml_.data_, ml_.data_ + ml_.size_ + 1, - newRC->data_); - RefCounted::decrementRefs(ml_.data_); - ml_.data_ = newRC->data_; - // No need to call writeTerminator(), we have + 1 above. - } - return ml_.data_; - } - - const Char * c_str() const { - auto const c = category(); -#ifdef FBSTRING_PERVERSE - if (c == isSmall) { - assert(small_[smallSize()] == TERMINATOR || smallSize() == maxSmallSize - || small_[smallSize()] == '\0'); - small_[smallSize()] = '\0'; - return small_; - } - assert(c == isMedium || c == isLarge); - assert(ml_.data_[ml_.size_] == TERMINATOR || ml_.data_[ml_.size_] == '\0'); - ml_.data_[ml_.size_] = '\0'; -#elif defined(FBSTRING_CONSERVATIVE) - if (c == isSmall) { - assert(small_[smallSize()] == '\0'); - return small_; - } - assert(c == isMedium || c == isLarge); - assert(ml_.data_[ml_.size_] == '\0'); -#else - if (c == isSmall) { - small_[smallSize()] = '\0'; - return small_; - } - assert(c == isMedium || c == isLarge); - ml_.data_[ml_.size_] = '\0'; -#endif - return ml_.data_; - } - - void shrink(const size_t delta) { - if (category() == isSmall) { - // Check for underflow - assert(delta <= smallSize()); - setSmallSize(smallSize() - delta); - } else if (category() == isMedium || RefCounted::refs(ml_.data_) == 1) { - // Medium strings and unique large strings need no special - // handling. - assert(ml_.size_ >= delta); - ml_.size_ -= delta; - } else { - assert(ml_.size_ >= delta); - // Shared large string, must make unique. This is because of the - // durn terminator must be written, which may trample the shared - // data. - if (delta) { - fbstring_core(ml_.data_, ml_.size_ - delta).swap(*this); - } - // No need to write the terminator. - return; - } - writeTerminator(); - } - - void reserve(size_t minCapacity) { - if (category() == isLarge) { - // Ensure unique - if (RefCounted::refs(ml_.data_) > 1) { - // We must make it unique regardless; in-place reallocation is - // useless if the string is shared. In order to not surprise - // people, reserve the new block at current capacity or - // more. That way, a string's capacity never shrinks after a - // call to reserve. - minCapacity = std::max(minCapacity, ml_.capacity()); - auto const newRC = RefCounted::create(& minCapacity); - fbstring_detail::pod_copy(ml_.data_, ml_.data_ + ml_.size_ + 1, - newRC->data_); - // Done with the old data. No need to call writeTerminator(), - // we have + 1 above. - RefCounted::decrementRefs(ml_.data_); - ml_.data_ = newRC->data_; - ml_.capacity_ = minCapacity | isLarge; - // size remains unchanged - } else { - // String is not shared, so let's try to realloc (if needed) - if (minCapacity > ml_.capacity()) { - // Asking for more memory - auto const newRC = - RefCounted::reallocate(ml_.data_, ml_.size_, - ml_.capacity(), minCapacity); - ml_.data_ = newRC->data_; - ml_.capacity_ = minCapacity | isLarge; - writeTerminator(); - } - assert(capacity() >= minCapacity); - } - } else if (category() == isMedium) { - // String is not shared - if (minCapacity <= ml_.capacity()) { - return; // nothing to do, there's enough room - } - if (minCapacity <= maxMediumSize) { - // Keep the string at medium size. Don't forget to allocate - // one extra Char for the terminating null. - size_t capacityBytes = goodMallocSize((1 + minCapacity) * sizeof(Char)); - ml_.data_ = static_cast( - smartRealloc( - ml_.data_, - ml_.size_ * sizeof(Char), - ml_.capacity() * sizeof(Char), - capacityBytes)); - writeTerminator(); - ml_.capacity_ = (capacityBytes / sizeof(Char) - 1) | isMedium; - } else { - // Conversion from medium to large string - fbstring_core nascent; - // Will recurse to another branch of this function - nascent.reserve(minCapacity); - nascent.ml_.size_ = ml_.size_; - fbstring_detail::pod_copy(ml_.data_, ml_.data_ + ml_.size_, - nascent.ml_.data_); - nascent.swap(*this); - writeTerminator(); - assert(capacity() >= minCapacity); - } - } else { - assert(category() == isSmall); - if (minCapacity > maxMediumSize) { - // large - auto const newRC = RefCounted::create(& minCapacity); - auto const size = smallSize(); - fbstring_detail::pod_copy(small_, small_ + size + 1, newRC->data_); - // No need for writeTerminator(), we wrote it above with + 1. - ml_.data_ = newRC->data_; - ml_.size_ = size; - ml_.capacity_ = minCapacity | isLarge; - assert(capacity() >= minCapacity); - } else if (minCapacity > maxSmallSize) { - // medium - // Don't forget to allocate one extra Char for the terminating null - auto const allocSizeBytes = - goodMallocSize((1 + minCapacity) * sizeof(Char)); - auto const data = static_cast(checkedMalloc(allocSizeBytes)); - auto const size = smallSize(); - fbstring_detail::pod_copy(small_, small_ + size + 1, data); - // No need for writeTerminator(), we wrote it above with + 1. - ml_.data_ = data; - ml_.size_ = size; - ml_.capacity_ = (allocSizeBytes / sizeof(Char) - 1) | isMedium; - } else { - // small - // Nothing to do, everything stays put - } - } - assert(capacity() >= minCapacity); - } - - Char * expand_noinit(const size_t delta) { - // Strategy is simple: make room, then change size - assert(capacity() >= size()); - size_t sz, newSz; - if (category() == isSmall) { - sz = smallSize(); - newSz = sz + delta; - if (newSz <= maxSmallSize) { - setSmallSize(newSz); - writeTerminator(); - return small_ + sz; - } - reserve(newSz); - } else { - sz = ml_.size_; - newSz = ml_.size_ + delta; - if (newSz > capacity()) { - reserve(newSz); - } - } - assert(capacity() >= newSz); - // Category can't be small - we took care of that above - assert(category() == isMedium || category() == isLarge); - ml_.size_ = newSz; - writeTerminator(); - assert(size() == newSz); - return ml_.data_ + sz; - } - - void push_back(Char c) { - assert(capacity() >= size()); - size_t sz; - if (category() == isSmall) { - sz = smallSize(); - if (sz < maxSmallSize) { - setSmallSize(sz + 1); - small_[sz] = c; - writeTerminator(); - return; - } - reserve(maxSmallSize * 2); - } else { - sz = ml_.size_; - if (sz == capacity()) { // always true for isShared() - reserve(sz * 3 / 2); // ensures not shared - } - } - assert(!isShared()); - assert(capacity() >= sz + 1); - // Category can't be small - we took care of that above - assert(category() == isMedium || category() == isLarge); - ml_.size_ = sz + 1; - ml_.data_[sz] = c; - writeTerminator(); - } - - size_t size() const { - return category() == isSmall ? smallSize() : ml_.size_; - } - - size_t capacity() const { - switch (category()) { - case isSmall: - return maxSmallSize; - case isLarge: - // For large-sized strings, a multi-referenced chunk has no - // available capacity. This is because any attempt to append - // data would trigger a new allocation. - if (RefCounted::refs(ml_.data_) > 1) return ml_.size_; - default: {} - } - return ml_.capacity(); - } - - bool isShared() const { - return category() == isLarge && RefCounted::refs(ml_.data_) > 1; - } - -#ifdef FBSTRING_PERVERSE - enum { TERMINATOR = '^' }; -#else - enum { TERMINATOR = '\0' }; -#endif - - void writeTerminator() { -#if defined(FBSTRING_PERVERSE) || defined(FBSTRING_CONSERVATIVE) - if (category() == isSmall) { - const auto s = smallSize(); - if (s != maxSmallSize) { - small_[s] = TERMINATOR; - } - } else { - ml_.data_[ml_.size_] = TERMINATOR; - } -#endif - } - -private: - // Disabled - fbstring_core & operator=(const fbstring_core & rhs); - - struct MediumLarge { - Char * data_; - size_t size_; - size_t capacity_; - - size_t capacity() const { - return capacity_ & capacityExtractMask; - } - }; - - struct RefCounted { - std::atomic refCount_; - Char data_[1]; - - static RefCounted * fromData(Char * p) { - return static_cast( - static_cast( - static_cast(static_cast(p)) - - sizeof(refCount_))); - } - - static size_t refs(Char * p) { - return fromData(p)->refCount_.load(std::memory_order_acquire); - } - - static void incrementRefs(Char * p) { - fromData(p)->refCount_.fetch_add(1, std::memory_order_acq_rel); - } - - static void decrementRefs(Char * p) { - auto const dis = fromData(p); - size_t oldcnt = dis->refCount_.fetch_sub(1, std::memory_order_acq_rel); - assert(oldcnt > 0); - if (oldcnt == 1) { - free(dis); - } - } - - static RefCounted * create(size_t * size) { - // Don't forget to allocate one extra Char for the terminating - // null. In this case, however, one Char is already part of the - // struct. - const size_t allocSize = goodMallocSize( - sizeof(RefCounted) + *size * sizeof(Char)); - auto result = static_cast(checkedMalloc(allocSize)); - result->refCount_.store(1, std::memory_order_release); - *size = (allocSize - sizeof(RefCounted)) / sizeof(Char); - return result; - } - - static RefCounted * create(const Char * data, size_t * size) { - const size_t effectiveSize = *size; - auto result = create(size); - fbstring_detail::pod_copy(data, data + effectiveSize, result->data_); - return result; - } - - static RefCounted * reallocate(Char *const data, - const size_t currentSize, - const size_t currentCapacity, - const size_t newCapacity) { - assert(newCapacity > 0 && newCapacity > currentSize); - auto const dis = fromData(data); - assert(dis->refCount_.load(std::memory_order_acquire) == 1); - // Don't forget to allocate one extra Char for the terminating - // null. In this case, however, one Char is already part of the - // struct. - auto result = static_cast( - smartRealloc(dis, - sizeof(RefCounted) + currentSize * sizeof(Char), - sizeof(RefCounted) + currentCapacity * sizeof(Char), - sizeof(RefCounted) + newCapacity * sizeof(Char))); - assert(result->refCount_.load(std::memory_order_acquire) == 1); - return result; - } - }; - - union { - mutable Char small_[sizeof(MediumLarge) / sizeof(Char)]; - mutable MediumLarge ml_; - }; - - enum { - lastChar = sizeof(MediumLarge) - 1, - maxSmallSize = lastChar / sizeof(Char), - maxMediumSize = 254 / sizeof(Char), // coincides with the small - // bin size in dlmalloc - categoryExtractMask = sizeof(size_t) == 4 ? 0xC0000000 : 0xC000000000000000, - capacityExtractMask = ~categoryExtractMask, - }; - static_assert(!(sizeof(MediumLarge) % sizeof(Char)), - "Corrupt memory layout for fbstring."); - - enum Category { - isSmall = 0, - isMedium = sizeof(size_t) == 4 ? 0x80000000 : 0x8000000000000000, - isLarge = sizeof(size_t) == 4 ? 0x40000000 : 0x4000000000000000, - }; - - Category category() const { - // Assumes little endian - return static_cast(ml_.capacity_ & categoryExtractMask); - } - - size_t smallSize() const { - assert(category() == isSmall && small_[maxSmallSize] <= maxSmallSize); - return static_cast(maxSmallSize) - - static_cast(small_[maxSmallSize]); - } - - void setSmallSize(size_t s) { - // Warning: this should work with uninitialized strings too, - // so don't assume anything about the previous value of - // small_[maxSmallSize]. - assert(s <= maxSmallSize); - small_[maxSmallSize] = maxSmallSize - s; - } -}; - -#if defined(__GNUC__) && !defined(__clang__) -# pragma GCC diagnostic pop -#endif - -#ifndef _LIBSTDCXX_FBSTRING -/** - * Dummy fbstring core that uses an actual std::string. This doesn't - * make any sense - it's just for testing purposes. - */ -template -class dummy_fbstring_core { -public: - dummy_fbstring_core() { - } - dummy_fbstring_core(const dummy_fbstring_core& another) - : backend_(another.backend_) { - } - dummy_fbstring_core(const Char * s, size_t n) - : backend_(s, n) { - } - void swap(dummy_fbstring_core & rhs) { - backend_.swap(rhs.backend_); - } - const Char * data() const { - return backend_.data(); - } - Char * mutable_data() { - //assert(!backend_.empty()); - return &*backend_.begin(); - } - void shrink(size_t delta) { - assert(delta <= size()); - backend_.resize(size() - delta); - } - Char * expand_noinit(size_t delta) { - auto const sz = size(); - backend_.resize(size() + delta); - return backend_.data() + sz; - } - void push_back(Char c) { - backend_.push_back(c); - } - size_t size() const { - return backend_.size(); - } - size_t capacity() const { - return backend_.capacity(); - } - bool isShared() const { - return false; - } - void reserve(size_t minCapacity) { - backend_.reserve(minCapacity); - } - -private: - std::basic_string backend_; -}; -#endif // !_LIBSTDCXX_FBSTRING - -/** - * This is the basic_string replacement. For conformity, - * basic_fbstring takes the same template parameters, plus the last - * one which is the core. - */ -#ifdef _LIBSTDCXX_FBSTRING -template -#else -template , - class A = std::allocator, - class Storage = fbstring_core > -#endif -class basic_fbstring { - - static void enforce( - bool condition, - void (*throw_exc)(const char*), - const char* msg) { - if (!condition) throw_exc(msg); - } - - bool isSane() const { - return - begin() <= end() && - empty() == (size() == 0) && - empty() == (begin() == end()) && - size() <= max_size() && - capacity() <= max_size() && - size() <= capacity() && - (begin()[size()] == Storage::TERMINATOR || begin()[size()] == '\0'); - } - - struct Invariant; - friend struct Invariant; - struct Invariant { -#ifndef NDEBUG - explicit Invariant(const basic_fbstring& s) : s_(s) { - assert(s_.isSane()); - } - ~Invariant() { - assert(s_.isSane()); - } - private: - const basic_fbstring& s_; -#else - explicit Invariant(const basic_fbstring&) {} -#endif - Invariant& operator=(const Invariant&); - }; - -public: - // types - typedef T traits_type; - typedef typename traits_type::char_type value_type; - typedef A allocator_type; - typedef typename A::size_type size_type; - typedef typename A::difference_type difference_type; - - typedef typename A::reference reference; - typedef typename A::const_reference const_reference; - typedef typename A::pointer pointer; - typedef typename A::const_pointer const_pointer; - - typedef E* iterator; - typedef const E* const_iterator; - typedef std::reverse_iterator reverse_iterator; - typedef std::reverse_iterator const_reverse_iterator; - - static const size_type npos; // = size_type(-1) - -private: - static void procrustes(size_type& n, size_type nmax) { - if (n > nmax) n = nmax; - } - -public: - // C++11 21.4.2 construct/copy/destroy - explicit basic_fbstring(const A& a = A()) { - } - - basic_fbstring(const basic_fbstring& str) - : store_(str.store_) { - } - - // Move constructor - basic_fbstring(basic_fbstring&& goner) : store_(std::move(goner.store_)) { - } - -#ifndef _LIBSTDCXX_FBSTRING - // This is defined for compatibility with std::string - /* implicit */ basic_fbstring(const std::string& str) - : store_(str.data(), str.size()) { - } -#endif - - basic_fbstring(const basic_fbstring& str, size_type pos, - size_type n = npos, const A& a = A()) { - assign(str, pos, n); - } - - /* implicit */ basic_fbstring(const value_type* s, const A& a = A()) - : store_(s, s ? traits_type::length(s) : ({ - basic_fbstring err = __PRETTY_FUNCTION__; - err += ": null pointer initializer not valid"; - std::__throw_logic_error(err.c_str()); - 0; - })) { - } - - basic_fbstring(const value_type* s, size_type n, const A& a = A()) - : store_(s, n) { - } - - basic_fbstring(size_type n, value_type c, const A& a = A()) { - auto const data = store_.expand_noinit(n); - fbstring_detail::pod_fill(data, data + n, c); - store_.writeTerminator(); - } - - template - basic_fbstring(InIt begin, InIt end, - typename std::enable_if< - !std::is_same::type, - value_type*>::value, const A>::type & a = A()) { - assign(begin, end); - } - - // Specialization for const char*, const char* - basic_fbstring(const value_type* b, const value_type* e) - : store_(b, e - b) { - } - - // Nonstandard constructor - basic_fbstring(value_type *s, size_type n, size_type c, - AcquireMallocatedString a) - : store_(s, n, c, a) { - } - - // Construction from initialization list - basic_fbstring(std::initializer_list il) { - assign(il.begin(), il.end()); - } - - ~basic_fbstring() { - } - - basic_fbstring& operator=(const basic_fbstring& lhs) { - if (FBSTRING_UNLIKELY(&lhs == this)) { - return *this; - } - auto const oldSize = size(); - auto const srcSize = lhs.size(); - if (capacity() >= srcSize && !store_.isShared()) { - // great, just copy the contents - if (oldSize < srcSize) - store_.expand_noinit(srcSize - oldSize); - else - store_.shrink(oldSize - srcSize); - assert(size() == srcSize); - fbstring_detail::pod_copy(lhs.begin(), lhs.end(), begin()); - store_.writeTerminator(); - } else { - // need to reallocate, so we may as well create a brand new string - basic_fbstring(lhs).swap(*this); - } - return *this; - } - - // Move assignment - basic_fbstring& operator=(basic_fbstring&& goner) { - if (FBSTRING_UNLIKELY(&goner == this)) { - // Compatibility with std::basic_string<>, - // C++11 21.4.2 [string.cons] / 23 requires self-move-assignment support. - return *this; - } - // No need of this anymore - this->~basic_fbstring(); - // Move the goner into this - new(&store_) fbstring_core(std::move(goner.store_)); - return *this; - } - -#ifndef _LIBSTDCXX_FBSTRING - // Compatibility with std::string - basic_fbstring & operator=(const std::string & rhs) { - return assign(rhs.data(), rhs.size()); - } - - // Compatibility with std::string - std::string toStdString() const { - return std::string(data(), size()); - } -#else - // A lot of code in fbcode still uses this method, so keep it here for now. - const basic_fbstring& toStdString() const { - return *this; - } -#endif - - basic_fbstring& operator=(const value_type* s) { - return assign(s); - } - - basic_fbstring& operator=(value_type c) { - if (empty()) { - store_.expand_noinit(1); - } else if (store_.isShared()) { - basic_fbstring(1, c).swap(*this); - return *this; - } else { - store_.shrink(size() - 1); - } - *store_.mutable_data() = c; - store_.writeTerminator(); - return *this; - } - - basic_fbstring& operator=(std::initializer_list il) { - return assign(il.begin(), il.end()); - } - - // C++11 21.4.3 iterators: - iterator begin() { return store_.mutable_data(); } - - const_iterator begin() const { return store_.data(); } - - const_iterator cbegin() const { return begin(); } - - iterator end() { - return store_.mutable_data() + store_.size(); - } - - const_iterator end() const { - return store_.data() + store_.size(); - } - - const_iterator cend() const { return end(); } - - reverse_iterator rbegin() { - return reverse_iterator(end()); - } - - const_reverse_iterator rbegin() const { - return const_reverse_iterator(end()); - } - - const_reverse_iterator crbegin() const { return rbegin(); } - - reverse_iterator rend() { - return reverse_iterator(begin()); - } - - const_reverse_iterator rend() const { - return const_reverse_iterator(begin()); - } - - const_reverse_iterator crend() const { return rend(); } - - // Added by C++11 - // C++11 21.4.5, element access: - const value_type& front() const { return *begin(); } - const value_type& back() const { - assert(!empty()); - // Should be begin()[size() - 1], but that branches twice - return *(end() - 1); - } - value_type& front() { return *begin(); } - value_type& back() { - assert(!empty()); - // Should be begin()[size() - 1], but that branches twice - return *(end() - 1); - } - void pop_back() { - assert(!empty()); - store_.shrink(1); - } - - // C++11 21.4.4 capacity: - size_type size() const { return store_.size(); } - - size_type length() const { return size(); } - - size_type max_size() const { - return std::numeric_limits::max(); - } - - void resize(const size_type n, const value_type c = value_type()) { - auto size = this->size(); - if (n <= size) { - store_.shrink(size - n); - } else { - // Do this in two steps to minimize slack memory copied (see - // smartRealloc). - auto const capacity = this->capacity(); - assert(capacity >= size); - if (size < capacity) { - auto delta = std::min(n, capacity) - size; - store_.expand_noinit(delta); - fbstring_detail::pod_fill(begin() + size, end(), c); - size += delta; - if (size == n) { - store_.writeTerminator(); - return; - } - assert(size < n); - } - auto const delta = n - size; - store_.expand_noinit(delta); - fbstring_detail::pod_fill(end() - delta, end(), c); - store_.writeTerminator(); - } - assert(this->size() == n); - } - - size_type capacity() const { return store_.capacity(); } - - void reserve(size_type res_arg = 0) { - enforce(res_arg <= max_size(), std::__throw_length_error, ""); - store_.reserve(res_arg); - } - - void shrink_to_fit() { - // Shrink only if slack memory is sufficiently large - if (capacity() < size() * 3 / 2) { - return; - } - basic_fbstring(cbegin(), cend()).swap(*this); - } - - void clear() { resize(0); } - - bool empty() const { return size() == 0; } - - // C++11 21.4.5 element access: - const_reference operator[](size_type pos) const { - return *(c_str() + pos); - } - - reference operator[](size_type pos) { - if (pos == size()) { - // Just call c_str() to make sure '\0' is present - c_str(); - } - return *(begin() + pos); - } - - const_reference at(size_type n) const { - enforce(n <= size(), std::__throw_out_of_range, ""); - return (*this)[n]; - } - - reference at(size_type n) { - enforce(n < size(), std::__throw_out_of_range, ""); - return (*this)[n]; - } - - // C++11 21.4.6 modifiers: - basic_fbstring& operator+=(const basic_fbstring& str) { - return append(str); - } - - basic_fbstring& operator+=(const value_type* s) { - return append(s); - } - - basic_fbstring& operator+=(const value_type c) { - push_back(c); - return *this; - } - - basic_fbstring& operator+=(std::initializer_list il) { - append(il); - return *this; - } - - basic_fbstring& append(const basic_fbstring& str) { -#ifndef NDEBUG - auto desiredSize = size() + str.size(); -#endif - append(str.data(), str.size()); - assert(size() == desiredSize); - return *this; - } - - basic_fbstring& append(const basic_fbstring& str, const size_type pos, - size_type n) { - const size_type sz = str.size(); - enforce(pos <= sz, std::__throw_out_of_range, ""); - procrustes(n, sz - pos); - return append(str.data() + pos, n); - } - - basic_fbstring& append(const value_type* s, size_type n) { -#ifndef NDEBUG - Invariant checker(*this); - (void) checker; -#endif - if (FBSTRING_UNLIKELY(!n)) { - // Unlikely but must be done - return *this; - } - auto const oldSize = size(); - auto const oldData = data(); - // Check for aliasing (rare). We could use "<=" here but in theory - // those do not work for pointers unless the pointers point to - // elements in the same array. For that reason we use - // std::less_equal, which is guaranteed to offer a total order - // over pointers. See discussion at http://goo.gl/Cy2ya for more - // info. - std::less_equal le; - if (FBSTRING_UNLIKELY(le(oldData, s) && !le(oldData + oldSize, s))) { - assert(le(s + n, oldData + oldSize)); - const size_type offset = s - oldData; - store_.reserve(oldSize + n); - // Restore the source - s = data() + offset; - } - // Warning! Repeated appends with short strings may actually incur - // practically quadratic performance. Avoid that by pushing back - // the first character (which ensures exponential growth) and then - // appending the rest normally. Worst case the append may incur a - // second allocation but that will be rare. - push_back(*s++); - --n; - memcpy(store_.expand_noinit(n), s, n * sizeof(value_type)); - assert(size() == oldSize + n + 1); - return *this; - } - - basic_fbstring& append(const value_type* s) { - return append(s, traits_type::length(s)); - } - - basic_fbstring& append(size_type n, value_type c) { - resize(size() + n, c); - return *this; - } - - template - basic_fbstring& append(InputIterator first, InputIterator last) { - insert(end(), first, last); - return *this; - } - - basic_fbstring& append(std::initializer_list il) { - return append(il.begin(), il.end()); - } - - void push_back(const value_type c) { // primitive - store_.push_back(c); - } - - basic_fbstring& assign(const basic_fbstring& str) { - if (&str == this) return *this; - return assign(str.data(), str.size()); - } - - basic_fbstring& assign(basic_fbstring&& str) { - return *this = std::move(str); - } - - basic_fbstring& assign(const basic_fbstring& str, const size_type pos, - size_type n) { - const size_type sz = str.size(); - enforce(pos <= sz, std::__throw_out_of_range, ""); - procrustes(n, sz - pos); - return assign(str.data() + pos, n); - } - - basic_fbstring& assign(const value_type* s, const size_type n) { - Invariant checker(*this); - (void) checker; - if (size() >= n) { - std::copy(s, s + n, begin()); - resize(n); - assert(size() == n); - } else { - const value_type *const s2 = s + size(); - std::copy(s, s2, begin()); - append(s2, n - size()); - assert(size() == n); - } - store_.writeTerminator(); - assert(size() == n); - return *this; - } - - basic_fbstring& assign(const value_type* s) { - return assign(s, traits_type::length(s)); - } - - basic_fbstring& assign(std::initializer_list il) { - return assign(il.begin(), il.end()); - } - - template - basic_fbstring& assign(ItOrLength first_or_n, ItOrChar last_or_c) { - return replace(begin(), end(), first_or_n, last_or_c); - } - - basic_fbstring& insert(size_type pos1, const basic_fbstring& str) { - return insert(pos1, str.data(), str.size()); - } - - basic_fbstring& insert(size_type pos1, const basic_fbstring& str, - size_type pos2, size_type n) { - enforce(pos2 <= str.length(), std::__throw_out_of_range, ""); - procrustes(n, str.length() - pos2); - return insert(pos1, str.data() + pos2, n); - } - - basic_fbstring& insert(size_type pos, const value_type* s, size_type n) { - enforce(pos <= length(), std::__throw_out_of_range, ""); - insert(begin() + pos, s, s + n); - return *this; - } - - basic_fbstring& insert(size_type pos, const value_type* s) { - return insert(pos, s, traits_type::length(s)); - } - - basic_fbstring& insert(size_type pos, size_type n, value_type c) { - enforce(pos <= length(), std::__throw_out_of_range, ""); - insert(begin() + pos, n, c); - return *this; - } - - iterator insert(const_iterator p, const value_type c) { - const size_type pos = p - begin(); - insert(p, 1, c); - return begin() + pos; - } - -private: - template class Selector {}; - - iterator insertImplDiscr(const_iterator p, - size_type n, value_type c, Selector<1>) { - Invariant checker(*this); - (void) checker; - auto const pos = p - begin(); - assert(p >= begin() && p <= end()); - if (capacity() - size() < n) { - const size_type sz = p - begin(); - reserve(size() + n); - p = begin() + sz; - } - const iterator oldEnd = end(); - if (n < size_type(oldEnd - p)) { - append(oldEnd - n, oldEnd); - //std::copy( - // reverse_iterator(oldEnd - n), - // reverse_iterator(p), - // reverse_iterator(oldEnd)); - fbstring_detail::pod_move(&*p, &*oldEnd - n, - begin() + pos + n); - std::fill(begin() + pos, begin() + pos + n, c); - } else { - append(n - (end() - p), c); - append(iterator(p), oldEnd); - std::fill(iterator(p), oldEnd, c); - } - store_.writeTerminator(); - return begin() + pos; - } - - template - iterator insertImplDiscr(const_iterator i, - InputIter b, InputIter e, Selector<0>) { - return insertImpl(i, b, e, - typename std::iterator_traits::iterator_category()); - } - - template - iterator insertImpl(const_iterator i, - FwdIterator s1, FwdIterator s2, std::forward_iterator_tag) { - Invariant checker(*this); - (void) checker; - const size_type pos = i - begin(); - const typename std::iterator_traits::difference_type n2 = - std::distance(s1, s2); - assert(n2 >= 0); - using namespace fbstring_detail; - assert(pos <= size()); - - const typename std::iterator_traits::difference_type maxn2 = - capacity() - size(); - if (maxn2 < n2) { - // realloc the string - reserve(size() + n2); - i = begin() + pos; - } - if (pos + n2 <= size()) { - const iterator tailBegin = end() - n2; - store_.expand_noinit(n2); - fbstring_detail::pod_copy(tailBegin, tailBegin + n2, end() - n2); - std::copy(const_reverse_iterator(tailBegin), const_reverse_iterator(i), - reverse_iterator(tailBegin + n2)); - std::copy(s1, s2, begin() + pos); - } else { - FwdIterator t = s1; - const size_type old_size = size(); - std::advance(t, old_size - pos); - const size_t newElems = std::distance(t, s2); - store_.expand_noinit(n2); - std::copy(t, s2, begin() + old_size); - fbstring_detail::pod_copy(data() + pos, data() + old_size, - begin() + old_size + newElems); - std::copy(s1, t, begin() + pos); - } - store_.writeTerminator(); - return begin() + pos; - } - - template - iterator insertImpl(const_iterator i, - InputIterator b, InputIterator e, - std::input_iterator_tag) { - const auto pos = i - begin(); - basic_fbstring temp(begin(), i); - for (; b != e; ++b) { - temp.push_back(*b); - } - temp.append(i, cend()); - swap(temp); - return begin() + pos; - } - -public: - template - iterator insert(const_iterator p, ItOrLength first_or_n, ItOrChar last_or_c) { - Selector::is_specialized> sel; - return insertImplDiscr(p, first_or_n, last_or_c, sel); - } - - iterator insert(const_iterator p, std::initializer_list il) { - return insert(p, il.begin(), il.end()); - } - - basic_fbstring& erase(size_type pos = 0, size_type n = npos) { - Invariant checker(*this); - (void) checker; - enforce(pos <= length(), std::__throw_out_of_range, ""); - procrustes(n, length() - pos); - std::copy(begin() + pos + n, end(), begin() + pos); - resize(length() - n); - return *this; - } - - iterator erase(iterator position) { - const size_type pos(position - begin()); - enforce(pos <= size(), std::__throw_out_of_range, ""); - erase(pos, 1); - return begin() + pos; - } - - iterator erase(iterator first, iterator last) { - const size_type pos(first - begin()); - erase(pos, last - first); - return begin() + pos; - } - - // Replaces at most n1 chars of *this, starting with pos1 with the - // content of str - basic_fbstring& replace(size_type pos1, size_type n1, - const basic_fbstring& str) { - return replace(pos1, n1, str.data(), str.size()); - } - - // Replaces at most n1 chars of *this, starting with pos1, - // with at most n2 chars of str starting with pos2 - basic_fbstring& replace(size_type pos1, size_type n1, - const basic_fbstring& str, - size_type pos2, size_type n2) { - enforce(pos2 <= str.length(), std::__throw_out_of_range, ""); - return replace(pos1, n1, str.data() + pos2, - std::min(n2, str.size() - pos2)); - } - - // Replaces at most n1 chars of *this, starting with pos, with chars from s - basic_fbstring& replace(size_type pos, size_type n1, const value_type* s) { - return replace(pos, n1, s, traits_type::length(s)); - } - - // Replaces at most n1 chars of *this, starting with pos, with n2 - // occurrences of c - // - // consolidated with - // - // Replaces at most n1 chars of *this, starting with pos, with at - // most n2 chars of str. str must have at least n2 chars. - template - basic_fbstring& replace(size_type pos, size_type n1, - StrOrLength s_or_n2, NumOrChar n_or_c) { - Invariant checker(*this); - (void) checker; - enforce(pos <= size(), std::__throw_out_of_range, ""); - procrustes(n1, length() - pos); - const iterator b = begin() + pos; - return replace(b, b + n1, s_or_n2, n_or_c); - } - - basic_fbstring& replace(iterator i1, iterator i2, const basic_fbstring& str) { - return replace(i1, i2, str.data(), str.length()); - } - - basic_fbstring& replace(iterator i1, iterator i2, const value_type* s) { - return replace(i1, i2, s, traits_type::length(s)); - } - -private: - basic_fbstring& replaceImplDiscr(iterator i1, iterator i2, - const value_type* s, size_type n, - Selector<2>) { - assert(i1 <= i2); - assert(begin() <= i1 && i1 <= end()); - assert(begin() <= i2 && i2 <= end()); - return replace(i1, i2, s, s + n); - } - - basic_fbstring& replaceImplDiscr(iterator i1, iterator i2, - size_type n2, value_type c, Selector<1>) { - const size_type n1 = i2 - i1; - if (n1 > n2) { - std::fill(i1, i1 + n2, c); - erase(i1 + n2, i2); - } else { - std::fill(i1, i2, c); - insert(i2, n2 - n1, c); - } - assert(isSane()); - return *this; - } - - template - basic_fbstring& replaceImplDiscr(iterator i1, iterator i2, - InputIter b, InputIter e, - Selector<0>) { - replaceImpl(i1, i2, b, e, - typename std::iterator_traits::iterator_category()); - return *this; - } - -private: - template - bool replaceAliased(iterator i1, iterator i2, - FwdIterator s1, FwdIterator s2, P*) { - return false; - } - - template - bool replaceAliased(iterator i1, iterator i2, - FwdIterator s1, FwdIterator s2, value_type*) { - static const std::less_equal le = - std::less_equal(); - const bool aliased = le(&*begin(), &*s1) && le(&*s1, &*end()); - if (!aliased) { - return false; - } - // Aliased replace, copy to new string - basic_fbstring temp; - temp.reserve(size() - (i2 - i1) + std::distance(s1, s2)); - temp.append(begin(), i1).append(s1, s2).append(i2, end()); - swap(temp); - return true; - } - -public: - template - void replaceImpl(iterator i1, iterator i2, - FwdIterator s1, FwdIterator s2, std::forward_iterator_tag) { - Invariant checker(*this); - (void) checker; - - // Handle aliased replace - if (replaceAliased(i1, i2, s1, s2, &*s1)) { - return; - } - - auto const n1 = i2 - i1; - assert(n1 >= 0); - auto const n2 = std::distance(s1, s2); - assert(n2 >= 0); - - if (n1 > n2) { - // shrinks - std::copy(s1, s2, i1); - erase(i1 + n2, i2); - } else { - // grows - fbstring_detail::copy_n(s1, n1, i1); - std::advance(s1, n1); - insert(i2, s1, s2); - } - assert(isSane()); - } - - template - void replaceImpl(iterator i1, iterator i2, - InputIterator b, InputIterator e, std::input_iterator_tag) { - basic_fbstring temp(begin(), i1); - temp.append(b, e).append(i2, end()); - swap(temp); - } - -public: - template - basic_fbstring& replace(iterator i1, iterator i2, - T1 first_or_n_or_s, T2 last_or_c_or_n) { - const bool - num1 = std::numeric_limits::is_specialized, - num2 = std::numeric_limits::is_specialized; - return replaceImplDiscr( - i1, i2, first_or_n_or_s, last_or_c_or_n, - Selector()); - } - - size_type copy(value_type* s, size_type n, size_type pos = 0) const { - enforce(pos <= size(), std::__throw_out_of_range, ""); - procrustes(n, size() - pos); - - fbstring_detail::pod_copy( - data() + pos, - data() + pos + n, - s); - return n; - } - - void swap(basic_fbstring& rhs) { - store_.swap(rhs.store_); - } - - const value_type* c_str() const { - return store_.c_str(); - } - - const value_type* data() const { return c_str(); } - - allocator_type get_allocator() const { - return allocator_type(); - } - - size_type find(const basic_fbstring& str, size_type pos = 0) const { - return find(str.data(), pos, str.length()); - } - - size_type find(const value_type* needle, const size_type pos, - const size_type nsize) const { - if (!nsize) return pos; - auto const size = this->size(); - if (nsize + pos > size) return npos; - // Don't use std::search, use a Boyer-Moore-like trick by comparing - // the last characters first - auto const haystack = data(); - auto const nsize_1 = nsize - 1; - auto const lastNeedle = needle[nsize_1]; - - // Boyer-Moore skip value for the last char in the needle. Zero is - // not a valid value; skip will be computed the first time it's - // needed. - size_type skip = 0; - - const E * i = haystack + pos; - auto iEnd = haystack + size - nsize_1; - - while (i < iEnd) { - // Boyer-Moore: match the last element in the needle - while (i[nsize_1] != lastNeedle) { - if (++i == iEnd) { - // not found - return npos; - } - } - // Here we know that the last char matches - // Continue in pedestrian mode - for (size_t j = 0; ; ) { - assert(j < nsize); - if (i[j] != needle[j]) { - // Not found, we can skip - // Compute the skip value lazily - if (skip == 0) { - skip = 1; - while (skip <= nsize_1 && needle[nsize_1 - skip] != lastNeedle) { - ++skip; - } - } - i += skip; - break; - } - // Check if done searching - if (++j == nsize) { - // Yay - return i - haystack; - } - } - } - return npos; - } - - size_type find(const value_type* s, size_type pos = 0) const { - return find(s, pos, traits_type::length(s)); - } - - size_type find (value_type c, size_type pos = 0) const { - return find(&c, pos, 1); - } - - size_type rfind(const basic_fbstring& str, size_type pos = npos) const { - return rfind(str.data(), pos, str.length()); - } - - size_type rfind(const value_type* s, size_type pos, size_type n) const { - if (n > length()) return npos; - pos = std::min(pos, length() - n); - if (n == 0) return pos; - - const_iterator i(begin() + pos); - for (; ; --i) { - if (traits_type::eq(*i, *s) - && traits_type::compare(&*i, s, n) == 0) { - return i - begin(); - } - if (i == begin()) break; - } - return npos; - } - - size_type rfind(const value_type* s, size_type pos = npos) const { - return rfind(s, pos, traits_type::length(s)); - } - - size_type rfind(value_type c, size_type pos = npos) const { - return rfind(&c, pos, 1); - } - - size_type find_first_of(const basic_fbstring& str, size_type pos = 0) const { - return find_first_of(str.data(), pos, str.length()); - } - - size_type find_first_of(const value_type* s, - size_type pos, size_type n) const { - if (pos > length() || n == 0) return npos; - const_iterator i(begin() + pos), - finish(end()); - for (; i != finish; ++i) { - if (traits_type::find(s, n, *i) != 0) { - return i - begin(); - } - } - return npos; - } - - size_type find_first_of(const value_type* s, size_type pos = 0) const { - return find_first_of(s, pos, traits_type::length(s)); - } - - size_type find_first_of(value_type c, size_type pos = 0) const { - return find_first_of(&c, pos, 1); - } - - size_type find_last_of (const basic_fbstring& str, - size_type pos = npos) const { - return find_last_of(str.data(), pos, str.length()); - } - - size_type find_last_of (const value_type* s, size_type pos, - size_type n) const { - if (!empty() && n > 0) { - pos = std::min(pos, length() - 1); - const_iterator i(begin() + pos); - for (;; --i) { - if (traits_type::find(s, n, *i) != 0) { - return i - begin(); - } - if (i == begin()) break; - } - } - return npos; - } - - size_type find_last_of (const value_type* s, - size_type pos = npos) const { - return find_last_of(s, pos, traits_type::length(s)); - } - - size_type find_last_of (value_type c, size_type pos = npos) const { - return find_last_of(&c, pos, 1); - } - - size_type find_first_not_of(const basic_fbstring& str, - size_type pos = 0) const { - return find_first_not_of(str.data(), pos, str.size()); - } - - size_type find_first_not_of(const value_type* s, size_type pos, - size_type n) const { - if (pos < length()) { - const_iterator - i(begin() + pos), - finish(end()); - for (; i != finish; ++i) { - if (traits_type::find(s, n, *i) == 0) { - return i - begin(); - } - } - } - return npos; - } - - size_type find_first_not_of(const value_type* s, - size_type pos = 0) const { - return find_first_not_of(s, pos, traits_type::length(s)); - } - - size_type find_first_not_of(value_type c, size_type pos = 0) const { - return find_first_not_of(&c, pos, 1); - } - - size_type find_last_not_of(const basic_fbstring& str, - size_type pos = npos) const { - return find_last_not_of(str.data(), pos, str.length()); - } - - size_type find_last_not_of(const value_type* s, size_type pos, - size_type n) const { - if (!this->empty()) { - pos = std::min(pos, size() - 1); - const_iterator i(begin() + pos); - for (;; --i) { - if (traits_type::find(s, n, *i) == 0) { - return i - begin(); - } - if (i == begin()) break; - } - } - return npos; - } - - size_type find_last_not_of(const value_type* s, - size_type pos = npos) const { - return find_last_not_of(s, pos, traits_type::length(s)); - } - - size_type find_last_not_of (value_type c, size_type pos = npos) const { - return find_last_not_of(&c, pos, 1); - } - - basic_fbstring substr(size_type pos = 0, size_type n = npos) const { - enforce(pos <= size(), std::__throw_out_of_range, ""); - return basic_fbstring(data() + pos, std::min(n, size() - pos)); - } - - int compare(const basic_fbstring& str) const { - // FIX due to Goncalo N M de Carvalho July 18, 2005 - return compare(0, size(), str); - } - - int compare(size_type pos1, size_type n1, - const basic_fbstring& str) const { - return compare(pos1, n1, str.data(), str.size()); - } - - int compare(size_type pos1, size_type n1, - const value_type* s) const { - return compare(pos1, n1, s, traits_type::length(s)); - } - - int compare(size_type pos1, size_type n1, - const value_type* s, size_type n2) const { - enforce(pos1 <= size(), std::__throw_out_of_range, ""); - procrustes(n1, size() - pos1); - // The line below fixed by Jean-Francois Bastien, 04-23-2007. Thanks! - const int r = traits_type::compare(pos1 + data(), s, std::min(n1, n2)); - return r != 0 ? r : n1 > n2 ? 1 : n1 < n2 ? -1 : 0; - } - - int compare(size_type pos1, size_type n1, - const basic_fbstring& str, - size_type pos2, size_type n2) const { - enforce(pos2 <= str.size(), std::__throw_out_of_range, ""); - return compare(pos1, n1, str.data() + pos2, - std::min(n2, str.size() - pos2)); - } - - // Code from Jean-Francois Bastien (03/26/2007) - int compare(const value_type* s) const { - // Could forward to compare(0, size(), s, traits_type::length(s)) - // but that does two extra checks - const size_type n1(size()), n2(traits_type::length(s)); - const int r = traits_type::compare(data(), s, std::min(n1, n2)); - return r != 0 ? r : n1 > n2 ? 1 : n1 < n2 ? -1 : 0; - } - -private: - // Data - Storage store_; -}; - -// non-member functions -// C++11 21.4.8.1/2 -template -inline -basic_fbstring operator+(const basic_fbstring& lhs, - const basic_fbstring& rhs) { - - basic_fbstring result; - result.reserve(lhs.size() + rhs.size()); - result.append(lhs).append(rhs); - return std::move(result); -} - -// C++11 21.4.8.1/2 -template -inline -basic_fbstring operator+(basic_fbstring&& lhs, - const basic_fbstring& rhs) { - return std::move(lhs.append(rhs)); -} - -// C++11 21.4.8.1/3 -template -inline -basic_fbstring operator+(const basic_fbstring& lhs, - basic_fbstring&& rhs) { - if (rhs.capacity() >= lhs.size() + rhs.size()) { - // Good, at least we don't need to reallocate - return std::move(rhs.insert(0, lhs)); - } - // Meh, no go. Forward to operator+(const&, const&). - auto const& rhsC = rhs; - return lhs + rhsC; -} - -// C++11 21.4.8.1/4 -template -inline -basic_fbstring operator+(basic_fbstring&& lhs, - basic_fbstring&& rhs) { - return std::move(lhs.append(rhs)); -} - -template -inline -basic_fbstring operator+( - const typename basic_fbstring::value_type* lhs, - const basic_fbstring& rhs) { - // - basic_fbstring result; - const typename basic_fbstring::size_type len = - basic_fbstring::traits_type::length(lhs); - result.reserve(len + rhs.size()); - result.append(lhs, len).append(rhs); - return result; -} - -template -inline -basic_fbstring operator+( - typename basic_fbstring::value_type lhs, - const basic_fbstring& rhs) { - - basic_fbstring result; - result.reserve(1 + rhs.size()); - result.push_back(lhs); - result.append(rhs); - return result; -} - -template -inline -basic_fbstring operator+( - const basic_fbstring& lhs, - const typename basic_fbstring::value_type* rhs) { - - typedef typename basic_fbstring::size_type size_type; - typedef typename basic_fbstring::traits_type traits_type; - - basic_fbstring result; - const size_type len = traits_type::length(rhs); - result.reserve(lhs.size() + len); - result.append(lhs).append(rhs, len); - return result; -} - -template -inline -basic_fbstring operator+( - const basic_fbstring& lhs, - typename basic_fbstring::value_type rhs) { - - basic_fbstring result; - result.reserve(lhs.size() + 1); - result.append(lhs); - result.push_back(rhs); - return result; -} - -template -inline -bool operator==(const basic_fbstring& lhs, - const basic_fbstring& rhs) { - return lhs.size() == rhs.size() && lhs.compare(rhs) == 0; } - -template -inline -bool operator==(const typename basic_fbstring::value_type* lhs, - const basic_fbstring& rhs) { - return rhs == lhs; } - -template -inline -bool operator==(const basic_fbstring& lhs, - const typename basic_fbstring::value_type* rhs) { - return lhs.compare(rhs) == 0; } - -template -inline -bool operator!=(const basic_fbstring& lhs, - const basic_fbstring& rhs) { - return !(lhs == rhs); } - -template -inline -bool operator!=(const typename basic_fbstring::value_type* lhs, - const basic_fbstring& rhs) { - return !(lhs == rhs); } - -template -inline -bool operator!=(const basic_fbstring& lhs, - const typename basic_fbstring::value_type* rhs) { - return !(lhs == rhs); } - -template -inline -bool operator<(const basic_fbstring& lhs, - const basic_fbstring& rhs) { - return lhs.compare(rhs) < 0; } - -template -inline -bool operator<(const basic_fbstring& lhs, - const typename basic_fbstring::value_type* rhs) { - return lhs.compare(rhs) < 0; } - -template -inline -bool operator<(const typename basic_fbstring::value_type* lhs, - const basic_fbstring& rhs) { - return rhs.compare(lhs) > 0; } - -template -inline -bool operator>(const basic_fbstring& lhs, - const basic_fbstring& rhs) { - return rhs < lhs; } - -template -inline -bool operator>(const basic_fbstring& lhs, - const typename basic_fbstring::value_type* rhs) { - return rhs < lhs; } - -template -inline -bool operator>(const typename basic_fbstring::value_type* lhs, - const basic_fbstring& rhs) { - return rhs < lhs; } - -template -inline -bool operator<=(const basic_fbstring& lhs, - const basic_fbstring& rhs) { - return !(rhs < lhs); } - -template -inline -bool operator<=(const basic_fbstring& lhs, - const typename basic_fbstring::value_type* rhs) { - return !(rhs < lhs); } - -template -inline -bool operator<=(const typename basic_fbstring::value_type* lhs, - const basic_fbstring& rhs) { - return !(rhs < lhs); } - -template -inline -bool operator>=(const basic_fbstring& lhs, - const basic_fbstring& rhs) { - return !(lhs < rhs); } - -template -inline -bool operator>=(const basic_fbstring& lhs, - const typename basic_fbstring::value_type* rhs) { - return !(lhs < rhs); } - -template -inline -bool operator>=(const typename basic_fbstring::value_type* lhs, - const basic_fbstring& rhs) { - return !(lhs < rhs); -} - -// C++11 21.4.8.8 -template -void swap(basic_fbstring& lhs, basic_fbstring& rhs) { - lhs.swap(rhs); -} - -// TODO: make this faster. -template -inline -std::basic_istream< - typename basic_fbstring::value_type, - typename basic_fbstring::traits_type>& - operator>>( - std::basic_istream::value_type, - typename basic_fbstring::traits_type>& is, - basic_fbstring& str) { - typename std::basic_istream::sentry sentry(is); - typedef std::basic_istream::value_type, - typename basic_fbstring::traits_type> - __istream_type; - typedef typename __istream_type::ios_base __ios_base; - size_t extracted = 0; - auto err = __ios_base::goodbit; - if (sentry) { - auto n = is.width(); - if (n == 0) { - n = str.max_size(); - } - str.erase(); - auto got = is.rdbuf()->sgetc(); - for (; extracted != n && got != T::eof() && !isspace(got); ++extracted) { - // Whew. We get to store this guy - str.push_back(got); - got = is.rdbuf()->snextc(); - } - if (got == T::eof()) { - err |= __ios_base::eofbit; - is.width(0); - } - } - if (!extracted) { - err |= __ios_base::failbit; - } - if (err) { - is.setstate(err); - } - return is; -} - -template -inline -std::basic_ostream::value_type, - typename basic_fbstring::traits_type>& -operator<<( - std::basic_ostream::value_type, - typename basic_fbstring::traits_type>& os, - const basic_fbstring& str) { - os.write(str.data(), str.size()); - return os; -} - -#ifndef _LIBSTDCXX_FBSTRING - -template -inline -std::basic_istream::value_type, - typename basic_fbstring::traits_type>& -getline( - std::basic_istream::value_type, - typename basic_fbstring::traits_type>& is, - basic_fbstring& str, - typename basic_fbstring::value_type delim) { - // Use the nonstandard getdelim() - char * buf = NULL; - size_t size = 0; - for (;;) { - // This looks quadratic but it really depends on realloc - auto const newSize = size + 128; - buf = static_cast(checkedRealloc(buf, newSize)); - is.getline(buf + size, newSize - size, delim); - if (is.bad() || is.eof() || !is.fail()) { - // done by either failure, end of file, or normal read - size += std::strlen(buf + size); - break; - } - // Here we have failed due to too short a buffer - // Minus one to discount the terminating '\0' - size = newSize - 1; - assert(buf[size] == 0); - // Clear the error so we can continue reading - is.clear(); - } - basic_fbstring result(buf, size, size + 1, - AcquireMallocatedString()); - result.swap(str); - return is; -} - -template -inline -std::basic_istream::value_type, - typename basic_fbstring::traits_type>& -getline( - std::basic_istream::value_type, - typename basic_fbstring::traits_type>& is, - basic_fbstring& str) { - // Just forward to the version with a delimiter - return getline(is, str, '\n'); -} - -#endif - -template -const typename basic_fbstring::size_type -basic_fbstring::npos = - static_cast::size_type>(-1); - -#ifndef _LIBSTDCXX_FBSTRING -// basic_string compatibility routines - -template -inline -bool operator==(const basic_fbstring& lhs, - const std::string& rhs) { - return lhs.compare(0, lhs.size(), rhs.data(), rhs.size()) == 0; -} - -template -inline -bool operator==(const std::string& lhs, - const basic_fbstring& rhs) { - return rhs == lhs; -} - -template -inline -bool operator!=(const basic_fbstring& lhs, - const std::string& rhs) { - return !(lhs == rhs); -} - -template -inline -bool operator!=(const std::string& lhs, - const basic_fbstring& rhs) { - return !(lhs == rhs); -} - -#if !defined(_LIBSTDCXX_FBSTRING) -typedef basic_fbstring fbstring; -#endif - -// fbstring is relocatable -template -FOLLY_ASSUME_RELOCATABLE(basic_fbstring); - -#else -_GLIBCXX_END_NAMESPACE_VERSION -#endif - -} // namespace folly - -#pragma GCC diagnostic pop - -#ifndef _LIBSTDCXX_FBSTRING - -namespace std { -template <> -struct hash< ::folly::fbstring> { - size_t operator()(const ::folly::fbstring& s) const { - return ::folly::hash::fnv32_buf(s.data(), s.size()); - } -}; -} - -#endif // _LIBSTDCXX_FBSTRING - -#undef FBSTRING_LIKELY -#undef FBSTRING_UNLIKELY - -#endif // FOLLY_BASE_FBSTRING_H_ diff --git a/hphp/third_party/folly/folly/FBVector.h b/hphp/third_party/folly/folly/FBVector.h deleted file mode 100644 index 7a0cc0f54..000000000 --- a/hphp/third_party/folly/folly/FBVector.h +++ /dev/null @@ -1,1763 +0,0 @@ -/* - * Copyright 2013 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. - */ - -/* - * Nicholas Ormrod (njormrod) - * Andrei Alexandrescu (aalexandre) - * - * FBVector is Facebook's drop-in implementation of std::vector. It has special - * optimizations for use with relocatable types and jemalloc. - */ - -#ifndef FOLLY_FBVECTOR_H -#define FOLLY_FBVECTOR_H - -//============================================================================= -// headers - -#include -#include -#include -#include -#include -#include -#include - -#include "folly/Likely.h" -#include "folly/Malloc.h" -#include "folly/Traits.h" - -#include - -// some files expected these from FBVector -#include -#include "folly/Foreach.h" -#include -#include - -//============================================================================= -// forward declaration - -#ifdef FOLLY_BENCHMARK_USE_NS_IFOLLY -namespace Ifolly { -#else -namespace folly { -#endif - template > - class fbvector; -} - -//============================================================================= -// compatibility - -#if __GNUC__ < 4 || __GNUC__ == 4 && __GNUC_MINOR__ < 7 -// PLEASE UPGRADE TO GCC 4.7 or above -#define FOLLY_FBV_COMPATIBILITY_MODE -#endif - -#ifndef FOLLY_FBV_COMPATIBILITY_MODE - -namespace folly { - -template -struct fbv_allocator_traits - : std::allocator_traits {}; - -template -struct fbv_is_nothrow_move_constructible - : std::is_nothrow_move_constructible {}; - -template -struct fbv_is_nothrow_constructible - : std::is_nothrow_constructible {}; - -template -struct fbv_is_copy_constructible - : std::is_copy_constructible {}; - -} - -#else - -namespace folly { - -template -struct fbv_allocator_traits { - static_assert(sizeof(A) == 0, - "If you want to use a custom allocator, then you must upgrade to gcc 4.7"); - // for some old code that deals with this case, see D566719, diff number 10. -}; - -template -struct fbv_allocator_traits> { - typedef std::allocator A; - - typedef T* pointer; - typedef const T* const_pointer; - typedef size_t size_type; - - typedef std::false_type propagate_on_container_copy_assignment; - typedef std::false_type propagate_on_container_move_assignment; - typedef std::false_type propagate_on_container_swap; - - static pointer allocate(A& a, size_type n) { - return static_cast(::operator new(n * sizeof(T))); - } - static void deallocate(A& a, pointer p, size_type n) { - ::operator delete(p); - } - - template - static void construct(A& a, R* p, Args&&... args) { - new (p) R(std::forward(args)...); - } - template - static void destroy(A& a, R* p) { - p->~R(); - } - - static A select_on_container_copy_construction(const A& a) { - return a; - } -}; - -template -struct fbv_is_nothrow_move_constructible - : std::false_type {}; - -template -struct fbv_is_nothrow_constructible - : std::false_type {}; - -template -struct fbv_is_copy_constructible - : std::true_type {}; - -} - -#endif - -//============================================================================= -// unrolling - -#define FOLLY_FBV_UNROLL_PTR(first, last, OP) do { \ - for (; (last) - (first) >= 4; (first) += 4) { \ - OP(((first) + 0)); \ - OP(((first) + 1)); \ - OP(((first) + 2)); \ - OP(((first) + 3)); \ - } \ - for (; (first) != (last); ++(first)) OP((first)); \ -} while(0); - -//============================================================================= -/////////////////////////////////////////////////////////////////////////////// -// // -// fbvector class // -// // -/////////////////////////////////////////////////////////////////////////////// - -#ifdef FOLLY_BENCHMARK_USE_NS_IFOLLY -namespace Ifolly { -#else -namespace folly { -#endif - -template -class fbvector : private boost::totally_ordered> { - - //=========================================================================== - //--------------------------------------------------------------------------- - // implementation -private: - - typedef folly::fbv_allocator_traits A; - - struct Impl : public Allocator { - // typedefs - typedef typename A::pointer pointer; - typedef typename A::size_type size_type; - - // data - pointer b_, e_, z_; - - // constructors - Impl() : Allocator(), b_(nullptr), e_(nullptr), z_(nullptr) {} - Impl(const Allocator& a) - : Allocator(a), b_(nullptr), e_(nullptr), z_(nullptr) {} - Impl(Allocator&& a) - : Allocator(std::move(a)), b_(nullptr), e_(nullptr), z_(nullptr) {} - - Impl(size_type n, const Allocator& a = Allocator()) - : Allocator(a) - { init(n); } - - Impl(Impl&& other) - : Allocator(std::move(other)), - b_(other.b_), e_(other.e_), z_(other.z_) - { other.b_ = other.e_ = other.z_ = nullptr; } - - // destructor - ~Impl() { - destroy(); - } - - // allocation - // note that 'allocate' and 'deallocate' are inherited from Allocator - T* D_allocate(size_type n) { - if (usingStdAllocator::value) { - return static_cast(malloc(n * sizeof(T))); - } else { - return folly::fbv_allocator_traits::allocate(*this, n); - } - } - - void D_deallocate(T* p, size_type n) noexcept { - if (usingStdAllocator::value) { - free(p); - } else { - folly::fbv_allocator_traits::deallocate(*this, p, n); - } - } - - // helpers - void swapData(Impl& other) { - std::swap(b_, other.b_); - std::swap(e_, other.e_); - std::swap(z_, other.z_); - } - - // data ops - inline void destroy() noexcept { - if (b_) { - // THIS DISPATCH CODE IS DUPLICATED IN fbvector::D_destroy_range_a. - // It has been inlined here for speed. It calls the static fbvector - // methods to perform the actual destruction. - if (usingStdAllocator::value) { - S_destroy_range(b_, e_); - } else { - S_destroy_range_a(*this, b_, e_); - } - - D_deallocate(b_, z_ - b_); - } - } - - void init(size_type n) { - if (UNLIKELY(n == 0)) { - b_ = e_ = z_ = nullptr; - } else { - size_type sz = folly::goodMallocSize(n * sizeof(T)) / sizeof(T); - b_ = D_allocate(sz); - e_ = b_; - z_ = b_ + sz; - } - } - - void - set(pointer newB, size_type newSize, size_type newCap) { - z_ = newB + newCap; - e_ = newB + newSize; - b_ = newB; - } - - void reset(size_type newCap) { - destroy(); - try { - init(newCap); - } catch (...) { - init(0); - throw; - } - } - void reset() { // same as reset(0) - destroy(); - b_ = e_ = z_ = nullptr; - } - } impl_; - - static void swap(Impl& a, Impl& b) { - using std::swap; - if (!usingStdAllocator::value) swap(a, b); - a.swapData(b); - } - - //=========================================================================== - //--------------------------------------------------------------------------- - // types and constants -public: - - typedef T value_type; - typedef value_type& reference; - typedef const value_type& const_reference; - typedef T* iterator; - typedef const T* const_iterator; - typedef size_t size_type; - typedef typename std::make_signed::type difference_type; - typedef Allocator allocator_type; - typedef typename A::pointer pointer; - typedef typename A::const_pointer const_pointer; - typedef std::reverse_iterator reverse_iterator; - typedef std::reverse_iterator const_reverse_iterator; - -private: - - typedef std::integral_constant::value && - sizeof(T) <= 16 // don't force large structures to be passed by value - > should_pass_by_value; - typedef typename std::conditional< - should_pass_by_value::value, T, const T&>::type VT; - typedef typename std::conditional< - should_pass_by_value::value, T, T&&>::type MT; - - typedef std::integral_constant>::value> usingStdAllocator; - typedef std::integral_constant moveIsSwap; - - //=========================================================================== - //--------------------------------------------------------------------------- - // allocator helpers -private: - - //--------------------------------------------------------------------------- - // allocate - - T* M_allocate(size_type n) { - return impl_.D_allocate(n); - } - - //--------------------------------------------------------------------------- - // deallocate - - void M_deallocate(T* p, size_type n) noexcept { - impl_.D_deallocate(p, n); - } - - //--------------------------------------------------------------------------- - // construct - - // GCC is very sensitive to the exact way that construct is called. For - // that reason there are several different specializations of construct. - - template - void M_construct(U* p, Args&&... args) { - if (usingStdAllocator::value) { - new (p) U(std::forward(args)...); - } else { - folly::fbv_allocator_traits::construct( - impl_, p, std::forward(args)...); - } - } - - template - static void S_construct(U* p, Args&&... args) { - new (p) U(std::forward(args)...); - } - - template - static void S_construct_a(Allocator& a, U* p, Args&&... args) { - folly::fbv_allocator_traits::construct( - a, p, std::forward(args)...); - } - - // scalar optimization - // TODO we can expand this optimization to: default copyable and assignable - template ::value>::type> - void M_construct(U* p, U arg) { - if (usingStdAllocator::value) { - *p = arg; - } else { - folly::fbv_allocator_traits::construct(impl_, p, arg); - } - } - - template ::value>::type> - static void S_construct(U* p, U arg) { - *p = arg; - } - - template ::value>::type> - static void S_construct_a(Allocator& a, U* p, U arg) { - folly::fbv_allocator_traits::construct(a, p, arg); - } - - // const& optimization - template ::value>::type> - void M_construct(U* p, const U& value) { - if (usingStdAllocator::value) { - new (p) U(value); - } else { - folly::fbv_allocator_traits::construct(impl_, p, value); - } - } - - template ::value>::type> - static void S_construct(U* p, const U& value) { - new (p) U(value); - } - - template ::value>::type> - static void S_construct_a(Allocator& a, U* p, const U& value) { - folly::fbv_allocator_traits::construct(a, p, value); - } - - //--------------------------------------------------------------------------- - // destroy - - void M_destroy(T* p) noexcept { - if (usingStdAllocator::value) { - if (!boost::has_trivial_destructor::value) p->~T(); - } else { - folly::fbv_allocator_traits::destroy(impl_, p); - } - } - - //=========================================================================== - //--------------------------------------------------------------------------- - // algorithmic helpers -private: - - //--------------------------------------------------------------------------- - // destroy_range - - // wrappers - void M_destroy_range_e(T* pos) noexcept { - D_destroy_range_a(pos, impl_.e_); - impl_.e_ = pos; - } - - // dispatch - // THIS DISPATCH CODE IS DUPLICATED IN IMPL. SEE IMPL FOR DETAILS. - void D_destroy_range_a(T* first, T* last) noexcept { - if (usingStdAllocator::value) { - S_destroy_range(first, last); - } else { - S_destroy_range_a(impl_, first, last); - } - } - - // allocator - static void S_destroy_range_a(Allocator& a, T* first, T* last) noexcept { - for (; first != last; ++first) - folly::fbv_allocator_traits::destroy(a, first); - } - - // optimized - static void S_destroy_range(T* first, T* last) noexcept { - if (!boost::has_trivial_destructor::value) { - // EXPERIMENTAL DATA on fbvector> (where each vector has - // size 0). - // The unrolled version seems to work faster for small to medium sized - // fbvectors. It gets a 10% speedup on fbvectors of size 1024, 64, and - // 16. - // The simple loop version seems to work faster for large fbvectors. The - // unrolled version is about 6% slower on fbvectors on size 16384. - // The two methods seem tied for very large fbvectors. The unrolled - // version is about 0.5% slower on size 262144. - - // for (; first != last; ++first) first->~T(); - #define FOLLY_FBV_OP(p) (p)->~T() - FOLLY_FBV_UNROLL_PTR(first, last, FOLLY_FBV_OP) - #undef FOLLY_FBV_OP - } - } - - //--------------------------------------------------------------------------- - // uninitialized_fill_n - - // wrappers - void M_uninitialized_fill_n_e(size_type sz) { - D_uninitialized_fill_n_a(impl_.e_, sz); - impl_.e_ += sz; - } - - void M_uninitialized_fill_n_e(size_type sz, VT value) { - D_uninitialized_fill_n_a(impl_.e_, sz, value); - impl_.e_ += sz; - } - - // dispatch - void D_uninitialized_fill_n_a(T* dest, size_type sz) { - if (usingStdAllocator::value) { - S_uninitialized_fill_n(dest, sz); - } else { - S_uninitialized_fill_n_a(impl_, dest, sz); - } - } - - void D_uninitialized_fill_n_a(T* dest, size_type sz, VT value) { - if (usingStdAllocator::value) { - S_uninitialized_fill_n(dest, sz, value); - } else { - S_uninitialized_fill_n_a(impl_, dest, sz, value); - } - } - - // allocator - template - static void S_uninitialized_fill_n_a(Allocator& a, T* dest, - size_type sz, Args&&... args) { - auto b = dest; - auto e = dest + sz; - try { - for (; b != e; ++b) - folly::fbv_allocator_traits::construct(a, b, - std::forward(args)...); - } catch (...) { - S_destroy_range_a(a, dest, b); - throw; - } - } - - // optimized - static void S_uninitialized_fill_n(T* dest, size_type n) { - if (folly::IsZeroInitializable::value) { - std::memset(dest, 0, sizeof(T) * n); - } else { - auto b = dest; - auto e = dest + n; - try { - for (; b != e; ++b) S_construct(b); - } catch (...) { - --b; - for (; b >= dest; --b) b->~T(); - throw; - } - } - } - - static void S_uninitialized_fill_n(T* dest, size_type n, const T& value) { - auto b = dest; - auto e = dest + n; - try { - for (; b != e; ++b) S_construct(b, value); - } catch (...) { - S_destroy_range(dest, b); - throw; - } - } - - //--------------------------------------------------------------------------- - // uninitialized_copy - - // it is possible to add an optimization for the case where - // It = move(T*) and IsRelocatable and Is0Initiailizable - - // wrappers - template - void M_uninitialized_copy_e(It first, It last) { - D_uninitialized_copy_a(impl_.e_, first, last); - impl_.e_ += std::distance(first, last); - } - - template - void M_uninitialized_move_e(It first, It last) { - D_uninitialized_move_a(impl_.e_, first, last); - impl_.e_ += std::distance(first, last); - } - - // dispatch - template - void D_uninitialized_copy_a(T* dest, It first, It last) { - if (usingStdAllocator::value) { - if (folly::IsTriviallyCopyable::value) { - S_uninitialized_copy_bits(dest, first, last); - } else { - S_uninitialized_copy(dest, first, last); - } - } else { - S_uninitialized_copy_a(impl_, dest, first, last); - } - } - - template - void D_uninitialized_move_a(T* dest, It first, It last) { - D_uninitialized_copy_a(dest, - std::make_move_iterator(first), std::make_move_iterator(last)); - } - - // allocator - template - static void - S_uninitialized_copy_a(Allocator& a, T* dest, It first, It last) { - auto b = dest; - try { - for (; first != last; ++first, ++b) - folly::fbv_allocator_traits::construct(a, b, *first); - } catch (...) { - S_destroy_range_a(a, dest, b); - throw; - } - } - - // optimized - template - static void S_uninitialized_copy(T* dest, It first, It last) { - auto b = dest; - try { - for (; first != last; ++first, ++b) - S_construct(b, *first); - } catch (...) { - S_destroy_range(dest, b); - throw; - } - } - - static void - S_uninitialized_copy_bits(T* dest, const T* first, const T* last) { - std::memcpy(dest, first, (last - first) * sizeof(T)); - } - - static void - S_uninitialized_copy_bits(T* dest, std::move_iterator first, - std::move_iterator last) { - T* bFirst = first.base(); - T* bLast = last.base(); - std::memcpy(dest, bFirst, (bLast - bFirst) * sizeof(T)); - } - - template - static void - S_uninitialized_copy_bits(T* dest, It first, It last) { - S_uninitialized_copy(dest, first, last); - } - - //--------------------------------------------------------------------------- - // copy_n - - // This function is "unsafe": it assumes that the iterator can be advanced at - // least n times. However, as a private function, that unsafety is managed - // wholly by fbvector itself. - - template - static It S_copy_n(T* dest, It first, size_type n) { - auto e = dest + n; - for (; dest != e; ++dest, ++first) *dest = *first; - return first; - } - - static const T* S_copy_n(T* dest, const T* first, size_type n) { - if (folly::IsTriviallyCopyable::value) { - std::memcpy(dest, first, n * sizeof(T)); - return first + n; - } else { - return S_copy_n(dest, first, n); - } - } - - static std::move_iterator - S_copy_n(T* dest, std::move_iterator mIt, size_type n) { - if (folly::IsTriviallyCopyable::value) { - T* first = mIt.base(); - std::memcpy(dest, first, n * sizeof(T)); - return std::make_move_iterator(first + n); - } else { - return S_copy_n>(dest, mIt, n); - } - } - - //=========================================================================== - //--------------------------------------------------------------------------- - // relocation helpers -private: - - // Relocation is divided into three parts: - // - // 1: relocate_move - // Performs the actual movement of data from point a to point b. - // - // 2: relocate_done - // Destroys the old data. - // - // 3: relocate_undo - // Destoys the new data and restores the old data. - // - // The three steps are used because there may be an exception after part 1 - // has completed. If that is the case, then relocate_undo can nullify the - // initial move. Otherwise, relocate_done performs the last bit of tidying - // up. - // - // The relocation trio may use either memcpy, move, or copy. It is decided - // by the following case statement: - // - // IsRelocatable && usingStdAllocator -> memcpy - // has_nothrow_move && usingStdAllocator -> move - // cannot copy -> move - // default -> copy - // - // If the class is non-copyable then it must be movable. However, if the - // move constructor is not noexcept, i.e. an error could be thrown, then - // relocate_undo will be unable to restore the old data, for fear of a - // second exception being thrown. This is a known and unavoidable - // deficiency. In lieu of a strong exception guarantee, relocate_undo does - // the next best thing: it provides a weak exception guarantee by - // destorying the new data, but leaving the old data in an indeterminate - // state. Note that that indeterminate state will be valid, since the - // old data has not been destroyed; it has merely been the source of a - // move, which is required to leave the source in a valid state. - - // wrappers - void M_relocate(T* newB) { - relocate_move(newB, impl_.b_, impl_.e_); - relocate_done(newB, impl_.b_, impl_.e_); - } - - // dispatch type trait - typedef std::integral_constant::value && usingStdAllocator::value - > relocate_use_memcpy; - - typedef std::integral_constant::value - && usingStdAllocator::value) - || !folly::fbv_is_copy_constructible::value - > relocate_use_move; - - // move - void relocate_move(T* dest, T* first, T* last) { - relocate_move_or_memcpy(dest, first, last, relocate_use_memcpy()); - } - - void relocate_move_or_memcpy(T* dest, T* first, T* last, std::true_type) { - std::memcpy(dest, first, (last - first) * sizeof(T)); - } - - void relocate_move_or_memcpy(T* dest, T* first, T* last, std::false_type) { - relocate_move_or_copy(dest, first, last, relocate_use_move()); - } - - void relocate_move_or_copy(T* dest, T* first, T* last, std::true_type) { - D_uninitialized_move_a(dest, first, last); - } - - void relocate_move_or_copy(T* dest, T* first, T* last, std::false_type) { - D_uninitialized_copy_a(dest, first, last); - } - - // done - void relocate_done(T* dest, T* first, T* last) noexcept { - if (folly::IsRelocatable::value && usingStdAllocator::value) { - // used memcpy; data has been relocated, do not call destructor - } else { - D_destroy_range_a(first, last); - } - } - - // undo - void relocate_undo(T* dest, T* first, T* last) noexcept { - if (folly::IsRelocatable::value && usingStdAllocator::value) { - // used memcpy, old data is still valid, nothing to do - } else if (folly::fbv_is_nothrow_move_constructible::value && - usingStdAllocator::value) { - // noexcept move everything back, aka relocate_move - relocate_move(first, dest, dest + (last - first)); - } else if (!folly::fbv_is_copy_constructible::value) { - // weak guarantee - D_destroy_range_a(dest, dest + (last - first)); - } else { - // used copy, old data is still valid - D_destroy_range_a(dest, dest + (last - first)); - } - } - - - //=========================================================================== - //--------------------------------------------------------------------------- - // construct/copy/destroy -public: - - fbvector() = default; - - explicit fbvector(const Allocator& a) : impl_(a) {} - - explicit fbvector(size_type n, const Allocator& a = Allocator()) - : impl_(n, a) - { M_uninitialized_fill_n_e(n); } - - fbvector(size_type n, VT value, const Allocator& a = Allocator()) - : impl_(n, a) - { M_uninitialized_fill_n_e(n, value); } - - template ::iterator_category> - fbvector(It first, It last, const Allocator& a = Allocator()) - #ifndef FOLLY_FBV_COMPATIBILITY_MODE - : fbvector(first, last, a, Category()) {} - #else - : impl_(std::distance(first, last), a) - { fbvector_init(first, last, Category()); } - #endif - - fbvector(const fbvector& other) - : impl_(other.size(), A::select_on_container_copy_construction(other.impl_)) - { M_uninitialized_copy_e(other.begin(), other.end()); } - - fbvector(fbvector&& other) noexcept : impl_(std::move(other.impl_)) {} - - fbvector(const fbvector& other, const Allocator& a) - #ifndef FOLLY_FBV_COMPATIBILITY_MODE - : fbvector(other.begin(), other.end(), a) {} - #else - : impl_(other.size(), a) - { fbvector_init(other.begin(), other.end(), std::forward_iterator_tag()); } - #endif - - fbvector(fbvector&& other, const Allocator& a) : impl_(a) { - if (impl_ == other.impl_) { - impl_.swapData(other.impl_); - } else { - impl_.init(other.size()); - M_uninitialized_move_e(other.begin(), other.end()); - } - } - - fbvector(std::initializer_list il, const Allocator& a = Allocator()) - #ifndef FOLLY_FBV_COMPATIBILITY_MODE - : fbvector(il.begin(), il.end(), a) {} - #else - : impl_(std::distance(il.begin(), il.end()), a) - { fbvector_init(il.begin(), il.end(), std::forward_iterator_tag()); } - #endif - - ~fbvector() = default; // the cleanup occurs in impl_ - - fbvector& operator=(const fbvector& other) { - if (UNLIKELY(this == &other)) return *this; - - if (!usingStdAllocator::value && - A::propagate_on_container_copy_assignment::value) { - if (impl_ != other.impl_) { - // can't use other's different allocator to clean up self - impl_.reset(); - } - (Allocator&)impl_ = (Allocator&)other.impl_; - } - - assign(other.begin(), other.end()); - return *this; - } - - fbvector& operator=(fbvector&& other) { - if (UNLIKELY(this == &other)) return *this; - moveFrom(std::move(other), moveIsSwap()); - return *this; - } - - fbvector& operator=(std::initializer_list il) { - assign(il.begin(), il.end()); - return *this; - } - - template ::iterator_category> - void assign(It first, It last) { - assign(first, last, Category()); - } - - void assign(size_type n, VT value) { - if (n > capacity()) { - // Not enough space. Do not reserve in place, since we will - // discard the old values anyways. - if (dataIsInternalAndNotVT(value)) { - T copy(std::move(value)); - impl_.reset(n); - M_uninitialized_fill_n_e(n, copy); - } else { - impl_.reset(n); - M_uninitialized_fill_n_e(n, value); - } - } else if (n <= size()) { - auto newE = impl_.b_ + n; - std::fill(impl_.b_, newE, value); - M_destroy_range_e(newE); - } else { - std::fill(impl_.b_, impl_.e_, value); - M_uninitialized_fill_n_e(n - size(), value); - } - } - - void assign(std::initializer_list il) { - assign(il.begin(), il.end()); - } - - allocator_type get_allocator() const noexcept { - return impl_; - } - -private: - - #ifndef FOLLY_FBV_COMPATIBILITY_MODE - // contract dispatch for iterator types fbvector(It first, It last) - template - fbvector(ForwardIterator first, ForwardIterator last, - const Allocator& a, std::forward_iterator_tag) - : impl_(std::distance(first, last), a) - { M_uninitialized_copy_e(first, last); } - - template - fbvector(InputIterator first, InputIterator last, - const Allocator& a, std::input_iterator_tag) - : impl_(a) - { for (; first != last; ++first) emplace_back(*first); } - - #else - // contract dispatch for iterator types without constructor forwarding - template - void - fbvector_init(ForwardIterator first, ForwardIterator last, - std::forward_iterator_tag) - { M_uninitialized_copy_e(first, last); } - - template - void - fbvector_init(InputIterator first, InputIterator last, - std::input_iterator_tag) - { for (; first != last; ++first) emplace_back(*first); } - #endif - - // contract dispatch for allocator movement in operator=(fbvector&&) - void - moveFrom(fbvector&& other, std::true_type) { - swap(impl_, other.impl_); - } - void moveFrom(fbvector&& other, std::false_type) { - if (impl_ == other.impl_) { - impl_.swapData(other.impl_); - } else { - impl_.reset(other.size()); - M_uninitialized_move_e(other.begin(), other.end()); - } - } - - // contract dispatch for iterator types in assign(It first, It last) - template - void assign(ForwardIterator first, ForwardIterator last, - std::forward_iterator_tag) { - auto const newSize = std::distance(first, last); - if (newSize > capacity()) { - impl_.reset(newSize); - M_uninitialized_copy_e(first, last); - } else if (newSize <= size()) { - auto newEnd = std::copy(first, last, impl_.b_); - M_destroy_range_e(newEnd); - } else { - auto mid = S_copy_n(impl_.b_, first, size()); - M_uninitialized_copy_e(mid, last); - } - } - - template - void assign(InputIterator first, InputIterator last, - std::input_iterator_tag) { - auto p = impl_.b_; - for (; first != last && p != impl_.e_; ++first, ++p) { - *p = *first; - } - if (p != impl_.e_) { - M_destroy_range_e(p); - } else { - for (; first != last; ++first) emplace_back(*first); - } - } - - // contract dispatch for aliasing under VT optimization - bool dataIsInternalAndNotVT(const T& t) { - if (should_pass_by_value::value) return false; - return dataIsInternal(t); - } - bool dataIsInternal(const T& t) { - return UNLIKELY(impl_.b_ <= std::addressof(t) && - std::addressof(t) < impl_.e_); - } - - - //=========================================================================== - //--------------------------------------------------------------------------- - // iterators -public: - - iterator begin() noexcept { - return impl_.b_; - } - const_iterator begin() const noexcept { - return impl_.b_; - } - iterator end() noexcept { - return impl_.e_; - } - const_iterator end() const noexcept { - return impl_.e_; - } - reverse_iterator rbegin() noexcept { - return reverse_iterator(end()); - } - const_reverse_iterator rbegin() const noexcept { - return const_reverse_iterator(end()); - } - reverse_iterator rend() noexcept { - return reverse_iterator(begin()); - } - const_reverse_iterator rend() const noexcept { - return const_reverse_iterator(begin()); - } - - const_iterator cbegin() const noexcept { - return impl_.b_; - } - const_iterator cend() const noexcept { - return impl_.e_; - } - const_reverse_iterator crbegin() const noexcept { - return const_reverse_iterator(end()); - } - const_reverse_iterator crend() const noexcept { - return const_reverse_iterator(begin()); - } - - //=========================================================================== - //--------------------------------------------------------------------------- - // capacity -public: - - size_type size() const noexcept { - return impl_.e_ - impl_.b_; - } - - size_type max_size() const noexcept { - // good luck gettin' there - return ~size_type(0); - } - - void resize(size_type n) { - if (n <= size()) { - M_destroy_range_e(impl_.b_ + n); - } else { - reserve(n); - M_uninitialized_fill_n_e(n - size()); - } - } - - void resize(size_type n, VT t) { - if (n <= size()) { - M_destroy_range_e(impl_.b_ + n); - } else if (dataIsInternalAndNotVT(t) && n > capacity()) { - T copy(t); - reserve(n); - M_uninitialized_fill_n_e(n - size(), copy); - } else { - reserve(n); - M_uninitialized_fill_n_e(n - size(), t); - } - } - - size_type capacity() const noexcept { - return impl_.z_ - impl_.b_; - } - - bool empty() const noexcept { - return impl_.b_ == impl_.e_; - } - - void reserve(size_type n) { - if (n <= capacity()) return; - if (impl_.b_ && reserve_in_place(n)) return; - - auto newCap = folly::goodMallocSize(n * sizeof(T)) / sizeof(T); - auto newB = M_allocate(newCap); - try { - M_relocate(newB); - } catch (...) { - M_deallocate(newB, newCap); - throw; - } - if (impl_.b_) - M_deallocate(impl_.b_, impl_.z_ - impl_.b_); - impl_.z_ = newB + newCap; - impl_.e_ = newB + (impl_.e_ - impl_.b_); - impl_.b_ = newB; - } - - void shrink_to_fit() noexcept { - auto const newCapacityBytes = folly::goodMallocSize(size() * sizeof(T)); - auto const newCap = newCapacityBytes / sizeof(T); - auto const oldCap = capacity(); - - if (newCap >= oldCap) return; - - void* p = impl_.b_; - if ((rallocm && usingStdAllocator::value) && - newCapacityBytes >= folly::jemallocMinInPlaceExpandable && - rallocm(&p, NULL, newCapacityBytes, 0, ALLOCM_NO_MOVE) - == ALLOCM_SUCCESS) { - impl_.z_ += newCap - oldCap; - } else { - T* newB; // intentionally uninitialized - try { - newB = M_allocate(newCap); - try { - M_relocate(newB); - } catch (...) { - M_deallocate(newB, newCap); - return; // swallow the error - } - } catch (...) { - return; - } - if (impl_.b_) - M_deallocate(impl_.b_, impl_.z_ - impl_.b_); - impl_.z_ = newB + newCap; - impl_.e_ = newB + (impl_.e_ - impl_.b_); - impl_.b_ = newB; - } - } - -private: - - bool reserve_in_place(size_type n) { - if (!usingStdAllocator::value || !rallocm) return false; - - // jemalloc can never grow in place blocks smaller than 4096 bytes. - if ((impl_.z_ - impl_.b_) * sizeof(T) < - folly::jemallocMinInPlaceExpandable) return false; - - auto const newCapacityBytes = folly::goodMallocSize(n * sizeof(T)); - void* p = impl_.b_; - if (rallocm(&p, NULL, newCapacityBytes, 0, ALLOCM_NO_MOVE) - == ALLOCM_SUCCESS) { - impl_.z_ = impl_.b_ + newCapacityBytes / sizeof(T); - return true; - } - return false; - } - - //=========================================================================== - //--------------------------------------------------------------------------- - // element access -public: - - reference operator[](size_type n) { - assert(n < size()); - return impl_.b_[n]; - } - const_reference operator[](size_type n) const { - assert(n < size()); - return impl_.b_[n]; - } - const_reference at(size_type n) const { - if (UNLIKELY(n >= size())) { - throw std::out_of_range("fbvector: index is greater than size."); - } - return (*this)[n]; - } - reference at(size_type n) { - auto const& cThis = *this; - return const_cast(cThis.at(n)); - } - reference front() { - assert(!empty()); - return *impl_.b_; - } - const_reference front() const { - assert(!empty()); - return *impl_.b_; - } - reference back() { - assert(!empty()); - return impl_.e_[-1]; - } - const_reference back() const { - assert(!empty()); - return impl_.e_[-1]; - } - - //=========================================================================== - //--------------------------------------------------------------------------- - // data access -public: - - T* data() noexcept { - return impl_.b_; - } - const T* data() const noexcept { - return impl_.b_; - } - - //=========================================================================== - //--------------------------------------------------------------------------- - // modifiers (common) -public: - - template - void emplace_back(Args&&... args) { - if (impl_.e_ != impl_.z_) { - M_construct(impl_.e_, std::forward(args)...); - ++impl_.e_; - } else { - emplace_back_aux(std::forward(args)...); - } - } - - void - push_back(const T& value) { - if (impl_.e_ != impl_.z_) { - M_construct(impl_.e_, value); - ++impl_.e_; - } else { - emplace_back_aux(value); - } - } - - void - push_back(T&& value) { - if (impl_.e_ != impl_.z_) { - M_construct(impl_.e_, std::move(value)); - ++impl_.e_; - } else { - emplace_back_aux(std::move(value)); - } - } - - void pop_back() { - assert(!empty()); - --impl_.e_; - M_destroy(impl_.e_); - } - - void swap(fbvector& other) noexcept { - if (!usingStdAllocator::value && - A::propagate_on_container_swap::value) - swap(impl_, other.impl_); - else impl_.swapData(other.impl_); - } - - void clear() noexcept { - M_destroy_range_e(impl_.b_); - } - -private: - - // std::vector implements a similar function with a different growth - // strategy: empty() ? 1 : capacity() * 2. - // - // fbvector grows differently on two counts: - // - // (1) initial size - // Instead of grwoing to size 1 from empty, and fbvector allocates at - // least 64 bytes. You may still use reserve to reserve a lesser amount - // of memory. - // (2) 1.5x - // For medium-sized vectors, the growth strategy is 1.5x. See the docs - // for details. - // This does not apply to very small or very large fbvectors. This is a - // heuristic. - // A nice addition to fbvector would be the capability of having a user- - // defined growth strategy, probably as part of the allocator. - // - - size_type computePushBackCapacity() const { - return empty() ? std::max(64 / sizeof(T), size_type(1)) - : capacity() < folly::jemallocMinInPlaceExpandable / sizeof(T) - ? capacity() * 2 - : sizeof(T) > folly::jemallocMinInPlaceExpandable / 2 && capacity() == 1 - ? 2 - : capacity() > 4096 * 32 / sizeof(T) - ? capacity() * 2 - : (capacity() * 3 + 1) / 2; - } - - template - void emplace_back_aux(Args&&... args); - - //=========================================================================== - //--------------------------------------------------------------------------- - // modifiers (erase) -public: - - iterator erase(const_iterator position) { - return erase(position, position + 1); - } - - iterator erase(const_iterator first, const_iterator last) { - assert(isValid(first) && isValid(last)); - assert(first <= last); - if (first != last) { - if (last == end()) { - M_destroy_range_e((iterator)first); - } else { - if (folly::IsRelocatable::value && usingStdAllocator::value) { - D_destroy_range_a((iterator)first, (iterator)last); - if (last - first >= cend() - last) { - std::memcpy((iterator)first, last, (cend() - last) * sizeof(T)); - } else { - std::memmove((iterator)first, last, (cend() - last) * sizeof(T)); - } - impl_.e_ -= (last - first); - } else { - std::copy(std::make_move_iterator((iterator)last), - std::make_move_iterator(end()), (iterator)first); - auto newEnd = impl_.e_ - std::distance(first, last); - M_destroy_range_e(newEnd); - } - } - } - return (iterator)first; - } - - //=========================================================================== - //--------------------------------------------------------------------------- - // modifiers (insert) -private: // we have the private section first because it defines some macros - - bool isValid(const_iterator it) { - return cbegin() <= it && it <= cend(); - } - - size_type computeInsertCapacity(size_type n) { - size_type nc = std::max(computePushBackCapacity(), size() + n); - size_type ac = folly::goodMallocSize(nc * sizeof(T)) / sizeof(T); - return ac; - } - - //--------------------------------------------------------------------------- - // - // make_window takes an fbvector, and creates an uninitialized gap (a - // window) at the given position, of the given size. The fbvector must - // have enough capacity. - // - // Explanation by picture. - // - // 123456789______ - // ^ - // make_window here of size 3 - // - // 1234___56789___ - // - // If something goes wrong and the window must be destroyed, use - // undo_window to provide a weak exception guarantee. It destroys - // the right ledge. - // - // 1234___________ - // - //--------------------------------------------------------------------------- - // - // wrap_frame takes an inverse window and relocates an fbvector around it. - // The fbvector must have at least as many elements as the left ledge. - // - // Explanation by picture. - // - // START - // fbvector: inverse window: - // 123456789______ _____abcde_______ - // [idx][ n ] - // - // RESULT - // _______________ 12345abcde6789___ - // - //--------------------------------------------------------------------------- - // - // insert_use_fresh_memory returns true iff the fbvector should use a fresh - // block of memory for the insertion. If the fbvector does not have enough - // spare capacity, then it must return true. Otherwise either true or false - // may be returned. - // - //--------------------------------------------------------------------------- - // - // These three functions, make_window, wrap_frame, and - // insert_use_fresh_memory, can be combined into a uniform interface. - // Since that interface involves a lot of case-work, it is built into - // some macros: FOLLY_FBVECTOR_INSERT_(START|TRY|END) - // Macros are used in an attempt to let GCC perform better optimizations, - // especially control flow optimization. - // - - //--------------------------------------------------------------------------- - // window - - void make_window(iterator position, size_type n) { - assert(isValid(position)); - assert(size() + n <= capacity()); - assert(n != 0); - - auto tail = std::distance(position, impl_.e_); - - if (tail <= n) { - relocate_move(position + n, position, impl_.e_); - relocate_done(position + n, position, impl_.e_); - impl_.e_ += n; - } else { - if (folly::IsRelocatable::value && usingStdAllocator::value) { - std::memmove(position + n, position, tail * sizeof(T)); - impl_.e_ += n; - } else { - D_uninitialized_move_a(impl_.e_, impl_.e_ - n, impl_.e_); - impl_.e_ += n; - std::copy_backward(std::make_move_iterator(position), - std::make_move_iterator(impl_.e_ - n), impl_.e_); - D_destroy_range_a(position, position + n); - } - } - } - - void undo_window(iterator position, size_type n) noexcept { - D_destroy_range_a(position + n, impl_.e_); - impl_.e_ = position; - } - - //--------------------------------------------------------------------------- - // frame - - void wrap_frame(T* ledge, size_type idx, size_type n) { - assert(size() >= idx); - assert(n != 0); - - relocate_move(ledge, impl_.b_, impl_.b_ + idx); - try { - relocate_move(ledge + idx + n, impl_.b_ + idx, impl_.e_); - } catch (...) { - relocate_undo(ledge, impl_.b_, impl_.b_ + idx); - throw; - } - relocate_done(ledge, impl_.b_, impl_.b_ + idx); - relocate_done(ledge + idx + n, impl_.b_ + idx, impl_.e_); - } - - //--------------------------------------------------------------------------- - // use fresh? - - bool insert_use_fresh(const_iterator cposition, size_type n) { - if (cposition == cend()) { - if (size() + n <= capacity()) return false; - if (reserve_in_place(size() + n)) return false; - return true; - } - - if (size() + n > capacity()) return true; - - return false; - } - - //--------------------------------------------------------------------------- - // interface - - #define FOLLY_FBVECTOR_INSERT_START(cpos, n) \ - assert(isValid(cpos)); \ - T* position = const_cast(cpos); \ - size_type idx = std::distance(impl_.b_, position); \ - bool fresh = insert_use_fresh(position, n); \ - T* b; \ - size_type newCap = 0; \ - \ - if (fresh) { \ - newCap = computeInsertCapacity(n); \ - b = M_allocate(newCap); \ - } else { \ - make_window(position, n); \ - b = impl_.b_; \ - } \ - \ - T* start = b + idx; \ - \ - try { \ - - // construct the inserted elements - - #define FOLLY_FBVECTOR_INSERT_TRY(cpos, n) \ - } catch (...) { \ - if (fresh) { \ - M_deallocate(b, newCap); \ - } else { \ - undo_window(position, n); \ - } \ - throw; \ - } \ - \ - if (fresh) { \ - try { \ - wrap_frame(b, idx, n); \ - } catch (...) { \ - - - // delete the inserted elements (exception has been thrown) - - #define FOLLY_FBVECTOR_INSERT_END(cpos, n) \ - M_deallocate(b, newCap); \ - throw; \ - } \ - if (impl_.b_) M_deallocate(impl_.b_, capacity()); \ - impl_.set(b, size() + n, newCap); \ - return impl_.b_ + idx; \ - } else { \ - return position; \ - } \ - - //--------------------------------------------------------------------------- - // insert functions -public: - - template - iterator emplace(const_iterator cpos, Args&&... args) { - FOLLY_FBVECTOR_INSERT_START(cpos, 1) - M_construct(start, std::forward(args)...); - FOLLY_FBVECTOR_INSERT_TRY(cpos, 1) - M_destroy(start); - FOLLY_FBVECTOR_INSERT_END(cpos, 1) - } - - iterator insert(const_iterator cpos, const T& value) { - if (dataIsInternal(value)) return insert(cpos, T(value)); - - FOLLY_FBVECTOR_INSERT_START(cpos, 1) - M_construct(start, value); - FOLLY_FBVECTOR_INSERT_TRY(cpos, 1) - M_destroy(start); - FOLLY_FBVECTOR_INSERT_END(cpos, 1) - } - - iterator insert(const_iterator cpos, T&& value) { - if (dataIsInternal(value)) return insert(cpos, T(std::move(value))); - - FOLLY_FBVECTOR_INSERT_START(cpos, 1) - M_construct(start, std::move(value)); - FOLLY_FBVECTOR_INSERT_TRY(cpos, 1) - M_destroy(start); - FOLLY_FBVECTOR_INSERT_END(cpos, 1) - } - - iterator insert(const_iterator cpos, size_type n, VT value) { - if (n == 0) return (iterator)cpos; - if (dataIsInternalAndNotVT(value)) return insert(cpos, n, T(value)); - - FOLLY_FBVECTOR_INSERT_START(cpos, n) - D_uninitialized_fill_n_a(start, n, value); - FOLLY_FBVECTOR_INSERT_TRY(cpos, n) - D_destroy_range_a(start, start + n); - FOLLY_FBVECTOR_INSERT_END(cpos, n) - } - - template ::iterator_category> - iterator insert(const_iterator cpos, It first, It last) { - return insert(cpos, first, last, Category()); - } - - iterator insert(const_iterator cpos, std::initializer_list il) { - return insert(cpos, il.begin(), il.end()); - } - - //--------------------------------------------------------------------------- - // insert dispatch for iterator types -private: - - template - iterator insert(const_iterator cpos, FIt first, FIt last, - std::forward_iterator_tag) { - size_type n = std::distance(first, last); - if (n == 0) return (iterator)cpos; - - FOLLY_FBVECTOR_INSERT_START(cpos, n) - D_uninitialized_copy_a(start, first, last); - FOLLY_FBVECTOR_INSERT_TRY(cpos, n) - D_destroy_range_a(start, start + n); - FOLLY_FBVECTOR_INSERT_END(cpos, n) - } - - template - iterator insert(const_iterator cpos, IIt first, IIt last, - std::input_iterator_tag) { - T* position = const_cast(cpos); - assert(isValid(position)); - size_type idx = std::distance(begin(), position); - - fbvector storage(std::make_move_iterator(position), - std::make_move_iterator(end()), - A::select_on_container_copy_construction(impl_)); - M_destroy_range_e(position); - for (; first != last; ++first) emplace_back(*first); - insert(cend(), std::make_move_iterator(storage.begin()), - std::make_move_iterator(storage.end())); - return impl_.b_ + idx; - } - - //=========================================================================== - //--------------------------------------------------------------------------- - // lexicographical functions (others from boost::totally_ordered superclass) -public: - - bool operator==(const fbvector& other) const { - return size() == other.size() && std::equal(begin(), end(), other.begin()); - } - - bool operator<(const fbvector& other) const { - return std::lexicographical_compare( - begin(), end(), other.begin(), other.end()); - } - - //=========================================================================== - //--------------------------------------------------------------------------- - // friends -private: - - template - friend _T* relinquish(fbvector<_T, _A>&); - - template - friend void attach(fbvector<_T, _A>&, _T* data, size_t sz, size_t cap); - -}; // class fbvector - - -//============================================================================= -//----------------------------------------------------------------------------- -// outlined functions (gcc, you finicky compiler you) - -template -template -void fbvector::emplace_back_aux(Args&&... args) { - size_type byte_sz = folly::goodMallocSize( - computePushBackCapacity() * sizeof(T)); - if (usingStdAllocator::value - && rallocm - && ((impl_.z_ - impl_.b_) * sizeof(T) >= - folly::jemallocMinInPlaceExpandable)) { - // Try to reserve in place. - // Ask rallocm to allocate in place at least size()+1 and at most sz space. - // rallocm will allocate as much as possible within that range, which - // is the best possible outcome: if sz space is available, take it all, - // otherwise take as much as possible. If nothing is available, then fail. - // In this fashion, we never relocate if there is a possibility of - // expanding in place, and we never relocate by less than the desired - // amount unless we cannot expand further. Hence we will not relocate - // sub-optimally twice in a row (modulo the blocking memory being freed). - size_type lower = folly::goodMallocSize(sizeof(T) + size() * sizeof(T)); - size_type upper = byte_sz; - size_type extra = upper - lower; - assert(extra >= 0); - - void* p = impl_.b_; - size_t actual; - - if (rallocm(&p, &actual, lower, extra, ALLOCM_NO_MOVE) - == ALLOCM_SUCCESS) { - impl_.z_ = impl_.b_ + actual / sizeof(T); - M_construct(impl_.e_, std::forward(args)...); - ++impl_.e_; - return; - } - } - - // Reallocation failed. Perform a manual relocation. - size_type sz = byte_sz / sizeof(T); - auto newB = M_allocate(sz); - auto newE = newB + size(); - try { - if (folly::IsRelocatable::value && usingStdAllocator::value) { - // For linear memory access, relocate before construction. - // By the test condition, relocate is noexcept. - // Note that there is no cleanup to do if M_construct throws - that's - // one of the beauties of relocation. - // Benchmarks for this code have high variance, and seem to be close. - relocate_move(newB, impl_.b_, impl_.e_); - M_construct(newE, std::forward(args)...); - ++newE; - } else { - M_construct(newE, std::forward(args)...); - ++newE; - try { - M_relocate(newB); - } catch (...) { - M_destroy(newE - 1); - throw; - } - } - } catch (...) { - M_deallocate(newB, sz); - throw; - } - if (impl_.b_) M_deallocate(impl_.b_, size()); - impl_.b_ = newB; - impl_.e_ = newE; - impl_.z_ = newB + sz; -} - -//============================================================================= -//----------------------------------------------------------------------------- -// specialized functions - -template -void swap(fbvector& lhs, fbvector& rhs) noexcept { - lhs.swap(rhs); -} - -//============================================================================= -//----------------------------------------------------------------------------- -// other - -template -void compactResize(fbvector* v, size_t sz) { - v->resize(sz); - v->shrink_to_fit(); -} - -// DANGER -// -// relinquish and attach are not a members function specifically so that it is -// awkward to call them. It is very easy to shoot yourself in the foot with -// these functions. -// -// If you call relinquish, then it is your responsibility to free the data -// and the storage, both of which may have been generated in a non-standard -// way through the fbvector's allocator. -// -// If you call attach, it is your responsibility to ensure that the fbvector -// is fresh (size and capacity both zero), and that the supplied data is -// capable of being manipulated by the allocator. -// It is acceptable to supply a stack pointer IF: -// (1) The vector's data does not outlive the stack pointer. This includes -// extension of the data's life through a move operation. -// (2) The pointer has enough capacity that the vector will never be -// relocated. -// (3) Insert is not called on the vector; these functions have leeway to -// relocate the vector even if there is enough capacity. -// (4) A stack pointer is compatible with the fbvector's allocator. -// - -template -T* relinquish(fbvector& v) { - T* ret = v.data(); - v.impl_.b_ = v.impl_.e_ = v.impl_.z_ = nullptr; - return ret; -} - -template -void attach(fbvector& v, T* data, size_t sz, size_t cap) { - assert(v.data() == nullptr); - v.impl_.b_ = data; - v.impl_.e_ = data + sz; - v.impl_.z_ = data + cap; -} - -} // namespace folly - -#endif // FOLLY_FBVECTOR_H diff --git a/hphp/third_party/folly/folly/File.cpp b/hphp/third_party/folly/folly/File.cpp deleted file mode 100644 index 3c961aaae..000000000 --- a/hphp/third_party/folly/folly/File.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#include "folly/File.h" - -#include -#include - -#include "folly/Exception.h" -#include "folly/FileUtil.h" -#include "folly/Format.h" -#include "folly/ScopeGuard.h" - -#include - -#include - -namespace folly { - -File::File() - : fd_(-1) - , ownsFd_(false) -{} - -File::File(int fd, bool ownsFd) - : fd_(fd) - , ownsFd_(ownsFd) -{} - -File::File(const char* name, int flags, mode_t mode) - : fd_(::open(name, flags, mode)) - , ownsFd_(false) { - if (fd_ == -1) { - throwSystemError(folly::format("open(\"{}\", {:#o}, 0{:#o}) failed", - name, flags, mode).fbstr()); - } - ownsFd_ = true; -} - -File::File(File&& other) - : fd_(other.fd_) - , ownsFd_(other.ownsFd_) { - - other.release(); -} - -File& File::operator=(File&& other) { - closeNoThrow(); - swap(other); - return *this; -} - -File::~File() { - closeNoThrow(); // ignore error -} - -/* static */ File File::temporary() { - // make a temp file with tmpfile(), dup the fd, then return it in a File. - FILE* tmpFile = tmpfile(); - checkFopenError(tmpFile, "tmpfile() failed"); - SCOPE_EXIT { fclose(tmpFile); }; - - int fd = dup(fileno(tmpFile)); - checkUnixError(fd, "dup() failed"); - - return File(fd, true); -} - -void File::release() { - fd_ = -1; - ownsFd_ = false; -} - -void File::swap(File& other) { - using std::swap; - swap(fd_, other.fd_); - swap(ownsFd_, other.ownsFd_); -} - -void swap(File& a, File& b) { - a.swap(b); -} - -void File::close() { - if (!closeNoThrow()) { - throwSystemError("close() failed"); - } -} - -bool File::closeNoThrow() { - int r = ownsFd_ ? ::close(fd_) : 0; - release(); - return r == 0; -} - -void File::lock() { doLock(LOCK_EX); } -bool File::try_lock() { return doTryLock(LOCK_EX); } -void File::lock_shared() { doLock(LOCK_SH); } -bool File::try_lock_shared() { return doTryLock(LOCK_SH); } - -void File::doLock(int op) { - checkUnixError(flockNoInt(fd_, op), "flock() failed (lock)"); -} - -bool File::doTryLock(int op) { - int r = flockNoInt(fd_, op | LOCK_NB); - // flock returns EWOULDBLOCK if already locked - if (r == -1 && errno == EWOULDBLOCK) return false; - checkUnixError(r, "flock() failed (try_lock)"); - return true; -} - -void File::unlock() { - checkUnixError(flockNoInt(fd_, LOCK_UN), "flock() failed (unlock)"); -} -void File::unlock_shared() { unlock(); } - -} // namespace folly diff --git a/hphp/third_party/folly/folly/File.h b/hphp/third_party/folly/folly/File.h deleted file mode 100644 index 71fcc0336..000000000 --- a/hphp/third_party/folly/folly/File.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_FILE_H_ -#define FOLLY_FILE_H_ - -#include -#include -#include -#include - -namespace folly { - -/** - * A File represents an open file. - */ -class File { - public: - /** - * Creates an empty File object, for late initialization. - */ - File(); - - /** - * Create a File object from an existing file descriptor. - * Takes ownership of the file descriptor if ownsFd is true. - */ - /* implicit */ File(int fd, - bool ownsFd = false); - - /** - * Open and create a file object. Throws on error. - */ - /* implicit */ File(const char* name, - int flags = O_RDONLY, - mode_t mode = 0644); - - ~File(); - - /** - * Create and return a temporary, owned file (uses tmpfile()). - */ - static File temporary(); - - /** - * Return the file descriptor, or -1 if the file was closed. - */ - int fd() const { return fd_; } - - /** - * Returns 'true' iff the file was successfully opened. - */ - explicit operator bool() const { - return fd_ >= 0; - } - - /** - * If we own the file descriptor, close the file and throw on error. - * Otherwise, do nothing. - */ - void close(); - - /** - * Closes the file (if owned). Returns true on success, false (and sets - * errno) on error. - */ - bool closeNoThrow(); - - /** - * Releases the file descriptor; no longer owned by this File. - */ - void release(); - - /** - * Swap this File with another. - */ - void swap(File& other); - - // movable - File(File&&); - File& operator=(File&&); - - // FLOCK (INTERPROCESS) LOCKS - // - // NOTE THAT THESE LOCKS ARE flock() LOCKS. That is, they may only be used - // for inter-process synchronization -- an attempt to acquire a second lock - // on the same file descriptor from the same process may succeed. Attempting - // to acquire a second lock on a different file descriptor for the same file - // should fail, but some systems might implement flock() using fcntl() locks, - // in which case it will succeed. - void lock(); - bool try_lock(); - void unlock(); - - void lock_shared(); - bool try_lock_shared(); - void unlock_shared(); - - private: - void doLock(int op); - bool doTryLock(int op); - - // unique - File(const File&) = delete; - File& operator=(const File&) = delete; - - int fd_; - bool ownsFd_; -}; - -void swap(File& a, File& b); - - -} // namespace folly - -#endif /* FOLLY_FILE_H_ */ diff --git a/hphp/third_party/folly/folly/FileUtil.cpp b/hphp/third_party/folly/folly/FileUtil.cpp deleted file mode 100644 index 45c313fff..000000000 --- a/hphp/third_party/folly/folly/FileUtil.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#include "folly/FileUtil.h" - -#include -#ifdef __APPLE__ -#include -#endif -#include - -#include "folly/detail/FileUtilDetail.h" - -namespace folly { - -using namespace fileutil_detail; - -int openNoInt(const char* name, int flags, mode_t mode) { - return wrapNoInt(open, name, flags, mode); -} - -int closeNoInt(int fd) { - int r = close(fd); - // Ignore EINTR. On Linux, close() may only return EINTR after the file - // descriptor has been closed, so you must not retry close() on EINTR -- - // in the best case, you'll get EBADF, and in the worst case, you'll end up - // closing a different file (one opened from another thread). - // - // Interestingly enough, the Single Unix Specification says that the state - // of the file descriptor is unspecified if close returns EINTR. In that - // case, the safe thing to do is also not to retry close() -- leaking a file - // descriptor is probably better than closing the wrong file. - if (r == -1 && errno == EINTR) { - r = 0; - } - return r; -} - -int fsyncNoInt(int fd) { - return wrapNoInt(fsync, fd); -} - -int fdatasyncNoInt(int fd) { -#if defined(__APPLE__) - return wrapNoInt(fcntl, fd, F_FULLFSYNC); -#elif defined(__FreeBSD__) - return wrapNoInt(fsync, fd); -#else - return wrapNoInt(fdatasync, fd); -#endif -} - -int ftruncateNoInt(int fd, off_t len) { - return wrapNoInt(ftruncate, fd, len); -} - -int truncateNoInt(const char* path, off_t len) { - return wrapNoInt(truncate, path, len); -} - -int flockNoInt(int fd, int operation) { - return wrapNoInt(flock, fd, operation); -} - -ssize_t readNoInt(int fd, void* buf, size_t count) { - return wrapNoInt(read, fd, buf, count); -} - -ssize_t preadNoInt(int fd, void* buf, size_t count, off_t offset) { - return wrapNoInt(pread, fd, buf, count, offset); -} - -ssize_t readvNoInt(int fd, const iovec* iov, int count) { - return wrapNoInt(writev, fd, iov, count); -} - -ssize_t writeNoInt(int fd, const void* buf, size_t count) { - return wrapNoInt(write, fd, buf, count); -} - -ssize_t pwriteNoInt(int fd, const void* buf, size_t count, off_t offset) { - return wrapNoInt(pwrite, fd, buf, count, offset); -} - -ssize_t writevNoInt(int fd, const iovec* iov, int count) { - return wrapNoInt(writev, fd, iov, count); -} - -ssize_t readFull(int fd, void* buf, size_t count) { - return wrapFull(read, fd, buf, count); -} - -ssize_t preadFull(int fd, void* buf, size_t count, off_t offset) { - return wrapFull(pread, fd, buf, count, offset); -} - -ssize_t writeFull(int fd, const void* buf, size_t count) { - return wrapFull(write, fd, const_cast(buf), count); -} - -ssize_t pwriteFull(int fd, const void* buf, size_t count, off_t offset) { - return wrapFull(pwrite, fd, const_cast(buf), count, offset); -} - -ssize_t readvFull(int fd, iovec* iov, int count) { - return wrapvFull(readv, fd, iov, count); -} - -#ifdef FOLLY_HAVE_PREADV -ssize_t preadvFull(int fd, iovec* iov, int count, off_t offset) { - return wrapvFull(preadv, fd, iov, count, offset); -} -#endif - -ssize_t writevFull(int fd, iovec* iov, int count) { - return wrapvFull(writev, fd, iov, count); -} - -#ifdef FOLLY_HAVE_PWRITEV -ssize_t pwritevFull(int fd, iovec* iov, int count, off_t offset) { - return wrapvFull(pwritev, fd, iov, count, offset); -} -#endif - -} // namespaces - diff --git a/hphp/third_party/folly/folly/FileUtil.h b/hphp/third_party/folly/folly/FileUtil.h deleted file mode 100644 index a542e1c98..000000000 --- a/hphp/third_party/folly/folly/FileUtil.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_FILEUTIL_H_ -#define FOLLY_FILEUTIL_H_ - -#include "folly/Portability.h" - -#include -#include -#include -#include -#include - -namespace folly { - -/** - * Convenience wrappers around some commonly used system calls. The *NoInt - * wrappers retry on EINTR. The *Full wrappers retry on EINTR and also loop - * until all data is written. Note that *Full wrappers weaken the thread - * semantics of underlying system calls. - */ -int openNoInt(const char* name, int flags, mode_t mode=0644); -int closeNoInt(int fd); -int fsyncNoInt(int fd); -int fdatasyncNoInt(int fd); -int ftruncateNoInt(int fd, off_t len); -int truncateNoInt(const char* path, off_t len); -int flockNoInt(int fd, int operation); - -ssize_t readNoInt(int fd, void* buf, size_t n); -ssize_t preadNoInt(int fd, void* buf, size_t n, off_t offset); -ssize_t readvNoInt(int fd, const iovec* iov, int count); - -ssize_t writeNoInt(int fd, const void* buf, size_t n); -ssize_t pwriteNoInt(int fd, const void* buf, size_t n, off_t offset); -ssize_t writevNoInt(int fd, const iovec* iov, int count); - -/** - * Wrapper around read() (and pread()) that, in addition to retrying on - * EINTR, will loop until all data is read. - * - * This wrapper is only useful for blocking file descriptors (for non-blocking - * file descriptors, you have to be prepared to deal with incomplete reads - * anyway), and only exists because POSIX allows read() to return an incomplete - * read if interrupted by a signal (instead of returning -1 and setting errno - * to EINTR). - * - * Note that this wrapper weakens the thread safety of read(): the file pointer - * is shared between threads, but the system call is atomic. If multiple - * threads are reading from a file at the same time, you don't know where your - * data came from in the file, but you do know that the returned bytes were - * contiguous. You can no longer make this assumption if using readFull(). - * You should probably use pread() when reading from the same file descriptor - * from multiple threads simultaneously, anyway. - * - * Note that readvFull and preadvFull require iov to be non-const, unlike - * readv and preadv. The contents of iov after these functions return - * is unspecified. - */ -ssize_t readFull(int fd, void* buf, size_t n); -ssize_t preadFull(int fd, void* buf, size_t n, off_t offset); -ssize_t readvFull(int fd, iovec* iov, int count); -#ifdef FOLLY_HAVE_PREADV -ssize_t preadvFull(int fd, iovec* iov, int count, off_t offset); -#endif - -/** - * Similar to readFull and preadFull above, wrappers around write() and - * pwrite() that loop until all data is written. - * - * Generally, the write() / pwrite() system call may always write fewer bytes - * than requested, just like read(). In certain cases (such as when writing to - * a pipe), POSIX provides stronger guarantees, but not in the general case. - * For example, Linux (even on a 64-bit platform) won't write more than 2GB in - * one write() system call. - * - * Note that writevFull and pwritevFull require iov to be non-const, unlike - * writev and pwritev. The contents of iov after these functions return - * is unspecified. - */ -ssize_t writeFull(int fd, const void* buf, size_t n); -ssize_t pwriteFull(int fd, const void* buf, size_t n, off_t offset); -ssize_t writevFull(int fd, iovec* iov, int count); -#ifdef FOLLY_HAVE_PWRITEV -ssize_t pwritevFull(int fd, iovec* iov, int count, off_t offset); -#endif - -} // namespaces - -#endif /* FOLLY_FILEUTIL_H_ */ - diff --git a/hphp/third_party/folly/folly/Fingerprint.h b/hphp/third_party/folly/folly/Fingerprint.h deleted file mode 100644 index a763473fe..000000000 --- a/hphp/third_party/folly/folly/Fingerprint.h +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright 2013 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. - */ - -/** - * Compute 64-, 96-, and 128-bit Rabin fingerprints, as described in - * Michael O. Rabin (1981) - * Fingerprinting by Random Polynomials - * Center for Research in Computing Technology, Harvard University - * Tech Report TR-CSE-03-01 - * - * The implementation follows the optimization described in - * Andrei Z. Broder (1993) - * Some applications of Rabin's fingerprinting method - * - * extended for fingerprints larger than 64 bits, and modified to use - * 64-bit instead of 32-bit integers for computation. - * - * The precomputed tables are in FingerprintTable.cpp, which is automatically - * generated by ComputeFingerprintTable.cpp. - * - * Benchmarked on 10/13/2009 on a 2.5GHz quad-core Xeon L5420, - * - Fingerprint<64>::update64() takes about 12ns - * - Fingerprint<96>::update64() takes about 30ns - * - Fingerprint<128>::update128() takes about 30ns - * (unsurprisingly, Fingerprint<96> and Fingerprint<128> take the - * same amount of time, as they both use 128-bit operations; the least - * significant 32 bits of Fingerprint<96> will always be 0) - * - * @author Tudor Bosman (tudorb@facebook.com) - */ - -#ifndef FOLLY_FINGERPRINT_H_ -#define FOLLY_FINGERPRINT_H_ - -#include - -#include "folly/Range.h" - -namespace folly { - -namespace detail { -template -struct FingerprintTable { - static const uint64_t poly[1 + (BITS-1)/64]; - static const uint64_t table[8][256][1 + (BITS-1)/64]; -}; -} // namespace detail - -/** - * Compute the Rabin fingerprint. - * - * TODO(tudorb): Extend this to allow removing values from the computed - * fingerprint (so we can fingerprint a sliding window, as in the Rabin-Karp - * string matching algorithm) - * - * update* methods return *this, so you can chain them together: - * Fingerprint<96>().update8(x).update(str).update64(val).write(output); - */ -template -class Fingerprint { - public: - Fingerprint() { - // Use a non-zero starting value. We'll use (1 << (BITS-1)) - fp_[0] = 1UL << 63; - for (int i = 1; i < size(); i++) - fp_[i] = 0; - } - - Fingerprint& update8(uint8_t v) { - uint8_t out = shlor8(v); - xortab(detail::FingerprintTable::table[0][out]); - return *this; - } - - // update32 and update64 are convenience functions to update the fingerprint - // with 4 and 8 bytes at a time. They are faster than calling update8 - // in a loop. They process the bytes in big-endian order. - Fingerprint& update32(uint32_t v) { - uint32_t out = shlor32(v); - for (int i = 0; i < 4; i++) { - xortab(detail::FingerprintTable::table[i][out&0xff]); - out >>= 8; - } - return *this; - } - - Fingerprint& update64(uint64_t v) { - uint64_t out = shlor64(v); - for (int i = 0; i < 8; i++) { - xortab(detail::FingerprintTable::table[i][out&0xff]); - out >>= 8; - } - return *this; - } - - Fingerprint& update(StringPiece str) { - // TODO(tudorb): We could be smart and do update64 or update32 if aligned - for (auto c : str) { - update8(uint8_t(c)); - } - return *this; - } - - /** - * Return the number of uint64s needed to hold the fingerprint value. - */ - static int size() { - return 1 + (BITS-1)/64; - } - - /** - * Write the computed fingeprint to an array of size() uint64_t's. - * For Fingerprint<64>, size()==1; we write 64 bits in out[0] - * For Fingerprint<96>, size()==2; we write 64 bits in out[0] and - * the most significant 32 bits of out[1] - * For Fingerprint<128>, size()==2; we write 64 bits in out[0] and - * 64 bits in out[1]. - */ - void write(uint64_t* out) const { - for (int i = 0; i < size(); i++) { - out[i] = fp_[i]; - } - } - - private: - // XOR the fingerprint with a value from one of the tables. - void xortab(const uint64_t* tab) { - for (int i = 0; i < size(); i++) { - fp_[i] ^= tab[i]; - } - } - - // Helper functions: shift the fingerprint value left by 8/32/64 bits, - // return the "out" value (the bits that were shifted out), and add "v" - // in the bits on the right. - uint8_t shlor8(uint8_t v); - uint32_t shlor32(uint32_t v); - uint64_t shlor64(uint64_t v); - - uint64_t fp_[1 + (BITS-1)/64]; -}; - -// Convenience functions - -/** - * Return the 64-bit Rabin fingerprint of a string. - */ -inline uint64_t fingerprint64(StringPiece str) { - uint64_t fp; - Fingerprint<64>().update(str).write(&fp); - return fp; -} - -/** - * Compute the 96-bit Rabin fingerprint of a string. - * Return the 64 most significant bits in *msb, and the 32 least significant - * bits in *lsb. - */ -inline void fingerprint96(StringPiece str, - uint64_t* msb, uint32_t* lsb) { - uint64_t fp[2]; - Fingerprint<96>().update(str).write(fp); - *msb = fp[0]; - *lsb = (uint32_t)(fp[1] >> 32); -} - -/** - * Compute the 128-bit Rabin fingerprint of a string. - * Return the 64 most significant bits in *msb, and the 64 least significant - * bits in *lsb. - */ -inline void fingerprint128(StringPiece str, - uint64_t* msb, uint64_t* lsb) { - uint64_t fp[2]; - Fingerprint<128>().update(str).write(fp); - *msb = fp[0]; - *lsb = fp[1]; -} - - -template <> -inline uint8_t Fingerprint<64>::shlor8(uint8_t v) { - uint8_t out = (uint8_t)(fp_[0] >> 56); - fp_[0] = (fp_[0] << 8) | ((uint64_t)v); - return out; -} - -template <> -inline uint32_t Fingerprint<64>::shlor32(uint32_t v) { - uint32_t out = (uint32_t)(fp_[0] >> 32); - fp_[0] = (fp_[0] << 32) | ((uint64_t)v); - return out; -} - -template <> -inline uint64_t Fingerprint<64>::shlor64(uint64_t v) { - uint64_t out = fp_[0]; - fp_[0] = v; - return out; -} - -template <> -inline uint8_t Fingerprint<96>::shlor8(uint8_t v) { - uint8_t out = (uint8_t)(fp_[0] >> 56); - fp_[0] = (fp_[0] << 8) | (fp_[1] >> 56); - fp_[1] = (fp_[1] << 8) | ((uint64_t)v << 32); - return out; -} - -template <> -inline uint32_t Fingerprint<96>::shlor32(uint32_t v) { - uint32_t out = (uint32_t)(fp_[0] >> 32); - fp_[0] = (fp_[0] << 32) | (fp_[1] >> 32); - fp_[1] = ((uint64_t)v << 32); - return out; -} - -template <> -inline uint64_t Fingerprint<96>::shlor64(uint64_t v) { - uint64_t out = fp_[0]; - fp_[0] = fp_[1] | (v >> 32); - fp_[1] = v << 32; - return out; -} - -template <> -inline uint8_t Fingerprint<128>::shlor8(uint8_t v) { - uint8_t out = (uint8_t)(fp_[0] >> 56); - fp_[0] = (fp_[0] << 8) | (fp_[1] >> 56); - fp_[1] = (fp_[1] << 8) | ((uint64_t)v); - return out; -} - -template <> -inline uint32_t Fingerprint<128>::shlor32(uint32_t v) { - uint32_t out = (uint32_t)(fp_[0] >> 32); - fp_[0] = (fp_[0] << 32) | (fp_[1] >> 32); - fp_[1] = (fp_[1] << 32) | ((uint64_t)v); - return out; -} - -template <> -inline uint64_t Fingerprint<128>::shlor64(uint64_t v) { - uint64_t out = fp_[0]; - fp_[0] = fp_[1]; - fp_[1] = v; - return out; -} - -} // namespace folly - -#endif /* FOLLY_FINGERPRINT_H_ */ - diff --git a/hphp/third_party/folly/folly/Foreach.h b/hphp/third_party/folly/folly/Foreach.h deleted file mode 100644 index c86784f87..000000000 --- a/hphp/third_party/folly/folly/Foreach.h +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_BASE_FOREACH_H_ -#define FOLLY_BASE_FOREACH_H_ - -/* - * Iterim macros (until we have C++0x range-based for) that simplify - * writing loops of the form - * - * for (Container::iterator i = c.begin(); i != c.end(); ++i) statement - * - * Just replace the above with: - * - * FOR_EACH (i, c) statement - * - * and everything is taken care of. - * - * The implementation is a bit convoluted to make sure the container is - * only evaluated once (however, keep in mind that c.end() is evaluated - * at every pass through the loop). To ensure the container is not - * evaluated multiple times, the macro defines one do-nothing if - * statement to inject the Boolean variable FOR_EACH_state1, and then a - * for statement that is executed only once, which defines the variable - * FOR_EACH_state2 holding a reference to the container being - * iterated. The workhorse is the last loop, which uses the just defined - * reference FOR_EACH_state2. - * - * The state variables are nested so they don't interfere; you can use - * FOR_EACH multiple times in the same scope, either at the same level or - * nested. - * - * In optimized builds g++ eliminates the extra gymnastics entirely and - * generates code 100% identical to the handwritten loop. - * - * This will not work with temporary containers. Consider BOOST_FOREACH - * if you need that. - */ - -#include - -namespace folly { namespace detail { - -/* - * Simple template for obtaining the unqualified type given a generic - * type T. For example, if T is const int, - * typeof(remove_cv_from_expression(T())) yields int. Due to a bug in - * g++, you need to actually use - * typeof(remove_cv_from_expression(T())) instead of typename - * boost::remove_cv::type. Note that the function - * remove_cv_from_expression is never defined - use it only inside - * typeof. - */ -template typename boost::remove_cv::type -remove_cv_from_expression(T value); - -}} - -/* - * Use a "reference reference" (auto&&) to take advantage of reference - * collapsing rules, if available. In this case, FOR_EACH* will work with - * temporary containers. - */ -#define FB_AUTO_RR(x, y) auto&& x = y - -/* - * The first AUTO should be replaced by decltype((c)) & - * FOR_EACH_state2, but bugs in gcc prevent that from functioning - * properly. The second pair of parens in decltype is actually - * required, see - * cpp-next.com/archive/2011/04/appearing-and-disappearing-consts-in-c/ - */ -#define FOR_EACH(i, c) \ - if (bool FOR_EACH_state1 = false) {} else \ - for (auto & FOR_EACH_state2 = (c); \ - !FOR_EACH_state1; FOR_EACH_state1 = true) \ - for (auto i = FOR_EACH_state2.begin(); \ - i != FOR_EACH_state2.end(); ++i) - -/* - * Similar to FOR_EACH, but iterates the container backwards by - * using rbegin() and rend(). - */ -#define FOR_EACH_R(i, c) \ - if (bool FOR_EACH_R_state1 = false) {} else \ - for (auto & FOR_EACH_R_state2 = (c); \ - !FOR_EACH_R_state1; FOR_EACH_R_state1 = true) \ - for (auto i = FOR_EACH_R_state2.rbegin(); \ - i != FOR_EACH_R_state2.rend(); ++i) - -/* - * Similar to FOR_EACH but also allows client to specify a 'count' variable - * to track the current iteration in the loop (starting at zero). - * Similar to python's enumerate() function. For example: - * string commaSeparatedValues = "VALUES: "; - * FOR_EACH_ENUMERATE(ii, value, columns) { // don't want comma at the end! - * commaSeparatedValues += (ii == 0) ? *value : string(",") + *value; - * } - */ -#define FOR_EACH_ENUMERATE(count, i, c) \ - if (bool FOR_EACH_state1 = false) {} else \ - for (auto & FOR_EACH_state2 = (c); \ - !FOR_EACH_state1; FOR_EACH_state1 = true) \ - if (size_t FOR_EACH_privateCount = 0) {} else \ - if (const size_t& count = FOR_EACH_privateCount) {} else \ - for (auto i = FOR_EACH_state2.begin(); \ - i != FOR_EACH_state2.end(); ++FOR_EACH_privateCount, ++i) - -/** - * Similar to FOR_EACH, but gives the user the key and value for each entry in - * the container, instead of just the iterator to the entry. For example: - * map testMap; - * FOR_EACH_KV(key, value, testMap) { - * cout << key << " " << value; - * } - */ -#define FOR_EACH_KV(k, v, c) \ - if (unsigned int FOR_EACH_state1 = 0) {} else \ - for (FB_AUTO_RR(FOR_EACH_state2, (c)); \ - !FOR_EACH_state1; FOR_EACH_state1 = 1) \ - for (auto FOR_EACH_state3 = FOR_EACH_state2.begin(); \ - FOR_EACH_state3 != FOR_EACH_state2.end(); \ - FOR_EACH_state1 == 2 \ - ? ((FOR_EACH_state1 = 0), ++FOR_EACH_state3) \ - : (FOR_EACH_state3 = FOR_EACH_state2.end())) \ - for (auto &k = FOR_EACH_state3->first; \ - !FOR_EACH_state1; ++FOR_EACH_state1) \ - for (auto &v = FOR_EACH_state3->second; \ - !FOR_EACH_state1; ++FOR_EACH_state1) - -namespace folly { namespace detail { - -// Boost 1.48 lacks has_less, we emulate a subset of it here. -template -class HasLess { - struct BiggerThanChar { char unused[2]; }; - template static char test(decltype(C() < D())*); - template static BiggerThanChar test(...); -public: - enum { value = sizeof(test(0)) == 1 }; -}; - -/** - * notThereYet helps the FOR_EACH_RANGE macro by opportunistically - * using "<" instead of "!=" whenever available when checking for loop - * termination. This makes e.g. examples such as FOR_EACH_RANGE (i, - * 10, 5) execute zero iterations instead of looping virtually - * forever. At the same time, some iterator types define "!=" but not - * "<". The notThereYet function will dispatch differently for those. - * - * Below is the correct implementation of notThereYet. It is disabled - * because of a bug in Boost 1.46: The filesystem::path::iterator - * defines operator< (via boost::iterator_facade), but that in turn - * uses distance_to which is undefined for that particular - * iterator. So HasLess (defined above) identifies - * boost::filesystem::path as properly comparable with <, but in fact - * attempting to do so will yield a compile-time error. - * - * The else branch (active) contains a conservative - * implementation. - */ - -#if 0 - -template -typename std::enable_if::value, bool>::type -notThereYet(T& iter, const U& end) { - return iter < end; -} - -template -typename std::enable_if::value, bool>::type -notThereYet(T& iter, const U& end) { - return iter != end; -} - -#else - -template -typename std::enable_if< - (std::is_arithmetic::value && std::is_arithmetic::value) || - (std::is_pointer::value && std::is_pointer::value), - bool>::type -notThereYet(T& iter, const U& end) { - return iter < end; -} - -template -typename std::enable_if< - !( - (std::is_arithmetic::value && std::is_arithmetic::value) || - (std::is_pointer::value && std::is_pointer::value) - ), - bool>::type -notThereYet(T& iter, const U& end) { - return iter != end; -} - -#endif - - -/** - * downTo is similar to notThereYet, but in reverse - it helps the - * FOR_EACH_RANGE_R macro. - */ -template -typename std::enable_if::value, bool>::type -downTo(T& iter, const U& begin) { - return begin < iter--; -} - -template -typename std::enable_if::value, bool>::type -downTo(T& iter, const U& begin) { - if (iter == begin) return false; - --iter; - return true; -} - -} } - -/* - * Iteration with given limits. end is assumed to be reachable from - * begin. end is evaluated every pass through the loop. - * - * NOTE: The type of the loop variable should be the common type of "begin" - * and "end". e.g. If "begin" is "int" but "end" is "long", we want "i" - * to be "long". This is done by getting the type of (true ? begin : end) - */ -#define FOR_EACH_RANGE(i, begin, end) \ - for (auto i = (true ? (begin) : (end)); \ - ::folly::detail::notThereYet(i, (end)); \ - ++i) - -/* - * Iteration with given limits. begin is assumed to be reachable from - * end by successive decrements. begin is evaluated every pass through - * the loop. - * - * NOTE: The type of the loop variable should be the common type of "begin" - * and "end". e.g. If "begin" is "int" but "end" is "long", we want "i" - * to be "long". This is done by getting the type of (false ? begin : end) - */ -#define FOR_EACH_RANGE_R(i, begin, end) \ - for (auto i = (false ? (begin) : (end)); ::folly::detail::downTo(i, (begin));) - -#endif diff --git a/hphp/third_party/folly/folly/Format-inl.h b/hphp/third_party/folly/folly/Format-inl.h deleted file mode 100644 index 70672d476..000000000 --- a/hphp/third_party/folly/folly/Format-inl.h +++ /dev/null @@ -1,1117 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_FORMAT_H_ -#error This file may only be included from Format.h. -#endif - -#include "folly/Traits.h" - -namespace folly { - -namespace detail { - -extern const char formatHexUpper[256][2]; -extern const char formatHexLower[256][2]; -extern const char formatOctal[512][3]; -extern const char formatBinary[256][8]; - -const size_t kMaxHexLength = 2 * sizeof(uintmax_t); -const size_t kMaxOctalLength = 3 * sizeof(uintmax_t); -const size_t kMaxBinaryLength = 8 * sizeof(uintmax_t); - -/** - * Convert an unsigned to hex, using repr (which maps from each possible - * 2-hex-bytes value to the 2-character representation). - * - * Just like folly::detail::uintToBuffer in Conv.h, writes at the *end* of - * the supplied buffer and returns the offset of the beginning of the string - * from the start of the buffer. The formatted string will be in range - * [buf+begin, buf+bufLen). - */ -template -size_t uintToHex(char* buffer, size_t bufLen, Uint v, - const char (&repr)[256][2]) { - // 'v >>= 7, v >>= 1' is no more than a work around to get rid of shift size - // warning when Uint = uint8_t (it's false as v >= 256 implies sizeof(v) > 1). - for (; !less_than(v); v >>= 7, v >>= 1) { - auto b = v & 0xff; - bufLen -= 2; - buffer[bufLen] = repr[b][0]; - buffer[bufLen + 1] = repr[b][1]; - } - buffer[--bufLen] = repr[v][1]; - if (v >= 16) { - buffer[--bufLen] = repr[v][0]; - } - return bufLen; -} - -/** - * Convert an unsigned to hex, using lower-case letters for the digits - * above 9. See the comments for uintToHex. - */ -template -inline size_t uintToHexLower(char* buffer, size_t bufLen, Uint v) { - return uintToHex(buffer, bufLen, v, formatHexLower); -} - -/** - * Convert an unsigned to hex, using upper-case letters for the digits - * above 9. See the comments for uintToHex. - */ -template -inline size_t uintToHexUpper(char* buffer, size_t bufLen, Uint v) { - return uintToHex(buffer, bufLen, v, formatHexUpper); -} - -/** - * Convert an unsigned to octal. - * - * Just like folly::detail::uintToBuffer in Conv.h, writes at the *end* of - * the supplied buffer and returns the offset of the beginning of the string - * from the start of the buffer. The formatted string will be in range - * [buf+begin, buf+bufLen). - */ -template -size_t uintToOctal(char* buffer, size_t bufLen, Uint v) { - auto& repr = formatOctal; - // 'v >>= 7, v >>= 2' is no more than a work around to get rid of shift size - // warning when Uint = uint8_t (it's false as v >= 512 implies sizeof(v) > 1). - for (; !less_than(v); v >>= 7, v >>= 2) { - auto b = v & 0x1ff; - bufLen -= 3; - buffer[bufLen] = repr[b][0]; - buffer[bufLen + 1] = repr[b][1]; - buffer[bufLen + 2] = repr[b][2]; - } - buffer[--bufLen] = repr[v][2]; - if (v >= 8) { - buffer[--bufLen] = repr[v][1]; - } - if (v >= 64) { - buffer[--bufLen] = repr[v][0]; - } - return bufLen; -} - -/** - * Convert an unsigned to binary. - * - * Just like folly::detail::uintToBuffer in Conv.h, writes at the *end* of - * the supplied buffer and returns the offset of the beginning of the string - * from the start of the buffer. The formatted string will be in range - * [buf+begin, buf+bufLen). - */ -template -size_t uintToBinary(char* buffer, size_t bufLen, Uint v) { - auto& repr = formatBinary; - if (v == 0) { - buffer[--bufLen] = '0'; - return bufLen; - } - for (; v; v >>= 7, v >>= 1) { - auto b = v & 0xff; - bufLen -= 8; - memcpy(buffer + bufLen, &(repr[b][0]), 8); - } - while (buffer[bufLen] == '0') { - ++bufLen; - } - return bufLen; -} - -} // namespace detail - - -template -Formatter::Formatter(StringPiece str, Args&&... args) - : str_(str), - values_(FormatValue::type>( - std::forward(args))...) { - static_assert(!containerMode || sizeof...(Args) == 1, - "Exactly one argument required in container mode"); -} - -template -template -void Formatter::operator()(Output& out) const { - auto p = str_.begin(); - auto end = str_.end(); - - // Copy raw string (without format specifiers) to output; - // not as simple as we'd like, as we still need to translate "}}" to "}" - // and throw if we see any lone "}" - auto outputString = [&out] (StringPiece s) { - auto p = s.begin(); - auto end = s.end(); - while (p != end) { - auto q = static_cast(memchr(p, '}', end - p)); - if (!q) { - out(StringPiece(p, end)); - break; - } - ++q; - out(StringPiece(p, q)); - p = q; - - if (p == end || *p != '}') { - throw std::invalid_argument( - "folly::format: single '}' in format string"); - } - ++p; - } - }; - - int nextArg = 0; - bool hasDefaultArgIndex = false; - bool hasExplicitArgIndex = false; - while (p != end) { - auto q = static_cast(memchr(p, '{', end - p)); - if (!q) { - outputString(StringPiece(p, end)); - break; - } - outputString(StringPiece(p, q)); - p = q + 1; - - if (p == end) { - throw std::invalid_argument( - "folly::format: '}' at end of format string"); - } - - // "{{" -> "{" - if (*p == '{') { - out(StringPiece(p, 1)); - ++p; - continue; - } - - // Format string - q = static_cast(memchr(p, '}', end - p)); - if (q == end) { - throw std::invalid_argument("folly::format: missing ending '}'"); - } - FormatArg arg(StringPiece(p, q)); - p = q + 1; - - int argIndex = 0; - auto piece = arg.splitKey(); // empty key component is okay - if (containerMode) { // static - if (piece.empty()) { - arg.setNextIntKey(nextArg++); - hasDefaultArgIndex = true; - } else { - arg.setNextKey(piece); - hasExplicitArgIndex = true; - } - } else { - if (piece.empty()) { - argIndex = nextArg++; - hasDefaultArgIndex = true; - } else { - try { - argIndex = to(piece); - } catch (const std::out_of_range& e) { - arg.error("argument index must be integer"); - } - arg.enforce(argIndex >= 0, "argument index must be non-negative"); - hasExplicitArgIndex = true; - } - } - - if (hasDefaultArgIndex && hasExplicitArgIndex) { - throw std::invalid_argument( - "folly::format: may not have both default and explicit arg indexes"); - } - - doFormat(argIndex, arg, out); - } -} - -namespace format_value { - -template -void formatString(StringPiece val, FormatArg& arg, FormatCallback& cb) { - if (arg.precision != FormatArg::kDefaultPrecision && - val.size() > arg.precision) { - val.reset(val.data(), arg.precision); - } - - constexpr int padBufSize = 128; - char padBuf[padBufSize]; - - // Output padding, no more than padBufSize at once - auto pad = [&padBuf, &cb, padBufSize] (int chars) { - while (chars) { - int n = std::min(chars, padBufSize); - cb(StringPiece(padBuf, n)); - chars -= n; - } - }; - - int padRemaining = 0; - if (arg.width != FormatArg::kDefaultWidth && val.size() < arg.width) { - char fill = arg.fill == FormatArg::kDefaultFill ? ' ' : arg.fill; - int padChars = arg.width - val.size(); - memset(padBuf, fill, std::min(padBufSize, padChars)); - - switch (arg.align) { - case FormatArg::Align::DEFAULT: - case FormatArg::Align::LEFT: - padRemaining = padChars; - break; - case FormatArg::Align::CENTER: - pad(padChars / 2); - padRemaining = padChars - padChars / 2; - break; - case FormatArg::Align::RIGHT: - case FormatArg::Align::PAD_AFTER_SIGN: - pad(padChars); - break; - default: - abort(); - break; - } - } - - cb(val); - - if (padRemaining) { - pad(padRemaining); - } -} - -template -void formatNumber(StringPiece val, int prefixLen, FormatArg& arg, - FormatCallback& cb) { - // precision means something different for numbers - arg.precision = FormatArg::kDefaultPrecision; - if (arg.align == FormatArg::Align::DEFAULT) { - arg.align = FormatArg::Align::RIGHT; - } else if (prefixLen && arg.align == FormatArg::Align::PAD_AFTER_SIGN) { - // Split off the prefix, then do any padding if necessary - cb(val.subpiece(0, prefixLen)); - val.advance(prefixLen); - arg.width = std::max(arg.width - prefixLen, 0); - } - format_value::formatString(val, arg, cb); -} - -template -void formatFormatter(const Formatter& formatter, - FormatArg& arg, - FormatCallback& cb) { - if (arg.width == FormatArg::kDefaultWidth && - arg.precision == FormatArg::kDefaultPrecision) { - // nothing to do - formatter(cb); - } else if (arg.align != FormatArg::Align::LEFT && - arg.align != FormatArg::Align::DEFAULT) { - // We can only avoid creating a temporary string if we align left, - // as we'd need to know the size beforehand otherwise - format_value::formatString(formatter.fbstr(), arg, cb); - } else { - auto fn = [&arg, &cb] (StringPiece sp) mutable { - int sz = static_cast(sp.size()); - if (arg.precision != FormatArg::kDefaultPrecision) { - sz = std::min(arg.precision, sz); - sp.reset(sp.data(), sz); - arg.precision -= sz; - } - if (!sp.empty()) { - cb(sp); - if (arg.width != FormatArg::kDefaultWidth) { - arg.width = std::max(arg.width - sz, 0); - } - } - }; - formatter(fn); - if (arg.width != FormatArg::kDefaultWidth && arg.width != 0) { - // Rely on formatString to do appropriate padding - format_value::formatString(StringPiece(), arg, cb); - } - } -} - -} // namespace format_value - -// Definitions for default FormatValue classes - -// Integral types (except bool) -template -class FormatValue< - T, typename std::enable_if< - std::is_integral::value && - !std::is_same::value>::type> - { - public: - explicit FormatValue(T val) : val_(val) { } - template - void format(FormatArg& arg, FormatCallback& cb) const { - arg.validate(FormatArg::Type::INTEGER); - doFormat(arg, cb); - } - - template - void doFormat(FormatArg& arg, FormatCallback& cb) const { - char presentation = arg.presentation; - if (presentation == FormatArg::kDefaultPresentation) { - presentation = std::is_same::value ? 'c' : 'd'; - } - - // Do all work as unsigned, we'll add the prefix ('0' or '0x' if necessary) - // and sign ourselves. - typedef typename std::make_unsigned::type UT; - UT uval; - char sign; - if (std::is_signed::value) { - if (folly::is_negative(val_)) { - uval = static_cast(-val_); - sign = '-'; - } else { - uval = static_cast(val_); - switch (arg.sign) { - case FormatArg::Sign::PLUS_OR_MINUS: - sign = '+'; - break; - case FormatArg::Sign::SPACE_OR_MINUS: - sign = ' '; - break; - default: - sign = '\0'; - break; - } - } - } else { - uval = val_; - sign = '\0'; - - arg.enforce(arg.sign == FormatArg::Sign::DEFAULT, - "sign specifications not allowed for unsigned values"); - } - - // max of: - // #x: 0x prefix + 16 bytes = 18 bytes - // #o: 0 prefix + 22 bytes = 23 bytes - // #b: 0b prefix + 64 bytes = 65 bytes - // ,d: 26 bytes (including thousands separators!) - // + nul terminator - // + 3 for sign and prefix shenanigans (see below) - constexpr size_t valBufSize = 69; - char valBuf[valBufSize]; - char* valBufBegin = nullptr; - char* valBufEnd = nullptr; - - // Defer to sprintf - auto useSprintf = [&] (const char* format) mutable { - valBufBegin = valBuf + 3; // room for sign and base prefix - valBufEnd = valBufBegin + sprintf(valBufBegin, format, - static_cast(uval)); - }; - - int prefixLen = 0; - - switch (presentation) { - case 'n': // TODO(tudorb): locale awareness? - case 'd': - arg.enforce(!arg.basePrefix, - "base prefix not allowed with '", presentation, - "' specifier"); - if (arg.thousandsSeparator) { - useSprintf("%'ju"); - } else { - // Use uintToBuffer, faster than sprintf - valBufBegin = valBuf + 3; - valBufEnd = valBufBegin + uint64ToBufferUnsafe(uval, valBufBegin); - } - break; - case 'c': - arg.enforce(!arg.basePrefix, - "base prefix not allowed with '", presentation, - "' specifier"); - arg.enforce(!arg.thousandsSeparator, - "thousands separator (',') not allowed with '", - presentation, "' specifier"); - valBufBegin = valBuf + 3; - *valBufBegin = static_cast(uval); - valBufEnd = valBufBegin + 1; - break; - case 'o': - case 'O': - arg.enforce(!arg.thousandsSeparator, - "thousands separator (',') not allowed with '", - presentation, "' specifier"); - valBufEnd = valBuf + valBufSize - 1; - valBufBegin = valBuf + detail::uintToOctal(valBuf, valBufSize - 1, uval); - if (arg.basePrefix) { - *--valBufBegin = '0'; - prefixLen = 1; - } - break; - case 'x': - arg.enforce(!arg.thousandsSeparator, - "thousands separator (',') not allowed with '", - presentation, "' specifier"); - valBufEnd = valBuf + valBufSize - 1; - valBufBegin = valBuf + detail::uintToHexLower(valBuf, valBufSize - 1, - uval); - if (arg.basePrefix) { - *--valBufBegin = 'x'; - *--valBufBegin = '0'; - prefixLen = 2; - } - break; - case 'X': - arg.enforce(!arg.thousandsSeparator, - "thousands separator (',') not allowed with '", - presentation, "' specifier"); - valBufEnd = valBuf + valBufSize - 1; - valBufBegin = valBuf + detail::uintToHexUpper(valBuf, valBufSize - 1, - uval); - if (arg.basePrefix) { - *--valBufBegin = 'X'; - *--valBufBegin = '0'; - prefixLen = 2; - } - break; - case 'b': - case 'B': - arg.enforce(!arg.thousandsSeparator, - "thousands separator (',') not allowed with '", - presentation, "' specifier"); - valBufEnd = valBuf + valBufSize - 1; - valBufBegin = valBuf + detail::uintToBinary(valBuf, valBufSize - 1, - uval); - if (arg.basePrefix) { - *--valBufBegin = presentation; // 0b or 0B - *--valBufBegin = '0'; - prefixLen = 2; - } - break; - default: - arg.error("invalid specifier '", presentation, "'"); - } - - if (sign) { - *--valBufBegin = sign; - ++prefixLen; - } - - format_value::formatNumber(StringPiece(valBufBegin, valBufEnd), prefixLen, - arg, cb); - } - - private: - T val_; -}; - -// Bool -template <> -class FormatValue { - public: - explicit FormatValue(bool val) : val_(val) { } - - template - void format(FormatArg& arg, FormatCallback& cb) const { - if (arg.presentation == FormatArg::kDefaultPresentation) { - arg.validate(FormatArg::Type::OTHER); - format_value::formatString(val_ ? "true" : "false", arg, cb); - } else { // number - FormatValue(val_).format(arg, cb); - } - } - - private: - bool val_; -}; - -// double -template <> -class FormatValue { - public: - explicit FormatValue(double val) : val_(val) { } - - template - void format(FormatArg& arg, FormatCallback& cb) const { - using ::double_conversion::DoubleToStringConverter; - using ::double_conversion::StringBuilder; - - arg.validate(FormatArg::Type::FLOAT); - - if (arg.presentation == FormatArg::kDefaultPresentation) { - arg.presentation = 'g'; - } - - const char* infinitySymbol = isupper(arg.presentation) ? "INF" : "inf"; - const char* nanSymbol = isupper(arg.presentation) ? "NAN" : "nan"; - char exponentSymbol = isupper(arg.presentation) ? 'E' : 'e'; - - if (arg.precision == FormatArg::kDefaultPrecision) { - arg.precision = 6; - } - - // 2+: for null terminator and optional sign shenanigans. - char buf[2 + std::max({ - (2 + DoubleToStringConverter::kMaxFixedDigitsBeforePoint + - DoubleToStringConverter::kMaxFixedDigitsAfterPoint), - (8 + DoubleToStringConverter::kMaxExponentialDigits), - (7 + DoubleToStringConverter::kMaxPrecisionDigits)})]; - StringBuilder builder(buf + 1, sizeof(buf) - 1); - - char plusSign; - switch (arg.sign) { - case FormatArg::Sign::PLUS_OR_MINUS: - plusSign = '+'; - break; - case FormatArg::Sign::SPACE_OR_MINUS: - plusSign = ' '; - break; - default: - plusSign = '\0'; - break; - }; - - double val = val_; - switch (arg.presentation) { - case '%': - val *= 100; - case 'f': - case 'F': - { - if (arg.precision > - DoubleToStringConverter::kMaxFixedDigitsAfterPoint) { - arg.precision = DoubleToStringConverter::kMaxFixedDigitsAfterPoint; - } - DoubleToStringConverter conv( - DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN, - infinitySymbol, - nanSymbol, - exponentSymbol, - -4, arg.precision, - 0, 0); - arg.enforce(conv.ToFixed(val, arg.precision, &builder), - "fixed double conversion failed"); - } - break; - case 'e': - case 'E': - { - if (arg.precision > DoubleToStringConverter::kMaxExponentialDigits) { - arg.precision = DoubleToStringConverter::kMaxExponentialDigits; - } - - DoubleToStringConverter conv( - DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN, - infinitySymbol, - nanSymbol, - exponentSymbol, - -4, arg.precision, - 0, 0); - CHECK(conv.ToExponential(val, arg.precision, &builder)); - } - break; - case 'n': // should be locale-aware, but isn't - case 'g': - case 'G': - { - if (arg.precision < DoubleToStringConverter::kMinPrecisionDigits) { - arg.precision = DoubleToStringConverter::kMinPrecisionDigits; - } else if (arg.precision > - DoubleToStringConverter::kMaxPrecisionDigits) { - arg.precision = DoubleToStringConverter::kMaxPrecisionDigits; - } - DoubleToStringConverter conv( - DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN, - infinitySymbol, - nanSymbol, - exponentSymbol, - -4, arg.precision, - 0, 0); - CHECK(conv.ToShortest(val, &builder)); - } - break; - default: - arg.error("invalid specifier '", arg.presentation, "'"); - } - - int len = builder.position(); - builder.Finalize(); - DCHECK_GT(len, 0); - - // Add '+' or ' ' sign if needed - char* p = buf + 1; - // anything that's neither negative nor nan - int prefixLen = 0; - if (plusSign && (*p != '-' && *p != 'n' && *p != 'N')) { - *--p = plusSign; - ++len; - prefixLen = 1; - } else if (*p == '-') { - prefixLen = 1; - } - - format_value::formatNumber(StringPiece(p, len), prefixLen, arg, cb); - } - - private: - double val_; -}; - -// float (defer to double) -template <> -class FormatValue { - public: - explicit FormatValue(float val) : val_(val) { } - - template - void format(FormatArg& arg, FormatCallback& cb) const { - FormatValue(val_).format(arg, cb); - } - - private: - float val_; -}; - -// Sring-y types (implicitly convertible to StringPiece, except char*) -template -class FormatValue< - T, typename std::enable_if< - (!std::is_pointer::value || - !std::is_same::type>::type>::value) && - std::is_convertible::value>::type> - { - public: - explicit FormatValue(StringPiece val) : val_(val) { } - - template - void format(FormatArg& arg, FormatCallback& cb) const { - if (arg.keyEmpty()) { - arg.validate(FormatArg::Type::OTHER); - arg.enforce(arg.presentation == FormatArg::kDefaultPresentation || - arg.presentation == 's', - "invalid specifier '", arg.presentation, "'"); - format_value::formatString(val_, arg, cb); - } else { - FormatValue(val_.at(arg.splitIntKey())).format(arg, cb); - } - } - - private: - StringPiece val_; -}; - -// Null -template <> -class FormatValue { - public: - explicit FormatValue(std::nullptr_t) { } - - template - void format(FormatArg& arg, FormatCallback& cb) const { - arg.validate(FormatArg::Type::OTHER); - arg.enforce(arg.presentation == FormatArg::kDefaultPresentation, - "invalid specifier '", arg.presentation, "'"); - format_value::formatString("(null)", arg, cb); - } -}; - -// Partial specialization of FormatValue for char* -template -class FormatValue< - T*, - typename std::enable_if< - std::is_same::type>::value>::type> - { - public: - explicit FormatValue(T* val) : val_(val) { } - - template - void format(FormatArg& arg, FormatCallback& cb) const { - if (arg.keyEmpty()) { - if (!val_) { - FormatValue(nullptr).format(arg, cb); - } else { - FormatValue(val_).format(arg, cb); - } - } else { - FormatValue::type>( - val_[arg.splitIntKey()]).format(arg, cb); - } - } - - private: - T* val_; -}; - -// Partial specialization of FormatValue for void* -template -class FormatValue< - T*, - typename std::enable_if< - std::is_same::type>::value>::type> - { - public: - explicit FormatValue(T* val) : val_(val) { } - - template - void format(FormatArg& arg, FormatCallback& cb) const { - if (!val_) { - FormatValue(nullptr).format(arg, cb); - } else { - // Print as a pointer, in hex. - arg.validate(FormatArg::Type::OTHER); - arg.enforce(arg.presentation == FormatArg::kDefaultPresentation, - "invalid specifier '", arg.presentation, "'"); - arg.basePrefix = true; - arg.presentation = 'x'; - if (arg.align == FormatArg::Align::DEFAULT) { - arg.align = FormatArg::Align::LEFT; - } - FormatValue( - reinterpret_cast(val_)).doFormat(arg, cb); - } - } - - private: - T* val_; -}; - -template -class TryFormatValue { - public: - template - static void formatOrFail(T& value, FormatArg& arg, FormatCallback& cb) { - arg.error("No formatter available for this type"); - } -}; - -template -class TryFormatValue< - T, - typename std::enable_if< - 0 < sizeof(FormatValue::type>)>::type> - { - public: - template - static void formatOrFail(T& value, FormatArg& arg, FormatCallback& cb) { - FormatValue::type>(value).format(arg, cb); - } -}; - -// Partial specialization of FormatValue for other pointers -template -class FormatValue< - T*, - typename std::enable_if< - !std::is_same::type>::value && - !std::is_same::type>::value>::type> - { - public: - explicit FormatValue(T* val) : val_(val) { } - - template - void format(FormatArg& arg, FormatCallback& cb) const { - if (arg.keyEmpty()) { - FormatValue((void*)val_).format(arg, cb); - } else { - TryFormatValue::formatOrFail(val_[arg.splitIntKey()], arg, cb); - } - } - private: - T* val_; -}; - -namespace detail { - -// Shortcut, so we don't have to use enable_if everywhere -struct FormatTraitsBase { - typedef void enabled; -}; - -// Traits that define enabled, value_type, and at() for anything -// indexable with integral keys: pointers, arrays, vectors, and maps -// with integral keys -template struct IndexableTraits; - -// Base class for sequences (vectors, deques) -template -struct IndexableTraitsSeq : public FormatTraitsBase { - typedef C container_type; - typedef typename C::value_type value_type; - static const value_type& at(const C& c, int idx) { - return c.at(idx); - } -}; - -// Base class for associative types (maps) -template -struct IndexableTraitsAssoc : public FormatTraitsBase { - typedef typename C::value_type::second_type value_type; - static const value_type& at(const C& c, int idx) { - return c.at(static_cast(idx)); - } -}; - -// std::array -template -struct IndexableTraits> - : public IndexableTraitsSeq> { -}; - -// std::vector -template -struct IndexableTraits> - : public IndexableTraitsSeq> { -}; - -// std::deque -template -struct IndexableTraits> - : public IndexableTraitsSeq> { -}; - -// fbvector -template -struct IndexableTraits> - : public IndexableTraitsSeq> { -}; - -// small_vector -template -struct IndexableTraits> - : public IndexableTraitsSeq> { -}; - -// std::map with integral keys -template -struct IndexableTraits< - std::map, - typename std::enable_if::value>::type> - : public IndexableTraitsAssoc> { -}; - -// std::unordered_map with integral keys -template -struct IndexableTraits< - std::unordered_map, - typename std::enable_if::value>::type> - : public IndexableTraitsAssoc> { -}; - -} // namespace detail - -// Partial specialization of FormatValue for integer-indexable containers -template -class FormatValue< - T, - typename detail::IndexableTraits::enabled> { - public: - explicit FormatValue(const T& val) : val_(val) { } - - template - void format(FormatArg& arg, FormatCallback& cb) const { - FormatValue::value_type>::type>( - detail::IndexableTraits::at( - val_, arg.splitIntKey())).format(arg, cb); - } - - private: - const T& val_; -}; - -namespace detail { - -// Define enabled, key_type, convert from StringPiece to the key types -// that we support -template struct KeyFromStringPiece; - -// std::string -template <> -struct KeyFromStringPiece : public FormatTraitsBase { - typedef std::string key_type; - static std::string convert(StringPiece s) { - return s.toString(); - } - typedef void enabled; -}; - -// fbstring -template <> -struct KeyFromStringPiece : public FormatTraitsBase { - typedef fbstring key_type; - static fbstring convert(StringPiece s) { - return s.toFbstring(); - } -}; - -// StringPiece -template <> -struct KeyFromStringPiece : public FormatTraitsBase { - typedef StringPiece key_type; - static StringPiece convert(StringPiece s) { - return s; - } -}; - -// Base class for associative types keyed by strings -template struct KeyableTraitsAssoc : public FormatTraitsBase { - typedef typename T::key_type key_type; - typedef typename T::value_type::second_type value_type; - static const value_type& at(const T& map, StringPiece key) { - return map.at(KeyFromStringPiece::convert(key)); - } -}; - -// Define enabled, key_type, value_type, at() for supported string-keyed -// types -template struct KeyableTraits; - -// std::map with string key -template -struct KeyableTraits< - std::map, - typename KeyFromStringPiece::enabled> - : public KeyableTraitsAssoc> { -}; - -// std::unordered_map with string key -template -struct KeyableTraits< - std::unordered_map, - typename KeyFromStringPiece::enabled> - : public KeyableTraitsAssoc> { -}; - -} // namespace detail - -// Partial specialization of FormatValue for string-keyed containers -template -class FormatValue< - T, - typename detail::KeyableTraits::enabled> { - public: - explicit FormatValue(const T& val) : val_(val) { } - - template - void format(FormatArg& arg, FormatCallback& cb) const { - FormatValue::value_type>::type>( - detail::KeyableTraits::at( - val_, arg.splitKey())).format(arg, cb); - } - - private: - const T& val_; -}; - -// Partial specialization of FormatValue for pairs -template -class FormatValue> { - public: - explicit FormatValue(const std::pair& val) : val_(val) { } - - template - void format(FormatArg& arg, FormatCallback& cb) const { - int key = arg.splitIntKey(); - switch (key) { - case 0: - FormatValue::type>(val_.first).format(arg, cb); - break; - case 1: - FormatValue::type>(val_.second).format(arg, cb); - break; - default: - arg.error("invalid index for pair"); - } - } - - private: - const std::pair& val_; -}; - -// Partial specialization of FormatValue for tuples -template -class FormatValue> { - typedef std::tuple Tuple; - public: - explicit FormatValue(const Tuple& val) : val_(val) { } - - template - void format(FormatArg& arg, FormatCallback& cb) const { - int key = arg.splitIntKey(); - arg.enforce(key >= 0, "tuple index must be non-negative"); - doFormat(key, arg, cb); - } - - private: - static constexpr size_t valueCount = std::tuple_size::value; - - template - typename std::enable_if::type - doFormatFrom(size_t i, FormatArg& arg, Callback& cb) const { - arg.enforce("tuple index out of range, max=", i); - } - - template - typename std::enable_if<(K < valueCount)>::type - doFormatFrom(size_t i, FormatArg& arg, Callback& cb) const { - if (i == K) { - FormatValue::type>::type>( - std::get(val_)).format(arg, cb); - } else { - doFormatFrom(i, arg, cb); - } - } - - template - void doFormat(size_t i, FormatArg& arg, Callback& cb) const { - return doFormatFrom<0>(i, arg, cb); - } - - const Tuple& val_; -}; - -// Partial specialization of FormatValue for nested Formatters -template -class FormatValue, void> { - typedef Formatter FormatterValue; - public: - explicit FormatValue(const FormatterValue& f) : f_(f) { } - - template - void format(FormatArg& arg, FormatCallback& cb) const { - format_value::formatFormatter(f_, arg, cb); - } - private: - const FormatterValue& f_; -}; - -/** - * Formatter objects can be appended to strings, and therefore they're - * compatible with folly::toAppend and folly::to. - */ -template -typename std::enable_if< - detail::IsSomeString::value>::type -toAppend(const Formatter& value, Tgt * result) { - value.appendTo(*result); -} - -} // namespace folly diff --git a/hphp/third_party/folly/folly/Format.cpp b/hphp/third_party/folly/folly/Format.cpp deleted file mode 100644 index 385a9bb8b..000000000 --- a/hphp/third_party/folly/folly/Format.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#include "folly/Format.h" - -namespace folly { -namespace detail { - -extern const FormatArg::Align formatAlignTable[]; -extern const FormatArg::Sign formatSignTable[]; - -} // namespace detail - -using namespace folly::detail; - -void FormatArg::initSlow() { - auto b = fullArgString.begin(); - auto end = fullArgString.end(); - - // Parse key - auto p = static_cast(memchr(b, ':', end - b)); - if (!p) { - key_ = StringPiece(b, end); - return; - } - key_ = StringPiece(b, p); - - if (*p == ':') { - // parse format spec - if (++p == end) return; - - // fill/align, or just align - Align a; - if (p + 1 != end && - (a = formatAlignTable[static_cast(p[1])]) != - Align::INVALID) { - fill = *p; - align = a; - p += 2; - if (p == end) return; - } else if ((a = formatAlignTable[static_cast(*p)]) != - Align::INVALID) { - align = a; - if (++p == end) return; - } - - Sign s; - unsigned char uSign = static_cast(*p); - if ((s = formatSignTable[uSign]) != Sign::INVALID) { - sign = s; - if (++p == end) return; - } - - if (*p == '#') { - basePrefix = true; - if (++p == end) return; - } - - if (*p == '0') { - enforce(align == Align::DEFAULT, "alignment specified twice"); - fill = '0'; - align = Align::PAD_AFTER_SIGN; - if (++p == end) return; - } - - if (*p >= '0' && *p <= '9') { - auto b = p; - do { - ++p; - } while (p != end && *p >= '0' && *p <= '9'); - width = to(StringPiece(b, p)); - - if (p == end) return; - } - - if (*p == ',') { - thousandsSeparator = true; - if (++p == end) return; - } - - if (*p == '.') { - auto b = ++p; - while (p != end && *p >= '0' && *p <= '9') { - ++p; - } - precision = to(StringPiece(b, p)); - - if (p == end) return; - } - - presentation = *p; - if (++p == end) return; - } - - error("extra characters in format string"); -} - -void FormatArg::validate(Type type) const { - enforce(keyEmpty(), "index not allowed"); - switch (type) { - case Type::INTEGER: - enforce(precision == kDefaultPrecision, - "precision not allowed on integers"); - break; - case Type::FLOAT: - enforce(!basePrefix, - "base prefix ('#') specifier only allowed on integers"); - enforce(!thousandsSeparator, - "thousands separator (',') only allowed on integers"); - break; - case Type::OTHER: - enforce(align != Align::PAD_AFTER_SIGN, - "'='alignment only allowed on numbers"); - enforce(sign == Sign::DEFAULT, - "sign specifier only allowed on numbers"); - enforce(!basePrefix, - "base prefix ('#') specifier only allowed on integers"); - enforce(!thousandsSeparator, - "thousands separator (',') only allowed on integers"); - break; - } -} - -} // namespace folly diff --git a/hphp/third_party/folly/folly/Format.h b/hphp/third_party/folly/folly/Format.h deleted file mode 100644 index 79a4e705e..000000000 --- a/hphp/third_party/folly/folly/Format.h +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_FORMAT_H_ -#define FOLLY_FORMAT_H_ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "folly/FBVector.h" -#include "folly/Conv.h" -#include "folly/Range.h" -#include "folly/Traits.h" -#include "folly/Likely.h" -#include "folly/String.h" -#include "folly/small_vector.h" -#include "folly/FormatArg.h" - -// Ignore shadowing warnings within this file, so includers can use -Wshadow. -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wshadow" - -namespace folly { - -// forward declarations -template class Formatter; -template -Formatter format(StringPiece fmt, Args&&... args); -template -Formatter vformat(StringPiece fmt, C&& container); -template class FormatValue; - -/** - * Formatter class. - * - * Note that this class is tricky, as it keeps *references* to its arguments - * (and doesn't copy the passed-in format string). Thankfully, you can't use - * this directly, you have to use format(...) below. - */ - -template -class Formatter { - template - friend Formatter format(StringPiece fmt, A&&... arg); - template - friend Formatter vformat(StringPiece fmt, C&& container); - public: - /** - * Append to output. out(StringPiece sp) may be called (more than once) - */ - template - void operator()(Output& out) const; - - /** - * Append to a string. - */ - template - typename std::enable_if::value>::type - appendTo(Str& str) const { - auto appender = [&str] (StringPiece s) { str.append(s.data(), s.size()); }; - (*this)(appender); - } - - /** - * Conversion to string - */ - std::string str() const { - std::string s; - appendTo(s); - return s; - } - - /** - * Conversion to fbstring - */ - fbstring fbstr() const { - fbstring s; - appendTo(s); - return s; - } - - private: - explicit Formatter(StringPiece str, Args&&... args); - - // Not copyable - Formatter(const Formatter&) = delete; - Formatter& operator=(const Formatter&) = delete; - - // Movable, but the move constructor and assignment operator are private, - // for the exclusive use of format() (below). This way, you can't create - // a Formatter object, but can handle references to it (for streaming, - // conversion to string, etc) -- which is good, as Formatter objects are - // dangerous (they hold references, possibly to temporaries) - Formatter(Formatter&&) = default; - Formatter& operator=(Formatter&&) = default; - - typedef std::tuple::type>...> ValueTuple; - static constexpr size_t valueCount = std::tuple_size::value; - - template - typename std::enable_if::type - doFormatFrom(size_t i, FormatArg& arg, Callback& cb) const { - arg.error("argument index out of range, max=", i); - } - - template - typename std::enable_if<(K < valueCount)>::type - doFormatFrom(size_t i, FormatArg& arg, Callback& cb) const { - if (i == K) { - std::get(values_).format(arg, cb); - } else { - doFormatFrom(i, arg, cb); - } - } - - template - void doFormat(size_t i, FormatArg& arg, Callback& cb) const { - return doFormatFrom<0>(i, arg, cb); - } - - StringPiece str_; - ValueTuple values_; -}; - -/** - * Formatter objects can be written to streams. - */ -template -std::ostream& operator<<(std::ostream& out, - const Formatter& formatter) { - auto writer = [&out] (StringPiece sp) { out.write(sp.data(), sp.size()); }; - formatter(writer); - return out; -} - -/** - * Create a formatter object. - * - * std::string formatted = format("{} {}", 23, 42).str(); - * LOG(INFO) << format("{} {}", 23, 42); - */ -template -Formatter format(StringPiece fmt, Args&&... args) { - return Formatter( - fmt, std::forward(args)...); -} - -/** - * Create a formatter object that takes one argument (of container type) - * and uses that container to get argument values from. - * - * std::map map { {"hello", "world"}, {"answer", "42"} }; - * - * The following are equivalent: - * format("{0[hello]} {0[answer]}", map); - * - * vformat("{hello} {answer}", map); - * - * but the latter is cleaner. - */ -template -Formatter vformat(StringPiece fmt, Container&& container) { - return Formatter( - fmt, std::forward(container)); -} - -/** - * Append formatted output to a string. - * - * std::string foo; - * format(&foo, "{} {}", 42, 23); - * - * Shortcut for toAppend(format(...), &foo); - */ -template -typename std::enable_if::value>::type -format(Str* out, StringPiece fmt, Args&&... args) { - format(fmt, std::forward(args)...).appendTo(*out); -} - -/** - * Append vformatted output to a string. - */ -template -typename std::enable_if::value>::type -vformat(Str* out, StringPiece fmt, Container&& container) { - vformat(fmt, std::forward(container)).appendTo(*out); -} - -/** - * Utilities for all format value specializations. - */ -namespace format_value { - -/** - * Format a string in "val", obeying appropriate alignment, padding, width, - * and precision. Treats Align::DEFAULT as Align::LEFT, and - * Align::PAD_AFTER_SIGN as Align::RIGHT; use formatNumber for - * number-specific formatting. - */ -template -void formatString(StringPiece val, FormatArg& arg, FormatCallback& cb); - -/** - * Format a number in "val"; the first prefixLen characters form the prefix - * (sign, "0x" base prefix, etc) which must be left-aligned if the alignment - * is Align::PAD_AFTER_SIGN. Treats Align::DEFAULT as Align::LEFT. Ignores - * arg.precision, as that has a different meaning for numbers (not "maximum - * field width") - */ -template -void formatNumber(StringPiece val, int prefixLen, FormatArg& arg, - FormatCallback& cb); - - -/** - * Format a Formatter object recursively. Behaves just like - * formatString(fmt.str(), arg, cb); but avoids creating a temporary - * string if possible. - */ -template -void formatFormatter(const Formatter& formatter, - FormatArg& arg, - FormatCallback& cb); - -} // namespace format_value - -/* - * Specialize folly::FormatValue for your type. - * - * FormatValue is constructed with a (reference-collapsed) T&&, which is - * guaranteed to stay alive until the FormatValue object is destroyed, so you - * may keep a reference (or pointer) to it instead of making a copy. - * - * You must define - * template - * void format(FormatArg& arg, Callback& cb) const; - * with the following semantics: format the value using the given argument. - * - * arg is given by non-const reference for convenience -- it won't be reused, - * so feel free to modify it in place if necessary. (For example, wrap an - * existing conversion but change the default, or remove the "key" when - * extracting an element from a container) - * - * Call the callback to append data to the output. You may call the callback - * as many times as you'd like (or not at all, if you want to output an - * empty string) - */ - -} // namespace folly - -#include "folly/Format-inl.h" - -#pragma GCC diagnostic pop - -#endif /* FOLLY_FORMAT_H_ */ diff --git a/hphp/third_party/folly/folly/FormatArg.h b/hphp/third_party/folly/folly/FormatArg.h deleted file mode 100644 index 124d621d7..000000000 --- a/hphp/third_party/folly/folly/FormatArg.h +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_FORMATARG_H_ -#define FOLLY_FORMATARG_H_ - -#include -#include "folly/Conv.h" -#include "folly/Likely.h" -#include "folly/Portability.h" -#include "folly/Range.h" - -namespace folly { - -/** - * Parsed format argument. - */ -struct FormatArg { - /** - * Parse a format argument from a string. Keeps a reference to the - * passed-in string -- does not copy the given characters. - */ - explicit FormatArg(StringPiece sp) - : fullArgString(sp), - fill(kDefaultFill), - align(Align::DEFAULT), - sign(Sign::DEFAULT), - basePrefix(false), - thousandsSeparator(false), - width(kDefaultWidth), - precision(kDefaultPrecision), - presentation(kDefaultPresentation), - nextKeyMode_(NextKeyMode::NONE) { - if (!sp.empty()) { - initSlow(); - } - } - - enum class Type { - INTEGER, - FLOAT, - OTHER - }; - /** - * Validate the argument for the given type; throws on error. - */ - void validate(Type type) const; - - /** - * Throw an exception if the first argument is false. The exception - * message will contain the argument string as well as any passed-in - * arguments to enforce, formatted using folly::to. - */ - template - void enforce(bool v, Args&&... args) const { - if (UNLIKELY(!v)) { - error(std::forward(args)...); - } - } - - template - void error(Args&&... args) const FOLLY_NORETURN; - /** - * Full argument string, as passed in to the constructor. - */ - StringPiece fullArgString; - - /** - * Fill - */ - static constexpr char kDefaultFill = '\0'; - char fill; - - /** - * Alignment - */ - enum class Align : uint8_t { - DEFAULT, - LEFT, - RIGHT, - PAD_AFTER_SIGN, - CENTER, - INVALID - }; - Align align; - - /** - * Sign - */ - enum class Sign : uint8_t { - DEFAULT, - PLUS_OR_MINUS, - MINUS, - SPACE_OR_MINUS, - INVALID - }; - Sign sign; - - /** - * Output base prefix (0 for octal, 0x for hex) - */ - bool basePrefix; - - /** - * Output thousands separator (comma) - */ - bool thousandsSeparator; - - /** - * Field width - */ - static constexpr int kDefaultWidth = -1; - int width; - - /** - * Precision - */ - static constexpr int kDefaultPrecision = -1; - int precision; - - /** - * Presentation - */ - static constexpr char kDefaultPresentation = '\0'; - char presentation; - - /** - * Split a key component from "key", which must be non-empty (an exception - * is thrown otherwise). - */ - template - StringPiece splitKey(); - - /** - * Is the entire key empty? - */ - bool keyEmpty() const { - return nextKeyMode_ == NextKeyMode::NONE && key_.empty(); - } - - /** - * Split an key component from "key", which must be non-empty and a valid - * integer (an exception is thrown otherwise). - */ - int splitIntKey(); - - void setNextIntKey(int val) { - assert(nextKeyMode_ == NextKeyMode::NONE); - nextKeyMode_ = NextKeyMode::INT; - nextIntKey_ = val; - } - - void setNextKey(StringPiece val) { - assert(nextKeyMode_ == NextKeyMode::NONE); - nextKeyMode_ = NextKeyMode::STRING; - nextKey_ = val; - } - - private: - void initSlow(); - template - StringPiece doSplitKey(); - - StringPiece key_; - int nextIntKey_; - StringPiece nextKey_; - enum class NextKeyMode { - NONE, - INT, - STRING, - }; - NextKeyMode nextKeyMode_; -}; - -template -inline void FormatArg::error(Args&&... args) const { - throw std::invalid_argument(to( - "folly::format: invalid format argument {", fullArgString, "}: ", - std::forward(args)...)); -} - -template -inline StringPiece FormatArg::splitKey() { - enforce(nextKeyMode_ != NextKeyMode::INT, "integer key expected"); - return doSplitKey(); -} - -template -inline StringPiece FormatArg::doSplitKey() { - if (nextKeyMode_ == NextKeyMode::STRING) { - nextKeyMode_ = NextKeyMode::NONE; - if (!emptyOk) { // static - enforce(!nextKey_.empty(), "non-empty key required"); - } - return nextKey_; - } - - if (key_.empty()) { - if (!emptyOk) { // static - error("non-empty key required"); - } - return StringPiece(); - } - - const char* b = key_.begin(); - const char* e = key_.end(); - const char* p; - if (e[-1] == ']') { - --e; - p = static_cast(memchr(b, '[', e - b)); - enforce(p, "unmatched ']'"); - } else { - p = static_cast(memchr(b, '.', e - b)); - } - if (p) { - key_.assign(p + 1, e); - } else { - p = e; - key_.clear(); - } - if (!emptyOk) { // static - enforce(b != p, "non-empty key required"); - } - return StringPiece(b, p); -} - -inline int FormatArg::splitIntKey() { - if (nextKeyMode_ == NextKeyMode::INT) { - nextKeyMode_ = NextKeyMode::NONE; - return nextIntKey_; - } - try { - return to(doSplitKey()); - } catch (const std::out_of_range& e) { - error("integer key required"); - return 0; // unreached - } -} - -} // namespace folly - -#endif /* FOLLY_FORMATARG_H_ */ - diff --git a/hphp/third_party/folly/folly/GroupVarint.cpp b/hphp/third_party/folly/folly/GroupVarint.cpp deleted file mode 100644 index 2ff34368b..000000000 --- a/hphp/third_party/folly/folly/GroupVarint.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#include "folly/GroupVarint.h" - -#if HAVE_GROUP_VARINT -namespace folly { - -const uint32_t GroupVarint32::kMask[] = { - 0xff, 0xffff, 0xffffff, 0xffffffff -}; - -const uint64_t GroupVarint64::kMask[] = { - 0xff, 0xffff, 0xffffff, 0xffffffff, - 0xffffffffffULL, 0xffffffffffffULL, 0xffffffffffffffULL, - 0xffffffffffffffffULL -}; - -} // namespace folly -#endif diff --git a/hphp/third_party/folly/folly/GroupVarint.h b/hphp/third_party/folly/folly/GroupVarint.h deleted file mode 100644 index dc2691954..000000000 --- a/hphp/third_party/folly/folly/GroupVarint.h +++ /dev/null @@ -1,608 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_GROUPVARINT_H_ -#define FOLLY_GROUPVARINT_H_ - -#ifndef __GNUC__ -#error GroupVarint.h requires GCC -#endif - -#if defined(__x86_64__) || defined(__i386__) -#define HAVE_GROUP_VARINT 1 - -#include -#include -#include "folly/detail/GroupVarintDetail.h" -#include "folly/Bits.h" -#include "folly/Range.h" -#include - -#ifdef __SSSE3__ -#include -namespace folly { -namespace detail { -extern const __m128i groupVarintSSEMasks[]; -} // namespace detail -} // namespace folly -#endif - -namespace folly { -namespace detail { -extern const uint8_t groupVarintLengths[]; -} // namespace detail -} // namespace folly - -namespace folly { - -template -class GroupVarint; - -/** - * GroupVarint encoding for 32-bit values. - * - * Encodes 4 32-bit integers at once, each using 1-4 bytes depending on size. - * There is one byte of overhead. (The first byte contains the lengths of - * the four integers encoded as two bits each; 00=1 byte .. 11=4 bytes) - * - * This implementation assumes little-endian and does unaligned 32-bit - * accesses, so it's basically not portable outside of the x86[_64] world. - */ -template <> -class GroupVarint : public detail::GroupVarintBase { - public: - - /** - * Return the number of bytes used to encode these four values. - */ - static size_t size(uint32_t a, uint32_t b, uint32_t c, uint32_t d) { - return kHeaderSize + kGroupSize + key(a) + key(b) + key(c) + key(d); - } - - /** - * Return the number of bytes used to encode four uint32_t values stored - * at consecutive positions in an array. - */ - static size_t size(const uint32_t* p) { - return size(p[0], p[1], p[2], p[3]); - } - - /** - * Return the number of bytes used to encode count (<= 4) values. - * If you clip a buffer after these many bytes, you can still decode - * the first "count" values correctly (if the remaining size() - - * partialSize() bytes are filled with garbage). - */ - static size_t partialSize(const type* p, size_t count) { - DCHECK_LE(count, kGroupSize); - size_t s = kHeaderSize + count; - for (; count; --count, ++p) { - s += key(*p); - } - return s; - } - - /** - * Return the number of values from *p that are valid from an encoded - * buffer of size bytes. - */ - static size_t partialCount(const char* p, size_t size) { - char v = *p; - size_t s = kHeaderSize; - s += 1 + b0key(v); - if (s > size) return 0; - s += 1 + b1key(v); - if (s > size) return 1; - s += 1 + b2key(v); - if (s > size) return 2; - s += 1 + b3key(v); - if (s > size) return 3; - return 4; - } - - /** - * Given a pointer to the beginning of an GroupVarint32-encoded block, - * return the number of bytes used by the encoding. - */ - static size_t encodedSize(const char* p) { - return (kHeaderSize + kGroupSize + - b0key(*p) + b1key(*p) + b2key(*p) + b3key(*p)); - } - - /** - * Encode four uint32_t values into the buffer pointed-to by p, and return - * the next position in the buffer (that is, one character past the last - * encoded byte). p needs to have at least size()+4 bytes available. - */ - static char* encode(char* p, uint32_t a, uint32_t b, uint32_t c, uint32_t d) { - uint8_t b0key = key(a); - uint8_t b1key = key(b); - uint8_t b2key = key(c); - uint8_t b3key = key(d); - *p++ = (b3key << 6) | (b2key << 4) | (b1key << 2) | b0key; - storeUnaligned(p, a); - p += b0key+1; - storeUnaligned(p, b); - p += b1key+1; - storeUnaligned(p, c); - p += b2key+1; - storeUnaligned(p, d); - p += b3key+1; - return p; - } - - /** - * Encode four uint32_t values from the array pointed-to by src into the - * buffer pointed-to by p, similar to encode(p,a,b,c,d) above. - */ - static char* encode(char* p, const uint32_t* src) { - return encode(p, src[0], src[1], src[2], src[3]); - } - - /** - * Decode four uint32_t values from a buffer, and return the next position - * in the buffer (that is, one character past the last encoded byte). - * The buffer needs to have at least 3 extra bytes available (they - * may be read but ignored). - */ - static const char* decode_simple(const char* p, uint32_t* a, uint32_t* b, - uint32_t* c, uint32_t* d) { - size_t k = loadUnaligned(p); - const char* end = p + detail::groupVarintLengths[k]; - ++p; - size_t k0 = b0key(k); - *a = loadUnaligned(p) & kMask[k0]; - p += k0+1; - size_t k1 = b1key(k); - *b = loadUnaligned(p) & kMask[k1]; - p += k1+1; - size_t k2 = b2key(k); - *c = loadUnaligned(p) & kMask[k2]; - p += k2+1; - size_t k3 = b3key(k); - *d = loadUnaligned(p) & kMask[k3]; - p += k3+1; - return end; - } - - /** - * Decode four uint32_t values from a buffer and store them in the array - * pointed-to by dest, similar to decode(p,a,b,c,d) above. - */ - static const char* decode_simple(const char* p, uint32_t* dest) { - return decode_simple(p, dest, dest+1, dest+2, dest+3); - } - -#ifdef __SSSE3__ - static const char* decode(const char* p, uint32_t* dest) { - uint8_t key = p[0]; - __m128i val = _mm_loadu_si128((const __m128i*)(p+1)); - __m128i mask = detail::groupVarintSSEMasks[key]; - __m128i r = _mm_shuffle_epi8(val, mask); - _mm_storeu_si128((__m128i*)dest, r); - return p + detail::groupVarintLengths[key]; - } - - static const char* decode(const char* p, uint32_t* a, uint32_t* b, - uint32_t* c, uint32_t* d) { - uint8_t key = p[0]; - __m128i val = _mm_loadu_si128((const __m128i*)(p+1)); - __m128i mask = detail::groupVarintSSEMasks[key]; - __m128i r = _mm_shuffle_epi8(val, mask); - - // Extracting 32 bits at a time out of an XMM register is a SSE4 feature -#ifdef __SSE4__ - *a = _mm_extract_epi32(r, 0); - *b = _mm_extract_epi32(r, 1); - *c = _mm_extract_epi32(r, 2); - *d = _mm_extract_epi32(r, 3); -#else /* !__SSE4__ */ - *a = _mm_extract_epi16(r, 0) + (_mm_extract_epi16(r, 1) << 16); - *b = _mm_extract_epi16(r, 2) + (_mm_extract_epi16(r, 3) << 16); - *c = _mm_extract_epi16(r, 4) + (_mm_extract_epi16(r, 5) << 16); - *d = _mm_extract_epi16(r, 6) + (_mm_extract_epi16(r, 7) << 16); -#endif /* __SSE4__ */ - - return p + detail::groupVarintLengths[key]; - } - -#else /* !__SSSE3__ */ - static const char* decode(const char* p, uint32_t* a, uint32_t* b, - uint32_t* c, uint32_t* d) { - return decode_simple(p, a, b, c, d); - } - - static const char* decode(const char* p, uint32_t* dest) { - return decode_simple(p, dest); - } -#endif /* __SSSE3__ */ - - private: - static uint8_t key(uint32_t x) { - // __builtin_clz is undefined for the x==0 case - return 3 - (__builtin_clz(x|1) / 8); - } - static size_t b0key(size_t x) { return x & 3; } - static size_t b1key(size_t x) { return (x >> 2) & 3; } - static size_t b2key(size_t x) { return (x >> 4) & 3; } - static size_t b3key(size_t x) { return (x >> 6) & 3; } - - static const uint32_t kMask[]; -}; - - -/** - * GroupVarint encoding for 64-bit values. - * - * Encodes 5 64-bit integers at once, each using 1-8 bytes depending on size. - * There are two bytes of overhead. (The first two bytes contain the lengths - * of the five integers encoded as three bits each; 000=1 byte .. 111 = 8 bytes) - * - * This implementation assumes little-endian and does unaligned 64-bit - * accesses, so it's basically not portable outside of the x86[_64] world. - */ -template <> -class GroupVarint : public detail::GroupVarintBase { - public: - /** - * Return the number of bytes used to encode these five values. - */ - static size_t size(uint64_t a, uint64_t b, uint64_t c, uint64_t d, - uint64_t e) { - return (kHeaderSize + kGroupSize + - key(a) + key(b) + key(c) + key(d) + key(e)); - } - - /** - * Return the number of bytes used to encode five uint64_t values stored - * at consecutive positions in an array. - */ - static size_t size(const uint64_t* p) { - return size(p[0], p[1], p[2], p[3], p[4]); - } - - /** - * Return the number of bytes used to encode count (<= 4) values. - * If you clip a buffer after these many bytes, you can still decode - * the first "count" values correctly (if the remaining size() - - * partialSize() bytes are filled with garbage). - */ - static size_t partialSize(const type* p, size_t count) { - DCHECK_LE(count, kGroupSize); - size_t s = kHeaderSize + count; - for (; count; --count, ++p) { - s += key(*p); - } - return s; - } - - /** - * Return the number of values from *p that are valid from an encoded - * buffer of size bytes. - */ - static size_t partialCount(const char* p, size_t size) { - uint16_t v = loadUnaligned(p); - size_t s = kHeaderSize; - s += 1 + b0key(v); - if (s > size) return 0; - s += 1 + b1key(v); - if (s > size) return 1; - s += 1 + b2key(v); - if (s > size) return 2; - s += 1 + b3key(v); - if (s > size) return 3; - s += 1 + b4key(v); - if (s > size) return 4; - return 5; - } - - /** - * Given a pointer to the beginning of an GroupVarint64-encoded block, - * return the number of bytes used by the encoding. - */ - static size_t encodedSize(const char* p) { - uint16_t n = loadUnaligned(p); - return (kHeaderSize + kGroupSize + - b0key(n) + b1key(n) + b2key(n) + b3key(n) + b4key(n)); - } - - /** - * Encode five uint64_t values into the buffer pointed-to by p, and return - * the next position in the buffer (that is, one character past the last - * encoded byte). p needs to have at least size()+8 bytes available. - */ - static char* encode(char* p, uint64_t a, uint64_t b, uint64_t c, - uint64_t d, uint64_t e) { - uint8_t b0key = key(a); - uint8_t b1key = key(b); - uint8_t b2key = key(c); - uint8_t b3key = key(d); - uint8_t b4key = key(e); - storeUnaligned( - p, - (b4key << 12) | (b3key << 9) | (b2key << 6) | (b1key << 3) | b0key); - p += 2; - storeUnaligned(p, a); - p += b0key+1; - storeUnaligned(p, b); - p += b1key+1; - storeUnaligned(p, c); - p += b2key+1; - storeUnaligned(p, d); - p += b3key+1; - storeUnaligned(p, e); - p += b4key+1; - return p; - } - - /** - * Encode five uint64_t values from the array pointed-to by src into the - * buffer pointed-to by p, similar to encode(p,a,b,c,d,e) above. - */ - static char* encode(char* p, const uint64_t* src) { - return encode(p, src[0], src[1], src[2], src[3], src[4]); - } - - /** - * Decode five uint64_t values from a buffer, and return the next position - * in the buffer (that is, one character past the last encoded byte). - * The buffer needs to have at least 7 bytes available (they may be read - * but ignored). - */ - static const char* decode(const char* p, uint64_t* a, uint64_t* b, - uint64_t* c, uint64_t* d, uint64_t* e) { - uint16_t k = loadUnaligned(p); - p += 2; - uint8_t k0 = b0key(k); - *a = loadUnaligned(p) & kMask[k0]; - p += k0+1; - uint8_t k1 = b1key(k); - *b = loadUnaligned(p) & kMask[k1]; - p += k1+1; - uint8_t k2 = b2key(k); - *c = loadUnaligned(p) & kMask[k2]; - p += k2+1; - uint8_t k3 = b3key(k); - *d = loadUnaligned(p) & kMask[k3]; - p += k3+1; - uint8_t k4 = b4key(k); - *e = loadUnaligned(p) & kMask[k4]; - p += k4+1; - return p; - } - - /** - * Decode five uint64_t values from a buffer and store them in the array - * pointed-to by dest, similar to decode(p,a,b,c,d,e) above. - */ - static const char* decode(const char* p, uint64_t* dest) { - return decode(p, dest, dest+1, dest+2, dest+3, dest+4); - } - - private: - enum { kHeaderBytes = 2 }; - - static uint8_t key(uint64_t x) { - // __builtin_clzll is undefined for the x==0 case - return 7 - (__builtin_clzll(x|1) / 8); - } - - static uint8_t b0key(uint16_t x) { return x & 7; } - static uint8_t b1key(uint16_t x) { return (x >> 3) & 7; } - static uint8_t b2key(uint16_t x) { return (x >> 6) & 7; } - static uint8_t b3key(uint16_t x) { return (x >> 9) & 7; } - static uint8_t b4key(uint16_t x) { return (x >> 12) & 7; } - - static const uint64_t kMask[]; -}; - -typedef GroupVarint GroupVarint32; -typedef GroupVarint GroupVarint64; - -/** - * Simplify use of GroupVarint* for the case where data is available one - * entry at a time (instead of one group at a time). Handles buffering - * and an incomplete last chunk. - * - * Output is a function object that accepts character ranges: - * out(StringPiece) appends the given character range to the output. - */ -template -class GroupVarintEncoder { - public: - typedef GroupVarint Base; - typedef T type; - - explicit GroupVarintEncoder(Output out) - : out_(out), - count_(0) { - } - - ~GroupVarintEncoder() { - finish(); - } - - /** - * Add a value to the encoder. - */ - void add(type val) { - buf_[count_++] = val; - if (count_ == Base::kGroupSize) { - char* p = Base::encode(tmp_, buf_); - out_(StringPiece(tmp_, p)); - count_ = 0; - } - } - - /** - * Finish encoding, flushing any buffered values if necessary. - * After finish(), the encoder is immediately ready to encode more data - * to the same output. - */ - void finish() { - if (count_) { - // This is not strictly necessary, but it makes testing easy; - // uninitialized bytes are guaranteed to be recorded as taking one byte - // (not more). - for (size_t i = count_; i < Base::kGroupSize; i++) { - buf_[i] = 0; - } - Base::encode(tmp_, buf_); - out_(StringPiece(tmp_, Base::partialSize(buf_, count_))); - count_ = 0; - } - } - - /** - * Return the appender that was used. - */ - Output& output() { - return out_; - } - const Output& output() const { - return out_; - } - - /** - * Reset the encoder, disregarding any state (except what was already - * flushed to the output, of course). - */ - void clear() { - count_ = 0; - } - - private: - Output out_; - char tmp_[Base::kMaxSize]; - type buf_[Base::kGroupSize]; - size_t count_; -}; - -/** - * Simplify use of GroupVarint* for the case where the last group in the - * input may be incomplete (but the exact size of the input is known). - * Allows for extracting values one at a time. - */ -template -class GroupVarintDecoder { - public: - typedef GroupVarint Base; - typedef T type; - - GroupVarintDecoder() { } - - explicit GroupVarintDecoder(StringPiece data, - size_t maxCount = (size_t)-1) - : rrest_(data.end()), - p_(data.data()), - end_(data.end()), - pos_(0), - count_(0), - remaining_(maxCount) { - } - - void reset(StringPiece data, size_t maxCount=(size_t)-1) { - rrest_ = data.end(); - p_ = data.data(); - end_ = data.end(); - pos_ = 0; - count_ = 0; - remaining_ = maxCount; - } - - /** - * Read and return the next value. - */ - bool next(type* val) { - if (pos_ == count_) { - // refill - size_t rem = end_ - p_; - if (rem == 0 || remaining_ == 0) { - return false; - } - // next() attempts to read one full group at a time, and so we must have - // at least enough bytes readable after its end to handle the case if the - // last group is full. - // - // The best way to ensure this is to ensure that data has at least - // Base::kMaxSize - 1 bytes readable *after* the end, otherwise we'll copy - // into a temporary buffer. - if (rem < Base::kMaxSize) { - memcpy(tmp_, p_, rem); - p_ = tmp_; - end_ = p_ + rem; - } - pos_ = 0; - const char* n = Base::decode(p_, buf_); - if (n <= end_) { - // Full group could be decoded - if (remaining_ >= Base::kGroupSize) { - remaining_ -= Base::kGroupSize; - count_ = Base::kGroupSize; - p_ = n; - } else { - count_ = remaining_; - remaining_ = 0; - p_ += Base::partialSize(buf_, count_); - } - } else { - // Can't decode a full group - count_ = Base::partialCount(p_, end_ - p_); - if (remaining_ >= count_) { - remaining_ -= count_; - p_ = end_; - } else { - count_ = remaining_; - remaining_ = 0; - p_ += Base::partialSize(buf_, count_); - } - if (count_ == 0) { - return false; - } - } - } - *val = buf_[pos_++]; - return true; - } - - StringPiece rest() const { - // This is only valid after next() returned false - CHECK(pos_ == count_ && (p_ == end_ || remaining_ == 0)); - // p_ may point to the internal buffer (tmp_), but we want - // to return subpiece of the original data - size_t size = end_ - p_; - return StringPiece(rrest_ - size, rrest_); - } - - private: - const char* rrest_; - const char* p_; - const char* end_; - char tmp_[Base::kMaxSize]; - type buf_[Base::kGroupSize]; - size_t pos_; - size_t count_; - size_t remaining_; -}; - -typedef GroupVarintDecoder GroupVarint32Decoder; -typedef GroupVarintDecoder GroupVarint64Decoder; - -} // namespace folly - -#endif /* defined(__x86_64__) || defined(__i386__) */ -#endif /* FOLLY_GROUPVARINT_H_ */ - diff --git a/hphp/third_party/folly/folly/Hash.h b/hphp/third_party/folly/folly/Hash.h deleted file mode 100644 index cbd07b56b..000000000 --- a/hphp/third_party/folly/folly/Hash.h +++ /dev/null @@ -1,399 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_BASE_HASH_H_ -#define FOLLY_BASE_HASH_H_ - -#include -#include -#include -#include -#include - -#include "folly/SpookyHashV1.h" -#include "folly/SpookyHashV2.h" - -/* - * Various hashing functions. - */ - -namespace folly { namespace hash { - -// This is a general-purpose way to create a single hash from multiple -// hashable objects. hash_combine_generic takes a class Hasher implementing -// hash; hash_combine uses a default hasher StdHasher that uses std::hash. -// hash_combine_generic hashes each argument and combines those hashes in -// an order-dependent way to yield a new hash. - - -// This is the Hash128to64 function from Google's cityhash (available -// under the MIT License). We use it to reduce multiple 64 bit hashes -// into a single hash. -inline size_t hash_128_to_64(const size_t upper, const size_t lower) { - // Murmur-inspired hashing. - const size_t kMul = 0x9ddfea08eb382d69ULL; - size_t a = (lower ^ upper) * kMul; - a ^= (a >> 47); - size_t b = (upper ^ a) * kMul; - b ^= (b >> 47); - b *= kMul; - return b; -} - -// Never used, but gcc demands it. -template -inline size_t hash_combine_generic() { - return 0; -} - -template -size_t hash_combine_generic(const T& t, const Ts&... ts) { - size_t seed = Hasher::hash(t); - if (sizeof...(ts) == 0) { - return seed; - } - size_t remainder = hash_combine_generic(ts...); - return hash_128_to_64(seed, remainder); -} - -// Simply uses std::hash to hash. Note that std::hash is not guaranteed -// to be a very good hash function; provided std::hash doesn't collide on -// the individual inputs, you are fine, but that won't be true for, say, -// strings or pairs -class StdHasher { - public: - template - static size_t hash(const T& t) { - return std::hash()(t); - } -}; - -template -size_t hash_combine(const T& t, const Ts&... ts) { - return hash_combine_generic(t, ts...); -} - -////////////////////////////////////////////////////////////////////// - -/* - * Thomas Wang 64 bit mix hash function - */ - -inline uint64_t twang_mix64(uint64_t key) { - key = (~key) + (key << 21); // key *= (1 << 21) - 1; key -= 1; - key = key ^ (key >> 24); - key = key + (key << 3) + (key << 8); // key *= 1 + (1 << 3) + (1 << 8) - key = key ^ (key >> 14); - key = key + (key << 2) + (key << 4); // key *= 1 + (1 << 2) + (1 << 4) - key = key ^ (key >> 28); - key = key + (key << 31); // key *= 1 + (1 << 31) - return key; -} - -/* - * Inverse of twang_mix64 - * - * Note that twang_unmix64 is significantly slower than twang_mix64. - */ - -inline uint64_t twang_unmix64(uint64_t key) { - // See the comments in jenkins_rev_unmix32 for an explanation as to how this - // was generated - key *= 4611686016279904257U; - key ^= (key >> 28) ^ (key >> 56); - key *= 14933078535860113213U; - key ^= (key >> 14) ^ (key >> 28) ^ (key >> 42) ^ (key >> 56); - key *= 15244667743933553977U; - key ^= (key >> 24) ^ (key >> 48); - key = (key + 1) * 9223367638806167551U; - return key; -} - -/* - * Thomas Wang downscaling hash function - */ - -inline uint32_t twang_32from64(uint64_t key) { - key = (~key) + (key << 18); - key = key ^ (key >> 31); - key = key * 21; - key = key ^ (key >> 11); - key = key + (key << 6); - key = key ^ (key >> 22); - return (uint32_t) key; -} - -/* - * Robert Jenkins' reversible 32 bit mix hash function - */ - -inline uint32_t jenkins_rev_mix32(uint32_t key) { - key += (key << 12); // key *= (1 + (1 << 12)) - key ^= (key >> 22); - key += (key << 4); // key *= (1 + (1 << 4)) - key ^= (key >> 9); - key += (key << 10); // key *= (1 + (1 << 10)) - key ^= (key >> 2); - // key *= (1 + (1 << 7)) * (1 + (1 << 12)) - key += (key << 7); - key += (key << 12); - return key; -} - -/* - * Inverse of jenkins_rev_mix32 - * - * Note that jenkinks_rev_unmix32 is significantly slower than - * jenkins_rev_mix32. - */ - -inline uint32_t jenkins_rev_unmix32(uint32_t key) { - // These are the modular multiplicative inverses (in Z_2^32) of the - // multiplication factors in jenkins_rev_mix32, in reverse order. They were - // computed using the Extended Euclidean algorithm, see - // http://en.wikipedia.org/wiki/Modular_multiplicative_inverse - key *= 2364026753U; - - // The inverse of a ^= (a >> n) is - // b = a - // for (int i = n; i < 32; i += n) { - // b ^= (a >> i); - // } - key ^= - (key >> 2) ^ (key >> 4) ^ (key >> 6) ^ (key >> 8) ^ - (key >> 10) ^ (key >> 12) ^ (key >> 14) ^ (key >> 16) ^ - (key >> 18) ^ (key >> 20) ^ (key >> 22) ^ (key >> 24) ^ - (key >> 26) ^ (key >> 28) ^ (key >> 30); - key *= 3222273025U; - key ^= (key >> 9) ^ (key >> 18) ^ (key >> 27); - key *= 4042322161U; - key ^= (key >> 22); - key *= 16773121U; - return key; -} - -/* - * Fowler / Noll / Vo (FNV) Hash - * http://www.isthe.com/chongo/tech/comp/fnv/ - */ - -const uint32_t FNV_32_HASH_START = 216613626UL; -const uint64_t FNV_64_HASH_START = 14695981039346656037ULL; - -inline uint32_t fnv32(const char* s, - uint32_t hash = FNV_32_HASH_START) { - for (; *s; ++s) { - hash += (hash << 1) + (hash << 4) + (hash << 7) + - (hash << 8) + (hash << 24); - hash ^= *s; - } - return hash; -} - -inline uint32_t fnv32_buf(const void* buf, - int n, - uint32_t hash = FNV_32_HASH_START) { - const char* char_buf = reinterpret_cast(buf); - - for (int i = 0; i < n; ++i) { - hash += (hash << 1) + (hash << 4) + (hash << 7) + - (hash << 8) + (hash << 24); - hash ^= char_buf[i]; - } - - return hash; -} - -inline uint32_t fnv32(const std::string& str, - uint64_t hash = FNV_32_HASH_START) { - return fnv32_buf(str.data(), str.size(), hash); -} - -inline uint64_t fnv64(const char* s, - uint64_t hash = FNV_64_HASH_START) { - for (; *s; ++s) { - hash += (hash << 1) + (hash << 4) + (hash << 5) + (hash << 7) + - (hash << 8) + (hash << 40); - hash ^= *s; - } - return hash; -} - -inline uint64_t fnv64_buf(const void* buf, - int n, - uint64_t hash = FNV_64_HASH_START) { - const char* char_buf = reinterpret_cast(buf); - - for (int i = 0; i < n; ++i) { - hash += (hash << 1) + (hash << 4) + (hash << 5) + (hash << 7) + - (hash << 8) + (hash << 40); - hash ^= char_buf[i]; - } - return hash; -} - -inline uint64_t fnv64(const std::string& str, - uint64_t hash = FNV_64_HASH_START) { - return fnv64_buf(str.data(), str.size(), hash); -} - -/* - * Paul Hsieh: http://www.azillionmonkeys.com/qed/hash.html - */ - -#define get16bits(d) (*((const uint16_t*) (d))) - -inline uint32_t hsieh_hash32_buf(const void* buf, int len) { - const char* s = reinterpret_cast(buf); - uint32_t hash = len; - uint32_t tmp; - int rem; - - if (len <= 0 || buf == 0) { - return 0; - } - - rem = len & 3; - len >>= 2; - - /* Main loop */ - for (;len > 0; len--) { - hash += get16bits (s); - tmp = (get16bits (s+2) << 11) ^ hash; - hash = (hash << 16) ^ tmp; - s += 2*sizeof (uint16_t); - hash += hash >> 11; - } - - /* Handle end cases */ - switch (rem) { - case 3: - hash += get16bits(s); - hash ^= hash << 16; - hash ^= s[sizeof (uint16_t)] << 18; - hash += hash >> 11; - break; - case 2: - hash += get16bits(s); - hash ^= hash << 11; - hash += hash >> 17; - break; - case 1: - hash += *s; - hash ^= hash << 10; - hash += hash >> 1; - } - - /* Force "avalanching" of final 127 bits */ - hash ^= hash << 3; - hash += hash >> 5; - hash ^= hash << 4; - hash += hash >> 17; - hash ^= hash << 25; - hash += hash >> 6; - - return hash; -}; - -#undef get16bits - -inline uint32_t hsieh_hash32(const char* s) { - return hsieh_hash32_buf(s, std::strlen(s)); -} - -inline uint32_t hsieh_hash32_str(const std::string& str) { - return hsieh_hash32_buf(str.data(), str.size()); -} - -////////////////////////////////////////////////////////////////////// - -} // namespace hash - -template -struct hasher; - -template<> struct hasher { - size_t operator()(int32_t key) const { - return hash::jenkins_rev_mix32(uint32_t(key)); - } -}; - -template<> struct hasher { - size_t operator()(uint32_t key) const { - return hash::jenkins_rev_mix32(key); - } -}; - -template<> struct hasher { - size_t operator()(int64_t key) const { - return hash::twang_mix64(uint64_t(key)); - } -}; - -template<> struct hasher { - size_t operator()(uint64_t key) const { - return hash::twang_mix64(key); - } -}; - -// recursion -template -struct TupleHasher { - size_t operator()(std::tuple const& key) const { - return hash::hash_combine( - TupleHasher()(key), - std::get(key)); - } -}; - -// base -template -struct TupleHasher<0, Ts...> { - size_t operator()(std::tuple const& key) const { - // we could do std::hash here directly, but hash_combine hides all the - // ugly templating implicitly - return hash::hash_combine(std::get<0>(key)); - } -}; - -} // namespace folly - -// Custom hash functions. -namespace std { - // Hash function for pairs. Requires default hash functions for both - // items in the pair. - template - class hash > { - public: - size_t operator()(const std::pair& x) const { - return folly::hash::hash_combine(x.first, x.second); - } - }; - - // Hash function for tuples. Requires default hash functions for all types. - template - struct hash> { - size_t operator()(std::tuple const& key) const { - folly::TupleHasher< - std::tuple_size>::value - 1, // start index - Ts...> hasher; - - return hasher(key); - } - }; -} // namespace std - -#endif diff --git a/hphp/third_party/folly/folly/Histogram-inl.h b/hphp/third_party/folly/folly/Histogram-inl.h deleted file mode 100644 index 42ff29899..000000000 --- a/hphp/third_party/folly/folly/Histogram-inl.h +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_HISTOGRAM_INL_H_ -#define FOLLY_HISTOGRAM_INL_H_ - -#include "folly/Conv.h" - -#include - -namespace folly { - -namespace detail { - -template -HistogramBuckets::HistogramBuckets(ValueType bucketSize, - ValueType min, - ValueType max, - const BucketType& defaultBucket) - : bucketSize_(bucketSize), - min_(min), - max_(max) { - CHECK_GT(bucketSize_, ValueType(0)); - CHECK_LT(min_, max_); - - unsigned int numBuckets = (max - min) / bucketSize; - // Round up if the bucket size does not fit evenly - if (numBuckets * bucketSize < max - min) { - ++numBuckets; - } - // Add 2 for the extra 'below min' and 'above max' buckets - numBuckets += 2; - buckets_.assign(numBuckets, defaultBucket); -} - -template -unsigned int HistogramBuckets::getBucketIdx( - ValueType value) const { - if (value < min_) { - return 0; - } else if (value >= max_) { - return buckets_.size() - 1; - } else { - // the 1 is the below_min bucket - return ((value - min_) / bucketSize_) + 1; - } -} - -template -template -unsigned int HistogramBuckets::getPercentileBucketIdx( - double pct, - CountFn countFromBucket, - double* lowPct, double* highPct) const { - CHECK_GE(pct, 0.0); - CHECK_LE(pct, 1.0); - - unsigned int numBuckets = buckets_.size(); - - // Compute the counts in each bucket - std::vector counts(numBuckets); - uint64_t totalCount = 0; - for (unsigned int n = 0; n < numBuckets; ++n) { - uint64_t bucketCount = - countFromBucket(const_cast(buckets_[n])); - counts[n] = bucketCount; - totalCount += bucketCount; - } - - // If there are no elements, just return the lowest bucket. - // Note that we return bucket 1, which is the first bucket in the - // histogram range; bucket 0 is for all values below min_. - if (totalCount == 0) { - // Set lowPct and highPct both to 0. - // getPercentileEstimate() will recognize this to mean that the histogram - // is empty. - if (lowPct) { - *lowPct = 0.0; - } - if (highPct) { - *highPct = 0.0; - } - return 1; - } - - // Loop through all the buckets, keeping track of each bucket's - // percentile range: [0,10], [10,17], [17,45], etc. When we find a range - // that includes our desired percentile, we return that bucket index. - double prevPct = 0.0; - double curPct = 0.0; - uint64_t curCount = 0; - unsigned int idx; - for (idx = 0; idx < numBuckets; ++idx) { - if (counts[idx] == 0) { - // skip empty buckets - continue; - } - - prevPct = curPct; - curCount += counts[idx]; - curPct = static_cast(curCount) / totalCount; - if (pct <= curPct) { - // This is the desired bucket - break; - } - } - - if (lowPct) { - *lowPct = prevPct; - } - if (highPct) { - *highPct = curPct; - } - return idx; -} - -template -template -T HistogramBuckets::getPercentileEstimate( - double pct, - CountFn countFromBucket, - AvgFn avgFromBucket) const { - - // Find the bucket where this percentile falls - double lowPct; - double highPct; - unsigned int bucketIdx = getPercentileBucketIdx(pct, countFromBucket, - &lowPct, &highPct); - if (lowPct == 0.0 && highPct == 0.0) { - // Invalid range -- the buckets must all be empty - // Return the default value for ValueType. - return ValueType(); - } - if (lowPct == highPct) { - // Unlikely to have exact equality, - // but just return the bucket average in this case. - // We handle this here to avoid division by 0 below. - return avgFromBucket(buckets_[bucketIdx]); - } - - CHECK_GE(pct, lowPct); - CHECK_LE(pct, highPct); - CHECK_LT(lowPct, highPct); - - // Compute information about this bucket - ValueType avg = avgFromBucket(buckets_[bucketIdx]); - ValueType low; - ValueType high; - if (bucketIdx == 0) { - if (avg > min_) { - // This normally shouldn't happen. This bucket is only supposed to track - // values less than min_. Most likely this means that integer overflow - // occurred, and the code in avgFromBucket() returned a huge value - // instead of a small one. Just return the minimum possible value for - // now. - // - // (Note that if the counter keeps being decremented, eventually it will - // wrap and become small enough that we won't detect this any more, and - // we will return bogus information.) - LOG(ERROR) << "invalid average value in histogram minimum bucket: " << - avg << " > " << min_ << ": possible integer overflow?"; - return getBucketMin(bucketIdx); - } - // For the below-min bucket, just assume the lowest value ever seen is - // twice as far away from min_ as avg. - high = min_; - low = high - (2 * (high - avg)); - // Adjust low in case it wrapped - if (low > avg) { - low = std::numeric_limits::min(); - } - } else if (bucketIdx == buckets_.size() - 1) { - if (avg < max_) { - // Most likely this means integer overflow occurred. See the comments - // above in the minimum case. - LOG(ERROR) << "invalid average value in histogram maximum bucket: " << - avg << " < " << max_ << ": possible integer overflow?"; - return getBucketMax(bucketIdx); - } - // Similarly for the above-max bucket, assume the highest value ever seen - // is twice as far away from max_ as avg. - low = max_; - high = low + (2 * (avg - low)); - // Adjust high in case it wrapped - if (high < avg) { - high = std::numeric_limits::max(); - } - } else { - low = getBucketMin(bucketIdx); - high = getBucketMax(bucketIdx); - if (avg < low || avg > high) { - // Most likely this means an integer overflow occurred. - // See the comments above. Return the midpoint between low and high - // as a best guess, since avg is meaningless. - LOG(ERROR) << "invalid average value in histogram bucket: " << - avg << " not in range [" << low << ", " << high << - "]: possible integer overflow?"; - return (low + high) / 2; - } - } - - // Since we know the average value in this bucket, we can do slightly better - // than just assuming the data points in this bucket are uniformly - // distributed between low and high. - // - // Assume that the median value in this bucket is the same as the average - // value. - double medianPct = (lowPct + highPct) / 2.0; - if (pct < medianPct) { - // Assume that the data points lower than the median of this bucket - // are uniformly distributed between low and avg - double pctThroughSection = (pct - lowPct) / (medianPct - lowPct); - return low + ((avg - low) * pctThroughSection); - } else { - // Assume that the data points greater than the median of this bucket - // are uniformly distributed between avg and high - double pctThroughSection = (pct - medianPct) / (highPct - medianPct); - return avg + ((high - avg) * pctThroughSection); - } -} - -} // detail - - -template -std::string Histogram::debugString() const { - std::string ret = folly::to( - "num buckets: ", buckets_.getNumBuckets(), - ", bucketSize: ", buckets_.getBucketSize(), - ", min: ", buckets_.getMin(), ", max: ", buckets_.getMax(), "\n"); - - for (unsigned int i = 0; i < buckets_.getNumBuckets(); ++i) { - folly::toAppend(" ", buckets_.getBucketMin(i), ": ", - buckets_.getByIndex(i).count, "\n", - &ret); - } - - return ret; -} - -template -void Histogram::toTSV(std::ostream& out, bool skipEmptyBuckets) const { - for (unsigned int i = 0; i < buckets_.getNumBuckets(); ++i) { - // Do not output empty buckets in order to reduce data file size. - if (skipEmptyBuckets && getBucketByIndex(i).count == 0) { - continue; - } - const auto& bucket = getBucketByIndex(i); - out << getBucketMin(i) << '\t' << getBucketMax(i) << '\t' - << bucket.count << '\t' << bucket.sum << '\n'; - } -} - -} // folly - -#endif // FOLLY_HISTOGRAM_INL_H_ diff --git a/hphp/third_party/folly/folly/Histogram.h b/hphp/third_party/folly/folly/Histogram.h deleted file mode 100644 index 3eb34f772..000000000 --- a/hphp/third_party/folly/folly/Histogram.h +++ /dev/null @@ -1,408 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_HISTOGRAM_H_ -#define FOLLY_HISTOGRAM_H_ - -#include -#include -#include -#include -#include -#include - -#include "folly/detail/Stats.h" - -namespace folly { - -namespace detail { - -/* - * A helper class to manage a set of histogram buckets. - */ -template -class HistogramBuckets { - public: - typedef T ValueType; - typedef BucketT BucketType; - - /* - * Create a set of histogram buckets. - * - * One bucket will be created for each bucketSize interval of values within - * the specified range. Additionally, one bucket will be created to track - * all values that fall below the specified minimum, and one bucket will be - * created for all values above the specified maximum. - * - * If (max - min) is not a multiple of bucketSize, the last bucket will cover - * a smaller range of values than the other buckets. - * - * (max - min) must be larger than or equal to bucketSize. - */ - HistogramBuckets(ValueType bucketSize, ValueType min, ValueType max, - const BucketType& defaultBucket); - - /* Returns the bucket size of each bucket in the histogram. */ - ValueType getBucketSize() const { - return bucketSize_; - } - - /* Returns the min value at which bucketing begins. */ - ValueType getMin() const { - return min_; - } - - /* Returns the max value at which bucketing ends. */ - ValueType getMax() const { - return max_; - } - - /* - * Returns the number of buckets. - * - * This includes the total number of buckets for the [min, max) range, - * plus 2 extra buckets, one for handling values less than min, and one for - * values greater than max. - */ - unsigned int getNumBuckets() const { - return buckets_.size(); - } - - /* Returns the bucket index into which the given value would fall. */ - unsigned int getBucketIdx(ValueType value) const; - - /* Returns the bucket for the specified value */ - BucketType& getByValue(ValueType value) { - return buckets_[getBucketIdx(value)]; - } - - /* Returns the bucket for the specified value */ - const BucketType& getByValue(ValueType value) const { - return buckets_[getBucketIdx(value)]; - } - - /* - * Returns the bucket at the specified index. - * - * Note that index 0 is the bucket for all values less than the specified - * minimum. Index 1 is the first bucket in the specified bucket range. - */ - BucketType& getByIndex(unsigned int idx) { - return buckets_[idx]; - } - - /* Returns the bucket at the specified index. */ - const BucketType& getByIndex(unsigned int idx) const { - return buckets_[idx]; - } - - /* - * Returns the minimum threshold for the bucket at the given index. - * - * The bucket at the specified index will store values in the range - * [bucketMin, bucketMin + bucketSize), or [bucketMin, max), if the overall - * max is smaller than bucketMin + bucketSize. - */ - ValueType getBucketMin(unsigned int idx) const { - if (idx == 0) { - return std::numeric_limits::min(); - } - if (idx == buckets_.size() - 1) { - return max_; - } - - return min_ + ((idx - 1) * bucketSize_); - } - - /* - * Returns the maximum threshold for the bucket at the given index. - * - * The bucket at the specified index will store values in the range - * [bucketMin, bucketMin + bucketSize), or [bucketMin, max), if the overall - * max is smaller than bucketMin + bucketSize. - */ - ValueType getBucketMax(unsigned int idx) const { - if (idx == buckets_.size() - 1) { - return std::numeric_limits::max(); - } - - return min_ + (idx * bucketSize_); - } - - /** - * Determine which bucket the specified percentile falls into. - * - * Looks for the bucket that contains the Nth percentile data point. - * - * @param pct The desired percentile to find, as a value from 0.0 to 1.0. - * @param countFn A function that takes a const BucketType&, and returns the - * number of values in that bucket. - * @param lowPct The lowest percentile stored in the selected bucket will be - * returned via this parameter. - * @param highPct The highest percentile stored in the selected bucket will - * be returned via this parameter. - * - * @return Returns the index of the bucket that contains the Nth percentile - * data point. - */ - template - unsigned int getPercentileBucketIdx(double pct, - CountFn countFromBucket, - double* lowPct = NULL, - double* highPct = NULL) const; - - /** - * Estimate the value at the specified percentile. - * - * @param pct The desired percentile to find, as a value from 0.0 to 1.0. - * @param countFn A function that takes a const BucketType&, and returns the - * number of values in that bucket. - * @param avgFn A function that takes a const BucketType&, and returns the - * average of all the values in that bucket. - * - * @return Returns an estimate for N, where N is the number where exactly pct - * percentage of the data points in the histogram are less than N. - */ - template - ValueType getPercentileEstimate(double pct, - CountFn countFromBucket, - AvgFn avgFromBucket) const; - - /* - * Iterator access to the buckets. - * - * Note that the first bucket is for all values less than min, and the last - * bucket is for all values greater than max. The buckets tracking values in - * the [min, max) actually start at the second bucket. - */ - typename std::vector::const_iterator begin() const { - return buckets_.begin(); - } - typename std::vector::iterator begin() { - return buckets_.begin(); - } - typename std::vector::const_iterator end() const { - return buckets_.end(); - } - typename std::vector::iterator end() { - return buckets_.end(); - } - - private: - const ValueType bucketSize_; - const ValueType min_; - const ValueType max_; - std::vector buckets_; -}; - -} // detail - - -/* - * A basic histogram class. - * - * Groups data points into equally-sized buckets, and stores the overall sum of - * the data points in each bucket, as well as the number of data points in the - * bucket. - * - * The caller must specify the minimum and maximum data points to expect ahead - * of time, as well as the bucket width. - */ -template -class Histogram { - public: - typedef T ValueType; - typedef detail::Bucket Bucket; - - Histogram(ValueType bucketSize, ValueType min, ValueType max) - : buckets_(bucketSize, min, max, Bucket()) {} - - /* Add a data point to the histogram */ - void addValue(ValueType value) { - Bucket& bucket = buckets_.getByValue(value); - // TODO: It would be nice to handle overflow here. - bucket.sum += value; - bucket.count += 1; - } - - /* - * Remove a data point to the histogram - * - * Note that this method does not actually verify that this exact data point - * had previously been added to the histogram; it merely subtracts the - * requested value from the appropriate bucket's sum. - */ - void removeValue(ValueType value) { - Bucket& bucket = buckets_.getByValue(value); - // TODO: It would be nice to handle overflow here. - bucket.sum -= value; - bucket.count -= 1; - } - - /* Remove all data points from the histogram */ - void clear() { - for (int i = 0; i < buckets_.getNumBuckets(); i++) { - buckets_.getByIndex(i).clear(); - } - } - - /* Merge two histogram data together */ - void merge(Histogram &hist) { - // the two histogram bucket definitions must match to support - // a merge. - if (getBucketSize() != hist.getBucketSize() || - getMin() != hist.getMin() || - getMax() != hist.getMax() || - getNumBuckets() != hist.getNumBuckets() ) { - throw std::invalid_argument("Cannot merge from input histogram."); - } - - for (int i = 0; i < buckets_.getNumBuckets(); i++) { - buckets_.getByIndex(i) += hist.buckets_.getByIndex(i); - } - } - - /* Copy bucket values from another histogram */ - void copy(Histogram &hist) { - // the two histogram bucket definition must match - if (getBucketSize() != hist.getBucketSize() || - getMin() != hist.getMin() || - getMax() != hist.getMax() || - getNumBuckets() != hist.getNumBuckets() ) { - throw std::invalid_argument("Cannot copy from input histogram."); - } - - for (int i = 0; i < buckets_.getNumBuckets(); i++) { - buckets_.getByIndex(i) = hist.buckets_.getByIndex(i); - } - } - - /* Returns the bucket size of each bucket in the histogram. */ - ValueType getBucketSize() const { - return buckets_.getBucketSize(); - } - /* Returns the min value at which bucketing begins. */ - ValueType getMin() const { - return buckets_.getMin(); - } - /* Returns the max value at which bucketing ends. */ - ValueType getMax() const { - return buckets_.getMax(); - } - /* Returns the number of buckets */ - unsigned int getNumBuckets() const { - return buckets_.getNumBuckets(); - } - - /* Returns the specified bucket (for reading only!) */ - const Bucket& getBucketByIndex(int idx) const { - return buckets_.getByIndex(idx); - } - - /* - * Returns the minimum threshold for the bucket at the given index. - * - * The bucket at the specified index will store values in the range - * [bucketMin, bucketMin + bucketSize), or [bucketMin, max), if the overall - * max is smaller than bucketMin + bucketSize. - */ - ValueType getBucketMin(unsigned int idx) const { - return buckets_.getBucketMin(idx); - } - - /* - * Returns the maximum threshold for the bucket at the given index. - * - * The bucket at the specified index will store values in the range - * [bucketMin, bucketMin + bucketSize), or [bucketMin, max), if the overall - * max is smaller than bucketMin + bucketSize. - */ - ValueType getBucketMax(unsigned int idx) const { - return buckets_.getBucketMax(idx); - } - - /* - * Get the bucket that the specified percentile falls into - * - * The lowest and highest percentile data points in returned bucket will be - * returned in the lowPct and highPct arguments, if they are non-NULL. - */ - unsigned int getPercentileBucketIdx(double pct, - double* lowPct = NULL, - double* highPct = NULL) const { - // We unfortunately can't use lambdas here yet; - // Some users of this code are still built with gcc-4.4. - CountFromBucket countFn; - return buckets_.getPercentileBucketIdx(pct, countFn, lowPct, highPct); - } - - /** - * Estimate the value at the specified percentile. - * - * @param pct The desired percentile to find, as a value from 0.0 to 1.0. - * - * @return Returns an estimate for N, where N is the number where exactly pct - * percentage of the data points in the histogram are less than N. - */ - ValueType getPercentileEstimate(double pct) const { - CountFromBucket countFn; - AvgFromBucket avgFn; - return buckets_.getPercentileEstimate(pct, countFn, avgFn); - } - - /* - * Get a human-readable string describing the histogram contents - */ - std::string debugString() const; - - /* - * Write the histogram contents in tab-separated values (TSV) format. - * Format is "min max count sum". - */ - void toTSV(std::ostream& out, bool skipEmptyBuckets = true) const; - - private: - struct CountFromBucket { - uint64_t operator()(const Bucket& bucket) const { - return bucket.count; - } - }; - struct AvgFromBucket { - ValueType operator()(const Bucket& bucket) const { - if (bucket.count == 0) { - return ValueType(0); - } - // Cast bucket.count to a signed integer type. This ensures that we - // perform division properly here: If bucket.sum is a signed integer - // type but we divide by an unsigned number, unsigned division will be - // performed and bucket.sum will be converted to unsigned first. - // If bucket.sum is unsigned, the code will still do unsigned division - // correctly. - // - // The only downside is if bucket.count is large enough to be negative - // when treated as signed. That should be extremely unlikely, though. - return bucket.sum / static_cast(bucket.count); - } - }; - - detail::HistogramBuckets buckets_; -}; - -} // folly - -#include "folly/Histogram-inl.h" - -#endif // FOLLY_HISTOGRAM_H_ diff --git a/hphp/third_party/folly/folly/IntrusiveList.h b/hphp/third_party/folly/folly/IntrusiveList.h deleted file mode 100644 index bc687297c..000000000 --- a/hphp/third_party/folly/folly/IntrusiveList.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_INTRUSIVELIST_H_ -#define FOLLY_INTRUSIVELIST_H_ - -/* - * This file contains convenience typedefs that make boost::intrusive::list - * easier to use. - */ - -#include - -namespace folly { - -/** - * An auto-unlink intrusive list hook. - */ -typedef boost::intrusive::list_member_hook< - boost::intrusive::link_mode > - IntrusiveListHook; - -/** - * An intrusive list. - * - * An IntrusiveList always uses an auto-unlink hook. - * Beware that IntrusiveList::size() is an O(n) operation, since it has to walk - * the entire list. - * - * Example usage: - * - * class Foo { - * // Note that the listHook member variable needs to be visible - * // to the code that defines the IntrusiveList instantiation. - * // The list hook can be made public, or you can make the other class a - * // friend. - * IntrusiveListHook listHook; - * }; - * - * typedef IntrusiveList FooList; - * - * Foo *foo = new Foo(); - * FooList myList; - * myList.push_back(*foo); - * - * Note that each IntrusiveListHook can only be part of a single list at any - * given time. If you need the same object to be stored in two lists at once, - * you need to use two different IntrusiveListHook member variables. - * - * The elements stored in the list must contain an IntrusiveListHook member - * variable. - * - * TODO: This should really be a template alias. However, gcc doesn't support - * template aliases yet. A subclass is a reasonable workaround for now. This - * subclass only supports the default constructor, but we could add other - * constructors if necessary. - */ -template -class IntrusiveList : public boost::intrusive::list< - T, - boost::intrusive::member_hook, - boost::intrusive::constant_time_size > { -}; - -/** - * A safe-link intrusive list hook. - */ -typedef boost::intrusive::list_member_hook< - boost::intrusive::link_mode > - SafeIntrusiveListHook; - -/** - * An intrusive list with const-time size() method. - * - * A CountedIntrusiveList always uses a safe-link hook. - * CountedIntrusiveList::size() is an O(1) operation. Users of this type - * of lists need to remove a member from a list by calling one of the - * methods on the list (e.g., erase(), pop_front(), etc.), rather than - * calling unlink on the member's list hook. Given references to a - * list and a member, a constant-time removal operation can be - * accomplished by list.erase(list.iterator_to(member)). Also, when a - * member is destroyed, it is NOT automatically removed from the list. - * - * Example usage: - * - * class Foo { - * // Note that the listHook member variable needs to be visible - * // to the code that defines the CountedIntrusiveList instantiation. - * // The list hook can be made public, or you can make the other class a - * // friend. - * SafeIntrusiveListHook listHook; - * }; - * - * typedef CountedIntrusiveList FooList; - * - * Foo *foo = new Foo(); - * FooList myList; - * myList.push_back(*foo); - * myList.pop_front(); - * - * Note that each SafeIntrusiveListHook can only be part of a single list at any - * given time. If you need the same object to be stored in two lists at once, - * you need to use two different SafeIntrusiveListHook member variables. - * - * The elements stored in the list must contain an SafeIntrusiveListHook member - * variable. - * - * TODO: This should really be a template alias. However, gcc doesn't support - * template aliases yet. A subclass is a reasonable workaround for now. This - * subclass only supports the default constructor, but we could add other - * constructors if necessary. - */ -template -class CountedIntrusiveList : public boost::intrusive::list< - T, - boost::intrusive::member_hook, - boost::intrusive::constant_time_size > { -}; - -} // folly - -#endif // FOLLY_INTRUSIVELIST_H_ diff --git a/hphp/third_party/folly/folly/LICENSE b/hphp/third_party/folly/folly/LICENSE deleted file mode 100644 index f433b1a53..000000000 --- a/hphp/third_party/folly/folly/LICENSE +++ /dev/null @@ -1,177 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS diff --git a/hphp/third_party/folly/folly/Lazy.h b/hphp/third_party/folly/folly/Lazy.h deleted file mode 100644 index fd3e51036..000000000 --- a/hphp/third_party/folly/folly/Lazy.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2013 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. - */ -#ifndef FOLLY_LAZY_H_ -#define FOLLY_LAZY_H_ - -#include -#include - -#include "folly/Optional.h" - -namespace folly { - -////////////////////////////////////////////////////////////////////// - -/* - * Lazy -- for delayed initialization of a value. The value's - * initialization will be computed on demand at its first use, but - * will not be recomputed if its value is requested again. The value - * may still be mutated after its initialization if the lazy is not - * declared const. - * - * The value is created using folly::lazy, usually with a lambda, and - * its value is requested using operator(). - * - * Note that the value is not safe for concurrent accesses by multiple - * threads, even if you declare it const. See note below. - * - * - * Example Usage: - * - * void foo() { - * auto const val = folly::lazy([&]{ - * return something_expensive(blah()); - * }); - * - * if (condition1) { - * use(val()); - * } - * if (condition2) { - * useMaybeAgain(val()); - * } else { - * // Unneeded in this branch. - * } - * } - * - * - * Rationale: - * - * - operator() is used to request the value instead of an implicit - * conversion because the slight syntactic overhead in common - * seems worth the increased clarity. - * - * - Lazy values do not model CopyConstructible because it is - * unclear what semantics would be desirable. Either copies - * should share the cached value (adding overhead to cases that - * don't need to support copies), or they could recompute the - * value unnecessarily. Sharing with mutable lazies would also - * leave them with non-value semantics despite looking - * value-like. - * - * - Not thread safe for const accesses. Many use cases for lazy - * values are local variables on the stack, where multiple - * threads shouldn't even be able to reach the value. It still - * is useful to indicate/check that the value doesn't change with - * const, particularly when it is captured by a large family of - * lambdas. Adding internal synchronization seems like it would - * pessimize the most common use case in favor of less likely use - * cases. - * - */ - -////////////////////////////////////////////////////////////////////// - -namespace detail { - -template -struct Lazy { - typedef typename std::result_of::type result_type; - - explicit Lazy(Func&& f) : func_(std::move(f)) {} - explicit Lazy(Func& f) : func_(f) {} - - Lazy(Lazy&& o) - : value_(std::move(o.value_)) - , func_(std::move(o.func_)) - {} - - Lazy(const Lazy&) = delete; - Lazy& operator=(const Lazy&) = delete; - Lazy& operator=(Lazy&&) = delete; - - const result_type& operator()() const { - return const_cast(*this)(); - } - - result_type& operator()() { - if (!value_) value_ = func_(); - return *value_; - } - -private: - Optional value_; - Func func_; -}; - -} - -////////////////////////////////////////////////////////////////////// - -template -detail::Lazy::type> -lazy(Func&& fun) { - return detail::Lazy::type>( - std::forward(fun) - ); -} - -////////////////////////////////////////////////////////////////////// - -} - -#endif diff --git a/hphp/third_party/folly/folly/Likely.h b/hphp/third_party/folly/folly/Likely.h deleted file mode 100644 index 09fb99365..000000000 --- a/hphp/third_party/folly/folly/Likely.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2013 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. - */ - -/** - * Compiler hints to indicate the fast path of an "if" branch: whether - * the if condition is likely to be true or false. - * - * @author Tudor Bosman (tudorb@fb.com) - */ - -#ifndef FOLLY_BASE_LIKELY_H_ -#define FOLLY_BASE_LIKELY_H_ - -#undef LIKELY -#undef UNLIKELY - -#if defined(__GNUC__) && __GNUC__ >= 4 -#define LIKELY(x) (__builtin_expect((x), 1)) -#define UNLIKELY(x) (__builtin_expect((x), 0)) -#else -#define LIKELY(x) (x) -#define UNLIKELY(x) (x) -#endif - -#endif /* FOLLY_BASE_LIKELY_H_ */ - diff --git a/hphp/third_party/folly/folly/Logging.h b/hphp/third_party/folly/folly/Logging.h deleted file mode 100644 index 8ffcec79e..000000000 --- a/hphp/third_party/folly/folly/Logging.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_LOGGING_H_ -#define FOLLY_LOGGING_H_ - -#include -#include - -#ifndef FB_LOG_EVERY_MS -/** - * Issues a LOG(severity) no more often than every - * milliseconds. Example: - * - * FB_LOG_EVERY_MS(INFO, 10000) << "At least ten seconds passed" - * " since you last saw this."; - * - * The implementation uses for statements to introduce variables in - * a nice way that doesn't mess surrounding statements. - */ -#define FB_LOG_EVERY_MS(severity, milliseconds) \ - for (bool LOG_EVERY_MS_once = true; LOG_EVERY_MS_once; ) \ - for (const ::clock_t LOG_EVERY_MS_now = ::clock(); LOG_EVERY_MS_once; ) \ - for (static ::clock_t LOG_EVERY_MS_last; LOG_EVERY_MS_once; \ - LOG_EVERY_MS_once = false) \ - if (1000 * (LOG_EVERY_MS_now - LOG_EVERY_MS_last) < \ - (milliseconds) * CLOCKS_PER_SEC) {} \ - else \ - (LOG_EVERY_MS_last = LOG_EVERY_MS_now, LOG(severity)) - -#endif - -#endif // FOLLY_LOGGING_H_ diff --git a/hphp/third_party/folly/folly/MPMCPipeline.h b/hphp/third_party/folly/folly/MPMCPipeline.h deleted file mode 100644 index 3981e2433..000000000 --- a/hphp/third_party/folly/folly/MPMCPipeline.h +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Copyright 2013 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 - -#include - -#include "folly/detail/MPMCPipelineDetail.h" - -namespace folly { - -/** - * Helper tag template to use amplification > 1 - */ -template class MPMCPipelineStage; - -/** - * Multi-Producer, Multi-Consumer pipeline. - * - * A N-stage pipeline is a combination of N+1 MPMC queues (see MPMCQueue.h). - * - * At each stage, you may dequeue the results from the previous stage (possibly - * from multiple threads) and enqueue results to the next stage. Regardless of - * the order of completion, data is delivered to the next stage in the original - * order. Each input is matched with a "ticket" which must be produced - * when enqueueing to the next stage. - * - * A given stage must produce exactly K ("amplification factor", default K=1) - * results for every input. This is enforced by requiring that each ticket - * is used exactly K times. - * - * Usage: - * - * // arguments are queue sizes - * MPMCPipeline pipeline(10, 10, 10); - * - * pipeline.blockingWrite(42); - * - * { - * int val; - * auto ticket = pipeline.blockingReadStage<0>(val); - * pipeline.blockingWriteStage<0>(ticket, folly::to(val)); - * } - * - * { - * std::string val; - * auto ticket = pipeline.blockingReadStage<1>(val); - * int ival = 0; - * try { - * ival = folly::to(val); - * } catch (...) { - * // We must produce exactly 1 output even on exception! - * } - * pipeline.blockingWriteStage<1>(ticket, ival); - * } - * - * int result; - * pipeline.blockingRead(result); - * // result == 42 - * - * To specify amplification factors greater than 1, use - * MPMCPipelineStage instead of T in the declaration: - * - * MPMCPipeline, - * MPMCPipelineStage> - * - * declares a two-stage pipeline: the first stage produces 2 strings - * for each input int, the second stage produces 4 ints for each input string, - * so, overall, the pipeline produces 2*4 = 8 ints for each input int. - * - * Implementation details: we use N+1 MPMCQueue objects; each intermediate - * queue connects two adjacent stages. The MPMCQueue implementation is abused; - * instead of using it as a queue, we insert in the output queue at the - * position determined by the input queue's popTicket_. We guarantee that - * all slots are filled (and therefore the queue doesn't freeze) because - * we require that each step produces exactly K outputs for every input. - */ -template class MPMCPipeline { - typedef std::tuple...> StageInfos; - typedef std::tuple< - detail::MPMCPipelineStageImpl, - detail::MPMCPipelineStageImpl< - typename detail::PipelineStageInfo::value_type>...> - StageTuple; - static constexpr size_t kAmplification = - detail::AmplificationProduct::value; - - public: - /** - * Ticket, returned by blockingReadStage, must be given back to - * blockingWriteStage. Tickets are not thread-safe. - */ - template - class Ticket { - public: - ~Ticket() noexcept { - CHECK_EQ(remainingUses_, 0) << "All tickets must be completely used!"; - } - -#ifndef NDEBUG - Ticket() noexcept - : owner_(nullptr), - remainingUses_(0), - value_(0xdeadbeeffaceb00c) { - } -#else - Ticket() noexcept : remainingUses_(0) { } -#endif - - Ticket(Ticket&& other) noexcept - : -#ifndef NDEBUG - owner_(other.owner_), -#endif - remainingUses_(other.remainingUses_), - value_(other.value_) { - other.remainingUses_ = 0; -#ifndef NDEBUG - other.owner_ = nullptr; - other.value_ = 0xdeadbeeffaceb00c; -#endif - } - - Ticket& operator=(Ticket&& other) noexcept { - if (this != &other) { - this->~Ticket(); - new (this) Ticket(std::move(other)); - } - return *this; - } - - private: - friend class MPMCPipeline; -#ifndef NDEBUG - MPMCPipeline* owner_; -#endif - size_t remainingUses_; - uint64_t value_; - - - Ticket(MPMCPipeline* owner, size_t amplification, uint64_t value) noexcept - : -#ifndef NDEBUG - owner_(owner), -#endif - remainingUses_(amplification), - value_(value * amplification) { - } - - uint64_t use(MPMCPipeline* owner) { - CHECK_GT(remainingUses_--, 0); -#ifndef NDEBUG - CHECK(owner == owner_); -#endif - return value_++; - } - }; - - /** - * Default-construct pipeline. Useful to move-assign later, - * just like MPMCQueue, see MPMCQueue.h for more details. - */ - MPMCPipeline() { } - - /** - * Construct a pipeline with N+1 queue sizes. - */ - template - explicit MPMCPipeline(Sizes... sizes) : stages_(sizes...) { } - - /** - * Push an element into (the first stage of) the pipeline. Blocking. - */ - template - void blockingWrite(Args&&... args) { - std::get<0>(stages_).blockingWrite(std::forward(args)...); - } - - /** - * Try to push an element into (the first stage of) the pipeline. - * Non-blocking. - */ - template - bool write(Args&&... args) { - return std::get<0>(stages_).write(std::forward(args)...); - } - - /** - * Read an element for stage Stage and obtain a ticket. Blocking. - */ - template - Ticket blockingReadStage( - typename std::tuple_element::type::value_type& elem) { - return Ticket( - this, - std::tuple_element::type::kAmplification, - std::get(stages_).blockingRead(elem)); - } - - /** - * Try to read an element for stage Stage and obtain a ticket. - * Non-blocking. - */ - template - bool readStage( - Ticket& ticket, - typename std::tuple_element::type::value_type& elem) { - uint64_t tval; - if (!std::get(stages_).readAndGetTicket(tval, elem)) { - return false; - } - ticket = Ticket( - this, - std::tuple_element::type::kAmplification, - tval); - return true; - } - - /** - * Complete an element in stage Stage (pushing it for stage Stage+1). - * Blocking. - */ - template - void blockingWriteStage(Ticket& ticket, Args&&... args) { - std::get(stages_).blockingWriteWithTicket( - ticket.use(this), - std::forward(args)...); - } - - /** - * Pop an element from (the final stage of) the pipeline. Blocking. - */ - void blockingRead( - typename std::tuple_element< - sizeof...(Stages), - StageTuple>::type::value_type& elem) { - std::get(stages_).blockingRead(elem); - } - - /** - * Try to pop an element from (the final stage of) the pipeline. - * Non-blocking. - */ - bool read( - typename std::tuple_element< - sizeof...(Stages), - StageTuple>::type::value_type& elem) { - return std::get(stages_).read(elem); - } - - /** - * Estimate queue size, measured as values from the last stage. - * (so if the pipeline has an amplification factor > 1, pushing an element - * into the first stage will cause sizeGuess() to be == amplification factor) - * Elements "in flight" (currently processed as part of a stage, so not - * in any queue) are also counted. - */ - ssize_t sizeGuess() const noexcept { - return (std::get<0>(stages_).writeCount() * kAmplification - - std::get(stages_).readCount()); - } - - private: - StageTuple stages_; -}; - - -} // namespaces - diff --git a/hphp/third_party/folly/folly/MPMCQueue.h b/hphp/third_party/folly/folly/MPMCQueue.h deleted file mode 100644 index fef1730e3..000000000 --- a/hphp/third_party/folly/folly/MPMCQueue.h +++ /dev/null @@ -1,861 +0,0 @@ -/* - * Copyright 2013 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace folly { - -namespace detail { - -template class Atom> -class SingleElementQueue; - -template class MPMCPipelineStageImpl; - -} // namespace detail - -/// MPMCQueue is a high-performance bounded concurrent queue that -/// supports multiple producers, multiple consumers, and optional blocking. -/// The queue has a fixed capacity, for which all memory will be allocated -/// up front. The bulk of the work of enqueuing and dequeuing can be -/// performed in parallel. -/// -/// The underlying implementation uses a ticket dispenser for the head and -/// the tail, spreading accesses across N single-element queues to produce -/// a queue with capacity N. The ticket dispensers use atomic increment, -/// which is more robust to contention than a CAS loop. Each of the -/// single-element queues uses its own CAS to serialize access, with an -/// adaptive spin cutoff. When spinning fails on a single-element queue -/// it uses futex()'s _BITSET operations to reduce unnecessary wakeups -/// even if multiple waiters are present on an individual queue (such as -/// when the MPMCQueue's capacity is smaller than the number of enqueuers -/// or dequeuers). -/// -/// NOEXCEPT INTERACTION: Ticket-based queues separate the assignment -/// of In benchmarks (contained in tao/queues/ConcurrentQueueTests) -/// it handles 1 to 1, 1 to N, N to 1, and N to M thread counts better -/// than any of the alternatives present in fbcode, for both small (~10) -/// and large capacities. In these benchmarks it is also faster than -/// tbb::concurrent_bounded_queue for all configurations. When there are -/// many more threads than cores, MPMCQueue is _much_ faster than the tbb -/// queue because it uses futex() to block and unblock waiting threads, -/// rather than spinning with sched_yield. -/// -/// queue positions from the actual construction of the in-queue elements, -/// which means that the T constructor used during enqueue must not throw -/// an exception. This is enforced at compile time using type traits, -/// which requires that T be adorned with accurate noexcept information. -/// If your type does not use noexcept, you will have to wrap it in -/// something that provides the guarantee. We provide an alternate -/// safe implementation for types that don't use noexcept but that are -/// marked folly::IsRelocatable and boost::has_nothrow_constructor, -/// which is common for folly types. In particular, if you can declare -/// FOLLY_ASSUME_FBVECTOR_COMPATIBLE then your type can be put in -/// MPMCQueue. -template class Atom = std::atomic, - typename = typename std::enable_if< - std::is_nothrow_constructible::value || - folly::IsRelocatable::value>::type> -class MPMCQueue : boost::noncopyable { - friend class detail::MPMCPipelineStageImpl; - public: - typedef T value_type; - - explicit MPMCQueue(size_t capacity) - : capacity_(capacity) - , slots_(new detail::SingleElementQueue[capacity + - 2 * kSlotPadding]) - , stride_(computeStride(capacity)) - , pushTicket_(0) - , popTicket_(0) - , pushSpinCutoff_(0) - , popSpinCutoff_(0) - { - // ideally this would be a static assert, but g++ doesn't allow it - assert(alignof(MPMCQueue) >= kFalseSharingRange); - } - - /// A default-constructed queue is useful because a usable (non-zero - /// capacity) queue can be moved onto it or swapped with it - MPMCQueue() noexcept - : capacity_(0) - , slots_(nullptr) - , stride_(0) - , pushTicket_(0) - , popTicket_(0) - , pushSpinCutoff_(0) - , popSpinCutoff_(0) - {} - - /// IMPORTANT: The move constructor is here to make it easier to perform - /// the initialization phase, it is not safe to use when there are any - /// concurrent accesses (this is not checked). - MPMCQueue(MPMCQueue&& rhs) noexcept - : capacity_(rhs.capacity_) - , slots_(rhs.slots_) - , stride_(rhs.stride_) - , pushTicket_(rhs.pushTicket_.load(std::memory_order_relaxed)) - , popTicket_(rhs.popTicket_.load(std::memory_order_relaxed)) - , pushSpinCutoff_(rhs.pushSpinCutoff_.load(std::memory_order_relaxed)) - , popSpinCutoff_(rhs.popSpinCutoff_.load(std::memory_order_relaxed)) - { - // relaxed ops are okay for the previous reads, since rhs queue can't - // be in concurrent use - - // zero out rhs - rhs.capacity_ = 0; - rhs.slots_ = nullptr; - rhs.stride_ = 0; - rhs.pushTicket_.store(0, std::memory_order_relaxed); - rhs.popTicket_.store(0, std::memory_order_relaxed); - rhs.pushSpinCutoff_.store(0, std::memory_order_relaxed); - rhs.popSpinCutoff_.store(0, std::memory_order_relaxed); - } - - /// IMPORTANT: The move operator is here to make it easier to perform - /// the initialization phase, it is not safe to use when there are any - /// concurrent accesses (this is not checked). - MPMCQueue const& operator= (MPMCQueue&& rhs) { - if (this != &rhs) { - this->~MPMCQueue(); - new (this) MPMCQueue(std::move(rhs)); - } - return *this; - } - - /// MPMCQueue can only be safely destroyed when there are no - /// pending enqueuers or dequeuers (this is not checked). - ~MPMCQueue() { - delete[] slots_; - } - - /// Returns the number of successful reads minus the number of successful - /// writes. Waiting blockingRead and blockingWrite calls are included, - /// so this value can be negative. - ssize_t size() const noexcept { - // since both pushes and pops increase monotonically, we can get a - // consistent snapshot either by bracketing a read of popTicket_ with - // two reads of pushTicket_ that return the same value, or the other - // way around. We maximize our chances by alternately attempting - // both bracketings. - uint64_t pushes = pushTicket_.load(std::memory_order_acquire); // A - uint64_t pops = popTicket_.load(std::memory_order_acquire); // B - while (true) { - uint64_t nextPushes = pushTicket_.load(std::memory_order_acquire); // C - if (pushes == nextPushes) { - // pushTicket_ didn't change from A (or the previous C) to C, - // so we can linearize at B (or D) - return pushes - pops; - } - pushes = nextPushes; - uint64_t nextPops = popTicket_.load(std::memory_order_acquire); // D - if (pops == nextPops) { - // popTicket_ didn't chance from B (or the previous D), so we - // can linearize at C - return pushes - pops; - } - pops = nextPops; - } - } - - /// Returns true if there are no items available for dequeue - bool isEmpty() const noexcept { - return size() <= 0; - } - - /// Returns true if there is currently no empty space to enqueue - bool isFull() const noexcept { - // careful with signed -> unsigned promotion, since size can be negative - return size() >= static_cast(capacity_); - } - - /// Returns is a guess at size() for contexts that don't need a precise - /// value, such as stats. - ssize_t sizeGuess() const noexcept { - return writeCount() - readCount(); - } - - /// Doesn't change - size_t capacity() const noexcept { - return capacity_; - } - - /// Returns the total number of calls to blockingWrite or successful - /// calls to write, including those blockingWrite calls that are - /// currently blocking - uint64_t writeCount() const noexcept { - return pushTicket_.load(std::memory_order_acquire); - } - - /// Returns the total number of calls to blockingRead or successful - /// calls to read, including those blockingRead calls that are currently - /// blocking - uint64_t readCount() const noexcept { - return popTicket_.load(std::memory_order_acquire); - } - - /// Enqueues a T constructed from args, blocking until space is - /// available. Note that this method signature allows enqueue via - /// move, if args is a T rvalue, via copy, if args is a T lvalue, or - /// via emplacement if args is an initializer list that can be passed - /// to a T constructor. - template - void blockingWrite(Args&&... args) noexcept { - enqueueWithTicket(pushTicket_++, std::forward(args)...); - } - - /// If an item can be enqueued with no blocking, does so and returns - /// true, otherwise returns false. This method is similar to - /// writeIfNotFull, but if you don't have a specific need for that - /// method you should use this one. - /// - /// One of the common usages of this method is to enqueue via the - /// move constructor, something like q.write(std::move(x)). If write - /// returns false because the queue is full then x has not actually been - /// consumed, which looks strange. To understand why it is actually okay - /// to use x afterward, remember that std::move is just a typecast that - /// provides an rvalue reference that enables use of a move constructor - /// or operator. std::move doesn't actually move anything. It could - /// more accurately be called std::rvalue_cast or std::move_permission. - template - bool write(Args&&... args) noexcept { - uint64_t ticket; - if (tryObtainReadyPushTicket(ticket)) { - // we have pre-validated that the ticket won't block - enqueueWithTicket(ticket, std::forward(args)...); - return true; - } else { - return false; - } - } - - /// If the queue is not full, enqueues and returns true, otherwise - /// returns false. Unlike write this method can be blocked by another - /// thread, specifically a read that has linearized (been assigned - /// a ticket) but not yet completed. If you don't really need this - /// function you should probably use write. - /// - /// MPMCQueue isn't lock-free, so just because a read operation has - /// linearized (and isFull is false) doesn't mean that space has been - /// made available for another write. In this situation write will - /// return false, but writeIfNotFull will wait for the dequeue to finish. - /// This method is required if you are composing queues and managing - /// your own wakeup, because it guarantees that after every successful - /// write a readIfNotFull will succeed. - template - bool writeIfNotFull(Args&&... args) noexcept { - uint64_t ticket; - if (tryObtainPromisedPushTicket(ticket)) { - // some other thread is already dequeuing the slot into which we - // are going to enqueue, but we might have to wait for them to finish - enqueueWithTicket(ticket, std::forward(args)...); - return true; - } else { - return false; - } - } - - /// Moves a dequeued element onto elem, blocking until an element - /// is available - void blockingRead(T& elem) noexcept { - dequeueWithTicket(popTicket_++, elem); - } - - /// If an item can be dequeued with no blocking, does so and returns - /// true, otherwise returns false. - bool read(T& elem) noexcept { - uint64_t ticket; - if (tryObtainReadyPopTicket(ticket)) { - // the ticket has been pre-validated to not block - dequeueWithTicket(ticket, elem); - return true; - } else { - return false; - } - } - - /// If the queue is not empty, dequeues and returns true, otherwise - /// returns false. If the matching write is still in progress then this - /// method may block waiting for it. If you don't rely on being able - /// to dequeue (such as by counting completed write) then you should - /// prefer read. - bool readIfNotEmpty(T& elem) noexcept { - uint64_t ticket; - if (tryObtainPromisedPopTicket(ticket)) { - // the matching enqueue already has a ticket, but might not be done - dequeueWithTicket(ticket, elem); - return true; - } else { - return false; - } - } - - private: - enum { - /// Once every kAdaptationFreq we will spin longer, to try to estimate - /// the proper spin backoff - kAdaptationFreq = 128, - - /// Memory locations on the same cache line are subject to false - /// sharing, which is very bad for performance - kFalseSharingRange = 64, - - /// To avoid false sharing in slots_ with neighboring memory - /// allocations, we pad it with this many SingleElementQueue-s at - /// each end - kSlotPadding = 1 + - (kFalseSharingRange - 1) / sizeof(detail::SingleElementQueue) - }; - - static_assert(kFalseSharingRange == 64, - "FOLLY_ON_NEXT_CACHE_LINE must track kFalseSharingRange"); - -// This literal "64' should be kFalseSharingRange, -// but gcc-4.8.0 and 4.8.1 reject it. -// FIXME: s/64/kFalseSharingRange/ if that ever changes. -#define FOLLY_ON_NEXT_CACHE_LINE __attribute__((aligned(64))) - - /// The maximum number of items in the queue at once - size_t capacity_ FOLLY_ON_NEXT_CACHE_LINE; - - /// An array of capacity_ SingleElementQueue-s, each of which holds - /// either 0 or 1 item. We over-allocate by 2 * kSlotPadding and don't - /// touch the slots at either end, to avoid false sharing - detail::SingleElementQueue* slots_; - - /// The number of slots_ indices that we advance for each ticket, to - /// avoid false sharing. Ideally slots_[i] and slots_[i + stride_] - /// aren't on the same cache line - int stride_; - - /// Enqueuers get tickets from here - Atom pushTicket_ FOLLY_ON_NEXT_CACHE_LINE; - - /// Dequeuers get tickets from here - Atom popTicket_ FOLLY_ON_NEXT_CACHE_LINE; - - /// This is how many times we will spin before using FUTEX_WAIT when - /// the queue is full on enqueue, adaptively computed by occasionally - /// spinning for longer and smoothing with an exponential moving average - Atom pushSpinCutoff_ FOLLY_ON_NEXT_CACHE_LINE; - - /// The adaptive spin cutoff when the queue is empty on dequeue - Atom popSpinCutoff_ FOLLY_ON_NEXT_CACHE_LINE; - - /// Alignment doesn't avoid false sharing at the end of the struct, - /// so fill out the last cache line - char padding_[kFalseSharingRange - sizeof(Atom)]; - -#undef FOLLY_ON_NEXT_CACHE_LINE - - /// We assign tickets in increasing order, but we don't want to - /// access neighboring elements of slots_ because that will lead to - /// false sharing (multiple cores accessing the same cache line even - /// though they aren't accessing the same bytes in that cache line). - /// To avoid this we advance by stride slots per ticket. - /// - /// We need gcd(capacity, stride) to be 1 so that we will use all - /// of the slots. We ensure this by only considering prime strides, - /// which either have no common divisors with capacity or else have - /// a zero remainder after dividing by capacity. That is sufficient - /// to guarantee correctness, but we also want to actually spread the - /// accesses away from each other to avoid false sharing (consider a - /// stride of 7 with a capacity of 8). To that end we try a few taking - /// care to observe that advancing by -1 is as bad as advancing by 1 - /// when in comes to false sharing. - /// - /// The simple way to avoid false sharing would be to pad each - /// SingleElementQueue, but since we have capacity_ of them that could - /// waste a lot of space. - static int computeStride(size_t capacity) noexcept { - static const int smallPrimes[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23 }; - - int bestStride = 1; - size_t bestSep = 1; - for (int stride : smallPrimes) { - if ((stride % capacity) == 0 || (capacity % stride) == 0) { - continue; - } - size_t sep = stride % capacity; - sep = std::min(sep, capacity - sep); - if (sep > bestSep) { - bestStride = stride; - bestSep = sep; - } - } - return bestStride; - } - - /// Returns the index into slots_ that should be used when enqueuing or - /// dequeuing with the specified ticket - size_t idx(uint64_t ticket) noexcept { - return ((ticket * stride_) % capacity_) + kSlotPadding; - } - - /// Maps an enqueue or dequeue ticket to the turn should be used at the - /// corresponding SingleElementQueue - uint32_t turn(uint64_t ticket) noexcept { - return ticket / capacity_; - } - - /// Tries to obtain a push ticket for which SingleElementQueue::enqueue - /// won't block. Returns true on immediate success, false on immediate - /// failure. - bool tryObtainReadyPushTicket(uint64_t& rv) noexcept { - auto ticket = pushTicket_.load(std::memory_order_acquire); // A - while (true) { - if (!slots_[idx(ticket)].mayEnqueue(turn(ticket))) { - // if we call enqueue(ticket, ...) on the SingleElementQueue - // right now it would block, but this might no longer be the next - // ticket. We can increase the chance of tryEnqueue success under - // contention (without blocking) by rechecking the ticket dispenser - auto prev = ticket; - ticket = pushTicket_.load(std::memory_order_acquire); // B - if (prev == ticket) { - // mayEnqueue was bracketed by two reads (A or prev B or prev - // failing CAS to B), so we are definitely unable to enqueue - return false; - } - } else { - // we will bracket the mayEnqueue check with a read (A or prev B - // or prev failing CAS) and the following CAS. If the CAS fails - // it will effect a load of pushTicket_ - if (pushTicket_.compare_exchange_strong(ticket, ticket + 1)) { - rv = ticket; - return true; - } - } - } - } - - /// Tries to obtain a push ticket which can be satisfied if all - /// in-progress pops complete. This function does not block, but - /// blocking may be required when using the returned ticket if some - /// other thread's pop is still in progress (ticket has been granted but - /// pop has not yet completed). - bool tryObtainPromisedPushTicket(uint64_t& rv) noexcept { - auto numPushes = pushTicket_.load(std::memory_order_acquire); // A - while (true) { - auto numPops = popTicket_.load(std::memory_order_acquire); // B - // n will be negative if pops are pending - int64_t n = numPushes - numPops; - if (n >= static_cast(capacity_)) { - // Full, linearize at B. We don't need to recheck the read we - // performed at A, because if numPushes was stale at B then the - // real numPushes value is even worse - return false; - } - if (pushTicket_.compare_exchange_strong(numPushes, numPushes + 1)) { - rv = numPushes; - return true; - } - } - } - - /// Tries to obtain a pop ticket for which SingleElementQueue::dequeue - /// won't block. Returns true on immediate success, false on immediate - /// failure. - bool tryObtainReadyPopTicket(uint64_t& rv) noexcept { - auto ticket = popTicket_.load(std::memory_order_acquire); - while (true) { - if (!slots_[idx(ticket)].mayDequeue(turn(ticket))) { - auto prev = ticket; - ticket = popTicket_.load(std::memory_order_acquire); - if (prev == ticket) { - return false; - } - } else { - if (popTicket_.compare_exchange_strong(ticket, ticket + 1)) { - rv = ticket; - return true; - } - } - } - } - - /// Similar to tryObtainReadyPopTicket, but returns a pop ticket whose - /// corresponding push ticket has already been handed out, rather than - /// returning one whose corresponding push ticket has already been - /// completed. This means that there is a possibility that the caller - /// will block when using the ticket, but it allows the user to rely on - /// the fact that if enqueue has succeeded, tryObtainPromisedPopTicket - /// will return true. The "try" part of this is that we won't have - /// to block waiting for someone to call enqueue, although we might - /// have to block waiting for them to finish executing code inside the - /// MPMCQueue itself. - bool tryObtainPromisedPopTicket(uint64_t& rv) noexcept { - auto numPops = popTicket_.load(std::memory_order_acquire); // A - while (true) { - auto numPushes = pushTicket_.load(std::memory_order_acquire); // B - if (numPops >= numPushes) { - // Empty, or empty with pending pops. Linearize at B. We don't - // need to recheck the read we performed at A, because if numPops - // is stale then the fresh value is larger and the >= is still true - return false; - } - if (popTicket_.compare_exchange_strong(numPops, numPops + 1)) { - rv = numPops; - return true; - } - } - } - - // Given a ticket, constructs an enqueued item using args - template - void enqueueWithTicket(uint64_t ticket, Args&&... args) noexcept { - slots_[idx(ticket)].enqueue(turn(ticket), - pushSpinCutoff_, - (ticket % kAdaptationFreq) == 0, - std::forward(args)...); - } - - // Given a ticket, dequeues the corresponding element - void dequeueWithTicket(uint64_t ticket, T& elem) noexcept { - slots_[idx(ticket)].dequeue(turn(ticket), - popSpinCutoff_, - (ticket % kAdaptationFreq) == 0, - elem); - } -}; - - -namespace detail { - -/// A TurnSequencer allows threads to order their execution according to -/// a monotonically increasing (with wraparound) "turn" value. The two -/// operations provided are to wait for turn T, and to move to the next -/// turn. Every thread that is waiting for T must have arrived before -/// that turn is marked completed (for MPMCQueue only one thread waits -/// for any particular turn, so this is trivially true). -/// -/// TurnSequencer's state_ holds 26 bits of the current turn (shifted -/// left by 6), along with a 6 bit saturating value that records the -/// maximum waiter minus the current turn. Wraparound of the turn space -/// is expected and handled. This allows us to atomically adjust the -/// number of outstanding waiters when we perform a FUTEX_WAKE operation. -/// Compare this strategy to sem_t's separate num_waiters field, which -/// isn't decremented until after the waiting thread gets scheduled, -/// during which time more enqueues might have occurred and made pointless -/// FUTEX_WAKE calls. -/// -/// TurnSequencer uses futex() directly. It is optimized for the -/// case that the highest awaited turn is 32 or less higher than the -/// current turn. We use the FUTEX_WAIT_BITSET variant, which lets -/// us embed 32 separate wakeup channels in a single futex. See -/// http://locklessinc.com/articles/futex_cheat_sheet for a description. -/// -/// We only need to keep exact track of the delta between the current -/// turn and the maximum waiter for the 32 turns that follow the current -/// one, because waiters at turn t+32 will be awoken at turn t. At that -/// point they can then adjust the delta using the higher base. Since we -/// need to encode waiter deltas of 0 to 32 inclusive, we use 6 bits. -/// We actually store waiter deltas up to 63, since that might reduce -/// the number of CAS operations a tiny bit. -/// -/// To avoid some futex() calls entirely, TurnSequencer uses an adaptive -/// spin cutoff before waiting. The overheads (and convergence rate) -/// of separately tracking the spin cutoff for each TurnSequencer would -/// be prohibitive, so the actual storage is passed in as a parameter and -/// updated atomically. This also lets the caller use different adaptive -/// cutoffs for different operations (read versus write, for example). -/// To avoid contention, the spin cutoff is only updated when requested -/// by the caller. -template class Atom> -struct TurnSequencer { - explicit TurnSequencer(const uint32_t firstTurn = 0) noexcept - : state_(encode(firstTurn << kTurnShift, 0)) - {} - - /// Returns true iff a call to waitForTurn(turn, ...) won't block - bool isTurn(const uint32_t turn) const noexcept { - auto state = state_.load(std::memory_order_acquire); - return decodeCurrentSturn(state) == (turn << kTurnShift); - } - - // Internally we always work with shifted turn values, which makes the - // truncation and wraparound work correctly. This leaves us bits at - // the bottom to store the number of waiters. We call shifted turns - // "sturns" inside this class. - - /// Blocks the current thread until turn has arrived. If - /// updateSpinCutoff is true then this will spin for up to kMaxSpins tries - /// before blocking and will adjust spinCutoff based on the results, - /// otherwise it will spin for at most spinCutoff spins. - void waitForTurn(const uint32_t turn, - Atom& spinCutoff, - const bool updateSpinCutoff) noexcept { - int prevThresh = spinCutoff.load(std::memory_order_relaxed); - const int effectiveSpinCutoff = - updateSpinCutoff || prevThresh == 0 ? kMaxSpins : prevThresh; - int tries; - - const uint32_t sturn = turn << kTurnShift; - for (tries = 0; ; ++tries) { - uint32_t state = state_.load(std::memory_order_acquire); - uint32_t current_sturn = decodeCurrentSturn(state); - if (current_sturn == sturn) { - break; - } - - // wrap-safe version of assert(current_sturn < sturn) - assert(sturn - current_sturn < std::numeric_limits::max() / 2); - - // the first effectSpinCutoff tries are spins, after that we will - // record ourself as a waiter and block with futexWait - if (tries < effectiveSpinCutoff) { - asm volatile ("pause"); - continue; - } - - uint32_t current_max_waiter_delta = decodeMaxWaitersDelta(state); - uint32_t our_waiter_delta = (sturn - current_sturn) >> kTurnShift; - uint32_t new_state; - if (our_waiter_delta <= current_max_waiter_delta) { - // state already records us as waiters, probably because this - // isn't our first time around this loop - new_state = state; - } else { - new_state = encode(current_sturn, our_waiter_delta); - if (state != new_state && - !state_.compare_exchange_strong(state, new_state)) { - continue; - } - } - state_.futexWait(new_state, futexChannel(turn)); - } - - if (updateSpinCutoff || prevThresh == 0) { - // if we hit kMaxSpins then spinning was pointless, so the right - // spinCutoff is kMinSpins - int target; - if (tries >= kMaxSpins) { - target = kMinSpins; - } else { - // to account for variations, we allow ourself to spin 2*N when - // we think that N is actually required in order to succeed - target = std::min(int{kMaxSpins}, std::max(int{kMinSpins}, tries * 2)); - } - - if (prevThresh == 0) { - // bootstrap - spinCutoff = target; - } else { - // try once, keep moving if CAS fails. Exponential moving average - // with alpha of 7/8 - spinCutoff.compare_exchange_weak( - prevThresh, prevThresh + (target - prevThresh) / 8); - } - } - } - - /// Unblocks a thread running waitForTurn(turn + 1) - void completeTurn(const uint32_t turn) noexcept { - uint32_t state = state_.load(std::memory_order_acquire); - while (true) { - assert(state == encode(turn << kTurnShift, decodeMaxWaitersDelta(state))); - uint32_t max_waiter_delta = decodeMaxWaitersDelta(state); - uint32_t new_state = encode( - (turn + 1) << kTurnShift, - max_waiter_delta == 0 ? 0 : max_waiter_delta - 1); - if (state_.compare_exchange_strong(state, new_state)) { - if (max_waiter_delta != 0) { - state_.futexWake(std::numeric_limits::max(), - futexChannel(turn + 1)); - } - break; - } - // failing compare_exchange_strong updates first arg to the value - // that caused the failure, so no need to reread state_ - } - } - - /// Returns the least-most significant byte of the current uncompleted - /// turn. The full 32 bit turn cannot be recovered. - uint8_t uncompletedTurnLSB() const noexcept { - return state_.load(std::memory_order_acquire) >> kTurnShift; - } - - private: - enum : uint32_t { - /// kTurnShift counts the bits that are stolen to record the delta - /// between the current turn and the maximum waiter. It needs to be big - /// enough to record wait deltas of 0 to 32 inclusive. Waiters more - /// than 32 in the future will be woken up 32*n turns early (since - /// their BITSET will hit) and will adjust the waiter count again. - /// We go a bit beyond and let the waiter count go up to 63, which - /// is free and might save us a few CAS - kTurnShift = 6, - kWaitersMask = (1 << kTurnShift) - 1, - - /// The minimum spin count that we will adaptively select - kMinSpins = 20, - - /// The maximum spin count that we will adaptively select, and the - /// spin count that will be used when probing to get a new data point - /// for the adaptation - kMaxSpins = 2000, - }; - - /// This holds both the current turn, and the highest waiting turn, - /// stored as (current_turn << 6) | min(63, max(waited_turn - current_turn)) - Futex state_; - - /// Returns the bitmask to pass futexWait or futexWake when communicating - /// about the specified turn - int futexChannel(uint32_t turn) const noexcept { - return 1 << (turn & 31); - } - - uint32_t decodeCurrentSturn(uint32_t state) const noexcept { - return state & ~kWaitersMask; - } - - uint32_t decodeMaxWaitersDelta(uint32_t state) const noexcept { - return state & kWaitersMask; - } - - uint32_t encode(uint32_t currentSturn, uint32_t maxWaiterD) const noexcept { - return currentSturn | std::min(uint32_t{ kWaitersMask }, maxWaiterD); - } -}; - - -/// SingleElementQueue implements a blocking queue that holds at most one -/// item, and that requires its users to assign incrementing identifiers -/// (turns) to each enqueue and dequeue operation. Note that the turns -/// used by SingleElementQueue are doubled inside the TurnSequencer -template class Atom> -struct SingleElementQueue { - - ~SingleElementQueue() noexcept { - if ((sequencer_.uncompletedTurnLSB() & 1) == 1) { - // we are pending a dequeue, so we have a constructed item - destroyContents(); - } - } - - /// enqueue using in-place noexcept construction - template ::value>::type> - void enqueue(const uint32_t turn, - Atom& spinCutoff, - const bool updateSpinCutoff, - Args&&... args) noexcept { - sequencer_.waitForTurn(turn * 2, spinCutoff, updateSpinCutoff); - new (contents_) T(std::forward(args)...); - sequencer_.completeTurn(turn * 2); - } - - /// enqueue using move construction, either real (if - /// is_nothrow_move_constructible) or simulated using relocation and - /// default construction (if IsRelocatable and has_nothrow_constructor) - template ::value && - boost::has_nothrow_constructor::value) || - std::is_nothrow_constructible::value>::type> - void enqueue(const uint32_t turn, - Atom& spinCutoff, - const bool updateSpinCutoff, - T&& goner) noexcept { - if (std::is_nothrow_constructible::value) { - // this is preferred - sequencer_.waitForTurn(turn * 2, spinCutoff, updateSpinCutoff); - new (contents_) T(std::move(goner)); - sequencer_.completeTurn(turn * 2); - } else { - // simulate nothrow move with relocation, followed by default - // construction to fill the gap we created - sequencer_.waitForTurn(turn * 2, spinCutoff, updateSpinCutoff); - memcpy(contents_, &goner, sizeof(T)); - sequencer_.completeTurn(turn * 2); - new (&goner) T(); - } - } - - bool mayEnqueue(const uint32_t turn) const noexcept { - return sequencer_.isTurn(turn * 2); - } - - void dequeue(uint32_t turn, - Atom& spinCutoff, - const bool updateSpinCutoff, - T& elem) noexcept { - if (folly::IsRelocatable::value) { - // this version is preferred, because we do as much work as possible - // before waiting - try { - elem.~T(); - } catch (...) { - // unlikely, but if we don't complete our turn the queue will die - } - sequencer_.waitForTurn(turn * 2 + 1, spinCutoff, updateSpinCutoff); - memcpy(&elem, contents_, sizeof(T)); - sequencer_.completeTurn(turn * 2 + 1); - } else { - // use nothrow move assignment - sequencer_.waitForTurn(turn * 2 + 1, spinCutoff, updateSpinCutoff); - elem = std::move(*ptr()); - destroyContents(); - sequencer_.completeTurn(turn * 2 + 1); - } - } - - bool mayDequeue(const uint32_t turn) const noexcept { - return sequencer_.isTurn(turn * 2 + 1); - } - - private: - /// Storage for a T constructed with placement new - char contents_[sizeof(T)] __attribute__((aligned(alignof(T)))); - - /// Even turns are pushes, odd turns are pops - TurnSequencer sequencer_; - - T* ptr() noexcept { - return static_cast(static_cast(contents_)); - } - - void destroyContents() noexcept { - try { - ptr()->~T(); - } catch (...) { - // g++ doesn't seem to have std::is_nothrow_destructible yet - } -#ifndef NDEBUG - memset(contents_, 'Q', sizeof(T)); -#endif - } -}; - -} // namespace detail - -} // namespace folly diff --git a/hphp/third_party/folly/folly/Malloc.h b/hphp/third_party/folly/folly/Malloc.h deleted file mode 100644 index e10c8255e..000000000 --- a/hphp/third_party/folly/folly/Malloc.h +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright 2013 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. - */ - -// Functions to provide smarter use of jemalloc, if jemalloc is being used. -// http://www.canonware.com/download/jemalloc/jemalloc-latest/doc/jemalloc.html - -#ifndef FOLLY_MALLOC_H_ -#define FOLLY_MALLOC_H_ - -// If using fbstring from libstdc++, then just define stub code -// here to typedef the fbstring type into the folly namespace. -// This provides backwards compatibility for code that explicitly -// includes and uses fbstring. -#if defined(_GLIBCXX_USE_FB) && !defined(_LIBSTDCXX_FBSTRING) - -#include -namespace folly { - using std::goodMallocSize; - using std::jemallocMinInPlaceExpandable; - using std::usingJEMalloc; - using std::smartRealloc; - using std::checkedMalloc; - using std::checkedCalloc; - using std::checkedRealloc; -} - -#else // !defined(_GLIBCXX_USE_FB) || defined(_LIBSTDCXX_FBSTRING) - -#ifdef _LIBSTDCXX_FBSTRING -#pragma GCC system_header -#define FOLLY_HAVE_MALLOC_H 1 -#else -#include "folly/Portability.h" -#endif - -// for malloc_usable_size -// NOTE: FreeBSD 9 doesn't have malloc.h. It's defitions -// are found in stdlib.h. -#ifdef FOLLY_HAVE_MALLOC_H -#include -#else -#include -#endif - -#include -#include -#include -#include - -#include - -#include - -/** - * Define various ALLOCM_* macros normally provided by jemalloc. We define - * them so that we don't have to include jemalloc.h, in case the program is - * built without jemalloc support. - */ -#ifndef ALLOCM_SUCCESS - -#define ALLOCM_SUCCESS 0 -#define ALLOCM_ERR_OOM 1 -#define ALLOCM_ERR_NOT_MOVED 2 - -#define ALLOCM_ZERO 64 -#define ALLOCM_NO_MOVE 128 - -#define ALLOCM_LG_ALIGN(la) (la) - -#if defined(JEMALLOC_MANGLE) && defined(JEMALLOC_EXPERIMENTAL) -#define rallocm je_rallocm -#endif - -#endif /* ALLOCM_SUCCESS */ - -/** - * Declare rallocm() and malloc_usable_size() as weak symbols. It - * will be provided by jemalloc if we are using jemalloc, or it will - * be NULL if we are using another malloc implementation. - */ -extern "C" int rallocm(void**, size_t*, size_t, size_t, int) -__attribute__((weak)); - -#ifdef _LIBSTDCXX_FBSTRING -namespace std _GLIBCXX_VISIBILITY(default) { -_GLIBCXX_BEGIN_NAMESPACE_VERSION -#else -namespace folly { -#endif - - -/** - * Determine if we are using jemalloc or not. - */ -inline bool usingJEMalloc() { - return rallocm != NULL; -} - -/** - * For jemalloc's size classes, see - * http://www.canonware.com/download/jemalloc/jemalloc-latest/doc/jemalloc.html - */ -inline size_t goodMallocSize(size_t minSize) { - if (!usingJEMalloc()) { - // Not using jemalloc - no smarts - return minSize; - } - if (minSize <= 64) { - // Choose smallest allocation to be 64 bytes - no tripping over - // cache line boundaries, and small string optimization takes care - // of short strings anyway. - return 64; - } - if (minSize <= 512) { - // Round up to the next multiple of 64; we don't want to trip over - // cache line boundaries. - return (minSize + 63) & ~size_t(63); - } - if (minSize <= 3840) { - // Round up to the next multiple of 256 - return (minSize + 255) & ~size_t(255); - } - if (minSize <= 4072 * 1024) { - // Round up to the next multiple of 4KB - return (minSize + 4095) & ~size_t(4095); - } - // Holy Moly - // Round up to the next multiple of 4MB - return (minSize + 4194303) & ~size_t(4194303); -} - -// We always request "good" sizes for allocation, so jemalloc can -// never grow in place small blocks; they're already occupied to the -// brim. Blocks larger than or equal to 4096 bytes can in fact be -// expanded in place, and this constant reflects that. -static const size_t jemallocMinInPlaceExpandable = 4096; - -/** - * Trivial wrappers around malloc, calloc, realloc that check for allocation - * failure and throw std::bad_alloc in that case. - */ -inline void* checkedMalloc(size_t size) { - void* p = malloc(size); - if (!p) std::__throw_bad_alloc(); - return p; -} - -inline void* checkedCalloc(size_t n, size_t size) { - void* p = calloc(n, size); - if (!p) std::__throw_bad_alloc(); - return p; -} - -inline void* checkedRealloc(void* ptr, size_t size) { - void* p = realloc(ptr, size); - if (!p) std::__throw_bad_alloc(); - return p; -} - -/** - * This function tries to reallocate a buffer of which only the first - * currentSize bytes are used. The problem with using realloc is that - * if currentSize is relatively small _and_ if realloc decides it - * needs to move the memory chunk to a new buffer, then realloc ends - * up copying data that is not used. It's impossible to hook into - * GNU's malloc to figure whether expansion will occur in-place or as - * a malloc-copy-free troika. (If an expand_in_place primitive would - * be available, smartRealloc would use it.) As things stand, this - * routine just tries to call realloc() (thus benefitting of potential - * copy-free coalescing) unless there's too much slack memory. - */ -inline void* smartRealloc(void* p, - const size_t currentSize, - const size_t currentCapacity, - const size_t newCapacity) { - assert(p); - assert(currentSize <= currentCapacity && - currentCapacity < newCapacity); - - if (usingJEMalloc()) { - // using jemalloc's API. Don't forget that jemalloc can never grow - // in place blocks smaller than 4096 bytes. - if (currentCapacity >= jemallocMinInPlaceExpandable && - rallocm(&p, NULL, newCapacity, 0, ALLOCM_NO_MOVE) == ALLOCM_SUCCESS) { - // Managed to expand in place - return p; - } - // Cannot expand; must move - auto const result = checkedMalloc(newCapacity); - std::memcpy(result, p, currentSize); - free(p); - return result; - } - - // No jemalloc no honey - auto const slack = currentCapacity - currentSize; - if (slack * 2 > currentSize) { - // Too much slack, malloc-copy-free cycle: - auto const result = checkedMalloc(newCapacity); - std::memcpy(result, p, currentSize); - free(p); - return result; - } - // If there's not too much slack, we realloc in hope of coalescing - return checkedRealloc(p, newCapacity); -} - -#ifdef _LIBSTDCXX_FBSTRING -_GLIBCXX_END_NAMESPACE_VERSION -#endif - -} // folly - -#endif // !defined(_GLIBCXX_USE_FB) || defined(_LIBSTDCXX_FBSTRING) - -#endif // FOLLY_MALLOC_H_ diff --git a/hphp/third_party/folly/folly/MapUtil.h b/hphp/third_party/folly/folly/MapUtil.h deleted file mode 100644 index fbaf0deb6..000000000 --- a/hphp/third_party/folly/folly/MapUtil.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_MAPUTIL_H_ -#define FOLLY_MAPUTIL_H_ - -namespace folly { - -/** - * Given a map and a key, return the value corresponding to the key in the map, - * or a given default value if the key doesn't exist in the map. - */ -template -typename Map::mapped_type get_default( - const Map& map, const typename Map::key_type& key, - const typename Map::mapped_type& dflt = - typename Map::mapped_type()) { - auto pos = map.find(key); - return (pos != map.end() ? pos->second : dflt); -} - -/** - * Given a map and a key, return a reference to the value corresponding to the - * key in the map, or the given default reference if the key doesn't exist in - * the map. - */ -template -const typename Map::mapped_type& get_ref_default( - const Map& map, const typename Map::key_type& key, - const typename Map::mapped_type& dflt) { - auto pos = map.find(key); - return (pos != map.end() ? pos->second : dflt); -} - -/** - * Given a map and a key, return a pointer to the value corresponding to the - * key in the map, or nullptr if the key doesn't exist in the map. - */ -template -const typename Map::mapped_type* get_ptr( - const Map& map, const typename Map::key_type& key) { - auto pos = map.find(key); - return (pos != map.end() ? &pos->second : nullptr); -} - -/** - * Non-const overload of the above. - */ -template -typename Map::mapped_type* get_ptr( - Map& map, const typename Map::key_type& key) { - auto pos = map.find(key); - return (pos != map.end() ? &pos->second : nullptr); -} - -} // namespace folly - -#endif /* FOLLY_MAPUTIL_H_ */ - diff --git a/hphp/third_party/folly/folly/Memory.h b/hphp/third_party/folly/folly/Memory.h deleted file mode 100644 index ce2573d3c..000000000 --- a/hphp/third_party/folly/folly/Memory.h +++ /dev/null @@ -1,346 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_MEMORY_H_ -#define FOLLY_MEMORY_H_ - -#include "folly/Traits.h" - -#include -#include -#include -#include -#include - -#include - -namespace folly { - -/** - * For exception safety and consistency with make_shared. Erase me when - * we have std::make_unique(). - * - * @author Louis Brandy (ldbrandy@fb.com) - * @author Xu Ning (xning@fb.com) - */ - -template, typename... Args> -std::unique_ptr make_unique(Args&&... args) { - return std::unique_ptr(new T(std::forward(args)...)); -} - -/* - * StlAllocator wraps a SimpleAllocator into a STL-compliant - * allocator, maintaining an instance pointer to the simple allocator - * object. The underlying SimpleAllocator object must outlive all - * instances of StlAllocator using it. - * - * A SimpleAllocator must provide two methods: - * - * void* allocate(size_t size); - * void deallocate(void* ptr); - * - * which, respectively, allocate a block of size bytes (aligned to the - * maximum alignment required on your system), throwing std::bad_alloc - * if the allocation can't be satisfied, and free a previously - * allocated block. - * - * Note that the following allocator resembles the standard allocator - * quite well: - * - * class MallocAllocator { - * public: - * void* allocate(size_t size) { - * void* p = malloc(size); - * if (!p) throw std::bad_alloc(); - * return p; - * } - * void deallocate(void* p) { - * free(p); - * } - * }; - * - * But note that if you pass StlAllocator to a - * standard container it will be larger due to the contained state - * pointer. - * - * author: Tudor Bosman - */ - -// This would be so much simpler with std::allocator_traits, but gcc 4.6.2 -// doesn't support it. -template class StlAllocator; - -template class StlAllocator { - public: - typedef void value_type; - typedef void* pointer; - typedef const void* const_pointer; - - StlAllocator() : alloc_(nullptr) { } - explicit StlAllocator(Alloc* a) : alloc_(a) { } - - Alloc* alloc() const { - return alloc_; - } - - template struct rebind { - typedef StlAllocator other; - }; - - bool operator!=(const StlAllocator& other) const { - return alloc_ != other.alloc_; - } - - bool operator==(const StlAllocator& other) const { - return alloc_ == other.alloc_; - } - - private: - Alloc* alloc_; -}; - -template -class StlAllocator { - public: - typedef T value_type; - typedef T* pointer; - typedef const T* const_pointer; - typedef T& reference; - typedef const T& const_reference; - - typedef ptrdiff_t difference_type; - typedef size_t size_type; - - StlAllocator() : alloc_(nullptr) { } - explicit StlAllocator(Alloc* a) : alloc_(a) { } - - template StlAllocator(const StlAllocator& other) - : alloc_(other.alloc()) { } - - T* allocate(size_t n, const void* hint = nullptr) { - return static_cast(alloc_->allocate(n * sizeof(T))); - } - - void deallocate(T* p, size_t n) { - alloc_->deallocate(p); - } - - size_t max_size() const { - return std::numeric_limits::max(); - } - - T* address(T& x) const { - return std::addressof(x); - } - - const T* address(const T& x) const { - return std::addressof(x); - } - - template - void construct(T* p, Args&&... args) { - new (p) T(std::forward(args)...); - } - - void destroy(T* p) { - p->~T(); - } - - Alloc* alloc() const { - return alloc_; - } - - template struct rebind { - typedef StlAllocator other; - }; - - bool operator!=(const StlAllocator& other) const { - return alloc_ != other.alloc_; - } - - bool operator==(const StlAllocator& other) const { - return alloc_ == other.alloc_; - } - - private: - Alloc* alloc_; -}; - -/** - * Helper function to obtain rebound allocators - * - * @author: Marcelo Juchem - */ -template -typename Allocator::template rebind::other rebind_allocator( - Allocator const& allocator -) { - return typename Allocator::template rebind::other(allocator); -} - -/* - * Helper classes/functions for creating a unique_ptr using a custom - * allocator. - * - * @author: Marcelo Juchem - */ - -// Derives from the allocator to take advantage of the empty base -// optimization when possible. -template -class allocator_delete - : private std::remove_reference::type -{ - typedef typename std::remove_reference::type allocator_type; - -public: - typedef typename Allocator::pointer pointer; - - allocator_delete() = default; - - explicit allocator_delete(const allocator_type& allocator) - : allocator_type(allocator) - {} - - explicit allocator_delete(allocator_type&& allocator) - : allocator_type(std::move(allocator)) - {} - - template - allocator_delete(const allocator_delete& other) - : allocator_type(other.get_allocator()) - {} - - allocator_type& get_allocator() const { - return *const_cast(this); - } - - void operator()(pointer p) const { - if (!p) return; - const_cast(this)->destroy(p); - const_cast(this)->deallocate(p, 1); - } -}; - -template -class is_simple_allocator { - FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(has_destroy, destroy); - - typedef typename std::remove_const< - typename std::remove_reference::type - >::type allocator; - typedef typename std::remove_reference::type value_type; - typedef value_type* pointer; - -public: - constexpr static bool value = !has_destroy::value - && !has_destroy::value; -}; - -template -struct as_stl_allocator { - typedef typename std::conditional< - is_simple_allocator::value, - folly::StlAllocator< - typename std::remove_reference::type, - typename std::remove_reference::type - >, - typename std::remove_reference::type - >::type type; -}; - -template -typename std::enable_if< - is_simple_allocator::value, - folly::StlAllocator< - typename std::remove_reference::type, - typename std::remove_reference::type - > ->::type make_stl_allocator(Allocator&& allocator) { - return folly::StlAllocator< - typename std::remove_reference::type, - typename std::remove_reference::type - >(&allocator); -} - -template -typename std::enable_if< - !is_simple_allocator::value, - typename std::remove_reference::type ->::type make_stl_allocator(Allocator&& allocator) { - return std::move(allocator); -} - -/** - * AllocatorUniquePtr: a unique_ptr that supports both STL-style - * allocators and SimpleAllocator - * - * @author: Marcelo Juchem - */ - -template -struct AllocatorUniquePtr { - typedef std::unique_ptr::value, - folly::StlAllocator::type, T>, - typename std::remove_reference::type - >::type - > - > type; -}; - -/** - * Functions to allocate a unique_ptr / shared_ptr, supporting both - * STL-style allocators and SimpleAllocator, analog to std::allocate_shared - * - * @author: Marcelo Juchem - */ - -template -typename AllocatorUniquePtr::type allocate_unique( - Allocator&& allocator, Args&&... args -) { - auto stlAllocator = folly::make_stl_allocator( - std::forward(allocator) - ); - auto p = stlAllocator.allocate(1); - - try { - stlAllocator.construct(p, std::forward(args)...); - - return {p, - folly::allocator_delete(std::move(stlAllocator)) - }; - } catch (...) { - stlAllocator.deallocate(p, 1); - throw; - } -} - -template -std::shared_ptr allocate_shared(Allocator&& allocator, Args&&... args) { - return std::allocate_shared( - folly::make_stl_allocator(std::forward(allocator)), - std::forward(args)... - ); -} - -} // namespace folly - -#endif /* FOLLY_MEMORY_H_ */ diff --git a/hphp/third_party/folly/folly/MemoryMapping.cpp b/hphp/third_party/folly/folly/MemoryMapping.cpp deleted file mode 100644 index f55157111..000000000 --- a/hphp/third_party/folly/folly/MemoryMapping.cpp +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#include "folly/MemoryMapping.h" -#include "folly/Format.h" - -#include -#include -#include -#include - -#define FLAGS_mlock_chunk_size (1 << 20) // 1MB - - - -namespace folly { - -/* protected constructor */ -MemoryMapping::MemoryMapping() - : mapStart_(nullptr) - , mapLength_(0) - , locked_(false) { -} - -MemoryMapping::MemoryMapping(File file, off_t offset, off_t length) - : mapStart_(nullptr) - , mapLength_(0) - , locked_(false) { - - init(std::move(file), offset, length, PROT_READ, false); -} - -void MemoryMapping::init(File file, - off_t offset, off_t length, - int prot, - bool grow) { - off_t pageSize = sysconf(_SC_PAGESIZE); - CHECK_GE(offset, 0); - - // Round down the start of the mapped region - size_t skipStart = offset % pageSize; - offset -= skipStart; - - file_ = std::move(file); - mapLength_ = length; - if (mapLength_ != -1) { - mapLength_ += skipStart; - - // Round up the end of the mapped region - mapLength_ = (mapLength_ + pageSize - 1) / pageSize * pageSize; - } - - // stat the file - struct stat st; - CHECK_ERR(fstat(file_.fd(), &st)); - off_t remaining = st.st_size - offset; - if (mapLength_ == -1) { - length = mapLength_ = remaining; - } else { - if (length > remaining) { - if (grow) { - PCHECK(0 == ftruncate(file_.fd(), offset + length)) - << "ftructate() failed, couldn't grow file"; - remaining = length; - } else { - length = remaining; - } - } - if (mapLength_ > remaining) mapLength_ = remaining; - } - - if (length == 0) { - mapLength_ = 0; - mapStart_ = nullptr; - } else { - unsigned char* start = static_cast( - mmap(nullptr, mapLength_, prot, MAP_SHARED, file_.fd(), offset)); - PCHECK(start != MAP_FAILED) - << " offset=" << offset - << " length=" << mapLength_; - mapStart_ = start; - data_.reset(start + skipStart, length); - } -} - -namespace { - -off_t memOpChunkSize(off_t length) { - off_t chunkSize = length; - if (FLAGS_mlock_chunk_size <= 0) { - return chunkSize; - } - - chunkSize = FLAGS_mlock_chunk_size; - off_t pageSize = sysconf(_SC_PAGESIZE); - off_t r = chunkSize % pageSize; - if (r) { - chunkSize += (pageSize - r); - } - return chunkSize; -} - -/** - * Run @op in chunks over the buffer @mem of @bufSize length. - * - * Return: - * - success: true + amountSucceeded == bufSize (op success on whole buffer) - * - failure: false + amountSucceeded == nr bytes on which op succeeded. - */ -bool memOpInChunks(std::function op, - void* mem, size_t bufSize, - size_t& amountSucceeded) { - // unmap/mlock/munlock take a kernel semaphore and block other threads from - // doing other memory operations. If the size of the buffer is big the - // semaphore can be down for seconds (for benchmarks see - // http://kostja-osipov.livejournal.com/42963.html). Doing the operations in - // chunks breaks the locking into intervals and lets other threads do memory - // operations of their own. - - size_t chunkSize = memOpChunkSize(bufSize); - - char* addr = static_cast(mem); - amountSucceeded = 0; - - while (amountSucceeded < bufSize) { - size_t size = std::min(chunkSize, bufSize - amountSucceeded); - if (op(addr + amountSucceeded, size) != 0) { - return false; - } - amountSucceeded += size; - } - - return true; -} - -} // anonymous namespace - -bool MemoryMapping::mlock(LockMode lock) { - size_t amountSucceeded = 0; - locked_ = memOpInChunks(::mlock, mapStart_, mapLength_, amountSucceeded); - if (locked_) { - return true; - } - - auto msg(folly::format( - "mlock({}) failed at {}", - mapLength_, amountSucceeded).str()); - - if (lock == LockMode::TRY_LOCK && (errno == EPERM || errno == ENOMEM)) { - PLOG(WARNING) << msg; - } else { - PLOG(FATAL) << msg; - } - - // only part of the buffer was mlocked, unlock it back - if (!memOpInChunks(::munlock, mapStart_, amountSucceeded, amountSucceeded)) { - PLOG(WARNING) << "munlock()"; - } - - return false; -} - -void MemoryMapping::munlock(bool dontneed) { - if (!locked_) return; - - size_t amountSucceeded = 0; - if (!memOpInChunks(::munlock, mapStart_, mapLength_, amountSucceeded)) { - PLOG(WARNING) << "munlock()"; - } - if (mapLength_ && dontneed && - ::madvise(mapStart_, mapLength_, MADV_DONTNEED)) { - PLOG(WARNING) << "madvise()"; - } - locked_ = false; -} - -void MemoryMapping::hintLinearScan() { - advise(MADV_SEQUENTIAL); -} - -MemoryMapping::~MemoryMapping() { - if (mapLength_) { - size_t amountSucceeded = 0; - if (!memOpInChunks(::munmap, mapStart_, mapLength_, amountSucceeded)) { - PLOG(FATAL) << folly::format( - "munmap({}) failed at {}", - mapLength_, amountSucceeded).str(); - } - } -} - -void MemoryMapping::advise(int advice) const { - if (mapLength_ && ::madvise(mapStart_, mapLength_, advice)) { - PLOG(WARNING) << "madvise()"; - } -} - -WritableMemoryMapping::WritableMemoryMapping(File file, off_t offset, off_t length) { - init(std::move(file), offset, length, PROT_READ | PROT_WRITE, true); -} - -} // namespace folly diff --git a/hphp/third_party/folly/folly/MemoryMapping.h b/hphp/third_party/folly/folly/MemoryMapping.h deleted file mode 100644 index 3a355892e..000000000 --- a/hphp/third_party/folly/folly/MemoryMapping.h +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_MEMORYMAPPING_H_ -#define FOLLY_MEMORYMAPPING_H_ - -#include "folly/FBString.h" -#include "folly/File.h" -#include "folly/Range.h" -#include -#include - -namespace folly { - -/** - * Maps files in memory (read-only). - * - * @author Tudor Bosman (tudorb@fb.com) - */ -class MemoryMapping : boost::noncopyable { - public: - /** - * Lock the pages in memory? - * TRY_LOCK = try to lock, log warning if permission denied - * MUST_LOCK = lock, fail assertion if permission denied. - */ - enum class LockMode { - TRY_LOCK, - MUST_LOCK - }; - /** - * Map a portion of the file indicated by filename in memory, causing a CHECK - * failure on error. - * - * By default, map the whole file. length=-1: map from offset to EOF. - * Unlike the mmap() system call, offset and length don't need to be - * page-aligned. length is clipped to the end of the file if it's too large. - * - * The mapping will be destroyed (and the memory pointed-to by data() will - * likely become inaccessible) when the MemoryMapping object is destroyed. - */ - explicit MemoryMapping(File file, - off_t offset=0, - off_t length=-1); - - virtual ~MemoryMapping(); - - /** - * Lock the pages in memory - */ - bool mlock(LockMode lock); - - /** - * Unlock the pages. - * If dontneed is true, the kernel is instructed to release these pages - * (per madvise(MADV_DONTNEED)). - */ - void munlock(bool dontneed=false); - - /** - * Hint that these pages will be scanned linearly. - * madvise(MADV_SEQUENTIAL) - */ - void hintLinearScan(); - - /** - * Advise the kernel about memory access. - */ - void advise(int advice) const; - - /** - * A bitwise cast of the mapped bytes as range of values. Only intended for - * use with POD or in-place usable types. - */ - template - Range asRange() const { - size_t count = data_.size() / sizeof(T); - return Range(static_cast( - static_cast(data_.data())), - count); - } - - /** - * A range of bytes mapped by this mapping. - */ - Range range() const { - return {data_.begin(), data_.end()}; - } - - /** - * Return the memory area where the file was mapped. - */ - StringPiece data() const { - return asRange(); - } - - bool mlocked() const { - return locked_; - } - - int fd() const { return file_.fd(); } - - protected: - MemoryMapping(); - - void init(File file, - off_t offset, off_t length, - int prot, - bool grow); - - File file_; - void* mapStart_; - off_t mapLength_; - bool locked_; - Range data_; -}; - -/** - * Maps files in memory for writing. - * - * @author Tom Jackson (tjackson@fb.com) - */ -class WritableMemoryMapping : public MemoryMapping { - public: - explicit WritableMemoryMapping(File file, - off_t offset = 0, - off_t length = -1); - /** - * A bitwise cast of the mapped bytes as range of mutable values. Only - * intended for use with POD or in-place usable types. - */ - template - Range asWritableRange() const { - size_t count = data_.size() / sizeof(T); - return Range(static_cast( - static_cast(data_.data())), - count); - } - - /** - * A range of mutable bytes mapped by this mapping. - */ - Range writableRange() const { - return data_; - } -}; - -} // namespace folly - -#endif /* FOLLY_MEMORYMAPPING_H_ */ diff --git a/hphp/third_party/folly/folly/Optional.h b/hphp/third_party/folly/folly/Optional.h deleted file mode 100644 index 4295e2715..000000000 --- a/hphp/third_party/folly/folly/Optional.h +++ /dev/null @@ -1,310 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_OPTIONAL_H_ -#define FOLLY_OPTIONAL_H_ - -/* - * Optional - For conditional initialization of values, like boost::optional, - * but with support for move semantics and emplacement. Reference type support - * has not been included due to limited use cases and potential confusion with - * semantics of assignment: Assigning to an optional reference could quite - * reasonably copy its value or redirect the reference. - * - * Optional can be useful when a variable might or might not be needed: - * - * Optional maybeLogger = ...; - * if (maybeLogger) { - * maybeLogger->log("hello"); - * } - * - * Optional enables a 'null' value for types which do not otherwise have - * nullability, especially useful for parameter passing: - * - * void testIterator(const unique_ptr& it, - * initializer_list idsExpected, - * Optional> ranksExpected = none) { - * for (int i = 0; it->next(); ++i) { - * EXPECT_EQ(it->doc().id(), idsExpected[i]); - * if (ranksExpected) { - * EXPECT_EQ(it->doc().rank(), (*ranksExpected)[i]); - * } - * } - * } - * - * Optional models OptionalPointee, so calling 'get_pointer(opt)' will return a - * pointer to nullptr if the 'opt' is empty, and a pointer to the value if it is - * not: - * - * Optional maybeInt = ...; - * if (int* v = get_pointer(maybeInt)) { - * cout << *v << endl; - * } - */ -#include -#include -#include -#include - -#include - - -namespace folly { - -namespace detail { struct NoneHelper {}; } - -typedef int detail::NoneHelper::*None; - -const None none = nullptr; - -/** - * gcc-4.7 warns about use of uninitialized memory around the use of storage_ - * even though this is explicitly initialized at each point. - */ -#if defined(__GNUC__) && !defined(__clang__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wuninitialized" -# pragma GCC diagnostic ignored "-Wpragmas" -# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" -#endif // __GNUC__ - -template -class Optional { - public: - static_assert(!std::is_reference::value, - "Optional may not be used with reference types"); - - Optional() - : hasValue_(false) { - } - - Optional(const Optional& src) { - if (src.hasValue()) { - construct(src.value()); - } else { - hasValue_ = false; - } - } - - Optional(Optional&& src) { - if (src.hasValue()) { - construct(std::move(src.value())); - src.clear(); - } else { - hasValue_ = false; - } - } - - /* implicit */ Optional(const None&) - : hasValue_(false) { - } - - /* implicit */ Optional(Value&& newValue) { - construct(std::move(newValue)); - } - - /* implicit */ Optional(const Value& newValue) { - construct(newValue); - } - - ~Optional() { - clear(); - } - - void assign(const None&) { - clear(); - } - - void assign(Optional&& src) { - if (src.hasValue()) { - assign(std::move(src.value())); - src.clear(); - } else { - clear(); - } - } - - void assign(const Optional& src) { - if (src.hasValue()) { - assign(src.value()); - } else { - clear(); - } - } - - void assign(Value&& newValue) { - if (hasValue()) { - value_ = std::move(newValue); - } else { - construct(std::move(newValue)); - } - } - - void assign(const Value& newValue) { - if (hasValue()) { - value_ = newValue; - } else { - construct(newValue); - } - } - - template - Optional& operator=(Arg&& arg) { - assign(std::forward(arg)); - return *this; - } - - Optional& operator=(Optional &&other) { - assign(std::move(other)); - return *this; - } - - Optional& operator=(const Optional &other) { - assign(other); - return *this; - } - - template - void emplace(Args&&... args) { - clear(); - construct(std::forward(args)...); - } - - void clear() { - if (hasValue()) { - hasValue_ = false; - value_.~Value(); - } - } - - const Value& value() const { - assert(hasValue()); - return value_; - } - - Value& value() { - assert(hasValue()); - return value_; - } - - bool hasValue() const { return hasValue_; } - - explicit operator bool() const { - return hasValue(); - } - - const Value& operator*() const { return value(); } - Value& operator*() { return value(); } - - const Value* operator->() const { return &value(); } - Value* operator->() { return &value(); } - - private: - template - void construct(Args&&... args) { - const void* ptr = &value_; - // for supporting const types - new(const_cast(ptr)) Value(std::forward(args)...); - hasValue_ = true; - } - - // uninitialized - union { Value value_; }; - bool hasValue_; -}; - -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic pop -#endif - -template -const T* get_pointer(const Optional& opt) { - return opt ? &opt.value() : nullptr; -} - -template -T* get_pointer(Optional& opt) { - return opt ? &opt.value() : nullptr; -} - -template -void swap(Optional& a, Optional& b) { - if (a.hasValue() && b.hasValue()) { - // both full - using std::swap; - swap(a.value(), b.value()); - } else if (a.hasValue() || b.hasValue()) { - std::swap(a, b); // fall back to default implementation if they're mixed. - } -} - -template::type>> -Opt make_optional(T&& v) { - return Opt(std::forward(v)); -} - -template -bool operator< (const Optional& a, const Optional& b) { - if (a.hasValue() != b.hasValue()) { return a.hasValue() < b.hasValue(); } - if (a.hasValue()) { return a.value() < b.value(); } - return false; -} - -template -bool operator==(const Optional& a, const Optional& b) { - if (a.hasValue() != b.hasValue()) { return false; } - if (a.hasValue()) { return a.value() == b.value(); } - return true; -} - -template -bool operator<=(const Optional& a, const Optional& b) { - return !(b < a); -} - -template -bool operator!=(const Optional& a, const Optional& b) { - return !(b == a); -} - -template -bool operator>=(const Optional& a, const Optional& b) { - return !(a < b); -} - -template -bool operator> (const Optional& a, const Optional& b) { - return b < a; -} - -// To supress comparability of Optional with T, despite implicit conversion. -template bool operator< (const Optional&, const V& other) = delete; -template bool operator<=(const Optional&, const V& other) = delete; -template bool operator==(const Optional&, const V& other) = delete; -template bool operator!=(const Optional&, const V& other) = delete; -template bool operator>=(const Optional&, const V& other) = delete; -template bool operator> (const Optional&, const V& other) = delete; -template bool operator< (const V& other, const Optional&) = delete; -template bool operator<=(const V& other, const Optional&) = delete; -template bool operator==(const V& other, const Optional&) = delete; -template bool operator!=(const V& other, const Optional&) = delete; -template bool operator>=(const V& other, const Optional&) = delete; -template bool operator> (const V& other, const Optional&) = delete; - -} // namespace folly - -#endif//FOLLY_OPTIONAL_H_ diff --git a/hphp/third_party/folly/folly/PackedSyncPtr.h b/hphp/third_party/folly/folly/PackedSyncPtr.h deleted file mode 100644 index 40d92106e..000000000 --- a/hphp/third_party/folly/folly/PackedSyncPtr.h +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_PACKEDSYNCPTR_H_ -#define FOLLY_PACKEDSYNCPTR_H_ - -#ifndef __x86_64__ -# error "PackedSyncPtr is x64-specific code." -#endif - -/* - * An 8-byte pointer with an integrated spin lock and 15-bit integer - * (you can use this for a size of the allocation, if you want, or - * something else, or nothing). - * - * This is using an x64-specific detail about the effective virtual - * address space. Long story short: the upper two bytes of all our - * pointers will be zero in reality---and if you have a couple billion - * such pointers in core, it makes pretty good sense to try to make - * use of that memory. The exact details can be perused here: - * - * http://en.wikipedia.org/wiki/X86-64#Canonical_form_addresses - * - * This is not a "smart" pointer: nothing automagical is going on - * here. Locking is up to the user. Resource deallocation is up to - * the user. Locks are never acquired or released outside explicit - * calls to lock() and unlock(). - * - * Change the value of the raw pointer with set(), but you must hold - * the lock when calling this function if multiple threads could be - * using this class. - * - * TODO(jdelong): should we use the low order bit for the lock, so we - * get a whole 16-bits for our integer? (There's also 2 more bits - * down there if the pointer comes from malloc.) - * - * @author Spencer Ahrens - * @author Jordan DeLong - */ - -#include "folly/SmallLocks.h" -#include -#include - -namespace folly { - -template -class PackedSyncPtr { - // This just allows using this class even with T=void. Attempting - // to use the operator* or operator[] on a PackedSyncPtr will - // still properly result in a compile error. - typedef typename std::add_lvalue_reference::type reference; - -public: - /* - * If you default construct one of these, you must call this init() - * function before using it. - * - * (We are avoiding a constructor to ensure gcc allows us to put - * this class in packed structures.) - */ - void init(T* initialPtr = 0, uint16_t initialExtra = 0) { - auto intPtr = reinterpret_cast(initialPtr); - CHECK(!(intPtr >> 48)); - data_.init(intPtr); - setExtra(initialExtra); - } - - /* - * Sets a new pointer. You must hold the lock when calling this - * function, or else be able to guarantee no other threads could be - * using this PackedSyncPtr<>. - */ - void set(T* t) { - auto intPtr = reinterpret_cast(t); - auto shiftedExtra = uintptr_t(extra()) << 48; - CHECK(!(intPtr >> 48)); - data_.setData(intPtr | shiftedExtra); - } - - /* - * Get the pointer. - * - * You can call any of these without holding the lock, with the - * normal types of behavior you'll get on x64 from reading a pointer - * without locking. - */ - T* get() const { - return reinterpret_cast(data_.getData() & (-1ull >> 16)); - } - T* operator->() const { return get(); } - reference operator*() const { return *get(); } - reference operator[](std::ptrdiff_t i) const { return get()[i]; } - - // Synchronization (logically const, even though this mutates our - // locked state: you can lock a const PackedSyncPtr to read it). - void lock() const { data_.lock(); } - void unlock() const { data_.unlock(); } - bool try_lock() const { return data_.try_lock(); } - - /* - * Access extra data stored in unused bytes of the pointer. - * - * It is ok to call this without holding the lock. - */ - uint16_t extra() const { - return data_.getData() >> 48; - } - - /* - * Don't try to put anything into this that has the high bit set: - * that's what we're using for the mutex. - * - * Don't call this without holding the lock. - */ - void setExtra(uint16_t extra) { - CHECK(!(extra & 0x8000)); - auto ptr = data_.getData() & (-1ull >> 16); - data_.setData((uintptr_t(extra) << 48) | ptr); - } - - // Logically private, but we can't have private data members and - // still be considered a POD. (In C++11 we are still a standard - // layout struct if this is private, but it doesn't matter, since - // gcc (4.6) won't let us use this with attribute packed still in - // that case.) - PicoSpinLock data_; -}; - -static_assert(sizeof(PackedSyncPtr) == 8, - "PackedSyncPtr should be only 8 bytes---something is " - "messed up"); - -} - -#endif - diff --git a/hphp/third_party/folly/folly/Padded.h b/hphp/third_party/folly/folly/Padded.h deleted file mode 100644 index 6de44a3bc..000000000 --- a/hphp/third_party/folly/folly/Padded.h +++ /dev/null @@ -1,506 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_PADDED_H_ -#define FOLLY_PADDED_H_ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "folly/Portability.h" - -/** - * Code that aids in storing data aligned on block (possibly cache-line) - * boundaries, perhaps with padding. - * - * Class Node represents one block. Given an iterator to a container of - * Node, class Iterator encapsulates an iterator to the underlying elements. - * Adaptor converts a sequence of Node into a sequence of underlying elements - * (not fully compatible with STL container requirements, see comments - * near the Node class declaration). - */ - -namespace folly { -namespace padded { - -/** - * A Node is a fixed-size container of as many objects of type T as would - * fit in a region of memory of size NS. The last NS % sizeof(T) - * bytes are ignored and uninitialized. - * - * Node only works for trivial types, which is usually not a concern. This - * is intentional: Node itself is trivial, which means that it can be - * serialized / deserialized using a simple memcpy. - */ -template -class Node; - -namespace detail { -// Shortcut to avoid writing the long enable_if expression every time -template struct NodeValid; -template -struct NodeValid::value && - sizeof(T) <= NS && - NS % alignof(T) == 0)>::type> { - typedef void type; -}; -} // namespace detail - -template -class Node::type> { - public: - typedef T value_type; - static constexpr size_t kNodeSize = NS; - static constexpr size_t kElementCount = NS / sizeof(T); - static constexpr size_t kPaddingBytes = NS % sizeof(T); - - T* data() { return storage_.data; } - const T* data() const { return storage_.data; } - - bool operator==(const Node& other) const { - return memcmp(data(), other.data(), sizeof(T) * kElementCount) == 0; - } - bool operator!=(const Node& other) const { - return !(*this == other); - } - - /** - * Return the number of nodes needed to represent n values. Rounds up. - */ - static constexpr size_t nodeCount(size_t n) { - return (n + kElementCount - 1) / kElementCount; - } - - /** - * Return the total byte size needed to represent n values, rounded up - * to the nearest full node. - */ - static constexpr size_t paddedByteSize(size_t n) { - return nodeCount(n) * NS; - } - - /** - * Return the number of bytes used for padding n values. - * Note that, even if n is a multiple of kElementCount, this may - * return non-zero if kPaddingBytes != 0, as the padding at the end of - * the last node is not included in the result. - */ - static constexpr size_t paddingBytes(size_t n) { - return (n ? (kPaddingBytes + - (kElementCount - 1 - (n-1) % kElementCount) * sizeof(T)) : - 0); - } - - /** - * Return the minimum byte size needed to represent n values. - * Does not round up. Even if n is a multiple of kElementCount, this - * may be different from paddedByteSize() if kPaddingBytes != 0, as - * the padding at the end of the last node is not included in the result. - * Note that the calculation below works for n=0 correctly (returns 0). - */ - static constexpr size_t unpaddedByteSize(size_t n) { - return paddedByteSize(n) - paddingBytes(n); - } - - private: - union Storage { - unsigned char bytes[NS]; - T data[kElementCount]; - } storage_; -}; - -// We must define kElementCount and kPaddingBytes to work around a bug -// in gtest that odr-uses them. -template constexpr size_t -Node::type>::kNodeSize; -template constexpr size_t -Node::type>::kElementCount; -template constexpr size_t -Node::type>::kPaddingBytes; - -template class Iterator; - -namespace detail { - -// Helper class to transfer the constness from From (a lvalue reference) -// and create a lvalue reference to To. -// -// TransferReferenceConstness -> const int& -// TransferReferenceConstness -> int& -// TransferReferenceConstness -> const int& -template -struct TransferReferenceConstness; - -template -struct TransferReferenceConstness< - From, To, typename std::enable_if::type>::value>::type> { - typedef typename std::add_lvalue_reference< - typename std::add_const::type>::type type; -}; - -template -struct TransferReferenceConstness< - From, To, typename std::enable_if::type>::value>::type> { - typedef typename std::add_lvalue_reference::type type; -}; - -// Helper class template to define a base class for Iterator (below) and save -// typing. -template -struct IteratorBase { - typedef boost::iterator_adaptor< - // CRTC - Iterator, - // Base iterator type - Iter, - // Value type - typename std::iterator_traits::value_type::value_type, - // Category or traversal - boost::use_default, - // Reference type - typename detail::TransferReferenceConstness< - typename std::iterator_traits::reference, - typename std::iterator_traits::value_type::value_type - >::type - > type; -}; - -} // namespace detail - -/** - * Wrapper around iterators to Node to return iterators to the underlying - * node elements. - */ -template -class Iterator : public detail::IteratorBase::type { - typedef typename detail::IteratorBase::type Super; - public: - typedef typename std::iterator_traits::value_type Node; - - Iterator() : pos_(0) { } - - explicit Iterator(Iter base) - : Super(base), - pos_(0) { - } - - // Return the current node and the position inside the node - const Node& node() const { return *this->base_reference(); } - size_t pos() const { return pos_; } - - private: - typename Super::reference dereference() const { - return (*this->base_reference()).data()[pos_]; - } - - bool equal(const Iterator& other) const { - return (this->base_reference() == other.base_reference() && - pos_ == other.pos_); - } - - void advance(typename Super::difference_type n) { - constexpr ssize_t elementCount = Node::kElementCount; // signed! - ssize_t newPos = pos_ + n; - if (newPos >= 0 && newPos < elementCount) { - pos_ = newPos; - return; - } - ssize_t nblocks = newPos / elementCount; - newPos %= elementCount; - if (newPos < 0) { - --nblocks; // negative - newPos += elementCount; - } - this->base_reference() += nblocks; - pos_ = newPos; - } - - void increment() { - if (++pos_ == Node::kElementCount) { - ++this->base_reference(); - pos_ = 0; - } - } - - void decrement() { - if (--pos_ == -1) { - --this->base_reference(); - pos_ = Node::kElementCount - 1; - } - } - - typename Super::difference_type distance_to(const Iterator& other) const { - constexpr ssize_t elementCount = Node::kElementCount; // signed! - ssize_t nblocks = - std::distance(this->base_reference(), other.base_reference()); - return nblocks * elementCount + (other.pos_ - pos_); - } - - friend class boost::iterator_core_access; - ssize_t pos_; // signed for easier advance() implementation -}; - -/** - * Given a container to Node, return iterators to the first element in - * the first Node / one past the last element in the last Node. - * Note that the last node is assumed to be full; if that's not the case, - * subtract from end() as appropriate. - */ - -template -Iterator cbegin(const Container& c) { - return Iterator(std::begin(c)); -} - -template -Iterator cend(const Container& c) { - return Iterator(std::end(c)); -} - -template -Iterator begin(const Container& c) { - return cbegin(c); -} - -template -Iterator end(const Container& c) { - return cend(c); -} - -template -Iterator begin(Container& c) { - return Iterator(std::begin(c)); -} - -template -Iterator end(Container& c) { - return Iterator(std::end(c)); -} - -/** - * Adaptor around a STL sequence container. - * - * Converts a sequence of Node into a sequence of its underlying elements - * (with enough functionality to make it useful, although it's not fully - * compatible with the STL containre requiremenets, see below). - * - * Provides iterators (of the same category as those of the underlying - * container), size(), front(), back(), push_back(), pop_back(), and const / - * non-const versions of operator[] (if the underlying container supports - * them). Does not provide push_front() / pop_front() or arbitrary insert / - * emplace / erase. Also provides reserve() / capacity() if supported by the - * underlying container. - * - * Yes, it's called Adaptor, not Adapter, as that's the name used by the STL - * and by boost. Deal with it. - * - * Internally, we hold a container of Node and the number of elements in - * the last block. We don't keep empty blocks, so the number of elements in - * the last block is always between 1 and Node::kElementCount (inclusive). - * (this is true if the container is empty as well to make push_back() simpler, - * see the implementation of the size() method for details). - */ -template -class Adaptor { - public: - typedef typename Container::value_type Node; - typedef typename Node::value_type value_type; - typedef value_type& reference; - typedef const value_type& const_reference; - typedef Iterator iterator; - typedef Iterator const_iterator; - typedef typename const_iterator::difference_type difference_type; - typedef typename Container::size_type size_type; - - static constexpr size_t kElementsPerNode = Node::kElementCount; - // Constructors - Adaptor() : lastCount_(Node::kElementCount) { } - explicit Adaptor(Container c, size_t lastCount=Node::kElementCount) - : c_(std::move(c)), - lastCount_(lastCount) { - } - explicit Adaptor(size_t n, const value_type& value = value_type()) - : c_(Node::nodeCount(n), fullNode(value)), - lastCount_(n % Node::kElementCount ?: Node::kElementCount) { - } - - Adaptor(const Adaptor&) = default; - Adaptor& operator=(const Adaptor&) = default; - Adaptor(Adaptor&& other) - : c_(std::move(other.c_)), - lastCount_(other.lastCount_) { - other.lastCount_ = Node::kElementCount; - } - Adaptor& operator=(Adaptor&& other) { - if (this != &other) { - c_ = std::move(other.c_); - lastCount_ = other.lastCount_; - other.lastCount_ = Node::kElementCount; - } - return *this; - } - - // Iterators - const_iterator cbegin() const { - return const_iterator(c_.begin()); - } - const_iterator cend() const { - auto it = const_iterator(c_.end()); - if (lastCount_ != Node::kElementCount) { - it -= (Node::kElementCount - lastCount_); - } - return it; - } - const_iterator begin() const { return cbegin(); } - const_iterator end() const { return cend(); } - iterator begin() { - return iterator(c_.begin()); - } - iterator end() { - auto it = iterator(c_.end()); - if (lastCount_ != Node::kElementCount) { - it -= (Node::kElementCount - lastCount_); - } - return it; - } - void swap(Adaptor& other) { - using std::swap; - swap(c_, other.c_); - swap(lastCount_, other.lastCount_); - } - bool empty() const { - return c_.empty(); - } - size_type size() const { - return (c_.empty() ? 0 : - (c_.size() - 1) * Node::kElementCount + lastCount_); - } - size_type max_size() const { - return ((c_.max_size() <= std::numeric_limits::max() / - Node::kElementCount) ? - c_.max_size() * Node::kElementCount : - std::numeric_limits::max()); - } - - const value_type& front() const { - assert(!empty()); - return c_.front().data()[0]; - } - value_type& front() { - assert(!empty()); - return c_.front().data()[0]; - } - - const value_type& back() const { - assert(!empty()); - return c_.back().data()[lastCount_ - 1]; - } - value_type& back() { - assert(!empty()); - return c_.back().data()[lastCount_ - 1]; - } - - void push_back(value_type x) { - if (lastCount_ == Node::kElementCount) { - c_.push_back(Node()); - lastCount_ = 0; - } - c_.back().data()[lastCount_++] = std::move(x); - } - - void pop_back() { - assert(!empty()); - if (--lastCount_ == 0) { - c_.pop_back(); - lastCount_ = Node::kElementCount; - } - } - - void clear() { - c_.clear(); - lastCount_ = Node::kElementCount; - } - - void reserve(size_type n) { - assert(n >= 0); - c_.reserve(Node::nodeCount(n)); - } - - size_type capacity() const { - return c_.capacity() * Node::kElementCount; - } - - const value_type& operator[](size_type idx) const { - return c_[idx / Node::kElementCount].data()[idx % Node::kElementCount]; - } - value_type& operator[](size_type idx) { - return c_[idx / Node::kElementCount].data()[idx % Node::kElementCount]; - } - - /** - * Return the underlying container and number of elements in the last block, - * and clear *this. Useful when you want to process the data as Nodes - * (again) and want to avoid copies. - */ - std::pair move() { - std::pair p(std::move(c_), lastCount_); - lastCount_ = Node::kElementCount; - return std::move(p); - } - - /** - * Return a const reference to the underlying container and the current - * number of elements in the last block. - */ - std::pair peek() const { - return std::make_pair(std::cref(c_), lastCount_); - } - - void padToFullNode(const value_type& padValue) { - // the if is necessary because c_ may be empty so we can't call c_.back() - if (lastCount_ != Node::kElementCount) { - auto last = c_.back().data(); - std::fill(last + lastCount_, last + Node::kElementCount, padValue); - lastCount_ = Node::kElementCount; - } - } - - private: - static Node fullNode(const value_type& value) { - Node n; - std::fill(n.data(), n.data() + kElementsPerNode, value); - return n; - } - Container c_; // container of Nodes - size_t lastCount_; // number of elements in last Node -}; - -} // namespace padded -} // namespace folly - -#endif /* FOLLY_PADDED_H_ */ - diff --git a/hphp/third_party/folly/folly/Portability.h b/hphp/third_party/folly/folly/Portability.h deleted file mode 100644 index 2989ebea1..000000000 --- a/hphp/third_party/folly/folly/Portability.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_PORTABILITY_H_ -#define FOLLY_PORTABILITY_H_ - -#ifndef FOLLY_NO_CONFIG -#include "folly-config.h" -#endif - -#ifdef FOLLY_HAVE_FEATURES_H -#include -#endif - - -#ifdef FOLLY_HAVE_SCHED_H - #include - #ifndef FOLLY_HAVE_PTHREAD_YIELD - #define pthread_yield sched_yield - #endif -#endif - - -// MaxAlign: max_align_t isn't supported by gcc -#ifdef __GNUC__ -struct MaxAlign { char c; } __attribute__((aligned)); -#else /* !__GNUC__ */ -# error Cannot define MaxAlign on this platform -#endif - - -// noreturn -#if defined(__clang__) || defined(__GNUC__) -# define FOLLY_NORETURN __attribute__((noreturn)) -#else -# define FOLLY_NORETURN -#endif - - -// portable version check -#ifndef __GNUC_PREREQ -# if defined __GNUC__ && defined __GNUC_MINOR__ -# define __GNUC_PREREQ(maj, min) ((__GNUC__ << 16) + __GNUC_MINOR__ >= \ - ((maj) << 16) + (min)) -# else -# define __GNUC_PREREQ(maj, min) 0 -# endif -#endif - - -/* Define macro wrappers for C++11's "final" and "override" keywords, which - * are supported in gcc 4.7 but not gcc 4.6. */ -#if !defined(FOLLY_FINAL) && !defined(FOLLY_OVERRIDE) -# if defined(__clang__) || __GNUC_PREREQ(4, 7) -# define FOLLY_FINAL final -# define FOLLY_OVERRIDE override -# else -# define FOLLY_FINAL /**/ -# define FOLLY_OVERRIDE /**/ -# endif -#endif - - -// Define to 1 if you have the `preadv' and `pwritev' functions, respectively -#if !defined(FOLLY_HAVE_PREADV) && !defined(FOLLY_HAVE_PWRITEV) -# if defined(__GLIBC_PREREQ) -# if __GLIBC_PREREQ(2, 10) -# define FOLLY_HAVE_PREADV 1 -# define FOLLY_HAVE_PWRITEV 1 -# endif -# endif -#endif - -#endif // FOLLY_PORTABILITY_H_ diff --git a/hphp/third_party/folly/folly/Preprocessor.h b/hphp/third_party/folly/folly/Preprocessor.h deleted file mode 100644 index 20fafabf9..000000000 --- a/hphp/third_party/folly/folly/Preprocessor.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2013 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. - */ - -// @author: Andrei Alexandrescu - -#ifndef FOLLY_PREPROCESSOR_ -#define FOLLY_PREPROCESSOR_ - -/** - * Necessarily evil preprocessor-related amenities. - */ - -/** - * FB_ONE_OR_NONE(hello, world) expands to hello and - * FB_ONE_OR_NONE(hello) expands to nothing. This macro is used to - * insert or eliminate text based on the presence of another argument. - */ -#define FB_ONE_OR_NONE(a, ...) FB_THIRD(a, ## __VA_ARGS__, a) -#define FB_THIRD(a, b, ...) __VA_ARGS__ - -/** - * Helper macro that extracts the first argument out of a list of any - * number of arguments. - */ -#define FB_ARG_1(a, ...) a - -/** - * Helper macro that extracts the second argument out of a list of any - * number of arguments. If only one argument is given, it returns - * that. - */ -#define FB_ARG_2_OR_1(...) FB_ARG_2_OR_1_IMPL(__VA_ARGS__, __VA_ARGS__) -// Support macro for the above -#define FB_ARG_2_OR_1_IMPL(a, b, ...) b - -/** - * FB_ANONYMOUS_VARIABLE(str) introduces an identifier starting with - * str and ending with a number that varies with the line. - */ -#ifndef FB_ANONYMOUS_VARIABLE -#define FB_CONCATENATE_IMPL(s1, s2) s1##s2 -#define FB_CONCATENATE(s1, s2) FB_CONCATENATE_IMPL(s1, s2) -#ifdef __COUNTER__ -#define FB_ANONYMOUS_VARIABLE(str) FB_CONCATENATE(str, __COUNTER__) -#else -#define FB_ANONYMOUS_VARIABLE(str) FB_CONCATENATE(str, __LINE__) -#endif -#endif - -/** - * Use FB_STRINGIZE(name) when you'd want to do what #name does inside - * another macro expansion. - */ -#define FB_STRINGIZE(name) #name - - -#endif // FOLLY_PREPROCESSOR_ diff --git a/hphp/third_party/folly/folly/ProducerConsumerQueue.h b/hphp/third_party/folly/folly/ProducerConsumerQueue.h deleted file mode 100644 index 2a6ed376f..000000000 --- a/hphp/third_party/folly/folly/ProducerConsumerQueue.h +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright 2013 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. - */ - -// @author Bo Hu (bhu@fb.com) -// @author Jordan DeLong (delong.j@fb.com) - -#ifndef PRODUCER_CONSUMER_QUEUE_H_ -#define PRODUCER_CONSUMER_QUEUE_H_ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace folly { - -/* - * ProducerConsumerQueue is a one producer and one consumer queue - * without locks. - */ -template -struct ProducerConsumerQueue : private boost::noncopyable { - typedef T value_type; - - // size must be >= 2. - // - // Also, note that the number of usable slots in the queue at any - // given time is actually (size-1), so if you start with an empty queue, - // isFull() will return true after size-1 insertions. - explicit ProducerConsumerQueue(uint32_t size) - : size_(size) - , records_(static_cast(std::malloc(sizeof(T) * size))) - , readIndex_(0) - , writeIndex_(0) - { - assert(size >= 2); - if (!records_) { - throw std::bad_alloc(); - } - } - - ~ProducerConsumerQueue() { - // We need to destruct anything that may still exist in our queue. - // (No real synchronization needed at destructor time: only one - // thread can be doing this.) - if (!boost::has_trivial_destructor::value) { - int read = readIndex_; - int end = writeIndex_; - while (read != end) { - records_[read].~T(); - if (++read == size_) { - read = 0; - } - } - } - - std::free(records_); - } - - template - bool write(Args&&... recordArgs) { - auto const currentWrite = writeIndex_.load(std::memory_order_relaxed); - auto nextRecord = currentWrite + 1; - if (nextRecord == size_) { - nextRecord = 0; - } - if (nextRecord != readIndex_.load(std::memory_order_acquire)) { - new (&records_[currentWrite]) T(std::forward(recordArgs)...); - writeIndex_.store(nextRecord, std::memory_order_release); - return true; - } - - // queue is full - return false; - } - - // move (or copy) the value at the front of the queue to given variable - bool read(T& record) { - auto const currentRead = readIndex_.load(std::memory_order_relaxed); - if (currentRead == writeIndex_.load(std::memory_order_acquire)) { - // queue is empty - return false; - } - - auto nextRecord = currentRead + 1; - if (nextRecord == size_) { - nextRecord = 0; - } - record = std::move(records_[currentRead]); - records_[currentRead].~T(); - readIndex_.store(nextRecord, std::memory_order_release); - return true; - } - - // pointer to the value at the front of the queue (for use in-place) or - // nullptr if empty. - T* frontPtr() { - auto const currentRead = readIndex_.load(std::memory_order_relaxed); - if (currentRead == writeIndex_.load(std::memory_order_acquire)) { - // queue is empty - return nullptr; - } - return &records_[currentRead]; - } - - // queue must not be empty - void popFront() { - auto const currentRead = readIndex_.load(std::memory_order_relaxed); - assert(currentRead != writeIndex_.load(std::memory_order_acquire)); - - auto nextRecord = currentRead + 1; - if (nextRecord == size_) { - nextRecord = 0; - } - records_[currentRead].~T(); - readIndex_.store(nextRecord, std::memory_order_release); - } - - bool isEmpty() const { - return readIndex_.load(std::memory_order_consume) == - writeIndex_.load(std::memory_order_consume); - } - - bool isFull() const { - auto nextRecord = writeIndex_.load(std::memory_order_consume) + 1; - if (nextRecord == size_) { - nextRecord = 0; - } - if (nextRecord != readIndex_.load(std::memory_order_consume)) { - return false; - } - // queue is full - return true; - } - - // * If called by consumer, then true size may be more (because producer may - // be adding items concurrently). - // * If called by producer, then true size may be less (because consumer may - // be removing items concurrently). - // * It is undefined to call this from any other thread. - size_t sizeGuess() const { - int ret = writeIndex_.load(std::memory_order_consume) - - readIndex_.load(std::memory_order_consume); - if (ret < 0) { - ret += size_; - } - return ret; - } - -private: - const uint32_t size_; - T* const records_; - - std::atomic readIndex_; - std::atomic writeIndex_; -}; - -} - -#endif diff --git a/hphp/third_party/folly/folly/RWSpinLock.h b/hphp/third_party/folly/folly/RWSpinLock.h deleted file mode 100644 index ab467c5cb..000000000 --- a/hphp/third_party/folly/folly/RWSpinLock.h +++ /dev/null @@ -1,743 +0,0 @@ -/* - * Copyright 2013 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. - */ - -/* - * Two Read-Write spin lock implementations. - * - * Ref: http://locklessinc.com/articles/locks - * - * Both locks here are faster than pthread_rwlock and have very low - * overhead (usually 20-30ns). They don't use any system mutexes and - * are very compact (4/8 bytes), so are suitable for per-instance - * based locking, particularly when contention is not expected. - * - * In most cases, RWSpinLock is a reasonable choice. It has minimal - * overhead, and comparable contention performance when the number of - * competing threads is less than or equal to the number of logical - * CPUs. Even as the number of threads gets larger, RWSpinLock can - * still be very competitive in READ, although it is slower on WRITE, - * and also inherently unfair to writers. - * - * RWTicketSpinLock shows more balanced READ/WRITE performance. If - * your application really needs a lot more threads, and a - * higher-priority writer, prefer one of the RWTicketSpinLock locks. - * - * Caveats: - * - * RWTicketSpinLock locks can only be used with GCC on x86/x86-64 - * based systems. - * - * RWTicketSpinLock<32> only allows up to 2^8 - 1 concurrent - * readers and writers. - * - * RWTicketSpinLock<64> only allows up to 2^16 - 1 concurrent - * readers and writers. - * - * RWSpinLock handles 2^30 - 1 concurrent readers. - * - * @author Xin Liu - */ - -#ifndef FOLLY_RWSPINLOCK_H_ -#define FOLLY_RWSPINLOCK_H_ - -/* -======================================================================== -Benchmark on (Intel(R) Xeon(R) CPU L5630 @ 2.13GHz) 8 cores(16 HTs) -======================================================================== - ------------------------------------------------------------------------------- -1. Single thread benchmark (read/write lock + unlock overhead) -Benchmark Iters Total t t/iter iter/sec -------------------------------------------------------------------------------- -* BM_RWSpinLockRead 100000 1.786 ms 17.86 ns 53.4M -+30.5% BM_RWSpinLockWrite 100000 2.331 ms 23.31 ns 40.91M -+85.7% BM_RWTicketSpinLock32Read 100000 3.317 ms 33.17 ns 28.75M -+96.0% BM_RWTicketSpinLock32Write 100000 3.5 ms 35 ns 27.25M -+85.6% BM_RWTicketSpinLock64Read 100000 3.315 ms 33.15 ns 28.77M -+96.0% BM_RWTicketSpinLock64Write 100000 3.5 ms 35 ns 27.25M -+85.7% BM_RWTicketSpinLock32FavorWriterRead 100000 3.317 ms 33.17 ns 28.75M -+29.7% BM_RWTicketSpinLock32FavorWriterWrite 100000 2.316 ms 23.16 ns 41.18M -+85.3% BM_RWTicketSpinLock64FavorWriterRead 100000 3.309 ms 33.09 ns 28.82M -+30.2% BM_RWTicketSpinLock64FavorWriterWrite 100000 2.325 ms 23.25 ns 41.02M -+ 175% BM_PThreadRWMutexRead 100000 4.917 ms 49.17 ns 19.4M -+ 166% BM_PThreadRWMutexWrite 100000 4.757 ms 47.57 ns 20.05M - ------------------------------------------------------------------------------- -2. Contention Benchmark 90% read 10% write -Benchmark hits average min max sigma ------------------------------------------------------------------------------- ----------- 8 threads ------------ -RWSpinLock Write 142666 220ns 78ns 40.8us 269ns -RWSpinLock Read 1282297 222ns 80ns 37.7us 248ns -RWTicketSpinLock Write 85692 209ns 71ns 17.9us 252ns -RWTicketSpinLock Read 769571 215ns 78ns 33.4us 251ns -pthread_rwlock_t Write 84248 2.48us 99ns 269us 8.19us -pthread_rwlock_t Read 761646 933ns 101ns 374us 3.25us - ----------- 16 threads ------------ -RWSpinLock Write 124236 237ns 78ns 261us 801ns -RWSpinLock Read 1115807 236ns 78ns 2.27ms 2.17us -RWTicketSpinLock Write 81781 231ns 71ns 31.4us 351ns -RWTicketSpinLock Read 734518 238ns 78ns 73.6us 379ns -pthread_rwlock_t Write 83363 7.12us 99ns 785us 28.1us -pthread_rwlock_t Read 754978 2.18us 101ns 1.02ms 14.3us - ----------- 50 threads ------------ -RWSpinLock Write 131142 1.37us 82ns 7.53ms 68.2us -RWSpinLock Read 1181240 262ns 78ns 6.62ms 12.7us -RWTicketSpinLock Write 83045 397ns 73ns 7.01ms 31.5us -RWTicketSpinLock Read 744133 386ns 78ns 11ms 31.4us -pthread_rwlock_t Write 80849 112us 103ns 4.52ms 263us -pthread_rwlock_t Read 728698 24us 101ns 7.28ms 194us - -*/ - -#if defined(__GNUC__) && (defined(__i386) || defined(__x86_64__) || \ - defined(ARCH_K8)) -#define RW_SPINLOCK_USE_X86_INTRINSIC_ -#include -#else -#undef RW_SPINLOCK_USE_X86_INTRINSIC_ -#endif - -#include -#include -#include -#include - -#include -#include - -#include "folly/Likely.h" - -namespace folly { - -/* - * A simple, small (4-bytes), but unfair rwlock. Use it when you want - * a nice writer and don't expect a lot of write/read contention, or - * when you need small rwlocks since you are creating a large number - * of them. - * - * Note that the unfairness here is extreme: if the lock is - * continually accessed for read, writers will never get a chance. If - * the lock can be that highly contended this class is probably not an - * ideal choice anyway. - * - * It currently implements most of the Lockable, SharedLockable and - * UpgradeLockable concepts except the TimedLockable related locking/unlocking - * interfaces. - */ -class RWSpinLock : boost::noncopyable { - enum : int32_t { READER = 4, UPGRADED = 2, WRITER = 1 }; - public: - RWSpinLock() : bits_(0) {} - - // Lockable Concept - void lock() { - int count = 0; - while (!LIKELY(try_lock())) { - if (++count > 1000) sched_yield(); - } - } - - // Writer is responsible for clearing up both the UPGRADED and WRITER bits. - void unlock() { - static_assert(READER > WRITER + UPGRADED, "wrong bits!"); - bits_.fetch_and(~(WRITER | UPGRADED), std::memory_order_release); - } - - // SharedLockable Concept - void lock_shared() { - int count = 0; - while (!LIKELY(try_lock_shared())) { - if (++count > 1000) sched_yield(); - } - } - - void unlock_shared() { - bits_.fetch_add(-READER, std::memory_order_release); - } - - // Downgrade the lock from writer status to reader status. - void unlock_and_lock_shared() { - bits_.fetch_add(READER, std::memory_order_acquire); - unlock(); - } - - // UpgradeLockable Concept - void lock_upgrade() { - int count = 0; - while (!try_lock_upgrade()) { - if (++count > 1000) sched_yield(); - } - } - - void unlock_upgrade() { - bits_.fetch_add(-UPGRADED, std::memory_order_acq_rel); - } - - // unlock upgrade and try to acquire write lock - void unlock_upgrade_and_lock() { - int64_t count = 0; - while (!try_unlock_upgrade_and_lock()) { - if (++count > 1000) sched_yield(); - } - } - - // unlock upgrade and read lock atomically - void unlock_upgrade_and_lock_shared() { - bits_.fetch_add(READER - UPGRADED, std::memory_order_acq_rel); - } - - // write unlock and upgrade lock atomically - void unlock_and_lock_upgrade() { - // need to do it in two steps here -- as the UPGRADED bit might be OR-ed at - // the same time when other threads are trying do try_lock_upgrade(). - bits_.fetch_or(UPGRADED, std::memory_order_acquire); - bits_.fetch_add(-WRITER, std::memory_order_release); - } - - - // Attempt to acquire writer permission. Return false if we didn't get it. - bool try_lock() { - int32_t expect = 0; - return bits_.compare_exchange_strong(expect, WRITER, - std::memory_order_acq_rel); - } - - // Try to get reader permission on the lock. This can fail if we - // find out someone is a writer or upgrader. - // Setting the UPGRADED bit would allow a writer-to-be to indicate - // its intention to write and block any new readers while waiting - // for existing readers to finish and release their read locks. This - // helps avoid starving writers (promoted from upgraders). - bool try_lock_shared() { - // fetch_add is considerably (100%) faster than compare_exchange, - // so here we are optimizing for the common (lock success) case. - int32_t value = bits_.fetch_add(READER, std::memory_order_acquire); - if (UNLIKELY(value & (WRITER|UPGRADED))) { - bits_.fetch_add(-READER, std::memory_order_release); - return false; - } - return true; - } - - // try to unlock upgrade and write lock atomically - bool try_unlock_upgrade_and_lock() { - int32_t expect = UPGRADED; - return bits_.compare_exchange_strong(expect, WRITER, - std::memory_order_acq_rel); - } - - // try to acquire an upgradable lock. - bool try_lock_upgrade() { - int32_t value = bits_.fetch_or(UPGRADED, std::memory_order_acquire); - - // Note: when failed, we cannot flip the UPGRADED bit back, - // as in this case there is either another upgrade lock or a write lock. - // If it's a write lock, the bit will get cleared up when that lock's done - // with unlock(). - return ((value & (UPGRADED | WRITER)) == 0); - } - - // mainly for debugging purposes. - int32_t bits() const { return bits_.load(std::memory_order_acquire); } - - class ReadHolder; - class UpgradedHolder; - class WriteHolder; - - class ReadHolder { - public: - explicit ReadHolder(RWSpinLock* lock = nullptr) : lock_(lock) { - if (lock_) lock_->lock_shared(); - } - - explicit ReadHolder(RWSpinLock& lock) : lock_(&lock) { - lock_->lock_shared(); - } - - ReadHolder(ReadHolder&& other) : lock_(other.lock_) { - other.lock_ = nullptr; - } - - // down-grade - explicit ReadHolder(UpgradedHolder&& upgraded) : lock_(upgraded.lock_) { - upgraded.lock_ = nullptr; - if (lock_) lock_->unlock_upgrade_and_lock_shared(); - } - - explicit ReadHolder(WriteHolder&& writer) : lock_(writer.lock_) { - writer.lock_ = nullptr; - if (lock_) lock_->unlock_and_lock_shared(); - } - - ReadHolder& operator=(ReadHolder&& other) { - using std::swap; - swap(lock_, other.lock_); - return *this; - } - - ReadHolder(const ReadHolder& other) = delete; - ReadHolder& operator=(const ReadHolder& other) = delete; - - ~ReadHolder() { if (lock_) lock_->unlock_shared(); } - - void reset(RWSpinLock* lock = nullptr) { - if (lock == lock_) return; - if (lock_) lock_->unlock_shared(); - lock_ = lock; - if (lock_) lock_->lock_shared(); - } - - void swap(ReadHolder* other) { - std::swap(lock_, other->lock_); - } - - private: - friend class UpgradedHolder; - friend class WriteHolder; - RWSpinLock* lock_; - }; - - class UpgradedHolder { - public: - explicit UpgradedHolder(RWSpinLock* lock = nullptr) : lock_(lock) { - if (lock_) lock_->lock_upgrade(); - } - - explicit UpgradedHolder(RWSpinLock& lock) : lock_(&lock) { - lock_->lock_upgrade(); - } - - explicit UpgradedHolder(WriteHolder&& writer) { - lock_ = writer.lock_; - writer.lock_ = nullptr; - if (lock_) lock_->unlock_and_lock_upgrade(); - } - - UpgradedHolder(UpgradedHolder&& other) : lock_(other.lock_) { - other.lock_ = nullptr; - } - - UpgradedHolder& operator =(UpgradedHolder&& other) { - using std::swap; - swap(lock_, other.lock_); - return *this; - } - - UpgradedHolder(const UpgradedHolder& other) = delete; - UpgradedHolder& operator =(const UpgradedHolder& other) = delete; - - ~UpgradedHolder() { if (lock_) lock_->unlock_upgrade(); } - - void reset(RWSpinLock* lock = nullptr) { - if (lock == lock_) return; - if (lock_) lock_->unlock_upgrade(); - lock_ = lock; - if (lock_) lock_->lock_upgrade(); - } - - void swap(UpgradedHolder* other) { - using std::swap; - swap(lock_, other->lock_); - } - - private: - friend class WriteHolder; - friend class ReadHolder; - RWSpinLock* lock_; - }; - - class WriteHolder { - public: - explicit WriteHolder(RWSpinLock* lock = nullptr) : lock_(lock) { - if (lock_) lock_->lock(); - } - - explicit WriteHolder(RWSpinLock& lock) : lock_(&lock) { - lock_->lock(); - } - - // promoted from an upgrade lock holder - explicit WriteHolder(UpgradedHolder&& upgraded) { - lock_ = upgraded.lock_; - upgraded.lock_ = nullptr; - if (lock_) lock_->unlock_upgrade_and_lock(); - } - - WriteHolder(WriteHolder&& other) : lock_(other.lock_) { - other.lock_ = nullptr; - } - - WriteHolder& operator =(WriteHolder&& other) { - using std::swap; - swap(lock_, other.lock_); - return *this; - } - - WriteHolder(const WriteHolder& other) = delete; - WriteHolder& operator =(const WriteHolder& other) = delete; - - ~WriteHolder () { if (lock_) lock_->unlock(); } - - void reset(RWSpinLock* lock = nullptr) { - if (lock == lock_) return; - if (lock_) lock_->unlock(); - lock_ = lock; - if (lock_) lock_->lock(); - } - - void swap(WriteHolder* other) { - using std::swap; - swap(lock_, other->lock_); - } - - private: - friend class ReadHolder; - friend class UpgradedHolder; - RWSpinLock* lock_; - }; - - // Synchronized<> adaptors - friend void acquireRead(RWSpinLock& l) { return l.lock_shared(); } - friend void acquireReadWrite(RWSpinLock& l) { return l.lock(); } - friend void releaseRead(RWSpinLock& l) { return l.unlock_shared(); } - friend void releaseReadWrite(RWSpinLock& l) { return l.unlock(); } - - private: - std::atomic bits_; -}; - - -#ifdef RW_SPINLOCK_USE_X86_INTRINSIC_ -// A more balanced Read-Write spin lock implemented based on GCC intrinsics. - -namespace detail { -template struct RWTicketIntTrait { - static_assert(kBitWidth == 32 || kBitWidth == 64, - "bit width has to be either 32 or 64 "); -}; - -template <> -struct RWTicketIntTrait<64> { - typedef uint64_t FullInt; - typedef uint32_t HalfInt; - typedef uint16_t QuarterInt; - -#ifdef __SSE2__ - static __m128i make128(const uint16_t v[4]) { - return _mm_set_epi16(0, 0, 0, 0, v[3], v[2], v[1], v[0]); - } - static inline __m128i fromInteger(uint64_t from) { - return _mm_cvtsi64_si128(from); - } - static inline uint64_t toInteger(__m128i in) { - return _mm_cvtsi128_si64(in); - } - static inline uint64_t addParallel(__m128i in, __m128i kDelta) { - return toInteger(_mm_add_epi16(in, kDelta)); - } -#endif -}; - -template <> -struct RWTicketIntTrait<32> { - typedef uint32_t FullInt; - typedef uint16_t HalfInt; - typedef uint8_t QuarterInt; - -#ifdef __SSE2__ - static __m128i make128(const uint8_t v[4]) { - return _mm_set_epi8(0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, v[3], v[2], v[1], v[0]); - } - static inline __m128i fromInteger(uint32_t from) { - return _mm_cvtsi32_si128(from); - } - static inline uint32_t toInteger(__m128i in) { - return _mm_cvtsi128_si32(in); - } - static inline uint32_t addParallel(__m128i in, __m128i kDelta) { - return toInteger(_mm_add_epi8(in, kDelta)); - } -#endif -}; -} // detail - - -template -class RWTicketSpinLockT : boost::noncopyable { - typedef detail::RWTicketIntTrait IntTraitType; - typedef typename detail::RWTicketIntTrait::FullInt FullInt; - typedef typename detail::RWTicketIntTrait::HalfInt HalfInt; - typedef typename detail::RWTicketIntTrait::QuarterInt - QuarterInt; - - union RWTicket { - FullInt whole; - HalfInt readWrite; - __extension__ struct { - QuarterInt write; - QuarterInt read; - QuarterInt users; - }; - } ticket; - - private: // Some x64-specific utilities for atomic access to ticket. - template static T load_acquire(T* addr) { - T t = *addr; // acquire barrier - asm volatile("" : : : "memory"); - return t; - } - - template - static void store_release(T* addr, T v) { - asm volatile("" : : : "memory"); - *addr = v; // release barrier - } - - public: - - RWTicketSpinLockT() { - store_release(&ticket.whole, FullInt(0)); - } - - void lock() { - if (kFavorWriter) { - writeLockAggressive(); - } else { - writeLockNice(); - } - } - - /* - * Both try_lock and try_lock_shared diverge in our implementation from the - * lock algorithm described in the link above. - * - * In the read case, it is undesirable that the readers could wait - * for another reader (before increasing ticket.read in the other - * implementation). Our approach gives up on - * first-come-first-serve, but our benchmarks showed improve - * performance for both readers and writers under heavily contended - * cases, particularly when the number of threads exceeds the number - * of logical CPUs. - * - * We have writeLockAggressive() using the original implementation - * for a writer, which gives some advantage to the writer over the - * readers---for that path it is guaranteed that the writer will - * acquire the lock after all the existing readers exit. - */ - bool try_lock() { - RWTicket t; - FullInt old = t.whole = load_acquire(&ticket.whole); - if (t.users != t.write) return false; - ++t.users; - return __sync_bool_compare_and_swap(&ticket.whole, old, t.whole); - } - - /* - * Call this if you want to prioritize writer to avoid starvation. - * Unlike writeLockNice, immediately acquires the write lock when - * the existing readers (arriving before the writer) finish their - * turns. - */ - void writeLockAggressive() { - // sched_yield() is needed here to avoid a pathology if the number - // of threads attempting concurrent writes is >= the number of real - // cores allocated to this process. This is less likely than the - // corresponding situation in lock_shared(), but we still want to - // avoid it - int count = 0; - QuarterInt val = __sync_fetch_and_add(&ticket.users, 1); - while (val != load_acquire(&ticket.write)) { - asm volatile("pause"); - if (UNLIKELY(++count > 1000)) sched_yield(); - } - } - - // Call this when the writer should be nicer to the readers. - void writeLockNice() { - // Here it doesn't cpu-relax the writer. - // - // This is because usually we have many more readers than the - // writers, so the writer has less chance to get the lock when - // there are a lot of competing readers. The aggressive spinning - // can help to avoid starving writers. - // - // We don't worry about sched_yield() here because the caller - // has already explicitly abandoned fairness. - while (!try_lock()) {} - } - - // Atomically unlock the write-lock from writer and acquire the read-lock. - void unlock_and_lock_shared() { - QuarterInt val = __sync_fetch_and_add(&ticket.read, 1); - } - - // Release writer permission on the lock. - void unlock() { - RWTicket t; - t.whole = load_acquire(&ticket.whole); - FullInt old = t.whole; - -#ifdef __SSE2__ - // SSE2 can reduce the lock and unlock overhead by 10% - static const QuarterInt kDeltaBuf[4] = { 1, 1, 0, 0 }; // write/read/user - static const __m128i kDelta = IntTraitType::make128(kDeltaBuf); - __m128i m = IntTraitType::fromInteger(old); - t.whole = IntTraitType::addParallel(m, kDelta); -#else - ++t.read; - ++t.write; -#endif - store_release(&ticket.readWrite, t.readWrite); - } - - void lock_shared() { - // sched_yield() is important here because we can't grab the - // shared lock if there is a pending writeLockAggressive, so we - // need to let threads that already have a shared lock complete - int count = 0; - while (!LIKELY(try_lock_shared())) { - asm volatile("pause"); - if (UNLIKELY((++count & 1023) == 0)) sched_yield(); - } - } - - bool try_lock_shared() { - RWTicket t, old; - old.whole = t.whole = load_acquire(&ticket.whole); - old.users = old.read; -#ifdef __SSE2__ - // SSE2 may reduce the total lock and unlock overhead by 10% - static const QuarterInt kDeltaBuf[4] = { 0, 1, 1, 0 }; // write/read/user - static const __m128i kDelta = IntTraitType::make128(kDeltaBuf); - __m128i m = IntTraitType::fromInteger(old.whole); - t.whole = IntTraitType::addParallel(m, kDelta); -#else - ++t.read; - ++t.users; -#endif - return __sync_bool_compare_and_swap(&ticket.whole, old.whole, t.whole); - } - - void unlock_shared() { - QuarterInt val = __sync_fetch_and_add(&ticket.write, 1); - } - - class WriteHolder; - - typedef RWTicketSpinLockT RWSpinLock; - class ReadHolder : boost::noncopyable { - public: - explicit ReadHolder(RWSpinLock *lock = nullptr) : - lock_(lock) { - if (lock_) lock_->lock_shared(); - } - - explicit ReadHolder(RWSpinLock &lock) : lock_ (&lock) { - if (lock_) lock_->lock_shared(); - } - - // atomically unlock the write-lock from writer and acquire the read-lock - explicit ReadHolder(WriteHolder *writer) : lock_(nullptr) { - std::swap(this->lock_, writer->lock_); - if (lock_) { - lock_->unlock_and_lock_shared(); - } - } - - ~ReadHolder() { - if (lock_) lock_->unlock_shared(); - } - - void reset(RWSpinLock *lock = nullptr) { - if (lock_) lock_->unlock_shared(); - lock_ = lock; - if (lock_) lock_->lock_shared(); - } - - void swap(ReadHolder *other) { - std::swap(this->lock_, other->lock_); - } - - private: - RWSpinLock *lock_; - }; - - class WriteHolder : boost::noncopyable { - public: - explicit WriteHolder(RWSpinLock *lock = nullptr) : lock_(lock) { - if (lock_) lock_->lock(); - } - explicit WriteHolder(RWSpinLock &lock) : lock_ (&lock) { - if (lock_) lock_->lock(); - } - - ~WriteHolder() { - if (lock_) lock_->unlock(); - } - - void reset(RWSpinLock *lock = nullptr) { - if (lock == lock_) return; - if (lock_) lock_->unlock(); - lock_ = lock; - if (lock_) lock_->lock(); - } - - void swap(WriteHolder *other) { - std::swap(this->lock_, other->lock_); - } - - private: - friend class ReadHolder; - RWSpinLock *lock_; - }; - - // Synchronized<> adaptors. - friend void acquireRead(RWTicketSpinLockT& mutex) { - mutex.lock_shared(); - } - friend void acquireReadWrite(RWTicketSpinLockT& mutex) { - mutex.lock(); - } - friend bool acquireReadWrite(RWTicketSpinLockT& mutex, - unsigned int milliseconds) { - mutex.lock(); - return true; - } - friend void releaseRead(RWTicketSpinLockT& mutex) { - mutex.unlock_shared(); - } - friend void releaseReadWrite(RWTicketSpinLockT& mutex) { - mutex.unlock(); - } -}; - -typedef RWTicketSpinLockT<32> RWTicketSpinLock32; -typedef RWTicketSpinLockT<64> RWTicketSpinLock64; - -#endif // RW_SPINLOCK_USE_X86_INTRINSIC_ - -} // namespace folly - -#ifdef RW_SPINLOCK_USE_X86_INTRINSIC_ -#undef RW_SPINLOCK_USE_X86_INTRINSIC_ -#endif - -#endif // FOLLY_RWSPINLOCK_H_ diff --git a/hphp/third_party/folly/folly/Random.cpp b/hphp/third_party/folly/folly/Random.cpp deleted file mode 100644 index dfb2559cc..000000000 --- a/hphp/third_party/folly/folly/Random.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#include "folly/Random.h" - -#include -#include -#include - -namespace folly { - -namespace { -std::atomic seedInput(0); -} - -uint32_t randomNumberSeed() { - struct timeval tv; - gettimeofday(&tv, NULL); - const uint32_t kPrime0 = 51551; - const uint32_t kPrime1 = 61631; - const uint32_t kPrime2 = 64997; - const uint32_t kPrime3 = 111857; - return kPrime0 * (seedInput++) - + kPrime1 * static_cast(getpid()) - + kPrime2 * static_cast(tv.tv_sec) - + kPrime3 * static_cast(tv.tv_usec); -} - -} diff --git a/hphp/third_party/folly/folly/Random.h b/hphp/third_party/folly/folly/Random.h deleted file mode 100644 index 65f8b398e..000000000 --- a/hphp/third_party/folly/folly/Random.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_BASE_RANDOM_H_ -#define FOLLY_BASE_RANDOM_H_ - -#include - -namespace folly { - -/* - * Return a good seed for a random number generator. - */ -uint32_t randomNumberSeed(); - -} - -#endif diff --git a/hphp/third_party/folly/folly/Range.cpp b/hphp/third_party/folly/folly/Range.cpp deleted file mode 100644 index 377e293c4..000000000 --- a/hphp/third_party/folly/folly/Range.cpp +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright 2013 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. - */ - -// @author Mark Rabkin (mrabkin@fb.com) -// @author Andrei Alexandrescu (andrei.alexandrescu@fb.com) - -#include "folly/Range.h" - -#include // __v16qi -#include - -namespace folly { - -/** - * Predicates that can be used with qfind and startsWith - */ -const AsciiCaseSensitive asciiCaseSensitive = AsciiCaseSensitive(); -const AsciiCaseInsensitive asciiCaseInsensitive = AsciiCaseInsensitive(); - -std::ostream& operator<<(std::ostream& os, const StringPiece& piece) { - os.write(piece.start(), piece.size()); - return os; -} - -namespace detail { - -size_t qfind_first_byte_of_memchr(const StringPiece& haystack, - const StringPiece& needles) { - size_t best = haystack.size(); - for (char needle: needles) { - const void* ptr = memchr(haystack.data(), needle, best); - if (ptr) { - auto found = static_cast(ptr) - haystack.data(); - best = std::min(best, found); - } - } - if (best == haystack.size()) { - return StringPiece::npos; - } - return best; -} - -} // namespace detail - -namespace { - -// It's okay if pages are bigger than this (as powers of two), but they should -// not be smaller. -constexpr size_t kMinPageSize = 4096; -static_assert(kMinPageSize >= 16, - "kMinPageSize must be at least SSE register size"); -#define PAGE_FOR(addr) \ - (reinterpret_cast(addr) / kMinPageSize) - - -#if FOLLY_HAVE_EMMINTRIN_H -inline size_t nextAlignedIndex(const char* arr) { - auto firstPossible = reinterpret_cast(arr) + 1; - return 1 + // add 1 because the index starts at 'arr' - ((firstPossible + 15) & ~0xF) // round up to next multiple of 16 - - firstPossible; -} - -// build sse4.2-optimized version even if -msse4.2 is not passed to GCC -size_t qfind_first_byte_of_needles16(const StringPiece& haystack, - const StringPiece& needles) - __attribute__ ((__target__("sse4.2"), noinline)); - -// helper method for case where needles.size() <= 16 -size_t qfind_first_byte_of_needles16(const StringPiece& haystack, - const StringPiece& needles) { - DCHECK(!haystack.empty()); - DCHECK(!needles.empty()); - DCHECK_LE(needles.size(), 16); - // benchmarking shows that memchr beats out SSE for small needle-sets - // with large haystacks. - if ((needles.size() <= 2 && haystack.size() >= 256) || - // must bail if we can't even SSE-load a single segment of haystack - (haystack.size() < 16 && - PAGE_FOR(haystack.end() - 1) != PAGE_FOR(haystack.data() + 15)) || - // can't load needles into SSE register if it could cross page boundary - PAGE_FOR(needles.end() - 1) != PAGE_FOR(needles.data() + 15)) { - return detail::qfind_first_byte_of_memchr(haystack, needles); - } - - auto arr2 = __builtin_ia32_loaddqu(needles.data()); - // do an unaligned load for first block of haystack - auto arr1 = __builtin_ia32_loaddqu(haystack.data()); - auto index = __builtin_ia32_pcmpestri128(arr2, needles.size(), - arr1, haystack.size(), 0); - if (index < 16) { - return index; - } - - // Now, we can do aligned loads hereafter... - size_t i = nextAlignedIndex(haystack.data()); - for (; i < haystack.size(); i+= 16) { - void* ptr1 = __builtin_assume_aligned(haystack.data() + i, 16); - auto arr1 = *reinterpret_cast(ptr1); - auto index = __builtin_ia32_pcmpestri128(arr2, needles.size(), - arr1, haystack.size() - i, 0); - if (index < 16) { - return i + index; - } - } - return StringPiece::npos; -} -#endif // FOLLY_HAVE_EMMINTRIN_H - -// Aho, Hopcroft, and Ullman refer to this trick in "The Design and Analysis -// of Computer Algorithms" (1974), but the best description is here: -// http://research.swtch.com/sparse -class FastByteSet { - public: - FastByteSet() : size_(0) { } // no init of arrays required! - - inline void add(uint8_t i) { - if (!contains(i)) { - dense_[size_] = i; - sparse_[i] = size_; - size_++; - } - } - inline bool contains(uint8_t i) const { - DCHECK_LE(size_, 256); - return sparse_[i] < size_ && dense_[sparse_[i]] == i; - } - - private: - uint16_t size_; // can't use uint8_t because it would overflow if all - // possible values were inserted. - uint8_t sparse_[256]; - uint8_t dense_[256]; -}; - -} // namespace - -namespace detail { - -size_t qfind_first_byte_of_byteset(const StringPiece& haystack, - const StringPiece& needles) { - FastByteSet s; - for (auto needle: needles) { - s.add(needle); - } - for (size_t index = 0; index < haystack.size(); ++index) { - if (s.contains(haystack[index])) { - return index; - } - } - return StringPiece::npos; -} - -#if FOLLY_HAVE_EMMINTRIN_H - -template -inline size_t scanHaystackBlock(const StringPiece& haystack, - const StringPiece& needles, - int64_t idx) -// inline is okay because it's only called from other sse4.2 functions - __attribute__ ((__target__("sse4.2"))); - -// Scans a 16-byte block of haystack (starting at blockStartIdx) to find first -// needle. If HAYSTACK_ALIGNED, then haystack must be 16byte aligned. -// If !HAYSTACK_ALIGNED, then caller must ensure that it is safe to load the -// block. -template -inline size_t scanHaystackBlock(const StringPiece& haystack, - const StringPiece& needles, - int64_t blockStartIdx) { - DCHECK_GT(needles.size(), 16); // should handled by *needles16() method - DCHECK(blockStartIdx + 16 <= haystack.size() || - (PAGE_FOR(haystack.data() + blockStartIdx) == - PAGE_FOR(haystack.data() + blockStartIdx + 15))); - - __v16qi arr1; - if (HAYSTACK_ALIGNED) { - void* ptr1 = __builtin_assume_aligned(haystack.data() + blockStartIdx, 16); - arr1 = *reinterpret_cast(ptr1); - } else { - arr1 = __builtin_ia32_loaddqu(haystack.data() + blockStartIdx); - } - - // This load is safe because needles.size() >= 16 - auto arr2 = __builtin_ia32_loaddqu(needles.data()); - size_t b = __builtin_ia32_pcmpestri128( - arr2, 16, arr1, haystack.size() - blockStartIdx, 0); - - size_t j = nextAlignedIndex(needles.data()); - for (; j < needles.size(); j += 16) { - void* ptr2 = __builtin_assume_aligned(needles.data() + j, 16); - arr2 = *reinterpret_cast(ptr2); - - auto index = __builtin_ia32_pcmpestri128( - arr2, needles.size() - j, arr1, haystack.size() - blockStartIdx, 0); - b = std::min(index, b); - } - - if (b < 16) { - return blockStartIdx + b; - } - return StringPiece::npos; -} - -size_t qfind_first_byte_of_sse42(const StringPiece& haystack, - const StringPiece& needles) - __attribute__ ((__target__("sse4.2"), noinline)); - -size_t qfind_first_byte_of_sse42(const StringPiece& haystack, - const StringPiece& needles) { - if (UNLIKELY(needles.empty() || haystack.empty())) { - return StringPiece::npos; - } else if (needles.size() <= 16) { - // we can save some unnecessary load instructions by optimizing for - // the common case of needles.size() <= 16 - return qfind_first_byte_of_needles16(haystack, needles); - } - - if (haystack.size() < 16 && - PAGE_FOR(haystack.end() - 1) != PAGE_FOR(haystack.data() + 16)) { - // We can't safely SSE-load haystack. Use a different approach. - if (haystack.size() <= 2) { - return qfind_first_of(haystack, needles, asciiCaseSensitive); - } - return qfind_first_byte_of_byteset(haystack, needles); - } - - auto ret = scanHaystackBlock(haystack, needles, 0); - if (ret != StringPiece::npos) { - return ret; - } - - size_t i = nextAlignedIndex(haystack.data()); - for (; i < haystack.size(); i += 16) { - auto ret = scanHaystackBlock(haystack, needles, i); - if (ret != StringPiece::npos) { - return ret; - } - } - - return StringPiece::npos; -} -#endif // FOLLY_HAVE_EMMINTRIN_H - -size_t qfind_first_byte_of_nosse(const StringPiece& haystack, - const StringPiece& needles) { - if (UNLIKELY(needles.empty() || haystack.empty())) { - return StringPiece::npos; - } - // The thresholds below were empirically determined by benchmarking. - // This is not an exact science since it depends on the CPU, the size of - // needles, and the size of haystack. - if (haystack.size() == 1 || - (haystack.size() < 4 && needles.size() <= 16)) { - return qfind_first_of(haystack, needles, asciiCaseSensitive); - } else if ((needles.size() >= 4 && haystack.size() <= 10) || - (needles.size() >= 16 && haystack.size() <= 64) || - needles.size() >= 32) { - return qfind_first_byte_of_byteset(haystack, needles); - } - - return qfind_first_byte_of_memchr(haystack, needles); -} - -} // namespace detail -} // namespace folly diff --git a/hphp/third_party/folly/folly/Range.h b/hphp/third_party/folly/folly/Range.h deleted file mode 100644 index 107bdb00d..000000000 --- a/hphp/third_party/folly/folly/Range.h +++ /dev/null @@ -1,757 +0,0 @@ -/* - * Copyright 2013 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. - */ - -// @author Mark Rabkin (mrabkin@fb.com) -// @author Andrei Alexandrescu (andrei.alexandrescu@fb.com) - -#ifndef FOLLY_RANGE_H_ -#define FOLLY_RANGE_H_ - -#include "folly/Portability.h" -#include "folly/FBString.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "folly/CpuId.h" -#include "folly/Traits.h" -#include "folly/Likely.h" - -// Ignore shadowing warnings within this file, so includers can use -Wshadow. -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wshadow" - -namespace folly { - -template class Range; - -/** - * Finds the first occurrence of needle in haystack. The algorithm is on - * average faster than O(haystack.size() * needle.size()) but not as fast - * as Boyer-Moore. On the upside, it does not do any upfront - * preprocessing and does not allocate memory. - */ -template ::value_type>> -inline size_t qfind(const Range & haystack, - const Range & needle, - Comp eq = Comp()); - -/** - * Finds the first occurrence of needle in haystack. The result is the - * offset reported to the beginning of haystack, or string::npos if - * needle wasn't found. - */ -template -size_t qfind(const Range & haystack, - const typename Range::value_type& needle); - -/** - * Finds the last occurrence of needle in haystack. The result is the - * offset reported to the beginning of haystack, or string::npos if - * needle wasn't found. - */ -template -size_t rfind(const Range & haystack, - const typename Range::value_type& needle); - - -/** - * Finds the first occurrence of any element of needle in - * haystack. The algorithm is O(haystack.size() * needle.size()). - */ -template -inline size_t qfind_first_of(const Range & haystack, - const Range & needle); - -/** - * Small internal helper - returns the value just before an iterator. - */ -namespace detail { - -/** - * For random-access iterators, the value before is simply i[-1]. - */ -template -typename std::enable_if< - std::is_same::iterator_category, - std::random_access_iterator_tag>::value, - typename std::iterator_traits::reference>::type -value_before(Iter i) { - return i[-1]; -} - -/** - * For all other iterators, we need to use the decrement operator. - */ -template -typename std::enable_if< - !std::is_same::iterator_category, - std::random_access_iterator_tag>::value, - typename std::iterator_traits::reference>::type -value_before(Iter i) { - return *--i; -} - -} // namespace detail - -/** - * Range abstraction keeping a pair of iterators. We couldn't use - * boost's similar range abstraction because we need an API identical - * with the former StringPiece class, which is used by a lot of other - * code. This abstraction does fulfill the needs of boost's - * range-oriented algorithms though. - * - * (Keep memory lifetime in mind when using this class, since it - * doesn't manage the data it refers to - just like an iterator - * wouldn't.) - */ -template -class Range : private boost::totally_ordered > { -public: - typedef std::size_t size_type; - typedef Iter iterator; - typedef Iter const_iterator; - typedef typename std::remove_reference< - typename std::iterator_traits::reference>::type - value_type; - typedef typename std::iterator_traits::reference reference; - typedef std::char_traits::type> - traits_type; - - static const size_type npos; - - // Works for all iterators - Range() : b_(), e_() { - } - -public: - // Works for all iterators - Range(Iter start, Iter end) : b_(start), e_(end) { - } - - // Works only for random-access iterators - Range(Iter start, size_t size) - : b_(start), e_(start + size) { } - - // Works only for Range - /* implicit */ Range(Iter str) - : b_(str), e_(b_ + strlen(str)) {} - // Works only for Range - /* implicit */ Range(const std::string& str) - : b_(str.data()), e_(b_ + str.size()) {} - // Works only for Range - Range(const std::string& str, std::string::size_type startFrom) { - if (UNLIKELY(startFrom > str.size())) { - throw std::out_of_range("index out of range"); - } - b_ = str.data() + startFrom; - e_ = str.data() + str.size(); - } - // Works only for Range - Range(const std::string& str, - std::string::size_type startFrom, - std::string::size_type size) { - if (UNLIKELY(startFrom > str.size())) { - throw std::out_of_range("index out of range"); - } - b_ = str.data() + startFrom; - if (str.size() - startFrom < size) { - e_ = str.data() + str.size(); - } else { - e_ = b_ + size; - } - } - Range(const Range& str, - size_t startFrom, - size_t size) { - if (UNLIKELY(startFrom > str.size())) { - throw std::out_of_range("index out of range"); - } - b_ = str.b_ + startFrom; - if (str.size() - startFrom < size) { - e_ = str.e_; - } else { - e_ = b_ + size; - } - } - // Works only for Range - /* implicit */ Range(const fbstring& str) - : b_(str.data()), e_(b_ + str.size()) { } - // Works only for Range - Range(const fbstring& str, fbstring::size_type startFrom) { - if (UNLIKELY(startFrom > str.size())) { - throw std::out_of_range("index out of range"); - } - b_ = str.data() + startFrom; - e_ = str.data() + str.size(); - } - // Works only for Range - Range(const fbstring& str, fbstring::size_type startFrom, - fbstring::size_type size) { - if (UNLIKELY(startFrom > str.size())) { - throw std::out_of_range("index out of range"); - } - b_ = str.data() + startFrom; - if (str.size() - startFrom < size) { - e_ = str.data() + str.size(); - } else { - e_ = b_ + size; - } - } - - // Allow implicit conversion from Range (aka StringPiece) to - // Range (aka ByteRange), as they're both frequently - // used to represent ranges of bytes. Allow explicit conversion in the other - // direction. - template ::value && - std::is_same::value), int>::type = 0> - /* implicit */ Range(const Range& other) - : b_(reinterpret_cast(other.begin())), - e_(reinterpret_cast(other.end())) { - } - - template ::value && - std::is_same::value), int>::type = 0> - explicit Range(const Range& other) - : b_(reinterpret_cast(other.begin())), - e_(reinterpret_cast(other.end())) { - } - - void clear() { - b_ = Iter(); - e_ = Iter(); - } - - void assign(Iter start, Iter end) { - b_ = start; - e_ = end; - } - - void reset(Iter start, size_type size) { - b_ = start; - e_ = start + size; - } - - // Works only for Range - void reset(const std::string& str) { - reset(str.data(), str.size()); - } - - size_type size() const { - assert(b_ <= e_); - return e_ - b_; - } - size_type walk_size() const { - assert(b_ <= e_); - return std::distance(b_, e_); - } - bool empty() const { return b_ == e_; } - Iter data() const { return b_; } - Iter start() const { return b_; } - Iter begin() const { return b_; } - Iter end() const { return e_; } - Iter cbegin() const { return b_; } - Iter cend() const { return e_; } - value_type& front() { - assert(b_ < e_); - return *b_; - } - value_type& back() { - assert(b_ < e_); - return detail::value_before(e_); - } - const value_type& front() const { - assert(b_ < e_); - return *b_; - } - const value_type& back() const { - assert(b_ < e_); - return detail::value_before(e_); - } - // Works only for Range - std::string str() const { return std::string(b_, size()); } - std::string toString() const { return str(); } - // Works only for Range - fbstring fbstr() const { return fbstring(b_, size()); } - fbstring toFbstring() const { return fbstr(); } - - // Works only for Range - int compare(const Range& o) const { - const size_type tsize = this->size(); - const size_type osize = o.size(); - const size_type msize = std::min(tsize, osize); - int r = traits_type::compare(data(), o.data(), msize); - if (r == 0) r = tsize - osize; - return r; - } - - value_type& operator[](size_t i) { - CHECK_GT(size(), i); - return b_[i]; - } - - const value_type& operator[](size_t i) const { - CHECK_GT(size(), i); - return b_[i]; - } - - value_type& at(size_t i) { - if (i >= size()) throw std::out_of_range("index out of range"); - return b_[i]; - } - - const value_type& at(size_t i) const { - if (i >= size()) throw std::out_of_range("index out of range"); - return b_[i]; - } - - // Works only for Range - uint32_t hash() const { - // Taken from fbi/nstring.h: - // Quick and dirty bernstein hash...fine for short ascii strings - uint32_t hash = 5381; - for (size_t ix = 0; ix < size(); ix++) { - hash = ((hash << 5) + hash) + b_[ix]; - } - return hash; - } - - void advance(size_type n) { - if (UNLIKELY(n > size())) { - throw std::out_of_range("index out of range"); - } - b_ += n; - } - - void subtract(size_type n) { - if (UNLIKELY(n > size())) { - throw std::out_of_range("index out of range"); - } - e_ -= n; - } - - void pop_front() { - assert(b_ < e_); - ++b_; - } - - void pop_back() { - assert(b_ < e_); - --e_; - } - - Range subpiece(size_type first, - size_type length = std::string::npos) const { - if (UNLIKELY(first > size())) { - throw std::out_of_range("index out of range"); - } - return Range(b_ + first, - std::min(length, size() - first)); - } - - // string work-alike functions - size_type find(Range str) const { - return qfind(*this, str); - } - - size_type find(Range str, size_t pos) const { - if (pos > size()) return std::string::npos; - size_t ret = qfind(subpiece(pos), str); - return ret == npos ? ret : ret + pos; - } - - size_type find(Iter s, size_t pos, size_t n) const { - if (pos > size()) return std::string::npos; - size_t ret = qfind(pos ? subpiece(pos) : *this, Range(s, n)); - return ret == npos ? ret : ret + pos; - } - - // Works only for Range which have Range(Iter) ctor - size_type find(const Iter s) const { - return qfind(*this, Range(s)); - } - - // Works only for Range which have Range(Iter) ctor - size_type find(const Iter s, size_t pos) const { - if (pos > size()) return std::string::npos; - size_type ret = qfind(subpiece(pos), Range(s)); - return ret == npos ? ret : ret + pos; - } - - size_type find(value_type c) const { - return qfind(*this, c); - } - - size_type rfind(value_type c) const { - return folly::rfind(*this, c); - } - - size_type find(value_type c, size_t pos) const { - if (pos > size()) return std::string::npos; - size_type ret = qfind(subpiece(pos), c); - return ret == npos ? ret : ret + pos; - } - - size_type find_first_of(Range needles) const { - return qfind_first_of(*this, needles); - } - - size_type find_first_of(Range needles, size_t pos) const { - if (pos > size()) return std::string::npos; - size_type ret = qfind_first_of(subpiece(pos), needles); - return ret == npos ? ret : ret + pos; - } - - // Works only for Range which have Range(Iter) ctor - size_type find_first_of(Iter needles) const { - return find_first_of(Range(needles)); - } - - // Works only for Range which have Range(Iter) ctor - size_type find_first_of(Iter needles, size_t pos) const { - return find_first_of(Range(needles), pos); - } - - size_type find_first_of(Iter needles, size_t pos, size_t n) const { - return find_first_of(Range(needles, n), pos); - } - - size_type find_first_of(value_type c) const { - return find(c); - } - - size_type find_first_of(value_type c, size_t pos) const { - return find(c, pos); - } - - void swap(Range& rhs) { - std::swap(b_, rhs.b_); - std::swap(e_, rhs.e_); - } - -private: - Iter b_, e_; -}; - -template -const typename Range::size_type Range::npos = std::string::npos; - -template -void swap(Range& lhs, Range& rhs) { - lhs.swap(rhs); -} - -/** - * Create a range from two iterators, with type deduction. - */ -template -Range makeRange(Iter first, Iter last) { - return Range(first, last); -} - -typedef Range StringPiece; -typedef Range ByteRange; - -std::ostream& operator<<(std::ostream& os, const StringPiece& piece); - -/** - * Templated comparison operators - */ - -template -inline bool operator==(const Range& lhs, const Range& rhs) { - return lhs.size() == rhs.size() && lhs.compare(rhs) == 0; -} - -template -inline bool operator<(const Range& lhs, const Range& rhs) { - return lhs.compare(rhs) < 0; -} - -/** - * Specializations of comparison operators for StringPiece - */ - -namespace detail { - -template -struct ComparableAsStringPiece { - enum { - value = - (std::is_convertible::value - && std::is_same::value) - || - (std::is_convertible::value - && std::is_same::value) - }; -}; - -} // namespace detail - -/** - * operator== through conversion for Range - */ -template -typename -std::enable_if::value, bool>::type -operator==(const T& lhs, const U& rhs) { - return StringPiece(lhs) == StringPiece(rhs); -} - -/** - * operator< through conversion for Range - */ -template -typename -std::enable_if::value, bool>::type -operator<(const T& lhs, const U& rhs) { - return StringPiece(lhs) < StringPiece(rhs); -} - -/** - * operator> through conversion for Range - */ -template -typename -std::enable_if::value, bool>::type -operator>(const T& lhs, const U& rhs) { - return StringPiece(lhs) > StringPiece(rhs); -} - -/** - * operator< through conversion for Range - */ -template -typename -std::enable_if::value, bool>::type -operator<=(const T& lhs, const U& rhs) { - return StringPiece(lhs) <= StringPiece(rhs); -} - -/** - * operator> through conversion for Range - */ -template -typename -std::enable_if::value, bool>::type -operator>=(const T& lhs, const U& rhs) { - return StringPiece(lhs) >= StringPiece(rhs); -} - -struct StringPieceHash { - std::size_t operator()(const StringPiece& str) const { - return static_cast(str.hash()); - } -}; - -/** - * Finds substrings faster than brute force by borrowing from Boyer-Moore - */ -template -size_t qfind(const Range& haystack, - const Range& needle, - Comp eq) { - // Don't use std::search, use a Boyer-Moore-like trick by comparing - // the last characters first - auto const nsize = needle.size(); - if (haystack.size() < nsize) { - return std::string::npos; - } - if (!nsize) return 0; - auto const nsize_1 = nsize - 1; - auto const lastNeedle = needle[nsize_1]; - - // Boyer-Moore skip value for the last char in the needle. Zero is - // not a valid value; skip will be computed the first time it's - // needed. - std::string::size_type skip = 0; - - auto i = haystack.begin(); - auto iEnd = haystack.end() - nsize_1; - - while (i < iEnd) { - // Boyer-Moore: match the last element in the needle - while (!eq(i[nsize_1], lastNeedle)) { - if (++i == iEnd) { - // not found - return std::string::npos; - } - } - // Here we know that the last char matches - // Continue in pedestrian mode - for (size_t j = 0; ; ) { - assert(j < nsize); - if (!eq(i[j], needle[j])) { - // Not found, we can skip - // Compute the skip value lazily - if (skip == 0) { - skip = 1; - while (skip <= nsize_1 && !eq(needle[nsize_1 - skip], lastNeedle)) { - ++skip; - } - } - i += skip; - break; - } - // Check if done searching - if (++j == nsize) { - // Yay - return i - haystack.begin(); - } - } - } - return std::string::npos; -} - -namespace detail { - -size_t qfind_first_byte_of_nosse(const StringPiece& haystack, - const StringPiece& needles); - -#if FOLLY_HAVE_EMMINTRIN_H -size_t qfind_first_byte_of_sse42(const StringPiece& haystack, - const StringPiece& needles); - -inline size_t qfind_first_byte_of(const StringPiece& haystack, - const StringPiece& needles) { - static auto const qfind_first_byte_of_fn = - folly::CpuId().sse42() ? qfind_first_byte_of_sse42 - : qfind_first_byte_of_nosse; - return qfind_first_byte_of_fn(haystack, needles); -} - -#else -inline size_t qfind_first_byte_of(const StringPiece& haystack, - const StringPiece& needles) { - return qfind_first_byte_of_nosse(haystack, needles); -} -#endif // FOLLY_HAVE_EMMINTRIN_H - -} // namespace detail - -template -size_t qfind_first_of(const Range & haystack, - const Range & needles, - Comp eq) { - auto ret = std::find_first_of(haystack.begin(), haystack.end(), - needles.begin(), needles.end(), - eq); - return ret == haystack.end() ? std::string::npos : ret - haystack.begin(); -} - -struct AsciiCaseSensitive { - bool operator()(char lhs, char rhs) const { - return lhs == rhs; - } -}; - -struct AsciiCaseInsensitive { - bool operator()(char lhs, char rhs) const { - return toupper(lhs) == toupper(rhs); - } -}; - -extern const AsciiCaseSensitive asciiCaseSensitive; -extern const AsciiCaseInsensitive asciiCaseInsensitive; - -template -size_t qfind(const Range& haystack, - const typename Range::value_type& needle) { - auto pos = std::find(haystack.begin(), haystack.end(), needle); - return pos == haystack.end() ? std::string::npos : pos - haystack.data(); -} - -template -size_t rfind(const Range& haystack, - const typename Range::value_type& needle) { - for (auto i = haystack.size(); i-- > 0; ) { - if (haystack[i] == needle) { - return i; - } - } - return std::string::npos; -} - -// specialization for StringPiece -template <> -inline size_t qfind(const Range& haystack, const char& needle) { - auto pos = static_cast( - ::memchr(haystack.data(), needle, haystack.size())); - return pos == nullptr ? std::string::npos : pos - haystack.data(); -} - -#if FOLLY_HAVE_MEMRCHR -template <> -inline size_t rfind(const Range& haystack, const char& needle) { - auto pos = static_cast( - ::memrchr(haystack.data(), needle, haystack.size())); - return pos == nullptr ? std::string::npos : pos - haystack.data(); -} -#endif - -// specialization for ByteRange -template <> -inline size_t qfind(const Range& haystack, - const unsigned char& needle) { - auto pos = static_cast( - ::memchr(haystack.data(), needle, haystack.size())); - return pos == nullptr ? std::string::npos : pos - haystack.data(); -} - -#if FOLLY_HAVE_MEMRCHR -template <> -inline size_t rfind(const Range& haystack, - const unsigned char& needle) { - auto pos = static_cast( - ::memrchr(haystack.data(), needle, haystack.size())); - return pos == nullptr ? std::string::npos : pos - haystack.data(); -} -#endif - -template -size_t qfind_first_of(const Range& haystack, - const Range& needles) { - return qfind_first_of(haystack, needles, asciiCaseSensitive); -} - -// specialization for StringPiece -template <> -inline size_t qfind_first_of(const Range& haystack, - const Range& needles) { - return detail::qfind_first_byte_of(haystack, needles); -} - -// specialization for ByteRange -template <> -inline size_t qfind_first_of(const Range& haystack, - const Range& needles) { - return detail::qfind_first_byte_of(StringPiece(haystack), - StringPiece(needles)); -} -} // !namespace folly - -#pragma GCC diagnostic pop - -FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1(folly::Range); - -#endif // FOLLY_RANGE_H_ diff --git a/hphp/third_party/folly/folly/ScopeGuard.h b/hphp/third_party/folly/folly/ScopeGuard.h deleted file mode 100644 index a3050a847..000000000 --- a/hphp/third_party/folly/folly/ScopeGuard.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_SCOPEGUARD_H_ -#define FOLLY_SCOPEGUARD_H_ - -#include -#include -#include - -#include "folly/Preprocessor.h" - -namespace folly { - -/** - * ScopeGuard is a general implementation of the "Initialization is - * Resource Acquisition" idiom. Basically, it guarantees that a function - * is executed upon leaving the currrent scope unless otherwise told. - * - * The makeGuard() function is used to create a new ScopeGuard object. - * It can be instantiated with a lambda function, a std::function, - * a functor, or a void(*)() function pointer. - * - * - * Usage example: Add a friend to memory iff it is also added to the db. - * - * void User::addFriend(User& newFriend) { - * // add the friend to memory - * friends_.push_back(&newFriend); - * - * // If the db insertion that follows fails, we should - * // remove it from memory. - * // (You could also declare this as "auto guard = makeGuard(...)") - * ScopeGuard guard = makeGuard([&] { friends_.pop_back(); }); - * - * // this will throw an exception upon error, which - * // makes the ScopeGuard execute UserCont::pop_back() - * // once the Guard's destructor is called. - * db_->addFriend(GetName(), newFriend.GetName()); - * - * // an exception was not thrown, so don't execute - * // the Guard. - * guard.dismiss(); - * } - * - * Examine ScopeGuardTest.cpp for some more sample usage. - * - * Stolen from: - * Andrei's and Petru Marginean's CUJ article: - * http://drdobbs.com/184403758 - * and the loki library: - * http://loki-lib.sourceforge.net/index.php?n=Idioms.ScopeGuardPointer - * and triendl.kj article: - * http://www.codeproject.com/KB/cpp/scope_guard.aspx - */ -class ScopeGuardImplBase { - public: - void dismiss() noexcept { - dismissed_ = true; - } - - protected: - ScopeGuardImplBase() - : dismissed_(false) {} - - ScopeGuardImplBase(ScopeGuardImplBase&& other) - : dismissed_(other.dismissed_) { - other.dismissed_ = true; - } - - bool dismissed_; -}; - -template -class ScopeGuardImpl : public ScopeGuardImplBase { - public: - explicit ScopeGuardImpl(const FunctionType& fn) - : function_(fn) {} - - explicit ScopeGuardImpl(FunctionType&& fn) - : function_(std::move(fn)) {} - - ScopeGuardImpl(ScopeGuardImpl&& other) - : ScopeGuardImplBase(std::move(other)), - function_(std::move(other.function_)) { - } - - ~ScopeGuardImpl() noexcept { - if (!dismissed_) { - execute(); - } - } - - private: - void* operator new(size_t) = delete; - - void execute() noexcept { function_(); } - - FunctionType function_; -}; - -template -ScopeGuardImpl::type> -makeGuard(FunctionType&& fn) { - return ScopeGuardImpl::type>( - std::forward(fn)); -} - -/** - * This is largely unneeded if you just use auto for your guards. - */ -typedef ScopeGuardImplBase&& ScopeGuard; - -namespace detail { -/** - * Internal use for the macro SCOPE_EXIT below - */ -enum class ScopeGuardOnExit {}; - -template -ScopeGuardImpl::type> -operator+(detail::ScopeGuardOnExit, FunctionType&& fn) { - return ScopeGuardImpl::type>( - std::forward(fn)); -} -} // namespace detail - -} // folly - -#define SCOPE_EXIT \ - auto FB_ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) \ - = ::folly::detail::ScopeGuardOnExit() + [&] - -#endif // FOLLY_SCOPEGUARD_H_ diff --git a/hphp/third_party/folly/folly/SmallLocks.h b/hphp/third_party/folly/folly/SmallLocks.h deleted file mode 100644 index 01387aee7..000000000 --- a/hphp/third_party/folly/folly/SmallLocks.h +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_SMALLLOCKS_H_ -#define FOLLY_SMALLLOCKS_H_ - -/* - * This header defines a few very small mutex types. These are useful - * in highly memory-constrained environments where contention is - * unlikely. - * - * Note: these locks are for use when you aren't likely to contend on - * the critical section, or when the critical section is incredibly - * small. Given that, both of the locks defined in this header are - * inherently unfair: that is, the longer a thread is waiting, the - * longer it waits between attempts to acquire, so newer waiters are - * more likely to get the mutex. For the intended use-case this is - * fine. - * - * @author Keith Adams - * @author Jordan DeLong - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#ifndef __x86_64__ -# error "SmallLocks.h is currently x64-only." -#endif - -#include "folly/Portability.h" - -namespace folly { - -////////////////////////////////////////////////////////////////////// - -namespace detail { - - /* - * A helper object for the condended case. Starts off with eager - * spinning, and falls back to sleeping for small quantums. - */ - class Sleeper { - static const uint32_t kMaxActiveSpin = 4000; - - uint32_t spinCount; - - public: - Sleeper() : spinCount(0) {} - - void wait() { - if (spinCount < kMaxActiveSpin) { - ++spinCount; - asm volatile("pause"); - } else { - /* - * Always sleep 0.5ms, assuming this will make the kernel put - * us down for whatever its minimum timer resolution is (in - * linux this varies by kernel version from 1ms to 10ms). - */ - struct timespec ts = { 0, 500000 }; - nanosleep(&ts, NULL); - } - } - }; - -} - -////////////////////////////////////////////////////////////////////// - -/* - * A really, *really* small spinlock for fine-grained locking of lots - * of teeny-tiny data. - * - * Zero initializing these is guaranteed to be as good as calling - * init(), since the free state is guaranteed to be all-bits zero. - * - * This class should be kept a POD, so we can used it in other packed - * structs (gcc does not allow __attribute__((packed)) on structs that - * contain non-POD data). This means avoid adding a constructor, or - * making some members private, etc. - */ -struct MicroSpinLock { - enum { FREE = 0, LOCKED = 1 }; - uint8_t lock_; - - /* - * Atomically move lock_ from "compare" to "newval". Return boolean - * success. Do not play on or around. - */ - bool cas(uint8_t compare, uint8_t newVal) { - bool out; - asm volatile("lock; cmpxchgb %2, (%3);" - "setz %0;" - : "=r" (out) - : "a" (compare), // cmpxchgb constrains this to be in %al - "q" (newVal), // Needs to be byte-accessible - "r" (&lock_) - : "memory", "flags"); - return out; - } - - // Initialize this MSL. It is unnecessary to call this if you - // zero-initialize the MicroSpinLock. - void init() { - lock_ = FREE; - } - - bool try_lock() { - return cas(FREE, LOCKED); - } - - void lock() { - detail::Sleeper sleeper; - do { - while (lock_ != FREE) { - asm volatile("" : : : "memory"); - sleeper.wait(); - } - } while (!try_lock()); - DCHECK(lock_ == LOCKED); - } - - void unlock() { - CHECK(lock_ == LOCKED); - asm volatile("" : : : "memory"); - lock_ = FREE; // release barrier on x86 - } -}; - -////////////////////////////////////////////////////////////////////// - -/* - * Spin lock on a single bit in an integral type. You can use this - * with 16, 32, or 64-bit integral types. - * - * This is useful if you want a small lock and already have an int - * with a bit in it that you aren't using. But note that it can't be - * as small as MicroSpinLock (1 byte), if you don't already have a - * convenient int with an unused bit lying around to put it on. - * - * To construct these, either use init() or zero initialize. We don't - * have a real constructor because we want this to be a POD type so we - * can put it into packed structs. - */ -template -struct PicoSpinLock { - // Internally we deal with the unsigned version of the type. - typedef typename std::make_unsigned::type UIntType; - - static_assert(std::is_integral::value, - "PicoSpinLock needs an integral type"); - static_assert(sizeof(IntType) == 2 || sizeof(IntType) == 4 || - sizeof(IntType) == 8, - "PicoSpinLock can't work on integers smaller than 2 bytes"); - - public: - static const UIntType kLockBitMask_ = UIntType(1) << Bit; - UIntType lock_; - - /* - * You must call this function before using this class, if you - * default constructed it. If you zero-initialized it you can - * assume the PicoSpinLock is in a valid unlocked state with - * getData() == 0. - * - * (This doesn't use a constructor because we want to be a POD.) - */ - void init(IntType initialValue = 0) { - CHECK(!(initialValue & kLockBitMask_)); - lock_ = initialValue; - } - - /* - * Returns the value of the integer we using for our lock, except - * with the bit we are using as a lock cleared, regardless of - * whether the lock is held. - * - * It is 'safe' to call this without holding the lock. (As in: you - * get the same guarantees for simultaneous accesses to an integer - * as you normally get.) - */ - IntType getData() const { - return static_cast(lock_ & ~kLockBitMask_); - } - - /* - * Set the value of the other bits in our integer. - * - * Don't use this when you aren't holding the lock, unless it can be - * guaranteed that no other threads may be trying to use this. - */ - void setData(IntType w) { - CHECK(!(w & kLockBitMask_)); - lock_ = (lock_ & kLockBitMask_) | w; - } - - /* - * Try to get the lock without blocking: returns whether or not we - * got it. - */ - bool try_lock() const { - bool ret = false; - -#define FB_DOBTS(size) \ - asm volatile("lock; bts" #size " %1, (%2); setnc %0" \ - : "=r" (ret) \ - : "i" (Bit), \ - "r" (&lock_) \ - : "memory", "flags") - - switch (sizeof(IntType)) { - case 2: FB_DOBTS(w); break; - case 4: FB_DOBTS(l); break; - case 8: FB_DOBTS(q); break; - } - -#undef FB_DOBTS - - return ret; - } - - /* - * Block until we can acquire the lock. Uses Sleeper to wait. - */ - void lock() const { - detail::Sleeper sleeper; - while (!try_lock()) { - sleeper.wait(); - } - } - - /* - * Release the lock, without changing the value of the rest of the - * integer. - */ - void unlock() const { -#define FB_DOBTR(size) \ - asm volatile("lock; btr" #size " %0, (%1)" \ - : \ - : "i" (Bit), \ - "r" (&lock_) \ - : "memory", "flags") - - - // Reads and writes can not be reordered wrt locked instructions, - // so we don't need a memory fence here. - switch (sizeof(IntType)) { - case 2: FB_DOBTR(w); break; - case 4: FB_DOBTR(l); break; - case 8: FB_DOBTR(q); break; - } - -#undef FB_DOBTR - } -}; - -////////////////////////////////////////////////////////////////////// - -/** - * Array of spinlocks where each one is padded to prevent false sharing. - * Useful for shard-based locking implementations in environments where - * contention is unlikely. - */ - -// TODO: generate it from configure (`getconf LEVEL1_DCACHE_LINESIZE`) -#define FOLLY_CACHE_LINE_SIZE 64 - -template -struct SpinLockArray { - T& operator[](size_t i) { - return data_[i].lock; - } - - const T& operator[](size_t i) const { - return data_[i].lock; - } - - constexpr size_t size() const { return N; } - - private: - struct PaddedSpinLock { - PaddedSpinLock() : lock() { } - T lock; - char padding[FOLLY_CACHE_LINE_SIZE - sizeof(T)]; - }; - static_assert(sizeof(PaddedSpinLock) == FOLLY_CACHE_LINE_SIZE, - "Invalid size of PaddedSpinLock"); - - // Check if T can theoretically cross a cache line. - // NOTE: It should be alignof(std::max_align_t), but max_align_t - // isn't supported by gcc 4.6.2. - static_assert(alignof(MaxAlign) > 0 && - FOLLY_CACHE_LINE_SIZE % alignof(MaxAlign) == 0 && - sizeof(T) <= alignof(MaxAlign), - "T can cross cache line boundaries"); - - char padding_[FOLLY_CACHE_LINE_SIZE]; - std::array data_; -} __attribute__((aligned)); - -////////////////////////////////////////////////////////////////////// - -typedef std::lock_guard MSLGuard; - -////////////////////////////////////////////////////////////////////// - -} - -#endif diff --git a/hphp/third_party/folly/folly/SpookyHash.cpp b/hphp/third_party/folly/folly/SpookyHash.cpp deleted file mode 100644 index a439b9d22..000000000 --- a/hphp/third_party/folly/folly/SpookyHash.cpp +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Copyright 2012 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. - */ - -// Spooky Hash -// A 128-bit noncryptographic hash, for checksums and table lookup -// By Bob Jenkins. Public domain. -// Oct 31 2010: published framework, disclaimer ShortHash isn't right -// Nov 7 2010: disabled ShortHash -// Oct 31 2011: replace End, ShortMix, ShortEnd, enable ShortHash again -// April 10 2012: buffer overflow on platforms without unaligned reads -// July 12 2012: was passing out variables in final to in/out in short -// July 30 2012: I reintroduced the buffer overflow - -#include "folly/SpookyHash.h" - -#include - -#define ALLOW_UNALIGNED_READS 1 - -namespace folly { -namespace hash { - -// -// short hash ... it could be used on any message, -// but it's used by Spooky just for short messages. -// -void SpookyHash::Short( - const void *message, - size_t length, - uint64_t *hash1, - uint64_t *hash2) -{ - uint64_t buf[2*sc_numVars]; - union - { - const uint8_t *p8; - uint32_t *p32; - uint64_t *p64; - size_t i; - } u; - - u.p8 = (const uint8_t *)message; - - if (!ALLOW_UNALIGNED_READS && (u.i & 0x7)) - { - memcpy(buf, message, length); - u.p64 = buf; - } - - size_t remainder = length%32; - uint64_t a=*hash1; - uint64_t b=*hash2; - uint64_t c=sc_const; - uint64_t d=sc_const; - - if (length > 15) - { - const uint64_t *end = u.p64 + (length/32)*4; - - // handle all complete sets of 32 bytes - for (; u.p64 < end; u.p64 += 4) - { - c += u.p64[0]; - d += u.p64[1]; - ShortMix(a,b,c,d); - a += u.p64[2]; - b += u.p64[3]; - } - - //Handle the case of 16+ remaining bytes. - if (remainder >= 16) - { - c += u.p64[0]; - d += u.p64[1]; - ShortMix(a,b,c,d); - u.p64 += 2; - remainder -= 16; - } - } - - // Handle the last 0..15 bytes, and its length - d = ((uint64_t)length) << 56; - switch (remainder) - { - case 15: - d += ((uint64_t)u.p8[14]) << 48; - case 14: - d += ((uint64_t)u.p8[13]) << 40; - case 13: - d += ((uint64_t)u.p8[12]) << 32; - case 12: - d += u.p32[2]; - c += u.p64[0]; - break; - case 11: - d += ((uint64_t)u.p8[10]) << 16; - case 10: - d += ((uint64_t)u.p8[9]) << 8; - case 9: - d += (uint64_t)u.p8[8]; - case 8: - c += u.p64[0]; - break; - case 7: - c += ((uint64_t)u.p8[6]) << 48; - case 6: - c += ((uint64_t)u.p8[5]) << 40; - case 5: - c += ((uint64_t)u.p8[4]) << 32; - case 4: - c += u.p32[0]; - break; - case 3: - c += ((uint64_t)u.p8[2]) << 16; - case 2: - c += ((uint64_t)u.p8[1]) << 8; - case 1: - c += (uint64_t)u.p8[0]; - break; - case 0: - c += sc_const; - d += sc_const; - } - ShortEnd(a,b,c,d); - *hash1 = a; - *hash2 = b; -} - - - - -// do the whole hash in one call -void SpookyHash::Hash128( - const void *message, - size_t length, - uint64_t *hash1, - uint64_t *hash2) -{ - if (length < sc_bufSize) - { - Short(message, length, hash1, hash2); - return; - } - - uint64_t h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11; - uint64_t buf[sc_numVars]; - uint64_t *end; - union - { - const uint8_t *p8; - uint64_t *p64; - size_t i; - } u; - size_t remainder; - - h0=h3=h6=h9 = *hash1; - h1=h4=h7=h10 = *hash2; - h2=h5=h8=h11 = sc_const; - - u.p8 = (const uint8_t *)message; - end = u.p64 + (length/sc_blockSize)*sc_numVars; - - // handle all whole sc_blockSize blocks of bytes - if (ALLOW_UNALIGNED_READS || ((u.i & 0x7) == 0)) - { - while (u.p64 < end) - { - Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - u.p64 += sc_numVars; - } - } - else - { - while (u.p64 < end) - { - memcpy(buf, u.p64, sc_blockSize); - Mix(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - u.p64 += sc_numVars; - } - } - - // handle the last partial block of sc_blockSize bytes - remainder = (length - ((const uint8_t *)end-(const uint8_t *)message)); - memcpy(buf, end, remainder); - memset(((uint8_t *)buf)+remainder, 0, sc_blockSize-remainder); - ((uint8_t *)buf)[sc_blockSize-1] = remainder; - Mix(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - - // do some final mixing - End(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - *hash1 = h0; - *hash2 = h1; -} - - - -// init spooky state -void SpookyHash::Init(uint64_t seed1, uint64_t seed2) -{ - m_length = 0; - m_remainder = 0; - m_state[0] = seed1; - m_state[1] = seed2; -} - - -// add a message fragment to the state -void SpookyHash::Update(const void *message, size_t length) -{ - uint64_t h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11; - size_t newLength = length + m_remainder; - uint8_t remainder; - union - { - const uint8_t *p8; - uint64_t *p64; - size_t i; - } u; - const uint64_t *end; - - // Is this message fragment too short? If it is, stuff it away. - if (newLength < sc_bufSize) - { - memcpy(&((uint8_t *)m_data)[m_remainder], message, length); - m_length = length + m_length; - m_remainder = (uint8_t)newLength; - return; - } - - // init the variables - if (m_length < sc_bufSize) - { - h0=h3=h6=h9 = m_state[0]; - h1=h4=h7=h10 = m_state[1]; - h2=h5=h8=h11 = sc_const; - } - else - { - h0 = m_state[0]; - h1 = m_state[1]; - h2 = m_state[2]; - h3 = m_state[3]; - h4 = m_state[4]; - h5 = m_state[5]; - h6 = m_state[6]; - h7 = m_state[7]; - h8 = m_state[8]; - h9 = m_state[9]; - h10 = m_state[10]; - h11 = m_state[11]; - } - m_length = length + m_length; - - // if we've got anything stuffed away, use it now - if (m_remainder) - { - uint8_t prefix = sc_bufSize-m_remainder; - memcpy(&(((uint8_t *)m_data)[m_remainder]), message, prefix); - u.p64 = m_data; - Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - Mix(&u.p64[sc_numVars], h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - u.p8 = ((const uint8_t *)message) + prefix; - length -= prefix; - } - else - { - u.p8 = (const uint8_t *)message; - } - - // handle all whole blocks of sc_blockSize bytes - end = u.p64 + (length/sc_blockSize)*sc_numVars; - remainder = (uint8_t)(length-((const uint8_t *)end-u.p8)); - if (ALLOW_UNALIGNED_READS || (u.i & 0x7) == 0) - { - while (u.p64 < end) - { - Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - u.p64 += sc_numVars; - } - } - else - { - while (u.p64 < end) - { - memcpy(m_data, u.p8, sc_blockSize); - Mix(m_data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - u.p64 += sc_numVars; - } - } - - // stuff away the last few bytes - m_remainder = remainder; - memcpy(m_data, end, remainder); - - // stuff away the variables - m_state[0] = h0; - m_state[1] = h1; - m_state[2] = h2; - m_state[3] = h3; - m_state[4] = h4; - m_state[5] = h5; - m_state[6] = h6; - m_state[7] = h7; - m_state[8] = h8; - m_state[9] = h9; - m_state[10] = h10; - m_state[11] = h11; -} - - -// report the hash for the concatenation of all message fragments so far -void SpookyHash::Final(uint64_t *hash1, uint64_t *hash2) -{ - // init the variables - if (m_length < sc_bufSize) - { - *hash1 = m_state[0]; - *hash2 = m_state[1]; - Short( m_data, m_length, hash1, hash2); - return; - } - - const uint64_t *data = (const uint64_t *)m_data; - uint8_t remainder = m_remainder; - - uint64_t h0 = m_state[0]; - uint64_t h1 = m_state[1]; - uint64_t h2 = m_state[2]; - uint64_t h3 = m_state[3]; - uint64_t h4 = m_state[4]; - uint64_t h5 = m_state[5]; - uint64_t h6 = m_state[6]; - uint64_t h7 = m_state[7]; - uint64_t h8 = m_state[8]; - uint64_t h9 = m_state[9]; - uint64_t h10 = m_state[10]; - uint64_t h11 = m_state[11]; - - if (remainder >= sc_blockSize) - { - // m_data can contain two blocks; handle any whole first block - Mix(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - data += sc_numVars; - remainder -= sc_blockSize; - } - - // mix in the last partial block, and the length mod sc_blockSize - memset(&((uint8_t *)data)[remainder], 0, (sc_blockSize-remainder)); - - ((uint8_t *)data)[sc_blockSize-1] = remainder; - Mix(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - - // do some final mixing - End(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - - *hash1 = h0; - *hash2 = h1; -} - -} // namespace hash -} // namespace folly diff --git a/hphp/third_party/folly/folly/SpookyHash.h b/hphp/third_party/folly/folly/SpookyHash.h deleted file mode 100644 index 3f26e5f38..000000000 --- a/hphp/third_party/folly/folly/SpookyHash.h +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright 2012 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. - */ - -// -// SpookyHash: a 128-bit noncryptographic hash function -// By Bob Jenkins, public domain -// Oct 31 2010: alpha, framework + SpookyHash::Mix appears right -// Oct 31 2011: alpha again, Mix only good to 2^^69 but rest appears right -// Dec 31 2011: beta, improved Mix, tested it for 2-bit deltas -// Feb 2 2012: production, same bits as beta -// Feb 5 2012: adjusted definitions of uint* to be more portable -// Mar 30 2012: 3 bytes/cycle, not 4. Alpha was 4 but wasn't thorough enough. -// -// Up to 3 bytes/cycle for long messages. Reasonably fast for short messages. -// All 1 or 2 bit deltas achieve avalanche within 1% bias per output bit. -// -// This was developed for and tested on 64-bit x86-compatible processors. -// It assumes the processor is little-endian. There is a macro -// controlling whether unaligned reads are allowed (by default they are). -// This should be an equally good hash on big-endian machines, but it will -// compute different results on them than on little-endian machines. -// -// Google's CityHash has similar specs to SpookyHash, and CityHash is faster -// on some platforms. MD4 and MD5 also have similar specs, but they are orders -// of magnitude slower. CRCs are two or more times slower, but unlike -// SpookyHash, they have nice math for combining the CRCs of pieces to form -// the CRCs of wholes. There are also cryptographic hashes, but those are even -// slower than MD5. -// - -#ifndef FOLLY_SPOOKYHASH_H_ -#define FOLLY_SPOOKYHASH_H_ - -#include -#include - -namespace folly { -namespace hash { - -class SpookyHash -{ -public: - // - // SpookyHash: hash a single message in one call, produce 128-bit output - // - static void Hash128( - const void *message, // message to hash - size_t length, // length of message in bytes - uint64_t *hash1, // in/out: in seed 1, out hash value 1 - uint64_t *hash2); // in/out: in seed 2, out hash value 2 - - // - // Hash64: hash a single message in one call, return 64-bit output - // - static uint64_t Hash64( - const void *message, // message to hash - size_t length, // length of message in bytes - uint64_t seed) // seed - { - uint64_t hash1 = seed; - Hash128(message, length, &hash1, &seed); - return hash1; - } - - // - // Hash32: hash a single message in one call, produce 32-bit output - // - static uint32_t Hash32( - const void *message, // message to hash - size_t length, // length of message in bytes - uint32_t seed) // seed - { - uint64_t hash1 = seed, hash2 = seed; - Hash128(message, length, &hash1, &hash2); - return (uint32_t)hash1; - } - - // - // Init: initialize the context of a SpookyHash - // - void Init( - uint64_t seed1, // any 64-bit value will do, including 0 - uint64_t seed2); // different seeds produce independent hashes - - // - // Update: add a piece of a message to a SpookyHash state - // - void Update( - const void *message, // message fragment - size_t length); // length of message fragment in bytes - - - // - // Final: compute the hash for the current SpookyHash state - // - // This does not modify the state; you can keep updating it afterward - // - // The result is the same as if SpookyHash() had been called with - // all the pieces concatenated into one message. - // - void Final( - uint64_t *hash1, // out only: first 64 bits of hash value. - uint64_t *hash2); // out only: second 64 bits of hash value. - - // - // left rotate a 64-bit value by k bytes - // - static inline uint64_t Rot64(uint64_t x, int k) - { - return (x << k) | (x >> (64 - k)); - } - - // - // This is used if the input is 96 bytes long or longer. - // - // The internal state is fully overwritten every 96 bytes. - // Every input bit appears to cause at least 128 bits of entropy - // before 96 other bytes are combined, when run forward or backward - // For every input bit, - // Two inputs differing in just that input bit - // Where "differ" means xor or subtraction - // And the base value is random - // When run forward or backwards one Mix - // I tried 3 pairs of each; they all differed by at least 212 bits. - // - static inline void Mix( - const uint64_t *data, - uint64_t &s0, uint64_t &s1, uint64_t &s2, uint64_t &s3, - uint64_t &s4, uint64_t &s5, uint64_t &s6, uint64_t &s7, - uint64_t &s8, uint64_t &s9, uint64_t &s10,uint64_t &s11) - { - s0 += data[0]; s2 ^= s10; s11 ^= s0; s0 = Rot64(s0,11); s11 += s1; - s1 += data[1]; s3 ^= s11; s0 ^= s1; s1 = Rot64(s1,32); s0 += s2; - s2 += data[2]; s4 ^= s0; s1 ^= s2; s2 = Rot64(s2,43); s1 += s3; - s3 += data[3]; s5 ^= s1; s2 ^= s3; s3 = Rot64(s3,31); s2 += s4; - s4 += data[4]; s6 ^= s2; s3 ^= s4; s4 = Rot64(s4,17); s3 += s5; - s5 += data[5]; s7 ^= s3; s4 ^= s5; s5 = Rot64(s5,28); s4 += s6; - s6 += data[6]; s8 ^= s4; s5 ^= s6; s6 = Rot64(s6,39); s5 += s7; - s7 += data[7]; s9 ^= s5; s6 ^= s7; s7 = Rot64(s7,57); s6 += s8; - s8 += data[8]; s10 ^= s6; s7 ^= s8; s8 = Rot64(s8,55); s7 += s9; - s9 += data[9]; s11 ^= s7; s8 ^= s9; s9 = Rot64(s9,54); s8 += s10; - s10 += data[10]; s0 ^= s8; s9 ^= s10; s10 = Rot64(s10,22); s9 += s11; - s11 += data[11]; s1 ^= s9; s10 ^= s11; s11 = Rot64(s11,46); s10 += s0; - } - - // - // Mix all 12 inputs together so that h0, h1 are a hash of them all. - // - // For two inputs differing in just the input bits - // Where "differ" means xor or subtraction - // And the base value is random, or a counting value starting at that bit - // The final result will have each bit of h0, h1 flip - // For every input bit, - // with probability 50 +- .3% - // For every pair of input bits, - // with probability 50 +- 3% - // - // This does not rely on the last Mix() call having already mixed some. - // Two iterations was almost good enough for a 64-bit result, but a - // 128-bit result is reported, so End() does three iterations. - // - static inline void EndPartial( - uint64_t &h0, uint64_t &h1, uint64_t &h2, uint64_t &h3, - uint64_t &h4, uint64_t &h5, uint64_t &h6, uint64_t &h7, - uint64_t &h8, uint64_t &h9, uint64_t &h10,uint64_t &h11) - { - h11+= h1; h2 ^= h11; h1 = Rot64(h1,44); - h0 += h2; h3 ^= h0; h2 = Rot64(h2,15); - h1 += h3; h4 ^= h1; h3 = Rot64(h3,34); - h2 += h4; h5 ^= h2; h4 = Rot64(h4,21); - h3 += h5; h6 ^= h3; h5 = Rot64(h5,38); - h4 += h6; h7 ^= h4; h6 = Rot64(h6,33); - h5 += h7; h8 ^= h5; h7 = Rot64(h7,10); - h6 += h8; h9 ^= h6; h8 = Rot64(h8,13); - h7 += h9; h10^= h7; h9 = Rot64(h9,38); - h8 += h10; h11^= h8; h10= Rot64(h10,53); - h9 += h11; h0 ^= h9; h11= Rot64(h11,42); - h10+= h0; h1 ^= h10; h0 = Rot64(h0,54); - } - - static inline void End( - uint64_t &h0, uint64_t &h1, uint64_t &h2, uint64_t &h3, - uint64_t &h4, uint64_t &h5, uint64_t &h6, uint64_t &h7, - uint64_t &h8, uint64_t &h9, uint64_t &h10,uint64_t &h11) - { - EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - } - - // - // The goal is for each bit of the input to expand into 128 bits of - // apparent entropy before it is fully overwritten. - // n trials both set and cleared at least m bits of h0 h1 h2 h3 - // n: 2 m: 29 - // n: 3 m: 46 - // n: 4 m: 57 - // n: 5 m: 107 - // n: 6 m: 146 - // n: 7 m: 152 - // when run forwards or backwards - // for all 1-bit and 2-bit diffs - // with diffs defined by either xor or subtraction - // with a base of all zeros plus a counter, or plus another bit, or random - // - static inline void ShortMix(uint64_t &h0, uint64_t &h1, uint64_t &h2, uint64_t &h3) - { - h2 = Rot64(h2,50); h2 += h3; h0 ^= h2; - h3 = Rot64(h3,52); h3 += h0; h1 ^= h3; - h0 = Rot64(h0,30); h0 += h1; h2 ^= h0; - h1 = Rot64(h1,41); h1 += h2; h3 ^= h1; - h2 = Rot64(h2,54); h2 += h3; h0 ^= h2; - h3 = Rot64(h3,48); h3 += h0; h1 ^= h3; - h0 = Rot64(h0,38); h0 += h1; h2 ^= h0; - h1 = Rot64(h1,37); h1 += h2; h3 ^= h1; - h2 = Rot64(h2,62); h2 += h3; h0 ^= h2; - h3 = Rot64(h3,34); h3 += h0; h1 ^= h3; - h0 = Rot64(h0,5); h0 += h1; h2 ^= h0; - h1 = Rot64(h1,36); h1 += h2; h3 ^= h1; - } - - // - // Mix all 4 inputs together so that h0, h1 are a hash of them all. - // - // For two inputs differing in just the input bits - // Where "differ" means xor or subtraction - // And the base value is random, or a counting value starting at that bit - // The final result will have each bit of h0, h1 flip - // For every input bit, - // with probability 50 +- .3% (it is probably better than that) - // For every pair of input bits, - // with probability 50 +- .75% (the worst case is approximately that) - // - static inline void ShortEnd(uint64_t &h0, uint64_t &h1, uint64_t &h2, uint64_t &h3) - { - h3 ^= h2; h2 = Rot64(h2,15); h3 += h2; - h0 ^= h3; h3 = Rot64(h3,52); h0 += h3; - h1 ^= h0; h0 = Rot64(h0,26); h1 += h0; - h2 ^= h1; h1 = Rot64(h1,51); h2 += h1; - h3 ^= h2; h2 = Rot64(h2,28); h3 += h2; - h0 ^= h3; h3 = Rot64(h3,9); h0 += h3; - h1 ^= h0; h0 = Rot64(h0,47); h1 += h0; - h2 ^= h1; h1 = Rot64(h1,54); h2 += h1; - h3 ^= h2; h2 = Rot64(h2,32); h3 += h2; - h0 ^= h3; h3 = Rot64(h3,25); h0 += h3; - h1 ^= h0; h0 = Rot64(h0,63); h1 += h0; - } - -private: - - // - // Short is used for messages under 192 bytes in length - // Short has a low startup cost, the normal mode is good for long - // keys, the cost crossover is at about 192 bytes. The two modes were - // held to the same quality bar. - // - static void Short( - const void *message, // message (array of bytes, not necessarily aligned) - size_t length, // length of message (in bytes) - uint64_t *hash1, // in/out: in the seed, out the hash value - uint64_t *hash2); // in/out: in the seed, out the hash value - - // number of uint64_t's in internal state - static const size_t sc_numVars = 12; - - // size of the internal state - static const size_t sc_blockSize = sc_numVars*8; - - // size of buffer of unhashed data, in bytes - static const size_t sc_bufSize = 2*sc_blockSize; - - // - // sc_const: a constant which: - // * is not zero - // * is odd - // * is a not-very-regular mix of 1's and 0's - // * does not need any other special mathematical properties - // - static const uint64_t sc_const = 0xdeadbeefdeadbeefLL; - - uint64_t m_data[2*sc_numVars]; // unhashed data, for partial messages - uint64_t m_state[sc_numVars]; // internal state of the hash - size_t m_length; // total length of the input so far - uint8_t m_remainder; // length of unhashed data stashed in m_data -}; - -} // namespace hash -} // namespace folly - -#endif - diff --git a/hphp/third_party/folly/folly/SpookyHashV1.cpp b/hphp/third_party/folly/folly/SpookyHashV1.cpp deleted file mode 100644 index 0940317fb..000000000 --- a/hphp/third_party/folly/folly/SpookyHashV1.cpp +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Copyright 2013 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. - */ - -// Spooky Hash -// A 128-bit noncryptographic hash, for checksums and table lookup -// By Bob Jenkins. Public domain. -// Oct 31 2010: published framework, disclaimer ShortHash isn't right -// Nov 7 2010: disabled ShortHash -// Oct 31 2011: replace End, ShortMix, ShortEnd, enable ShortHash again -// April 10 2012: buffer overflow on platforms without unaligned reads -// July 12 2012: was passing out variables in final to in/out in short -// July 30 2012: I reintroduced the buffer overflow - -#include "folly/SpookyHashV1.h" - -#include - -#define ALLOW_UNALIGNED_READS 1 - -namespace folly { -namespace hash { - -// -// short hash ... it could be used on any message, -// but it's used by Spooky just for short messages. -// -void SpookyHashV1::Short( - const void *message, - size_t length, - uint64_t *hash1, - uint64_t *hash2) -{ - uint64_t buf[2*sc_numVars]; - union - { - const uint8_t *p8; - uint32_t *p32; - uint64_t *p64; - size_t i; - } u; - - u.p8 = (const uint8_t *)message; - - if (!ALLOW_UNALIGNED_READS && (u.i & 0x7)) - { - memcpy(buf, message, length); - u.p64 = buf; - } - - size_t remainder = length%32; - uint64_t a=*hash1; - uint64_t b=*hash2; - uint64_t c=sc_const; - uint64_t d=sc_const; - - if (length > 15) - { - const uint64_t *end = u.p64 + (length/32)*4; - - // handle all complete sets of 32 bytes - for (; u.p64 < end; u.p64 += 4) - { - c += u.p64[0]; - d += u.p64[1]; - ShortMix(a,b,c,d); - a += u.p64[2]; - b += u.p64[3]; - } - - //Handle the case of 16+ remaining bytes. - if (remainder >= 16) - { - c += u.p64[0]; - d += u.p64[1]; - ShortMix(a,b,c,d); - u.p64 += 2; - remainder -= 16; - } - } - - // Handle the last 0..15 bytes, and its length - d = ((uint64_t)length) << 56; - switch (remainder) - { - case 15: - d += ((uint64_t)u.p8[14]) << 48; - case 14: - d += ((uint64_t)u.p8[13]) << 40; - case 13: - d += ((uint64_t)u.p8[12]) << 32; - case 12: - d += u.p32[2]; - c += u.p64[0]; - break; - case 11: - d += ((uint64_t)u.p8[10]) << 16; - case 10: - d += ((uint64_t)u.p8[9]) << 8; - case 9: - d += (uint64_t)u.p8[8]; - case 8: - c += u.p64[0]; - break; - case 7: - c += ((uint64_t)u.p8[6]) << 48; - case 6: - c += ((uint64_t)u.p8[5]) << 40; - case 5: - c += ((uint64_t)u.p8[4]) << 32; - case 4: - c += u.p32[0]; - break; - case 3: - c += ((uint64_t)u.p8[2]) << 16; - case 2: - c += ((uint64_t)u.p8[1]) << 8; - case 1: - c += (uint64_t)u.p8[0]; - break; - case 0: - c += sc_const; - d += sc_const; - } - ShortEnd(a,b,c,d); - *hash1 = a; - *hash2 = b; -} - - - - -// do the whole hash in one call -void SpookyHashV1::Hash128( - const void *message, - size_t length, - uint64_t *hash1, - uint64_t *hash2) -{ - if (length < sc_bufSize) - { - Short(message, length, hash1, hash2); - return; - } - - uint64_t h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11; - uint64_t buf[sc_numVars]; - uint64_t *end; - union - { - const uint8_t *p8; - uint64_t *p64; - size_t i; - } u; - size_t remainder; - - h0=h3=h6=h9 = *hash1; - h1=h4=h7=h10 = *hash2; - h2=h5=h8=h11 = sc_const; - - u.p8 = (const uint8_t *)message; - end = u.p64 + (length/sc_blockSize)*sc_numVars; - - // handle all whole sc_blockSize blocks of bytes - if (ALLOW_UNALIGNED_READS || ((u.i & 0x7) == 0)) - { - while (u.p64 < end) - { - Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - u.p64 += sc_numVars; - } - } - else - { - while (u.p64 < end) - { - memcpy(buf, u.p64, sc_blockSize); - Mix(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - u.p64 += sc_numVars; - } - } - - // handle the last partial block of sc_blockSize bytes - remainder = (length - ((const uint8_t *)end-(const uint8_t *)message)); - memcpy(buf, end, remainder); - memset(((uint8_t *)buf)+remainder, 0, sc_blockSize-remainder); - ((uint8_t *)buf)[sc_blockSize-1] = remainder; - Mix(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - - // do some final mixing - End(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - *hash1 = h0; - *hash2 = h1; -} - - - -// init spooky state -void SpookyHashV1::Init(uint64_t seed1, uint64_t seed2) -{ - m_length = 0; - m_remainder = 0; - m_state[0] = seed1; - m_state[1] = seed2; -} - - -// add a message fragment to the state -void SpookyHashV1::Update(const void *message, size_t length) -{ - uint64_t h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11; - size_t newLength = length + m_remainder; - uint8_t remainder; - union - { - const uint8_t *p8; - uint64_t *p64; - size_t i; - } u; - const uint64_t *end; - - // Is this message fragment too short? If it is, stuff it away. - if (newLength < sc_bufSize) - { - memcpy(&((uint8_t *)m_data)[m_remainder], message, length); - m_length = length + m_length; - m_remainder = (uint8_t)newLength; - return; - } - - // init the variables - if (m_length < sc_bufSize) - { - h0=h3=h6=h9 = m_state[0]; - h1=h4=h7=h10 = m_state[1]; - h2=h5=h8=h11 = sc_const; - } - else - { - h0 = m_state[0]; - h1 = m_state[1]; - h2 = m_state[2]; - h3 = m_state[3]; - h4 = m_state[4]; - h5 = m_state[5]; - h6 = m_state[6]; - h7 = m_state[7]; - h8 = m_state[8]; - h9 = m_state[9]; - h10 = m_state[10]; - h11 = m_state[11]; - } - m_length = length + m_length; - - // if we've got anything stuffed away, use it now - if (m_remainder) - { - uint8_t prefix = sc_bufSize-m_remainder; - memcpy(&(((uint8_t *)m_data)[m_remainder]), message, prefix); - u.p64 = m_data; - Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - Mix(&u.p64[sc_numVars], h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - u.p8 = ((const uint8_t *)message) + prefix; - length -= prefix; - } - else - { - u.p8 = (const uint8_t *)message; - } - - // handle all whole blocks of sc_blockSize bytes - end = u.p64 + (length/sc_blockSize)*sc_numVars; - remainder = (uint8_t)(length-((const uint8_t *)end-u.p8)); - if (ALLOW_UNALIGNED_READS || (u.i & 0x7) == 0) - { - while (u.p64 < end) - { - Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - u.p64 += sc_numVars; - } - } - else - { - while (u.p64 < end) - { - memcpy(m_data, u.p8, sc_blockSize); - Mix(m_data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - u.p64 += sc_numVars; - } - } - - // stuff away the last few bytes - m_remainder = remainder; - memcpy(m_data, end, remainder); - - // stuff away the variables - m_state[0] = h0; - m_state[1] = h1; - m_state[2] = h2; - m_state[3] = h3; - m_state[4] = h4; - m_state[5] = h5; - m_state[6] = h6; - m_state[7] = h7; - m_state[8] = h8; - m_state[9] = h9; - m_state[10] = h10; - m_state[11] = h11; -} - - -// report the hash for the concatenation of all message fragments so far -void SpookyHashV1::Final(uint64_t *hash1, uint64_t *hash2) -{ - // init the variables - if (m_length < sc_bufSize) - { - *hash1 = m_state[0]; - *hash2 = m_state[1]; - Short( m_data, m_length, hash1, hash2); - return; - } - - const uint64_t *data = (const uint64_t *)m_data; - uint8_t remainder = m_remainder; - - uint64_t h0 = m_state[0]; - uint64_t h1 = m_state[1]; - uint64_t h2 = m_state[2]; - uint64_t h3 = m_state[3]; - uint64_t h4 = m_state[4]; - uint64_t h5 = m_state[5]; - uint64_t h6 = m_state[6]; - uint64_t h7 = m_state[7]; - uint64_t h8 = m_state[8]; - uint64_t h9 = m_state[9]; - uint64_t h10 = m_state[10]; - uint64_t h11 = m_state[11]; - - if (remainder >= sc_blockSize) - { - // m_data can contain two blocks; handle any whole first block - Mix(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - data += sc_numVars; - remainder -= sc_blockSize; - } - - // mix in the last partial block, and the length mod sc_blockSize - memset(&((uint8_t *)data)[remainder], 0, (sc_blockSize-remainder)); - - ((uint8_t *)data)[sc_blockSize-1] = remainder; - Mix(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - - // do some final mixing - End(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - - *hash1 = h0; - *hash2 = h1; -} - -} // namespace hash -} // namespace folly diff --git a/hphp/third_party/folly/folly/SpookyHashV1.h b/hphp/third_party/folly/folly/SpookyHashV1.h deleted file mode 100644 index cbd80d41b..000000000 --- a/hphp/third_party/folly/folly/SpookyHashV1.h +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright 2013 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. - */ - -// This is version 1 of SpookyHash, incompatible with version 2. -// -// SpookyHash: a 128-bit noncryptographic hash function -// By Bob Jenkins, public domain -// Oct 31 2010: alpha, framework + SpookyHash::Mix appears right -// Oct 31 2011: alpha again, Mix only good to 2^^69 but rest appears right -// Dec 31 2011: beta, improved Mix, tested it for 2-bit deltas -// Feb 2 2012: production, same bits as beta -// Feb 5 2012: adjusted definitions of uint* to be more portable -// Mar 30 2012: 3 bytes/cycle, not 4. Alpha was 4 but wasn't thorough enough. -// -// Up to 3 bytes/cycle for long messages. Reasonably fast for short messages. -// All 1 or 2 bit deltas achieve avalanche within 1% bias per output bit. -// -// This was developed for and tested on 64-bit x86-compatible processors. -// It assumes the processor is little-endian. There is a macro -// controlling whether unaligned reads are allowed (by default they are). -// This should be an equally good hash on big-endian machines, but it will -// compute different results on them than on little-endian machines. -// -// Google's CityHash has similar specs to SpookyHash, and CityHash is faster -// on some platforms. MD4 and MD5 also have similar specs, but they are orders -// of magnitude slower. CRCs are two or more times slower, but unlike -// SpookyHash, they have nice math for combining the CRCs of pieces to form -// the CRCs of wholes. There are also cryptographic hashes, but those are even -// slower than MD5. -// - -#ifndef FOLLY_SPOOKYHASHV1_H_ -#define FOLLY_SPOOKYHASHV1_H_ - -#include -#include - -namespace folly { -namespace hash { - -class SpookyHashV1 -{ -public: - // - // SpookyHash: hash a single message in one call, produce 128-bit output - // - static void Hash128( - const void *message, // message to hash - size_t length, // length of message in bytes - uint64_t *hash1, // in/out: in seed 1, out hash value 1 - uint64_t *hash2); // in/out: in seed 2, out hash value 2 - - // - // Hash64: hash a single message in one call, return 64-bit output - // - static uint64_t Hash64( - const void *message, // message to hash - size_t length, // length of message in bytes - uint64_t seed) // seed - { - uint64_t hash1 = seed; - Hash128(message, length, &hash1, &seed); - return hash1; - } - - // - // Hash32: hash a single message in one call, produce 32-bit output - // - static uint32_t Hash32( - const void *message, // message to hash - size_t length, // length of message in bytes - uint32_t seed) // seed - { - uint64_t hash1 = seed, hash2 = seed; - Hash128(message, length, &hash1, &hash2); - return (uint32_t)hash1; - } - - // - // Init: initialize the context of a SpookyHash - // - void Init( - uint64_t seed1, // any 64-bit value will do, including 0 - uint64_t seed2); // different seeds produce independent hashes - - // - // Update: add a piece of a message to a SpookyHash state - // - void Update( - const void *message, // message fragment - size_t length); // length of message fragment in bytes - - - // - // Final: compute the hash for the current SpookyHash state - // - // This does not modify the state; you can keep updating it afterward - // - // The result is the same as if SpookyHash() had been called with - // all the pieces concatenated into one message. - // - void Final( - uint64_t *hash1, // out only: first 64 bits of hash value. - uint64_t *hash2); // out only: second 64 bits of hash value. - - // - // left rotate a 64-bit value by k bytes - // - static inline uint64_t Rot64(uint64_t x, int k) - { - return (x << k) | (x >> (64 - k)); - } - - // - // This is used if the input is 96 bytes long or longer. - // - // The internal state is fully overwritten every 96 bytes. - // Every input bit appears to cause at least 128 bits of entropy - // before 96 other bytes are combined, when run forward or backward - // For every input bit, - // Two inputs differing in just that input bit - // Where "differ" means xor or subtraction - // And the base value is random - // When run forward or backwards one Mix - // I tried 3 pairs of each; they all differed by at least 212 bits. - // - static inline void Mix( - const uint64_t *data, - uint64_t &s0, uint64_t &s1, uint64_t &s2, uint64_t &s3, - uint64_t &s4, uint64_t &s5, uint64_t &s6, uint64_t &s7, - uint64_t &s8, uint64_t &s9, uint64_t &s10,uint64_t &s11) - { - s0 += data[0]; s2 ^= s10; s11 ^= s0; s0 = Rot64(s0,11); s11 += s1; - s1 += data[1]; s3 ^= s11; s0 ^= s1; s1 = Rot64(s1,32); s0 += s2; - s2 += data[2]; s4 ^= s0; s1 ^= s2; s2 = Rot64(s2,43); s1 += s3; - s3 += data[3]; s5 ^= s1; s2 ^= s3; s3 = Rot64(s3,31); s2 += s4; - s4 += data[4]; s6 ^= s2; s3 ^= s4; s4 = Rot64(s4,17); s3 += s5; - s5 += data[5]; s7 ^= s3; s4 ^= s5; s5 = Rot64(s5,28); s4 += s6; - s6 += data[6]; s8 ^= s4; s5 ^= s6; s6 = Rot64(s6,39); s5 += s7; - s7 += data[7]; s9 ^= s5; s6 ^= s7; s7 = Rot64(s7,57); s6 += s8; - s8 += data[8]; s10 ^= s6; s7 ^= s8; s8 = Rot64(s8,55); s7 += s9; - s9 += data[9]; s11 ^= s7; s8 ^= s9; s9 = Rot64(s9,54); s8 += s10; - s10 += data[10]; s0 ^= s8; s9 ^= s10; s10 = Rot64(s10,22); s9 += s11; - s11 += data[11]; s1 ^= s9; s10 ^= s11; s11 = Rot64(s11,46); s10 += s0; - } - - // - // Mix all 12 inputs together so that h0, h1 are a hash of them all. - // - // For two inputs differing in just the input bits - // Where "differ" means xor or subtraction - // And the base value is random, or a counting value starting at that bit - // The final result will have each bit of h0, h1 flip - // For every input bit, - // with probability 50 +- .3% - // For every pair of input bits, - // with probability 50 +- 3% - // - // This does not rely on the last Mix() call having already mixed some. - // Two iterations was almost good enough for a 64-bit result, but a - // 128-bit result is reported, so End() does three iterations. - // - static inline void EndPartial( - uint64_t &h0, uint64_t &h1, uint64_t &h2, uint64_t &h3, - uint64_t &h4, uint64_t &h5, uint64_t &h6, uint64_t &h7, - uint64_t &h8, uint64_t &h9, uint64_t &h10,uint64_t &h11) - { - h11+= h1; h2 ^= h11; h1 = Rot64(h1,44); - h0 += h2; h3 ^= h0; h2 = Rot64(h2,15); - h1 += h3; h4 ^= h1; h3 = Rot64(h3,34); - h2 += h4; h5 ^= h2; h4 = Rot64(h4,21); - h3 += h5; h6 ^= h3; h5 = Rot64(h5,38); - h4 += h6; h7 ^= h4; h6 = Rot64(h6,33); - h5 += h7; h8 ^= h5; h7 = Rot64(h7,10); - h6 += h8; h9 ^= h6; h8 = Rot64(h8,13); - h7 += h9; h10^= h7; h9 = Rot64(h9,38); - h8 += h10; h11^= h8; h10= Rot64(h10,53); - h9 += h11; h0 ^= h9; h11= Rot64(h11,42); - h10+= h0; h1 ^= h10; h0 = Rot64(h0,54); - } - - static inline void End( - uint64_t &h0, uint64_t &h1, uint64_t &h2, uint64_t &h3, - uint64_t &h4, uint64_t &h5, uint64_t &h6, uint64_t &h7, - uint64_t &h8, uint64_t &h9, uint64_t &h10,uint64_t &h11) - { - EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - } - - // - // The goal is for each bit of the input to expand into 128 bits of - // apparent entropy before it is fully overwritten. - // n trials both set and cleared at least m bits of h0 h1 h2 h3 - // n: 2 m: 29 - // n: 3 m: 46 - // n: 4 m: 57 - // n: 5 m: 107 - // n: 6 m: 146 - // n: 7 m: 152 - // when run forwards or backwards - // for all 1-bit and 2-bit diffs - // with diffs defined by either xor or subtraction - // with a base of all zeros plus a counter, or plus another bit, or random - // - static inline void ShortMix(uint64_t &h0, uint64_t &h1, uint64_t &h2, uint64_t &h3) - { - h2 = Rot64(h2,50); h2 += h3; h0 ^= h2; - h3 = Rot64(h3,52); h3 += h0; h1 ^= h3; - h0 = Rot64(h0,30); h0 += h1; h2 ^= h0; - h1 = Rot64(h1,41); h1 += h2; h3 ^= h1; - h2 = Rot64(h2,54); h2 += h3; h0 ^= h2; - h3 = Rot64(h3,48); h3 += h0; h1 ^= h3; - h0 = Rot64(h0,38); h0 += h1; h2 ^= h0; - h1 = Rot64(h1,37); h1 += h2; h3 ^= h1; - h2 = Rot64(h2,62); h2 += h3; h0 ^= h2; - h3 = Rot64(h3,34); h3 += h0; h1 ^= h3; - h0 = Rot64(h0,5); h0 += h1; h2 ^= h0; - h1 = Rot64(h1,36); h1 += h2; h3 ^= h1; - } - - // - // Mix all 4 inputs together so that h0, h1 are a hash of them all. - // - // For two inputs differing in just the input bits - // Where "differ" means xor or subtraction - // And the base value is random, or a counting value starting at that bit - // The final result will have each bit of h0, h1 flip - // For every input bit, - // with probability 50 +- .3% (it is probably better than that) - // For every pair of input bits, - // with probability 50 +- .75% (the worst case is approximately that) - // - static inline void ShortEnd(uint64_t &h0, uint64_t &h1, uint64_t &h2, uint64_t &h3) - { - h3 ^= h2; h2 = Rot64(h2,15); h3 += h2; - h0 ^= h3; h3 = Rot64(h3,52); h0 += h3; - h1 ^= h0; h0 = Rot64(h0,26); h1 += h0; - h2 ^= h1; h1 = Rot64(h1,51); h2 += h1; - h3 ^= h2; h2 = Rot64(h2,28); h3 += h2; - h0 ^= h3; h3 = Rot64(h3,9); h0 += h3; - h1 ^= h0; h0 = Rot64(h0,47); h1 += h0; - h2 ^= h1; h1 = Rot64(h1,54); h2 += h1; - h3 ^= h2; h2 = Rot64(h2,32); h3 += h2; - h0 ^= h3; h3 = Rot64(h3,25); h0 += h3; - h1 ^= h0; h0 = Rot64(h0,63); h1 += h0; - } - -private: - - // - // Short is used for messages under 192 bytes in length - // Short has a low startup cost, the normal mode is good for long - // keys, the cost crossover is at about 192 bytes. The two modes were - // held to the same quality bar. - // - static void Short( - const void *message, // message (array of bytes, not necessarily aligned) - size_t length, // length of message (in bytes) - uint64_t *hash1, // in/out: in the seed, out the hash value - uint64_t *hash2); // in/out: in the seed, out the hash value - - // number of uint64_t's in internal state - static const size_t sc_numVars = 12; - - // size of the internal state - static const size_t sc_blockSize = sc_numVars*8; - - // size of buffer of unhashed data, in bytes - static const size_t sc_bufSize = 2*sc_blockSize; - - // - // sc_const: a constant which: - // * is not zero - // * is odd - // * is a not-very-regular mix of 1's and 0's - // * does not need any other special mathematical properties - // - static const uint64_t sc_const = 0xdeadbeefdeadbeefLL; - - uint64_t m_data[2*sc_numVars]; // unhashed data, for partial messages - uint64_t m_state[sc_numVars]; // internal state of the hash - size_t m_length; // total length of the input so far - uint8_t m_remainder; // length of unhashed data stashed in m_data -}; - -} // namespace hash -} // namespace folly - -#endif diff --git a/hphp/third_party/folly/folly/SpookyHashV2.cpp b/hphp/third_party/folly/folly/SpookyHashV2.cpp deleted file mode 100644 index 6df4fc792..000000000 --- a/hphp/third_party/folly/folly/SpookyHashV2.cpp +++ /dev/null @@ -1,373 +0,0 @@ -/* - * Copyright 2013 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. - */ - -// Spooky Hash -// A 128-bit noncryptographic hash, for checksums and table lookup -// By Bob Jenkins. Public domain. -// Oct 31 2010: published framework, disclaimer ShortHash isn't right -// Nov 7 2010: disabled ShortHash -// Oct 31 2011: replace End, ShortMix, ShortEnd, enable ShortHash again -// April 10 2012: buffer overflow on platforms without unaligned reads -// July 12 2012: was passing out variables in final to in/out in short -// July 30 2012: I reintroduced the buffer overflow -// August 5 2012: SpookyV2: d = should be d += in short hash, and remove extra mix from long hash - -#include "folly/SpookyHashV2.h" - -#include - -#define ALLOW_UNALIGNED_READS 1 - -namespace folly { -namespace hash { - -// -// short hash ... it could be used on any message, -// but it's used by Spooky just for short messages. -// -void SpookyHashV2::Short( - const void *message, - size_t length, - uint64_t *hash1, - uint64_t *hash2) -{ - uint64_t buf[2*sc_numVars]; - union - { - const uint8_t *p8; - uint32_t *p32; - uint64_t *p64; - size_t i; - } u; - - u.p8 = (const uint8_t *)message; - - if (!ALLOW_UNALIGNED_READS && (u.i & 0x7)) - { - memcpy(buf, message, length); - u.p64 = buf; - } - - size_t remainder = length%32; - uint64_t a=*hash1; - uint64_t b=*hash2; - uint64_t c=sc_const; - uint64_t d=sc_const; - - if (length > 15) - { - const uint64_t *end = u.p64 + (length/32)*4; - - // handle all complete sets of 32 bytes - for (; u.p64 < end; u.p64 += 4) - { - c += u.p64[0]; - d += u.p64[1]; - ShortMix(a,b,c,d); - a += u.p64[2]; - b += u.p64[3]; - } - - //Handle the case of 16+ remaining bytes. - if (remainder >= 16) - { - c += u.p64[0]; - d += u.p64[1]; - ShortMix(a,b,c,d); - u.p64 += 2; - remainder -= 16; - } - } - - // Handle the last 0..15 bytes, and its length - d += ((uint64_t)length) << 56; - switch (remainder) - { - case 15: - d += ((uint64_t)u.p8[14]) << 48; - case 14: - d += ((uint64_t)u.p8[13]) << 40; - case 13: - d += ((uint64_t)u.p8[12]) << 32; - case 12: - d += u.p32[2]; - c += u.p64[0]; - break; - case 11: - d += ((uint64_t)u.p8[10]) << 16; - case 10: - d += ((uint64_t)u.p8[9]) << 8; - case 9: - d += (uint64_t)u.p8[8]; - case 8: - c += u.p64[0]; - break; - case 7: - c += ((uint64_t)u.p8[6]) << 48; - case 6: - c += ((uint64_t)u.p8[5]) << 40; - case 5: - c += ((uint64_t)u.p8[4]) << 32; - case 4: - c += u.p32[0]; - break; - case 3: - c += ((uint64_t)u.p8[2]) << 16; - case 2: - c += ((uint64_t)u.p8[1]) << 8; - case 1: - c += (uint64_t)u.p8[0]; - break; - case 0: - c += sc_const; - d += sc_const; - } - ShortEnd(a,b,c,d); - *hash1 = a; - *hash2 = b; -} - - - - -// do the whole hash in one call -void SpookyHashV2::Hash128( - const void *message, - size_t length, - uint64_t *hash1, - uint64_t *hash2) -{ - if (length < sc_bufSize) - { - Short(message, length, hash1, hash2); - return; - } - - uint64_t h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11; - uint64_t buf[sc_numVars]; - uint64_t *end; - union - { - const uint8_t *p8; - uint64_t *p64; - size_t i; - } u; - size_t remainder; - - h0=h3=h6=h9 = *hash1; - h1=h4=h7=h10 = *hash2; - h2=h5=h8=h11 = sc_const; - - u.p8 = (const uint8_t *)message; - end = u.p64 + (length/sc_blockSize)*sc_numVars; - - // handle all whole sc_blockSize blocks of bytes - if (ALLOW_UNALIGNED_READS || ((u.i & 0x7) == 0)) - { - while (u.p64 < end) - { - Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - u.p64 += sc_numVars; - } - } - else - { - while (u.p64 < end) - { - memcpy(buf, u.p64, sc_blockSize); - Mix(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - u.p64 += sc_numVars; - } - } - - // handle the last partial block of sc_blockSize bytes - remainder = (length - ((const uint8_t *)end-(const uint8_t *)message)); - memcpy(buf, end, remainder); - memset(((uint8_t *)buf)+remainder, 0, sc_blockSize-remainder); - ((uint8_t *)buf)[sc_blockSize-1] = remainder; - - // do some final mixing - End(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - *hash1 = h0; - *hash2 = h1; -} - - - -// init spooky state -void SpookyHashV2::Init(uint64_t seed1, uint64_t seed2) -{ - m_length = 0; - m_remainder = 0; - m_state[0] = seed1; - m_state[1] = seed2; -} - - -// add a message fragment to the state -void SpookyHashV2::Update(const void *message, size_t length) -{ - uint64_t h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11; - size_t newLength = length + m_remainder; - uint8_t remainder; - union - { - const uint8_t *p8; - uint64_t *p64; - size_t i; - } u; - const uint64_t *end; - - // Is this message fragment too short? If it is, stuff it away. - if (newLength < sc_bufSize) - { - memcpy(&((uint8_t *)m_data)[m_remainder], message, length); - m_length = length + m_length; - m_remainder = (uint8_t)newLength; - return; - } - - // init the variables - if (m_length < sc_bufSize) - { - h0=h3=h6=h9 = m_state[0]; - h1=h4=h7=h10 = m_state[1]; - h2=h5=h8=h11 = sc_const; - } - else - { - h0 = m_state[0]; - h1 = m_state[1]; - h2 = m_state[2]; - h3 = m_state[3]; - h4 = m_state[4]; - h5 = m_state[5]; - h6 = m_state[6]; - h7 = m_state[7]; - h8 = m_state[8]; - h9 = m_state[9]; - h10 = m_state[10]; - h11 = m_state[11]; - } - m_length = length + m_length; - - // if we've got anything stuffed away, use it now - if (m_remainder) - { - uint8_t prefix = sc_bufSize-m_remainder; - memcpy(&(((uint8_t *)m_data)[m_remainder]), message, prefix); - u.p64 = m_data; - Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - Mix(&u.p64[sc_numVars], h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - u.p8 = ((const uint8_t *)message) + prefix; - length -= prefix; - } - else - { - u.p8 = (const uint8_t *)message; - } - - // handle all whole blocks of sc_blockSize bytes - end = u.p64 + (length/sc_blockSize)*sc_numVars; - remainder = (uint8_t)(length-((const uint8_t *)end-u.p8)); - if (ALLOW_UNALIGNED_READS || (u.i & 0x7) == 0) - { - while (u.p64 < end) - { - Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - u.p64 += sc_numVars; - } - } - else - { - while (u.p64 < end) - { - memcpy(m_data, u.p8, sc_blockSize); - Mix(m_data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - u.p64 += sc_numVars; - } - } - - // stuff away the last few bytes - m_remainder = remainder; - memcpy(m_data, end, remainder); - - // stuff away the variables - m_state[0] = h0; - m_state[1] = h1; - m_state[2] = h2; - m_state[3] = h3; - m_state[4] = h4; - m_state[5] = h5; - m_state[6] = h6; - m_state[7] = h7; - m_state[8] = h8; - m_state[9] = h9; - m_state[10] = h10; - m_state[11] = h11; -} - - -// report the hash for the concatenation of all message fragments so far -void SpookyHashV2::Final(uint64_t *hash1, uint64_t *hash2) -{ - // init the variables - if (m_length < sc_bufSize) - { - *hash1 = m_state[0]; - *hash2 = m_state[1]; - Short( m_data, m_length, hash1, hash2); - return; - } - - const uint64_t *data = (const uint64_t *)m_data; - uint8_t remainder = m_remainder; - - uint64_t h0 = m_state[0]; - uint64_t h1 = m_state[1]; - uint64_t h2 = m_state[2]; - uint64_t h3 = m_state[3]; - uint64_t h4 = m_state[4]; - uint64_t h5 = m_state[5]; - uint64_t h6 = m_state[6]; - uint64_t h7 = m_state[7]; - uint64_t h8 = m_state[8]; - uint64_t h9 = m_state[9]; - uint64_t h10 = m_state[10]; - uint64_t h11 = m_state[11]; - - if (remainder >= sc_blockSize) - { - // m_data can contain two blocks; handle any whole first block - Mix(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - data += sc_numVars; - remainder -= sc_blockSize; - } - - // mix in the last partial block, and the length mod sc_blockSize - memset(&((uint8_t *)data)[remainder], 0, (sc_blockSize-remainder)); - - ((uint8_t *)data)[sc_blockSize-1] = remainder; - - // do some final mixing - End(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - - *hash1 = h0; - *hash2 = h1; -} - -} // namespace hash -} // namespace folly diff --git a/hphp/third_party/folly/folly/SpookyHashV2.h b/hphp/third_party/folly/folly/SpookyHashV2.h deleted file mode 100644 index b5121addc..000000000 --- a/hphp/third_party/folly/folly/SpookyHashV2.h +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright 2013 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. - */ - -// This is version 2 of SpookyHash, incompatible with version 1. -// -// SpookyHash: a 128-bit noncryptographic hash function -// By Bob Jenkins, public domain -// Oct 31 2010: alpha, framework + SpookyHash::Mix appears right -// Oct 31 2011: alpha again, Mix only good to 2^^69 but rest appears right -// Dec 31 2011: beta, improved Mix, tested it for 2-bit deltas -// Feb 2 2012: production, same bits as beta -// Feb 5 2012: adjusted definitions of uint* to be more portable -// Mar 30 2012: 3 bytes/cycle, not 4. Alpha was 4 but wasn't thorough enough. -// August 5 2012: SpookyV2 (different results) -// -// Up to 3 bytes/cycle for long messages. Reasonably fast for short messages. -// All 1 or 2 bit deltas achieve avalanche within 1% bias per output bit. -// -// This was developed for and tested on 64-bit x86-compatible processors. -// It assumes the processor is little-endian. There is a macro -// controlling whether unaligned reads are allowed (by default they are). -// This should be an equally good hash on big-endian machines, but it will -// compute different results on them than on little-endian machines. -// -// Google's CityHash has similar specs to SpookyHash, and CityHash is faster -// on new Intel boxes. MD4 and MD5 also have similar specs, but they are orders -// of magnitude slower. CRCs are two or more times slower, but unlike -// SpookyHash, they have nice math for combining the CRCs of pieces to form -// the CRCs of wholes. There are also cryptographic hashes, but those are even -// slower than MD5. -// - -#ifndef FOLLY_SPOOKYHASHV2_H_ -#define FOLLY_SPOOKYHASHV2_H_ - -#include -#include - -namespace folly { -namespace hash { - -class SpookyHashV2 -{ -public: - // - // SpookyHash: hash a single message in one call, produce 128-bit output - // - static void Hash128( - const void *message, // message to hash - size_t length, // length of message in bytes - uint64_t *hash1, // in/out: in seed 1, out hash value 1 - uint64_t *hash2); // in/out: in seed 2, out hash value 2 - - // - // Hash64: hash a single message in one call, return 64-bit output - // - static uint64_t Hash64( - const void *message, // message to hash - size_t length, // length of message in bytes - uint64_t seed) // seed - { - uint64_t hash1 = seed; - Hash128(message, length, &hash1, &seed); - return hash1; - } - - // - // Hash32: hash a single message in one call, produce 32-bit output - // - static uint32_t Hash32( - const void *message, // message to hash - size_t length, // length of message in bytes - uint32_t seed) // seed - { - uint64_t hash1 = seed, hash2 = seed; - Hash128(message, length, &hash1, &hash2); - return (uint32_t)hash1; - } - - // - // Init: initialize the context of a SpookyHash - // - void Init( - uint64_t seed1, // any 64-bit value will do, including 0 - uint64_t seed2); // different seeds produce independent hashes - - // - // Update: add a piece of a message to a SpookyHash state - // - void Update( - const void *message, // message fragment - size_t length); // length of message fragment in bytes - - - // - // Final: compute the hash for the current SpookyHash state - // - // This does not modify the state; you can keep updating it afterward - // - // The result is the same as if SpookyHash() had been called with - // all the pieces concatenated into one message. - // - void Final( - uint64_t *hash1, // out only: first 64 bits of hash value. - uint64_t *hash2); // out only: second 64 bits of hash value. - - // - // left rotate a 64-bit value by k bytes - // - static inline uint64_t Rot64(uint64_t x, int k) - { - return (x << k) | (x >> (64 - k)); - } - - // - // This is used if the input is 96 bytes long or longer. - // - // The internal state is fully overwritten every 96 bytes. - // Every input bit appears to cause at least 128 bits of entropy - // before 96 other bytes are combined, when run forward or backward - // For every input bit, - // Two inputs differing in just that input bit - // Where "differ" means xor or subtraction - // And the base value is random - // When run forward or backwards one Mix - // I tried 3 pairs of each; they all differed by at least 212 bits. - // - static inline void Mix( - const uint64_t *data, - uint64_t &s0, uint64_t &s1, uint64_t &s2, uint64_t &s3, - uint64_t &s4, uint64_t &s5, uint64_t &s6, uint64_t &s7, - uint64_t &s8, uint64_t &s9, uint64_t &s10,uint64_t &s11) - { - s0 += data[0]; s2 ^= s10; s11 ^= s0; s0 = Rot64(s0,11); s11 += s1; - s1 += data[1]; s3 ^= s11; s0 ^= s1; s1 = Rot64(s1,32); s0 += s2; - s2 += data[2]; s4 ^= s0; s1 ^= s2; s2 = Rot64(s2,43); s1 += s3; - s3 += data[3]; s5 ^= s1; s2 ^= s3; s3 = Rot64(s3,31); s2 += s4; - s4 += data[4]; s6 ^= s2; s3 ^= s4; s4 = Rot64(s4,17); s3 += s5; - s5 += data[5]; s7 ^= s3; s4 ^= s5; s5 = Rot64(s5,28); s4 += s6; - s6 += data[6]; s8 ^= s4; s5 ^= s6; s6 = Rot64(s6,39); s5 += s7; - s7 += data[7]; s9 ^= s5; s6 ^= s7; s7 = Rot64(s7,57); s6 += s8; - s8 += data[8]; s10 ^= s6; s7 ^= s8; s8 = Rot64(s8,55); s7 += s9; - s9 += data[9]; s11 ^= s7; s8 ^= s9; s9 = Rot64(s9,54); s8 += s10; - s10 += data[10]; s0 ^= s8; s9 ^= s10; s10 = Rot64(s10,22); s9 += s11; - s11 += data[11]; s1 ^= s9; s10 ^= s11; s11 = Rot64(s11,46); s10 += s0; - } - - // - // Mix all 12 inputs together so that h0, h1 are a hash of them all. - // - // For two inputs differing in just the input bits - // Where "differ" means xor or subtraction - // And the base value is random, or a counting value starting at that bit - // The final result will have each bit of h0, h1 flip - // For every input bit, - // with probability 50 +- .3% - // For every pair of input bits, - // with probability 50 +- 3% - // - // This does not rely on the last Mix() call having already mixed some. - // Two iterations was almost good enough for a 64-bit result, but a - // 128-bit result is reported, so End() does three iterations. - // - static inline void EndPartial( - uint64_t &h0, uint64_t &h1, uint64_t &h2, uint64_t &h3, - uint64_t &h4, uint64_t &h5, uint64_t &h6, uint64_t &h7, - uint64_t &h8, uint64_t &h9, uint64_t &h10,uint64_t &h11) - { - h11+= h1; h2 ^= h11; h1 = Rot64(h1,44); - h0 += h2; h3 ^= h0; h2 = Rot64(h2,15); - h1 += h3; h4 ^= h1; h3 = Rot64(h3,34); - h2 += h4; h5 ^= h2; h4 = Rot64(h4,21); - h3 += h5; h6 ^= h3; h5 = Rot64(h5,38); - h4 += h6; h7 ^= h4; h6 = Rot64(h6,33); - h5 += h7; h8 ^= h5; h7 = Rot64(h7,10); - h6 += h8; h9 ^= h6; h8 = Rot64(h8,13); - h7 += h9; h10^= h7; h9 = Rot64(h9,38); - h8 += h10; h11^= h8; h10= Rot64(h10,53); - h9 += h11; h0 ^= h9; h11= Rot64(h11,42); - h10+= h0; h1 ^= h10; h0 = Rot64(h0,54); - } - - static inline void End( - const uint64_t *data, - uint64_t &h0, uint64_t &h1, uint64_t &h2, uint64_t &h3, - uint64_t &h4, uint64_t &h5, uint64_t &h6, uint64_t &h7, - uint64_t &h8, uint64_t &h9, uint64_t &h10,uint64_t &h11) - { - h0 += data[0]; h1 += data[1]; h2 += data[2]; h3 += data[3]; - h4 += data[4]; h5 += data[5]; h6 += data[6]; h7 += data[7]; - h8 += data[8]; h9 += data[9]; h10 += data[10]; h11 += data[11]; - EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11); - } - - // - // The goal is for each bit of the input to expand into 128 bits of - // apparent entropy before it is fully overwritten. - // n trials both set and cleared at least m bits of h0 h1 h2 h3 - // n: 2 m: 29 - // n: 3 m: 46 - // n: 4 m: 57 - // n: 5 m: 107 - // n: 6 m: 146 - // n: 7 m: 152 - // when run forwards or backwards - // for all 1-bit and 2-bit diffs - // with diffs defined by either xor or subtraction - // with a base of all zeros plus a counter, or plus another bit, or random - // - static inline void ShortMix(uint64_t &h0, uint64_t &h1, uint64_t &h2, uint64_t &h3) - { - h2 = Rot64(h2,50); h2 += h3; h0 ^= h2; - h3 = Rot64(h3,52); h3 += h0; h1 ^= h3; - h0 = Rot64(h0,30); h0 += h1; h2 ^= h0; - h1 = Rot64(h1,41); h1 += h2; h3 ^= h1; - h2 = Rot64(h2,54); h2 += h3; h0 ^= h2; - h3 = Rot64(h3,48); h3 += h0; h1 ^= h3; - h0 = Rot64(h0,38); h0 += h1; h2 ^= h0; - h1 = Rot64(h1,37); h1 += h2; h3 ^= h1; - h2 = Rot64(h2,62); h2 += h3; h0 ^= h2; - h3 = Rot64(h3,34); h3 += h0; h1 ^= h3; - h0 = Rot64(h0,5); h0 += h1; h2 ^= h0; - h1 = Rot64(h1,36); h1 += h2; h3 ^= h1; - } - - // - // Mix all 4 inputs together so that h0, h1 are a hash of them all. - // - // For two inputs differing in just the input bits - // Where "differ" means xor or subtraction - // And the base value is random, or a counting value starting at that bit - // The final result will have each bit of h0, h1 flip - // For every input bit, - // with probability 50 +- .3% (it is probably better than that) - // For every pair of input bits, - // with probability 50 +- .75% (the worst case is approximately that) - // - static inline void ShortEnd(uint64_t &h0, uint64_t &h1, uint64_t &h2, uint64_t &h3) - { - h3 ^= h2; h2 = Rot64(h2,15); h3 += h2; - h0 ^= h3; h3 = Rot64(h3,52); h0 += h3; - h1 ^= h0; h0 = Rot64(h0,26); h1 += h0; - h2 ^= h1; h1 = Rot64(h1,51); h2 += h1; - h3 ^= h2; h2 = Rot64(h2,28); h3 += h2; - h0 ^= h3; h3 = Rot64(h3,9); h0 += h3; - h1 ^= h0; h0 = Rot64(h0,47); h1 += h0; - h2 ^= h1; h1 = Rot64(h1,54); h2 += h1; - h3 ^= h2; h2 = Rot64(h2,32); h3 += h2; - h0 ^= h3; h3 = Rot64(h3,25); h0 += h3; - h1 ^= h0; h0 = Rot64(h0,63); h1 += h0; - } - -private: - - // - // Short is used for messages under 192 bytes in length - // Short has a low startup cost, the normal mode is good for long - // keys, the cost crossover is at about 192 bytes. The two modes were - // held to the same quality bar. - // - static void Short( - const void *message, // message (array of bytes, not necessarily aligned) - size_t length, // length of message (in bytes) - uint64_t *hash1, // in/out: in the seed, out the hash value - uint64_t *hash2); // in/out: in the seed, out the hash value - - // number of uint64_t's in internal state - static const size_t sc_numVars = 12; - - // size of the internal state - static const size_t sc_blockSize = sc_numVars*8; - - // size of buffer of unhashed data, in bytes - static const size_t sc_bufSize = 2*sc_blockSize; - - // - // sc_const: a constant which: - // * is not zero - // * is odd - // * is a not-very-regular mix of 1's and 0's - // * does not need any other special mathematical properties - // - static const uint64_t sc_const = 0xdeadbeefdeadbeefLL; - - uint64_t m_data[2*sc_numVars]; // unhashed data, for partial messages - uint64_t m_state[sc_numVars]; // internal state of the hash - size_t m_length; // total length of the input so far - uint8_t m_remainder; // length of unhashed data stashed in m_data -}; - -} // namespace hash -} // namespace folly - -#endif diff --git a/hphp/third_party/folly/folly/StlAllocator.h b/hphp/third_party/folly/folly/StlAllocator.h deleted file mode 100644 index 4cbe8f8b3..000000000 --- a/hphp/third_party/folly/folly/StlAllocator.h +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_STLALLOCATOR_H_ -#define FOLLY_STLALLOCATOR_H_ - -#include "folly/Traits.h" - -#include -#include -#include -#include -#include - -#include - -namespace folly { - -/** - * Wrap a SimpleAllocator into a STL-compliant allocator. - * - * The SimpleAllocator must provide two methods: - * void* allocate(size_t size); - * void deallocate(void* ptr); - * which, respectively, allocate a block of size bytes (aligned to the maximum - * alignment required on your system), throwing std::bad_alloc if the - * allocation can't be satisfied, and free a previously allocated block. - * - * Note that the following allocator resembles the standard allocator - * quite well: - * - * class MallocAllocator { - * public: - * void* allocate(size_t size) { - * void* p = malloc(size); - * if (!p) throw std::bad_alloc(); - * return p; - * } - * void deallocate(void* p) { - * free(p); - * } - * }; - * - * author: Tudor Bosman - */ - -// This would be so much simpler with std::allocator_traits, but gcc 4.6.2 -// doesn't support it -template class StlAllocator; - -template class StlAllocator { - public: - typedef void value_type; - typedef void* pointer; - typedef const void* const_pointer; - template struct rebind { - typedef StlAllocator other; - }; -}; - -template -class StlAllocator { - public: - typedef T value_type; - typedef T* pointer; - typedef const T* const_pointer; - typedef T& reference; - typedef const T& const_reference; - - typedef ptrdiff_t difference_type; - typedef size_t size_type; - - StlAllocator() : alloc_(nullptr) { } - explicit StlAllocator(Alloc* alloc) : alloc_(alloc) { } - - template StlAllocator(const StlAllocator& other) - : alloc_(other.alloc()) { } - - T* allocate(size_t n, const void* hint = nullptr) { - return static_cast(alloc_->allocate(n * sizeof(T))); - } - - void deallocate(T* p, size_t n) { - alloc_->deallocate(p); - } - - size_t max_size() const { - return std::numeric_limits::max(); - } - - T* address(T& x) const { - return std::addressof(x); - } - - const T* address(const T& x) const { - return std::addressof(x); - } - - template - void construct(T* p, Args&&... args) { - new (p) T(std::forward(args)...); - } - - void destroy(T* p) { - p->~T(); - } - - Alloc* alloc() const { - return alloc_; - } - - template struct rebind { - typedef StlAllocator other; - }; - - bool operator!=(const StlAllocator& other) const { - return alloc_ != other.alloc_; - } - - bool operator==(const StlAllocator& other) const { - return alloc_ == other.alloc_; - } - - private: - Alloc* alloc_; -}; - -/* - * Helper classes/functions for creating a unique_ptr using a custom allocator - * - * @author: Marcelo Juchem - */ - -// A deleter implementation based on std::default_delete, -// which uses a custom allocator to free memory -template -class allocator_delete { - typedef typename std::remove_reference::type allocator_type; - -public: - allocator_delete() = default; - - explicit allocator_delete(const allocator_type& allocator): - allocator_(allocator) - {} - - explicit allocator_delete(allocator_type&& allocator): - allocator_(std::move(allocator)) - {} - - template - allocator_delete(const allocator_delete& other): - allocator_(other.get_allocator()) - {} - - allocator_type& get_allocator() const { - return allocator_; - } - - void operator()(typename allocator_type::pointer p) const { - if (!p) { - return; - } - - allocator_.destroy(p); - allocator_.deallocate(p, 1); - } - -private: - mutable allocator_type allocator_; -}; - -template -class is_simple_allocator { - FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(has_destroy, destroy); - - typedef typename std::remove_const< - typename std::remove_reference::type - >::type allocator; - typedef typename std::remove_reference::type value_type; - typedef value_type* pointer; - -public: - constexpr static bool value = !has_destroy::value - && !has_destroy::value; -}; - -template -typename std::enable_if< - is_simple_allocator::value, - folly::StlAllocator< - typename std::remove_reference::type, - typename std::remove_reference::type - > ->::type make_stl_allocator(Allocator&& allocator) { - return folly::StlAllocator< - typename std::remove_reference::type, - typename std::remove_reference::type - >(&allocator); -} - -template -typename std::enable_if< - !is_simple_allocator::value, - typename std::remove_reference::type ->::type make_stl_allocator(Allocator&& allocator) { - return std::move(allocator); -} - -template -struct AllocatorUniquePtr { - typedef std::unique_ptr::value, - folly::StlAllocator::type, T>, - typename std::remove_reference::type - >::type - > - > type; -}; - -template -typename AllocatorUniquePtr::type allocate_unique( - Allocator&& allocator, Args&&... args -) { - auto stlAllocator = folly::make_stl_allocator( - std::forward(allocator) - ); - auto p = stlAllocator.allocate(1); - - try { - stlAllocator.construct(p, std::forward(args)...); - - return {p, - folly::allocator_delete(std::move(stlAllocator)) - }; - } catch (...) { - stlAllocator.deallocate(p, 1); - throw; - } -} - -template -std::shared_ptr allocate_shared(Allocator&& allocator, Args&&... args) { - return std::allocate_shared( - folly::make_stl_allocator(std::forward(allocator)), - std::forward(args)... - ); -} - -} // namespace folly - -#endif /* FOLLY_STLALLOCATOR_H_ */ diff --git a/hphp/third_party/folly/folly/String-inl.h b/hphp/third_party/folly/folly/String-inl.h deleted file mode 100644 index e61ef3321..000000000 --- a/hphp/third_party/folly/folly/String-inl.h +++ /dev/null @@ -1,651 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_STRING_INL_H_ -#define FOLLY_STRING_INL_H_ - -#include -#include - -#ifndef FOLLY_BASE_STRING_H_ -#error This file may only be included from String.h -#endif - -namespace folly { - -namespace detail { -// Map from character code to value of one-character escape sequence -// ('\n' = 10 maps to 'n'), 'O' if the character should be printed as -// an octal escape sequence, or 'P' if the character is printable and -// should be printed as is. -extern const char cEscapeTable[]; -} // namespace detail - -template -void cEscape(StringPiece str, String& out) { - char esc[4]; - esc[0] = '\\'; - out.reserve(out.size() + str.size()); - auto p = str.begin(); - auto last = p; // last regular character - // We advance over runs of regular characters (printable, not double-quote or - // backslash) and copy them in one go; this is faster than calling push_back - // repeatedly. - while (p != str.end()) { - char c = *p; - unsigned char v = static_cast(c); - char e = detail::cEscapeTable[v]; - if (e == 'P') { // printable - ++p; - } else if (e == 'O') { // octal - out.append(&*last, p - last); - esc[1] = '0' + ((v >> 6) & 7); - esc[2] = '0' + ((v >> 3) & 7); - esc[3] = '0' + (v & 7); - out.append(esc, 4); - ++p; - last = p; - } else { // special 1-character escape - out.append(&*last, p - last); - esc[1] = e; - out.append(esc, 2); - ++p; - last = p; - } - } - out.append(&*last, p - last); -} - -namespace detail { -// Map from the character code of the character following a backslash to -// the unescaped character if a valid one-character escape sequence -// ('n' maps to 10 = '\n'), 'O' if this is the first character of an -// octal escape sequence, 'X' if this is the first character of a -// hexadecimal escape sequence, or 'I' if this escape sequence is invalid. -extern const char cUnescapeTable[]; - -// Map from the character code to the hex value, or 16 if invalid hex char. -extern const unsigned char hexTable[]; -} // namespace detail - -template -void cUnescape(StringPiece str, String& out, bool strict) { - out.reserve(out.size() + str.size()); - auto p = str.begin(); - auto last = p; // last regular character (not part of an escape sequence) - // We advance over runs of regular characters (not backslash) and copy them - // in one go; this is faster than calling push_back repeatedly. - while (p != str.end()) { - char c = *p; - if (c != '\\') { // normal case - ++p; - continue; - } - out.append(&*last, p - last); - if (p == str.end()) { // backslash at end of string - if (strict) { - throw std::invalid_argument("incomplete escape sequence"); - } - out.push_back('\\'); - last = p; - continue; - } - ++p; - char e = detail::cUnescapeTable[static_cast(*p)]; - if (e == 'O') { // octal - unsigned char val = 0; - for (int i = 0; i < 3 && p != str.end() && *p >= '0' && *p <= '7'; - ++i, ++p) { - val = (val << 3) | (*p - '0'); - } - out.push_back(val); - last = p; - } else if (e == 'X') { // hex - ++p; - if (p == str.end()) { // \x at end of string - if (strict) { - throw std::invalid_argument("incomplete hex escape sequence"); - } - out.append("\\x"); - last = p; - continue; - } - unsigned char val = 0; - unsigned char h; - for (; (p != str.end() && - (h = detail::hexTable[static_cast(*p)]) < 16); - ++p) { - val = (val << 4) | h; - } - out.push_back(val); - last = p; - } else if (e == 'I') { // invalid - if (strict) { - throw std::invalid_argument("invalid escape sequence"); - } - out.push_back('\\'); - out.push_back(*p); - ++p; - last = p; - } else { // standard escape sequence, \' etc - out.push_back(e); - ++p; - last = p; - } - } - out.append(&*last, p - last); -} - -namespace detail { -// Map from character code to escape mode: -// 0 = pass through -// 1 = unused -// 2 = pass through in PATH mode -// 3 = space, replace with '+' in QUERY mode -// 4 = percent-encode -extern const unsigned char uriEscapeTable[]; -} // namespace detail - -template -void uriEscape(StringPiece str, String& out, UriEscapeMode mode) { - static const char hexValues[] = "0123456789abcdef"; - char esc[3]; - esc[0] = '%'; - // Preallocate assuming that 25% of the input string will be escaped - out.reserve(out.size() + str.size() + 3 * (str.size() / 4)); - auto p = str.begin(); - auto last = p; // last regular character - // We advance over runs of passthrough characters and copy them in one go; - // this is faster than calling push_back repeatedly. - unsigned char minEncode = static_cast(mode); - while (p != str.end()) { - char c = *p; - unsigned char v = static_cast(c); - unsigned char discriminator = detail::uriEscapeTable[v]; - if (LIKELY(discriminator <= minEncode)) { - ++p; - } else if (mode == UriEscapeMode::QUERY && discriminator == 3) { - out.append(&*last, p - last); - out.push_back('+'); - ++p; - last = p; - } else { - out.append(&*last, p - last); - esc[1] = hexValues[v >> 4]; - esc[2] = hexValues[v & 0x0f]; - out.append(esc, 3); - ++p; - last = p; - } - } - out.append(&*last, p - last); -} - -template -void uriUnescape(StringPiece str, String& out, UriEscapeMode mode) { - out.reserve(out.size() + str.size()); - auto p = str.begin(); - auto last = p; - // We advance over runs of passthrough characters and copy them in one go; - // this is faster than calling push_back repeatedly. - while (p != str.end()) { - char c = *p; - unsigned char v = static_cast(v); - switch (c) { - case '%': - { - if (UNLIKELY(std::distance(p, str.end()) < 3)) { - throw std::invalid_argument("incomplete percent encode sequence"); - } - auto h1 = detail::hexTable[static_cast(p[1])]; - auto h2 = detail::hexTable[static_cast(p[2])]; - if (UNLIKELY(h1 == 16 || h2 == 16)) { - throw std::invalid_argument("invalid percent encode sequence"); - } - out.append(&*last, p - last); - out.push_back((h1 << 4) | h2); - p += 3; - last = p; - break; - } - case '+': - if (mode == UriEscapeMode::QUERY) { - out.append(&*last, p - last); - out.push_back(' '); - ++p; - last = p; - break; - } - // else fallthrough - default: - ++p; - break; - } - } - out.append(&*last, p - last); -} - -namespace detail { - -/* - * The following functions are type-overloaded helpers for - * internalSplit(). - */ -inline size_t delimSize(char) { return 1; } -inline size_t delimSize(StringPiece s) { return s.size(); } -inline bool atDelim(const char* s, char c) { - return *s == c; -} -inline bool atDelim(const char* s, StringPiece sp) { - return !std::memcmp(s, sp.start(), sp.size()); -} - -// These are used to short-circuit internalSplit() in the case of -// 1-character strings. -inline char delimFront(char c) { - // This one exists only for compile-time; it should never be called. - std::abort(); - return c; -} -inline char delimFront(StringPiece s) { - assert(!s.empty() && s.start() != nullptr); - return *s.start(); -} - -/* - * These output conversion templates allow us to support multiple - * output string types, even when we are using an arbitrary - * OutputIterator. - */ -template struct OutputConverter {}; - -template<> struct OutputConverter { - std::string operator()(StringPiece sp) const { - return sp.toString(); - } -}; - -template<> struct OutputConverter { - fbstring operator()(StringPiece sp) const { - return sp.toFbstring(); - } -}; - -template<> struct OutputConverter { - StringPiece operator()(StringPiece sp) const { return sp; } -}; - -/* - * Shared implementation for all the split() overloads. - * - * This uses some external helpers that are overloaded to let this - * algorithm be more performant if the deliminator is a single - * character instead of a whole string. - * - * @param ignoreEmpty iff true, don't copy empty segments to output - */ -template -void internalSplit(DelimT delim, StringPiece sp, OutputIterator out, - bool ignoreEmpty) { - assert(sp.empty() || sp.start() != nullptr); - - const char* s = sp.start(); - const size_t strSize = sp.size(); - const size_t dSize = delimSize(delim); - - OutputConverter conv; - - if (dSize > strSize || dSize == 0) { - if (!ignoreEmpty || strSize > 0) { - *out++ = conv(sp); - } - return; - } - if (boost::is_same::value && dSize == 1) { - // Call the char version because it is significantly faster. - return internalSplit(delimFront(delim), sp, out, - ignoreEmpty); - } - - int tokenStartPos = 0; - int tokenSize = 0; - for (int i = 0; i <= strSize - dSize; ++i) { - if (atDelim(&s[i], delim)) { - if (!ignoreEmpty || tokenSize > 0) { - *out++ = conv(StringPiece(&s[tokenStartPos], tokenSize)); - } - - tokenStartPos = i + dSize; - tokenSize = 0; - i += dSize - 1; - } else { - ++tokenSize; - } - } - - if (!ignoreEmpty || tokenSize > 0) { - tokenSize = strSize - tokenStartPos; - *out++ = conv(StringPiece(&s[tokenStartPos], tokenSize)); - } -} - -template StringPiece prepareDelim(const String& s) { - return StringPiece(s); -} -inline char prepareDelim(char c) { return c; } - -template -bool splitFixed(const Delim& delimiter, - StringPiece input, - StringPiece& out) { - if (exact && UNLIKELY(std::string::npos != input.find(delimiter))) { - return false; - } - out = input; - return true; -} - -template -bool splitFixed(const Delim& delimiter, - StringPiece input, - StringPiece& outHead, - StringPieces&... outTail) { - size_t cut = input.find(delimiter); - if (UNLIKELY(cut == std::string::npos)) { - return false; - } - StringPiece head(input.begin(), input.begin() + cut); - StringPiece tail(input.begin() + cut + detail::delimSize(delimiter), - input.end()); - if (LIKELY(splitFixed(delimiter, tail, outTail...))) { - outHead = head; - return true; - } - return false; -} - -} - -////////////////////////////////////////////////////////////////////// - -template -void split(const Delim& delimiter, - const String& input, - std::vector& out, - bool ignoreEmpty) { - detail::internalSplit( - detail::prepareDelim(delimiter), - StringPiece(input), - std::back_inserter(out), - ignoreEmpty); -} - -template -void split(const Delim& delimiter, - const String& input, - fbvector& out, - bool ignoreEmpty) { - detail::internalSplit( - detail::prepareDelim(delimiter), - StringPiece(input), - std::back_inserter(out), - ignoreEmpty); -} - -template -void splitTo(const Delim& delimiter, - const String& input, - OutputIterator out, - bool ignoreEmpty) { - detail::internalSplit( - detail::prepareDelim(delimiter), - StringPiece(input), - out, - ignoreEmpty); -} - -template -bool split(const Delim& delimiter, - StringPiece input, - StringPiece& outHead, - StringPieces&... outTail) { - return detail::splitFixed( - detail::prepareDelim(delimiter), - input, - outHead, - outTail...); -} - -namespace detail { - -template -struct IsStringContainerIterator : - IsSomeString::value_type> { -}; - -template -void internalJoinAppend(Delim delimiter, - Iterator begin, - Iterator end, - String& output) { - assert(begin != end); - if (std::is_same::value && - delimSize(delimiter) == 1) { - internalJoinAppend(delimFront(delimiter), begin, end, output); - return; - } - toAppend(*begin, &output); - while (++begin != end) { - toAppend(delimiter, *begin, &output); - } -} - -template -typename std::enable_if::value>::type -internalJoin(Delim delimiter, - Iterator begin, - Iterator end, - String& output) { - output.clear(); - if (begin == end) { - return; - } - const size_t dsize = delimSize(delimiter); - Iterator it = begin; - size_t size = it->size(); - while (++it != end) { - size += dsize + it->size(); - } - output.reserve(size); - internalJoinAppend(delimiter, begin, end, output); -} - -template -typename std::enable_if::value>::type -internalJoin(Delim delimiter, - Iterator begin, - Iterator end, - String& output) { - output.clear(); - if (begin == end) { - return; - } - internalJoinAppend(delimiter, begin, end, output); -} - -} // namespace detail - -template -void join(const Delim& delimiter, - Iterator begin, - Iterator end, - String& output) { - detail::internalJoin( - detail::prepareDelim(delimiter), - begin, - end, - output); -} - -template -void backslashify(const String1& input, String2& output, bool hex_style) { - static const char hexValues[] = "0123456789abcdef"; - output.clear(); - output.reserve(3 * input.size()); - for (unsigned char c : input) { - // less than space or greater than '~' are considered unprintable - if (c < 0x20 || c > 0x7e || c == '\\') { - bool hex_append = false; - output.push_back('\\'); - if (hex_style) { - hex_append = true; - } else { - if (c == '\r') output += 'r'; - else if (c == '\n') output += 'n'; - else if (c == '\t') output += 't'; - else if (c == '\a') output += 'a'; - else if (c == '\b') output += 'b'; - else if (c == '\0') output += '0'; - else if (c == '\\') output += '\\'; - else { - hex_append = true; - } - } - if (hex_append) { - output.push_back('x'); - output.push_back(hexValues[(c >> 4) & 0xf]); - output.push_back(hexValues[c & 0xf]); - } - } else { - output += c; - } - } -} - -template -void humanify(const String1& input, String2& output) { - int numUnprintable = 0; - int numPrintablePrefix = 0; - for (unsigned char c : input) { - if (c < 0x20 || c > 0x7e || c == '\\') { - ++numUnprintable; - } - if (numUnprintable == 0) { - ++numPrintablePrefix; - } - } - - // hexlify doubles a string's size; backslashify can potentially - // explode it by 4x. Now, the printable range of the ascii - // "spectrum" is around 95 out of 256 values, so a "random" binary - // string should be around 60% unprintable. We use a 50% hueristic - // here, so if a string is 60% unprintable, then we just use hex - // output. Otherwise we backslash. - // - // UTF8 is completely ignored; as a result, utf8 characters will - // likely be \x escaped (since most common glyphs fit in two bytes). - // This is a tradeoff of complexity/speed instead of a convenience - // that likely would rarely matter. Moreover, this function is more - // about displaying underlying bytes, not about displaying glyphs - // from languages. - if (numUnprintable == 0) { - output = input; - } else if (5 * numUnprintable >= 3 * input.size()) { - // However! If we have a "meaningful" prefix of printable - // characters, say 20% of the string, we backslashify under the - // assumption viewing the prefix as ascii is worth blowing the - // output size up a bit. - if (5 * numPrintablePrefix >= input.size()) { - backslashify(input, output); - } else { - output = "0x"; - hexlify(input, output, true /* append output */); - } - } else { - backslashify(input, output); - } -} - -template -bool hexlify(const InputString& input, OutputString& output, - bool append_output) { - if (!append_output) output.clear(); - - static char hexValues[] = "0123456789abcdef"; - int j = output.size(); - output.resize(2 * input.size() + output.size()); - for (int i = 0; i < input.size(); ++i) { - int ch = input[i]; - output[j++] = hexValues[(ch >> 4) & 0xf]; - output[j++] = hexValues[ch & 0xf]; - } - return true; -} - -template -bool unhexlify(const InputString& input, OutputString& output) { - if (input.size() % 2 != 0) { - return false; - } - output.resize(input.size() / 2); - int j = 0; - auto unhex = [](char c) -> int { - return c >= '0' && c <= '9' ? c - '0' : - c >= 'A' && c <= 'F' ? c - 'A' + 10 : - c >= 'a' && c <= 'f' ? c - 'a' + 10 : - -1; - }; - - for (int i = 0; i < input.size(); i += 2) { - int highBits = unhex(input[i]); - int lowBits = unhex(input[i + 1]); - if (highBits < 0 || lowBits < 0) { - return false; - } - output[j++] = (highBits << 4) + lowBits; - } - return true; -} - -namespace detail { -/** - * Hex-dump at most 16 bytes starting at offset from a memory area of size - * bytes. Return the number of bytes actually dumped. - */ -size_t hexDumpLine(const void* ptr, size_t offset, size_t size, - std::string& line); -} // namespace detail - -template -void hexDump(const void* ptr, size_t size, OutIt out) { - size_t offset = 0; - std::string line; - while (offset < size) { - offset += detail::hexDumpLine(ptr, offset, size, line); - *out++ = line; - } -} - -} // namespace folly - -#endif /* FOLLY_STRING_INL_H_ */ - diff --git a/hphp/third_party/folly/folly/String.cpp b/hphp/third_party/folly/folly/String.cpp deleted file mode 100644 index aae439fc1..000000000 --- a/hphp/third_party/folly/folly/String.cpp +++ /dev/null @@ -1,335 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#include "folly/String.h" -#include "folly/Format.h" - -#include -#include -#include -#include -#include -#include - -#undef FOLLY_DEMANGLE -#if defined(__GNUG__) && __GNUG__ >= 4 -# include -# define FOLLY_DEMANGLE 1 -#endif - -namespace folly { - -namespace { - -inline void stringPrintfImpl(std::string& output, const char* format, - va_list args) { - // Tru to the space at the end of output for our output buffer. - // Find out write point then inflate its size temporarily to its - // capacity; we will later shrink it to the size needed to represent - // the formatted string. If this buffer isn't large enough, we do a - // resize and try again. - - const auto write_point = output.size(); - auto remaining = output.capacity() - write_point; - output.resize(output.capacity()); - - va_list args_copy; - va_copy(args_copy, args); - int bytes_used = vsnprintf(&output[write_point], remaining, format, - args_copy); - va_end(args_copy); - if (bytes_used < 0) { - throw std::runtime_error( - to("Invalid format string; snprintf returned negative " - "with format string: ", format)); - } else if (bytes_used < remaining) { - // There was enough room, just shrink and return. - output.resize(write_point + bytes_used); - } else { - output.resize(write_point + bytes_used + 1); - remaining = bytes_used + 1; - va_list args_copy; - va_copy(args_copy, args); - bytes_used = vsnprintf(&output[write_point], remaining, format, - args_copy); - va_end(args_copy); - if (bytes_used + 1 != remaining) { - throw std::runtime_error( - to("vsnprint retry did not manage to work " - "with format string: ", format)); - } - output.resize(write_point + bytes_used); - } -} - -} // anon namespace - -std::string stringPrintf(const char* format, ...) { - // snprintf will tell us how large the output buffer should be, but - // we then have to call it a second time, which is costly. By - // guestimating the final size, we avoid the double snprintf in many - // cases, resulting in a performance win. We use this constructor - // of std::string to avoid a double allocation, though it does pad - // the resulting string with nul bytes. Our guestimation is twice - // the format string size, or 32 bytes, whichever is larger. This - // is a hueristic that doesn't affect correctness but attempts to be - // reasonably fast for the most common cases. - std::string ret(std::max(32UL, strlen(format) * 2), '\0'); - ret.resize(0); - - va_list ap; - va_start(ap, format); - stringPrintfImpl(ret, format, ap); - va_end(ap); - return ret; -} - -// Basic declarations; allow for parameters of strings and string -// pieces to be specified. -std::string& stringAppendf(std::string* output, const char* format, ...) { - va_list ap; - va_start(ap, format); - stringPrintfImpl(*output, format, ap); - va_end(ap); - return *output; -} - -void stringPrintf(std::string* output, const char* format, ...) { - output->clear(); - va_list ap; - va_start(ap, format); - stringPrintfImpl(*output, format, ap); - va_end(ap); -}; - -namespace { - -struct PrettySuffix { - const char* suffix; - double val; -}; - -const PrettySuffix kPrettyTimeSuffixes[] = { - { "s ", 1e0L }, - { "ms", 1e-3L }, - { "us", 1e-6L }, - { "ns", 1e-9L }, - { "ps", 1e-12L }, - { "s ", 0 }, - { 0, 0 }, -}; - -const PrettySuffix kPrettyBytesMetricSuffixes[] = { - { "TB", 1e12L }, - { "GB", 1e9L }, - { "MB", 1e6L }, - { "kB", 1e3L }, - { "B ", 0L }, - { 0, 0 }, -}; - -const PrettySuffix kPrettyBytesBinarySuffixes[] = { - { "TB", int64_t(1) << 40 }, - { "GB", int64_t(1) << 30 }, - { "MB", int64_t(1) << 20 }, - { "kB", int64_t(1) << 10 }, - { "B ", 0L }, - { 0, 0 }, -}; - -const PrettySuffix kPrettyBytesBinaryIECSuffixes[] = { - { "TiB", int64_t(1) << 40 }, - { "GiB", int64_t(1) << 30 }, - { "MiB", int64_t(1) << 20 }, - { "KiB", int64_t(1) << 10 }, - { "B ", 0L }, - { 0, 0 }, -}; - -const PrettySuffix kPrettyUnitsMetricSuffixes[] = { - { "tril", 1e12L }, - { "bil", 1e9L }, - { "M", 1e6L }, - { "k", 1e3L }, - { " ", 0 }, - { 0, 0 }, -}; - -const PrettySuffix kPrettyUnitsBinarySuffixes[] = { - { "T", int64_t(1) << 40 }, - { "G", int64_t(1) << 30 }, - { "M", int64_t(1) << 20 }, - { "k", int64_t(1) << 10 }, - { " ", 0 }, - { 0, 0 }, -}; - -const PrettySuffix kPrettyUnitsBinaryIECSuffixes[] = { - { "Ti", int64_t(1) << 40 }, - { "Gi", int64_t(1) << 30 }, - { "Mi", int64_t(1) << 20 }, - { "Ki", int64_t(1) << 10 }, - { " ", 0 }, - { 0, 0 }, -}; - -const PrettySuffix* const kPrettySuffixes[PRETTY_NUM_TYPES] = { - kPrettyTimeSuffixes, - kPrettyBytesMetricSuffixes, - kPrettyBytesBinarySuffixes, - kPrettyBytesBinaryIECSuffixes, - kPrettyUnitsMetricSuffixes, - kPrettyUnitsBinarySuffixes, - kPrettyUnitsBinaryIECSuffixes, -}; - -} // namespace - -std::string prettyPrint(double val, PrettyType type, bool addSpace) { - char buf[100]; - - // pick the suffixes to use - assert(type >= 0); - assert(type < PRETTY_NUM_TYPES); - const PrettySuffix* suffixes = kPrettySuffixes[type]; - - // find the first suffix we're bigger than -- then use it - double abs_val = fabs(val); - for (int i = 0; suffixes[i].suffix; ++i) { - if (abs_val >= suffixes[i].val) { - snprintf(buf, sizeof buf, "%.4g%s%s", - (suffixes[i].val ? (val / suffixes[i].val) - : val), - (addSpace ? " " : ""), - suffixes[i].suffix); - return std::string(buf); - } - } - - // no suffix, we've got a tiny value -- just print it in sci-notation - snprintf(buf, sizeof buf, "%.4g", val); - return std::string(buf); -} - -std::string hexDump(const void* ptr, size_t size) { - std::ostringstream os; - hexDump(ptr, size, std::ostream_iterator(os, "\n")); - return os.str(); -} - -fbstring errnoStr(int err) { - int savedErrno = errno; - - // Ensure that we reset errno upon exit. - auto guard(makeGuard([&] { errno = savedErrno; })); - - char buf[1024]; - buf[0] = '\0'; - - fbstring result; - - // https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/strerror_r.3.html - // http://www.kernel.org/doc/man-pages/online/pages/man3/strerror.3.html -#if defined(__APPLE__) || defined(__FreeBSD__) || \ - ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !_GNU_SOURCE) - // Using XSI-compatible strerror_r - int r = strerror_r(err, buf, sizeof(buf)); - - // OSX/FreeBSD use EINVAL and Linux uses -1 so just check for non-zero - if (r != 0) { - result = to( - "Unknown error ", err, - " (strerror_r failed with error ", errno, ")"); - } else { - result.assign(buf); - } -#else - // Using GNU strerror_r - result.assign(strerror_r(err, buf, sizeof(buf))); -#endif - - return result; -} - -#ifdef FOLLY_DEMANGLE - -fbstring demangle(const char* name) { - int status; - size_t len = 0; - // malloc() memory for the demangled type name - char* demangled = abi::__cxa_demangle(name, nullptr, &len, &status); - if (status != 0) { - return name; - } - // len is the length of the buffer (including NUL terminator and maybe - // other junk) - return fbstring(demangled, strlen(demangled), len, AcquireMallocatedString()); -} - -#else - -fbstring demangle(const char* name) { - return name; -} - -#endif -#undef FOLLY_DEMANGLE - -namespace detail { - -size_t hexDumpLine(const void* ptr, size_t offset, size_t size, - std::string& line) { - // Line layout: - // 8: address - // 1: space - // (1+2)*16: hex bytes, each preceded by a space - // 1: space separating the two halves - // 3: " |" - // 16: characters - // 1: "|" - // Total: 78 - line.clear(); - line.reserve(78); - const uint8_t* p = reinterpret_cast(ptr) + offset; - size_t n = std::min(size - offset, size_t(16)); - format("{:08x} ", offset).appendTo(line); - - for (size_t i = 0; i < n; i++) { - if (i == 8) { - line.push_back(' '); - } - format(" {:02x}", p[i]).appendTo(line); - } - - // 3 spaces for each byte we're not printing, one separating the halves - // if necessary - line.append(3 * (16 - n) + (n <= 8), ' '); - line.append(" |"); - - for (size_t i = 0; i < n; i++) { - char c = (p[i] >= 32 && p[i] <= 126 ? static_cast(p[i]) : '.'); - line.push_back(c); - } - line.append(16 - n, ' '); - line.push_back('|'); - DCHECK_EQ(line.size(), 78); - - return n; -} - -} // namespace detail - -} // namespace folly diff --git a/hphp/third_party/folly/folly/String.h b/hphp/third_party/folly/folly/String.h deleted file mode 100644 index d706a0321..000000000 --- a/hphp/third_party/folly/folly/String.h +++ /dev/null @@ -1,502 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_BASE_STRING_H_ -#define FOLLY_BASE_STRING_H_ - -#include -#include -#include - -#ifdef __GNUC__ -# include -# include -#endif - -#include "folly/Conv.h" -#include "folly/FBString.h" -#include "folly/FBVector.h" -#include "folly/Range.h" -#include "folly/ScopeGuard.h" - -// Compatibility function, to make sure toStdString(s) can be called -// to convert a std::string or fbstring variable s into type std::string -// with very little overhead if s was already std::string -namespace folly { - -inline -std::string toStdString(const folly::fbstring& s) { - return std::string(s.data(), s.size()); -} - -inline -const std::string& toStdString(const std::string& s) { - return s; -} - -// If called with a temporary, the compiler will select this overload instead -// of the above, so we don't return a (lvalue) reference to a temporary. -inline -std::string&& toStdString(std::string&& s) { - return std::move(s); -} - -/** - * C-Escape a string, making it suitable for representation as a C string - * literal. Appends the result to the output string. - * - * Backslashes all occurrences of backslash and double-quote: - * " -> \" - * \ -> \\ - * - * Replaces all non-printable ASCII characters with backslash-octal - * representation: - * -> \376 - * - * Note that we use backslash-octal instead of backslash-hex because the octal - * representation is guaranteed to consume no more than 3 characters; "\3760" - * represents two characters, one with value 254, and one with value 48 ('0'), - * whereas "\xfe0" represents only one character (with value 4064, which leads - * to implementation-defined behavior). - */ -template -void cEscape(StringPiece str, String& out); - -/** - * Similar to cEscape above, but returns the escaped string. - */ -template -String cEscape(StringPiece str) { - String out; - cEscape(str, out); - return out; -} - -/** - * C-Unescape a string; the opposite of cEscape above. Appends the result - * to the output string. - * - * Recognizes the standard C escape sequences: - * - * \' \" \? \\ \a \b \f \n \r \t \v - * \[0-7]+ - * \x[0-9a-fA-F]+ - * - * In strict mode (default), throws std::invalid_argument if it encounters - * an unrecognized escape sequence. In non-strict mode, it leaves - * the escape sequence unchanged. - */ -template -void cUnescape(StringPiece str, String& out, bool strict = true); - -/** - * Similar to cUnescape above, but returns the escaped string. - */ -template -String cUnescape(StringPiece str, bool strict = true) { - String out; - cUnescape(str, out, strict); - return out; -} - -/** - * URI-escape a string. Appends the result to the output string. - * - * Alphanumeric characters and other characters marked as "unreserved" in RFC - * 3986 ( -_.~ ) are left unchanged. In PATH mode, the forward slash (/) is - * also left unchanged. In QUERY mode, spaces are replaced by '+'. All other - * characters are percent-encoded. - */ -enum class UriEscapeMode : unsigned char { - // The values are meaningful, see generate_escape_tables.py - ALL = 0, - QUERY = 1, - PATH = 2 -}; -template -void uriEscape(StringPiece str, - String& out, - UriEscapeMode mode = UriEscapeMode::ALL); - -/** - * Similar to uriEscape above, but returns the escaped string. - */ -template -String uriEscape(StringPiece str, UriEscapeMode mode = UriEscapeMode::ALL) { - String out; - uriEscape(str, out, mode); - return out; -} - -/** - * URI-unescape a string. Appends the result to the output string. - * - * In QUERY mode, '+' are replaced by space. %XX sequences are decoded if - * XX is a valid hex sequence, otherwise we throw invalid_argument. - */ -template -void uriUnescape(StringPiece str, - String& out, - UriEscapeMode mode = UriEscapeMode::ALL); - -/** - * Similar to uriUnescape above, but returns the unescaped string. - */ -template -String uriUnescape(StringPiece str, UriEscapeMode mode = UriEscapeMode::ALL) { - String out; - uriUnescape(str, out, mode); - return out; -} - -/** - * stringPrintf is much like printf but deposits its result into a - * string. Two signatures are supported: the first simply returns the - * resulting string, and the second appends the produced characters to - * the specified string and returns a reference to it. - */ -std::string stringPrintf(const char* format, ...) - __attribute__ ((format (printf, 1, 2))); - -/** Similar to stringPrintf, with different signiture. - */ -void stringPrintf(std::string* out, const char* fmt, ...) - __attribute__ ((format (printf, 2, 3))); - -std::string& stringAppendf(std::string* output, const char* format, ...) - __attribute__ ((format (printf, 2, 3))); - -/** - * Backslashify a string, that is, replace non-printable characters - * with C-style (but NOT C compliant) "\xHH" encoding. If hex_style - * is false, then shorthand notations like "\0" will be used instead - * of "\x00" for the most common backslash cases. - * - * There are two forms, one returning the input string, and one - * creating output in the specified output string. - * - * This is mainly intended for printing to a terminal, so it is not - * particularly optimized. - * - * Do *not* use this in situations where you expect to be able to feed - * the string to a C or C++ compiler, as there are nuances with how C - * parses such strings that lead to failures. This is for display - * purposed only. If you want a string you can embed for use in C or - * C++, use cEscape instead. This function is for display purposes - * only. - */ -template -void backslashify(const String1& input, String2& output, bool hex_style=false); - -template -String backslashify(const String& input, bool hex_style=false) { - String output; - backslashify(input, output, hex_style); - return output; -} - -/** - * Take a string and "humanify" it -- that is, make it look better. - * Since "better" is subjective, caveat emptor. The basic approach is - * to count the number of unprintable characters. If there are none, - * then the output is the input. If there are relatively few, or if - * there is a long "enough" prefix of printable characters, use - * backslashify. If it is mostly binary, then simply hex encode. - * - * This is an attempt to make a computer smart, and so likely is wrong - * most of the time. - */ -template -void humanify(const String1& input, String2& output); - -template -String humanify(const String& input) { - String output; - humanify(input, output); - return output; -} - -/** - * Same functionality as Python's binascii.hexlify. Returns true - * on successful conversion. - * - * If append_output is true, append data to the output rather than - * replace it. - */ -template -bool hexlify(const InputString& input, OutputString& output, - bool append=false); - -/** - * Same functionality as Python's binascii.unhexlify. Returns true - * on successful conversion. - */ -template -bool unhexlify(const InputString& input, OutputString& output); - -/* - * A pretty-printer for numbers that appends suffixes of units of the - * given type. It prints 4 sig-figs of value with the most - * appropriate unit. - * - * If `addSpace' is true, we put a space between the units suffix and - * the value. - * - * Current types are: - * PRETTY_TIME - s, ms, us, ns, etc. - * PRETTY_BYTES_METRIC - kB, MB, GB, etc (goes up by 10^3 = 1000 each time) - * PRETTY_BYTES - kB, MB, GB, etc (goes up by 2^10 = 1024 each time) - * PRETTY_BYTES_IEC - KiB, MiB, GiB, etc - * PRETTY_UNITS_METRIC - k, M, G, etc (goes up by 10^3 = 1000 each time) - * PRETTY_UNITS_BINARY - k, M, G, etc (goes up by 2^10 = 1024 each time) - * PRETTY_UNITS_BINARY_IEC - Ki, Mi, Gi, etc - * - * @author Mark Rabkin - */ -enum PrettyType { - PRETTY_TIME, - - PRETTY_BYTES_METRIC, - PRETTY_BYTES_BINARY, - PRETTY_BYTES = PRETTY_BYTES_BINARY, - PRETTY_BYTES_BINARY_IEC, - PRETTY_BYTES_IEC = PRETTY_BYTES_BINARY_IEC, - - PRETTY_UNITS_METRIC, - PRETTY_UNITS_BINARY, - PRETTY_UNITS_BINARY_IEC, - - PRETTY_NUM_TYPES -}; - -std::string prettyPrint(double val, PrettyType, bool addSpace = true); - -/** - * Write a hex dump of size bytes starting at ptr to out. - * - * The hex dump is formatted as follows: - * - * for the string "abcdefghijklmnopqrstuvwxyz\x02" -00000000 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 |abcdefghijklmnop| -00000010 71 72 73 74 75 76 77 78 79 7a 02 |qrstuvwxyz. | - * - * that is, we write 16 bytes per line, both as hex bytes and as printable - * characters. Non-printable characters are replaced with '.' - * Lines are written to out one by one (one StringPiece at a time) without - * delimiters. - */ -template -void hexDump(const void* ptr, size_t size, OutIt out); - -/** - * Return the hex dump of size bytes starting at ptr as a string. - */ -std::string hexDump(const void* ptr, size_t size); - -/** - * Return a fbstring containing the description of the given errno value. - * Takes care not to overwrite the actual system errno, so calling - * errnoStr(errno) is valid. - */ -fbstring errnoStr(int err); - -/** - * Return the demangled (prettyfied) version of a C++ type. - * - * This function tries to produce a human-readable type, but the type name will - * be returned unchanged in case of error or if demangling isn't supported on - * your system. - * - * Use for debugging -- do not rely on demangle() returning anything useful. - * - * This function may allocate memory (and therefore throw). - */ -fbstring demangle(const char* name); -inline fbstring demangle(const std::type_info& type) { - return demangle(type.name()); -} - -/** - * Debug string for an exception: include type and what(). - */ -inline fbstring exceptionStr(const std::exception& e) { - return folly::to(demangle(typeid(e)), ": ", e.what()); -} - -inline fbstring exceptionStr(std::exception_ptr ep) { - try { - std::rethrow_exception(ep); - } catch (const std::exception& e) { - return exceptionStr(e); - } catch (...) { - return ""; - } -} - -/* - * Split a string into a list of tokens by delimiter. - * - * The split interface here supports different output types, selected - * at compile time: StringPiece, fbstring, or std::string. If you are - * using a vector to hold the output, it detects the type based on - * what your vector contains. If the output vector is not empty, split - * will append to the end of the vector. - * - * You can also use splitTo() to write the output to an arbitrary - * OutputIterator (e.g. std::inserter() on a std::set<>), in which - * case you have to tell the function the type. (Rationale: - * OutputIterators don't have a value_type, so we can't detect the - * type in splitTo without being told.) - * - * Examples: - * - * std::vector v; - * folly::split(":", "asd:bsd", v); - * - * std::set s; - * folly::splitTo(":", "asd:bsd:asd:csd", - * std::inserter(s, s.begin())); - * - * Split also takes a flag (ignoreEmpty) that indicates whether adjacent - * delimiters should be treated as one single separator (ignoring empty tokens) - * or not (generating empty tokens). - */ - -template -void split(const Delim& delimiter, - const String& input, - std::vector& out, - bool ignoreEmpty = false); - -template -void split(const Delim& delimiter, - const String& input, - folly::fbvector& out, - bool ignoreEmpty = false); - -template -void splitTo(const Delim& delimiter, - const String& input, - OutputIterator out, - bool ignoreEmpty = false); - -/* - * Split a string into a fixed number of pieces by delimiter. Returns 'true' if - * the fields were all successfully populated. - * - * Example: - * - * folly::StringPiece name, key, value; - * if (folly::split('\t', line, name, key, value)) - * ... - * - * The 'exact' template paremeter specifies how the function behaves when too - * many fields are present in the input string. When 'exact' is set to its - * default value of 'true', a call to split will fail if the number of fields in - * the input string does not exactly match the number of output parameters - * passed. If 'exact' is overridden to 'false', all remaining fields will be - * stored, unsplit, in the last field, as shown below: - * - * folly::StringPiece x, y. - * if (folly::split(':', "a:b:c", x, y)) - * assert(x == "a" && y == "b:c"); - */ -template -bool split(const Delim& delimiter, - StringPiece input, - StringPiece& outHead, - StringPieces&... outTail); - -/* - * Join list of tokens. - * - * Stores a string representation of tokens in the same order with - * deliminer between each element. - */ - -template -void join(const Delim& delimiter, - Iterator begin, - Iterator end, - String& output); - -template -void join(const Delim& delimiter, - const Container& container, - String& output) { - join(delimiter, container.begin(), container.end(), output); -} - -template -void join(const Delim& delimiter, - const std::initializer_list& values, - String& output) { - join(delimiter, values.begin(), values.end(), output); -} - -template -std::string join(const Delim& delimiter, - const Container& container) { - std::string output; - join(delimiter, container.begin(), container.end(), output); - return output; -} - -template -std::string join(const Delim& delimiter, - const std::initializer_list& values) { - std::string output; - join(delimiter, values.begin(), values.end(), output); - return output; -} - -} // namespace folly - -// Hash functions for string and fbstring usable with e.g. hash_map -#ifdef __GNUC__ -namespace __gnu_cxx { - -template -struct hash > : private hash { - size_t operator()(const folly::basic_fbstring & s) const { - return hash::operator()(s.c_str()); - } -}; - -template -struct hash > : private hash { - size_t operator()(const std::basic_string & s) const { - return hash::operator()(s.c_str()); - } -}; - -} // namespace __gnu_cxx -#endif - -// Hook into boost's type traits -namespace boost { -template -struct has_nothrow_constructor > : true_type { - enum { value = true }; -}; -} // namespace boost - -#include "folly/String-inl.h" - -#endif diff --git a/hphp/third_party/folly/folly/Subprocess.cpp b/hphp/third_party/folly/folly/Subprocess.cpp deleted file mode 100644 index 846076dd2..000000000 --- a/hphp/third_party/folly/folly/Subprocess.cpp +++ /dev/null @@ -1,779 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#include "folly/Subprocess.h" - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include - -#include "folly/Conv.h" -#include "folly/Exception.h" -#include "folly/FileUtil.h" -#include "folly/ScopeGuard.h" -#include "folly/String.h" -#include "folly/io/Cursor.h" - -extern char** environ; - -constexpr int kExecFailure = 127; -constexpr int kChildFailure = 126; - -namespace folly { - -ProcessReturnCode::State ProcessReturnCode::state() const { - if (rawStatus_ == RV_NOT_STARTED) return NOT_STARTED; - if (rawStatus_ == RV_RUNNING) return RUNNING; - if (WIFEXITED(rawStatus_)) return EXITED; - if (WIFSIGNALED(rawStatus_)) return KILLED; - throw std::runtime_error(to( - "Invalid ProcessReturnCode: ", rawStatus_)); -} - -void ProcessReturnCode::enforce(State expected) const { - State s = state(); - if (s != expected) { - throw std::logic_error(to("Invalid state ", s, - " expected ", expected)); - } -} - -int ProcessReturnCode::exitStatus() const { - enforce(EXITED); - return WEXITSTATUS(rawStatus_); -} - -int ProcessReturnCode::killSignal() const { - enforce(KILLED); - return WTERMSIG(rawStatus_); -} - -bool ProcessReturnCode::coreDumped() const { - enforce(KILLED); - return WCOREDUMP(rawStatus_); -} - -std::string ProcessReturnCode::str() const { - switch (state()) { - case NOT_STARTED: - return "not started"; - case RUNNING: - return "running"; - case EXITED: - return to("exited with status ", exitStatus()); - case KILLED: - return to("killed by signal ", killSignal(), - (coreDumped() ? " (core dumped)" : "")); - } - CHECK(false); // unreached -} - -CalledProcessError::CalledProcessError(ProcessReturnCode rc) - : returnCode_(rc), - what_(returnCode_.str()) { -} - -SubprocessSpawnError::SubprocessSpawnError(const char* executable, - int errCode, - int errnoValue) - : errnoValue_(errnoValue), - what_(to(errCode == kExecFailure ? - "failed to execute " : - "error preparing to execute ", - executable, ": ", errnoStr(errnoValue))) { -} - -namespace { - -// Copy pointers to the given strings in a format suitable for posix_spawn -std::unique_ptr cloneStrings(const std::vector& s) { - std::unique_ptr d(new const char*[s.size() + 1]); - for (int i = 0; i < s.size(); i++) { - d[i] = s[i].c_str(); - } - d[s.size()] = nullptr; - return d; -} - -// Check a wait() status, throw on non-successful -void checkStatus(ProcessReturnCode returnCode) { - if (returnCode.state() != ProcessReturnCode::EXITED || - returnCode.exitStatus() != 0) { - throw CalledProcessError(returnCode); - } -} - -} // namespace - -Subprocess::Options& Subprocess::Options::fd(int fd, int action) { - if (action == Subprocess::PIPE) { - if (fd == 0) { - action = Subprocess::PIPE_IN; - } else if (fd == 1 || fd == 2) { - action = Subprocess::PIPE_OUT; - } else { - throw std::invalid_argument( - to("Only fds 0, 1, 2 are valid for action=PIPE: ", fd)); - } - } - fdActions_[fd] = action; - return *this; -} - -Subprocess::Subprocess( - const std::vector& argv, - const Options& options, - const char* executable, - const std::vector* env) - : pid_(-1), - returnCode_(RV_NOT_STARTED) { - if (argv.empty()) { - throw std::invalid_argument("argv must not be empty"); - } - if (!executable) executable = argv[0].c_str(); - spawn(cloneStrings(argv), executable, options, env); -} - -Subprocess::Subprocess( - const std::string& cmd, - const Options& options, - const std::vector* env) - : pid_(-1), - returnCode_(RV_NOT_STARTED) { - if (options.usePath_) { - throw std::invalid_argument("usePath() not allowed when running in shell"); - } - const char* shell = getenv("SHELL"); - if (!shell) { - shell = "/bin/sh"; - } - - std::unique_ptr argv(new const char*[4]); - argv[0] = shell; - argv[1] = "-c"; - argv[2] = cmd.c_str(); - argv[3] = nullptr; - spawn(std::move(argv), shell, options, env); -} - -Subprocess::~Subprocess() { - CHECK_NE(returnCode_.state(), ProcessReturnCode::RUNNING) - << "Subprocess destroyed without reaping child"; - closeAll(); -} - -namespace { -void closeChecked(int fd) { - checkUnixError(::close(fd), "close"); -} - -struct ChildErrorInfo { - int errCode; - int errnoValue; -}; - -void childError(int errFd, int errCode, int errnoValue) FOLLY_NORETURN; -void childError(int errFd, int errCode, int errnoValue) { - ChildErrorInfo info = {errCode, errnoValue}; - // Write the error information over the pipe to our parent process. - // We can't really do anything else if this write call fails. - writeNoInt(errFd, &info, sizeof(info)); - // exit - _exit(errCode); -} - -} // namespace - -void Subprocess::closeAll() { - for (auto& p : pipes_) { - closeChecked(p.parentFd); - } - pipes_.clear(); -} - -void Subprocess::setAllNonBlocking() { - for (auto& p : pipes_) { - int fd = p.parentFd; - int flags = ::fcntl(fd, F_GETFL); - checkUnixError(flags, "fcntl"); - int r = ::fcntl(fd, F_SETFL, flags | O_NONBLOCK); - checkUnixError(r, "fcntl"); - } -} - -void Subprocess::spawn( - std::unique_ptr argv, - const char* executable, - const Options& optionsIn, - const std::vector* env) { - if (optionsIn.usePath_ && env) { - throw std::invalid_argument( - "usePath() not allowed when overriding environment"); - } - - // Make a copy, we'll mutate options - Options options(optionsIn); - - // On error, close all of the pipes_ - auto pipesGuard = makeGuard([&] { - for (auto& p : this->pipes_) { - CHECK_ERR(::close(p.parentFd)); - } - }); - - // Create a pipe to use to receive error information from the child, - // in case it fails before calling exec() - int errFds[2]; - int r = ::pipe(errFds); - checkUnixError(r, "pipe"); - SCOPE_EXIT { - CHECK_ERR(::close(errFds[0])); - if (errFds[1] >= 0) { - CHECK_ERR(::close(errFds[1])); - } - }; - // Ask the child to close the read end of the error pipe. - options.fdActions_[errFds[0]] = CLOSE; - // Set the close-on-exec flag on the write side of the pipe. - // This way the pipe will be closed automatically in the child if execve() - // succeeds. If the exec fails the child can write error information to the - // pipe. - r = fcntl(errFds[1], F_SETFD, FD_CLOEXEC); - checkUnixError(r, "set FD_CLOEXEC"); - - // Perform the actual work of setting up pipes then forking and - // executing the child. - spawnInternal(std::move(argv), executable, options, env, errFds[1]); - - // After spawnInternal() returns the child is alive. We have to be very - // careful about throwing after this point. We are inside the constructor, - // so if we throw the Subprocess object will have never existed, and the - // destructor will never be called. - // - // We should only throw if we got an error via the errFd, and we know the - // child has exited and can be immediately waited for. In all other cases, - // we have no way of cleaning up the child. - - // Close writable side of the errFd pipe in the parent process - CHECK_ERR(::close(errFds[1])); - errFds[1] = -1; - - // Read from the errFd pipe, to tell if the child ran into any errors before - // calling exec() - readChildErrorPipe(errFds[0], executable); - - // We have fully succeeded now, so release the guard on pipes_ - pipesGuard.dismiss(); -} - -void Subprocess::spawnInternal( - std::unique_ptr argv, - const char* executable, - Options& options, - const std::vector* env, - int errFd) { - // Parent work, pre-fork: create pipes - std::vector childFds; - // Close all of the childFds as we leave this scope - SCOPE_EXIT { - // These are only pipes, closing them shouldn't fail - for (int cfd : childFds) { - CHECK_ERR(::close(cfd)); - } - }; - - int r; - for (auto& p : options.fdActions_) { - if (p.second == PIPE_IN || p.second == PIPE_OUT) { - int fds[2]; - r = ::pipe(fds); - checkUnixError(r, "pipe"); - PipeInfo pinfo; - pinfo.direction = p.second; - int cfd; - if (p.second == PIPE_IN) { - // Child gets reading end - pinfo.parentFd = fds[1]; - cfd = fds[0]; - } else { - pinfo.parentFd = fds[0]; - cfd = fds[1]; - } - p.second = cfd; // ensure it gets dup2()ed - pinfo.childFd = p.first; - childFds.push_back(cfd); - pipes_.push_back(pinfo); - } - } - - // This should already be sorted, as options.fdActions_ is - DCHECK(std::is_sorted(pipes_.begin(), pipes_.end())); - - // Note that the const casts below are legit, per - // http://pubs.opengroup.org/onlinepubs/009695399/functions/exec.html - - char** argVec = const_cast(argv.get()); - - // Set up environment - std::unique_ptr envHolder; - char** envVec; - if (env) { - envHolder = cloneStrings(*env); - envVec = const_cast(envHolder.get()); - } else { - envVec = environ; - } - - // Block all signals around vfork; see http://ewontfix.com/7/. - // - // As the child may run in the same address space as the parent until - // the actual execve() system call, any (custom) signal handlers that - // the parent has might alter parent's memory if invoked in the child, - // with undefined results. So we block all signals in the parent before - // vfork(), which will cause them to be blocked in the child as well (we - // rely on the fact that Linux, just like all sane implementations, only - // clones the calling thread). Then, in the child, we reset all signals - // to their default dispositions (while still blocked), and unblock them - // (so the exec()ed process inherits the parent's signal mask) - // - // The parent also unblocks all signals as soon as vfork() returns. - sigset_t allBlocked; - r = ::sigfillset(&allBlocked); - checkUnixError(r, "sigfillset"); - sigset_t oldSignals; - - r = pthread_sigmask(SIG_SETMASK, &allBlocked, &oldSignals); - checkPosixError(r, "pthread_sigmask"); - SCOPE_EXIT { - // Restore signal mask - r = pthread_sigmask(SIG_SETMASK, &oldSignals, nullptr); - CHECK_EQ(r, 0) << "pthread_sigmask: " << errnoStr(r); // shouldn't fail - }; - - pid_t pid = vfork(); - if (pid == 0) { - int errnoValue = prepareChild(options, &oldSignals); - if (errnoValue != 0) { - childError(errFd, kChildFailure, errnoValue); - } - - errnoValue = runChild(executable, argVec, envVec, options); - // If we get here, exec() failed. - childError(errFd, kExecFailure, errnoValue); - } - // In parent. Make sure vfork() succeeded. - checkUnixError(pid, errno, "vfork"); - - // Child is alive. We have to be very careful about throwing after this - // point. We are inside the constructor, so if we throw the Subprocess - // object will have never existed, and the destructor will never be called. - // - // We should only throw if we got an error via the errFd, and we know the - // child has exited and can be immediately waited for. In all other cases, - // we have no way of cleaning up the child. - pid_ = pid; - returnCode_ = ProcessReturnCode(RV_RUNNING); -} - -int Subprocess::prepareChild(const Options& options, - const sigset_t* sigmask) const { - // While all signals are blocked, we must reset their - // dispositions to default. - for (int sig = 1; sig < NSIG; ++sig) { - ::signal(sig, SIG_DFL); - } - // Unblock signals; restore signal mask. - int r = pthread_sigmask(SIG_SETMASK, sigmask, nullptr); - if (r != 0) { - return r; // pthread_sigmask() returns an errno value - } - - // Close parent's ends of all pipes - for (auto& p : pipes_) { - r = ::close(p.parentFd); - if (r == -1) { - return errno; - } - } - - // Close all fds that we're supposed to close. - // Note that we're ignoring errors here, in case some of these - // fds were set to close on exec. - for (auto& p : options.fdActions_) { - if (p.second == CLOSE) { - ::close(p.first); - } else { - r = ::dup2(p.second, p.first); - if (r == -1) { - return errno; - } - } - } - - // If requested, close all other file descriptors. Don't close - // any fds in options.fdActions_, and don't touch stdin, stdout, stderr. - // Ignore errors. - if (options.closeOtherFds_) { - for (int fd = getdtablesize() - 1; fd >= 3; --fd) { - if (options.fdActions_.count(fd) == 0) { - ::close(fd); - } - } - } - - // Opt to receive signal on parent death, if requested - if (options.parentDeathSignal_ != 0) { - r = prctl(PR_SET_PDEATHSIG, options.parentDeathSignal_, 0, 0, 0); - if (r == -1) { - return errno; - } - } - - return 0; -} - -int Subprocess::runChild(const char* executable, - char** argv, char** env, - const Options& options) const { - // Now, finally, exec. - int r; - if (options.usePath_) { - ::execvp(executable, argv); - } else { - ::execve(executable, argv, env); - } - return errno; -} - -void Subprocess::readChildErrorPipe(int pfd, const char* executable) { - ChildErrorInfo info; - auto rc = readNoInt(pfd, &info, sizeof(info)); - if (rc == 0) { - // No data means the child executed successfully, and the pipe - // was closed due to the close-on-exec flag being set. - return; - } else if (rc != sizeof(ChildErrorInfo)) { - // An error occurred trying to read from the pipe, or we got a partial read. - // Neither of these cases should really occur in practice. - // - // We can't get any error data from the child in this case, and we don't - // know if it is successfully running or not. All we can do is to return - // normally, as if the child executed successfully. If something bad - // happened the caller should at least get a non-normal exit status from - // the child. - LOG(ERROR) << "unexpected error trying to read from child error pipe " << - "rc=" << rc << ", errno=" << errno; - return; - } - - // We got error data from the child. The child should exit immediately in - // this case, so wait on it to clean up. - wait(); - - // Throw to signal the error - throw SubprocessSpawnError(executable, info.errCode, info.errnoValue); -} - -ProcessReturnCode Subprocess::poll() { - returnCode_.enforce(ProcessReturnCode::RUNNING); - DCHECK_GT(pid_, 0); - int status; - pid_t found = ::waitpid(pid_, &status, WNOHANG); - checkUnixError(found, "waitpid"); - if (found != 0) { - returnCode_ = ProcessReturnCode(status); - pid_ = -1; - } - return returnCode_; -} - -bool Subprocess::pollChecked() { - if (poll().state() == ProcessReturnCode::RUNNING) { - return false; - } - checkStatus(returnCode_); - return true; -} - -ProcessReturnCode Subprocess::wait() { - returnCode_.enforce(ProcessReturnCode::RUNNING); - DCHECK_GT(pid_, 0); - int status; - pid_t found; - do { - found = ::waitpid(pid_, &status, 0); - } while (found == -1 && errno == EINTR); - checkUnixError(found, "waitpid"); - DCHECK_EQ(found, pid_); - returnCode_ = ProcessReturnCode(status); - pid_ = -1; - return returnCode_; -} - -void Subprocess::waitChecked() { - wait(); - checkStatus(returnCode_); -} - -void Subprocess::sendSignal(int signal) { - returnCode_.enforce(ProcessReturnCode::RUNNING); - int r = ::kill(pid_, signal); - checkUnixError(r, "kill"); -} - -pid_t Subprocess::pid() const { - return pid_; -} - -namespace { - -std::pair queueFront(const IOBufQueue& queue) { - auto* p = queue.front(); - if (!p) return std::make_pair(nullptr, 0); - return io::Cursor(p).peek(); -} - -// fd write -bool handleWrite(int fd, IOBufQueue& queue) { - for (;;) { - auto p = queueFront(queue); - if (p.second == 0) { - return true; // EOF - } - - ssize_t n; - do { - n = ::write(fd, p.first, p.second); - } while (n == -1 && errno == EINTR); - if (n == -1 && errno == EAGAIN) { - return false; - } - checkUnixError(n, "write"); - queue.trimStart(n); - } -} - -// fd read -bool handleRead(int fd, IOBufQueue& queue) { - for (;;) { - auto p = queue.preallocate(100, 65000); - ssize_t n; - do { - n = ::read(fd, p.first, p.second); - } while (n == -1 && errno == EINTR); - if (n == -1 && errno == EAGAIN) { - return false; - } - checkUnixError(n, "read"); - if (n == 0) { - return true; - } - queue.postallocate(n); - } -} - -bool discardRead(int fd) { - static const size_t bufSize = 65000; - // Thread unsafe, but it doesn't matter. - static std::unique_ptr buf(new char[bufSize]); - - for (;;) { - ssize_t n; - do { - n = ::read(fd, buf.get(), bufSize); - } while (n == -1 && errno == EINTR); - if (n == -1 && errno == EAGAIN) { - return false; - } - checkUnixError(n, "read"); - if (n == 0) { - return true; - } - } -} - -} // namespace - -std::pair Subprocess::communicate( - StringPiece input) { - IOBufQueue inputQueue; - inputQueue.wrapBuffer(input.data(), input.size()); - - auto outQueues = communicateIOBuf(std::move(inputQueue)); - auto outBufs = std::make_pair(outQueues.first.move(), - outQueues.second.move()); - std::pair out; - if (outBufs.first) { - outBufs.first->coalesce(); - out.first.assign(reinterpret_cast(outBufs.first->data()), - outBufs.first->length()); - } - if (outBufs.second) { - outBufs.second->coalesce(); - out.second.assign(reinterpret_cast(outBufs.second->data()), - outBufs.second->length()); - } - return out; -} - -std::pair Subprocess::communicateIOBuf( - IOBufQueue input) { - // If the user supplied a non-empty input buffer, make sure - // that stdin is a pipe so we can write the data. - if (!input.empty()) { - // findByChildFd() will throw std::invalid_argument if no pipe for - // STDIN_FILENO exists - findByChildFd(STDIN_FILENO); - } - - std::pair out; - - auto readCallback = [&] (int pfd, int cfd) -> bool { - if (cfd == STDOUT_FILENO) { - return handleRead(pfd, out.first); - } else if (cfd == STDERR_FILENO) { - return handleRead(pfd, out.second); - } else { - // Don't close the file descriptor, the child might not like SIGPIPE, - // just read and throw the data away. - return discardRead(pfd); - } - }; - - auto writeCallback = [&] (int pfd, int cfd) -> bool { - if (cfd == STDIN_FILENO) { - return handleWrite(pfd, input); - } else { - // If we don't want to write to this fd, just close it. - return true; - } - }; - - communicate(std::move(readCallback), std::move(writeCallback)); - - return out; -} - -void Subprocess::communicate(FdCallback readCallback, - FdCallback writeCallback) { - returnCode_.enforce(ProcessReturnCode::RUNNING); - setAllNonBlocking(); - - std::vector fds; - fds.reserve(pipes_.size()); - std::vector toClose; - toClose.reserve(pipes_.size()); - - while (!pipes_.empty()) { - fds.clear(); - toClose.clear(); - - for (auto& p : pipes_) { - pollfd pfd; - pfd.fd = p.parentFd; - // Yes, backwards, PIPE_IN / PIPE_OUT are defined from the - // child's point of view. - pfd.events = (p.direction == PIPE_IN ? POLLOUT : POLLIN); - fds.push_back(pfd); - } - - int r; - do { - r = ::poll(fds.data(), fds.size(), -1); - } while (r == -1 && errno == EINTR); - checkUnixError(r, "poll"); - - for (int i = 0; i < pipes_.size(); ++i) { - auto& p = pipes_[i]; - DCHECK_EQ(fds[i].fd, p.parentFd); - short events = fds[i].revents; - - bool closed = false; - if (events & POLLOUT) { - DCHECK(!(events & POLLIN)); - if (writeCallback(p.parentFd, p.childFd)) { - toClose.push_back(i); - closed = true; - } - } - - if (events & POLLIN) { - DCHECK(!(events & POLLOUT)); - if (readCallback(p.parentFd, p.childFd)) { - toClose.push_back(i); - closed = true; - } - } - - if ((events & (POLLHUP | POLLERR)) && !closed) { - toClose.push_back(i); - closed = true; - } - } - - // Close the fds in reverse order so the indexes hold after erase() - for (int idx : boost::adaptors::reverse(toClose)) { - auto pos = pipes_.begin() + idx; - closeChecked(pos->parentFd); - pipes_.erase(pos); - } - } -} - -int Subprocess::findByChildFd(int childFd) const { - auto pos = std::lower_bound( - pipes_.begin(), pipes_.end(), childFd, - [] (const PipeInfo& info, int fd) { return info.childFd < fd; }); - if (pos == pipes_.end() || pos->childFd != childFd) { - throw std::invalid_argument(folly::to( - "child fd not found ", childFd)); - } - return pos - pipes_.begin(); -} - -void Subprocess::closeParentFd(int childFd) { - int idx = findByChildFd(childFd); - closeChecked(pipes_[idx].parentFd); - pipes_.erase(pipes_.begin() + idx); -} - -namespace { - -class Initializer { - public: - Initializer() { - // We like EPIPE, thanks. - ::signal(SIGPIPE, SIG_IGN); - } -}; - -Initializer initializer; - -} // namespace - -} // namespace folly - diff --git a/hphp/third_party/folly/folly/Subprocess.h b/hphp/third_party/folly/folly/Subprocess.h deleted file mode 100644 index 94cc40897..000000000 --- a/hphp/third_party/folly/folly/Subprocess.h +++ /dev/null @@ -1,528 +0,0 @@ -/* - * Copyright 2013 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. - */ - -/** - * Subprocess library, modeled after Python's subprocess module - * (http://docs.python.org/2/library/subprocess.html) - * - * This library defines one class (Subprocess) which represents a child - * process. Subprocess has two constructors: one that takes a vector - * and executes the given executable without using the shell, and one - * that takes a string and executes the given command using the shell. - * Subprocess allows you to redirect the child's standard input, standard - * output, and standard error to/from child descriptors in the parent, - * or to create communication pipes between the child and the parent. - * - * The simplest example is a thread-safe version of the system() library - * function: - * Subprocess(cmd).wait(); - * which executes the command using the default shell and waits for it - * to complete, returning the exit status. - * - * A thread-safe version of popen() (type="r", to read from the child): - * Subprocess proc(cmd, Subprocess::pipeStdout()); - * // read from proc.stdout() - * proc.wait(); - * - * A thread-safe version of popen() (type="w", to write from the child): - * Subprocess proc(cmd, Subprocess::pipeStdin()); - * // write to proc.stdin() - * proc.wait(); - * - * If you want to redirect both stdin and stdout to pipes, you can, but - * note that you're subject to a variety of deadlocks. You'll want to use - * nonblocking I/O; look at the implementation of communicate() for an example. - * - * communicate() is a way to communicate to a child via its standard input, - * standard output, and standard error. It buffers everything in memory, - * so it's not great for large amounts of data (or long-running processes), - * but it insulates you from the deadlocks mentioned above. - */ -#ifndef FOLLY_SUBPROCESS_H_ -#define FOLLY_SUBPROCESS_H_ - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include "folly/io/IOBufQueue.h" -#include "folly/MapUtil.h" -#include "folly/Portability.h" -#include "folly/Range.h" - -namespace folly { - -/** - * Class to wrap a process return code. - */ -class Subprocess; -class ProcessReturnCode { - friend class Subprocess; - public: - enum State { - NOT_STARTED, - RUNNING, - EXITED, - KILLED - }; - - /** - * Process state. One of: - * NOT_STARTED: process hasn't been started successfully - * RUNNING: process is currently running - * EXITED: process exited (successfully or not) - * KILLED: process was killed by a signal. - */ - State state() const; - - /** - * Helper wrappers around state(). - */ - bool notStarted() const { return state() == NOT_STARTED; } - bool running() const { return state() == RUNNING; } - bool exited() const { return state() == EXITED; } - bool killed() const { return state() == KILLED; } - - /** - * Exit status. Only valid if state() == EXITED; throws otherwise. - */ - int exitStatus() const; - - /** - * Signal that caused the process's termination. Only valid if - * state() == KILLED; throws otherwise. - */ - int killSignal() const; - - /** - * Was a core file generated? Only valid if state() == KILLED; throws - * otherwise. - */ - bool coreDumped() const; - - /** - * String representation; one of - * "not started" - * "running" - * "exited with status " - * "killed by signal " - * "killed by signal (core dumped)" - */ - std::string str() const; - - /** - * Helper function to enforce a precondition based on this. - * Throws std::logic_error if in an unexpected state. - */ - void enforce(State state) const; - private: - explicit ProcessReturnCode(int rv) : rawStatus_(rv) { } - static constexpr int RV_NOT_STARTED = -2; - static constexpr int RV_RUNNING = -1; - - int rawStatus_; -}; - -/** - * Base exception thrown by the Subprocess methods. - */ -class SubprocessError : public std::exception {}; - -/** - * Exception thrown by *Checked methods of Subprocess. - */ -class CalledProcessError : public SubprocessError { - public: - explicit CalledProcessError(ProcessReturnCode rc); - ~CalledProcessError() throw() { } - const char* what() const throw() FOLLY_OVERRIDE { return what_.c_str(); } - ProcessReturnCode returnCode() const { return returnCode_; } - private: - ProcessReturnCode returnCode_; - std::string what_; -}; - -/** - * Exception thrown if the subprocess cannot be started. - */ -class SubprocessSpawnError : public SubprocessError { - public: - SubprocessSpawnError(const char* executable, int errCode, int errnoValue); - ~SubprocessSpawnError() throw() {} - const char* what() const throw() FOLLY_OVERRIDE { return what_.c_str(); } - int errnoValue() const { return errnoValue_; } - - private: - int errnoValue_; - std::string what_; -}; - -/** - * Subprocess. - */ -class Subprocess : private boost::noncopyable { - public: - static const int CLOSE = -1; - static const int PIPE = -2; - static const int PIPE_IN = -3; - static const int PIPE_OUT = -4; - - /** - * Class representing various options: file descriptor behavior, and - * whether to use $PATH for searching for the executable, - * - * By default, we don't use $PATH, file descriptors are closed if - * the close-on-exec flag is set (fcntl FD_CLOEXEC) and inherited - * otherwise. - */ - class Options : private boost::orable { - friend class Subprocess; - public: - Options() - : closeOtherFds_(false), - usePath_(false), - parentDeathSignal_(0) { - } - - /** - * Change action for file descriptor fd. - * - * "action" may be another file descriptor number (dup2()ed before the - * child execs), or one of CLOSE, PIPE_IN, and PIPE_OUT. - * - * CLOSE: close the file descriptor in the child - * PIPE_IN: open a pipe *from* the child - * PIPE_OUT: open a pipe *to* the child - * - * PIPE is a shortcut; same as PIPE_IN for stdin (fd 0), same as - * PIPE_OUT for stdout (fd 1) or stderr (fd 2), and an error for - * other file descriptors. - */ - Options& fd(int fd, int action); - - /** - * Shortcut to change the action for standard input. - */ - Options& stdin(int action) { return fd(STDIN_FILENO, action); } - - /** - * Shortcut to change the action for standard output. - */ - Options& stdout(int action) { return fd(STDOUT_FILENO, action); } - - /** - * Shortcut to change the action for standard error. - * Note that stderr(1) will redirect the standard error to the same - * file descriptor as standard output; the equivalent of bash's "2>&1" - */ - Options& stderr(int action) { return fd(STDERR_FILENO, action); } - - Options& pipeStdin() { return fd(STDIN_FILENO, PIPE_IN); } - Options& pipeStdout() { return fd(STDOUT_FILENO, PIPE_OUT); } - Options& pipeStderr() { return fd(STDERR_FILENO, PIPE_OUT); } - - /** - * Close all other fds (other than standard input, output, error, - * and file descriptors explicitly specified with fd()). - * - * This is potentially slow; it's generally a better idea to - * set the close-on-exec flag on all file descriptors that shouldn't - * be inherited by the child. - * - * Even with this option set, standard input, output, and error are - * not closed; use stdin(CLOSE), stdout(CLOSE), stderr(CLOSE) if you - * desire this. - */ - Options& closeOtherFds() { closeOtherFds_ = true; return *this; } - - /** - * Use the search path ($PATH) when searching for the executable. - */ - Options& usePath() { usePath_ = true; return *this; } - - /** - * Child will receive a signal when the parent exits. - */ - Options& parentDeathSignal(int sig) { - parentDeathSignal_ = sig; - return *this; - } - - /** - * Helpful way to combine Options. - */ - Options& operator|=(const Options& other); - - private: - typedef boost::container::flat_map FdMap; - FdMap fdActions_; - bool closeOtherFds_; - bool usePath_; - int parentDeathSignal_; - }; - - static Options pipeStdin() { return Options().stdin(PIPE); } - static Options pipeStdout() { return Options().stdout(PIPE); } - static Options pipeStderr() { return Options().stderr(PIPE); } - - /** - * Create a subprocess from the given arguments. argv[0] must be listed. - * If not-null, executable must be the actual executable - * being used (otherwise it's the same as argv[0]). - * - * If env is not-null, it must contain name=value strings to be used - * as the child's environment; otherwise, we inherit the environment - * from the parent. env must be null if options.usePath is set. - */ - explicit Subprocess( - const std::vector& argv, - const Options& options = Options(), - const char* executable = nullptr, - const std::vector* env = nullptr); - ~Subprocess(); - - /** - * Create a subprocess run as a shell command (as shell -c 'command') - * - * The shell to use is taken from the environment variable $SHELL, - * or /bin/sh if $SHELL is unset. - */ - explicit Subprocess( - const std::string& cmd, - const Options& options = Options(), - const std::vector* env = nullptr); - - /** - * Communicate with the child until all pipes to/from the child are closed. - * - * The input buffer is written to the process' stdin pipe, and data is read - * from the stdout and stderr pipes. Non-blocking I/O is performed on all - * pipes simultaneously to avoid deadlocks. - * - * The stdin pipe will be closed after the full input buffer has been written. - * An error will be thrown if a non-empty input buffer is supplied but stdin - * was not configured as a pipe. - * - * Returns a pair of buffers containing the data read from stdout and stderr. - * If stdout or stderr is not a pipe, an empty IOBuf queue will be returned - * for the respective buffer. - * - * Note that communicate() returns when all pipes to/from the child are - * closed; the child might stay alive after that, so you must still wait(). - * - * communicateIOBuf uses IOBufQueue for buffering (which has the advantage - * that it won't try to allocate all data at once). communicate - * uses strings for simplicity. - */ - std::pair communicateIOBuf( - IOBufQueue input = IOBufQueue()); - - std::pair communicate( - StringPiece input = StringPiece()); - - /** - * Communicate with the child until all pipes to/from the child are closed. - * - * readCallback(pfd, cfd) will be called whenever there's data available - * on any pipe *from* the child (PIPE_OUT). pfd is the file descriptor - * in the parent (that you use to read from); cfd is the file descriptor - * in the child (used for identifying the stream; 1 = child's standard - * output, 2 = child's standard error, etc) - * - * writeCallback(pfd, cfd) will be called whenever a pipe *to* the child is - * writable (PIPE_IN). pfd is the file descriptor in the parent (that you - * use to write to); cfd is the file descriptor in the child (used for - * identifying the stream; 0 = child's standard input, etc) - * - * The read and write callbacks must read from / write to pfd and return - * false during normal operation or true at end-of-file; - * communicate() will then close the pipe. Note that pfd is - * nonblocking, so be prepared for read() / write() to return -1 and - * set errno to EAGAIN (in which case you should return false). - * - * NOTE that you MUST consume all data passed to readCallback (or return - * true, which will close the pipe, possibly sending SIGPIPE to the child or - * making its writes fail with EPIPE), and you MUST write to a writable pipe - * (or return true, which will close the pipe). To do otherwise is an - * error. You must do this even for pipes you are not interested in. - * - * Note that communicate() returns when all pipes to/from the child are - * closed; the child might stay alive after that, so you must still wait(). - * - * Most users won't need to use this; the simpler version of communicate - * (which buffers data in memory) will probably work fine. - */ - typedef std::function FdCallback; - void communicate(FdCallback readCallback, FdCallback writeCallback); - - /** - * Return the child's pid, or -1 if the child wasn't successfully spawned - * or has already been wait()ed upon. - */ - pid_t pid() const; - - /** - * Return the child's status (as per wait()) if the process has already - * been waited on, -1 if the process is still running, or -2 if the process - * hasn't been successfully started. NOTE that this does not poll, but - * returns the status stored in the Subprocess object. - */ - ProcessReturnCode returnCode() const { return returnCode_; } - - /** - * Poll the child's status and return it, return -1 if the process - * is still running. NOTE that it is illegal to call poll again after - * poll indicated that the process has terminated, or to call poll on a - * process that hasn't been successfully started (the constructor threw an - * exception). - */ - ProcessReturnCode poll(); - - /** - * Poll the child's status. If the process is still running, return false. - * Otherwise, return true if the process exited with status 0 (success), - * or throw CalledProcessError if the process exited with a non-zero status. - */ - bool pollChecked(); - - /** - * Wait for the process to terminate and return its status. - * Similarly to poll, it is illegal to call wait after the process - * has already been reaped or if the process has not successfully started. - */ - ProcessReturnCode wait(); - - /** - * Wait for the process to terminate, throw if unsuccessful. - */ - void waitChecked(); - - /** - * Set all pipes from / to child non-blocking. communicate() does - * this for you. - */ - void setAllNonBlocking(); - - /** - * Get parent file descriptor corresponding to the given file descriptor - * in the child. Throws if childFd isn't a pipe (PIPE_IN / PIPE_OUT). - * Do not close() the return file descriptor; use closeParentFd, below. - */ - int parentFd(int childFd) const { - return pipes_[findByChildFd(childFd)].parentFd; - } - int stdin() const { return parentFd(0); } - int stdout() const { return parentFd(1); } - int stderr() const { return parentFd(2); } - - /** - * Close the parent file descriptor given a file descriptor in the child. - */ - void closeParentFd(int childFd); - - /** - * Send a signal to the child. Shortcuts for the commonly used Unix - * signals are below. - */ - void sendSignal(int signal); - void terminate() { sendSignal(SIGTERM); } - void kill() { sendSignal(SIGKILL); } - - private: - static const int RV_RUNNING = ProcessReturnCode::RV_RUNNING; - static const int RV_NOT_STARTED = ProcessReturnCode::RV_NOT_STARTED; - - // spawn() sets up a pipe to read errors from the child, - // then calls spawnInternal() to do the bulk of the work. Once - // spawnInternal() returns it reads the error pipe to see if the child - // encountered any errors. - void spawn( - std::unique_ptr argv, - const char* executable, - const Options& options, - const std::vector* env); - void spawnInternal( - std::unique_ptr argv, - const char* executable, - Options& options, - const std::vector* env, - int errFd); - - // Actions to run in child. - // Note that this runs after vfork(), so tread lightly. - // Returns 0 on success, or an errno value on failure. - int prepareChild(const Options& options, const sigset_t* sigmask) const; - int runChild(const char* executable, char** argv, char** env, - const Options& options) const; - - /** - * Read from the error pipe, and throw SubprocessSpawnError if the child - * failed before calling exec(). - */ - void readChildErrorPipe(int pfd, const char* executable); - - /** - * Close all file descriptors. - */ - void closeAll(); - - // return index in pipes_ - int findByChildFd(int childFd) const; - - pid_t pid_; - ProcessReturnCode returnCode_; - - // The number of pipes between parent and child is assumed to be small, - // so we're happy with a vector here, even if it means linear erase. - // sorted by childFd - struct PipeInfo : private boost::totally_ordered { - int parentFd; - int childFd; - int direction; // one of PIPE_IN / PIPE_OUT - bool operator<(const PipeInfo& other) const { - return childFd < other.childFd; - } - bool operator==(const PipeInfo& other) const { - return childFd == other.childFd; - } - }; - std::vector pipes_; -}; - -inline Subprocess::Options& Subprocess::Options::operator|=( - const Subprocess::Options& other) { - if (this == &other) return *this; - // Replace - for (auto& p : other.fdActions_) { - fdActions_[p.first] = p.second; - } - closeOtherFds_ |= other.closeOtherFds_; - usePath_ |= other.usePath_; - return *this; -} - -} // namespace folly - -#endif /* FOLLY_SUBPROCESS_H_ */ - diff --git a/hphp/third_party/folly/folly/Synchronized.h b/hphp/third_party/folly/folly/Synchronized.h deleted file mode 100644 index b5a3c5ba0..000000000 --- a/hphp/third_party/folly/folly/Synchronized.h +++ /dev/null @@ -1,692 +0,0 @@ -/* - * Copyright 2013 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. - */ - -/** - * This module implements a Synchronized abstraction useful in - * mutex-based concurrency. - * - * @author: Andrei Alexandrescu (andrei.alexandrescu@fb.com) - */ - -#ifndef SYNCHRONIZED_H_ -#define SYNCHRONIZED_H_ - -#include -#include -#include -#include "folly/Preprocessor.h" -#include "folly/Traits.h" - -namespace folly { - -namespace detail { -enum InternalDoNotUse {}; - -/** - * Free function adaptors for std:: and boost:: - */ - -/** - * Yields true iff T has .lock() and .unlock() member functions. This - * is done by simply enumerating the mutexes with this interface in - * std and boost. - */ -template -struct HasLockUnlock { - enum { value = IsOneOf::value }; -}; - -/** - * Acquires a mutex for reading by calling .lock(). The exception is - * boost::shared_mutex, which has a special read-lock primitive called - * .lock_shared(). - */ -template -typename std::enable_if< - HasLockUnlock::value && !std::is_same::value>::type -acquireRead(T& mutex) { - mutex.lock(); -} - -/** - * Special case for boost::shared_mutex. - */ -template -typename std::enable_if::value>::type -acquireRead(T& mutex) { - mutex.lock_shared(); -} - -/** - * Acquires a mutex for reading with timeout by calling .timed_lock(). This - * applies to three of the boost mutex classes as enumerated below. - */ -template -typename std::enable_if::value, bool>::type -acquireRead(T& mutex, - unsigned int milliseconds) { - return mutex.timed_lock_shared(boost::posix_time::milliseconds(milliseconds)); -} - -/** - * Acquires a mutex for reading and writing by calling .lock(). - */ -template -typename std::enable_if::value>::type -acquireReadWrite(T& mutex) { - mutex.lock(); -} - -#ifndef __APPLE__ // OSX doesn't have timed mutexes -/** - * Acquires a mutex for reading and writing with timeout by calling - * .try_lock_for(). This applies to two of the std mutex classes as - * enumerated below. - */ -template -typename std::enable_if< - IsOneOf::value, bool>::type -acquireReadWrite(T& mutex, - unsigned int milliseconds) { - return mutex.try_lock_for(std::chrono::milliseconds(milliseconds)); -} - -/** - * Acquires a mutex for reading and writing with timeout by calling - * .timed_lock(). This applies to three of the boost mutex classes as - * enumerated below. - */ -template -typename std::enable_if< - IsOneOf::value, bool>::type -acquireReadWrite(T& mutex, - unsigned int milliseconds) { - return mutex.timed_lock(boost::posix_time::milliseconds(milliseconds)); -} -#endif // __APPLE__ - -/** - * Releases a mutex previously acquired for reading by calling - * .unlock(). The exception is boost::shared_mutex, which has a - * special primitive called .unlock_shared(). - */ -template -typename std::enable_if< - HasLockUnlock::value && !std::is_same::value>::type -releaseRead(T& mutex) { - mutex.unlock(); -} - -/** - * Special case for boost::shared_mutex. - */ -template -typename std::enable_if::value>::type -releaseRead(T& mutex) { - mutex.unlock_shared(); -} - -/** - * Releases a mutex previously acquired for reading-writing by calling - * .unlock(). - */ -template -typename std::enable_if::value>::type -releaseReadWrite(T& mutex) { - mutex.unlock(); -} - -} // namespace detail - -/** - * Synchronized encapsulates an object of type T (a "datum") paired - * with a mutex. The only way to access the datum is while the mutex - * is locked, and Synchronized makes it virtually impossible to do - * otherwise. The code that would access the datum in unsafe ways - * would look odd and convoluted, thus readily alerting the human - * reviewer. In contrast, the code that uses Synchronized correctly - * looks simple and intuitive. - * - * The second parameter must be a mutex type. Supported mutexes are - * std::mutex, std::recursive_mutex, std::timed_mutex, - * std::recursive_timed_mutex, boost::mutex, boost::recursive_mutex, - * boost::shared_mutex, boost::timed_mutex, - * boost::recursive_timed_mutex, and the folly/RWSpinLock.h - * classes. - * - * You may define Synchronized support by defining 4-6 primitives in - * the same namespace as the mutex class (found via ADL). The - * primitives are: acquireRead, acquireReadWrite, releaseRead, and - * releaseReadWrite. Two optional primitives for timout operations are - * overloads of acquireRead and acquireReadWrite. For signatures, - * refer to the namespace detail below, which implements the - * primitives for mutexes in std and boost. - */ -template -struct Synchronized { - /** - * Default constructor leaves both members call their own default - * constructor. - */ - Synchronized() = default; - - /** - * Copy constructor copies the data (with locking the source and - * all) but does NOT copy the mutex. Doing so would result in - * deadlocks. - */ - Synchronized(const Synchronized& rhs) { - auto guard = rhs.operator->(); - datum_ = rhs.datum_; - } - - /** - * Move constructor moves the data (with locking the source and all) - * but does not move the mutex. - */ - Synchronized(Synchronized&& rhs) { - auto guard = rhs.operator->(); - datum_ = std::move(rhs.datum_); - } - - /** - * Constructor taking a datum as argument copies it. There is no - * need to lock the constructing object. - */ - explicit Synchronized(const T& rhs) : datum_(rhs) {} - - /** - * Constructor taking a datum rvalue as argument moves it. Again, - * there is no need to lock the constructing object. - */ - explicit Synchronized(T&& rhs) : datum_(std::move(rhs)) {} - - /** - * The canonical assignment operator only assigns the data, NOT the - * mutex. It locks the two objects in ascending order of their - * addresses. - */ - Synchronized& operator=(const Synchronized& rhs) { - if (this == &rhs) { - // Self-assignment, pass. - } else if (this < &rhs) { - auto guard1 = operator->(); - auto guard2 = rhs.operator->(); - datum_ = rhs.datum_; - } else { - auto guard1 = rhs.operator->(); - auto guard2 = operator->(); - datum_ = rhs.datum_; - } - return *this; - } - - /** - * Move assignment operator, only assigns the data, NOT the - * mutex. It locks the two objects in ascending order of their - * addresses. - */ - Synchronized& operator=(Synchronized&& rhs) { - if (this == &rhs) { - // Self-assignment, pass. - } else if (this < &rhs) { - auto guard1 = operator->(); - auto guard2 = rhs.operator->(); - datum_ = std::move(rhs.datum_); - } else { - auto guard1 = rhs.operator->(); - auto guard2 = operator->(); - datum_ = std::move(rhs.datum_); - } - return *this; - } - - /** - * Lock object, assign datum. - */ - Synchronized& operator=(const T& rhs) { - auto guard = operator->(); - datum_ = rhs; - return *this; - } - - /** - * Lock object, move-assign datum. - */ - Synchronized& operator=(T&& rhs) { - auto guard = operator->(); - datum_ = std::move(rhs); - return *this; - } - - /** - * A LockedPtr lp keeps a modifiable (i.e. non-const) - * Synchronized object locked for the duration of lp's - * existence. Because of this, you get to access the datum's methods - * directly by using lp->fun(). - */ - struct LockedPtr { - /** - * Found no reason to leave this hanging. - */ - LockedPtr() = delete; - - /** - * Takes a Synchronized and locks it. - */ - explicit LockedPtr(Synchronized* parent) : parent_(parent) { - acquire(); - } - - /** - * Takes a Synchronized and attempts to lock it for some - * milliseconds. If not, the LockedPtr will be subsequently null. - */ - LockedPtr(Synchronized* parent, unsigned int milliseconds) { - using namespace detail; - if (acquireReadWrite(parent->mutex_, milliseconds)) { - parent_ = parent; - return; - } - // Could not acquire the resource, pointer is null - parent_ = NULL; - } - - /** - * This is used ONLY inside SYNCHRONIZED_DUAL. It initializes - * everything properly, but does not lock the parent because it - * "knows" someone else will lock it. Please do not use. - */ - LockedPtr(Synchronized* parent, detail::InternalDoNotUse) - : parent_(parent) { - } - - /** - * Copy ctor adds one lock. - */ - LockedPtr(const LockedPtr& rhs) : parent_(rhs.parent_) { - acquire(); - } - - /** - * Assigning from another LockedPtr results in freeing the former - * lock and acquiring the new one. The method works with - * self-assignment (does nothing). - */ - LockedPtr& operator=(const LockedPtr& rhs) { - if (parent_ != rhs.parent_) { - if (parent_) parent_->mutex_.unlock(); - parent_ = rhs.parent_; - acquire(); - } - return *this; - } - - /** - * Destructor releases. - */ - ~LockedPtr() { - using namespace detail; - if (parent_) releaseReadWrite(parent_->mutex_); - } - - /** - * Safe to access the data. Don't save the obtained pointer by - * invoking lp.operator->() by hand. Also, if the method returns a - * handle stored inside the datum, don't use this idiom - use - * SYNCHRONIZED below. - */ - T* operator->() { - return parent_ ? &parent_->datum_ : NULL; - } - - /** - * This class temporarily unlocks a LockedPtr in a scoped - * manner. It is used inside of the UNSYNCHRONIZED macro. - */ - struct Unsynchronizer { - explicit Unsynchronizer(LockedPtr* p) : parent_(p) { - using namespace detail; - releaseReadWrite(parent_->parent_->mutex_); - } - Unsynchronizer(const Unsynchronizer&) = delete; - Unsynchronizer& operator=(const Unsynchronizer&) = delete; - ~Unsynchronizer() { - parent_->acquire(); - } - LockedPtr* operator->() const { - return parent_; - } - private: - LockedPtr* parent_; - }; - friend struct Unsynchronizer; - Unsynchronizer typeHackDoNotUse(); - - template - friend void lockInOrder(P1& p1, P2& p2); - - private: - void acquire() { - using namespace detail; - if (parent_) acquireReadWrite(parent_->mutex_); - } - - // This is the entire state of LockedPtr. - Synchronized* parent_; - }; - - /** - * ConstLockedPtr does exactly what LockedPtr does, but for const - * Synchronized objects. Of interest is that ConstLockedPtr only - * uses a read lock, which is faster but more restrictive - you only - * get to call const methods of the datum. - * - * Much of the code between LockedPtr and - * ConstLockedPtr is identical and could be factor out, but there - * are enough nagging little differences to not justify the trouble. - */ - struct ConstLockedPtr { - ConstLockedPtr() = delete; - explicit ConstLockedPtr(const Synchronized* parent) : parent_(parent) { - acquire(); - } - ConstLockedPtr(const Synchronized* parent, detail::InternalDoNotUse) - : parent_(parent) { - } - ConstLockedPtr(const ConstLockedPtr& rhs) : parent_(rhs.parent_) { - acquire(); - } - explicit ConstLockedPtr(const LockedPtr& rhs) : parent_(rhs.parent_) { - acquire(); - } - ConstLockedPtr(const Synchronized* parent, unsigned int milliseconds) { - if (parent->mutex_.timed_lock( - boost::posix_time::milliseconds(milliseconds))) { - parent_ = parent; - return; - } - // Could not acquire the resource, pointer is null - parent_ = NULL; - } - - ConstLockedPtr& operator=(const ConstLockedPtr& rhs) { - if (parent_ != rhs.parent_) { - if (parent_) parent_->mutex_.unlock_shared(); - parent_ = rhs.parent_; - acquire(); - } - } - ~ConstLockedPtr() { - using namespace detail; - if (parent_) releaseRead(parent_->mutex_); - } - - const T* operator->() const { - return parent_ ? &parent_->datum_ : NULL; - } - - struct Unsynchronizer { - explicit Unsynchronizer(ConstLockedPtr* p) : parent_(p) { - using namespace detail; - releaseRead(parent_->parent_->mutex_); - } - Unsynchronizer(const Unsynchronizer&) = delete; - Unsynchronizer& operator=(const Unsynchronizer&) = delete; - ~Unsynchronizer() { - using namespace detail; - acquireRead(parent_->parent_->mutex_); - } - ConstLockedPtr* operator->() const { - return parent_; - } - private: - ConstLockedPtr* parent_; - }; - friend struct Unsynchronizer; - Unsynchronizer typeHackDoNotUse(); - - template - friend void lockInOrder(P1& p1, P2& p2); - - private: - void acquire() { - using namespace detail; - if (parent_) acquireRead(parent_->mutex_); - } - - const Synchronized* parent_; - }; - - /** - * This accessor offers a LockedPtr. In turn. LockedPtr offers - * operator-> returning a pointer to T. The operator-> keeps - * expanding until it reaches a pointer, so syncobj->foo() will lock - * the object and call foo() against it. - */ - LockedPtr operator->() { - return LockedPtr(this); - } - - /** - * Same, for constant objects. You will be able to invoke only const - * methods. - */ - ConstLockedPtr operator->() const { - return ConstLockedPtr(this); - } - - /** - * Attempts to acquire for a given number of milliseconds. If - * acquisition is unsuccessful, the returned LockedPtr is NULL. - */ - LockedPtr timedAcquire(unsigned int milliseconds) { - return LockedPtr(this, milliseconds); - } - - /** - * As above, for a constant object. - */ - ConstLockedPtr timedAcquire(unsigned int milliseconds) const { - return ConstLockedPtr(this, milliseconds); - } - - /** - * Used by SYNCHRONIZED_DUAL. - */ - LockedPtr internalDoNotUse() { - return LockedPtr(this, detail::InternalDoNotUse()); - } - - /** - * ditto - */ - ConstLockedPtr internalDoNotUse() const { - return ConstLockedPtr(this, detail::InternalDoNotUse()); - } - - /** - * Sometimes, although you have a mutable object, you only want to - * call a const method against it. The most efficient way to achieve - * that is by using a read lock. You get to do so by using - * obj.asConst()->method() instead of obj->method(). - */ - const Synchronized& asConst() const { - return *this; - } - - /** - * Swaps with another Synchronized. Protected against - * self-swap. Only data is swapped. Locks are acquired in increasing - * address order. - */ - void swap(Synchronized& rhs) { - if (this == &rhs) { - return; - } - if (this > &rhs) { - return rhs.swap(*this); - } - auto guard1 = operator->(); - auto guard2 = rhs.operator->(); - - using std::swap; - swap(datum_, rhs.datum_); - } - - /** - * Swap with another datum. Recommended because it keeps the mutex - * held only briefly. - */ - void swap(T& rhs) { - LockedPtr guard = operator->(); - - using std::swap; - swap(datum_, rhs); - } - - /** - * Copies datum to a given target. - */ - void copy(T* target) const { - ConstLockedPtr guard = operator->(); - *target = datum_; - } - - /** - * Returns a fresh copy of the datum. - */ - T copy() const { - ConstLockedPtr guard = operator->(); - return datum_; - } - -private: - T datum_; - mutable Mutex mutex_; -}; - -// Non-member swap primitive -template -void swap(Synchronized& lhs, Synchronized& rhs) { - lhs.swap(rhs); -} - -/** - * SYNCHRONIZED is the main facility that makes Synchronized - * helpful. It is a pseudo-statement that introduces a scope where the - * object is locked. Inside that scope you get to access the unadorned - * datum. - * - * Example: - * - * Synchronized> svector; - * ... - * SYNCHRONIZED (svector) { ... use svector as a vector ... } - * or - * SYNCHRONIZED (v, svector) { ... use v as a vector ... } - * - * Refer to folly/docs/Synchronized.md for a detailed explanation and more - * examples. - */ -#define SYNCHRONIZED(...) \ - if (bool SYNCHRONIZED_state = false) {} else \ - for (auto SYNCHRONIZED_lockedPtr = \ - (FB_ARG_2_OR_1(__VA_ARGS__)).operator->(); \ - !SYNCHRONIZED_state; SYNCHRONIZED_state = true) \ - for (auto& FB_ARG_1(__VA_ARGS__) = \ - *SYNCHRONIZED_lockedPtr.operator->(); \ - !SYNCHRONIZED_state; SYNCHRONIZED_state = true) - -#define TIMED_SYNCHRONIZED(timeout, ...) \ - if (bool SYNCHRONIZED_state = false) {} else \ - for (auto SYNCHRONIZED_lockedPtr = \ - (FB_ARG_2_OR_1(__VA_ARGS__)).timedAcquire(timeout); \ - !SYNCHRONIZED_state; SYNCHRONIZED_state = true) \ - for (auto FB_ARG_1(__VA_ARGS__) = \ - SYNCHRONIZED_lockedPtr.operator->(); \ - !SYNCHRONIZED_state; SYNCHRONIZED_state = true) - -/** - * Similar to SYNCHRONIZED, but only uses a read lock. - */ -#define SYNCHRONIZED_CONST(...) \ - SYNCHRONIZED(FB_ARG_1(__VA_ARGS__), \ - (FB_ARG_2_OR_1(__VA_ARGS__)).asConst()) - -/** - * Similar to TIMED_SYNCHRONIZED, but only uses a read lock. - */ -#define TIMED_SYNCHRONIZED_CONST(timeout, ...) \ - TIMED_SYNCHRONIZED(timeout, FB_ARG_1(__VA_ARGS__), \ - (FB_ARG_2_OR_1(__VA_ARGS__)).asConst()) - -/** - * Temporarily disables synchronization inside a SYNCHRONIZED block. - */ -#define UNSYNCHRONIZED(name) \ - for (decltype(SYNCHRONIZED_lockedPtr.typeHackDoNotUse()) \ - SYNCHRONIZED_state3(&SYNCHRONIZED_lockedPtr); \ - !SYNCHRONIZED_state; SYNCHRONIZED_state = true) \ - for (auto name = *SYNCHRONIZED_state3.operator->(); \ - !SYNCHRONIZED_state; SYNCHRONIZED_state = true) - -/** - * Locks two objects in increasing order of their addresses. - */ -template -void lockInOrder(P1& p1, P2& p2) { - if (static_cast(p1.operator->()) > - static_cast(p2.operator->())) { - p2.acquire(); - p1.acquire(); - } else { - p1.acquire(); - p2.acquire(); - } -} - -/** - * Synchronizes two Synchronized objects (they may encapsulate - * different data). Synchronization is done in increasing address of - * object order, so there is no deadlock risk. - */ -#define SYNCHRONIZED_DUAL(n1, e1, n2, e2) \ - if (bool SYNCHRONIZED_state = false) {} else \ - for (auto SYNCHRONIZED_lp1 = (e1).internalDoNotUse(); \ - !SYNCHRONIZED_state; SYNCHRONIZED_state = true) \ - for (auto& n1 = *SYNCHRONIZED_lp1.operator->(); \ - !SYNCHRONIZED_state; SYNCHRONIZED_state = true) \ - for (auto SYNCHRONIZED_lp2 = (e2).internalDoNotUse(); \ - !SYNCHRONIZED_state; SYNCHRONIZED_state = true) \ - for (auto& n2 = *SYNCHRONIZED_lp2.operator->(); \ - !SYNCHRONIZED_state; SYNCHRONIZED_state = true) \ - if ((::folly::lockInOrder( \ - SYNCHRONIZED_lp1, SYNCHRONIZED_lp2), \ - false)) {} \ - else - -} /* namespace folly */ - -#endif // SYNCHRONIZED_H_ diff --git a/hphp/third_party/folly/folly/ThreadCachedArena.cpp b/hphp/third_party/folly/folly/ThreadCachedArena.cpp deleted file mode 100644 index 3c09cb5ae..000000000 --- a/hphp/third_party/folly/folly/ThreadCachedArena.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#include "folly/ThreadCachedArena.h" - -namespace folly { - -ThreadCachedArena::ThreadCachedArena(size_t minBlockSize) - : minBlockSize_(minBlockSize) { -} - -SysArena* ThreadCachedArena::allocateThreadLocalArena() { - SysArena* arena = new SysArena(minBlockSize_); - auto disposer = [this] (SysArena* t, TLPDestructionMode mode) { - std::unique_ptr tp(t); // ensure it gets deleted - if (mode == TLPDestructionMode::THIS_THREAD) { - zombify(std::move(*t)); - } - }; - arena_.reset(arena, disposer); - return arena; -} - -void ThreadCachedArena::zombify(SysArena&& arena) { - std::lock_guard lock(zombiesMutex_); - zombies_.merge(std::move(arena)); -} - -} // namespace folly - diff --git a/hphp/third_party/folly/folly/ThreadCachedArena.h b/hphp/third_party/folly/folly/ThreadCachedArena.h deleted file mode 100644 index 8e9f0aef7..000000000 --- a/hphp/third_party/folly/folly/ThreadCachedArena.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_THREADCACHEDARENA_H_ -#define FOLLY_THREADCACHEDARENA_H_ - -#include -#include -#include -#include - -#include "folly/Likely.h" -#include "folly/Arena.h" -#include "folly/ThreadLocal.h" - -namespace folly { - -/** - * Thread-caching arena: allocate memory which gets freed when the arena gets - * destroyed. - * - * The arena itself allocates memory using malloc() in blocks of - * at least minBlockSize bytes. - * - * For speed, each thread gets its own Arena (see Arena.h); when threads - * exit, the Arena gets merged into a "zombie" Arena, which will be deallocated - * when the ThreadCachedArena object is destroyed. - */ -class ThreadCachedArena { - public: - explicit ThreadCachedArena( - size_t minBlockSize = SysArena::kDefaultMinBlockSize); - - void* allocate(size_t size) { - SysArena* arena = arena_.get(); - if (UNLIKELY(!arena)) { - arena = allocateThreadLocalArena(); - } - - return arena->allocate(size); - } - - void deallocate(void* p) { - // Deallocate? Never! - } - - private: - ThreadCachedArena(const ThreadCachedArena&) = delete; - ThreadCachedArena(ThreadCachedArena&&) = delete; - ThreadCachedArena& operator=(const ThreadCachedArena&) = delete; - ThreadCachedArena& operator=(ThreadCachedArena&&) = delete; - - SysArena* allocateThreadLocalArena(); - - // Zombify the blocks in arena, saving them for deallocation until - // the ThreadCachedArena is destroyed. - void zombify(SysArena&& arena); - - size_t minBlockSize_; - SysArena zombies_; // allocated from threads that are now dead - std::mutex zombiesMutex_; - ThreadLocalPtr arena_; // per-thread arena -}; - -} // namespace folly - -#endif /* FOLLY_THREADCACHEDARENA_H_ */ - diff --git a/hphp/third_party/folly/folly/ThreadCachedInt.h b/hphp/third_party/folly/folly/ThreadCachedInt.h deleted file mode 100644 index 69c03d1f0..000000000 --- a/hphp/third_party/folly/folly/ThreadCachedInt.h +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2013 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. - */ - -/** - * Higher performance (up to 10x) atomic increment using thread caching. - * - * @author Spencer Ahrens (sahrens) - */ - -#ifndef FOLLY_THREADCACHEDINT_H -#define FOLLY_THREADCACHEDINT_H - -#include -#include "folly/Likely.h" -#include "folly/ThreadLocal.h" - -namespace folly { - - -// Note that readFull requires holding a lock and iterating through all of the -// thread local objects with the same Tag, so if you have a lot of -// ThreadCachedInt's you should considering breaking up the Tag space even -// further. -template -class ThreadCachedInt : boost::noncopyable { - struct IntCache; - - public: - explicit ThreadCachedInt(IntT initialVal = 0, uint32_t cacheSize = 1000) - : target_(initialVal), cacheSize_(cacheSize) { - } - - void increment(IntT inc) { - auto cache = cache_.get(); - if (UNLIKELY(cache == NULL || cache->parent_ == NULL)) { - cache = new IntCache(*this); - cache_.reset(cache); - } - cache->increment(inc); - } - - // Quickly grabs the current value which may not include some cached - // increments. - IntT readFast() const { - return target_.load(std::memory_order_relaxed); - } - - // Reads the current value plus all the cached increments. Requires grabbing - // a lock, so this is significantly slower than readFast(). - IntT readFull() const { - IntT ret = readFast(); - for (const auto& cache : cache_.accessAllThreads()) { - if (!cache.reset_.load(std::memory_order_acquire)) { - ret += cache.val_.load(std::memory_order_relaxed); - } - } - return ret; - } - - // Quickly reads and resets current value (doesn't reset cached increments). - IntT readFastAndReset() { - return target_.exchange(0, std::memory_order_release); - } - - // This function is designed for accumulating into another counter, where you - // only want to count each increment once. It can still get the count a - // little off, however, but it should be much better than calling readFull() - // and set(0) sequentially. - IntT readFullAndReset() { - IntT ret = readFastAndReset(); - for (auto& cache : cache_.accessAllThreads()) { - if (!cache.reset_.load(std::memory_order_acquire)) { - ret += cache.val_.load(std::memory_order_relaxed); - cache.reset_.store(true, std::memory_order_release); - } - } - return ret; - } - - void setCacheSize(uint32_t newSize) { - cacheSize_.store(newSize, std::memory_order_release); - } - - uint32_t getCacheSize() const { - return cacheSize_.load(); - } - - ThreadCachedInt& operator+=(IntT inc) { increment(inc); return *this; } - ThreadCachedInt& operator-=(IntT inc) { increment(-inc); return *this; } - // pre-increment (we don't support post-increment) - ThreadCachedInt& operator++() { increment(1); return *this; } - ThreadCachedInt& operator--() { increment(-1); return *this; } - - // Thread-safe set function. - // This is a best effort implementation. In some edge cases, there could be - // data loss (missing counts) - void set(IntT newVal) { - for (auto& cache : cache_.accessAllThreads()) { - cache.reset_.store(true, std::memory_order_release); - } - target_.store(newVal, std::memory_order_release); - } - - // This is a little tricky - it's possible that our IntCaches are still alive - // in another thread and will get destroyed after this destructor runs, so we - // need to make sure we signal that this parent is dead. - ~ThreadCachedInt() { - for (auto& cache : cache_.accessAllThreads()) { - cache.parent_ = NULL; - } - } - - private: - std::atomic target_; - std::atomic cacheSize_; - ThreadLocalPtr cache_; // Must be last for dtor ordering - - // This should only ever be modified by one thread - struct IntCache { - ThreadCachedInt* parent_; - mutable std::atomic val_; - mutable uint32_t numUpdates_; - std::atomic reset_; - - explicit IntCache(ThreadCachedInt& parent) - : parent_(&parent), val_(0), numUpdates_(0), reset_(false) {} - - void increment(IntT inc) { - if (LIKELY(!reset_.load(std::memory_order_acquire))) { - // This thread is the only writer to val_, so it's fine do do - // a relaxed load and do the addition non-atomically. - val_.store( - val_.load(std::memory_order_relaxed) + inc, - std::memory_order_release - ); - } else { - val_.store(inc, std::memory_order_relaxed); - reset_.store(false, std::memory_order_release); - } - ++numUpdates_; - if (UNLIKELY(numUpdates_ > - parent_->cacheSize_.load(std::memory_order_acquire))) { - flush(); - } - } - - void flush() const { - parent_->target_.fetch_add(val_, std::memory_order_release); - val_.store(0, std::memory_order_release); - numUpdates_ = 0; - } - - ~IntCache() { - if (parent_) { - flush(); - } - } - }; -}; - -} - -#endif diff --git a/hphp/third_party/folly/folly/ThreadLocal.h b/hphp/third_party/folly/folly/ThreadLocal.h deleted file mode 100644 index b4c031e75..000000000 --- a/hphp/third_party/folly/folly/ThreadLocal.h +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Copyright 2013 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. - */ - -/** - * Improved thread local storage for non-trivial types (similar speed as - * pthread_getspecific but only consumes a single pthread_key_t, and 4x faster - * than boost::thread_specific_ptr). - * - * Also includes an accessor interface to walk all the thread local child - * objects of a parent. accessAllThreads() initializes an accessor which holds - * a global lock *that blocks all creation and destruction of ThreadLocal - * objects with the same Tag* and can be used as an iterable container. - * - * Intended use is for frequent write, infrequent read data access patterns such - * as counters. - * - * There are two classes here - ThreadLocal and ThreadLocalPtr. ThreadLocalPtr - * has semantics similar to boost::thread_specific_ptr. ThreadLocal is a thin - * wrapper around ThreadLocalPtr that manages allocation automatically. - * - * @author Spencer Ahrens (sahrens) - */ - -#ifndef FOLLY_THREADLOCAL_H_ -#define FOLLY_THREADLOCAL_H_ - -#include "folly/Portability.h" -#include -#include "folly/Likely.h" -#include - - -namespace folly { -enum class TLPDestructionMode { - THIS_THREAD, - ALL_THREADS -}; -} // namespace - -#include "folly/detail/ThreadLocalDetail.h" - -namespace folly { - -template class ThreadLocalPtr; - -template -class ThreadLocal { - public: - ThreadLocal() { } - - T* get() const { - T* ptr = tlp_.get(); - if (LIKELY(ptr != nullptr)) { - return ptr; - } - - // separated new item creation out to speed up the fast path. - return makeTlp(); - } - - T* operator->() const { - return get(); - } - - T& operator*() const { - return *get(); - } - - void reset(T* newPtr = nullptr) { - tlp_.reset(newPtr); - } - - typedef typename ThreadLocalPtr::Accessor Accessor; - Accessor accessAllThreads() const { - return tlp_.accessAllThreads(); - } - - // movable - ThreadLocal(ThreadLocal&&) = default; - ThreadLocal& operator=(ThreadLocal&&) = default; - - private: - // non-copyable - ThreadLocal(const ThreadLocal&) = delete; - ThreadLocal& operator=(const ThreadLocal&) = delete; - - T* makeTlp() const { - T* ptr = new T(); - tlp_.reset(ptr); - return ptr; - } - - mutable ThreadLocalPtr tlp_; -}; - -/* - * The idea here is that __thread is faster than pthread_getspecific, so we - * keep a __thread array of pointers to objects (ThreadEntry::elements) where - * each array has an index for each unique instance of the ThreadLocalPtr - * object. Each ThreadLocalPtr object has a unique id that is an index into - * these arrays so we can fetch the correct object from thread local storage - * very efficiently. - * - * In order to prevent unbounded growth of the id space and thus huge - * ThreadEntry::elements, arrays, for example due to continuous creation and - * destruction of ThreadLocalPtr objects, we keep a set of all active - * instances. When an instance is destroyed we remove it from the active - * set and insert the id into freeIds_ for reuse. These operations require a - * global mutex, but only happen at construction and destruction time. - * - * We use a single global pthread_key_t per Tag to manage object destruction and - * memory cleanup upon thread exit because there is a finite number of - * pthread_key_t's available per machine. - */ - -template -class ThreadLocalPtr { - public: - ThreadLocalPtr() : id_(threadlocal_detail::StaticMeta::create()) { } - - ThreadLocalPtr(ThreadLocalPtr&& other) : id_(other.id_) { - other.id_ = 0; - } - - ThreadLocalPtr& operator=(ThreadLocalPtr&& other) { - assert(this != &other); - destroy(); - id_ = other.id_; - other.id_ = 0; - return *this; - } - - ~ThreadLocalPtr() { - destroy(); - } - - T* get() const { - return static_cast(threadlocal_detail::StaticMeta::get(id_).ptr); - } - - T* operator->() const { - return get(); - } - - T& operator*() const { - return *get(); - } - - void reset(T* newPtr = nullptr) { - threadlocal_detail::ElementWrapper& w = - threadlocal_detail::StaticMeta::get(id_); - if (w.ptr != newPtr) { - w.dispose(TLPDestructionMode::THIS_THREAD); - w.set(newPtr); - } - } - - explicit operator bool() const { - return get() != nullptr; - } - - /** - * reset() with a custom deleter: - * deleter(T* ptr, TLPDestructionMode mode) - * "mode" is ALL_THREADS if we're destructing this ThreadLocalPtr (and thus - * deleting pointers for all threads), and THIS_THREAD if we're only deleting - * the member for one thread (because of thread exit or reset()) - */ - template - void reset(T* newPtr, Deleter deleter) { - threadlocal_detail::ElementWrapper& w = - threadlocal_detail::StaticMeta::get(id_); - if (w.ptr != newPtr) { - w.dispose(TLPDestructionMode::THIS_THREAD); - w.set(newPtr, deleter); - } - } - - // Holds a global lock for iteration through all thread local child objects. - // Can be used as an iterable container. - // Use accessAllThreads() to obtain one. - class Accessor { - friend class ThreadLocalPtr; - - threadlocal_detail::StaticMeta& meta_; - boost::mutex* lock_; - int id_; - - public: - class Iterator; - friend class Iterator; - - // The iterators obtained from Accessor are bidirectional iterators. - class Iterator : public boost::iterator_facade< - Iterator, // Derived - T, // value_type - boost::bidirectional_traversal_tag> { // traversal - friend class Accessor; - friend class boost::iterator_core_access; - const Accessor* const accessor_; - threadlocal_detail::ThreadEntry* e_; - - void increment() { - e_ = e_->next; - incrementToValid(); - } - - void decrement() { - e_ = e_->prev; - decrementToValid(); - } - - T& dereference() const { - return *static_cast(e_->elements[accessor_->id_].ptr); - } - - bool equal(const Iterator& other) const { - return (accessor_->id_ == other.accessor_->id_ && - e_ == other.e_); - } - - explicit Iterator(const Accessor* accessor) - : accessor_(accessor), - e_(&accessor_->meta_.head_) { - } - - bool valid() const { - return (e_->elements && - accessor_->id_ < e_->elementsCapacity && - e_->elements[accessor_->id_].ptr); - } - - void incrementToValid() { - for (; e_ != &accessor_->meta_.head_ && !valid(); e_ = e_->next) { } - } - - void decrementToValid() { - for (; e_ != &accessor_->meta_.head_ && !valid(); e_ = e_->prev) { } - } - }; - - ~Accessor() { - release(); - } - - Iterator begin() const { - return ++Iterator(this); - } - - Iterator end() const { - return Iterator(this); - } - - Accessor(const Accessor&) = delete; - Accessor& operator=(const Accessor&) = delete; - - Accessor(Accessor&& other) noexcept - : meta_(other.meta_), - lock_(other.lock_), - id_(other.id_) { - other.id_ = 0; - other.lock_ = nullptr; - } - - Accessor& operator=(Accessor&& other) noexcept { - // Each Tag has its own unique meta, and accessors with different Tags - // have different types. So either *this is empty, or this and other - // have the same tag. But if they have the same tag, they have the same - // meta (and lock), so they'd both hold the lock at the same time, - // which is impossible, which leaves only one possible scenario -- - // *this is empty. Assert it. - assert(&meta_ == &other.meta_); - assert(lock_ == nullptr); - using std::swap; - swap(lock_, other.lock_); - swap(id_, other.id_); - } - - Accessor() - : meta_(threadlocal_detail::StaticMeta::instance()), - lock_(nullptr), - id_(0) { - } - - private: - explicit Accessor(int id) - : meta_(threadlocal_detail::StaticMeta::instance()), - lock_(&meta_.lock_) { - lock_->lock(); - id_ = id; - } - - void release() { - if (lock_) { - lock_->unlock(); - id_ = 0; - lock_ = nullptr; - } - } - }; - - // accessor allows a client to iterate through all thread local child - // elements of this ThreadLocal instance. Holds a global lock for each - Accessor accessAllThreads() const { - static_assert(!std::is_same::value, - "Must use a unique Tag to use the accessAllThreads feature"); - return Accessor(id_); - } - - private: - void destroy() { - if (id_) { - threadlocal_detail::StaticMeta::destroy(id_); - } - } - - // non-copyable - ThreadLocalPtr(const ThreadLocalPtr&) = delete; - ThreadLocalPtr& operator=(const ThreadLocalPtr&) = delete; - - int id_; // every instantiation has a unique id -}; - -} // namespace folly - -#endif /* FOLLY_THREADLOCAL_H_ */ diff --git a/hphp/third_party/folly/folly/TimeoutQueue.cpp b/hphp/third_party/folly/folly/TimeoutQueue.cpp deleted file mode 100644 index 28621860a..000000000 --- a/hphp/third_party/folly/folly/TimeoutQueue.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#include "folly/TimeoutQueue.h" -#include - -namespace folly { - -TimeoutQueue::Id TimeoutQueue::add( - int64_t now, - int64_t delay, - Callback callback) { - Id id = nextId_++; - timeouts_.insert({id, now + delay, -1, std::move(callback)}); - return id; -} - -TimeoutQueue::Id TimeoutQueue::addRepeating( - int64_t now, - int64_t interval, - Callback callback) { - Id id = nextId_++; - timeouts_.insert({id, now + interval, interval, std::move(callback)}); - return id; -} - -int64_t TimeoutQueue::nextExpiration() const { - return (timeouts_.empty() ? std::numeric_limits::max() : - timeouts_.get().begin()->expiration); -} - -bool TimeoutQueue::erase(Id id) { - return timeouts_.get().erase(id); -} - -int64_t TimeoutQueue::runInternal(int64_t now, bool onceOnly) { - auto& byExpiration = timeouts_.get(); - int64_t nextExp; - do { - auto end = byExpiration.upper_bound(now); - std::vector expired; - std::move(byExpiration.begin(), end, std::back_inserter(expired)); - byExpiration.erase(byExpiration.begin(), end); - for (auto& event : expired) { - // Reinsert if repeating, do this before executing callbacks - // so the callbacks have a chance to call erase - if (event.repeatInterval >= 0) { - timeouts_.insert({event.id, now + event.repeatInterval, - event.repeatInterval, event.callback}); - } - } - - // Call callbacks - for (auto& event : expired) { - event.callback(event.id, now); - } - nextExp = nextExpiration(); - } while (!onceOnly && nextExp <= now); - return nextExp; -} - -} // namespace folly - diff --git a/hphp/third_party/folly/folly/TimeoutQueue.h b/hphp/third_party/folly/folly/TimeoutQueue.h deleted file mode 100644 index 6473cbbcb..000000000 --- a/hphp/third_party/folly/folly/TimeoutQueue.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2013 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. - */ - -/** - * Simple timeout queue. Call user-specified callbacks when their timeouts - * expire. - * - * This class assumes that "time" is an int64_t and doesn't care about time - * units (seconds, milliseconds, etc). You call runOnce() / runLoop() using - * the same time units that you use to specify callbacks. - * - * @author Tudor Bosman (tudorb@fb.com) - */ - -#ifndef FOLLY_TIMEOUTQUEUE_H_ -#define FOLLY_TIMEOUTQUEUE_H_ - -#include -#include -#include -#include -#include -#include - -namespace folly { - -class TimeoutQueue { - public: - typedef int64_t Id; - typedef std::function Callback; - - TimeoutQueue() : nextId_(1) { } - - /** - * Add a one-time timeout event that will fire "delay" time units from "now" - * (that is, the first time that run*() is called with a time value >= now - * + delay). - */ - Id add(int64_t now, int64_t delay, Callback callback); - - /** - * Add a repeating timeout event that will fire every "interval" time units - * (it will first fire when run*() is called with a time value >= - * now + interval). - * - * run*() will always invoke each repeating event at most once, even if - * more than one "interval" period has passed. - */ - Id addRepeating(int64_t now, int64_t interval, Callback callback); - - /** - * Erase a given timeout event, returns true if the event was actually - * erased and false if it didn't exist in our queue. - */ - bool erase(Id id); - - /** - * Process all events that are due at times <= "now" by calling their - * callbacks. - * - * Callbacks are allowed to call back into the queue and add / erase events; - * they might create more events that are already due. In this case, - * runOnce() will only go through the queue once, and return a "next - * expiration" time in the past or present (<= now); runLoop() - * will process the queue again, until there are no events already due. - * - * Note that it is then possible for runLoop to never return if - * callbacks re-add themselves to the queue (or if you have repeating - * callbacks with an interval of 0). - * - * Return the time that the next event will be due (same as - * nextExpiration(), below) - */ - int64_t runOnce(int64_t now) { return runInternal(now, true); } - int64_t runLoop(int64_t now) { return runInternal(now, false); } - - /** - * Return the time that the next event will be due. - */ - int64_t nextExpiration() const; - - private: - int64_t runInternal(int64_t now, bool runOnce); - // noncopyable - TimeoutQueue(const TimeoutQueue&) = delete; - TimeoutQueue& operator=(const TimeoutQueue&) = delete; - - struct Event { - Id id; - int64_t expiration; - int64_t repeatInterval; - Callback callback; - }; - - typedef boost::multi_index_container< - Event, - boost::multi_index::indexed_by< - boost::multi_index::ordered_unique>, - boost::multi_index::ordered_non_unique> - > - > Set; - - enum { - BY_ID=0, - BY_EXPIRATION=1 - }; - - Set timeouts_; - Id nextId_; -}; - -} // namespace folly - -#endif /* FOLLY_TIMEOUTQUEUE_H_ */ - diff --git a/hphp/third_party/folly/folly/Traits.h b/hphp/third_party/folly/folly/Traits.h deleted file mode 100644 index 37bd58d38..000000000 --- a/hphp/third_party/folly/folly/Traits.h +++ /dev/null @@ -1,502 +0,0 @@ -/* - * Copyright 2013 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. - */ - -// @author: Andrei Alexandrescu - -#ifndef FOLLY_BASE_TRAITS_H_ -#define FOLLY_BASE_TRAITS_H_ - -#include -#include -#include - -#include - -#include -#include -#include -#include - -namespace folly { - -/** - * IsRelocatable::value describes the ability of moving around - * memory a value of type T by using memcpy (as opposed to the - * conservative approach of calling the copy constructor and then - * destroying the old temporary. Essentially for a relocatable type, - * the following two sequences of code should be semantically - * equivalent: - * - * void move1(T * from, T * to) { - * new(to) T(from); - * (*from).~T(); - * } - * - * void move2(T * from, T * to) { - * memcpy(to, from, sizeof(T)); - * } - * - * Most C++ types are relocatable; the ones that aren't would include - * internal pointers or (very rarely) would need to update remote - * pointers to pointers tracking them. All C++ primitive types and - * type constructors are relocatable. - * - * This property can be used in a variety of optimizations. Currently - * fbvector uses this property intensively. - * - * The default conservatively assumes the type is not - * relocatable. Several specializations are defined for known - * types. You may want to add your own specializations. Do so in - * namespace folly and make sure you keep the specialization of - * IsRelocatable in the same header as SomeStruct. - * - * You may also declare a type to be relocatable by including - * `typedef std::true_type IsRelocatable;` - * in the class header. - * - * It may be unset in a base class by overriding the typedef to false_type. - */ -/* - * IsTriviallyCopyable describes the value semantics property. C++11 contains - * the type trait is_trivially_copyable; however, it is not yet implemented - * in gcc (as of 4.7.1), and the user may wish to specify otherwise. - */ -/* - * IsZeroInitializable describes the property that default construction is the - * same as memset(dst, 0, sizeof(T)). - */ - -namespace traits_detail { - -#define FOLLY_HAS_TRUE_XXX(name) \ - BOOST_MPL_HAS_XXX_TRAIT_DEF(name); \ - template struct name ## _is_true \ - : std::is_same {}; \ - template struct has_true_ ## name \ - : std::conditional< \ - has_ ## name ::value, \ - name ## _is_true, \ - std::false_type \ - >:: type {}; - -FOLLY_HAS_TRUE_XXX(IsRelocatable) -FOLLY_HAS_TRUE_XXX(IsZeroInitializable) -FOLLY_HAS_TRUE_XXX(IsTriviallyCopyable) - -#undef FOLLY_HAS_TRUE_XXX -} - -template struct IsTriviallyCopyable - : std::integral_constant::value || - // TODO: add alternate clause is_trivially_copyable, when available - traits_detail::has_true_IsTriviallyCopyable::value - > {}; - -template struct IsRelocatable - : std::integral_constant::value || - // TODO add this line (and some tests for it) when we upgrade to gcc 4.7 - //std::is_trivially_move_constructible::value || - IsTriviallyCopyable::value || - traits_detail::has_true_IsRelocatable::value - > {}; - -template struct IsZeroInitializable - : std::integral_constant::value || - traits_detail::has_true_IsZeroInitializable::value - > {}; - -} // namespace folly - -/** - * Use this macro ONLY inside namespace folly. When using it with a - * regular type, use it like this: - * - * // Make sure you're at namespace ::folly scope - * template<> FOLLY_ASSUME_RELOCATABLE(MyType) - * - * When using it with a template type, use it like this: - * - * // Make sure you're at namespace ::folly scope - * template - * FOLLY_ASSUME_RELOCATABLE(MyType) - */ -#define FOLLY_ASSUME_RELOCATABLE(...) \ - struct IsRelocatable< __VA_ARGS__ > : std::true_type {}; - -/** - * Use this macro ONLY inside namespace boost. When using it with a - * regular type, use it like this: - * - * // Make sure you're at namespace ::boost scope - * template<> FOLLY_ASSUME_HAS_NOTHROW_CONSTRUCTOR(MyType) - * - * When using it with a template type, use it like this: - * - * // Make sure you're at namespace ::boost scope - * template - * FOLLY_ASSUME_HAS_NOTHROW_CONSTRUCTOR(MyType) - */ -#define FOLLY_ASSUME_HAS_NOTHROW_CONSTRUCTOR(...) \ - struct has_nothrow_constructor< __VA_ARGS__ > : ::boost::true_type {}; - -/** - * The FOLLY_ASSUME_FBVECTOR_COMPATIBLE* macros below encode two - * assumptions: first, that the type is relocatable per IsRelocatable - * above, and that it has a nothrow constructor. Most types can be - * assumed to satisfy both conditions, but it is the responsibility of - * the user to state that assumption. User-defined classes will not - * work with fbvector (see FBVector.h) unless they state this - * combination of properties. - * - * Use FOLLY_ASSUME_FBVECTOR_COMPATIBLE with regular types like this: - * - * FOLLY_ASSUME_FBVECTOR_COMPATIBLE(MyType) - * - * The versions FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1, _2, _3, and _4 - * allow using the macro for describing templatized classes with 1, 2, - * 3, and 4 template parameters respectively. For template classes - * just use the macro with the appropriate number and pass the name of - * the template to it. Example: - * - * template class MyType { ... }; - * ... - * // Make sure you're at global scope - * FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(MyType) - */ - -// Use this macro ONLY at global level (no namespace) -#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE(...) \ - namespace folly { template<> FOLLY_ASSUME_RELOCATABLE(__VA_ARGS__) } \ - namespace boost { \ - template<> FOLLY_ASSUME_HAS_NOTHROW_CONSTRUCTOR(__VA_ARGS__) } -// Use this macro ONLY at global level (no namespace) -#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1(...) \ - namespace folly { \ - template FOLLY_ASSUME_RELOCATABLE(__VA_ARGS__) } \ - namespace boost { \ - template FOLLY_ASSUME_HAS_NOTHROW_CONSTRUCTOR(__VA_ARGS__) } -// Use this macro ONLY at global level (no namespace) -#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(...) \ - namespace folly { \ - template \ - FOLLY_ASSUME_RELOCATABLE(__VA_ARGS__) } \ - namespace boost { \ - template \ - FOLLY_ASSUME_HAS_NOTHROW_CONSTRUCTOR(__VA_ARGS__) } -// Use this macro ONLY at global level (no namespace) -#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE_3(...) \ - namespace folly { \ - template \ - FOLLY_ASSUME_RELOCATABLE(__VA_ARGS__) } \ - namespace boost { \ - template \ - FOLLY_ASSUME_HAS_NOTHROW_CONSTRUCTOR(__VA_ARGS__) } -// Use this macro ONLY at global level (no namespace) -#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE_4(...) \ - namespace folly { \ - template \ - FOLLY_ASSUME_RELOCATABLE(__VA_ARGS__) } \ - namespace boost { \ - template \ - FOLLY_ASSUME_HAS_NOTHROW_CONSTRUCTOR(__VA_ARGS__) } - -/** - * Instantiate FOLLY_ASSUME_FBVECTOR_COMPATIBLE for a few types. It is - * safe to assume that pair is compatible if both of its components - * are. Furthermore, all STL containers can be assumed to comply, - * although that is not guaranteed by the standard. - */ - -namespace std { - -template - struct pair; -#ifndef _GLIBCXX_USE_FB -template - class basic_string; -#else -template - class basic_string; -#endif -template - class vector; -template - class deque; -template - class list; -template - class set; -template - class map; -template - class shared_ptr; - -} - -namespace boost { - -template class shared_ptr; - -template -struct has_nothrow_constructor< std::pair > - : ::boost::mpl::and_< has_nothrow_constructor, - has_nothrow_constructor > {}; - -} // namespace boost - -namespace folly { - -// STL commonly-used types -template -struct IsRelocatable< std::pair > - : ::boost::mpl::and_< IsRelocatable, IsRelocatable > {}; - -// Is T one of T1, T2, ..., Tn? -template -struct IsOneOf { - enum { value = false }; -}; - -template -struct IsOneOf { - enum { value = std::is_same::value || IsOneOf::value }; -}; - -/** - * A traits class to check for incomplete types. - * - * Example: - * - * struct FullyDeclared {}; // complete type - * struct ForwardDeclared; // incomplete type - * - * is_complete::value // evaluates to true - * is_complete::value // evaluates to true - * is_complete::value // evaluates to false - * - * struct ForwardDeclared {}; // declared, at last - * - * is_complete::value // now it evaluates to true - * - * @author: Marcelo Juchem - */ -template -class is_complete { - template struct sfinae {}; - template - constexpr static bool test(sfinae*) { return true; } - template constexpr static bool test(...) { return false; } -public: - constexpr static bool value = test(nullptr); -}; - -/* - * Complementary type traits for integral comparisons. - * - * For instance, `if(x < 0)` yields an error in clang for unsigned types - * when -Werror is used due to -Wtautological-compare - * - * - * @author: Marcelo Juchem - */ - -namespace detail { - -template -struct is_negative_impl { - constexpr static bool check(T x) { return x < 0; } -}; - -template -struct is_negative_impl { - constexpr static bool check(T x) { return false; } -}; - -template -bool less_than_impl( - typename std::enable_if< - (rhs <= std::numeric_limits::max() - && rhs > std::numeric_limits::min()), - LHS - >::type const lhs -) { - return lhs < rhs; -} - -template -bool less_than_impl( - typename std::enable_if< - (rhs > std::numeric_limits::max()), - LHS - >::type const -) { - return true; -} - -template -bool less_than_impl( - typename std::enable_if< - (rhs <= std::numeric_limits::min()), - LHS - >::type const -) { - return false; -} - -template -bool greater_than_impl( - typename std::enable_if< - (rhs <= std::numeric_limits::max() - && rhs >= std::numeric_limits::min()), - LHS - >::type const lhs -) { - return lhs > rhs; -} - -template -bool greater_than_impl( - typename std::enable_if< - (rhs > std::numeric_limits::max()), - LHS - >::type const -) { - return false; -} - -template -bool greater_than_impl( - typename std::enable_if< - (rhs < std::numeric_limits::min()), - LHS - >::type const -) { - return true; -} - -} // namespace detail { - -// same as `x < 0` -template -constexpr bool is_negative(T x) { - return folly::detail::is_negative_impl::value>::check(x); -} - -// same as `x <= 0` -template -constexpr bool is_non_positive(T x) { return !x || folly::is_negative(x); } - -template -bool less_than(LHS const lhs) { - return detail::less_than_impl< - RHS, rhs, typename std::remove_reference::type - >(lhs); -} - -template -bool greater_than(LHS const lhs) { - return detail::greater_than_impl< - RHS, rhs, typename std::remove_reference::type - >(lhs); -} - -} // namespace folly - -FOLLY_ASSUME_FBVECTOR_COMPATIBLE_3(std::basic_string); -FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(std::vector); -FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(std::list); -FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(std::deque); -FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(std::unique_ptr); -FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1(std::shared_ptr); -FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1(std::function); - -// Boost -FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1(boost::shared_ptr); - -#define FOLLY_CREATE_HAS_MEMBER_FN_TRAITS_IMPL(classname, func_name, cv_qual) \ - template \ - class classname { \ - template < \ - typename UTheClass_, RTheReturn_ (UTheClass_::*)(TTheArgs_...) cv_qual \ - > struct sfinae {}; \ - template \ - constexpr static bool test(sfinae*) \ - { return true; } \ - template \ - constexpr static bool test(...) { return false; } \ - public: \ - constexpr static bool value = test(nullptr); \ - } - -/* - * The FOLLY_CREATE_HAS_MEMBER_FN_TRAITS is used to create traits - * classes that check for the existence of a member function with - * a given name and signature. It currently does not support - * checking for inherited members. - * - * Such classes receive two template parameters: the class to be checked - * and the signature of the member function. A static boolean field - * named `value` (which is also constexpr) tells whether such member - * function exists. - * - * Each traits class created is bound only to the member name, not to - * its signature nor to the type of the class containing it. - * - * Say you need to know if a given class has a member function named - * `test` with the following signature: - * - * int test() const; - * - * You'd need this macro to create a traits class to check for a member - * named `test`, and then use this traits class to check for the signature: - * - * namespace { - * - * FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(has_test_traits, test); - * - * } // unnamed-namespace - * - * void some_func() { - * cout << "Does class Foo have a member int test() const? " - * << boolalpha << has_test_traits::value; - * } - * - * You can use the same traits class to test for a completely different - * signature, on a completely different class, as long as the member name - * is the same: - * - * void some_func() { - * cout << "Does class Foo have a member int test()? " - * << boolalpha << has_test_traits::value; - * cout << "Does class Foo have a member int test() const? " - * << boolalpha << has_test_traits::value; - * cout << "Does class Bar have a member double test(const string&, long)? " - * << boolalpha << has_test_traits::value; - * } - * - * @author: Marcelo Juchem - */ -#define FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(classname, func_name) \ - template class classname; \ - FOLLY_CREATE_HAS_MEMBER_FN_TRAITS_IMPL(classname, func_name, ); \ - FOLLY_CREATE_HAS_MEMBER_FN_TRAITS_IMPL(classname, func_name, const); \ - FOLLY_CREATE_HAS_MEMBER_FN_TRAITS_IMPL(classname, func_name, volatile); \ - FOLLY_CREATE_HAS_MEMBER_FN_TRAITS_IMPL(classname, func_name, volatile const) - -#endif //FOLLY_BASE_TRAITS_H_ diff --git a/hphp/third_party/folly/folly/Unicode.cpp b/hphp/third_party/folly/folly/Unicode.cpp deleted file mode 100644 index e71cbe1cf..000000000 --- a/hphp/third_party/folly/folly/Unicode.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#include "folly/Unicode.h" - -namespace folly { - -////////////////////////////////////////////////////////////////////// - -fbstring codePointToUtf8(char32_t cp) { - fbstring result; - - // Based on description from http://en.wikipedia.org/wiki/UTF-8. - - if (cp <= 0x7f) { - result.resize(1); - result[0] = static_cast(cp); - } else if (cp <= 0x7FF) { - result.resize(2); - result[1] = static_cast(0x80 | (0x3f & cp)); - result[0] = static_cast(0xC0 | (cp >> 6)); - } else if (cp <= 0xFFFF) { - result.resize(3); - result[2] = static_cast(0x80 | (0x3f & cp)); - result[1] = (0x80 | static_cast((0x3f & (cp >> 6)))); - result[0] = (0xE0 | static_cast(cp >> 12)); - } else if (cp <= 0x10FFFF) { - result.resize(4); - result[3] = static_cast(0x80 | (0x3f & cp)); - result[2] = static_cast(0x80 | (0x3f & (cp >> 6))); - result[1] = static_cast(0x80 | (0x3f & (cp >> 12))); - result[0] = static_cast(0xF0 | (cp >> 18)); - } - - return result; -} - -////////////////////////////////////////////////////////////////////// - -} - diff --git a/hphp/third_party/folly/folly/Unicode.h b/hphp/third_party/folly/folly/Unicode.h deleted file mode 100644 index d88aa7147..000000000 --- a/hphp/third_party/folly/folly/Unicode.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2013 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. - */ - -// Some utility routines relating to unicode. - -#ifndef FOLLY_UNICODE_H_ -#define FOLLY_UNICODE_H_ - -#include "folly/FBString.h" - -namespace folly { - -////////////////////////////////////////////////////////////////////// - -/* - * Encode a single unicode code point into a UTF-8 byte sequence. - * - * Return value is undefined if `cp' is an invalid code point. - */ -fbstring codePointToUtf8(char32_t cp); - -////////////////////////////////////////////////////////////////////// - -} - -#endif diff --git a/hphp/third_party/folly/folly/Uri-inl.h b/hphp/third_party/folly/folly/Uri-inl.h deleted file mode 100644 index 71a23cbbd..000000000 --- a/hphp/third_party/folly/folly/Uri-inl.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_URI_H_ -#error This file may only be included from folly/Uri.h -#endif - -#include "folly/Conv.h" - -namespace folly { - -template -String Uri::toString() const { - String str; - toAppend(scheme_, "://", &str); - if (!password_.empty()) { - toAppend(username_, ":", password_, "@", &str); - } else if (!username_.empty()) { - toAppend(username_, "@", &str); - } - toAppend(host_, &str); - if (port_ != 0) { - toAppend(":", port_, &str); - } - toAppend(path_, &str); - if (!query_.empty()) { - toAppend("?", query_, &str); - } - if (!fragment_.empty()) { - toAppend("#", fragment_, &str); - } - return str; -} - -} // namespace folly - diff --git a/hphp/third_party/folly/folly/Uri.cpp b/hphp/third_party/folly/folly/Uri.cpp deleted file mode 100644 index 9ae5c713d..000000000 --- a/hphp/third_party/folly/folly/Uri.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#include "folly/Uri.h" - -#include -#include - -namespace folly { - -namespace { - -fbstring submatch(const boost::cmatch& m, size_t idx) { - auto& sub = m[idx]; - return fbstring(sub.first, sub.second); -} - -template -void toLower(String& s) { - for (auto& c : s) { - c = tolower(c); - } -} - -} // namespace - -Uri::Uri(StringPiece str) : port_(0) { - static const boost::regex uriRegex( - "([a-zA-Z][a-zA-Z0-9+.-]*):" // scheme: - "([^?#]*)" // authority and path - "(?:\\?([^#]*))?" // ?query - "(?:#(.*))?"); // #fragment - static const boost::regex authorityAndPathRegex("//([^/]*)(/.*)?"); - - boost::cmatch match; - if (UNLIKELY(!boost::regex_match(str.begin(), str.end(), match, uriRegex))) { - throw std::invalid_argument("invalid URI"); - } - - scheme_ = submatch(match, 1); - toLower(scheme_); - - StringPiece authorityAndPath(match[2].first, match[2].second); - boost::cmatch authorityAndPathMatch; - if (!boost::regex_match(authorityAndPath.begin(), - authorityAndPath.end(), - authorityAndPathMatch, - authorityAndPathRegex)) { - // Does not start with //, doesn't have authority - path_ = authorityAndPath.fbstr(); - } else { - static const boost::regex authorityRegex( - "(?:([^@:]*)(?::([^@]*))?@)?" // username, password - "(\\[[^\\]]*\\]|[^\\[:]*)" // host (IP-literal, dotted-IPv4, or - // named host) - "(?::(\\d*))?"); // port - - auto authority = authorityAndPathMatch[1]; - boost::cmatch authorityMatch; - if (!boost::regex_match(authority.first, - authority.second, - authorityMatch, - authorityRegex)) { - throw std::invalid_argument("invalid URI authority"); - } - - StringPiece port(authorityMatch[4].first, authorityMatch[4].second); - if (!port.empty()) { - port_ = to(port); - } - - username_ = submatch(authorityMatch, 1); - password_ = submatch(authorityMatch, 2); - host_ = submatch(authorityMatch, 3); - path_ = submatch(authorityAndPathMatch, 2); - } - - query_ = submatch(match, 3); - fragment_ = submatch(match, 4); -} - -} // namespace folly diff --git a/hphp/third_party/folly/folly/Uri.h b/hphp/third_party/folly/folly/Uri.h deleted file mode 100644 index 8885bb9f4..000000000 --- a/hphp/third_party/folly/folly/Uri.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_URI_H_ -#define FOLLY_URI_H_ - -#include "folly/String.h" - -namespace folly { - -/** - * Class representing a URI. - * - * Consider http://www.facebook.com/foo/bar?key=foo#anchor - * - * The URI is broken down into its parts: scheme ("http"), authority - * (ie. host and port, in most cases: "www.facebook.com"), path - * ("/foo/bar"), query ("key=foo") and fragment ("anchor"). The scheme is - * lower-cased. - * - * If this Uri represents a URL, note that, to prevent ambiguity, the component - * parts are NOT percent-decoded; you should do this yourself with - * uriUnescape() (for the authority and path) and uriUnescape(..., - * UriEscapeMode::QUERY) (for the query, but probably only after splitting at - * '&' to identify the individual parameters). - */ -class Uri { - public: - /** - * Parse a Uri from a string. Throws std::invalid_argument on parse error. - */ - explicit Uri(StringPiece str); - - const fbstring& scheme() const { return scheme_; } - const fbstring& username() const { return username_; } - const fbstring& password() const { return password_; } - const fbstring& host() const { return host_; } - uint32_t port() const { return port_; } - const fbstring& path() const { return path_; } - const fbstring& query() const { return query_; } - const fbstring& fragment() const { return fragment_; } - - template - String toString() const; - - std::string str() const { return toString(); } - fbstring fbstr() const { return toString(); } - - private: - fbstring scheme_; - fbstring username_; - fbstring password_; - fbstring host_; - uint32_t port_; - fbstring path_; - fbstring query_; - fbstring fragment_; -}; - -} // namespace folly - -#include "folly/Uri-inl.h" - -#endif /* FOLLY_URI_H_ */ diff --git a/hphp/third_party/folly/folly/Varint.h b/hphp/third_party/folly/folly/Varint.h deleted file mode 100644 index 3349cb153..000000000 --- a/hphp/third_party/folly/folly/Varint.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_VARINT_H_ -#define FOLLY_VARINT_H_ - -#include "folly/Range.h" - -namespace folly { - -/** - * Variable-length integer encoding, using a little-endian, base-128 - * representation. - * - * The MSb is set on all bytes except the last. - * - * Details: - * https://developers.google.com/protocol-buffers/docs/encoding#varints - * - * If you want to encode multiple values, GroupVarint (in GroupVarint.h) - * is faster and likely smaller. - */ - -/** - * Maximum length (in bytes) of the varint encoding of a 32-bit value. - */ -constexpr size_t kMaxVarintLength32 = 5; - -/** - * Maximum length (in bytes) of the varint encoding of a 64-bit value. - */ -constexpr size_t kMaxVarintLength64 = 10; - -/** - * Encode a value in the given buffer, returning the number of bytes used - * for encoding. - * buf must have enough space to represent the value (at least - * kMaxVarintLength64 bytes to encode arbitrary 64-bit values) - */ -size_t encodeVarint(uint64_t val, uint8_t* buf); - -/** - * Decode a value from a given buffer, advances data past the returned value. - */ -uint64_t decodeVarint(ByteRange& data); - -/** - * ZigZag encoding that maps signed integers with a small absolute value - * to unsigned integers with a small (positive) values. Without this, - * encoding negative values using Varint would use up 9 or 10 bytes. - * - * if x >= 0, encodeZigZag(x) == 2*x - * if x < 0, encodeZigZag(x) == -2*x + 1 - */ - -inline uint64_t encodeZigZag(int64_t val) { - // Bit-twiddling magic stolen from the Google protocol buffer document; - // val >> 63 is an arithmetic shift because val is signed - return static_cast((val << 1) ^ (val >> 63)); -} - -inline int64_t decodeZigZag(uint64_t val) { - return static_cast((val >> 1) ^ -(val & 1)); -} - -// Implementation below - -inline size_t encodeVarint(uint64_t val, uint8_t* buf) { - uint8_t* p = buf; - while (val >= 128) { - *p++ = 0x80 | (val & 0x7f); - val >>= 7; - } - *p++ = val; - return p - buf; -} - -inline uint64_t decodeVarint(ByteRange& data) { - const int8_t* begin = reinterpret_cast(data.begin()); - const int8_t* end = reinterpret_cast(data.end()); - const int8_t* p = begin; - uint64_t val = 0; - - if (LIKELY(end - begin >= kMaxVarintLength64)) { // fast path - int64_t b; - do { - b = *p++; val = (b & 0x7f) ; if (b >= 0) break; - b = *p++; val |= (b & 0x7f) << 7; if (b >= 0) break; - b = *p++; val |= (b & 0x7f) << 14; if (b >= 0) break; - b = *p++; val |= (b & 0x7f) << 21; if (b >= 0) break; - b = *p++; val |= (b & 0x7f) << 28; if (b >= 0) break; - b = *p++; val |= (b & 0x7f) << 35; if (b >= 0) break; - b = *p++; val |= (b & 0x7f) << 42; if (b >= 0) break; - b = *p++; val |= (b & 0x7f) << 49; if (b >= 0) break; - b = *p++; val |= (b & 0x7f) << 56; if (b >= 0) break; - b = *p++; val |= (b & 0x7f) << 63; if (b >= 0) break; - throw std::invalid_argument("Invalid varint value"); // too big - } while (false); - } else { - int shift = 0; - while (p != end && *p < 0) { - val |= static_cast(*p++ & 0x7f) << shift; - shift += 7; - } - if (p == end) throw std::invalid_argument("Invalid varint value"); - val |= static_cast(*p++) << shift; - } - - data.advance(p - begin); - return val; -} - -} // namespaces - -#endif /* FOLLY_VARINT_H_ */ - diff --git a/hphp/third_party/folly/folly/detail/AtomicHashUtils.h b/hphp/third_party/folly/folly/detail/AtomicHashUtils.h deleted file mode 100644 index adf6927ce..000000000 --- a/hphp/third_party/folly/folly/detail/AtomicHashUtils.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2013 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. - */ - -// Some utilities used by AtomicHashArray and AtomicHashMap -// -// Note: no include guard; different -inl.h files include this and -// undef it more than once in a translation unit. - -#if !(defined(__x86__) || defined(__i386__) || defined(__x86_64__)) -#define FOLLY_SPIN_WAIT(condition) \ - for (int counter = 0; condition; ++counter) { \ - if (counter < 10000) continue; \ - pthread_yield(); \ - } -#else -#define FOLLY_SPIN_WAIT(condition) \ - for (int counter = 0; condition; ++counter) { \ - if (counter < 10000) { \ - asm volatile("pause"); \ - continue; \ - } \ - pthread_yield(); \ - } -#endif diff --git a/hphp/third_party/folly/folly/detail/BitIteratorDetail.h b/hphp/third_party/folly/folly/detail/BitIteratorDetail.h deleted file mode 100644 index 891b84b87..000000000 --- a/hphp/third_party/folly/folly/detail/BitIteratorDetail.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_DETAIL_BITITERATORDETAIL_H_ -#define FOLLY_DETAIL_BITITERATORDETAIL_H_ - -#include -#include -#include - -namespace folly { - -template class BitIterator; - -namespace bititerator_detail { - -// Reference to a bit. -// Templatize on both parent reference and value types to capture -// const-ness correctly and to work with the case where Ref is a -// reference-like type (not T&), just like our BitReference here. -template -class BitReference { - public: - BitReference(Ref r, size_t bit) : ref_(r), bit_(bit) { } - - operator bool() const { - return ref_ & (one_ << bit_); - } - - BitReference& operator=(bool b) { - if (b) { - set(); - } else { - clear(); - } - return *this; - } - - void set() { - ref_ |= (one_ << bit_); - } - - void clear() { - ref_ &= ~(one_ << bit_); - } - - void flip() { - ref_ ^= (one_ << bit_); - } - - private: - // shortcut to avoid writing static_cast everywhere - const static Value one_ = 1; - - Ref ref_; - size_t bit_; -}; - -template -struct BitIteratorBase { - static_assert(std::is_integral::value, - "BitIterator may only be used with integral types"); - typedef boost::iterator_adaptor< - BitIterator, // Derived - BaseIter, // Base - bool, // Value - boost::use_default, // CategoryOrTraversal - bititerator_detail::BitReference< - typename std::iterator_traits::reference, - typename std::iterator_traits::value_type - >, // Reference - ssize_t> type; -}; - - -} // namespace bititerator_detail -} // namespace folly - -#endif /* FOLLY_DETAIL_BITITERATORDETAIL_H_ */ - diff --git a/hphp/third_party/folly/folly/detail/BitsDetail.h b/hphp/third_party/folly/folly/detail/BitsDetail.h deleted file mode 100644 index 59a1a49a8..000000000 --- a/hphp/third_party/folly/folly/detail/BitsDetail.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_DETAIL_BITSDETAIL_H_ -#define FOLLY_DETAIL_BITSDETAIL_H_ - -namespace folly { -namespace detail { - -// If we're targeting an architecture with popcnt support, use -// __builtin_popcount directly, as it's presumably inlined. -// If not, use runtime detection using __attribute__((ifunc)) -// (see Bits.cpp) -#ifdef __POPCNT__ - -inline int popcount(unsigned int x) { - return __builtin_popcount(x); -} -inline int popcountll(unsigned long long x) { - return __builtin_popcountll(x); -} - -#else /* !__POPCNT__ */ - -int popcount(unsigned int x); -int popcountll(unsigned long long x); - -#endif /* !__POPCNT__ */ - -} // namespace detail -} // namespace folly - -#endif /* FOLLY_DETAIL_BITSDETAIL_H_ */ - diff --git a/hphp/third_party/folly/folly/detail/DiscriminatedPtrDetail.h b/hphp/third_party/folly/folly/detail/DiscriminatedPtrDetail.h deleted file mode 100644 index bca053343..000000000 --- a/hphp/third_party/folly/folly/detail/DiscriminatedPtrDetail.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_DETAIL_DISCRIMINATEDPTRDETAIL_H_ -#define FOLLY_DETAIL_DISCRIMINATEDPTRDETAIL_H_ - -#include - -namespace folly { -namespace dptr_detail { - -/** - * Given a target type and a list of types, return the 1-based index of the - * type in the list of types. Fail to compile if the target type doesn't - * appear in the list. - * - * GetIndex::value == 3 - * GetIndex::value -> fails to compile - */ -template struct GetTypeIndex; - -// When recursing, we never reach the 0- or 1- template argument base case -// unless the target type is not in the list. If the target type is in the -// list, we stop recursing when it is at the head of the remaining type -// list via the GetTypeIndex partial specialization. -template -struct GetTypeIndex { - static const size_t value = 1; -}; - -template -struct GetTypeIndex { - static const size_t value = 1 + GetTypeIndex::value; -}; - -// Generalize std::is_same for variable number of type arguments -template -struct IsSameType; - -template <> -struct IsSameType<> { - static const bool value = true; -}; - -template -struct IsSameType { - static const bool value = true; -}; - -template -struct IsSameType { - static const bool value = - std::is_same::value && IsSameType::value; -}; - -// Define type as the type of all T in (non-empty) Types..., asserting that -// all types in Types... are the same. -template -struct SameType; - -template -struct SameType { - typedef T type; - static_assert(IsSameType::value, - "Not all types in pack are the same"); -}; - -// Determine the result type of applying a visitor of type V on a pointer -// to type T. -template -struct VisitorResult1 { - typedef typename std::result_of::type type; -}; - -// Determine the result type of applying a visitor of type V on a const pointer -// to type T. -template -struct ConstVisitorResult1 { - typedef typename std::result_of::type type; -}; - -// Determine the result type of applying a visitor of type V on pointers of -// all types in Types..., asserting that the type is the same for all types -// in Types... -template -struct VisitorResult { - typedef typename SameType< - typename VisitorResult1::type...>::type type; -}; - -// Determine the result type of applying a visitor of type V on const pointers -// of all types in Types..., asserting that the type is the same for all types -// in Types... -template -struct ConstVisitorResult { - typedef typename SameType< - typename ConstVisitorResult1::type...>::type type; -}; - -template struct ApplyVisitor1; - -template -struct ApplyVisitor1 { - R operator()(size_t index, V&& visitor, void* ptr) const { - CHECK(false); // NOTREACHED - } -}; - -template -struct ApplyVisitor1 { - R operator()(size_t index, V&& visitor, void* ptr) const { - return (index == 1 ? visitor(static_cast(ptr)) : - ApplyVisitor1()( - index - 1, std::forward(visitor), ptr)); - } -}; - -template struct ApplyConstVisitor1; - -template -struct ApplyConstVisitor1 { - R operator()(size_t index, V&& visitor, void* ptr) const { - CHECK(false); // NOTREACHED - } -}; - -template -struct ApplyConstVisitor1 { - R operator()(size_t index, V&& visitor, void* ptr) const { - return (index == 1 ? visitor(static_cast(ptr)) : - ApplyConstVisitor1()( - index - 1, std::forward(visitor), ptr)); - } -}; - -template -struct ApplyVisitor - : ApplyVisitor1< - V, typename VisitorResult::type, Types...> { -}; - -template -struct ApplyConstVisitor - : ApplyConstVisitor1< - V, typename ConstVisitorResult::type, Types...> { -}; - -} // namespace dptr_detail -} // namespace folly - -#endif /* FOLLY_DETAIL_DISCRIMINATEDPTRDETAIL_H_ */ - diff --git a/hphp/third_party/folly/folly/detail/FileUtilDetail.h b/hphp/third_party/folly/folly/detail/FileUtilDetail.h deleted file mode 100644 index 108138b78..000000000 --- a/hphp/third_party/folly/folly/detail/FileUtilDetail.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_DETAIL_FILEUTILDETAIL_H_ -#define FOLLY_DETAIL_FILEUTILDETAIL_H_ - -#include -#include - -#include - -/** - * Helper functions and templates for FileUtil.cpp. Declared here so - * they can be unittested. - */ -namespace folly { namespace fileutil_detail { - -// Wrap call to f(args) in loop to retry on EINTR -template -ssize_t wrapNoInt(F f, Args... args) { - ssize_t r; - do { - r = f(args...); - } while (r == -1 && errno == EINTR); - return r; -} - -inline void incr(ssize_t n) { } -inline void incr(ssize_t n, off_t& offset) { offset += n; } - -// Wrap call to read/pread/write/pwrite(fd, buf, count, offset?) to retry on -// incomplete reads / writes. The variadic argument magic is there to support -// an additional argument (offset) for pread / pwrite; see the incr() functions -// above which do nothing if the offset is not present and increment it if it -// is. -template -ssize_t wrapFull(F f, int fd, void* buf, size_t count, Offset... offset) { - char* b = static_cast(buf); - ssize_t totalBytes = 0; - ssize_t r; - do { - r = f(fd, b, count, offset...); - if (r == -1) { - if (errno == EINTR) { - continue; - } - return r; - } - - totalBytes += r; - b += r; - count -= r; - incr(r, offset...); - } while (r != 0 && count); // 0 means EOF - - return totalBytes; -} - -// Wrap call to readv/preadv/writev/pwritev(fd, iov, count, offset?) to -// retry on incomplete reads / writes. -template -ssize_t wrapvFull(F f, int fd, iovec* iov, int count, Offset... offset) { - ssize_t totalBytes = 0; - ssize_t r; - do { - r = f(fd, iov, count, offset...); - if (r == -1) { - if (errno == EINTR) { - continue; - } - return r; - } - - if (r == 0) { - break; // EOF - } - - totalBytes += r; - incr(r, offset...); - while (r != 0 && count != 0) { - if (r >= iov->iov_len) { - r -= iov->iov_len; - ++iov; - --count; - } else { - iov->iov_base = static_cast(iov->iov_base) + r; - iov->iov_len -= r; - r = 0; - } - } - } while (count); - - return totalBytes; -} - -}} // namespaces - -#endif /* FOLLY_DETAIL_FILEUTILDETAIL_H_ */ - diff --git a/hphp/third_party/folly/folly/detail/FingerprintPolynomial.h b/hphp/third_party/folly/folly/detail/FingerprintPolynomial.h deleted file mode 100644 index 9d191422b..000000000 --- a/hphp/third_party/folly/folly/detail/FingerprintPolynomial.h +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2013 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. - */ - -#ifndef FOLLY_BUILD_FINGERPRINTPOLYNOMIAL_H_ -#define FOLLY_BUILD_FINGERPRINTPOLYNOMIAL_H_ - -#include - -namespace folly { -namespace detail { - -/** - * Representation of a polynomial of degree DEG over GF(2) (that is, - * with binary coefficients). - * - * Probably of no use outside of Fingerprint code; used by - * GenerateFingerprintTables and the unittest. - */ -template -class FingerprintPolynomial { - public: - FingerprintPolynomial() { - for (int i = 0; i < size(); i++) { - val_[i] = 0; - } - } - - explicit FingerprintPolynomial(const uint64_t* vals) { - for (int i = 0; i < size(); i++) { - val_[i] = vals[i]; - } - } - - void write(uint64_t* out) const { - for (int i = 0; i < size(); i++) { - out[i] = val_[i]; - } - } - - void add(const FingerprintPolynomial& other) { - for (int i = 0; i < size(); i++) { - val_[i] ^= other.val_[i]; - } - } - - // Multiply by X. The actual degree must be < DEG. - void mulX() { - CHECK_EQ(0, val_[0] & (1UL<<63)); - uint64_t b = 0; - for (int i = size()-1; i >= 0; i--) { - uint64_t nb = val_[i] >> 63; - val_[i] = (val_[i] << 1) | b; - b = nb; - } - } - - // Compute (this * X) mod P(X), where P(X) is a monic polynomial of degree - // DEG+1 (represented as a FingerprintPolynomial object, with the - // implicit coefficient of X^(DEG+1)==1) - // - // This is a bit tricky. If k=DEG+1: - // Let P(X) = X^k + p_(k-1) * X^(k-1) + ... + p_1 * X + p_0 - // Let this = A(X) = a_(k-1) * X^(k-1) + ... + a_1 * X + a_0 - // Then: - // A(X) * X - // = a_(k-1) * X^k + (a_(k-2) * X^(k-1) + ... + a_1 * X^2 + a_0 * X) - // = a_(k-1) * X^k + (the binary representation of A, left shift by 1) - // - // if a_(k-1) = 0, we can ignore the first term. - // if a_(k-1) = 1, then: - // X^k mod P(X) - // = X^k - P(X) - // = P(X) - X^k - // = p_(k-1) * X^(k-1) + ... + p_1 * X + p_0 - // = exactly the binary representation passed in as an argument to this - // function! - // - // So A(X) * X mod P(X) is: - // the binary representation of A, left shift by 1, - // XOR p if a_(k-1) == 1 - void mulXmod(const FingerprintPolynomial& p) { - bool needXOR = (val_[0] & (1UL<<63)); - val_[0] &= ~(1UL<<63); - mulX(); - if (needXOR) { - add(p); - } - } - - // Compute (this * X^k) mod P(X) by repeatedly multiplying by X (see above) - void mulXkmod(int k, const FingerprintPolynomial& p) { - for (int i = 0; i < k; i++) { - mulXmod(p); - } - } - - // add X^k, where k <= DEG - void addXk(int k) { - DCHECK_GE(k, 0); - DCHECK_LE(k, DEG); - int word_offset = (DEG - k) / 64; - int bit_offset = 63 - (DEG - k) % 64; - val_[word_offset] ^= (1UL << bit_offset); - } - - // Set the highest 8 bits to val. - // If val is interpreted as polynomial of degree 7, then this sets *this - // to val * X^(DEG-7) - void setHigh8Bits(uint8_t val) { - val_[0] = ((uint64_t)val) << (64-8); - for (int i = 1; i < size(); i++) { - val_[i] = 0; - } - } - - static int size() { - return 1 + DEG/64; - } - private: - // Internal representation: big endian - // val_[0] contains the highest order coefficients, with bit 63 as the - // highest order coefficient - // - // If DEG+1 is not a multiple of 64, val_[size()-1] only uses the highest - // order (DEG+1)%64 bits (the others are always 0) - uint64_t val_[1 + DEG/64]; -}; - -} // namespace detail -} // namespace folly - -#endif /* FOLLY_BUILD_FINGERPRINTPOLYNOMIAL_H_ */ - diff --git a/hphp/third_party/folly/folly/detail/Futex.h b/hphp/third_party/folly/folly/detail/Futex.h deleted file mode 100644 index 93544bc26..000000000 --- a/hphp/third_party/folly/folly/detail/Futex.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2013 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 -#include -#include -#include -#include -#include -#include -#include - -namespace folly { namespace detail { - -/** - * Futex is an atomic 32 bit unsigned integer that provides access to the - * futex() syscall on that value. It is templated in such a way that it - * can interact properly with DeterministicSchedule testing. - * - * If you don't know how to use futex(), you probably shouldn't be using - * this class. Even if you do know how, you should have a good reason - * (and benchmarks to back you up). - */ -template